Processwire - Template from Scratch

Aus Wikizone
Version vom 23. Oktober 2019, 09:29 Uhr von 84.157.23.32 (Diskussion) (→‎Seitentemplates - delayed output)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Wir erstellt man in Processwire ein Template ? So gehts.

Links[Bearbeiten]

ProcessWire - Snippets

Tipp: Module lassen sich unter Module > Neu direkt importieren wenn man den Modulklassennamen angibt.

Processwire installieren[Bearbeiten]

  • Install
    • Installer laufen lassen, als Option Blank Template wählen.
    • Backend Template
      • Reno Modul installieren (ist schon drin, muss nur aktiviert werden)
    • Language Support
      • Language Support Module installieren
      • Language in Setup anlegen (German)
      • Language Pack für Core hochladen

Wichtige Module installieren[Bearbeiten]

  • Markup Simple Navigation Modul installieren (Klassenname: MarkupSimpleNavigation)

Template / Theme erstellen[Bearbeiten]

Basisstruktur im Templateordner[Bearbeiten]

Diese ist schon angelegt, wenn man das blank Template am Anfang auswählt.

/scripts
/styles
/errors 
home.php
basic-page.php 
admin.php

Seitentemplates anlegen - einfache Variante[Bearbeiten]

Ein Template kann z.b. so aussehen:

basic-page.php

<?php include('./head.inc'); // include header markup ?>
<h1><?php echo $page->title; ?></h1>
<?php include('./foot.inc'); // include footer markup ?>

head.inc

<!DOCTYPE html>
<html lang="en">
<head>
    <title><?php echo $page->title; ?></title>
</head>
<body>

foot.inc

</body>
</html>

Im Backend kann man unter Setup > Templates neue Templates anlegen, die man dann wiederum Seiten zuweisen kann. Processwire sucht dann im Templatesverzeichnis nach dem passenden Dateinamen.

Hinweis: Processwire kann alle möglichen Seiten ausgeben, also auch JSON oder XML

Seitentemplates - delayed output[Bearbeiten]

Dies ist die Default vorgehensweise in Processwire für komplexere Seiten. Hierbei werden die Templateinhalte in Einzelschritten geladen:

_init.php -> myTemplate.php -> _main.php

myTemplate.php enthält den spezifischen Seiteninhalt, speichert diesen in Variablen und übergibt das Ganze an _main.php

Damit Processwire die genannten Dateien so wie oben verarbeitet, müssen sie in der config.php registriert sein:

/**
 * Prepend template file
 *
 * PHP file in /site/templates/ that will be loaded before each page's template file.
 * Example: _init.php
 *
 * @var string
 *
 */
$config->prependTemplateFile = '_init.php';

/**
 * Append template file
 *
 * PHP file in /site/templates/ that will be loaded after each page's template file.
 * Example: _main.php
 *
 * @var string
 *
 */
$config->appendTemplateFile = '_main.php';

Beispiel:

basic-page.php:

<?php
    $content = $page->body . $page->video;
?>

_main.php:

<?php
    include("./head.inc");
?>
 
<div id="page">
    <header class="">
        <a id="logo" href="<?php echo $pages->get('/')->url; ?>">
            <h1>
                <?php echo $pages->get('/')->title; ?>
            </h1>
        </a>
    </header>
    <aside>
        <nav>
            <?php
                // echo nav links
                $children = $pages->get('/')->children;
                foreach($children as $child){       
                    echo '<a name="'.$child->title.'" class="ajax-link'.$class.'" href="'. $child->url .'">'. $child->title .'</a>';
                }
            ?>
        </nav>
    </aside>
    <div class="content-container cf">
        <div class="content current-content">
            <?php
                // add content to page from template file
                echo $content;
            ?>
        </div>
    </div>
<?php
    include("./foot.inc");
?>

_main.php enthält also die generelle Struktur und die Seitentemplates die spezifischen Inhalte. Man kann das allerdings auch besser aufbauen als oben.

Markup Regions[Bearbeiten]

https://processwire.com/docs/front-end/output/markup-regions/

Setzt man in der config.php

$config->useMarkupRegions;

Sucht ProcessWire nach id Attributen (oder den Attributen pw-id oder data-pw-id) im Markup. Diese merkt sich PW dann als MarkupRegions. Wenn ein nachfolgendes Template die gleiche id nutzt wird diese Region ersetzt. In der Regel wird man in der _main.php die Basis Definition machen und in den Inhaltstemplates, kann man dann die Regions ersetzten. So kann man auch schön Teile einer Seite mit einem Inhaltstemplate ersetzen und den Rest gleich lassen.

_main.php

<div id="page">
  <?php include($headerMarkup); ?>
  <main id="main">
    <p>Hauptinhalt</p>  
  </main>
  <?php include($footerMarkup); ?>
</div>
'''basic-page.php'''
<pre>
<main id="main">
  <?=page()->body?>
</main>

Fazit: Kann sehr praktisch sein und erleichtert die Ausgabe je nach Arbeitsweise. Zeigt aber eine geringfügig schlechtere Performance und ist evtl. für manche Aufgaben die sehr dynamisch mit Daten umgehen nicht so gut geeignet.

AJAX Loading[Bearbeiten]

Mit der delayed Technik kann man jetzt auch nur Teile der Seite laden. So hat man niemals eine weiße Seite. So wollen wir vorgehen:

  • Wir laden die neuen Inhalte in einen HTML Container
  • Neue Inhalte werden gelanden und an vorhandenen Inhalte angehängt
  • Dann machen wir einen animierten Seitenübergang und löschen die alten Inhalte

Processwire stellt eine $ajax Variable zur Verfügung die wir nutzen können um zu prüfen, ob ein AJAX Request vorliegt. Wenn nicht können wir die komplette Seite austauschen.

So könnte das aussehen (_main.php):

<?php
// include page structure if not an ajax request
if(!$ajax):
    include("./head.inc");
?>
 
<div id="page">
    <header class="">
        <a id="logo" href="<?php echo $pages->get('/')->url; ?>">
            <h1>
            <?php echo $pages->get('/')->title; ?>
            </h1>
        </a>
    </header>
    <aside>
        <nav>
        <?php
        // nav content here 
        ?>
        </nav>
    </aside>
    <div class="content-container cf">
        <div class="content current-content">
        <?php endif; // end if ajax ?>
         
        <?php
        // if ajax then only return $content
        echo $content;
        ?>
         
        <?php if(!$ajax): ?>
        </div>
    </div>
<?php
    include("./foot.inc");
endif; // end if ajax
?>

AJAX über jQuery[Bearbeiten]

Um das AJAX Loading der Inhalte zu realisieren nutzen wir jQuery. Dies notwendigen Funktionen binden wir über die main.js ein und laden das ganze in der foot.inc .

foot.inc

  <div id="footer" class="footer">
    <div class="container">
      <p>&copy; <?php echo $homepage->site_title .' '. Date('Y'); ?></p>
      <?php echo $footer; ?>
    </div>
  </div>
</div><!--/#page-->
<?php
  // If the page is editable, then output a link that takes us straight to the page edit screen:
  if($page->editable()) {
    echo "<a class='nav' id='editpage' href='{$config->urls->admin}page/edit/?id={$page->id}'>Edit</a>";
  }
?>
<script src="<?php echo AIOM::JS(array('scripts/jquery-2.2.4.min.js','scripts/jquery.chocolat.js','scripts/jquery.fitvids.js','scripts/jquery.flexslider-min.js', 'scripts/main.js')); ?>"></script>
</body>
</html>

main.js

$(function() {
 
    var href;
    var title;
 
    $('body').on('click','a.ajax-link',function(e) { // nav link clicked
 
        href = $(this).attr("href");
        title = $(this).attr("name");
 
        // load content via AJAX
        loadContent(href);
 
        // prevent click and reload
        e.preventDefault();
    });
 
    function loadContent(url){ // Load content
 
        // variable for page data
        $pageData = '';
 
        // send Ajax request
        $.ajax({
            type: "POST",
            url: url,
            data: { ajax: true },
            success: function(data,status){
                $pageData = data;
            }
        }).done(function(){ // when finished and successful
 
            // construct new content
            $pageData = '<div class="content no-opacity ajax">' + $pageData + '</div>';
 
            // add content to page
            $('.content-container').append($pageData);
 
            // remove old content
            $('.content.current-content').remove();
 
            // show new content and clean up classes
            $(this).removeClass('no-opacity').removeClass('ajax').addClass('current-content');
             
        }); // end of ajax().done()
    } // end of loadContent()
});

Extras[Bearbeiten]

Außerdem möchten wir ein paar nützliche Sachen einbinden:

  • Animierter Seitenübergang
  • Reinitialisieren der Skripte nach dem Seitenübergang (Lightboxen, Slideshows etc..)
  • Already Loaded Check - wenn ein User geklickt hat soll ein erneuter Klick nicht den Ladevorgang neu starten.
  • Browser History - wir nutzen html5 history um die richtigen Inhalte zu laden wenn der User den Backbutton nutzt. Sonst würde die letzte komplette Seite geladen, statt nur der letzen ausgetauschten Inhalte.

So sieht main.js am Ende aus:

$(function() {

	// fitvids for responsivce videos
	$(".content .video").fitVids();

	// light box
	$('.gallery').Chocolat();

	// flexslider
	$('.flexslider').flexslider({
		pauseOnHover: true,
		controlNav: false,
		prevText: "",
		nextText: "",
	});


	/*
	*   HTML5 pushstate and ajax content
	*/

	var href;
	var title;

  	$('body').on('click','a.ajax-link',function(e) { // nav link clicked

    	// check to see it contents already accessed
    	if($(this).hasClass('current')){

        	console.log('current page');

			// prevent click through
			e.preventDefault();
			// exist click function (dont load new content)
        	return true;
    	}

      	$('.current').removeClass('current'); // remove .current from active link
		$('.parent').removeClass('parent'); // remove .current from active link
      	$(this).addClass('current'); // add .current

		var parent = $(this).attr('parent');
		if(parent){ // if link page has parent nav
			$('nav a[name="'+parent+'"]').addClass('parent'); // add .current
		}

		// add progress spinner on cursor
      	$('body').css('cursor','progress');

		// remove cusor spinner
		var pointerTimeout = setTimeout(function () {
			$('body').css('cursor','auto'); // progress spinner on cursor
		}, 1000);

      	href = $(this).attr("href");
      	title = $(this).attr("name");

		// load content via AJAX
      	loadContent(href);

		// add new url to html5 history
      	history.pushState('', +href, href); // push url to history

		// change title to new page
      	$('title').html(title);

		// send GA pageview
      	ga('send', 'pageview', href);

		// prevent click and reload
    	e.preventDefault();
	});

    // for back / forwards browser navigation
    var toggleHistoryVisitNum = 0;
    function toggleHistory(){
        window.onpopstate = function(event) {
            if(window.location.hash == ""){
                loadContent(location.pathname);
            }
        };
    }

	function loadContent(url){ // Load content

		// variable for page data
    	$pageData = '';

		// send Ajax request
    	$.ajax({
	        type: "POST",
	        url: url,
	        data: { ajax: true },
	        success: function(data,status){
				$pageData = data;
      		}
    	}).done(function(){ // when finished and successful

			// construct new content
      		$pageData = '<div class="content no-opacity ajax">' + $pageData + '</div>';

			// add content to page
			$('.content-container').append($pageData);

			// animate the requested content
	      	$('.content.current-content').animate({
				// animate out old content
	        	opacity: 0
	      	}, 400, function(){

				$('.content.current-content').slideUp(400);

	          	// content animation finished
	          	$('.content.ajax').animate({
					// animate in new content
	            	opacity: 1
	          	}, 400, function(){

					// remove old content
		            $('.content.current-content').remove();

					// show new content and clean up classes
		            $(this).removeClass('no-opacity').removeClass('ajax').addClass('current-content');

					/*
					* run js functions for new content
					*/

					// fitvids for responsivce videos
					$(".content .video").fitVids();

					// light box
					$('.gallery').Chocolat();

	          	});
			});

	      	// toggle use of back and forward buttons on browser
	      	if(!toggleHistoryVisitNum){
	        	toggleHistory();
	      	}

			// keep track of number of Ajax requests
			toggleHistoryVisitNum++;

    	}); // end of ajax().done()
  	} // end of loadContent()
});

Hinweis: Im Beispiel laden wir direkt HTML Inhalte, man könnte aber auch JSON laden (z.b. mit Mustache JS: https://mustache.github.io/#demo ) um die Ladezeiten weiter zu verkürzen.