Aufbau der Navigation mit SP Page und SP PageTree

Aus SiteparkWiki
Zur Navigation springen Zur Suche springen

Exemplarischer Aufbau der Navigation mit den PHP-Klassen aus der sp-site-api

Initiale Eingabe

Zunächst müssen die initialen Standard-Eingabefelder für die Navigation zur Verfügung gestellt werden. Dazu gehört in der Regel ein vom ArtikelNamen unabhängiger Titel. Zusätzlich muss angegeben werden, ob der aktuelle Artikel der Root-Knoten der Navigation ist. Ein weiterer Parameter kann bestimmen, ob der Artikel nicht automatisch in die Navigation aufgenommen werden soll. Schließlich wird noch die für die Navigation relevante Verknüpfung zu dem ElternObjekt eingegeben.

<sp:io type="in">
  <label>Titel</label> <sp:text name="sp_title"/><br />
  <sp:checkbox name="sp_isHomepage" value="true"/> <label for="<sp:print name="system.tag"/>">Ist die Homepage:</label><br />
  <sp:checkbox name="sp_hideInMenu" value="true"/>  <label for="<sp:print name="system.tag"/>">Im Menü verbergen</label><br />
  <br />
  Navigation:
  <spt:iterator name="sp_parentIterator">
    <label>Alternativer Navigations-Titel für diese Verknüpfung</label><br />
    <sp:text name="sp_title"/><br>
    <label>Übergeordnetes Navigations-Objekt<br>
    <spt:link name="sp_parent" pools="!{gui.informations}"/></label><br />
  </spt:iterator>
</sp:io>

Initiale Ausgabe (PHP)

Im ersten Teil der PHP-Ausgabe werden verschiedene Objekte wie SP_Context initialisiert. Anschließend wird die SP_Page Klasse mit den Daten des aktuellen Artikels initialisiert und mit den nötigen Werten befüllt.

<sp:io type="out"><?php
  if (!class_exists("SP_Context")) {
    // Die Contextklasse setzt die Pfade aller vom IES zur Verfügung gestellten Module wie sp-base und sp-site-api in den PHP-Include Pfad.
    require_once(__DIR__ . '/<sp:print name="system.information.url.relativePathToDocumentRoot" /><sp:print name="system.publisher.directory"/>WEB-IES/ies-api/php/SP/Context.php');
    SP_Context::init();
  }
  SP_Context::requireClass('sp-site-api', 'SP/Page');

  // erstelle das Page-Objekt mit den Daten dieser Seite
  $page = SP_Page::create(
        "<sp:print name="id.withoutSignature"/>",
        <sp:print name="version"/>,
        "<sp:print name="url"/>",
        "<sp:print name="name" encoding="ascii,html,script"/>",
        "<sp:print name="sp_title" default="${name}" encoding="ascii,html,script"/>");

  // definiere den root Knoten 
  <sp:if name="sp_isHomepage">
    $page->setHomepage(true);
  </sp:if>

  // Die Verlinkung(en) auf andere Artikel sind die Navigations-Eltern dieses Objektes
  <sp:loop collection="referenceLinks('sp_parent.link')" item="it">
    <sp:if name="it.child.isPublished">
      $page->addParent("<sp:print name="it.child.withoutSignature"/>",
                       "<sp:print name="it.child.url"/>");
    </sp:if>
  </sp:loop>

  // Alle Verlinkungen auf diesen Artikel sind Navigations-Kinder
  <sp:loop collection="referrerLinks('sp_parent.link')" item="it">
    <sp:if condition="${it.parent.url} != '' && ${it.parent.sp_hideInMenu} != true">
      $page->addChild("<sp:print name="it.parent.withoutSignature"/>",
                      "<sp:print name="it.parent.url"/>");
      <sp:if name="it.item.parent.sp_title">
        $page->setChildAttribute("<sp:print name="it.parent.withoutSignature"/>",
                                 "title",
                                 "<sp:print name="it.item.parent.sp_title"/>");
      </sp:if>
    </sp:if>
  </sp:loop>


  // Beim Einlesen dieser Seite für die Navigationsinformationen durch andere Seiten darf die Seite nicht weiter ausgeführt werden. 
  if (!$page->isActive()) {
    return;
  }

?></sp:io>


Anschließen können basierend auf einem Template mit diesen Ein- und Ausgabebereichen mehrere Testartikel angelegt und miteinander verknüpft werden. In den vom IES generierten Dateien sollten die gewünschten Daten und PHP-Objekte generiert worden sein.


Ausgabe der Navigation in der HTML-Seite

In dem sp-site-api Modul werden neben der Klasse SP_Page unter anderem auch die Klassen SP_PageTree und SP_PageTreeHtmlRenderer zur Verfügung gestellt. Diese ermöglichen eine performante Ausgabe einer HTML-Navigationsstruktur. Das folgende Beispiel zeigt verschiedenen Modi der Ausgabe des Navigation-Baums.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
  <title><sp:print name="sp_title" default="${name}"/></title>
  <meta http-equiv="content-type" content="text/html; charset=<sp:print name="system.publisher.encoding"/>" />
  <style>
    /* zunächst die Styles zurücksetzen. */
    .leaf > a {
      font-weight: normal;
      font-style: normal;
      text-decoration: initial;
    }
    /* Der aktuelle Eintrag wird fett dargestellt. */
    .active > a {
      font-weight: bold;
    }
    /* der direkte Parent ist kursiv. */
    .parent > a {
      font-style: italic;
    }
    /* Die Objekte in dem Pfad bis zum RootNode werden unterstrichen.  */
    .path > a {
      text-decoration: underline;
    }
  </style>

</head>
<body>
  <h1><sp:print name="sp_title" default="${name}"/></h1>

  <?php
  // Für die Ausgabe wird der Navigations-Renderer benötigt. Derzeit gibt es nur einen HTML-Renderer
  $renderer = SP_Page::getActive()->getNavigation()->getRenderer("html");
  ?>
  <strong>Zunächst mal die Kinder dieser Seite:</strong><br />
    $activeChildren = SP_Page::getActive()->getChildren();
    if ($activeChildren !== null && count($activeChildren) > 0) {
      $renderer->renderChildren('menu', SP_Page::getActive()->getNavigation()->getActive(), 'class-fh-ms');
    } else {
      echo 'Dieses Objekt besitzt keine Kinder-Elemente. <br />';
    }
  <br />
  <strong>Die Kinder des Root-Knotens:</strong><br />
  <?php
    $renderer->renderChildren('menu', SP_Page::getActive()->getNavigation()->getRoot(), 'class-fh-ms', 1 );
  ?>

  <h2>Hier wird nun das gesamte Menü in den verschiedenen Modi dargestellt</h2>

  <strong>Modus: menu</strong> (Es werden die Nodes bis zum aktiven Node sowie alle Geschwister und Kinder dargestellt)<br />
  <?php
    $renderer->render('menu');
  ?>
  <strong>Modus: compact</strong> (Bis zum aktiven Node werden keine Geschwister dargestellt. Es werde noch die Kinder des aktiven Nodes angezeigt. Hat der aktive Node keine Kinder werden dessen Geschwister angezeigt)<br />
  <?php
    $renderer->render('compact');
  ?>
  <strong>Modus: path</strong> (Es werden nur die Nodes die direkt zum aktiven Node führen dargestellt, ohne Geschwister oder Kinder<br />
  <?php
    $renderer->render('path');
  ?>
  <strong>Modus: sitemap</strong> (Es werden ab den start-Node alle Unter-Nodes dargestellt)<br />
  <?php
    $renderer->render('sitemap');
  ?>
  <strong>Modus: sitemap, nur 2 Ebenen und ohne das Root-Element</strong><br />
  <?php
    $renderer->render('sitemap', false, null, 2);
  ?>


<br />
<h2>Legende</h2>
<ul>
  <li class="leaf active">
    <a href="#">Das aktive Element</a>
  </li>
  <li class="leaf parent">
    <a href="#">Das Eltern-Element</a>
  </li>
  <li class="leaf path">
    <a href="#">Ein Element im Pfad</a>
  </li>
</ul>

</body>
</html>


Vollständiges TestTemplate

Der folgende Quelltext zeigt das gesamte Template. Zusätzlich sind weitere Optionen und Parameter integriert, die einen Eindruck über die komplexen Möglichkeiten der API andeuten soll. Alle weiteren Detail sind in der Dokumentation beschrieben.


<sp:io type="in">
  <label>Titel</label><br />
  <sp:text name="sp_title" size="40"/><br />
  <sp:checkbox name="sp_isHomepage" value="true"/> <label for="<sp:print name="system.tag"/>">Ist die Homepage.</label><br />
  <sp:checkbox name="sp_isRubric"   value="true"/> <label for="<sp:print name="system.tag"/>">Ist eine Rubrik.</label><br />
  <sp:checkbox name="sp_hideInMenu" value="true"/> <label for="<sp:print name="system.tag"/>">Im Menü verbergen.</label><br />
  <sp:checkbox name="sp_nopage"     value="true"/> <label for="<sp:print name="system.tag"/>">Ist keine eigene Seite.</label><br />
  Url einer externe Seite:<br />
  <sp:text name="sp_external" size="40"/><br>
  Medium:<br />
  <spt:link name="sp_media" pools="!{gui.informations}"/> <sp:checkbox name="sp_media_as_download" value="true"/> <label for="<sp:print name="system.tag"/>">Als Download: </label><br />
  Farbe der Rubrik:<br />
  <sp:text name="sp_border_color" size="40"/><br />
  <br />

  <strong>Navigation:</strong>
  <spt:iterator name="sp_parentIterator">
    <label>Alternativer Navigations-Titel für diese Verknüpfung:</label><br />
    <sp:text name="sp_title" size="40"/><br />
    <label>Alternativer Navigations Sortierung für diese Verknüpfung:</label><br />
    <sp:text name="sp_sort" size="40"/><br />
    <label>Übergeordnetes Navigations-Objekt</label><br />
    <spt:link name="sp_parent" pools="!{gui.informations}"/><br />
  </spt:iterator>


</sp:io><sp:io type="out"><?php
  /*
   * Initialisierung
   */
  if (!class_exists("SP_Context")) {
    // setzen des php include_path
    require_once(__DIR__ . '/<sp:print name="system.information.url.relativePathToDocumentRoot" /><sp:print name="system.publisher.directory"/>WEB-IES/ies-api/php/SP/Context.php');
    SP_Context::init();
  }
  SP_Context::requireClass('sp-site-api', 'SP/Page');

  /*
   * erstelle das Page-Objekt mit den Daten dieser Seite
   */
  $page = SP_Page::create(
        "<sp:print name="withoutSignature"/>",
        <sp:print name="version"/>,
        "<sp:print name="url"/>",
        "<sp:print name="name" encoding="ascii,html,script"/>",
        "<sp:print name="sp_title" default="${name}" encoding="ascii,html,script"/>");
 
  /*
   * Definiere diese Seite als Root-Knoten 
   */
  <sp:if name="sp_isHomepage">
    $page->setHomepage(true);
  </sp:if>

  /*
   * Definiere den Rubrik-Seite
   * Kann zur Ermittlung von Rubrik-Information dienen (zB. Farben)
   */
  <sp:if name="sp_isRubric">
    $page->setRubric(true);
  </sp:if>
  <sp:if name="sp_border_color">
    $page->setAttribute("colorTheme", "<sp:print name="sp_border_color"/>");
  </sp:if>

  /*
   * Verweist der Menü-Eintrag auf eine externe Url?
   */
  <sp:if name="sp_external">
    $page->setLink("<sp:print name="sp_external"/>");
  </sp:if>

  /*
   * Soll ein 'Blind-Eintrag' im Menü ergänzt werden?
   */
  <sp:if name="sp_nopage">
    $page->setLink("");
  </sp:if>

  /*
   * Gibt es Personalsierungs-Informationen zu diesem Objekt
   */
  <sp:if name="publicationState(${system.publisher}).personalization.hasRestrictions">
    $page->setAuthType("<sp:condition><sp:if condition="${publicationState(${system.publisher}).personalization.isEmpty} || ${publicationState(${system.publisher}).personalization.personalizationExclusion}">deny</sp:if><sp:else>allow</sp:else></sp:condition>");
    <sp:loop collection="publicationState(${system.publisher}).personalization.personalizationGroups" item="it">
      $page->addAuthGroup("<sp:print name="it.withoutType"/>");
    </sp:loop>
    // Wenn die Inhalten nicht dargestellt werden dürfen, kann hier abgebrochen werden.
    if ($page->isAllow()) {
      return;
    }
  </sp:if>

  /*
   * Die Verlinkung(en) auf andere Artikel sind Navigations-Eltern
   */
  <sp:loop collection="referenceLinks('sp_parent.link')" item="it">
    <sp:if name="it.child.isPublished">
      $page->addParent("<sp:print name="it.child.withoutSignature"/>",
                       "<sp:print name="it.child.url"/>");
    </sp:if>
  </sp:loop>

  /*
   * Alle Verlinkungen auf diesen Artikel sind Navigations-Kinder
   */
  <sp:loop collection="referrerLinks('sp_parent.link')" item="it">
    <sp:if condition="${it.parent.url} != '' && ${it.parent.sp_hideInMenu} != true">
      $page->addChild("<sp:print name="it.parent.withoutSignature"/>",
                      "<sp:print name="it.parent.url"/>");
      <sp:if name="it.item.parent.sp_title">
        $page->setChildAttribute("<sp:print name="it.parent.withoutSignature"/>",
                                 "title",
                                 "<sp:print name="it.item.parent.sp_title"/>");
      </sp:if>
      <sp:if name="it.item.parent.sp_sort">
        $page->setChildAttribute("<sp:print name="it.parent.withoutSignature"/>",
                                 "sort",
                                 "<sp:print name="it.item.parent.sp_sort"/>");
      </sp:if>
    </sp:if>
  </sp:loop>
 
  /*
   * Beim Einlesen für die Navigation darf die Seite nicht weiter ausgeführt werden. 
   */
  if (!$page->isActive()) {
    return;
  }


  /*
   * Sollte diese Artikel (Menü-Eintrag) auf ein Medium verweisen, kann es als Download zur Verfügung gestellt werden. 
   */
  <sp:if name="sp_media.link.url">
    <sp:condition>
      <sp:if name="sp_media_as_download">
        header("Content-type: application/octet-stream");
        header("Content-Disposition: attachment; filename=\"<sp:print name="sp_media.link.filename"/>\"");
      </sp:if>
      <sp:else>
        header("Content-type: <sp:print name="sp_media.link.mime"/>");
      </sp:else>
    </sp:condition>
    readfile($_SERVER["DOCUMENT_ROOT"]."<sp:print name="sp_media.link.url"/>");
    return;
  </sp:if>

  /*
   * Schließlich kann die HTML-Seite ausgegeben werden. 
   */
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
  <title><sp:print name="sp_title" default="${name}"/></title>
  <meta http-equiv="content-type" content="text/html; charset=<sp:print name="system.publisher.encoding"/>" />
  <style>
    /*
     * Kleine Style-Anpassung zur Veranschaulichung verschiedener Attribute
     */
    .leaf > a {
      font-weight: normal;
      font-style: normal;
      text-decoration: initial;
    }
    /* Der aktuelle Eintrag wird fett dargestellt. */
    .active > a {
      font-weight: bold;
    }
    /* der direkte Parent ist kursiv. */
    .parent > a {
      font-style: italic;
    }
    /* Die Objekte in dem Pfad bis zum RootNode werden unterstrichen.  */
    .path > a {
      text-decoration: underline;
    }

    /*
     * Über die Rubrik kann das ganze Farbschema definiert werden.
     */ 
    body a {
      color: #00008B;
      <?php
        $rubric = SP_Page::getActive()->getRubric();
        if ($rubric != null) {
          $borderColor = $rubric->getAttribute('colorTheme');
           echo "      color: " . $borderColor . ";";
        }
      ?>
    }

  </style>
 
</head>
<body>
  <h1><sp:print name="sp_title" default="${name}"/></h1>
 
  <?php
  /*
   * Für die Ausgabe wird der Navigations-Renderer benötigt. Derzeit gibt es nur einen HTML-Renderer
   */
  $renderer = SP_Page::getActive()->getNavigation()->getRenderer("html");
  ?>

  <strong>Aktive Rubrik:</strong><br />
  <?php
    $rubric = SP_Page::getActive()->getRubric();
    if ($rubric != null) {
       echo "Aktive Rubrik: " . $rubric->getTitle() . "<br />";
    } else {
       echo "Es befindet sich keine Rubrikseite im aktuellen Pfad.<br />";
    }
  ?><br />


  <strong>Zunächst mal die Kinder dieser Seite:</strong><br />
  <?php
    $activeChildren = SP_Page::getActive()->getChildren();
    if ($activeChildren !== null && count($activeChildren) > 0) {
      $renderer->renderChildren('menu', SP_Page::getActive()->getNavigation()->getActive(), 'class-fh-ms');
    } else {
      echo 'Dieses Objekt besitzt keine Kinder-Elemente. <br />';
    }
  ?>
  <br />
  <strong>Die Kinder des Root-Knotens:</strong><br />
  <?php
    $renderer->renderChildren('menu', SP_Page::getActive()->getNavigation()->getRoot(), 'class-fh-ms', 1 );
  ?>
  <br /> 

  <h2>Hier wird nun das gesamte Menü in den verschiedenen Modi dargestellt</h2>
 
  <strong>Modus: menu</strong><br />
   Es werden die Nodes bis zum aktiven Node sowie alle Geschwister und Kinder dargestellt.<br />
  <?php
    $renderer->render('menu');
  ?>

  <strong>Modus: compact</strong><br />
   Bis zum aktiven Node werden keine Geschwister dargestellt. Es werde noch die Kinder des aktiven Nodes angezeigt. Hat der aktive Node keine Kinder werden dessen Geschwister angezeigt.<br />
  <?php
    $renderer->render('compact');
  ?>

  <strong>Modus: path</strong><br />
   Es werden nur die Nodes die direkt zum aktiven Node führen dargestellt, ohne Geschwister oder Kinder.<br />
  <?php
    $renderer->render('path');
  ?>

  <strong>Modus: sitemap</strong><br />
   Es werden ab den start-Node alle Unter-Nodes dargestellt.<br />
  <?php
    $renderer->render('sitemap');
  ?>

  <strong>Modus: sitemap</strong><br />
   Hier nur 2 Ebenen und ohne das Root-Element.<br />
  <?php
    $renderer->render('sitemap', false, null, 2);
  ?>

  <strong>Modus: sitemap</strong><br />
   Mit Callback-Funktion für Ergänzungen:<br />
   - filter: alle Element mit dem Text 'child' im Title werden herausgefiltert<br />
   - render: alle Rubrikseiten werden 'uppercase' dargestellt<br />
  <?php
    $renderer->render('sitemap', false, null, 2, function($node, $mode, $info, $renderer) {

      if ($mode === 'beforerender') {

      } else if ($mode === 'filter') {

        if (stripos($node->getPage()->getTitle(), 'child') !== false) {
          return false;
        }
        return true;

      } else if ($mode === 'render') {
        return array(
               'li' => array( 
                 'title' => $node->getPage()->getTitle()
               ),
               'a' => array(
                 'class' => 'additionalClass',
                 'style' => $node->getPage()->isRubric() ? 'text-transform: uppercase' : ''
               ),
        );
      }
    });
  ?>

<br />
<div class="legend">
  <h2>Legende</h2>
  <ul>
    <li class="leaf active">
      <a href="#">Fett: Das aktive Element</a>
    </li>
    <li class="leaf parent">
      <a href="#">Kursiv: Das Eltern-Element</a>
    </li>
    <li class="leaf path">
      <a href="#">Unterstrichen: Ein Element im Pfad</a>
    </li>
  </ul>
</div>
<br />
<div class="debug">
  <h2>Debug-Informationen</h2>
  <?php SP_Page::status(); ?>
</div>

</body>
</html>

</sp:io>