<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wiki.stephanschlegel.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=84.136.104.26</id>
	<title>Wikizone - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.stephanschlegel.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=84.136.104.26"/>
	<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=Spezial:Beitr%C3%A4ge/84.136.104.26"/>
	<updated>2026-05-06T16:01:17Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Multilanguage_Website&amp;diff=24405</id>
		<title>ProcessWire - Multilanguage Website</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Multilanguage_Website&amp;diff=24405"/>
		<updated>2020-02-14T14:09:45Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* 3. Templates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mehrsprachige Websites mit ProcessWire / Andere Default Sprache&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* https://processwire.com/talk/topic/5518-multi-language-site/&lt;br /&gt;
* http://processwire.com/api/multi-language-support/multi-language-urls/&lt;br /&gt;
* https://processwire.com/talk/topic/9322-change-default-language-for-homepage/ (Modul Solution, mehr zur Anschauung)&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Es gibt mehrere Bereiche an die man denken muss. Einmal geht es um die Eingabe im Backend, die Ausgabe im Frontend, die Sprachnavigation auf der Website und die Templates:&lt;br /&gt;
* Wie übersetze ich Text in meinen &amp;#039;&amp;#039;&amp;#039;Templates&amp;#039;&amp;#039;&amp;#039; ? Siehe unten i18n&lt;br /&gt;
* Wie übersetze ich &amp;#039;&amp;#039;&amp;#039;Inhalte&amp;#039;&amp;#039;&amp;#039; im Backend ?  Dafür ist das Modul Language Support Fields zuständig&lt;br /&gt;
* Wie sollen meine URLs aussehen ? Prinzipiell kann man jedem User unterschiedliche Sprachen auch ohne andere Domain liefern (über seine Session) aber i.d.R wollen wir die URL anzupassen.&lt;br /&gt;
** &amp;#039;&amp;#039;domain.de/en/meineSeite&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;domain.de/myPage&amp;#039;&amp;#039;&lt;br /&gt;
** Um &amp;#039;&amp;#039;&amp;#039;Seitennamen&amp;#039;&amp;#039;&amp;#039; zu übersetzen oder ein Domainkürzel in der URL voranzustellen benötige ich das Modul &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
&lt;br /&gt;
== Welche Module benötige ich ==&lt;br /&gt;
Da es einige Module in diesem Zusammenhang gibt hier eine Übersicht. Man findet Sie am besten unter &lt;br /&gt;
 Modules &amp;gt; Core &amp;gt; Language&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support&amp;#039;&amp;#039;&amp;#039; - ProcessWire multi-language support. Basis Modul bracht man für alle Beispiele unten. Reicht aus, wenn man nur die Standard-Sprache ändern will.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Fields&amp;#039;&amp;#039;&amp;#039; Required to use multi-language fields. Basis für mehrsprachige Felder im Backend. &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Page Names&amp;#039;&amp;#039;&amp;#039;	(LanguageSupportPageNames) Required to use multi-language page names. Braucht man nur wenn &amp;#039;&amp;#039;&amp;#039;alle Seiten in allen Sprachen unterschiedliche Namen&amp;#039;&amp;#039;&amp;#039; bekommen sollen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Tabs&amp;#039;&amp;#039;&amp;#039; Organizes multi-language fields into tabs for a cleaner easier to use interface.&lt;br /&gt;
Für mehrsprachige Seiten benötigen wir im Frontend i.d.R. folgende Module: &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Fields&amp;#039;&amp;#039;&amp;#039; - Sind Felder, die im Backend für jede Sprache eine eigene Eingabe bieten.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; benötigt man, damit man für die Sprachen eigene URLs angeben kann. Z.B. bei Home in Englisch /en/ usw. &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePageTitleLanguage&amp;#039;&amp;#039;&amp;#039; benötigt man wenn man alle Seitenamen anpassen will (seite1_de seite1_fr ...) Für einen reinen Sprachpfad (/de/Seite) braucht man das nicht.&lt;br /&gt;
== Deutsch als Default Sprache ==&lt;br /&gt;
Wenn nur die Standardsprache verändern möchte, aber keine Mehrsprachige Seite benötigt brauchen wir nur das Modul &amp;#039;&amp;#039;&amp;#039;Language Support&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Enable languages support -&amp;gt; &amp;#039;&amp;#039;&amp;#039;Core Module &amp;quot;Language Support&amp;quot; aktivieren&amp;#039;&amp;#039;&amp;#039;. -&amp;gt; Sprachen stehen num im Backend Setup zur Verfügung&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Titel der Default Sprache&amp;#039;&amp;#039;&amp;#039; anpassen (z.B. Deutsch (default) )&lt;br /&gt;
* Language Pack in der default Sprache installieren -&amp;gt; Dies ist ab jetzt die Default Sprache. Nicht vergessen auch den &amp;#039;&amp;#039;&amp;#039;lang Tag im Header&amp;#039;&amp;#039;&amp;#039; zu setzen, damit der Browser die Sprache richtig erkennt.&lt;br /&gt;
&lt;br /&gt;
== Mehrere Sprachen ==&lt;br /&gt;
Für mehrsprachige Seiten benötigen wir i.d.R. folgende Module: &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Fields&amp;#039;&amp;#039;&amp;#039; für die Backend Übersetzungen und &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; für die URLs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePageTitleLanguage&amp;#039;&amp;#039;&amp;#039; benötigt man wenn man alle Seitenamen anpassen will (seite1_de seite1_fr ...) Für einen reinen Sprachpfad (/de/Seite) braucht man das nicht.&lt;br /&gt;
* Für weitere Sprachen entsprechend hinzufügen.&lt;br /&gt;
Vorgehen:&lt;br /&gt;
* Language Packs installieren: drop in the none english language pack (for admin backend) into the default language, (e.g. german langpack)&lt;br /&gt;
* As a nice sideeffect every new user in your system gets the native language per default without have it to select from the list.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Default Sprache Deutsch ===&lt;br /&gt;
Todo Testen&lt;br /&gt;
* Modul Language Support&lt;br /&gt;
* Translation File unter Setup Sprachen hochladen&lt;br /&gt;
* Übersetzungen von Hand hinzufügen&lt;br /&gt;
&lt;br /&gt;
== Templates für Multilanguage vorbereiten (i18n) ==&lt;br /&gt;
 https://processwire.com/docs/multi-language-support/code-i18n/&lt;br /&gt;
 https://processwire.com/blog/posts/functional-fields/&lt;br /&gt;
=== Functional fields ===&lt;br /&gt;
In PW3 kamen die functional Fields hinzu. Mit diesen kann man statischen Text dynamisch im Page Editor ansprechen. D.h. das Functional Field findet Texte im Template und macht sie automatisch editierbar im Page Editor. Das Ganze funktioniert auch Mehrsprachig.&lt;br /&gt;
 [[ProcessWire - Functional Fields]]&lt;br /&gt;
 https://processwire.com/blog/posts/functional-fields/&lt;br /&gt;
Beispiele für die Ausgabe von &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
__text(&amp;#039;your text&amp;#039;);&lt;br /&gt;
__textarea(&amp;#039;your text&amp;#039;);&lt;br /&gt;
__richtext(&amp;#039;&amp;lt;p&amp;gt;your text&amp;lt;/p&amp;gt;&amp;#039;); &lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;) // define a identifier for this text snippet...&lt;br /&gt;
__text(&amp;#039;subscribe&amp;#039;) // ..reuse it this way&lt;br /&gt;
$pages-&amp;gt;get(&amp;#039;/&amp;#039;)-&amp;gt;mytext-&amp;gt;subscribe // reuse from another template&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;, &amp;#039;Submit button&amp;#039;); // Label for page editor&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;, &amp;#039;label=Submit button, notes=Test&amp;#039;); // or use a pw selector&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;name=subscribe, label=Submit button, notes=Test&amp;#039;); // equivalent to upper&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Standard Multilanguage nach i18n ===&lt;br /&gt;
&lt;br /&gt;
 $out = $this-&amp;gt;_(&amp;quot;Live long and prosper&amp;quot;);  // syntax within a class&lt;br /&gt;
 $out = __(&amp;quot;Live long and prosper!&amp;quot;); // syntax outside of a class&lt;br /&gt;
&lt;br /&gt;
Dann im Backend unter der Sprache &amp;quot;Find files to translate&amp;quot; und die Template Datei auswählen.&lt;br /&gt;
&lt;br /&gt;
=== Variablen in i18n ===&lt;br /&gt;
 $out = sprintf(__(&amp;quot;Created %d pages.&amp;quot;), $count); &lt;br /&gt;
 $out = sprintf(__(&amp;#039;Your city is %1$s, and your zip code is %2$s.&amp;#039;), $city, $zipcode);&lt;br /&gt;
&lt;br /&gt;
=== Plural in Multilanguage Files===&lt;br /&gt;
Hierzu gibt es die _n() Funktion&lt;br /&gt;
 $out = sprintf(_n(&amp;quot;Created %d page.&amp;quot;, &amp;quot;Created %d pages.&amp;quot;, $count), $count);&lt;br /&gt;
=== Unterschiedliche Übersetzungen bei gleichem Wortlaut ===&lt;br /&gt;
Manchmal muss der Begriff in anderen Sprachen unterschiedlich übersetzt werden, obwohl er in der Default Sprache gleich lautet:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$label = _x(&amp;#039;Comment&amp;#039;, &amp;#039;noun&amp;#039;); // or $this-&amp;gt;_x(&amp;#039;Comment&amp;#039;, &amp;#039;noun&amp;#039;) in a class&lt;br /&gt;
...&lt;br /&gt;
// some other place in the code&lt;br /&gt;
echo _x(&amp;#039;Comment&amp;#039;, &amp;#039;column name&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Kommentare für den Übersetzer und User ===&lt;br /&gt;
 $date = __(&amp;#039;g:i:s a&amp;#039;); // Date string in PHP date() format&lt;br /&gt;
 echo __(&amp;quot;Welcome Guest&amp;quot;); // Headline for guest user // Keep it short (2-3 words)&lt;br /&gt;
=== Regeln ===&lt;br /&gt;
* Eine Zeile ein Paar Anführungszeichen&lt;br /&gt;
* Nur eine Übersetzungsfunktion pro Zeile&lt;br /&gt;
&lt;br /&gt;
== Language Switcher ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// remember what language is set to&lt;br /&gt;
$savedLanguage = $user-&amp;gt;language;&lt;br /&gt;
&lt;br /&gt;
foreach($languages as $language) {&lt;br /&gt;
&lt;br /&gt;
  // if user is already viewing the page in this language, skip it&lt;br /&gt;
  if($language-&amp;gt;id == $savedLanguage-&amp;gt;id) continue;&lt;br /&gt;
&lt;br /&gt;
  // if this page isn&amp;#039;t viewable (active) for the language, skip it&lt;br /&gt;
  if(!$page-&amp;gt;viewable($language)) continue;&lt;br /&gt;
&lt;br /&gt;
  // set the user&amp;#039;s language, so that the $page-&amp;gt;url and any other&lt;br /&gt;
  // fields we access from it will be reflective of the $language&lt;br /&gt;
  $user-&amp;gt;language = $language;&lt;br /&gt;
&lt;br /&gt;
  // output a link to this page in the other language&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$page-&amp;gt;url&amp;#039;&amp;gt;$language-&amp;gt;title: $page-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
// restore the original language setting&lt;br /&gt;
$user-&amp;gt;language = $savedLanguage;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Multilanguage Page allgemeines Vorgehen ===&lt;br /&gt;
==== 1. Module ====&lt;br /&gt;
* s.o. alle benötigten installieren&lt;br /&gt;
==== 2. Backend ====&lt;br /&gt;
* Default Sprache festlegen&lt;br /&gt;
* Alle Felder die Übersetzt werden sollen auf Multilanguage Felder umstellen&lt;br /&gt;
==== 3. Templates ====&lt;br /&gt;
Das &amp;#039;&amp;#039;&amp;#039;lang Attribut&amp;#039;&amp;#039;&amp;#039; sollte zur Sprache passen. Mit der _x Funktion kann PW beliebige Strings in Templates übersetzten. PW findet diese automatisch.&lt;br /&gt;
_main.php (oder header..)&lt;br /&gt;
 &amp;lt;html lang=&amp;quot;&amp;lt;?php echo _x(&amp;#039;en&amp;#039;, &amp;#039;HTML language code&amp;#039;); ?&amp;gt;&amp;quot;&lt;br /&gt;
Im Backend kann man nun in den Spracheinstellungen Verwaltung &amp;gt; Sprachen &amp;gt; meineSprache das Label HTML language code suchen und mit dem passenden lang tag ersetzen.&lt;br /&gt;
Das Funktioniert auch mit beliebigen anderen Werten die man im Template hartkodiert hat.&lt;br /&gt;
&lt;br /&gt;
So etwas sollte auch möglich sein:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;? $lang = $pages-&amp;gt;get(1)-&amp;gt;getLanguageValue($user-&amp;gt;language, &amp;#039;name&amp;#039;); ?&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;&amp;lt;?php echo $lang ?&amp;gt;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier wird einfach der Name der Sprache die der Benutzer im Moment hat (Session) ausgegeben. Der Name der Sprache sollte im Backend entsprechend der Norm gewählt sein. (de, it, fi...)&lt;br /&gt;
&lt;br /&gt;
==== Language Switcher ====&lt;br /&gt;
Der Language Switcher sollte verschiedene Dinge berücksichtigen erledigen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Prantner ===&lt;br /&gt;
language-switcher.inc&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
$imgMarkup = &amp;quot;&amp;quot;;&lt;br /&gt;
$out = &amp;quot;&amp;quot;;&lt;br /&gt;
foreach($languages as $language) {&lt;br /&gt;
	if(!$page-&amp;gt;viewable($language)) continue; // is page viewable in this language?&lt;br /&gt;
	$url = $page-&amp;gt;localUrl($language);&lt;br /&gt;
	$hreflang = $homepage-&amp;gt;getLanguageValue($language, &amp;#039;name&amp;#039;);&lt;br /&gt;
	$imgUrl = $config-&amp;gt;urls-&amp;gt;templates.&amp;#039;assets/flags/&amp;#039;.$language-&amp;gt;name.&amp;#039;_sq.png&amp;#039;;&lt;br /&gt;
	$imgMarkup = &amp;quot;&amp;lt;img src=\&amp;quot;$imgUrl\&amp;quot; title=\&amp;quot;$language-&amp;gt;title\&amp;quot; alt=\&amp;quot;$language-&amp;gt;title\&amp;quot; class=\&amp;quot;circle z-depth-1\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	if($language-&amp;gt;id == $user-&amp;gt;language-&amp;gt;id) {&lt;br /&gt;
		$out .= &amp;quot;&amp;lt;span class=\&amp;quot;lang-button current\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	} else {&lt;br /&gt;
		$out .= &amp;quot;&amp;lt;span class=\&amp;quot;lang-button\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	$out .= &amp;quot;&amp;lt;a hreflang=\&amp;quot;$hreflang\&amp;quot; href=\&amp;quot;$url\&amp;quot;&amp;gt;$imgMarkup&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
$out = &amp;#039;&amp;lt;div id=&amp;quot;nav-lang&amp;quot;&amp;gt;&amp;#039;.$out.&amp;#039;&amp;lt;/div&amp;gt;&amp;#039;;&lt;br /&gt;
echo $out; &lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
language-switcher-mobile.inc&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;ul id=&amp;quot;nav-mobile&amp;quot; class=&amp;quot;right hide-on-med-and-down&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
	foreach($languages as $language) {&lt;br /&gt;
		if(!$page-&amp;gt;viewable($language)) continue; // is page viewable in this language?&lt;br /&gt;
		if($language-&amp;gt;id == $user-&amp;gt;language-&amp;gt;id) {&lt;br /&gt;
			echo &amp;quot;&amp;lt;li class=&amp;#039;current&amp;#039;&amp;gt;&amp;quot;;&lt;br /&gt;
		} else {&lt;br /&gt;
			echo &amp;quot;&amp;lt;li&amp;gt;&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$url = $page-&amp;gt;localUrl($language);&lt;br /&gt;
		$hreflang = $homepage-&amp;gt;getLanguageValue($language, &amp;#039;name&amp;#039;);&lt;br /&gt;
		echo &amp;quot;&amp;lt;a hreflang=&amp;#039;$hreflang&amp;#039; href=&amp;#039;$url&amp;#039;&amp;gt;$language-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Multilanguage_Website&amp;diff=24404</id>
		<title>ProcessWire - Multilanguage Website</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Multilanguage_Website&amp;diff=24404"/>
		<updated>2020-02-14T14:07:58Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mehrsprachige Websites mit ProcessWire / Andere Default Sprache&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* https://processwire.com/talk/topic/5518-multi-language-site/&lt;br /&gt;
* http://processwire.com/api/multi-language-support/multi-language-urls/&lt;br /&gt;
* https://processwire.com/talk/topic/9322-change-default-language-for-homepage/ (Modul Solution, mehr zur Anschauung)&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Es gibt mehrere Bereiche an die man denken muss. Einmal geht es um die Eingabe im Backend, die Ausgabe im Frontend, die Sprachnavigation auf der Website und die Templates:&lt;br /&gt;
* Wie übersetze ich Text in meinen &amp;#039;&amp;#039;&amp;#039;Templates&amp;#039;&amp;#039;&amp;#039; ? Siehe unten i18n&lt;br /&gt;
* Wie übersetze ich &amp;#039;&amp;#039;&amp;#039;Inhalte&amp;#039;&amp;#039;&amp;#039; im Backend ?  Dafür ist das Modul Language Support Fields zuständig&lt;br /&gt;
* Wie sollen meine URLs aussehen ? Prinzipiell kann man jedem User unterschiedliche Sprachen auch ohne andere Domain liefern (über seine Session) aber i.d.R wollen wir die URL anzupassen.&lt;br /&gt;
** &amp;#039;&amp;#039;domain.de/en/meineSeite&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;domain.de/myPage&amp;#039;&amp;#039;&lt;br /&gt;
** Um &amp;#039;&amp;#039;&amp;#039;Seitennamen&amp;#039;&amp;#039;&amp;#039; zu übersetzen oder ein Domainkürzel in der URL voranzustellen benötige ich das Modul &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
&lt;br /&gt;
== Welche Module benötige ich ==&lt;br /&gt;
Da es einige Module in diesem Zusammenhang gibt hier eine Übersicht. Man findet Sie am besten unter &lt;br /&gt;
 Modules &amp;gt; Core &amp;gt; Language&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support&amp;#039;&amp;#039;&amp;#039; - ProcessWire multi-language support. Basis Modul bracht man für alle Beispiele unten. Reicht aus, wenn man nur die Standard-Sprache ändern will.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Fields&amp;#039;&amp;#039;&amp;#039; Required to use multi-language fields. Basis für mehrsprachige Felder im Backend. &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Page Names&amp;#039;&amp;#039;&amp;#039;	(LanguageSupportPageNames) Required to use multi-language page names. Braucht man nur wenn &amp;#039;&amp;#039;&amp;#039;alle Seiten in allen Sprachen unterschiedliche Namen&amp;#039;&amp;#039;&amp;#039; bekommen sollen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Tabs&amp;#039;&amp;#039;&amp;#039; Organizes multi-language fields into tabs for a cleaner easier to use interface.&lt;br /&gt;
Für mehrsprachige Seiten benötigen wir im Frontend i.d.R. folgende Module: &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Fields&amp;#039;&amp;#039;&amp;#039; - Sind Felder, die im Backend für jede Sprache eine eigene Eingabe bieten.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; benötigt man, damit man für die Sprachen eigene URLs angeben kann. Z.B. bei Home in Englisch /en/ usw. &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePageTitleLanguage&amp;#039;&amp;#039;&amp;#039; benötigt man wenn man alle Seitenamen anpassen will (seite1_de seite1_fr ...) Für einen reinen Sprachpfad (/de/Seite) braucht man das nicht.&lt;br /&gt;
== Deutsch als Default Sprache ==&lt;br /&gt;
Wenn nur die Standardsprache verändern möchte, aber keine Mehrsprachige Seite benötigt brauchen wir nur das Modul &amp;#039;&amp;#039;&amp;#039;Language Support&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Enable languages support -&amp;gt; &amp;#039;&amp;#039;&amp;#039;Core Module &amp;quot;Language Support&amp;quot; aktivieren&amp;#039;&amp;#039;&amp;#039;. -&amp;gt; Sprachen stehen num im Backend Setup zur Verfügung&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Titel der Default Sprache&amp;#039;&amp;#039;&amp;#039; anpassen (z.B. Deutsch (default) )&lt;br /&gt;
* Language Pack in der default Sprache installieren -&amp;gt; Dies ist ab jetzt die Default Sprache. Nicht vergessen auch den &amp;#039;&amp;#039;&amp;#039;lang Tag im Header&amp;#039;&amp;#039;&amp;#039; zu setzen, damit der Browser die Sprache richtig erkennt.&lt;br /&gt;
&lt;br /&gt;
== Mehrere Sprachen ==&lt;br /&gt;
Für mehrsprachige Seiten benötigen wir i.d.R. folgende Module: &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Fields&amp;#039;&amp;#039;&amp;#039; für die Backend Übersetzungen und &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; für die URLs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePageTitleLanguage&amp;#039;&amp;#039;&amp;#039; benötigt man wenn man alle Seitenamen anpassen will (seite1_de seite1_fr ...) Für einen reinen Sprachpfad (/de/Seite) braucht man das nicht.&lt;br /&gt;
* Für weitere Sprachen entsprechend hinzufügen.&lt;br /&gt;
Vorgehen:&lt;br /&gt;
* Language Packs installieren: drop in the none english language pack (for admin backend) into the default language, (e.g. german langpack)&lt;br /&gt;
* As a nice sideeffect every new user in your system gets the native language per default without have it to select from the list.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Default Sprache Deutsch ===&lt;br /&gt;
Todo Testen&lt;br /&gt;
* Modul Language Support&lt;br /&gt;
* Translation File unter Setup Sprachen hochladen&lt;br /&gt;
* Übersetzungen von Hand hinzufügen&lt;br /&gt;
&lt;br /&gt;
== Templates für Multilanguage vorbereiten (i18n) ==&lt;br /&gt;
 https://processwire.com/docs/multi-language-support/code-i18n/&lt;br /&gt;
 https://processwire.com/blog/posts/functional-fields/&lt;br /&gt;
=== Functional fields ===&lt;br /&gt;
In PW3 kamen die functional Fields hinzu. Mit diesen kann man statischen Text dynamisch im Page Editor ansprechen. D.h. das Functional Field findet Texte im Template und macht sie automatisch editierbar im Page Editor. Das Ganze funktioniert auch Mehrsprachig.&lt;br /&gt;
 [[ProcessWire - Functional Fields]]&lt;br /&gt;
 https://processwire.com/blog/posts/functional-fields/&lt;br /&gt;
Beispiele für die Ausgabe von &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
__text(&amp;#039;your text&amp;#039;);&lt;br /&gt;
__textarea(&amp;#039;your text&amp;#039;);&lt;br /&gt;
__richtext(&amp;#039;&amp;lt;p&amp;gt;your text&amp;lt;/p&amp;gt;&amp;#039;); &lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;) // define a identifier for this text snippet...&lt;br /&gt;
__text(&amp;#039;subscribe&amp;#039;) // ..reuse it this way&lt;br /&gt;
$pages-&amp;gt;get(&amp;#039;/&amp;#039;)-&amp;gt;mytext-&amp;gt;subscribe // reuse from another template&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;, &amp;#039;Submit button&amp;#039;); // Label for page editor&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;, &amp;#039;label=Submit button, notes=Test&amp;#039;); // or use a pw selector&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;name=subscribe, label=Submit button, notes=Test&amp;#039;); // equivalent to upper&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Standard Multilanguage nach i18n ===&lt;br /&gt;
&lt;br /&gt;
 $out = $this-&amp;gt;_(&amp;quot;Live long and prosper&amp;quot;);  // syntax within a class&lt;br /&gt;
 $out = __(&amp;quot;Live long and prosper!&amp;quot;); // syntax outside of a class&lt;br /&gt;
&lt;br /&gt;
Dann im Backend unter der Sprache &amp;quot;Find files to translate&amp;quot; und die Template Datei auswählen.&lt;br /&gt;
&lt;br /&gt;
=== Variablen in i18n ===&lt;br /&gt;
 $out = sprintf(__(&amp;quot;Created %d pages.&amp;quot;), $count); &lt;br /&gt;
 $out = sprintf(__(&amp;#039;Your city is %1$s, and your zip code is %2$s.&amp;#039;), $city, $zipcode);&lt;br /&gt;
&lt;br /&gt;
=== Plural in Multilanguage Files===&lt;br /&gt;
Hierzu gibt es die _n() Funktion&lt;br /&gt;
 $out = sprintf(_n(&amp;quot;Created %d page.&amp;quot;, &amp;quot;Created %d pages.&amp;quot;, $count), $count);&lt;br /&gt;
=== Unterschiedliche Übersetzungen bei gleichem Wortlaut ===&lt;br /&gt;
Manchmal muss der Begriff in anderen Sprachen unterschiedlich übersetzt werden, obwohl er in der Default Sprache gleich lautet:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$label = _x(&amp;#039;Comment&amp;#039;, &amp;#039;noun&amp;#039;); // or $this-&amp;gt;_x(&amp;#039;Comment&amp;#039;, &amp;#039;noun&amp;#039;) in a class&lt;br /&gt;
...&lt;br /&gt;
// some other place in the code&lt;br /&gt;
echo _x(&amp;#039;Comment&amp;#039;, &amp;#039;column name&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Kommentare für den Übersetzer und User ===&lt;br /&gt;
 $date = __(&amp;#039;g:i:s a&amp;#039;); // Date string in PHP date() format&lt;br /&gt;
 echo __(&amp;quot;Welcome Guest&amp;quot;); // Headline for guest user // Keep it short (2-3 words)&lt;br /&gt;
=== Regeln ===&lt;br /&gt;
* Eine Zeile ein Paar Anführungszeichen&lt;br /&gt;
* Nur eine Übersetzungsfunktion pro Zeile&lt;br /&gt;
&lt;br /&gt;
== Language Switcher ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// remember what language is set to&lt;br /&gt;
$savedLanguage = $user-&amp;gt;language;&lt;br /&gt;
&lt;br /&gt;
foreach($languages as $language) {&lt;br /&gt;
&lt;br /&gt;
  // if user is already viewing the page in this language, skip it&lt;br /&gt;
  if($language-&amp;gt;id == $savedLanguage-&amp;gt;id) continue;&lt;br /&gt;
&lt;br /&gt;
  // if this page isn&amp;#039;t viewable (active) for the language, skip it&lt;br /&gt;
  if(!$page-&amp;gt;viewable($language)) continue;&lt;br /&gt;
&lt;br /&gt;
  // set the user&amp;#039;s language, so that the $page-&amp;gt;url and any other&lt;br /&gt;
  // fields we access from it will be reflective of the $language&lt;br /&gt;
  $user-&amp;gt;language = $language;&lt;br /&gt;
&lt;br /&gt;
  // output a link to this page in the other language&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$page-&amp;gt;url&amp;#039;&amp;gt;$language-&amp;gt;title: $page-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
// restore the original language setting&lt;br /&gt;
$user-&amp;gt;language = $savedLanguage;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Multilanguage Page allgemeines Vorgehen ===&lt;br /&gt;
==== 1. Module ====&lt;br /&gt;
* s.o. alle benötigten installieren&lt;br /&gt;
==== 2. Backend ====&lt;br /&gt;
* Default Sprache festlegen&lt;br /&gt;
* Alle Felder die Übersetzt werden sollen auf Multilanguage Felder umstellen&lt;br /&gt;
==== 3. Templates ====&lt;br /&gt;
Das &amp;#039;&amp;#039;&amp;#039;lang Attribut&amp;#039;&amp;#039;&amp;#039; sollte zur Sprache passen. Mit der _x Funktion kann PW beliebige Strings in Templates übersetzten. PW findet diese automatisch.&lt;br /&gt;
_main.php (oder header..)&lt;br /&gt;
 &amp;lt;html lang=&amp;quot;&amp;lt;?php echo _x(&amp;#039;en&amp;#039;, &amp;#039;HTML language code&amp;#039;); ?&amp;gt;&amp;quot;&lt;br /&gt;
Im Backend kann man nun in den Spracheinstellungen Verwaltung &amp;gt; Sprachen &amp;gt; meineSprache das Label HTML language code suchen und mit dem passenden lang tag ersetzen.&lt;br /&gt;
&lt;br /&gt;
So etwas sollte auch möglich sein:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;? $lang = $pages-&amp;gt;get(1)-&amp;gt;getLanguageValue($user-&amp;gt;language, &amp;#039;name&amp;#039;); ?&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;&amp;lt;?php echo $lang ?&amp;gt;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Funktioniert auch mit beliebigen anderen Werten die man im Template hartkodiert hat.&lt;br /&gt;
&lt;br /&gt;
==== Language Switcher ====&lt;br /&gt;
Der Language Switcher sollte verschiedene Dinge berücksichtigen erledigen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Prantner ===&lt;br /&gt;
language-switcher.inc&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
$imgMarkup = &amp;quot;&amp;quot;;&lt;br /&gt;
$out = &amp;quot;&amp;quot;;&lt;br /&gt;
foreach($languages as $language) {&lt;br /&gt;
	if(!$page-&amp;gt;viewable($language)) continue; // is page viewable in this language?&lt;br /&gt;
	$url = $page-&amp;gt;localUrl($language);&lt;br /&gt;
	$hreflang = $homepage-&amp;gt;getLanguageValue($language, &amp;#039;name&amp;#039;);&lt;br /&gt;
	$imgUrl = $config-&amp;gt;urls-&amp;gt;templates.&amp;#039;assets/flags/&amp;#039;.$language-&amp;gt;name.&amp;#039;_sq.png&amp;#039;;&lt;br /&gt;
	$imgMarkup = &amp;quot;&amp;lt;img src=\&amp;quot;$imgUrl\&amp;quot; title=\&amp;quot;$language-&amp;gt;title\&amp;quot; alt=\&amp;quot;$language-&amp;gt;title\&amp;quot; class=\&amp;quot;circle z-depth-1\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	if($language-&amp;gt;id == $user-&amp;gt;language-&amp;gt;id) {&lt;br /&gt;
		$out .= &amp;quot;&amp;lt;span class=\&amp;quot;lang-button current\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	} else {&lt;br /&gt;
		$out .= &amp;quot;&amp;lt;span class=\&amp;quot;lang-button\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	$out .= &amp;quot;&amp;lt;a hreflang=\&amp;quot;$hreflang\&amp;quot; href=\&amp;quot;$url\&amp;quot;&amp;gt;$imgMarkup&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
$out = &amp;#039;&amp;lt;div id=&amp;quot;nav-lang&amp;quot;&amp;gt;&amp;#039;.$out.&amp;#039;&amp;lt;/div&amp;gt;&amp;#039;;&lt;br /&gt;
echo $out; &lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
language-switcher-mobile.inc&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;ul id=&amp;quot;nav-mobile&amp;quot; class=&amp;quot;right hide-on-med-and-down&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
	foreach($languages as $language) {&lt;br /&gt;
		if(!$page-&amp;gt;viewable($language)) continue; // is page viewable in this language?&lt;br /&gt;
		if($language-&amp;gt;id == $user-&amp;gt;language-&amp;gt;id) {&lt;br /&gt;
			echo &amp;quot;&amp;lt;li class=&amp;#039;current&amp;#039;&amp;gt;&amp;quot;;&lt;br /&gt;
		} else {&lt;br /&gt;
			echo &amp;quot;&amp;lt;li&amp;gt;&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$url = $page-&amp;gt;localUrl($language);&lt;br /&gt;
		$hreflang = $homepage-&amp;gt;getLanguageValue($language, &amp;#039;name&amp;#039;);&lt;br /&gt;
		echo &amp;quot;&amp;lt;a hreflang=&amp;#039;$hreflang&amp;#039; href=&amp;#039;$url&amp;#039;&amp;gt;$language-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Multilanguage_Website&amp;diff=24403</id>
		<title>ProcessWire - Multilanguage Website</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Multilanguage_Website&amp;diff=24403"/>
		<updated>2020-02-14T09:45:25Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mehrsprachige Websites mit ProcessWire / Andere Default Sprache&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* https://processwire.com/talk/topic/5518-multi-language-site/&lt;br /&gt;
* http://processwire.com/api/multi-language-support/multi-language-urls/&lt;br /&gt;
* https://processwire.com/talk/topic/9322-change-default-language-for-homepage/ (Modul Solution, mehr zur Anschauung)&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Es gibt mehrere Bereiche an die man denken muss. Einmal geht es um die Eingabe im Backend, die Ausgabe im Frontend, die Sprachnavigation auf der Website und die Templates:&lt;br /&gt;
* Wie übersetze ich Text in meinen &amp;#039;&amp;#039;&amp;#039;Templates&amp;#039;&amp;#039;&amp;#039; ? Siehe unten i18n&lt;br /&gt;
* Wie übersetze ich &amp;#039;&amp;#039;&amp;#039;Inhalte&amp;#039;&amp;#039;&amp;#039; im Backend ?  Dafür ist das Modul Language Support Fields zuständig&lt;br /&gt;
* Wie sollen meine URLs aussehen ? Prinzipiell kann man jedem User unterschiedliche Sprachen auch ohne andere Domain liefern (über seine Session) aber i.d.R wollen wir die URL anzupassen.&lt;br /&gt;
** &amp;#039;&amp;#039;domain.de/en/meineSeite&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;domain.de/myPage&amp;#039;&amp;#039;&lt;br /&gt;
** Um &amp;#039;&amp;#039;&amp;#039;Seitennamen&amp;#039;&amp;#039;&amp;#039; zu übersetzen oder ein Domainkürzel in der URL voranzustellen benötige ich das Modul &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
&lt;br /&gt;
== Welche Module benötige ich ==&lt;br /&gt;
Da es einige Module in diesem Zusammenhang gibt hier eine Übersicht. Man findet Sie am besten unter &lt;br /&gt;
 Modules &amp;gt; Core &amp;gt; Language&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support&amp;#039;&amp;#039;&amp;#039; - ProcessWire multi-language support. Basis Modul bracht man für alle Beispiele unten. Reicht aus, wenn man nur die Standard-Sprache ändern will.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Fields&amp;#039;&amp;#039;&amp;#039; Required to use multi-language fields. Basis für mehrsprachige Felder im Backend. &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Page Names&amp;#039;&amp;#039;&amp;#039;	(LanguageSupportPageNames) Required to use multi-language page names. Braucht man nur wenn &amp;#039;&amp;#039;&amp;#039;alle Seiten in allen Sprachen unterschiedliche Namen&amp;#039;&amp;#039;&amp;#039; bekommen sollen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Languages Support - Tabs&amp;#039;&amp;#039;&amp;#039; Organizes multi-language fields into tabs for a cleaner easier to use interface.&lt;br /&gt;
&lt;br /&gt;
== Deutsch als Default Sprache ==&lt;br /&gt;
Wenn nur die Standardsprache verändert wird aber die Seite einsprachig bleibt brauchen wir nur das Modul &amp;#039;&amp;#039;&amp;#039;Language Support&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Enable languages support -&amp;gt; &amp;#039;&amp;#039;&amp;#039;Core Module &amp;quot;Language Support&amp;quot; aktivieren&amp;#039;&amp;#039;&amp;#039;. -&amp;gt; Languages stehen im Backend Setup zur Verfügung&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Titel der Default Sprache&amp;#039;&amp;#039;&amp;#039; anpassen (z.B. Deutsch (default) )&lt;br /&gt;
* Language Pack in der default Sprache installieren -&amp;gt; Dies ist ab jetzt die Default Sprache. Nicht vergessen auch den &amp;#039;&amp;#039;&amp;#039;lang Tag im Header&amp;#039;&amp;#039;&amp;#039; zu setzen, damit der Browser die Sprache richtig erkennt.&lt;br /&gt;
&lt;br /&gt;
== Mehrere Sprachen ==&lt;br /&gt;
Für mehrsprachige Seiten benötigen wir i.d.R. folgende Module: &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Language Support Fields&amp;#039;&amp;#039;&amp;#039; für die Backend Übersetzungen und &amp;#039;&amp;#039;&amp;#039;Language Support Page Names&amp;#039;&amp;#039;&amp;#039; für die URLs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePageTitleLanguage&amp;#039;&amp;#039;&amp;#039; benötigt man wenn man alle Seitenamen anpassen will (seite1_de seite1_fr ...) Für einen reinen Sprachpfad (/de/Seite) braucht man das nicht.&lt;br /&gt;
... ToDo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Für weitere Sprachen entsprechend hinzufügen.&lt;br /&gt;
 &lt;br /&gt;
drop in the none english language pack (for admin backend) into the default language, (e.g. german langpack)&lt;br /&gt;
 &lt;br /&gt;
add a new language to it and drop in a language pack for any none english language or simply don&amp;#039;t drop in a language pack to get the english version (but not as the default one!)&lt;br /&gt;
As a nice sideeffect every new user in your system gets the native language per default without have it to select from the list.&lt;br /&gt;
&lt;br /&gt;
So, yes, this is no solution if you once have set it up and need to switch the default language afterwards, but just want to note it.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Default Sprache Deutsch ===&lt;br /&gt;
Todo Testen&lt;br /&gt;
* Modul Language Support&lt;br /&gt;
* Translation File unter Setup Sprachen hochladen&lt;br /&gt;
* Übersetzungen von Hand hinzufügen&lt;br /&gt;
&lt;br /&gt;
== Templates für Multilanguage vorbereiten (i18n) ==&lt;br /&gt;
 https://processwire.com/docs/multi-language-support/code-i18n/&lt;br /&gt;
 https://processwire.com/blog/posts/functional-fields/&lt;br /&gt;
=== Functional fields ===&lt;br /&gt;
In PW3 kamen die functional Fields hinzu. Mit diesen kann man statischen Text dynamisch im Page Editor ansprechen. D.h. das Functional Field findet Texte im Template und macht sie automatisch editierbar im Page Editor. Das Ganze funktioniert auch Mehrsprachig.&lt;br /&gt;
 [[ProcessWire - Functional Fields]]&lt;br /&gt;
 https://processwire.com/blog/posts/functional-fields/&lt;br /&gt;
Beispiele für die Ausgabe von &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
__text(&amp;#039;your text&amp;#039;);&lt;br /&gt;
__textarea(&amp;#039;your text&amp;#039;);&lt;br /&gt;
__richtext(&amp;#039;&amp;lt;p&amp;gt;your text&amp;lt;/p&amp;gt;&amp;#039;); &lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;) // define a identifier for this text snippet...&lt;br /&gt;
__text(&amp;#039;subscribe&amp;#039;) // ..reuse it this way&lt;br /&gt;
$pages-&amp;gt;get(&amp;#039;/&amp;#039;)-&amp;gt;mytext-&amp;gt;subscribe // reuse from another template&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;, &amp;#039;Submit button&amp;#039;); // Label for page editor&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;subscribe&amp;#039;, &amp;#039;label=Submit button, notes=Test&amp;#039;); // or use a pw selector&lt;br /&gt;
__text(&amp;#039;Subscribe Now&amp;#039;, &amp;#039;name=subscribe, label=Submit button, notes=Test&amp;#039;); // equivalent to upper&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Standard Multilanguage nach i18n ===&lt;br /&gt;
&lt;br /&gt;
 $out = $this-&amp;gt;_(&amp;quot;Live long and prosper&amp;quot;);  // syntax within a class&lt;br /&gt;
 $out = __(&amp;quot;Live long and prosper!&amp;quot;); // syntax outside of a class&lt;br /&gt;
&lt;br /&gt;
Dann im Backend unter der Sprache &amp;quot;Find files to translate&amp;quot; und die Template Datei auswählen.&lt;br /&gt;
&lt;br /&gt;
=== Variablen in i18n ===&lt;br /&gt;
 $out = sprintf(__(&amp;quot;Created %d pages.&amp;quot;), $count); &lt;br /&gt;
 $out = sprintf(__(&amp;#039;Your city is %1$s, and your zip code is %2$s.&amp;#039;), $city, $zipcode);&lt;br /&gt;
&lt;br /&gt;
=== Plural in Multilanguage Files===&lt;br /&gt;
Hierzu gibt es die _n() Funktion&lt;br /&gt;
 $out = sprintf(_n(&amp;quot;Created %d page.&amp;quot;, &amp;quot;Created %d pages.&amp;quot;, $count), $count);&lt;br /&gt;
=== Unterschiedliche Übersetzungen bei gleichem Wortlaut ===&lt;br /&gt;
Manchmal muss der Begriff in anderen Sprachen unterschiedlich übersetzt werden, obwohl er in der Default Sprache gleich lautet:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$label = _x(&amp;#039;Comment&amp;#039;, &amp;#039;noun&amp;#039;); // or $this-&amp;gt;_x(&amp;#039;Comment&amp;#039;, &amp;#039;noun&amp;#039;) in a class&lt;br /&gt;
...&lt;br /&gt;
// some other place in the code&lt;br /&gt;
echo _x(&amp;#039;Comment&amp;#039;, &amp;#039;column name&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Kommentare für den Übersetzer und User ===&lt;br /&gt;
 $date = __(&amp;#039;g:i:s a&amp;#039;); // Date string in PHP date() format&lt;br /&gt;
 echo __(&amp;quot;Welcome Guest&amp;quot;); // Headline for guest user // Keep it short (2-3 words)&lt;br /&gt;
=== Regeln ===&lt;br /&gt;
* Eine Zeile ein Paar Anführungszeichen&lt;br /&gt;
* Nur eine Übersetzungsfunktion pro Zeile&lt;br /&gt;
&lt;br /&gt;
== Language Switcher ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// remember what language is set to&lt;br /&gt;
$savedLanguage = $user-&amp;gt;language;&lt;br /&gt;
&lt;br /&gt;
foreach($languages as $language) {&lt;br /&gt;
&lt;br /&gt;
  // if user is already viewing the page in this language, skip it&lt;br /&gt;
  if($language-&amp;gt;id == $savedLanguage-&amp;gt;id) continue;&lt;br /&gt;
&lt;br /&gt;
  // if this page isn&amp;#039;t viewable (active) for the language, skip it&lt;br /&gt;
  if(!$page-&amp;gt;viewable($language)) continue;&lt;br /&gt;
&lt;br /&gt;
  // set the user&amp;#039;s language, so that the $page-&amp;gt;url and any other&lt;br /&gt;
  // fields we access from it will be reflective of the $language&lt;br /&gt;
  $user-&amp;gt;language = $language;&lt;br /&gt;
&lt;br /&gt;
  // output a link to this page in the other language&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$page-&amp;gt;url&amp;#039;&amp;gt;$language-&amp;gt;title: $page-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
// restore the original language setting&lt;br /&gt;
$user-&amp;gt;language = $savedLanguage;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Prantner ===&lt;br /&gt;
language-switcher.inc&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
$imgMarkup = &amp;quot;&amp;quot;;&lt;br /&gt;
$out = &amp;quot;&amp;quot;;&lt;br /&gt;
foreach($languages as $language) {&lt;br /&gt;
	if(!$page-&amp;gt;viewable($language)) continue; // is page viewable in this language?&lt;br /&gt;
	$url = $page-&amp;gt;localUrl($language);&lt;br /&gt;
	$hreflang = $homepage-&amp;gt;getLanguageValue($language, &amp;#039;name&amp;#039;);&lt;br /&gt;
	$imgUrl = $config-&amp;gt;urls-&amp;gt;templates.&amp;#039;assets/flags/&amp;#039;.$language-&amp;gt;name.&amp;#039;_sq.png&amp;#039;;&lt;br /&gt;
	$imgMarkup = &amp;quot;&amp;lt;img src=\&amp;quot;$imgUrl\&amp;quot; title=\&amp;quot;$language-&amp;gt;title\&amp;quot; alt=\&amp;quot;$language-&amp;gt;title\&amp;quot; class=\&amp;quot;circle z-depth-1\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	if($language-&amp;gt;id == $user-&amp;gt;language-&amp;gt;id) {&lt;br /&gt;
		$out .= &amp;quot;&amp;lt;span class=\&amp;quot;lang-button current\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	} else {&lt;br /&gt;
		$out .= &amp;quot;&amp;lt;span class=\&amp;quot;lang-button\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	$out .= &amp;quot;&amp;lt;a hreflang=\&amp;quot;$hreflang\&amp;quot; href=\&amp;quot;$url\&amp;quot;&amp;gt;$imgMarkup&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
$out = &amp;#039;&amp;lt;div id=&amp;quot;nav-lang&amp;quot;&amp;gt;&amp;#039;.$out.&amp;#039;&amp;lt;/div&amp;gt;&amp;#039;;&lt;br /&gt;
echo $out; &lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
language-switcher-mobile.inc&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;ul id=&amp;quot;nav-mobile&amp;quot; class=&amp;quot;right hide-on-med-and-down&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
	foreach($languages as $language) {&lt;br /&gt;
		if(!$page-&amp;gt;viewable($language)) continue; // is page viewable in this language?&lt;br /&gt;
		if($language-&amp;gt;id == $user-&amp;gt;language-&amp;gt;id) {&lt;br /&gt;
			echo &amp;quot;&amp;lt;li class=&amp;#039;current&amp;#039;&amp;gt;&amp;quot;;&lt;br /&gt;
		} else {&lt;br /&gt;
			echo &amp;quot;&amp;lt;li&amp;gt;&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$url = $page-&amp;gt;localUrl($language);&lt;br /&gt;
		$hreflang = $homepage-&amp;gt;getLanguageValue($language, &amp;#039;name&amp;#039;);&lt;br /&gt;
		echo &amp;quot;&amp;lt;a hreflang=&amp;#039;$hreflang&amp;#039; href=&amp;#039;$url&amp;#039;&amp;gt;$language-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24402</id>
		<title>ProcessWire - Hooks</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24402"/>
		<updated>2020-02-13T19:10:30Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Basic Starter Hook */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Wo setzt man hooks am Besten ein ==&lt;br /&gt;
Es gibt einige Standard Dateien die für solche Zwecke geeignet sind:&lt;br /&gt;
&lt;br /&gt;
=== /site/init.php ===&lt;br /&gt;
 PW Boot -&amp;gt; Module mit autoload -&amp;gt; init.php&lt;br /&gt;
This file is included during ProcessWire&amp;#039;s boot initialization, immediately after autoload modules have been loaded and had their init() methods called. Anything you do in here will behave the same as an init() method on a module. When this file is called, the current $page has not yet been determined. This is an excellent place to attach &amp;#039;&amp;#039;&amp;#039;hooks that don&amp;#039;t need to know anything about the current page&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== /site/ready.php ===&lt;br /&gt;
 API geladen und ready -&amp;gt; $page bereit aber noch nicht gerendert&lt;br /&gt;
This file is included immediately after the API is fully ready. It behaves the same as a ready() method in an autoload module. The current $page has been determined, but not yet rendered. This is an excellent place to &amp;#039;&amp;#039;&amp;#039;attach hooks that may need to know something about the current page&amp;#039;&amp;#039;&amp;#039;. It&amp;#039;s also an excellent place to &amp;#039;&amp;#039;&amp;#039;include additional classes or libraries that will be used on all pages&amp;#039;&amp;#039;&amp;#039; in your site.&lt;br /&gt;
&lt;br /&gt;
=== /site/finished.php ===&lt;br /&gt;
 Seite gerendert&lt;br /&gt;
This file is included when ProcessWire has finished rendering and delivering a page, and is in the process of shutting down. It is called immediately before the API is disengaged, so you can still access any API variable and &amp;#039;&amp;#039;&amp;#039;update $session values&amp;#039;&amp;#039;&amp;#039; as needed. Admittedly, this is probably &amp;#039;&amp;#039;&amp;#039;not the place you would put hooks&amp;#039;&amp;#039;&amp;#039;, but it is an ideal place to &amp;#039;&amp;#039;&amp;#039;perform your own shutdown&amp;#039;&amp;#039;&amp;#039;, should your application call for it.&lt;br /&gt;
&lt;br /&gt;
== Beispiele für Hooks ==&lt;br /&gt;
=== Basic Starter Hook ===&lt;br /&gt;
Erster Schritt zum ausprobieren&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// i.e. in ready.php&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::saved&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page); // tracy debugger output&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// more specific&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added(template=basic-page)&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Feld manipulieren ===&lt;br /&gt;
Beim Speichern einer Seite ein Feld anpassen &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;oe&amp;#039;) {&lt;br /&gt;
        // make title uppercase&lt;br /&gt;
        $page-&amp;gt;name = strtoupper($page-&amp;gt;name);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Seiten im Backend sortieren ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/*sort clients of key account manager by title in backend*/&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;key_account_manager&amp;#039;) {&lt;br /&gt;
        // Sort the Page Reference field by title&lt;br /&gt;
        $page-&amp;gt;pr_kam_clients-&amp;gt;sort(&amp;#039;title&amp;#039;) ;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Externe Klasse verfügbar machen ===&lt;br /&gt;
/site/ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object;&lt;br /&gt;
  if($page-&amp;gt;template == &amp;#039;product&amp;#039;) {&lt;br /&gt;
    // update other related pages when &amp;#039;product&amp;#039; page is saved&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  // include an external helper class...&lt;br /&gt;
  require(&amp;#039;./classes/ProductCart.php&amp;#039;);&lt;br /&gt;
  // ...and establish it as a new $cart API variable, and&lt;br /&gt;
  // populate it with products saved in the user&amp;#039;s session:&lt;br /&gt;
  $wire-&amp;gt;wire(&amp;#039;cart&amp;#039;, new ProductCart($session-&amp;gt;productsInCart));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is also a /site/finished.php that obtains the products from the $cart and saves them back to the session, ready for the next request:&lt;br /&gt;
&lt;br /&gt;
/site/finished.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  $session-&amp;gt;productsInCart = $cart-&amp;gt;getArray();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Hook Methode ===&lt;br /&gt;
Beispiel aus der ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var ProcessWire $wire */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Example of a custom hook method&lt;br /&gt;
 * &lt;br /&gt;
 * This hook adds a “numPosts” method to pages using template “category”.&lt;br /&gt;
 * The return value is the quantity of posts in category.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage:&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 * $numPosts = $page-&amp;gt;numPosts(); // returns integer&lt;br /&gt;
 * numPosts = $page-&amp;gt;numPosts(true); // returns string like &amp;quot;5 posts&amp;quot;&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$wire-&amp;gt;addHook(&amp;#039;Page(template=category)::numPosts&amp;#039;, function($event) {&lt;br /&gt;
	/** @var Page $page */&lt;br /&gt;
	$page = $event-&amp;gt;object;&lt;br /&gt;
&lt;br /&gt;
	// only category pages have numPosts&lt;br /&gt;
	if($page-&amp;gt;template != &amp;#039;category&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
	// find number of posts&lt;br /&gt;
	$numPosts = $event-&amp;gt;pages-&amp;gt;count(&amp;quot;template=blog-post, categories=$page&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if($event-&amp;gt;arguments(0) === true) {&lt;br /&gt;
		// if true argument was specified, format it as a &amp;quot;5 posts&amp;quot; type string&lt;br /&gt;
		$numPosts = sprintf(_n(&amp;#039;%d post&amp;#039;, &amp;#039;%d posts&amp;#039;, $numPosts), $numPosts);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	$event-&amp;gt;return = $numPosts;&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24401</id>
		<title>ProcessWire - Hooks</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24401"/>
		<updated>2020-02-13T19:10:11Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Basic Starter Hook */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Wo setzt man hooks am Besten ein ==&lt;br /&gt;
Es gibt einige Standard Dateien die für solche Zwecke geeignet sind:&lt;br /&gt;
&lt;br /&gt;
=== /site/init.php ===&lt;br /&gt;
 PW Boot -&amp;gt; Module mit autoload -&amp;gt; init.php&lt;br /&gt;
This file is included during ProcessWire&amp;#039;s boot initialization, immediately after autoload modules have been loaded and had their init() methods called. Anything you do in here will behave the same as an init() method on a module. When this file is called, the current $page has not yet been determined. This is an excellent place to attach &amp;#039;&amp;#039;&amp;#039;hooks that don&amp;#039;t need to know anything about the current page&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== /site/ready.php ===&lt;br /&gt;
 API geladen und ready -&amp;gt; $page bereit aber noch nicht gerendert&lt;br /&gt;
This file is included immediately after the API is fully ready. It behaves the same as a ready() method in an autoload module. The current $page has been determined, but not yet rendered. This is an excellent place to &amp;#039;&amp;#039;&amp;#039;attach hooks that may need to know something about the current page&amp;#039;&amp;#039;&amp;#039;. It&amp;#039;s also an excellent place to &amp;#039;&amp;#039;&amp;#039;include additional classes or libraries that will be used on all pages&amp;#039;&amp;#039;&amp;#039; in your site.&lt;br /&gt;
&lt;br /&gt;
=== /site/finished.php ===&lt;br /&gt;
 Seite gerendert&lt;br /&gt;
This file is included when ProcessWire has finished rendering and delivering a page, and is in the process of shutting down. It is called immediately before the API is disengaged, so you can still access any API variable and &amp;#039;&amp;#039;&amp;#039;update $session values&amp;#039;&amp;#039;&amp;#039; as needed. Admittedly, this is probably &amp;#039;&amp;#039;&amp;#039;not the place you would put hooks&amp;#039;&amp;#039;&amp;#039;, but it is an ideal place to &amp;#039;&amp;#039;&amp;#039;perform your own shutdown&amp;#039;&amp;#039;&amp;#039;, should your application call for it.&lt;br /&gt;
&lt;br /&gt;
== Beispiele für Hooks ==&lt;br /&gt;
=== Basic Starter Hook ===&lt;br /&gt;
Erster Schritt zum ausprobieren (z.B. in ready.php)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::saved&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page); // tracy debugger output&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// more specific&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added(template=basic-page)&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Feld manipulieren ===&lt;br /&gt;
Beim Speichern einer Seite ein Feld anpassen &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;oe&amp;#039;) {&lt;br /&gt;
        // make title uppercase&lt;br /&gt;
        $page-&amp;gt;name = strtoupper($page-&amp;gt;name);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Seiten im Backend sortieren ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/*sort clients of key account manager by title in backend*/&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;key_account_manager&amp;#039;) {&lt;br /&gt;
        // Sort the Page Reference field by title&lt;br /&gt;
        $page-&amp;gt;pr_kam_clients-&amp;gt;sort(&amp;#039;title&amp;#039;) ;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Externe Klasse verfügbar machen ===&lt;br /&gt;
/site/ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object;&lt;br /&gt;
  if($page-&amp;gt;template == &amp;#039;product&amp;#039;) {&lt;br /&gt;
    // update other related pages when &amp;#039;product&amp;#039; page is saved&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  // include an external helper class...&lt;br /&gt;
  require(&amp;#039;./classes/ProductCart.php&amp;#039;);&lt;br /&gt;
  // ...and establish it as a new $cart API variable, and&lt;br /&gt;
  // populate it with products saved in the user&amp;#039;s session:&lt;br /&gt;
  $wire-&amp;gt;wire(&amp;#039;cart&amp;#039;, new ProductCart($session-&amp;gt;productsInCart));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is also a /site/finished.php that obtains the products from the $cart and saves them back to the session, ready for the next request:&lt;br /&gt;
&lt;br /&gt;
/site/finished.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  $session-&amp;gt;productsInCart = $cart-&amp;gt;getArray();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Hook Methode ===&lt;br /&gt;
Beispiel aus der ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var ProcessWire $wire */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Example of a custom hook method&lt;br /&gt;
 * &lt;br /&gt;
 * This hook adds a “numPosts” method to pages using template “category”.&lt;br /&gt;
 * The return value is the quantity of posts in category.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage:&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 * $numPosts = $page-&amp;gt;numPosts(); // returns integer&lt;br /&gt;
 * numPosts = $page-&amp;gt;numPosts(true); // returns string like &amp;quot;5 posts&amp;quot;&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$wire-&amp;gt;addHook(&amp;#039;Page(template=category)::numPosts&amp;#039;, function($event) {&lt;br /&gt;
	/** @var Page $page */&lt;br /&gt;
	$page = $event-&amp;gt;object;&lt;br /&gt;
&lt;br /&gt;
	// only category pages have numPosts&lt;br /&gt;
	if($page-&amp;gt;template != &amp;#039;category&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
	// find number of posts&lt;br /&gt;
	$numPosts = $event-&amp;gt;pages-&amp;gt;count(&amp;quot;template=blog-post, categories=$page&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if($event-&amp;gt;arguments(0) === true) {&lt;br /&gt;
		// if true argument was specified, format it as a &amp;quot;5 posts&amp;quot; type string&lt;br /&gt;
		$numPosts = sprintf(_n(&amp;#039;%d post&amp;#039;, &amp;#039;%d posts&amp;#039;, $numPosts), $numPosts);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	$event-&amp;gt;return = $numPosts;&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24400</id>
		<title>ProcessWire - Hooks</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24400"/>
		<updated>2020-02-13T19:09:32Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Beispiele für Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Wo setzt man hooks am Besten ein ==&lt;br /&gt;
Es gibt einige Standard Dateien die für solche Zwecke geeignet sind:&lt;br /&gt;
&lt;br /&gt;
=== /site/init.php ===&lt;br /&gt;
 PW Boot -&amp;gt; Module mit autoload -&amp;gt; init.php&lt;br /&gt;
This file is included during ProcessWire&amp;#039;s boot initialization, immediately after autoload modules have been loaded and had their init() methods called. Anything you do in here will behave the same as an init() method on a module. When this file is called, the current $page has not yet been determined. This is an excellent place to attach &amp;#039;&amp;#039;&amp;#039;hooks that don&amp;#039;t need to know anything about the current page&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== /site/ready.php ===&lt;br /&gt;
 API geladen und ready -&amp;gt; $page bereit aber noch nicht gerendert&lt;br /&gt;
This file is included immediately after the API is fully ready. It behaves the same as a ready() method in an autoload module. The current $page has been determined, but not yet rendered. This is an excellent place to &amp;#039;&amp;#039;&amp;#039;attach hooks that may need to know something about the current page&amp;#039;&amp;#039;&amp;#039;. It&amp;#039;s also an excellent place to &amp;#039;&amp;#039;&amp;#039;include additional classes or libraries that will be used on all pages&amp;#039;&amp;#039;&amp;#039; in your site.&lt;br /&gt;
&lt;br /&gt;
=== /site/finished.php ===&lt;br /&gt;
 Seite gerendert&lt;br /&gt;
This file is included when ProcessWire has finished rendering and delivering a page, and is in the process of shutting down. It is called immediately before the API is disengaged, so you can still access any API variable and &amp;#039;&amp;#039;&amp;#039;update $session values&amp;#039;&amp;#039;&amp;#039; as needed. Admittedly, this is probably &amp;#039;&amp;#039;&amp;#039;not the place you would put hooks&amp;#039;&amp;#039;&amp;#039;, but it is an ideal place to &amp;#039;&amp;#039;&amp;#039;perform your own shutdown&amp;#039;&amp;#039;&amp;#039;, should your application call for it.&lt;br /&gt;
&lt;br /&gt;
== Beispiele für Hooks ==&lt;br /&gt;
=== Basic Starter Hook ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::saved&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page); // tracy debugger output&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// more specific&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added(template=basic-page)&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Feld manipulieren ===&lt;br /&gt;
Beim Speichern einer Seite ein Feld anpassen &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;oe&amp;#039;) {&lt;br /&gt;
        // make title uppercase&lt;br /&gt;
        $page-&amp;gt;name = strtoupper($page-&amp;gt;name);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Seiten im Backend sortieren ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/*sort clients of key account manager by title in backend*/&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;key_account_manager&amp;#039;) {&lt;br /&gt;
        // Sort the Page Reference field by title&lt;br /&gt;
        $page-&amp;gt;pr_kam_clients-&amp;gt;sort(&amp;#039;title&amp;#039;) ;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Externe Klasse verfügbar machen ===&lt;br /&gt;
/site/ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object;&lt;br /&gt;
  if($page-&amp;gt;template == &amp;#039;product&amp;#039;) {&lt;br /&gt;
    // update other related pages when &amp;#039;product&amp;#039; page is saved&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  // include an external helper class...&lt;br /&gt;
  require(&amp;#039;./classes/ProductCart.php&amp;#039;);&lt;br /&gt;
  // ...and establish it as a new $cart API variable, and&lt;br /&gt;
  // populate it with products saved in the user&amp;#039;s session:&lt;br /&gt;
  $wire-&amp;gt;wire(&amp;#039;cart&amp;#039;, new ProductCart($session-&amp;gt;productsInCart));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is also a /site/finished.php that obtains the products from the $cart and saves them back to the session, ready for the next request:&lt;br /&gt;
&lt;br /&gt;
/site/finished.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  $session-&amp;gt;productsInCart = $cart-&amp;gt;getArray();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Hook Methode ===&lt;br /&gt;
Beispiel aus der ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var ProcessWire $wire */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Example of a custom hook method&lt;br /&gt;
 * &lt;br /&gt;
 * This hook adds a “numPosts” method to pages using template “category”.&lt;br /&gt;
 * The return value is the quantity of posts in category.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage:&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 * $numPosts = $page-&amp;gt;numPosts(); // returns integer&lt;br /&gt;
 * numPosts = $page-&amp;gt;numPosts(true); // returns string like &amp;quot;5 posts&amp;quot;&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$wire-&amp;gt;addHook(&amp;#039;Page(template=category)::numPosts&amp;#039;, function($event) {&lt;br /&gt;
	/** @var Page $page */&lt;br /&gt;
	$page = $event-&amp;gt;object;&lt;br /&gt;
&lt;br /&gt;
	// only category pages have numPosts&lt;br /&gt;
	if($page-&amp;gt;template != &amp;#039;category&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
	// find number of posts&lt;br /&gt;
	$numPosts = $event-&amp;gt;pages-&amp;gt;count(&amp;quot;template=blog-post, categories=$page&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if($event-&amp;gt;arguments(0) === true) {&lt;br /&gt;
		// if true argument was specified, format it as a &amp;quot;5 posts&amp;quot; type string&lt;br /&gt;
		$numPosts = sprintf(_n(&amp;#039;%d post&amp;#039;, &amp;#039;%d posts&amp;#039;, $numPosts), $numPosts);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	$event-&amp;gt;return = $numPosts;&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24399</id>
		<title>ProcessWire - Hooks</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24399"/>
		<updated>2020-02-13T19:09:02Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Custom Hook Methode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Wo setzt man hooks am Besten ein ==&lt;br /&gt;
Es gibt einige Standard Dateien die für solche Zwecke geeignet sind:&lt;br /&gt;
&lt;br /&gt;
=== /site/init.php ===&lt;br /&gt;
 PW Boot -&amp;gt; Module mit autoload -&amp;gt; init.php&lt;br /&gt;
This file is included during ProcessWire&amp;#039;s boot initialization, immediately after autoload modules have been loaded and had their init() methods called. Anything you do in here will behave the same as an init() method on a module. When this file is called, the current $page has not yet been determined. This is an excellent place to attach &amp;#039;&amp;#039;&amp;#039;hooks that don&amp;#039;t need to know anything about the current page&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== /site/ready.php ===&lt;br /&gt;
 API geladen und ready -&amp;gt; $page bereit aber noch nicht gerendert&lt;br /&gt;
This file is included immediately after the API is fully ready. It behaves the same as a ready() method in an autoload module. The current $page has been determined, but not yet rendered. This is an excellent place to &amp;#039;&amp;#039;&amp;#039;attach hooks that may need to know something about the current page&amp;#039;&amp;#039;&amp;#039;. It&amp;#039;s also an excellent place to &amp;#039;&amp;#039;&amp;#039;include additional classes or libraries that will be used on all pages&amp;#039;&amp;#039;&amp;#039; in your site.&lt;br /&gt;
&lt;br /&gt;
=== /site/finished.php ===&lt;br /&gt;
 Seite gerendert&lt;br /&gt;
This file is included when ProcessWire has finished rendering and delivering a page, and is in the process of shutting down. It is called immediately before the API is disengaged, so you can still access any API variable and &amp;#039;&amp;#039;&amp;#039;update $session values&amp;#039;&amp;#039;&amp;#039; as needed. Admittedly, this is probably &amp;#039;&amp;#039;&amp;#039;not the place you would put hooks&amp;#039;&amp;#039;&amp;#039;, but it is an ideal place to &amp;#039;&amp;#039;&amp;#039;perform your own shutdown&amp;#039;&amp;#039;&amp;#039;, should your application call for it.&lt;br /&gt;
&lt;br /&gt;
== Beispiele für Hooks ==&lt;br /&gt;
=== Basic Starter Hook ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page); // tracy debugger output&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// more specific&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added(template=basic-page)&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Feld manipulieren ===&lt;br /&gt;
Beim Speichern einer Seite ein Feld anpassen &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;oe&amp;#039;) {&lt;br /&gt;
        // make title uppercase&lt;br /&gt;
        $page-&amp;gt;name = strtoupper($page-&amp;gt;name);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Seiten im Backend sortieren ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/*sort clients of key account manager by title in backend*/&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;key_account_manager&amp;#039;) {&lt;br /&gt;
        // Sort the Page Reference field by title&lt;br /&gt;
        $page-&amp;gt;pr_kam_clients-&amp;gt;sort(&amp;#039;title&amp;#039;) ;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Externe Klasse verfügbar machen ===&lt;br /&gt;
/site/ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object;&lt;br /&gt;
  if($page-&amp;gt;template == &amp;#039;product&amp;#039;) {&lt;br /&gt;
    // update other related pages when &amp;#039;product&amp;#039; page is saved&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  // include an external helper class...&lt;br /&gt;
  require(&amp;#039;./classes/ProductCart.php&amp;#039;);&lt;br /&gt;
  // ...and establish it as a new $cart API variable, and&lt;br /&gt;
  // populate it with products saved in the user&amp;#039;s session:&lt;br /&gt;
  $wire-&amp;gt;wire(&amp;#039;cart&amp;#039;, new ProductCart($session-&amp;gt;productsInCart));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is also a /site/finished.php that obtains the products from the $cart and saves them back to the session, ready for the next request:&lt;br /&gt;
&lt;br /&gt;
/site/finished.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  $session-&amp;gt;productsInCart = $cart-&amp;gt;getArray();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Hook Methode ===&lt;br /&gt;
Beispiel aus der ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var ProcessWire $wire */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Example of a custom hook method&lt;br /&gt;
 * &lt;br /&gt;
 * This hook adds a “numPosts” method to pages using template “category”.&lt;br /&gt;
 * The return value is the quantity of posts in category.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage:&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 * $numPosts = $page-&amp;gt;numPosts(); // returns integer&lt;br /&gt;
 * numPosts = $page-&amp;gt;numPosts(true); // returns string like &amp;quot;5 posts&amp;quot;&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$wire-&amp;gt;addHook(&amp;#039;Page(template=category)::numPosts&amp;#039;, function($event) {&lt;br /&gt;
	/** @var Page $page */&lt;br /&gt;
	$page = $event-&amp;gt;object;&lt;br /&gt;
&lt;br /&gt;
	// only category pages have numPosts&lt;br /&gt;
	if($page-&amp;gt;template != &amp;#039;category&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
	// find number of posts&lt;br /&gt;
	$numPosts = $event-&amp;gt;pages-&amp;gt;count(&amp;quot;template=blog-post, categories=$page&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if($event-&amp;gt;arguments(0) === true) {&lt;br /&gt;
		// if true argument was specified, format it as a &amp;quot;5 posts&amp;quot; type string&lt;br /&gt;
		$numPosts = sprintf(_n(&amp;#039;%d post&amp;#039;, &amp;#039;%d posts&amp;#039;, $numPosts), $numPosts);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	$event-&amp;gt;return = $numPosts;&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24398</id>
		<title>ProcessWire - Hooks</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Hooks&amp;diff=24398"/>
		<updated>2020-02-13T19:01:02Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Beispiele für Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Wo setzt man hooks am Besten ein ==&lt;br /&gt;
Es gibt einige Standard Dateien die für solche Zwecke geeignet sind:&lt;br /&gt;
&lt;br /&gt;
=== /site/init.php ===&lt;br /&gt;
 PW Boot -&amp;gt; Module mit autoload -&amp;gt; init.php&lt;br /&gt;
This file is included during ProcessWire&amp;#039;s boot initialization, immediately after autoload modules have been loaded and had their init() methods called. Anything you do in here will behave the same as an init() method on a module. When this file is called, the current $page has not yet been determined. This is an excellent place to attach &amp;#039;&amp;#039;&amp;#039;hooks that don&amp;#039;t need to know anything about the current page&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== /site/ready.php ===&lt;br /&gt;
 API geladen und ready -&amp;gt; $page bereit aber noch nicht gerendert&lt;br /&gt;
This file is included immediately after the API is fully ready. It behaves the same as a ready() method in an autoload module. The current $page has been determined, but not yet rendered. This is an excellent place to &amp;#039;&amp;#039;&amp;#039;attach hooks that may need to know something about the current page&amp;#039;&amp;#039;&amp;#039;. It&amp;#039;s also an excellent place to &amp;#039;&amp;#039;&amp;#039;include additional classes or libraries that will be used on all pages&amp;#039;&amp;#039;&amp;#039; in your site.&lt;br /&gt;
&lt;br /&gt;
=== /site/finished.php ===&lt;br /&gt;
 Seite gerendert&lt;br /&gt;
This file is included when ProcessWire has finished rendering and delivering a page, and is in the process of shutting down. It is called immediately before the API is disengaged, so you can still access any API variable and &amp;#039;&amp;#039;&amp;#039;update $session values&amp;#039;&amp;#039;&amp;#039; as needed. Admittedly, this is probably &amp;#039;&amp;#039;&amp;#039;not the place you would put hooks&amp;#039;&amp;#039;&amp;#039;, but it is an ideal place to &amp;#039;&amp;#039;&amp;#039;perform your own shutdown&amp;#039;&amp;#039;&amp;#039;, should your application call for it.&lt;br /&gt;
&lt;br /&gt;
== Beispiele für Hooks ==&lt;br /&gt;
=== Basic Starter Hook ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page); // tracy debugger output&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// more specific&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Pages::added(template=basic-page)&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
  $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
  bd($page);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Feld manipulieren ===&lt;br /&gt;
Beim Speichern einer Seite ein Feld anpassen &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;oe&amp;#039;) {&lt;br /&gt;
        // make title uppercase&lt;br /&gt;
        $page-&amp;gt;name = strtoupper($page-&amp;gt;name);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Seiten im Backend sortieren ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/*sort clients of key account manager by title in backend*/&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saveReady&amp;#039;, function(HookEvent $event) {&lt;br /&gt;
    $page = $event-&amp;gt;arguments(0);&lt;br /&gt;
    // If the page has the relevant template...&lt;br /&gt;
    if($page-&amp;gt;template == &amp;#039;key_account_manager&amp;#039;) {&lt;br /&gt;
        // Sort the Page Reference field by title&lt;br /&gt;
        $page-&amp;gt;pr_kam_clients-&amp;gt;sort(&amp;#039;title&amp;#039;) ;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Externe Klasse verfügbar machen ===&lt;br /&gt;
/site/ready.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object;&lt;br /&gt;
  if($page-&amp;gt;template == &amp;#039;product&amp;#039;) {&lt;br /&gt;
    // update other related pages when &amp;#039;product&amp;#039; page is saved&lt;br /&gt;
  }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  // include an external helper class...&lt;br /&gt;
  require(&amp;#039;./classes/ProductCart.php&amp;#039;);&lt;br /&gt;
  // ...and establish it as a new $cart API variable, and&lt;br /&gt;
  // populate it with products saved in the user&amp;#039;s session:&lt;br /&gt;
  $wire-&amp;gt;wire(&amp;#039;cart&amp;#039;, new ProductCart($session-&amp;gt;productsInCart));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is also a /site/finished.php that obtains the products from the $cart and saves them back to the session, ready for the next request:&lt;br /&gt;
&lt;br /&gt;
/site/finished.php&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
if($page-&amp;gt;template != &amp;#039;admin&amp;#039;) {&lt;br /&gt;
  $session-&amp;gt;productsInCart = $cart-&amp;gt;getArray();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Hook Methode ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/** @var ProcessWire $wire */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Example of a custom hook method&lt;br /&gt;
 * &lt;br /&gt;
 * This hook adds a “numPosts” method to pages using template “category”.&lt;br /&gt;
 * The return value is the quantity of posts in category.&lt;br /&gt;
 *&lt;br /&gt;
 * Usage:&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 * $numPosts = $page-&amp;gt;numPosts(); // returns integer&lt;br /&gt;
 * numPosts = $page-&amp;gt;numPosts(true); // returns string like &amp;quot;5 posts&amp;quot;&lt;br /&gt;
 * ~~~~~&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$wire-&amp;gt;addHook(&amp;#039;Page(template=category)::numPosts&amp;#039;, function($event) {&lt;br /&gt;
	/** @var Page $page */&lt;br /&gt;
	$page = $event-&amp;gt;object;&lt;br /&gt;
&lt;br /&gt;
	// only category pages have numPosts&lt;br /&gt;
	if($page-&amp;gt;template != &amp;#039;category&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
	// find number of posts&lt;br /&gt;
	$numPosts = $event-&amp;gt;pages-&amp;gt;count(&amp;quot;template=blog-post, categories=$page&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if($event-&amp;gt;arguments(0) === true) {&lt;br /&gt;
		// if true argument was specified, format it as a &amp;quot;5 posts&amp;quot; type string&lt;br /&gt;
		$numPosts = sprintf(_n(&amp;#039;%d post&amp;#039;, &amp;#039;%d posts&amp;#039;, $numPosts), $numPosts);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	$event-&amp;gt;return = $numPosts;&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24397</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24397"/>
		<updated>2020-02-13T15:56:07Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* wireCache */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
 https://processwire.com/talk/topic/10548-my-way-of-using-wirecache-and-some-hints-to-make-life-easier-with-it/&lt;br /&gt;
&lt;br /&gt;
Auf die WireCache Klasse kann mit der &amp;#039;&amp;#039;&amp;#039;$cache&amp;#039;&amp;#039;&amp;#039; variable zugegriffen werden. Die angelegten Caches werden in der PW Datenbank gespeichert. Es gibt diverse Methoden&lt;br /&gt;
 https://processwire.com/api/ref/wire-cache/&lt;br /&gt;
Standard Cachezeit ist 24h&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039; Beispiel - delayed output variable eines Templates cachen.&lt;br /&gt;
Je nach Seite wird ein neues Cache Objekt angelegt. Wir nutzen ein Prefix (c für cache und content für die Variable $content) und hängen die Page ID an. So kann man den Cache leicht identifizieren. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$cacheId = &amp;#039;c_content_&amp;#039;.$page-&amp;gt;id;&lt;br /&gt;
$content = $cache-&amp;gt;get($cacheId);&lt;br /&gt;
if(!$content) {&lt;br /&gt;
	include(&amp;quot;./partials/layout-blocks.inc&amp;quot;);&lt;br /&gt;
	$content = renderLayoutBlocks($page,$additionalHeaderData); // 2nd passed by reference&lt;br /&gt;
	$cache-&amp;gt;save($cacheId, $content);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Beispiel Cache für AJAX, GET, POST umgehen ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (!count($input-&amp;gt;get) &amp;amp;&amp;amp; !count($input-&amp;gt;post) &amp;amp;&amp;amp; !$config-&amp;gt;ajax) {&lt;br /&gt;
    // cache logic from above&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performante Datenbankabfragen ==&lt;br /&gt;
Mit Modul oder per Hand (pdo) möglich.&lt;br /&gt;
 https://modules.processwire.com/modules/rock-finder/&lt;br /&gt;
 https://processwire.com/talk/topic/16346-how-to-transform-page-selectors-into-sql-queries/&lt;br /&gt;
my module is a companion module for RockGrid. Those grids always need an array of objects as data source. Using $pages-&amp;gt;find() and then foreach() to create such an array is terribly inefficient when dealing with several thousands of pages. That&amp;#039;s why I built RockFinder -&amp;gt; it creates an efficient SQL statement for you and it is almost as easy as using regular PW selectors.&lt;br /&gt;
&lt;br /&gt;
The benefit of using SQL is that it is incredibly efficient. The downside is that some pw-related tasks become a headache (like checking for published state, sort order, multilanguage field values, to name just a few). RockFinder takes care of all that.&lt;br /&gt;
&lt;br /&gt;
It returns an array of objects that you can easily pass to a RockGrid or do whatever you want with it.&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24396</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24396"/>
		<updated>2020-02-13T15:54:24Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Caching */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
 https://processwire.com/talk/topic/10548-my-way-of-using-wirecache-and-some-hints-to-make-life-easier-with-it/&lt;br /&gt;
&lt;br /&gt;
Auf die WireCache Klasse kann mit der &amp;#039;&amp;#039;&amp;#039;$cache&amp;#039;&amp;#039;&amp;#039; variable zugegriffen werden. Die angelegten Caches werden in der PW Datenbank gespeichert. Es gibt diverse Methoden&lt;br /&gt;
 https://processwire.com/api/ref/wire-cache/&lt;br /&gt;
Standard Cachezeit ist 24h&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039; Beispiel - delayed output variable eines Templates cachen.&lt;br /&gt;
Je nach Seite wird ein neues Cache Objekt angelegt. Wir nutzen ein Prefix (c für cache und content für die Variable $content) und hängen die Page ID an. So kann man den Cache leicht identifizieren. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$cacheId = &amp;#039;c_content_&amp;#039;.$page-&amp;gt;id;&lt;br /&gt;
$content = $cache-&amp;gt;get($cacheId);&lt;br /&gt;
if(!$content) {&lt;br /&gt;
	include(&amp;quot;./partials/layout-blocks.inc&amp;quot;);&lt;br /&gt;
	$content = renderLayoutBlocks($page,$additionalHeaderData); // 2nd passed by reference&lt;br /&gt;
	$cache-&amp;gt;save($cacheId, $content);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performante Datenbankabfragen ==&lt;br /&gt;
Mit Modul oder per Hand (pdo) möglich.&lt;br /&gt;
 https://modules.processwire.com/modules/rock-finder/&lt;br /&gt;
 https://processwire.com/talk/topic/16346-how-to-transform-page-selectors-into-sql-queries/&lt;br /&gt;
my module is a companion module for RockGrid. Those grids always need an array of objects as data source. Using $pages-&amp;gt;find() and then foreach() to create such an array is terribly inefficient when dealing with several thousands of pages. That&amp;#039;s why I built RockFinder -&amp;gt; it creates an efficient SQL statement for you and it is almost as easy as using regular PW selectors.&lt;br /&gt;
&lt;br /&gt;
The benefit of using SQL is that it is incredibly efficient. The downside is that some pw-related tasks become a headache (like checking for published state, sort order, multilanguage field values, to name just a few). RockFinder takes care of all that.&lt;br /&gt;
&lt;br /&gt;
It returns an array of objects that you can easily pass to a RockGrid or do whatever you want with it.&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24395</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24395"/>
		<updated>2020-02-13T15:52:17Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Caching */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
 https://processwire.com/talk/topic/10548-my-way-of-using-wirecache-and-some-hints-to-make-life-easier-with-it/&lt;br /&gt;
&lt;br /&gt;
Auf die WireCache Klasse kann mit der &amp;#039;&amp;#039;&amp;#039;$cache&amp;#039;&amp;#039;&amp;#039; variable zugegriffen werden. Die angelegten Caches werden in der PW Datenbank gespeichert. Es gibt diverse Methoden&lt;br /&gt;
 https://processwire.com/api/ref/wire-cache/&lt;br /&gt;
Standard Cachezeit ist 24h&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039; Beispiel - delayed output variable eines Templates cachen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$cacheId = &amp;#039;c_content_&amp;#039;.$page-&amp;gt;id;&lt;br /&gt;
$content = $cache-&amp;gt;get($cacheId);&lt;br /&gt;
if(!$content) {&lt;br /&gt;
	include(&amp;quot;./partials/layout-blocks.inc&amp;quot;);&lt;br /&gt;
	$content = renderLayoutBlocks($page,$additionalHeaderData); // 2nd passed by reference&lt;br /&gt;
	$cache-&amp;gt;save($cacheId, $content);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performante Datenbankabfragen ==&lt;br /&gt;
Mit Modul oder per Hand (pdo) möglich.&lt;br /&gt;
 https://modules.processwire.com/modules/rock-finder/&lt;br /&gt;
 https://processwire.com/talk/topic/16346-how-to-transform-page-selectors-into-sql-queries/&lt;br /&gt;
my module is a companion module for RockGrid. Those grids always need an array of objects as data source. Using $pages-&amp;gt;find() and then foreach() to create such an array is terribly inefficient when dealing with several thousands of pages. That&amp;#039;s why I built RockFinder -&amp;gt; it creates an efficient SQL statement for you and it is almost as easy as using regular PW selectors.&lt;br /&gt;
&lt;br /&gt;
The benefit of using SQL is that it is incredibly efficient. The downside is that some pw-related tasks become a headache (like checking for published state, sort order, multilanguage field values, to name just a few). RockFinder takes care of all that.&lt;br /&gt;
&lt;br /&gt;
It returns an array of objects that you can easily pass to a RockGrid or do whatever you want with it.&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24394</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24394"/>
		<updated>2020-02-13T14:56:40Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Caching */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
 https://processwire.com/talk/topic/10548-my-way-of-using-wirecache-and-some-hints-to-make-life-easier-with-it/&lt;br /&gt;
&lt;br /&gt;
Example als Hook für die ready.php (ungetestet)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// the next ensures that the following code will only run on front end (otherwise back end would get cached, too which results in problems)&lt;br /&gt;
// make sure to place anything you need in the backend before this line or change it to your needs..&lt;br /&gt;
if ((strpos($page-&amp;gt;url, wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin) !== false) || ($page-&amp;gt;id &amp;amp;&amp;amp; $page-&amp;gt;is(&amp;#039;parent|has_parent=2&amp;#039;))) return;&lt;br /&gt;
&lt;br /&gt;
$cacheName = &amp;quot;prefix__$page-&amp;gt;id-$page-&amp;gt;template-{$user-&amp;gt;language-&amp;gt;name}&amp;quot;;&lt;br /&gt;
if ($urlSegment1) $cacheName .= &amp;quot;-$urlSegment1&amp;quot;;&lt;br /&gt;
if ($urlSegment2) $cacheName .= &amp;quot;-$urlSegment2&amp;quot;;&lt;br /&gt;
if ($urlSegment3) $cacheName .= &amp;quot;-$urlSegment3&amp;quot;;&lt;br /&gt;
if ($input-&amp;gt;pageNum &amp;gt; 1) $cacheName .= &amp;quot;-page$input-&amp;gt;pageNum&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// if already cached exit here printing cached content (only 1 db query)&lt;br /&gt;
$wire-&amp;gt;addHookBefore(&amp;#039;Page::render&amp;#039;, function() use($cache, $cacheName) {&lt;br /&gt;
&lt;br /&gt;
    $cached = $cache-&amp;gt;get($cacheName);&lt;br /&gt;
    if ($cached) {&lt;br /&gt;
        exit($cached);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// not cached so far, continue as usual but save generated content to cache&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) use($cache, $cacheName) {&lt;br /&gt;
&lt;br /&gt;
    $cached = $cache-&amp;gt;get($cacheName);&lt;br /&gt;
&lt;br /&gt;
    if (!$cached) {&lt;br /&gt;
        $cache-&amp;gt;save($cacheName, $event-&amp;gt;return);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    unset($cached);&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
/* of course not the same as a proper flat file cache like procache but at least saving many database queries&lt;br /&gt;
make sure to adjust the $cacheName as needed, you can even cache query strings, almost everything you&amp;#039;d like&lt;br /&gt;
and you could wrap everything in a condition to only run if no incoming post or query strings so form submits keep working&lt;br /&gt;
example&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
if (!count($input-&amp;gt;get) &amp;amp;&amp;amp; !count($input-&amp;gt;post) &amp;amp;&amp;amp; !$config-&amp;gt;ajax) {&lt;br /&gt;
    // cache logic from above&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performante Datenbankabfragen ==&lt;br /&gt;
Mit Modul oder per Hand (pdo) möglich.&lt;br /&gt;
 https://modules.processwire.com/modules/rock-finder/&lt;br /&gt;
 https://processwire.com/talk/topic/16346-how-to-transform-page-selectors-into-sql-queries/&lt;br /&gt;
my module is a companion module for RockGrid. Those grids always need an array of objects as data source. Using $pages-&amp;gt;find() and then foreach() to create such an array is terribly inefficient when dealing with several thousands of pages. That&amp;#039;s why I built RockFinder -&amp;gt; it creates an efficient SQL statement for you and it is almost as easy as using regular PW selectors.&lt;br /&gt;
&lt;br /&gt;
The benefit of using SQL is that it is incredibly efficient. The downside is that some pw-related tasks become a headache (like checking for published state, sort order, multilanguage field values, to name just a few). RockFinder takes care of all that.&lt;br /&gt;
&lt;br /&gt;
It returns an array of objects that you can easily pass to a RockGrid or do whatever you want with it.&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24393</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24393"/>
		<updated>2020-02-13T14:55:29Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Caching */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
 https://processwire.com/talk/topic/10548-my-way-of-using-wirecache-and-some-hints-to-make-life-easier-with-it/&lt;br /&gt;
&lt;br /&gt;
Example (ungetestet)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// the next ensures that the following code will only run on front end (otherwise back end would get cached, too which results in problems)&lt;br /&gt;
// make sure to place anything you need in the backend before this line or change it to your needs..&lt;br /&gt;
if ((strpos($page-&amp;gt;url, wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin) !== false) || ($page-&amp;gt;id &amp;amp;&amp;amp; $page-&amp;gt;is(&amp;#039;parent|has_parent=2&amp;#039;))) return;&lt;br /&gt;
&lt;br /&gt;
$cacheName = &amp;quot;prefix__$page-&amp;gt;id-$page-&amp;gt;template-{$user-&amp;gt;language-&amp;gt;name}&amp;quot;;&lt;br /&gt;
if ($urlSegment1) $cacheName .= &amp;quot;-$urlSegment1&amp;quot;;&lt;br /&gt;
if ($urlSegment2) $cacheName .= &amp;quot;-$urlSegment2&amp;quot;;&lt;br /&gt;
if ($urlSegment3) $cacheName .= &amp;quot;-$urlSegment3&amp;quot;;&lt;br /&gt;
if ($input-&amp;gt;pageNum &amp;gt; 1) $cacheName .= &amp;quot;-page$input-&amp;gt;pageNum&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// if already cached exit here printing cached content (only 1 db query)&lt;br /&gt;
$wire-&amp;gt;addHookBefore(&amp;#039;Page::render&amp;#039;, function() use($cache, $cacheName) {&lt;br /&gt;
&lt;br /&gt;
    $cached = $cache-&amp;gt;get($cacheName);&lt;br /&gt;
    if ($cached) {&lt;br /&gt;
        exit($cached);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// not cached so far, continue as usual but save generated content to cache&lt;br /&gt;
$wire-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) use($cache, $cacheName) {&lt;br /&gt;
&lt;br /&gt;
    $cached = $cache-&amp;gt;get($cacheName);&lt;br /&gt;
&lt;br /&gt;
    if (!$cached) {&lt;br /&gt;
        $cache-&amp;gt;save($cacheName, $event-&amp;gt;return);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    unset($cached);&lt;br /&gt;
&lt;br /&gt;
});&lt;br /&gt;
/* of course not the same as a proper flat file cache like procache but at least saving many database queries&lt;br /&gt;
make sure to adjust the $cacheName as needed, you can even cache query strings, almost everything you&amp;#039;d like&lt;br /&gt;
and you could wrap everything in a condition to only run if no incoming post or query strings so form submits keep working&lt;br /&gt;
example&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
if (!count($input-&amp;gt;get) &amp;amp;&amp;amp; !count($input-&amp;gt;post) &amp;amp;&amp;amp; !$config-&amp;gt;ajax) {&lt;br /&gt;
    // cache logic from above&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performante Datenbankabfragen ==&lt;br /&gt;
Mit Modul oder per Hand (pdo) möglich.&lt;br /&gt;
 https://modules.processwire.com/modules/rock-finder/&lt;br /&gt;
 https://processwire.com/talk/topic/16346-how-to-transform-page-selectors-into-sql-queries/&lt;br /&gt;
my module is a companion module for RockGrid. Those grids always need an array of objects as data source. Using $pages-&amp;gt;find() and then foreach() to create such an array is terribly inefficient when dealing with several thousands of pages. That&amp;#039;s why I built RockFinder -&amp;gt; it creates an efficient SQL statement for you and it is almost as easy as using regular PW selectors.&lt;br /&gt;
&lt;br /&gt;
The benefit of using SQL is that it is incredibly efficient. The downside is that some pw-related tasks become a headache (like checking for published state, sort order, multilanguage field values, to name just a few). RockFinder takes care of all that.&lt;br /&gt;
&lt;br /&gt;
It returns an array of objects that you can easily pass to a RockGrid or do whatever you want with it.&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24392</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24392"/>
		<updated>2020-02-13T10:46:49Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
 https://processwire.com/api/ref/wire-file-tools/render/&lt;br /&gt;
 https://processwire.com/api/ref/functions/wire-render-file/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
 https://processwire.com/talk/topic/20797-clean-syntax-for-rendering-pages-with-specific-template/&lt;br /&gt;
 https://processwire.com/talk/topic/3145-multiple-views-for-templates/page/2/?tab=comments#comment-32876 (ergiebiger thread)&lt;br /&gt;
 https://processwire.com/api/ref/inputfield-checkboxes/render/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-value/ (mit Markup File)&lt;br /&gt;
 https://processwire.com/docs/front-end/output/delayed/ (delayed output)&lt;br /&gt;
 https://processwire.com/talk/topic/13958-help-understanding-render-and-rendervalue/&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render // modern version for wireRenderFile&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$page-&amp;gt;render($filename); // $filename assumed in /site/templates/&lt;br /&gt;
$page-&amp;gt;render($pathname); // $pathname is full path, but must resolve somewhere in web root&lt;br /&gt;
$page-&amp;gt;render($options); // array of options and/or your own variables&lt;br /&gt;
$page-&amp;gt;render(array(&amp;#039;foo&amp;#039; =&amp;gt; &amp;#039;bar&amp;#039;)); // same as above&lt;br /&gt;
$page-&amp;gt;render($filename, $options); // specify filename and options/vars, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
foreach ($pages-&amp;gt;find(&amp;quot;template=article&amp;quot;) as $article) {&lt;br /&gt;
    $content .= $article-&amp;gt;render(&amp;#039;teaser.php&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24391</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24391"/>
		<updated>2020-02-13T10:40:19Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
 https://processwire.com/api/ref/wire-file-tools/render/&lt;br /&gt;
 https://processwire.com/api/ref/functions/wire-render-file/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
 https://processwire.com/talk/topic/20797-clean-syntax-for-rendering-pages-with-specific-template/&lt;br /&gt;
 https://processwire.com/talk/topic/3145-multiple-views-for-templates/page/2/?tab=comments#comment-32876 (ergiebiger thread)&lt;br /&gt;
 https://processwire.com/api/ref/inputfield-checkboxes/render/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-value/ (mit Markup File)&lt;br /&gt;
 https://processwire.com/docs/front-end/output/delayed/ (delayed output)&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render // modern version for wireRenderFile&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$page-&amp;gt;render($filename); // $filename assumed in /site/templates/&lt;br /&gt;
$page-&amp;gt;render($pathname); // $pathname is full path, but must resolve somewhere in web root&lt;br /&gt;
$page-&amp;gt;render($options); // array of options and/or your own variables&lt;br /&gt;
$page-&amp;gt;render(array(&amp;#039;foo&amp;#039; =&amp;gt; &amp;#039;bar&amp;#039;)); // same as above&lt;br /&gt;
$page-&amp;gt;render($filename, $options); // specify filename and options/vars, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
foreach ($pages-&amp;gt;find(&amp;quot;template=article&amp;quot;) as $article) {&lt;br /&gt;
    $content .= $article-&amp;gt;render(&amp;#039;teaser.php&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24390</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24390"/>
		<updated>2020-02-13T10:38:13Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
 https://processwire.com/api/ref/wire-file-tools/render/&lt;br /&gt;
 https://processwire.com/api/ref/functions/wire-render-file/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
 https://processwire.com/talk/topic/20797-clean-syntax-for-rendering-pages-with-specific-template/&lt;br /&gt;
 https://processwire.com/talk/topic/3145-multiple-views-for-templates/page/2/?tab=comments#comment-32876 (ergiebiger thread)&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render // modern version for wireRenderFile&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$page-&amp;gt;render($filename); // $filename assumed in /site/templates/&lt;br /&gt;
$page-&amp;gt;render($pathname); // $pathname is full path, but must resolve somewhere in web root&lt;br /&gt;
$page-&amp;gt;render($options); // array of options and/or your own variables&lt;br /&gt;
$page-&amp;gt;render(array(&amp;#039;foo&amp;#039; =&amp;gt; &amp;#039;bar&amp;#039;)); // same as above&lt;br /&gt;
$page-&amp;gt;render($filename, $options); // specify filename and options/vars, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
foreach ($pages-&amp;gt;find(&amp;quot;template=article&amp;quot;) as $article) {&lt;br /&gt;
    $content .= $article-&amp;gt;render(&amp;#039;teaser.php&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24389</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24389"/>
		<updated>2020-02-13T10:31:09Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
 https://processwire.com/api/ref/functions/wire-render-file/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
 https://processwire.com/talk/topic/20797-clean-syntax-for-rendering-pages-with-specific-template/&lt;br /&gt;
 https://processwire.com/talk/topic/3145-multiple-views-for-templates/page/2/?tab=comments#comment-32876 (ergiebiger thread)&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render // modern version for wireRenderFile&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$page-&amp;gt;render($filename); // $filename assumed in /site/templates/&lt;br /&gt;
$page-&amp;gt;render($pathname); // $pathname is full path, but must resolve somewhere in web root&lt;br /&gt;
$page-&amp;gt;render($options); // array of options and/or your own variables&lt;br /&gt;
$page-&amp;gt;render(array(&amp;#039;foo&amp;#039; =&amp;gt; &amp;#039;bar&amp;#039;)); // same as above&lt;br /&gt;
$page-&amp;gt;render($filename, $options); // specify filename and options/vars, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
foreach ($pages-&amp;gt;find(&amp;quot;template=article&amp;quot;) as $article) {&lt;br /&gt;
    $content .= $article-&amp;gt;render(&amp;#039;teaser.php&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24388</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24388"/>
		<updated>2020-02-13T10:27:58Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Snippets */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
 https://processwire.com/api/ref/functions/wire-render-file/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render // shorthand for wireRenderFile&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
foreach ($pages-&amp;gt;find(&amp;quot;template=article&amp;quot;) as $article) {&lt;br /&gt;
    $content .= $article-&amp;gt;render(&amp;#039;teaser.php&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24387</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24387"/>
		<updated>2020-02-13T10:27:37Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
 https://processwire.com/api/ref/functions/wire-render-file/&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render // shorthand for wireRenderFile&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
 https://processwire.com/api/ref/page/render-field/&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
foreach ($pages-&amp;gt;find(&amp;quot;template=article&amp;quot;) as $article) {&lt;br /&gt;
    $content .= $article-&amp;gt;render(&amp;#039;teaser.php&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhiglight&amp;gt;&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24386</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24386"/>
		<updated>2020-02-13T10:18:13Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
 $files-&amp;gt;render&lt;br /&gt;
 $page-&amp;gt;render&lt;br /&gt;
&lt;br /&gt;
== Snippets ==&lt;br /&gt;
 $content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24385</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24385"/>
		<updated>2020-02-13T10:17:12Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
$files-&amp;gt;render&lt;br /&gt;
$page-&amp;gt;render&lt;br /&gt;
== Snippets ==&lt;br /&gt;
 $content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
=== Pass global vars ===&lt;br /&gt;
You can pass variable via render like:&lt;br /&gt;
&lt;br /&gt;
 echo $pages-&amp;gt;get($child-&amp;gt;id)-&amp;gt;render(array(&amp;#039;mobile&amp;#039; =&amp;gt; $mobile));&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
 https://processwire.com/talk/topic/20915-wirerenderfile-question-about-page/&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24384</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24384"/>
		<updated>2020-02-13T10:17:07Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
&lt;br /&gt;
== Performante Datenbankabfragen ==&lt;br /&gt;
Mit Modul oder per Hand (pdo) möglich.&lt;br /&gt;
 https://modules.processwire.com/modules/rock-finder/&lt;br /&gt;
 https://processwire.com/talk/topic/16346-how-to-transform-page-selectors-into-sql-queries/&lt;br /&gt;
my module is a companion module for RockGrid. Those grids always need an array of objects as data source. Using $pages-&amp;gt;find() and then foreach() to create such an array is terribly inefficient when dealing with several thousands of pages. That&amp;#039;s why I built RockFinder -&amp;gt; it creates an efficient SQL statement for you and it is almost as easy as using regular PW selectors.&lt;br /&gt;
&lt;br /&gt;
The benefit of using SQL is that it is incredibly efficient. The downside is that some pw-related tasks become a headache (like checking for published state, sort order, multilanguage field values, to name just a few). RockFinder takes care of all that.&lt;br /&gt;
&lt;br /&gt;
It returns an array of objects that you can easily pass to a RockGrid or do whatever you want with it.&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24383</id>
		<title>ProcessWire - Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Performance&amp;diff=24383"/>
		<updated>2020-02-13T10:12:35Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Caching */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Caching ==&lt;br /&gt;
=== wireCache ===&lt;br /&gt;
 https://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades&lt;br /&gt;
 https://processwire.com/talk/topic/15286-delete-and-generate-new-markup-cache/&lt;br /&gt;
&lt;br /&gt;
== Performance Testen ==&lt;br /&gt;
=== Zeit und RAM ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $t = $p-&amp;gt;title . microtime();&lt;br /&gt;
    $b = $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
    $out .= $t . $b;&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 18384.08ms, 25.00 MB&lt;br /&gt;
 &lt;br /&gt;
// 8000 pages with title and body fields&lt;br /&gt;
$pp = $pages(&amp;#039;template=basic, parent=1384&amp;#039;);&lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pp as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17617.05ms, 25.00MB&lt;br /&gt;
 &lt;br /&gt;
$out = &amp;#039;&amp;#039;;&lt;br /&gt;
foreach($pages(&amp;#039;template=basic, parent=1384&amp;#039;) as $p) {&lt;br /&gt;
    $out .= $p-&amp;gt;title . microtime();&lt;br /&gt;
    $out .= $p-&amp;gt;body . mt_rand(0, 1e5);&lt;br /&gt;
}&lt;br /&gt;
echo strlen($out);&lt;br /&gt;
// 17927.9ms, 25.00MB&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Performance und Repeater Matrix ==&lt;br /&gt;
 https://processwire.com/talk/topic/20156-are-there-hidden-scalabilityperformance-issues-when-making-heavy-use-of-repeater-matrix-or-pagetable-fields/&lt;br /&gt;
=== Backend ===&lt;br /&gt;
* Repeater Items per AJAX laden &lt;br /&gt;
 https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#repeater-upgrades-continued-in-pw-3.0.5&lt;br /&gt;
=== Frontend ===&lt;br /&gt;
* Können viele Datenbank (PDO) Queries auslösen, da alle Page Objekte geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
=== Auswahl aus verschiedenen Repeatern über ein Select, statt einzelnes RepeaterMatrix Feld ===&lt;br /&gt;
Evtl. könnte man ein Auswahlfeld generieren, das dann wiederum einen Contentblock wählt, statt alles in ein RepeaterMatrix Feld zu packen.&lt;br /&gt;
&lt;br /&gt;
* Layoutblock -&amp;gt; Select Feld&lt;br /&gt;
* Optionen werden geladen.&lt;br /&gt;
&lt;br /&gt;
=== Alternativ PageTables ===&lt;br /&gt;
 https://processwire.com/talk/topic/7459-module-pagetableextended/&lt;br /&gt;
Könnte Performancemäßig die bessere Alternative sein. Prüfen und auch mit PageTableExtended verbinden.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24382</id>
		<title>ProcessWire - Rendering Funktionen</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Rendering_Funktionen&amp;diff=24382"/>
		<updated>2020-02-13T10:09:47Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: Die Seite wurde neu angelegt: „In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann. PW stellt aber auch einige Funktionen bereit, mit dene…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In den Template Strategiene sind einige Methoden beschrieben wie man Seiten in ProcessWire rendern kann.&lt;br /&gt;
PW stellt aber auch einige Funktionen bereit, mit denen man Dateien oder Seiten direkt rendern kann. Diese arbeiten auch gut mit dem WireCache zusammen. So kann man Teile der Ausgabe rendern und gleich Cachen.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
== Functions ==&lt;br /&gt;
$files-&amp;gt;render&lt;br /&gt;
$page-&amp;gt;render&lt;br /&gt;
== Snippets ==&lt;br /&gt;
 $content .= $files-&amp;gt;render(&amp;#039;teasers&amp;#039;, [&amp;#039;items&amp;#039; =&amp;gt; $pages-&amp;gt;find(&amp;quot;template=article&amp;quot;)]);&lt;br /&gt;
&lt;br /&gt;
=== Check if File is called directly or via wireRenderFile ===&lt;br /&gt;
Is there a way to check in my included template file if this child page is called directly, or whether it has been included from somewhere else? A simple check, so I can use the template in both scenarios (stand-alone view for just this page, or in cases I use wireRenderFile somewhere else). Right now I am passing the child page&amp;#039;s ID like this:&lt;br /&gt;
&lt;br /&gt;
wireRenderFile(&amp;#039;my_template&amp;#039;, array(&amp;#039;pid&amp;#039; =&amp;gt; $child-&amp;gt;id));&lt;br /&gt;
And in my_template, to access it&amp;#039;s own page fields, etc. I use&lt;br /&gt;
&lt;br /&gt;
 $thisPage = pages()-&amp;gt;get($pid);&lt;br /&gt;
 $myField = $thisPage-&amp;gt;foo; // etc.&lt;br /&gt;
Is that the way to go, or do I overlook something obvious?&lt;br /&gt;
&lt;br /&gt;
d&amp;#039;oh of course, it&amp;#039;s as simple as:&lt;br /&gt;
&lt;br /&gt;
 if(isset($pid)) {&lt;br /&gt;
    $thisPage = pages()-&amp;gt;get($pid); // we get called with wireRenderFile&lt;br /&gt;
 } else {&lt;br /&gt;
    $thisPage = $page; // business as usual&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24253</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24253"/>
		<updated>2020-01-06T10:19:15Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* = Konfigurationsdatei statt getModuleInfo */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
Statt einer getModuleInfo() Funktion kann man auch eine json Konfiguration als Datei hinterlegen&lt;br /&gt;
&lt;br /&gt;
Siehe Beispiel unten&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== CSS und JavaScript in Modulen ==&lt;br /&gt;
Bei Process Modulen möchte man für das Styling etc. im Backend oft CSS und JS Dateien hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Einfach Eine Datei MeinModul.css und / oder MeinModul.js hinzufügen. JQuery ist im Admin bereits geladen daher kann eine MeinModul.js Starter Datei so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * This JS file is only loaded when the ProcessHello module is run&lt;br /&gt;
 *&lt;br /&gt;
 * You should delete it if you have no javascript to add.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
$(document).ready(function() {&lt;br /&gt;
	// do something&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;br /&gt;
&lt;br /&gt;
=== Konfigurationsdatei statt getModuleInfo ===&lt;br /&gt;
Bei größeren Konfigruation (z.B. Admin Page Navigation) sinnvoll.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessHello.info.php&lt;br /&gt;
 * &lt;br /&gt;
 * Return information about this module.&lt;br /&gt;
 *&lt;br /&gt;
 * If preferred, you can use a getModuleInfo() method in your module file, &lt;br /&gt;
 * or you can use a ModuleName.info.json file (if you prefer JSON definition). &lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$info = array(&lt;br /&gt;
	// Your module&amp;#039;s title&lt;br /&gt;
	&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello: Process Module Example&amp;#039;, &lt;br /&gt;
	// A 1 sentence description of what your module does&lt;br /&gt;
	&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;A starting point module skeleton from which to build your own Process module.&amp;#039;, &lt;br /&gt;
	// Module version number: use 1 for 0.0.1 or 100 for 1.0.0, and so on&lt;br /&gt;
	&amp;#039;version&amp;#039; =&amp;gt; 1, &lt;br /&gt;
	// Name of person who created this module (change to your name)&lt;br /&gt;
	&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ryan Cramer&amp;#039;, &lt;br /&gt;
	// Icon to accompany this module (optional), uses font-awesome icon names, minus the &amp;quot;fa-&amp;quot; part&lt;br /&gt;
	&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;thumbs-up&amp;#039;, &lt;br /&gt;
	// URL to more info: change to your full modules.processwire.com URL (if available), or something else if you prefer&lt;br /&gt;
	&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;http://modules.processwire.com/&amp;#039;, &lt;br /&gt;
	// name of permission required of users to execute this Process (optional)&lt;br /&gt;
	&amp;#039;permission&amp;#039; =&amp;gt; &amp;#039;helloworld&amp;#039;, &lt;br /&gt;
	// permissions that you want automatically installed/uninstalled with this module (name =&amp;gt; description)&lt;br /&gt;
	&amp;#039;permissions&amp;#039; =&amp;gt; array(&lt;br /&gt;
		&amp;#039;helloworld&amp;#039; =&amp;gt; &amp;#039;Run the HelloWorld module&amp;#039;&lt;br /&gt;
	), &lt;br /&gt;
	&lt;br /&gt;
	// page that you want created to execute this module&lt;br /&gt;
	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;helloworld&amp;#039;,&lt;br /&gt;
		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;setup&amp;#039;, &lt;br /&gt;
		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;&lt;br /&gt;
	),&lt;br /&gt;
	// optional extra navigation that appears in admin&lt;br /&gt;
	// if you change this, you&amp;#039;ll need to a Modules &amp;gt; Refresh to see changes&lt;br /&gt;
	&amp;#039;nav&amp;#039; =&amp;gt; array(&lt;br /&gt;
		array(&lt;br /&gt;
			&amp;#039;url&amp;#039; =&amp;gt; &amp;#039;&amp;#039;, &lt;br /&gt;
			&amp;#039;label&amp;#039; =&amp;gt; &amp;#039;Hello&amp;#039;, &lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
		), &lt;br /&gt;
		array(&lt;br /&gt;
			&amp;#039;url&amp;#039; =&amp;gt; &amp;#039;something/&amp;#039;, &lt;br /&gt;
			&amp;#039;label&amp;#039; =&amp;gt; &amp;#039;Something&amp;#039;, &lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;beer&amp;#039;, &lt;br /&gt;
		),&lt;br /&gt;
	)&lt;br /&gt;
	// for more options that you may specify here, see the file: /wire/core/Process.php&lt;br /&gt;
	// and the file: /wire/core/Module.php&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24252</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24252"/>
		<updated>2020-01-06T10:17:48Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Beispiele */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
Statt einer getModuleInfo() Funktion kann man auch eine json Konfiguration als Datei hinterlegen&lt;br /&gt;
&lt;br /&gt;
Siehe Beispiel unten&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== CSS und JavaScript in Modulen ==&lt;br /&gt;
Bei Process Modulen möchte man für das Styling etc. im Backend oft CSS und JS Dateien hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Einfach Eine Datei MeinModul.css und / oder MeinModul.js hinzufügen. JQuery ist im Admin bereits geladen daher kann eine MeinModul.js Starter Datei so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * This JS file is only loaded when the ProcessHello module is run&lt;br /&gt;
 *&lt;br /&gt;
 * You should delete it if you have no javascript to add.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
$(document).ready(function() {&lt;br /&gt;
	// do something&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;br /&gt;
&lt;br /&gt;
=== Konfigurationsdatei statt getModuleInfo ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessHello.info.php&lt;br /&gt;
 * &lt;br /&gt;
 * Return information about this module.&lt;br /&gt;
 *&lt;br /&gt;
 * If preferred, you can use a getModuleInfo() method in your module file, &lt;br /&gt;
 * or you can use a ModuleName.info.json file (if you prefer JSON definition). &lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
$info = array(&lt;br /&gt;
	// Your module&amp;#039;s title&lt;br /&gt;
	&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello: Process Module Example&amp;#039;, &lt;br /&gt;
	// A 1 sentence description of what your module does&lt;br /&gt;
	&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;A starting point module skeleton from which to build your own Process module.&amp;#039;, &lt;br /&gt;
	// Module version number: use 1 for 0.0.1 or 100 for 1.0.0, and so on&lt;br /&gt;
	&amp;#039;version&amp;#039; =&amp;gt; 1, &lt;br /&gt;
	// Name of person who created this module (change to your name)&lt;br /&gt;
	&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ryan Cramer&amp;#039;, &lt;br /&gt;
	// Icon to accompany this module (optional), uses font-awesome icon names, minus the &amp;quot;fa-&amp;quot; part&lt;br /&gt;
	&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;thumbs-up&amp;#039;, &lt;br /&gt;
	// URL to more info: change to your full modules.processwire.com URL (if available), or something else if you prefer&lt;br /&gt;
	&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;http://modules.processwire.com/&amp;#039;, &lt;br /&gt;
	// name of permission required of users to execute this Process (optional)&lt;br /&gt;
	&amp;#039;permission&amp;#039; =&amp;gt; &amp;#039;helloworld&amp;#039;, &lt;br /&gt;
	// permissions that you want automatically installed/uninstalled with this module (name =&amp;gt; description)&lt;br /&gt;
	&amp;#039;permissions&amp;#039; =&amp;gt; array(&lt;br /&gt;
		&amp;#039;helloworld&amp;#039; =&amp;gt; &amp;#039;Run the HelloWorld module&amp;#039;&lt;br /&gt;
	), &lt;br /&gt;
	&lt;br /&gt;
	// page that you want created to execute this module&lt;br /&gt;
	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;helloworld&amp;#039;,&lt;br /&gt;
		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;setup&amp;#039;, &lt;br /&gt;
		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;&lt;br /&gt;
	),&lt;br /&gt;
	// optional extra navigation that appears in admin&lt;br /&gt;
	// if you change this, you&amp;#039;ll need to a Modules &amp;gt; Refresh to see changes&lt;br /&gt;
	&amp;#039;nav&amp;#039; =&amp;gt; array(&lt;br /&gt;
		array(&lt;br /&gt;
			&amp;#039;url&amp;#039; =&amp;gt; &amp;#039;&amp;#039;, &lt;br /&gt;
			&amp;#039;label&amp;#039; =&amp;gt; &amp;#039;Hello&amp;#039;, &lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
		), &lt;br /&gt;
		array(&lt;br /&gt;
			&amp;#039;url&amp;#039; =&amp;gt; &amp;#039;something/&amp;#039;, &lt;br /&gt;
			&amp;#039;label&amp;#039; =&amp;gt; &amp;#039;Something&amp;#039;, &lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;beer&amp;#039;, &lt;br /&gt;
		),&lt;br /&gt;
	)&lt;br /&gt;
	// for more options that you may specify here, see the file: /wire/core/Process.php&lt;br /&gt;
	// and the file: /wire/core/Module.php&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24251</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24251"/>
		<updated>2020-01-06T10:16:59Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* getModuleInfo */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
Statt einer getModuleInfo() Funktion kann man auch eine json Konfiguration als Datei hinterlegen&lt;br /&gt;
&lt;br /&gt;
Siehe Beispiel unten&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== CSS und JavaScript in Modulen ==&lt;br /&gt;
Bei Process Modulen möchte man für das Styling etc. im Backend oft CSS und JS Dateien hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Einfach Eine Datei MeinModul.css und / oder MeinModul.js hinzufügen. JQuery ist im Admin bereits geladen daher kann eine MeinModul.js Starter Datei so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * This JS file is only loaded when the ProcessHello module is run&lt;br /&gt;
 *&lt;br /&gt;
 * You should delete it if you have no javascript to add.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
$(document).ready(function() {&lt;br /&gt;
	// do something&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24250</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24250"/>
		<updated>2020-01-06T10:14:26Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Autoload Module und Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== CSS und JavaScript in Modulen ==&lt;br /&gt;
Bei Process Modulen möchte man für das Styling etc. im Backend oft CSS und JS Dateien hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Einfach Eine Datei MeinModul.css und / oder MeinModul.js hinzufügen. JQuery ist im Admin bereits geladen daher kann eine MeinModul.js Starter Datei so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * This JS file is only loaded when the ProcessHello module is run&lt;br /&gt;
 *&lt;br /&gt;
 * You should delete it if you have no javascript to add.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
$(document).ready(function() {&lt;br /&gt;
	// do something&lt;br /&gt;
}); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24249</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24249"/>
		<updated>2020-01-06T10:06:06Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* = executeSomething */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24248</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24248"/>
		<updated>2020-01-06T10:05:53Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* execute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24247</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24247"/>
		<updated>2020-01-06T10:05:33Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* execute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * This function is executed when a page with your Process assigned is accessed. &lt;br /&gt;
 	 *&lt;br /&gt;
	 * This can be seen as your main or index function. You&amp;#039;ll probably want to replace&lt;br /&gt;
	 * everything in this function. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___execute() {&lt;br /&gt;
		// greetingType and greeting are automatically populated to this module&lt;br /&gt;
		// and they were defined in ProcessHello.config.php&lt;br /&gt;
		if($this-&amp;gt;greetingType == &amp;#039;message&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;greeting); &lt;br /&gt;
		} else if($this-&amp;gt;greetingType == &amp;#039;warning&amp;#039;) {&lt;br /&gt;
			$this-&amp;gt;warning($this-&amp;gt;greeting); &lt;br /&gt;
		} else {&lt;br /&gt;
			$this-&amp;gt;error($this-&amp;gt;greeting); &lt;br /&gt;
		}&lt;br /&gt;
		// generate some navigation&lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;$this-&amp;gt;greeting&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;dl class=&amp;#039;nav&amp;#039;&amp;gt;&lt;br /&gt;
				&amp;lt;dt&amp;gt;&amp;lt;a href=&amp;#039;./something/&amp;#039;&amp;gt;Do Something&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
				&amp;lt;dd&amp;gt;Runs the executeSomething() function.&amp;lt;/dd&amp;gt;&lt;br /&gt;
			&amp;lt;/dl&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out;&lt;br /&gt;
	}	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== executeSomething ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called when the URL is this module&amp;#039;s page URL + &amp;quot;/something/&amp;quot;&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___executeSomething() {&lt;br /&gt;
		// set a new headline, replacing the one used by our page&lt;br /&gt;
		// this is optional as PW will auto-generate a headline &lt;br /&gt;
		$this-&amp;gt;headline(&amp;#039;This is something!&amp;#039;); &lt;br /&gt;
		// add a breadcrumb that returns to our main page &lt;br /&gt;
		// this is optional as PW will auto-generate breadcrumbs&lt;br /&gt;
		$this-&amp;gt;breadcrumb(&amp;#039;../&amp;#039;, &amp;#039;Hello&amp;#039;); &lt;br /&gt;
		$out = 	&amp;quot;&lt;br /&gt;
			&amp;lt;h2&amp;gt;Not much to to see here&amp;lt;/h2&amp;gt;&lt;br /&gt;
			&amp;lt;p&amp;gt;&amp;lt;a href=&amp;#039;../&amp;#039;&amp;gt;Go Back&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
			&amp;quot;;&lt;br /&gt;
		return $out; &lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== install &amp;amp; uninstall ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
	 * Called only when your module is installed&lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___install() {&lt;br /&gt;
		parent::___install(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
	/**&lt;br /&gt;
	 * Called only when your module is uninstalled&lt;br /&gt;
	 *&lt;br /&gt;
	 * This should return the site to the same state it was in before the module was installed. &lt;br /&gt;
	 *&lt;br /&gt;
	 * If you don&amp;#039;t need anything here, you can simply remove this method. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function ___uninstall() {&lt;br /&gt;
		parent::___uninstall(); // always remember to call parent method&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24246</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24246"/>
		<updated>2020-01-06T09:58:39Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Spezielle Funktionen in Modulen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
=== execute ===&lt;br /&gt;
&lt;br /&gt;
== Vererbung und Eigenschaften in Modulen ==&lt;br /&gt;
Modules are not different from PHP classes.&lt;br /&gt;
&lt;br /&gt;
To change the properties of MyModule class from within MyOtherModule class, you can either:&lt;br /&gt;
&lt;br /&gt;
#Make MyOtherModule class extend MyModule. It will thus inherit MyModule&amp;#039;s public and protected properties and methods&lt;br /&gt;
#Make the properties firstName and lastName configurable by passing them as parameters/arguments in MyModule&amp;#039;s constructor method.&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24245</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24245"/>
		<updated>2020-01-06T09:10:49Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Welche Typen von Modulen gibt es ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Repräsentiert einen Datentyp. Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über ein Formular im Admin Bereich. Im Gegensatz zum Fieldtype geht es hier um das UI im Backend. Das Handling der Daten liegt beim Fieldtype.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24244</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24244"/>
		<updated>2020-01-06T09:04:41Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
Wichtigste Resourcen&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://processwire.com/talk/forum/19-moduleplugin-development/ - Forum zum Thema Module entwickeln&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ - Hook Cheatsheet&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/ - Neuere Konfigurationsmöglichkeiten&lt;br /&gt;
Weitere Links&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24243</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24243"/>
		<updated>2020-01-06T09:02:26Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://processwire.com/api/ref/module/ - Module API&lt;br /&gt;
 https://processwire.com/api/ref/configurable-module/ - Klasse für Konfigurierbare Module&lt;br /&gt;
 https://processwire.com/docs/modules/types/ - Welche Modultypen gibt es ?&lt;br /&gt;
 https://github.com/ryancramerdesign/ProcessHello - Modul Skelett Beispiel für eigene Backend (Process) Module&lt;br /&gt;
 https://github.com/ryancramerdesign/FieldtypeMapMarker - Beispiel für ein Fieldtype und Inputfield Modul&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/ - Module die andere Module benötigen&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/api/ref/module/&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ // Hook Cheatsheet&lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
 $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
Hook auf eine einzelne Instanz (hier pages Objekt)&lt;br /&gt;
 $this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;saved&amp;#039;, function($event){...});&lt;br /&gt;
 $this-&amp;gt;addHook(&amp;#039;Page::method&amp;#039;, ...) // Spricht ALLE Instanzen an - Regelfall&lt;br /&gt;
 $page-&amp;gt;addHook(&amp;#039;method&amp;#039;, ...) // Spricht nur die EINE Instanz der Seite an.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24242</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24242"/>
		<updated>2020-01-06T08:37:12Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Autoload Module und Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/api/ref/module/&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ // Hook Cheatsheet&lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
Das gleiche aber nicht mit anonymer Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  // add hook after Page::render() and make it call the &amp;quot;test&amp;quot; method of $this module&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;test&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public function test($event) {&lt;br /&gt;
  // modify the return value of Page::render() to include the following:&lt;br /&gt;
  $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hook Beispiele ===&lt;br /&gt;
Hook am Ende des Renderings&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {...});&lt;br /&gt;
Füge eine Funktion &amp;quot;summarize&amp;quot; zum Page Objekt hinzu.&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24241</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24241"/>
		<updated>2020-01-06T08:01:40Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - Guter Ausgangspunkt&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/api/ref/module/&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ // Hook Cheatsheet&lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Spezielle Funktionen in Modulen ==&lt;br /&gt;
&lt;br /&gt;
=== getModuleInfo ===&lt;br /&gt;
Ist die einzige Pflichtfunktion in Modulen. Hier werden title, version, summary und weitere Daten hinterlegt. Hier kann man auch angeben ob ein Modul ein Autoload Modul ist.&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
 public function init(){}&lt;br /&gt;
Initialisiert das Modul. ProcessWire ruft diese Funktion auf, wenn das Modul geladen ist. Bei Autoload Modulen wird es aufgerufen wenn die ProcessWire API bereit ist. Daher ist es ein guter Ort um Hooks einzubinden.&lt;br /&gt;
&lt;br /&gt;
=== ready ===&lt;br /&gt;
  public function ready() {&lt;br /&gt;
    if($this-&amp;gt;page-&amp;gt;template == &amp;#039;admin&amp;#039;) {&lt;br /&gt;
      $this-&amp;gt;message($this-&amp;gt;hi());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
Wird in autoload Modulen aufgerufen wenn die API bereit ist. Nützlich für Hooks:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Autoload Module und Hooks ==&lt;br /&gt;
Autoload werden automatisch geladen müssen also nicht in einem Template o.ä. gestartet werden. Daher bieten sie sich an um die Funktionalität von ProcessWire zu erweitern. Als Werkzeug dafür dienen Hooks. Mit Hooks kann man an vielen Stellen den Rendering Process der Seiten beeinflussen. Außerdem kann man für eigene Module ebenfalls Hooks bereitstellen. &lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
 https://processwire.com/docs/modules/hooks/&lt;br /&gt;
Hooks sind ein mächtiges Werkzeug. Sie geben einem die Möglichkeit an vielen Stellen &amp;quot;einzuhaken&amp;quot; und Funktionalität einzubauen.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, function($event) {&lt;br /&gt;
    $event-&amp;gt;return .= &amp;#039;&amp;lt;p&amp;gt;Hallo&amp;lt;/p&amp;gt;&amp;#039;;&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Schreibt am Ende Jeder Seite ein &amp;quot;Hallo&amp;quot;. Das $event Objekt ist ein [https://processwire.com/api/ref/hook-event/ HookEvent]. Im Beispiel fügen wir einfach etwas Markup hinzu. Über $event-&amp;gt;arguments() kann man aber auf ale Argumente zugreifen.&lt;br /&gt;
&lt;br /&gt;
=== Hooks zum erweitern von existierenden Klassen nutzen ===&lt;br /&gt;
Mit Hooks kann man in vorhandene Klassen einhaken. Z.B. läßt sich das Page Objekt erweitern.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function ready() {&lt;br /&gt;
  $this-&amp;gt;addHook(&amp;#039;Page::summarize&amp;#039;, $this, &amp;#039;summarize&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
public function summarize($event) {&lt;br /&gt;
  $page = $event-&amp;gt;object; // the $event-&amp;gt;object represents the object hooked (Page)&lt;br /&gt;
  $maxlen = $event-&amp;gt;arguments(0); // first argument is the optional max length&lt;br /&gt;
  if(!$maxlen) $maxlen = 200; // if no $maxlen was present, we&amp;#039;ll use a default of 200&lt;br /&gt;
  $summary = $this-&amp;gt;sanitizer-&amp;gt;truncate($page-&amp;gt;body, $maxlen); // use sanitizer truncate method to create a summary&lt;br /&gt;
  $event-&amp;gt;return = $summary; // populate $summary to $event-&amp;gt;return, the return value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So kann man ganz einfach eine Zusammenfassung aller Kindseiten in einem Template rendern:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
foreach($page-&amp;gt;children as $item) {&lt;br /&gt;
  $summary = $item-&amp;gt;summarize(150);&lt;br /&gt;
  echo &amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;#039;$item-&amp;gt;url&amp;#039;&amp;gt;$item-&amp;gt;title&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;$summary&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Autoload Module nur im Admin Bereich laden ===&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; &amp;#039;template=admin&amp;#039; &lt;br /&gt;
statt&lt;br /&gt;
 &amp;#039;autoload&amp;#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24240</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24240"/>
		<updated>2020-01-06T07:19:19Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Admin Funktionalität: Countdown zum Seitenveröffentlichen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/api/ref/module/&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - &lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ // Hook Cheatsheet&lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the &amp;#039;&amp;#039;&amp;#039;Publish Later buttons&amp;#039;&amp;#039;&amp;#039;, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Creating two fields&amp;#039;&amp;#039;&amp;#039; within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adding hooks&amp;#039;&amp;#039;&amp;#039; to both &amp;#039;&amp;#039;ProcessPageEdit::buildForm&amp;#039;&amp;#039; and &amp;#039;&amp;#039;ProcessPageListActions::getExtraActions&amp;#039;&amp;#039; enabling me to add the two buttons.&lt;br /&gt;
&lt;br /&gt;
Checking to see if one of the buttons was clicked with my &amp;#039;&amp;#039;&amp;#039;ready() function&amp;#039;&amp;#039;&amp;#039; then setting the corresponding page’s checkbox to true.&lt;br /&gt;
&lt;br /&gt;
Using a &amp;#039;&amp;#039;&amp;#039;LazyCron hook function&amp;#039;&amp;#039;&amp;#039; to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
&lt;br /&gt;
The result is a &amp;#039;&amp;#039;&amp;#039;module that has settings&amp;#039;&amp;#039;&amp;#039; (the time interval to check and publish at a later time), &amp;#039;&amp;#039;&amp;#039;hooks into the admin&amp;#039;&amp;#039;&amp;#039; using buttons and fields, and enables us to &amp;#039;&amp;#039;&amp;#039;use other modules installe&amp;#039;&amp;#039;&amp;#039;d in PW (i.e. LazyCron).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_Snippets&amp;diff=24239</id>
		<title>ProcessWire - Module Snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_Snippets&amp;diff=24239"/>
		<updated>2020-01-06T07:10:51Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nützliche Snippets für das Schreiben von Modulen&lt;br /&gt;
&lt;br /&gt;
== Testen ob ein bestimmtes Modul installiert ist ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (!$this-&amp;gt;modules-&amp;gt;isInstalled(&amp;#039;Tasker&amp;#039;)) {&lt;br /&gt;
  $this-&amp;gt;message(&amp;#039;Tasker module is missing.  Install it before using Dataset module.&amp;#039;);&lt;br /&gt;
  return;&lt;br /&gt;
}&lt;br /&gt;
$this-&amp;gt;tasker = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;Tasker&amp;#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Selektoren ==&lt;br /&gt;
=== Einfacher Selector Check ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// check the page selector&lt;br /&gt;
if (strlen($selector)&amp;lt;2 || !strpos($selector, &amp;#039;=&amp;#039;)) {&lt;br /&gt;
  $this-&amp;gt;error(&amp;quot;ERROR: invalid page selector &amp;#039;{$selector}&amp;#039; found in the input.&amp;quot;);&lt;br /&gt;
  return false;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_Snippets&amp;diff=24238</id>
		<title>ProcessWire - Module Snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_Snippets&amp;diff=24238"/>
		<updated>2020-01-06T06:57:14Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: Die Seite wurde neu angelegt: „Nützliche Snippets für das Schreiben von Modulen  == Testen ob ein bestimmtes Modul installiert ist == &amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt; if (!$this-&amp;gt;modules-&amp;gt;isIn…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Nützliche Snippets für das Schreiben von Modulen&lt;br /&gt;
&lt;br /&gt;
== Testen ob ein bestimmtes Modul installiert ist ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (!$this-&amp;gt;modules-&amp;gt;isInstalled(&amp;#039;Tasker&amp;#039;)) {&lt;br /&gt;
  $this-&amp;gt;message(&amp;#039;Tasker module is missing.  Install it before using Dataset module.&amp;#039;);&lt;br /&gt;
  return;&lt;br /&gt;
}&lt;br /&gt;
$this-&amp;gt;tasker = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;Tasker&amp;#039;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24237</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24237"/>
		<updated>2020-01-06T06:55:00Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/api/ref/module/&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - &lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ // Hook Cheatsheet&lt;br /&gt;
&lt;br /&gt;
 [[ProcessWire - Module Snippets]]&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the Publish Later buttons, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
Creating two fields within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
Adding hooks to both ProcessPageEdit::buildForm and ProcessPageListActions::getExtraActions enabling me to add the two buttons.&lt;br /&gt;
Checking to see if one of the buttons was clicked with my ready() function then setting the corresponding page’s checkbox to true.&lt;br /&gt;
Using a LazyCron hook function to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
The result is a module that has settings (the time interval to check and publish at a later time), hooks into the admin using buttons and fields, and enables us to use other modules installed in PW (i.e. LazyCron).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24236</id>
		<title>ProcessWire - Module schreiben</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Module_schreiben&amp;diff=24236"/>
		<updated>2020-01-04T17:04:22Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Welche Typen von Modulen gibt es ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/a-beginners-introduction-to-writing-modules-in-processwire--cms-26862&lt;br /&gt;
 https://processwire.com/api/ref/module/&lt;br /&gt;
 https://processwire.com/docs/modules/ - Introduction, Development, Hooks, Types, Pro Modules, Third Party&lt;br /&gt;
 https://processwire.com/docs/modules/development/ - &lt;br /&gt;
 https://processwire.com/blog/posts/new-module-configuration-options/&lt;br /&gt;
 https://processwire.com/talk/topic/778-module-dependencies/&lt;br /&gt;
 http://somatonic.github.io/Captain-Hook/ // Hook Cheatsheet&lt;br /&gt;
&lt;br /&gt;
== Wo - Was ? ==&lt;br /&gt;
=== Welche Typen von Modulen gibt es ? ===&lt;br /&gt;
 https://processwire.com/docs/modules/types/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fieldtype&amp;#039;&amp;#039;&amp;#039;: Meistens keine Public API, Handelt Daten / Datenbank - https://processwire.com/api/ref/fieldtype/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Inputfield&amp;#039;&amp;#039;&amp;#039;: Sammelt User Eingaben über Formulare für Felder (Fieldtypes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process&amp;#039;&amp;#039;&amp;#039; for creating admin processes/applications.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Textformatter&amp;#039;&amp;#039;&amp;#039; for formatting text.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminTheme&amp;#039;&amp;#039;&amp;#039; for creating themes in the admin.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;WireMail&amp;#039;&amp;#039;&amp;#039; for modules that send email and extend the WireMail class.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Tfa&amp;#039;&amp;#039;&amp;#039; for implementing a specific kind of two-factor authentication.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImageSizerEngine&amp;#039;&amp;#039;&amp;#039; for modules that extend ImageSizerEngine for resizing images.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileCompiler&amp;#039;&amp;#039;&amp;#039; for modules that extend FileCompilerModule for compilation of files.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FileValidator&amp;#039;&amp;#039;&amp;#039; for modules that extend FileValidatorModule for validation of files.&lt;br /&gt;
&lt;br /&gt;
=== Wie werden Felder in der Datenbank angelegt ? ===&lt;br /&gt;
Dazu nutzt man den Typ &amp;quot;Fieldtype&amp;quot;&lt;br /&gt;
=== Wo rendert man die Ausgabe ? ===&lt;br /&gt;
Bei Ryan Cramers Event Beispiel legt er zwei Klassen Event und EventArray an, die auch die Render Funktionen enthalten.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
=== Frontend Rendering Module ===&lt;br /&gt;
A bit old but working&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * FrontEndRender&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class FrontEndRender extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;FrontEndRender&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Outputs html and static variables to frontend&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// protected variable only accessable within module&lt;br /&gt;
	protected $name = &amp;#039;Ben&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* render function to be called in PW template like this:&lt;br /&gt;
	* $FrontEndRender = $modules-&amp;gt;getModule(&amp;#039;FrontEndRender&amp;#039;);&lt;br /&gt;
	* echo &amp;#039;&amp;lt;h1&amp;gt;&amp;#039; . $FrontEndRender-&amp;gt;render() . &amp;#039;&amp;lt;/h1&amp;gt;&amp;#039;;&lt;br /&gt;
	*&lt;br /&gt;
	*/&lt;br /&gt;
	public function render(){&lt;br /&gt;
		return &amp;quot;Hello &amp;quot; . $this-&amp;gt;name;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Hello World Modul ===&lt;br /&gt;
Beispiel Modul mit Hooks (liegt immer in der Standardinstallation)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php namespace ProcessWire;&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * ProcessWire &amp;#039;Hello world&amp;#039; demonstration module&lt;br /&gt;
 *&lt;br /&gt;
 * Demonstrates the Module interface and how to add hooks.&lt;br /&gt;
 * &lt;br /&gt;
 * See README file for further links regarding module development.&lt;br /&gt;
 * &lt;br /&gt;
 * This file is licensed under the MIT license&lt;br /&gt;
 * https://processwire.com/about/license/mit/&lt;br /&gt;
 * &lt;br /&gt;
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer&lt;br /&gt;
 * https://processwire.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class Helloworld extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * getModuleInfo is a module required by all modules to tell ProcessWire about them&lt;br /&gt;
	 *&lt;br /&gt;
	 * @return array&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
&lt;br /&gt;
		return array(&lt;br /&gt;
&lt;br /&gt;
			// The module&amp;#039;s title, typically a little more descriptive than the class name&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Hello World&amp;#039;, &lt;br /&gt;
&lt;br /&gt;
			// version number &lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 3, &lt;br /&gt;
&lt;br /&gt;
			// summary is brief description of what this module is&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;An example module used for demonstration purposes.&amp;#039;,&lt;br /&gt;
			&lt;br /&gt;
			// Optional URL to more information about the module&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://processwire.com&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
			// singular=true: indicates that only one instance of the module is allowed.&lt;br /&gt;
			// This is usually what you want for modules that attach hooks. &lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true, &lt;br /&gt;
&lt;br /&gt;
			// autoload=true: indicates the module should be started with ProcessWire.&lt;br /&gt;
			// This is necessary for any modules that attach runtime hooks, otherwise those&lt;br /&gt;
			// hooks won&amp;#039;t get attached unless some other code calls the module on it&amp;#039;s own.&lt;br /&gt;
			// Note that autoload modules are almost always also &amp;#039;singular&amp;#039; (seen above).&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true, &lt;br /&gt;
		&lt;br /&gt;
			// Optional font-awesome icon name, minus the &amp;#039;fa-&amp;#039; part&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;#039;smile-o&amp;#039;, &lt;br /&gt;
			);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Initialize the module&lt;br /&gt;
	 *&lt;br /&gt;
	 * ProcessWire calls this when the module is loaded. For &amp;#039;autoload&amp;#039; modules, this will be called&lt;br /&gt;
	 * when ProcessWire&amp;#039;s API is ready. As a result, this is a good place to attach hooks. &lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// add a hook after the $pages-&amp;gt;save, to issue a notice every time a page is saved&lt;br /&gt;
		$this-&amp;gt;pages-&amp;gt;addHookAfter(&amp;#039;save&amp;#039;, $this, &amp;#039;example1&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a hook after each page is rendered and modify the output&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;#039;Page::render&amp;#039;, $this, &amp;#039;example2&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello&amp;#039; method to every page that returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello();&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;#039;Page::hello&amp;#039;, $this, &amp;#039;example3&amp;#039;); &lt;br /&gt;
&lt;br /&gt;
		// add a &amp;#039;hello_world&amp;#039; property to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
		// use &amp;quot;echo $page-&amp;gt;hello_world;&amp;quot; in your template file to display output&lt;br /&gt;
		$this-&amp;gt;addHookProperty(&amp;#039;Page::hello_world&amp;#039;, $this, &amp;#039;example4&amp;#039;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example1 hooks into the pages-&amp;gt;save method and displays a notice every time a page is saved&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example1($event) {&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0]; &lt;br /&gt;
		$this-&amp;gt;message(&amp;quot;Hello World! You saved {$page-&amp;gt;path}.&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example2 hooks into every page after it&amp;#039;s rendered and adds &amp;quot;Hello World&amp;quot; text at the bottom&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example2($event) {&lt;br /&gt;
&lt;br /&gt;
		/** @var Page $page */&lt;br /&gt;
		$page = $event-&amp;gt;object; &lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t add this to the admin pages&lt;br /&gt;
		if($page-&amp;gt;template == &amp;#039;admin&amp;#039;) return;&lt;br /&gt;
&lt;br /&gt;
		// add a &amp;quot;Hello World&amp;quot; paragraph right before the closing body tag&lt;br /&gt;
		$event-&amp;gt;return = str_replace(&amp;quot;&amp;lt;/body&amp;gt;&amp;quot;, &amp;quot;&amp;lt;p&amp;gt;Hello World!&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;quot;, $event-&amp;gt;return); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example3 adds a &amp;#039;hello&amp;#039; method (not property) to every page that simply returns &amp;quot;Hello World&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example3($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Example 4 adds a &amp;#039;hello_world&amp;#039; property (not method) to every page that returns &amp;quot;Hello [user]&amp;quot;&lt;br /&gt;
	 * &lt;br /&gt;
	 * @param HookEvent $event&lt;br /&gt;
	 *&lt;br /&gt;
	 */&lt;br /&gt;
	public function example4($event) {&lt;br /&gt;
		$event-&amp;gt;return = &amp;quot;Hello &amp;quot; . $this-&amp;gt;user-&amp;gt;name; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Admin Bereich mit eigener Funktionalität erweitern (Modul)===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
* ProcessSimpleAdminPage&lt;br /&gt;
*&lt;br /&gt;
* @author Ben Byford&lt;br /&gt;
* http://www.benbyford.com&lt;br /&gt;
*&lt;br /&gt;
* @see http://www.processwire.com&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
class ProcessSimpleAdminPage extends Process {&lt;br /&gt;
&lt;br /&gt;
    public static function getModuleInfo() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Process Simple Admin Page&amp;#039;,&lt;br /&gt;
            &amp;#039;summary&amp;#039; =&amp;gt; &amp;#039;Simple Process module that adds new admin page with&amp;#039;,&lt;br /&gt;
            &amp;#039;version&amp;#039; =&amp;gt; 001,&lt;br /&gt;
&lt;br /&gt;
            // Modules that extend Process may specify a &amp;#039;page&amp;#039; attribute in the&lt;br /&gt;
            // getModuleInfo(), this page will automatically be given the module&lt;br /&gt;
            // process when added to teh pagetree.&lt;br /&gt;
&lt;br /&gt;
            // I have exampled but commented out the &amp;#039;page&amp;#039; settings below&lt;br /&gt;
            // so that I can show how one might add a page to install() and&lt;br /&gt;
            // uninstall() in this and other modules (that might not extend&lt;br /&gt;
            // Process)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        // 	&amp;#039;page&amp;#039; =&amp;gt; array(&lt;br /&gt;
        // 		&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;site-config&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;parent&amp;#039; =&amp;gt; &amp;#039;admin&amp;#039;,&lt;br /&gt;
        // 		&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;Site Config&amp;#039;&lt;br /&gt;
        // 	   )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function execute() {&lt;br /&gt;
        return &amp;#039;&lt;br /&gt;
            &amp;lt;h2&amp;gt;Edit the text here in the module&amp;lt;/h2&amp;gt;&lt;br /&gt;
            &amp;lt;p&amp;gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Donec scelerisque pulvinar sem eu lobortis. Maecenas turpis ipsum, tempus dictum pharetra eu, consectetur vitae arcu. Fusce orci mauris, semper at tempus quis, volutpat molestie tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed quam tortor, tincidunt sed semper lacinia, scelerisque dapibus quam. Morbi at nisi luctus lacus auctor ultrices eu eu leo.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Praesent faucibus purus id felis tincidunt dignissim. Sed sit amet ligula mi, eget semper dui. Proin consectetur gravida massa, nec luctus purus hendrerit in. Etiam volutpat, elit non venenatis suscipit, libero neque consectetur diam, id rutrum magna odio ac ligula. Maecenas sollicitudin congue neque fermentum vestibulum. Morbi nec leo nisi. Donec at nisl odio, et porta ligula.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;p&amp;gt;Sed quis arcu nisi, ac tempor augue. Praesent non elit libero, a ullamcorper lorem. Curabitur porta odio eu nunc ultricies interdum id nec risus. Donec nibh nibh, porta eget vehicula ac, aliquet eget ante. Phasellus eget lorem eu eros eleifend ultrices. Cras sit amet neque sit amet nibh fringilla cursus ut id mauris. Praesent quis nunc justo, sed suscipit lectus. Phasellus eget ultrices risus. Curabitur eu semper est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut suscipit, nisl ut imperdiet eleifend, turpis arcu placerat tortor, nec laoreet lacus neque ac tellus. Aenean ac lacus justo, quis ultricies nisi.&amp;lt;/p&amp;gt;&lt;br /&gt;
            &amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
    public function install(){&lt;br /&gt;
&lt;br /&gt;
        // create new page to add to CMS&lt;br /&gt;
		$page = new Page();&lt;br /&gt;
&lt;br /&gt;
        // add page attributes&lt;br /&gt;
        $page-&amp;gt;template = &amp;quot;admin&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;name = &amp;quot;cms-faq&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;title = &amp;quot;CMS FAQ&amp;quot;;&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
&lt;br /&gt;
        // set this module as the page process, this allows us to display the above&lt;br /&gt;
        $page-&amp;gt;process = &amp;#039;ProcessSimpleAdminPage&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        // get admin page and set as page parent&lt;br /&gt;
        $admin = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;id=2&amp;quot;);&lt;br /&gt;
        $page-&amp;gt;parent = $admin;&lt;br /&gt;
&lt;br /&gt;
        // save page&lt;br /&gt;
        $page-&amp;gt;save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
&lt;br /&gt;
        // delete created page&lt;br /&gt;
        $page = $this-&amp;gt;pages-&amp;gt;get(&amp;quot;name=cms-faq&amp;quot;);&lt;br /&gt;
        if(count($page)) $this-&amp;gt;pages-&amp;gt;delete($page, true);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ProcessWire Textformatter Modul ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * TextformatterFindReplace&lt;br /&gt;
 *&lt;br /&gt;
 * Author: Ben Byford&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2010 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class TextformatterFindReplace extends Textformatter implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;#039;TextformatterFindReplace&amp;#039;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; 0.1,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;Finds and replaces any instance of config input to config output&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;#039;Ben Byford&amp;#039;,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;#039;https://github.com/benbyford/PW-starter-modules&amp;#039;&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
     * Find and Replace the input string&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $str The block of text to parse&lt;br /&gt;
     *&lt;br /&gt;
     * The incoming string is replaced with the formatted version of itself.&lt;br /&gt;
	 **/&lt;br /&gt;
&lt;br /&gt;
	public function format(&amp;amp;$str) {&lt;br /&gt;
		$find = $this-&amp;gt;findStr;&lt;br /&gt;
		$str = preg_replace_callback($find, array($this,&amp;quot;replace&amp;quot;), $str);&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// adding three underscores to a function allows other modules to hook it&lt;br /&gt;
	public function ___replace($match) {&lt;br /&gt;
		return $this-&amp;gt;replaceStr;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Admin Funktionalität: Countdown zum Seitenveröffentlichen ===&lt;br /&gt;
 https://webdesign.tutsplus.com/tutorials/extending-the-processwire-admin-using-custom-modules--cms-26863&lt;br /&gt;
&lt;br /&gt;
The module PageDeferredPublish, on clicking one of the Publish Later buttons, sets LazyCron to check that page’s countdown every minute and publishes the page when its countdown reaches 0. This means I can publish a page approximately 24 hours in advance (obviously the checking interval and delay time can be changed to your requirements).&lt;br /&gt;
&lt;br /&gt;
I did this by:&lt;br /&gt;
&lt;br /&gt;
Creating two fields within my install() function: a checkbox field to set to true when a button is checked to indicate the page should countdown, and a countdown field to store the count in seconds for that specific page.&lt;br /&gt;
Adding hooks to both ProcessPageEdit::buildForm and ProcessPageListActions::getExtraActions enabling me to add the two buttons.&lt;br /&gt;
Checking to see if one of the buttons was clicked with my ready() function then setting the corresponding page’s checkbox to true.&lt;br /&gt;
Using a LazyCron hook function to check all pages that are unpublished for true checkboxes and then comparing the seconds field to see if the page needs publishing. If not then deduct the elapsed time in seconds.&lt;br /&gt;
The result is a module that has settings (the time interval to check and publish at a later time), hooks into the admin using buttons and fields, and enables us to use other modules installed in PW (i.e. LazyCron).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * DeferredPublish (0.0.1)&lt;br /&gt;
 * DeferredPublish publishes a page after a defined amount of time has elapsed using lazycron.&lt;br /&gt;
 *&lt;br /&gt;
 * @author Ben Byford&lt;br /&gt;
 * http://www.benbyford.com&lt;br /&gt;
 *&lt;br /&gt;
 * ProcessWire 2.x&lt;br /&gt;
 * Copyright (C) 2011 by Ryan Cramer&lt;br /&gt;
 * Licensed under GNU/GPL v2, see LICENSE.TXT&lt;br /&gt;
 *&lt;br /&gt;
 * http://www.processwire.com&lt;br /&gt;
 * http://www.ryancramer.com&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
class PageDeferredPublish extends WireData implements Module {&lt;br /&gt;
&lt;br /&gt;
	public static function getModuleInfo() {&lt;br /&gt;
		return array(&lt;br /&gt;
			&amp;#039;title&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;version&amp;#039; =&amp;gt; &amp;quot;0.0.1&amp;quot;,&lt;br /&gt;
			&amp;#039;summary&amp;#039; =&amp;gt; &amp;quot;PageDeferredPublish&amp;quot;,&lt;br /&gt;
			&amp;#039;author&amp;#039; =&amp;gt; &amp;quot;Ben Byford&amp;quot;,&lt;br /&gt;
			&amp;#039;href&amp;#039; =&amp;gt; &amp;quot;https://github.com/benbyford/PW-intermediate-modules&amp;quot;,&lt;br /&gt;
			&amp;#039;icon&amp;#039; =&amp;gt; &amp;quot;clock-o&amp;quot;,&lt;br /&gt;
			&amp;#039;autoload&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;singular&amp;#039; =&amp;gt; true,&lt;br /&gt;
			&amp;#039;requires&amp;#039; =&amp;gt; &amp;quot;LazyCron&amp;quot;,&lt;br /&gt;
		);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	private $postPubLater 	= null;&lt;br /&gt;
	private $pageID 		= null;&lt;br /&gt;
	private $pagePubLater 	= null;&lt;br /&gt;
	private $currentPage	= null;&lt;br /&gt;
&lt;br /&gt;
	private $submitName 	= &amp;#039;pdp_pub_later_submit&amp;#039;;&lt;br /&gt;
	private $listPageName	= &amp;#039;pdp_pub_later_list&amp;#039;;&lt;br /&gt;
	private $fieldName 		= &amp;#039;pdp_pub_later&amp;#039;;&lt;br /&gt;
	private $checkboxName	= &amp;#039;pdp_pub_later_check&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
	private $defaultInterval = &amp;#039;everyMinute&amp;#039;;&lt;br /&gt;
	private $defaultTime 	= 86400;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	public function install(){&lt;br /&gt;
		// add new fields needed for module&lt;br /&gt;
		$this-&amp;gt;installFields();&lt;br /&gt;
	}&lt;br /&gt;
	public function uninstall(){&lt;br /&gt;
		// uninstall fields&lt;br /&gt;
		$this-&amp;gt;uninstallFields();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// initialize the hook in your AutoLoad module&lt;br /&gt;
	public function init() {&lt;br /&gt;
&lt;br /&gt;
		// get defaults from module setting in CMS&lt;br /&gt;
		$this-&amp;gt;defaultTime = $this-&amp;gt;pub_after;&lt;br /&gt;
		$this-&amp;gt;defaultInterval = $this-&amp;gt;cron_check;&lt;br /&gt;
&lt;br /&gt;
		// get admin URL&lt;br /&gt;
		$this-&amp;gt;adminUrl = $this-&amp;gt;wire(&amp;#039;config&amp;#039;)-&amp;gt;urls-&amp;gt;admin;&lt;br /&gt;
&lt;br /&gt;
		// add hooks to CRON, PageList, PageEdit&lt;br /&gt;
	    $this-&amp;gt;addHookAfter(&amp;quot;LazyCron::{$this-&amp;gt;defaultInterval}&amp;quot;, $this, &amp;#039;publishDefferedPages&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHook(&amp;quot;ProcessPageListActions::getExtraActions&amp;quot;, $this, &amp;#039;hookPageListActions&amp;#039;);&lt;br /&gt;
		$this-&amp;gt;addHookAfter(&amp;quot;ProcessPageEdit::buildForm&amp;quot;, $this, &amp;quot;editForm&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ready() {&lt;br /&gt;
&lt;br /&gt;
		// if list button clicked then grab id&lt;br /&gt;
		$this-&amp;gt;pagePubLater = $this-&amp;gt;input-&amp;gt;get($this-&amp;gt;listPageName);&lt;br /&gt;
		$this-&amp;gt;pageID = $this-&amp;gt;input-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$this-&amp;gt;currentPage = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// if pagelist pub later submit button clicked&lt;br /&gt;
		if($this-&amp;gt;pagePubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if page edit submit button clicked&lt;br /&gt;
		$this-&amp;gt;postPubLater = $this-&amp;gt;input-&amp;gt;post($this-&amp;gt;submitName);&lt;br /&gt;
		if($this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;message(&amp;quot;Page {$this-&amp;gt;currentPage-&amp;gt;name} deffered for publish.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// if either deffered publish sumbit found then publish page later&lt;br /&gt;
		if($this-&amp;gt;pagePubLater || $this-&amp;gt;postPubLater){&lt;br /&gt;
			$this-&amp;gt;publishLater();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	*	Hook: ProcessPageEdit::buildForm&lt;br /&gt;
	*&lt;br /&gt;
	*	add Publish Later button to edit page if not yet published&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___editForm(HookEvent $form) {&lt;br /&gt;
&lt;br /&gt;
		// get the InputFieldForm object from the event (return value of buildForm())&lt;br /&gt;
		$form = $form-&amp;gt;return;&lt;br /&gt;
&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;input-&amp;gt;get-&amp;gt;id);&lt;br /&gt;
		$check = $page-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		// check if publish button available and therfore unpublished&lt;br /&gt;
		$target = $form-&amp;gt;get(&amp;#039;submit_publish&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		if($target &amp;amp;&amp;amp; $check == false){&lt;br /&gt;
&lt;br /&gt;
			// get InputfieldText module&lt;br /&gt;
			$submit2 = $this-&amp;gt;modules-&amp;gt;get(&amp;#039;InputfieldSubmit&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;name&amp;#039;, $this-&amp;gt;submitName);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;id&amp;#039;, &amp;#039;publish_later&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;class&amp;#039;, &amp;#039;ui-button ui-widget ui-corner-all head_button_clone ui-state-default ui-priority-secondary&amp;#039;);&lt;br /&gt;
			$submit2-&amp;gt;attr(&amp;#039;value&amp;#039;, &amp;#039;Publish Later&amp;#039;); // Button: save unpublished&lt;br /&gt;
&lt;br /&gt;
			// get form element save and place before&lt;br /&gt;
			$target = $form-&amp;gt;get(&amp;#039;submit_save&amp;#039;);&lt;br /&gt;
			$form-&amp;gt;insertBefore($submit2, $target);&lt;br /&gt;
&lt;br /&gt;
			$form-&amp;gt;return = $form;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	public function ___hookPageListActions(HookEvent $event) {&lt;br /&gt;
&lt;br /&gt;
		// get current page&lt;br /&gt;
		$page = $event-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// check to see if page is published&lt;br /&gt;
		$pagePub = $page-&amp;gt;is(Page::statusUnpublished);&lt;br /&gt;
&lt;br /&gt;
		// check to see if page template has deffered field&lt;br /&gt;
		$pageHasDefferField = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
		$actions = array();&lt;br /&gt;
&lt;br /&gt;
		// don&amp;#039;t get homepage or pages that are already published or are being deffered for publish&lt;br /&gt;
		if($page-&amp;gt;id &amp;gt; 1 &amp;amp;&amp;amp; $pagePub == &amp;quot;published&amp;quot; &amp;amp;&amp;amp; !is_null($pageHasDefferField) &amp;amp;&amp;amp; $page-&amp;gt;get($this-&amp;gt;checkboxName) == false) {&lt;br /&gt;
&lt;br /&gt;
			$actions[&amp;#039;publish_later&amp;#039;] = array(&lt;br /&gt;
				&amp;#039;cn&amp;#039;   =&amp;gt; &amp;#039;PublishLater&amp;#039;,&lt;br /&gt;
				&amp;#039;name&amp;#039; =&amp;gt; &amp;#039;pub later&amp;#039;,&lt;br /&gt;
				&amp;#039;url&amp;#039;  =&amp;gt; &amp;quot;{$this-&amp;gt;adminUrl}?{$this-&amp;gt;listPageName}=1&amp;amp;id={$page-&amp;gt;id}&amp;quot;,&lt;br /&gt;
			);&lt;br /&gt;
		}&lt;br /&gt;
		if(count($actions)) $event-&amp;gt;return = $actions + $event-&amp;gt;return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Publish Page on cron job&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and publishes&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publish($page) {&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusUnpublished);&lt;br /&gt;
		$page-&amp;gt;removeStatus(Page::statusHidden);&lt;br /&gt;
		$page-&amp;gt;Save();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Main publish later function&lt;br /&gt;
	*&lt;br /&gt;
	* Gets deffered page and sets fields to be checked be lazy cron&lt;br /&gt;
	*/&lt;br /&gt;
	private function ___publishLater() {&lt;br /&gt;
&lt;br /&gt;
		// get page&lt;br /&gt;
		$page = $this-&amp;gt;pages-&amp;gt;get($this-&amp;gt;pageID);&lt;br /&gt;
&lt;br /&gt;
		// set output formatting to false&lt;br /&gt;
		$page-&amp;gt;of(false);&lt;br /&gt;
&lt;br /&gt;
		//  set field time to settings default time and checkbox to true&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;checkboxName, true);&lt;br /&gt;
		$page-&amp;gt;set($this-&amp;gt;fieldName, $this-&amp;gt;defaultTime);&lt;br /&gt;
&lt;br /&gt;
		// save page&lt;br /&gt;
		$page-&amp;gt;save();&lt;br /&gt;
		$page-&amp;gt;of(true);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Lazy Cron hook function&lt;br /&gt;
	*&lt;br /&gt;
	* Triggers every [set time interval] and checks pages&lt;br /&gt;
	* Publishes page if time runout&lt;br /&gt;
	*&lt;br /&gt;
	* Adds publish page log&lt;br /&gt;
	*/&lt;br /&gt;
	public function ___publishDefferedPages(HookEvent $e){&lt;br /&gt;
&lt;br /&gt;
		// seconds since last lazycron&lt;br /&gt;
		$seconds = $e-&amp;gt;arguments[0];&lt;br /&gt;
&lt;br /&gt;
		// find all pages with deffered field&lt;br /&gt;
		$defferedPages = $this-&amp;gt;pages-&amp;gt;find(&amp;quot;{$this-&amp;gt;checkboxName}=1,include=unpublished&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		// for each page decrease time for deffered field&lt;br /&gt;
		foreach ($defferedPages as $page) {&lt;br /&gt;
&lt;br /&gt;
			// get current page time&lt;br /&gt;
			$timeTillPublish = $page-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
&lt;br /&gt;
			// set time to time minus time past&lt;br /&gt;
			$timeLeft = $timeTillPublish - $seconds;&lt;br /&gt;
&lt;br /&gt;
			// if time passed 0 or less then publish page&lt;br /&gt;
			$page-&amp;gt;of(false);&lt;br /&gt;
			if($timeLeft &amp;lt;= 0){&lt;br /&gt;
				// remove flags and save&lt;br /&gt;
				$this-&amp;gt;publish($page);&lt;br /&gt;
&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, 0);&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;checkboxName, 0);&lt;br /&gt;
&lt;br /&gt;
				// log a page has been published&lt;br /&gt;
				$log = wire(&amp;#039;log&amp;#039;);&lt;br /&gt;
				$log-&amp;gt;message(&amp;#039;CRON:&amp;#039;. $seconds .&amp;#039; Pages: &amp;#039;. $page-&amp;gt;name .&amp;#039; published&amp;#039;);&lt;br /&gt;
			}else{&lt;br /&gt;
				$page-&amp;gt;set($this-&amp;gt;fieldName, $timeLeft);&lt;br /&gt;
			}&lt;br /&gt;
			// save page time&lt;br /&gt;
			$page-&amp;gt;Save();&lt;br /&gt;
			$page-&amp;gt;of(true);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Install new module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function installFields(){&lt;br /&gt;
&lt;br /&gt;
		// install pub later checkbox field&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$f = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		if($f){&lt;br /&gt;
&lt;br /&gt;
			// if field already found then don&amp;#039;t try and make it&lt;br /&gt;
			$this-&amp;gt;message($this-&amp;gt;fieldName . &amp;#039; field found&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
		}else{&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store crontime&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeInteger&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;fieldName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Time Left&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = $this-&amp;gt;defaultTime;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
&lt;br /&gt;
			// create new field object to store whether to publish or not&lt;br /&gt;
			$f = new Field();&lt;br /&gt;
			// get a field type&lt;br /&gt;
			$f-&amp;gt;type = $this-&amp;gt;modules-&amp;gt;get(&amp;quot;FieldtypeCheckbox&amp;quot;);&lt;br /&gt;
			$f-&amp;gt;name = $this-&amp;gt;checkboxName;&lt;br /&gt;
			$f-&amp;gt;label = &amp;#039;Publish Page later&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;defaultValue = false;&lt;br /&gt;
			$f-&amp;gt;columnWidth = 50;&lt;br /&gt;
			$f-&amp;gt;icon = &amp;#039;clock-o&amp;#039;;&lt;br /&gt;
			$f-&amp;gt;collapsed = 1;&lt;br /&gt;
			$f-&amp;gt;flags = 4;&lt;br /&gt;
&lt;br /&gt;
			$f-&amp;gt;save(); // save the field&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	* Uninstall module specific fields&lt;br /&gt;
	*/&lt;br /&gt;
	private function uninstallFields(){&lt;br /&gt;
&lt;br /&gt;
		// find installed fields&lt;br /&gt;
		$fInt = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;fieldName);&lt;br /&gt;
		$fCheck = $this-&amp;gt;fields-&amp;gt;get($this-&amp;gt;checkboxName);&lt;br /&gt;
&lt;br /&gt;
		if($fInt &amp;amp;&amp;amp; $fCheck){&lt;br /&gt;
			$fieldIntUsed = $fInt-&amp;gt;numFieldgroups();&lt;br /&gt;
			$fieldCheckUsed = $fCheck-&amp;gt;numFieldgroups();&lt;br /&gt;
&lt;br /&gt;
			if($fieldIntUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fInt-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fInt);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if($fieldCheckUsed){&lt;br /&gt;
				// field in use by template&lt;br /&gt;
				$this-&amp;gt;message(&amp;#039;Unable to uninstall field &amp;#039;.$fCheck-&amp;gt;name);&lt;br /&gt;
			}else{&lt;br /&gt;
				// delete installed fields&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;delete($fCheck);&lt;br /&gt;
				$this-&amp;gt;fields-&amp;gt;save;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Inputfield / Fieldtype ===&lt;br /&gt;
[[ProcessWire - Fieldtype erstellen (Module)]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=Processwire_-_Writing_Modules&amp;diff=24235</id>
		<title>Processwire - Writing Modules</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=Processwire_-_Writing_Modules&amp;diff=24235"/>
		<updated>2020-01-04T17:00:02Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Siehe&lt;br /&gt;
 [[ProcessWire - Module schreiben]]&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Scheduler&amp;diff=24234</id>
		<title>ProcessWire - Scheduler</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Scheduler&amp;diff=24234"/>
		<updated>2020-01-04T16:45:53Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Cronjobs / Scheduler für ProcessWire&lt;br /&gt;
&lt;br /&gt;
== Module ==&lt;br /&gt;
=== LazyCron ===&lt;br /&gt;
 https://processwire.com/talk/topic/5403-cron-job-for-processwire-mailer-template/&lt;br /&gt;
 https://processwire.com/docs/more/lazy-cron/&lt;br /&gt;
&lt;br /&gt;
== Tipps im Zusammenhang ==&lt;br /&gt;
=== Seitenaufruf triggern ===&lt;br /&gt;
Man kann Seiten so bauen, dass sie beim Aufruf Aktionen ausführen. Um die Seitenaufrufe ohne Browser durchzuführen kann man so etwas machen:&lt;br /&gt;
&lt;br /&gt;
Ah, ok. You may also try to create a simple php script somewhere outside of pw with this content:&lt;br /&gt;
&lt;br /&gt;
 header(&amp;quot;location: http://www.your-site.com/page-url-with-mail-script/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
or with this one:&lt;br /&gt;
&lt;br /&gt;
 file_get_contents(&amp;quot;http://www.your-site.com/page-url-with-mail-script/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
and call that with your cronjob.&lt;br /&gt;
&lt;br /&gt;
== Tasker ==&lt;br /&gt;
Modul für lang andauernde Jobs&lt;br /&gt;
 https://modules.processwire.com/modules/tasker/&lt;br /&gt;
 https://github.com/mtwebit/Tasker/wiki/How-to-use-Tasker&lt;br /&gt;
 https://processwire.com/talk/topic/17708-tasker/&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=Linux_-_Cronjobs&amp;diff=24233</id>
		<title>Linux - Cronjobs</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=Linux_-_Cronjobs&amp;diff=24233"/>
		<updated>2020-01-04T16:42:20Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Links ==&lt;br /&gt;
http://www.debian-administration.org/articles/56&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cronjobs / Crontab ==&lt;br /&gt;
&lt;br /&gt;
Cronjobs sind Aufgaben, die regelmäßig zu einer bestimmten Zeit ausgeführt werden.&lt;br /&gt;
Das können z.B. Serverstatistiken sein, die man jede Nacht generieren möchte.&lt;br /&gt;
&lt;br /&gt;
Anzeigen kann man die Liste der Cronjobs, des aktuellen Benutzers, mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
 crontab -l&lt;br /&gt;
&lt;br /&gt;
Alle Cronjobs aller Benutzer anzeigen geht mit einem Trick:&lt;br /&gt;
 for user in $(cut -f1 -d: /etc/passwd); do echo $user; crontab -u $user -l; &lt;br /&gt;
 done&lt;br /&gt;
&lt;br /&gt;
Editieren kann man die Liste mit:&lt;br /&gt;
&lt;br /&gt;
 crontab -e&lt;br /&gt;
&lt;br /&gt;
Nun wird die Liste der Cronjobs mit dem Standardeditor (in der Regel vim) geöffnet.&lt;br /&gt;
&lt;br /&gt;
Hier stehen nun Zeilen dirn, die in etwa, wie die folgende aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0 */6 * * * /usr/local/confixx/runwebalizer.sh 2&amp;gt;/dev/null &amp;gt;/dev/null&lt;br /&gt;
0 20 * * * /srv/scripts/backup.sh 2&amp;gt;&amp;amp;1 | /usr/global/bin/cmail -s &amp;quot;Mail-Subject&amp;quot; mail@admin.de&lt;br /&gt;
1 3 * * * /srv/scripts/backup.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ersten 5 Stellen sind Zahlen, die durch Leerzeichen, oder Tabs getrennt werden dürfen.&lt;br /&gt;
Dabei stehen die Zahlen der Reihenfolge nach für:&lt;br /&gt;
&lt;br /&gt;
Die Minute zu der das Script ausgeführt werden soll (0-59),&lt;br /&gt;
&lt;br /&gt;
Die Stunde (0-23),&lt;br /&gt;
&lt;br /&gt;
Den Tag (1-31),&lt;br /&gt;
&lt;br /&gt;
Den Monat (1-12),&lt;br /&gt;
&lt;br /&gt;
Den Wochentag (0-6 wobei 0=Sonntag).&lt;br /&gt;
&lt;br /&gt;
Ein * steht jeweils für &amp;#039;jeden&amp;#039;.&lt;br /&gt;
 1 3 * * * &lt;br /&gt;
&lt;br /&gt;
würde also bedeuten: Jeden Tag um 3.01 Uhr&lt;br /&gt;
 */6 &lt;br /&gt;
&lt;br /&gt;
bedeutet: &amp;quot;Wenn die Zahl durch 6 teilbar ist&amp;quot;, also alle 6 Stunden/Minute, ...&lt;br /&gt;
 0 */6 * * * &lt;br /&gt;
&lt;br /&gt;
würde bedeuten jede 6 Stunden und 0 Minuten, also jeden Tag um 0.00, um 6.00, um 12.00 und um 18.00.&lt;br /&gt;
&lt;br /&gt;
Man kann mehrere Zahlen auch durch Komata trennen:&lt;br /&gt;
 7 1,2,5 * * * würde jeden Tag um 1:07, um 2:07 und um 5:07 ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Auch - (bis) ist Möglich&lt;br /&gt;
 0 0 * * 1-5 würde jeden Wochentag um 0.00 ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Nach dieser Zeitangabe folgt das Komando, dass zu den zuvor festgelegten Zeiten ausgeführt werden soll.&lt;br /&gt;
z.B.: Das Script /srv/scripts/backup.sh aufrufen, welches z.B. wichtige Dateien sichert. &lt;br /&gt;
&lt;br /&gt;
Quelle: http://www.orgapage.net/pages/server/linux/cronjobs.php (1.10.2009)&lt;br /&gt;
&lt;br /&gt;
== Cronjobs auf all-inkl Server ==&lt;br /&gt;
Manche Provider erlauben keinen direkten Zugriff auf Crontab. All-inkl erlaubt seit Mitte 2010  das zeitgesteuerte Ausführen von php Skripten.&lt;br /&gt;
 Tools -&amp;gt; Cronjobs -&amp;gt; Cronjobs &lt;br /&gt;
Neuen Cronjob anlegen und dann die auszuführende Datei angeben (vollständige url)&lt;br /&gt;
&lt;br /&gt;
=== Geschütztes cron Verzeichnis ===&lt;br /&gt;
Die Skripte kann man z.B. in ein Webverzeichnis cron/ legen, und dieses dann über .htaccess schützen. &lt;br /&gt;
=== Endung phpx ===&lt;br /&gt;
Skripte benennt man mit der Endung phpx das umgeht (ungetestet) die Zeitbeschränkung von php. Eventuell kann auch das Ausführen über exec das Timeout Problem umgehen.&lt;br /&gt;
&lt;br /&gt;
=== Aufruf über exec ===&lt;br /&gt;
Der php Interpreter kann auch direkt über das exec Kommando aufgerufen werden. Siehe Typo3 Beispiel.&lt;br /&gt;
&lt;br /&gt;
=== Kontroll E-Mail ===&lt;br /&gt;
Ausgaben des Skriptes werden in eine Mail geschrieben, wenn eine Kontroll-Adresse angegeben ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Cronjob für Direct Mail Versand in Typo3 auf all-inkl Server ===&lt;br /&gt;
Wenn PHP so konfiguriert ist, daß man das exec (oder auch  einige andere) Kommandos ausführen kann, so ist es möglich dennoch Cronjobs auszuführen. Auch das Anfahren des Command Line Interface in Typo3 ist darüber möglich.&lt;br /&gt;
&lt;br /&gt;
Eine Anfrage bei all-inkl ergab folgendes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Zitat Anfang&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...direkte Shell Befehle können Sie im KAS nicht eingeben. Bitte erstellen Sie sich dazu ein PHP Script mit der &amp;#039;&amp;#039;&amp;#039;Endung .phpx&amp;#039;&amp;#039;&amp;#039; und folgendem Inhalt:...&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;?php&lt;br /&gt;
    exec(&amp;quot;php /www/htdocs/w00c43b9/typo3/cli_dispatch.phpsh direct_mail masssend&amp;quot;,$ausgabe);&lt;br /&gt;
    echo &amp;quot;&amp;amp;lt;pre&amp;amp;gt;&amp;quot;;&lt;br /&gt;
    print_r($ausgabe);&lt;br /&gt;
    echo &amp;quot;&amp;amp;lt;/pre&amp;amp;gt;&amp;quot;;&lt;br /&gt;
 ?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Geben Sie dann die HTTP Adresse zu diesem Script im KAS an. Den gewünschten Zeitintervall für die Ausführung können Sie dann auch im KAS mit angeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Zitat Ende&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den absoluten Pfad zum Skript bekommt man über die Funktion phpinfo. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Beispiel mit Geschützter phpx Datei ===&lt;br /&gt;
Man sollte das Skript evtl. vor dem Zugriff von außen schützen. Dazu kann man ein Verzeichnis (z.B. cron) anlegen und dies über htaccess (oder direkt im all-inkl kas mit Passwort schützen)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Server sind scheinbar so konfiguriert, daß ein Skript mit der Endung phpx direkt über den statischen php-interpreter ausgeführt wird. Damit sollten auch Timeout Probleme kein Problem darstellen.&lt;br /&gt;
&lt;br /&gt;
== Cronjobs auf dem Mac ==&lt;br /&gt;
 http://www.apfelwiki.de/Main/CRON&lt;br /&gt;
Seit Mac OS X 10.4 werden die periodic-Skripte nicht mehr über cron, sondern mittels launchd ausgeführt.&lt;br /&gt;
 http://www.apfelwiki.de/Main/Launchd&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire&amp;diff=24232</id>
		<title>ProcessWire</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire&amp;diff=24232"/>
		<updated>2020-01-04T16:10:25Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ProcessWire CMS ==&lt;br /&gt;
== Einsatz ==&lt;br /&gt;
* bei gehobenen Anforderungen an eine exzellente Programmierbarkeit, optimierte Datenverwaltung und Schnelligkeit&lt;br /&gt;
* Sparsamer Code im Frontend (Verzicht auf Pagebuilder: man kann genau steuern, welcher HTML-Code ausgegeben wird)&lt;br /&gt;
* wenn Sicherheit des Gesamtsystems eine sehr hohe Priorität hat&lt;br /&gt;
* bei speziellen Wünschen an Funktion und Design unter Inkaufnahme eines gewissen Programmieraufwands&lt;br /&gt;
* bei größeren Editoren-Teams mit wechselnden Mitarbeitern (Sie profitieren von geringen Einarbeitungszeiten und einer guten Beschränkung und Kontrolle aller Eingaben)&lt;br /&gt;
&lt;br /&gt;
== Argumente ==&lt;br /&gt;
=== Für Entwickler ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flexibilität&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
ProcessWire wird genau so programmiert, wie es das Konzept und gewünschte Layout vorsieht. Der Einsatz von komplexen Templates von Drittanbietern, die den Rahmen des gestalterisch Möglichen vorgeben, ist nicht notwendig. ProcessWire ist nicht nur ein CMS, sondern eine kleine, moderne und sehr elegante Programmierumgebung (PHP-Framework). Damit kann man benötigte Elemente gut selbst programmieren (100% genau so wie man es braucht) und ist nicht auf vorgefertigte Modulen und deren Möglichkeiten beschränkt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Schnell&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Das schlanke Programmdesign und übersichtliche Datenbankdesign ermöglichen schnell ladende und schlanke Webseiten ohne Code-Ballast. Das moderne Cachesystem (Zwischenspeicher) bringt weitere Geschwindigkeit.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sicherheit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Soweit uns bekannt ist, wurde ProcessWire bisher noch nie erfolgreich gehackt. (100% Schutz gibt es im Internet allerdings nie.)&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;International&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Beherrscht eine echte Mehrsprachigkeit im Frontend perfekt out-of-the-box. Einfacher und übersichtlicher geht’s nicht.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Skalierbarkeit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Auch sehr große Systeme mit vielen Daten und mehreren Domains sind für ProcessWire kein Problem. Beispiele aus der Praxis belegen uns dies eindrucksvoll.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Gute Programmierbarkeit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Das System (Core) hinter ProcessWire ist durchdacht, anpassbar und ausgefeilt. Programmieraufgaben in Teams werden erleichtert und die Entwicklungszeiten verkürzt. Das System ist wenig komplex, einfach anzupassen und zu warten, also sicher und aktuell zu halten.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Datenverwaltung&amp;#039;&amp;#039;&amp;#039; &lt;br /&gt;
&lt;br /&gt;
Eine ProcessWire Seite kann beliebig viele Inhaltsbereiche (Datenfelder) mit unterschiedlichen Eigenschaften haben. Es kann genau festgelegt werden, welche Inhalte diese haben dürfen, wo und wie diese verwendet und dargestellt werden. Das Endresultat ist damit sehr gut plan- und steuerbar.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Contentausgabe&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Die mögliche softwaregestützte Datenaufbereitung ist eine der großen Stärken dieses Systems. Bis ins kleinste Detail kann programmiert werden, wie Menus und Inhalte automatisiert oder manuell zusammengestellt, gefiltert, sortiert, ausgegeben und dargestellt werden, welche SEO-Features man benötigt und wie diese eingesetzt werden.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Dokumentation&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Vieles in ProcessWire ist nach einer sehr kurzen Einarbeitungszeit selbsterklärend, besonders, wenn man das von jQuery übernommene Programmierprinzip bereits kennt. In einem, von einer sehr freundlichen, hilfsbereiten Community betriebenem Internetforum gibt es zu fast allen Fragen versierte Anwtorten. Es ist immer wieder verblüffend, was andere User mit ProcessWire machen und wie sie arbeiten.&lt;br /&gt;
&lt;br /&gt;
=== Für Designer und Kunden ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Designfreiheit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Unabhängigkeit von vorgefertigten Design-Templates und -Add-ons und deren Möglichkeiten und Einschränkungen. Der Designer hat die volle Kontrolle über wirklich alle Aspekte des Layouts. Das Template-System ist einfach anzuwenden und bleibt dadurch übersichtlich: Designaufaben können präzise gelöst werden. Wollen das tolle neue Design-Feature, was Sie irgendwo gesehen haben? Das lässt sich einbauen, ohne auf Fremdmodule warten zu müssen.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;International&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Mehrsprachige Websites sind sehr einfach zu verwalten.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Rechtevergabe&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Rechte können sehr genau eingestellt werden. Es können sogar Benutzerrechte für einzelne Felder festlegt werden und dort, welche Art von Inhalten erlaubt ist oder welche enthaltene Sprache bearbeitet werden darf. Das schützt die Software wirkungsvoll for Falscheingaben und ist besonders in Teams mit wechselnden oder ungeschulten Mitarbeitern sehr hilfreich.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Perfekte Verwaltung&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Der Admin-Bereich kann anwendungsbezogen nach den Bedürfnissen der Redakteure genau geplant und aufgebaut werden und damit auch komplexe Sachverhalte einfach und übersichtlich verwaltbar machen. Eingabebereiche sind meist selbsterklärend und genaue Vorgaben für die einzelnen Bereiche schützen den Bearbeiter. Er muss keine Angst haben, Layouts durch Fehleingaben zu zerschießen.&lt;br /&gt;
    &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Pflegeleicht&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Geringe Wartungskosten. Auch wenn ProcessWire als ein sehr sicheres CMS gilt, ist es ratsam, die Software, Module und Templates aktuell zu halten und auch gute Backups nicht zu vergessen. Hierfür bieten wir günstige Service-Verträge.&lt;br /&gt;
&lt;br /&gt;
== Versionshinweise ==&lt;br /&gt;
 https://processwire.com/blog/posts/pw-3.0.148-master/&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_N%C3%BCtzliche_Module&amp;diff=24231</id>
		<title>ProcessWire - Nützliche Module</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_N%C3%BCtzliche_Module&amp;diff=24231"/>
		<updated>2020-01-04T16:09:38Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Bedienungshilfen / Backend */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dies ist keine vollständige Liste aber mal eine kleine Hilfe auch um Kunden Module anbieten zu können...&lt;br /&gt;
 * Cool&lt;br /&gt;
 ** Sehr Hilfreich - Killerfeatures für Kunden&lt;br /&gt;
 *** Musthave Ohne geht&amp;#039;s nicht&lt;br /&gt;
== Development ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessAdminActions&amp;#039;&amp;#039;&amp;#039; *** - Musthave bei komplexen Projekten. Kann viele Dinge als Batch erledigen und spart unendlich Zeit.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;GithubConnect&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ConnectPageFields&amp;#039;&amp;#039;&amp;#039; 2 Wege Verbindung für Datenfelder. Kein richtiges m:n, hält aber die Felder synchron&lt;br /&gt;
&lt;br /&gt;
=== Debugging ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TracyDebugger&amp;#039;&amp;#039;&amp;#039; *** - für programmierlastige Projekte das Debugger Tool der Wahl&lt;br /&gt;
&lt;br /&gt;
=== Daten Im- / Exportieren ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImportPagesCSV&amp;#039;&amp;#039;&amp;#039; *** - Importiert CSV Daten als Seiten in ProcessWire&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TableCsvImportExport&amp;#039;&amp;#039;&amp;#039; - Importiert CSV Daten in Table Fields&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;[[DataSet - ProcessWire Modul|DataSet]]&amp;#039;&amp;#039;&amp;#039; - Importiert CSV und XML Daten. Config-Datei für Anweisungen. Kann mit Tasker auch als Cronjob laufen. Für große Datenssätze. https://github.com/mtwebit/DataSet&lt;br /&gt;
&lt;br /&gt;
== Administration / Verwaltung ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminRestrictBranch&amp;#039;&amp;#039;&amp;#039; - Redakteure auf einen Seitenzweig beschränken&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ModulesManager&amp;#039;&amp;#039;&amp;#039; - Zugriff auf das offizielle Modulverzeichnis aus dem Backend heraus&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageReferencesTab&amp;#039;&amp;#039;&amp;#039; - Seitenverwaltung &amp;quot;Welche Seiten verweisen auf diese mittels PageReference Field&amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;EmailNewUser&amp;#039;&amp;#039;&amp;#039; - User über seinen Account Informieren. Nutzen mit &amp;#039;&amp;#039;&amp;#039;PasswordForceChange&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessWireUpgrade&amp;#039;&amp;#039;&amp;#039; ** - Informiert über Upgrades - nützlich wenn man Updates als kostnpfl. Service anbieten möchte&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessJumplinks&amp;#039;&amp;#039;&amp;#039; * - Redirects verwalten&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageRenameOptions&amp;#039;&amp;#039;&amp;#039; - Ändert den Seitennamen bei Änderung des Titels. Am besten mit PagePathHistory verwenden wg. broken Links.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RestrictRepeaterMatrix&amp;#039;&amp;#039;&amp;#039; Zugriff auf Repeater Matrix Felder regulieren [[RestrictRepeaterMatrix (ProcessWire Modul)]]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TemplatesChildPages&amp;#039;&amp;#039;&amp;#039; Zugriff auf Kindseiten einschränken - erweitert Zugriffsregeln auf Seitenbasis, statt nur auf Templatebasis&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CronjobDatabaseBackup&amp;#039;&amp;#039;&amp;#039; Automatische Datenbank Backups - nutzt LazyCron&lt;br /&gt;
&lt;br /&gt;
== Navigation / Menüs ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessMenuBuilder&amp;#039;&amp;#039;&amp;#039; - Manuelle Custom Menu Erstellung - sinnvoll bei sehr individuellen Navigationen. &lt;br /&gt;
&lt;br /&gt;
== Hilfe für Redakteure ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AssistedURL&amp;#039;&amp;#039;&amp;#039; - Linkhilfe Adrians Fork ist oft weiter (https://github.com/adrianbj/processwire-fieldtype-assisted-url)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PrevNextTabs&amp;#039;&amp;#039;&amp;#039; - Abkürzung zur nächsten / vorigen Seite im Backend. Sinnvoll wenn viele Seiten bearbeitet werden müssen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CkeLinkFiles&amp;#039;&amp;#039;&amp;#039; - Dateilinks leichter im Editor einfügen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SchedulePages&amp;#039;&amp;#039;&amp;#039; - Seiten auf Termin veröffentlichen&lt;br /&gt;
&lt;br /&gt;
== Sicherheit und Recht ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;EmailObfuscation&amp;#039;&amp;#039;&amp;#039; **&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupCookieConsent&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CookieManagementBanner&amp;#039;&amp;#039;&amp;#039; - von Adrian&lt;br /&gt;
&lt;br /&gt;
== Bedienungshilfen / Backend / Fieldtypes==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AssistedUrl&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MinimalFieldset&amp;#039;&amp;#039;&amp;#039; - läßt flexiblere Gestaltung der Felder im Backend zu.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageListSelectMultipleQuickly&amp;#039;&amp;#039;&amp;#039; Mehrere Seiten auwählen ohne jedesmal den Baum neu zu öffnen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageFieldInfo&amp;#039;&amp;#039;&amp;#039; - zusätzliche Infos bei Page Select Checkboxen anzeigen (bei hover und wenn gecheckt)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypeTime&amp;#039;&amp;#039;&amp;#039; - Feld zum Speichern von Zeit und Zeitintervallen, Es gibt noch ein weiteres schickes Zeitfeld mit Eingabehilfe (Genbänkle oder Yoga Namaste ? - TODO)&lt;br /&gt;
&lt;br /&gt;
== Markup etc. erzeugen ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Pages2Pdf&amp;#039;&amp;#039;&amp;#039; - Wrapper für mPdf Library zur PDF Erzeugung&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RockPdf&amp;#039;&amp;#039;&amp;#039; - Wrapper für mPdf Library von Bernhard Baumrock (https://github.com/BernhardBaumrock/RockPDF, https://modules.processwire.com/modules/rock-pdf/, https://processwire.com/talk/topic/19468-360%C2%B0-feedbacksurvey-tool/)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterHannaCode&amp;#039;&amp;#039;&amp;#039; ** - Snippets für den Editor erstellen, die dann komplexeren Markup erzeugen können (Killer für Wordpress-Lovers) &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterAutoAnchor&amp;#039;&amp;#039;&amp;#039; - Erzeugt automatisch Ids und Slugs in den Überschrift Elementen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterSoundmanager&amp;#039;&amp;#039;&amp;#039; - Formatter für den Soundmanager2 Player - kann auch Multitrack und krasse Visualisierungen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePhone&amp;#039;&amp;#039;&amp;#039; - Standard Telefonformatierung (https://github.com/adrianbj/FieldtypePhone)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterFileInfo&amp;#039;&amp;#039;&amp;#039; - Dateiinfo Textformatter&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;InputfieldMarkupCKEditor&amp;#039;&amp;#039;&amp;#039; - Text in Formbuilder Formularen einfügen&lt;br /&gt;
&lt;br /&gt;
== Social Functionality Community Stuff ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;NewsletterSubscription&amp;#039;&amp;#039;&amp;#039; - Double Opt In Funktionalität für Newsletter etc.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LoginRegister&amp;#039;&amp;#039;&amp;#039; - Ryans Self Registration Beispiel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Mal Testen ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessCustomUploadNames&amp;#039;&amp;#039;&amp;#039; - Renaming Rulse for Pages and other fields.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessBlog&amp;#039;&amp;#039;&amp;#039; - Blog Modul von Ryan (für das Blog Profile entwickelt)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupSocialShareButtons&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupPlyrMediaPlayer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;InstagramFeed&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MediaLibrary&amp;#039;&amp;#039;&amp;#039; - Medienzugriff über Libraries. Auswahl in übergeordneten Seiten möglich.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FrontEndEditLightbox&amp;#039;&amp;#039;&amp;#039; - Frontend Editing der aktuellen Seite in einer Lightbox&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sassify&amp;#039;&amp;#039;&amp;#039; - Kompiliert Sass/SCSS/Compass - evtl. praktisch während der Entwicklungsphase&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Oauth2Login&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupSitemap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blackhole&amp;#039;&amp;#039;&amp;#039; - Lockt Bots die robots.txt missachten in ein Blackhole -&amp;gt; werden gebannt&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminThemeBoss&amp;#039;&amp;#039;&amp;#039; - Platzsparendes zurückhaltendes uikit 3 Theme. Setzt auf dem Standard Theme auf&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MillSite&amp;#039;&amp;#039;&amp;#039; - Milligram SiteProfile mit einigen Zusatzfeatures&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MillBlog&amp;#039;&amp;#039;&amp;#039; - Milligram Framework Blog SiteProfile&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SeoMaestro&amp;#039;&amp;#039;&amp;#039; - SEO Tool noch Alpha (2019-02)&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_N%C3%BCtzliche_Module&amp;diff=24230</id>
		<title>ProcessWire - Nützliche Module</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_N%C3%BCtzliche_Module&amp;diff=24230"/>
		<updated>2020-01-04T15:55:37Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Administration / Verwaltung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dies ist keine vollständige Liste aber mal eine kleine Hilfe auch um Kunden Module anbieten zu können...&lt;br /&gt;
 * Cool&lt;br /&gt;
 ** Sehr Hilfreich - Killerfeatures für Kunden&lt;br /&gt;
 *** Musthave Ohne geht&amp;#039;s nicht&lt;br /&gt;
== Development ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessAdminActions&amp;#039;&amp;#039;&amp;#039; *** - Musthave bei komplexen Projekten. Kann viele Dinge als Batch erledigen und spart unendlich Zeit.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;GithubConnect&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ConnectPageFields&amp;#039;&amp;#039;&amp;#039; 2 Wege Verbindung für Datenfelder. Kein richtiges m:n, hält aber die Felder synchron&lt;br /&gt;
&lt;br /&gt;
=== Debugging ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TracyDebugger&amp;#039;&amp;#039;&amp;#039; *** - für programmierlastige Projekte das Debugger Tool der Wahl&lt;br /&gt;
&lt;br /&gt;
=== Daten Im- / Exportieren ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImportPagesCSV&amp;#039;&amp;#039;&amp;#039; *** - Importiert CSV Daten als Seiten in ProcessWire&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TableCsvImportExport&amp;#039;&amp;#039;&amp;#039; - Importiert CSV Daten in Table Fields&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;[[DataSet - ProcessWire Modul|DataSet]]&amp;#039;&amp;#039;&amp;#039; - Importiert CSV und XML Daten. Config-Datei für Anweisungen. Kann mit Tasker auch als Cronjob laufen. Für große Datenssätze. https://github.com/mtwebit/DataSet&lt;br /&gt;
&lt;br /&gt;
== Administration / Verwaltung ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminRestrictBranch&amp;#039;&amp;#039;&amp;#039; - Redakteure auf einen Seitenzweig beschränken&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ModulesManager&amp;#039;&amp;#039;&amp;#039; - Zugriff auf das offizielle Modulverzeichnis aus dem Backend heraus&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageReferencesTab&amp;#039;&amp;#039;&amp;#039; - Seitenverwaltung &amp;quot;Welche Seiten verweisen auf diese mittels PageReference Field&amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;EmailNewUser&amp;#039;&amp;#039;&amp;#039; - User über seinen Account Informieren. Nutzen mit &amp;#039;&amp;#039;&amp;#039;PasswordForceChange&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessWireUpgrade&amp;#039;&amp;#039;&amp;#039; ** - Informiert über Upgrades - nützlich wenn man Updates als kostnpfl. Service anbieten möchte&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessJumplinks&amp;#039;&amp;#039;&amp;#039; * - Redirects verwalten&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageRenameOptions&amp;#039;&amp;#039;&amp;#039; - Ändert den Seitennamen bei Änderung des Titels. Am besten mit PagePathHistory verwenden wg. broken Links.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RestrictRepeaterMatrix&amp;#039;&amp;#039;&amp;#039; Zugriff auf Repeater Matrix Felder regulieren [[RestrictRepeaterMatrix (ProcessWire Modul)]]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TemplatesChildPages&amp;#039;&amp;#039;&amp;#039; Zugriff auf Kindseiten einschränken - erweitert Zugriffsregeln auf Seitenbasis, statt nur auf Templatebasis&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CronjobDatabaseBackup&amp;#039;&amp;#039;&amp;#039; Automatische Datenbank Backups - nutzt LazyCron&lt;br /&gt;
&lt;br /&gt;
== Navigation / Menüs ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessMenuBuilder&amp;#039;&amp;#039;&amp;#039; - Manuelle Custom Menu Erstellung - sinnvoll bei sehr individuellen Navigationen. &lt;br /&gt;
&lt;br /&gt;
== Hilfe für Redakteure ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AssistedURL&amp;#039;&amp;#039;&amp;#039; - Linkhilfe Adrians Fork ist oft weiter (https://github.com/adrianbj/processwire-fieldtype-assisted-url)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PrevNextTabs&amp;#039;&amp;#039;&amp;#039; - Abkürzung zur nächsten / vorigen Seite im Backend. Sinnvoll wenn viele Seiten bearbeitet werden müssen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CkeLinkFiles&amp;#039;&amp;#039;&amp;#039; - Dateilinks leichter im Editor einfügen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SchedulePages&amp;#039;&amp;#039;&amp;#039; - Seiten auf Termin veröffentlichen&lt;br /&gt;
&lt;br /&gt;
== Sicherheit und Recht ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;EmailObfuscation&amp;#039;&amp;#039;&amp;#039; **&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupCookieConsent&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CookieManagementBanner&amp;#039;&amp;#039;&amp;#039; - von Adrian&lt;br /&gt;
&lt;br /&gt;
== Bedienungshilfen / Backend ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AssistedUrl&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MinimalFieldset&amp;#039;&amp;#039;&amp;#039; - läßt flexiblere Gestaltung der Felder im Backend zu.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageListSelectMultipleQuickly&amp;#039;&amp;#039;&amp;#039; Mehrere Seiten auwählen ohne jedesmal den Baum neu zu öffnen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageFieldInfo&amp;#039;&amp;#039;&amp;#039; - zusätzliche Infos bei Page Select Checkboxen anzeigen (bei hover und wenn gecheckt)&lt;br /&gt;
&lt;br /&gt;
== Markup etc. erzeugen ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Pages2Pdf&amp;#039;&amp;#039;&amp;#039; - Wrapper für mPdf Library zur PDF Erzeugung&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RockPdf&amp;#039;&amp;#039;&amp;#039; - Wrapper für mPdf Library von Bernhard Baumrock (https://github.com/BernhardBaumrock/RockPDF, https://modules.processwire.com/modules/rock-pdf/, https://processwire.com/talk/topic/19468-360%C2%B0-feedbacksurvey-tool/)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterHannaCode&amp;#039;&amp;#039;&amp;#039; ** - Snippets für den Editor erstellen, die dann komplexeren Markup erzeugen können (Killer für Wordpress-Lovers) &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterAutoAnchor&amp;#039;&amp;#039;&amp;#039; - Erzeugt automatisch Ids und Slugs in den Überschrift Elementen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterSoundmanager&amp;#039;&amp;#039;&amp;#039; - Formatter für den Soundmanager2 Player - kann auch Multitrack und krasse Visualisierungen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePhone&amp;#039;&amp;#039;&amp;#039; - Standard Telefonformatierung (https://github.com/adrianbj/FieldtypePhone)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterFileInfo&amp;#039;&amp;#039;&amp;#039; - Dateiinfo Textformatter&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;InputfieldMarkupCKEditor&amp;#039;&amp;#039;&amp;#039; - Text in Formbuilder Formularen einfügen&lt;br /&gt;
&lt;br /&gt;
== Social Functionality Community Stuff ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;NewsletterSubscription&amp;#039;&amp;#039;&amp;#039; - Double Opt In Funktionalität für Newsletter etc.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LoginRegister&amp;#039;&amp;#039;&amp;#039; - Ryans Self Registration Beispiel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Mal Testen ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessCustomUploadNames&amp;#039;&amp;#039;&amp;#039; - Renaming Rulse for Pages and other fields.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessBlog&amp;#039;&amp;#039;&amp;#039; - Blog Modul von Ryan (für das Blog Profile entwickelt)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupSocialShareButtons&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupPlyrMediaPlayer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;InstagramFeed&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MediaLibrary&amp;#039;&amp;#039;&amp;#039; - Medienzugriff über Libraries. Auswahl in übergeordneten Seiten möglich.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FrontEndEditLightbox&amp;#039;&amp;#039;&amp;#039; - Frontend Editing der aktuellen Seite in einer Lightbox&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sassify&amp;#039;&amp;#039;&amp;#039; - Kompiliert Sass/SCSS/Compass - evtl. praktisch während der Entwicklungsphase&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Oauth2Login&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupSitemap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blackhole&amp;#039;&amp;#039;&amp;#039; - Lockt Bots die robots.txt missachten in ein Blackhole -&amp;gt; werden gebannt&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminThemeBoss&amp;#039;&amp;#039;&amp;#039; - Platzsparendes zurückhaltendes uikit 3 Theme. Setzt auf dem Standard Theme auf&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MillSite&amp;#039;&amp;#039;&amp;#039; - Milligram SiteProfile mit einigen Zusatzfeatures&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MillBlog&amp;#039;&amp;#039;&amp;#039; - Milligram Framework Blog SiteProfile&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SeoMaestro&amp;#039;&amp;#039;&amp;#039; - SEO Tool noch Alpha (2019-02)&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_N%C3%BCtzliche_Module&amp;diff=24229</id>
		<title>ProcessWire - Nützliche Module</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_N%C3%BCtzliche_Module&amp;diff=24229"/>
		<updated>2020-01-04T15:43:12Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: /* Hilfe für Redakteure */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dies ist keine vollständige Liste aber mal eine kleine Hilfe auch um Kunden Module anbieten zu können...&lt;br /&gt;
 * Cool&lt;br /&gt;
 ** Sehr Hilfreich - Killerfeatures für Kunden&lt;br /&gt;
 *** Musthave Ohne geht&amp;#039;s nicht&lt;br /&gt;
== Development ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessAdminActions&amp;#039;&amp;#039;&amp;#039; *** - Musthave bei komplexen Projekten. Kann viele Dinge als Batch erledigen und spart unendlich Zeit.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;GithubConnect&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ConnectPageFields&amp;#039;&amp;#039;&amp;#039; 2 Wege Verbindung für Datenfelder. Kein richtiges m:n, hält aber die Felder synchron&lt;br /&gt;
&lt;br /&gt;
=== Debugging ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TracyDebugger&amp;#039;&amp;#039;&amp;#039; *** - für programmierlastige Projekte das Debugger Tool der Wahl&lt;br /&gt;
&lt;br /&gt;
=== Daten Im- / Exportieren ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ImportPagesCSV&amp;#039;&amp;#039;&amp;#039; *** - Importiert CSV Daten als Seiten in ProcessWire&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TableCsvImportExport&amp;#039;&amp;#039;&amp;#039; - Importiert CSV Daten in Table Fields&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;[[DataSet - ProcessWire Modul|DataSet]]&amp;#039;&amp;#039;&amp;#039; - Importiert CSV und XML Daten. Config-Datei für Anweisungen. Kann mit Tasker auch als Cronjob laufen. Für große Datenssätze. https://github.com/mtwebit/DataSet&lt;br /&gt;
&lt;br /&gt;
== Administration / Verwaltung ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminRestrictBranch&amp;#039;&amp;#039;&amp;#039; - Redakteure auf einen Seitenzweig beschränken&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ModulesManager&amp;#039;&amp;#039;&amp;#039; - Zugriff auf das offizielle Modulverzeichnis aus dem Backend heraus&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageReferencesTab&amp;#039;&amp;#039;&amp;#039; - Seitenverwaltung &amp;quot;Welche Seiten verweisen auf diese mittels PageReference Field&amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;EmailNewUser&amp;#039;&amp;#039;&amp;#039; - User über seinen Account Informieren. Nutzen mit &amp;#039;&amp;#039;&amp;#039;PasswordForceChange&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessWireUpgrade&amp;#039;&amp;#039;&amp;#039; ** - Informiert über Upgrades - nützlich wenn man Updates als kostnpfl. Service anbieten möchte&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessJumplinks&amp;#039;&amp;#039;&amp;#039; * - Redirects verwalten&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageRenameOptions&amp;#039;&amp;#039;&amp;#039; - Ändert den Seitennamen bei Änderung des Titels. Am besten mit PagePathHistory verwenden wg. broken Links.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RestrictRepeaterMatrix&amp;#039;&amp;#039;&amp;#039; Zugriff auf Repeater Matrix Felder regulieren [[RestrictRepeaterMatrix (ProcessWire Modul)]]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;	TemplatesChildPages&amp;#039;&amp;#039;&amp;#039; Zugriff auf Kindseiten einschränken - erweitert Zugriffsregeln auf Seitenbasis, statt nur auf Templatebasis&lt;br /&gt;
&lt;br /&gt;
== Navigation / Menüs ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessMenuBuilder&amp;#039;&amp;#039;&amp;#039; - Manuelle Custom Menu Erstellung - sinnvoll bei sehr individuellen Navigationen. &lt;br /&gt;
&lt;br /&gt;
== Hilfe für Redakteure ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AssistedURL&amp;#039;&amp;#039;&amp;#039; - Linkhilfe Adrians Fork ist oft weiter (https://github.com/adrianbj/processwire-fieldtype-assisted-url)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PrevNextTabs&amp;#039;&amp;#039;&amp;#039; - Abkürzung zur nächsten / vorigen Seite im Backend. Sinnvoll wenn viele Seiten bearbeitet werden müssen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CkeLinkFiles&amp;#039;&amp;#039;&amp;#039; - Dateilinks leichter im Editor einfügen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SchedulePages&amp;#039;&amp;#039;&amp;#039; - Seiten auf Termin veröffentlichen&lt;br /&gt;
&lt;br /&gt;
== Sicherheit und Recht ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;EmailObfuscation&amp;#039;&amp;#039;&amp;#039; **&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupCookieConsent&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CookieManagementBanner&amp;#039;&amp;#039;&amp;#039; - von Adrian&lt;br /&gt;
&lt;br /&gt;
== Bedienungshilfen / Backend ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AssistedUrl&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MinimalFieldset&amp;#039;&amp;#039;&amp;#039; - läßt flexiblere Gestaltung der Felder im Backend zu.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageListSelectMultipleQuickly&amp;#039;&amp;#039;&amp;#039; Mehrere Seiten auwählen ohne jedesmal den Baum neu zu öffnen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PageFieldInfo&amp;#039;&amp;#039;&amp;#039; - zusätzliche Infos bei Page Select Checkboxen anzeigen (bei hover und wenn gecheckt)&lt;br /&gt;
&lt;br /&gt;
== Markup etc. erzeugen ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Pages2Pdf&amp;#039;&amp;#039;&amp;#039; - Wrapper für mPdf Library zur PDF Erzeugung&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RockPdf&amp;#039;&amp;#039;&amp;#039; - Wrapper für mPdf Library von Bernhard Baumrock (https://github.com/BernhardBaumrock/RockPDF, https://modules.processwire.com/modules/rock-pdf/, https://processwire.com/talk/topic/19468-360%C2%B0-feedbacksurvey-tool/)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterHannaCode&amp;#039;&amp;#039;&amp;#039; ** - Snippets für den Editor erstellen, die dann komplexeren Markup erzeugen können (Killer für Wordpress-Lovers) &lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterAutoAnchor&amp;#039;&amp;#039;&amp;#039; - Erzeugt automatisch Ids und Slugs in den Überschrift Elementen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterSoundmanager&amp;#039;&amp;#039;&amp;#039; - Formatter für den Soundmanager2 Player - kann auch Multitrack und krasse Visualisierungen&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FieldtypePhone&amp;#039;&amp;#039;&amp;#039; - Standard Telefonformatierung (https://github.com/adrianbj/FieldtypePhone)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TextformatterFileInfo&amp;#039;&amp;#039;&amp;#039; - Dateiinfo Textformatter&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;InputfieldMarkupCKEditor&amp;#039;&amp;#039;&amp;#039; - Text in Formbuilder Formularen einfügen&lt;br /&gt;
&lt;br /&gt;
== Social Functionality Community Stuff ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;NewsletterSubscription&amp;#039;&amp;#039;&amp;#039; - Double Opt In Funktionalität für Newsletter etc.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LoginRegister&amp;#039;&amp;#039;&amp;#039; - Ryans Self Registration Beispiel&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Mal Testen ==&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessCustomUploadNames&amp;#039;&amp;#039;&amp;#039; - Renaming Rulse for Pages and other fields.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ProcessBlog&amp;#039;&amp;#039;&amp;#039; - Blog Modul von Ryan (für das Blog Profile entwickelt)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupSocialShareButtons&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupPlyrMediaPlayer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;InstagramFeed&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MediaLibrary&amp;#039;&amp;#039;&amp;#039; - Medienzugriff über Libraries. Auswahl in übergeordneten Seiten möglich.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FrontEndEditLightbox&amp;#039;&amp;#039;&amp;#039; - Frontend Editing der aktuellen Seite in einer Lightbox&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sassify&amp;#039;&amp;#039;&amp;#039; - Kompiliert Sass/SCSS/Compass - evtl. praktisch während der Entwicklungsphase&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Oauth2Login&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MarkupSitemap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blackhole&amp;#039;&amp;#039;&amp;#039; - Lockt Bots die robots.txt missachten in ein Blackhole -&amp;gt; werden gebannt&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AdminThemeBoss&amp;#039;&amp;#039;&amp;#039; - Platzsparendes zurückhaltendes uikit 3 Theme. Setzt auf dem Standard Theme auf&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MillSite&amp;#039;&amp;#039;&amp;#039; - Milligram SiteProfile mit einigen Zusatzfeatures&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MillBlog&amp;#039;&amp;#039;&amp;#039; - Milligram Framework Blog SiteProfile&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SeoMaestro&amp;#039;&amp;#039;&amp;#039; - SEO Tool noch Alpha (2019-02)&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
	<entry>
		<id>https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Scheduler&amp;diff=24228</id>
		<title>ProcessWire - Scheduler</title>
		<link rel="alternate" type="text/html" href="https://wiki.stephanschlegel.de/index.php?title=ProcessWire_-_Scheduler&amp;diff=24228"/>
		<updated>2020-01-04T15:20:59Z</updated>

		<summary type="html">&lt;p&gt;84.136.104.26: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Cronjobs / Scheduler für ProcessWire&lt;br /&gt;
&lt;br /&gt;
== Module ==&lt;br /&gt;
=== LazyCron ===&lt;br /&gt;
 https://processwire.com/talk/topic/5403-cron-job-for-processwire-mailer-template/&lt;br /&gt;
 https://processwire.com/docs/more/lazy-cron/&lt;br /&gt;
&lt;br /&gt;
== Tipps im Zusammenhang ==&lt;br /&gt;
=== Seitenaufruf triggern ===&lt;br /&gt;
Man kann Seiten so bauen, dass sie beim Aufruf Aktionen ausführen. Um die Seitenaufrufe ohne Browser durchzuführen kann man so etwas machen:&lt;br /&gt;
&lt;br /&gt;
Ah, ok. You may also try to create a simple php script somewhere outside of pw with this content:&lt;br /&gt;
&lt;br /&gt;
 header(&amp;quot;location: http://www.your-site.com/page-url-with-mail-script/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
or with this one:&lt;br /&gt;
&lt;br /&gt;
 file_get_contents(&amp;quot;http://www.your-site.com/page-url-with-mail-script/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
and call that with your cronjob.&lt;/div&gt;</summary>
		<author><name>84.136.104.26</name></author>
	</entry>
</feed>