CMS MODX

Zuletzt aktualisiert am 13. Juli 2023 von Lars

Einige Etomite Entwickler haben sich abgespalten und den Etomite-Core zu einem eigenen CMS weiterentwickelt. Da es von Etomite aber schon seit Jahren keine neue Version mehr gibt, kann man MODX durchaus als Etomite-Nachfolger sehen.

Ich habe meine Etomite-Seiten nun alle auf MODX Evolution umgestellt.

Update: Inzwischen läuft alles unter WordPress. Für das Archiv lasse ich diesen Artikel hier aber noch online.

In diesem Artikel findest du viele Informationen zum Content Management System MODX...

MODX Evolution - Wo bekomme ich Hilfe?

Das CMS MODX Evolution ist nicht immer über alle Zweifel erhaben. In der Vergangenheit gab es leider immer mal wieder Probleme mit Updates für diverse Sicherheitslücken. Generell scheinen die Autoren recht langsam zu reagieren.

Wo soll man ansetzen, wenn man nicht weiter weiss? Hier einige Ressourcen:

So erstellst Du eine 301 Umleitung, die für MODX kompatibel ist

Immer wenn Du die Seitenstruktur änderst und neue URLs eingeführt werden, musst Du Google und anderen Suchdienstanbietern Bescheid sagen, wo Sie die neue Seite finden. Hierzu gibt es die sog. 301 Weiterleigung. Die normalerweise angegebene Syntax kollidiert jedoch mit den Anweisungen für die sogennanten Friendly URLS, so dass die Umleitungen nicht funktionieren. Um dies zu korrigieren, kannst Du die folgende Syntax verwenden:

RewriteRule alteurl.html$ http://www.mypage.ch/neueurl.html [R=301,L]

Das ganze packst Du in die .htaccess vor die Anweisungen für die Friendly URLS.

Hardening MODX Evolution oder wie mache ich mein MODX sicherer?

Jedes Programm ist anfällig für Sicherheitslücken, die z.B. durch unsauber programmierten Code entstehen. Dies gilt insbeondere auch für das CMS, also das Content-Management-System, was hinter einer Webseite steht.

Ist die Webseite gehackt worden, helfen Dir die allgemeinen Tipps in meinem Artikel "Meine Webseite wurde gehackt, was nun?" weiter.

Bevor das Kind in den Brunnen fällt, kannst Du aber auch einiges dafür tun, Deine MODX Evolution Webseite nach aussen besser abzusichern:

Updates und Berechtigungen

  • Neuste Updates zeitnah einspielen. Am Besten in eine Newsletter eintragen, um kein Update zu verpassen.
  • Datenbank nur für localhost zugreifbar machen
  • Benutzerrechte nur des Ordners /assets/cache auf 700 setzen
  • Alle Dateien ausser index.html im Ordner /assets/cache auf 600 setzen
  • Nur Ordner/assets/export auf 755 setzen
  • Nur Ordner/assets/images auf 755 setzen
  • Nur Ordner /assets/files auf 755 setzen
  • Datei /manager/includes/config.inc.php nach vollendeter Installation auf 600 setzen.
  • Nicht mehr verwendete Snippets und Plugins suchen und löschen - sowohl aus der Datenbank, als auch aus dem File-System (Verzeichnis /assets/plugins)
  • Ordnerstruktur beibehalten und möglichst keine neuen Ordner erstellen. Dies ist aber letztens eine Philosophiefrage: Die "Extremen" tendieren dazu, die Pfadstruktur von MODX komplett auseinanderzubrechen, um das Eindringen / Erkennen für Hacker noch schwerer zu machen. Das dürfte für den Standarduser aber ziemlich realitätsfremd sein.
  • Sollte es einen neuen Ordner brauchen, sollte man dieser mit einer index.html versehen werden, wie es MODX selber auch macht - siehe unten.

Standard index.html für Verzeichnisse ohne direkten Zugriff

Es gibt auch andere Wege um das Ziel zu erreichen, aber Du kannst zur Sicherheit in jedes neues Verzeichniss eine index.html legen, wie es MODX selber auch macht. Dadurch wird das Listen des Verzeichnisinhaltes verhindert.

<h2>Unauthorized access</h2>
You're not allowed to access file folder

.htaccess Magic

Mit der .htacces-Datei lässt sich noch einiges absichern. Beispielsweise lässt der folgende Code...

<FilesMatch ".php$">
Order Allow,Deny
Deny from all
</FilesMatch>
<FilesMatch "index?.php$">
Order Allow,Deny
Allow from all
</FilesMatch>
<FilesMatch "veriword?.php$">
Order Allow,Deny
Allow from all
</FilesMatch>
<FilesMatch "login.processor[0-9]?.php$">
Order Allow,Deny
Allow from all
</FilesMatch>

...nur noch den externen Aufruf von index.php und veriword.php von extern zu. Die Zeile mit dem login benötigt MODX für ein erfolgreiches Manager-Login.

Relevante Dateien von MODX überwachen lassen.

Viele Hacker versuchen daher die index.php und die .htaccess zu verändern. Dies sollte MODX mit den entsprechenden Einstellung allerdings melden:

Die Datei manager/includes/config.inc.php enthält die wichtigen Konfigurationseinstellungen.

MODX wurde trotzdem gehackt, wasn nun?

Hoster senden in der Regel eine Nachricht mit den Dateien, die infiziert sind. Blöd ist nur, dass inzwischen die meisten Hacker erst einmal ein Backdoor installieren und die seite relativ lange unkomprimitiert lassen. Es kann also sein, dass man zwar die infizierte Datei löscht, wenig später aber wieder neue auf der Seites sind. Oft hilft nur eine Neuinstallation des CMS. Aber aufgepasst, Schadcode kann auch in der Datenbank sein. Normalerweise sollte dieser mit folgender SQL-Abfrage gefunden werden können:

SELECT * FROM modx_site_plugins WHERE plugincode LIKE '%base64%'

Es gibt ein Tool namens Evocheck, was eine komprimitierte Installation überprüfen können soll. Download hier.

Einsatz aber nur für erfahrene User. Bitte die beiliegende Readme beachten! Und nicht vergessen, die evocheck.php nach dem Einsatz wieder löschen.

Ansonsten helfen dir hoffentlich die allgemeinen Tipps in meinem Artikel "Meine Webseite wurde gehackt, was nun?" weiter.

MODX beschleunigen oder die Crux mit dem Pagespeed

Pagespeed Insights von Google
Pagespeed Insights von Google

Geschwindigkeit ist angeblich ein Rankingfaktor. Sie entscheidet also mit darüber, wie weit vorne Deine Seite in den Suchergebnissen landet. Wie stark die Geschwindigkeit darauf in der Praxis Einfluss hat, darüber streiten sich die Geister bzw. SEO-Experten. Ganz sicher ist allerdings, das Google die Geschwindigkeit misst. Du selbst kannst die Messung auf der folgenden Seite aufrufen:

Pagespeed Insights

Auf der Seite erhälst Du auch Hinweise, wie Du die Leistung verbessern kannst.

Ich habe meine Seite beim Provider all-inkl.com. Mit ein paar Änderungen in der Datei .htaccess konnte ich die Leistung der Seite verbessern.

Was ist die .htaccess-Datei?

Wenn Du keinen eigenen Webserver betreibst oder gemietet hast, hast Du wahrscheinlich ein Webhosting-Angebot. Dort kannst Du eine oder mehrere Webseiten auf einem Webserver erstellen. Auf diesem Webserver befinden sich aber noch diverse andere Webseiten von verschiendenen anderen Kunden.

Wenn Du Einstellungen des Webservers für Deine Webseite(n) verändern willst, wäre es schlecht, wenn sich dadurch auch die Einstellungen der anderen Kunden ändern würden. Daher gibt es die Datei .htacccess.

Die Datei .htaccess ist eine reine Textdatei, die die Konfiguration des Verzeichnisses und aller Unterverzeichnisse beeinflusst, in der sie sich befindet. Du kannst darin Dinge, wie z.B. Verzeichnisschutz oder Umleitungen oder eben auch verschiedene Komprimierungsfunktionen für Deine Webseite(n) konfigurieren.

Je nach Webserverkonfiguration funktionieren die folgenden Einstellungen bei anderen Providern möglicherweise nicht genau so.

Diese Einstellungen habe ich in meinen MODX-Seiten:

### Compression STUFF ###
php_flag zlib.output_compression On
php_value zlib.output_compression_level 5
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 days"
ExpiresByType text/html "access plus 5 minutes"
ExpiresByType text/xml "access plus 6 hours"
ExpiresByType text/css "access plus 1 weeks"
ExpiresByType text/javascript "access plus 1 weeks"
ExpiresByType application/javascript "access plus 1 weeks"
ExpiresByType application/x-javascript "access plus 1 weeks"
ExpiresByType text/ecmascript "access plus 1 weeks"
ExpiresByType image/gif "access plus 1 years"
ExpiresByType image/png "access plus 1 years"
ExpiresByType image/jpeg "access plus 1 years"
ExpiresByType image/ico "access plus 1 years"
ExpiresByType image/icon "access plus 1 years"
ExpiresByType image/x-icon "access plus 1 years"
ExpiresByType video/x-flv "access plus 1 years"
ExpiresByType video/quicktime "access plus 1 years"
ExpiresByType application/x-shockwave-flash "access plus 1 years"
ExpiresByType application/pdf "access plus 1 years"
</IfModule>

# gzip Compression if availiable
AddEncoding gzip .gzip
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-shockwave-flash
</IfModule>

Konkret habe ich diese Zeilen ans Ende der .htaccess-Datei gesetzt.

Was bringt das?

Die Einträge aktiviern den Cache des Webservers und definieren die Ablaufzeit für bestimmte Dateien.

Nachdem ich diese Änderungen aktiviert habe, sagt mir Pagespeed Insights:

  • Mobil: 76/100
  • Desktop: 85/100

Zum Vergleich die Werte ohne diese Einträge:

  • Mobil: 69/100
  • Desktop: 74/100

Die Verbesserung kann sich also sehen lassen. Trotzdem wird noch ziemlich viel bemängelt. Allerdings muss man sich die Frage stellen, inwieweit der Zeitaufwand gerechtfertigt ist, das Ganze weiter zu verbessern. Ich habe mal diverse bekannte Seiten (z.B. heise.de, selbstaendig-im-netz.de, tagseoblog.de, tagesschau.de) in Pagespeed Insights untersucht. Im Vergleich stehe ich da nicht wirklich schlecht mit meinen Seiten da.

Blog mit CMS MODX und ditto

Ein Blog gehört zu den Standardfunktionen, die ein CMS können muss. Bei MODX
geht das mit dem Snippet "ditto".

Damit hatte ich einige Schwierigkeiten. Die Dokumentation dazu fand zumindest
ich nicht allzu verständlich. Ich versuche daher in diesem Artikel eine andere
Herangehensweise.

Hier zunächst einmal ein funktionierendes Beispiel:

Die Blog-Übersicht

		
<h1>Blog</h1>

[!Ditto? &id=`blog` &startID=`2` &summarize=`10` &removeChunk=`Comments` 
&tpl=`ditto_blog` &paginate=`1` 
&extenders=`summary,dateFilter` 
&paginateAlwaysShowLinks=`1` 
&tagData=`documentTags` 
&dateSource=`pub_date` 
&dateFormat=`%d-%m-%y %H:%M`!]
<br>
<br>

<center><h3>Zeige  [+blog_start+] -  [+blog_stop+] von  [+blog_total+] Einträgen
 [+blog_previous+]  [+blog_pages+]  [+blog_next+]</h3></center>

Nehmen wir den Ditto-Aufruf mal auseinander:

&id=`blog`

Diese ID wird verwendet, um zum Beispiel die Verbindung mit
anderen Snippets wie reflect zu erstellen.

&startID=`2`

ID des Ordner der die anzuzeigenden Einträge enthält. Mehrere Ordner
lassen sich mit Komma getrennt angeben.

&summarize=`10`

Anzahl der Posts, die auf einer Seite angezeigt werden sollen

&removeChunk=`Comments`

Name von Chunks, die nicht in den Inhalt kommen

&tpl=`ditto_blog`

Das ist der Chunk, der die Blogübersichtausgabe enthält

&paginate=`1`

Ausgabe auf mehrere Seiten aufteilen (1 Ja - 0
Nein)

&extenders=`summary,dateFilter`

Load extenders expanding functionality of Ditto? Derzeit
unklar.

&paginateAlwaysShowLinks=`1`

Bei eingeschalteter Aufteilung auf mehrere Seiten immer die
Vor/Zurück Links anzeigen?

&tagData=`documentTags` 

Field containing tagging information? Derzeit unklar.

&dateSource=`pub_date`

Source of the placeholder? Derzeit unklar.

&dateFormat=`%d-%m-%y %H:%M`]]

Datumsformat nach PHP

date()

Das Chunk "ditto_blog"

Das Chunk wird durch den Ditto-Aufruf

&tpl=`ditto_blog`

referenziert

<table><tr>
<td widht="117"> 
<a href="[~~]" title="">
<img border="0" src="http://www.supersales.ch/images/thmb/.png" width="96">
</a> 
</td>
<td>
<h4><a href="[~~]" title=""></a></h4>

<div class="blog_info"><br></div>
</td></tr></table>
</div>
<p></p>

Das Chunk zeigt die Zusammenfassung des jeweiligen Blogeintrags.

Die Blogeinträge sind normale Artikel, die alle unter der durch

&startID=`2`

angegebenen ID angelegt sind.

Ein Blog mit CMS MODX und ditto

Das ganze sieht dann so aus:

Ein Blog mit CMS MODX und ditto

So erstellt man ein Kontaktformular mit dem CMS MODX und dem Snippet eForm

Ein Kontaktformular gehört zu einer professionellen Homepage. Auch das CMS MODx Evolution bleibt hier nicht aussen vor. Hier hilft das Snippet eForm. Am Besten schauen wir uns hier ein Beispiel an:

Dort, wo das Kontaktformular erscheinen soll, rufen wir eForm auf mit:

[!eForm? &formid=`ContactForm` &subject=`Feedback auf www.schlageter.ch` 
&to=`email@semail.com`  &tpl=`ContactForm` &report=`ContactFormReport` 
&thankyou=`eFeedbackThanks` &vericode=`1` !]

Alle Parameter erkläre ich weiter unten. Wichtig sind die drei Chunks, die eingebunden werden. "ContactForm", "ContactFormReport" und "eFeedbackThanks". Sie können natürlich auch anders benannt werden, allerdings muss dann sowohl der eForm-Aufruf, als auch der Chunkname entsprechend angepasst sein.

ContactForm

Dieses Chunk definiert das Formular

[+validationmessage+]

<form method="post" action="[~[*id*]~]">
 
<fieldset>
    
    <legend>Gute Ideen werden gerne veröffentlicht...</legend>
 
 
        <input name="formid" type="hidden" value="ContactForm" />
 
        <label>Ihr Vor- und Zuname:</label><br />
        <input name="kf_name" class="text" type="text" eform="Ihr Vor- und Zuname::1" /><br />
 
        <label>Ihre E-Mail Adresse <em>(optional)</em>:</label><br />
        <input name="kf_mail" class="text" type="text" eform="Ihre E-Mail Adresse:email:0" /><br />
 
        <label>Ihre Homepage <em>(optional)</em>:</label><br />
        <input name="kf_homepage" class="text" type="text" eform="Ihre Homepage::0" /><br />
 
        <label>Ihre Nachricht:</label><br />
        <textarea name="kf_nachricht"  rows="10" cols="50" eform="Ihre Nachricht:textarea:1"></textarea><br />      
	
	Bitte geben Sie den Anti Spam Code (Captcha) in das Feld darunter ein<br />
    <img src="[+verimageurl+]" alt="verification code" border="1"/>
    
    <br />
    <label accesskey="c" for="vericode">Anti Spam Code bitte hier wiederholen: </label>
    <input type="text" name="vericode" size="20" />
    <input type="submit" name="kf_submit" class="button" value="Nachricht abschicken" />
 
    </fieldset>
 
</form><br />

Eine Select-Box könnte z.B. so aussehen

<input type="checkbox" name="kf_anmeldung[]" value="10.09.2014 Veranstaltung 
1" eform="Anmeldung::0" />30.10.2014 Veranstaltung 2<br />
<input type="checkbox" name="kf_anmeldung[]" value="30.10.2014 Veranstaltung 2" 
/>30.10.2014 Veranstaltung 2<br />

Wichtig

  • [+validationmessage+] wird mit einer Nachricht ersetzt, wenn das Feld nicht wie gewünscht aufgerufen wurde.
  • [+verimageurl+] wird mit einem Captcha ersetzt. Wenn es nicht klappt, am Besten den ganzen Absatz 1:1 übernehmen.
  • => Dieser Wert "ContactForm" wird im eForm-Aufruf mit formid referenziert.
  • Name für label, input und id sollte möglichst pro Feld immer gleich sein und muss in den folgenden Chunks entsprechend verwendet werden.
  • :1 definiert ein erforderliches Feld, :0 ein nicht erforderliches.
  • :email: definiert, dass auf eine gültige E-Mail-Adresse geprüft wird.

ContactFormReport

Dieses Formular definiert das Mail, das der Formualarempfänger per Mail erhält

Es wurde eine Nachricht über www.schlageter.ch gesendet.<br />
Folgende Informationen hat der Kunde hinterlassen:
 
<br /><br />
 
<table border="1" cellpadding="5">
<tr>
<td>Vor- und Zuname:</td>
<td>[+kf_name+]</td>
</tr>
<tr>
<td>E-Mail:</td>
<td>[+kf_mail+]</td>
</tr>
<tr>
<td>Homepage:</td>
<td>[+kf_homepage+]</td>
</tr>
<tr>
<td>Nachricht:</td>
<td>[+kf_nachricht+]</td>
</tr>
</table>
 
<br /><br />
 
Um den Kunden per E-Mail zu antworten klicken Sie hier: <a href="mailto:[+kf_mail+]?subject=Ihre Nachricht an uns">[+kf_mail+]</a>

eFeedbackThanks

Dieses Formular definiert die Meldung, die statt des Kontaktformulars bei erfolgreichem Versand angezeigt wird.

<h2>Vielen Dank für Ihre Nachricht</h2>
<p>Vielen Dank für Ihr Feedback auf www.schlageter.ch.</p>
<h3>Folgende Daten wurden übermittelt</h3>
<table  cellpadding="5">
<tr>
<td>Vor- und Zuname:</td>
<td><i>[+kf_name+]</i></td>
</tr>
<tr>
<td>E-Mail:</td>
<td><i>[+kf_mail+]</i></td>
</tr>
<tr>
<td>Homepage:</td>
<td><i>[+kf_homepage+]</i></td>
</tr>
<tr>
<td>Nachricht:</td>
<td><i>[+kf_nachricht+]</i></td>
</tr>
</table>

Die eForm-Parameter

&formid=`ContactForm`

Die hier definierte ID muss im Formular als hidden Field entsprechend wieder auftauchen.

&subject=`Feedback auf www.schlageter.ch`

Definiert das Betreff des E-Mails, das der Empfänger des Formulars enthält.

&to=`lars@schlageter.ch`

Definiert den Empfänger des Formulars.

&tpl=`ContactForm`

Definiert den Namen des Junks, das das Formular enthält.

&thankyou=`ThankYou`

Definiert den Namen des Junks, das eine benutzerdefinierte Erfolgsmeldung
auf dem Bildschirm ausgibt.

&ccsender=`1`

Bei 1 werden die Formuardaten auch an den Absender gesendet. Bei 0 nicht.

&automessage=`AutoMessage`

Definiert den Namen des Junks, das ein Feedback an den Absender mit der
E-Mail email sendet. &ccsender sollte auf 0 stehen, sonst bekommt der
Absender 2 Mails.

&report=`ContactFormReport`

Definiert den Namen des Junks, das das E-Mail beschreibt, dass die Formulardaten sendet.

&vericode=`1`

Schaltet die Verwendung von Captchas ein. Hakelt gerne. Falls was nicht funktioniert, den Abschnitt von oben 1:1 ans Formularende übernehmen:

Bitte geben Sie den Anti Spam Code (Captcha) in das Feld darunter ein<br />
    <img src="[+verimageurl+]" alt="verification code" border="1"/>
    
    <p>
		<label accesskey="c" for="vericode">Anti Spam Code bitte hier wiederholen: </label>
    <input type="text" name="vericode" size="20" />
        <input type="submit" name="kf_submit" id="kf_submit" class="button" value="Nachricht abschicken" />
 
    </fieldset>
 
</form><br />

Troubleshooting eForm für MODx

Ich habe Stunden nach der Fehlersuche verbracht, warum der Captcha-Code immer ungültig war, trotz dass ich ihn garantiert korrekt eingegeben habe. Ursache war, dass ich am Seitenende ein Kommentarformular eingebunden habe. Dies nutzt die gleiche Captcha-Funktion. Auf einer Seite darf diese ohne weitere Massnahmen nur einmal eingebaut werden.

Wie bei allen Snippets kann es Sinn machen das Snippet nochmal neu zu installieren, wenn ominöse Fehlermeldungen auftreten.

eForm extended

Bestätigungsmail an Formularausfüller

Wir möchten ein Bestätigungsmail an den Ausfüller des Formulars senden.

Hinzu kommen hier die Parameter automessage, autosender und autosendername. Automessage gibt ein Chunk an, dass die Mail definiert. Dies kann weitgehend analog zum ContactFormReport aufgebaut werden. Zu beachten gilt natürlich, dass ein böser Zeitgenosse fremde Leute anwählen kann, diese dann die Bestätigungsmail erhalten und sich dadurch belästigt fühlen.

Das Feld für E-Mail sollte hier auch email heissen.

[!eForm? &formid=`ContactForm` &subject=`Feedback auf www.schlageter.ch` 
&to=`lars@schlageter.ch`  &tpl=`ContactForm` &report=`ContactFormReport` &thankyou=`eFeedbackThanks` 
&vericode=`1` 
&automessage=`email` &autosender=`absender@kopa.ch` &AutosenderName =`formular@schlageter.ch`   
!]

Abfüllen der Formulardaten in eine Datenbank

Hierzu haben sich schon andere Gedanken gemacht. Interessant sind die zwei folgenden Links:

  • [Snippet] eForm2db 0.1BETA - Manipulate DB records via eForm and MODx DBAPI
  • Use eForm to Store data into Database

Update: Links führen leider jetzt ins Leere.

Konkret für das Beispiel sieht das so aus:

Snippet eForm2db

<?php
function eForm2db( &$fields )
{
/*--------------------------------------------------------------- 
eForm2db
Version: 0.1 [Beta 1]
Author: pixelchutes

// Bring needed resources into scope
global $modx, $table_prefix;

// Init our array
$dbTable = array(); // key = DB Column; Value = Insert/Update value

// Insert field/value pairs to insert/update in our table
$dbTable[kf_name] = $fields[kf_name];
$dbTable[kf_mail] = $fields[kf_mail];
$dbTable[kf_homepage] = $fields[kf_homepage];
$dbTable[kf_nachricht] = $fields[kf_nachricht];
$dbTable[datetime] = date( 'YmdHis', strtotime( $fields[postdate] ) ); 

// Run the db insert query
$dbQuery = $modx->db->insert( $dbTable, $table_prefix . 'formulardata' ); 

return true;
}

Vor den Aufruf des Kontaktform-Chunks muss das Snippet mit...

[!eform2db!]

...aufgerufen werden.

Im eform-Aufruf ergänzt man...

 &eFormOnBeforeMailSent=`eForm2db`

Die Formularfelder werden in ein Array dbtable gepackt. Hat man select-Felder, dann sind diese bereits in einem Array abgespeichert, und man sollte diese z.B. mit implode in einen String umwandeln.

Beispiel

 $String = implode(";", $fields[kf_anmeldung]);

Mehr Info hierzu unter PHP Array in ein String umwandeln.

Zusätzlich wird noch das aktuelle Datum in ein Feld gespeichert.

Es wird die MODX-DB verwendet. Die Tabelle, die die Formulardaten aufnimmt, legt man z.B. mittels PHPMyAdmin an. Im obigen Beispiel beginnt ihr Name mit dem allgemeinen Table Prefix, der bei der Installation von MODX definiert wurde und endet auf formulardata.

Die Tabelle muss neben den Spalten, die die Formularfelder aufnehmen, auch noch einen Index haben, der mit dem Attribut Autoinkrment versehen ist und somit automatisch eine fortlaufende Nummer erhält. Kritisch wird es, wenn ein Nutzer in ein Feld ein Hochkomma eingibt, da dann der SQL-Ausdruck ungültig wird. Dies kann man aber z.B. mittels str_replace abfangen:

$dbTable[kf_name] = $fields[kf_name];
$dbTable[kf_name] = str_replace(''', ' ', $dbTable[kf_name]);

Das Hochkomma (im PHP Code maskiert mit dem Backslash) wir hiermit einfach in ein Leerzeichen umgewandelt.

Event-Kalender mit CMS MODX und ditto

Das Ditto Snippet wird häufig für Blog bzw. Blog-ähnliche Funktionen verwendet. Es kann jedoch viel mehr. Z.B. ein Event-Kalender sein. Der folgende Inhalt ist recht stark bei MODx – How to setup an Events Page using Ditto and CALx abgekupfert (wenigstens ist die Übersetzungsleistung meine eigene ;-), das Sie sehr schön diverse Fähigkeiten von MODX, Template-Variablen und ditto zeigt.

Die Kalendereinträge sind nichts anderes als HTML-Seiten in einem Ordner. Damit diesen ein Start- und Enddatum zugewiesen werden kann, werden hierfür zwei Template-Variablen definiert: Die erste heisste EventStartDate und die zweite EventEndDate.

  • Elemente
  • Elemente-Verwaltung
  • Template-Variablen
Event-Kalender für die Homepage mit CMS MODX und ditto

Beim Erstellen der Variablen muss man vor allem darauf achten, dass diese auch den entsprechenden Templates zugewiesen werden.

Anschliessend erstellt man in einem Ordner (bei mir hier "Events Folder" mit ID 4) die entsprechenden Events. Hier brauchen wir nur den Titel, das richtige Template und müssen die Variablen passend abfüllen

Event-Kalender für die Homepage mit CMS MODX und ditto

Im nächsten Schritt erstellen wir das Chunk "chkShowEvent"

Start: [+EventStartDate:date=`%d-%b-%y %H:%M`+] End: [+EventEndDate:date=`%d-%b-%y %H:%M`+]
[+title+] 

Dieser Codeschnipsel definiert, wie der jeweilige Eintrag auszusehen hat. EventStartDate und EventEndDate verweisen auf die zuvor definierten Template-Variablen.

An die Stelle, an der der Kalender erscheinen soll, schreiben wir:

[!Ditto? &startID=`4` &tpl=`chkShowEvent` &paginate=`0` &paginateAlwaysShowLinks=`0` &orderBy=`EventStartDate ASC` &extenders=`summary,dateFilter` &dateSource=`EventStartDate` &summarize=`3` &dateFormat=`%d. %b. %y` &filter=`EventStartDate,@EVAL return strtotime("now");,3`!]

Nehmen wir den Ditto-Aufruf mal auseinander:

&startID=`2`

ID des Ordner der die anzuzeigenden Einträge enthält. Mehrere Ordner
lassen sich mit Komma getrennt angeben.

&tpl=`chkShowEvent`

Dies ist die Angabe auf das Chunk, das die Ausgabe der Kalendereinträge bestimmt.

&paginate=`0`

Keine Seitennummern auf Ergebnisseite.

&paginateAlwaysShowLinks=`0

Link auf vorige und nächste Seitenicht zeigen/p>

&orderBy=`EventStartDate ASC`

Sortierreihenfolge festlegen

&extenders=`summary,dateFilter`

Purpose: Load extenders expanding functionality of Ditto, Values: Comma-separated list of names ???

&dateSource=`EventStartDate`

Source of the placeholder ???

&summarize=`3`

Anzahl der anzuzeigenden Objekte

&dateFormat=`%d. %b. %y`

Format der Variablen

&filter=`EventStartDate,@EVAL return strtotime("now");,3`

Filter, welche Einträge angezeigt werden sollen. In dem Fall nur solche, die noch anstehen.

Beispiele:

  • Nur die zeigen, wo die Template Variable 'shapes' gleich 'square' ist: &filter = `shapes, 'square', 2`
  • Nur die zeigen, wo die Template Variable 'shapes' 'square' enthält: &filter = `shapes, 'square', 7`

Vergleichsoperatoren für Filter

5 oder <=Weniger als oder gleich

WertBedeutung
1 oder! =Nicht gleich
2 oder =Gleich
3 oder <Weniger als
4 oder >Grösser als
6 oder > =Größer oder gleich
7Enthält
8Enthält nicht

Dito Platzhalter

  • [~[+id+]~] - Create a full URL based on the ID. Does not create a link itself.
  • [+title+] - Contents of one of document title fields. There's probably an order in which it tries to find a title.
  • [+summary+] - Either introtext in full if set, or the first part of content.
  • [+content+] - Displays the content of the document.
  • [+link+] - Creates a link to the document, with the text "Read more...".
  • [+author+] - The name of the author. Based on createdby.
  • [+date+] - The date, in the set format. Set as createdon by default (editedon & pub_date are options)and follows this format string [1].
  • [+pagetitle+] - The title of the document.
  • [+longtitle+] - The longtitle of the document.
  • [+description+] - The description of the document.
  • [+introtext+] - The summary of the document.
  • [+content+] - The content of the document.
  • [+alias+] - The alias of the page. Used in creating Friendly URLs.
  • [+menutitle+] - The menu title of the document.
  • [+id+] - the integer id for the document.
  • [+parent+] - the integer of the parent
  • [+menuindex+] - the menu index (sort order) integer
  • [+contentType+] - Returns string of content type specified in manager Content Type drop down menu.
  • [+template+] - integer template id to be used with this content.
  • [+searchable+] - Returns 1 (true) or 0 (false) to designate whether this page content should be searchable.
  • [+cacheable+] - Returns 1 (true) or 0 (false) if this page should be cached. This is set to false by default so dynamic snippets function properly.
  • [+createdby+] - Returns integer id number of user who created content.
  • [+createdon+] - Date (in seconds since January 1, 1970) when the content was created.
  • [+editedby+] - Integer id number of the user who last edited the content.
  • [+editedon+] - Returns date of the last edit (in seconds since January 1, 1970).
  • [+deleted+] - Returns 1 (true) or 0 (false). When true, this document will appear in the recyling bin until the recycling bin gets emptied. At that point, the record is removed entirely from the database (REALLY deleted).
  • [+deletedon+] - Returns date of document deletion (in seconds since January 1, 1970).
  • [+next+] - next button
  • [+previous+] - previous button
  • [+splitter+] - splitter if always show is 0
  • [+pages+] - page list
  • [+totalPages+] - total number of pages
  • [+start+] - the # of the first item shown
  • [+stop+] - the # of the last item shown
  • [+current+] - the # of current page shown (in Ditto 2.x use [+currentPage+] )
  • [+total+] - the total # of pages
  • [+item[x]+] – rendered output of an individual document

Kommentarfunktion für MODX mit snippet JOT nachrüsten

Seiten müssen die Möglichkeit zur User-Interaktion bieten. Ein testhalber eingebautes Facebook-Kommentarfeld wird von meinen Besuchern aber so gut wie nie benutzt. Abhilfe schafft hier das Snippet JOT, was eine MODX eigene Kommentarfunktion ermöglicht.

Zäumen wir das Pferd von Hinten auf, hier mein JOT Aufruf:

[[JotComment? 
&pagination=`10` &captcha=`1` 
&moderated=`1` &canmoderate=`Mods` 
&badwords=`scheiss,scheis,fresse,kack,fick,link,http` &bw=`2`]]

Was bedeuten die JOT-Parameter?

&pagination=`10`

Es werden max. 10 Kommentare auf einer Seite angezeigt.

&captcha=`1`

Captchas werden verwendet

&moderated=`1`

Die Kommentarfunktion ist moderiert

&canmoderate=`Mods`

Dies ist die Gruppe, die moderieren darf. Nachzusehen bzw. zu konfigurieren
unter Sicherheit, Web-Benutzergruppen

Kommentarfunktion für MODX mit JOT
&badwords=`scheiss,scheis,fresse,kack,fick,link,http`

Dies ist der Verweis auf ein Chunk, das sog. Badwords enthält. Mit diesen
lässt sich definieren, dass bestimmte Wörter in Kommentaren gar nicht vorkommen
dürfen. Diese Funktion ist sehr nützlich, um Spammer schon recht früh
abzuwehren. Ohne hatte ich zum Teil bis zu 10 Spam-Kommentarversuche pro Tag. Im
Chunk stehne die Wörter durch Komma getrennt und klein geschrieben (z.B.
"scheiss, fresse, link"

&bw=`2`

Definiert, was passiert, wenn ein Badword gefunden wird. Bei 2 wird der
Kommentar abgewiesen. Bei 1 nicht publiziert und bei 0 wird das Auffinden
ignoriert.

Probleme bei der Implementierung von JOT

Captcha erscheint nur auf Englisch

Thomas Jakobi stellt auf seiner Website die deutsche Version zur Verfügung. Update: Leider nicht mehr online! Zu beachten ist, dass dieser Inhalt ins Verzeichnis assets/snippets/jot/templates kopiert werden muss.

Captcha in MODX JOT erscheint nicht

Seltsamerweise erschien bei mir zunächst das Captcha-Bild nicht. Nach einigem relativ hilflosem Rumgesuche stellte ich fest, dass der Pfad nicht stimmt.

Kommentarfunktion für MODX mit JOT

Er muss veriword.php im Pad manager/includes aufrufen. Bei mir war dieser Pfad falsch. Hier suchte ich sehr lange rum, schliesslich lud ich das Snippet hier frisch herunter und alles funktionierte perfekt.

Schuld ist die Datei...

/assets/snippets/jot/templates/chunk.form.inc.html

Dort ist in der Zeile mit veriword eine Variable für den Managerpfad eingeführt, die es zumindest bei mir nirgendwo gibt.

Aus...

<div style="width:150px;margin-top: 5px;margin-bottom: 5px;"><a href="[+jot.link.current:esc+]"><img src="[(modx_manager_url)]includes/veriword.php?rand=[+jot.seed+]"....

...wird also...

<div style="width:150px;margin-top: 5px;margin-bottom: 5px;"><a href="[+jot.link.current:esc+]"><img src="manager/includes/veriword.php?rand=[+jot.seed+]"... 

Bei der deutschen Variante von Thomas Jakobi ist dieses Problem bereits gelöst.

Kommentare sollen nicht auf allen Seiten eingeblendet werden

Da ich gerne ein Template für alle Seiten benutze will ich das Snippet so modifizieren, dass es nur auf bestimmten Seiten erscheint. Hier ist meine - sicher noch verbesserungswürdige Umsetzung.

<?php
/*####
#
# Name: Jot
# Version: 1.1.4
# Author: Armand "bS" Pondman (apondman@zerobarrier.nl)
# Date: Aug 04, 2008
#
# Latest Version: http://modxcms.com/Jot-998.html
# Jot Demo Site: http://projects.zerobarrier.nl/modx/
# Documentation: http://wiki.modxcms.com/index.php/Jot (wiki)
#
####*/

jotPath = $modx->config['base_path'] . 'assets/snippets/jot/';
include_once($jotPath.'jot.class.inc.php');

Jot = new CJot;
$Jot->VersionCheck("1.1.4");
$Jot->Set("path",$jotPath);
$Jot->Set("action", $action);
$Jot->Set("postdelay", $postdelay);
$Jot->Set("docid", $docid);
$Jot->Set("tagid", $tagid);
$Jot->Set("subscribe", $subscribe);
$Jot->Set("moderated", $moderated);
$Jot->Set("captcha", $captcha);
$Jot->Set("badwords", $badwords);
$Jot->Set("bw", $bw);
$Jot->Set("sortby", $sortby);
$Jot->Set("numdir", $numdir);
$Jot->Set("customfields", $customfields);
$Jot->Set("guestname", $guestname);
$Jot->Set("canpost", $canpost);
$Jot->Set("canview", $canview);
$Jot->Set("canedit", $canedit);
$Jot->Set("canmoderate", $canmoderate);
$Jot->Set("trusted", $trusted);
$Jot->Set("pagination", $pagination);
$Jot->Set("placeholders", $placeholders);
$Jot->Set("subjectSubscribe", $subjectSubscribe);
$Jot->Set("subjectModerate", $subjectModerate);
$Jot->Set("subjectAuthor", $subjectAuthor);
$Jot->Set("notify", $notify);
$Jot->Set("notifyAuthor", $notifyAuthor);
$Jot->Set("validate", $validate);
$Jot->Set("title", $title);
$Jot->Set("authorid", $authorid);
$Jot->Set("css", $css);
$Jot->Set("cssFile", $cssFile);
$Jot->Set("cssRowAlt", $cssRowAlt);
$Jot->Set("cssRowMe", $cssRowMe);
$Jot->Set("cssRowAuthor", $cssRowAuthor);
$Jot->Set("tplForm", $tplForm);
$Jot->Set("tplComments", $tplComments);
$Jot->Set("tplModerate", $tplModerate);
$Jot->Set("tplNav", $tplNav);
$Jot->Set("tplNotify", $tplNotify);
$Jot->Set("tplNotifyModerator", $tplNotifyModerator);
$Jot->Set("tplNotifyAuthor", $tplNotifyAuthor);
$Jot->Set("tplSubscribe", $tplSubscribe);
$Jot->Set("debug", $debug);
$Jot->Set("output", $output);


$top= isset ($top) && intval($top) ? $top : 0;
$id= isset ($id) && intval($id) ? intval($id) : $modx->documentIdentifier;
$topLevel= isset ($topLevel) && intval($topLevel) ? intval($topLevel) : 0;
if ($id && $id != $top) {
$pid= $id;
if (!$topLevel || count($modx->getParentIds($id)) >= $topLevel) {
while ($parentIds= $modx->getParentIds($id, 1)) {
$pid= array_pop($parentIds);
if ($pid == $top) {
break;
}
$id= $pid;
if ($topLevel && count($modx->getParentIds($id)) < $topLevel) {
break;
}
}
}
}
$ultimate = $id;

id="";


$parent = $modx->getParent($modx->documentIdentifier,'','id');
$id = $modx->documentIdentifier


if (($parent != "") && ($id != 338) && ($id != 41) && ($id != 361) && ($parent 
!= 337))
{
return $Jot->Run();
}
?>

Wenn ich mich auf einer Rubrik-Seite befinde (ID = der nächst höheren und auf bestimmten anderen IDs, wird das Snippet verlassen, ohne JOT wirklich aufzurufen.

Das TOC Plugion und PHP 7

TOC steht für table of content, also Inhaltsverzeichnis. Laut diversen Suchmaschinen-Experen, liebt Google lange Text. Damit diese gut lesbar bleiben, ist ein Inhaltsverzeichnis sinnvoll. Und dieses sollte möglichst automatisch erstellt werden. Hierzu gibt es das TOC-Plugin.

Die Installaton gestaltet sich relativ einfach. Unter Elemente -> Elemente-Verwaltung -> Plugins erstellt man ein neues Plugin mit dem banen "Page Toc Generator". Dann kopiert man den Code von der oben verlinkten Webseite in das Code-Feld. In der Rubrik "Systemereignisse" setzt man den Haken bei "OnWebPagePrerender".

Damit man das Verzeichnis auch sieht, muss man diverse Kommentare setzen. Das Ganze sollte dann so aussehen.

<!--#toc_plugin#_START_CONFIGURATION-->
<!--#toc_plugin#_END_CONFIGURATION-->
<!--#toc_plugin#_TOC_OUTPUT-->
<!--#toc_plugin#_START_TOC_INDEXING-->
...HTML-Code, für den das Inhaltsverzeichnis erstellt werden soll...
<!--#toc_plugin#_END_TOC_INDEXING-->

Unter PHP 7 oder höher funktioniert es aber nicht. Das liegt daran, dass die verwendete Funkton...

ereg

nicht mehr in PHP 7 und höher unterstützt wird und gegen...

reg_prematch

...getauscht werden muss.

Das Plugin sieht danach bei mir so aus...

Plugin Name: TOC Generator
Plugin URI:
Description: This plugin is used to automatically generate a table of contents

from HTML headings on that page.
Version: 0.9.2
Author: Samit Vartak and Paul Bohman

KNOWN BUGS: 
- default value for $end_level doesn't work, you must specify it explicitly
- the start configuration marker seems to be required, even though it's not 
supposed to be.

TO DO:
- use a simplified syntax to call the plugin, perhaps something like: 
*[pageTOC? &amp;start_level=`2` &amp;heading_tag=`h2`]* // this is where the 
output 
would go (eliminating the need for that tag)
*[startTOC]*
*[endTOC]*
(Note: making the opening tag visible in the rich text editor (e.g. *[ ]*, 
instead of &lt;!-- --&gt;) has the benefit of ensuring non-technical people 
don't 
accidentally delete the comments when editing the page) 
- If I do this, create an optional backwards compatibility mode, as an external

include
- extract the functions and documentation to external files in the assets 
folder, leaving only the parameters in plugin
- make sure multiple instances of TOCs can exist on the same page
- give the option to use existing anchors, instead of the automatically 
generated ones
- give the option to have no heading for the TOC
- and option to have no surrounding tag around TOC
- make code more efficient by looking for the output tag, and if it isn't there,

stop running the code, so it doesn't slow down the pages that don't use the TOC

plugin

*/

// PARAMETERS

// TOC SETTINGS
$default_start_level = 'h2'; // can be h1 through h6
$default_end_level = 'h3'; // can be h1 through h6
$default_toc_list_type = 'ul'; // type of list for table of contents; can be ul

(bulleted list) or ol (numbered list)
$default_toc_heading_text = 'Inhaltsverzeichnis'; // The text of the heading 
above the table of contents
$default_toc_heading_tag = 'h2'; //what level of heading to use above the table

of contents
$default_toc_parent = 'div'; //the table of contents will be embeded in this tag
$default_toc_parent_id = 'toc'; //the id which gets applied to the parent tag

// TAGS TO USE IN THE DOCUMENT
$open_param = &quot;&lt;!--&quot;;
$close_param = &quot;--&gt;&quot;;
$start_marker = &quot;#toc_plugin#_START_TOC_INDEXING&quot;; // begin cataloging 
headings 
here
$end_marker = &quot;#toc_plugin#_END_TOC_INDEXING&quot;; //end cataloging 
headings here
$output_marker = &quot;#toc_plugin#_TOC_OUTPUT&quot;; // The generated table of 
content 
will be inserted here
$start_config_marker = &quot;#toc_plugin#_START_CONFIGURATION&quot;;
$toc_list_type_marker = &quot;#toc_plugin#_list_type=&quot;;
$start_level_marker = &quot;#toc_plugin#_start_level=&quot;;
$end_level_marker = &quot;#toc_plugin#_end_level=&quot;;
$toc_heading_text_marker = &quot;#toc_plugin#_header=&quot;;
$toc_heading_marker = &quot;#toc_plugin#_header_tag=&quot;;
$toc_parent_marker = &quot;#toc_plugin#_parent_tag=&quot;;
$toc_parent_id_marker = &quot;#toc_plugin#_parent_tag_id=&quot;;
$end_config_marker = &quot;#toc_plugin#_END_CONFIGURATION&quot;;

// END PARAMETERS
// BEGIN PLUGIN CODE

//Information gathering
$source = &amp;$modx-&gt;documentOutput; //fetching the page source

$search_content1 = $open_param.$toc_list_type_marker.'(.*)'.$close_param; 
//fetching the list type
preg_match($search_content1, $source, $option1);
if ($option1[1] !='') { $toc_list_type = $option1[1]; } else $toc_list_type =

$default_toc_list_type; 

$search_content2 = $open_param.$start_level_marker.'(.*)'.$close_param; 
//fetching the start level
preg_match($search_content2, $source, $option2);
if ($option2[1] !='') { $start_level = $option2[1]; } else $start_level = 
$default_start_level; 

$search_content3 = $open_param.$end_level_marker.'(.*)'.$close_param; //fetching

the end level
preg_match($search_content3, $source, $option3);
if ($option3[1] !='') { $end_level = $option3[1]; } else $end_level = 
$default_end_level; 

$search_content4 = $open_param.$toc_heading_text_marker.'(.*)'.$close_param; 
//fetching the header text
preg_match($search_content4, $source, $option4);
if ($option4[1] !='') { $toc_heading_text = $option4[1]; } else 
$toc_heading_text = $default_toc_heading_text;

$search_content5 = $open_param.$toc_heading_marker.'(.*)'.$close_param; 
//fetching the header tag which contains the header
preg_match($search_content5, $source, $option5);
if ($option5[1] !='') { $toc_heading_tag = $option5[1]; } else $toc_heading_tag

= $default_toc_heading_tag;

$search_content6 = $open_param.$toc_parent_marker.'(.*)'.$close_param; 
//fetching the tag which will enclose table of contents
preg_match($search_content6, $source, $option6);
if ($option6[1] !='') { $toc_parent = $option6[1]; } else $toc_parent = 
$default_toc_parent;

$search_content7 = $open_param.$toc_parent_id_marker.'(.*)'.$close_param; 
//fetching the CSS id which we want to apply to table of contents
preg_match($search_content7, $source, $option7);
if ($option7[1] !='') { $toc_parent_id = $option7[1]; } else $toc_parent_id =

$default_toc_parent_id;


//Functions
function strip_special_chars($val)
{
$return_str = &quot;&quot;;
for($i=1; $i &lt;= strlen($val); $i++)
{
if ( ((ord(substr($val, $i-1, 1)) &gt;= 97) and (ord(substr($val, $i-1, 1)) 
&lt;= 
122)) or ((ord(substr($val, $i-1, 1)) &gt;= 65) and (ord(substr($val, $i-1, 1)) 
&lt;= 
90)) or ((ord(substr($val, $i-1, 1)) &gt;= 48) and (ord(substr($val, $i-1, 1)) 
&lt;= 
57)))
{
$return_str .= substr($val, $i-1, 1);
}
else if(ord(substr($val, $i-1, 1)) == 32) 
{
$return_str .= &quot;_&quot;; 
}
}
return($return_str);
}
//Plugin code starts
$search_content = '/[^|]('.$start_marker.')(.+?)('.$end_marker.')/sim';
preg_match($search_content, $source, $actual_source);
$content = $actual_source[0];
$search_header = 
'=&lt;h['.$start_level.'-'.$end_level.'][^&gt;]*&gt;(.*)&lt;/h['.$start_level.'-'.$end_level.']&gt;=siU';
preg_match_all($search_header, $content, $header_tags_info, PREG_SET_ORDER);

$named_anchors = array();
$header_tags = array();
foreach ($header_tags_info as $val) {
array_push($header_tags,$val[0]);
array_push($named_anchors,$val[1]);
}
$i = 0;
if ($toc_heading_tag!='')
{
$hx = 
&quot;&lt;&quot;.$toc_heading_tag.&quot;&gt;&quot;.$toc_heading_text.&quot;&lt;/&quot;.$toc_heading_tag.&quot;&gt;&quot;;
}
else $hx='';
if($toc_parent == &quot;&quot;){
$initial_tag = $hx.&quot;\n&quot;.&quot;&lt;&quot;. $toc_list_type .&quot; 
id=\&quot;&quot;.$toc_parent_id.&quot;\&quot;&gt;&quot;;
$final_tag = &quot;\n&lt;/&quot;.$toc_list_type.&quot;&gt;&quot;;
}else{
$initial_tag = &quot;\n&lt;&quot;. $toc_parent .&quot; 
id=\&quot;&quot;.$toc_parent_id.&quot;\&quot;&gt;&quot;.&quot;\n&quot;.$hx.&quot;\n&lt;&quot;.

$toc_list_type .&quot;&gt;&quot;;
$final_tag = 
&quot;\n&lt;/&quot;.$toc_list_type.&quot;&gt;\n&lt;/&quot;.$toc_parent.&quot;&gt;&quot;;
}
$display_content = $initial_tag; 
$prev_tag = &quot;&quot;;
$prev_tag_value = 7;
$current_tag_pointer = &quot;&quot;;
$tag_pattern = '=&lt;h['.$start_level.'-'.$end_level.'][^&gt;]*&gt;=siU';
$h1=array();
$h2=array();
$h3=array();
$h4=array();
$h5=array();
$h6=array();
foreach($header_tags as $tags){
$tag = &quot;&quot;;
$tag_value = 0;
$replace_var = &quot;&quot;;
if (preg_match(&quot;/&lt;h1/&quot;, $header_tags[$i])){
//$tag = &quot;&lt;h1&gt;&quot;;
preg_match_all($tag_pattern, $header_tags[$i], $tag1, PREG_SET_ORDER);
$tag2 = $tag1[0];
$tag = $tag2[0];
$tag_value = 1;
$current_tag_pointer = $h1;
}
elseif (preg_match(&quot;/&lt;h2/&quot;, $header_tags[$i])) {
//$tag = &quot;&lt;h2&gt;&quot;;
preg_match_all($tag_pattern, $header_tags[$i], $tag1, PREG_SET_ORDER);
$tag2 = $tag1[0];
$tag = $tag2[0];
$tag_value = 2;
$current_tag_pointer = $h2;
}
elseif (preg_match(&quot;/&lt;h3/&quot;, $header_tags[$i])) {
//$tag = &quot;&lt;h3&gt;&quot;;
preg_match_all($tag_pattern, $header_tags[$i], $tag1, PREG_SET_ORDER);
$tag2 = $tag1[0];
$tag = $tag2[0];
$tag_value = 3;
$current_tag_pointer = $h3;
}
elseif (preg_match(&quot;/&lt;h4/&quot;, $header_tags[$i])) {
//$tag = &quot;&lt;h4&gt;&quot;;
preg_match_all($tag_pattern, $header_tags[$i], $tag1, PREG_SET_ORDER);
$tag2 = $tag1[0];
$tag = $tag2[0];
$tag_value = 4;
$current_tag_pointer = $h4;
}
elseif (preg_match(&quot;/&lt;h5/&quot;, $header_tags[$i])){
//$tag = &quot;&lt;h5&gt;&quot;;
preg_match_all($tag_pattern, $header_tags[$i], $tag1, PREG_SET_ORDER);
$tag2 = $tag1[0];
$tag = $tag2[0];
$tag_value = 5;
$current_tag_pointer = $h5;
}
elseif (preg_match(&quot;/&lt;h6/&quot;, $header_tags[$i])){
//$tag = &quot;&lt;h6&gt;&quot;;
preg_match_all($tag_pattern, $header_tags[$i], $tag1, PREG_SET_ORDER);
$tag2 = $tag1[0];
$tag = $tag2[0];
$tag_value = 6;
$current_tag_pointer = $h6;
}
$temp = $i + 1;
// $replace_var = $tag.&quot;&lt;a name=\&quot;&quot;. $named_anchors[$i]. 
&quot;-&quot;. $temp .&quot;\&quot; id=\&quot;&quot;. 
$named_anchors[$i]. &quot;-&quot;. 
$temp.&quot;\&quot;&gt;&lt;/a&gt;&quot;.$named_anchors[$i].&quot;&lt;/h&quot;.$tag_value.&quot;&gt;&quot;;
// $replace_var = $tag.&quot;&lt;a name=\&quot;&quot;. ereg_replace(&quot; 
&quot;, &quot;_&quot;, $named_anchors[$i]). 
&quot;_&quot;. $temp .&quot;\&quot; id=\&quot;&quot;. ereg_replace(&quot; 
&quot;, &quot;_&quot;, $named_anchors[$i]). &quot;_&quot;. 
$temp.&quot;\&quot;&gt;&lt;/a&gt;&quot;.$named_anchors[$i].&quot;&lt;/h&quot;.$tag_value.&quot;&gt;&quot;;
// latest one // $replace_var = $tag.&quot;&lt;a name=\&quot;&quot;. 
ereg_replace(&quot; &quot;, &quot;_&quot;, 
strip_tags($named_anchors[$i])). &quot;_&quot;. $temp .&quot;\&quot; 
id=\&quot;&quot;. ereg_replace(&quot; &quot;, &quot;_&quot;, 
strip_tags($named_anchors[$i])). &quot;_&quot;. 
$temp.&quot;\&quot;&gt;&lt;/a&gt;&quot;.$named_anchors[$i].&quot;&lt;/h&quot;.$tag_value.&quot;&gt;&quot;;
$replace_var = $tag.&quot;&lt;a name=\&quot;&quot;. 
strip_special_chars(strip_tags($named_anchors[$i])). &quot;_&quot;. $temp 
.&quot;\&quot; id=\&quot;&quot;. 
strip_special_chars(strip_tags($named_anchors[$i])). &quot;_&quot;. 
$temp.&quot;\&quot;&gt;&lt;/a&gt;&quot;.$named_anchors[$i].&quot;&lt;/h&quot;.$tag_value.&quot;&gt;&quot;;
$source = str_replace($tags, $replace_var, $source);
if($prev_tag_value &lt; $tag_value){
$display_content = $display_content . &quot;\n&lt;&quot;. $toc_list_type 
.&quot;&gt;&quot;;
// $display_content = $display_content . &quot;\n&lt;li&gt;&lt;a 
href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.$named_anchors[$i].&quot;-&quot;.$temp.&quot;\&quot;&gt;&qquot;.

$named_anchors[$i] .&quot;&lt;/a&gt;&quot;;
// $display_content = $display_content . &quot;\n&lt;li&gt;&lt;a 
href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.ereg_replace(&quot; 
&quot;, &quot;_&quot;, 
$named_anchors[$i]).&quot;_&quot;.$temp.&quot;\&quot;&gt;&quot;. 
$named_anchors[$i] .&quot;&lt;/a&gt;&quot;;
// latest one // $display_content = $display_content . &quot;\n&lt;li&gt;&lt;a

href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.ereg_replace(&quot; 
&quot;, &quot;_&quot;, 
strip_tags($named_anchors[$i])).&quot;_&quot;.$temp.&quot;\&quot;&gt;&quot;. 
strip_tags($named_anchors[$i]) 
.&quot;&lt;/a&gt;&quot;;
$display_content = $display_content . &quot;\n&lt;li&gt;&lt;a 
href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.strip_special_chars(strip_tags($named_anchors[$i])).&quot;_&quot;.$temp.&quot;\&quot;&gt;&quot;.

strip_tags($named_anchors[$i]) .&quot;&lt;/a&gt;&quot;;

if($tag_value == 6){
array_unshift( $h6, &quot;\n&lt;/&quot;. $toc_list_type .&quot;&gt;&quot;);
array_unshift( $h6, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 5){
array_unshift( $h5, &quot;\n&lt;/&quot;. $toc_list_type .&quot;&gt;&quot;);
array_unshift( $h5, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 4){
array_unshift( $h4, &quot;\n&lt;/&quot;. $toc_list_type .&quot;&gt;&quot;);
array_unshift( $h4, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 3){
array_unshift( $h3, &quot;\n&lt;/&quot;. $toc_list_type .&quot;&gt;&quot;);
array_unshift( $h3, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 2){
array_unshift( $h2, &quot;\n&lt;/&quot;. $toc_list_type .&quot;&gt;&quot;);
array_unshift( $h2, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 1){
array_unshift( $h1, &quot;\n&lt;/&quot;. $toc_list_type .&quot;&gt;&quot;);
array_unshift( $h1, &quot;&lt;/li&gt;&quot;);
}
}
else{
if($tag_value &lt; 6){
foreach($h6 as $value){
$display_content = $display_content .$value;
}
$h6 = array( Null );
}
if($tag_value &lt; 5){
foreach($h5 as $value){
$display_content = $display_content .$value;
}
$h5 = array( Null );
}
if($tag_value &lt; 4){
foreach($h4 as $value){
$display_content = $display_content .$value;
}
$h4 = array( Null );
}
if($tag_value &lt; 3){
foreach($h3 as $value){
$display_content = $display_content .$value;
}
$h3 = array( Null );
}
if($tag_value &lt; 2){
foreach($h2 as $value){
$display_content = $display_content .$value; 
}
$h2 = array( Null );
}
if($tag_value &lt; 1){
foreach($h1 as $value){
$display_content = $display_content .$value;
}
$h1 = array( Null );
}
if($tag_value == 6){
$display_content = $display_content .array_shift($h6);
}
if($tag_value == 5){
$display_content = $display_content .array_shift($h5);
}
if($tag_value == 4){
$display_content = $display_content .array_shift($h4);
}
if($tag_value == 3){
$display_content = $display_content .array_shift($h3);
}
if($tag_value == 2){
$display_content = $display_content .array_shift($h2);
}
if($tag_value == 1){
$display_content = $display_content .array_shift($h1);
}
// $display_content = $display_content . &quot;\n&lt;li&gt;&lt;a 
href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.$named_anchors[$i].&quot;-&quot;.$temp.&quot;\&quot;&gt;&quot;.

$named_anchors[$i] .&quot;&lt;/a&gt;&quot;;
// $display_content = $display_content . &quot;\n&lt;li&gt;&lt;a 
href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.ereg_replace(&quot; 
&quot;, &quot;_&quot;, 
$named_anchors[$i]).&quot;_&quot;.$temp.&quot;\&quot;&gt;&quot;. 
$named_anchors[$i] .&quot;&lt;/a&gt;&quot;;
// latest one // $display_content = $display_content . &quot;\n&lt;li&gt;&lt;a

href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.ereg_replace(&quot; 
&quot;, &quot;_&quot;, 
strip_tags($named_anchors[$i])).&quot;_&quot;.$temp.&quot;\&quot;&gt;&quot;. 
strip_tags($named_anchors[$i]) 
.&quot;&lt;/a&gt;&quot;;
$display_content = $display_content . &quot;\n&lt;li&gt;&lt;a 
href=\&quot;&quot;.$_SERVER[&quot;REQUEST_URI&quot;].&quot;#&quot;.strip_special_chars(strip_tags($named_anchors[$i])).&quot;_&quot;.$temp.&quot;\&quot;&gt;&quot;.

strip_tags($named_anchors[$i]) .&quot;&lt;/a&gt;&quot;;

if($tag_value == 6){
array_unshift( $h6, &quot;&lt;/li&gt;&quot;); 
}elseif($tag_value == 5){
array_unshift( $h5, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 4){
array_unshift( $h4, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 3){
array_unshift( $h3, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 2){
array_unshift( $h2, &quot;&lt;/li&gt;&quot;);
}elseif($tag_value == 1){
array_unshift( $h1, &quot;&lt;/li&gt;&quot;);
}
}
$prev_tag = $tag;
$prev_tag_value = $tag_value;
$i++;
}
$empty = &quot;&quot;;
$display_content =$display_content. &quot;&lt;/li&gt;&quot;.$final_tag;


//Remove toc codes from the source.
$source = str_replace($open_param.$start_config_marker.$close_param, 
$display_content, $source);
$source = 
str_replace($open_param.$toc_list_type_marker.$toc_list_type.$close_param, '',

$source);
$source = str_replace($open_param.$start_level_marker.$start_level.$close_param,

'', $source);
$source = str_replace($open_param.$end_level_marker.$end_level.$close_param, '',

$source);
$source = 
str_replace($open_param.$toc_heading_text_marker.$toc_heading_text.$close_param,

'', $source);
$source = 
str_replace($open_param.$toc_heading_marker.$toc_heading_tag.$close_param, '',

$source);
$source = str_replace($open_param.$toc_parent_marker.$toc_parent.$close_param,

'', $source);
$source = 
str_replace($open_param.$toc_parent_id_marker.$toc_parent_id.$close_param, '',

$source);
$source = str_replace($open_param.$start_marker.$close_param, '', $source);
$source = str_replace($open_param.$end_config_marker.$close_param, '', $source);
$source = str_replace($open_param.$output_marker.$close_param, '', $source);
$source = str_replace($open_param.$end_marker.$close_param, '', $source);</p>
Du willst meine Arbeit unterstützen? Dann freue ich mich über eine kleine Spende!

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert