Viele Seiten im Internet haben einen Bereich für Jedermann und einen für Mitglieder, z.B. Vereins- und Verbandsportale oder Shops, die spezielle Seiten und Services nur für registrierte Mitglieder anbieten. Solche Seiten kann man als Community-Portale bezeichnen.
Freilich wird die Bezeichnung Community-Portal auch für wesentlich komplexere Portale verwendet, die zum Beispiel eine an sozialen Netzwerken wie Facebook orientierte Funktionalität aufweisen. Hierzu gehören Timelines, Gruppen-Mitgliedschaften, Blogs, Wikis und gemeinschaftlich nutzbare Kalenderfunktionen usw. Diese sind hier nicht Gegenstand des Artikels. Wer sich dafür interessiert, der sei auf das Commons-Projekt und das Organic-Groups-Projekt verwiesen.
Im folgenden geht es hauptsächlich darum, wie man eine Menu-Struktur einrichtet, die eine saubere Trennung von internem und externem Bereich ermöglicht.
Um die folgenden Schritte nachzuvollziehen, sollte man eine frische Drupal-Installation aufsetzen und folgende Module installieren bzw. aktivieren:
- Nice-Menus
- 'me'-Aliases
- Pathauto
- Devel
- Devel generate
Als Alternative zu Nice-Menus kann man auch Menu Block verwenden oder Accordion menu.
Einrichten des Nice-Menus
Ich habe mich für das horizontale Nice-Menu entschieden, weil es sofort nach der Installation auch als hierarchisches Menu voll funktioniert und gut aussieht und außerdem relativ kompakt ist, was für die folgenden Schritte ebenfalls von Vorteil ist. Zunächst muss man das Nice-Menu aber noch einwenig konfigurieren.
Nach der Installation findet man zwei Blöcke (Nice menu 1 und Nice menu 2) unter admin/structure/block im Bereich disabled. Diese füge ich beide in die Header-Region ein und benenne sie um z.B. in Nice external menu und Nice internal menu (vgl. Abb. 1, Reihenfolge beachten).
Abb. 1: Integration von zwei Nice-Menus in die Header-Region
Dort rufe ich dann für beide Menus die Konfiguration auf (configure) und stelle als wichtigste Option den Menu Style beider Menus auf down. Damit habe ich die horizontale Version gewählt.
Einrichten der Menu-Punkte des internen und des externen Menus
Jetzt richte ich die Menupunkte für die beiden umschaltbaren Menus ein. Unter admin/structure/menu füge ich zum vorhandenen Main menu ein weiteres Menu Menu-internal hinzu.
Ich benötige jetzt einige Lore-Ipsum-Artikel und Basic-Pages. Die Lore-Ipsum-Artikel sollen auf der externen Landing-Page (Home) erscheinen. Hierzu erzeuge ich mit dem Devel generate-Modul einfach 20 Artikel ohne Kommentare. Auf keinen Fall Menupunkte automatisch erzeugen, das mache ich später von Hand.
Ich benötige jetzt noch ein paar Basic-Pages, auf die ich per Menu-Items verlinken kann. Im Erstellungsformular der Basic-Pages beachte ich folgendes:
- Ich lege die Menu-Punkte direkt beim Erstellen der Basic-Pages an (Menu settings: Provide a menu link).
- Außerdem definiere ich dort manuell die Url-Aliase. Ich habe ja das Modul Pathauto installiert (Url path settings: Url alias).
Im einzelnen benötige ich folgende Basic-Pages:
Basic Pages für das Main Menu (Titel: Url alias)
- Services: content/services
- Contact: content/contact
Basic Pages für das Menu-internal (Titel: Url alias)
- Incentives: internal/incentives
Ich will im internen Menu außerdem einen Link auf das Userprofil eines eingeloggten Besuchers haben. Dazu brauche ich das Modul 'me'-Aliases, weil dieses einen einheitlichen Link (also ohne User-Id) für alle eingeloggten User zur Verfügung stellt. Für den internen Menu-Link "Profil" steht mir dann folgende Zugriffsmöglichkeit zur Verfügung:
- Profil: user/me
Meine beiden Menus sollen am Ende aussehen, wie im Teaserbild dieses Artikels ganz oben dargestellt. Ich brauche jetzt deshalb noch die Pfade zu den beiden Landing-Pages:
- Home:
- Community: community/frontpage
Die Erstellung von Landing-Pages ist ein eigenes Thema, das in diesem Posting nicht behandelt wird.
Als Landing-Page für den Menu-Punkt Home belasse ich es bei der Standard-Einstellung meiner Sandbox-Installation (Default Drupal Frontpage).
Als Landing-Page für den Menu-Punkt Community, der sowohl im internen als auch externen Menu enthalten sein soll, lege ich entweder eine weitere Basic Page an oder ich aktiviere den View Front page (unter: admin/structure/views), gebe ihm den Titel Community und setze im View unter Page settings den Pfad auf /community/frontpage.
Wichtig ist, dass der erste Teil des Pfades community nicht internal oder content ist, weil ich später per Hook entscheiden will, welches Menu abhängig vom Kontext bei Aufruf der Community-Seite gezeigt werden soll. Die Konfigurationsmöglichkeiten von Drupal alleine reichen hier nicht aus.
Nachdem ich also festgelegt habe, welcher Content unter welchem Item der beiden Menus (intern und extern) angezeigt werden soll und welche Pfade diese Inhalte haben sollen, kann ich die beiden Menu-Listen aufbauen.
Ich behalte das "Main-menu" und lege ein zusätzliches Menu "Main-internal" an (s. Abb. 2).
Abb. 2: Anlegen eines zusätzlichen Menus "Main-internal" unter "admin/structure/menu"
Für "Main menu" erzeuge ich folgende Menu-Einträge (Titel: Pfad):
- Home: <front>
- Services: content/services
- Contact: content/contact
- Community: community/frontpage
Für "Main internal" (Titel: Pfad):
- Community: community/frontpage
- Incentives: internal/incentives
- Profil: user/me
- Home: <front>
Der Pfadbestandteil content ist übrigens für das externe Menu nicht so wichtig. Dort könnte der Pfad auch lauten node/23 oder einfach services oder ähnlich. Interner Inhalt sollte dagegen, wenn es irgendwie geht, immer als ersten Pfadbestandteil internal beinhalten. Ausnahmen davon, z.B. user/me, erfordern dort immer eine Sonderbehandlung, wie weiter unten beim Thema Konfiguration deutlich wird. Aber zumindest für den anonymen User bleibt bei dem gewählten Ansatz für den Designer die volle Flexilibilität für die Pfad-Festlegung erhalten.
Nachdem ich die beiden Menu-Listen aufgebaut habe, muss ich die Menu-Listen noch mit den Nice-Menus verbinden. Dazu gehe ich zum Beispiel für das interne Nice-Menu in die Konfiguration (admin/structure/block/manage/nice_menus/2/configure) und stelle dort unter Menu parent <main-internal> ein. Das gleiche mache ich für das externe Nice-Menu, nur das ich dort <main-menu> auswähle.
Danach sollten beide Menus ähnlich wie im Teaser-Bild ganz oben aussehen.
Allerdings möchte ich ja immer nur eines der Menus abhängig vom Kontext sehen:
- Anonymous user: immer externes Menu
- User logged in nach Klick auf Home: externes Menu
- User logged in nach Klick auf Community: internes Menu
Die Darstellung der Nice-Menus enthält im Bartik-Theme noch einen Schönheitsfehler. Die Schrift des aktiven Menus ist weiß genauso wie der Hintergrund des Menus. Dies werde ich später verbessern, wenn ich das noch zu erstellende "sandbox1"-Sub-Theme modifiziere.
Außerdem sehe ich im Moment noch das Standard-Haupt-Menu im Drupal-Theme, das ich noch ausschalten muss.
Es ist aber zunächst gute Praxis zu versuchen, dem gewünschten Ziel mit den Konfigurationsmöglichkeiten von Drupal so nahe wie möglich zu kommen.
Deaktivieren des Standard-Menus des Themes
Als erstes wird das originale Theme-Menu, das Standard-Menu, abgeschaltet. Die meisten Themes ermöglichen dies unter "admin/appearence/settings". Für das Bartik-Theme der frischen Installation wird dann unter "admin/appearence/settings/bartik":TOGGLE DISPLAY der Punkt "Main menu" deaktiviert.
Konfiguration der beiden "Nice menu"-Blöcke
Ich kann bis zu einem gewissen Grad für jeden Block einstellen, auf welchen Seiten er erscheinen soll. Für das Nice external menu gehe ich deshalb unter admin/structure/block/ auf configure und stelle dort unter pages ein:
All pages except those listed
- internal/*
- user/me
Ich schließe die Anzeige also aus für die Profil-Seite des eingeloggten Users sowie für alle Seiten, die speziell über den Pfad als internal gekennzeichnet sind.
Ähnlich gehe ich für das Nice internal menu vor:
Only the listed pages
- internal/*
- user/me
- community/*
Ich zeige das interne Menu nur auf den Seiten an, die speziell als intern deklariert sind (über den Pfad), auf der Profilseite des eingeloggten Besuchers und für die Community-Seite.
Außerdem muss ich die Anzeige des internen Menus für alle anonymen Nutzer ausschließen. Ich aktiviere unter roles für den Block Nice internal menu:
show block for specific roles
- authenticated user
- adminstrator
Damit funktioniert das Menu für alle Menupunkte bis auf den Menu-Punkt Community. Wenn man darauf klickt, werden beide Menus unerwünschter Weise angezeigt.
Leider reichen die Konfigurationsmöglichkeiten von Drupal hier nicht mehr aus, um speziell diesen Fall zu lösen. Ich kann leider nur entweder bestimmte Seiten ausschließen oder bestimmte Seiten einschließen. Eine Kombination von beidem ist nicht möglich.
Hier bleibt nur die Programierung eines Hooks, was im folgenden gezeigt wird.
Es bleiben also zwei Probleme übrig, die ich mit einem Sub-Theme lösen muß:
- Eine Modifikation der Style-Sheets des Bartik-Basis-Theme, damit ein aktiver Menu-Punkt in roter Schrift angezeigt wird.
- Die Anzeige nur eines Menus (intern oder extern), wenn auf den Menu-Punkt "Community" als eingeloggter Benutzer geklickt wird.
Dazu muss ich jetzt ein Sub-Theme "sandbox1" einrichten.
Das Bartik-Theme, welches die frische Drupal-Installation verwendet, befindet sich im "themes"-Ordner direkt unterhalb des Root-Ordners. Dort sollte man niemals etwas verändern. Stattdessen lege ich ein vom Theme "Bartik" abgeleitetes Sub-Theme im Ordner "sites/all/themes/" an mit dem Namen sandbox1.
Grundsätzlich sollte man originale Themes nicht überschreiben, sondern immer ein Sub-Theme anlegen. Das gilt nicht nur für die Drupal-Standard-Themes, sondern auch für freie oder gekaufte Themes aus dem Internet.
Der Vorteil ist, dass man diese Themes dann updaten kann und außerdem im Sub-Theme nur die eigenen Modifikationen vorfindet, was die Übersicht über das Projekt ganz hervorragend verbessert.
Einrichten eines Sub-Themes "sandbox1" für Modifikationen am Basis-Theme "Bartik"
Die Einrichtung des Sub-Themes beginnt mit dem Anlegen von sandbox1.info.
name = Sandbox1 description = An experimental theme for playing around with theming aspects. base theme = bartik package = Core screenshot = screenshot.png ; Information e.g. added by drupal.org packaging script version = "7.x-1.2-alpha1" core = 7.x ; exclude this to prevent update requests ; project = "drupal" ; datestamp = "1372529459" stylesheets[all][] = quick-changes.css regions[header] = Header regions[help] = Help regions[page_top] = Page top regions[page_bottom] = Page bottom regions[highlighted] = Highlighted regions[featured] = Featured regions[content] = Content regions[sidebar_first] = Sidebar first regions[sidebar_second] = Sidebar second regions[triptych_first] = Triptych first regions[triptych_middle] = Triptych middle regions[triptych_last] = Triptych last regions[footer_firstcolumn] = Footer first column regions[footer_secondcolumn] = Footer second column regions[footer_thirdcolumn] = Footer third column regions[footer_fourthcolumn] = Footer fourth column regions[footer] = Footer
Eine .info-Datei deklariert ein Sub-Theme
- durch hinzufügen der Zeile "base theme = bartik" und
- durch Übernahme der regions das Basis-Themes (Copy&Paste aus bartik.info).
Außerdem erzeuge ich im Theme-Ordner sandbox1 eine CSS-Datei quick-changes.css die ich in sandbox1.info deklariere:
- stylesheets[all][] = quick-changes.css
In diese Datei füge ich später ein 3-zeiliges CSS-Snippet (s. weiter unten) für die Änderung der Schriftfarbe des aktiven Menupunktes ein.
Außerdem lege ich für den zu implementierenden Hook noch eine leere Template-Datei an:
- template.php
Damit habe ich alle notwendigen Vorbereitungen für das Theming unseres Community-Hauptmenus getroffen.
Schönheitsreparatur CSS-Style-Sheet
Obwohl Nice-Menus nach der Installation schon sehr gut aussehen, benötigt man noch eine kleine Style-Modifikation, denn das Bartik-Theme verhindert eine gute Anzeige des aktiven Links des Menus. Um dem aktiven Menu-Link z.B. eine rote Farbe zu geben muss ich irgendwo folgenden Style hinzufügen:
.nice-menu a.active { color:red !important; }
Dieses Snippet füge ich, wie schon angedeutet, in die zuvor angelegte Datei quick-changes.css ein. Einmal Cache clear und die Menu-Schrift-Farben sollten so aussehen, wie im Teaser-Bild dieses Artikels ganz oben.
Aufbau von zwei getrennten Menus
Als letztes bleibt noch das Problem, dass bei einem Klick auf den "Community"-Link beide Menus erscheinen.
Ich hatte die Datei template.php im Ordner des sandbox1-Themes angelegt. Dort füge ich folgenden Hook ein:
<?php /** * Implements hook_block_list_alter() * * Hides the the external menu * if page 'community' is active and user is logged in * * @param $blocks (call by ref) * A list of all blocks * * @return void */ function sandbox1_block_list_alter(&$blocks) { // Suppress the first menu only for the community frontpage if user is logged in if (((drupal_get_path_alias()== 'community/frontpage')) && (user_is_logged_in())) { // Go through all blocks and hide the first nice menu (delta 1). foreach ($blocks as $i => $block) { if (($block->module == 'nice_menus') && ($block->delta == 1)){ unset($blocks[$i]); } } } }
Der Hook fängt die Anfrage an die Community-Landing-Page über einen Pfadvergleich ab und prüft, ob der User eingelogged ist. In diesem Fall werden alle aktiven Blöcke des Themes durchlaufen und speziell das erste gefundene Nice-Menu wird aus der Liste herausgenommen, bevor die Seite gerendert wird.
Alle anderen Fälle wurden ja schon durch die Konfiguration abgefangen, so dass hier jetzt eine einfache Logik reicht, um das gewünschte Ergebnis zu erzielen.
Das Menu verhält sich jetzt, wie gewünscht. Sowohl externe als auch interne Besucher bekommen immer die gleiche Community-Seite zu sehen. Ist der Besucher eingeloggt, wird die Community-Seite zur Landing-Page. Er kann aber durch Klicken auf Home (dann am Ende des Menus) auch die Menu-Punkte für den externen User erreichen.
Fazit
Es wurden zunächst alle Konfigurationsmöglichkeiten ausgenutzt, die zur Verfügung standen. Es zeigte sich, dass diese nicht ausreichen. Deshalb wurde ein Sub-Theme angelegt.
Dort wurden einige optische Schönheitskorrekturen vorgenommen und es wurde zur Lösung des Haupt-Problems, nämlich die Kontext-abhängige Anzeige von internem und externem Menu ein Hook der Block-Api von Drupal implementiert (hook_block_list_alter).
Grundsätzlich sollte man immer in dieser Reihenfolge vorgehen, wenn man ein Theme modifzieren möchte:
- Sub-Theme erstellen,
- "Preprocess"-Hooks suchen (bzw. "Process"-Hooks),
- "Alter"-Hooks suchen,
- Templates modifzieren als letzte Möglichkeit.
Leider ist die Versuchung bei diesem und ähnlichen Problemen groß, "quick & dirty" die Template-Datei page.tpl.php zu überschreiben. Das Ergebnis wird dann, wenn das Projekt weiter fortgeschritten ist, grandioser Spagetti-Code sein.
Hält man sich dagegen an die vorgeschlagene Reihenfolge, hat man gute Chancen, ein Investitionssicheres Portal zu bauen.
Kommentare
Das interne Menu für anonyme
Das interne Menu für anonyme Besucher lässt sich nur ausblenden, wenn man denen die Berechtigung dafür entzieht. Das wird in Deiner Beschreibung nicht erwähnt.
Danke für den Hinweis
Ich hab den Artikel entsprechend ergänzt.