Grav

Aus LT42-Wiki
Zur Navigation springenZur Suche springen

Momentan setze ich auf vielen Seiten das Content Management System TYPO3 ein, das sehr mächtig und vielseitig, dadurch aber an einigen Stellen auch kompliziert ist. Besonders für kleine Seiten ist daher der Wunsch nach einem kleineren CMS immer größer geworden. Die Ausgabe 15/2016 der c't stellt eine spannende Alternative zur Verfügung: Das CMS Grav, das ohne Datenbank auskommt und alle Daten in Dateien speichert.

Wahlweise gibt es Grav mit oder ohne Administration-Oberfläche. Die Inhalte der Seiten können auch über die Kommandozeile manipuliert werden. Zur Bedienung durch Endanwender ist aber eine Admin-Oberfläche sinnvoll.

Installation

Zunächst lädt man sich das entsprechende Paket von der Seit https://getgrav.org/downloads herunter:

wget https://github.com/getgrav/grav/releases/download/1.1.1/grav-admin-v1.1.1.zip

Nach dem Entpacken

unzip grav-admin-v1.1.1.zip

ist die Installation unter http://example.com/grav-admin erreichbar. Für eine öffentliche Homepage kann es sinnvoll sein, die Dateien in das Wurzelverzeichnis der Domain zu kopieren. Wichtig ist hierbei, auch die .htaccess-Datei mit zu kopieren.

Wenn die Verzeichnisrechte stimmen, bekommen wir einen Dialog angezeigt, um unsere Benutzerdaten einzugeben. Falls nicht:

sudo chown www-data.www-data ./grav-admin
sudo chmod -R 0770 ./grav-admin

Grav-admin-createuser.png

Nach dem Erstellen des Benutzers, bekommen wir das Dashboard zu Gesicht, in dem wir Infos über den Zustand der Installation und die letzten Änderungen erhalten.

Grav-admin-dashboard.png

Konfiguration

Die Grav-Installation lässt sich bequem über die Weboberfläche konfigurieren. Dafür wählt man links den Punkt "Configuration" aus. Hier kann man im ersten Schritt die Sprache "de" unter "languages" hinzufügen, um die Administration auf Deutsch anzuzeigen. Mit dem Klick auf entsprechenden Button zum Speichern an der Oberseite des Bildschirms, wird die entsprechende Änderung wirksam. Hier können wir auch den Seitentitel und weitere Einstellungen verändern.

Grav arbeitet mit der Auszeichnungssprache YAML. Im Prinzip kann man also die gesamte Konfiguration über die Dateien unter ./grav-admin/user/config/ manipulieren.

Seiten erstellen

Unter dem Menüpunkt "Seiten" lassen sich die Seiten anlegen und verwalten. Um eine neue Seite zu erstellen, wählt man im oberen Menü den Punkt "Seite hinzufügen" aus. Die Positionierung der Seite, der Name und die Sichtbarkeit lassen sich im darauffolgenden Dialog einstellen. Die Seite lässt sich nach dem Erstellen noch weiter konfigurieren.

Grav arbeitet mit unterschiedlichen Seitenvorlagen. Je nach Theme lassen sich neben "Default" noch weitere Templates auswählen. So können beispielsweise ein Blog oder ein Kontaktformular einfach eingestellt werden.

Inhalt einstellen

Nach dem Anlegen der Seite landet man direkt auf der Eingabemaske für Inhalt. Hier bedient sich Grav der Textauszeichnungssprache Markdown. Die verschiedenen Gestaltungs- und Logikmerkmale, wie Überschrift, fett, kursiv oder unterstrichen lassen sich bequem über die Formatierungsleiste auswählen. Alternativ kann man die Formartierung auch direkt in das Textfeld eingeben, der Editor wandelt das dann auch entsprechend um:

  • # steht für die Überschrift 1, weitere Rauten markieren dann die weiteren Ebenen (## für Überschrift 2, etc.).
  • **Text** lässt den Text fett werden
  • _Text_ unterstreicht den Text
  • ~~Text~~ streicht den Text durch
  • === fügt einen Strich für die Zusammenfassung ein
  • [](http://) setzt einen Link
  • ![](http://) fügt ein Bild aus der angegebenen Quelle ein

Theme mit Twig

Spannend wird die Themeerstellung. Grav nutzt die Engine Twig, um Layouts mit Inhalt zu füllen. Dadurch lassen sich auch komplexe Layouts relativ einfach in Grav einbinden.

Zunächst installiert man die Developer-Tools entweder über die Oberfläche oder über den GPM (Grav Paket Manager):

./grav-admin/bin/gpm install devtools

Daraufhin lässt man sich ein neues leeres Theme anlegen:

./grav-admin/bin/plugin devtools new-theme
Enter Theme Name: LT42
Enter Theme Description: Simple theme providing bootstrap
Enter Developer Name: Lukas Thiel
Enter Developer Email: lukas@lt42.de
Please choose a template type
  [0] pure-blank
  [1] inheritence
 > 0

Das "pure-blank"-Theme erstellt ein neues leeres Theme, "inheritence" würde ein bestehendes Theme erweitern. Daraufhin ist unter ./grav-admin/user/themes/ das angegebene Verzeichnis erstellt worden. Generell gilt folgende Struktur:

lt42
|--blueprints
|--css
|--css-compiled
|--fonts
|--images
|--js
|--scss
|--templates
   |--forms
   |--modular
   |--partials
lt42.php
lt42.yaml
blueprints.yaml
screenshot.jpg
scss.sh
thumbnail.jpg

Die Struktur ist relativ selbst erklärend. Das Grav-Team rät, die entsprechenden Dateien in die dafürvorgesehenden Verzeichnisse zu legen. Für die Verwendung sind mindestens die blueprints.yaml, lt42.php, lt42.yaml und das Verzeichnis templates/ nötig. Für eine Veröffentlichung zusätzlich:

  • CHANGELOG.md
  • LICENSE
  • README.md
  • screenshot.jpg
  • thumbnail.jpg

Theme erstellen

Im Verzeichnis templates/ finden sich die HTML-Grundgerüste des Themes und der verschiedenen Templates. Im Ordner partials/ findet sich die Datei base.html.twig. Diese stellt das Grundgerüst des Themes dar. Die Datei spricht für sich: Es gibt verschiedene Blocks, in denen dynamisch Inhalt generiert wird. Interessant sind zusätzlich die angegebenen Assets. Jede CSS- oder JavaScript-Datei wird hinzugefügt. Daraufhin werden die Assets generiert und ausgegeben. Der Vorteil ist, dass man prinzipiell völlig ungeordnet alle Dateien in das Twig-Template schreiben könnte und die Sortierung trotzdem richtig wäre. Eine Übersicht über die verschiedenen Möglichkeiten bietet der Artikel zum Asset Manager in der Grav-Doku (https://learn.getgrav.org/themes/asset-manager).

Mein Ziel ist es, ein Bootstrap-Layout einzubinden. Ich habe das Bootstrap-Paket heruntergeladen und in die entsprechenden Verzeichnisse entpackt. Zusätzlich auch die im Bootstrap-Grundlayout (http://getbootstrap.com/getting-started/#template) eingebundenen JavaScripts, damit diese nicht von extern eingebunden werden müssen. Meine base.html.twig sieht so aus:

 {% set theme_config = attribute(config.themes, config.system.pages.theme) %}
 <!DOCTYPE html>
 <html lang="{{ grav.language.getActive ?: theme_config.default_lang }}">
 <head>
 {% block head %}
     <meta charset="utf-8" />
     <title>{{ site.title|e('html') }}: {% if header.title %}{{ header.title|e('html') }}{% endif %}</title>
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     {% include 'partials/metadata.html.twig' %} 
     <link rel="icon" type="image/png" href="{{ url('theme://images/logo.png') }}" />
     <link rel="canonical" href="{{ page.url(true, true) }}" />
     {% block stylesheets %}
         {#% do assets.addCss('http://yui.yahooapis.com/pure/0.6.0/pure-min.css', 100) %#}
         {% do assets.addCss('theme://css/font-awesome.min.css', 100) %}
         {% do assets.addCss('theme://css/bootstrap.min.css', 99) %}
         {% do assets.addCss('theme://css/bootstrap-theme.min.css', 98) %}
         {% do assets.addCss('theme://css/anpassung.css', 97) %}
     {% endblock %}
     {{ assets.css() }} 
     {% block javascripts %}
         {% do assets.addJs('jquery', {'priority' : 100, 'group' : 'bottom'}) %}
         {% do assets.addJs('theme://js/bootstrap.min.js',  {'priority' : 99, 'group' : 'bottom'}) %}
     {% endblock %}
     {{ assets.js() }}
 {% endblock head%}
 </head> 
         <!--<div class="wrapper padding">
             <a class="logo left" href="{{base_url == '' ? '/' : base_url }}">
                 <i class="fa fa-rebel"></i >
                 {{ config.site.title }}--> 
 <body id="top" class="{{ page.header.body_classes }}">
     <div id="einspaltig">
         <div class="container">
             {% block header %}
             <div class="row" id="header">
               <div class="col-md-12">
                 <div id="skiplink">
                  <a class="skip" title="Direkt zur Navigation springen" href="#navigation">Zur Navigation springen</a><br>
                  <a class="skip" title="Direkt zum Content springen" href="#content">Zum Content springen</a>
                 </div>
               Header
               </div>
             </div>
             {% endblock %}
             {% block header_navigation %}
             <div id="navbar">
                 <nav class="navbar navbar-default" role="navigation">
                     <a id="navigation" name="navigation"></a>
                     <!-- Titel und Schalter werden für eine bessere mobile Ansicht zusammengefasst -->
                     <div class="navbar-header">
                         <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
                             <span class="sr-only">Navigation ein-/ausblenden</span>
                             <span class="icon-bar"></span>
                             <span class="icon-bar"></span>
                             <span class="icon-bar"></span>
                         </button>
                         <a class="navbar-brand" href="#">Hauptmenü</a>
                     </div>
                     <!-- Alle Navigationslinks, Formulare und anderer Inhalt werden hier zusammengefasst und können dann ein- und ausgeblendet werden -->
                     <div class="collapse navbar-collapse" id="navbar-collapse-1">
                         {% include 'partials/navigation.html.twig' %}
                     </div> <!-- /.navbar-collapse -->
                 </nav>
             </div>
             {% endblock %}
             <!--<div  id="service">Service</div>
             <div id="rootline">Rootline</div>-->
             {% block body %}
             <div id="inhalt" class="row equalheight">
                 <div class="col-md-12 equal" id="hauptinhalt">
                     {% block content %}
                     {% endblock %}
                 </div>
             </div>
             {% endblock %}
             {% block footer %}
                 <div class="row" id="footer">
                     <div class="col-md-12">&copy; &nbsp; Netthelp.de (2016)</div>
                 </div>
             {% endblock %}
         </div>
     </div>
 {% block bottom %}
     {{ assets.js('bottom') }}
 {% endblock %}
 </body>

Spannend ist, dass Twig von Haus aus eine Funktion zum Ein- oder Abschalten des DropDown-Menüs mitbringt. Die navigation.html.twig sieht so aus:

 {% macro loop(page) %}
     {% for p in page.children.visible %}
         {% set current_page = (p.active or p.activeChild) ? 'active' : '' %}
         {% if p.children.visible.count > 0 %}
             <li class="dropdown {{ current_page }}">
                 <a href="{{ p.url }}" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                     {% if p.header.icon %}<i class="fa fa-{{ p.header.icon }}"></i>{% endif %}
                     {{ p.menu }}
                     <span class="caret"></span>
                 </a>
                 <ul class="dropdown-menu">
                     {{ _self.loop(p) }}
                 </ul>
             </li>
         {% else %}
             <li class="{{ current_page }}">
                 <a href="{{ p.url }}">
                     {% if p.header.icon %}<i class="fa fa-{{ p.header.icon }}"></i>{% endif %}
                     {{ p.menu }}
                 </a>
             </li>
         {% endif %}
     {% endfor %}
 {% endmacro %}
 
 <ul class="nav navbar-nav">
     {% if theme_config.dropdown.enabled %}
          {{ _self.loop(pages) }}
     {% else %}
         {% for page in pages.children.visible %}
             {% set current_page = (page.active or page.activeChild) ? 'active' : '' %}
             <li class="{{ current_page }}">
                 <a href="{{ page.url }}">
                     {% if page.header.icon %}<i class="fa fa-{{ page.header.icon }}"></i>{% endif %}
                     {{ page.menu }}
                  </a>
             </li>
         {% endfor %}
     {% endif %}
     {% for mitem in site.menu %}
         <li>
             <a href="{{ mitem.url }}">
                 {% if mitem.icon %}<i class="fa fa-{{ mitem.icon }}"></i>{% endif %}
                 {{ mitem.text }}
             </a>
         </li>
     {% endfor %}
 </ul>

Im oberen Teil wird eine Funktion zum Loopen des Dropdown-Menüs erzeugt. Diese wird im unteren Teil, je nachdem ob das Menü aktiviert ist oder nicht, ausgeführt.

Theme aktivieren

Stellt man das Theme in den Einstellungen oder unter dem Menü-Punkt "Themes" um, kann man sein Ergebnis sofort betrachten. Nun fehlen noch ein zweites Menü und die Einbindung des DropDown-Menüs.

Weblinks