Hlavní navigace

Cocoon v příkladech: Instalace a první aplikace

Pavel Sýkora 19. 11. 2003

Uběhly dva týdny, a tak je tu opět seriál o Cocoonu. Dnes se podíváme, jak Cocoon nainstalovat v Linuxu i MS Windows, a také vytvoříme první webovou aplikaci - jednoduché fotoalbum. Při tom se dozvíme o dalším typu komponenty, kterou je reader.

Co budeme potřebovat

Pro instalaci Cocoonu je zapotřebí počítač s nainstalovaným Unixem (vyzkoušeno na Linuxové distribuci Aurox 9.1) nebo MS Windows (vyzkoušeno na Windows 2000 a Windows ME). Další nezbytnou rekvizitou je SDK pro Java 2 Platform, Standard Edition (J2SE) verze 1.4 nebo vyšší, doporučuji verzi alespoň 1.4.2. Pokud SDK Javy nemáte nainstalováno ani je nenajdete jako balíček v distribuci, lze si je zdarma stáhnout z URL java.sun.com/j2se/1­.4.2/download­.html. Pro Cocoon je opravdu nutný vyvojový kit (SDK), prostředí pro běh (JRE) nestačí. Aby programy Javu našly, je nezbytné nastavit (a v unixovském shellu (ba)sh také exportovat) proměnnou prostředí s názvem JAVA_HOME, která obsahuje cestu k hlavnímu adresáři s SDK. Vřele doporučuji, abyste si nastavení této proměnné dali do profilu. Doporučuji také přidat na začátek seznamu PATH adresář se spustitelnými programy SDK, je to adresář bin pod JAVA_HOME. I toto nastavení je nejlépší mít v profilu.

Aktuální zdrojovou distribuci Cocoonu (od verze 2.1 nejsou binární archivy nabízeny) lze stáhnout z URL cocoon.apache­.org/mirror.cgi (cca 30 MB). V době psaní článku je k dispozici verze 2.1.2. Pokud budete pracovat v Unixu (Linuxu), zvolte TAR/GZIP formát, pro MS Windows formát ZIP. Distribuce obsahuje zdrojové kódy Cocoonu a také všechny programy a knihovny (samozřejmě kromě těch, které obsahuje vlastní SDK Javy) nutné pro kompilaci a běh Cocoonu. Distribuce obsahuje i servletový kontejner Jetty a relační databázi hsqldb, takže webové aplikace Cocoonu je možné pohodlně ladit na lokálním počítači.

Instalujeme Cocoon

Pro instalaci Cocoonu budeme potřebovat nezanedbatelné místo na disku – přes 300 MB. Už z toho je vidět, že Cocoon toho opravdu neobsahuje málo. Administrátorská práva nejsou nutná, Cocoon lze bez problému nainstalovat (i provozovat) v domovském adresáři běžného uživatele. Po rozbalení distribučního archivu nalezneme nový adresář cocoon-2.1.2. To je hlavní adresář Cocoonu. Najdeme v něm dva důležité skripty (resp. dávky pro uživatele MS Windows): build.sh (resp. build.bat) a cocoon.sh (resp. cocoon.bat). První z nich slouží hlavně ke kompilaci Cocoonu, ale také k vytváření webových archivů ( *.war) s vašimi aplikacemi pro použití v jiných servletových kontejnerech (např. v Tomcatu). Kompilace je řízena programem Ant.

Uživatelé MS Windows musejí nyní udělat pár kroků navíc (laskavý uživatel Linuxu polituje a zbytek odstavce přeskočí). V adresáři cocoon-2.1.2/tools/bin jsou tři dávkové soubory ( ant.bat, antRun.bat a lcp.bat), které mají unixové konce řádků, pročež nejsou v MS Windows funkční. Pokud si s opravou nejste jisti, můžete originální soubory přejmenovat a použít opravené soubory, které najdete v archivu bat-crlf.zip. Uživatelé MS Windows Millenium Edition nebo Windows 98 musejí ještě kliknout pravým tlačítkem myši na dávku build.bat, v kontextovém menu vybrat"Properties" (Vlastnosti) a v záložce „Memory“ (Paměť) nastavit velikost paměti prostředí (environment) na maximum (4096). Stejnou operaci je potřeba udělat s dávkou cocoon.bat.

Nyní můžeme Cocoon zkompilovat. V adresáři cocoon-2.1.2 zadáme (bez uvozovek!) z příkazového řádku v Unixu příkaz „ ./build.sh“, v MS Windows příkaz „ build“. Nyní je čas na šálek kávy nebo čaje – kompilace otestuje vaši trpělivost. Na mém počítači s procesorem AMD Duron 1,2 GHz a 384 MB operační paměti trvala skoro deset minut. Naštěstí jen ta první, případné rekompilace jsou jednak podstatně rychlejší, jednak rekompilaci celého Cocoonu budete potřebovat skutečně zřídka. Úspěšná kompilace končí textem „BUILD SUCCESSFUL“ a vytvoří adresáře cocoon-2.1.2/build/cocoon-2.1.2 a cocoon-2.1.2/build/we­bapp. V prvním adresáři jsou umístěny přeložené třídy Javy (převážně v souborech cocoon*.jar) a dokumentace. V druhém jsou pak soubory vlastních aplikací Cocoonu: mapy, XML a XSL soubory, konfigurace, ale také všechny externí knihovny ( jar). V tomto (druhém) adresáři budeme také vytvářet svou aplikaci.

Spouštíme

Cocoon spustíme na lokálním počítači jako servlet z příkazového řádku v adresáři cocoon-2.1.2 příkazem (bez uvozovek!) „./cocoon.sh servlet“ v Unixu, resp. příkazem „cocoon servlet“ v MS Windows. Pak spustíme webový prohlížeč (Mozilla, Internet Explorer apod.) a zadáme (bez uvozovek) adresu (URL): „ http://localhost:8888“.Je-li vše v pořádku, v prohlížeči se objeví úvodní stránka Cocoonu s odkazy na příklady (samples) a dokumentaci.

První aplikace

Je čas, abychom se podívali, jak vytvořit webovou aplikaci. Abychom nezabředli do (zatím) nepodstatných detailů, začneme skutečně jednoduše. Vytvoříme primitivní fotoalbum. Soustředíme se však na Cocoon jako takový, takže prioritou bude jednoduchost a přehlednost, nikoliv efektivita nebo estetika.

Fotoalbum: Požadavky

Fotoalbum by mělo mít dva režimy zobrazení: seznam fotografií s náhledy a zobrazení vlastní fotografie v originální velikosti. Seznam bude tvořit nadpis alba, datum kolekce fotografií a obyčejná tabulka, kde v prvním sloupci bude náhled (fotografie zmenšená na jednotnou výšku 60 pixelů). Ve druhém sloupci tabulky bude popis fotografie, jako je to znázorněno na následujícím informativním obrázku:

Displej

Navigace ze seznamu fotografií do režimu zobrazení jedné fotografie bude zajištěna tím, že každý náhled v seznamu bude zároveň odkaz na původní soubor JPEG. Kliknutím na něj prohlížeč danou fotografii zobrazí. Pro návrat zpět můžeme využít funkci „Back/Zpět“ prohlížeče.

Fotoalbum: Návrh

Kromě souborů JPEG s jednotlivými fotografiemi budeme potřebovat také soubor(y), který bude nebo které budou obsahovat informace jako název fotografie a datum pořízení. Budou to XML soubory podle následujícího příkladu:

<?xml version="1.0" encoding="UTF-8"?>

<fotka src="hg04">
 <datum>29.9.2001</datum>
 <popis>Pohled na Dachstein</popis>
</fotka>

Formát je velmi jednoduchý. Element <datum> obsahuje datum pořízení fotografie, element <popis> zase stručný popis. Atribut src kořenového elementu <fotka> obsahuje jméno souboru s vlastní fotografií – přesněji řečeno jméno bez přípony .jpg. Ačkoliv nám tento přístup umožňuje pojmenovávat soubory s popisem nezávisle na jménech souborů JPEG, pro přehlednost je (zatím) pojmenujeme stejně, jen přípona bude .xml místo .jpg.

Dále bude nutné popsat vlastní fotoalbum. Zajistí to opět XML soubor – řekněme album.xml. Budeme potřebovat název alba, dobu, ze které jsou fotografie, a seznam fotografií, které jsou zařazené do alba. Soubor může vypadat například takto:

<?xml version="1.0" encoding="UTF-8"?>

<album xmlns:cinclude="http://apache.org/cocoon/include/1.0">
 <nazev>Pekelné hory</nazev>
 <datum>28.-30.9.2001</datum>
 <fotky>
  <cinclude:include src="texty/hg01.xml"/>
  <cinclude:include src="texty/hg03.xml"/>
  <cinclude:include src="texty/hg04.xml"/>
  <cinclude:include src="texty/hg05.xml"/>
  <cinclude:include src="texty/hg06.xml"/>
 </fotky>
</album>

Elementy <album>, <nazev> a <datum> asi nemá cenu komentovat, neboť jejich účel je zřejmý. Mnohem zajímavější jsou prvky <cinclude:include> ve jmenném prostoru http://apache.org/cocoon/include/1.0. Jsou to, řekněme,„příkazy“ pro transformátor „CInclude“, který vloží obsahy XML souborů s popisem jednotlivých fotografií (budou to soubory hg*.xml v adresáři texty) do souboru album.xml. Výsledkem této transformace budou události SAX reprezentující tato XML data:

<album>

...
 <fotky>
  <fotka src="hg01">
   ...
  </fotka>
  <fotka src="hg02">
   ...
  </fotka>

  ...
 </fotky>
</album>

Po této transformaci bude následovat už jen XSL transformace na XHTML a serializace.

Zatím jsme neřešili, jakým způsobem bude Cocoon posílat prohlížeči binární data JPEG souborů. Pro tento případ je celý mechanismus roury s (minimálně) generátorem a serializátorem zjevně předimenzovaný. Navíc binární JPEG formát lze stěží převést do rozumné XML reprezentace. Cocoon má pro tyto účely typ komponenty zvaný reader. Je to vlastně generátor se serializátorem v jedné komponentě, někdy i s nějakou ne-XML transformací.

Fotoalbum: Realizace

V adresáři cocoon-2.1.2/build/webapp si vytvoříme podadresář album, což bude domovský adresář naší aplikace, kde bude umístěna mapa ( sitemap.xmap). Dále si pod adresářem album vytvoříme následující adresářovou strukturu:

fotky
Zde budou fotografie ve formátu JPEG.
texty
Zde budou popisy jednotlivých fotografií. Podle výše uvedeného pravidla budou mít soubory stejný název jako JPEG ve výše uvedeném adresáři, jen přípona bude .xml místo .jpg. Dále zde bude soubor album.xml.
xsl
Místo pro XSL dokumenty pro transformace.
styl
Zde budou kaskádové styly (CSS) pro prezentaci.

Všechny potřebné soubory najdete také v archivu album02.zip (150 KB – včetně několika zkušebních fotografií). Zkontrolujte si ještě celou adresářovou strukturu, cesta k souboru např. album.xml by měla být: cocoon-2.1.2/build/webapp/album/texty/album.xml. Pokud si archiv rozbalíte do výše zmíněného adresáře „ album“, můžete si ověřovat poznatky prakticky. Zadejte v prohlížeči například: „ http://localhost:8888/album/seznam.html“. Všimli jste si, že jsem se nezmínil o restartu Cocoonu (který vám asi ještě běží podle postupu uvedeného výše)? Protože jej prostě restartovat v tomto případě není potřeba.

Podívejme se nyní na mapu aplikace, což je soubor sitemap.xmap, který musí být umístěn v adresáři cocoon-2.1.2/build/webapp/album. Pro přehlednost má následující výpis mapy očíslované řádky. Nezapomeňte, že tato čísla řádků do XML souboru nepatří.

01: <?xml version="1.0"?>
02:
03: <map:sitemap
04:  xmlns:map="http://apache.org/cocoon/sitemap/1.0">
05:
06: <map:components>
07:  <map:readers default="resource">
08:  <map:reader name="image"
09:      src="org.apache.cocoon.reading.ImageReader"/>
10:  </map:readers>
11: </map:components>
12:
13: <map:pipelines>
14:  <map:pipeline>
15:
16:   <map:match pattern="">
17:    <map:redirect-to uri="seznam.html"/>
18:   </map:match>
19:
20:   <map:match pattern="*.jpg">
21:    <map:read type="image"
22:       src="fotky/{1}.jpg" mime-type="image/jpeg"/>
23:   </map:match>
24:
25:   <map:match pattern="nahled/*.jpg">
26:    <map:read type="image"
27:       src="fotky/{1}.jpg" mime-type="image/jpeg">
28:     <map:parameter name="height" value="60"/>
29:    </map:read>
30:   </map:match>
31:
32:   <map:match pattern="*.css">
33:    <map:read src="styl/{1}.css" mime-type="text/css"/>
34:   </map:match>
35:
36:   <map:match pattern="seznam.html">
37:    <map:generate type="file" src="texty/album.xml"/>
38:    <map:transform type="cinclude"/>
39:    <map:transform type="xslt" src="xsl/seznam.xsl"/>
40:    <map:serialize type="xhtml"/>
41:   </map:match>
42:
43:  </map:pipeline>
44: </map:pipelines>
45:</map:sitemap>

Na řádcích 6 až 11 je deklarována komponenta typu reader, která bude zajišťovat posílání JPEG souborů prohlížeči, ale také „výrobu“náhledů zmenšováním originálních JPEG souborů na výšku 60 pixelů. Deklarace jednotlivých komponent je, dle mého názoru, poněkud slabším místem Cocoonu, protože není vždy úplně zřejmé, co, kdy a jak se musí nebo nemusí deklarovat. Ani dokumentace není v tomto vždy přesná a jednotná. Jednoduše řečeno, implicitní a základní komponenty se deklarovat nemusejí, pokud ovšem nechceme v deklaraci změnit nějaký jejich parametr (každá komponenta je více či méně parametrizovaná). Volitelné nebo uživatelské komponenty se obvykle deklarovat musejí. Poněkud kontroverzní také je, že při deklaraci typů komponent (např. v naší mapě uvádí skupinu deklarací komponent typu „reader“ řádek 7) se povinně zadává implicitní komponenta. Ačkoliv se to zdá dost komplikované, skutečnost je (většinou) jednodušší. Všimněte si také, že atribut src na řádku 9 specifikuje třídu (tj. přeložený kód Javy), která požadovanou komponentu implementuje.

Na řádku 13 začíná specifikace rour – naše mapa má rouru jen jednu. Elementy <map:match> v rouře rozdělují zpracování HTTP požadavku podle konce řetězce požadovaného URL. Řádky 16 až 18 specifikují, že pokud nebude požadován žádný konkrétní soubor, provede se přesměrovaní, jako by byl požadován soubor seznam.html. Na lokální instalaci Cocoonu by takový „prázdný“ požadavek na aplikaci album vypadal takto: „ http://local­host:8888/album/“.

POZOR! Na konci tohoto URL musí být to lomítko! Je to proto, že webové aplikace v Cocoonu (nebo jejich části) mohou být postaveny hierarchicky. Znamená to tedy (zjednodušeně řečeno), že pokud např. prohlížeč žádá "http://localhost:8888/xxx/yyy", má být vrácen zdroj yyy aplikace xxx. Pokud naproti tomu prohlížeč žádá

http://localhost:8888/xxx/yyy/“, je to prázdný požadavek na aplikaci yyy, která je hierarchicky pod aplikací xxx. Ačkoliv lze zařídit, aby Cocoon vrátil v obou případech totéž, implicitně tomu tak není.

Řádky 20 až 23 specifikují, že pokud bude prohlížeč požadovat jakýkoliv soubor s příponou „ .jpg“, Cocoon vrátí stejnojmenný soubor z adresáře fotky a sdělí prohlížeči, že vrácená data jsou typu obrázek ve formátu JPEG. Výběr bude úspěšný například pro požadavek „ http://localhost:8888/album/hg01.jpg“, ale ne už pro požadavek „ http://localhost:8888/album/adresar/hg01.jpg“, protože žolíkový znak „ *“ se úspěšně srovná jen se jménem, ve kterém se nevyskytuje znak lomítko („ /“) jako oddělovač cesty. Libovolný znak jména včetně lomítka lze zadat jako žolíkový dvojznak „ **“. Znaky ze začátku URL požadavku až po lomítko za názvem aplikace (v našem případě„… /album/“) se samozřejmě zde nepočítají, cesta se bere jako relativní. Znaky „ {1}“ (řádek 22) označují konkrétní řetězec, který vyhověl při srovnání s prvním žolíkovým znakem, obdobně řetězec, který vyhověl při srovnání s druhým žolíkovým znakem, by byl označen „ {2}“ apod. Zkuste si sami uvědomit, jak by vypadal zápis pravidla na řádcích 20 až 23:

  1. Kdybyste nechtěli oddělit jmenný prostor názvů JPEG souborů klienta a aplikace a mít stejnou adresářovou strukturu, tj. pokud by prohlížeč požadoval soubor „ http://.../2001/leto/hg01.jpg“, Cocoon by vrátil soubor „ .../2001/leto/hg01.jpg“. Názvy adresářů nebudou ovšem konstantní.
  2. Kdybyste chtěli, aby se vracely JPEG soubory výhradně z adresáře „ fotky“, i když uživatel zadá libovolný adresář.

Řešení najdete na konci článku.

O něco zajímavější jsou řádky 25 až 30. Zde se praví, že pokud prohlížeč bude požadovat jakýkoliv soubor s příponou „ .jpg“ z (virtuálního) adresáře „ nahled“, vezme se opět stejnojmenný soubor ze (skutečného) adresáře „ fotky“, ale komponenta „Image Reader“ obrázek zmenší na výšku 60 pixelů. Vytvořili jsme tak virtuální adresář s náhledy.

Řádky 32 až 34 jsou poměrně nezajímavé – musí tam být, aby Cocoon věděl, jak má posílat soubory s kaskádovými styly prohlížeči.

Nejzajímavější jsou řádky 36 až 41. Tam je specifikováno (či nakonfigurováno, chcete-li), co se bude dít, pokud si webový klient vyžádá soubor seznam.html

  • (řádek 37) vygenerují se události SAX na základě XML dat v souboru „ texty/album.xml“,
  • (řádek 38) transformátor „CInclude“ vloží na označená místa XML data ze souborů „ texty/hg*.xml“,
  • (řádek 39) XML data se převedou XSL transformací na XHTML,
  • (řádek 40) XHTML se serializuje do proudu a pošle zpět webovému klientovi.

K dokončení aplikace chybí už jen vytvořit vlastní XSL dokument pro transformaci našich XML dat popisujících album na tabulku v XHTML a kaskádový styl pro lepší prezentaci. Jedno z možných řešení je v následujících výpisech, které ponechávám bez komentáře (jsou opravdu jednoduché).

Soubor "xsl/seznam.xsl":

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:template match="/">
  <html>
   <head>
    <title>
     Fotoalbum
    </title>
    <link rel="stylesheet" href="seznam.css" type="text/css" />
    <meta http-equiv="content-type"
          content="text/html; charset=utf-8" />
   </head>
   <body>
    <h1><xsl:value-of select="/album/nazev"/></h1>
    <h2><xsl:value-of select="/album/datum"/></h2>
    <table>
    <xsl:apply-templates/>
    </table>
   </body>
  </html>
 </xsl:template>

 <xsl:template match="album/nazev|album/datum"/>

 <xsl:template match="fotka">
  <tr>
   <td>
    <a href="{@src}.jpg">
     <img src="nahled/{@src}.jpg"/>
    </a>
   </td>
   <td><xsl:value-of select="popis"/></td>
  </tr>
 </xsl:template>

</xsl:stylesheet>

Soubor "styl/seznam.css":

table {border: 1px #a00 solid; border-collapse: collapse; }
td {border: 1px #a00 solid; padding: 3px; text-align: center;}
a img {border: 0px;}
h1, h2 {color: #a00;}

Nyní zbývá už jen aplikaci vyzkoušet. Zadejte tedy do prohlížeče třeba" http://localhos­t:8888/album/ ". Nezapomeňte na ukončující lomítko v URL.

Výhledy na příště

Naše webové fotoalbum je zatím velice primitivní. Co zlepšit? Rozhodně by to chtělo lepší režim prohlížení jednotlivých fotografií. Třeba takový, že se na stránce zobrazí fotografie ve své originální velikosti, vlevo od ní náhled na předchozí a vpravo náhled na následující foto. Tyto náhledy by mohly sloužit také jako odkazy. A taky by tam měl být odkaz zpátky na seznam s náhledy, protože využívat funkci „Zpět“ prohlížeče nemusí být vždy možné.

Také popisy jsou jenom v češtině a naši přátelé ze zahraničí z nich moc nemají. Nešlo by to album nějak jednoduše internacionali­zovat? A když už mluvíme o změnách, tak většina aplikací dnes „skinuje“, má témata nebo mění svůj vzhled jiným způsobem. Možná by stálo za to dát uživatelům možnost alespoň si vybrat barevné schéma, abychom byli „in“.

No a taky Pepa a Jarda používají hlavně PDA a tam jsou ty originální fotografie moc velké. A mám dávat ty svoje fotky vůbec na web, co když se nebudou líbit? Jak to zjistit? Nějaká on-line anketa typu „líbí/nelíbí“ nebo „nejlepší a nejhorší fotka“ by se také hodila. I když to vlastně bude málo, lepší by bylo hodnocení každé fotky známkami jako ve škole nebo dokonce diskuse, kde by každý mohl prezentovat svůj názor na každou fotku, nebo … A taky by se dalo …

Je vidět, že možností, jak fotoalbum zdokonalit, je skutečně dost. Zcela jistě něco z toho (a zcela jistě ne všechno) realizujeme v příštím pokračování.

Řešení příkladů

Příklad 1:

<map:match pattern="**.jpg">
 <map:read type="image"
    src="{1}.jpg" mime-type="image/jpeg"/>

</map:match>

Příklad 2:

<map:match pattern="**/*.jpg">
 <map:read type="image"
    src="fotky/{2}.jpg" mime-type="image/jpeg"/>

</map:match>
Našli jste v článku chybu?

28. 1. 2005 19:26

AlesPavel (neregistrovaný)

nejak se mi nenacitaji obrazky na uvodni strance netusite kde je problem?

20. 11. 2003 9:22

hardcoder*ke (neregistrovaný)

nenarazil som na ziaden problem. pomalost aplikacie moze byt sposebena zlozitostou transformacie alebo spomalenim pri ziskavani dat. stalo by za uvahu spravit podrobne logovanie a najst _hrdlo_ ktore to sposobuje. inak cocoon je nadherny sposob komunikacie. dufam ze sa mu bude darit.

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Root.cz: Telegram spustil anonymní blog Telegraph

Telegram spustil anonymní blog Telegraph

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Vitalia.cz: Spor o mortadelu: podle Lidlu falšovaná nebyla

Spor o mortadelu: podle Lidlu falšovaná nebyla

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

DigiZone.cz: Sat novinky: Fransat UHD Demo

Sat novinky: Fransat UHD Demo

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

DigiZone.cz: ČRa DVB-T2 ověřeno: Hisense a Sencor

ČRa DVB-T2 ověřeno: Hisense a Sencor

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Vitalia.cz: Jmenuje se Janina a žije bez cukru

Jmenuje se Janina a žije bez cukru

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

120na80.cz: Vitaminová abeceda

Vitaminová abeceda