Files
pm/debug.tex
KoCoder bc4044803e
Some checks failed
Word Count / count-words (push) Failing after 31s
Keine Ahnung Bro
2026-03-23 08:18:04 +01:00

6008 lines
277 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
% Options for packages loaded elsewhere
\PassOptionsToPackage{unicode}{hyperref}
\PassOptionsToPackage{hyphens}{url}
\documentclass[
12pt,
twoside]{article}
\usepackage{xcolor}
\usepackage[top=2.5cm, bottom=2cm, left=3cm, right=2cm, a4paper]{geometry}
\usepackage{amsmath,amssymb}
\setcounter{secnumdepth}{5}
\usepackage{iftex}
\ifPDFTeX
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{textcomp} % provide euro and other symbols
\else % if luatex or xetex
\usepackage{unicode-math} % this also loads fontspec
\defaultfontfeatures{Scale=MatchLowercase}
\defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
\fi
\usepackage{lmodern}
\ifPDFTeX\else
% xetex/luatex font selection
\fi
% Use upquote if available, for straight quotes in verbatim environments
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
\IfFileExists{microtype.sty}{% use microtype if available
\usepackage[]{microtype}
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
}{}
\usepackage{setspace}
\makeatletter
\@ifundefined{KOMAClassName}{% if non-KOMA class
\IfFileExists{parskip.sty}{%
\usepackage{parskip}
}{% else
\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt plus 2pt minus 1pt}}
}{% if KOMA class
\KOMAoptions{parskip=half}}
\makeatother
\usepackage{color}
\usepackage{fancyvrb}
\newcommand{\VerbBar}{|}
\newcommand{\VERB}{\Verb[commandchars=\\\{\}]}
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
% Add ',fontsize=\small' for more characters per line
\newenvironment{Shaded}{}{}
\newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
\newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{#1}}
\newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
\newcommand{\BuiltInTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{#1}}
\newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{#1}}}
\newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}}
\newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
\newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{#1}}
\newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
\newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{#1}}}
\newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
\newcommand{\ExtensionTok}[1]{#1}
\newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
\newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{#1}}
\newcommand{\ImportTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{\textbf{#1}}}
\newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
\newcommand{\NormalTok}[1]{#1}
\newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
\newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{#1}}
\newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{#1}}
\newcommand{\RegionMarkerTok}[1]{#1}
\newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}}
\newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{#1}}
\newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
\newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
\usepackage{longtable,booktabs,array}
\usepackage{calc} % for calculating minipage widths
% Correct order of tables after \paragraph or \subparagraph
\usepackage{etoolbox}
\makeatletter
\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{}
\makeatother
% Allow footnotes in longtable head/foot
\IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}}
\makesavenoteenv{longtable}
\usepackage{graphicx}
\makeatletter
\newsavebox\pandoc@box
\newcommand*\pandocbounded[1]{% scales image to fit in text height/width
\sbox\pandoc@box{#1}%
\Gscale@div\@tempa{\textheight}{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}%
\Gscale@div\@tempb{\linewidth}{\wd\pandoc@box}%
\ifdim\@tempb\p@<\@tempa\p@\let\@tempa\@tempb\fi% select the smaller of both
\ifdim\@tempa\p@<\p@\scalebox{\@tempa}{\usebox\pandoc@box}%
\else\usebox{\pandoc@box}%
\fi%
}
% Set default figure placement to htbp
\def\fps@figure{htbp}
\makeatother
% definitions for citeproc citations
\NewDocumentCommand\citeproctext{}{}
\NewDocumentCommand\citeproc{mm}{%
\begingroup\def\citeproctext{#2}\cite{#1}\endgroup}
\makeatletter
% allow citations to break across lines
\let\@cite@ofmt\@firstofone
% avoid brackets around text for \cite:
\def\@biblabel#1{}
\def\@cite#1#2{{#1\if@tempswa , #2\fi}}
\makeatother
\newlength{\cslhangindent}
\setlength{\cslhangindent}{1.5em}
\newlength{\csllabelwidth}
\setlength{\csllabelwidth}{3em}
\newenvironment{CSLReferences}[2] % #1 hanging-indent, #2 entry-spacing
{\begin{list}{}{%
\setlength{\itemindent}{0pt}
\setlength{\leftmargin}{0pt}
\setlength{\parsep}{0pt}
% turn on hanging indent if param 1 is 1
\ifodd #1
\setlength{\leftmargin}{\cslhangindent}
\setlength{\itemindent}{-1\cslhangindent}
\fi
% set entry spacing
\setlength{\itemsep}{#2\baselineskip}}}
{\end{list}}
\usepackage{calc}
\newcommand{\CSLBlock}[1]{\hfill\break\parbox[t]{\linewidth}{\strut\ignorespaces#1\strut}}
\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{\strut#1\strut}}
\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{\strut#1\strut}}
\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1}
\setlength{\emergencystretch}{3em} % prevent overfull lines
\providecommand{\tightlist}{%
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\usepackage{float}
\floatplacement{figure}{H}
\raggedbottom
\usepackage{etoolbox}
\makeatletter
\patchcmd{\LT@array}{\tabskip\z@}{\tabskip\fill}{}{}
\makeatother
% Rename Table prefix to Tab.
\AtBeginDocument{%
\renewcommand{\tablename}{Tab.}
\renewcommand{\figurename}{Abb.}
\renewcommand{\listtablename}{Tabellenverzeichnis}
}
\usepackage[hyphens]{url}
\usepackage[hidelinks]{hyperref}
\usepackage[htt]{hyphenat}
\usepackage{microtype}
\setlength{\emergencystretch}{5em} % Increased to solve overfull lines
\usepackage{tocloft}
% Dotted lines for all levels in TOC
\renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}}
\renewcommand{\cftsubsecleader}{\cftdotfill{\cftdotsep}}
\renewcommand{\cftsubsubsecleader}{\cftdotfill{\cftdotsep}}
% Bold section numbers and names
\renewcommand{\cftsecfont}{\bfseries}
\renewcommand{\cftsecpagefont}{\bfseries}
% Adjust vertical spacing before sections
\setlength{\cftbeforesecskip}{0.5em}
\usepackage{fvextra}
\fvset{breaklines=true}
\usepackage{needspace}
\let\oldShaded\Shaded
\let\oldendShaded\endShaded
\renewenvironment{Shaded}{\needspace{8\baselineskip}\oldShaded}{\oldendShaded}
\usepackage{fancyhdr}
% Define the fancy style but don't activate yet
\fancypagestyle{fancy}{
\fancyhf{} % clear all header and footer fields
\fancyhead[RO,LE]{\includegraphics[height=2cm]{images/preamble/logo.png}}
\fancyhead[LO,RE]{\nouppercase{\leftmark}}
\fancyfoot[RO,LE]{\thepage}
\renewcommand{\headrulewidth}{0.4pt}
\renewcommand{\footrulewidth}{0.4pt}
\newgeometry{top=2.5cm, bottom=4cm, left=3cm, right=2cm, includehead, includefoot, a4paper}
\setlength{\headheight}{2cm}
\setlength{\footskip}{1.5cm}
}
\renewcommand{\sectionmark}[1]{\markboth{#1}{}} % Set section title to \leftmark
% Default style for preamble/TOC
\pagestyle{plain}
\fancyhf{}
\fancyfoot[C]{\thepage}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0pt}
\usepackage{bookmark}
\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
\urlstyle{same}
\hypersetup{
hidelinks,
pdfcreator={LaTeX via pandoc}}
\author{}
\date{}
\begin{document}
\begin{titlepage}
\begin{minipage}[c]{0.25\textwidth}
\includegraphics[width=\textwidth]{images/preamble/szu.png}
\end{minipage}
\hfill
\begin{minipage}[c]{0.7\textwidth}
\raggedleft
\textbf{Schulzentrum HTL HAK Ungargasse} \\
Höhere Lehranstalt für Informationstechnologie \\
Ausbildungsschwerpunkt Netzwerktechnik
\end{minipage}
\rule{\textwidth}{0.4pt}
\vspace{1cm}
\begin{center}
\includegraphics[width=0.6\textwidth]{images/preamble/logo.png} \\
\vspace{1.5cm}
\textbf{\Huge DIPLOMARBEIT} \\
\vspace{1cm}
\textbf{\huge AlumniHub} \\
\vspace{0.5cm}
\textit{\Large Plattform für den Absolventenverein des SZ HTL HAK Ungargasse}
\end{center}
\vspace{1.75cm}
\begin{minipage}[t]{0.5\textwidth}
\textbf{Verfasser:} \\
Adam Gaiswinkler \\
Florian Edlmayer \\
Konstantin Hintermayer
\end{minipage}
\begin{minipage}[t]{0.4\textwidth}
\textbf{Betreuer:} \\
Prof. Thomas Gürth
\end{minipage}
\vspace{0.5cm}
\begin{minipage}[t]{0.5\textwidth}
\textbf{Klasse:} \\
5BHITN
\end{minipage}
\begin{minipage}[t]{0.4\textwidth}
\textbf{Schuljahr:} \\
2025/26
\end{minipage}
\vspace{0.75cm}
\begin{minipage}[t]{0.5\textwidth}
\textbf{Abgabevermerk:} \\
Wien, am 26.03.2026
\end{minipage}
\begin{minipage}[t]{0.4\textwidth}
\textbf{Projektnummer:} \\
3U\_HI\_2026\_04 \\
\hrulefill
\end{minipage}
\vspace{0.25cm}
\textbf{übernommen von:} \\
\hrulefill
\end{titlepage}
\newpage
\pagenumbering{Roman}
\cleardoublepage
\section*{Eidesstattliche Erklärung}
Ich erkläre, dass ich die vorliegende Diplomarbeit selbstständig und ohne fremde Hilfe verfasst, andere als die angegebenen Quellen und Hilfsmittel nicht benutzt, sowie die aus anderen Werken übernommene Inhalte durch Quellenangaben erkenntlich gemacht habe.
\vspace{1.5cm}
\begin{tabular}{@{}p{5cm}p{1cm}p{7cm}@{}}
Wien, am 26.03.2026 & & \hrulefill \\
& & Adam Gaiswinkler
\end{tabular}
\vspace{1.2cm}
\begin{tabular}{@{}p{5cm}p{1cm}p{7cm}@{}}
Wien, am 26.03.2026 & & \hrulefill \\
& & Florian Edlmayer
\end{tabular}
\vspace{1.2cm}
\begin{tabular}{@{}p{5cm}p{1cm}p{7cm}@{}}
Wien, am 26.03.2026 & & \hrulefill \\
& & Konstantin Hintermayer
\end{tabular}
\makeatletter
\renewcommand{\tableofcontents}{%
\@starttoc{toc}%
}
\renewcommand{\listoffigures}{%
\@starttoc{lof}%
}
\renewcommand{\listoftables}{%
\@starttoc{lot}%
}
\setstretch{1.5}
\newpage
\section*{Kurzfassung}\label{kurzfassung}
In dieser Diplomarbeit geht es um die Entwicklung von „AlumniHub`` --
einer Webanwendung für den Absolventenverein der HTL Ungargasse. Wir
haben die alte, händische Verwaltung durch ein modernes, automatisiertes
System ersetzt, das auf ASP.NET Core, Blazor und dem Oqtane CMS basiert.
Ziel war es, eine Plattform zu schaffen, die Kommunikation,
Event-Planung und Networking an einem zentralen Ort bündelt.
Die technische Arbeit haben wir uns im Team aufgeteilt: Konstantin
Hintermayer hat als Product Owner die Linux-Serverinfrastruktur, die
PostgreSQL-Datenbank und die CI/CD-Pipelines aufgesetzt sowie das
digitale „Schwarze Brett`` programmiert. Adam Gaiswinkler hat das
responsive Design im Stil der Schule entworfen und das Event-Anmeldetool
sowie die „Hall of Fame`` inklusive PDF-Export umgesetzt. Florian
Edlmayer war für den Premiumbereich mit seinem Freigabe-Workflow
verantwortlich, hat die DSGVO-Vorgaben umgesetzt und den sicheren Login
über LinkedIn (OAuth 2.0) eingebaut. Das Ergebnis ist eine
leistungsstarke Plattform, die dem Verein die tägliche Verwaltungsarbeit
deutlich erleichtert. \cleardoublepage
\section*{Abstract}\label{abstract}
This diploma thesis covers the development of ``AlumniHub,'' a web
platform designed for the alumni association of the HTL Ungargasse. Our
goal was to replace their old, manual administration with an automated
system using ASP.NET Core, Blazor, and the Oqtane CMS. The platform now
handles communication, event planning, and networking in one central
place.
The technical work was split between the team: Konstantin Hintermayer
(Product Owner) was responsible for the Linux server infrastructure, the
PostgreSQL database, and CI/CD pipelines, while also building the
digital ``Blackboard.'' Adam Gaiswinkler created the responsive UI in
the school's style and implemented the event registration tool and the
``Hall of Fame'' with automated PDF exports. Florian Edlmayer developed
the premium area and its approval workflow, ensured GDPR compliance, and
integrated the LinkedIn Login (OAuth 2.0). Overall, we built a
high-performance system that significantly simplifies the association's
daily administrative work.
\newpage
\section*{Danksagung}\label{danksagung}
Obwohl die Umsetzung dieses Projektes durch das Projektteam selbst
erfolgte, gab es eine Reihe an Personen, die uns tatkräftig unterstützt
haben.
An erster Stelle möchten wir uns bei Herrn Prof.~Thomas Gürth und Herrn
Prof.~Johannes Kreuzer bedanken. Ohne Ihre umfangreiche Unterstützung,
Kreativität und fachliche Kompetenz wäre das Projekt in dieser Form
nicht realisierbar gewesen. Sie beide haben uns durch dieses Projekt
begleitet, uns mit wertvollen Ideen und Hilfestellungen versorgt und uns
gleichzeitig den notwendigen Freiraum für eigenständiges Lernen und
Arbeiten gelassen.
Neben einer exzellenten Betreuung bedarf eine Diplomarbeit auch eines
engagierten Auftraggebers. Bei Lukas Aigner bedanken wir uns für das
Vertrauen in unser Team und die Ermöglichung dieses Projektes. Trotz
zeitlicher Verzögerungen im Rahmen der Arbeit, erfuhren wir von seiner
Seite großes Verständnis und fanden stets eine konstruktive Basis für
Diskussionen vor.
Ein besonders komplexer Aspekt dieser Arbeit war die Infrastruktur, für
die im Prozess drei verschiedene Lösungen evaluiert wurden. Wir danken
Stefan Reinel und dem Team von LiveDesign für die bereitwillige
Unterstützung. Ohne die aktuelle Infrastruktur hätten wir bis heute
keine zufriedenstellende Lösung für unsere Homepage gefunden.
Als wir im Oktober kurzfristig eine neue Produktionsumgebung benötigten,
wurde uns unbürokratisch durch die Schule geholfen. Herr Prof.~Harald
Dassler und Herr Prof.~Andreas Resch haben uns zur Weiterentwicklung
eine virtuelle Maschine zur Verfügung gestellt und den externen Zugriff
ermöglicht. Es ist nicht selbstverständlich, dass in einer Institution
dieser Größe ein solcher Wunsch so schnell und unkompliziert umgesetzt
wird.
Für den „frischen Wind`` und das sorgfältige Korrekturlesen der Arbeit
bedanken wir uns herzlich bei Frau Prof.~Gertrude Brindlmayer. Ihr
geschulter Blick auf Details, die dem Team im Arbeitsprozess entgangen
waren, stellte eine große Bereicherung für die finale Qualität dieser
Arbeit dar.
\newpage
\section*{Vorwort}\label{vorwort}
Diese Diplomarbeit ist im Schuljahr 2025/2026 an der HTL SZU Ungargasse
im Rahmen des Projekts Alumnihub entstanden. Unser Ziel war es, zusammen
mit dem Absolventenverein die veraltete Mitgliederverwaltung zu
modernisieren und eine neue digitale Plattform für ehemalige
Schülerinnen und Schüler zu entwickeln. Was am Anfang nur eine
Pflichtaufgabe für die Schule war, wurde für uns schnell zu einem
Projekt, mit dem wir uns voll identifiziert haben. Besonders
herausfordernd war, dass unser Team im Laufe der Zeit kleiner wurde und
wir am Ende nur noch zu dritt waren. Konstantin Hintermayer, Florian
Edlmayer und Adam Gaiswinkler mussten deshalb viel mehr Verantwortung
übernehmen und technisches Wissen schneller aufbauen als eigentlich
geplant. Die Arbeit am AlumniHub war für uns die ideale Chance, das
Wissen aus der Theorie endlich mal in einem echten und anspruchsvollen
Softwareprojekt einzusetzen. Es war eine stressige, aber sehr lehrreiche
Zeit, und wir sind stolz auf das, was wir am Ende als Team abgeliefert
haben.
\newpage
\section*{Einsatz Künstlicher Intelligenz
(KI-Disclaimer)}\label{einsatz-kuxfcnstlicher-intelligenz-ki-disclaimer}
Diese Diplomarbeit und die Software Alumnihub wurden von uns
eigenständig entwickelt. KI-Tools haben wir lediglich als Unterstützung
für Aufgaben wie Korrekturlesen, Übersetzungen oder zur gezielten
Recherche genutzt. Auch beim Debugging im Code war die KI eine Hilfe.
Der eigentliche Quellcode, die inhaltliche Ausarbeitung und alle
technischen Entscheidungen stammen jedoch direkt von uns. Sämtliche
KI-Vorschläge wurden von uns kritisch hinterfragt und nur nach eigener
Prüfung übernommen.
\cleardoublepage
\pagestyle{empty}
\pagenumbering{arabic}
\setcounter{page}{1}
\cleardoublepage
\section*{Inhaltsverzeichnis}\label{inhaltsverzeichnis}
\tableofcontents
\cleardoublepage
\pagestyle{fancy}
\pagenumbering{arabic}
\section{Einleitung Allgemeiner Teil}\label{einleitung-allgemeiner-teil}
\subsection{Diplomarbeitsantrag}\label{diplomarbeitsantrag}
\subsubsection{Ausgangslage}\label{ausgangslage}
Der Absolventenverein verwaltet derzeit rund 300 Alumni, deren Daten in
einer CSV-Datei gepflegt werden. Die Event-Anmeldungen der letzten Jahre
erfolgten über ``Perspective Funnels'', dessen Lizenz mittlerweile
abgelaufen ist. Für die kommenden Veranstaltungen wird daher ein Ersatz
für die Event-Registrierung benötigt. Die aktuellen Abläufe sind
dezentral und teilweise manuell, was zu einem erhöhten
Verwaltungsaufwand und fehlender Übersicht führt. Eine integrierte
Plattform für Mitgliederverwaltung, Kommunikation und Event-Organisation
existiert bislang nicht.
\subsubsection{Zielsetzung}\label{zielsetzung}
Ziel der Diplomarbeit ist es, ein Content-Management-System als Basis
für eine innovative, barrierefreie und sichere Webseite des
Absolventenvereins zu nutzen, dabei vorhandene Funktionen des CMS
anzupassen und eigene Module wie eine Eventanmeldung, eine Hall of Fame,
ein Schwarzes Brett als Kommunikationsplattform sowie einen
Premiumbereich zu entwickeln. Die Anwendung soll modular, erweiterbar
und leicht wartbar sein und auf einem Schulserver betrieben werden, um
eine kontrollierte und geschützte Umgebung für die Verwaltung der
Mitglieder- und Veranstaltungsdaten zu gewährleisten.
\subsubsection{Tatsächliches Ergebnis}\label{tatsuxe4chliches-ergebnis}
Im Rahmen des Projekts konnten alle priorisierten Tickets aus dem
Product Backlog erfolgreich umgesetzt werden.
Konstantin Hintermayer übernahm als Product Owner die Kommunikation mit
dem Auftraggeber und stellte einen funktionsfähigen Linux-Server als
technische Grundlage bereit. Zusätzlich erarbeitete er Skripte sowie
Dokumentationen zur Serverlandschaft und führte die Grundkonfiguration
des CMS durch. Weiters wurden die Module „Auswertung`` und „Schwarzes
Brett`` erfolgreich an den Kunden übergeben.
Adam Gaiswinkler war für die Dokumentation der Benutzeroberfläche und
des Backends sowie für die Entwicklung eines CMS-Moduls zur Anmeldung
und für die „Hall of Fame`` verantwortlich. Auch die grundlegende
Konfiguration des CMS wurde erfolgreich umgesetzt.
Florian Edlmayer entwickelte ein funktionierendes CMS-Modul für den
Premiumbereich der Absolventen. Zusätzlich erstellte er eine
vollständige und gesetzeskonforme Datenschutzerklärung. Ein weiterer
wichtiger Bestandteil war die erfolgreiche Implementierung einer
OAuth-Authentifizierung über LinkedIn.
\subsection{Das Team}\label{das-team}
Konstantin Hintermayer
Geboren am: 25.05.2007
E-Mail: {[}konstantin.hintermayer@edu.szu.at{]}
Individuelle Themenstellung: Projektleitung (Product Owner), (Server-)
Infrastruktur, Aufsetzen und Installieren der Server / Linux /
Datenbank, Adminzugriff, Skripten, Entwicklung der Auswertungen und des
schwarzen Bretts.
Florian Edlmayer
Geboren am: 20.08.2006
E-Mail: {[}florian.edlmayer@edu.szu.at{]}
Individuelle Themenstellung: Entwicklung eines CMS-Moduls für den
Premiumbereich des Absolventenvereins,Verfassung der
Datenschutzerklärung und zuständig für Informationssicherheit,
Implementierung von OAuth, Datenbankmanagement.
Adam Gaiswinkler
Geboren am: 11.10.2006
E-Mail: {[}adam.gaiswinkler@edu.szu.at{]}
Individuelle Themenstellung:Entwicklung zweier CMS-Module: (Anmeldetool
für Treffen und Hall of Fame), Grundkonfiguration des CMS, Starker Fokus
auf nutzerfreundliches und responsives Design, Web-Entwicklung mit C\#
und ASP.NET.
\subsection{Fachliches
Umfeld(Technologien)}\label{fachliches-umfeldtechnologien}
Im fachlichen Umfeld dieser Diplomarbeit kommt das
Content-Management-System Oqtane zum Einsatz. Oqtane basiert auf
modernen Webtechnologien wie ASP.NET Core und der Plattform .NET und
ermöglicht die Entwicklung modularer und skalierbarer Webanwendungen.
Durch seine komponentenbasierte Architektur können Funktionen in Form
von Modulen flexibel entwickelt und in die Anwendung integriert werden.
Dies erleichtert sowohl die Erweiterbarkeit als auch die Wartung der
Plattform. Besonders im Kontext dieser Arbeit bietet Oqtane eine
geeignete Grundlage, um eine dynamische und benutzerorientierte
Alumni-Plattform umzusetzen, da sowohl Inhalte als auch
Benutzerverwaltung effizient verwaltet werden können.
Siehe Details unter: Konstantin Hintermayer individueller Teil
\section{Projektplanung}\label{projektplanung}
\subsection{Projektziele}\label{projektziele}
\subsubsection{Hauptziele}\label{hauptziele}
\begin{itemize}
\item
\textbf{HZ01 -- Verwaltung von Absolventenprofilen} Das System
ermöglicht die zentrale Erstellung und Pflege individueller Profile
für Absolventen.
\item
\textbf{HZ02 -- Organisation von Veranstaltungen} Die Applikation
stellt Funktionen zur Planung und Durchführung von Vereinstreffen und
Events bereit.
\item
\textbf{HZ03 -- Darstellung der Hall of Fame} Besondere Leistungen und
Erfolge von Absolventen werden in einer digitalen Ehrengalerie
präsentiert.
\item
\textbf{HZ04 -- Firmen- und Jobstellenmarkt} Integration einer
Plattform für Stellenangebote, Praktika und die Vernetzung mit
Partnerunternehmen.
\item
\textbf{HZ05 -- Zugriffsverwaltung und Sicherheit} Implementierung
einer rollenbasierten Zugriffskontrolle und Gewährleistung der
Datensicherheit nach DSGVO-Standard.
\item
\textbf{HZ06 -- Schülerzugang und CMS} Einbindung eines
Redaktionssystems für Beiträge und eines speziellen Zugangs für
aktuelle Schüler.
\end{itemize}
\subsubsection{Hardwareanforderungen und
Infrastruktur}\label{hardwareanforderungen-und-infrastruktur}
\begin{itemize}
\item
\textbf{HW01 -- Benutzerkapazität (300 Nutzer)} Das System unterstützt
mindestens 300 gleichzeitig registrierte Benutzer für Administration
und Nutzung.
\item
\textbf{HW02 -- Aktive Zugriffsrate (50 Zugriffe/Tag)} Die
Infrastruktur ist auf ca. 50 tägliche aktive Nutzer ohne
Performanceverluste ausgelegt.
\item
\textbf{HW03 -- Einfache Skalierbarkeit} Die Architektur erlaubt eine
flexible Erweiterung der Ressourcen ohne strukturelle Änderungen.
\item
\textbf{HW04/05 -- Hochverfügbarkeit (99,5 \%)} Der Betrieb erfolgt
auf skalierbaren Cloud-Servern mit einer angestrebten Verfügbarkeit
von 99,5 \% pro Jahr.
\item
\textbf{HW07 -- Disaster Recovery (24h)} Im Falle eines Totalausfalls
muss das System innerhalb von 24 Stunden wieder benutzbar sein.
\end{itemize}
\textbf{HW12/13 -- Automatisierte Sicherung} Implementierung einer
automatisierten Datensicherung für Datenbanken und Binaries auf
unabhängigen Speichern.
\textbf{RED-1 -- Redundanz der Datenbank} Die Datenbank wird zur
Ausfallsicherheit redundant geführt und nutzt automatische
Master-Promotion im Fehlerfall.
\subsubsection{Schnittstellenanforderungen}\label{schnittstellenanforderungen}
\begin{itemize}
\item
\textbf{SnT-1 -- REST-API (Frontend/Backend)} Bereitstellung einer API
zur Kommunikation zwischen dem Oqtane-CMS und dem Blazor-Frontend für
dynamische CRUD-Operationen.
\item
\textbf{SnT-2 -- SMTP-E-Mail-Versand (Brevo)} Einbindung des Brevo
SMTP-Dienstes für den Versand von bis zu 300 transaktionalen E-Mails
pro Tag.
\item
\textbf{SnT-3 -- LinkedIn OAuth (Phase 2)} Schnittstelle zur
Authentifizierung und zum automatisierten Abruf von Profildaten über
LinkedIn.
\end{itemize}
\subsubsection{Software und
Zugriffsverwaltung}\label{software-und-zugriffsverwaltung}
\begin{itemize}
\item
\textbf{SW-1/2 -- Barrierefreiheit und Browser-Support} Unterstützung
gängiger Browser und Einhaltung des WCAG 2.1 AA Standards für
Barrierefreiheit.
\item
\textbf{SW-4 -- Sicherer Administrationszugriff} Administrativer
Zugriff erfolgt ausschließlich über eine verschlüsselte VPN-Verbindung
(Wireguard) und SSH.
\item
\textbf{ZUG-1--4 -- Authentifizierung und Rollen} Implementierung
klassischer Login-Verfahren, Passwort-Reset-Funktionen und einer
detaillierten Benutzerverwaltung für Administratoren.
\item
\textbf{ZUG-6/7 -- Magic Link und 2FA (Phase 2)} Erweiterung der
Sicherheit durch passwortlose Anmeldung via E-Mail-Link und
Zwei-Faktor-Authentifizierung.
\end{itemize}
\subsubsection{Funktionsmodule (Phase 2)}\label{funktionsmodule-phase-2}
\begin{itemize}
\item
\textbf{HoF-1/2 -- Hall of Fame Funktionen} Nutzer können ihre Erfolge
online präsentieren und als ansprechendes PDF für Treffen exportieren.
\item
\textbf{JOB-1/2 -- Job- und Praktikumsbörse} Plattform für
Stellenanzeigen und Bewerbungen inklusive Dashboard für Arbeitgeber
und statistischen Auswertungen.
\item
\textbf{PRE-1--3 -- Premiumbereich} Exklusiver Zugang für
Premiummitglieder inklusive Einsicht in Ingenieuranträge und
erweiterter Event-Organisation.
\item
\textbf{ScB-1--4 -- Schwarzes Brett} Ein interaktiver Feed für
Nachrichten, Event-Ankündigungen und Werbung mit integriertem
Meldesystem für Inhalte.
\item
\textbf{AfT-1--5 -- Anmeldetool für Treffen} Umfassendes Tool zur
Eventerstellung, zum Versand von Einladungen (E-Mail/SMS) und zur
Zielgruppenfilterung.
\item
\textbf{Pro-1--4 -- Erweiterte Profilverwaltung} Nutzer können ihre
Daten manuell pflegen oder automatisiert mit ihrem LinkedIn-Profil
abgleichen lassen.
\end{itemize}
\subsubsection{Daten und Dokumentation}\label{daten-und-dokumentation}
\begin{itemize}
\item
\textbf{DB-1 -- PostgreSQL Datenbank} Entwicklung eines relationalen
Datenbankdesigns inklusive Indizierung und Verschlüsselung zur
Gewährleistung der Datenintegrität.
\item
\textbf{DOK-1--6 -- Projektdokumentation} Erstellung aller notwendigen
Handbücher (Benutzer, Admin, Git) und des Disaster-Recovery-Handbuchs.
\end{itemize}
\subsubsection{Nicht-Ziele}\label{nicht-ziele}
\begin{itemize}
\item
\textbf{NZ01 -- Zahlungsprozesse} Die technische Abwicklung von
Zahlungen im Premiumbereich liegt außerhalb des Projektscopes.
\item
\textbf{NZ02 -- Externe Kalenderlösungen} Die Integration externer
Dienste wie V-Calendar oder V-Event ist nicht vorgesehen.
\end{itemize}
\subsection{Aufgabenverteilung}\label{aufgabenverteilung}
\subsubsection{Konstantin Hintermayer}\label{konstantin-hintermayer}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.2051}}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.7949}}@{}}
\caption{Aufgabenverteilung Konstantin Hintermayer}\tabularnewline
\toprule\noalign{}
\endfirsthead
\endhead
\bottomrule\noalign{}
\endlastfoot
Themenstellung: & Die individuelle Themenstellung umfasst die Konzeption
und Umsetzung der Infrastruktur sowie die Entwicklung von Modulen für
Auswertungen und ein Schwarzes Brett innerhalb der Plattform. \\
Ziele und Anforderungen & HW01, HW02, HW03, HW04/05, HW07, RED-1, SW-4,
ScB-1--4 \\
\end{longtable}
\subsubsection{Florian Edlmayer}\label{florian-edlmayer}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.1943}}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.8057}}@{}}
\caption{Aufgabenverteilung Florian Edlmayer}\tabularnewline
\toprule\noalign{}
\endfirsthead
\endhead
\bottomrule\noalign{}
\endlastfoot
Themenstellung: & Die individuelle Themenstellung umfasst die
Entwicklung eines Premiumbereichs sowie die Umsetzung von Datenschutz-
und Informationssicherheitsmaßnahmen, einschließlich Zugriffs- und
Profilverwaltung. \\
Ziele und Anforderungen & HZ01, HZ05, HW12/13, SnT-3, ZUG-1--4, ZUG-6/7,
PRE-1--3, Pro-1--4, DB-1 \\
\end{longtable}
\subsubsection{Adam Gaiswinkler}\label{adam-gaiswinkler}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.2087}}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.7913}}@{}}
\caption{Aufgabenverteilung Adam Gaiswinkler}\tabularnewline
\toprule\noalign{}
\endfirsthead
\endhead
\bottomrule\noalign{}
\endlastfoot
Themenstellung: & Die individuelle Themenstellung umfasst die
Entwicklung einer Hall of Fame, die Implementierung eines Anmeldetools
für Treffen sowie die Konfiguration des Content-Management-Systems. \\
Ziele und Anforderungen & HZ02, HZ03, HZ06, SnT-2, SW-1/2, HoF-1/2,
AfT-1--5 \\
\end{longtable}
\subsection{Geplante Projektablauf}\label{geplante-projektablauf}
\subsubsection{Meilensteine}\label{meilensteine}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.0087}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.0405}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.9509}}@{}}
\caption{Meilensteine}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
\#
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Datum
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
\#
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Datum
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\emph{1} & \emph{19.09.2025} & Fertigstellung und Abgabe des
Diplomarbeitsantrags. \\
\emph{2} & \emph{30.10.2025} & {[}cite\_start{]}Server-Setup mit
SSH-Zugriff {[}cite: 83{]}{[}cite\_start{]}, CMS-Grundkonfiguration
(Oqtane) im SZU-Design {[}cite: 69, 83{]}{[}cite\_start{]},
Implementierung der Eingabemaske und API für das Anmeldetool {[}cite:
137{]} {[}cite\_start{]}sowie Erstellung der DSGVO-Richtlinien {[}cite:
38{]} {[}cite\_start{]}und Backupstrategie{[}cite: 55, 62{]}. \\
\emph{3} & \emph{27.11.2025} & {[}cite\_start{]}Entwicklung grafischer
Auswertungen für Anmeldedaten {[}cite: 120{]} {[}cite\_start{]}und
Integration der OAuth-Authentifizierung zum automatisierten Abruf von
Profildaten{[}cite: 78, 148{]}. \\
\emph{4} & \emph{18.12.2025} & Realisierung des Schwarzen Bretts inkl.
{[}cite\_start{]}Sperrfunktion für Administratoren {[}cite:
133{]}{[}cite\_start{]}, Entwicklung des Hall of Fame Moduls mit
PDF-Generierung {[}cite: 105{]} {[}cite\_start{]}sowie Aufbau der
Premiumkunden-Verwaltung{[}cite: 113{]}. \\
\emph{5} & \emph{21.01.2026} & {[}cite\_start{]}Erstellung der
technischen Systemdokumentation {[}cite: 155{]}{[}cite\_start{]},
Durchführung von Modultests und Implementierung der
Premium-Serviceverwaltung{[}cite: 113{]}. \\
\emph{6} & \emph{08.02.2026} & {[}cite\_start{]}Projektabschluss der
Infrastruktur-Tasks, Übergabe der Disaster-Recovery-Skripte {[}cite:
58{]} {[}cite\_start{]}und Finalisierung der gesamten
Projektdokumentation{[}cite: 155{]}. \\
\emph{7} & \emph{12.03.2026} & Finale Abgabe des Diplomarbeitsbuchs. \\
\emph{8} & \emph{22.04.2026} & Defensio (Projektabschluss). \\
\end{longtable}
\subsubsection{Sprints}\label{sprints}
\begin{longtable}[]{@{}lllll@{}}
\caption{Sprints und deren Schwerpunkte}\tabularnewline
\toprule\noalign{}
\# & Beginn & Ende & Hinweis & Meilensteine \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\# & Beginn & Ende & Hinweis & Meilensteine \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
1 & 02.10.2025 & 16.10.2025 & & \\
2 & 16.10.2025 & 30.10.2025 & & 1. Meilenstein \\
3 & 30.10.2025 & 13.11.2025 & Herbstferien & \\
4 & 13.11.2025 & 27.11.2025 & & 2. Meilenstein \\
5 & 27.11.2025 & 11.12.2025 & & \\
6 & 11.12.2025 & 25.12.2025 & Weihnachtsferien & 3. Meilenstein \\
7 & 25.12.2025 & 08.01.2026 & Weihnachtsferien & \\
8 & 08.01.2026 & 22.01.2026 & & 4. Meilenstein \\
9 & 22.01.2026 & 05.02.2026 & Semesterferien & \\
10 & 05.02.2026 & 19.02.2026 & & 5. Meilenstein \\
11 & 19.02.2026 & 05.03.2026 & & \\
12 & 05.03.2026 & 19.03.2026 & & 6. Meilenstein \\
\end{longtable}
\subsection{Projektumfeld Analyse}\label{projektumfeld-analyse}
\subsubsection{Grafische Darstellung}\label{grafische-darstellung}
\pandocbounded{\includegraphics[keepaspectratio]{39eb1359e85e22bd63fd1e8e89e8c71f3b107392.pdf}}
\subsubsection{Beschreibung der wichtigsten
Umfelder}\label{beschreibung-der-wichtigsten-umfelder}
\begin{longtable}[]{@{}llll@{}}
\caption{Projektumfelder und deren Bewertung}\tabularnewline
\toprule\noalign{}
ID & Name & Beschreibung & Bewertung \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
ID & Name & Beschreibung & Bewertung \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
8 & Konkurrenz & Potenziell vorhandene Konkurrenz & - \\
\end{longtable}
\subsection{Risikoanalyse}\label{risikoanalyse}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 10\tabcolsep) * \real{0.0153}}
>{\raggedright\arraybackslash}p{(\linewidth - 10\tabcolsep) * \real{0.0918}}
>{\raggedright\arraybackslash}p{(\linewidth - 10\tabcolsep) * \real{0.6582}}
>{\centering\arraybackslash}p{(\linewidth - 10\tabcolsep) * \real{0.1122}}
>{\centering\arraybackslash}p{(\linewidth - 10\tabcolsep) * \real{0.0714}}
>{\centering\arraybackslash}p{(\linewidth - 10\tabcolsep) * \real{0.0510}}@{}}
\caption{Risikoanalyse und Bewertung}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
\#
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Bezeichnung
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} & \begin{minipage}[b]{\linewidth}\centering
Wahrscheinlichkeit (\%)
\end{minipage} & \begin{minipage}[b]{\linewidth}\centering
Auswirkung (\%)
\end{minipage} & \begin{minipage}[b]{\linewidth}\centering
Risikowert
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
\#
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Bezeichnung
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} & \begin{minipage}[b]{\linewidth}\centering
Wahrscheinlichkeit (\%)
\end{minipage} & \begin{minipage}[b]{\linewidth}\centering
Auswirkung (\%)
\end{minipage} & \begin{minipage}[b]{\linewidth}\centering
Risikowert
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
1 & Infrastruktur & {[}cite\_start{]}Instabilität oder Fehlkonfiguration
der Cloud-Server (Hetzner CX22), die den Live-Betrieb
unterbrechen{[}cite: 45, 51{]}. & 60 & 90 & 5400 \\
2 & Datenverlust & {[}cite\_start{]}Kritischer Ausfall der Datenbank
ohne erfolgreiche Wiederherstellung durch automatisierte Backups{[}cite:
51, 62{]}. & 15 & 95 & 1425 \\
3 & DSGVO-Verstoß & {[}cite\_start{]}Fehlende Konformität bei der
Speicherung personenbezogener Daten oder beim Opt-In-Verfahren{[}cite:
38, 39, 128{]}. & 10 & 100 & 1000 \\
4 & Sicherheitslücke & {[}cite\_start{]}Unbefugter Zugriff auf das
System durch Schwachstellen in der VPN- oder SSH-Verbindung{[}cite:
83{]}. & 15 & 85 & 1275 \\
5 & API-Schnittstellen & {[}cite\_start{]}Ausfall oder Inkompatibilität
externer Dienste wie LinkedIn OAuth oder Brevo Mailservice{[}cite: 74,
78{]}. & 30 & 50 & 1500 \\
6 & Performance & {[}cite\_start{]}Beeinträchtigung der Systemleistung
bei steigenden Zugriffszahlen in Phase 2{[}cite: 44, 46{]}. & 40 & 40 &
1600 \\
7 & Wiederherstellung & {[}cite\_start{]}Überschreitung des geplanten
Disaster-Recovery-Zeitraums von 24 Stunden{[}cite: 51, 66{]}. & 25 & 70
& 1750 \\
8 & Oqtane-Integration & {[}cite\_start{]}Komplexität bei der
Entwicklung und Einbindung benutzerdefinierter CMS-Module{[}cite: 69,
72{]}. & 30 & 45 & 1350 \\
9 & Dokumentation & {[}cite\_start{]}Lückenhafte technische
Dokumentation erschwert die Wartung durch den Absolventenverein{[}cite:
155{]}. & 20 & 30 & 600 \\
\end{longtable}
\subsubsection{Grafische Darstellung}\label{grafische-darstellung-1}
\pandocbounded{\includegraphics[keepaspectratio]{663e9454cc5dacd0b2a92b630e8be28bbcb7a29d.pdf}}
\subsection{Projektressourcen}\label{projektressourcen}
\subsubsection{2.6.3 Personelle Ressourcen
(geplant)}\label{personelle-ressourcen-geplant}
\begin{longtable}[]{@{}lc@{}}
\caption{Personelle Ressourcen}\tabularnewline
\toprule\noalign{}
Teammitglied & Personenstunden \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
Teammitglied & Personenstunden \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\textbf{Konstantin Hintermayer} & 180 \\
\textbf{Florian Edlmayer} & 180 \\
\textbf{Adam Gaiswinkler} & 180 \\
\textbf{SUMME} & \textbf{540} \\
\end{longtable}
\subsubsection{Persönliche
Ressourcen(real)}\label{persuxf6nliche-ressourcenreal}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1692}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2462}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2462}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3385}}@{}}
\caption{Persönliche Ressourcen(real)}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Monat/Datum
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Florian Edlmayer
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Adam Gaiswinkler
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Konstantin Hintermayer
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Monat/Datum
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Florian Edlmayer
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Adam Gaiswinkler
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Konstantin Hintermayer
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
30.05. & 1.0h & 1.0h & 0.2h \\
13.06. & 0.5h & 0.8h & 13.8h \\
27.06. & 1.2h & 2.1h & 1.5h \\
11.07. & 0.8h & 0.5h & 5.0h \\
25.07. & 1.5h & 1.2h & 2.5h \\
08.08. & 0.5h & 1.5h & 1.0h \\
22.08. & 1.0h & 2.0h & 1.8h \\
05.09. & 2.3h & 1.4h & 3.2h \\
19.09. & 1.8h & 2.8h & 4.5h \\
03.10. & 10.8h & 18.1h & 22.8h \\
17.10. & 1.0h & 5.5h & 10.8h \\
31.10. & 8.0h & 3.5h & 2.2h \\
14.11. & 3.8h & 10.0h & 5.0h \\
28.11. & 6.0h & 14.3h & 22.5h \\
12.12. & 25.0h & 20.0h & 30.0h \\
26.12. & 15.0h & 14.7h & 25.0h \\
09.01. & 3.0h & 20.0h & 25.0h \\
23.01. & 30.0h & 12.5h & 24.0h \\
06.02. & 17.5h & 47.9h & 16.0h \\
20.02. & 31.0h & 9.5h & 24.0h \\
06.03. & 42.9h & 24.0h & 8.7h \\
\textbf{Gesamt} & \textbf{204.6h} & \textbf{213.3h} & \textbf{249.5h} \\
\end{longtable}
\subsection{Projektmanagement mit
Scrum}\label{projektmanagement-mit-scrum}
\subsubsection{Grundlagen der
Scrum-Methode}\label{grundlagen-der-scrum-methode}
Für die Organisation und Umsetzung des Projekts wurde das agile
Projektmanagement-Framework Scrum verwendet. Scrum ist ein weit
verbreitetes Vorgehensmodell in der Softwareentwicklung, das besonders
für Projekte geeignet ist, bei denen sich Anforderungen im Laufe der
Entwicklung verändern können. Im Gegensatz zu klassischen
Projektmanagementmethoden arbeitet Scrum nicht mit einer vollständig im
Voraus geplanten Entwicklung, sondern mit kurzen, wiederkehrenden
Entwicklungszyklen.
Das zentrale Element von Scrum ist der sogenannte Sprint. Ein Sprint
stellt einen fest definierten Zeitraum dar, in dem bestimmte Funktionen
oder Anforderungen umgesetzt werden. In diesem Projekt betrug die Dauer
eines Sprints zwei Wochen. Innerhalb dieses Zeitraums arbeitete das
Entwicklungsteam daran, ausgewählte Aufgaben zu implementieren und am
Ende des Sprints ein funktionsfähiges Ergebnis zu präsentieren.
Die agile Arbeitsweise von Scrum basiert auf einer iterativen und
inkrementellen Entwicklung. Das bedeutet, dass die Software Schritt für
Schritt erweitert wird. Nach jedem Sprint steht eine neue Version der
Anwendung zur Verfügung, die zusätzliche Funktionen enthält. Dadurch
können Änderungen oder neue Anforderungen flexibel in zukünftige Sprints
integriert werden.
Die grundlegende Struktur und der Ablauf eines Scrum-Projekts sind in
Abbildung X dargestellt.
\pandocbounded{\includegraphics[keepaspectratio]{./images/02-Allgemein/scrum-framework.png}}
\emph{Abbildung X: Übersicht des Scrum-Frameworks mit Rollen, Artefakten
und Ereignissen}
Die Abbildung zeigt die wichtigsten Elemente des Scrum-Frameworks. Auf
der linken Seite sind die Anforderungen des Projekts dargestellt, die im
sogenannten Product Backlog gesammelt werden. Dieses Backlog enthält
alle geplanten Funktionen, Verbesserungen und Aufgaben des Projekts.
Diese Anforderungen stammen häufig von Stakeholdern oder zukünftigen
Benutzern der Anwendung.
Während der Sprintplanung wählt das Entwicklungsteam gemeinsam mit dem
Product Owner jene Aufgaben aus dem Product Backlog aus, die im nächsten
Sprint umgesetzt werden sollen. Diese Aufgaben werden anschließend im
Sprint Backlog gesammelt und bilden die Grundlage für die Arbeit während
des Sprints.
Im Zentrum der Abbildung befindet sich der Sprint-Zyklus, der
typischerweise zwischen einer und vier Wochen dauert. In diesem Projekt
wurde eine Sprintdauer von zwei Wochen festgelegt. Während dieser Phase
arbeitet das Entwicklungsteam an der Umsetzung der definierten Aufgaben.
Dabei durchläuft die Entwicklung mehrere Schritte, wie beispielsweise
Design, Entwicklung, Testen und Deployment.
Ein wichtiger Bestandteil des Scrum-Prozesses ist das Daily Scrum, ein
kurzes tägliches Meeting des Teams. In diesem Meeting berichten die
Teammitglieder über ihren aktuellen Fortschritt, geplante Aufgaben sowie
mögliche Hindernisse im Entwicklungsprozess. Ziel dieses Meetings ist
es, den Arbeitsfortschritt zu koordinieren und Probleme frühzeitig zu
erkennen.
Am Ende jedes Sprints findet das Sprint Review statt. Dabei präsentiert
das Entwicklungsteam die umgesetzten Funktionen des Produkts.
Stakeholder und Projektbeteiligte haben in diesem Meeting die
Möglichkeit, Feedback zu geben und neue Anforderungen einzubringen.
Anschließend folgt die Sprint Retrospective, in der das Team den Ablauf
des vergangenen Sprints reflektiert. Dabei wird analysiert, welche
Aspekte des Arbeitsprozesses gut funktioniert haben und welche
Verbesserungen für zukünftige Sprints möglich sind.
Durch diese strukturierte Vorgehensweise ermöglicht Scrum eine
kontinuierliche Weiterentwicklung der Software sowie eine regelmäßige
Überprüfung des Projektfortschritts.
\subsubsection{Rollenverteilung im
Projektteam}\label{rollenverteilung-im-projektteam}
Das Scrum-Framework definiert drei zentrale Rollen, die jeweils
unterschiedliche Aufgaben innerhalb des Projekts übernehmen. Diese
Rollen tragen dazu bei, Verantwortlichkeiten klar zu strukturieren und
den Entwicklungsprozess effizient zu gestalten.
Der Product Owner ist für die inhaltliche Planung des Produkts
verantwortlich. Zu seinen Aufgaben gehört die Verwaltung und
Priorisierung des Product Backlogs. Er entscheidet, welche Funktionen
oder Anforderungen für das Produkt am wichtigsten sind und sorgt dafür,
dass das Entwicklungsteam stets an den relevantesten Aufgaben arbeitet.
Außerdem fungiert der Product Owner als Verbindung zwischen Stakeholdern
und Entwicklungsteam.
Der Scrum Master unterstützt das Team bei der Anwendung der
Scrum-Methodik und stellt sicher, dass die Scrum-Prinzipien im Projekt
eingehalten werden. Zu seinen Aufgaben gehört unter anderem die
Organisation und Moderation der Scrum-Meetings. Darüber hinaus hilft er
dem Team dabei, Hindernisse im Entwicklungsprozess zu identifizieren und
zu beseitigen.
Das Entwicklungsteam ist für die technische Umsetzung der Anforderungen
verantwortlich. Es besteht aus den Entwicklern, die die Software planen,
implementieren, testen und integrieren. Das Team organisiert seine
Arbeit weitgehend selbstständig und entscheidet gemeinsam über die
technische Umsetzung der Aufgaben.
Durch diese klar definierten Rollen wird sichergestellt, dass
organisatorische und technische Aufgaben effizient verteilt werden
können und der Entwicklungsprozess strukturiert abläuft.
\subsubsection{Sprintplanung und
Umsetzung}\label{sprintplanung-und-umsetzung}
Die Sprintplanung stellt einen wichtigen Bestandteil des Scrum-Prozesses
dar. Zu Beginn jedes neuen Sprints trifft sich das Team, um gemeinsam zu
entscheiden, welche Aufgaben innerhalb des kommenden Zeitraums umgesetzt
werden sollen.
Als Grundlage dient das Product Backlog, in dem alle bekannten
Anforderungen des Projekts gesammelt werden. Während der Sprintplanung
wählt das Team jene Aufgaben aus, die innerhalb der nächsten zwei Wochen
realistisch umgesetzt werden können. Diese Aufgaben werden anschließend
in das Sprint Backlog übernommen.
Während des Sprints arbeitet das Entwicklungsteam an der Umsetzung
dieser Aufgaben. Die Arbeit wird dabei häufig in kleinere Teilaufgaben
unterteilt, um die Fortschritte besser verfolgen zu können.
Zur Koordination der täglichen Arbeit findet das sogenannte Daily Scrum
statt. Dieses kurze Meeting dauert in der Regel maximal 15 Minuten und
dient dazu, den aktuellen Stand der Entwicklung zu besprechen. Jedes
Teammitglied beantwortet dabei typischerweise drei Fragen:
• Was habe ich seit dem letzten Meeting erledigt? • Woran werde ich
heute arbeiten? • Welche Hindernisse gibt es aktuell?
Durch diese regelmäßige Abstimmung wird sichergestellt, dass alle
Teammitglieder über den aktuellen Projektstand informiert sind und
mögliche Probleme frühzeitig erkannt werden.
Am Ende eines Sprints werden die entwickelten Funktionen im Sprint
Review präsentiert. In diesem Meeting wird überprüft, welche
Anforderungen erfolgreich umgesetzt wurden und ob weitere Anpassungen
notwendig sind. Anschließend reflektiert das Team im Rahmen der Sprint
Retrospective den Arbeitsprozess und identifiziert mögliche
Verbesserungen für zukünftige Sprints.
\subsubsection{Vorteile von Scrum für die
Webentwicklung}\label{vorteile-von-scrum-fuxfcr-die-webentwicklung}
Die Verwendung von Scrum bietet insbesondere für Webentwicklungsprojekte
zahlreiche Vorteile. Webanwendungen entwickeln sich häufig dynamisch
weiter, da sich Anforderungen, Technologien oder Benutzerbedürfnisse im
Laufe der Zeit verändern können.
Durch die Aufteilung der Entwicklung in kurze Sprints kann das Projekt
flexibel auf solche Veränderungen reagieren. Neue Anforderungen können
einfach in zukünftige Sprints integriert werden, ohne den gesamten
Projektplan neu strukturieren zu müssen.
Ein weiterer Vorteil liegt in der kontinuierlichen Überprüfung der
entwickelten Funktionen. Da am Ende jedes Sprints ein funktionierender
Teil der Software präsentiert wird, können Fehler oder
Verbesserungsmöglichkeiten frühzeitig erkannt werden. Dies reduziert das
Risiko größerer Probleme in späteren Projektphasen.
Darüber hinaus fördert Scrum die Zusammenarbeit innerhalb des Teams.
Regelmäßige Meetings und eine transparente Aufgabenverteilung sorgen
dafür, dass alle Teammitglieder stets über den aktuellen Stand des
Projekts informiert sind.
Insgesamt ermöglicht Scrum eine strukturierte, flexible und transparente
Vorgehensweise bei der Entwicklung von Webanwendungen. Dadurch kann die
Qualität der entwickelten Software verbessert und der
Entwicklungsprozess effizienter gestaltet werden.
\cleardoublepage
\section{Adam Gaiswinkler}\label{adam-gaiswinkler-1}
\subsection{1. Einleitung des individuellen
Teils}\label{einleitung-des-individuellen-teils}
\subsubsection{1.1 Motivation}\label{motivation}
Zu Beginn des Projekts war meine persönliche Motivation überschaubar.
Wie viele Schulprojekte startete auch AlumniHub zunächst als eine
Pflichtaufgabe -- ich war zwar von Anfang an dabei, jedoch ohne
besonders großes Interesse am Thema. Das Projekt war zu Beginn eine von
vielen schulischen Anforderungen, und ich sah es zunächst nicht als
etwas, mit dem ich mich wirklich identifizieren würde. Die
Aufgabenstellung klang zwar interessant, aber der persönliche Bezug
fehlte noch.
Das änderte sich im Laufe des Projekts grundlegend -- und zwar durch
einen Umstand, den ich anfangs nicht als Chance gesehen hätte: die
Teamverkleinerung. Als das Team von ursprünglich sechs auf drei Personen
verkleinert wurde, änderte sich meine Rolle schlagartig. Plötzlich trug
ich deutlich mehr Verantwortung -- nicht nur für meinen eigenen Bereich,
sondern auch für das Gesamtprojekt. Aufgaben, die ursprünglich andere
übernehmen sollten, lagen nun in meinen Händen. Diese Situation zwang
mich, proaktiver zu handeln, eigenständiger zu entscheiden und tiefer in
die Materie einzutauchen als ursprünglich geplant.
Genau in diesem Moment begann mein Interesse zu wachsen. Je mehr
Verantwortung ich übernahm, desto mehr identifizierte ich mich mit dem
Projekt und seinen Zielen. Ich merkte, dass die Entscheidungen, die ich
traf, direkte Auswirkungen auf das Endergebnis hatten -- und das
motivierte mich, die Arbeit wirklich gut zu machen und nicht nur
fertigzustellen.
Die technische Herausforderung, eine moderne Webanwendung mit Blazor und
ASP.NET umzusetzen, sowie die Möglichkeit, ein echtes System für eine
reale Organisation zu entwickeln, wurden zur echten Motivation. Die
Aufgabenstellung bot mir die Chance, theoretisches Wissen aus dem
Unterricht in einem praxisnahen Umfeld anzuwenden und gleichzeitig einen
konkreten Mehrwert für den Absolventenverein zu schaffen. Rückblickend
war die Teamverkleinerung -- obwohl sie damals als Problem wahrgenommen
wurde -- einer der wichtigsten Faktoren für meine persönliche
Entwicklung im Rahmen dieses Projekts.
\subsubsection{1.2 Individuelle
Themenstellung}\label{individuelle-themenstellung}
Entwicklung des Anmeldetools und Hall-of-Fame-Moduls für Oqtane,
Integration in das bestehende CMS-System, Responsive UI-Entwicklung mit
Blazor und ASP.NET Core
\subsubsection{1.3 Teamrolle}\label{teamrolle}
Developer, Tester der eigenen Module
\subsubsection{1.4 Untersuchungsanliegen}\label{untersuchungsanliegen}
Modulentwicklung mit Blazor und ASP.NET Core, Integration eigenständiger
Module in die Oqtane-Architektur, Entwicklung reaktiver
Benutzeroberflächen mit Blazor-Komponenten, Responsive UI-Design mit
Bootstrap und eigenem CSS, PDF-Generierung mit QuestPDF, Fehlerbehebung
und Optimierung der mobilen Darstellung
\subsubsection{1.5 Persönlicher
Aufgabenbereich}\label{persuxf6nlicher-aufgabenbereich}
Der persönliche Aufgabenbereich umfasste die Entwicklung der beiden
zentralen CMS-Module der Plattform: das Anmeldetool für Veranstaltungen
sowie das Hall-of-Fame-Modul. Beide Module wurden als eigenständige,
wiederverwendbare Oqtane-Module konzipiert und vollständig in C\# und
Blazor implementiert. Neben der technischen Umsetzung lag ein besonderer
Fokus auf der Benutzerfreundlichkeit, der responsiven Darstellung und
der nahtlosen Integration in das bestehende CMS-System.
\subsubsection{1.6 Abgrenzung der Arbeit}\label{abgrenzung-der-arbeit}
Das Datenbankdesign sowie die Infrastruktur und das Server-Setup waren
nicht Teil meines persönlichen Aufgabenbereichs und wurden von anderen
Teammitgliedern verantwortet. Mein persönlicher Anteil beschränkt sich
auf die Entwicklung der Anwendungsschicht, konkret auf die
Implementierung der beiden CMS-Module -- das Anmeldetool und das
Hall-of-Fame-Modul -- sowie deren Integration in das bestehende System.
\subsection{2. Technologien}\label{technologien}
In diesem Kapitel werden die Technologien und Werkzeuge vorgestellt, die
für die Entwicklung des Anmeldetools, des Hall-of-Fame-Moduls und des
Oqtane-Themes eingesetzt wurden. Die Beschreibungen sind dabei so
gestaltet, dass sie auch für Leserinnen und Leser ohne technischen
Hintergrund verständlich sind.
\subsubsection{2.1 C\# und ASP.NET Core}\label{c-und-asp.net-core}
C\# ist eine moderne Programmiersprache von Microsoft, die besonders für
die Entwicklung von Webanwendungen und Softwaresystemen geeignet ist.
Sie ist klar strukturiert, gut lesbar und weit verbreitet in der
professionellen Softwareentwicklung. ASP.NET Core ist ein Framework --
also eine Art Werkzeugkasten -- das auf C\# aufbaut und die Entwicklung
von Webseiten und Webanwendungen vereinfacht.\footnote{Microsoft (2026a)
(siehe Internet-/Intranetverzeichnis).} Es stellt vorgefertigte
Bausteine bereit, sodass man nicht alles von Grund auf neu programmieren
muss. Für AlumniHub bildete ASP.NET Core die technische Grundlage aller
entwickelten Module.
\subsubsection{2.2 Blazor}\label{blazor}
Blazor ist ein Framework, das es ermöglicht, interaktive Weboberflächen
direkt in C\# zu entwickeln.\footnote{Microsoft (2026b) (siehe
Internet-/Intranetverzeichnis).} Normalerweise werden solche
Oberflächen -- also alles, was der Benutzer auf dem Bildschirm sieht und
mit dem er interagiert -- mit einer anderen Programmiersprache namens
JavaScript umgesetzt. Blazor erlaubt es, dasselbe in C\# zu schreiben,
was die Entwicklung vereinheitlicht und übersichtlicher macht. Konkret
bedeutet das: Wenn ein Benutzer beispielsweise auf den „Zusagen''-Button
klickt, reagiert die Seite sofort und aktualisiert sich automatisch --
ohne dass die gesamte Seite neu geladen werden muss.
\subsubsection{2.3 Oqtane}\label{oqtane}
Oqtane ist ein Content-Management-System (CMS) -- also eine Software,
mit der Webseiten und deren Inhalte verwaltet werden können, ähnlich wie
WordPress oder Typo3.\footnote{Oqtane Foundation (2026a) (siehe
Internet-/Intranetverzeichnis).} Das Besondere an Oqtane ist, dass es
vollständig auf Blazor und C\# aufbaut und eine modulare Architektur
besitzt: Man kann eigene Erweiterungen -- sogenannte Module --
entwickeln und in das System einbinden, ohne den Kern der Software
verändern zu müssen.\footnote{Oqtane Foundation (2026b) (siehe
Internet-/Intranetverzeichnis).} Für AlumniHub wurde Oqtane als
Grundlage gewählt, weil es Benutzerverwaltung, Seitenstruktur und viele
weitere Standardfunktionen bereits mitbringt und somit viel
Entwicklungsaufwand spart.
\subsubsection{2.4 Bootstrap und CSS}\label{bootstrap-und-css}
CSS (Cascading Style Sheets) ist die Sprache, mit der das Aussehen einer
Webseite festgelegt wird -- also Farben, Schriftarten, Abstände und das
Layout. Bootstrap ist eine fertige Sammlung von CSS-Regeln und
Hilfsmitteln, die von Twitter entwickelt wurde und kostenlos verfügbar
ist.\footnote{Bootstrap (2026) (siehe Internet-/Intranetverzeichnis).}
Der große Vorteil von Bootstrap ist, dass es sogenanntes Responsive
Design einfach umsetzbar macht: Die Webseite passt sich automatisch an
verschiedene Bildschirmgrößen an -- egal ob Desktop, Tablet oder
Smartphone. Für AlumniHub wurde Bootstrap als Basis verwendet, ergänzt
durch eigenes CSS für das individuelle Erscheinungsbild der Plattform.
\subsubsection{2.5 QuestPDF}\label{questpdf}
QuestPDF ist eine kostenlose Open-Source-Bibliothek -- also eine fertige
Programmsammlung -- die es ermöglicht, PDF-Dokumente direkt aus C\#-Code
heraus zu erstellen.\footnote{QuestPDF (2026) (siehe
Internet-/Intranetverzeichnis).} Anstatt ein PDF manuell zu gestalten,
beschreibt man im Code wie das Dokument aussehen soll, und QuestPDF
generiert daraus automatisch eine fertige PDF-Datei. Im
Hall-of-Fame-Modul wurde QuestPDF eingesetzt, um jedem Absolventen zu
ermöglichen, sein eigenes Profil als visuell ansprechendes PDF
herunterzuladen.
\subsubsection{2.6 Gitea}\label{gitea}
Wenn mehrere Personen gemeinsam an einem Softwareprojekt arbeiten,
braucht man ein System, das alle Änderungen am Code nachverfolgt und
verhindert, dass sich Änderungen verschiedener Personen gegenseitig
überschreiben. Dieses Konzept nennt sich Versionskontrolle. Gitea ist
eine selbst gehostete Plattform für genau diesen Zweck -- ähnlich wie
GitHub, aber auf einem eigenen Server betrieben.\footnote{Gitea (2026)
(siehe Internet-/Intranetverzeichnis).} Jede Änderung am Code wird als
sogenannter „Commit'' gespeichert, sodass man jederzeit nachvollziehen
kann, wer wann was geändert hat, und bei Bedarf auf eine ältere Version
zurückwechseln kann.
\subsubsection{2.7 Entwicklungsumgebung}\label{entwicklungsumgebung}
Eine Entwicklungsumgebung -- auch IDE (Integrated Development
Environment) genannt -- ist ein Programm, das Entwicklerinnen und
Entwickler beim Schreiben von Code unterstützt. Sie bietet unter anderem
Funktionen wie automatische Vervollständigung, Fehlererkennung und
integrierte Debugging-Werkzeuge, die das Auffinden und Beheben von
Fehlern im Code erleichtern.
Zu Beginn des Projekts wurde Visual Studio 2022 auf Windows verwendet.
Visual Studio 2022 ist die führende Entwicklungsumgebung von Microsoft
für .NET-Projekte und bietet eine umfangreiche Unterstützung für ASP.NET
Core und Blazor. Im Laufe des Projekts erfolgte jedoch ein Wechsel von
Windows auf macOS. Da Visual Studio 2022 auf Mac nicht mehr verfügbar
ist -- Microsoft hat die Mac-Version eingestellt -- wurde als Ersatz
JetBrains Rider eingesetzt.\footnote{JetBrains (2026) (siehe
Internet-/Intranetverzeichnis).} JetBrains Rider ist eine
next-generation IDE von JetBrains, die plattformübergreifend
funktioniert und eine moderne Entwicklungsumgebung für .NET-Projekte auf
macOS bietet. Der Umstieg auf JetBrains Rider ermöglichte es, die
Entwicklung auf dem neuen System ohne größere Unterbrechungen
fortzusetzen.
\subsubsection{2.8 Plattformwechsel: Windows zu
macOS}\label{plattformwechsel-windows-zu-macos}
Ein besonderer Aspekt der Entwicklung war der Wechsel von Windows auf
macOS während des Projektverlaufs. Unter Plattform versteht man in der
Softwareentwicklung das Betriebssystem, auf dem eine Anwendung läuft --
also etwa Windows, macOS oder Linux. Dieser Wechsel brachte spezifische
Herausforderungen mit sich, da Oqtane primär für Windows entwickelt
wurde. Obwohl Oqtane grundsätzlich auch auf macOS und Linux lauffähig
ist, ist die Unterstützung für diese Plattformen veraltet und nicht
vollständig angepasst. In der Praxis bedeutete das: Der Code ließ sich
teilweise nicht fehlerfrei kompilieren -- also in ein lauffähiges
Programm umwandeln -- und Oqtane startete zunächst nur mit
Fehlermeldungen. Durch den Einsatz von JetBrains Rider als IDE konnten
diese Probleme weitgehend gelöst werden, da JetBrains Rider eine bessere
plattformübergreifende Integration bietet als die ursprünglich
verwendete Entwicklungsumgebung.
\subsection{3. Module}\label{module}
\subsubsection{3.1 Entwicklung des Oqtane
Themes}\label{entwicklung-des-oqtane-themes}
\paragraph{3.1.1 Ziel des Themes}\label{ziel-des-themes}
Im Rahmen des Projekts AlumniHub wurde ein eigenes Theme für das
Content-Management-System Oqtane entwickelt. Ziel dieser Entwicklung war
es, das Standarddesign von Oqtane vollständig durch eine
projektspezifische Benutzeroberfläche zu ersetzen, die den Anforderungen
des Absolventenvereins der HTL Ungargasse entspricht. Das Standardtheme
von Oqtane ist funktional, jedoch generisch gehalten und bietet keinen
Bezug zur Schule oder zum Projekt. Aus diesem Grund wurde frühzeitig die
Entscheidung getroffen, ein vollständig eigenes Theme zu entwickeln, das
sowohl optisch als auch technisch auf die Bedürfnisse der Plattform
zugeschnitten ist.
Das visuelle Design orientiert sich dabei am bestehenden
Erscheinungsbild der offiziellen Schulwebseite szu.at. Es wurde bewusst
ein schlichtes Grau-Weiß-Farbschema gewählt, das eine klare,
professionelle und zeitlose Optik erzeugt. Auf auffällige Farben oder
komplexe Animationen wurde verzichtet, um die Inhalte in den Vordergrund
zu stellen und eine ruhige Benutzeroberfläche zu schaffen, die für ein
breites Publikum -- von Schülerinnen und Schülern über Lehrkräfte bis
hin zu ehemaligen Absolventinnen und Absolventen -- zugänglich ist.
Neben der visuellen Gestaltung standen auch technische Anforderungen im
Mittelpunkt. Das Theme sollte auf unterschiedlichen Endgeräten -- sowohl
auf Desktop-Computern als auch auf Smartphones und Tablets --
zuverlässig und benutzerfreundlich funktionieren. Responsive Design war
daher von Anfang an eine zentrale Anforderung. Darüber hinaus sollte das
Theme vollständig in die Oqtane-Architektur integriert sein, sodass alle
Standardfunktionen des CMS wie Benutzerverwaltung, Seitenverwaltung und
Modulintegration weiterhin ohne Einschränkungen genutzt werden können.
\paragraph{3.1.2 Technische Umsetzung}\label{technische-umsetzung}
Als technische Grundlage diente die Theme-Architektur von Oqtane. Das
Layout wurde in einer zentralen Razor-Datei (\texttt{Theme.razor})
definiert, welche von der Basisklasse \texttt{ThemeBase} erbt. Durch
diese Vererbung stehen im Theme automatisch zentrale Funktionen des
Frameworks zur Verfügung, darunter der Seitenzustand
(\texttt{PageState}), Navigationsdaten, Systemeinstellungen sowie
Informationen über den aktuell angemeldeten Benutzer. Dies ermöglicht
eine tiefe Integration des Themes in das CMS, ohne dass zusätzliche
Schnittstellen oder externe Datenzugriffe notwendig sind.
\subparagraph{3.1.2.1 Navigationsleiste}\label{navigationsleiste}
Ein zentrales Element des Themes ist die fixierte Navigationsleiste am
oberen Rand der Seite. Sie ist in drei klar voneinander getrennte
Bereiche gegliedert: Auf der linken Seite befindet sich das Logo der HTL
Ungargasse, das als visuelles Erkennungsmerkmal der Schule dient und
beim Klick zur Startseite führt. In der Mitte werden die
Navigationspunkte der Plattform angezeigt, die dynamisch aus der
Seitenstruktur des CMS generiert werden. Auf der rechten Seite befinden
sich die Benutzerfunktionen, also Login, Registrierung und bei
angemeldeten Benutzern ein Link zum eigenen Profil.
Die dynamische Generierung der Navigationspunkte stellte dabei eine
besondere Herausforderung dar. Das Oqtane-Framework stellt standardmäßig
eine \texttt{\textless{}Menu\textgreater{}}-Komponente bereit, die
automatisch alle im System registrierten Seiten in der Navigation
anzeigt. Diese Komponente gibt jedoch nicht nur öffentlich sichtbare
Inhaltsseiten aus, sondern auch interne Systemseiten wie Login,
Register, Reset, Profile, Search, Privacy, Terms, Not Found oder Admin.
Diese Seiten sind für den normalen Benutzer nicht relevant und sollten
daher nicht in der Hauptnavigation erscheinen.
Da die Standardkomponente das Burger-Menü automatisch generierte und
keine Möglichkeit bot, dieses individuell anzupassen, wurde auf sie
verzichtet. Da ein eigenes, maßgeschneidertes Burger-Menü benötigt
wurde, wurde stattdessen eine vollständig eigene Navigation
implementiert. Dabei wurde auf die Basisklassen \texttt{MenuBase} und
\texttt{MenuItemsBase} zurückgegriffen, die von Oqtane bereitgestellt
werden und den Zugriff auf die Seitenliste ermöglichen. Die
Navigationspunkte werden direkt über \texttt{PageState.Pages} abgerufen
und anschließend mittels LINQ gefiltert. Dabei werden nur Root-Seiten
berücksichtigt, also Seiten ohne übergeordnete Seite
(\texttt{ParentId\ ==\ null}), die als Navigationsseite markiert sind
und nicht in einer manuell definierten Ausschlussliste
(\texttt{hiddenNames}) stehen.
\begin{Shaded}
\begin{Highlighting}[]
\DataTypeTok{var}\NormalTok{ hiddenNames }\OperatorTok{=} \KeywordTok{new}\OperatorTok{[]}
\OperatorTok{\{}
\StringTok{"Login"}\OperatorTok{,} \StringTok{"Register"}\OperatorTok{,} \StringTok{"Reset"}\OperatorTok{,} \StringTok{"Profile"}\OperatorTok{,}
\StringTok{"Search"}\OperatorTok{,} \StringTok{"Privacy"}\OperatorTok{,} \StringTok{"Terms"}\OperatorTok{,} \StringTok{"Not Found"}\OperatorTok{,} \StringTok{"Admin"}
\OperatorTok{\};}
\KeywordTok{foreach} \OperatorTok{(}\DataTypeTok{var}\NormalTok{ item }\KeywordTok{in}\NormalTok{ PageState}\OperatorTok{.}\FunctionTok{Pages}
\OperatorTok{.}\FunctionTok{Where}\OperatorTok{(}\NormalTok{p }\OperatorTok{=\textgreater{}}\NormalTok{ p}\OperatorTok{.}\FunctionTok{ParentId} \OperatorTok{==} \KeywordTok{null}
\OperatorTok{\&\&} \OperatorTok{!}\NormalTok{hiddenNames}\OperatorTok{.}\FunctionTok{Contains}\OperatorTok{(}\NormalTok{p}\OperatorTok{.}\FunctionTok{Name}\OperatorTok{)))}
\OperatorTok{\{}
\NormalTok{ @item}\OperatorTok{.}\FunctionTok{Name}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Durch diesen Ansatz werden ausschließlich die für Benutzer relevanten
Inhaltsseiten in der Navigation angezeigt. Gleichzeitig bleibt die
Navigation vollständig dynamisch: Werden im CMS neue Seiten angelegt und
als Navigationsseiten markiert, erscheinen diese automatisch im Menü,
ohne dass eine Anpassung am Theme-Code notwendig ist.
!{[}Abbildung 4.2: Filterlogik der Oqtane-Navigation{]}
(./images/06-Adam/navigationsfilter\_v2.svg) \emph{Abbildung 4.2:
Filterlogik zur Bereinigung der Oqtane-Navigation (Theme.razor)} Dies
reduziert den Wartungsaufwand erheblich und stellt sicher, dass die
Navigation stets dem aktuellen Stand der Plattform entspricht.
\subparagraph{3.1.2.2 Responsive Design und
Burger-Menü}\label{responsive-design-und-burger-menuxfc}
Da die Plattform auch auf mobilen Endgeräten genutzt werden soll, war
die Umsetzung eines responsiven Layouts eine wichtige Anforderung. Auf
Desktop-Geräten werden alle Navigationspunkte direkt in der
Navigationsleiste angezeigt. Auf kleineren Bildschirmen wie Smartphones
oder Tablets würde dies jedoch zu Platzproblemen führen, da die Anzahl
der Navigationspunkte die verfügbare Breite überschreiten kann.
Aus diesem Grund wurde für mobile Endgeräte ein sogenanntes Burger-Menü
implementiert. Dabei handelt es sich um ein Icon mit drei horizontalen
Linien, das auf kleinen Bildschirmen anstelle der ausgeschriebenen
Navigationspunkte angezeigt wird. Beim Anklicken öffnet sich eine
seitliche Sidebar, in der alle Navigationspunkte vertikal untereinander
dargestellt werden. Die Sidebar ist so aufgebaut, dass der obere Bereich
die Navigationspunkte enthält und bei einer großen Anzahl von Einträgen
gescrollt werden kann. Am unteren Rand der Sidebar sind die Login- und
Registrierungsfunktionen fixiert, sodass diese unabhängig von der Anzahl
der Navigationspunkte immer sichtbar und erreichbar sind.
Die technische Umsetzung des Burger-Menüs basiert auf einer
CSS-Checkbox-Lösung. Das Öffnen und Schließen der Sidebar wird über den
Zustand einer versteckten Checkbox gesteuert, der mittels CSS-Selektoren
ausgewertet wird. Dieser Ansatz ermöglicht eine funktionsfähige mobile
Navigation ohne den Einsatz zusätzlicher JavaScript-Frameworks oder
komplexer Logik. Die gesamte Darstellungslogik -- also welche Elemente
auf welcher Bildschirmgröße sichtbar sind -- wird über CSS Media Queries
gesteuert, die zwischen der Desktop- und der mobilen Ansicht
unterscheiden.
\subparagraph{3.1.2.3 Pane-Struktur}\label{pane-struktur}
Für den Inhaltsbereich der Plattform wurden mehrere sogenannte Panes
definiert. Panes sind in Oqtane Platzhalter-Bereiche innerhalb eines
Themes, in die später Module oder Inhalte eingefügt werden können. Durch
die Definition mehrerer Panes mit unterschiedlichen Breiten und
Positionen können Seiten sehr flexibel und ohne Änderungen am Theme-Code
strukturiert werden.
Im entwickelten Theme stehen unter anderem folgende Panes zur Verfügung:
ein vollbreiter Bereich oben (\texttt{Top\ 100\%}), ein linker Bereich
(\texttt{Left\ 50\%}), eine rechte Seitenleiste
(\texttt{Right\ Sidebar\ 33\%}) sowie ein vollbreiter Bereich am unteren
Rand (\texttt{Bottom\ 100\%}). Diese Struktur ermöglicht es, Seiten
sowohl ein- als auch mehrspaltig aufzubauen, je nach den Anforderungen
des jeweiligen Inhalts.
\subparagraph{3.1.2.4 Weitere
Integrationen}\label{weitere-integrationen}
Neben Navigation und Layout wurden weitere Funktionen in das Theme
integriert. Die Cookie-Consent-Komponente von Oqtane wurde eingebunden,
sodass beim ersten Besuch der Plattform ein Hinweis zur Verwendung von
Cookies angezeigt wird. Diese Funktion ist über die Site-Einstellungen
von Oqtane steuerbar, das Theme rendert lediglich die entsprechende
Komponente.
Darüber hinaus wurde das ControlPanel von Oqtane integriert, das
Administratorinnen und Administratoren direkten Zugriff auf
Verwaltungsfunktionen bietet. Login- und Registrierungsoptionen werden
über den \texttt{SettingService} aus den Site-Einstellungen geladen,
sodass diese Funktionen ohne Anpassungen am Code aktiviert oder
deaktiviert werden können.
\paragraph{3.1.3 Herausforderungen}\label{herausforderungen}
Die eigene Implementierung der Navigation über \texttt{PageState.Pages}
und eine LINQ-basierte Filterlogik erwies sich als die richtige
Entscheidung. Sie löste nicht nur das Problem der fehlenden
Anpassbarkeit der Standardkomponente, sondern brachte gleichzeitig einen
wesentlichen Mehrwert: Die Navigation ist vollständig wartungsfrei.
Werden im CMS neue Seiten angelegt, erscheinen diese automatisch im Menü
-- ohne dass eine einzige Zeile Code angepasst werden muss. Zudem
ermöglichte dieser Ansatz die vollständige Kontrolle über das
Erscheinungsbild des Burger-Menüs, was mit der Standardkomponente nicht
möglich gewesen wäre.
\subsubsection{3.2 Anmeldetool}\label{anmeldetool}
\paragraph{3.2.1 Ziel des Moduls}\label{ziel-des-moduls}
Das Anmeldetool wurde entwickelt, um Mitgliedern des Absolventenvereins
eine einfache und strukturierte Möglichkeit zu bieten, sich zu
Veranstaltungen und Treffen an- oder abzumelden. Ziel des Moduls ist es,
den organisatorischen Aufwand bei der Planung und Verwaltung von Treffen
deutlich zu reduzieren und gleichzeitig eine benutzerfreundliche
Oberfläche für alle Beteiligten bereitzustellen.
Das Modul wurde als zusätzliche Funktionserweiterung in das bestehende
Content-Management-System integriert. Es ermöglicht sowohl die
Verwaltung der Veranstaltungen auf Seiten der Organisatoren als auch
eine intuitive Interaktion für die Teilnehmerinnen und Teilnehmer. Durch
den Einsatz moderner Webtechnologien konnte dabei eine reaktionsschnelle
und geräteübergreifend nutzbare Lösung geschaffen werden.
\paragraph{3.2.2 Frontend (Eingabemaske)}\label{frontend-eingabemaske}
Die Benutzeroberfläche des Anmeldetools wurde mithilfe von Blazor
realisiert, einem Framework der ASP.NET-Technologieplattform, das die
Entwicklung interaktiver Weboberflächen in C\# ermöglicht. Die
Eingabemaske wurde als eigenständige Blazor-Komponente implementiert,
die flexibel in verschiedene Seiten der Anwendung eingebettet werden
kann.
Beim Aufruf der Seite wird dem Benutzer zunächst eine kurze Beschreibung
der jeweiligen Veranstaltung angezeigt. Im Anschluss stehen zwei klar
gekennzeichnete Schaltflächen zur Verfügung: Zusagen zur Bestätigung der
Teilnahme sowie Absagen zur Ablehnung. Die Schaltflächen wurden farblich
differenziert gestaltet -- eine grüne Hervorhebung signalisiert die
Zusage, eine rote Darstellung steht für die Absage. Diese Gestaltung
orientiert sich an etablierten Designkonventionen moderner
Webanwendungen und verbessert die intuitive Bedienbarkeit erheblich.
Darüber hinaus wurde besonderer Wert auf eine responsive Darstellung
gelegt, damit das Tool sowohl auf Desktop-Geräten als auch auf
Smartphones und Tablets problemlos genutzt werden kann.
\paragraph{3.2.3 API-Schnittstelle}\label{api-schnittstelle}
Die Anbindung des Anmeldetools an das bestehende CMS erfolgt über klar
definierte Schnittstellen innerhalb der ASP.NET-Architektur. Die
Komponente kommuniziert mit dem Backend, um Veranstaltungsdaten
abzurufen sowie Anmelde- und Absagestatus der Teilnehmenden zu
übermitteln und zu persistieren.
Die Datenübertragung erfolgt nach dem Prinzip der sauberen
Komponentenarchitektur: Die Blazor-Komponente ist als eigenständige
Einheit konzipiert, die über Parameter und Rückruffunktionen mit
übergeordneten Seitenkomponenten kommuniziert. Diese Struktur
gewährleistet eine klare Trennung zwischen Darstellungslogik und
Datenzugriff und erleichtert zukünftige Erweiterungen der Schnittstelle
erheblich.
\paragraph{3.2.4 Datenauswertung}\label{datenauswertung}
Die im Anmeldetool erfassten Daten bilden die Grundlage für die
Verwaltung und Auswertung von Veranstaltungsteilnahmen. Organisatoren
können auf Basis der gespeicherten An- und Abmeldungen die
Teilnehmerzahlen einsehen und die Planung entsprechend anpassen.
Die Architektur des Moduls ist bereits auf zukünftige Erweiterungen
ausgelegt. Geplante Erweiterungen umfassen eine übersichtliche
Teilnehmerliste mit Namen und Anmeldestatus, die Möglichkeit, eine
maximale Teilnehmeranzahl je Veranstaltung festzulegen sowie eine
direkte Speicherung und Auswertung der Anmeldedaten in einer Datenbank.
Durch diese Erweiterungen soll das Anmeldetool künftig nicht nur als
Interaktionselement für Teilnehmer, sondern auch als vollwertiges
Verwaltungswerkzeug für Veranstaltungsorganisatoren dienen.
\paragraph{3.2.5 UX-Überlegungen (User
Experience)}\label{ux-uxfcberlegungen-user-experience}
Ein zentraler Aspekt bei der Entwicklung des Anmeldetools war die
Benutzerfreundlichkeit der Oberfläche. UX (User Experience) bezeichnet
dabei die Gesamtheit aller Erfahrungen, die ein Benutzer bei der
Interaktion mit einer Anwendung macht -- von der visuellen Gestaltung
über die Bedienbarkeit bis hin zur allgemeinen Zufriedenheit mit dem
System. Die Module wurden so gestaltet, dass Benutzerinnen und Benutzer
die Funktionen ohne zusätzliche Schulung verwenden können. Eine klare
Struktur, eine intuitive Bedienung sowie ein konsistentes
Erscheinungsbild innerhalb des bestehenden Systems standen dabei im
Vordergrund.
Während der Testphase wurden mehrere visuelle Darstellungsprobleme
identifiziert und behoben. Dazu zählten Fehler bei der
Overlay-Darstellung -- insbesondere in Bezug auf Hintergrundfarben,
Transparenzen und z-Index-Ebenen -- sowie sogenannte Clipping-Fehler auf
mobilen Endgeräten, bei denen Inhalte teilweise abgeschnitten oder
außerhalb des sichtbaren Bereichs angezeigt wurden. Die Layoutstruktur
und Elementgrößen wurden angepasst, ausreichende Abstände zu
Seitenrändern sichergestellt und Tests auf verschiedenen Browser- und
Gerätevarianten durchgeführt.
Diese Maßnahmen stellen einen ersten Schritt in der kontinuierlichen
Verbesserung der mobilen Benutzeroberfläche dar. Weitere
Feinabstimmungen sind geplant, um das Anmeldetool langfristig als
stabile, benutzerfreundliche und geräteübergreifend konsistente Lösung
zu etablieren.
\subsubsection{3.3 Hall of Fame}\label{hall-of-fame}
Das Hall-of-Fame-Modul ist ein zentrales Modul der AlumniHub-Plattform.
Es dient dazu, ehemalige Absolventinnen und Absolventen der HTL
Ungargasse sichtbar zu machen und ihre beruflichen Werdegänge zu
präsentieren. Das Modul wurde als eigenständiges, wiederverwendbares
Oqtane-Modul entwickelt und ermöglicht es registrierten Benutzerinnen
und Benutzern, sich selbst mit einem persönlichen Profil einzutragen.
\paragraph{3.3.1 Benutzeroberfläche}\label{benutzeroberfluxe4che}
Die Benutzeroberfläche des Moduls besteht aus mehreren Komponenten, die
zusammen eine vollständige und benutzerfreundliche Verwaltung der
Hall-of-Fame-Einträge ermöglichen.
\subparagraph{3.3.1.1 Übersichtsseite
(Index.razor)}\label{uxfcbersichtsseite-index.razor}
Die Hauptseite der Hall of Fame ist die erste Seite, die Besucherinnen
und Besucher zu sehen bekommen. Sie zeigt alle veröffentlichten Einträge
in einem responsiven Kartenlayout. Auf Desktop-Geräten werden die Karten
in drei Spalten nebeneinander dargestellt, auf kleineren Bildschirmen
passt sich das Layout automatisch an und zeigt die Karten einspaltig
untereinander an.
Jede Karte enthält das Foto der jeweiligen Person als großes
Vorschaubild im oberen Bereich. Darunter werden Name und
Abschlussjahrgang als Überschrift angezeigt. Im unteren Bereich der
Karte befindet sich ein Teaser der Beschreibung, also ein kurzer
Ausschnitt aus dem Werdegang der Person. Da Beschreibungen
unterschiedlich lang sein können, wurde die Kartenhöhe durch eine
Kombination aus CSS-Flexbox und einer Maximalhöhe mit
\texttt{overflow:\ hidden} vereinheitlicht. Dadurch weisen alle Karten
in einer Zeile dieselbe Höhe auf, was ein sauberes und gleichmäßiges
Erscheinungsbild der Übersichtsseite gewährleistet. Am unteren Rand
jeder Karte befindet sich ein „Details ansehen''-Button, der die
Besucherin oder den Besucher zur vollständigen Detailseite des Eintrags
weiterleitet.
Über der Kartenliste befindet sich eine Filterleiste mit einer
Suchfunktion und einer Sortieroption. Die Suchleiste ermöglicht eine
Echtzeit-Suche über Name und Beschreibung aller Einträge -- die
Ergebnisse aktualisieren sich direkt beim Tippen, ohne dass die Seite
neu geladen werden muss. Neben der Suchleiste befindet sich ein
Sortier-Dropdown, über das zwischen verschiedenen Sortierkriterien
gewählt werden kann, darunter Datum, Name und Jahrgang. Ein
Toggle-Button mit dynamischem Pfeil-Icon ermöglicht das Umschalten
zwischen aufsteigender und absteigender Sortierrichtung.
Für angemeldete Benutzerinnen und Benutzer wird im oberen rechten
Bereich der Seite ein zusätzlicher Button angezeigt. Hat die Person noch
keinen eigenen Eintrag erstellt, erscheint ein „Eintragen''-Button.
Existiert bereits ein Eintrag, ändert sich der Button automatisch zu
„Mein Eintrag'', über den der bestehende Eintrag bearbeitet werden kann.
Administratorinnen und Administratoren sehen zusätzlich Warnhinweise bei
gemeldeten Einträgen sowie einen Lösch-Button bei jedem Eintrag.
\subparagraph{3.3.1.2 Detailseite
(Details.razor)}\label{detailseite-details.razor}
Die Detailseite zeigt einen einzelnen Hall-of-Fame-Eintrag in seiner
vollständigen Form. Das Layout ist zweispaltig aufgebaut: Auf der linken
Seite befindet sich das Foto der Person, eingebettet in einen unscharfen
Bildhintergrund, der dasselbe Foto in einer größeren, weichgezeichneten
Version als Hintergrund verwendet. Dieser Effekt verleiht der Seite eine
moderne und ansprechende Optik. Das eigentliche Foto ist dabei als klar
umrandetes Porträtbild in der Mitte des linken Bereichs positioniert.
Auf der rechten Seite werden zunächst eine Breadcrumb-Navigation („Hall
of Fame / Details'') sowie Name und Jahrgang der Person angezeigt. Der
Jahrgang wird in blauer Schrift als „Absolvent des Jahrgangs
{[}Jahr{]}'' dargestellt, was einen visuellen Akzent setzt und den
Jahrgang klar hervorhebt. Darunter folgt die vollständige Beschreibung
unter der Überschrift „Werdegang \& Erfolge''.
Am unteren Rand der Seite befinden sich je nach Konfiguration des
Eintrags bis zu vier Buttons. Der „PDF Vorschau''-Button öffnet einen
modalen Dialog mit einer vollständigen Vorschau des generierten PDFs
sowie einem „Herunterladen''-Button. Der „Zurück''-Button führt zurück
zur Übersichtsseite. Falls die Person einen optionalen Link hinterlegt
hat, wird zusätzlich ein „Webseite besuchen''-Button angezeigt, der die
Besucherin oder den Besucher direkt zur externen Webseite der Person
führt. Für eingeloggte Benutzer gibt es außerdem einen „Melden''-Button,
über den ein Eintrag gemeldet werden kann.
\subparagraph{3.3.1.3 Edit-Seite
(Edit.razor)}\label{edit-seite-edit.razor}
Die Edit-Komponente dient sowohl zum Erstellen eines neuen Eintrags als
auch zum Bearbeiten eines bestehenden. Das Formular ist klar
strukturiert und enthält alle notwendigen Felder für einen vollständigen
Hall-of-Fame-Eintrag.
Das Formular enthält zunächst ein Textfeld für den Namen der Person
sowie ein Zahlenfeld für den Abschlussjahrgang. Für die Beschreibung
wird ein Rich-Text-Editor eingesetzt, der Formatierungsmöglichkeiten wie
Fett, Kursiv, Listen und weitere Optionen bietet. Unterhalb des Editors
wird ein Live-Zeichenzähler angezeigt, der die aktuelle Zeichenanzahl in
Echtzeit aktualisiert und die maximal erlaubte Zeichenanzahl von 504
Zeichen anzeigt. Dadurch sehen Benutzerinnen und Benutzer jederzeit, wie
viel Platz noch zur Verfügung steht.
Für das Foto gibt es ein Upload-Feld mit einer Vorschaufunktion. Wird
ein Bild ausgewählt, wird es sofort als Vorschau angezeigt, bevor der
Eintrag gespeichert wird. Erlaubt sind nur JPG- und PNG-Dateien mit
einer maximalen Größe von 5 MB. Zusätzlich gibt es ein optionales
Link-Feld, in das eine externe Webseite der Person eingetragen werden
kann.
Am unteren Rand des Formulars befinden sich zwei Speicheroptionen: „Als
Entwurf speichern'' und „Veröffentlichen''. Einträge im Entwurfsmodus
sind nur für die eigene Person sichtbar und erscheinen nicht in der
öffentlichen Übersicht. Erst durch das Veröffentlichen wird der Eintrag
für alle Besucherinnen und Besucher sichtbar. Eine Eigentümerprüfung
verhindert, dass Benutzerinnen und Benutzer fremde Einträge bearbeiten,
und eine Duplikatprüfung stellt sicher, dass pro Person nur ein Eintrag
erstellt werden kann. Dabei greifen die beiden Prüfungen zu
unterschiedlichen Zeitpunkten: Die Duplikatprüfung wird beim Erstellen
eines neuen Eintrags ausgeführt und verhindert, dass eine Person mehrere
Einträge anlegt. Die Eigentümerprüfung hingegen greift beim Bearbeiten
eines bestehenden Eintrags und stellt sicher, dass nur die Person, die
den Eintrag erstellt hat, diesen auch ändern kann.
Der folgende Lebenszyklus zeigt die möglichen Zustände eines Eintrags
und die Übergänge zwischen ihnen:
!{[}Abbildung 4.1: Lebenszyklus eines Hall-of-Fame-Eintrags{]}
(./images/06-Adam/hall\_of\_fame\_lebenszyklus\_v4.svg) \emph{Abbildung
4.1: Lebenszyklus eines Hall-of-Fame-Eintrags}
\subparagraph{3.3.1.4 Meldefunktion}\label{meldefunktion}
Die Meldefunktion ermöglicht es angemeldeten Benutzerinnen und
Benutzern, einen Hall-of-Fame-Eintrag zu melden, wenn dieser
unangemessene oder falsche Inhalte enthält. Durch Klicken auf den
„Melden''-Button auf der Detailseite öffnet sich ein kleines
Popup-Fenster, in das der Meldegrund eingetragen werden kann. Nach dem
Absenden wird die Meldung in der Datenbank gespeichert und im
Admin-Modul angezeigt. Administratorinnen und Administratoren können
dort alle eingegangenen Meldungen einsehen und bei Bedarf den
betreffenden Eintrag löschen oder freigeben.
Die Meldefunktion ist dabei nicht direkt im Hall-of-Fame-Modul
implementiert, sondern wird über eine zentrale Schnittstelle aus einem
gemeinsamen Interfaces-Paket eingebunden. Dieses Konzept ermöglicht es,
dieselbe Melde-Oberfläche in beliebig vielen weiteren Modulen
wiederzuverwenden, ohne die Logik mehrfach implementieren zu müssen.
!{[}Abbildung 6.1: Ablauf des globalen Reporting-Systems{]}
(./images/06-Adam/reporting\_sequence\_diagram.svg) \emph{Abbildung 6.1:
Ablauf des globalen Reporting-Systems (Sequenzdiagramm)}
\subparagraph{3.3.1.5 PDF-Export}\label{pdf-export}
Der PDF-Export ist eine besondere Funktion des Hall-of-Fame-Moduls. Er
ermöglicht es Benutzerinnen und Benutzern, ihren Eintrag als visuell
ansprechendes PDF-Dokument herunterzuladen. Die PDF-Generierung wird
über die Open-Source-Bibliothek QuestPDF in der Community-Edition
realisiert.
Das generierte PDF folgt einem Glasmorphismus-Design und ist im Format
DIN A4 gehalten. Das Foto der Person wird als vollflächiges
Hintergrundbild verwendet, das die gesamte Seite ausfüllt. Im oberen
Bereich der Seite befindet sich ein halbtransparenter dunkler Bereich,
in dem der Name der Person in großen Großbuchstaben mit weitem
Zeichenabstand dargestellt wird. Darunter wird der Jahrgang mit erhöhtem
Zeichenabstand angezeigt. Im unteren Bereich der Seite befindet sich ein
weiterer halbtransparenter Bereich mit der vollständigen Beschreibung.
Der charakteristische Glaseffekt wird durch mehrschichtige
halbtransparente Hintergründe mit abgerundeten Ecken erzeugt, die dem
Dokument ein modernes und professionelles Erscheinungsbild verleihen.
Die PDF-Vorschau kann direkt auf der Detailseite über den „PDF
Vorschau''-Button geöffnet werden. Es öffnet sich ein modaler Dialog,
der das generierte PDF in einer Vorschau anzeigt. Über einen
„Herunterladen''-Button kann das PDF direkt auf das Gerät gespeichert
werden.
\paragraph{3.3.2 Datenmodell}\label{datenmodell}
Das Modul verwendet zwei Entitäten, die in der Datenbank als Tabellen
abgebildet werden.
\textbf{Entität HallOfFame}
Die zentrale Entität repräsentiert einen einzelnen Absolventeneintrag
und wird in der Datenbanktabelle \texttt{SZUAbsolventenvereinHallOfFame}
gespeichert.
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.1875}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.1875}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.6250}}@{}}
\caption{Entität HallOfFame}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Spalte
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Datentyp
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Spalte
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Datentyp
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\texttt{HallOfFameId} & \texttt{int} (PK, Auto-Inkrement) &
Primärschlüssel \\
\texttt{ModuleId} & \texttt{int} (FK → \texttt{Module}) & Fremdschlüssel
zur Oqtane-Modulinstanz \\
\texttt{Name} & \texttt{string} & Name der Person \\
\texttt{Year} & \texttt{int} & Abschlussjahrgang \\
\texttt{Description} & \texttt{string} & Beschreibung bzw. Werdegang \\
\texttt{Image} & \texttt{string} & Relativer Pfad zum hochgeladenen
Foto \\
\texttt{Link} & \texttt{string} & Optionaler externer Link \\
\texttt{Status} & \texttt{string} (max. 50) & Veröffentlichungsstatus:
„Draft'' oder „Published'' \\
\texttt{UserId} & \texttt{int} & ID der Benutzerin bzw. des Benutzers,
der den Eintrag erstellt hat \\
\texttt{IsReported} & \texttt{bool} & Kennzeichnung, ob der Eintrag
gemeldet wurde \\
\texttt{ReportReason} & \texttt{string} & (Legacy) Ursprüngliches Feld
für Meldegrund, abgelöst durch die Report-Tabelle \\
\texttt{CreatedBy} & \texttt{string} & Erstellt von (Audit) \\
\texttt{CreatedOn} & \texttt{DateTime} & Erstellzeitpunkt (Audit) \\
\texttt{ModifiedBy} & \texttt{string} & Zuletzt geändert von (Audit) \\
\texttt{ModifiedOn} & \texttt{DateTime} & Zeitpunkt der letzten Änderung
(Audit) \\
\end{longtable}
Die Entität implementiert das Oqtane-Interface \texttt{IAuditable},
wodurch die Audit-Felder automatisch vom Framework befüllt werden. Der
Fremdschlüssel \texttt{ModuleId} verknüpft jeden Eintrag mit einer
bestimmten Modulinstanz und ermöglicht so den Multi-Tenant-Betrieb.
\textbf{Entität HallOfFameReport}
Die zweite Entität bildet einzelne Meldungen zu einem Eintrag ab und
wird in der Tabelle \texttt{SZUAbsolventenvereinHallOfFameReport}
gespeichert.
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.2277}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.1782}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.5941}}@{}}
\caption{Entität HallOfFameReport}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Spalte
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Datentyp
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Spalte
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Datentyp
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\texttt{HallOfFameReportId} & \texttt{int} (PK, Auto-Inkrement) &
Primärschlüssel \\
\texttt{HallOfFameId} & \texttt{int} (FK →
\texttt{SZUAbsolventenvereinHallOfFame}) & Zugehöriger Eintrag \\
\texttt{Reason} & \texttt{string} & Meldegrund \\
\texttt{CreatedBy} & \texttt{string} & Erstellt von (Audit) \\
\texttt{CreatedOn} & \texttt{DateTime} & Erstellzeitpunkt (Audit) \\
\texttt{ModifiedBy} & \texttt{string} & Zuletzt geändert von (Audit) \\
\texttt{ModifiedOn} & \texttt{DateTime} & Zeitpunkt der letzten Änderung
(Audit) \\
\end{longtable}
Der Fremdschlüssel zu \texttt{SZUAbsolventenvereinHallOfFame} ist mit
kaskadierendem Löschen konfiguriert, sodass beim Löschen eines Eintrags
automatisch alle zugehörigen Meldungen entfernt werden. Zwischen den
beiden Entitäten besteht eine 1:n-Beziehung: Ein Eintrag kann beliebig
viele Meldungen besitzen.
\paragraph{3.3.3 Datenbankmigrationen}\label{datenbankmigrationen}
Die Datenbankstruktur wird über Entity Framework Core Migrationen
versioniert verwaltet.\footnote{Microsoft (2026c) (siehe
Internet-/Intranetverzeichnis).}
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.4545}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3636}}
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.1818}}@{}}
\caption{Datenbankmigrationen im Hall-of-Fame-Modul}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Migration
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Versionsnummer
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Inhalt
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Migration
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Versionsnummer
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Inhalt
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\texttt{InitializeModule} & \texttt{01.00.00.00} & Erstellt die
Haupttabelle mit allen Grundspalten sowie den Audit-Spalten \\
\texttt{AddReportingColumns} & \texttt{01.00.00.02} & Erweitert die
Haupttabelle um die Spalten \texttt{IsReported} und
\texttt{ReportReason} \\
\texttt{AddReportTable} & \texttt{01.00.00.03} & Erstellt die
eigenständige Report-Tabelle mit Fremdschlüssel zur Haupttabelle \\
\end{longtable}
\begin{quote}
\textbf{Hinweis:} Version \texttt{01.00.00.01} wurde im
Entwicklungsprozess übersprungen, da eine fehlerhafte Migration erstellt
und anschließend wieder gelöscht werden musste.
\end{quote}
\paragraph{3.3.4 Implementierungsdetails und
Problemlösungen}\label{implementierungsdetails-und-problemluxf6sungen}
Während der Entwicklung traten mehrere technische Herausforderungen auf,
die im Folgenden zusammen mit ihren Lösungen beschrieben werden.
\textbf{Bild-Upload-System}
In der ursprünglichen Version des Moduls mussten Benutzerinnen und
Benutzer eine Bild-URL manuell in ein Textfeld eingeben. Dies war wenig
benutzerfreundlich und erforderte, dass Bilder zunächst anderweitig
hochgeladen und verlinkt werden mussten. Das implementierte
Bild-Upload-System ersetzt dies durch eine
Blazor-\texttt{InputFile}-Komponente mit Live-Vorschau,
Fortschrittsanzeige und Lösch-Button. Die Upload-Methode prüft die
Dateigröße (maximal 5 MB), öffnet die Datei als Stream und übermittelt
sie als \texttt{MultipartFormDataContent} an den Server, der den
Dateityp serverseitig validiert, einen UUID-Dateinamen (Universally
Unique Identifier -- eine zufällig generierte, global eindeutige
Zeichenkette) generiert und die Datei speichert. Das System unterstützt
beide Blazor-Rendering-Modi.
\textbf{Concurrency Exception beim Löschen}
Beim Löschen von Einträgen mit vorhandenen Meldungen trat eine
\texttt{DbUpdateConcurrencyException} auf, verursacht durch Konflikte im
Entity Framework Change Tracker. Die Lösung bestand in der Aufteilung
der Löschoperation in zwei separate Transaktionen mit jeweils eigenem
DbContext: zuerst werden alle zugehörigen Meldungen gelöscht, danach der
Haupteintrag.
\textbf{Kartendesign-Optimierung}
Karten hatten ursprünglich unterschiedliche Höhen durch variierende
Beschreibungslängen. Die Lösung kombiniert CSS-Flexbox auf dem
Kartenelement mit einer Maximalhöhe von 150 Pixeln und
\texttt{overflow:\ hidden} auf dem Beschreibungscontainer, sodass alle
Karten einer Zeile dieselbe Höhe aufweisen.
\textbf{Sortier-Toggle}
Die ursprünglich fest codierten Sortierrichtungen wurden durch einen
Toggle-Button neben dem Sortier-Dropdown ersetzt, der mit einem
dynamischen Pfeil-Icon zwischen aufsteigender und absteigender
Sortierung umschaltet. Die Sortierlogik ist in einer berechneten
Eigenschaft gekapselt, die Suche und Sortierung kombiniert.
\subsection{4. Übergangslösung \&
Probleme}\label{uxfcbergangsluxf6sung-probleme}
\subsubsection{4.1 Übergangslösung (Sommer
2025)}\label{uxfcbergangsluxf6sung-sommer-2025}
\paragraph{4.1.1 Gründe \& Technische
Umsetzung}\label{gruxfcnde-technische-umsetzung}
Da AlumniHub zum Zeitpunkt des ersten Absolvententreffens im Sommer 2025
noch nicht vollständig fertiggestellt war und das Hosting von Oqtane auf
dem Hetzner-Server zu unerwarteten technischen Problemen geführt hatte,
wurde im Team die Entscheidung getroffen, eine eigenständige
Übergangslösung zu entwickeln. Diese sollte pünktlich zur Veranstaltung
einsatzbereit sein und die wichtigsten Funktionen abdecken: die
Möglichkeit für Absolventinnen und Absolventen, ihre Teilnahme am
Treffen zu bestätigen oder abzusagen, sowie die Möglichkeit, direktes
Feedback zum Diplomprojekt AlumniHub abzugeben.
Da für die Entwicklung nur wenig Zeit zur Verfügung stand und eine
vollständige Integration in die noch nicht fertige AlumniHub-Plattform
nicht realistisch war, wurde bewusst auf einen schlanken, eigenständigen
Technologie-Stack gesetzt. Dieser sollte schnell entwickelt, einfach
deploybar und zuverlässig betreibbar sein -- ohne die Komplexität eines
vollständigen CMS-Systems.
Das Frontend der Übergangslösung wurde mit den grundlegenden
Webtechnologien HTML, CSS und JavaScript umgesetzt. Die Hauptseite
(\texttt{index.html}) präsentiert den Besucherinnen und Besuchern eine
übersichtliche und schlichte Oberfläche, die sich am Erscheinungsbild
der HTL Ungargasse orientiert -- mit dem Schullogo im Header und einem
Grau-Weiß-Farbschema. Im Mittelpunkt der Seite befindet sich ein
zentraler Button, über den ein modales Overlay-Formular geöffnet werden
kann. Über dieses Formular konnten Nutzerinnen und Nutzer ihre
E-Mail-Adresse eingeben und ihr Feedback zum Projekt abgeben. Die
Steuerung des Overlays erfolgt über JavaScript: Das Formular öffnet sich
beim Klick auf den Button und schließt sich automatisch, wenn außerhalb
des Formularfensters geklickt wird. Dadurch ist eine intuitive Bedienung
ohne zusätzliche Schaltflächen möglich.
Für die Verarbeitung von Zu- und Absagen zum Absolvententreffen wurden
zusätzlich zwei separate Bestätigungsseiten erstellt:
\texttt{zusage.html} und \texttt{absage.html}. Diese Seiten werden dem
Nutzer nach dem erfolgreichen Absenden des jeweiligen Formulars
angezeigt und bestätigen den Eingang der Rückmeldung. Das Design aller
drei Seiten ist einheitlich gehalten und folgt dem gleichen
Layout-Prinzip mit Header, Logo und zentriertem Hauptinhalt.
Das Backend der Lösung, das für die Verarbeitung der Formulardaten und
den Versand von Bestätigungs-E-Mails zuständig war, wurde von einem
Teammitglied entwickelt und war nicht Teil des eigenen Aufgabenbereichs.
Es wurde mit Node.js und dem Framework Express umgesetzt und auf einem
virtuellen Server bei Hetzner Cloud gehostet.
\paragraph{4.1.2 Differenzen zur finalen
Lösung}\label{differenzen-zur-finalen-luxf6sung}
Im direkten Vergleich zur finalen AlumniHub-Plattform unterscheidet sich
die Übergangslösung in mehreren wesentlichen Punkten. Während AlumniHub
auf dem vollständigen CMS Oqtane basiert und eine umfangreiche
Funktionspalette bietet -- darunter Benutzerverwaltung, Hall of Fame,
Anmeldetool, Datenauswertung und ein individuelles Theme -- beschränkte
sich die Übergangslösung bewusst auf das absolut Notwendige.
Es gab keine Benutzerkonten und keine Authentifizierung. Jede Person,
die die Seite aufrief, konnte das Formular ausfüllen und absenden, ohne
sich vorher registrieren oder anmelden zu müssen. Es gab keine Datenbank
im klassischen Sinne -- die eingegangenen Formulardaten wurden
serverseitig als einfache JSON-Dateien gespeichert. Es gab keine
administrativen Funktionen, keine Übersichten und keine Möglichkeit, die
Daten direkt über eine Benutzeroberfläche auszuwerten.
Diese bewusste Reduktion auf das Wesentliche war jedoch kein Nachteil,
sondern eine pragmatische Entscheidung: Die Lösung musste schnell
funktionieren und zuverlässig sein -- und das war sie. Nach dem
Absolvententreffen wurde die Seite nicht einfach abgeschaltet, sondern
zu einer reinen Feedback-Seite für das Diplomprojekt umfunktioniert.
Über diese Seite konnten Interessierte -- darunter Lehrkräfte,
Mitschülerinnen und Mitschüler sowie externe Besucher der Präsentation
-- weiterhin direktes Feedback zum Projekt abgeben. Die Seite blieb so
lange in Betrieb, bis die vollständig entwickelte AlumniHub-Plattform
diese Funktion nativ übernahm.
\subsubsection{4.2 Probleme}\label{probleme}
\paragraph{4.2.1 Technische Probleme}\label{technische-probleme}
Eine der größten technischen Herausforderungen im gesamten
Projektverlauf war der Zeitdruck, der insbesondere im Vorfeld des ersten
Absolvententreffens spürbar war. Da AlumniHub zu diesem Zeitpunkt noch
nicht einsatzbereit war, musste die Übergangslösung in sehr kurzer Zeit
konzipiert, entwickelt und in Betrieb genommen werden. Dieser Zeitdruck
führte dazu, dass keine ausreichende Zeit für gründliches Testen oder
für die Umsetzung zusätzlicher Funktionen blieb.
Ein weiteres technisches Problem ergab sich durch den Wechsel von
Windows auf macOS während der Entwicklung. Da Oqtane primär für Windows
entwickelt ist und die macOS-Unterstützung veraltet bzw. nicht
vollständig angepasst ist, traten nach dem Plattformwechsel Build-Fehler
auf und Oqtane startete zunächst nur mit Fehlern. Diese Probleme
erschwerten die Weiterentwicklung erheblich, da zunächst die
Entwicklungsumgebung stabilisiert werden musste, bevor die eigentliche
Arbeit an den Modulen fortgesetzt werden konnte. Durch den Wechsel auf
JetBrains Rider als Entwicklungsumgebung konnten die Build-Probleme
schließlich behoben und die Arbeit an den Modulen erfolgreich
fortgesetzt werden.
Ein weiteres technisches Problem betraf das Hosting von Oqtane auf dem
Hetzner-Server. Die Einrichtung und der Betrieb von Oqtane auf dem
Server bereiteten unerwartete Schwierigkeiten, die den regulären
Entwicklungsfortschritt verlangsamten. Da dieser Bereich nicht zum
eigenen Aufgabengebiet gehörte, konnten die genauen Ursachen nicht
vollständig nachvollzogen werden. Die Probleme wirkten sich jedoch auf
den gesamten Zeitplan des Projekts aus und waren mitverantwortlich
dafür, dass AlumniHub zum Zeitpunkt des ersten Treffens noch nicht
fertig war.
In den letzten Monaten des Projekts wurde daher ein Wechsel des
Hosting-Anbieters vorgenommen. Statt Hetzner wird die Plattform nun bei
LiveDesign gehostet, einem österreichischen Unternehmen, das sich auf
Hosting, Webdesign und individuelle Webservices spezialisiert
hat.\footnote{LiveDesign (2026) (siehe Internet-/Intranetverzeichnis).}
LiveDesign betreibt seine Datacenter ausschließlich in Österreich und
bietet eine zuverlässige Infrastruktur für CMS-Systeme wie Oqtane. Durch
diesen Wechsel konnten die bestehenden Stabilitätsprobleme behoben und
der Betrieb der Plattform deutlich verbessert werden.
\paragraph{4.2.2 Kontakt mit Oqtane- und
Hetzner-Support}\label{kontakt-mit-oqtane--und-hetzner-support}
Im Verlauf des Projekts wurde sowohl der Support von Oqtane als auch der
Support von Hetzner kontaktiert, um Hilfe bei den aufgetretenen
technischen Problemen zu erhalten.
Der Kontakt mit dem Oqtane-Support erfolgte über GitHub Issues sowie per
E-Mail. Die gemeldeten Probleme betrafen spezifische Verhaltensweisen
des Frameworks beim Hosting auf dem Hetzner-Server. Trotz der
Kontaktaufnahme konnte keine hilfreiche Antwort erhalten werden, die zur
Lösung der Probleme beigetragen hätte. Die offizielle Dokumentation
sowie vorhandene Community-Beiträge lieferten ebenfalls keine
ausreichenden Antworten auf die spezifischen Konfigurationsprobleme.
Auch der Hetzner-Support wurde bezüglich der Serverprobleme kontaktiert.
Die Anfragen bezogen sich auf die Serverkonfiguration und mögliche
Ursachen für die Instabilität beim Betrieb von Oqtane. Auch hier führten
die Rückmeldungen nicht zu einer vollständigen Lösung des Problems. Die
Erfahrung zeigte, dass bei der Nutzung spezialisierter Frameworks auf
externen Hosting-Plattformen mit eingeschränktem Support gerechnet
werden muss und eine intensive Eigenrecherche oft unumgänglich ist.
\paragraph{4.2.3 Organisatorische
Probleme}\label{organisatorische-probleme}
Neben den technischen Herausforderungen gab es auch auf
organisatorischer Ebene ein zentrales Problem, das den Projektverlauf im
Sommer 2025 erheblich beeinflusste. Während der Sommermonate war die
aktive Mitarbeit innerhalb des Teams sehr ungleich verteilt. Ein
Großteil der Teammitglieder war urlaubsbedingt nicht oder nur
eingeschränkt erreichbar und beteiligte sich in dieser Zeit kaum am
Projekt. Nur ein kleiner Teil des Teams arbeitete in dieser Phase aktiv
weiter und trieb den Fortschritt voran.
Diese Situation führte zu einer deutlichen Ungleichverteilung der
Arbeitsbelastung. Wenige Personen mussten in dieser Zeit deutlich mehr
Verantwortung übernehmen als ursprünglich geplant, was zu Frustration
und Verzögerungen führte. Im Nachhinein zeigt sich, dass klarere
Absprachen und verbindlichere Vereinbarungen zu Beginn des Sommers dazu
beigetragen hätten, diese Situation zu vermeiden oder zumindest
abzumildern.
\subsection{5. Learnings}\label{learnings}
\subsubsection{5.1 Technisch}\label{technisch}
Das Projekt hat auf technischer Ebene wertvolle praktische Kenntnisse
vermittelt, die über den schulischen Rahmen hinausgehen. Der konsequente
Einsatz von Git zur Versionskontrolle war dabei eine der wichtigsten
Erfahrungen. Durch die tägliche Arbeit mit Branches, Commits und Merges
wurde ein tiefes Verständnis für kollaborative Softwareentwicklung
aufgebaut. Es wurde deutlich, wie wichtig aussagekräftige
Commit-Nachrichten, regelmäßige Commits und eine saubere Branch-Struktur
für die Zusammenarbeit im Team sind.
Im Bereich Responsive Design wurden durch die praktische Umsetzung des
Themes und der Übergangslösung wichtige Kenntnisse vertieft. Dabei wurde
gelernt, wie man mit CSS Media Queries und Flexbox Layouts baut, die
sich zuverlässig an unterschiedliche Bildschirmgrößen anpassen.
Besonders die Umsetzung des Burger-Menüs für mobile Geräte war eine
lehrreiche Erfahrung, die zeigte, wie man komplexe UI-Funktionen mit
minimalem JavaScript-Einsatz realisieren kann.
Auch der Umgang mit JavaScript zur Steuerung von UI-Elementen wurde
durch die Arbeit am Projekt verbessert. Die Implementierung der
Overlay-Steuerung in der Übergangslösung sowie die Menü-Logik im Theme
haben gezeigt, wie JavaScript gezielt und effizient eingesetzt werden
kann, um eine bessere Benutzererfahrung zu schaffen.
\subsubsection{5.2 Methodisch}\label{methodisch}
Auf methodischer Ebene hat das Projekt gezeigt, wie wichtig eine klare
und verbindliche Aufgabenverteilung von Beginn an ist. In Phasen, in
denen nicht klar war, wer welche Aufgaben übernimmt, entstanden Lücken
und Verzögerungen. Eine strukturiertere Planung mit klar definierten
Zuständigkeiten hätte in solchen Situationen geholfen, den Überblick zu
behalten und die Arbeit effizienter aufzuteilen.
Darüber hinaus hat sich gezeigt, dass regelmäßige Team-Meetings ein
wichtiges Werkzeug für den Projekterfolg sind. In Phasen, in denen der
Austausch im Team spärlicher war, dauerte es länger, bis Probleme
erkannt und gelöst wurden. Kurze, regelmäßige Abstimmungen hätten dazu
beigetragen, den aktuellen Stand besser zu kommunizieren und gemeinsam
schneller auf Hindernisse zu reagieren.
\subsubsection{5.3 Persönlich}\label{persuxf6nlich}
Auf persönlicher Ebene war die wichtigste Erkenntnis aus diesem Projekt
die Bedeutung von Eigeninitiative. In einem größeren Teamprojekt kann
man sich nicht immer darauf verlassen, dass andere Aufgaben erledigen
oder Entscheidungen treffen. Gerade in den Phasen, in denen nicht alle
Teammitglieder aktiv waren, hat sich gezeigt, dass proaktives Handeln
entscheidend ist, um das Projekt voranzubringen. Diese Erfahrung hat das
Bewusstsein dafür gestärkt, Verantwortung nicht nur für den eigenen
Bereich, sondern auch für das Gesamtprojekt zu übernehmen und bei Bedarf
auch Aufgaben außerhalb des ursprünglich geplanten Bereichs zu
übernehmen.
\subsection{6. Testen \&
Qualitätssicherung}\label{testen-qualituxe4tssicherung}
\subsubsection{6.1 Funktionstests der
Module}\label{funktionstests-der-module}
Alle entwickelten Module -- das Anmeldetool und das Hall-of-Fame-Modul
-- wurden manuell auf ihre korrekte Funktionsweise getestet. Dabei
wurden sämtliche Benutzeraktionen systematisch durchgegangen, darunter
das An- und Abmelden zu Veranstaltungen, das Erstellen, Bearbeiten und
Veröffentlichen von Hall-of-Fame-Einträgen, das Hochladen von Bildern
sowie die Melde- und Löschfunktionen. Fehler und unerwartetes Verhalten
wurden dokumentiert und im Anschluss behoben.
Beim Anmeldetool wurde insbesondere geprüft, ob die Zusage- und
Absage-Buttons korrekt reagieren und der Status unmittelbar in der
Oberfläche angezeigt wird. Beim Hall-of-Fame-Modul lag ein besonderer
Fokus auf dem Bild-Upload: Es wurde getestet, ob die Größenbeschränkung
von 5 MB korrekt greift, ob die Vorschau sofort erscheint und ob
ungültige Dateiformate abgelehnt werden. Auch die Duplikatprüfung --
also die Sicherstellung, dass pro Person nur ein Eintrag erstellt werden
kann -- sowie die Eigentümerprüfung wurden gezielt getestet.
\subsubsection{6.2 Theme-Tests}\label{theme-tests}
Das entwickelte Oqtane-Theme wurde auf verschiedenen Browsern und
Geräten getestet, um eine konsistente Darstellung sicherzustellen.
Getestet wurde unter anderem auf aktuellen Versionen von Chrome, Firefox
und Safari sowie auf mobilen Geräten mit unterschiedlichen
Bildschirmgrößen. Ein besonderer Fokus lag dabei auf der korrekten
Darstellung des Burger-Menüs auf mobilen Endgeräten sowie auf der
Funktionsfähigkeit der dynamischen Navigation.
\subsubsection{6.3 Usability-Tests}\label{usability-tests}
Neben den technischen Tests wurde die Plattform auch von weiteren
Personen -- darunter Mitschülerinnen und Mitschüler sowie Lehrkräfte --
auf ihre Benutzerfreundlichkeit geprüft. Das Feedback aus diesen Tests
floss in die weitere Entwicklung ein und führte unter anderem zu
Anpassungen in der Darstellung und Bedienung einzelner Elemente.
Ein häufiges Feedback war, dass die Unterscheidung zwischen Zusage und
Absage auf den ersten Blick nicht immer sofort klar war. Daraufhin
wurden die Schaltflächen farblich deutlicher gestaltet -- Grün für die
Zusage, Rot für die Absage -- und die Beschriftungen präzisiert. Solche
Rückmeldungen aus echten Nutzertests sind wertvoll, weil Entwicklerinnen
und Entwickler ihre eigene Anwendung oft anders wahrnehmen als Personen,
die das System zum ersten Mal sehen.
\subsection{7. Fazit und Ausblick}\label{fazit-und-ausblick}
\subsubsection{7.1 Zielerreichung}\label{zielerreichung}
Rückblickend auf das Projekt AlumniHub lässt sich festhalten, dass die
zentralen Ziele meines individuellen Aufgabenbereichs erfolgreich
umgesetzt wurden.
Das \textbf{Hall-of-Fame-Modul} wurde vollständig fertiggestellt. Es
umfasst die Übersichtsseite mit responsivem Kartenlayout, die
Detailseite mit Glasmorphismus-Design, die Edit-Seite mit
Rich-Text-Editor und Bild-Upload, die Meldefunktion sowie den PDF-Export
mit QuestPDF. Das Modul ist in das Oqtane-CMS integriert und produktiv
einsetzbar.
Das \textbf{Anmeldetool} wurde ebenfalls implementiert und ist
funktionsfähig. Grundfunktionen wie Zusage und Absage zu Veranstaltungen
sowie die responsive Darstellung sind vollständig umgesetzt.
Weiterführende Funktionen wie eine vollständige Teilnehmerliste mit
Datenbankanbindung und die Möglichkeit, maximale Teilnehmerzahlen
festzulegen, sind architektonisch bereits vorbereitet, jedoch noch nicht
vollständig implementiert.
Das \textbf{Theme} wurde von einem anderen Teammitglied entwickelt und
ist ebenfalls fertiggestellt. Der Wechsel des Hostings von Hetzner zu
LiveDesign hat die Stabilität der Plattform deutlich verbessert.
Insgesamt bin ich mit dem erreichten Stand zufrieden -- insbesondere
angesichts der Herausforderungen durch die Teamverkleinerung, den
Plattformwechsel von Windows auf macOS und die technischen Probleme mit
dem ursprünglichen Hosting.
\subsubsection{7.2 Ausblick}\label{ausblick}
Für die Weiterentwicklung der Plattform gibt es mehrere sinnvolle
nächste Schritte. Im Bereich des Anmeldetools wäre die Implementierung
einer vollständigen Teilnehmerliste mit direkter Datenbankanbindung der
wichtigste nächste Schritt. Ergänzend dazu sollte die Möglichkeit
geschaffen werden, maximale Teilnehmerzahlen pro Veranstaltung
festzulegen, sodass Organisatoren die Kapazität von Treffen besser
steuern können.
Langfristig könnte die Plattform um eine automatische
E-Mail-Benachrichtigung erweitert werden, die Mitglieder an
bevorstehende Veranstaltungen erinnert. Auch eine Exportfunktion für die
Anmeldedaten -- beispielsweise als CSV oder PDF -- wäre für die
Vereinsverwaltung hilfreich.
AlumniHub bietet als Plattform eine solide Grundlage, die in den
kommenden Jahren kontinuierlich erweitert werden kann, um den
Absolventenverein der HTL Ungargasse langfristig digital zu
unterstützen.
\cleardoublepage
\section{Florian Edlmayer}\label{florian-edlmayer-1}
\subsection{Einleitung des individuellen
Teils}\label{einleitung-des-individuellen-teils-1}
\subsubsection{Ausgangssituation}\label{ausgangssituation}
Der Absolventenverein der HTL Ungargasse hatte keine digitale
Infrastruktur zur Vernetzung seiner Mitglieder. AlumniHub soll das
ändern: eine zentrale Plattform für Mitgliederverwaltung,
Veranstaltungen und Netzwerkfunktionen.
\subsubsection{Zielsetzung}\label{zielsetzung-1}
Mein persönlicher Beitrag umfasst die DSGVO-konforme
Datenschutzimplementierung, ein automatisiertes Backup- und
Restore-System, die Anbindung von LinkedIn als externem
Identitätsanbieter sowie den Premium-Bereich.
\subsubsection{Nutzen für den
Absolventenverein}\label{nutzen-fuxfcr-den-absolventenverein}
Für den Verein bedeutet das: rechtskonforme Datenverarbeitung,
gesicherte Datenverfügbarkeit und ein niedrigschwelliger Login für
Absolventinnen und Absolventen mit bestehendem LinkedIn-Konto.
\subsubsection{Themenstellung}\label{themenstellung}
\paragraph{Entwicklung eines
CMS-Moduls}\label{entwicklung-eines-cms-moduls}
\textbf{Premiumbereich:} Ein Schwerpunkt der Arbeit ist die
Entwicklung eines Oqtane-Moduls, das einen Premiumbereich bereitstellt.
Dieser Bereich soll ausschließlich autorisierten Benutzerinnen und
Benutzern zugänglich sein und spezielle Inhalte oder Funktionen anbieten
wie zum Beispiel das hochladen und ansehen von Ingeneuranträgen als
Vorlage oder das finden und Kontakt aufnehmen mit anderen Benutzern.
Dabei werden grundlegende Konzepte wie Benutzerrollen, Zugriffsrechte
und die Integration in das bestehende CMS eingebunden.
\subparagraph{Erstellung einer
Datenschutzerklärung}\label{erstellung-einer-datenschutzerkluxe4rung}
Eine weitere wichtige Aufgabe war die Erstellung einer gesetzeskonformen
Datenschutzerklärung. Diese ist erforderlich, um die Nutzerinnen und
Nutzer unserer Webanwendung transparent und verständlich darüber zu
informieren, welche personenbezogenen Daten erhoben, verarbeitet und
gespeichert werden, zu welchem Zweck dies geschieht und welche Rechte
ihnen in Bezug auf ihre Daten zustehen.
\paragraph{LinkedIn-Anmeldung}\label{linkedin-anmeldung}
Zur Verbesserung der Benutzerfreundlichkeit und zur vereinfachten
Anmeldung wird eine Anmeldung über LinkedIn mithilfe des
OAuth-2.0-Protokolls implementiert. Diese ermöglicht es Benutzerinnen
und Benutzern, sich mit einem bestehenden LinkedIn-Konto zu registrieren
oder anzumelden.
\paragraph{Datenbank-Backup und
Restore}\label{datenbank-backup-und-restore}
Abschließend wurde das Thema Datensicherung umfassend behandelt, da die
Sicherstellung der Datenintegrität und Systemverfügbarkeit einen
zentralen Bestandteil moderner Webanwendungen darstellt. In diesem
Zusammenhang wurde ein Skript zur automatisierten Erstellung von
Datenbank-Backups entwickelt, das in regelmäßigen Abständen
Sicherungskopien der relevanten Daten erzeugt. Zusätzlich wurde ein
weiteres Skript implementiert, das im Falle eines Datenverlusts oder
Systemausfalls eine strukturierte und zuverlässige Wiederherstellung der
gesicherten Daten ermöglicht.
Ziel dieser Maßnahmen ist es, das Risiko von Datenverlusten zu
minimieren und einen stabilen sowie sicheren Betrieb der Webanwendung zu
gewährleisten. Die Backup- und Restore-Prozesse sollen automatisiert
ablaufen, um menschliche Fehler zu reduzieren und eine kontinuierliche,
regelmäßige Datensicherung sicherzustellen. Dadurch wird eine hohe
Ausfallsicherheit sowie die langfristige Verfügbarkeit der gespeicherten
Informationen gewährleistet.
\subsubsection{Detail-Zielsetzungen}\label{detail-zielsetzungen}
\paragraph{Zielsetzung}\label{zielsetzung-2}
\subparagraph{Technische Zielsetzung}\label{technische-zielsetzung}
Die technische Zielsetzung dieses Diplomarbeitsprojekts bestand darin,
eine stabile und sichere Webplattform für Alumni zu entwickeln. Als
technische Grundlage wurde das Content-Management-System Oqtane
verwendet, welches auf modernen Webtechnologien basiert und eine
modulare Erweiterung von Funktionen ermöglicht. Die Implementierung
erfolgte innerhalb der von Oqtane bereitgestellten Architektur, wodurch
verschiedene Funktionen effizient integriert werden konnten.
Ein zentraler Bestandteil der technischen Umsetzung war die Integration
einer externen Authentifizierung über LinkedIn mithilfe des Protokolls
OAuth 2.0. Dadurch können sich Benutzer direkt mit ihrem LinkedIn-Konto
auf der Plattform anmelden, ohne ein separates Benutzerkonto erstellen
zu müssen. Dies vereinfacht den Registrierungsprozess und erhöht
gleichzeitig die Sicherheit, da sensible Zugangsdaten ausschließlich
beim externen Anbieter verbleiben.
Zusätzlich wurde eine Datenbank auf Basis von PostgreSQL eingesetzt, um
Benutzerinformationen, Inhalte sowie weitere Systemdaten zuverlässig zu
speichern. Um die langfristige Verfügbarkeit dieser Daten
sicherzustellen, wurde ein automatisiertes Backup-System implementiert.
Dieses System erstellt regelmäßig Sicherungskopien der Datenbank sowie
der wichtigsten Systemdateien. Ergänzend dazu wurde ein Restore-Skript
entwickelt, mit dem gespeicherte Daten im Falle eines Systemfehlers oder
Datenverlustes wiederhergestellt werden können.
Ein weiterer wichtiger Aspekt der technischen Zielsetzung war die
Einhaltung rechtlicher Anforderungen im Bereich Datenschutz. Daher wurde
eine Datenschutzerklärung erstellt und die Plattform so konzipiert, dass
personenbezogene Daten der Benutzer entsprechend den Vorgaben der
Datenschutz-Grundverordnung verarbeitet werden.
\subparagraph{Funktionale Zielsetzung}\label{funktionale-zielsetzung}
Die funktionale Zielsetzung des Projekts bestand darin, eine
Onlineplattform zu entwickeln, die ehemaligen Schülerinnen und Schülern
die Möglichkeit bietet, miteinander in Kontakt zu bleiben und sich über
aktuelle Veranstaltungen zu informieren. Die Plattform dient somit als
digitales Netzwerk für Alumni und soll den Austausch zwischen ehemaligen
Mitgliedern der Bildungseinrichtung fördern.
Eine zentrale Funktion der Anwendung ist die Anmeldung über LinkedIn,
wodurch Benutzer schnell und unkompliziert Zugriff auf die Plattform
erhalten können. Nach der erfolgreichen Anmeldung wird automatisch ein
Benutzerkonto erstellt oder ein bestehendes Konto zugeordnet.
Darüber hinaus bietet die Plattform Funktionen zur Darstellung und
Verwaltung von Veranstaltungen. Alumni können sich über kommende Events
informieren und sich für diese anmelden. Dadurch wird die Organisation
von Treffen oder Veranstaltungen erleichtert und die Vernetzung
innerhalb der Alumni-Community gefördert.
Zusätzlich wurde ein Premiumbereich entwickelt, der bestimmte Inhalte
oder Funktionen exklusiv für berechtigte Benutzer bereitstellt. Dieser
Bereich erweitert die Möglichkeiten der Plattform und ermöglicht es,
spezielle Inhalte nur bestimmten Benutzergruppen zugänglich zu machen.
Durch diese Funktionen entsteht eine Plattform, die nicht nur als
Informationsquelle dient, sondern auch aktiv zur Vernetzung der Alumni
beiträgt.
\subparagraph{Gestalterische
Zielsetzung}\label{gestalterische-zielsetzung}
Neben den technischen und funktionalen Anforderungen spielte auch die
Gestaltung der Plattform eine wichtige Rolle. Ziel war es, eine
übersichtliche und benutzerfreundliche Oberfläche zu entwickeln, die
eine einfache Navigation und intuitive Bedienung ermöglicht.
Die Struktur der Webseite wurde so aufgebaut, dass wichtige Funktionen
wie Login, Benutzerprofil oder Eventübersicht schnell erreichbar sind.
Ein klares Layout und eine strukturierte Seitenaufteilung tragen dazu
bei, dass sich Benutzer auf der Plattform leicht orientieren können.
Darüber hinaus wurde darauf geachtet, dass die Plattform ein modernes
Erscheinungsbild besitzt und den Erwartungen einer zeitgemäßen
Webanwendung entspricht. Durch eine klare Gestaltung und eine reduzierte
Benutzeroberfläche wird sichergestellt, dass Inhalte übersichtlich
dargestellt werden und die Nutzung der Plattform möglichst komfortabel
ist.
Insgesamt verfolgt die gestalterische Zielsetzung das Ziel, eine
Plattform zu schaffen, die sowohl funktional als auch optisch
ansprechend ist und den Benutzern eine positive Nutzungserfahrung
bietet.
\subsection{Anforderungen an das entwickelte Modul bzw. die
Funktionalität}\label{anforderungen-an-das-entwickelte-modul-bzw.-die-funktionalituxe4t}
funktionale / nichtfunktionale Anforderungen
Use Cases
\subsection{Technologien}\label{technologien-1}
\subsubsection{ASP.NET und .NET}\label{asp.net-und-.net}
Für die Entwicklung der Webanwendung wurde das Framework ASP.NET in
Kombination mit der Plattform .NET verwendet. .NET ist eine von
Microsoft entwickelte Entwicklungsplattform zur Erstellung moderner
Anwendungen für Web, Desktop, Cloud und mobile Systeme.
ASP.NET stellt innerhalb der .NET-Plattform ein Framework dar, das
speziell für die Entwicklung von Webanwendungen und Webservices
konzipiert wurde. Es bietet eine Vielzahl an Funktionen zur Verarbeitung
von HTTP-Anfragen, zur Verwaltung von Benutzersitzungen sowie zur
Integration von Datenbanken und externen Diensten.
Die Anwendung basiert auf .NET 8, einer aktuellen Version der Plattform,
welche insbesondere Verbesserungen in den Bereichen Performance,
Sicherheit und Skalierbarkeit bietet. Durch die Nutzung dieser Plattform
ist es möglich, stabile und performante Webanwendungen zu entwickeln,
die auch bei steigender Benutzeranzahl zuverlässig funktionieren.
Ein wesentliches Konzept von ASP.NET ist die serverseitige Verarbeitung
von Webanfragen. Wenn ein Benutzer eine Seite im Browser aufruft, wird
zunächst eine Anfrage an den Webserver gesendet. Der Server verarbeitet
diese Anfrage innerhalb der ASP.NET-Anwendung und sendet anschließend
eine generierte Antwort zurück an den Browser.
Durch diese Architektur wird sichergestellt, dass die eigentliche Logik
der Anwendung auf dem Server ausgeführt wird, während der Browser
lediglich für die Darstellung der Benutzeroberfläche zuständig ist.
Ein weiterer Vorteil von ASP.NET besteht in der klar strukturierten
Architektur moderner Webanwendungen. Typischerweise wird eine Anwendung
in mehrere Schichten unterteilt, beispielsweise:
• Präsentationsschicht (Benutzeroberfläche) • Geschäftslogik •
Datenzugriffsschicht
Diese Struktur erleichtert die Wartung und Erweiterung der Anwendung
erheblich, da Änderungen gezielt innerhalb einzelner Komponenten
vorgenommen werden können.
\subsubsection{PostgreSQL-Datenbank}\label{postgresql-datenbank}
Zur Speicherung der Daten der Plattform wurde das relationale
Datenbanksystem PostgreSQL eingesetzt. PostgreSQL gehört zu den
leistungsfähigsten Open-Source-Datenbanken und wird weltweit in vielen
professionellen Anwendungen verwendet.
Eine relationale Datenbank speichert Informationen in Tabellen, die aus
Zeilen und Spalten bestehen. Jede Tabelle repräsentiert eine bestimmte
Art von Daten, beispielsweise Benutzer, Veranstaltungen oder
Systeminformationen. Beziehungen zwischen Tabellen werden durch
sogenannte Schlüssel definiert.
Durch diese Struktur kann beispielsweise gespeichert werden:
• welcher Benutzer existiert • welche Veranstaltungen geplant sind •
welcher Benutzer sich für welches Event angemeldet hat
PostgreSQL bietet darüber hinaus zahlreiche Funktionen, die für moderne
Webanwendungen wichtig sind. Dazu gehören unter anderem:
• hohe Stabilität und Zuverlässigkeit • Unterstützung komplexer
Datenabfragen mittels SQL • Transaktionssicherheit zur Vermeidung von
Datenverlust • Erweiterbarkeit durch zusätzliche Module
Ein weiterer wichtiger Vorteil besteht darin, dass PostgreSQL sehr gut
mit modernen Webframeworks wie ASP.NET zusammenarbeitet. Dadurch kann
die Anwendung effizient auf gespeicherte Daten zugreifen und diese
verarbeiten.
Da die Plattform personenbezogene Daten von Benutzern speichert, wurde
zusätzlich ein Backup-System implementiert, das regelmäßige Sicherungen
der Datenbank erstellt. Dadurch können Daten im Falle eines Fehlers oder
eines Systemausfalls wiederhergestellt werden.
\subsubsection{OAuth 2.0}\label{oauth-2.0}
Für die sichere Anmeldung über externe Identitätsanbieter wurde das
Protokoll OAuth 2.0 implementiert. OAuth 2.0 ist ein branchenweiter
Standard für die Autorisierung, der es Benutzern ermöglicht, Anwendungen
von Drittanbietern Zugriff auf ihre Daten zu gewähren, ohne ihre
Passwörter für das jeweilige Portal preiszugeben. Im Rahmen dieses
Projekts wurde OAuth 2.0 als Authentifizierungsverfahren mit LinkedIn
gewählt, um den Registrierungsprozess der Benutzer signifikant zu
vereinfachen, da bestehende LinkedIn-Accounts genutzt werden können.
Gleichzeitig wird dadurch ein hohes Sicherheitsniveau sichergestellt.
\subsection{Module}\label{module-1}
\subsubsection{Datenschutz und rechtliche
Grundlagen}\label{datenschutz-und-rechtliche-grundlagen}
\paragraph[Bedeutung der Datenschutz-Grundverordnung
(DSGVO)]{\texorpdfstring{Bedeutung der Datenschutz-Grundverordnung
(DSGVO)\footnote{Europäische Union (2016) (siehe
Internet-/Intranetverzeichnis).}}{Bedeutung der Datenschutz-Grundverordnung (DSGVO)}}\label{bedeutung-der-datenschutz-grundverordnung-dsgvo-dsgvo}
Die Datenschutz-Grundverordnung (DSGVO) ist eine rechtsverbindliche
Verordnung der Europäischen Union, die seit dem 25. Mai 2018 unmittelbar
in allen EU-Mitgliedstaaten gilt und den Schutz personenbezogener Daten
regelt. Sie wurde erlassen, um die Rechte natürlicher Personen bei der
Verarbeitung ihrer Daten zu stärken und ein einheitliches
Datenschutzniveau innerhalb der EU zu gewährleisten.
Der Anwendungsbereich der DSGVO umfasst alle Verarbeitungen
personenbezogener Daten, unabhängig davon, ob diese automatisiert oder
in nicht-automatisierten Akten erfolgt. Personenbezogene Daten sind
dabei definiert als alle Informationen, die sich auf eine identifizierte
oder identifizierbare natürliche Person beziehen.
Die DSGVO verfolgt mehrere grundsätzliche Ziele:
• Schutz der Grundrechte natürlicher Personen bei der Verarbeitung ihrer
personenbezogenen Daten sowie die Gewährleistung ihrer Grundfreiheiten.
• Einheitlicher Datenschutz in der EU, sodass sowohl Unternehmen
innerhalb als auch außerhalb der EU, die Daten von EU-Bürgern
verarbeiten, denselben Regeln unterliegen. • Schaffung klarer Pflichten
für Datenverantwortliche und -verarbeiter zur rechtskonformen
Datenverarbeitung.
Die DSGVO enthält dabei zentral die Grundsätze der Datenverarbeitung
(Art. 5 DSGVO), die sicherstellen, dass personenbezogene Daten nur
rechtmäßig, zweckgebunden und in transparentem Umfang verarbeitet
werden. Dazu gehören unter anderem:
• Rechtmäßigkeit, Fairness und Transparenz der Verarbeitung •
Zweckbindung der Datenerhebung • Datenminimierung und Speicherbegrenzung
• Integrität und Vertraulichkeit der Datenverarbeitung
Die Einhaltung dieser Prinzipien ist für jede Organisation
verpflichtend, die personenbezogene Daten verarbeitet -- unabhängig von
ihrer Größe oder Branche. Dies umfasst sowohl technische Aspekte der
Datenverarbeitung als auch die Informationspflichten gegenüber
Betroffenen, wie sie etwa in Datenschutzerklärungen umgesetzt werden
müssen.
\paragraph[Umsetzung der Datenschutzanforderungen auf der
Webseite]{\texorpdfstring{Umsetzung der Datenschutzanforderungen auf der
Webseite\footnote{Bundeskanzleramt Österreich (2024) (siehe
Internet-/Intranetverzeichnis).}}{Umsetzung der Datenschutzanforderungen auf der Webseite}}\label{umsetzung-der-datenschutzanforderungen-auf-der-webseite-ris}
Im Rahmen der Diplomarbeit wurde eine umfassende Analyse der
Datenschutzanforderungen durchgeführt und entsprechende Maßnahmen zur
Umsetzung auf der Webseite implementiert. Dabei wurden insbesondere die
Vorgaben der DSGVO berücksichtigt, um sicherzustellen, dass die Webseite
den geltenden Datenschutzbestimmungen entspricht.
Ein zentrales Element der Umsetzung war die Erstellung einer
gesetzeskonformen Datenschutzerklärung. Diese informiert die Nutzerinnen
und Nutzer über:
• die Art der verarbeiteten personenbezogenen Daten, • den Zweck der
Datenverarbeitung, • die Rechtsgrundlagen der Verarbeitung, • die
Speicherdauer der Daten, • die Weitergabe von Daten an Dritte • sowie
über die Rechte der Betroffenen gemäß Art. 15--22 DSGVO.
Die Datenschutzerklärung wird auf der Webseite gut sichtbar eingebunden
und ist für alle Nutzerinnen und Nutzer jederzeit zugänglich.
Neben der Informationspflicht wurden auch technische und
organisatorische Maßnahmen umgesetzt, um die Sicherheit der
verarbeiteten Daten zu gewährleisten. Dazu gehören unter anderem:
• Verschlüsselte Übertragung von Daten mittels HTTPS •
Passwortgeschützter Zugriff auf sensible Daten • Regelmäßige
Sicherheitsupdates der eingesetzten Software • Minimierung der
Datenerhebung auf das Notwendige: Es werden nur die Daten erhoben, die
für die Funktion der Webseite unbedingt erforderlich sind. (Grundprinzip
der Datenminimierung nach Art. 5 Abs. 1 lit. c DSGVO)
\paragraph[Schutz personenbezogener Daten der
Benutzer]{\texorpdfstring{Schutz personenbezogener Daten der
Benutzer\footnote{Datenschutzbehörde (2024) (siehe
Internet-/Intranetverzeichnis).}}{Schutz personenbezogener Daten der Benutzer}}\label{schutz-personenbezogener-daten-der-benutzer-dsb}
Der Schutz personenbezogener Daten der Benutzer stellt einen zentralen
Bestandteil der Webseite des Absolventenvereins dar. Sämtliche
Datenverarbeitungen erfolgen unter Berücksichtigung der Vorgaben der
Datenschutz-Grundverordnung (DSGVO) sowie des österreichischen
Datenschutzgesetzes (DSG).
Gemäß Art. 5 Abs. 1 lit. c DSGVO gilt der Grundsatz der
Datenminimierung, wonach nur jene personenbezogenen Daten erhoben werden
dürfen, die für den jeweiligen Zweck erforderlich sind. Auf der Webseite
werden daher ausschließlich solche Daten verarbeitet, die für die
Verwaltung der Mitgliedschaft, die Organisation von Veranstaltungen
sowie die Kommunikation mit Absolventinnen und Absolventen notwendig
sind.
Darüber hinaus wird der Grundsatz der Zweckbindung gemäß Art. 5 Abs. 1
lit. b DSGVO eingehalten. Die erhobenen Daten werden ausschließlich für
die in der Datenschutzerklärung definierten Zwecke verarbeitet,
insbesondere zur Organisation der Vereinstätigkeit und zur Durchführung
von Absolvententreffen.
Eine darüber hinausgehende Weitergabe an Dritte erfolgt grundsätzlich
nicht, außer wenn dies zur Erfüllung der Vereinszwecke erforderlich ist
oder eine gesetzliche Verpflichtung besteht.
Zur Gewährleistung der Integrität und Vertraulichkeit gemäß Art. 5 Abs.
1 lit. f DSGVO werden geeignete technische und organisatorische
Maßnahmen eingesetzt. Dazu zählen insbesondere der Betrieb der Webseite
auf einem Server innerhalb Österreichs, der Abschluss von
Auftragsverarbeitungsverträgen gemäß Art. 28 DSGVO mit externen
Dienstleistern sowie Maßnahmen zum Schutz vor unbefugtem Zugriff,
Verlust oder Missbrauch von Daten.
Zusätzlich werden die Betroffenenrechte gemäß Art. 15--22 DSGVO
ausdrücklich gewährleistet. Benutzerinnen und Benutzer haben das Recht
auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung,
Datenübertragbarkeit sowie Widerspruch gegen die Verarbeitung.
Darüber hinaus besteht das Recht, sich bei der österreichischen
Datenschutzbehörde zu beschweren, sofern eine rechtswidrige Verarbeitung
vermutet wird.
Durch die Kombination aus klar definierten Rechtsgrundlagen,
transparenten Informationspflichten und technischen Schutzmaßnahmen wird
ein hohes Datenschutzniveau gewährleistet und der verantwortungsvolle
Umgang mit personenbezogenen Daten sichergestellt.
\subsubsection{Backup \& Restore}\label{backup-restore}
\paragraph[Notwendigkeit von Datensicherung
{[}{]}]{\texorpdfstring{Notwendigkeit von Datensicherung {[}Bundesamt
für Sicherheit in der Informationstechnik
(BSI)\footnote{(2024) (siehe Internet-/Intranetverzeichnis).}{]}\footnote{BSI
(2023) (siehe Internet-/Intranetverzeichnis).}}{Notwendigkeit von Datensicherung {[}Bundesamt für Sicherheit in der Informationstechnik (BSI){]}}}\label{notwendigkeit-von-datensicherung-bsi_datensicherungbsi_grundschutz}
Die Datensicherung ist ein wesentlicher Bestandteil der Webseite des
Absolventenvereins. Sie dient dazu, die auf der Webseite gespeicherten
Daten vor Verlust oder Beschädigung zu schützen. Digitale Daten bilden
die Grundlage für die Funktion der Webseite und müssen daher regelmäßig
gesichert werden. Ein Verlust oder eine Beschädigung der Daten kann zu
erheblichen Problemen führen.
Mögliche Ursachen für einen Datenverlust sind unter anderem
Hardwaredefekte, Softwarefehler, menschliches Versagen oder
Cyberangriffe. Ohne geeignete Sicherheitsmaßnahmen besteht das Risiko,
dass Daten unwiederbringlich verloren gehen oder nur mit erheblichen
Kosten wiederhergestellt werden können.
Neben der technischen besteht ebenfalls eine rechtliche und
organisatorische Notwendigkeit der Datensicherung. Gemäß Art. 5 Abs. 1
lit. f DSGVO müssen personenbezogene Daten gesichert werden, um die
Integrität und Vertraulichkeit der Daten zu gewährleisten. Dazu zählt
auch der Schutz vor unbeabsichtigtem Verlust. Eine Backup-Sicherung ist
daher ein notwendiger Schritt, um die Daten vor Verlust zu schützen.
\paragraph{Konzeption des
Backup-Systems}\label{konzeption-des-backup-systems}
Ziel des Backup-Systems war es im Falle eines Datenverlustes oder eines
Systemausfalls die Daten wiederherstellen zu können. Es wurde eine
Strategie entwickelt die es ermöglicht sowohl die Datenbank als auch die
Dateien auf dem Server zu sichern. Der Umfang des Backups besteht aus
zwei zentralen Komponenten:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
Die PostgreSQL-Datenbank
\item
Der Ordner ``oqtane.server'' auf dem Server
Damit wird eine vollständige Sicherung der Webseite ermöglicht, da bei
einer reinen Datenbanksicherung wichtige Anwendungsdateien fehlen
würden.
\end{enumerate}
\begin{figure}
\centering
\includegraphics[width=0.8\linewidth,height=0.7\textheight]{flowchart-backup-system.pdf}
\caption{Ablaufdiagramm des Backup-Systems}
\end{figure}
Alle Backups werden lokal auf dem Server gespeichert. Diese Backups sind
alle zu finden unter: /var/backups Für jedes Backup wird in diesem
Verzeichnis ein Unterordner erstellt, dessen Name einen UTC-Zeitstempel
enthält. Das ermöglicht eine saubere Trennung der Sicherungsstände und
einfache chronologische Zuordnung.
\begin{Shaded}
\begin{Highlighting}[]
\VariableTok{TS}\OperatorTok{=}\StringTok{"}\VariableTok{$(}\FunctionTok{date} \AttributeTok{{-}u}\NormalTok{ +}\StringTok{\textquotesingle{}\%Y\%m\%dT\%H\%M\%SZ\textquotesingle{}}\VariableTok{)}\StringTok{"}
\VariableTok{DEST\_DIR}\OperatorTok{=}\StringTok{"}\VariableTok{$\{BACKUP\_ROOT\}}\StringTok{/}\VariableTok{$\{TS\}}\StringTok{"}
\end{Highlighting}
\end{Shaded}
date -u erzeugt einen Zeitstempel in UTC, wodurch die Benennung
unabhängig von Zeitzonen eindeutig bleibt. Der Zeitstempel wird als
Ordnername verwendet, sodass jedes Backup separat abgelegt wird (z. B.
20260226T023000Z).
Die Sicherung der PostgreSQL-Datenbank erfolgt mithilfe des Programms
pg\_dump, welches ein vollständiges Abbild der Datenbank erzeugt:
\begin{Shaded}
\begin{Highlighting}[]
\ExtensionTok{pg\_dump} \AttributeTok{{-}h} \StringTok{"}\VariableTok{$PGHOST}\StringTok{"} \AttributeTok{{-}p} \StringTok{"}\VariableTok{$PGPORT}\StringTok{"} \AttributeTok{{-}U} \StringTok{"}\VariableTok{$PGUSER}\StringTok{"} \AttributeTok{{-}d} \StringTok{"}\VariableTok{$PGDATABASE}\StringTok{"} \AttributeTok{{-}Fc} \DataTypeTok{\textbackslash{}}
\AttributeTok{{-}f} \StringTok{"}\VariableTok{$\{DEST\_DIR\}}\StringTok{/db\_}\VariableTok{$\{PGDATABASE\}}\StringTok{.dump"}
\end{Highlighting}
\end{Shaded}
Hierbei werden Host, Port, Benutzer und Datenbankname über Variablen
definiert, wodurch das Skript flexibel einsetzbar bleibt. Der Parameter
-Fc erstellt das Backup im sogenannten Custom-Format. Dieses Format
bietet den Vorteil, dass beim Wiederherstellen einzelne Tabellen oder
Datenbankobjekte selektiv importiert werden können. Zudem ist es
komprimiert und effizienter als ein reines SQL-Textdump. Die Ausgabe
wird direkt in eine Datei im zuvor erstellten Backup-Ordner geschrieben.
Die Authentifizierung erfolgt über eine .pgpass-Datei, wodurch das
Backup automatisiert und ohne manuelle Passworteingabe ausgeführt werden
kann.
Neben der Datenbank wird auch der gesamte Anwendungsordner gesichert.
Dies geschieht mithilfe des tar-Programms, das Dateien zu einem
komprimierten Archiv zusammenfasst:
\begin{Shaded}
\begin{Highlighting}[]
\FunctionTok{tar} \AttributeTok{{-}cvpzf} \StringTok{"}\VariableTok{$\{DEST\_DIR\}}\StringTok{/oqtane\_files.tar.gz"} \DataTypeTok{\textbackslash{}}
\AttributeTok{{-}{-}absolute{-}names} \StringTok{"}\VariableTok{$OQTANE\_DIR}\StringTok{"}
\end{Highlighting}
\end{Shaded}
Das Programm tar steht für Tape Archive und wurde ursprünglich
entwickelt, um mehrere Dateien auf Magnetbändern zu sichern. Heute wird
es unter Linux und Unix-Systemen verwendet, um mehrere Dateien und
Verzeichnisse zu einem einzigen Archiv zusammenzufassen. Dabei kann das
Archiv optional komprimiert werden.
Der Parameter -c erstellt ein neues Archiv, -v gibt die verarbeiteten
Dateien aus, -p erhält die ursprünglichen Dateiberechtigungen und -z
aktiviert die gzip-Komprimierung. Die Komprimierung reduziert den
Speicherbedarf der Sicherung erheblich. Besonders relevant ist die
Option --absolute-names, da dadurch die vollständigen Pfadangaben im
Archiv gespeichert werden. Dies erleichtert eine spätere exakte
Wiederherstellung der ursprünglichen Verzeichnisstruktur. Durch die
Kombination aus Datenbank-Dump und komprimiertem Dateisystem-Archiv
entsteht ein vollständiges Vollbackup des Systems.
Ein Cronjob ist eine zeitgesteuerte Aufgabe unter Linux-Systemen, die
automatisch zu einem festgelegten Zeitpunkt ausgeführt wird. Er basiert
auf dem Hintergrunddienst cron, der regelmäßig überprüft, ob geplante
Befehle gestartet werden müssen.
Im vorliegenden Projekt wird ein Cronjob verwendet, um das Backup-Skript
täglich automatisiert auszuführen. Der entsprechende Eintrag lautet:
\begin{Shaded}
\begin{Highlighting}[]
\ExtensionTok{30}\NormalTok{ 2 }\PreprocessorTok{*} \PreprocessorTok{*} \PreprocessorTok{*}\NormalTok{ /home/florian/backup{-}script.sh }\OperatorTok{\textgreater{}\textgreater{}}\NormalTok{ /home/florian/backup.log }\DecValTok{2}\OperatorTok{\textgreater{}\&}\DecValTok{1}
\end{Highlighting}
\end{Shaded}
Die ersten fünf Felder definieren den Ausführungszeitpunkt. In diesem
Fall bedeutet die Konfiguration, dass das Skript täglich um 02:30 Uhr
gestartet wird. Die nächtliche Ausführung reduziert die Systembelastung
während der regulären Nutzung.
Der hintere Teil des Befehls (\textgreater\textgreater{}
/home/florian/backup.log 2\textgreater\&1) sorgt dafür, dass sowohl
normale Ausgaben als auch Fehlermeldungen in einer Logdatei gespeichert
werden. Dadurch kann überprüft werden, ob das Backup erfolgreich
durchgeführt wurde.
Durch den Einsatz eines Cronjobs wird eine regelmäßige und zuverlässige
Datensicherung ohne manuelles Eingreifen gewährleistet.
Ein weiterer Bestandteil des Backup-Systems ist ein automatischer
Mechanismus zur Verwaltung alter Sicherungen. Da Backups langfristig
Speicherplatz beanspruchen können, wurde eine Funktion implementiert,
die nur eine definierte Anzahl an Sicherungen beibehält. In der
Konfiguration des Skripts wird festgelegt, dass maximal 30 Backups
gespeichert werden.
\begin{Shaded}
\begin{Highlighting}[]
\VariableTok{RETAIN}\OperatorTok{=}\NormalTok{30}
\end{Highlighting}
\end{Shaded}
Während der Ausführung überprüft das Skript, wie viele Backup-Ordner im
Backup-Verzeichnis vorhanden sind. Die Ordner werden anhand ihres
Zeitstempels sortiert, wodurch die chronologische Reihenfolge der
Sicherungen eindeutig bestimmt werden kann. Sollte die Anzahl der
vorhandenen Backups den definierten Wert überschreiten, werden
automatisch die ältesten Sicherungen gelöscht.
\begin{Shaded}
\begin{Highlighting}[]
\ControlFlowTok{if} \KeywordTok{((} \VariableTok{$\{}\OperatorTok{\#}\VariableTok{backups}\OperatorTok{[@]}\VariableTok{\}} \OperatorTok{\textgreater{}} \VariableTok{RETAIN} \KeywordTok{));} \ControlFlowTok{then}
\ControlFlowTok{for}\NormalTok{ old }\KeywordTok{in} \StringTok{"}\VariableTok{$\{backups}\OperatorTok{[@]:}\NormalTok{RETAIN}\VariableTok{\}}\StringTok{"}\KeywordTok{;} \ControlFlowTok{do}
\FunctionTok{rm} \AttributeTok{{-}rf} \AttributeTok{{-}{-}} \StringTok{"}\VariableTok{$old}\StringTok{"}
\ControlFlowTok{done}
\ControlFlowTok{fi}
\end{Highlighting}
\end{Shaded}
Durch diesen Rotationsmechanismus wird verhindert, dass sich im Laufe
der Zeit eine unbegrenzte Anzahl von Backups ansammelt und dadurch der
verfügbare Speicherplatz auf dem Server erschöpft wird. Gleichzeitig
bleibt eine ausreichende Anzahl an Sicherungsständen erhalten, um im
Fehlerfall auf frühere Systemzustände zurückgreifen zu können.
Die gewählte Anzahl von 30 Sicherungen ermöglicht es, bei täglicher
Ausführung des Backup-Skripts ungefähr einen Monat an
Wiederherstellungspunkten vorzuhalten. Dadurch wird ein sinnvoller
Kompromiss zwischen Datensicherheit und effizienter Speicherverwaltung
erreicht.
\paragraph{Implementierung der
Backup-Skripte}\label{implementierung-der-backup-skripte}
Die Implementierung des Backup-Systems erfolgte mittels eines
Bash-Skripts, das auf dem Linux-Server ausgeführt wird. Bash eignet sich
besonders für administrative Aufgaben, da sie direkten Zugriff auf
Systembefehle, Dateien und Prozesse bietet. Das Skript wurde so
aufgebaut, dass es robust, automatisierbar und nachvollziehbar arbeitet.
Zu Beginn des Skripts wird folgende Konfiguration gesetzt:
\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{\#\#!/usr/bin/env bash}
\BuiltInTok{set} \AttributeTok{{-}euo}\NormalTok{ pipefail}
\end{Highlighting}
\end{Shaded}
Die erste Zeile definiert den Interpreter, mit dem das Skript ausgeführt
wird. Dadurch wird sichergestellt, dass unabhängig von der
Systemkonfiguration die korrekte Bash-Version verwendet wird.
Die Anweisung set -euo pipefail erhöht die Sicherheit und Stabilität des
Skripts:
\texttt{-e} beendet das Skript sofort, wenn ein Befehl fehlschlägt •
\texttt{-u} verhindert die Verwendung nicht definierter Variablen •
\texttt{-o\ pipefail} sorgt dafür, dass auch Fehler innerhalb von
Befehls-Pipelines erkannt werden
Diese Einstellungen verhindern, dass das Backup bei Fehlern unbemerkt
unvollständig ausgeführt wird.
Im nächsten Schritt werden zentrale Konfigurationsvariablen definiert:
\begin{Shaded}
\begin{Highlighting}[]
\VariableTok{BACKUP\_ROOT}\OperatorTok{=}\StringTok{"/var/backups/fullserver"}
\VariableTok{RETAIN}\OperatorTok{=}\NormalTok{30}
\VariableTok{PGHOST}\OperatorTok{=}\StringTok{"localhost"}
\VariableTok{PGPORT}\OperatorTok{=}\StringTok{"5432"}
\VariableTok{PGUSER}\OperatorTok{=}\StringTok{"florian"}
\VariableTok{PGDATABASE}\OperatorTok{=}\StringTok{"oqtane"}
\end{Highlighting}
\end{Shaded}
Durch die Verwendung von Variablen bleibt das Skript flexibel und
wartbar. Änderungen, beispielsweise am Speicherort oder an der
Datenbank, können an einer zentralen Stelle vorgenommen werden, ohne den
restlichen Code anzupassen.
Die Sicherung der PostgreSQL-Datenbank erfolgt mittels pg\_dump im
Custom-Format. Dieses Format ist besonders geeignet, da es eine flexible
Wiederherstellung ermöglicht. Die Ausgabe wird direkt in den jeweiligen
Backup-Ordner geschrieben. Die Authentifizierung erfolgt über eine
.pgpass-Datei, wodurch das Backup ohne interaktive Passworteingabe
automatisiert durchgeführt werden kann.
Anschließend wird der gesamte Anwendungsordner mit tar archiviert und
komprimiert. Dabei werden Dateirechte und Verzeichnisstrukturen
vollständig erhalten. Dies ist insbesondere bei Webanwendungen wichtig,
da falsche Berechtigungen nach einer Wiederherstellung zu
Funktionsstörungen führen könnten.
Ein weiterer wichtiger Bestandteil der Implementierung ist das
integrierte Rotationsprinzip. Das Skript überprüft automatisch, wie
viele Backup-Ordner vorhanden sind. Falls die definierte Grenze von 30
Sicherungen überschritten wird, werden die ältesten Backups gelöscht.
Dadurch wird verhindert, dass der verfügbare Speicherplatz durch alte
Sicherungen dauerhaft belegt wird. Die Sortierung erfolgt anhand der
Zeitstempel im Ordnernamen, wodurch die chronologische Reihenfolge
eindeutig bestimmbar ist.
Zusätzlich gibt das Skript Statusmeldungen mit Zeitangaben aus:
\begin{Shaded}
\begin{Highlighting}[]
\BuiltInTok{echo} \StringTok{"[}\VariableTok{$(}\FunctionTok{date} \AttributeTok{{-}Iseconds}\VariableTok{)}\StringTok{] Backup{-}Job abgeschlossen."}
\end{Highlighting}
\end{Shaded}
Diese Meldungen werden bei automatischer Ausführung über den Cronjob in
eine Logdatei geschrieben. Dadurch kann jederzeit überprüft werden, ob
das Backup erfolgreich abgeschlossen wurde oder ob Fehler aufgetreten
sind.
Die Implementierung zeichnet sich durch folgende Eigenschaften aus:
• vollständige Sicherung von Datenbank und Anwendungsdateien •
automatische tägliche Ausführung • integrierte Fehlerbehandlung •
strukturierte Archivierung mit Zeitstempel • automatische
Speicherverwaltung durch Rotationsmechanismus
Durch diese Umsetzung wurde ein zuverlässiges und wartbares
Backup-System geschaffen, das den kontinuierlichen Betrieb der
Webanwendung unterstützt und im Fehlerfall eine schnelle
Wiederherstellung ermöglicht.
\paragraph{Wiederherstellung von Daten mittels
Restore-Skripten}\label{wiederherstellung-von-daten-mittels-restore-skripten}
Neben der Datensicherung stellt die strukturierte Wiederherstellung der
Daten einen zentralen Bestandteil des Backup-Systems dar. Zu diesem
Zweck wurde ein eigenes Bash-Skript implementiert, das sowohl die
PostgreSQL-Datenbank als auch die Anwendungsdateien aus einem gewählten
Backup-Ordner wiederherstellt (Restore-Skript). Ziel war es, einen klar
definierten und kontrollierten Prozess zu schaffen, der im Fehlerfall
eine vollständige Rücksetzung des Systems ermöglicht.
Das Skript beginnt -- analog zum Backup-Skript -- mit einer sicheren
Ausführungskonfiguration:
\begin{Shaded}
\begin{Highlighting}[]
\CommentTok{\#\#!/usr/bin/env bash}
\BuiltInTok{set} \AttributeTok{{-}euo}\NormalTok{ pipefail}
\end{Highlighting}
\end{Shaded}
Auch hier sorgt set -euo pipefail dafür, dass das Skript bei Fehlern
sofort abbricht, keine undefinierten Variablen verwendet werden und
Fehler innerhalb von Befehls-Pipelines korrekt erkannt werden. Dadurch
wird verhindert, dass ein unvollständiger oder fehlerhafter Restore
durchgeführt wird.
Zu Beginn wird festgelegt, welcher Backup-Ordner verwendet werden soll.
Wird beim Start ein Argument übergeben, wird dieses als
Backup-Verzeichnis interpretiert. Andernfalls wird automatisch das
neueste Backup ausgewählt:
\begin{Shaded}
\begin{Highlighting}[]
\ControlFlowTok{if} \KeywordTok{[[} \VariableTok{$\#} \OtherTok{{-}ge}\NormalTok{ 1 }\KeywordTok{]];} \ControlFlowTok{then}
\VariableTok{BACKUP\_SUBDIR}\OperatorTok{=}\StringTok{"}\VariableTok{$1}\StringTok{"}
\ControlFlowTok{else}
\VariableTok{BACKUP\_SUBDIR}\OperatorTok{=}\StringTok{"}\VariableTok{$(}\FunctionTok{ls} \AttributeTok{{-}1} \StringTok{"}\VariableTok{$\{BACKUP\_ROOT\}}\StringTok{"} \KeywordTok{|} \FunctionTok{sort} \KeywordTok{|} \FunctionTok{tail} \AttributeTok{{-}n}\NormalTok{ 1}\VariableTok{)}\StringTok{"}
\ControlFlowTok{fi}
\end{Highlighting}
\end{Shaded}
Diese Logik ermöglicht eine flexible Nutzung des Skripts.
Administratoren können gezielt einen bestimmten Sicherungsstand
auswählen oder standardmäßig den aktuellsten verwenden.
Vor Beginn der Wiederherstellung erfolgt eine deutliche
Sicherheitswarnung:
\begin{Shaded}
\begin{Highlighting}[]
\BuiltInTok{read} \AttributeTok{{-}rp} \StringTok{"Fortfahren? (ja/nein): "} \VariableTok{answer}
\ControlFlowTok{if} \KeywordTok{[[} \StringTok{"}\VariableTok{$answer}\StringTok{"} \OtherTok{!=} \StringTok{"ja"} \KeywordTok{]];} \ControlFlowTok{then}
\BuiltInTok{exit}\NormalTok{ 0}
\ControlFlowTok{fi}
\end{Highlighting}
\end{Shaded}
Diese Abfrage dient als Schutzmechanismus, da der Restore-Prozess
bestehende Daten überschreibt. Erst nach expliziter Bestätigung wird die
Wiederherstellung gestartet.
Die Wiederherstellung der PostgreSQL-Datenbank erfolgt in mehreren
Schritten. Zunächst wird -- falls vorhanden -- die bestehende Datenbank
gelöscht:
\begin{Shaded}
\begin{Highlighting}[]
\ExtensionTok{dropdb} \AttributeTok{{-}h} \StringTok{"}\VariableTok{$PGHOST}\StringTok{"} \AttributeTok{{-}p} \StringTok{"}\VariableTok{$PGPORT}\StringTok{"} \AttributeTok{{-}U} \StringTok{"}\VariableTok{$PGUSER}\StringTok{"} \StringTok{"}\VariableTok{$PGDATABASE}\StringTok{"}
\end{Highlighting}
\end{Shaded}
Anschließend wird eine neue, leere Datenbank erstellt:
\begin{Shaded}
\begin{Highlighting}[]
\ExtensionTok{createdb} \AttributeTok{{-}h} \StringTok{"}\VariableTok{$PGHOST}\StringTok{"} \AttributeTok{{-}p} \StringTok{"}\VariableTok{$PGPORT}\StringTok{"} \AttributeTok{{-}U} \StringTok{"}\VariableTok{$PGUSER}\StringTok{"} \StringTok{"}\VariableTok{$PGDATABASE}\StringTok{"}
\end{Highlighting}
\end{Shaded}
Danach wird der zuvor gesicherte Dump eingespielt:
\begin{Shaded}
\begin{Highlighting}[]
\ExtensionTok{pg\_restore} \AttributeTok{{-}h} \StringTok{"}\VariableTok{$PGHOST}\StringTok{"} \AttributeTok{{-}p} \StringTok{"}\VariableTok{$PGPORT}\StringTok{"} \AttributeTok{{-}U} \StringTok{"}\VariableTok{$PGUSER}\StringTok{"} \DataTypeTok{\textbackslash{}}
\AttributeTok{{-}d} \StringTok{"}\VariableTok{$PGDATABASE}\StringTok{"} \AttributeTok{{-}c} \StringTok{"}\VariableTok{$DUMP\_FILE}\StringTok{"}
\end{Highlighting}
\end{Shaded}
Das Programm pg\_restore liest das im Custom-Format gespeicherte Backup
ein und stellt sämtliche Tabellen, Indizes und Datenbankobjekte wieder
her. Die Option -c sorgt dafür, dass bestehende Objekte vor dem Import
entfernt werden, wodurch Konflikte vermieden werden.
Neben der Datenbank wird auch der Anwendungsordner wiederhergestellt.
Bevor die neuen Dateien entpackt werden, wird der aktuell vorhandene
Ordner zur Sicherheit umbenannt:
\begin{Shaded}
\begin{Highlighting}[]
\FunctionTok{mv} \StringTok{"}\VariableTok{$OQTANE\_DIR}\StringTok{"} \StringTok{"}\VariableTok{$\{OQTANE\_DIR\}}\StringTok{.old.}\VariableTok{$(}\FunctionTok{date}\NormalTok{ +\%s}\VariableTok{)}\StringTok{"}
\end{Highlighting}
\end{Shaded}
Diese Maßnahme verhindert einen vollständigen Datenverlust, falls beim
Restore ein Problem auftreten sollte. Anschließend wird das Archiv
entpackt:
\begin{Shaded}
\begin{Highlighting}[]
\FunctionTok{tar} \AttributeTok{{-}xvpzf} \StringTok{"}\VariableTok{$FILES\_ARCHIVE}\StringTok{"}
\end{Highlighting}
\end{Shaded}
Der Parameter -x extrahiert das Archiv, -p stellt die ursprünglichen
Dateiberechtigungen wieder her und -z dekomprimiert das gzip-Archiv.
Dadurch wird die komplette Ordnerstruktur in ihrem ursprünglichen
Zustand wiederhergestellt.
Zusammenfassend ermöglicht das Restore-Skript eine vollständige
Rücksetzung der Webanwendung auf einen definierten Sicherungsstand.
Durch die Kombination aus Sicherheitsabfrage, kontrollierter
Datenbank-Neuerstellung und strukturiertem Dateirestore wird ein
zuverlässiger und nachvollziehbarer Wiederherstellungsprozess
gewährleistet. Das System ergänzt somit das Backup-Konzept um eine
praxisnahe und technisch saubere Lösung für den Ernstfall.
\paragraph{Fazit zur Datensicherung}\label{fazit-zur-datensicherung}
Durch die Implementierung eines automatisierten Backup- und
Restore-Systems wurde eine zuverlässige Grundlage für die
Datensicherheit der Webanwendung geschaffen. Das entwickelte System
ermöglicht eine regelmäßige Sicherung sowohl der PostgreSQL-Datenbank
als auch der vollständigen Anwendungsdateien. Durch die automatisierte
Ausführung mittels Cronjob wird sichergestellt, dass die Backups ohne
manuelles Eingreifen täglich erstellt werden.
Zusätzlich sorgt das integrierte Rotationsprinzip dafür, dass ältere
Sicherungen automatisch entfernt werden und der verfügbare Speicherplatz
effizient genutzt wird. Die strukturierte Ablage der Backups mit
Zeitstempeln erleichtert die Auswahl eines bestimmten Sicherungsstandes.
Neben der Datensicherung wurde auch ein Restore-Skript implementiert,
das eine vollständige Wiederherstellung des Systems ermöglicht. Dabei
werden sowohl die Datenbank als auch die Anwendungsdateien aus einem
ausgewählten Backup wiederhergestellt. Sicherheitsmechanismen wie
Bestätigungsabfragen und das temporäre Umbenennen bestehender Daten
reduzieren das Risiko von Datenverlust während des
Wiederherstellungsprozesses.
Insgesamt stellt das entwickelte Backup-System eine robuste und
praxisnahe Lösung dar, die den sicheren Betrieb der Webanwendung
unterstützt und im Fehlerfall eine schnelle Wiederherstellung der Daten
ermöglicht.
\subsubsection{Benutzerverwaltung und Authentifizierung per
LinkedIn}\label{benutzerverwaltung-und-authentifizierung-per-linkedin}
\paragraph{Authentifizierung mittels OAuth
2.0}\label{authentifizierung-mittels-oauth-2.0}
Zur Anmeldung auf der entwickelten Webplattform wurde eine
Authentifizierung über das Business-Netzwerk LinkedIn implementiert.
Dabei kommt das standardisierte Autorisierungsprotokoll OAuth 2.0 zum
Einsatz.\footnote{IETF (2012) (siehe Internet-/Intranetverzeichnis).}
Dieses Verfahren ermöglicht es, Benutzer über externe Identitätsanbieter
zu authentifizieren, ohne dass deren Zugangsdaten direkt an die
Webanwendung übertragen werden müssen.
OAuth 2.0 basiert auf dem Prinzip der delegierten Autorisierung. Dabei
erlaubt ein Benutzer einer Anwendung, bestimmte Informationen seines
Kontos bei einem externen Dienst zu verwenden. Die eigentlichen
Zugangsdaten -- beispielsweise das LinkedIn-Passwort -- verbleiben dabei
ausschließlich beim Identitätsanbieter.
Der grundlegende Ablauf einer OAuth-Authentifizierung ist in Abbildung X
dargestellt. Dabei wird der Benutzer zunächst zur Login-Seite des
externen Anbieters weitergeleitet. Nach erfolgreicher Anmeldung
bestätigt der Benutzer, dass die Anwendung Zugriff auf bestimmte
Profildaten erhalten darf.
\begin{figure}
\centering
\pandocbounded{\includegraphics[keepaspectratio]{./images/04-Florian/oauth-flow.png}}
\caption{Ablauf der OAuth-Authentifizierung (Erstellt mit ChatGPT)}
\end{figure}
Die Abbildung zeigt den Ablauf der OAuth-Authentifizierung. Zunächst
erfolgt die Weiterleitung zur LinkedIn-Anmeldeseite, anschließend wird
ein Autorisierungscode (Authorization Code) an die Webanwendung
zurückgegeben, welcher gegen einen Zugriffstoken (Access Token)
ausgetauscht wird.
Nach der Autorisierung sendet LinkedIn einen solchen Autorisierungscode
an die Webanwendung zurück. Dieser Code wird anschließend vom Server der
Anwendung gegen ein Zugriffstoken ausgetauscht. Mit Hilfe dieses Tokens
kann die Webanwendung anschließend die freigegebenen Benutzerdaten vom
LinkedIn-Server abrufen.
Der Vorteil dieses Verfahrens liegt darin, dass die Webanwendung zu
keinem Zeitpunkt Zugriff auf das Passwort des Benutzers erhält. Dadurch
wird ein höheres Sicherheitsniveau erreicht und gleichzeitig die
Benutzerfreundlichkeit verbessert, da sich Benutzer mit ihrem
bestehenden LinkedIn-Konto anmelden können.
\paragraph{Technische Umsetzung der LinkedIn-Anmeldung in
Oqtane}\label{technische-umsetzung-der-linkedin-anmeldung-in-oqtane}
Die technische Umsetzung der LinkedIn-Authentifizierung erfolgte über
das integrierte External Login System\footnote{Oqtane Foundation (2024a)
(siehe Internet-/Intranetverzeichnis).} des Content-Management-Systems
Oqtane. Dieses System ermöglicht die Integration externer
Identitätsanbieter über standardisierte Protokolle wie OAuth 2.0.
Für die Verbindung mit LinkedIn wurde zunächst eine Entwickleranwendung
im LinkedIn Developer Portal\footnote{Microsoft (2026d) (siehe
Internet-/Intranetverzeichnis).} erstellt. Dabei werden zwei zentrale
Zugangsdaten generiert:
• Client ID • Client Secret
Diese dienen zur Identifikation der Webanwendung gegenüber den
LinkedIn-Servern.
Innerhalb der Oqtane-Konfiguration wurde anschließend ein neuer
OAuth-Provider eingerichtet. Dabei mussten mehrere Parameter definiert
werden, darunter die Autorisierungs- und Token-Endpunkte von LinkedIn.
Beispielsweise wird für die Benutzerautorisierung folgende Adresse
verwendet:
https://www.linkedin.com/oauth/v2/authorization
Nach erfolgreicher Anmeldung stellt LinkedIn über folgenden Endpunkt ein
Zugriffstoken bereit:
https://www.linkedin.com/oauth/v2/accessToken
Zusätzlich wurde eine sogenannte Redirect-URL definiert. Diese URL wird
von LinkedIn verwendet, um den Benutzer nach erfolgreicher
Authentifizierung wieder zurück zur Webanwendung zu leiten.
In der Konfiguration wurde außerdem festgelegt, welche Benutzerdaten von
LinkedIn übernommen werden sollen. Dazu gehören unter anderem:
• Benutzername • E-Mail-Adresse • eindeutige Benutzer-ID
Diese Daten werden von Oqtane als sogenannte Claims verarbeitet und
anschließend dem Benutzerkonto der Plattform zugeordnet.
Darüber hinaus wurde in den Einstellungen aktiviert, dass bei der ersten
Anmeldung automatisch ein neues Benutzerkonto erstellt werden kann.
Dadurch können sich neue Nutzer direkt über ihr LinkedIn-Konto
registrieren.
\paragraph{Ablauf des
Anmeldeprozesses}\label{ablauf-des-anmeldeprozesses}
Der eigentliche Anmeldevorgang erfolgt in mehreren aufeinanderfolgenden
Schritten. Zunächst wählt der Benutzer auf der Login-Seite der
Webplattform die Option zur Anmeldung über LinkedIn aus.
Daraufhin wird der Benutzer zur LinkedIn-Authentifizierungsseite
weitergeleitet. Dort meldet sich der Benutzer mit seinem LinkedIn-Konto
an und bestätigt die Autorisierung der Anwendung.
Nach erfolgreicher Authentifizierung sendet LinkedIn eine Antwort an die
zuvor definierte Redirect-URL der Webanwendung. Diese Antwort enthält
einen Autorisierungscode, der anschließend vom Server der Webanwendung
gegen ein Zugriffstoken ausgetauscht wird.
Wie in Abschnitt 4.3.1 beschrieben, werden die abgerufenen Profildaten
zur Identifikation oder Neuanlage des Benutzerkontos in der lokalen
Datenbank verwendet.
\subsubsection{Implementierung des
Premium-Bereichs}\label{implementierung-des-premium-bereichs}
\paragraph{Ziel und Zweck des
Premium-Bereichs}\label{ziel-und-zweck-des-premium-bereichs}
Der Premium-Bereich wurde mit dem Ziel entwickelt, den Mitgliedern des
SZU Absolventenvereins einen klar abgegrenzten, exklusiven Bereich
innerhalb der VereinsWebseite bereitzustellen. Das Premium-Bereich-Modul
ist eine Erweiterung für das Content-Management-System Oqtane und bildet
das Kernstück des digitalen Mitgliederbereichs. Es ermöglicht
Mitgliedern, sich über das Hochladen eines Ingenieur-Antrags als
PDF-Dokument für eine zeitlich begrenzte Premium-Mitgliedschaft zu
qualifizieren. Premium-Mitglieder erhalten Zugang zu exklusiven
Funktionen wie dem Einsehen genehmigter Anträge anderer Mitglieder sowie
einer Mitgliedersuche mit integrierter Kontaktfunktion.
Der zentrale Zweck besteht darin, einen Anreiz für aktives Engagement im
Verein zu schaffen. Dadurch entsteht ein Kreislauf, in dem qualitativ
hochwertige Anträge die Wissensbasis der Gemeinschaft bereichern und
gleichzeitig den einreichenden Mitgliedern einen konkreten Gegenwert in
Form von Premium-Funktionen bieten. Der Premium-Bereich dient darüber
hinaus als digitales Netzwerk-Instrument. Durch die Kombination aus
Antrags-Einsicht, Mitgliedersuche und Kontaktfunktion wird eine
Plattform geschaffen, die den fachlichen Austausch zwischen Absolventen
fördert und die Vernetzung innerhalb der Gemeinschaft stärkt. Die
zeitliche Begrenzung der Premium-Mitgliedschaft auf 12 Monate sorgt
dafür, dass Mitglieder regelmäßig aktiv bleiben und neue Inhalte
beitragen, um ihren Premium-Status aufrechtzuerhalten.
Die Entwicklung erfolgte als eigenständiges Oqtane-Modul, wodurch eine
nahtlose Integration in die bestehende VereinsWebseite gewährleistet
wird. Das Modul nutzt dabei die vom Framework bereitgestellten
Mechanismen für Authentifizierung, Autorisierung, Datenbankzugriff und
Dateiverwaltung. Die Architektur folgt etablierten Entwurfsmustern wie
dem Repository-Pattern, Dependency Injection und dem
Service-Layer-Muster. Das kumulative Premium-System mit vollständigem
Audit-Trail stellt sicher, dass alle Statusänderungen transparent und
nachvollziehbar sind. Die modulare Struktur erlaubt eine einfache
Erweiterung um zusätzliche Funktionen, ohne die bestehende Codebasis
grundlegend verändern zu müssen.
\begin{figure}
\centering
\includegraphics[width=0.8\linewidth,height=0.7\textheight]{architecture-premium-module.pdf}
\caption{Architektur des Premium-Bereich-Moduls}
\end{figure}
\paragraph{Funktionalität und Features des
Premium-Bereichs}\label{funktionalituxe4t-und-features-des-premium-bereichs}
Der Premium-Bereich umfasst mehrere miteinander verknüpfte Funktionen,
die gemeinsam ein geschlossenes System bilden.
\textbf{Ingenieur-Antrags-Workflow}
Das Kernfeature ist der Ingenieur-Antrags-Workflow. Mitglieder können
über eine dedizierte Seite einen Antrag mit Titel, Kurzbeschreibung und
PDF-Dokument einreichen. Die zentrale Datenstruktur dafür ist die Klasse
\texttt{EngineerApplication}, die in der Tabelle
\texttt{SZUAbsolventenvereinEngineerApplications} gespeichert wird:
\begin{Shaded}
\begin{Highlighting}[]
\OperatorTok{[}\FunctionTok{Table}\OperatorTok{(}\StringTok{"SZUAbsolventenvereinEngineerApplications"}\OperatorTok{)]}
\KeywordTok{public} \KeywordTok{class}\NormalTok{ EngineerApplication }\OperatorTok{:}\NormalTok{ ModelBase}
\OperatorTok{\{}
\OperatorTok{[}\NormalTok{Key}\OperatorTok{]} \KeywordTok{public} \DataTypeTok{int}\NormalTok{ ApplicationId }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \DataTypeTok{int}\NormalTok{ UserId }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \DataTypeTok{int}\NormalTok{ ModuleId }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \DataTypeTok{int}\NormalTok{ FileId }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\OperatorTok{[}\FunctionTok{StringLength}\OperatorTok{(}\DecValTok{256}\OperatorTok{)]} \KeywordTok{public} \DataTypeTok{string}\NormalTok{ Title }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \DataTypeTok{string}\NormalTok{ ShortDescription }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\OperatorTok{[}\FunctionTok{StringLength}\OperatorTok{(}\DecValTok{50}\OperatorTok{)]} \KeywordTok{public} \DataTypeTok{string}\NormalTok{ Status }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public}\NormalTok{ DateTime}\OperatorTok{?}\NormalTok{ SubmittedOn }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public}\NormalTok{ DateTime}\OperatorTok{?}\NormalTok{ ApprovedOn }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Die Klasse erbt von \texttt{ModelBase}, wodurch automatisch die
Audit-Felder \texttt{CreatedBy}, \texttt{CreatedOn}, \texttt{ModifiedBy}
und \texttt{ModifiedOn} zur Verfügung stehen. Das Feld \texttt{Status}
bildet den aktuellen Workflow-Schritt ab und kann die Werte „Draft'',
„Submitted'', „Approved'' oder „Rejected'' annehmen. Die hochgeladene
PDF-Datei wird nicht direkt gespeichert, sondern über eine
\texttt{FileId} im Oqtane-Dateisystem referenziert. Das nachfolgende
Klassendiagramm veranschaulicht diese Struktur und zeigt, wie
Basis-Metadaten durch die Vorfahrenklasse abgedeckt werden, während
antragsbezogene Daten in der Hauptklasse gekapselt bleiben.
\begin{figure}
\centering
\pandocbounded{\includegraphics[keepaspectratio]{classdiagram-engineer-application.pdf}}
\caption{Klassendiagramm der EngineerApplication-Entität}
\end{figure}
Der Lebenszyklus eines Antrags durchläuft mehrere Phasen. Der Benutzer
beginnt auf der Antragsseite \texttt{Apply.razor}, wo ein Formular mit
Feldern für Titel, Kurzbeschreibung und einer Dateiauswahl über den
integrierten Oqtane FileManager bereitgestellt wird. Der FileManager
stellt eine benutzerfreundliche Upload-Oberfläche mit
Fortschrittsanzeige bereit und akzeptiert ausschließlich PDF-Dateien.
Die hochgeladene Datei wird dabei im Oqtane-Dateisystem abgelegt und das
Modul speichert lediglich die resultierende Datei-ID in der
Antragstabelle.
Beim Absenden des Formulars erstellt die clientseitige Methode
\texttt{SubmitApplication} ein neues \texttt{EngineerApplication}-Objekt
und befüllt es mit den Formulardaten. Der Status wird dabei automatisch
auf „Published'' gesetzt, zusammen mit den Zeitstempeln für Einreichung
und Genehmigung. Die Methode unterscheidet anhand der
\texttt{ApplicationId}, ob ein neuer Antrag über den Service
\texttt{AddApplicationAsync} erstellt oder ein bestehender über
\texttt{UpdateApplicationAsync} aktualisiert werden soll. Falls keine
Datei ausgewählt wurde, wird der Vorgang mit einer entsprechenden
Fehlermeldung abgebrochen. Die Kommunikation mit dem Server erfolgt über
den injizierten \texttt{ApplicationService}, der als HTTP-Client die
Anfragen an den REST-Controller weiterleitet. Wie die nachfolgende
Abbildung zeigt, führt die klare Gestaltung der Benutzeroberfläche den
Benutzer im Browser intuitiv durch den Einreichungsprozess und stellt
auch den Bearbeitungsstatus des Antrags übersichtlich dar.
\begin{figure}
\centering
\pandocbounded{\includegraphics[keepaspectratio]{./images/04-Florian/ingenieur-antrag-status.png}}
\caption{Formular zum Einreichen eines Ingenieur-Antrags}
\end{figure}
Serverseitig nimmt die Methode \texttt{AddApplicationAsync} im
\texttt{ServerEngineerApplicationService} den Antrag entgegen. Dort wird
zunächst geprüft, ob der Benutzer authentifiziert ist und die
View-Berechtigung für das Modul besitzt. Die User-ID wird aus dem
HTTP-Kontext extrahiert und dem Antrag zugewiesen, um sicherzustellen,
dass kein Benutzer Anträge im Namen anderer erstellen kann. Anschließend
wird der Antrag über das Repository in der Datenbank persistiert.
Ein Administrator kann den Antrag entweder genehmigen oder ablehnen. Bei
einer Genehmigung wird der Status auf „Approved'' gesetzt und
automatisch über den \texttt{PremiumService} eine 12-monatige
Premium-Mitgliedschaft vergeben. Bei einer Ablehnung wechselt der Status
auf „Rejected'' und der Benutzer hat die Möglichkeit, einen
überarbeiteten Antrag erneut einzureichen.
\textbf{Anzeige genehmigter Anträge}
Die Antrags-Listenansicht bildet das zweite zentrale Feature. Wie in der
nachfolgenden Abbildung zu sehen ist, sehen Premium-Mitglieder alle
genehmigten und veröffentlichten Anträge in einer benutzerfreundlichen
Kartenansicht und können Titel, Autor sowie Kurzbeschreibung auf einen
Blick erfassen. Jede Karte enthält das Veröffentlichungsdatum und
integrierte Schaltflächen, über welche das zugehörige PDF-Dokument bei
Bedarf direkt im Browser via eingebettetem iFrame betrachtet oder
heruntergeladen werden kann.
\begin{figure}
\centering
\pandocbounded{\includegraphics[keepaspectratio]{./images/04-Florian/ingenieur-antraege-liste.png}}
\caption{Ansicht der genehmigten Ingenieur-Anträge}
\end{figure}
Die serverseitige Methode \texttt{GetApplicationsAsync} steuert dabei
die rollenbasierte Zugriffslogik und entscheidet, welche Anträge ein
Benutzer sehen darf:
\begin{Shaded}
\begin{Highlighting}[]
\KeywordTok{public}\NormalTok{ Task}\OperatorTok{\textless{}}\NormalTok{List}\OperatorTok{\textless{}}\NormalTok{EngineerApplication}\OperatorTok{\textgreater{}\textgreater{}} \FunctionTok{GetApplicationsAsync}\OperatorTok{(}\DataTypeTok{int}\NormalTok{ ModuleId}\OperatorTok{)}
\OperatorTok{\{}
\DataTypeTok{var}\NormalTok{ user }\OperatorTok{=}\NormalTok{ \_accessor}\OperatorTok{.}\FunctionTok{HttpContext}\OperatorTok{.}\FunctionTok{User}\OperatorTok{;}
\KeywordTok{if} \OperatorTok{(}\NormalTok{\_userPermissions}\OperatorTok{.}\FunctionTok{IsAuthorized}\OperatorTok{(}\NormalTok{user}\OperatorTok{,}\NormalTok{ \_alias}\OperatorTok{.}\FunctionTok{SiteId}\OperatorTok{,}\NormalTok{ EntityNames}\OperatorTok{.}\FunctionTok{Module}\OperatorTok{,}\NormalTok{ ModuleId}\OperatorTok{,}\NormalTok{ PermissionNames}\OperatorTok{.}\FunctionTok{Edit}\OperatorTok{))}
\OperatorTok{\{}
\KeywordTok{return}\NormalTok{ Task}\OperatorTok{.}\FunctionTok{FromResult}\OperatorTok{(}\NormalTok{\_repository}\OperatorTok{.}\FunctionTok{GetEngineerApplications}\OperatorTok{(}\NormalTok{ModuleId}\OperatorTok{).}\FunctionTok{ToList}\OperatorTok{());}
\OperatorTok{\}}
\DataTypeTok{var}\NormalTok{ userId }\OperatorTok{=}\NormalTok{ \_accessor}\OperatorTok{.}\FunctionTok{HttpContext}\OperatorTok{.}\FunctionTok{GetUserId}\OperatorTok{();}
\DataTypeTok{var}\NormalTok{ results }\OperatorTok{=} \KeywordTok{new}\NormalTok{ List}\OperatorTok{\textless{}}\NormalTok{EngineerApplication}\OperatorTok{\textgreater{}();}
\KeywordTok{if} \OperatorTok{(}\NormalTok{userId }\OperatorTok{!=} \OperatorTok{{-}}\DecValTok{1}\OperatorTok{)}
\OperatorTok{\{}
\DataTypeTok{var}\NormalTok{ ownApps }\OperatorTok{=}\NormalTok{ \_repository}\OperatorTok{.}\FunctionTok{GetEngineerApplications}\OperatorTok{(}\NormalTok{ModuleId}\OperatorTok{)}
\OperatorTok{.}\FunctionTok{Where}\OperatorTok{(}\NormalTok{a }\OperatorTok{=\textgreater{}}\NormalTok{ a}\OperatorTok{.}\FunctionTok{UserId} \OperatorTok{==}\NormalTok{ userId}\OperatorTok{).}\FunctionTok{ToList}\OperatorTok{();}
\NormalTok{ results}\OperatorTok{.}\FunctionTok{AddRange}\OperatorTok{(}\NormalTok{ownApps}\OperatorTok{);}
\OperatorTok{\}}
\KeywordTok{if} \OperatorTok{(}\FunctionTok{IsUserPremium}\OperatorTok{(}\NormalTok{user}\OperatorTok{))}
\OperatorTok{\{}
\DataTypeTok{var}\NormalTok{ approved }\OperatorTok{=}\NormalTok{ \_repository}\OperatorTok{.}\FunctionTok{GetEngineerApplications}\OperatorTok{(}\NormalTok{ModuleId}\OperatorTok{,} \StringTok{"Approved"}\OperatorTok{);}
\DataTypeTok{var}\NormalTok{ published }\OperatorTok{=}\NormalTok{ \_repository}\OperatorTok{.}\FunctionTok{GetEngineerApplications}\OperatorTok{(}\NormalTok{ModuleId}\OperatorTok{,} \StringTok{"Published"}\OperatorTok{);}
\NormalTok{ results}\OperatorTok{.}\FunctionTok{AddRange}\OperatorTok{(}\NormalTok{approved}\OperatorTok{.}\FunctionTok{Union}\OperatorTok{(}\NormalTok{published}\OperatorTok{));}
\NormalTok{ results }\OperatorTok{=}\NormalTok{ results}\OperatorTok{.}\FunctionTok{GroupBy}\OperatorTok{(}\NormalTok{a }\OperatorTok{=\textgreater{}}\NormalTok{ a}\OperatorTok{.}\FunctionTok{ApplicationId}\OperatorTok{).}\FunctionTok{Select}\OperatorTok{(}\NormalTok{g }\OperatorTok{=\textgreater{}}\NormalTok{ g}\OperatorTok{.}\FunctionTok{First}\OperatorTok{()).}\FunctionTok{ToList}\OperatorTok{();}
\OperatorTok{\}}
\KeywordTok{return}\NormalTok{ Task}\OperatorTok{.}\FunctionTok{FromResult}\OperatorTok{(}\NormalTok{results}\OperatorTok{);}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Administratoren erhalten alle Anträge zurück. Für andere Benutzer werden
zunächst die eigenen Anträge geladen. Wenn der Benutzer Premium-Status
hat, werden zusätzlich alle genehmigten und veröffentlichten Anträge
anderer Benutzer ergänzt. Durch \texttt{GroupBy} werden Duplikate
entfernt, die entstehen können, wenn ein eigener Antrag gleichzeitig den
Status „Approved'' trägt.
\textbf{Mitgliedersuche und Kontaktfunktion}
Die in der nachfolgenden Abbildung gezeigte Mitgliedersuche ermöglicht
Premium-Mitgliedern das gezielte Auffinden anderer registrierter
Benutzer. Die clientseitige Blazor-Komponente \texttt{UserSearch.razor}
stellt ein Eingabefeld bereit, über das der Benutzer seinen Suchbegriff
eingibt. Aus der dann resultierenden Listenansicht heraus ermöglicht
eine Kontaktfunktion, Nachrichten sicher an die gefundenen Personen zu
senden, ohne deren direkte E-Mail-Adresse offenzulegen. Bei jeder Suche
wird der eingegebene Text an den \texttt{ServerUserContactService} auf
dem Server übermittelt, der die eigentliche Suchlogik implementiert.
\begin{figure}
\centering
\pandocbounded{\includegraphics[keepaspectratio]{./images/04-Florian/premium-mitglieder-suche.png}}
\caption{Mitgliedersuche und Kontaktfunktion für Premium-Mitglieder}
\end{figure}
Die serverseitige Methode \texttt{SearchUsersAsync} prüft zunächst, ob
der Suchbegriff mindestens drei Zeichen umfasst, um übermäßig breite
Abfragen zu vermeiden, und ob der anfragende Benutzer authentifiziert
ist. Anschließend durchsucht sie die Benutzerdatenbank sowohl nach
Anzeigenamen als auch nach Benutzernamen, wobei die Suche
groß-/kleinschreibungsunabhängig erfolgt. Die Ergebnisse werden auf
maximal 20 Treffer begrenzt.
Besonders wichtig ist die Datenschutzfilterung: Anstatt die
vollständigen \texttt{User}-Objekte aus der Datenbank zurückzugeben,
erstellt die Methode neue, reduzierte Objekte, die nur die Felder
\texttt{UserId}, \texttt{Username}, \texttt{DisplayName} und
\texttt{PhotoFileId} enthalten. E-Mail-Adressen, Passwort-Hashes und
andere sensible Daten werden dadurch serverseitig herausgefiltert, bevor
die Ergebnisse an den Client gesendet werden. Dieser Ansatz folgt dem
Prinzip der Datenminimierung --- es werden nur die Informationen
übertragen, die für die Anzeige der Suchergebnisse tatsächlich benötigt
werden.
Die Kontaktfunktion ermöglicht es Premium-Mitgliedern, aus den
Suchergebnissen heraus direkt eine Nachricht an den gefundenen Benutzer
zu senden. Der Nachrichtenversand erfolgt über das
Oqtane-Benachrichtigungssystem. Dabei werden pro Nachricht zwei separate
Benachrichtigungen erstellt: Die erste Benachrichtigung hat ein leeres
\texttt{ToEmail}-Feld und wird daher ausschließlich als In-App-Nachricht
zugestellt, die der Empfänger beim nächsten Login in seinem
Benachrichtigungsbereich sieht. Die zweite Benachrichtigung enthält die
E-Mail-Adresse des Empfängers und löst zusätzlich eine E-Mail-Zustellung
aus. Durch diesen dualen Mechanismus wird sichergestellt, dass der
Empfänger sowohl innerhalb als auch außerhalb der Plattform erreicht
wird, ohne dass der Absender die E-Mail-Adresse des Empfängers jemals zu
sehen bekommt.
Ergänzend steht eine Meldefunktion zur Verfügung, über die
Premium-Mitglieder Anträge mit unangemessenen oder fehlerhaften Inhalten
melden können. Diese Community-Moderationsfunktion unterstützt die
Administratoren bei der Qualitätssicherung der veröffentlichten Inhalte.
\textbf{Datenbank und Persistenz}
Alle Entitäten werden über einen Entity-Framework-Core-Datenbankkontext
verwaltet, der die Multi-Tenant-Datenbankverbindung automatisch
handhabt:
\begin{Shaded}
\begin{Highlighting}[]
\KeywordTok{public} \KeywordTok{class}\NormalTok{ PremiumAreaContext }\OperatorTok{:}\NormalTok{ DBContextBase}\OperatorTok{,}\NormalTok{ ITransientService}\OperatorTok{,}\NormalTok{ IMultiDatabase}
\OperatorTok{\{}
\KeywordTok{public} \KeywordTok{virtual}\NormalTok{ DbSet}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{PremiumArea}\OperatorTok{\textgreater{}}\NormalTok{ PremiumArea }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \KeywordTok{virtual}\NormalTok{ DbSet}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{EngineerApplication}\OperatorTok{\textgreater{}}\NormalTok{ EngineerApplication }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \KeywordTok{virtual}\NormalTok{ DbSet}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{UserPremium}\OperatorTok{\textgreater{}}\NormalTok{ UserPremium }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \KeywordTok{virtual}\NormalTok{ DbSet}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{PremiumEvent}\OperatorTok{\textgreater{}}\NormalTok{ PremiumEvent }\OperatorTok{\{} \KeywordTok{get}\OperatorTok{;} \KeywordTok{set}\OperatorTok{;} \OperatorTok{\}}
\KeywordTok{public} \FunctionTok{PremiumAreaContext}\OperatorTok{(}\NormalTok{IDBContextDependencies DBContextDependencies}\OperatorTok{)} \OperatorTok{:} \KeywordTok{base}\OperatorTok{(}\NormalTok{DBContextDependencies}\OperatorTok{)}
\OperatorTok{\{}
\OperatorTok{\}}
\KeywordTok{protected} \KeywordTok{override} \DataTypeTok{void} \FunctionTok{OnModelCreating}\OperatorTok{(}\NormalTok{ModelBuilder builder}\OperatorTok{)}
\OperatorTok{\{}
\KeywordTok{base}\OperatorTok{.}\FunctionTok{OnModelCreating}\OperatorTok{(}\NormalTok{builder}\OperatorTok{);}
\NormalTok{ builder}\OperatorTok{.}\FunctionTok{Entity}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{PremiumArea}\OperatorTok{\textgreater{}().}\FunctionTok{ToTable}\OperatorTok{(}\NormalTok{ActiveDatabase}\OperatorTok{.}\FunctionTok{RewriteName}\OperatorTok{(}\StringTok{"SZUAbsolventenvereinPremiumArea"}\OperatorTok{));}
\NormalTok{ builder}\OperatorTok{.}\FunctionTok{Entity}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{EngineerApplication}\OperatorTok{\textgreater{}().}\FunctionTok{ToTable}\OperatorTok{(}\NormalTok{ActiveDatabase}\OperatorTok{.}\FunctionTok{RewriteName}\OperatorTok{(}\StringTok{"SZUAbsolventenvereinEngineerApplications"}\OperatorTok{));}
\NormalTok{ builder}\OperatorTok{.}\FunctionTok{Entity}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{UserPremium}\OperatorTok{\textgreater{}().}\FunctionTok{ToTable}\OperatorTok{(}\NormalTok{ActiveDatabase}\OperatorTok{.}\FunctionTok{RewriteName}\OperatorTok{(}\StringTok{"SZUAbsolventenvereinUserPremium"}\OperatorTok{));}
\NormalTok{ builder}\OperatorTok{.}\FunctionTok{Entity}\OperatorTok{\textless{}}\NormalTok{Models}\OperatorTok{.}\FunctionTok{PremiumEvent}\OperatorTok{\textgreater{}().}\FunctionTok{ToTable}\OperatorTok{(}\NormalTok{ActiveDatabase}\OperatorTok{.}\FunctionTok{RewriteName}\OperatorTok{(}\StringTok{"SZUAbsolventenvereinPremiumEvents"}\OperatorTok{));}
\OperatorTok{\}}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Die Tabellennamen werden über \texttt{ActiveDatabase.RewriteName()}
datenbankunabhängig gehalten, sodass das Modul mit verschiedenen
Datenbanksystemen betrieben werden kann. Neben der Antragstabelle
speichert das Modul den Premium-Status der Benutzer in der Tabelle
\texttt{UserPremium} mit einem Ablaufdatum und einer Quellenangabe.
Ergänzend protokolliert die Tabelle \texttt{PremiumEvent} jede Änderung
am Premium-Status als Audit-Trail, wodurch die vollständige Historie
aller Premium-Vergaben nachvollziehbar bleibt.
Die Datenbankstruktur wird über Oqtane's Migrationssystem aufgebaut. Die
folgende Migration zeigt exemplarisch die Erstellung der drei
Kerntabellen:
\begin{Shaded}
\begin{Highlighting}[]
\OperatorTok{[}\FunctionTok{DbContext}\OperatorTok{(}\KeywordTok{typeof}\OperatorTok{(}\NormalTok{PremiumAreaContext}\OperatorTok{))]}
\OperatorTok{[}\FunctionTok{Migration}\OperatorTok{(}\StringTok{"SZUAbsolventenverein.Module.PremiumArea.01.00.00.01"}\OperatorTok{)]}
\KeywordTok{public} \KeywordTok{class}\NormalTok{ AddPremiumTables }\OperatorTok{:}\NormalTok{ MultiDatabaseMigration}
\OperatorTok{\{}
\KeywordTok{public} \FunctionTok{AddPremiumTables}\OperatorTok{(}\NormalTok{IDatabase database}\OperatorTok{)} \OperatorTok{:} \KeywordTok{base}\OperatorTok{(}\NormalTok{database}\OperatorTok{)} \OperatorTok{\{} \OperatorTok{\}}
\KeywordTok{protected} \KeywordTok{override} \DataTypeTok{void} \FunctionTok{Up}\OperatorTok{(}\NormalTok{MigrationBuilder migrationBuilder}\OperatorTok{)}
\OperatorTok{\{}
\DataTypeTok{var}\NormalTok{ engAppBuilder }\OperatorTok{=} \KeywordTok{new} \FunctionTok{EngineerApplicationEntityBuilder}\OperatorTok{(}\NormalTok{migrationBuilder}\OperatorTok{,}\NormalTok{ ActiveDatabase}\OperatorTok{);}
\NormalTok{ engAppBuilder}\OperatorTok{.}\FunctionTok{Create}\OperatorTok{();}
\DataTypeTok{var}\NormalTok{ userPremBuilder }\OperatorTok{=} \KeywordTok{new} \FunctionTok{UserPremiumEntityBuilder}\OperatorTok{(}\NormalTok{migrationBuilder}\OperatorTok{,}\NormalTok{ ActiveDatabase}\OperatorTok{);}
\NormalTok{ userPremBuilder}\OperatorTok{.}\FunctionTok{Create}\OperatorTok{();}
\DataTypeTok{var}\NormalTok{ premEventBuilder }\OperatorTok{=} \KeywordTok{new} \FunctionTok{PremiumEventEntityBuilder}\OperatorTok{(}\NormalTok{migrationBuilder}\OperatorTok{,}\NormalTok{ ActiveDatabase}\OperatorTok{);}
\NormalTok{ premEventBuilder}\OperatorTok{.}\FunctionTok{Create}\OperatorTok{();}
\OperatorTok{\}}
\KeywordTok{protected} \KeywordTok{override} \DataTypeTok{void} \FunctionTok{Down}\OperatorTok{(}\NormalTok{MigrationBuilder migrationBuilder}\OperatorTok{)}
\OperatorTok{\{}
\DataTypeTok{var}\NormalTok{ engAppBuilder }\OperatorTok{=} \KeywordTok{new} \FunctionTok{EngineerApplicationEntityBuilder}\OperatorTok{(}\NormalTok{migrationBuilder}\OperatorTok{,}\NormalTok{ ActiveDatabase}\OperatorTok{);}
\NormalTok{ engAppBuilder}\OperatorTok{.}\FunctionTok{Drop}\OperatorTok{();}
\DataTypeTok{var}\NormalTok{ userPremBuilder }\OperatorTok{=} \KeywordTok{new} \FunctionTok{UserPremiumEntityBuilder}\OperatorTok{(}\NormalTok{migrationBuilder}\OperatorTok{,}\NormalTok{ ActiveDatabase}\OperatorTok{);}
\NormalTok{ us}
\NormalTok{ erPremBuilder}\OperatorTok{.}\FunctionTok{Drop}\OperatorTok{();}
\DataTypeTok{var}\NormalTok{ premEventBuilder }\OperatorTok{=} \KeywordTok{new} \FunctionTok{PremiumEventEntityBuilder}\OperatorTok{(}\NormalTok{migrationBuilder}\OperatorTok{,}\NormalTok{ ActiveDatabase}\OperatorTok{);}
\NormalTok{ premEventBuilder}\OperatorTok{.}\FunctionTok{Drop}\OperatorTok{();}
\OperatorTok{\}}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Insgesamt definieren vier Migrationen die Datenbankstruktur: Die erste
erstellt die Basistabelle des Moduls, die zweite fügt die drei Tabellen
für Ingenieur-Anträge, Premium-Status und Premium-Events hinzu, die
dritte entfernt nicht mehr benötigte Spalten nach der Umstellung auf den
Oqtane FileManager, und die vierte ergänzt die Felder für Titel und
Kurzbeschreibung. Alle Migrationen werden bei der Modulinstallation
automatisch ausgeführt.
\paragraph{Zugriffsbeschränkung und
Benutzerrechte}\label{zugriffsbeschruxe4nkung-und-benutzerrechte}
Der Zugriff auf den Premium-Bereich wird durch ein mehrstufiges
Berechtigungssystem gesteuert, das sowohl auf Server- als auch auf
Client-Seite durchgesetzt wird. Die verfügbaren Berechtigungen werden in
der \texttt{ModuleInfo}-Klasse registriert:
\begin{Shaded}
\begin{Highlighting}[]
\KeywordTok{public} \KeywordTok{class}\NormalTok{ ModuleInfo }\OperatorTok{:}\NormalTok{ IModule}
\OperatorTok{\{}
\KeywordTok{public}\NormalTok{ ModuleDefinition ModuleDefinition }\OperatorTok{=\textgreater{}} \KeywordTok{new}\NormalTok{ ModuleDefinition}
\OperatorTok{\{}
\NormalTok{ Name }\OperatorTok{=} \StringTok{"PremiumArea"}\OperatorTok{,}
\NormalTok{ Description }\OperatorTok{=} \StringTok{"This module adds a premium member system to Octane."}\OperatorTok{,}
\NormalTok{ Version }\OperatorTok{=} \StringTok{"1.0.3"}\OperatorTok{,}
\NormalTok{ ServerManagerType }\OperatorTok{=} \StringTok{"SZUAbsolventenverein.Module.PremiumArea.Manager.PremiumAreaManager, SZUAbsolventenverein.Module.PremiumArea.Server.Oqtane"}\OperatorTok{,}
\NormalTok{ ReleaseVersions }\OperatorTok{=} \StringTok{"1.0.0,1.0.1,1.0.2,1.0.3"}\OperatorTok{,}
\NormalTok{ Dependencies }\OperatorTok{=} \StringTok{"SZUAbsolventenverein.Module.PremiumArea.Shared.Oqtane"}\OperatorTok{,}
\NormalTok{ PermissionNames }\OperatorTok{=}\NormalTok{ $}\StringTok{"\{PermissionNames.View\},\{PermissionNames.Edit\},\{PermissionNames.Browse\}"}
\OperatorTok{\};}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Die Eigenschaft \texttt{PermissionNames} legt fest, welche
Berechtigungen für das Modul verfügbar sind. Die View-Berechtigung
erlaubt grundlegende Funktionen wie das Einreichen eigener Anträge. Die
Edit-Berechtigung gewährt administrative Funktionen wie das Genehmigen
und Ablehnen von Anträgen. Über \texttt{ReleaseVersions} wird die
Migrationshistorie definiert, sodass Oqtane bei Updates die korrekten
Migrationsschritte ausführt.
Grundsätzlich wird zwischen drei Zugriffsebenen unterschieden:
Standardbenutzer können ausschließlich eigene Ingenieur-Anträge
einreichen und verwalten. Sie haben keinen Einblick in die Anträge
anderer Mitglieder und können weder die Mitgliedersuche noch die
Kontaktfunktion nutzen. Premium-Mitglieder erhalten Zugang zu allen
Funktionen des Premium-Bereichs. Administratoren besitzen
uneingeschränkten Zugriff und können sämtliche Anträge unabhängig vom
Status einsehen, genehmigen oder ablehnen.
Der Premium-Status kann auf zwei Wegen erlangt werden: durch die
Genehmigung eines Ingenieur-Antrags mit automatischer 12-monatiger
Mitgliedschaft oder durch manuelle Zuweisung der Oqtane-Rolle „Premium
Member'' durch einen Administrator. Die serverseitige Methode
\texttt{IsUserPremium} berücksichtigt beide Wege:
\begin{Shaded}
\begin{Highlighting}[]
\KeywordTok{private} \DataTypeTok{bool} \FunctionTok{IsUserPremium}\OperatorTok{(}\NormalTok{System}\OperatorTok{.}\FunctionTok{Security}\OperatorTok{.}\FunctionTok{Claims}\OperatorTok{.}\FunctionTok{ClaimsPrincipal}\NormalTok{ user}\OperatorTok{)}
\OperatorTok{\{}
\KeywordTok{if} \OperatorTok{(!}\NormalTok{user}\OperatorTok{.}\FunctionTok{Identity}\OperatorTok{.}\FunctionTok{IsAuthenticated}\OperatorTok{)}
\KeywordTok{return} \KeywordTok{false}\OperatorTok{;}
\KeywordTok{if} \OperatorTok{(}\NormalTok{user}\OperatorTok{.}\FunctionTok{IsInRole}\OperatorTok{(}\StringTok{"Premium Member"}\OperatorTok{))}
\KeywordTok{return} \KeywordTok{true}\OperatorTok{;}
\DataTypeTok{int}\NormalTok{ userId }\OperatorTok{=} \OperatorTok{{-}}\DecValTok{1}\OperatorTok{;}
\DataTypeTok{var}\NormalTok{ claim }\OperatorTok{=}\NormalTok{ user}\OperatorTok{.}\FunctionTok{Claims}\OperatorTok{.}\FunctionTok{FirstOrDefault}\OperatorTok{(}\NormalTok{item }\OperatorTok{=\textgreater{}}
\NormalTok{ item}\OperatorTok{.}\FunctionTok{Type} \OperatorTok{==}\NormalTok{ System}\OperatorTok{.}\FunctionTok{Security}\OperatorTok{.}\FunctionTok{Claims}\OperatorTok{.}\FunctionTok{ClaimTypes}\OperatorTok{.}\FunctionTok{NameIdentifier}\OperatorTok{);}
\KeywordTok{if} \OperatorTok{(}\NormalTok{claim }\OperatorTok{!=} \KeywordTok{null}\OperatorTok{)}
\DataTypeTok{int}\OperatorTok{.}\FunctionTok{TryParse}\OperatorTok{(}\NormalTok{claim}\OperatorTok{.}\FunctionTok{Value}\OperatorTok{,} \KeywordTok{out}\NormalTok{ userId}\OperatorTok{);}
\KeywordTok{if} \OperatorTok{(}\NormalTok{userId }\OperatorTok{!=} \OperatorTok{{-}}\DecValTok{1}\OperatorTok{)}
\KeywordTok{return}\NormalTok{ \_premiumService}\OperatorTok{.}\FunctionTok{IsPremium}\OperatorTok{(}\NormalTok{userId}\OperatorTok{);}
\KeywordTok{return} \KeywordTok{false}\OperatorTok{;}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
\begin{figure}
\centering
\includegraphics[width=0.8\linewidth,height=0.7\textheight]{flowchart-premium-access.pdf}
\caption{Zugriffsprüfung für Premium-Inhalte}
\end{figure}
Zuerst wird die Oqtane-Rollenzugehörigkeit geprüft, anschließend der
datenbankbasierte Premium-Status mit Ablaufdatum. Das kumulative System
sorgt dafür, dass bei mehrfacher Vergabe die Laufzeiten addiert und
nicht überschrieben werden --- ein Mitglied, das während einer laufenden
Premium-Mitgliedschaft einen weiteren Antrag genehmigt bekommt, erhält
die zusätzlichen 12 Monate auf das bestehende Ablaufdatum aufgeschlagen.
Die Zugriffsbeschränkung wird konsequent auf beiden Seiten durchgesetzt:
Clientseitig entscheidet die Blazor-Komponente anhand der Benutzerrolle,
welche UI-Elemente angezeigt werden. Serverseitig prüft jeder
Service-Aufruf die Berechtigung des anfragenden Benutzers über das
\texttt{{[}Authorize{]}}-Attribut mit Policies wie
\texttt{PolicyNames.ViewModule} und \texttt{PolicyNames.EditModule},
bevor Daten zurückgegeben oder Änderungen vorgenommen werden. Dadurch
wird sichergestellt, dass selbst bei einer Manipulation der
Client-Anfrage keine unberechtigten Zugriffe möglich sind.
\paragraph{Mehrwert für registrierte
Mitglieder}\label{mehrwert-fuxfcr-registrierte-mitglieder}
Der Premium-Bereich schafft einen konkreten Mehrwert für registrierte
Mitglieder auf mehreren Ebenen. Auf fachlicher Ebene entsteht durch die
gesammelten Ingenieur-Anträge eine wachsende Wissensbasis, die allen
Premium-Mitgliedern zugänglich ist. Absolventen können von den Arbeiten
anderer Mitglieder lernen, sich inspirieren lassen und fachliche Ansätze
vergleichen. Durch die Möglichkeit, Titel und Kurzbeschreibungen zu
hinterlegen, können Mitglieder gezielt nach relevanten Themen suchen,
ohne jedes PDF-Dokument einzeln öffnen zu müssen.
Auf sozialer Ebene fördert der Premium-Bereich die Vernetzung innerhalb
der Absolventengemeinschaft. Die Mitgliedersuche mit integrierter
Kontaktfunktion ermöglicht es, gezielt nach ehemaligen Kommilitonen oder
Fachkollegen zu suchen und direkt Kontakt aufzunehmen. Der duale
Benachrichtigungsmechanismus --- bestehend aus In-App-Nachricht und
E-Mail --- stellt sicher, dass Kontaktanfragen zuverlässig zugestellt
werden und kein Mitglied eine Nachricht verpasst.
Auf motivationaler Ebene bietet das zeitlich begrenzte Premium-System
einen Anreiz zur aktiven Teilnahme. Die Aussicht auf exklusive
Funktionen motiviert Mitglieder, eigene Ingenieur-Anträge einzureichen
und damit sowohl den eigenen Premium-Status zu erlangen als auch zur
Wissensbasis der Gemeinschaft beizutragen. Die Meldefunktion gibt
Mitgliedern zusätzlich die Möglichkeit, aktiv zur Qualitätssicherung
beizutragen und unangemessene Inhalte zu kennzeichnen.
Der Datenschutz wird dabei konsequent gewahrt. Die Mitgliedersuche gibt
nur ausgewählte, nicht-sensible Informationen wie Anzeigenamen und
Benutzernamen zurück. E-Mail-Adressen und andere persönliche Daten
werden serverseitig herausgefiltert und sind für andere Mitglieder nicht
einsehbar. Die Kontaktaufnahme erfolgt ausschließlich über das
plattformeigene Benachrichtigungssystem, sodass kein Mitglied seine
persönlichen Kontaktdaten preisgeben muss.
\subsection{Learnings}\label{learnings-1}
\subsubsection{Technische und fachliche
Erkenntnisse}\label{technische-und-fachliche-erkenntnisse}
Während der Umsetzung meiner Diplomarbeit konnte ich umfangreiche
technische Erfahrungen in der Entwicklung moderner Webanwendungen
sammeln. Ein zentraler Bestandteil der Arbeit war die Implementierung
einer Alumni-Plattform, die ehemaligen Studierenden eine Möglichkeit
bietet, sich zu vernetzen, Profile zu verwalten und sich für
Veranstaltungen anzumelden.
Ein wesentlicher Lernaspekt war die praktische Anwendung des
Webframeworks ASP.NET auf Basis der Plattform .NET 8. Dabei konnte ich
ein tieferes Verständnis für die Architektur moderner Webanwendungen
entwickeln. Besonders wichtig war das Zusammenspiel zwischen
Backend-Logik, Datenbankzugriff und Benutzeroberfläche. Durch die
Entwicklung verschiedener Funktionen, wie beispielsweise
Eventverwaltung, Benutzerprofile und Premiumbereiche, wurde deutlich,
wie wichtig eine klare Strukturierung der Anwendung ist.
Ein weiterer technischer Schwerpunkt war die Arbeit mit der relationalen
Datenbank PostgreSQL. Hier lernte ich, Datenmodelle zu entwerfen,
Tabellenbeziehungen zu definieren und effiziente Datenbankabfragen zu
erstellen. Da die Plattform personenbezogene Daten verwaltet, musste
auch auf Aspekte wie Datensicherheit und Datenintegrität geachtet
werden. In diesem Zusammenhang wurde zusätzlich ein Backup- und
Restore-System implementiert, das eine Wiederherstellung der Datenbank
im Falle eines Fehlers ermöglicht. Dadurch konnte ich praktische
Erfahrungen im Bereich Datensicherung und Systemstabilität sammeln.
Ein weiterer wichtiger Bestandteil der Arbeit war die Integration eines
externen Authentifizierungssystems über LinkedIn. Dabei wurde das
Autorisierungsprotokoll OAuth 2.0 eingesetzt. Durch diese
Implementierung konnte ich ein besseres Verständnis für moderne
Authentifizierungsverfahren entwickeln. Besonders interessant war dabei
der Ablauf des OAuth-Authentifizierungsprozesses, bei dem Benutzer über
einen externen Anbieter authentifiziert werden, ohne dass ihre
Zugangsdaten direkt an die eigene Anwendung übermittelt werden.
Zusätzlich wurden Funktionen im Bereich Datenschutz umgesetzt, die den
Anforderungen der DSGVO entsprechen. Dazu zählen unter anderem
Möglichkeiten zur Verwaltung personenbezogener Daten sowie
automatisierte Löschmechanismen. Diese Aspekte haben mir gezeigt, wie
wichtig Datenschutz und rechtliche Anforderungen bei der Entwicklung
moderner Webanwendungen sind.
Insgesamt konnte ich durch die praktische Umsetzung der Plattform ein
deutlich tieferes Verständnis für Webtechnologien, Systemarchitekturen
und Backend-Entwicklung gewinnen.
\subsubsection{Agile Projektarbeit und Teamarbeit
(Zeitmanagement)}\label{agile-projektarbeit-und-teamarbeit-zeitmanagement}
Neben den technischen Aspekten konnte ich auch wichtige Erfahrungen im
Bereich der agilen Projektarbeit sammeln. Für die Organisation des
Projekts wurde das agile Framework Scrum eingesetzt.
Die Arbeit wurde in Sprints von jeweils zwei Wochen unterteilt.
Innerhalb eines Sprints wurden konkrete Aufgaben definiert, die bis zum
Ende des Zeitraums umgesetzt werden sollten. Diese Struktur ermöglichte
eine klare Planung der Arbeitsschritte und half dabei, größere
Entwicklungsaufgaben in kleinere, überschaubare Teilaufgaben zu
unterteilen.
Ein wichtiger Bestandteil der Scrum-Arbeitsweise war die regelmäßige
Reflexion der Arbeit. Nach jedem Sprint konnte überprüft werden, welche
Funktionen erfolgreich umgesetzt wurden und welche Aufgaben
möglicherweise angepasst werden mussten. Dadurch entstand ein iterativer
Entwicklungsprozess, bei dem das System kontinuierlich verbessert wurde.
Auch das Zeitmanagement spielte eine zentrale Rolle. Da die Entwicklung
der Plattform mehrere unterschiedliche Komponenten umfasste --
beispielsweise Datenbankstruktur, Benutzerverwaltung,
Authentifizierungssystem und Eventverwaltung -- war eine gute Planung
notwendig. Durch die Aufteilung in Sprints konnte ich meine Arbeitszeit
besser strukturieren und Prioritäten setzen.
Darüber hinaus zeigte sich, dass agile Methoden besonders gut für
Softwareprojekte geeignet sind, da sie flexible Anpassungen während der
Entwicklung ermöglichen. Neue Anforderungen oder Verbesserungen konnten
relativ einfach in zukünftige Sprints integriert werden.
Diese Erfahrungen haben mir ein besseres Verständnis für moderne
Softwareentwicklungsprozesse vermittelt und gezeigt, wie wichtig
strukturierte Planung und kontinuierliche Verbesserung in technischen
Projekten sind.
\subsubsection{Persönliche
Weiterentwicklung}\label{persuxf6nliche-weiterentwicklung}
Neben den technischen und organisatorischen Erkenntnissen stellte die
Diplomarbeit auch eine wichtige persönliche Weiterentwicklung dar. Die
eigenständige Planung und Umsetzung eines komplexen Softwareprojekts
erforderte ein hohes Maß an Selbstorganisation und Durchhaltevermögen.
Während der Entwicklung der Alumni-Plattform musste ich regelmäßig neue
Technologien und Konzepte erlernen. Dazu gehörten unter anderem
Webframeworks, Datenbanksysteme, Authentifizierungsprotokolle sowie
Aspekte der IT-Sicherheit und des Datenschutzes. Der Umgang mit diesen
Technologien hat meine Fähigkeiten im Bereich der Softwareentwicklung
deutlich erweitert.
Darüber hinaus lernte ich, technische Probleme systematisch zu
analysieren und eigenständig Lösungen zu entwickeln. Besonders bei der
Integration verschiedener Systeme -- beispielsweise der
LinkedIn-Authentifizierung oder der Datenbankfunktionen -- traten immer
wieder Herausforderungen auf, die eine intensive Recherche und
Fehlersuche erforderten.
Ein weiterer wichtiger Lernaspekt war die Verbesserung meiner
Fähigkeiten im Bereich der Dokumentation. Die Erstellung eines
Diplomarbeitsbuchs erfordert eine strukturierte und verständliche
Beschreibung technischer Inhalte. Dadurch konnte ich lernen, komplexe
technische Zusammenhänge klar und nachvollziehbar darzustellen.
Zusammenfassend hat mir die Diplomarbeit nicht nur tiefere technische
Kenntnisse vermittelt, sondern auch meine Fähigkeiten im Bereich
Problemlösung, Selbstorganisation und Projektplanung gestärkt. Diese
Kompetenzen stellen eine wichtige Grundlage für zukünftige berufliche
Tätigkeiten im Bereich der Softwareentwicklung dar.
\subsection{Fazit und Ausblick}\label{fazit-und-ausblick-1}
\subsubsection{Zusammenfassung der
Arbeit}\label{zusammenfassung-der-arbeit}
Ziel dieser Diplomarbeit war die Konzeption und Entwicklung einer
webbasierten Alumni-Plattform für den Absolventenverein. Die Plattform
soll ehemaligen Studierenden ermöglichen, miteinander in Kontakt zu
bleiben, Informationen auszutauschen und an Veranstaltungen des Vereins
teilzunehmen. Dadurch wird die Kommunikation zwischen Absolventinnen und
Absolventen sowie der Bildungseinrichtung langfristig gestärkt.
Im Rahmen der Arbeit wurde eine moderne Webanwendung entwickelt, die auf
dem Framework ASP.NET und der Plattform .NET 8 basiert. Die Speicherung
der Daten erfolgt in der relationalen Datenbank PostgreSQL, welche eine
strukturierte Verwaltung der Benutzerdaten, Eventinformationen und
Systemdaten ermöglicht.
Ein wichtiger Bestandteil der Plattform ist die Benutzerverwaltung. Um
den Registrierungs- und Loginprozess zu vereinfachen, wurde eine
Authentifizierung über LinkedIn implementiert. Dabei wird das
Autorisierungsprotokoll OAuth 2.0 verwendet, wodurch sich Benutzer mit
ihrem bestehenden LinkedIn-Konto anmelden können. Dies erhöht die
Benutzerfreundlichkeit und reduziert gleichzeitig den Verwaltungsaufwand
für eigene Login-Daten.
Neben der Benutzerverwaltung wurden auch weitere Funktionen umgesetzt.
Dazu zählen unter anderem die Verwaltung von Veranstaltungen, für die
sich Alumni anmelden können, sowie ein Premiumbereich mit erweiterten
Funktionen für Mitglieder. Zusätzlich wurden Maßnahmen im Bereich
Datenschutz umgesetzt, um den Anforderungen der DSGVO gerecht zu werden.
Dazu gehört beispielsweise die Möglichkeit zur Verwaltung und Löschung
personenbezogener Daten sowie ein System zur Sicherung der Datenbank
mittels Backup- und Restore-Funktionen.
Die Entwicklung der Plattform erfolgte nach agilen Prinzipien mithilfe
des Scrum-Frameworks. Die Arbeit wurde in mehrere zweiwöchige Sprints
unterteilt, wodurch eine strukturierte und iterative Entwicklung der
einzelnen Funktionen möglich war.
Zusammenfassend konnte mit der Diplomarbeit eine funktionale und
erweiterbare Plattform entwickelt werden, die eine moderne Grundlage für
die digitale Vernetzung von Alumni darstellt.
\subsubsection{Mögliche Erweiterungen der
Webseite}\label{muxf6gliche-erweiterungen-der-webseite}
Obwohl bereits viele grundlegende Funktionen implementiert wurden,
bietet die Plattform weiteres Potenzial für zukünftige Erweiterungen und
Verbesserungen.
Eine mögliche Erweiterung wäre beispielsweise die Integration eines
internen Nachrichtensystems. Dadurch könnten Alumni direkt über die
Plattform miteinander kommunizieren und Kontakte einfacher pflegen.
Ebenso wäre die Implementierung eines Forums oder einer
Diskussionsplattform denkbar, in der Mitglieder Erfahrungen austauschen
oder berufliche Themen diskutieren können.
Auch im Bereich der Eventverwaltung könnten zusätzliche Funktionen
integriert werden. Beispielsweise wäre es möglich, automatische
Erinnerungen für Veranstaltungen zu versenden oder Teilnehmerlisten
übersichtlich darzustellen. Darüber hinaus könnten Eventberichte oder
Fotogalerien vergangener Veranstaltungen integriert werden.
Ein weiterer möglicher Ausbau betrifft den Premiumbereich der Plattform.
Hier könnten zusätzliche Inhalte oder exklusive Funktionen für
Mitglieder bereitgestellt werden, etwa spezielle Networking-Angebote
oder Zugriff auf besondere Veranstaltungen.
Auch technische Erweiterungen sind denkbar. Dazu zählen beispielsweise:
eine mobile Optimierung der Plattform
eine eigene mobile App
Integration weiterer Social-Media-Plattformen
erweiterte Such- und Filterfunktionen für Alumni-Profile
Durch diese Erweiterungen könnte die Plattform langfristig noch
attraktiver und vielseitiger gestaltet werden.
\subsubsection{Zukunftspotenzial für den
Absolventenverein}\label{zukunftspotenzial-fuxfcr-den-absolventenverein}
Die entwickelte Alumni-Plattform bietet dem Absolventenverein eine
moderne digitale Infrastruktur zur Verwaltung und Vernetzung seiner
Mitglieder. Durch die zentrale Plattform können Informationen,
Veranstaltungen und Kontakte effizient organisiert werden.
Ein wesentlicher Vorteil besteht darin, dass der Verein seine Mitglieder
dauerhaft an die Institution binden kann. Ehemalige Studierende haben
die Möglichkeit, weiterhin mit ihrer Ausbildungsstätte und anderen
Absolventinnen und Absolventen in Verbindung zu bleiben. Dies stärkt das
Netzwerk des Vereins und kann langfristig auch neue Kooperationen oder
berufliche Kontakte ermöglichen.
Darüber hinaus kann die Plattform auch zur Organisation von
Veranstaltungen genutzt werden. Alumni können sich direkt online für
Events anmelden und erhalten Informationen zu kommenden Aktivitäten.
Dadurch wird die Planung von Veranstaltungen für den Verein deutlich
erleichtert.
Langfristig kann die Plattform zu einem zentralen digitalen Treffpunkt
für Absolventinnen und Absolventen werden. Besonders im beruflichen
Umfeld kann ein starkes Alumni-Netzwerk einen großen Mehrwert bieten,
beispielsweise durch den Austausch von Erfahrungen oder beruflichen
Möglichkeiten.
Insgesamt besitzt die entwickelte Plattform ein großes
Zukunftspotenzial. Durch kontinuierliche Erweiterungen und die aktive
Nutzung durch die Mitglieder kann sie zu einem wichtigen Instrument für
die Vernetzung und Weiterentwicklung des Absolventenvereins werden.
\cleardoublepage
\section{Konstantin Hintermayer}\label{konstantin-hintermayer-1}
\subsection{Einleitung}\label{einleitung}
\subsubsection{Motivation}\label{motivation-1}
Gegenstand der Diplomarbeit ist die Entwicklung modularer Webanwendungen
mit Blazor und Oqtane. Aufbauend auf fundierten Kenntnissen in der
Fullstack-Entwicklung (React, Node.js, Golang), welche privat bei
diversen Projekten gesammelt worden sind, fokussiert sich die Arbeit auf
die Architekturvorteile des .NET-Stacks. Besonders im Fokus stehen die
Konsistenz durch statische Typisierung sowie das Zusammenspiel modularer
Komponenten in verteilten Systemen.
Mein Untersuchungsanliegen: Inwieweit optimiert die Integration von
Blazor und dem Oqtane-Framework die Konsistenz und Wartbarkeit modularer
Web-Architekturen im Vergleich zu den für mich gewohnten
Technologie-Stacks (React/Node.js)?
\subsubsection{Auftrag und persönliche
Aufgabenstellungen}\label{auftrag-und-persuxf6nliche-aufgabenstellungen}
Mein Aufgabenbereich in diesem Projekt war vielseitig und umfasste
sowohl leitende als auch tiefgreifende technische Aspekte. Die Rollen
lassen sich in drei Kerngebiete unterteilen:
\paragraph{Projektleitung und Organisation (Product
Owner)}\label{projektleitung-und-organisation-product-owner}
~
Als Product Owner war ich für die Definition der Produktvision und die
Priorisierung des Backlogs verantwortlich. Dies beinhaltete: -
\textbf{Anforderungsmanagement}: Erhebung und Strukturierung der
Anforderungen in Zusammenarbeit mit den Betreuern und dem Team. -
\textbf{Sprint-Planung}: Organisation der 14-tägigen Sprints in
YouTrack, um einen kontinuierlichen Entwicklungsfluss sicherzustellen. -
\textbf{Qualitätssicherung}: Definition der \emph{Definition of Done}
(DoD) und Durchführung von Code-Reviews zur Einhaltung von
Architekturstandards.
\paragraph{Infrastruktur und
Systemarchitektur}\label{infrastruktur-und-systemarchitektur}
~
Ein wesentlicher Teil meiner Arbeit lag in der Bereitstellung der
technischen Basis für das gesamte Team: - \textbf{Deployment-Strategie}:
Konzeption und Umsetzung der Server-Infrastruktur auf Basis von Debian
Linux und NginX als Reverse-Proxy. - \textbf{Datenbankdesign}: Entwurf
des relationalen Datenmodells in PostgreSQL, inklusive der Absicherung
durch SSL/TLS. - \textbf{CI/CD-Pipeline}: Automatisierung der Build- und
Deployment-Prozesse mittels Gitea Actions für eine effiziente
Integration der Teambeiträge.
\paragraph{Modulentwicklung}\label{modulentwicklung}
~
Zusätzlich zur Infrastruktur habe ich drei zentrale Module für den
Alumnihub entworfen und implementiert:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\tightlist
\item
\textbf{Anmeldetool (EventRegistration)}: Ein Modul zum verwalten von
Anmeldungen für Veranstaltungen. Es ermöglicht die einfache Erstellung
von Einladungen zu Veranstaltungen und die Verwaltung von Anmeldungen.
\item
\textbf{Reporting System}: Ein generisches System zur Meldung von
Inhalten, das nach dem \emph{Open-Closed-Prinzip} entworfen wurde, um
eine einfache Erweiterbarkeit für alle anderen Module zu bieten.
\item
\textbf{Schwarzes Brett (BlackBoard)}: Ein Community-Modul für den
Austausch von Informationen, inklusive eines automatisierten
E-Mail-Digest-Systems zur Nutzerbenachrichtigung.
\end{enumerate}
\subsection{Anforderungen an das entwickelte Modul bzw. die
Funktionalität}\label{anforderungen-an-das-entwickelte-modul-bzw.-die-funktionalituxe4t-1}
\subsubsection{Modulanforderungen / funktionale
Anforderungen}\label{modulanforderungen-funktionale-anforderungen}
Die funktionalen Anforderungen beschreiben die spezifischen Dienste und
Funktionen, die das System für die Benutzer bereitstellen muss. Im
Rahmen der individuellen Aufgabenstellung wurden Anforderungen für fünf
Teilbereiche definiert:
\paragraph{Anmeldetool
(EventRegistration)}\label{anmeldetool-eventregistration}
~
Das Ziel des Anmeldetools ist die effiziente Organisation von
Vereinsveranstaltungen.
\begin{itemize}
\tightlist
\item
\textbf{Veranstaltungsmanagement}: Administratoren müssen in der Lage
sein, neue Veranstaltungen anzulegen. Dabei müssen Name, Beschreibung,
Datum, Zeit und Ort festlegbar sein.
\item
\textbf{Anmeldeprozess}: Registrierte Benutzer müssen sich für eine
Veranstaltung an- oder abmelden können.
\item
\textbf{Teilnehmerliste}: Für jede Veranstaltung muss eine Übersicht
der Rückmeldungen (Anmeldungen/Absagen) für Administratoren einsehbar
sein.
\item
\textbf{Statistische Auswertung}: Die Rückmeldungen sollen grafisch
(z. B. als Tortendiagramm) aufbereitet werden, um die Planung zu
erleichtern.
\end{itemize}
\paragraph{Globales Reporting System}\label{globales-reporting-system}
~
Das Reporting System dient der Qualitätssicherung von Benutzerinhalten
über alle Module hinweg.
\begin{itemize}
\tightlist
\item
\textbf{Inhalte melden}: Benutzer müssen die Möglichkeit haben,
unangemessene Einträge (z. B. am Schwarzen Brett) zu melden. Dabei
muss ein Grund für die Meldung angegeben werden können.
\item
\textbf{Generische Schnittstelle}: Das System muss so entworfen sein,
dass beliebige andere Module (z. B. Hall of Fame) die Meldefunktion
ohne Änderungen am Kern des Reporting-Systems einbinden können.
\item
\textbf{Administrations-Dashboard}: Gemeldete Inhalte müssen in einer
zentralen Übersicht für Administratoren aufgelistet werden.
\item
\textbf{Moderationsaktionen}: Administratoren müssen gemeldete Inhalte
löschen, die Meldung ignorieren oder den Ersteller verwarnen/sperren
können.
\end{itemize}
\paragraph{Schwarzes Brett
(BlackBoard)}\label{schwarzes-brett-blackboard}
~
Das Schwarze Brett fungiert als digitale Kommunikationsplattform für den
Verein.
\begin{itemize}
\tightlist
\item
\textbf{Beiträge erstellen}: Benutzer müssen Textbeiträge erstellen
können. Die Formatierung der Texte soll über einen Rich-Text-Editor
möglich sein.
\item
\textbf{Bilder-Upload}: Zu jedem Beitrag soll optional ein Bild
hochgeladen werden können, welches automatisch für die Vorschau
skaliert wird.
\item
\textbf{Benachrichtigungs-System}: Das System soll regelmäßig
(täglich/wöchentlich) eine Zusammenfassung neuer Beiträge per E-Mail
an alle interessierten Mitglieder versenden.
\end{itemize}
\paragraph{Mass Mailing (Admin)}\label{mass-mailing-admin}
~
Dieses Modul ermöglicht die direkte Kommunikation des Vorstandes mit
allen Mitgliedern.
\begin{itemize}
\tightlist
\item
\textbf{Rundmails verfassen}: Administratoren müssen E-Mails mit
Betreff und Inhalt an alle registrierten Benutzer verfassen können.
\item
\textbf{Personalisierung}: Die E-Mails sollen automatisch mit dem
Namen des Empfängers personalisiert werden können.
\item
\textbf{Verspätetes Senden}: Aufgrund von Versandlimits des Providers
(Brevo) müssen die E-Mails in eine Warteschlange eingereiht und
zeitversetzt in Batches versendet werden.
\end{itemize}
\paragraph{Token Lifetime Management
(Admin)}\label{token-lifetime-management-admin}
~
Dies dient der Konfiguration sicherheitsrelevanter Parameter.
\begin{itemize}
\tightlist
\item
\textbf{Gültigkeitsdauer}: Administratoren müssen die Gültigkeitsdauer
für temporäre Links (z. B. Passwort-Reset, E-Mail-Bestätigung) über
eine grafische Oberfläche anpassen können.
\item
\textbf{Einstellungs-Persistenz}: Die geänderten Werte müssen
dauerhaft gespeichert werden und sofort für neue Token-Generierungen
wirksam sein.
\end{itemize}
\subsubsection{Use Cases}\label{use-cases}
Um die Interaktion der Benutzer mit den Modulen zu verdeutlichen, wurden
folgende Use Cases definiert:
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.0660}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2170}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1698}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.5472}}@{}}
\caption{Wesentliche Use Cases der entwickelten Module}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
ID
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Name
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Akteur
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
ID
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Name
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Akteur
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Beschreibung
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
UC-01 & Veranstaltung erstellen & Administrator & Ein Administrator legt
ein neues Absolvententreffen mit Ort und Datum an. \\
UC-02 & Zu Event anmelden & Mitglied & Ein Absolvent bestätigt seine
Teilnahme an einem Event über die Weboberfläche. \\
UC-03 & Inhalt melden & Mitglied & Ein Benutzer meldet einen
beleidigenden Post am Schwarzen Brett über den ``Melden''-Button. \\
UC-04 & Meldung bearbeiten & Moderator & Ein Vorstandsmitglied sichtet
eine Meldung und löscht den entsprechenden Beitrag. \\
UC-05 & Rundmail versenden & Administrator & Der Vorstand erstellt eine
Einladung zur Generalversammlung für alle 500 Mitglieder. \\
\end{longtable}
Es ist zu beachten, dass es sich hierbei um eine Auswahl handelt und
nicht alle Use Cases der Module abgebildet werden.
\subsection{Technisches Umfeld}\label{technisches-umfeld}
Mein Aufgabenbereich umfasst einerseits die Entwicklung eigener Module,
sowie das Bereitstellen des Services.
\subsubsection{Auswahlverfahren}\label{auswahlverfahren}
\paragraph{Entscheidungsfindung CMS}\label{entscheidungsfindung-cms}
~
Auch steht die Wahl der Programmiersprache und des CMS an. Nachdem wir
im Unterricht fast ausschließlich mit C\# entwickelt haben und nicht in
eine komplett unbekannte Entwicklungsumgebung abdriften wollten, haben
wir uns für die Webentwicklung mit ASP.NET Core 9 (Upgrade im Lauf der
Diplomarbeit auf .NET Core 10) und dem CMS Oqtane entschieden. Auch hier
gab es einige Kandidaten:
\begin{itemize}
\tightlist
\item
Piranha CMS \textgreater{} Piranha erscheint auf den ersten Blick
nicht so flexibel wie Oqtane, es basiert auf .NET 8.0 und wird nicht
so aktiv gewartet.
\item
Umbraco \textgreater{} Bei Umbraco muss viel in der Admin Oberfläche
von dem CMS gearbeitet werden, im Großen und Ganzen wirkt dieses CMS
nicht so flexibel. Die Dokumentation wirkt auf den ersten Blick sehr
gut!
\item
DNN / Dot Net Nuke \textgreater{} Dieses CMS ist der Platzhirsch. Es
wird von der DNN Foundation gewartet, arbeitet mit dem Dotnet
Framework, welches nicht unter Linux läuft. Und ein Windows Server ist
im Betrieb teurer und in der Absicherung aufwändiger.
\item
Oqtane \textgreater{} Oqtane wirkt auf sehr modular und flexibel, auch
innerhalb von Modulen kann man auf alle Funktionen des ASP.NET Core
Frameworks verwenden. Die Dokumentation wirkt nicht besonders gut,
aber ausreichend. Die enthaltenen Fehlinformationen und mangelnden
Anleitungen für den Betrieb mit Linux sind erst im Nachhinein
aufgefallen.
\end{itemize}
Insbesondere aufgrund seiner sehr hohen Flexibilität, haben wir uns am
Ende für Oqtane entschieden.
\paragraph{Entscheidungsfindung restliche
Infrastruktur}\label{entscheidungsfindung-restliche-infrastruktur}
~
Als Betriebssystem habe ich mich für Linux entschieden, einfach, da ich
mit Linux im Serverumfeld die meisten und besten Erfahrungen gemacht
habe.
Im Bereich der Datenbanken musste ich mir ein paar Fragen stellen:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\tightlist
\item
Auf welche Art Datenbank setzen wir? SQL, NoSQL, Graph, \ldots{}
\item
Mit welcher speziellen Implementierung bekommen wir Support und bei
welcher haben wir Vorwissen im Team?
\item
Ist das auserkorene System kompatibel mit dem CMS, auf dem wir
aufbauen?
\end{enumerate}
Es war von Anfang an klar, dass es ein SQL-basiertes System wird, da wir
im Team nur mit SQL-basierten Systemen Erfahrungen haben. Außerdem
unterstützt unser CMS (Oqtane) nur SQL-basierte Systeme. In der
Linuxwelt kommen jetzt nur noch ein paar Datenbankmanagementsysteme in
die Auswahl: PostgreSQL, MySQL / MariaDB, SQLite. Da ist die Wahl auf
PostgreSQL gefallen. Grund dafür war meine Vorerfahrung mit diesem
Datenbankmanagementsystem, welche ich in meinem Nebenjob erlangt habe.
\subsubsection{Beschreibung und Architektur von
Oqtane}\label{beschreibung-und-architektur-von-oqtane}
Oqtane ist ein Framework und CMS zur Entwicklung von Webseiten mithilfe
von ASP.NET und Blazor.\footnote{Oqtane Foundation (2024a) (siehe
Internet-/Intranetverzeichnis).} Ein Oqtane-System besteht aus
mehreren Komponenten.
In dieser Diplomarbeit fokussieren wir uns hauptsächlich auf
\texttt{Themes} und \texttt{Modules}, aber es gibt auch
\texttt{Language\ Packs} und \texttt{Pure\ Extensions}.\footnote{Oqtane
Foundation (2024b) (siehe Internet-/Intranetverzeichnis).}
Ein \texttt{Module} (Modul) soll neue Funktionalitäten in das CMS
hinzufügen und ein \texttt{Theme} soll die ganze Gestaltung der Webseite
(die Shell) festlegen.\footnote{Ebenda.}
\paragraph{Architektur eines Moduls}\label{architektur-eines-moduls}
~
Ein Modul in Oqtane besteht aus vier Projekten:
\begin{itemize}
\item
Im Server-Projekt liegt Sourcecode, welcher serverseitig ausgeführt
werden soll. Dazu gehören unter anderem alle Repositories, Controller,
Manager, Migrationen und Server-Services und Server-Startuplogik.
\item
Im Client-Projekt liegen Code und Razor-Komponenten für den Client.
Also Client-Startuplogik, Client-Services, Ressourcendateien (.resx),
die Komponenten / das User Interface und die Moduldefinitionen für
jedes Modul.
\item
Im Shared-Projekt wird geteilter Sourcecode abgelegt, der server- und
clientseitig verwendet wird. In der Praxis bleibt es hierbei bei den
EntityFramework-Modellen zum Speichern der Daten im Arbeitsspeicher.
\item
Im Package Projekt findet man Skripte zum Debuggen und Releasen eines
Moduls und die NuGet-Spezifikation.
\begin{itemize}
\tightlist
\item
Beim Debug werden die DLLs, PDBs und statischen Assets wie Skripte
und Stylesheets der drei anderen Projekte in den bereits gebauten
Oqtane.Server
\texttt{oqtane.framework/oqtane.server/bin/debug/net10.0/...}
kopiert.
\item
Beim Release wird ein NuGet-Paket erstellt und unter
oqtane.framework/oqtane.server/Packages abgelegt. Dort abgelegte
NuGet-Pakete werden beim nächsten Start des Oqtane Servers
installiert (Datenbank Migrationen werden gemacht und die Pakete
entpackt).
\end{itemize}
\end{itemize}
\subsubsection{Zusammenspiel der
Infrastruktur}\label{zusammenspiel-der-infrastruktur}
In diesem Kapitel erkläre ich wie die ausgewählten Komponenten
zusammenspielen.
\paragraph{NginX as Reverse Proxy}\label{nginx-as-reverse-proxy}
~
NginX fungiert in unserer Infrastruktur als \texttt{Reverse\ Proxy}. Ein
Reverse Proxy nimmt Anfragen aus dem Internet entgegen und leitet sie an
interne Server (wie Kestrel) weiter. Dies bietet mehrere Vorteile:
\begin{itemize}
\tightlist
\item
\textbf{Sicherheit}: Die interne Applikation ist nicht direkt dem
Internet ausgesetzt.
\item
\textbf{SSL-Terminierung}: NginX übernimmt die rechenintensive
Verschlüsselung (HTTPS), während die Applikation dahinter über
einfaches HTTP kommuniziert.
\item
\textbf{Statische Inhalte}: NginX kann statische Dateien (Bilder, CSS)
effizienter ausliefern als ein Applikationsserver.
\end{itemize}
Wir verwenden NginX für die SSL Terminierung. Das Zertifikat wird von
Let's Encrypt bereit gestellt und mittels HTTP-Challenges und dem
Certbot auf dem Server aktualisiert.
Hier ist ein Auszug der NginX-Konfiguration (\texttt{nginx.conf}) für
den Alumnihub:
\begin{Shaded}
\begin{Highlighting}[]
\DataTypeTok{server} \OperatorTok{\{}
\DataTypeTok{listen} \DecValTok{443} \KeywordTok{ssl}\OperatorTok{;}
\DataTypeTok{server\_name}\NormalTok{ alumni.example.com}\OperatorTok{;}
\DataTypeTok{ssl\_certificate}\NormalTok{ /etc/letsencrypt/live/alumni.example.com/fullchain.pem}\OperatorTok{;}
\DataTypeTok{ssl\_certificate\_key}\NormalTok{ /etc/letsencrypt/live/alumni.example.com/privkey.pem}\OperatorTok{;}
\DataTypeTok{location}\NormalTok{ / }\OperatorTok{\{}
\DataTypeTok{proxy\_pass}\NormalTok{ http://}\DecValTok{127}\NormalTok{.}\DecValTok{0}\NormalTok{.}\DecValTok{0}\NormalTok{.}\DecValTok{1}\NormalTok{:}\DecValTok{5000}\OperatorTok{;}
\DataTypeTok{proxy\_http\_version} \DecValTok{1}\NormalTok{.}\DecValTok{1}\OperatorTok{;}
\DataTypeTok{proxy\_set\_header}\NormalTok{ Upgrade }\VariableTok{$http\_upgrade}\OperatorTok{;}
\DataTypeTok{proxy\_set\_header}\NormalTok{ Connection keep{-}alive}\OperatorTok{;}
\DataTypeTok{proxy\_set\_header}\NormalTok{ Host }\VariableTok{$host}\OperatorTok{;}
\DataTypeTok{proxy\_cache\_bypass} \VariableTok{$http\_upgrade}\OperatorTok{;}
\OperatorTok{\}}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Oqtane selber läuft als Systemd-Service im Kestrel Backend. Kestrel ist
ein kleiner Webserver, welcher in das ASP.NET Core Framework eingebaut
worden ist. Oqtane (bzw. der ASP.NET Core Server ``Kestrel'') hört auf
der Loopback IP und Port 5000. Damit ist Oqtane nur durch NginX
erreichbar. PostgreSQL ist die Datenbank in dem System: Sie hört wieder
auf der Loopback IP und Port 5432. In der folgenden Grafik ist das
System Schematisch dargestellt.
\pandocbounded{\includegraphics[keepaspectratio]{783200d10c9c35d1be650f901b7a01885746de85.pdf}}
Zusätzlich gab es einen Administrationszugang zu den Servern, welcher
über SSH möglich war. Dieser wurde für die Installation und
Konfiguration der einzelnen Komponenten verwendet. Der SSH Service ist
in jeder Umgebung anders erreichbar gewesen.
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.3182}}
>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.6818}}@{}}
\caption{SSH Zugänge in den unterschiedlichen Umgebungen}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Umgebung
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Administrationszugang
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Umgebung
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Administrationszugang
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
Hetzner & Wireguard \\
Schule & Highport \\
LiveDesign & IPSEC VPN \\
\end{longtable}
Die VPN basierten Zugänge sind tendenziell schwieriger zu finden und
auszunutzen, während die Lösung in der Schule mittels Highport den SSH
Service öffentlich erreichbar macht. Durch den \texttt{Highport} ist der
SSH Service schwieriger zu finden. Zur Authentifizierung mit SSH
verwenden wir SSH-Keys, da diese durch ihre komplexität sicherer sind,
als Passwörter.
\subsubsection{Entwicklung mit ASP.NET}\label{entwicklung-mit-asp.net}
\paragraph[Blazor]{\texorpdfstring{Blazor\footnote{Wikipedia (2024)
(siehe Internet-/Intranetverzeichnis).}}{Blazor}}\label{blazor-wikipedia_blazor}
~
Blazor ist ein kostenloses und quelloffenes Web-Framework, welches es
möglich macht Benutzeroberflächen für Web-Browser, basierend auf C\# und
HTML, zu erstellen. Es wird von Microsoft als teil des ASP.NET Core
Frameworks entwickelt.
Blazor hat mehrere Hosting-Modelle:
\begin{longtable}[]{@{}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2967}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1978}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1978}}
>{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3077}}@{}}
\caption{Vergleich der Blazor Hosting-Modelle}\tabularnewline
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Hosting-Modell
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Ausführungsort
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Interaktivität
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Kommunikation
\end{minipage} \\
\midrule\noalign{}
\endfirsthead
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Hosting-Modell
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Ausführungsort
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Interaktivität
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Kommunikation
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\textbf{Static Server} & Server & Keine & HTTP (Initialer Load) \\
\textbf{Interactive Server} & Server & Hoch (Echtzeit) & SignalR /
WebSockets \\
\textbf{Interactive WebAssembly} & Client (Browser) & Hoch (Lokal) &
REST API / HTTP \\
\textbf{Interactive Auto } & Server \& Client (ab zweitem Besuch) & Hoch
(Echtzeit / Lokal) & SignalR / Websockets / RestAPI / HTTP \\
\textbf{Hybrid} & Native App (WebView) & Hoch (Lokal) & n/a
(Mutterprozess) \\
\end{longtable}
Im Rahmen dieser Diplomarbeit haben wir und für den RenderModus
Interactive Server entschieden. Dieser Modus ist für Oqtane die
Standardeinstellung und bietet eine gute Balance zwischen Leistung und
Benutzerfreundlichkeit. Das ist für unseren Anwendungsfall auch die
Empfehlung von Oqtane selbst.\footnote{Oqtane Foundation (2024c) (siehe
Internet-/Intranetverzeichnis).}
Razor-Komponenten (in dieser Arbeit, sowie umgangssprachlich, auch oft
nur Komponenten genannt) sind der Grundbaustein für Blazor Web Apps. Sie
bestehen aus HTML, welches mit der Verwendung von inline C\# beeinflusst
werden kann. Blazor stellt sicher, dass das gerenderte Markup
aktualisiert wird, wenn sich der Status der Komponente ändert. Dieser
Code kann entweder vollständig in einer .razor Datei liegen, oder in
einer seperaten Code-Behind-Datei. Inline C\# Code wird mithilfe von
\texttt{@}- Zeichen markiert. Hier ist ein Beispiel für einen einfachen
Counter:
\begin{Shaded}
\begin{Highlighting}[]
\DataTypeTok{\textless{}}\KeywordTok{h1}\DataTypeTok{\textgreater{}}\NormalTok{Counter}\DataTypeTok{\textless{}/}\KeywordTok{h1}\DataTypeTok{\textgreater{}}
\DataTypeTok{\textless{}}\KeywordTok{p}\DataTypeTok{\textgreater{}}\NormalTok{Count: }\ControlFlowTok{@count}\DataTypeTok{\textless{}/}\KeywordTok{p}\DataTypeTok{\textgreater{}}
\DataTypeTok{\textless{}}\KeywordTok{button}\OtherTok{ }\ErrorTok{@}\OtherTok{onclick}\OperatorTok{=}\StringTok{"Increment"}\DataTypeTok{\textgreater{}}\NormalTok{Increment}\DataTypeTok{\textless{}/}\KeywordTok{button}\DataTypeTok{\textgreater{}}
\ControlFlowTok{@code}
\NormalTok{\{}
\NormalTok{ private int count = 0;}
\NormalTok{ private void Increment()}
\NormalTok{ \{}
\NormalTok{ count++;}
\NormalTok{ \}}
\NormalTok{\}}
\end{Highlighting}
\end{Shaded}
Mit\texttt{@count} in Zeile 3 wird der Wert der Variablen count in den
\texttt{\textless{}p\textgreater{}} Tag mit eingebaut. Mit
\texttt{@onclick="Increment"} in Zeile 5 wird die onclick Property vom
\texttt{\textless{}button\textgreater{}}Tag auf die Increment Methode im
C\# Code gesetzt. Der \texttt{@code} Block in Zeile 7 ist der C\# Code,
welcher diese Komponente dynamisch macht. Hier ist die Variable count
und die Methode Increment definiert, welche dieser Komponente interaktiv
macht.
Razor hat auch eine Reihe an Keywords, wie zum Beispiel (nur
Auszugsweise, bzw. die die wir verwendet haben):
\begin{itemize}
\tightlist
\item
namespace: Gibt den aktuellen Namespace in der Razor Datei an.
\item
inherits: Gibt die Superklasse der generierten C\# Klasse an.
\item
using: Gibt die im C\# Code benutzen/verfügbaren Namespaces an
\item
foreach: Für Wiederholungen im Markup
\item
if: Für Verzweigungen im Markup
\end{itemize}
\paragraph{Kommunikation zwischen Front- und
Backend}\label{kommunikation-zwischen-front--und-backend}
~
Die Interaktion zwischen Client und Server folgt in Oqtane einem klaren
Architekturmuster, das je nach Render-Modus unterschiedliche
Technologien nutzt.
\begin{itemize}
\tightlist
\item
\textbf{SignalR (Interactive Server)}: Für Echtzeit-Interaktionen
nutzt Oqtane SignalR. Dabei wird eine persistente WebSocket-Verbindung
(oder Fallbacks wie Long-Polling) aufgebaut. Zustandsänderungen im UI
lösen C\#-Events auf dem Server aus, und das resultierende ``Diff''
des DOMs wird zurück an den Client gestreamt.
\item
\textbf{REST API (WebAssembly/Shared)}: Bei Modulen, die Daten
asynchron laden (im WebAssembly-Modus), kommuniziert der Client über
einen \texttt{HttpClient} mit dem Controller.
\end{itemize}
Der folgende Ablauf zeigt die Kommunikation bei einer typischen
Datenabfrage in einem Oqtane-Modul:
\pandocbounded{\includegraphics[keepaspectratio]{fdabef25f189cc75c2e232304f32645db4899631.pdf}}
Durch diese Abstraktion bleibt die Geschäftslogik im Backend
(Repository/Controller) gekapselt, während das Frontend lediglich die
Daten präsentiert und Benutzereingaben entgegennimmt. Dies unterstützt
die Testbarkeit, da Repositories unabhängig vom UI getestet werden
können.
\subsubsection{Dependency Injection}\label{dependency-injection}
Dependency Injection ist ein Entwurfsmuster, bei dem die Abhängigkeiten
eines Objekts nicht von diesem selbst erzeugt, sondern von außen
„injiziert`` werden.
Wie der Software-Architekt Martin Fowler, der den Begriff im Jahr 2004
maßgeblich prägte, beschreibt, geht es im Kern darum, die Erzeugung von
Objekten von deren Nutzung zu trennen.\footnote{Fowler, Martin (2004)
(siehe Internet-/Intranetverzeichnis).} Anstatt dass eine Klasse ihre
Hilfsobjekte mittels des new-Operators selbst instanziiert, werden ihr
diese meist über den Konstruktor zur Verfügung gestellt.
In den folgenden beiden Kapiteln wird das Dependency Inversion Principle
und das Microsoft Dependency Injection Framework genauer vorgestellt.
\paragraph[Dependency Inversion Principle ]{\texorpdfstring{Dependency
Inversion Principle\footnote{Microsoft (2024a) (siehe
Internet-/Intranetverzeichnis).}
\footnote{LogRocket (2024) (siehe Internet-/Intranetverzeichnis).}}{Dependency Inversion Principle }}\label{dependency-inversion-principle-ms_dependency_inversion-logrocket_dependency_inversion}
~
Das Dependency-Inversion-Principle (DIP / auf Deutsch:
Abhängigkeits-Umkehr-Prinzip) ist eines von den fünf \texttt{SOLID}
Prinzipien in der Softwareentwicklung.
Das DIP unterscheidet zwischen high-level und low-level Modulen.
\begin{itemize}
\tightlist
\item
Die High-Level-Module beschreiben die Applikations- / Businesslogik,
ohne direkt mit den Low-Level-Modulen zu interagieren, sondern
lediglich auf Abstraktionen.\footnote{OO Design (2024) (siehe
Internet-/Intranetverzeichnis).}
\item
Die Abstraktionen sollen nicht von Implementierungsdetails abhängig
sein, sondern die Low-Level-Implementierung sollen gemäß der
Abstraktionsschicht implementiert werden.\footnote{Ebenda.}
\end{itemize}
Ausgangslage ist eine Softwarearchitektur im
Direct-Dependency-Graph-Modell.
\pandocbounded{\includegraphics[keepaspectratio]{e08c4f02a7a60ce024375d5f7f40dd2b284b916f.pdf}}
Bei diesem Beispiel ist die Klasse A ein high-level Modul, welches
direkt auf die Klasse B referenziert, was das DI-Prinzip verbietet. Das
Problem dabei: Die einzelnen Klassen sind eng gekoppelt, was das
Austauschen von B mit einer anderen Klasse unmöglich macht. Genau dieses
Problem wird vom DIP gelöst.
\pandocbounded{\includegraphics[keepaspectratio]{e3614678caacf98b36a36fa9f5451a214d4f21c6.pdf}}
Das High-Level-Modul ruft lediglich eine Abstraktion eines
Low-Level-Moduls auf, welche von einem, oder mehreren Low-Level-Modulen
implementiert wurde. Für das High-Level-Modul ist es hier egal, welches
Low-Level-Modul die Implementierung bereitstellt. Dadurch erhält man
einen viel modulareren Aufbau in der Software. Die einzelnen Module sind
auch leichter austauschbar und testbar. Genau diese Modularität macht
Dependency Injection möglich.
\paragraph{Microsoft Dependency Injection
Framework}\label{microsoft-dependency-injection-framework}
~
Dependency Injection ist in .NET genau so wie Konfiguration,
Protokollierung und das Optionsmuster ins Framework
integriert.\footnote{Microsoft (2024b) (siehe
Internet-/Intranetverzeichnis).}
Alle Dependencies werden in einem \texttt{Service-Container} zur
Verwaltung registriert. .NET hat einen eingebauten
\texttt{Service-Container} (eine Implementierung des
\texttt{IServiceProvider}).\footnote{Ebenda.}
Das Dependency Injection Framework verwaltet alle Instanzen. Nach Bedarf
werden Instanzen erstellt, oder wieder entsorgt (sofern das Service
nicht mehr gebraucht wird). Beim Instanziieren einer Klasse werden alle
im Konstruktor erwarteten Dependencies bereitgestellt, bzw. selbst
instanziiert und danach bereitgestellt.\footnote{Ebenda.}
Hier ein Beispiel aus der Dokumentation von Microsoft:\footnote{Ebenda.}
\begin{Shaded}
\begin{Highlighting}[]
\NormalTok{HostApplicationBuilder builder }\OperatorTok{=}\NormalTok{ Host}\OperatorTok{.}\FunctionTok{CreateApplicationBuilder}\OperatorTok{(}\NormalTok{args}\OperatorTok{);}
\NormalTok{builder}\OperatorTok{.}\FunctionTok{Services}\OperatorTok{.}\FunctionTok{AddHostedService}\OperatorTok{\textless{}}\NormalTok{Worker}\OperatorTok{\textgreater{}();}
\NormalTok{builder}\OperatorTok{.}\FunctionTok{Services}\OperatorTok{.}\FunctionTok{AddSingleton}\OperatorTok{\textless{}}\NormalTok{IMessageWriter}\OperatorTok{,}\NormalTok{ MessageWriter}\OperatorTok{\textgreater{}();}
\KeywordTok{using}\NormalTok{ IHost host }\OperatorTok{=}\NormalTok{ builder}\OperatorTok{.}\FunctionTok{Build}\OperatorTok{();}
\NormalTok{host}\OperatorTok{.}\FunctionTok{Run}\OperatorTok{();}
\KeywordTok{public} \KeywordTok{class}\NormalTok{ MessageWriter }\OperatorTok{:}\NormalTok{ IMessageWriter}
\OperatorTok{\{}
\KeywordTok{public} \DataTypeTok{void} \FunctionTok{Write}\OperatorTok{(}\DataTypeTok{string}\NormalTok{ message}\OperatorTok{)}
\OperatorTok{\{}
\NormalTok{ Console}\OperatorTok{.}\FunctionTok{WriteLine}\OperatorTok{(}\NormalTok{$}\StringTok{"MessageWriter.Write(message: }\SpecialCharTok{\textbackslash{}"}\StringTok{\{message\}}\SpecialCharTok{\textbackslash{}"}\StringTok{)"}\OperatorTok{);}
\OperatorTok{\}}
\OperatorTok{\}}
\KeywordTok{public} \KeywordTok{interface}\NormalTok{ IMessageWriter}
\OperatorTok{\{}
\DataTypeTok{void} \FunctionTok{Write}\OperatorTok{(}\DataTypeTok{string}\NormalTok{ message}\OperatorTok{);}
\OperatorTok{\}}
\KeywordTok{public} \KeywordTok{sealed} \KeywordTok{class} \FunctionTok{Worker}\OperatorTok{(}\NormalTok{IMessageWriter messageWriter}\OperatorTok{)} \OperatorTok{:}\NormalTok{ BackgroundService}
\OperatorTok{\{}
\KeywordTok{protected} \KeywordTok{override}\NormalTok{ async Task }\FunctionTok{ExecuteAsync}\OperatorTok{(}\NormalTok{CancellationToken stoppingToken}\OperatorTok{)}
\OperatorTok{\{}
\KeywordTok{while} \OperatorTok{(!}\NormalTok{stoppingToken}\OperatorTok{.}\FunctionTok{IsCancellationRequested}\OperatorTok{)}
\OperatorTok{\{}
\NormalTok{ messageWriter}\OperatorTok{.}\FunctionTok{Write}\OperatorTok{(}\NormalTok{$}\StringTok{"Worker running at: \{DateTimeOffset.Now\}"}\OperatorTok{);}
\NormalTok{ await Task}\OperatorTok{.}\FunctionTok{Delay}\OperatorTok{(}\DecValTok{1}\NormalTok{\_000}\OperatorTok{,}\NormalTok{ stoppingToken}\OperatorTok{);}
\OperatorTok{\}}
\OperatorTok{\}}
\OperatorTok{\}}
\end{Highlighting}
\end{Shaded}
Das ist ein simples Beispiel, welches Teile des DI Frameworks zeigt. Wir
haben einen Service (Klasse Worker), ein Dependency (Klasse
MessageWriter) und eine Abstraktionsebene, von dem Dependency (Interface
IMessageWriter).
Bei Programmstart wird zuerst manuell der \texttt{Service-Container}
erstellt, danach alle Module registriert (entweder als HostedService,
oder als Modul mit einer spezifischen Lifetime (Scoped, Transient,
Singleton)).
Mit dem Aufruf von \texttt{builder.Build()} wird intern ein Dependency
Graph erstellt und mit \texttt{host.Run()} wird versucht die Klasse
Worker zu instanziieren und zu starten. Nachdem Worker ein Dependency
auf IMessageWriter hat, wird über den zuvor erstellten Dependency-Graph
die Implementierung von IMessageWriter gesucht. Jetzt wird MessageWriter
instanziiert und dem Konstruktor von Worker übergeben, damit seine
Dependencies befriedigt werden.
So sieht der Abhängigkeitsgraph bei diesem Beispiel aus.
\pandocbounded{\includegraphics[keepaspectratio]{f39b4f0675636b8f1bfe34f890466e3db009627c.pdf}}
\subsubsection{Continuous Integration}\label{sec:continuous-integration}
Gitea, das Versionskontrollsystem dieser Diplomarbeit, hat einen
Continuous-Integration-System eingebaut. Im Kern ist es baugleich zu den
GitHub-Pipelines. Man kann im \texttt{.gitea/workflow} Ordner
\texttt{.yml} Dateien ablegen, welche dann das Verhalten der Workflows
definieren.
Man kann definieren auf welcher Änderung im Git Repository die Pipeline
losgetreten wird (Keyword: \texttt{on}) und entweder eigene Kommandos
aufreihen, oder auf bestehende \texttt{actions} zurückgreifen, welche
dann der Reihe nach ausgeführt werden (Keyword: \texttt{jobs}).
Die meisten Pipelines sind folgendermaßen Aufgebaut: Clone
-\textgreater{} Checkout -\textgreater{} Submodule Checkout (optional)
-\textgreater{} Dependencies einrichten (zum Beispiel das dotnet SDK)
-\textgreater{} Build ausführen. -\textgreater{} Release erstellen und
Artefakte veröffentlichen (z.B. in Registries). Aber man kann auch
andere Dinge tun, z.B. mithilfe von Künstlicher Intelligenz Code und
Dokumentation überprüfen.
Anwendungen von Gitea Actions bei dieser Diplomarbeit:
\begin{itemize}
\tightlist
\item
\textbf{APT-Package Repository}: Zum Bauen von Oqtane und allen
Modulen, verpacken in ein .deb Paket und in die Registry pushen.
\item
\textbf{Interfaces Projekt}: Zum Bauen vom Interfaces-Projekt,
verpacken in ein NuGet Paket und in die Registry pushen. Die
Versionierung des NuGet-Pakets erfolgt dabei automatisiert über
Git-Tags, was eine konsistente Verknüpfung zwischen Quellcode-Stand
und Paketversion sicherstellt.
\item
\textbf{ursprünglich: oqtane.framework}: Zum bauen und Verpacken in
einen Docker Container und in die Registry pushen.
\item
\textbf{PM Repository}: Zum automatischen Überprüfen der Dokumente,
unter anderem, mithilfe von KI, wie zum Beispiel Gemini.
\end{itemize}
Ein Beispiel für eine Konfiguration einer Gitea Action:
\begin{Shaded}
\begin{Highlighting}[]
\FunctionTok{name}\KeywordTok{:}\AttributeTok{ build{-}debian{-}package}
\FunctionTok{on}\KeywordTok{:}
\AttributeTok{ }\FunctionTok{push}\KeywordTok{:}
\AttributeTok{ }\FunctionTok{tags}\KeywordTok{:}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\StringTok{"*"}
\FunctionTok{jobs}\KeywordTok{:}
\AttributeTok{ }\FunctionTok{build}\KeywordTok{:}
\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ Build the debian package}
\AttributeTok{ }\FunctionTok{runs{-}on}\KeywordTok{:}\AttributeTok{ ubuntu{-}latest}
\AttributeTok{ }\FunctionTok{steps}\KeywordTok{:}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Git clone"}
\AttributeTok{ }\FunctionTok{run}\KeywordTok{:}\AttributeTok{ git clone $\{\{ gitea.server\_url \}\}/$\{\{ gitea.repository \}\}.git .}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Git checkout"}
\AttributeTok{ }\FunctionTok{run}\KeywordTok{:}\AttributeTok{ git checkout "$\{\{ gitea.sha \}\}"}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Submodules auschecken"}
\AttributeTok{ }\FunctionTok{run}\KeywordTok{:}\AttributeTok{ git submodule update {-}{-}init}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Dotnet SDK einrichten"}
\AttributeTok{ }\FunctionTok{uses}\KeywordTok{:}\AttributeTok{ actions/setup{-}dotnet@v4}
\AttributeTok{ }\FunctionTok{with}\KeywordTok{:}
\AttributeTok{ }\FunctionTok{dotnet{-}version}\KeywordTok{:}\AttributeTok{ }\StringTok{"10.0.x"}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Configure nuget source"}
\AttributeTok{ }\FunctionTok{run}\KeywordTok{:}\AttributeTok{ dotnet nuget add source {-}{-}name DAV {-}{-}username kocoder {-}{-}password $\{\{ secrets.REGISTRY\_TOKEN\_KOCODER \}\} https://git.kocoder.xyz/api/packages/Diplomarbeit{-}Absolventenverein/nuget/index.json {-}{-}store{-}password{-}in{-}clear{-}text}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Build .deb"}
\AttributeTok{ }\FunctionTok{run}\KeywordTok{:}\AttributeTok{ ./run{-}build.sh "$\{\{ gitea.ref\_name \}\}" "./alumnihub" "Release"}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Upload .deb"}
\AttributeTok{ }\FunctionTok{run}\KeywordTok{:}\AttributeTok{ curl {-}{-}user kocoder:$\{\{ secrets.REGISTRY\_TOKEN\_KOCODER \}\} {-}{-}upload{-}file ./alumnihub.deb https://git.kocoder.xyz/api/packages/Diplomarbeit{-}Absolventenverein/debian/pool/trixie/main/upload}
\AttributeTok{ }\KeywordTok{{-}}\AttributeTok{ }\FunctionTok{name}\KeywordTok{:}\AttributeTok{ }\StringTok{"Create release"}
\AttributeTok{ }\FunctionTok{uses}\KeywordTok{:}\AttributeTok{ akkuman/gitea{-}release{-}action@v1}
\AttributeTok{ }\FunctionTok{with}\KeywordTok{:}
\FunctionTok{ files}\KeywordTok{: }\CharTok{|{-}}
\NormalTok{ ./alumnihub.deb}
\NormalTok{ ./alumnihub/opt/alumnihub/Packages/*.nupkg}
\end{Highlighting}
\end{Shaded}
Die Konfiguration führt teilweise vorgefertigte Actions aus, wie zum
Beispiel das Einrichten des .NET SDKs oder das Erstellen eines Releases.
Aber es werden auch eigene Skripte ausgeführt, wie zum Beispiel das
Bauen des .deb Pakets. Dieses ist im Repository unter
\texttt{./run-build.sh} zu finden. Dieses Skript ist für die
Automatisierung des Build-Prozesses zuständig und kümmert sich um das
Kompilieren der Anwendung, das Erstellen des Debian-Pakets und das
Hochladen in die Registry. Abgelegt ist es im APT-Package Repository
unter \texttt{./gitea/workflows}.
\subsubsection{Debian Paket}\label{debian-paket}
Um die Anwendung und ihre Abhängigkeiten konsistent auf dem Zielserver
(Linux/Hetzner) zu installieren, wurde ein eigenes Debian-Paket
(\texttt{.deb}) erstellt. Das Debian-Paketformat bietet den Vorteil,
dass es Metadaten über Versionen, Abhängigkeiten und
Installationsskripte (Maintainer Scripts) enthält.
\paragraph{Struktur eines
Debian-Pakets}\label{struktur-eines-debian-pakets}
~
Ein \texttt{.deb}-Paket ist im Grunde ein \texttt{ar}-Archiv, das drei
wesentliche Bestandteile enthält:
\begin{itemize}
\tightlist
\item
\textbf{debian-binary}: Eine Textdatei mit der Versionsnummer des
Paketformats.
\item
\textbf{control.tar.gz}: Enthält die Metadaten
(\texttt{control}-Datei) und Skripte, die vor oder nach der
Installation ausgeführt werden (z. B. \texttt{postinst} zum Starten
des Systemd-Services).
\item
\textbf{data.tar.gz}: Enthält die eigentlichen Anwendungsdateien (die
kompilierten Oqtane-DLLs und statischen Assets), die in das
Zielverzeichnis (z. B. \texttt{/opt/alumnihub}) entpackt werden.
\end{itemize}
\paragraph{Automatisierung im
Build-Prozess}\label{automatisierung-im-build-prozess}
~
Der Bau des Pakets erfolgt vollautomatisch in der Gitea-CI-Pipeline.
Dabei werden die folgenden Schritte durchlaufen:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\tightlist
\item
\textbf{Dotnet Publish}: Kompilieren der Anwendung für Linux-x64.
\item
\textbf{Paketierung}: Erstellen der Verzeichnisstruktur gemäß dem FHS
(Filesystem Hierarchy Standard).
\item
\textbf{dpkg-deb}: Aufruf des Standard-Werkzeugs
\texttt{dpkg-deb\ -\/-build}, um das fertige Paket zu schnüren. Dabei
wird auch hier ein Git-Tag als Grundlage für die Paketversion
verwendet.
\item
\textbf{Publish}: Das Paket wird in die Gitea Package Registry
hochgeladen und steht dort für das Deployment via \texttt{apt} zur
Verfügung.
\end{enumerate}
Durch diesen Prozess wird sichergestellt, dass jede Version der Software
eindeutig identifizierbar und einfach rückrollbar (Rollback) ist.
\subsection{Projektmanagement \& Tools}\label{projektmanagement-tools}
\subsubsection{Scrum}\label{scrum}
Zum verwalten des Projektes haben wir uns an den Grundprinzipien von
Scrum orientiert, um flexibel auf Änderungen reagieren zu können. Die
Arbeit wurde in 14-tägige Sprints unterteilt.
\begin{itemize}
\tightlist
\item
\textbf{Sprint Planning}: Zu Beginn jedes Sprints wurden die Aufgaben
aus dem Product Backlog in das Sprint Backlog übernommen.
\item
\textbf{Dailies/Weekly}: Wir hielten wöchentliche Treffen ab, um den
Fortschritt zu synchronisieren und Blocker zu identifizieren.
\item
\textbf{Sprint Review}: Am Ende eines Sprints wurden die funktionalen
Module präsentiert und der Stand der Diplomarbeit mit dem Betreuer
besprochen.
\item
\textbf{Sprint Retrospective}: Am Ende eines Sprints wurde der Sprint
reflektiert und es wurden Maßnahmen zur Verbesserung des Prozesses
abgeleitet.
\item
\textbf{Definition of Done}: Zu Beginn der Diplomarbeit haben wir uns
auf eine Definition of Done geeinigt, welche im Laufe der Diplomarbeit
immer wieder angepasst wurde.
\end{itemize}
Mehr zum Projektmanagementprozess steht im allgemeinen Teil
\hyperref[projektmanagement-mit-scrum]{Projektmanagement mit Scrum}.
\subsubsection{Projektmanagement-Tools (YouTrack \&
Gitea)}\label{projektmanagement-tools-youtrack-gitea}
Für die initiale Planung und die ersten Sprints wurde YouTrack von
JetBrains eingesetzt. Im Vergleich zu einfachen To-Do-Listen bietet
YouTrack mächtige Features für Softwareteams wie agile Boards, Time
Tracking und Gantt-Diagramme.
Im weiteren Verlauf der Diplomarbeit sind wir jedoch auf die
integrierten Projektmanagement-Funktionen von Gitea umgestiegen.
\hyperref[issues]{Siehe Issues}. Dieser Wechsel erfolgte, um den
gesamten Entwicklungsprozess --- von der Aufgabenverwaltung
(Issues/Boards) über die Versionskontrolle bis hin zur CI/CD-Pipeline
(Gitea Actions) --- an einem zentralen Ort zu bündeln. Dies verbesserte
die Übersichtlichkeit und reduzierte den administrativen Aufwand für die
Pflege unterschiedlicher Systeme.
\subsubsection{Git}\label{git}
Git fungierte im Rahmen dieser Diplomarbeit als zentrales Werkzeug zur
Versionskontrolle und Koordination sämtlicher Entwicklungsschritte. Die
wichtigste Komponente stellt dabei der Commit dar, welcher als
eindeutiger Snapshot des Projektzustands dient und über eine spezifische
Commit-SHA (Secure Hash Algorithm) referenziert werden kann. Um eine
lückenlose Nachvollziehbarkeit zu gewährleisten, wurde für jeden
abgeschlossenen Arbeitsschritt ein eigener Commit erstellt.
Im Gegensatz zu zentralisierten Systemen wie SVN, welches wir im
Unterricht beigebracht bekommen haben, speichert Git die vollständige
Historie eines Projekts lokal auf dem Rechner jedes Beteiligten. Dies
ermöglicht nicht nur ein Offline-Arbeiten, sondern bietet auch eine hohe
Ausfallsicherheit.
Der Workflow in Git basiert auf drei Hauptbereichen:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\tightlist
\item
Working Directory: Der aktuelle Zustand der Dateien auf der
Festplatte.
\item
Staging Area (Index): Eine Zwischenebene, in der Änderungen für den
nächsten Snapshot vorgemerkt werden.
\item
Repository (HEAD): Der dauerhafte Speicher der versionierten Stände
(Commits).
\end{enumerate}
Eines der mächtigsten Features von Git ist das Branching. Es erlaubt es,
isolierte Entwicklungszweige zu erstellen, um neue Features oder
experimentelle Analysen zu implementieren, ohne die Stabilität des
Hauptzweigs \texttt{main} zu gefährden.
Sobald eine Änderung erfolgreich getestet wurde, wird sie durch einen
Merge wieder in den Hauptzweig integriert. Sollten dabei
widersprüchliche Änderungen an denselben Dateizeilen auftreten,
unterstützt Git den Nutzer bei der Auflösung dieser sogenannten
Merge-Konflikte.
Für die Sicherung und Zusammenarbeit wurde im Rahmen dieser Arbeit
Remote-Repositories auf einer Gitea Instanz verwendet. Durch die Befehle
push und pull wird der lokale Stand mit dem Server synchronisiert. Dies
gewährleistet eine konsistente Datenbasis und dient gleichzeitig als
kontinuierliches Backup des Projektfortschritts.
\subsubsection{Gitea}\label{gitea-1}
Als schlanke und selbst gehostete Open-Source-Alternative zu Plattformen
wie GitHub oder GitLab wurde für die Verwaltung der Repositories Gitea
eingesetzt. Hauptgrund für die Benutzung von Gitea war meine bereits
aufgesetzte Instanz, welche schon mehrere Jahre in Verwendung war und
mit der es im Team auch schon Erfahrungen gab. Gitea bietet neben der
reinen Git-Verwaltung essenzielle Werkzeuge für den
Software-Lebenszyklus, wie ein integriertes Issue-Tracking, Code-Reviews
über Pull-Requests sowie eine Benutzerverwaltung, und unterstützt somit
die strukturierte Umsetzung der Diplomarbeit im Team.\footnote{Gitea
(2024a) (siehe Internet-/Intranetverzeichnis).}
\paragraph{Repositories}\label{repositories}
~
Ein Repository bildet den zentralen Speicherort für einen Projektteil.
In Gitea wurden separate Repositories für die einzelnen Module und
Themes, das Oqtane Framework, Skripte, die Dokumentation angelegt. Ein
zusätzliches Repository bindet alle übrigen Quellcode-Repositories als
Submodule ein, das macht die Einrichtung einer neuen
Entwicklungsumgebung sehr kompfortabel. Dies ermöglichte eine saubere
Trennung der verschiedenen Projektkomponenten.\footnote{Ebenda.}
\paragraph{Issues}\label{issues}
~
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/GiteaIssues-TaskBoard.png}
\caption{Gitea Issues Board}
\end{figure}
Zur Aufgabenplanung und Fehlerverfolgung wurde das integrierte
Issue-System genutzt. Jede anstehende Aufgabe oder entdeckte
Schwachstelle wurde als „Issue`` erfasst, einem Verantwortlichen
zugewiesen und mit Labels (z. B. „Bug``, „Feature`` oder
„Dokumentation``) versehen. Dies half dabei, den Überblick über den
Projektfortschritt zu behalten und die Anforderungen aus dem Lastenheft
strukturiert abzuarbeiten.\footnote{Ebenda.} \footnote{Gitea (2024b)
(siehe Internet-/Intranetverzeichnis).}
\paragraph{Pull Requests}\label{pull-requests}
~
Um die Qualität des Codes zu sichern, wurden Änderungen nicht direkt in
den Hauptzweig eingespielt, sondern über Pull Requests eingereicht. Ein
Teammitglied konnte so die Änderungen eines anderen sichten,
kommentieren und bei Bedarf Korrekturen anfordern. Erst nach einer
erfolgreichen Überprüfung wurde der Code in den main-Branch
gemergt.\footnote{Gitea (2024a) (siehe Internet-/Intranetverzeichnis).}
\footnote{Gitea (2024c) (siehe Internet-/Intranetverzeichnis).}
\paragraph{Actions}\label{actions}
~
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/GiteaActions-Overview.png}
\caption{Gitea Actions}
\end{figure}
Gitea Actions wurden eingesetzt, um CI/CD-Pipelines (Continuous
Integration / Continuous Deployment) zu realisieren. Bei jedem Push oder
Pull Request wurden automatisierte Skripte ausgeführt, die das Projekt
bauten. Dies reduzierte manuelle Fehlerquellen erheblich. Außerdem
konnten wir mithilfe von CI/CD den Release Prozess einmalig festlegen
und automatisieren, ohne bei jedem Update manuell den selben Prozess
wiederholt durchgehen zu müssen. Das APT-Package Projekt enthält die
CI/CD Konfiguration für das bauen von Oqtane, der Module und Themes,
sowie das verpacken in ein APT Paket und dem veröffentlichen aller
Pakete als eingenes Gitea Release. {[}Siehe Abschnitt
\ref{sec:continuous-integration}{]}\footnote{Gitea (2024a) (siehe
Internet-/Intranetverzeichnis).} \footnote{Gitea (2024d) (siehe
Internet-/Intranetverzeichnis).}
\paragraph{Releases}\label{releases}
~
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/GiteaActions-Releases.png}
\caption{Gitea Releases}
\end{figure}
Über die Release-Funktion wurden wichtige Meilensteine der Diplomarbeit
festgeschrieben. Hierbei wird ein spezifischer Git-Tag mit einer
Versionsnummer versehen und die dazugehörigen Binärdateien, Pakete und
Dokumente archiviert. So lässt sich jederzeit auf einen stabilen,
abgabebereiten Stand des Projekts zugreifen.\footnote{Gitea (2024a)
(siehe Internet-/Intranetverzeichnis).}
\paragraph{Package Repositories}\label{package-repositories}
~
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/GiteaPackageRepository.png}
\caption{Gitea Packages}
\end{figure}
Gitea fungierte zusätzlich als Register für Pakete und Container-Images.
Selbst erstellte Artefakte, wie das Debian Paket für die Bereitstellung
der Anwendung, wurden direkt in der Gitea-Instanz versioniert
gespeichert. Dadurch waren alle notwendigen Komponenten für das
Deployment an einem zentralen Ort verfügbar und abrufbar. Gitea selbst
unterstützt verschiedenste Pakettypen. Darunter fallen unteranderem
NuGet- und Debianpakete. Für beide haben wir in dieser Arbeit verwendung
gefunden.\footnote{Ebenda.} \footnote{Gitea (2024e) (siehe
Internet-/Intranetverzeichnis).}
\subsubsection{Kommunikation}\label{kommunikation}
\subsection{Module}\label{module-2}
\subsubsection{Admin Modules}\label{admin-modules}
Eine C\#-Solution, welche einige Module, welche für den Admineinsatz
geschrieben worden sind beinhaltet. Dieses Modul besteht aus 3
Teilmodulen. Einem Modul für den Versand von Rundmails, eines für die
Einstellung von der Token Lebenszeit bei Tokens, welche per E-Mail
verschickt werden und eines, welches das Reporting-System übernimmt.
\paragraph{Mass Mailing}\label{mass-mailing}
~
Das Mass Mailer Modul ist eine administrative Erweiterung für den
Alumnihub, die es dem Vorstand ermöglicht, personalisierte Rundschreiben
an alle registrierten Mitglieder zu versenden. Da die Pflege der
Mitgliederdaten direkt im CMS erfolgt, bietet dieses Modul eine nahtlose
Integration ohne den Export von CSV-Listen in externe Newsletter-Tools.
\subparagraph{Integration von Brevo}\label{integration-von-brevo}
\hfill\break
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/Brevo.png}
\caption{Brevo Integration}
\end{figure}
Für den tatsächlichen Versand der E-Mails nutzen wir den Cloud-Dienst
Brevo. Dieser bietet eine zuverlässige Zustellung (hohe Reputation der
Mailserver), sowie die Möglichkeit die Zustell-, Öffnungs- und
Klickraten zu beobachten. Das Limit von 300 E-Mails pro Tag stellt uns
jedoch in der kostenlosen Variante vor eine Herausforderung.
\texttt{Batch-Processing}: Mails werden nicht sofort (``Fire and
Forget'') versendet, sondern in eine Versandwarteschlange geschrieben.
Nachdem schon die Notifications Infrastruktur, welche sich auch um den
Mail versand kümmert, ins Framework eingebaut worden ist, wird diese
gleich zum \texttt{schedulen} unserer E-Mails genutzt. Immer 100 Mails
alle 24 Stunden bis alle Ziele die Mails erhalten haben. Das Limit von
100 / Tag ist konservativ sehr niedrig angesetzt, damit Funktionen wie
Passwort Reset Mails nicht (leicht) dadurch beeinflusst werden können.
\paragraph{Token Lifetime}\label{token-lifetime}
~
Das Token Lifetime Modul wurde geschrieben, um die Token-Lebenszeit
konfigurierbar zu machen. Notwendig war das, um die Passwort Reset Links
im initialen Mail versand länger gültig sein zu lassen. Durch das
\texttt{Batch\ Processing} war es möglich, dass eine Mail erst Tage nach
erstellen des Links hinaus geschickt wird und bei einer Standard
Ablaufdauer von 2 Tagen sind manche Links schon ungültig, bis sie den
Mail Server erreichen. Ziel war es, die Änderung der Lebenszeit für
Administratoren im User Interface im Admin Bereich möglich zu machen.
Technisch bedeutet das, dass die standardmäßig vorkonfigurierten
\texttt{DataProtectionTokenProviderOptions} explizit konfiguriert werden
müssen.\footnote{Lock, Andrew (2024) (siehe
Internet-/Intranetverzeichnis).} Der ASP.NET Core
\texttt{UserManager}, welcher das generieren der Tokens übernimmt,
verwendet einen \texttt{DataProtectorTokenProvider} und dieser wiederum
kann mithilfe der \texttt{DataProtectionTokenProviderOptions}
konfiguriert werden.
Es gibt 2 Möglichkeiten, wie man dieses Problem Lösen kann:
\begin{itemize}
\tightlist
\item
\texttt{der\ Workaround}: Ein eigenes Modul, welches in seiner
\texttt{Server/Startup.cs} die benötigten Werte korrekt setzt. Das hat
den Vorteil, dass wir keine Änderungen im CMS selbst haben, sondern
nur unsere eigene Erweiterung dafür schreiben. Andererseits könnte
durch die undeterministische Ladereihenfolge eine
\texttt{race-condition} auftreten.\footnote{Maryland, University of
(2024) (siehe Internet-/Intranetverzeichnis).} Darüber hinaus
besteht kein gleichzeitiger Zugriff auf die appsettings.json und den
IServiceProvider, in dem die Konfiguration gesetzt werden muss.
=\textgreater{} Nachdem wir in der Konfigurationsphase auch noch
keinen Datenbankzugriff haben, können die Werte nicht aus der
Datenbank geladen werden, sondern wir müssen auf eine Textdatei
zurückgreifen.
\item
\texttt{die\ saubere\ Lösung}: Eine Änderung im Kern von Oqtane. Also
wird in unserem Fork von Oqtane die Konfigurationslogik für die
Tokenlifetime implementiert. In diesem Fall könnte die
Konfigurationslogik direkt in
\texttt{OqtaneServiceCollectionExtensions.cs} hinzugefügt werden, da
hier auch alle anderen Oqtane Spezifischen Konfigurationen gesetzt
werden. Das hat den Vorteil, dass der Code aufgeräumter und sauberer
ist und die \texttt{race-condition} verhindert werden kann.\footnote{Ebenda.}
Der initiale Grund dagegen ist, dass wir ein weiteres Git Repository
zum warten haben (den Fork vom Oqtane.Framework), welches jetzt nicht
mehr mit Upstream Commit-gleich ist.
\end{itemize}
\texttt{Der\ Workaround} ist die Möglichkeit für die wir uns entschieden
haben, allerdings ist das nicht die schönste Lösung. Eine eventuell
nachfolgende Diplomarbeit kann an dieser Stelle ansetzen und
\texttt{die\ saubere\ Lösung} implementieren.
\paragraph{Reporting System}\label{reporting-system}
~
Eine weitere Anforderung der Diplomarbeit war es Einträge in Modulen wie
der \texttt{Hall\ of\ Fame}, dem \texttt{Schwarzen\ Brett} und dem
Premium Bereich (\texttt{Engineer\ Applications}) melden zu können. Am
Anfang war es wichtig, dass jeder schnell vorankommt, allerdings haben
wir die Kommunikation Teamintern ein wenig verschlafen und dadurch ein
paar Funktionen doppelt geschrieben. Dadurch kam es zu Inkonsistenzen in
der Verwendung der unterschiedlichen Reporting Systeme. Deswegen haben
wir uns am Ende für eine globales Reporting System entschieden.
Angestrebt wurde folgender Ablauf für das Melden eines Eintrags:
\pandocbounded{\includegraphics[keepaspectratio]{896637356e9d54db4c95ce116530ff0191d7fcce.pdf}}
Im oben dargestellten Ablaufdiagram werden das ReportingComponent und
der ReportingHandler vom Reporting System über Dependency Injection
bereitgestellt, nicht vom Modul selbst. Dadurch erreichen wir eine
bessere Trennung der Zuständigkeiten und halten das S in SOLID ein:
\texttt{Single\ responsibility}. Die Module sind jetzt nicht mehr für
das Reporting selber verantwortlich, sondern müssen nur das Reportings
System einbinden.
Darüber hinaus erfüllt dieses Design das \textbf{Open/Closed Principle}:
Das Reporting-System ist offen für Erweiterungen (neue Module können
einfach andocken), aber geschlossen für Modifikationen (der Kern des
Reporting-Systems muss nicht für jedes neue Modul geändert werden).
Damit DI funktioniert muss für den DI Consumer
(\texttt{also\ das\ Modul,\ welches\ das\ Reporting\ System\ einbinden\ möchte})
das Interface zur Kompilierzeit zur Verfügung stehen. Um das zu
erreichen habe ich eine neue Klassenbibliothek erstellt: Sie heißt
\texttt{Interfaces}, wird per Gitea Actions automatisch in ein
NuGet-Paket gebaut und in der \texttt{Gitea\ Actions\ NuGet\ Registry}
veröffentlicht. Die Versionierung folgt dabei strikt den Git-Tags im
Repository, was eine saubere Release-History ermöglicht. Dieses
NuGet-Paket wird dann in jedem notwendigen Modul als Dependency
hinzugefügt und damit kann man Modulübergreifend auf die Services und
das \texttt{IReportingComponent} zugreifen.
Die Implementierung des \texttt{IReportingComponents} stellt nur eine
Property (\texttt{ReportType}, welche den TypeName der Razor Komponente
zurückliefert, damit \texttt{DynamicComponent} sie laden kann) und eine
Methode (\texttt{ConstructParameterList}, welche das Parameter
Dictionary erstellt. Nur zwecks Typensicherheit eingefügt) bereit. Mit
dem \texttt{DynamicComponent} von Razor ist es möglich, per C\# Code
unterschiedliche Komponenten zu rendern und damit auch die per DI
injizierte Klasse.
\begin{Shaded}
\begin{Highlighting}[]
\ControlFlowTok{@inject}\NormalTok{ IReportUI ReportUI}
\DataTypeTok{\textless{}}\KeywordTok{DynamicComponent}
\OtherTok{ Type}\OperatorTok{=}\StringTok{"@ReportUI.ReportType"}
\OtherTok{ Parameters}\OperatorTok{=}\StringTok{"@ReportUI.ConstructParameterList(\_item, RenderModeBoundary)"}
\DataTypeTok{/\textgreater{}}
\ControlFlowTok{@code}\NormalTok{ \{}
\NormalTok{ private IReportable \_item;}
\NormalTok{\}}
\end{Highlighting}
\end{Shaded}
Die Bereitstellung des Moduls geschieht im \texttt{AdminModules} Modul.
\subsubsection{Event Registration}\label{event-registration}
Dieses Modul ermöglicht es Administratoren und Absolventen,
Veranstaltungen zu erstellen, auf der Seite zu veröffentlichen und zu
verwalten, während Mitglieder ihre Teilnahme direkt über das Portal
bestätigen oder absagen können. Dieses Modul dient der organisatorischen
Unterstützung von Absolvententreffen und anderen Vereinsaktivitäten.
Dieses Modul wurde gemeinsam mit Adam Gaiswinkler geschrieben, wobei er
sich um die Darstellung der Events im Frontend gekümmert hat, während
ich mich der Infrastruktur im Hintergrund angenommen habe.
\paragraph{Backend und Datenhaltung}\label{backend-und-datenhaltung}
~
Die serverseitige Implementierung basiert auf dem Repository-Pattern des
Oqtane-Frameworks. Hierbei kommen zwei zentrale Repositories zum
Einsatz:
Das \texttt{EventRepository} verwaltet die Metadaten der Veranstaltungen
wie Name, Beschreibung, Datum und Ort. Das \texttt{ResponseRepository}
speichert die Rückmeldungen der Benutzer. Ein Eintrag verknüpft dabei
die UserId mit der EventId und dem Status der Rückmeldung. Die
Kommunikation zwischen dem Client und dem Server erfolgt über einen
REST-API-Controller \texttt{EventRegistrationController}, der
sicherstellt, dass nur autorisierte Benutzer Änderungen vornehmen oder
detaillierte Statistiken einsehen können.
\subparagraph{Entity Relationship
Diagram}\label{entity-relationship-diagram}
\hfill\break
\begin{figure}
\centering
\includegraphics[width=0.5\linewidth,height=\textheight,keepaspectratio]{erd-event-registration.pdf}
\caption{ER Diagramm des Event Registration Moduls}
\end{figure}
\paragraph{Statistik und
Visualisierung}\label{statistik-und-visualisierung}
~
Ein wesentlicher Teil der administrativen Ansicht ist die Visualisierung
der Anmeldezahlen. Hierfür wurde eine Integration von Chart.js
realisiert, um den aktuellen Stand der Rückmeldungen grafisch
aufzubereiten.
Um die Brücke zwischen dem C\#-basierten Blazor-Frontend und der
JavaScript-Bibliothek Chart.js zu schlagen, wurde ein dedizierter
Interop-Service implementiert. Der JS-Interop-Layer ist zwar
standardmäßig in Oqtane-Modulen vorgesehen, wurde jedoch in diesem Modul
zum ersten Mal angepasst und produktiv eingesetzt. Der Ablauf der
grafischen Darstellung gestaltet sich wie folgt:
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\tightlist
\item
Datenaufbereitung: In der Edit-Komponente werden alle Rückmeldungen zu
einem Event geladen und nach ihrem Typ, oder beliebigen anderen
Merkmalen aggregiert.
\item
JS-Interop: Über die \texttt{CreateChart}-Methode der Interop-Klasse
wird die JavaScript-Funktion \texttt{createChart} in der
\texttt{Module.js} aufgerufen. Dabei werden die aggregierten Daten,
Beschriftungen und Konfigurationsoptionen übergeben.
\item
Canvas-Rendering: Die JavaScript-Logik erzeugt dynamisch ein
HTML5-canvas-Element innerhalb eines Container-Divs und initialisiert
daraufhin die Chart.js-Instanz, welche ein übersichtliches Pie-Chart
mit den Registrierungsstatistiken rendert.
\end{enumerate}
Durch diese Trennung bleibt die Geschäftslogik im C\#-Code, während für
die performante und ansprechende Darstellung auf etablierte
Web-Technologien zurückgegriffen wird.
\begin{figure}
\centering
\includegraphics[width=0.4\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/EventRegistration-PieChart.png}
\caption{EventRegistration Pie Chart}
\end{figure}
\subsubsection{Schwarzes Brett}\label{schwarzes-brett}
Das Modul ``Schwarzes Brett'' dient als digitale Anschlagtafel für den
Absolventenverein. Mitglieder können hier Gesuche, Angebote oder
allgemeine Informationen veröffentlichen. Es stellt eine zentrale
Informationsdrehscheibe dar, die den informellen Austausch innerhalb des
Vereins fördern soll.
\paragraph{Struktur und Anzeige}\label{struktur-und-anzeige}
~
Die Anzeige der Einträge erfolgt in einer responsiven Grid-Ansicht
(Index-Komponente), wobei jeder Eintrag als Karte (Card) dargestellt
wird. Dieses Design sorgt für eine übersichtliche Präsentation auch bei
einer größeren Anzahl von Mitteilungen.
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/BlackBoard-Overview.png}
\caption{Übersicht der Einträge auf dem Schwarzen Brett}
\end{figure}
Bilderunterstützung: Das Modul nutzt die Oqtane-interne Dateiverwaltung.
Wenn ein Bild für einen Eintrag hochgeladen wurde, wird dieses über
einen Image-Proxy skaliert und als Vorschaubild angezeigt. Fehlt ein
Bild, wird ein konsistenter Platzhalter verwendet, um das visuelle
Gleichgewicht der Grid-Ansicht zu wahren.
Detailansicht: Die Details-Komponente bietet eine fokussierte Ansicht
des Eintrags mit vollständiger HTML-Beschreibung. Für die Eingabe und
Formatierung der Texte wird auf die von Oqtane standardmäßig
bereitgestellten Rich-Text-Editoren zurückgegriffen, was eine
konsistente Nutzerführung im gesamten CMS gewährleistet. Ergänzt wird
dies durch Metadaten wie Erstellungsdatum und Autor.
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/BlackBoard-Details.png}
\caption{Detailansicht eines Eintrags auf dem Schwarzen Brett}
\end{figure}
\paragraph{Automatisierter
E-Mail-Digest}\label{automatisierter-e-mail-digest}
~
Um die Mitglieder regelmäßig über neue Inhalte zu informieren, wurde ein
automatisierter \texttt{Cronjob} implementiert. Dieser Job läuft im
Hintergrund des Oqtane-Frameworks und führt folgende Schritte aus:
\begin{itemize}
\tightlist
\item
Filterung: Der Job identifiziert alle Einträge, die seit dem letzten
Versand erstellt wurden.
\item
Zielgruppenselektion: Es werden alle Benutzer identifiziert, die der
Rolle ``Absolventen'' angehören.
\item
Zusammenstellung: Für jeden dieser Benutzer wird eine personalisierte
Email-Notification generiert, welche eine Zusammenfassung der neuen
Einträge enthält.
\item
Versand: Die generierten Notifications werden in die Warteschlange der
Notification-Infrastruktur eingereiht und sukzessive versendet.
Integration des Reporting-Systems
\end{itemize}
\paragraph{Reporting System}\label{reporting-system-1}
~
Ein wichtiges Merkmal des Schwarzen Bretts zur Sicherstellung der
Inhaltsqualität ist die Anbindung an das globale Reporting-System (siehe
5.4). In der Detailansicht wird über Dependency Injection die
IReportUI-Komponente eingebunden. Mithilfe der DynamicComponent von
Blazor wird die Melde-Funktion nahtlos in die Oberfläche des Moduls
integriert. Dadurch können unangemessene Inhalte direkt von Benutzern
gemeldet werden.
\begin{figure}
\centering
\includegraphics[width=0.7\linewidth,height=\textheight,keepaspectratio]{./images/05-Konstantin/ReportSystem-HandleReport.png}
\caption{Reporting System}
\end{figure}
\paragraph{Technischer Hintergrund}\label{technischer-hintergrund}
~
Auf der Serverseite folgt das Modul dem etablierten Muster mit einem
\texttt{BlackBoardRepository} für den effizienten Datenbankzugriff und
einem \texttt{BlackBoardController} für die API-Bereitstellung. Die
Implementierung des Scheduled Jobs als HostedServiceBase ermöglicht eine
tiefe Integration in die Oqtane-Infrastruktur bei gleichzeitig geringem
Ressourcenverbrauch.
\subsection{Learnings}\label{learnings-2}
\subsubsection{Produktion != Staging}\label{produktion-staging}
Ein Learning, welches doch relativ schnell aufkam ist im Bereich der IT
eigentlich kein unbekanntes. Wir hatten dieses Learning relativ bald, im
Frühling 2025, als die ersten Probleme mit dem Deployment von Oqtane
aufkamen. Oqtane war zwar in unserer Entwicklungsumgebung sehr einfach
zum einrichten gewesen, das Deployment in der Cloud vom Hetzner war
jedoch geplagt von Problemen. Im Zeitraum von Mai bis Okober hatten wir
keine laufende Produktivumgebung. Dadurch sind wir mit dieser
Diplomarbeit auch in
\hyperref[arbeitszeiteinschuxe4tzung-zeitverzug]{Zeitverzug} gekommen.
Hätte ich mich vor dem Start der Diplomarbeit mit dem Deployment von
Oqtane auseinander gesetzt, dann wäre das in
\hyperref[fehlende-dokumentation]{Fehlende Dokumentation} beschriebene
Problem früher aufgekommen und der Zeitverzug wäre nicht so groß, oder
noch ganz vermeidbar gewesen.
\subsubsection{Teamleitung (Motivation /
Downsizing)}\label{teamleitung-motivation-downsizing}
Nachdem ich mich von Anfang an volkommen in das Deploymentproblem von
Oqtane gestürzt habe, habe ich meine Rolle als Teamleitung etwas
schleifen lassen. Dadurch fehlte bei einigen Teammitgliedern initial die
Identifikation mit dem Projekt und in weitererfolge auch die Motivation
an diesem Projekt mitzuarbeiten. Nachdem im Verlauf des Frühlings und
über den Sommer von der hälfte des Teams trotz Besprechungen und
Mahnungen keine Beiträge zu dem Projekt kamen, haben Hr. Prof.~Gürth und
ich uns dazu entschieden uns von 2 Personen vor unterschreiben des
Projektantrages zu trennen. Grund dazu war die Angst, die mangelnde
Motivation zieht das restliche Team mit hinunter. Wir wollten uns trotz
des Downsizings nicht an Funktionalitäten sparen und haben uns für das
nächste halbe bis dreiviertel Jahr einen ziemlich strikten Zeitplan
vorgenommen.
\subsubsection{Arbeitszeiteinschätzung
(Zeitverzug)}\label{arbeitszeiteinschuxe4tzung-zeitverzug}
Ein wesentliches Learning aus dem Projektverlauf war die Diskrepanz
zwischen der initialen Planung und dem tatsächlichen Aufwand.
Ursprünglich wurde der Zeitaufwand für das Aufsetzen der Infrastruktur
und die Einarbeitung in das Oqtane-Framework auf etwa drei Wochen
geschätzt. In der Realität nahm dieser Prozess jedoch mehrere Monate in
Anspruch.
Es gibt mehrere Gründe dafür:
\paragraph{Fehlende Dokumentation}\label{fehlende-dokumentation}
~
\begin{itemize}
\tightlist
\item
\texttt{Fehlende\ oder\ nur\ schlechte\ Dokumentation\ von\ Oqtane}:
Einige Probleme im Deployment wurden in langer und mühseliger Arbeit
auseinander gebrochen und in weitere immer kleinere Probleme
unterteilt. Dadurch, dass wir alle keine Erfahrung mit der Entwicklung
und dem Deployment von ASP.NET Code Anwendungen hatten und die
Dokumentation doch schlecht war, blieb uns manchmal nichts anderes
übrig als mit WireShark den Netzwerktraffic mit zu schneiden und
nebenbei im Git Repository die geloggten Zeilen Code zu finden und so
das Framework von innen heraus kennen zu lernen. Dadurch hatte ich
dann nach einer Einarbeitungszeit von 4 Monaten ziemlich jede Stelle
im Sourcecode von Oqtane gesehen und finde mich um das schneller
zurecht.
\item
\texttt{Team-Konsolidierung}: Durch das notwendige Downsizing des
Teams mussten Aufgaben neu verteilt werden, was die individuelle
Arbeitslast erhöhte und die Konzentration auf die Kernentwicklung
zeitweise verzögert hat.
\item
\texttt{Abhängigkeit\ von\ der\ Infrastruktur}: Dadurch, dass wir bis
in den Oktober / November hinein nicht wussten, ob wir weiter bei
Oqtane bleiben können, haben die anderen Teammitglieder nicht mit der
sinnvollen Entwicklung ihrer Module starten können. Zitat: ``Und was
wenn wir am Ende doch noch das CMS umstellen müssen?'' =\textgreater{}
Auch wenn der Auftrag war, mit der Modulentwicklung zu starten war die
Motivation meiner Teammitglieder nicht so hoch. Selbst wenn sie nicht
direkt von der Infrastruktur mit der Ausführung ihrer Aufgaben
abhängig waren, motiviert waren sie wegen der Umstände auch nicht.
\end{itemize}
Reaktion auf den Verzug: Um das Projektziel dennoch zu erreichen, wurde
der Zeitplan im Herbst 2025 massiv gestrafft. Durch die Umstellung auf
einen strikteren 14-tägigen Sprint-Rhythmus und die Priorisierung von
Core-Funktionalitäten (MVP-Ansatz) konnte der Rückstand teilweise
aufgeholt werden.
\begin{quote}
Fazit: Die Erfahrung zeigt, dass gerade bei ``Nischen-Frameworks'' wie
Oqtane ein deutlich höherer Puffer für die Einarbeitungs- und
Infrastrukturphase (Faktor 2 bis 3 der ursprünglichen Schätzung)
eingeplant werden muss.
\end{quote}
\texttt{90\%\ fertig,\ oder\ fertig?}: Es gibt einige ``Regeln'', wie:
das \texttt{Paretoprinzip}, \texttt{Hofstadters\ Law} und die
\texttt{90-90\ Regel}. Letztere wurde im Jahr 1985 von Jon Bentley in
einer Kolumne ``Programming pearls'' veröffentlicht. Ausgeschrieben
lautet sie:
\begin{quote}
{[}Rule of Credibility{]} The first 90 percent of the code accounts for
the first 90 percent of the development time. The remaining 10 percent
of the code accounts for the other 90 percent of the development
time.\footnote{Bentley, Jon (1985).} Diese Diplomarbeit liefert weitere
Evidenz, dass diese Faustregel stimmt.
\end{quote}
\subsubsection{Sprints und Meetings (in Zukunft ja
asynchron)}\label{sprints-und-meetings-in-zukunft-ja-asynchron}
Ein zentrales Problem in unserer ursprünglichen Arbeitsweise war die
Kopplung von Besprechungsterminen mit festen „Commit-Deadlines`` (dem
Ende des aktuellen Sprint zyklusses). Da wir uns einmal pro Woche für
sechs Stunden am Stück trafen, entstand ein destruktives Muster:
\begin{itemize}
\tightlist
\item
Der ``Last-Minute-Commit''-Druck: In den Stunden unmittelbar vor dem
Meeting wurden Aufgaben unter Zeitdruck abgeschlossen, um im Meeting
Fortschritte präsentieren zu können. Dies führte dazu, dass unfertiger
oder unzureichend getesteter Code („Quick and Dirty``) in das
Repository gepusht wurde.
\item
Fehlende Review-Kultur: Da die Commits erst kurz vor dem Meeting
eintrafen, blieb dem restlichen Team keine Zeit für fundierte
Code-Reviews. Die Besprechungszeit wurde somit für die Fehlersuche
statt für strategische Planung genutzt.
\item
Ineffizienz: Lange Präsenz-Meetings blockierten wertvolle
Entwicklungszeit, ohne die technische Qualität zu steigern.
\end{itemize}
Lösungsansatz: Meetings und Besprechungen asynchron zueinander setzen.
\begin{itemize}
\tightlist
\item
Asynchrone Daily-Updates: Statusberichte erfolgen schriftlich (z. B.
in Gitea Issues oder YouTrack), nicht mehr in stundenlangen
Call-Marathons. Das nimmt den zeitlichen Druck vom einzelnen
Entwickler. Oder zumindest in kurzen Commitnachrichten, welche am Ende
des Tages automatisch an alle Teammitglieder zum Überblick gesendet
werden (eventuell mit @username tagging, um eine Person nochmal genau
anzusprechen)
\item
Review-First-Policy: Ein Feature gilt erst dann als „fertig``, wenn es
einen asynchronen Code-Review-Prozess durchlaufen hat. Das Meeting
dient nur noch der Klärung von Blockern, nicht der Präsentation von
Code. Das war eigentlich schon von Anfang an in unserer
\texttt{Definition\ of\ Done} festgelegt worden.
\item
Entkoppelung von Meeting und Deadline: Meetings sollten der
Synchronisation dienen, während die Abgabe von Arbeitspaketen
kontinuierlich (Continuous Integration) erfolgen muss, um Lastspitzen
(in der \hyperref[continuous-integration]{Gitea Actions} Pipeline) am
Tag der Besprechung zu vermeiden.
\end{itemize}
\pagebreak
\cleardoublepage
\section{Tabellenverzeichnis}\label{tabellenverzeichnis}
\listoftables
\cleardoublepage
\section{Abbildungsverzeichnis}\label{abbildungsverzeichnis}
\listoffigures
\cleardoublepage
\section{Quellenverzeichnis}\label{quellenverzeichnis}
\phantomsection\label{refs}
\begin{CSLReferences}{1}{1}
\bibitem[\citeproctext]{ref-bentley1985programming}
\textbf{Bentley, Jon (\textbf{1985}):} Programming pearls\emph{ In:
Communications of the ACM}, Band 28, Ausgabe 9, 1985896--901, DOI:
\href{https://doi.org/10.1145/4284.315122}{10.1145/4284.315122}.
\bibitem[\citeproctext]{ref-bootstrap}
\textbf{Bootstrap (\textbf{2026}):} Bootstrap Framework, 2026, abgerufen
am 17.03.2026, \url{https://getbootstrap.com/}.
\bibitem[\citeproctext]{ref-bsi_grundschutz}
\textbf{BSI (\textbf{2023}):} IT-Grundschutz-Kompendium -- CON.3:
Datensicherungskonzept, 2023, abgerufen am 19.03.2026,
\url{https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Grundschutz/Kompendium/IT_Grundschutz_Kompendium_Edition2023.pdf}.
\bibitem[\citeproctext]{ref-bsi_datensicherung}
\textbf{Bundesamt für Sicherheit in der Informationstechnik (BSI)
(\textbf{2024}):} Datensicherung und Datenverlust, 2024, abgerufen am
19.03.2026,
\url{https://www.bsi.bund.de/DE/Themen/Verbraucherinnen-und-Verbraucher/Informationen-und-Empfehlungen/Cyber-Sicherheitsempfehlungen/Daten-sichern-verschluesseln-und-loeschen/Datensicherung-und-Datenverlust/Datensicherung-wie-geht-das/datensicherung-wie-geht-das_node.html}.
\bibitem[\citeproctext]{ref-ris}
\textbf{Bundeskanzleramt Österreich (\textbf{2024}):}
Rechtsinformationssystem des Bundes (RIS), 2024, abgerufen am
19.03.2026, \url{https://www.ris.bka.gv.at/}.
\bibitem[\citeproctext]{ref-dsb}
\textbf{Datenschutzbehörde (\textbf{2024}):} Österreichische
Datenschutzbehörde (DSB), 2024, abgerufen am 19.03.2026,
\url{https://dsb.gv.at/}.
\bibitem[\citeproctext]{ref-dsgvo}
\textbf{Europäische Union (\textbf{2016}):} Datenschutz-Grundverordnung
(DSGVO), 2016, abgerufen am 19.03.2026,
\url{https://eur-lex.europa.eu/eli/reg/2016/679/}.
\bibitem[\citeproctext]{ref-fowler_dependency_injection}
\textbf{Fowler, Martin (\textbf{2004}):} Inversion of Control Containers
and the Dependency Injection pattern, 2004, abgerufen am 19.03.2026,
\url{https://martinfowler.com/articles/injection.html}.
\bibitem[\citeproctext]{ref-gitea_docs}
\textbf{Gitea (\textbf{2024a}):} Gitea Documentation, 2024, abgerufen am
19.03.2026, \url{https://docs.gitea.com/}.
\bibitem[\citeproctext]{ref-gitea_issue_tracker}
\textbf{Gitea (\textbf{2024b}):} Gitea Issue Tracker Comparison, 2024,
abgerufen am 19.03.2026,
\url{https://docs.gitea.com/installation/comparison\#issue-tracker}.
\bibitem[\citeproctext]{ref-gitea_pull_requests}
\textbf{Gitea (\textbf{2024c}):} Gitea Pull Requests, 2024, abgerufen am
19.03.2026, \url{https://docs.gitea.com/usage/pull-request}.
\bibitem[\citeproctext]{ref-gitea_actions}
\textbf{Gitea (\textbf{2024d}):} Gitea Actions Overview, 2024, abgerufen
am 19.03.2026, \url{https://docs.gitea.com/usage/actions/overview}.
\bibitem[\citeproctext]{ref-gitea_packages}
\textbf{Gitea (\textbf{2024e}):} Gitea Packages Overview, 2024,
abgerufen am 19.03.2026,
\url{https://docs.gitea.com/usage/packages/overview}.
\bibitem[\citeproctext]{ref-gitea_about}
\textbf{Gitea (\textbf{2026}):} Gitea -- Open Source Git Service, 2026,
abgerufen am 17.03.2026, \url{https://about.gitea.com/}.
\bibitem[\citeproctext]{ref-rfc6749}
\textbf{IETF (\textbf{2012}):} IETF RFC 6749 -- The OAuth 2.0
Authorization Framework, 2012, abgerufen am 19.03.2026,
\url{https://datatracker.ietf.org/doc/html/rfc6749}.
\bibitem[\citeproctext]{ref-jetbrains_rider}
\textbf{JetBrains (\textbf{2026}):} JetBrains Rider, 2026, abgerufen am
17.03.2026, \url{https://www.jetbrains.com/rider/}.
\bibitem[\citeproctext]{ref-livedesign}
\textbf{LiveDesign (\textbf{2026}):} LiveDesign -- Hosting, Design \&
Branding, 2026, abgerufen am 17.03.2026, \url{https://livedesign.at/}.
\bibitem[\citeproctext]{ref-andrewlock_token_lifetime}
\textbf{Lock, Andrew (\textbf{2024}):} Implementing custom token
providers for passwordless authentication in ASP.NET Core Identity,
2024, abgerufen am 19.03.2026,
\url{https://andrewlock.net/implementing-custom-token-providers-for-passwordless-authentication-in-asp-net-core-identity/\#changing-the-default-token-lifetime}.
\bibitem[\citeproctext]{ref-logrocket_dependency_inversion}
\textbf{LogRocket (\textbf{2024}):} Dependency Inversion Principle,
2024, abgerufen am 19.03.2026,
\url{https://blog.logrocket.com/dependency-inversion-principle/}.
\bibitem[\citeproctext]{ref-race_conditions_pdf}
\textbf{Maryland, University of (\textbf{2024}):} Race Conditions, 2024,
abgerufen am 19.03.2026,
\url{https://www.cs.umd.edu/projects/syschat/raceConditions.pdf}.
\bibitem[\citeproctext]{ref-ms_dependency_inversion}
\textbf{Microsoft (\textbf{2024a}):} Dependency Inversion, 2024,
abgerufen am 19.03.2026,
\url{https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles\#dependency-inversion}.
\bibitem[\citeproctext]{ref-ms_di_overview}
\textbf{Microsoft (\textbf{2024b}):} Dependency Injection Overview,
2024, abgerufen am 19.03.2026,
\url{https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview}.
\bibitem[\citeproctext]{ref-aspnet_core_docs}
\textbf{Microsoft (\textbf{2026a}):} ASP.NET Core Documentation, 2026,
abgerufen am 17.03.2026,
\url{https://learn.microsoft.com/en-us/aspnet/core/}.
\bibitem[\citeproctext]{ref-blazor_docs}
\textbf{Microsoft (\textbf{2026b}):} Blazor Documentation, 2026,
abgerufen am 17.03.2026,
\url{https://learn.microsoft.com/en-us/aspnet/core/blazor/}.
\bibitem[\citeproctext]{ref-ef_core_migrations}
\textbf{Microsoft (\textbf{2026c}):} Entity Framework Core --
Migrations, 2026, abgerufen am 17.03.2026,
\url{https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/}.
\bibitem[\citeproctext]{ref-linkedin_auth_flow}
\textbf{Microsoft (\textbf{2026d}):} LinkedIn Developer Documentation --
Authorization Code Flow, 2026, abgerufen am 19.03.2026,
\url{https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow}.
\bibitem[\citeproctext]{ref-oodesign_dependency_inversion}
\textbf{OO Design (\textbf{2024}):} Dependency Inversion Principle,
2024, abgerufen am 19.03.2026,
\url{https://www.oodesign.com/dependency-inversion-principle}.
\bibitem[\citeproctext]{ref-oqtane_about}
\textbf{Oqtane Foundation (\textbf{2024a}):} About Oqtane, 2024,
abgerufen am 19.03.2026, \url{https://www.oqtane.org/\#about}.
\bibitem[\citeproctext]{ref-oqtane_docs_extensions}
\textbf{Oqtane Foundation (\textbf{2024b}):} Oqtane Extensions, 2024,
abgerufen am 19.03.2026,
\url{https://docs.oqtane.org/dev/extensions/index.html}.
\bibitem[\citeproctext]{ref-oqtane_docs_rendermodes}
\textbf{Oqtane Foundation (\textbf{2024c}):} Oqtane Render Modes, 2024,
abgerufen am 19.03.2026,
\url{https://docs.oqtane.org/guides/concepts/render-modes/index.html}.
\bibitem[\citeproctext]{ref-oqtane_framework_site}
\textbf{Oqtane Foundation (\textbf{2026a}):} Oqtane Framework, 2026,
abgerufen am 17.03.2026, \url{https://www.oqtane.org/}.
\bibitem[\citeproctext]{ref-oqtane_docs_dev}
\textbf{Oqtane Foundation (\textbf{2026b}):} Oqtane Developer
Documentation, 2026, abgerufen am 17.03.2026,
\url{https://docs.oqtane.org/}.
\bibitem[\citeproctext]{ref-questpdf}
\textbf{QuestPDF (\textbf{2026}):} QuestPDF Open Source Library, 2026,
abgerufen am 17.03.2026, \url{https://www.questpdf.com/}.
\bibitem[\citeproctext]{ref-wikipedia_blazor}
\textbf{Wikipedia (\textbf{2024}):} Blazor, 2024, abgerufen am
19.03.2026, \url{https://en.wikipedia.org/wiki/Blazor}.
\end{CSLReferences}
\end{document}