}w !"#$%&'()+,-./012345
Masarykova univerzita Fakulta informatiky
Centralizované CMS s uživatelsky přívětivou správou obsahu Diplomová práce
Bc. Jiří Beneš
Brno, jaro 2014
Prohlášení Prohlašuji, že tato diplomová práce je mým původním autorským dílem, které jsem vypracoval samostatně. Všechny zdroje, prameny a literaturu, které jsem při vypracování používal nebo z nich čerpal, v práci řádně cituji s uvedením úplného odkazu na příslušný zdroj.
Bc. Jiří Beneš
Vedoucí práce: RNDr. Barbora Kozlíková, Ph.D. ii
Poděkování Chtěl bych poděkovat RNDr. Barboře Kozlíkové, Ph.D. za vedení a pomoc při vypracování této práce.
iii
Shrnutí Tato diplomová práce se zabývá implementací CMS navrhnutého jako jednostránková aplikace s uživatelsky přívětivou editací obsahu. Snaží se minimalizovat nevýhody SPA, optimalizovat rychlost načítání stránek a omezit objem dat přenesených mezi prohlížečem a serverem.
iv
Klíčová slova CMS, SPA, contenteditable, AngularJS, MongoDB, REST API, fronta zpráv, PhantomJS, HTML snapshot
v
Obsah 1 2
3
4
5
6
Úvod . . . . . . . . . . . . . . . . Charakteristiky aplikace . . . . 2.1 Přehledná editace . . . . . . . 2.2 Jednostránková aplikace . . . 2.2.1 Výhody SPA . . . . . 2.2.2 Nevýhody SPA . . . . 2.3 Rychlost, přenesená data . . . 2.4 Mikrostránky . . . . . . . . . 2.5 Multidoménové CMS . . . . . Optimalizace pro vyhledávače 3.1 History URL . . . . . . . . . 3.2 Indexace Googlem . . . . . . Databáze . . . . . . . . . . . . . . 4.1 MongoDB . . . . . . . . . . . 4.2 Kolekce . . . . . . . . . . . . 4.2.1 Web . . . . . . . . . . 4.2.2 User . . . . . . . . . . 4.2.3 UserWeb . . . . . . . Dekompozice CMS . . . . . . . 5.1 Frontend . . . . . . . . . . . . 5.2 Backend . . . . . . . . . . . . 5.3 Uživatelův web . . . . . . . . 5.4 Konfigurace webového serveru 5.4.1 Směrování URL . . . . Backend . . . . . . . . . . . . . . 6.1 Slovníček pojmů . . . . . . . 6.2 CakePHP . . . . . . . . . . . 6.2.1 Controllery . . . . . . 6.3 REST API . . . . . . . . . . 6.3.1 Controllery . . . . . . 6.4 Asynchronní zpracování úkolů 6.4.1 PhantomJS . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 4 4 5 5 6 6 7 7 8 8 10 13 13 14 14 15 16 17 17 17 17 18 18 20 20 20 22 25 25 27 27 1
6.4.2 Beanstalk . . . . . . . . . . . . 6.4.3 Asynchronizace . . . . . . . . . 6.4.4 Workery . . . . . . . . . . . . . 6.5 Vytvoření nového webu . . . . . . . . . 7 Prezentační vrstva . . . . . . . . . . . . . 7.1 AngularJS . . . . . . . . . . . . . . . . 7.1.1 Oboustranné provazování dat . 7.2 Směrovač . . . . . . . . . . . . . . . . 7.3 Šablony a dekompozice HTML . . . . 7.4 Controllery . . . . . . . . . . . . . . . 7.5 Zdroje . . . . . . . . . . . . . . . . . . 7.6 Direktivy . . . . . . . . . . . . . . . . 8 Editační rozhraní . . . . . . . . . . . . . 8.1 Principy editace . . . . . . . . . . . . . 8.2 Technologie rozhraní . . . . . . . . . . 8.3 Dekompozice . . . . . . . . . . . . . . 8.4 Příklady šablon pro editaci . . . . . . 8.5 Zpracování chyb API . . . . . . . . . . 8.6 Controllery . . . . . . . . . . . . . . . 8.7 Direktivy . . . . . . . . . . . . . . . . 8.8 Zdroje . . . . . . . . . . . . . . . . . . 9 Developerská sekce . . . . . . . . . . . . 9.1 Controllery . . . . . . . . . . . . . . . 9.2 Zdroje . . . . . . . . . . . . . . . . . . 9.3 Filtry . . . . . . . . . . . . . . . . . . 10 Výkonnostní optimalizace . . . . . . . . 10.1 Kompilování výstupů API . . . . . . . 10.2 Minifikace a sloučení knihoven a stylů 10.3 Memcached . . . . . . . . . . . . . . . 10.4 Browser cache . . . . . . . . . . . . . . 10.5 GZip . . . . . . . . . . . . . . . . . . . 11 Závěr . . . . . . . . . . . . . . . . . . . . . Literatura . . . . . . . . . . . . . . . . . . . . A Elektronická příloha . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28 28 30 31 32 32 33 33 34 35 35 37 38 38 39 40 42 43 44 44 44 45 45 45 46 47 47 48 48 48 48 50 52 53
2
Kapitola 1
Úvod S příchodem technologií HTML5 a jejich rychlou implementací do webových prohlížečů dostávají front-end vývojáři webových aplikací do rukou nástroje, díky kterým mohou přesunout značnou část aplikační logiky ze serveru do prohlížeče. Vznikají čím dál lepší JavaScriptové frameworky, API externích služeb, knihovny umožňující vytvářet a pohodlně udržovat čím dál ambicióznější aplikace, zpracovávané přímo v prohlížeči. JavaScriptový vývojář může například z prohlížeče přistupovat k mikrofonu, webkameře, datové schránce, může využívat akcelerátor grafické karty a asynchronně zpracovávat výpočty. Svou aplikaci může vytvořit v některém moderním frameworku architektury Model-View-Controller, jako například AngularJS 1 , EmberJS 2 , ExtJS 3 , které usnadňují vývoj a zpřehledňují vyvíjenou aplikaci. Pro tyto frameworky existuje mnoho rozšíření, díky kterým lze do aplikace rychle a snadno implementovat Drag and Drop, multiupload souborů, paralax a mnoho dalších funkcionalit. Cílem diplomové práce je využít tyto nové technologie k navrhnutí a implementování CMS (Content Management System, systém pro správu obsahu webu), které poskytne uživateli přívětivé rozhraní pro editaci obsahu. Prezentační vrstva CMS by měla být navrhnuta jako SPA (single page application), tedy aplikace, která zašle uživateli pouze první stránku a následná navigace webem je zpracovávána JavaScriptem a asynchronními požadavky přes API na datové úložiště webu. V následujících kapitolách je popsána architektura CMS, charakteristiky SPA, mechanismy minimalizace nevýhod použití SPA a způsoby optimalizace systému k lepšímu využití benefitů SPA. Bude rozebrána architektura CMS a jeho komponent, popsány principy jeho fungování a použitých knihoven, technologií a frameworků třetích stran.
1. 2. 3.
http://angularjs.org/ http://emberjs.com/ http://www.sencha.com/products/extjs/
3
Kapitola 2
Charakteristiky aplikace 2.1
Přehledná editace
Aplikací pro správu obsahu webu je již na trhu spousta. Přes snahy mnoha jejich vývojářů však nejsou vždy dostatečně uživatelsky přívětivé. S rostoucí složitostí aplikace se z pravidla rovněž zvyšuje její nepřehlednost. Nedostatečně intuitivní pracovní prostředí může uživatele mást a od jeho používání úplně odradit. HTML 5 přichází se specifikací HTML atributu contenteditable. Ten umožňuje editaci textového obsahu neformulářových elementů, jako například div, span, p, přímo v prohlížeči, bez nutnosti použití třetích knihoven. Uživatel tak může editovat obsah HTML dokumentu bez nutnosti přepnutí do oddělené administrativní sekce, či bez nahrazení oblastí pro editaci formulářovými prvky. Z hlediska UX (user experience, uživatelský prožitek) se tak jedná o velký krok kupředu [14]. WYSIWYG (What you see is what you get) editory HTML kódu umožňují upravovat kód bez hlubších znalostí jazyka HTML. Uživatel může HTML elementy vkládat pomocí ovládacích prvků editoru, který kód zobrazí vykreslený prohlížečem. Editory zpravidla pracují s textovými poli (textareas), ale mnoho již mají svou verzi pro contenteditable elementy. Tyto novější editory už jsou součástí několika pluginů pro populární CMS, ale zpravidla nevyužívají jejich plného potenciálu. Pluginy neúměrně snižují výkon aplikace, nebo nekombinují další HTML 5 technologie k dosáhnutí většího uživatelského komfortu. Vyvíjené CMS, které tvoří obsah této diplomové práce, by tedy mělo mít implementovanou editaci obsahu webu neformulářovými elementy s contenteditable atributy s editory WYSIWYG. Uživatel by měl mít možnost přeskládat tyto elementy ručně pomocí techniky Drag and Drop. Výsledné CMS bude pro uživatele intuitivní, přehledné a bude moct obsah editovat tak, jak jej vidí nepřihlášený uživatel. 4
2. Charakteristiky aplikace
2.2
Jednostránková aplikace
Nyní je vhodné si zadefinovat pojem AJAX, za kterým se schovává funkcionalita, nad kterou jsou jednostránkové aplikace vytvořeny. Jedná se asynchronní XMLHttpRequest vyslaný JavaScriptem, jehož výsledek je opět zpracován JavaScriptem [15]. Může se jej využít například k načtení nových příspěvků diskuzního fóra bez nutnosti znovu načíst celou stránku, odeslání a zpracování formuláře na pozadí či logování všech kliknutí uživatele na stránce. Prezentační vrstva bude naimplementována jako SPA, tedy jednostránková aplikace. U staršího vícestránkového modelu byla nutnost při navigaci skrze web vždy načítat celý obsah konkrétní stránky. S příchodem AJAXu a jeho podporou v prohlížečích tato nutnost odpadá. Je možné načíst pouze první stránku, její skripty a styly, a při přechodu na jinou stránku prohlížeči zaslat pouze změněný obsah.
2.2.1 Výhody SPA Mezi výhody tohoto řešení patří rychlost. První vykreslení stránky může být pomalejší než u klasických vícestránkových webů, ale následná navigace skrz web je rychlejší. Zpravidla jsou na straně serveru výkonově a paměťově méně náročné a zasílají prohlížeči pouze potřebná data bez duplicit. Odpadá tak nutnost ze strany klienta stahovat opakující se kód, jako je například hlavička a patička webu. S tím souvisí další parametr a tím je velikost přenesených dat, což lze docenit zejména na mobilních zařízeních. Prohlížeč nemusí pokaždé vykreslit celou stránku, ale pouze jednotlivé části. Uživatele tak při prohlížení webu díky tomu nemusí rušit problikávání celého webu. Další výhodou je lepší práce s cachováním webu. Pokud je potřeba provést výkonové optimalizace kódu na straně serveru, je tak nutno se zaměřit pouze na API pro získávání dat z databáze, se kterou SPA komunikuje. Styly, skripty, šablony webu a další statické soubory se mohou cachovat v prohlížeči, spojovat, komprimovat, atp. U klasických vícestránkových aplikací je serverová optimalizace mnohem složitější. Například šablony webu nelze ukládat do cache prohlížeče a při každém požadavku se zpracují na straně serveru a jsou znova zaslány. 5
2. Charakteristiky aplikace 2.2.2 Nevýhody SPA SPA mají také mnoho nevýhod. S rychlým vývojem webových prohlížečů jich však lze čím dál více odstranit. Celá výsledná aplikace je závislá na JavaScriptu. Ten musí mít webový prohlížeč povolen. V posledních letech jej ale naprostá většina uživatelů povolen má. Problém ale nastává ve chvíli, kdy dojde k fatální chybě v kódu aplikace. Uživateli se nemusí zobrazit žádný obsah. Problém lze pokládat za ekvivaletní k problému, kdy dojde k chybě v kódu vícestránkové aplikace na straně serveru, a tudíž je sporné, jestli se skutečně jedná o chybu tohoto modelu. Chybu ale může v případě CMS způsobit například uživatelova knihovna. U moderních frameworků, jako jsou AngularJS či Ember, nemusí nutně tyto chyby způsobit pád aplikace. Aplikace je závislá na použití moderního webového prohlížeče, který podporuje vybrané HTML 5 technologie. Díky automatickým aktualizacím naprostá většina uživatelů prohlížečů Chrome, Firefox, Opera a Safari používá vždy jedny z nejnovějších verzí. Starší verze Internet Exploreru nemusí podporovat všechny závislosti, ale na mnoho z nich již existují knihovny k zaručení zpětné kompatibility a navíc počet uživatelů tohoto prohlížeče neustále klesá [16]. První SPA aplikace používaly k uchování svého stavu kotvy (část URL za znakem #), což není ze sémantického hlediska správné a URL z pohledu uživatele nevypadaly hezky. Jiná alternativa neexistovala, protože při změně jiné části URL stránky prohlížeče posílají serveru nový HTTP požadavek a v důsledku toho se celá stránka načte znova. HTML 5 však přichází s History API, díky kterým lze použití kotev nahradit za plnohodnotné URL. Problematika bude hlouběji rozepsána v následující kapitole. Za hlavní problém u CMS navrhnutého jako SPA lze pokládat SEO (Search Engine Optimization), tedy optimalizaci pro webové vyhledávače. Roboti procházející weby zpravidla nedovedou zpracovat JavaScript a tudíž se jim žádný obsah nezobrazí. Tento problém již lze také ve vybraných vyhledávačích řešit, implementace však není triviální. Tato problematika bude v následujících kapitolách rozebrána detailněji.
2.3
Rychlost, přenesená data
Jednostránkové aplikace, jak již bylo zmíněno, dávají větší prostor pro optimalizace rychlosti vykreslování stránek a pro minimalizaci velikosti přenesených dat ze strany serveru prohlížeči. CMS bude implementovat několik metod jak toho dosáhnout, budou popsány v kapitole o optimalizacích. 6
2. Charakteristiky aplikace
2.4
Mikrostránky
Aplikace sama, jakožto CMS, bude unikátní kombinací použitých technologií vhodná ke správě malých webů. Vývoj SPA CMS pro robustní weby je náročné a v některých směrech technologicky omezené. Velké CMS by mohlo stírat výhody SPA.
2.5
Multidoménové CMS
Výsledná aplikace, po odstranění negativních aspektů SPA a provedení optimalizací, bude náročná na nastavení serveru a jeho správu. Vzhledem k tomu, že je určená pro malé weby, je vhodnější CMS koncipovat jako aplikaci, která bude spravovat více webů na různých doménách zároveň. Klient, který CMS bude chtít využít, se bude moct spolehnout na servis poskytovatele CMS. CMS bude obsahovat nástroje pro snadné vytvoření nového webu a nahrání knihoven klienta, aby byl web rychle připraven k použití.
7
Kapitola 3
Optimalizace pro vyhledávače Před samotným popisem problematiky optimalizace pro vyhledávače uvedeme stručný slovníček pojmů, které se v této kapitole budou využívat. •
Hezká URL – obsahuje #! fragment např. www.example.com/#!home
•
„Škaredá“ URL – obsahuje proměnnou _escaped_fragment_ např. www.example.com/?_escaped_fragment_=home
•
Čistá URL – URL nesplňující podmínky z dvou předchozích pojmů např. www.example.com
•
DOM – objektový model dokumentu (Document Object Model) je objektová reprezentace HTML dokumentu
•
Google Webmaster Tools1 – nástroje poskytované společností Google, které mohou pomoci webovým vývojářům správně nakonfigurovat web pro Google roboty a vyhnout se případným penalizacím ve výsledcích vyhledávání
3.1
History URL
Webům, kterým je během navigace obsah načítán AJAXem, je potřeba vytvořit mechanismus, díky kterému si aplikace uchová svůj stav. Bez něj by se po znovunačtení webu uživateli zobrazila opět úvodní stránka a provedené změny by byly ignorovány [8]. Uživatel by byl ochuzen o použití historie prohlížeče, tedy tlačítek vpřed a zpět, které by nefungovaly intuitivně. V případě použití tlačítka zpět by byl uživateli zobrazen web z předchozí domény. Rovněž je žádoucí, aby mohl uživatel s ostatními sdílet odkaz na současný stav webu, nebo jej mohl uložit do záložek prohlížeče. 1.
https://www.google.com/webmasters/tools/
8
3. Optimalizace pro vyhledávače Před příchodem HTML 5 k tomuto účelu šlo využít pouze URL fragment (část URL za znakem #, který je primárně určen k odkazu na konkrétní element HTML stránky). URL fragment je jediná část URL, jejiž změna nevyvolá zaslání nového požadavku serveru. Toto řešení vyhovuje všem kladeným požadavkům, ale je pro uživatele méně přívětivé. Ze sémantického hlediska není čisté, což má za důsledek, že neumožňuje vyhledávačům indexovat stav. V HTML 5 může být řešení s URL fragmentem vyměněno díky rozšíření History API. DOM objekt window poskytuje objekt history, který uchovává informace o historii navštívených stránek v záložce prohlížeče [12]. V historii se lze pohybovat pomocí metod history objektu. Metoda void back() slouží pro návrat na předchozí stránku, void forward() pro přechod o jednu stránku v historii dopředu, nebo přeskočit o počet stránek vpřed či vzad metodou void go(optional long delta). Spuštění následujícího JavaScriptového kódu v prohlížeči provede přesun v historii zpět o 3 poslední navštívené URL. window.history.go(-3);
HTML 5 definuje nové metody: void pushState( any data, DOMString title, optional DOMString? url = null); void replaceState( any data, DOMString title, optional DOMString? url = null);
Metoda pushState změní URL bez zaslání HTTP požadavku a do history objektu vloží nový záznam. Má-li například uživatel zobrazenou stránku www.example.com/first.html a spustí v prohlížeči následující JavaScriptový kód, obsah stránky se nijak nezmění, aktualizuje se history objekt a URL bude změněna na www.example.com/second.html. var stateObj = { foo: "bar" }; history.pushState(stateObj, "page 2", "second.html");
Návratem tlačítkem zpět prohlížeče se změní URL zpět. Metoda replaceState funguje obdobně jako pushState, nevkládá však do historie nový objekt, ale nahrazuje současný. History API tedy poskytuje metody pro jednostránkové aplikace, díky kterým mohou využívat plnohodnotné URL jako klasické aplikace. Je však na vývojáři aby, zajistil konzistentní obsah webu, když uživatel navštíví konkrétní URL přímo a ne pomocí přesměrování SPA. 9
3. Optimalizace pro vyhledávače
3.2
Indexace Googlem
Donedávna představovalo indexování jednostránkových aplikací problém, protože roboti webových vyhledávačů nedokáží zpracovat JavaScript. Z nejpopulárnějších vyhledávačů zveřejnil návrh na řešení tohoto problému Google, následně jej i implementoval a společně s ním i několik dalších vyhledávačů. Spočívá v poskytnutí alternativního obsahu stránek pro vyhledávače. V následujícím textu se zaměříme pouze na toto řešení [4]. Řešení vzniklo v době, kdy SPA používaly k uchování stavu URL fragment (#). Ten byl primárně určen jako kotva, tedy odkaz na konkrétní kus textu stránky. http://www.example.com/home.html#section-2
URL výše tedy odkazuje na druhou sekci domovské stránky domény www.example.com. Prvním krokem bylo oddělení sémantiky uchovatele stavu SPA od kotev. Toho bylo docíleno záměnou značky # za #!. URL fragment začínající vykřičníkem nyní Google vnímá jako stav stránky. http://www.example.com/home.html#!section-2
URL výše se dá chápat jako domovská stránka domény www.example.com, na které je AJAXem načten obsah druhé sekce. Google obsah těchto #! fragmentů přemapuje na hodnotu URL parametru _escaped_fragment_ a dotáže se serveru na tuto novou URL, která bude mít v našem případě následující tvar. http://www.example.com/home.html?_escaped_fragment_=section-2
Na této URL může developer SPA poskytnout alternativní obsah pro stránku s načtenou druhou sekcí. Zpravidla se jedná o vygenerovaný HTML snapshot požadované stránky. Diagramy 3.1 a 3.2 znázorňují komunikaci prohlížeče a Google robota se serverem při návštěvě SPA využívající #! fragment. Popsané řešení však nepokrývá stránky bez #! fragmentu, které mohou rovněž mít AJAXem načítaný obsah. Typicky se jedná o výchozí stránku domény, např. na následující URL. http://www.example.com/
10
3. Optimalizace pro vyhledávače
Obrázek 3.1: Komunikace běžného uživatele
Obrázek 3.2: Komunikace Google robota Aby nemusela obsahovat prázný fragment, Google doporučuje vložit následující HTML meta tag do HTML hlavičky webu. <meta name="fragment" content="!">
Tag má vždy tento tvar se stejnými hodnotami atributů. Nedoporučuje se ho přidávat i na stránky, které fragment obsahují. Při použití metody pushState History API, popsané v předchozí podkapitole, by pro změnu URL jednostránkové aplikace měl být meta tag obsažen ve všech stránkách. Diagram 3.3 zobrazuje komunikaci Google robota se stránkou obsahující meta tag. Google ve svých Webmaster tools poskytuje službu Fetch as Google, která dovoluje zadat URL webu a zobrazit její zdrojový kód tak, jak jej vidí Google robot. Po zadání URL obsahující #! fragment zobrazí obsah stránky s přemapovanou URL pomocí _escaped_fragment_. Nedostatek této služby je, že umí zobrazit pouze obsah pro přemapované 11
3. Optimalizace pro vyhledávače URL podle fragmentu v URL. Služba zdrojový kód neparsuje a HTML meta tagy ignoruje. Nelze tedy pomocí ní testovat SPA používající novou History API.
Obrázek 3.3: Komunikace Google robota se serverem při dotazu na čistou URL
12
Kapitola 4
Databáze V této kapitole se zaměříme na výběr databáze, budou popsány její charakteristiky a důvody, proč byla vybrána. Poté bude popsán návrh databázové struktury CMS.
4.1
MongoDB
K ukládání dat webů bude sloužit databáze MongoDB1 z rodiny NoSQL databází. Na rozdíl od tradičních relačních databází MongoDB neukládá data do tabulek, nýbrž do kolekcí, které jsou ve formátu BSON (binární JSON). Tento formát je nadmnožina JSONu. Obsahuje navíc například datový typ datum a binární data [10]. Zatímco se tabulkám musí navrhnout struktura, kterou pak splňuje každý jejich záznam, kolekce obsahují záznamy s heterogenní strukturou a mohou se od sebe lišit. Záznamy tabulky jsou n-tice z jednoduchých datových typů, jako je číslo, text, řetězec, datum atp. Záznamy kolekcí mohou obsahovat například i vnořená pole do libovolné hloubky, regulární výrazy a JavaScriptový kód. Velkou výhodou databáze MongoDB je právě heterogenní struktura záznamů. Tam, kde v relačních databázích byla nutnost datové objekty dekomponovat do několika tabulek a záznamy navzájem provázat, v MongoDB lze objekty ukládat v nedekomponované formě tak, jak jsou. Tímto lze v jistých případech podstatně zvýšit výkon celé databáze. Hlavní nevýhodou MongoDB je absence operací typu JOIN, známých z SQL. Databáze by měla být navrhována tak, aby tuto operaci nebylo potřeba provádět. Toho, co by se dalo v relačních databázích označit jako relace 1:m, lze díky heterogenní struktuře kolekcí MongoDB triviálně docílit uložením již „spojených“ záznamů. Jeden záznam by tedy mohl být uložen tak, že v kořenu BSONu by byl objekt s paritou 1 a ostatní záznamy by tvořily jeho listy. Problém nastává u relací n:m. Ty je nutno řešit individuálně. Řešení je 1.
http://www.mongodb.org/
13
4. Databáze často svázáno s vyššími požadavky na režii při aktualizaci dat, v některých případech i na jejich získávání, a navíc může být ohrožena jejich integrita. Je však možné tuto relaci nasimulovat vazební kolekcí a se správným použitím indexů nebude výkon znatelně degradován. NoSQL databáze byla pro CMS vybrána kvůli vyššímu výkonu, protože není zatížena prováděním operací JOIN na 1:n relacích. Ten by při použití SQL databáze mohl být postižen hlavně v případech, kdy budou v databázi uložena data pro velké množství webů. Z datové struktury webu popsané v následující podkapitole bude patrné, že seskládání záznamů pro konkrétní web za použití relačních databází by mohlo být časově náročné.
4.2
Kolekce
Databáze CMS je rozvrhnuta do tří kolekcí. První se jmenuje Web a jeden její záznam obsahuje všechna data od konkrétního webu. Druhá kolekce se jmenuje User a obsahuje data o uživatelích. Poslední kolekce slouží k nasimulování m:n relace mezi webem a uživatelem pro potřeby autorizace. Diagramy v této podkapitole popisují stromové struktury těchto kolekcí. 4.2.1 Web Stromový záznam kolekce Web, znázorněné na obrázku 4.1, ve svém kořenu obsahuje jednoduché informace o webu, jako je jméno domény webu a příznak, jestli je aktivní či zablokovaný. Podstrom meta obsahuje meta informace o webu, tedy klíčová slova, titulek a popis. Podstrom s kořenem pages obsahuje meta informace o jednotlivých stránkách webu a jejich obsah. V hloubce 0 tohoto podstromu je uložena URL stránky, titulek, text odkazu a identifikátor. Textový obsah stránky je rozčleněn do sekcí, které obsahují snippety. Každý snippet si drží jméno svého typu a pole hodnot snippetu. Web může obsahovat snippet, který se bude vyskytovat na více stránkách a se stejnými hodnotami. Pokud bude editován na stránce jedné, jeho hodnoty se zkopírují i do ostatních stránek. Takový snippet se nazývá globální. Bylo by vhodné, kdyby v záznamu kolekce mohlo být více stránek provázaných se stejným objektem snippetu. Stromová struktura kolekce by ale byla narušena a vznikla by tak obecná grafová struktura, kterou MongoDB nepovoluje. Tento problém je vyřešen vyčleněním hodnot globálních snippetů do vlastního podstromu kolekce global_snippets a se snippety stránek je provázán identifikátory. 14
4. Databáze Kolekce Web si udržuje seznam šablon svých stránek a snippetů v objektech template a snippet. V podstromu assets jsou vedeny celkové seznamy uživatelských CSS a JS knihoven. Ty jsou označeny jako css_all a js_all. Uživatel bude moci tyto knihovny deaktivovat, aby se ve webu nevypsaly. Seznamy aktivních knihoven jsou uchovávány v objektech css a js.
Obrázek 4.1: Kolekce Web
4.2.2 User Kolekce User (obrázek 4.2) obsahuje uživatelské jméno a heslo, které jsou potřebné k autentizaci. Je zde veden příznak is_active, kterým lze přístup uživateli zablokovat. Příznak is_admin slouží k označení uživatele s administrátorskými právy. 15
4. Databáze
Obrázek 4.2: Kolekce User 4.2.3 UserWeb Kolekce UserWeb (obrázek 4.3) slouží jako vazební kolekce mezi kolekcí User a Web. Je zde uveden příznak určující míru administrativních omezení.
Obrázek 4.3: Kolekce UserWeb
16
Kapitola 5
Dekompozice CMS CMS bude rozděleno do tří hlavních částí. První z nich bude SPA spustitelná v prohlížeči uživatele. Tuto aplikaci označíme jako Frontend. Jako Backend označíme druhou část a tou je aplikace na straně serveru. Poslední částí je uživatelův Web.
5.1
Frontend
Frontend bude tvořen z Prezentační vrstvy. Tato vrstva bude uživateli zobrazovat konkrétní web pomocí dat získaných z databáze pomocí API z Backendu a souborů z uživatelova Webu. Po autentizaci uživatele bude Prezentační vrstva rozšířena o knihovny umožňující editaci obsahu webu. Tuto část CMS nazveme Editační rozhraní.
5.2
Backend
Kvůli lepší škálovatelnosti a omezení koherence bude Backend rozdělen do několika menších celků vystavěných nad kostrou aplikace rozšířenou o knihovny sdílené skrz aplikaci. Ty budou sloužit například k autentizaci a komunikaci s databází. Prvním z menších celků je API pro komunikaci s databází. Budou jí zasílány požadavky pro získávání a modifikaci dat. Dalším celkem je Developerská sekce sloužící ke správě šablon, skriptů a stylů webu. Tato sekce bude rovněž navrhnuta jako SPA používající API pro komunikaci s databází.
5.3
Uživatelův web
Uživatelův web obsáhne vše, co je specifické pouze pro jeho asociovanou doménu. Budou to například šablony pro SPA, HTML snapshoty pro indexaci vyhledávači, knihovny stylů a JavaScriptů, obrázky, dokumenty atp. 17
5. Dekompozice CMS
5.4
Konfigurace webového serveru
Ještě před samotným rozborem architektury Frontendu a Backendu je potřeba popsat způsob, jakým jsou na ně webovým serverem směrována jednotlivá URL. Je důležité, aby jak zdroje samotné aplikace CMS společné všem doménám, tak i zdroje specifické uživatelovu webu vystupovaly pouze pod uživatelovou doménou. Hlavním důvodem je striktní bezpečnostní politika moderních prohlížečů. V případě, že by se například web dotazoval API na jiné doméně, webový prohlížeč by mohl tyto požadavky zakazovat. URL splňující jistá pravidla budou směrovány na Frontend CMS, jiná na Backend CMS a ostatní na uživatelův web. Za webový server byl zvolen populární Open-source server NGINX1 a to kvůli svému výkonu a bezpečnosti. Pomocí něj budou implementovány směrování URL. Jelikož byl jeden z požadavků na CMS editovat web tak, jak jej vidí nepřihlášený uživatel, bylo nutné vymyslet mechanismus, na jehož základě by bylo zobrazeno Prezentační či Editační rozhraní. Jako nejvýhodnější byl vyhodnocen mechanismus směrování do Editačního rozhraní podmíněné použitím SSL zabezpečení. Vzhledem k tomu, že je CMS určené především na malé weby, omezení s tím spojené byly vyhodnocené jako přijatelné. Jedná se zejména o nemožnost použití SLL při návštěvě nepřihlášeného uživatele. Jakýkoli jiný zásah do URL by znamenal značnou režii na udržení cest ve směrovačích (Routerech) Frontend a Backend částí aplikace. Detekci lze provést i nezávisle na tvaru URL, například pomocí použití COOKIE. I toto řešení má svá negativa, jako je ztížený přechod mezi Frontend a Backend částí. Veškerý přístup k webu pomocí protokolu HTTPS vyžaduje autentizaci uživatele do CMS. 5.4.1 Směrování URL Směrování URL jsou znázorněna na diagramu 5.1. Každé přesměrování je podmíněno použitým protokolem (HTTP či HTTPS zabezpečeným SSL) a kořenovou částí cesty k souboru. Například http + /views/ označuje URL začínající http://www.example.com/views/. Jak bylo popsáno v kapitole o indexování webů vyhledávači, URL obsahující proměnnou _escape_meta_fragment jsou směrovány na HTML Snapshoty. Ty jsou uloženy v uživatelově webu. Využít komponenty frontendu lze přes složku /cms/, která je při použití 1.
http://nginx.org/
18
5. Dekompozice CMS
Obrázek 5.1: Směrování URL web HTTP serverem protokolu HTTP směrována do Prezentační vrstvy aplikace a při použití HTTPS do Editačního rozhraní. Aplikace má po přihlášení do Editačního rozhraní mírně modifikované šablony. Jejich správný výběr je zaručen směrováním složky /views/. Přihlásit se do aplikace lze navštívením webu pod HTTPS protokolem, nebo otevřením URL /admin/. Pokud adresa složky v URL nezačíná vyjmenovanými řetězci, použijí se poslední pravidla pro kořenové složky /. Ty jsou pod HTTPS protokolem vedeny do Backend části aplikace, kde si konkrétní případy převezme směrovač použitého frameworku. Pod HTTP protokolem jsou vedeny do uživatelova webu, kde jej převezme SPA načítaná z Frontendu složkou /cms/.
19
Kapitola 6
Backend 6.1
Slovníček pojmů
Před samotným popisem aplikace je vhodné si uvést několik základních pojmů, se kterými se bude v této kapitole pracovat [1]. •
MVC – software architektura Model–View–Controller
•
CakePHP1 – PHP framework dodržující architekturu MVC
•
Plugin CakePHP – část CakePHP aplikace, která má vlastní MVC strukturu a je se zbytkem aplikace provázána minimálně
•
Twig2 – PHP šablonovací systém
•
Komponenta – třída sdílena mezi controllery CakePHP
•
Shell – třída určena ke spouštění přes Unix shell
•
ORM (Object–relational mapping) – aplikační vrstva pro transformaci dat mezi relační databází a objekty aplikace, v CMS modifikována pro použití dokumentové databáze MongoDB
6.2
CakePHP
Backend aplikace bude implementovan převážně v MVC PHP frameworku CakePHP. Ten umožňuje rychlý vývoj aplikace, která bude snadno udržovatelná, škálovatelná a bezpečná. API a Developerská sekce bude vytvořena jako samostatný Plugin. Aplikace bude obsahovat pluginy třetích stran pro použití Twigu3 , MongoDB 4 a pro kompilaci JS a CSS souborů5 (viz. kapitola o optimalizacích). 1. 2. 3. 4. 5.
http://cakephp.org/ http://twig.sensiolabs.org/ https://github.com/predominant/TwigView https://github.com/ichikaway/cakephp-mongodb/ https://github.com/markstory/asset_compress
20
6. Backend
Obrázek 6.1: MVC architektura
6
Pro lepší pochopení MVC architektury frameworku a způsobu, jakým aplikace funguje, je vhodné uvést diagram zachycující zjednodušený cyklus zpracování HTTP požadavku frameworkem. Diagram je znázorněn na obrázku 6.1. Jak je zřejmé, klientův požadavek po inicializaci aplikace přebírá Dispečer (Dispatcher). Ten na základě URL požadavku a HTTP metody určí, který Controller pomocí jaké metody požadavek dále zpracuje. Aplikace využívá celkem čtyři HTTP metody: •
GET pro získání dat,
•
POST pro vytvoření nového datového objektu,
•
PUT pro úpravu datového objektu,
•
DELETE pro smazání datového objektu.
Rozdělení požadavků do těchto metod zpřehledňuje aplikaci, což je důležité zejména v REST API [7]. CakePHP obsahuje třídu Router, pomocí které lze pravidla pro výběr Controlleru zadefinovat. Například následující pravidlo zpracovává pouze požadavky s HTTP metodu PUT, cestou k souboru začínající složkou api, další složkou označující jméno Controlleru. Jména dalších složek budou použity jako hodnoty parametrů metody edit vybraného Controlleru. 6. Zdroj: http://book.cakephp.org/2.0/en/cakephp-overview/ understanding-model-view-controller.html
21
6. Backend Router::connect(’/api/:controller/*’, array( "plugin" => "api", "action" => "edit", "[method]" => "PUT" ));
Toto pravidlo může splňovat například následující URL, pokud bude zaslána HTTP požadavkem PUT. Podle zavedené sémantiky by měl být následně upraven databázový záznam uživatele mathew daty zaslanými v těle požadavku. http://www.example.com/api/users/mathew/
Jakmile zpracování požadavku převezme Controller, využije Modely, sloužící v CakePHP jako ORM vrstva, ke komunikaci s databází, v případě CMS s MongoDB. Controller má také k dispozici Komponenty, prvky Pluginů a knihovny třetích stran. Controller svůj výstup pošle vrstvě View, ve která neprovádí žádnou výpočetní logiku, pouze do šablony zanese data obdržená Controllerem. 6.2.1 Controllery Hierarchie Controllerů Backendu je zachycena diagramem tříd 6.2. Bílou barvou jsou znázorněny třídy CakePHP a třídy třetích stran. Controller je systémový controller CakePHP, který má základní vlastnosti pro práci s komponentami, modely, view atp. Všechny controllery použité uvnitř frameworku jej musí rozšiřovat. AppController slouží k zadefinování chování, které má být uniformní pro celou aplikaci. V případě CMS je to používání šablonovacího systému Twig. Ostatní controllery aplikace by měly rozšiřovat AppController a tím tedy automaticky použít i Twig. AppController dále definuje proměnné web, domain, folder, folder_name. Proměnná web obsahuje databázový záznam kolekce Web navštíveného webu, je používána zejména v API při editaci obsahu webu. Domain obsahuje řetězec se jménem domény webu, který je klíčový při autorizaci. Proměnné folder a folder_name obsahují řetězce s cestou ke složce webu v adresářovém systému a její jméno. Tyto dvě proměnné jsou důležité zejména v Developerské sekci a API při práci s uživatelovými šablonami a knihovnami. Controller definuje vazby na třídy User, Web a UserWeb, které jsou nástrojem ORM vrstvy aplikace, tedy modely frameworku. 22
6. Backend
Obrázek 6.2: Controllery backendu
23
6. Backend Třídy User, Web a UserWeb rozšiřují třídu AppModel. Ta má podobnou roli jako AppController v kontextu controllerů. Rozšiřuje systémovou třídu Model pro práci s databází a umožňuje upravit její chování. AppModel CMS je využit k jednotnému použití ovladače pro komunikaci s databází MongoDB. AdminController rozšiřuje AppController. Jeho základním úkolem je zajištění autentizace a autorizace uživatele. Dosahuje toho konfigurací systémové komponenty AuthComponent a definicí nových metod pro složitější autentizaci, která je nutná v multidoménovém prostředí. Ze systémových komponent dále používá SessionComponent pro práci s proměnnými session a RequestHandlerComponent pro práci s HTTP požadavkem. Poslední komponentou je PheanstalkComponent. Ta je použita v případě nutnosti zpracovat nějaký úkol asynchronně. Naváže spojení s distribuovanou frontou a vloží do ní zprávu se jménem úkolu a jeho parametry. Z fronty zprávu převezme k tomu určený skript a úkol dokončí. Asynchronní zpracování úkolu se využije v případě, kdy dosažení výsledku úkolu může trvat dlouho a nemusí být zaslán hned s HTTP odpovědí prohlížeči. Úkol díky tomu nebude blokovat odeslání HTTP odpovědi. Využití lze nalézt například po změně obsahu stránky uživatelem, kdy je potřeba vytvořit nový HTML Snapshot. Práce s frontou bude rozepsána dále v této kapitole. AdminController v CMS zpracovává přihlašovací formulář a odhlašování uživatele. Funkcionalita je navázána na metody login() a logout(), se kterými pracuje Dispečer aplikace. Controller hlídá, aby uživatel směl editovat pouze svá data. Pokud je v některé sekci vyžadováno přihlášení, musí příslušný controller rozšiřovat AdminController. V CMS to jsou všechny sekce vyjma Prezentační vrstvy, která však ke své práci Backend a tedy ani controllery přímo nevyžaduje. EditController je prostředek k využití Editačního rozhraní. Má dvě jednoduché funkce. Zaprvé musí zajistit, že rozhraní využije pouze autentizovaný uživatel s potřebnými právy, což je docíleno rozšířením AdminControlleru. Zadruhé musí Prezentační vrstvu obohatit o editační knihovny. Editačnímu rozhraní se bude věnovat samostatná kapitola práce. DeveloperAppController a TemplatesController jsou součástí Developer pluginu aplikace, který představuje Developerskou sekci. Plugin má vlastní MVC strukturu a DeveloperAppController obohacuje o nové chování 24
6. Backend všechny controllery pluginu. V celé Developerské sekci je vyžadována autentizace uživatele, a tak DeveloperAppController rozšiřuje AdminController. Developerská sekce je navhrnuta jako SPA, a proto je plugin velice jednoduchý. Mimo základní controller obsahuje ještě TemplatesController, který má pouze jednu metodu. Tou je index() a umí pouze seskládat HTML šablonu Developerské sekce. Zbytek funkcionalit přebírá SPA v komunikaci s API. Samotná SPA bude blíže rozepsána v samostatné kapitole. API plugin tvoří poslední skupinu controllerů. ApiAppController stejně jako DeveloperAppController rozšiřuje AdminController. V API je použita nová komponenta ViewComponent k modifikaci šablon Prezentační vrstvy webu. Controllery dědící ApiAppController spravují vždy jeden typ zdroje aplikace, kterými může být například menu, meta informace, šablony, snippety webu atp. Tyto controllery mohou mít metody index() pro získání dat, edit() pro modifikaci dat, add() pro vytvoření dat a delete() pro odstranění dat zdroje. API bude podrobněji popsána v následující podkapitole.
6.3
REST API
API slouží jako HTTP rozhraní pro komunikaci SPA s databází aplikace. API dodržuje architekturu REST. REST byl navržen k jednotnému a jednoduchému přístupu ke zdrojům (resources) aplikace. V CMS každý zdroj zpracovává vlastní controller. Každý zdroj má podle definice RESTu svou vlastní URL a je dostupný pod čtyřmi HTTP metodami – již zmíněnými GET, PUT, POST a DELETE, které zpracovávají vždy metody index(), edit(), add() a delete() příslušného controlleru. Následující kód reprezentuje smazání souboru se jménem test.pdf webu www.example.com. DELETE /api/files/test.pdf Host: www.example.com
Dispečer aplikace předá požadavek controlleru FilesController, který jej zpracuje metodou delete(string $file). 6.3.1 Controllery Controllery AssetsController, FilesController a ViewsController jsou používány v Developerské sekci aplikace a controllery MenuController, MetaController a SnippetsController v Editačním rozhraní. 25
6. Backend AssetsController spravuje uživatelovy knihovny se styly a JavaScripty. Controller má ve své proměnné types zadefinované typy souborů, které smí zpracovat. Jsou jimi soubory CSS a JavaScript, mohou však být povoleny i soubory CSS preprocesorů, tedy LESS7 , SASS8 , SCSS a Stylus9 , či CoffeeScript10 , který může být zkompilován do JavaScriptu. Metoda index() vrací celkový seznam knihoven webu a seznam knihoven, které jsou aktuálně na webu povoleny. Metoda edit() dovoluje uživateli měnit pořadí načítání knihoven a zakázat použití konkrétní knihovny na webu. Pomocí delete() lze smazat jednu konkrétní knihovnu a add() slouží k nahrávání knihoven. FilesController je podobný AssetsControlleru. Spravuje soubory, které neobsahují žádnou aplikační logiku webu. Mohou jimi být obrázky, soubory PDF, XLS atp. CMS neumožňuje jejich přímou editaci a tudíž FilesController nedefinuje metodu edit(). Metodu add() sdílí s AssetsControllerem, protože má stejnou logiku a MIME type detekce souboru probíhá až v controlleru. ViewsController je posledním z controllerů používaných v Developerské sekci. Opět spravuje některé typy souborů webu, konkrétně šablony. Jedná se o HTML šablony se syntaxí AngularJS. Lze je rozdělit na šablony pro layout stránek, které určují rozložení konkrétní stránky, na které může být použita vždy pouze jedna. Stránka se dělí na menší oblasti nazvané Snippety, ty mají své vlastní šablony. Metody ViewsControlleru se od předchozích dvou controllerů liší zpracováním souborů. Každou šablonu je nutno modifikovat a uložit dvakrát. Jednou pro Editační rozhraní a jednou pro Prezentační vrstvu. Metody souborům upraví speciální AngularJS atributy elementů či přidají nové. Modifikaci podmiňuje požadavek na CMS, aby byl web editovatelný tak, jak jej vidí nepřihlášený uživatel. AngularJS, ve kterém je implementována Prezentační vrstva i Editační rozhraní, tak využije téměř totožné šablony. Jedny však budou obsahovat atributy umožňující editovat obsah šablon. Controller používá k transformaci šablon komponentu ViewsComponent. Controllery API využívané v Editačním rozhraní se od controllerů Developerské sekce liší typem spravovaných zdrojů. Zatímco controllery Deve7. 8. 9. 10.
http://lesscss.org/ http://sass-lang.com/ http://learnboost.github.io/stylus/ http://coffeescript.org/
26
6. Backend loperské sekce pracují se soubory, controllery Editačního rozhraní pracují s daty uloženými v databázi. Upravují záznamy v kolekci Web editovaného webu.
Controllery Editačního rozhraní mají podobnou logiku, upravují vždy jinou část stejného záznamu kolekce. MetaController je navrhnut k editaci meta informací webu, MenuController upravuje strukturu stránek webu, ze které jsou generovány menu a SnippetsController k uložení změn při editaci obsahu stránek.
6.4
Asynchronní zpracování úkolů
Před samotným popisem asynchronního zpracování úkolů v aplikace je vhodné popsat použité programy třetích stran. Jsou to prohlížeč PhantomJS 11 a fronta zpráv Beanstalk 12 . Řešení je inspirováno článkem vydaným Yearofmoo [17].
6.4.1 PhantomJS PhantomJS je headless prohlížeč s open-source jádrem WebKit, které používá například prohlížeč Safari a dříve Chrome, které společně s Operou přešly na Blink, který je postavený na základech WebKitu. Jako headless jsou označovány prohlížeče, které nemají grafické rozhraní [6]. Jsou proto používány hlavně na serverech k automatizaci předem definovaných úkolů, často k jednotkovému testování JavaScriptových aplikací, CSS preprocesorů, testování dostupnosti aplikace či rychlosti jejího stáhnutí a vykreslení. K PhantomJS lze přistupovat přes JavaScriptové API, které je rychlé a podporuje manipulaci s DOMem, CSS selektory, JSON, Canvas a SVG [5]. Pomocí PhantomJS lze například vytvořit screenshot stránky, procházet její HTML obsah, testovat pozici elementů, zasílat aplikaci požadavky s POST daty, měřit rychlost načtení aplikace a množství přenesených dat či stáhnout záznam provozu sítě ve formátu HAR (formát, který používá k tomuto účelu i Firefox či Internet Explorer od verze 9). V CMS bude PhantomJS použit k vytvoření HTML snapshotů webových stránek.
11. http://phantomjs.org/ 12. http://kr.github.io/beanstalkd/
27
6. Backend 6.4.2 Beanstalk Beanstalk je jednoduchá a rychlá fronta zpráv. Její architektura vychází z programu Memcached 13 sloužícího k uchovávání dat aplikace v operační paměti. Beanstalk má univerzální rozhraní, ale byla primárně vyvinuta ke zrychlení načítání stránek webových aplikací asynchronním zpracováním časově náročných úkolů. Je podobné RabbitMQ 14 , ale na rozdíl od něj je plně nezávislé na aplikaci, která ji využívá. Mezi výhody Beanstalk patří prioritizace zpráv/úkolů, možnost využívat jak operační paměť, tak persistentní paměť, distribuce mezi více servery, zabránění stárnutí, odložení úkolů a dobrá podpora aplikací třetích stran. Ve spojitosti s frontami zpráv je zadefinováno několik pojmů: •
Tube / Queue / Roura – fronta zpráv se může rozdělit do více paralelních rour, které poté mohou být zpracovávány jinými způsoby,
•
Job / Message / Zpráva / Úloha – objekt předán frontě,
•
Producer / Producent – program zasílající zprávy,
•
Consumer / Konzument – program, který vyzvedává zprávy z fronty,
•
Worker – program zpracovávající zprávy / úlohy.
6.4.3 Asynchronizace Jelikož některé akce CMS prováděné serverem jsou časově náročné, má smysl uvažovat, jestli je neprovádět na pozadí. Akce musí splňovat předpoklad, že není nutné zaslat její výsledek v reálném čase. Backend je implementován v jazyce PHP, které je jednovláknové a nemá nativní podporu provádění asynchronních operací. Mechanismus asynchronního zpracování akcí však lze provést zasíláním zpráv přes frontu. Namísto toho, aby byl dílčí úkol HTTP požadavku zpracován hned, je zaslána zpráva s popisem úkolu do distribuované fronty. HTTP požadavek je zpracován dál a odpověď zaslána bez toho, aby čekala na výsledek úkolu. Na pozadí však musí být spuštěn konzument, který periodicky kontroluje frontu zpráv. Pokud v ní nalezne zprávu s úkolem, předá ji workeru, který ji zpracuje. Diagram 6.3 zobrazuje komunikaci webového prohlížeče se serverem, kde je dílčí úkol X zpracován asynchronně. 13. http://memcached.org/ 14. https://www.rabbitmq.com/
28
6. Backend Prohlížeč serveru, respektive API, zašle HTTP požadavek (akce 2). Úloha X je časově náročná a tak API zasílá zprávu s popisem úkolu X frontě zpráv (akce 2.1). Prohlížeči jsou ihned zaslána data v odpovědi požadavku (akce 2.2). Mezitím se konzument periodicky dotazuje fronty, jestli neobsahuje zprávy (akce 1 a 3). Pokud ano, konzument instanciuje workera a předá mu popis úkolu, který má zpracovat (akce 4). Jakmile worker svou práci dokončí, zašle konzumentovi oznámení o dokončení úlohy (akce 4.1). V CMS je vytvořen konzument a workery pro několik různých úkolů. Jsou implementovány jako třídy rozšiřující systémovou třídu Shell frameworku CakePHP, která je určena ke spuštění z příkazové řádky systému.
Obrázek 6.3: Asynchronní zpracování úkolů
Konzument je třída spouštěná v pozorovacím módu nad konkrétní rourou fronty. Konzument periodicky kontroluje obsah roury. Pokud v rouře nalezne zprávu, je předána konkrétnímu workeru ke zpracování. Zpráva obsahuje serializované jméno úkolu s jeho parametry. 29
6. Backend 6.4.4 Workery Workerů je v CMS několik a jsou použity v REST API. SnapshotShell je důležitý z pohledu SEO. Jako parametr přijímá URL identifikátor webu a vytvoří jeho HTML snapshot, který je uložen ve složce webu. Při návštěvě webu robotem pro indexaci stránek je mu předložen tento HTML snapshot namísto běžné stránky tak, jak bylo popsáno v kapitole o optimalizaci pro vyhledávače. Samotný proces vytvoření snapshotu zahajuje SnapshotShell voláním skriptu využívajícího prohlížeč PhantomJS. Skript instanciuje PhantomaJS a předá mu URL webu. Jakmile je HTML navštíveného webu poskládáno pomocí SPA, SPA nastaví příznak, pomocí kterého skript zjistí, že již může HTML snapshot vytvořit. Snapshot je předán zpátky SnapshotShellu, který jej uloží na disk. Zpráva pro vytvoření nového HTML snapshotu je zaslána do fronty pokaždé, kdy je HTML obsah stránky modifikován. Jedná se tedy o případy, kdy REST API modifikuje databázovou kolekci Web controllerem MenuController, MetaController, SnippetsController, či modifikuje šablony webu controllerem ViewsController. Zprávy jsou do fronty vždy vkládány již zmiňovanou komponentou PheanstalkComponent15 . DataShell slouží k optimalizaci rychlosti načtení webové stránky. Pomocí DataShellu se databázové výstupy API předkompilují do statických souborů ve formátu JSON. Namísto toho, aby při požadavku na získání dat byla spuštěná v Backendu API a vyhledával se záznam v databázi, stáhne se pouze statický soubor. DataShellu je předáno jméno domény a seznam souborů, které se mají překompilovat. Zprávy jsou do fronty zasílány API při modifikaci menu, meta informací webu či textové úpravě obsahu stránek. Tyto modifikace probíhají v controllerech MenuController, MetaController a SnippetsController. Důvodu vytvoření této funkcionality se více věnuje kapitola o optimalizacích. AssetCompressShell je posledním ze shellů. Je součástí CakePHP pluginu AssetCompress, který byl modifikován pro potřeby multidoménového CMS. Plugin slouží ke spojení a minifikaci CSS stylů a JavaScriptových souborů webu za účelem snížení počtu přenesených dat prohlížeči a počtu 15. https://github.com/pda/pheanstalk
30
6. Backend HTTP požadavků. Plugin podporuje kompilování CoffeeScriptu do čistého JavaScriptu a kompilaci zdrojových kódů CSS preprocesorů, jako jsou SASS, LESS a Stylus. Shellu je předáno jméno domény webu, kterému se mají překompilovat knihovny. Požadavky jsou do fronty vkládány v API, konkrétně v controlleru AssetsController při změně knihoven v Developerské sekci. Problematika modifikací těchto souborů je hlouběji rozepsána v kapitole o optimalizacích.
6.5
Vytvoření nového webu
S workery silně souvisí třída WebShell. Ta rovněž rozšiřuje Shell a je spustitelná z příkazové řádky. Není však primárně vytvořena pro účely asynchronizace. Při zavedení nového webu do systému je pro něj potřeba vytvořit adresářovou strukturu, základní záznamy v kolekcích databáze a překopírovat soubory nutné ke spuštění webu. K účelu automatizace těchto operací byl vytvořen WebShell, kterému stačí jako parametr zadat jméno domény a shell povinné záznamy a soubory vytvoří. Jelikož jsou jednotlivé workery na frontě zpráv nezávislé díky přenesení komunikace s ní do konzumenta, může být i WebShell použit skrze frontu. Pokud bude vytvořeno grafické rozhraní pro správu jednotlivých webů, bude možno shell takto použít bez nutnosti jeho modifikace.
31
Kapitola 7
Prezentační vrstva Jelikož je Prezentační vrstva navrhnuta jako SPA nad frameworkem AngularJS, je vhodné k přehlednějšímu popisu architektury uvést základní pojmy svázané s použitým frameworkem [3]. •
Scope – rozsah působnosti proměnných, má hierarchickou strukturu. Root scope je na nejvyšší úrovni.
•
Controller – je vázán na element DOM webu. Jeho scope je omezeno na tento element.
•
View – HTML šablona s upravenou syntaxí AngularJS.
•
Model – objekt, se kterým interaguje uživatel. Model je sdílený mezi View a Controllerem.
•
Direktiva – HTML šablona rozšířená uživatelem o vlastní značky a funkce.
•
Filtr – formátuje hodnotu výrazu při výpisu uživateli.
•
Modul – samostatná část aplikace, která má vlastní MVC strukturu.
•
Resource / zdroj – objekt pro interakci s REST API.
7.1
AngularJS
AngularJS je JavaScriptový framework architektury Model–View–Controller vyvíjený společností Google. Mezi jeho klady patří například snadná udržovatelnost, rozšiřitelnost, jednoduchá tvorba komponent a oboustranné provazování dat (two–way data binding). 32
7. Prezentační vrstva 7.1.1 Oboustranné provazování dat Za použití two–way data binding je model se šablonou svázán tak, že pokud jsou uživatelem hodnoty proměnných v šabloně webu upraveny, změny se automaticky aplikují i na modely. Pokud je model upraven v controlleru, tak se změny analogicky promítnou i do šablon. Pro ilustraci použití mějme proměnnou s titulkem stránky webu, která je vypsána v HTML meta tagu title a v menu webu. Na stejné stránce mějme formulář pro editaci této proměnné. Jakmile je stisknuta klávesa nad formulářovým prvkem provázaným s danou proměnnou, hodnota se okamžitě aktualizuje v titulku i menu. Tato vlastnost frameworku je vhodná pro lepší UX při editaci webu v editačním rozhraní.
7.2
Směrovač
AngularJS je vhodný k tvorbě JavaScriptových aplikací, které rovněž zahrnují i tvorbu URL aplikace a navigaci skrz aplikaci. AngularJS nativně pracuje s URL aplikací za použití # fragmentem URL, tedy kotvou. Pokud je potřeba zajistit indexování stránek vyhledávači, jak je popsáno v kapitole o optimalizacích, může být kotva snadno rozšířena o vykřičník a použije se značka #! namísto #. Framework rovněž umožňuje pohodlně upravit práci s URL tak, ať využije plnohodnotné URL za použití nové History Api. Pokud však prohlížeč uživatele nedovoluje použít History Api, je uživateli předloženo jedno z předchozích řešení. O tvorbu URL aplikace se stará objekt $routeProvider (směrovač), který je potřeba nastavit podobně jako v backendu aplikace ve směrovači CakePHP. V základním použití však musí být mimo názvu controlleru nastavena i URL šablony, která se má použít pro vykreslení stránky. Níže je příklad nastavení směrovače. $routeProvider .when(’/’, { templateUrl: ’/views/home.tpl.html’, controller: ’HomeCtrl’ }) .when(’/:page’, { templateUrl: ’/views/main.tpl.html’, controller: ’MainCtrl’ }) .otherwise({ redirectTo: ’/’ });
33
7. Prezentační vrstva Vzhledem k dynamické povaze webu používajícího CMS musí všechny URL zpracovávat vždy stejný controller a rozdílná funkcionalita je řešena až na nižších úrovních pomocí dekompozice do snippetů. K docílení zpracování veškerých URL jedním controllerem lze docílit využitím univerzálního pravidla, jako je znázorněno v předchozím případě v definici URL ’/:page’, kde page je proměnná nahrazující jakoukoliv URL. Problém však nastává při definování šablon k jednotlivým stránkám. Šablony do systému nahrává uživatel a CMS by mělo umožnit, aby jednotlivé stránky mohly mít rozdílnou šablonu. Univerzální pravidlo tak nelze využít. Zpracování objektu $routeProvider probíhá v inicializační fázi životního cyklu aplikace AngularJS, ve které nelze využívat vyšší funkce, jako jsou AJAX, zdroje atp., a pravidla pro $routeProvider tedy nelze definovat dynamicky. V CMS je všem stránkám přiřazena stejná výchozí šablona a dynamičnost je zajištěna až controllerem, který zjistí, jakou šablonu použít, stáhne ji, zkompiluje a nahradí za výchozí. V důsledku je pak použití jiné než výchozí šablony pomalejší. Následující kód představuje nastavení směrovače CMS. $routeProvider .when(’/:page’, { templateUrl: ’/views/main.tpl.html’, controller: ’MainCtrl’ }) .otherwise({ redirectTo: ’/’ });
7.3
Šablony a dekompozice HTML
HTML stránka se dá rozdělit do tří úrovní. Nejvýše je index.html. Slouží k zalinkování knihoven s JavaScripty a styly, spuštění hlavního modulu CMS a v elementu body k zaznačení oblasti šablony webu. Šablona stránky, tedy druhá úroveň, může obsahovat jak běžné HTML, tak jakékoliv uživatelovy značky vlastních AngularJS aplikací. Z pohledu CMS jsou důležité definice oblastí snippetů. Oblast může být například postranní sloupec stránky, hlavní obsah, patička webu atp. Jakýkoliv obsah editovatelný pomocí CMS by měl být uvnitř těchto oblastí jako součást snippetu. Třetí úroveň tvoří snippety. Ty mohou reprezentovat například jednoduchý text, nadpis, obrázek, galerii či kontaktní formulář. Pokud má být nějaká část textu editovatelná, musí ji uživatel označit pomocí atributů CMS. Uži34
7. Prezentační vrstva vatel bude moci v editačním rozhraní měnit pořadí těchto snippetů v rámci oblasti a editovat obsah.
7.4
Controllery
Prezentační vrstva CMS, jak bylo v předchozí kapitole napsáno, obsahuje k základnímu zpracování stránek pouze jeden controller. MainCtrl je navrhnut k sestavení webové stránky z dat databáze a uživatelových šablon. Akce controlleru jsou zachyceny diagramem aktivit 7.1. Controller se nejprve dotáže API na data o webu – meta informace stránky a strukturu. Jakmile controller od API dostane data, vytvoří modely a vloží do nich přijatá data. Framework modely prováže s šablonou stránky a vypíše hodnoty modelů. Tím dojde k sestavení menu webu a vypsání meta informací. V dalším kroku controller již zná informace o stránce, kterou má vykreslit, a tak zažádá API o zaslání textového obsahu stránky. Po přijetí dat opět vytvoří potřebné modely. Controller v této chvíli již má všechna potřebná data a tak zkontroluje, jestli má stránka nastavenou defaltní šablonu, která je přidělena směrovačem. Pokud ne, stáhne novou šablonu a vyrenderuje ji. Poslední akcí controlleru je zapsání značky do HTML šablony indikující, že je již kompletní. Značku využije pouze headless browser PhantomJS k vytvoření HTML snapshotu, jak je popsáno v kapitole o backendu aplikace. Veškerá komunikace s API jde přes tzv. zdroje.
7.5
Zdroje
Slouží ke komunikaci s REST API. Každému zdroji je potřeba zadat URL zdroje API a zadefinovat metody možné k použití. Následné použití v controllerech lze provést přes jednoduché rozhraní. V prezentační vrstvě v rámci optimalizací bylo toto vzorové použití mírně narušeno. Jelikož je použití CMS zaměřeno na malé weby, jejichž obsah nebude editován s vysokou frekvencí, nejsou zdroje asociovány s URL API, ale s jejími výstupy předkompilovaných do statických souborů ve formátu JSON. Při stažení dat tak není nutné spouštět API a hledat záznamy v databázi, ale stačí pouze stáhnout statický soubor. Předkompilované soubory jsou ve složce datasources uživatelova webu. Web.json obsahuje veškeré informace o webu a jeho struktuře. V souborech <page_id>.json je uložen obsah stránky s příslušným identifikátorem. Toto 35
7. Prezentační vrstva
Obrázek 7.1: Akce controlleru MainCtrl
36
7. Prezentační vrstva rozdělení je ideální z pohledu množství přenesených dat a počtu provedených HTTP požadavků.
7.6
Direktivy
Slouží k rozšíření HTML elementů o nové funkce nebo k definování vlastních HTML elementů. Direktivy jsou frameworkem kompilovány do standardního HTML a případně doplněny o vlastní JavaScriptovou funkcionalitu. Pomáhají zapouzdřit části aplikace, zjednodušit její použití a zlepšují udržovatelnost aplikace [2]. Direktivy mohou představovat jakékoli HTML objekty – položky v menu, výstražné zprávy, modální okna, tooltipy, přepínací panely atp. snippet je jediná direktiva prezentační vrstvy. Díky snippetům lze dekomponovat šablonu na menší logické celky. Šabloně se při vykreslení stránky předá seznam typů snippetů a jejich hodnoty. Direktiva stáhne ke každému snippetu šablonu v notaci AngularJS a zkompiluje ji společně s předanými hodnotami [9].
37
Kapitola 8
Editační rozhraní Editační rozhraní rozšiřuje prezentační vrstvu o funkce potřebné k editaci webu. Jakmile uživatel navštíví url /admin/ webu, je přesměrován na backend k autentizaci. Jakmile uživatel prokáže svou identitu, je přesměrován do editačního rozhraní. Editační rozhraní ve své podstatě obsahuje prezentační vrstvu a přebírá celé její chování. Je ale obohacena o ovládací prvky a další sadu knihoven, díky kterým uživatel může web editovat. Uživatel skrze formuláře a přímou editaci obsahu upravuje modely aplikace, které jsou vždy korektně modifikovány oboustranným provazováním dat. Ve chvíli, kdy je některý z modelů pozměněn, uživateli je nabídnuta možnost změny potvrdit. Jakmile tak uživatel učiní, modely jsou odeslány do REST API v backendu aplikace. Backend změny uloží do databáze, zaktualizuje HTML snapshoty a překompiluje JSON soubory reprezentující výstupy databáze.
8.1
Principy editace
Naprostá většina CMS dovoluje uživateli spravovat obsah webu pouze přes administrativní rozhraní. Pokud tomu tak není a lze editovat obsah přímo, má editace značná omezení a větsinou nelze například měnit pozice elementů. Když už CMS dovoluje měnit pozici elementů, je tomu tak zpravidla docíleno pomocí absolutního pozicování elementů s pixelovou přesností. Uživatel v tomto případě dostane do rukou příliš velikou volnost a je pak jednoduché design stránky degradovat. Vyvíjené CMS se snaží nalézt kompromis mezi volností přímé editace obsahu a omezení povolených akcí tak, aby nebyl porušen původní design stránky. Grafik by měl navrhnout design webu, vytvořit HTML šablony, rozdělit do menších logických celků, kterými jsou snippety, a nahrát je do CMS v developerské sekci. Uživatel v editačním rozhraní by měl pouze vytvářet strukturu webu, stránkám volit šablony, plnit je snippety a editovat jejich obsah. Namísto přesného pozicování jednotlivých elementů bude možno pouze 38
8. Editační rozhraní měnit pořadí snippetů, které mohou reprezentovat jeden či skupinu HTML elementů.
8.2
Technologie rozhraní
Editace je docílena za pomoci několika technik, knihoven a HTML5 technologií. Drag and drop je použit ke změně pořadí elementů. K implementaci byl vybrán sortable 1 plugin grafické nadstavby jQuerUI 2 frameworku jQuery 3 a direktiva4 , která tvoří nadstavbu nad frameworkem AngularJS. Jakmile je element, na který je knihovna aplikována, tažen kurzorem myši, plugin změní jeho CSS pozici na absolutní a podle pohybu myši přizpůsobuje jeho X a Y souřadnice tak, aby kopíroval dráhu kurzoru. Ve chvíli, kdy se souřadnice elementu změní natolik, aby v některé z os předešel svého sourozence, jejich pořadí v DOMu se prohodí. Po uvolnění kurzoru myši je absolutní pozicování opět odebráno. uiSortable direktiva AngularJS provazuje elementy DOMu s modely aplikace. Po změně pořadí elementů je příslušný model ihned aktualizován a modely mohou být odeslány API k uložení změn do databáze. Aplikovat chování na element lze HTML atributem ui:sortable, který jako argument přijímá konfigurační objekt, který se nachází ve scope příslušného controlleru. Objekt může obsahovat funkce, které se provedou po dokončení některé z fází přeskládání elementů. Model se s elementem prováže direktivou ngModel. Následuje příklad nastavení elementu [14].
contenteditable je HTML atribut navržený k lepšímu požitku uživatele při editaci obsahu webu. Před jeho standardizací mohl uživatel přes webový prohlížeč editovat text pouze pomocí formulářových prvků – inputů a textových ploch. Pokud by měl být obsah webu editován přímo a ne v administračním rozhraní, musel by být všechen obsah zabalen do formuláře a text vypsán v textových plochách. Toto řešení je však pracné a pro uživatele nepřehledné. 1. 2. 3. 4.
https://jqueryui.com/sortable/ https://jqueryui.com/ http://jquery.com/ https://github.com/angular-ui/ui-sortable
39
8. Editační rozhraní Contenteditable atribut může být přiřazen jakémukoli elementu a tím uživateli dovolit přímou editaci jeho obsahu, bez jakéhokoliv grafického rozdílu. Elementy však nejsou svázány s žádným formulářem a případné odeslání změn v obsahu elementu musí být zpracováno JavaScriptem. Následující kód demonstruje použití.
Textový obsah elementu
WYSIWYG je typ textových editorů, které umožňují zjednodušené formátování HTML dokumentu. Namísto psaní HTML kódu může uživatel pomocí jednoduchého grafického rozhraní intuitivně HTML značky vkládat i bez jejich hlubší znalosti. Dříve byly editory tvořeny pouze pro textové plochy formulářů, rozšiřovaly je o ovládací prvky a jejich obsah interpretovaly jako HTML kód. V posledních době vznikají editory i pro neformulářové HTML elementy s atributem contenteditable. WYSIWYG editory jsou implementovány zpravidla ve všech CMS. CKEditor 5 je jednoduchý WYSIWYG editor. Jako jeden z mála editorů může být použit na elementy s contenteditable atributem. Byl vybrán pro svou stabilitu a jednoduchou manipulaci s editorem nad editovanými elementy.
8.3
Dekompozice
Stránka je rozdělena do tří sekcí pro editaci. Meta informace a nastavení webu lze upravovat v první sekci rozhraní. Web je rozšířen o tlačítko (viz. obrázek 8.1, značka „1“), kterým lze zpřístupnit modální okno s formulářem pro editaci těchto informací. Změny se během editace ihned promítají do celého webu skrz modely a uživatel může jejich uložení do databáze potvrdit příslušným ovládacím prvkem. Snippety tvoří hlavní obsah stránky. Na obrázku 8.1 je značkou „2“ označena sekce se snippety. Těchto sekcí může být na jedné obrazovce více. Na tomto webu je zelenou barvou označeno několik textových snippetů. Jejich pořadí lze měnit technikou drag and drop. 5.
http://ckeditor.com/
40
8. Editační rozhraní
Obrázek 8.1: Snímek obrazovky editačního rozhraní testovacího webu se znázorněnými sekcemi pro editaci Obsah je možno editovat CKEditorem nad elementy s kladnými hodnotami contenteditable atributů. Funkcionalita drag and drop má vyšší prioritu než CKEditor. Po kliknutí na element se editor implicitně neaktivuje a provedenou akci přebírá uiSortable direktiva. Toto chování bylo v CMS nutné narušit. Editačnímu rozhraní byl přidán nový stav, určující, zda je jQuery sortable plugin umožňující použití drag and drop aktivní. Stav lze změnit kliknutím, nikoli však tažením, na některý ze snippetů a tím plugin zakázat a obnovit stav kliknutím mimo plochu obsahující snippety. Snippet může obsahovat více informací, než pouze textový obsah. Vždy je to alespoň typ snippetu. Kliknutím pravým tlačítkem myši lze vyvolat modální okno, ve kterém lze dodatečné informace upravovat. Informace jsou editovatelné pomocí klasických formulářů. Možnost přidat či smazat snippet je zpřístupněna v tomtéž modálním okně. K vyvolání modálního okna po stlačení pravého tlačítka myši byla vytvořena direktiva ngRightClick. Direktiva se prováže s HTML elementem přidáním atributu ng-right-click, jehož hodnota je funkce controlleru příslušného scope. Následující kód demonstruje použití.
Pokud je snippet přidán, smazán nebo upraven jeho typ, musí být šablona stránky vyrenderována znova, aby se snippet direktivy vykreslily správně. 41
8. Editační rozhraní Aby bylo možné vyvolat modální okno snippetů a tím zpřístupnit možnost vložit nový, ikdyž zatím sekce žádný neobsahuje, je direktiva ngRightClick přidána i k elementu, který snippety obaluje. Aktivuje se však pouze tehdy, pokud žádné snippety neobsahuje. Poslední funkcionalita k editaci snippetů je zajištění placeholderu. Pokud textový snippet nemá žádný obsah, je mu uměle přiřazena hodnota – oznámení o absenci textu. Uživatel by neměl možnost vyvolat editor nad elementem s prázdným obsahem, který by měl nulové rozměry. Změny ve snippetech se zasílají k uložení do controlleru SnippetsController v Api. Menu a stránky je možno editovat přímo skrz jakékoliv menu stránek webu. Na obrázku 8.1 má tato sekce značku „3“. Pořadí stránek webu v menu CMS dovoluje změnit technikou drag and drop. Text odkazu nelze editovat přímo, aby nedošlo ke konfliktu contenteditable funkcionality oproti přesměrování odkazu. Je však k dispozici modální okno, přístupné po stisknutí pravého tlačítka myši nad odkazem, podobně jako tomu je u snippetů. Okno obsahuje formulář pro editaci informací o stránce a odkazu v menu – text odkazu, URL, titulek a šablona stránky. Stejně jako u snippetů lze přidat či smazat stránky v modálním okně. Pokud je aktuální stránce změněna šablona, je nutné ji přerenderovat. Změny v menu a stránkách se zasílají k uložení do controlleru MenuController v Api.
8.4
Příklady šablon pro editaci
Pro lepší názornost použití atributů k vytvoření požadovaných funkcionalit ve vlastních šablonách mohou sloužit vzorové šablony této sekce. Šablony pro CMS vytváří developer webu a tedy musí při jejich tvorbě použít zavedenou syntaxi a direktivy CMS. Vypuštěním povinných HTML atributů je editační rozhraní ochuzeno o příslušné funkce. CMS ze šablon pro prezentační vrstvu automaticky odstraní atributy direktiv editačního rozhraní. Šablona stránky s vloženým menu, respektive HTML kódem ve vlastním souboru reprezentující menu, a sekcí main se snippety může vypadat následovně:
42
8. Editační rozhraní
<snippet ng-repeat="snippet in snippets.main" snippet="snippet">
Menu
vytvořené jako HTML seznam může mít následující kód:
Snippet s nadpisem h3 a textovým blokem může vypadat následovně. Developer tvořící šablony snippetů nemusí zapisovat všechny atributy elementů s editovatelným textem, stačí zadat ng-model a ostatní CMS doplní automaticky.
8.5
Zpracování chyb API
Pokud dojde k chybě API nebo vypršení relace, backend zašle v JSON odpovědi API oznámení o chybě. Není praktické kontrolovat každý výstup API jednotlivě. Pomocí response interceptorů lze globálně kontrolovat všechnu komunikaci zdrojů frameworku s API [11]. V CMS je vytvořen a zaregistrován interceptor, který kontroluje všechna data zaslaná z API. Pokud obsahují zprávu o chybě, je zpracování přerušeno a zaslán signál s popisem chyby skrz aplikaci editačního rozhraní. Signál zachytí továrnička chybových zpráv, která vytvoří HTML zprávu s chybou a zobrazí ji uživateli. 43
8. Editační rozhraní
8.6
Controllery
EditCtrl je jediný controller editačního rozhraní. Je nadřazen controlleru MainCtrl prezentační vrstvy a může tedy přistupovat k jeho scope a metodám. Controller koordinuje spolupráci modelů, direktiv, zdrojů a obsluhu modální okna a další grafické prvky CMS. Jsou v něm definovány funkce pro renderování šablony, přidávání snippetů a stránek, validaci hodnot snippetů a stránek atp.
8.7
Direktivy
snippet představuje nižší jednotku dekompozice šablony. contenteditable direktiva propaguje uživatelovy změny provedené v HTML obsahu elementu s nastaveným atributem contenteditable do modelů aplikace. ngRightClick přetěžuje funkci pravého tlačítka myši za vlastní funkci definovanou v AngularJS aplikaci. uiSortable direktina provazuje sortable plugin frameworku jQuery určeného k řazení HTML elementů s modely aplikace AngularJS.
8.8
Zdroje
Meta je používán ke komunikaci s MetaControllerem API a slouží ke správě meta informací webu. Menu
komunikuje s MenuCtonrollerem API, slouží ke správě stránek webu.
Snippets petů.
komunikuje se SnippetsCtonrollerem API, slouží k editaci snip-
Web komunikuje s WebCtonrollerem API, slouží ke stažení informací o webu, dostupných šablon, snippetů a jejich struktury.
44
Kapitola 9
Developerská sekce Ačkoliv je developerská sekce součástí backendu, je vhodné rozebrat její strukturu až po popisu frontendu a zadefinování komponent frameworku AngularJS, protože je navrhnuta rovněž jako SPA. Sekce slouží ke správě šablon a souborů webu. Vyžaduje vyšší stupeň přístupových práv než editační rozhraní. Míra práv uživatele je držena v záznamu kolekce UserWeb databáze. Aplikace je jednoduchá a tvoří ji pouze jedna stránka. Router frameworku AngularJS tedy není vůbec použit. Na stránce je zobrazen seznam JavaScriptových knihoven, souborů se styly, šablon, obrázků a dalších souborů, které uživatel nahrál. Uživatel může jednotlivé soubory nahrávat technikou Drag and Drop a mazat příslušným ovládacím prvkem. JavaScriptové soubory a styly jsou automaticky spojeny a minifikovány. Uživatel má možnost pořadí těchto dvou typů souborů měnit a zakázat jejich použití.
9.1
Controllery
FilesCtrl po své inicializaci přes API backendu stáhne seznamy souborů nahraných uživatelem do modelů. Seznamy jsou vypsány do šablony stránky. Controller obsahuje funkce pro nahrávání, mazání a změnu stavu souboru a konfigurační objekty pro sortable direktivu.
9.2
Zdroje
Files je jediný zdroj developerské sekce. Komunikuje s controllery FilesController, AssetsController a ViewsController API backendu. Soubory jsou zpracovány obdobně a tak je použit pouze jeden zdroj pro tři controllery API. 45
9. Developerská sekce
9.3
Filtry
FilesFilters je modul obsahující filtry aplikace. FileName je jediný filtr developerské sekce. Upravuje jméno souboru při výpisu do šablony.
46
Kapitola 10
Výkonnostní optimalizace Jednostránkové aplikace vytváří nový prostor k optimalizacím webů z hlediska množství přenesených dat ze serveru, počtu HTTP požadavků a snižují náročnost na serverové zpracování požadavků. Při prvním navštívení stránky SPA se stáhnou JavaScriptové knihovny, soubory se styly, šablona stránky a další potřebné soubory. Není nutné, aby stránku generovaly serverové skripty a tím vznikala časová prodleva a režie na serverové zpracování. O další zpracování webu se stará aplikace v prohlížeči a není nutné stahovat duplicitní data. Stačí získat pouze potřebná data z databáze pomocí API a případně stáhnout nové šablony. Všechny soubory jsou staženy opětovně až ve chvíli, kdy uživatel manuálně obnoví stránku. Klasické weby oproti SPA při procházení webu posílají vždy nový plnohodnotný HTTP požadavek na server, který ho zpracuje a zasílá vždy celý HTML dokument a knihovny. SPA však lze optimalizovat, aby se jeho výkonnostní výhody ještě více prohloubily. Následující optimalizace jsou prováděny automaticky na všechny weby CMS. Pokud se developer webů rozhodne použít CMS, které je předmětem této diplomové práce, bude využívat jejich benefity bez jakéhokoli konfigurování služeb či dodatečného programování.
10.1 Kompilování výstupů API Aplikace zpravidla potřebuje ze serverových skriptů pouze API pro komunikaci s databází. CMS je určeno pro malé weby, u kterých nebude obsah aktualizován často. V prezentační vrstvě tedy není potřeba, aby každý požadavek na data z databáze zpracovával backend. CMS poskytuje obsah více přímočarou cestou. Při změně dat webu jsou výstupy API pro prezentační vrstvu zapsány do statických souborů. Namísto toho, aby se při návštěvě webu aplikace dotazovala API a tím vznikala režie pro běh frameworku a získání dat, je pouze stažen statický soubor a backend není vůbec zatěžován. 47
10. Výkonnostní optimalizace Data jsou do souboru zapisována workerem, který dostane z API požadavek skrz frontu zpráv. Jako důsledek jsou touto optimalizací omezena místa, kde by mohly vznikat bezpečnostní nedostatky.
10.2 Minifikace a sloučení knihoven a stylů JavaScriptové knihovny CMS jsou minifikovány a sloučeny do jednoho souboru. Spojením se omezuje počet provedených HTTP požadavků prezentační vrstvou při navštívení webu. Minifikací, tedy odstraněním nepotřebných bílých znaků, se zmenší objem přenesených dat. Uživatelovy knihovny a styly jsou rovněž zpracovány stejným způsobem. Ve chvíli, kdy je v developerské sekci nahrána či upravena některá z knihoven, je zaslána zpráva do fronty a worker AssetCompressShell překompiluje knihovny.
10.3 Memcached K minimalizaci prodlevy mezi zasláním HTTP požadavku a zahájením stahování odpovědi byla použita výkonná paměťová cache Memcached [18]. Často požadované soubory webu a soubory CMS jsou uloženy do cache. Webový server namísto toho, aby pokaždé potřebný soubor hledal na pevném disku a četl jej, sáhne pouze do paměti RAM a rovnou odešle soubor v odpovědi požadavku.
10.4 Browser cache K další minimalizaci počtu HTTP požadavků a objemu přenesených dat byl nakonfigurován webový server, aby souborům nastavil expired parametr hlavičky. Prohlížeč na jeho základě soubor uloží na patřičnou dobu do své cache. Při opětovné potřebě stáhnout soubor se nezasílá požadavek na server, ale použije se soubor z cache [13].
10.5 GZip GZip je forma komprimace souborů, kterou podporuje HTTP protokol. Lze ji aplikovat na jakýkoliv typ souboru a efektivita komprimace se odvíjí hlavně od typu komprimovaného souboru. Úroveň komprimace má nejlepší výsledky u textových souborů. U již komprimovaných souborů, jako je například soubor typu .zip, může být zkomprimovaný soubor větší než originál, protože 48
10. Výkonnostní optimalizace GZip doplňuje soubor o novou hlavičku a samotný soubor tohoto typu je již dobře zkomprimován. Komprimace a dekomprimace konzumuje dodatečný čas procesoru. Použití GZipu se ale zpravidla vyplácí, protože ušetřený čas při stažení souboru je větší než čas potřebný ke komprimaci a dekomprimaci. Webový server CMS má GZip komprimaci nakonfigurovanou.
49
Kapitola 11
Závěr Cílem diplomové práce bylo navrhnout a implementovat CMS jako jednostránkovou aplikaci. Toto řešení má jak výhody tak i nevýhody. Řada nevýhod, jako je indexace vyhledávači a tvar URL, se podařilo eliminovat. Sadou optimalizací byly výkonnostní výhody ještě zvýrazněny. Aby se setřely zbylé hlavní nevýhody, je CMS určeno zejména pro malé weby, které nepodhléhají kontinuálnímu vývoji a neobsahují složité funkce. Hlavní požadavek CMS byl uživatelsky přívětivé rozhraní pro správu obsahu za použití moderních technologií. Uspokojivého uživatelského zážitku při editaci se dosáhnout podařilo. Obsah lze upravovat tak, jak jej vidí nepřihlášený uživatel. Pokud uživatel potřebuje upravit text, může ho upravit přímo. Web je rozčleněn do menších logických celků, tzv. snippetů, které může jednoduše přidávat, mazat a přesouvat technikou drag and drop. Jednotlivé stránky může rovněž spravovat ze stejné obrazovky skrz menu webu. Výsledné CMS tvoří kvalitní základ pro další rozvoj. V budoucnu bude vytvořeno více druhů snippetů – galerie, kontaktní formulář, blog atp. Rozvinuta bude developerská sekce, která slouží ke správě šablon, skriptů, stylů a dalších souborů webu. Další úpravy budou podmíněny požadavky plynoucími z ostrého nasazení webů na CMS.
50
Literatura [1] Cake Software Foundation. Cakephp cookbook, 2014. URL: http: //book.cakephp.org/2.0/en/index.html. [2] Fullstack.io. Build custom directives with angularjs. July 2013. URL: http://www.ng-newsletter.com/posts/directives.html. [3] Google. Angularjs api docs, 2010. [Online; cit. 16.02.2014]. URL: https://docs.angularjs.org/api. [4] Google. Making ajax applications crawlable, 2012. [Online; cit. 18.01.2014]. URL: https://developers.google.com/webmasters/ ajax-crawling/. [5] Ariya Hidayat. Phantomjs documentation, 2010. [Online; cit. 20.01.2014]. URL: http://phantomjs.org/documentation/. [6] Colin Ihrig. Headless webkit and phantomjs. December 2012. URL: http://www.sitepoint.com/headless-webkit-and-phantomjs/. [7] Martin Malý. Rest: architektura pro webové api. August 2009. URL: http://www.zdrojak.cz/clanky/ rest-architektura-pro-webove-api/. [8] Martin Malý. Přepište historii webových stránek. February 2011. URL: http://www.zdrojak.cz/clanky/ prepiste-historii-webovych-stranek/. [9] One Hungry Mind. Angularjs dynamic templates – yes we can! October 2012. URL: http://onehungrymind.com/ angularjs-dynamic-templates/. [10] MongoDB. The mongodb manual, 2011. [Online; cit. 23.02.2014]. URL: http://docs.mongodb.org/manual/. 51
Literatura [11] Jakub Mrozek. Tvorba moderního e-shopu: zpracování chyb. August 2013. URL: http://www.zdrojak.cz/clanky/ tvorba-moderniho-e-shopu-zpracovani-chyb/. [12] Mozilla Developer Network. Manipulating the browser history. February 2010. URL: https://developer.mozilla.org/en-US/docs/Web/ Guide/API/DOM/Manipulating_the_browser_history. [13] Mark Nottingham. Caching tutorial. May 2013. URL: https://www. mnot.net/cache_docs/. [14] Jack Osborne. The contenteditable attribute. January 2012. URL: http://html5doctor.com/the-contenteditable-attribute/. [15] W3Schools. Ajax introduction, 2014. [Online; cit. 4.02.2014]. URL: http://www.w3schools.com/ajax/ajax_intro.asp. [16] W3Schools. Browser statistics, 2014. [Online; cit. 15.03.2014]. URL: http://www.w3schools.com/browsers/browsers_stats.asp. [17] Yearofmoo. Angularjs and seo. January 2013. URL: http://www. yearofmoo.com/2012/11/angularjs-and-seo.html. [18] Adam Štrauch. Memcached: zrychlete svůj web pomocí cache. December 2012. URL: http://www.root.cz/clanky/ memcached-zrychlete-svuj-web-pomoci-cache/.
52
Příloha A
Elektronická příloha 1.
Zdrojové kódy CMS jsou obsaženy v archívu cms.zip.
53