Archiv der Kategorie: PHP

Jetzt gilt’s: Rezepte aus WordPress importieren

Ich hab mir meine Tabelle mit den Rohdaten mal mit so etwa 30 Rezepten geladen, die sieht jetzt so aus:

rohdaten_screenshot

rohdaten_screenshot

Eigentlich brauchen wir nur die Felder titel und content, den URLsafe name können wir auch mit einer eingebauten Joomla-Funktion aus dem titel erzeugen:

$akt_name =JFilterOutput::stringURLSafe($akt_titel);

Aber das ist reine Geschmackssache. Jedenfalls wird die Foreach-Schleife jetzt ordentlich aufgebohrt, da kommt erstmal die Logik für die Belegung des JTable-Objekts rein. Meine drei Felder hab ich mir vorher natürlich auf Variable gelegt, ist übersichtlicher.

JTable-Objekt erzeugen, füllen und mit Insert wegschreiben

$article = JTable::getInstance(‚content‘);

$article->title            = $akt_titel;
        $article->alias            = $akt_name;
        $article->introtext        = $text;
        $article->catid            = 8;
        $article->created          = JFactory::getDate()->toSQL();
        //$article->created_by_alias = ‚Super User‘;
        $article->created_by = 839;
        $article->state            = 1;
        $article->access           = 1;
        $article->metadata         = ‚{„robots“:““,“author“:““,“rights“:““,“xreference“:““}‘;
        $article->language         = ‚*‘;

Dann fehlt nur noch die Logik zum Wegschreiben des befüllten Objekts, die hab ich aus dem Codebeispiel hier übernommen:

https://stackoverflow.com/questions/12643725/create-a-joomla-article-programatically?lq=1

Das sieht dann recht übersichtlich so aus:

// Check to make sure our data is valid, raise notice if it’s not.

        if (!$article->check()) {
            JError::raiseNotice(500, $article->getError());

            return FALSE;
        }

        // Now store the article, raise notice if it doesn’t get stored.

        if (!$article->store(TRUE)) {
            JError::raiseNotice(500, $article->getError());

            return FALSE;
        }

Das war schon die ganze Mechanik! Jetzt müssen wir nur noch die where-Klausel aus dem Select rausnehmen, die liest sich dann ganz einfach so:

$query = „SELECT * FROM 00_rohdaten;“;

Damit steppt unsere Foreach-Schleife brav durch alle Zeilen der Tabelle rohdaten durch und legt die neuen Artikel an. Kurzer Blick in die Beitragsübersicht:

neue_rezepte_screenshot

neue_rezepte_screenshot

Na bitte, da sind sie ja alle!  Auch im Modul „Die neuesten Rezepte“:

die_neuesten_rezepte

die_neuesten_rezepte

Die haben jetzt natürlich alle dasselbe Datum, nämlich heute, aber das anzupassen wäre jetzt schon Feinarbeit. Man könnte natürlich versuchen, das Erstellungsdatum aus WordPress zu übernehmen, das geht sicher auch, aber ich lass es jetzt mal gut sein. Noch ein kurzer Blick auf das alphabetische Inhaltsverzeichnis:

ivz_screenshot

ivz_screenshot

Alles OK, wir haben alles.

Fazit und Ausblick

So schlimm war das doch jetzt gar nicht, oder? Ich gebe zu, mit den Rohdaten aus dem Inselfischkochbuch war die Bereinigungsaktion vor dem Import recht einfach, einfach weil die Rezepte in den meisten Fällen aus sehr straightem HTML-Code bestehen, und den kann man mit wenig Modifikationen nach Joomla übernehmen.

Was mich aber noch ein bisschen fuchst: meine schönen Kategorien aus WordPress sind natürlich futsch, und ich hab eigentlich keine Lust, bei über 300 Rezepten die Kategorien nachträglich als Tags (Schlagworte) manuell einzufügen. Erinnern sie sich, ich hatte in diesem Beitrag meine WordPress-Kategorien als Schlagwortliste nach Joomla abgebildet. Das hatte soweit funktioniert, weil ich in WordPress keine verschachtelten Kategorien benutzt habe, keine ideale Lösung, aber besser als nix. Mal schauen, ob mir dazu noch was einfällt. Aber wir machen hier jetzt erstmal eine wohlverdiente Pause!

Beiträge aus WordPress holen: einige Vorüberlegungen

Nochmal kurz als Gedächtnisstütze: wir brauchen eigentlich nur drei Felder, und zwar:

$article->title            = 'This is my super cool title!';
$article->alias            = JFilterOutput::stringURLSafe('This is my super cool title!');
$article->introtext        = '<p>This is my super cool article!</p>';

Den title können wir straight aus der wp_contents übernehmen, das ist der post_title. Für den alias bietet sich das Feld post_name an, der wäre auch schon URLsafe. Und der introtext kommt aus dem Feld post_content. Sieht doch schon mal ganz gut aus.

Was nicht gehen wird

So leid es mir tut, aber Bilder und Links können wir leider nicht übernehmen, die müssen wir vor dem Import rausputzen. Warum? Ganz einfach, weil sich beide auf eine spezifische URL beziehen, die wir in Joomla nicht nachstellen können. Nochmal kurz zur Erinnerung, ein im Beitragseditor eingefügtes Bild sieht im Quelltext so aus:

caption_screenshot

caption_screenshot

Mit dem caption-Shortcode kann Joomla natürlich auch nix anfangen, der muss raus. Mit dem Link gehts uns nicht viel anders, wenn der so aussieht:

ahref_screenshot

ahref_screenshot

…muss eigentlich der ganze Tag weg bis auf den Text „Haferflockenmüsli a la Oma“, mal sehen ob wir das hinkriegen.

Was drinbleiben kann

Ich geh mal nur vom Inselfisch-Kochbuch aus, da muss jeder sehen, wie das bei ihm selber aussieht und die Ersetzungen ggf. anpassen. Ich hab nicht so sehr viele Formatierungen verwendet, das sind eigentlich nur die Überschriften h1…maximal h4, ab und zu mal eine nummerierte oder unnummerierte Liste, und gelegentlich ein <strong>, das kann alles drinbleiben. Shortcodes oder auch Gallerys oder sowas hab ich gar nicht verwendet, da muss nix passieren. Downloadlinks haben wir gar keine, und auch keine Gimmicks wie Slider oder sowas, das habe ich im Zuge der barrierefreien Restrukturierung alles eliminiert – gute Sache! Jetzt ist nämlich nur noch relativ sauberer HTML-Code im post_content übrig, den man in den meisten Fällen übernehmen können wird.

Zuerst mal: Export aus WordPress

Ich zieh mir  nur die oben genannten drei Felder post_title, post_content und post_name ab, Kriterien post_type = post und post_status = publish. Nachgedanke: die ID nehmen wir auch noch mit, die ist ja unique und macht uns im phpmyadmin das Leben leichter. Davon nehm ich mal nur eine Handvoll Datensätze zum Testen, nicht gleich alle über 300. Die schiebe ich in eine neue MySQL-Tabelle  und dann frisch ans Werk!

Nicht konvertierbare Tags rausschmeissen

Den caption-Shortcode mitsamt dem img-Tag werden wir so los:

replace_caption

replace_caption

Sorry, das gibts nur als Screenshot weil WordPress den Sourcecode verhunzt hat! Das ist übrigens nicht auf meinem Mist gewachsen, das hat Tante Google hergegeben – sehr praktisch.

Ebenso können wir dank fleissigen googlens die alt-Tags entfernen, aber den Linktext stehen lassen:

$text = preg_replace('#<a.*?>(.*?)</a>#i', '\1', $text);

Das wars aber auch schon mit den Bereinigungen! Die ganzen <hx> und so weiter können stehenbleiben, wir wollen ja die Überschriftsformatierungen und all das mitnehmen.

Testausgabe mit der PHP Bridge

Ich hab mir ein Miniformular gebastelt, das nur aus einem „absenden“ Button besteht, und zum Testen in die if (isset($_POST[‚absenden‘])){…} Bedingung  folgenden Code gepackt:

$db = JFactory::getDBO();

$query = „SELECT * FROM 00_rohdaten where id = 224;“;
$db->setQuery($query);

$result = $db->query();
$results = $db->loadObjectList();

foreach ($results as $zeile) :
echo $zeile->id;
echo $zeile->titel;
$text = $zeile->content;

//caption-shortcode mitsamt img-tag weg
$text = preg_replace(‚/\\.*\\[.caption\\]/‘, “, $text);

//a href weg, Linktext stehen lassen
$text = preg_replace(‚#<a.*?>(.*?)</a>#i‘, ‚\1‘, $text);

//bereinigten text ausgeben
echo $text;
//echo $zeile->content;
echo „<br>“;
endforeach;
In den Select muss natürlich der richtige Tabellenname rein, und die ID des Datensatzes, den man zum Testen ausgeben möchte. In der Foreach-Schleife werden dann die Ersetzungen für den Content ausgeführt und das Endergebnis ausgegeben, so hat man eine ganz gute Kontrolle darüber, wie der importierte Datensatz in Joomla aussehen wird.

Und wie gehts jetzt weiter?

Ich tendiere dazu, mir die bereinigten Rohdaten in eine separate Tabelle schreiben zu lassen, und aus dieser dann die Import-Logik zu füttern. Aber darüber muss ich noch ein wenig nachdenken, und dann gibts einen neuen Beitrag.

Noch mehr postmeta: benutzerdefinierte Felder in wooCommerce

Falls ihnen die 48 vordefinierten Felder in wooCommerce noch nicht reichen, haben sie auch die Möglichkeit, zusätzliche Felder anzulegen. Diese laufen unter dem Stichwort „benutzerdefinierte Felder“ und können im Produkt-Editor angelegt werden.

Ein Feld für Material

Zu dem in meinem Online-Lädchen verkauften Glasperlenschmuck lege ich jetzt mal ein benutzerdefiniertes Feld „Material“ an.  Das kriegt dann zum Beispiel den Wert „Böhmische Glasperlen“, das sieht im Editor so aus:

benutzerdefinierte_felder

benutzerdefinierte_felder

Keine Hexerei, und was auf der Datenbank passiert kann man leicht erraten:

material_postmeta

material_postmeta

Es wird ein neuer Datensatz mit dem meta_key „Material“ angelegt, der kriegt den meta_value „Böhnmische Glasschliffperlen“. Das war’s schon, mehr ist nicht passiert.

Wie verwendet man nun diese Felder?

wooCommerce weist einen da höflich auf den Codex zum Thema Custom Fields hin. Das ist zwar gut gemeint, aber nicht besonders hilfreich. Deswegen gibts bei Tante Google auch jede Menge Einträge, wenn man nach „woocommerce show custom fields“ sucht, hier ist ein besonders netter von Theme Location

Der Knackpunkt ist: man muss die wooCommerce-Templates editieren, wenn man seine benutzerdefinierten Felder auch beim Produkt angezeigt kriegen will. Wir versuchen uns mal an einem einfachen Beispiel. Da sollen wir eine Zeile im Template content-single-product.php ergänzen:

<?php echo get_post_meta( get_the_ID(), ‘Material’, true ); ?>

Edit die content-single-product.php – ja, wie?

Also, erstmal muss man das richtige Template finden, und das ist bei der endlosen Latte von wooCommerce-Templates gar nicht so einfach. Schauen sie mal rein unter Plugins/wooCommerce/Bearbeiten, das ist alles andere als übersichtlich. Die content-single-product.php steht in der langen Liste ziemlich weit unten.

„At the appropriate place“ soll diese Zeile rein, heißt es im Tutorial, und das ist eine Runde Trial and Error. Ich habs jetzt im ersten Anlauf noch nicht hingekriegt, da muss ich nochmal ne Runde googlen. So richtig Spaß macht das nicht, die wooCommerce-templates sind viel zu schlecht dokumentiert, das ist eine einzige Raterei.

Anderer Ansatz: wooCommerce Hooks

Ich hatte jetzt relativ schnell die Schn… voll von der Rumprobiererei und bin auf einen anderen Lösungsansatz gestossen, um unser benutzerdefiniertes Feld auch anzeigen zu lassen. WooCommerce bietet eine lange Latte von vordefinierten Hooks, in die man sich einklinken kann, das ist hier bei BusinessBloomer recht ausführlich dokumentiert, man muss sich allerdings anmelden, um das Tutorial anschauen zu können, und dazu hatte ich keine Lust. Ein andermal vielleicht…

Prinzipiell sollte es so funktionieren: man editiert die functions.php und fügt den entsprechenden Hook hinzu, in dem ruft man die zugehörige selbstdefinierte Funktion auf, und die Funktion letztendlich sollte unser Custom Field dann auch anzeigen. Ich habs nicht hingekriegt, jedenfalls nicht in unter einer Stunde, und da hatte ich dann einfach keinen Bock mehr. Ist vielleicht nicht mein Tag zum Rumprobieren heute.

Noch ein Versuch: vielleicht mit einem Plugin?

Das hier sieht ganz gut aus:

WooCommerce Custom Product Data Fields

Das schau ich mir mal an, aber ich glaube, ich brauch erstmal ’ne Pause. Ja kruzitürken, kann das denn so schwer sein, so ein lumpiges Custom Field auch anzeigen zu lassen?

Ist echt nicht mein Tag heute, die Doku zu dem Plugin ist gerade nicht erreichbar.

acf_nicht_ereichbar

acf_nicht_ereichbar

Hab ich noch ein anderes Plugin probiert, ACF for WooCommerce.

ACF  = Advanced Custom Fields. Aber zu früh gefreut: auch kein Glück, da muß ich mal direkt die Support-Seite zitieren:

  • It seems only to work on Checkout Page. How do I show field on the Product Page?

    Any answer is most welcome, thank you.

Viewing 6 replies – 1 through 6 (of 6 total)

Tscha, Pech gehabt. Die Anzeige der Custom Fields in der Single Product Ansicht geht hier nur mit der kostenpflichtigen Pro-Version des Plugins, und die kostet 29 $. No way, José.

Jetzt reichts mit der Probiererei – noch einmal mit System.

Also, wir fangen nochmal sauber von vorne an. Wo soll unser benutzerdefiniertes Feld „Material“ erscheinen? In der Startseite des Shops, jedenfalls reicht mir das für’s erste. Direkt nach dem Titel, vor dem Warenkorb-Button.

Dafür editieren wir die woocommerce/templates/content-product.php.

Durch schlichtes Ausprobieren und Raten habe ich schließlich die richtige Position gefunden: nach der Zeile:

do_action( ‚woocommerce_shop_loop_item_title‘ );

Das hat nicht auf Anhieb funktioniert, da WordPress im Template anscheinend die get_post_meta()-Funktion nur mit ein bißchen Überredungskunst akzeptiert. Ich habe dann folgendes Snippet gefunden, damit kriegen wir endlich den meta_value unseres Custom Fields „Material“ angezeigt :

global $wp_query;
$postid = $wp_query->post->ID;
echo get_post_meta($postid, ‚Material‚, true);
wp_reset_query();

Ergebnis- meine benutzerdefinierten Felder werden jetzt direkt unter dem Titel jedes Produkts angezeigt:

shop_mit_cf

shop_mit_cf

Na bitte, geht doch. Aber ein Gfrett war das jetzt schon!

Wie gehts jetzt weiter?

Wenn man das benutzerdefinierte Feld noch woanders anzeigen möchte: tscha, das wird jetzt ein Exempel in Trial&Error. Für jede Seite im Shop herausfinden, welches Template für die Anzeige verwendet wird, dort die Stelle suchen an welcher das Custom Field erscheinen soll, da das obige Code Snippet einfügen und hoffen dass es funkioniert. Viel Spaß mit Tante Google und der wooCommerce-Doku…

Ich finde das Thema Custom Fields in wooCommerce insgesamt wahnsinnig unübersichtlich und lausig schlecht dokumentiert. Anscheinend gibt es einige Plugins, die einem da das Leben leichter machen sollen, aber ich hab bislang nur kostenpflichtige Exemplare gefunden, die das auch können was wir brauchen. Und ich zahle nicht für Plugins, Punktum. OpenSource, sie wissen schon. So, und das ist ein schöner Schlußsatz für diesen Beitrag, lassen wir es mal gut sein.

Nachtrag zu Evis CSV-Importer

Euer Wunsch ist mir Befehl

Ich bin gebeten worden, die Mechanik wie man abfragt ob ein Datensatz beim Import schon vorhanden ist, doch noch mal näher zu erläutern. Also, eigentlich ist die Sache ganz einfach. Wir haben ja die Konvention, daß der Beitragstitel für einen Mitgliedsbeitrag immer mit der eindeutigen ID (mnr) beginnt, danach folgt ein Leerzeichen, danach Vorname, Leerzeichen, Nachname. Da die ID eindeutig ist, reicht es völlig aus auf einen Substring zu prüfen. der Select sieht im einfachsten Fall so aus:

$schon_da = $wpdb->get_results( "SELECT * from wp_posts 
                where post_status like 'publish'
                and SUBSTRING_INDEX( post_title,' ',1) like ".$arr_akt_zeile[0]."");

Der SUBSTRING_INDEX() holt uns alle Zeichen des Post Title vor dem ersten Leerzeichen, und das vergleichen wir mit der aktuellen Mitgliedsnummer, die im Array $arr_akt_zeile[0] steckt. Den post status checken wir noch ab, und das wars schon!

Man kann dann bequem die Anzahl der zurückgegebenen Datensätze mit num_rows abholen:

$anzahl_gefunden = $wpdb->num_rows;

Dann packt man den ganzen Programmabschnitt zur Anlage des neuen Datensatz in eine If-Abfrage, die nur ausgeführt wird wenn noch kein Datensatz mit der aktuellen ID vorhanden ist. Ich hänge mal noch eine Debug-Ausgabe in den else-Zweig, aber das reicht jetzt wirklich.

if ($anzahl_gefunden == 0) {

... neuen Beitrag anlegen und benutzerdefinierte Felder füllen...

} else {
        
        echo "Datensatz mit der ID ".$arr_akt_zeile[0]." Ist bereits vorhanden <br>";
        
    }

 

Nützlicher Helfer: Bulk Delete

Wahrscheinlich gehts euch wie mir, beim Testen hab ich -zig Beiträge neu angelegt, und die in WordPress oder im phpmyadmin einzeln wieder rauszuloschen, ist ein mühselig Spiel.Nein, da kann man lange suchen, es gibt im WordPress-Beitragseditor keine Möglichkeit „Alle Beiträge“ zu selektieren.

Dafür gibts ein praktisches Plugin: Bulk Delete erlaubt die selektive Löschung von Beiträgen etc. en masse, das kann man hier ganz gut gebrauchen. Schauts euch mal an, ist immer wieder mal nützlich!

 

 

Evis CSV-Importer die Zweite: ein Beitrag pro Mitglied

Die Vorgabe wie gehabt

Für jedes Mitglied wird ein Beitrag in WordPress angelegt. Der Beitragstitel besteht aus der ID (Mitgliedsnummer), dem Vornamen und dem Nachnamen, das ganze mit Leerzeichen getrennt. Die restlichen Mitgliedsdaten kommen in benutzerdefinierte Felder. Soweit alles klar? dann wollen wir mal.

Zeilenweise Verarbeitung der CSV-Datei

Unsere CSV-Datei haben wir zeilenweise eingelesen, wir arbeiten sie jetzt innerhalb der while (!feof)-Schleife bis zum Dateiende ab. Nur mal kurz zum Rekapitulieren, die Debug-Ausgabe sollte in etwa so aussehen:

csv_debugausgabe

csv_debugausgabe

Wir haben pro Mitglied (=pro Zeile) 9 Datenfelder jeweils in ein Array eingelesen, die holen wir uns mit dem Index 0..8, das sah für die Debug-Ausgabe so aus:

//Debug-Ausgabe des Arrays
 echo $arr_akt_zeile[0]."<br>";
 echo $arr_akt_zeile[1]."<br>";
 echo $arr_akt_zeile[2]."<br>";
 echo $arr_akt_zeile[3]."<br>";
 echo $arr_akt_zeile[4]."<br>";
 echo $arr_akt_zeile[5]."<br>";
 echo $arr_akt_zeile[6]."<br>";
 echo $arr_akt_zeile[7]."<br>";
 echo $arr_akt_zeile[8]."<br><br>";

Jetzt müssen wir nur noch die Feldnamen für die einzelnen Datenfelder zuordnen, dann können wir. Ich mach da mal eine einfache Liste:

0 mnr
1 vorname
2 nachname
3 geschlecht
4 email
5 plz
6 ort
7 strassehausnummer
8 telefon

Alles paletti? Jetzt bauen wir uns für jede Input-Zeile einen neuen Beitrag.

Die Funktion wp_insert_post() ganz minimalistisch

Welche Parameter man der Funktion zum Erzeugen eines neuen Beitrags alle mitgeben, kann jeder selber im Codex nachlesen. Wir begnügen uns hier mit ganz wenigen Feldern:

  1. Titel: Mitgliedsnummer, Vorname und Name, mit Leerzeichen getrennt
  2. Content: ich geb hier einfach die Mitgliedsnummer nochmal aus
  3. Status publish
  4. Author, 1 ist mein Admin:
// Beitragsobjekt anlegen
    $my_post = array();
    $my_post['post_title']    = $arr_akt_zeile[0]." ".$arr_akt_zeile[1]." ".$arr_akt_zeile[2];
    $my_post['post_content']  = "Mitgliedsnummer: ".$arr_akt_zeile[0];
    $my_post['post_status']   = 'publish';
    $my_post['post_author']   = 1;

    // Beitrag in Datenbank einfügen
    $neue_id = wp_insert_post( $my_post );

Wichtig: mit der letzten Anweisung $neue_id=… holen wir uns den Rückgabewert der Funktion wp_insert_post, das ist die WordPress-ID des neuen Beitrags. Die brauchen wir nämlich gleich noch für die beutzerdefinierten Felder. Aber jetzt lassen wir die ganze Sache einfach mal laufen, und wenn alles paßt, sollte unser CSV-Importer-Plugin jetzt brav 10 neue Beiträge anlegen:

zehn_neue_beiträge

zehn_neue_beiträge

Hinweis: sollte ein Beitrag mehr als erwartet (ohne Titel) angelegt werden, ist am Ende der CSV-Datei ein CR/LF zuviel, das erzeugt eine leere Zeile. Im Notepad++ rauslöschen, dann paßts.

Wir befüllen jetzt noch die benutzerdefinierten Felder

Dazu benutzen wir die Funktion add_post_meta(), die sieht minimalistisch abgespeckt so aus:

add_post_meta( $post_id, $meta_key, $meta_value)

Dabei ist zu beachten, daß man über die $post_id die WordPress-ID des aktuellen Beitrags mitgeben muß. Aber die haben wir uns ja weiter oben schon geholt, die steckt in der Variablen $neue_id. Also, wir fügen jetzt mal nur zwei Felder zum Testen ein:

//*** Benutzerdefinierte Felder füllen
    
    //Postleitzahl
    add_post_meta( $neue_id,'plz', $arr_akt_zeile[5]);
    
    //Ort
    add_post_meta( $neue_id,'ort', $arr_akt_zeile[6]);
    
    //*** Ende Benutzerdefinierte Felder füllen

Wenn man den Import jetzt nochmal laufen läßt und in einen der neu erzeugten Beiträge reinschaut, sollte man etwa Folgendes sehen:

zwei_benutzerdef_felder

zwei_benutzerdef_felder

Das wars!

Fertig ist unser hausgemachter CSV-Importer, der – man sehe und staune! – auch Custom Fields befüllen kann. Wirklich keine Hexerei, oder?

Die Anzeige der benutzerdefinierten Felder basteln wir wie gehabt in einen Shortcode oder ins Template rein und benutzen dafür die Funktion get_post_meta(), das haben wir alles schon gehabt, das muß ich jetzt nicht wirklich nochmal ausführlich beschreiben.

Ja aber, was ist jetzt mit den neuen Mitgliedern?

Das ist eine sehr berechtigte Frage. Ich stelle das Szenario nochmal kurz vor:

  1. Die existierende Excel-Liste mit Mitgliederdaten ist als CSV-Datei bereits einmal importiert worden
  2. Änderungen an den Mitglieder-Daten werden ab jetzt in WordPress durch Editieren des jeweilige Beitrags gepflegt, d.h. wir definieren WordPress als unser führendes System
  3. Was geschieht jetzt, wenn neue Mitglieder nach dem ersten Import hinzukommen?

Wie bereits mal kurz erwähnt könnte man jetzt für jedes neue Mitglied manuell einen neuen Beitrag mit ID und Name im Titel anlegen, und auch die benötigten benutzerdefinierten Felder manuell nachziehen. Das gefällt mir allerdings überhaupt nicht, weil es 1. viel Handarbeit und 2. sehr fehleranfällig ist. Deswegen entscheide ich mich für eine

Hybrid-Lösung:

  1. Die alte Excel-Liste wird weiter gepflegt, die Mitgliedsnummern werden fortlaufend weiter vergeben.
  2. Wenn genügend neue Mitglieder hinzugekommen sind (z.B. zum Monatsende) wird die Excel-Liste neu importiert, und zwar ganz.
  3. Ich ergänze das Import-Plugin um einen Select, der vor dem Anlegen eines neuen Beitrages abfragt, ob in der Tabelle wp_posts bereits ein Beitrag mit dem Titel nach dem Muster mnr+vorname+nachname vorhanden ist. Falls ja (if-Abfrage), wird für den aktuellen Datensatz kein neuer Beitrag angelegt, der betreffende Datensatz wird einfach übersprungen.

So stelle ich sicher, daß nur für die neu hinzugekommenen Mitglieder auch neue Beiträge angelegt werden, und daß bereits vorhandene Daten nicht überschrieben werden. Wie das mit dem Select dann praktisch aussieht, daß kann jeder selber ausprobieren, nicht vergessen global $wpdb zu deklarieren, dann klappt das schon.

 

 

Jetzt wirds mir doch zu dumm: wir basteln uns einen CSV-Importer

Kostenpflichtige Plugins – seh ich doch gar nicht ein!

Wie ich schon mehrfach gesagt habe, habe ich erstens nicht das Budget, mir zu Testzwecken kostenpflichtige Plugins zu kaufen, und zweitens bin ich eine große Anhängerin der Open-Source-Idee. Das ist ja mit der Hauptgrund, warum WordPress so erfolgreich ist, es ist Open Source und kostenlos, es gibt fantastische Supportforen, es gibt für nahezu jedes Programmierproblem eine Lösung, ohne daß man Bares dafür hinlegen muß.

Da alle CSV-Import-Plugins die ich getestet habe in den kostenfreien Versionen keinen Import von Custom Fields können und ich diese Möglichkeit aber unbedingt brauche, gehen wir jetzt einen anderen Weg:

Wir schreiben unseren eigenen CSV-Importer

Ich bleibe dabei beim Beispiel des Turnverein Weiß-Blau mit dem Mitgliederverzeichnis. Die Vorgaben bleiben gleich:

  • die Adressdaten liegen als CSV-Datei vor (Spezifikation folgt)
  • für jeden Mitgliederdatensatz soll ein eigener Beitrag angelegt werden
  • im Beitragstitel brauchen wir die Mitgliedsnummer (ID) und den Namen
  • die restlichen Adressdaten sollen in Benutzerdefinierte Felder geschrieben werden.

Alles klar soweit? Na, dann wollen wir mal!

Die Spezifikation der CSV-Datei

Ich nehme da mal was ganz Rudimentäres, das kann sich jeder selber mit Excel und im Notepad++ zurechtschnitzen. Pro Datensatz eine Zeile, Zeilenende mit CR/LF markiert, Trennzeichen ein Semikolon (;), kein Textbegrenzungszeichen. Kodierung UTF-8 ohne BOM. Wir haben zehn Datensätze:

import_notepad

import_notepad

Der Anfang: wir basteln uns ein neues Plugin

Das haben wir schon ein paar mal gemacht, das wiederhole ich nur im Schnelldurchgang. Plugin-Verzeichnis anlegen, PHP-Datei anlegen, Header richtig ausfüllen, AdminScreen anlegen:

/*
Plugin Name: Evis CSV Importer
Plugin URI: http://localhost/wordpress/wp-content/plugins/evis-csv_importer
Description: Importiert eine CSV-Datei
Version: 1.0
Author: Evi Leu
Author URI: http://www.evileu.de
*/
add_action('admin_menu', 'evisbasicPluginMenu');

function evisbasicPluginMenu() {
$appName = 'EvisCSVImporter';
$appID = 'evis-csv-importer';
add_menu_page($appName, $appName, 'administrator', $appID, 'importerpluginAdminScreen');
}

function importerpluginAdminScreen() {
    echo "<h1>Importiere CSV Datei</h1>";
}

Import starten mit Button

Zum starten des Imports brauchen wir noch einen Button, den legen wir wie schon oft gehabt mit einem kleinen Formular an, das kommt als erstes in die Funktion importerpluginAdminScreen:

//Begin Formular
echo '<form method="post">';
echo '<input type="submit" name = "starten" value="Import starten"/>'."<br>";
echo "</form>";
// End Formular

if (isset($_POST['starten'])){

...

}

Pfad und Dateiname der Import-Datei fest verdrahtet

In die if-isset-Abfrage legen wir jetzt unsere Import-Funktionalität. Natürlich wäre es schick wenn man hier die Import-Datei mit einem Datei-öffnen-Dialog auswählen könnte, aber dazu brauchts eine Runde Javascript, und das sprengt hier definitiv den Rahmen. Ich machs mir einfach: da ich immer mit der selben Import-Datei arbeite, verdrahte ich den Pfad und den Dateinamen im Code, das kann sich dann jeder selber zurechtbiegen wie er es braucht. Jedenfalls wird die CSV-Datei mit fopen() geöffnet:

//Pfad zur Import-Datei fest verdrahtet
$datei=fopen("C:/xampp/htdocs/alte_programmierer/wp-content/plugins/evis-csv-importer/nur_adressen.csv","r+");

Datei zeilenweise einlesen und Debug-Ausgabe

Dann lege ich eine While-Schleife bis zum Dateiende (feof) an und lese mit fgets() zeilenweise ein. Jede Zeile wird mit explode() am Semikolon in ein Array eingespeist, und damit man gleich mal was sieht, gebe ich dieses Array dann noch zeilenweise aus, das ist nur zum Debuggen und fliegt später wieder raus. Ja, ich hörs schon, das müßte man mit einer for…next i Schleife machen, aber ich finde es so übersichtlicher.

//Begin Importdatei zeilenweise einlesen
while(!feof($datei))
{
    $zeile = fgets($datei);
    echo $zeile."<br>";

    //aktuelle Zeile am Semikolon in Array explodieren
    $arr_akt_zeile = explode(';',$zeile);

    //Debug-Ausgabe des Arrays
    echo $arr_akt_zeile[0]."<br>";
    echo $arr_akt_zeile[1]."<br>";
    echo $arr_akt_zeile[2]."<br>";
    echo $arr_akt_zeile[3]."<br>";
    echo $arr_akt_zeile[4]."<br>";
    echo $arr_akt_zeile[5]."<br>";
    echo $arr_akt_zeile[6]."<br>";
    echo $arr_akt_zeile[7]."<br>";
    echo $arr_akt_zeile[8]."<br>";
} //Ende Importdatei zeilenweise einlesen

fclose($datei);  

Am Ende nicht vergessen mit fclose() die Importdatei wieder zu schließen.

(Zwischenbemerkung: ja, ich kenne die Funktion fgetcsv(), die ist aber ein bißchen hakelig in der Bedienung. wir nehmen lieber fgets().)

So, das wars schon mit dem zeilenweisen Import der CSV-Datei. Jetzt müssen wir die eingelesenen Daten noch entsprechend verarbeiten, aber dazu gibt es einen neuen Beitrag.

WordPress als führendes System für das Mitgliederverzeichnis: ein Anfang

Erstmal eine Hypothese

Ich habs ja sonst nicht so mit theoretischen Konstrukten, ich bin eher praktisch veranlagt. Aber der Turnverein Weiß-Blau möchte seine Mitgliederstammdaten in Zukunft mit WordPress verwalten, also an die Arbeit. Ich gehe mal davon aus, daß der Import zumindest der Adressdaten mit Custom Fields geklappt hat und unser bestehender Mitgliederstamm jetzt sauber in den WordPress-eigenen Tabellen abgelegt ist.Notfalls mit einem kostenpflichtigen Plugin, das wars uns jetzt wert.

Erstmal sehen sie: nichts.

Kurz zur Erinnerung: für jedes Mitglied gibt es einen eigenen Beitrag. Im Beitragstitel steht die Mitgliedsnummer und der Name. Im Beitragsinhalt möchten wir die Adressdaten sehen. Wieso möchten? Die haben wir doch importiert? Ja aaaber – nach dem Import sehen wir im Beitrag jetzt erst einmal – nichts. Nur wenn man im Dashboard auf „Alle Beiträge“ geht und da bei einem Beitrag „Bearbeiten“ wählt und dann ein bißchen runterscrollt, dann sieht man die Benutzerdefinierten Felder mit ihren Inhalten.

fridolin_CF

fridolin_CF

Das ist unser Editor für Bestandsdaten. Wenn sich an den Adressdaten eines Mitglieds etwas ändern sollte, suchen wir uns den entsprechenden Beitrag und gehen in den Bearbeitungsmodus, hier können wir unsere Änderungen direkt einpflegen. Kann ich damit leben, die Benutzer brauchen halt eine kurze Einweisung in den Beitragseditor und sollten auch nicht vergessen auf „Aktualisieren“ zu klicken, dann funktioniert das schon.

Und wenn es neue Mitglieder einzutragen gibt?

Natürlich könnte man einfach in WordPress einen neuen Beitrag erstellen und die Daten manuell eingeben, aber das ist dann schon ein bisschen sperriger in der Benutzerführung. Man muß nämlich die relevanten Benutzerdefinierten Felder einzeln hinzufügen, und das ist aufwendig und fehlerträchtig. Ausserdem muss man sich genau überlegen, woher man die Mitgliedsnummer nimmt, unsere so wichtige eindeutige ID. Was gibt es für Alternativen?

Man könnte die neuen Benutzer in der alten Excel-Liste eintragen, die Mitgliedsnummern fortlaufend weiterführen und einen neuen Import fahren, aber was passiert dann mit unseren Bestandsdaten? Werden die „alten“ Mitgliederdaten dann überschrieben? Was passiert mit Änderungen, die ich in den Bestandsdaten eventuell bereits gemacht habe? Wäre dann die Excel-Datei unser führendes System, und nicht WordPress? Lauter Fragen, auf die es so ad hoc keine einfachen Antworten gibt.

Noch ’ne Hypothese

Viele CSV-Import-Plugins, die ich ausprobiert habe, bieten zumindest in der Pro Edition ein Feature, mit dem man auswählen kann ob bereits bestehende Datensätze upgedated werden sollen oder nicht. Ausprobieren konnte ich das nicht, aber es scheint mir die einzig praktikable Lösung zu sein, wenn man neue Mitglieder-Datensätze nicht einzeln manuell eingeben, sondern per neuem Import hinzufügen möchte. Das würde natürlich bedeuten daß wir von unserer alten Excel-Tabelle nicht wegkommen, aber sei’s drum. Gehen wir mal davon aus, dass das funktioniert mit dem selektiven Import, und kümmern wir uns mal um das nächstliegende Problem:

Die immer noch unsichtbaren Custom Fields

Man sieht die benutzerdefinierten Felder ja nach wie vor nur im Beitragseditor, und das soll sich jetzt schnellstens ändern. Eine sehr kurze und praktische Lösung ist ein Shortcode, den wir wie gewohnt in die functions.php unseres Child-Themes setzen. Der kann zum Beispiel so aussehen:

function ms(){
    
    $aktID=get_the_id();
    $strassehsnr= get_post_meta($aktID, 'strassehausnummer', true);
    echo $strassehsnr;
        
}
add_shortcode('meinshortcode','ms');

Ich habs ein bisschen ausführlicher als unbedingt nötig hingeschrieben, aber so läßt sichs besser erklären. Die Funktion ms() macht Folgendes:

  • get_the_id() holt die WordPress-ID des aktuellen Beitrags
  • get_post_meta() holt zur aktuellen Beitrags-ID den Wert des Custom Field namens „strassehausnummer“, das „true“ am Ende bewirkt, daß eine Einzelvariable ausgegeben wird und kein Array
  • echo gibt die ganze Chose aus.

Fertig! Shortcode in einen Beitrag einsetzen, Beitrag ansehen und da steht sie, die Strasse und Hausnummer. Die anderen Custom Fields holt man sich mit genau der gleichen Mechanik, man muß halt nur den get_post_meta() mit dem korrekten Namen des Feldes füttern. Also, damit kann ich jetzt echt leben!

Verwendung im Theme

Man kann die Funktion get_post_meta() natürlich auch in einem Template nutzen, sie z.B. an geeigneter Stelle in ein Child seiner single.php einbauen, aber das ist sehr Theme-spezifisch, da muss jeder selber rumdoktern, das versuche ich zu vermeiden wo es nur geht. In der Praxis wird man sich wahrscheinlich einen benutzerdefinierten Post Type anlegen und dort die entsprechenden Ergänzungen vornehmen, aber davon laß ich hier mal die Finger, das sieht wie gesagt in jedem Theme anders aus. Mir reicht mein funktionaler Shortcode, der ist universell einsetzbar.

Ja, aber was ist jetzt mit dem Anlegen neuer Mitglieder?

Machen wir das echt im alten Excel?
Gute Frage, und ich glaube, dazu brauchts einen neuen Beitrag.

Da staunste Bauklötze: vielleicht serialized, vielleicht nicht

Wenns klemmt im Datenkorsett

Ich habe mich schon an anderer Stelle darüber ausgelassen, daß das Datenmodell von WordPress alles andere als relational wohlstrukturiert und insgesamt renovierungsbedürftig ist.  Wenn  man sich ein bißchen mit multifunktionalen Plugins wie z.B. woocommerce beschäftigt, stellt man immer wieder fest, daß Entitäten in die vorhandenen WordPress-Tabellen gequetscht werden, die eigentlich nicht wirklich hineinpassen. Um beim Beispiel woocommerce zu bleiben, weil das ja doch sehr verbreitet und gut dokumentiert ist: da werden die zu verkaufenden Produkte in der Tabelle wp_posts wie Beiträge angelegt, und was man hier an Produkteigenschaften und benutzerdefinierten Feldern nicht mehr untergebracht hat, landet in der wp_options, der wp_postmeta oder in der wp_terms und ihren Verwandten. Damit man hier auch alles unterbringt, obs jetzt logisch paßt oder nicht, kommt ein tolles Konstrukt zum Einsatz (übrigens nicht nur in WordPress, das gibts auch anderswo):

Serialized Data

Was ist das? Wenn sie einen Datenbankeintrag finden , der so aussieht:

a:1:{s:14:“pa_code“;a:6:{s:4:“name“;s:14:“pa_code“;s:5:“value“;s:0:““;s:8:“position“;i:0;s:10:“is_visible“;i:1;s:12:“is_variation“;i:0;s:11:“is_taxonomy“;i:1;}}

Das sind serialisierte Daten. Es handelt sich hier um ein Produktattribut namens „code“, das in dieser besonders verklausulierten Form abgelegt wurde. Wer genauer wissen möchte, was serialisierte Daten sind, kann hier im PHP-Manual mal nachlesen, und mir ganz ehrlich sagen wenn er auch nur die Hälfte verstanden hat. Soweit ich es kapiert habe, erlaubt es die Serialisierung, in einem einzigen Datenbankfeld mehrere Variable unterschiedlicher Datentypen zu speichern.  In einem Array, mit Hilfe eines Objekts. Aber, ich gebe wie gesagt ehrlich zu ich hab nicht genau verstanden, wie das wirklich funktioniert. Da schwirrt es in den Manuals nur so von Objekten und Klassen und Methoden, und mir fehlt echt die Geduld (und der Hang zur Verkomplizierung) mich da weiter einzulesen. Ich habs versucht, und bin dabei unter anderem auf einen besonderen Leckerbissen gestossen:

Eine Funktion namens maybe_serialize()

Allen Ernstes. Laut Codex macht die Folgendes: sie serialisiert Daten, falls nötig. Ich muß da unbedingt mal wörtlich zitieren:

Confusingly, strings that contain already serialized values are serialized again, resulting in a nested serialization. Other strings are unmodified.

Ach nee, „confusingly“, also verwirrenderweise? Da brat mir doch einer einen Storch! Und wie kriegt man die Daten da wieder raus? Gute Frage. Es gibt tatsächlich auch eine Funktion namens maybe_unserialize(), ist das nicht schön? Irgendwas muß man damit doch anfangen können… meine Recherchen haben aber noch nicht ergeben, was man sinnvollerweise damit macht.

Wofür soll das alles gut sein?

Der Knackpunkt ist: hier werden buntgemischte Daten in ein einziges Tabellenfeld geklemmt, statt sie sauber nach Datentypen getrennt in einer passenden Tabelle zu speichern. Damit bringt man mehr Informationen als vorgesehen in einer bereits vorhandenen Tabelle unter, aber das Auslesen und Weiterverarbeiten wird beliebig kompliziert und erstmal sehr verwirrend und umständlich. Und mal ganz im Ernst: eine Funktion, die „maybe“ irgendwas macht, die kann ich nicht gebrauchen. Das sind Stilblüten der objektorientierten Programmierung, die allem widersprechen, was ich über Datenmodellierung und Tabellenstrukturierung je gelernt habe.

Um beim Beispiel woocommerce zu bleiben: das Plugin gönnt sich ja ohnehin ein gutes Dutzend eigener Tabellen. Warum legt man dann für die Produktattribute nicht auch eine eigene Tabelle an, in der die nötigen Daten in lesbarer und vor allem auch per SQL auswertbarer Form gespeichert werden? Kostet auch nicht mehr, und wäre unendlich benutzerfreundlicher. Den Mumpitz mit den vielleicht oder auch nicht serialisierten Daten könnte man sich dann echt sparen.

Hooks – jetzt kommt Action ins Spiel

Was sind WordPress Hooks?

Das kann man googlen, und z.B. die netzialisten haben einen sehr informativen Beitrag zu dem Thema geschrieben. Sehr kurz und knapp gesagt, Hooks sind definierte Ankerpunkte im WordPress-Code, an denen man eigene Funktionen einhängen kann. Dabei unterscheidet man zwischen Action Hooks und Filter Hooks, dazu gleich mehr. Eine umfassende Liste aller WordPress Hooks hat Adam R. Brown in seiner Hook Database zusammengetragen. Auch der Codex zur Plugin API bietet umfangreiche Information zum Thema.

Unterschied zwischen Action und Filter

Ich zitiere hier mal schlicht und unbefangen den Codex:

You can sometimes accomplish the same goal with either an action or a filter. For example, if you want your plugin to change the text of a post, you might add an action function to publish_post (so the post is modified as it is saved to the database), or a filter function to the_content (so the post is modified as it is displayed in the browser screen)

Also: wenn man Beiträge gezielt verändern möchte, z.B. eine Copyright-Zeile an jeden veröffentlichten Beitrag anhängen, gibt es (mindestens) zwei Wege zum Ziel. Hier wird empfohlen: entweder man verwendet einen action-Hook auf den Event publish_post, der beim Speichern eines Beitrags in der Datenbank die Copyright-Zeile anhängt. Oder man hakt sich mit einem Filter auf the_content an den Inhalt des Beitrags ran, und hängt die Copyright-Zeile beim Anzeigen des Beitrags in der Einzelansicht an. So weit so gut – ich hab mir allerdings einen Wolf gegooglet, um ein funktionierendes Beispiel für den action hook zu finden, leider bis jetzt ohne brauchbares Ergebnis. Das mit dem Filter dagegen geht relativ easy:

Der Filter

könnte zum Beispiel so aussehen:

function copyright_nach_post_content($content){
if (is_single()) {	
	$content .= '<p>Hier kommt dein Copyright (C) EL 2017 hin </p>';
}
	return $content;
}
add_filter( "the_content", "copyright_nach_post_content" );

Dieser Code kommt in die functions.php unseres Child-Themes und zieht jedesmal, wenn ein Beitrag aufgerufen wird. Die Abfrage auf if(is_single()) stellt sicher, dass auch wirklich nur Beiträge in der Einzelansicht betroffen sind.

Der entsprechende Action-Hook

war wie oben bereits gesagt nicht aufzufinden. Dann machen wir halt was anderes, wo richtig Action drin ist:

Wir versenden eine Email, wenn ein neuer Beitrag veröffentlicht wurde

Dabei stelle ich euch die simple, aber sehr effektive Funktion wp_mail vor. Der Code sieht so aus:

add_action('publish_post', 'send_emails_on_new_post');

function send_emails_on_new_post($post)
{
    $emails = "evi.s.leu@gmx.de"; 
    $title = get_the_title($post->ID);
    $url = get_permalink($post->ID);
    $message = "Link zum Beitrag: \n{$url}";
 
    wp_mail($emails, "Neuer Beitrag! {$title}", $message); 
}

Zuerst wird der action-hook auf das Ereignis publish_post gesetzt und mit unserer Funktion verknüpft. Diese bekommt als Parameter den $post mit, darüber können wir auf die Attribute des aktuellen Beitrags zugreifen, das tun wir mit der $post->ID. Wir holen uns den Titel und den Permalink und basteln uns daraus eine kleine E-Mail-Nachricht an den oben eingesetzten Empfänger.Die Funktion wp_mail braucht ganz minimalistisch wirklich nur 3 Parameter, den Empfänger, den Betreff und den Inhalt, und schon fluppts! Der Empfänger wird jedesmal benachrichtigt, wenn ein neuer Beitrag veröffentlicht wurde.

Ein übersichtlicher Beitrag zur wp_mail-Funktion ist hier bei OSTraining zu finden, die kompletten Informationen findet ihr natürlich im Codex.

Tabelleneditor revisited: und jetzt das Ganze nochmal als Shortcode

Ja, ich weiß, ich habs unterschlagen. Wir haben uns einen hübschen kleinen Tabelleneditor im Admin Panel gebastelt, mit Unterseite und allem Pipapo. Aber da kamen schon die ersten Rückfragen. Was, wenn ich meinen Besuchern die Möglichkeit anbieten möchte, Tabellendaten auf meiner Webseite zu editieren? Wie sieht das dann die Verzweigung auf die Unterseite aus? Ein Anwendungsfall wäre es zum Beispiel, wenn ich meinen Nutzern das Editieren der eigenen Adressdaten ermöglichen möchte. Da wird man zwar im Zweifelsfall ein Login vorschalten, denkbar ist z.B. daß sich der Benutzer mit seiner Mitgliedsnummer anmeldet und diese beim Aufruf des Unterformulars abgefragt wird. Das führt mir aber jetzt entschieden zu weit, ich lasse es mal bei dem Formular mit den Edit-Buttons, sonst blickt hier keiner mehr durch. Die Funktionalität wird also die selbe sein wie beim Plugin Adressen Tabelle, nur diesmal auf der Blog-Oberfläche.

Wir basteln uns einen Shortcode

Der erste Teil ist simpel. Wir schnappen uns die Funktion pluginAdressenTabelle() und schieben sie in einen Shortcode. Für den legen wir uns eine neue Seite an und fügen ihn dort ein, ich hab ihn mal [adr_tabelle] genannt. Das müsste eigentlich ohne Änderungen klappen, das Ergebnis sollte etwa so aussehen:

shortcode_adresseneditor

shortcode_adresseneditor

Wenn man jetzt auf einen „Edit“-Button klickt, landet man (logischerweise) noch im Admin Panel, und das soll natürlich nicht sein, da brauchen wir was anderes. Nämlich eine Blogseite, die nicht im Menü erscheinen soll, sondern nur durch Klick auf den Edit-Button aus der aufrufenden Seite auftauchen darf.

Seite ohne Menüeintrag? Mit Menü!

Man legt eine neue Seite an, benennt sie entsprechend, z.B. Adresseneditor Unterseite. Dann legt man unter Design/Menüs ein neues Menü an, in das man alle Seiten ausser der eben erstellten einträgt (alle markieren und dann bei der Einzelseite das Häkchen entfernen). Ist ein bißchen von hinten durch die Brust ins Auge, aber so funktionierts.

Aufzurufende Seite bei Formular-Action hinterlegen

Wir klemmen uns einfach den Permalink der soeben erstellten Unterseite und legen sie auf die Variable für den Action-Tag, das sieht dann z.B. so aus:

$meinplugin_URL = „http://localhost/turnverein/adresseneditor/adresseneditor-unterseite/“;

Das wars schon.

Wie kommt jetzt unsere Editor-Funktionalität in die Unterseite?

Per Shortcode! Mein Freund und Lieblingshelfer, ich sags immer wieder. Wir erstellen einen neuen Shortcode, mit aussagekräftigen Namen, z.B.:

add_shortcode (‚mein_adr_editor‘, ‚meinAdressenEditor‘);

Dann brauchen wir natürlich noch die Funktion zum Shortcode, die heißt jetzt meinAdressenEditor(). In diese kopieren wir uns den ganzen Inhalt der Funktion AdresseEditAusgabe() aus dem Plugin, das wir vorher erstellt haben. Nicht vergessen unten den Link zurück auf die aufrufende Seite zu aktualisieren, der sollte jetzt etwa so ausehen:

echo „<a href = ‚http://localhost/turnverein/adresseneditor/’/><h1>Zurück zur Adressenliste</h1></a>“;

Den soeben erstellten Shortcode [mein_adr_editor] in die Unterseite einfügen, speichern und feddisch, das wars! Die Bildschirmausgabe sollte etwa so aussehen:

adresseneditor_unterseite

adresseneditor_unterseite

Übrigens nicht wundern: wenn man sich die Unterseite „allein“ anschaut, also aus dem Edit-Modus mit „Seite ansehen“, sieht man – nichts. Da fehlt der Parameter mit der ID, also ergibt unser Select eine leere Ausgabe, und dann sieht mal halt nichts, punktum.

So, jetzt hab ich aber alle Aspekte des Tabelleneditors erschöpfend behandelt – hoffe ich zumindest. Wenn jetzt noch Rückfragen kommen – ich bin unbekannt verreist 😉