This repository has been archived on 2024-11-29. You can view files and clone it, but cannot push or open issues or pull requests.
bakalarka/kap-modelova-implementace.tex

297 lines
23 KiB
TeX

\chapter{Modelová implementace}\label{kap:modelova-implementace}
Tato část práce se věnuje tvorbě modelové implementace systému pro generování statického webu dle definovaných požadavků v kapitole \ref{kap:taxonomie-pozadavku}. Systém je vytvářen na základě poznatků z předchozích částí práce.
\section{Výběr vhodného systému}\label{kap:vyber-vhodneho-systemu}
Modelový web se skládá ze dvou sytstémů, a to ze systému pro správu obsahu a systému pro jeho generování do HTML.
Pro správu obsahu i šablon a statických souborů byl zvolen distribuovaný verzovací systém Git. Hlavní výhodou tohoto verzovacího systému je jeho rozšířené využití v praxi a dokáže s ním tedy pracovat spousta uživatelů. Zároveň má v porovnání s jinými verzovacími systémy spousty výhod.\todo{Příklady + citace}
Protože forma modelového webu odpovídá paradigmatu webové prezentace ze sekce \ref{kap:paradigmata-webova-prezentace}, byl pro jeho generování použit program Zola\footnote{\url{https://www.getzola.org/}}, jehož výhody jsou v sekci \ref{kap:paradigmata-webova-prezentace} popsány.
\section{Tvorba šablony}
Jak se uvádí v dokumentaci\footnote{\url{https://www.getzola.org/documentation/content/overview/}}, Zola pracuje s několika druhy stránek, primárně s takzvanou \uv{sekcí} a \uv{stránkou}. Každá sekce může mít vlastní obsah, ovšem může obsahovat i další subsekce, díky čemuž lze dělit obsah do stromové struktury. Stránka slouží pouze k předání obsahu a nikoliv k dalšímu větvení struktury. Dá se tedy říci, že stránka značí konec dané větve. Kořenem celého stromu je speciální sekce s názvem \uv{index}. Každá tato část standardně využívá vlastní HTML šablonu, ovšem nejde o pravidlo a každá část větve může využívat jinou šablonu. To je užitečné například u stránek s různým druhem obsahu. V rámci modelového webu zůstává druh obsahu stejný a není tedy třeba odchylovat se od standardní struktury.
Soubory se šablonami se nachází ve složce \texttt{templates/}, ve které generátor vždy očekává šablonu \texttt{index.html}. Ta se využívá jak k vykreslení úvodní kořenové stránky, tak ji mohou ostatní šablony rozšiřovat. Tato kořenová šablona tedy obsahuje základní strukturu celé stránky, přičemž navazující šablony jen mění určité části obsahu a nedefinují celou strukturu znovu.
Generátor v šablonách hledá vlastní řídící sekvence, které se popisují závorkami. Existují tři druhy kombinací, které lze použít:
\begin{itemize}
\item \texttt{\{\% \%\}} -- Metoda, funkce, cykly, podmínky, práce s proměnnou atd.
\item \texttt{\{\{ \}\}} -- Výpis do HTML
\item \texttt{\{\# \#\}} -- Komentář
\end{itemize}
Generátor také vyžaduje konfigurační soubor \texttt{config.toml} v kořenové složce projektu, který obsahuje různé nastavení stránky, globální proměnné a chování generátoru.
\begin{lstlisting}[label=lst:jednoducha-konfigurace,caption=Příklad jednoduché konfigurace v souboru \texttt{config.toml}]
# Adresa ze které se generují odkazy
base_url = "https://ucitelonline.pedf.cuni.cz"
# Název stránky
title = "Učitel online"
# Popis stránky
description = "Web pro ditstribuci užitečných materiálů"
# Zda se bude zpracovávat CSS systémem Sass
compile_sass = true
\end{lstlisting}
\todo[inline]{Tohle je pěkná ukázka, vyberte klidně ještě jednu dvě, které jsou něčím zajímavé, typické, nebo naopak výjimečné pro ilustraci toho, co chcete o daném, systému sdělit.}
Systém vždy zpracuje úvodní šablonu \texttt{index.html}, ze které pak lze odvíjet ostatní šablony. Tato hlavní šablona obsahuje strukturu celé webové stránky a nesmí v ní tedy chybět validní HTML struktura, tedy hlavička, tělo, metadata, kódování a podobně. Do struktury lze vkládat libovolné řídící sekvence pro generátor, které ovlivňují výsledný výstup.
\begin{lstlisting}[label=lst:zakladni-sablona,caption=Základní šablona \texttt{index.html}]
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<title>{{ config.title }}</title>
</head>
<body>
</body>
</html>
\end{lstlisting}
V příkladu \ref{lst:zakladni-sablona} je název stránky mezi tagy \texttt{<title></title>} vyplněn generátorem. Ten do šablony vloží hodnotu konstanty \texttt{config.title}, která je nastavena v konfiguračním souboru \texttt{config.toml} z příkladu \ref{lst:jednoducha-konfigurace}. Názvem stránky bude tedy řetězec \uv{Učitel online}. Generátor dokáže převzít kteroukoliv konstantu z kontextu konfiguračního souboru.
Všechny direktivy lze v rámci generátoru navazovat na sebe, podobně jako je tomu v Unixových systémech. Spojování funkcí a filtrů se provádí znakem \texttt{|}, stejně jako v POSIX\footnote{Portable Operating System Interface -- Rodina standardů Unixových systémů} shellu, kde výstup jednoho příkazu se stane vstupem příkazu navazujícího. Například je možné název stránky vypsat ve velkých písmenech i přesto, že v konfiguračním souboru je formátován pouze s velkým písmenem na začátku. K převedení na velká písmena slouží filtr \texttt{upper}. Názvem stránky bude po zpracování programem \ref{lst:filtr-upper} řetězec \uv{UČITEL ONLINE}.
\begin{lstlisting}[label=lst:filtr-upper,caption=Základní šablona s filtrem pro přepsání názvu na velká písmena]
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<title>{{ config.title | upper }}</title>
</head>
<body>
</body>
</html>
\end{lstlisting}
V šabloně je také možnost vytvořit bloky, které lze v navazujících šablonách měnit. K vysvětlení principu fungování bloků je možné název stránky z příkladu \ref{lst:filtr-upper} obalit blokem \texttt{title} a těla vložit blok \texttt{content}.
\begin{lstlisting}[label=lst:bloky,caption=Využití bloků v šabloně z příkladu \ref{lst:filtr-upper}]
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<title>{% block title %}{{ config.title | upper }}{% endblock %}</title>
</head>
<body>
{% block content %}
Ahoj, světe!
{% endblock %}
</body>
</html>
\end{lstlisting}
Název stránky zůstane stejný a v jejím těle přibude text \uv{Ahoj, světe!}. Vytvoříme-li novou šablonu s názvem \texttt{section.html}, generátor nám umožní rozšířit ji o původní šablonu \texttt{index.html} a měnit pouze definované bloky. Není tedy nutné znovu definovat celou strukturu stránky. Pro importování, nebo-li rozšíření šablony, slouží direktiva \texttt{extends}.
\begin{lstlisting}[label=lst:sablona-section,caption=Definice nové šablony \texttt{section.html} rozšiřující šablonu z příkladu \ref{lst:bloky}]
{% extends "index.html" %}
{% block title %}{{ config.title | upper }} &ndash; {{ section.title }}{% endblock %}
{% block content %}
Toto je obsah kategorie.
{% endblock %}
\end{lstlisting}
Šablona \texttt{section.html} se v rámci generátoru Zola implicitně využívá pro všechny existující sekce\footnote{\url{https://www.getzola.org/documentation/content/section/}}. Názvem stránky v této šabloně bude, stejně jako u hlavní šablony, název stránky z konstanty \texttt{config.title} definované v konfiguračním souboru, ale také spojovník a název dané sekce. Za pseudo-výstup lze považovat například \uv{UČITEL ONLINE -- základní a střední škola}, bude-li se uživatel nacházet v sekci pro základní a střední školy.
V bloku s obsahem bude původní obsah \uv{Ahoj, světe!} nahrazen za řetězec \uv{Toto je obsah kategorie}. Ten ovšem nechceme definovat přímo v šabloně, nýbrž cílem generátoru je vyplňovat obsah ze zdrojových souborů v sázecím jazyce, viz. sekce \ref{kap:princip-generatoru}. Zola pro vkládání obsahu využívá stejný princip jako v ostatních případech, tedy vypsání obsahu proměnné, v tomto případě proměnné \texttt{section.content}, která obsahuje zkompilované HTML z daného Markdown souboru. Zároveň je dobrou praktikou provést vyčištění vstupu filtrem \texttt{safe}\footnote{\url{https://tera.netlify.com/docs/\#safe}}.
\begin{lstlisting}[label=lst:sablona-section-vlozeni-obsahu,caption=Vkládání obsahu ze zdrojového Markdown souboru]
{% extends "index.html" %}
{% block title %}{{ config.title | upper }} &ndash; {{ section.title }}{% endblock %}
{% block content %}
{{ section.content | safe }}
{% endblock %}
\end{lstlisting}
Z principu by žádný obsah neměl být definován přímo v šabloně, nýbrž by měl být do stránky vkládán generátorem z proměnných, nebo ze sázeného obsahu. V rámci modelové implementace je toto nepsané pravidlo dodržováno.
\section{Automatické generování vícevrstvé navigace}
\quest[inline]{Je následující část srozumitelná?}
Obsah modelové implementace je dělen do stromové datové struktury o potencionálně nekonečné hloubce, kdy každá část větve je v rámci generátoru vlastní kategorií, nikoliv stránkou. Pro modelovou implementaci bylo zvoleno, aby navigace byla generována v návaznosti na aktivní cestu ve stromě. Ve stránce jsou dvě různé navigace, jedna hlavní a vždy vidiětelná, která obsahuje rozdělení obsahu dle škol a druhá navigace, která zobrazuje aktivní větev stromu.
První vrstvou struktury jsou hlavní sekce, v rámci implementace pojemnované jako $L_1$, které jsou vypsány vždy ve vlastní navigaci. Pod touto navigací se zobrazuje seznam všech kategorií, které vybraná položka v $L_1$ obsahuje. Pokud uživatel zvolí kteroukoliv položku v $L_2$, v navigaci se objeví další sloupec, který obsahuje všechny podkategorie vybrané položky, tedy položky ve vrstvě $L_3$. Takto lze stromem procházet potencionálně do nekonečna. Styly modelové šablony ovšem počítají s maximální hloubkou čtyř subkategorií.
Tato funkcionalita je implementována pomocí dvou cyklů. První cyklus (příklad \ref{lst:obsah-cyklus1}) se provádí pro všechny rodiče aktivní ktegorie vrstev $L_2,L_3,\dotsc,L_n$, kde $n$ je aktuílní vrstva. V každé iteraci se mění kontext, ve kterém generátor pracuje. Z daného kontextu generátor vypisuje všechny subkategorie každého svého rodiče. V druhém cyklu (příklad \ref{lst:obsah-cyklus2}) se vypisují všichni potomci dané stránky, tedy potomci ve vrstvě $L_{n+1}$.
\begin{lstlisting}[label=lst:obsah-cyklus1,caption=Cyklus pro vypisování všech rodičů v dané větvi navigace]
{% if section.ancestors %}
{% for s in section.ancestors %}
{% if loop.index < 2 %}{% continue %}{% endif %}
<ul>
{% set s = get_section(path=s) %}
{% for s in s.subsections %}
{% set s = get_section(path=s) %}
<li><a href="{{ s.permalink }}"
{% if current_path == s.path %}
class="active"
{% elif current_path is containing(s.path) %}
class="ancestor"
{% endif %}
>{{ s.title }}</a></li>
{% endfor %}
</ul>
{% endfor %}
{% endif %}
\end{lstlisting}
\begin{lstlisting}[label=lst:obsah-cyklus2,caption=Cyklus pro vypisování všech potomků dané stránky do navigace]
{% if section.subsections %}
<ul>
{% for s in section.subsections %}
{% set s = get_section(path=s) %}
<li><a href="{{ s.permalink }}">{{ s.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
\end{lstlisting}
\section{Rozšíření šablony}
Ve výchozím stavu generátor neumí vkládat nic jiného, než je uvedeno ve specifikaci CommonMark, viz. sekce \ref{kap:markdown}. Dle požadavků modelového webu je nutné, aby generátor uměl vkládat videa přímo do stránky. Taková funkcionalita není soušástí specifikace CommonMark a je tedy potřeba rozšířit generátor. Nejvhodnějším způsobem přidání vlastních funkcionalit je využití filtrů, které se v rámci generátoru nazývají \uv{shortcode}.
Principem vlastních filtrů je to, že si uživatel vytvoří vlastní šablonu, kterou lze vyvolat speciální řídící sekvencí přímo z obsahu. Každý tento shortcode může pracovat s libovolným množstvím proměnných a po zpracování vloží do místa vyvolání zkompilovaný HTML kód. Lze tedy tvrdit, že shortcode je v své podstatě imperativní funkce, která umí pracovat s parametry.
Pro tvorbu těchto filtrů je v generátoru Zola určena složka \texttt{templates/shortcodes}, která obsahuje jejich HTML šablony a kód pro zpracování generátorem. Název HTML souboru definuje název vlastního filtru. Vytvoříme-li uvitř této složky soubor nazvaný \texttt{video.html}, budeme v obsahu schopni využívat vlastní filtr s názvem \texttt{video}.
\begin{lstlisting}[label=lst:jednoduchy-filtr,caption=Příklad jednoduchého filtru s jedním atributem]
<video controls><source src="{{ src }}"></video>
\end{lstlisting}
V příkladu \ref{lst:jednoduchy-filtr} bude filtr očekávat atribut \texttt{src} a bude vracet jednoduchý HTML kód pro vložení videa do stránky. Tento filtr lze vyvolat kdekoliv v obsahu, tedy v kterémkoliv souboru s koncovkou \texttt{.md}. Za názvem filtru se do závorky uvádí parametry oddělené čárkou. U posledního parametru se čárky nevuvádí, což platí i v případě, kdy se uvádí pouze jeden parametr, jako je tomu v příkladu \ref{lst:vyvolani-filtru}.
\begin{lstlisting}[label=lst:vyvolani-filtru,caption=Vyvolání vlastního filtru s jedním parametrem]
{{ video(src="video.webm") }}
\end{lstlisting}
V rámci vybraného generátoru není nutné specifikovat atributy na jeden řádek a lze je pro přehlednost vypisovat na více řádků, jako tomu je například u programu \ref{lst:formatovani-atributu}, zůstane-li dodržena koncepce oddělení atributů čárkou, tedy že poslední atribut vždy zůstane bez čárky. Výstupem této direktivy bude následující HTML kód.
\begin{lstlisting}[caption=Výstup direktivy z příkladu \ref{lst:jednoduchy-filtr}]
<video controls><source src="video.webm"></video>
\end{lstlisting}
Součástí požadavků pro modelový web jsou i citace přiložených souborů a videí. Existující filtr je tedy třeba rozšířit o možnost přiložení různých metadat. Tato metadata ovšem nejsou pro vložení videa povinná. Ve specifikaci vlastních filtrů lze využívat všechny operátory, které generátor nabízí. Nejlepším přístupem k tomuto problému je tedy využití jednoduchých podmínek, které kontrolují, zda je každá z hodnot zadána jako parametr a v případě že ano, vepíše se do obsahu. Atributy ošetřené podmínkami tedy nejsou povinné, zatímco nevyplněný atribut \texttt{src} by při generování vyvolal chybu. V následujícím příkladu jsou přidány podmínky pro kontrolu a případné vložení, jimiž jsou název videa (\texttt{title}), jméno autora (\texttt{author}) a rok vytvoření (\texttt{year}).
\begin{lstlisting}[label=lst:filtr-s-podminkami,caption=Filtr pro vkládání videa s využitím podmínek]
<video controls><source src="{{ src }}"></video>
{% if title or year and author %}
<div class="metadata">
{% if title %}{{ title }}{% endif %}
{% if author and year %}
({{ year }}, {{ author }})
{% endif %}
</div>
{% endif %}
\end{lstlisting}
Filtr je možné opět vyvolat pomocí stejné direktivy kdekoliv v obsahu, ovšem nyní lze libovolně přidat parametry pro metadata.
\begin{lstlisting}[label=lst:formatovani-atributu,caption=Vyvolání filtru \ref{lst:filtr-s-podminkami} s formátováním na řádky]
{{ video(
src="video.webm",
title="Název videa",
author="Jméno autora",
year="2020"
) }}
\end{lstlisting}
Protože byly zadány všechny povinné i nepovinné atributy, výtupem toho filtru budou i části kódu s metadaty.
\begin{lstlisting}[caption=Výstup direktivy z příkladu \ref{lst:formatovani-atributu}]
<video controls><source src="video.webm"></video>
<div class="metadata">
Název videa (2020, Jméno autora)
</div>
\end{lstlisting}
Pro modelový web byla zvážena možnost vypisování obsahu automaticky, tedy že program zkontroluje složku s obsahem a pokud narazí na soubor se specifikovanou koncovkou, vypíše jej do obsahu podle daných pravidel. Generátor Zola umožňuje prohledávání složek a práci se soubory, v rámci Zoly takzvanými \uv{assety}. Tuto funkcionalitu lze tedy implementovat jednoduchým cyklem a filtem, které zpracují všechny případné soubory ve složce dané stránky. Zoubory lze filtrovat mnoha způsoby, z nichž je nejuniverzálnější funkce \texttt{matching()}, která dovoluje filtrovat vstup regulárními výrazy dle implementace regex v jazyce Rust\footnote{\url{https://docs.rs/regex/1.3.6/regex/}}. V následujícím příkladu je pro ilustraci této funkcionality implementován program vypisující obrázky s předem definovanými koncovkami.
\begin{lstlisting}[caption=Automatický výpis obrázků s pevně definovanými koncovkami]
{% if section.assets %}
{% for asset in section.assets %}
{% if asset is matching("\.(?i:jpg|gif|png)$") %}
<img src="{{ get_url(path=asset) }}" alt="{{ asset }}">
{% endif %}
{% endfor %}
{% endif %}
\end{lstlisting}
Toto řešení ovšem není ve výsledném modelu implemntováno, protože jedním z požadavků je možnost vkládání souborů na libovolné místo v obsahu. Na stejném principu je ovšem vytvořen filtr pro vládání souborů, který tento požadavek splňuje. Výhodou filtru je, že ho lze vyvolat kdekoliv v obsahu a není vázán na pevně dané místo v šabloně. Ten očekává alespoň jeden parametr uvádějící název souboru bez koncovky, pro dle kterého pak filtr vyhledá všechny různé formáty s tímto názvem a ty vloží do stránky. Druhým libovolným parametrem je název souboru, který se do stránky vloží místo názvu souboru. to umožňuje uivateli volně pracovat s názvy souborů v souborvé struktuře bez ovlivnění obsahu stránky.
\begin{lstlisting}[label=lst:filtr-souboru,caption=Filtr pro výpis souborů s automatickým hledáním]
{% if section.assets and filename %}
<div class="file">
<div class="title">
{% if title %}
{{ title }}
{% else %}
{{ filename }}
{% endif %}
</div>
{% for asset in section.assets %}
{% if asset is matching(section.path ~ filename ~ "\..*$") %}
<a href="{{ get_url(path=asset) }}" class="format">{{ asset | split(pat=".") | last }}</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
\end{lstlisting}
V první části filtr zkontroluje, zda byl vyplněn parametr \texttt{title} a v přípdě že ano, nastaví ho jako název souoru v obsahu. V opačném případě využije název souboru samotného. Ve druhém kroku nastává kontrola, zda se ve složce nacházejí soubory (mimo hlavní soubor \texttt{\_index.md}) a pokud ano, přes všechny soubory se iteruje kontrola, zda soubor splňuje podmínku názvu. Kontrola této podmínky je tvořena kombinací proměnných generátoru a regulárního výrazu. Každý soubor který splňuje podmínku je poté vypsán do obahu jako přímý odkaz k jeho stažení.
Jako text v odkazu se použije koncovka souboru, která se záskává spojením několika filtrů, tedy filtru \texttt{split(pat=".")}, který rozdělí řetězec podle znaku tečka do pole a navazující filtr \texttt{last} vrátí poslední položku v poli. Tím filtr získá samotnou koncovku souboru.
Filtr lze vyvolat stejně, jako je tomu u filtru pro vkládání videa. Název filtru je opět definován názvem souboru \texttt{tmeplates/shortcodes/document.html} a bude jím tedy název \texttt{document()}.
\begin{lstlisting}[label=lst:vyvolani-filtru-souboru,caption=Vyvolání filtru \ref{lst:filtr-souboru}]
{{ document(
filename="pracovni-list",
title="Pracovní list"
) }}
\end{lstlisting}
V příkladu \ref{lst:vyvolani-filtru-souboru} je definován i nepovinný atribut \texttt{title}, který kvůli přehlednosti umožňuje nastavit název. Atribut \texttt{filename} definuje název souboru ve složce bez koncovky. Všechny soubory, které chce uživatel vypsat, musí tedy mít stejný název a musí se lišit pouze koncovkou. Jsou li ve složce soubory s názvem \texttt{pracovni-list} a koncovkami \texttt{pdf}, \texttt{odt}, \texttt{djvu} a \texttt{ps}, bute výstupem filtru následující HTML.
\begin{lstlisting}[caption=Výstup direktivy z příkladu \ref{lst:vyvolani-filtru-souboru}]
<div class="file">
<div class="title">Pracovní list</div>
<a href="pracovni-list.pdf">pdf</a>
<a href="pracovni-list.odt">odt</a>
<a href="pracovni-list.djvu">djvu</a>
<a href="pracovni-list.ps">ps</a>
</div>
\end{lstlisting}
\section{Optimalizace}
Optimalizace modelové implementace je provedena na základě článku ze serveru \cite{calomel_optimization}, který se věnuje sestavením užitečných rad pro optimalizaci webových stránek na serverech s omezeným připojením do sítě a pro zvýšení spokojenosti uživatelů z užívání optimalizovaného webu, jak je rozebráno v sekci \ref{kap:vyhody-statickych-webovych-stranek}.
Jak se na webu Colomel píše, provozování webserveru může být hodnotná zkušenost, ale zároveň může být i zkouškou trpělivosti. Chcete svým uživatelům předávat všechny vaše stránky a obrázky, ovšem máte jen omezenou šířku pásma, pomocí které můžete data přenášet. Pokud přetížíte své pipojení, klienti nevštěvující váš web server si budou myslet, že je pomalý a neresponzivní. Je tedy třeba webový server nastavit tím nejlepším možným způsobem s cílem získat co nejvíce návštěv a zlepšit zážitek vašim návštěvníkům. Následující rady slouží ke snížení zátěže serveru, ke zrcyhlení odesílání stránek a k zastavení nechtěnného a škodlivého provozu.
Práce se věnuje pouze technickým optimalizacím spojených s tvorbou samotné webové stránky, nikoliv však optimalizacím sítě, web serveru a vizuálního návrhu. Nenačítá-li se stránka během několika vteřin, většina uživatelů jednoduše odejde. Cílem této sekce je provést optimalizace, které urychlí načítání modelové implementace.
\subsection{Typy a kvalita obrázků}
\subsection{Ikona \textit{favicon.ico}}
\subsection{Obecné HTML optimalizace}
\todo[inline]{\url{https://developer.yahoo.com/performance/rules.html}}
\subsection{Videa a jejich vložení do stránky}
\todo[inline]{Váhody CDN a problematika sledování uživatelů.}
\section{Správa obsahu a verzování}
Statické stránky neumožňují správu uživatelů v prámci webové aplikace, tedy že se případný editor nebo administrátor přihlásí a upravuje obsah klikáním, či psaním ve WYSIWYG\footnote{What You See Is What You Get -- Princip editoru který během psaní formátuje text tak, jak bude ve výsledku vypadat, například LibreOffice Writer atd.} editoru. Správu uživatelů lze jednoduše řešit omezením přístupu na web server, kde jen oprávnění uživatelé mohou do obsahu zasahovat. To je ovšem velmi tězkopádné řešení, protože neumožňuje práci více uživatelům najednou a neudržuje předešlé verze obsahu a historii úprav. Lepší alternativou je využití některého verzovacího systému. Pro účely modelové implementace byl vybrán distribuovaný verzovací systém Git, jak je vysvětleno v sekci \ref{kap:vyber-vhodneho-systemu}.