You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
bakalarka/kap-modelova-implementace.tex

390 lines
36 KiB
TeX

\chapter{Modelová implementace}\label{kap:modelova-implementace}
Tato část práce se věnuje tvorbě modelové implementace systému pro generování sta\-tických webových stránek dle definovaných požadavků v~kapitole \ref{kap:taxonomie-pozadavku}. Jsou zde vybrány vhodné součásti, ze kterých je modelová implementace složena.
\section{Výběr vhodného systému}\label{kap:vyber-vhodneho-systemu}
Modelový systém se skládá ze dvou částí, a to z~verzovacího systému pro správu obsahu a generátoru statického HTML. Obě tyto součásti jsou vybírány na základě poznatků z~předchozích částí práce.
\subsection{Verzovací systém pro správu obsahu}\label{kap:vyber-vhodneho-systemu-verzovani}
Pro správu obsahu i šablon a statických souborů byl zvolen distribuovaný verzovací systém Git, jenž má v~porovnání s~jinými verzovacími systémy, zejména centralizovanými, spousty výhod. Jeho hlavní výhodou je rozšířené využití v~praxi a snadné používání. Díky svým decentralizovaným vlastnostem ho lze využívat v~mnoha odlišných pracovních postupech. S~naklonovaným repozitářem lze pracovat i bez připojení k~síti, což lze považovat i za druh zálohy. Git také umožňuje slučování různých změn od mnoha uživatelů a dovoluje jednoduše řešit potenciální konflikty. \citep{why_is_git_better_than_x}
Skvěle využitelnou funkcí pro modelovou implementaci je také to, že po provedení změn v~repozitáři lze pomocí Gitu spouštět skripty, které mohou provádět automatické gene\-rování obsahu a další užitečné operace. Tato funkcionalita je implementována v~rámci modelové implementace v~sekci \ref{kap:automaticke-generovani-obsahu}.
\subsection{Generátor statického webu}
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/}}.
Vybraný generátor splňuje všechny požadavky z~kapitoly \ref{kap:taxonomie-pozadavku} a oproti jiným systémům je výhodný tím, že je napsaný v~jazyce Rust. Je tedy mnohem rychlejší a bezpečnější než většina jeho alternativ \citep{benchmarks_game}. Tato výhoda v~rychlosti se znatelně projevuje při zpracování obsáhlých webových stránek či mnoha obrázků.
V~rámci generátoru je využíván značkovací jazyk Markdown, který je snadný a velmi rozšířený. Kromě těchto výhod si zachovává většinu funkcí a rysů, jež lze najít v~ostatních složitých systémech. Zároveň je možné generátor zkompilovat\footnote{Spustitelné programy musí být převedeny do strojového kódu procesem zvaným \textit{kompilace}.} do jednoho staticky linkovaného\footnote{Spustitelný soubor má všechny potřebné knihovny integrované v~sobě.} binárního souboru, s~nímž se pracuje mnohem lépe než se složitým frameworkem.
\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~tak\-zvanou \textit{sekcí} a \textit{stránkou}.
\textit{Stránka} slouží pouze k~předání obsahu a nikoliv k~dalšímu větvení struktury. Dá se tedy říci, že stránka v~rámci stromové struktury reprezentuje konec větve.
Každá \textit{sekce} může mít vlastní obsah, ovšem může obsahovat i další subsekce, pomocí nichž lze obsah ve stromové struktuře větvit. Kořenem celého stromu je speciální sekce s~názvem \textit{index}.
Pro každou část se obvykle používá vlastní HTML šablona, ovšem není to pravidlem a každá část větve může využívat šablonu jinou. To je užitečné například u~stránek s~různými druhy obsahu. V~rámci modelového webu zůstává druh obsahu stejný a není tedy třeba se odchylovat 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 jako základ, jež 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é její části a nedefinují celou strukturu znovu.
Generátor Zola v~šablonách hledá vlastní řídící sekvence, jež se popisují kombinací složených závorek a dalších znaků. 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, jenž 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 pro kterou 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}
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í 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 generátoru, 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 tyto řídící sekvence, nebo také \textit{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 se výstup jednoho příkazu 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 řetězec s~názvem 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}
Generátor umožnuje v~šabloně vytvářet speciální bloky, jejichž obsah lze v~navazujících šablonách měnit. Je tedy možné měnit části struktury šablony. K~vysvětlení principu fungování bloků je název stránky v~příkladu \ref{lst:bloky} obalen blokem \texttt{title} a do těla vložen 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, podobně 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 modelový 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~důvodu přenositelnosti systému a principu staticky generovaných stránek 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}
Obsah modelové implementace je dělen do stromové datové struktury o~potenciá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 se navigace generovala v~návaznosti na aktivní cestu ve stromě. Ve stránce jsou dvě různé navigace: hlavní, která je vždy viditelná a obsahuje rozdělení obsahu dle škol, a vedlejší, jež zobrazuje aktivní větev stromu.
\begin{figure}[h]\centering
\includegraphics{img/generovani-vicevrstve-navigace}
\caption{Diagram průběhu generování vícevrstvé navigace}
\end{figure}
První vrstvou struktury jsou hlavní sekce, v~rámci implementace pojmenované jako $L_1$, které jsou vždy vypsány ve vlastní navigaci. Pod touto navigací je zobrazen 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 všechny podkategorie ve vrstvě $L_3$. Takto lze stromem procházet potenciálně donekonečna. Styly modelové šablony ovšem počítají s~maxi\-mální hloubkou čtyř subkategorií.
Tato funkcionalita je implementována pomocí tří cyklů, z~nichž jeden je vložený. První cyklus (příklad \ref{lst:obsah-cyklus1}) se provádí pro všechny rodiče aktivní kategorie 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 pomocí vnořeného cyklu všechny subkategorie. Ve 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}\label{kap:rozsireni-sablony}
Ve výchozím stavu neumí generátor zpracovávat nic jiného, než co 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ů, jež se v~rámci generátoru nazývají \textit{shortcode}.
Principem vlastních filtrů je to, že si uživatel vytvoří vlastní šablonu, kterou lze vyvolat pomocí speciální řídící sekvence 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. Dá se tedy říci, že shortcode je ve své podstatě funkce, jež umí pracovat s~parametry.
Pro tvorbu těchto filtrů je v~generátoru Zola určena složka \texttt{templates/shortcodes} obsahující jejich HTML šablony a kód pro zpracování generátorem. Název HTML souboru definuje název vlastního filtru. Vytvoříme-li uvnitř 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}, v~nichž je uložen obsah. Za názvem filtru se do závorky uvádí parametry. Pro lepší přehlednost lze parametry oddělovat čárkou, což ovšem není pro správné zpracování generátorem nutné.
\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 další zpřehlednění vypisovat na více řádků, stejně jako v~příkladu \ref{lst:formatovani-atributu}. 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, jež 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 opět možné vyvolat pomocí stejné direktivy kdekoliv v~obsahu, ovšem nyní lze libovolně přidávat 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ýstupem 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 projde 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, pro které se v~rámci Zoly používá termín \uv{asset}. Tuto funkcionalitu lze tedy implementovat jednoduchým cyklem a filtrem, které zpracují všechny případné soubory ve složce dané stránky. Soubory lze filtrovat mnoha způsoby, z~nichž je nejuniverzálnější funkce \texttt{matching()}, která dovoluje filtrovat vstup regulárními výrazy dle jejich implementace 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 implementová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 vytvořen filtr pro vklá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ě. Filtr očekává alespoň jeden parametr uvádějící název souboru bez koncovky, podle něhož pak 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 uživateli volně pracovat s~názvy souborů v~souborové 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}: pokud ano, nastaví ho jako název souboru v~obsahu, v~opačném případě využije název souboru samotného.
Ve druhém kroku nastává kontrola, jestli se ve složce nacházejí soubory (mimo hlavní soubor \texttt{\_index.md}) --- když ano, tak se iterativně zkontrolují všechny soubory, zda splňují 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 obsahu 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ího filtru \texttt{last}, jenž 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{templates/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ý pro přehlednost umožňuje nastavit název. Atribut \texttt{filename} definuje název souboru ve složce bez koncovky. Všechny soubory, jež 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}, bude 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}\label{kap:optimalizace}
Optimalizace 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 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 Calomel píše, provozování webserveru může být hodnotnou 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é připojení, klienti navš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 zrychlení odesílání stránek a k~zastavení nechtěného a škodlivého provozu.
Práce se věnuje pouze technickým optimalizacím spojeným 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ů}
Fotografie a grafika využívají mnohem více dat pro přenos než běžný HTML text a je tedy nutné provést optimalizaci (kompresi) obrázků na co nejmenší možnou velikost souborů. Obrázky není třeba renderovat na více než 72 dpi a pro každý druh grafiky je třeba zvolit vhodný formát, tj. formát JPEG pro fotografie a formáty PNG či SVG pro jednoduchou grafiku. Rastrové obrázky by neměly přesáhnout maximální rozlišení zobrazované na stránce. Klíčové je také nevyužívat obrázky v~případě, kdy je lze nahradit čistým HTML a CSS.
Obrázky ve formátu JPEG mají velice efektivní ztrátovou kompresi, pomocí které lze zredukovat velikost obrázku o~značnou část. Autor článku tvrdí, že většinu obrázků lze komprimovat až o~50\% bez viditelné ztráty na kvalitě. Své obrázky dokonce zkomprimoval ze 27 kilobajtů na pouhých 8 kilobajtů s~JPEG kompresí 60\%.
\subsection{Ikona \textit{favicon.ico}}
Původně je \textit{favicon.ico} výtvorem firmy Microsoft, kdy Internet Explorer automaticky odesílal požadavek na pevnou URL \texttt{/favicon.ico} od kořene webového serveru. Jde o~malou ikonku, jež se dnes zobrazuje u~každé záložky s~webovou stránkou. Problémem je, že se požadavkům na~ní nelze vyhnout a vždy se počítá s~tím, že ikona webu existuje. Odesílá se vždy s~každou stránkou a některé prohlížeče se po ní dotazují z~neznámých důvodu dvakrát. Autor článku uvádí, že u~některých serverů bylo až 30\% přenesených dat využito jen na odesílání ikony.
Principem optimalizace je udržet ikonu co nejmenší, v~nejlepším případě tak malou, aby se vešla do jednoho TCP paketu, tedy do velikosti 1460 bajtů na většině systémů. Toho lze docílit tím, že ikona nebude větší než 16x16 pixelů s~nízkou barevnou hloubkou, nejlépe pouze se~čtyřmi barvami. Také je možné poslat jen 1x1 pixelů veliký prázdný obrázek nebo vracet stavový kód 204\footnote{204 No Content -- Server úspěšně zpracoval požadavek, ale nevrací žádný obsah.} a neodesílat ikonu žádnou.
\subsection{Obecné HTML optimalizace}
Redukcí nepotřebných znaků v~HTML lze také ušetřit značnou část přenesených dat. Nejvhodnější je:
\begin{itemize}
\item nepoužívání HTML komentářů,
\item využití CSS pro formátování stránek,
\item využití oddělovače nebo elementu \texttt{span} namísto tabulek,
\item odstranění přebytečných tagů a prázdných mezer a řádků,
\item vytvoření obrázkových náhledů namísto odesílání obrázků v~plném rozlišení,
\item recyklování již použitých obrázků a tlačítek.
\end{itemize}
K~odstranění přebytečných mezer, zalomení řádků, HTML komentářů a prázdných řádků lze použít automatický filtr pro kompresi výstupu. Toto by se nabízelo jako jedna z~dalších možností pro implementaci rozšíření. Generátor Zola provádí kompresi CSS, ovšem nemá zabudovanou funkcionalitu pro minifikaci výsledného HTML, která je v~době psaní této práce vyvíjena\footnote{\url{https://github.com/getzola/zola/issues/542/}}.
Touto redukcí lze ušetřit 2\% přenosu dat oproti ručně psanému neoptimalizovanému kódu. Je-li průměrná velikost stránky sto kilobajtů, lze touto optimalizací ušetřit dva kilobajty při každém odeslání stránky. Při odeslání sta tisíce stránek za měsíc je ve výsledku ušetřeno dvě stě megabajtů dat, které jsou jinak zbytečně odesílány uživatelům, kteří je stejně nezobrazí.
Další obecné rady pro optimalizaci HTML jsou uvedeny na serveru \cite{yahoo_optimization}, kde se uvádí spousta dalších způsobů ke zrychlení načítání stránky a k~nižšímu vytížení sítě.
Připojením externích CSS a JavaScript souborů je umožněno jejich ukládání do paměti cache, což snižuje HTTP požadavky vůči serveru. Je-li obsah těchto souborů přímo ve stránce, je odesílán pokaždé s~novou stránkou, a to vede ke zbytečnému vytěžování sítě. S~tím souvisí i velikost stránek, kdy se soubory s~větší než danou maximální velikostí do mezipaměti neukládají, a je proto dobré tuto velikost nepřekračovat.
Připojením externího CSS přímo do hlavičky je umožněno progresivní vykreslování webové stránky, které urychluje \uv{Time To First Byte}, viz sekce \ref{kap:vyhody-statickych-webovych-stranek}. Naopak umístěním případných JavaScript souborů až na konec celé stránky se prioritizuje načítání viditelného obsahu před méně důležitými skripty.
\subsection{Optimalizace videa}
Protože v~modelové implementaci jsou do stránky vkládána i videa, je nutné provádět jejich optimalizaci podobně, jako je tomu u~obrázků. Důležité je používat kvalitní kompresi, pouze nutné rozlišení a renderovat videa ve správném poměru stran a bez zbytečných černých okrajů. Při zpracování videa je dobré neprovádět jeho transkódování do jiného formátu z~původního, ovšem zároveň je třeba dbát na kompatibilitu s~prohlížeči, které různé formáty a kontejnery neumí vždy nativně přehrát.
\section{Správa obsahu a verzování}
Statické stránky neumožňují správu uživatelů v~rámci webové aplikace, tedy to, že se případný editor nebo administrátor přihlásí a upravuje obsah klikáním či psaním v~edi\-toru, který během psaní formátuje text tak, jak bude ve výsledku vypadat. Správu uživatelů lze jednoduše řešit omezením přístupu na web server, kde mohou do obsahu zasahovat jen oprávnění uživatelé. To je však velmi těžkopádné řešení, protože neumožňuje práci více uživatelů 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-verzovani}.
V~tomto systému jsou soubory uloženy v~repozitářích, kde každý projekt je vlastní repozitář. V~rámci jednotlivých repozitářů se ukládají všechny změny obsahu prostřednictvím takzvaných \textit{commitů} --- záznamů o~provedených změnách včetně jejich krátkého popisu a autora. Tyto revize lze provádět v~různých větvích repozitáře a větve je možné mezi sebou spojovat a kombinovat. Rovněž je možné se vracet do kteréhokoliv bodu v~historii v~rámci každé větvě.
Nastane-li konflikt při nahrávání změn, umožňuje Git jejich snadné vyřešení. Konflikt je stav, kdy například dva různí uživatelé provedli úpravy na stejném místě stejného souboru a snaží se je nahrát do repozitáře. Git v~tuto chvíli druhého uživatele upozorní, že původní soubor byl změněn a je třeba tento konflikt vyřešit. Zamezuje se tak přepsání změn prvního uživatele.
K~systému Git existují různé služby, jež tento systém rozšiřují o~webové grafické rozhraní s~množstvím dalších rozšíření. Nejčastěji používanými službami jsou GitHub\footnote{\url{https://github.com/}}, GitLab\footnote{\url{https://gitlab.com/}}, nebo Bitbucket\footnote{\url{https://bitbucket.org/}}, z~nichž některé lze provozovat na vlastním serveru. Snadným systémem pro vlastní provozování je také program Gitea\footnote{\url{https://gitea.com/}}, jenž je oproti předem zmíněným systémům zcela svobodným softwarem\footnote{Respektuje základní svobody uživatele (\url{https://www.gnu.org/philosophy/free-sw.html})} a je velmi jednoduchý na instalaci a správu. Tyto systémy mají navíc integrovaný jednoduchý editor pro úpravu souborů přímo z~webového rozhraní a také umí zobrazovat náhled souborů s~obsahem napsaným v~jazyce Markdown, jenž je popsán v~sekci \ref{kap:markdown}.
\subsection{Automatizace generování obsahu}\label{kap:automaticke-generovani-obsahu}
Tato část práce se věnuje samotné implementaci automatického generování obsahu na základě změn v~repozitáři.
Jak bylo zmíněno v~sekci \ref{kap:vyber-vhodneho-systemu-verzovani}, git umožňuje nastavení takzvaných \textit{Git hooks}, jež se v~určité chvíli spustí. Jak uvádí dokumentace\footnote{\url{https://git-scm.com/docs/githooks/}}, existuje spousta druhů hooků, které jsou vyvolány v~různé části zpracování požadavku. V~případě této implementace je nejvhodnější hook \textit{post-receive}, jenž je spouštěn až po nahrání a zpracování všech změn v~repozitáři.
Skript \ref{lst:git-hook-skript} po vyvolání Gitem provede veškeré potřebné operace ke zpracování nového obsahu na web serveru Je složen z~několika částí.
Jako první probíhá na řádcích 1--3 nastavení proměnných, ve kterých se ukládá odkaz na vzdálený Git repozitář, název složky, do níž se obsah má klonovat, a název složky, do které se má kopírovat výstup čili vygenerované HTML. Dále se skript na řádku 5 přepíná do složky, v~níž se sám nachází --- aby skript fungoval vždy, ať je spuštěný ze kteréhokoliv místa v~souborovém systému.
V~další části skriptu probíhá na řádku 7 kontrola, zda již existuje složka s~naklonovaným Git repozitářem. Pokud složka neexistuje, provede se naklonování vzdáleného repozitáře a tím i vytvoření složky.
Třetí část provádí generování statického obsahu. Nejprve se skript přepne do repozitáře, v~němž provede příkaz \texttt{git pull}, který do složky stáhne poslední změny ze vzdáleného repozitáře: synchronizuje obsah na poslední verzi. Po synchronizaci repozitáře proběhne samotné spuštění generátoru, jenž z~obsahu vygeneruje statické HTML, které vloží do složky \texttt{./public}. Poté na řádcích 12--14 probíhá kopírování nově vygenerovaného obsahu do složky \texttt{/srv/www/ucitelonline} včetně nastavení unixových práv souborů na bezpečné hodnoty, které se liší pro složky a pro soubory.
\begin{lstlisting}[
label=lst:git-hook-skript,
caption=Skript pro automatizované generování obsahu,
language=sh,
numbers=left,
stepnumber=1,
firstnumber=0
]
#!/bin/sh -e
GREPO="https://git.microlab.space/pedf/ucitelonline"
GDIR="ucitelonline"
WEBROOT="/srv/www/ucitelonline"
cd $(dirname $0)
[ ! -d "$GDIR" ] && git clone --recursive "$GREPO" "$GDIR"
cd "$GDIR"
git pull
zola build
rsync --recursive --delete --checksum \
--group --groupmap=*:www-data --chmod=D750,F640 \
public/ "$WEBROOT"
\end{lstlisting}
Skript spoléhá na to, že systém má již předem správně nakonfigurované uživatele, uživatelské skupiny a web server, a že jsou nainstalované potřebné programy Git, Rsync a generátor Zola. Systémový uživatel, pod kterým je vyvolán Git hook, musí být ve skupině \textit{www-data}, nebo v~jiné skupině společně s~uživatelem, pod nímž je spuštěn web server. Zároveň musí mít uživatel práva pro zápis do cílové složky, tedy kořenové složky web serveru: \texttt{/srv/www/ucitelonline}.
Ve většině případů by bylo vhodné klonovat a generovat obsah v~dočasné složce, napří\-klad v~\texttt{/tmp}, a po zkopírování souborů do složky web serveru opět zdrojové soubory smazat. To se ovšem v~této implementaci nehodí, protože repozitář se zdrojovými soubory může být velký a jeho klonování může potenciálně zabrat zbytečné množství času, na rozdíl od~příkazu \texttt{git pull}, který pouze stáhne změny. Generátor zároveň při generování zpracuje pouze nutné změny, zatímco po čistém naklonování musí zpracovat celý obsah znovu, což může také trvat dlouho, obzvlášť při zpracování mnoha obrázků. V~této implementaci se tedy zachováním naklonovaného repozitáře výrazně zkracuje čas celého skriptu.