Schlagworte/Tags nach Joomla importieren: ohne Gewährleistung

Mit diesem Codebeispiel von der Stackexchange ging es jetzt doch relativ flott, ich habe mir eine Mechanik gebastelt, mit der ich tatsächlich Schlagwörter nach Joomla importieren kann. Ich zeig hier erst mal nur den funktionalen Code für ein Schlagwort, die Schleife über eine Liste (im Zweifelsfall eine Import-Tabelle) kann sich jeder selber dazu denken. Die ganze Sache sieht so aus:

$data = "Joschis Cocktailbar";
 $date = new JDate();
 include_once JPATH_BASE . '/administrator/components/com_tags/tables/tag.php';
 $table = new TagsTableTag(JFactory::getDbo());

 $table->title = $data;
 $table->note = '';
 $table->description = '';
 $table->published = 1;
 $table->checked_out = 0;
 $table->checked_out_time = '0000-00-00 00:00:00';
 $table->created_user_id = 839;
 $table->created_time = $date->toSql();
 $table->modified_user_id = 0;
 $table->modified_time = '0000-00-00 00:00:00';
 $table->hits = 0;
 $table->language = '*';
 //$table->parent_id = 1; (hat keine Auswirkung)
 //$table->level = 1; (hat keine Auswirkung)
 $table->path = JFilterOutput::stringURLSafe($data);
 
 $table->check();

 $table->store();

Warum sich die parent_id und der level nicht setzen lassen ist mit ehrlich gesagt völlig schleierhaft, aber es scheint weiter keine Auswirkungen zu haben, der Tag „Joschis Cocktailbar“ wird mit diesem Code erzeugt und scheint auch regulär zu funktionieren. Jetzt noch die Daten aus einer Importtabelle laden und mit einem Foreach durchsteppen, das ist der nächste Schritt, das mach ich später noch.

Was, wenn ein Schlagwort schon vorhanden ist?

Soweit ich das nachvollziehen konnte, wird es dann nicht neu angelegt, und auch nicht überschrieben, sondern der Datensatz wird dann nicht neu erzeugt.

Und woher nehm ich die Kategorienliste aus WordPress?

Das habe ich in diesem Beitrag zu den WordPress-Kategorien ausführlich beschrieben, das sollte jeder nachvollziehen können. Wir nehmen da einen kleinen Umweg über mein Leib-und Magendatenbankerl Access, das ist etwas handlicher als MySQL.

Warum ohne Gewährleistung?

Weil die programmatisch neu erzeugten Schlagwort-Datensätze nicht ganz logisch identisch mit den manuell angelegten sind, so bleiben z.B. die parent_id und der level beide = 0. Was das im Ernstfall für Auswirkungen hat kann ich nicht sagen, dafür ist mir die Logik der #__tags-Tabelle zu undurchsichtig. Die schliesst nämlich die Möglichkeit zum Schachteln von Tags in einer Baumstruktur ein, das hab ich bisher unterschlagen dass das in Joomla geht. Ich verwende es aber auch nicht, deswegen sei mir die Auslassung erlaubt. Wer sich dran versuchen möchte, geschachtelte WordPress-Kategorien nach geschachtelten Joomla-Tags abzubilden, bittesehr, aber ich mach hier mal einen Punkt. Die Schlagwörter sollten sich so importieren lassen, und das ohne Handarbeit des Praktikanten. Ist doch mal ein ganz guter Anfang. Jetzt müssten wir sie nur noch unseren zu importierenden Rezepte zuordnen können, aber dazu gibts eine Denkpause und einen neuen Beitrag.

Kleiner Nachtrag

Ich hab den Import jetzt mal durchgezogen, hat klaglos funktioniert, alle 28 Datensätze sind einwandfrei in Joomla als neue Tags importiert worden. Jetzt gehts dann aber ans Eingemachte, das mit dem Mapping wird ne harte Nuss!

WordPress-Kategorien auf Joomla-Tags abbilden? Vorüberlegungen

Etwas vereinfachte Voraussetzungen

OK, meine Ausgangsbasis ist relativ simpel, ich habe (bis auf eine Ausnahme) im Original-Inselfisch-Kochbuch keine geschachtelten Kategorien verwendet, das spielt sich alles auf einer Ebene ab. Deswegen kann ich es mir erlauben, die Kategorien in Joomla als Schlagwörter (Tags) abzubilden. Ich habe mir auch die Einschränkung erlaubt, dass ich Schlagwörter in Joomla nur für die Rezepte verwende, für meine einzige andere „echte“ Joomla-Kategorie, die Kochbücher, werden keine Tags verwendet, und auch sonst nirgendwo. Mit dieser Mechanik kann ich bisher manuell meine Schlagwörter der Reihe nach erfassen und jedem Rezept zuordnen. Jetzt wärs natürlich schick, wenn man beim Datenimport irgendwie die Kategorien aus WordPress mitgeben und diese als Schlagwörter in Joomla automatisch zuordnen könnte. Aber ich fürchte, das wird ein langer Weg… na ja, auch der fängt mit dem ersten Schritt an.

Kann man Schlagwörter überhaupt importieren?

Geht bestimmt, die Frage ist nur: wie aufwendig? Und: wann macht man das? Ich denke, wir sollten hier von einer jungfräulichen Joomla-Installation ausgehen, in die noch keine Rezeptdaten (ich bleibe beim Inselfisch-Kochbuch) importiert worden sind, und in der auch noch keine Schlagwörter per Hand angelegt worden sind. Dann könnte man es mal versuchen, ich hab auf Stack Exchange einen Artikel gefunden: Adding tags to Joomla programmatically. Der sollte eigentlich genau das machen, was wir brauchen, aber ehrlich gesagt, das muss ich mir nochmal in aller Ruhe anschauen.

Und dann hätte ich noch Plan B: es sind nur ca. 30 Kategorien. Die können wir schlimmstenfalls auch zu Fuß einhacken, das macht der Praktikant. Aber jetzt gesetzt mal den Fall, wir schaffen es die Tags zu importieren, was dann?

Zuordnung der Joomla-Tags zu den WordPress-Kategorien

Da wird in irgendeiner Form ein Mapping stattfinden müssen. Das könnte ein bisserl kompliziert werden, weil ich in WordPress theoretisch beliebig viele Kategorien zu einem Beitrag zuordnen kann. Ich mach hier mal einen rigorosen Cut und sage: drei Kategorien pro Beitrag, das muss reichen.

Zuordnung der gemappten Schlagwörter zu den importierten Beiträgen

Sagen wir mal, ich schaffe es die WordPress-Kategorien den Joomla-Tag-ids zuzuordnen und beim Import mitzugeben. Weitergehen könnte es dann so in der Art wie in diesem Codeschnipsel:

$basePath = JPATH_ADMINISTRATOR.'/components/com_content';
require_once $basePath.'/models/article.php';
$articlemodel = new ContentModelArticle(array('table_path' => $basePath . '/tables'));

$params = array(
    'id' => 123,                // Article being tagged
    'tags' => array(7,8,9,14)   // Tag IDs from #__tags to tag article with
);
if($articlemodel->save($params)){
    echo 'Success!';
}

Wie gesagt, das Mapping auf die jetzt in Joomla schon vorhandenen Schlagwörter muss irgendwie hinhauen. Das ist jetzt erstmal ein Haufen Holz, ich geh mir jetzt wieder mal mein Denkmützerl aufsetzen und schaue, wie weit ich komme. Wenn ich was rausgefunden habe, gibts einen neuen Beitrag.

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.

PHP Bridge für Joomla – wenn der Sourcerer an Grenzen stößt

Ich hab ja jetzt schon viel PHP mit dem Sourcerer eingesetzt, aber für komplexere Skripte ist das nur eine Krücke. Jetzt bin ich auf meinen Recherchen auf die PHP Bridge von Henry Schorradt gestossen, und die ebnet den Weg erheblich. Schaut mal hier auf Henrys Homepage:

http://www.henryschorradt.de/information/joomla/

Da ist bestens erklärt, wie das Ding einzusetzen ist, und hier auf youtube gibts auch noch ein informatives Video dazu – steile Sache!

Der Knackpunkt ist: man kann eigene PHP-Skripte im Template-Pfad ablegen und mit der Bridge als Menuitems zugänglich machen. Diese werden dann innerhalb der Component ausgeführt, d.h. sie füllen nur den Inhaltsteil. Das macht das Leben mit eigenen PHP-Scripten in Joomla doch deutlich leichter!

Das Ding wird meine Basis für den WP-Import, glaube ich. Ich geh mal weiter recherchieren…

So schnell geb ich nicht auf: Joomla-Artikel importieren, eine heisse Spur

Ich sitze ja hier immer noch auf über 300 Rezepten aus dem Inselfisch-Kochbuch und habe keine Lust, die per Copy&Paste nach Joomla reinzuschubsen, mehr zu dem Thema habe ich bereits in diesem Artikel erzählt. Jetzt bin ich bei Stackexchange auf einen vielversprechenden Ansatz gestossen, wie man einen Joomla-Artikel programmatisch erzeugen kann, mehr Infos hier:

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

Im letzten Beitrag des Threads (Support Joomla 2.5 and Joomla 3.0) findet sich ein Sourcecode, der ohne weitere Modifikationen funktioniert. Ich zitier den mal hier rein:

if (version_compare(JVERSION, '3.0', 'lt')) {
    JTable::addIncludePath(JPATH_PLATFORM . 'joomla/database/table');        
}   
$article = JTable::getInstance('content');
$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>';
$article->catid            = 9;
$article->created          = JFactory::getDate()->toSQL();
$article->created_by_alias = 'Super User';
$article->state            = 1;
$article->access           = 1;
$article->metadata         = '{"page_title":"","author":"","robots":""}';
$article->language         = '*';

// 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;
}

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

Das sieht soweit gut aus, fehlt höchstens noch ein Eintrag für das Feld created_by (bei mir 839), und die metadata sehen bei mir in der Datenbank auch einen Hauch anders aus, aber das ist Kleinkram. Die catid wäre eigentlich immer 8 für Rezepte, das kann man fest verdrahten. Ich habs mal in den Sourcerer reingeschubst und ein paar mal laufen lassen, die neuen Artikel sind einwandfrei erzeugt worden.

So, jetzt gilts: wie kriegen wir unsere WordPress-Datensätze hier rein?

Eigentlich müssen wir nur diese drei Felder ordentlich füllen:

$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>';

Schön wärs natürlich, wenn wir die WordPress-Kategorien als Tags mit übernehmen könnten, aber das wär nur ein extra-Zuckerl, jetzt kümmere ich mich erstmal um den Überbau, und der erfordert etwas Planung. Ich geh mal mein Denkmützerl aufsetzen und melde mich dann wieder mit einem neuen Beitrag.

 

Zusätzliche Felder in BreezingForms – nee, da mach ich lieber was eigenes

BreezingForms machts einem nicht leicht

Also, ich sag das ja ungern, aber nach einigen Tagen fruchtloser Recherche bin ich fast soweit zu sagen: ich gebs auf. Was ich wollte: mein BreezingForms-Formular um ein dynamisches Feld erweitern, in dem entweder die ID oder der Titel des Rezeptes erscheint, von dem aus das Formular abgeschickt wurde. Im Prinzip sollte das mit einem versteckten Feld funktionieren, aber das liess sich bei mir nicht speichern – eine Einschränkung der Free Edition? Bei Crosstec war nichts zum Thema zu finden, direkten Support gibts dort nur gegen Cash. In den Joomla-Foren hab ich auch nichts zielführendes gefunden, vielleicht war mir auch nur Tante Google nicht gewogen, aber ich mach da jetzt erstmal einen Break und denke über eine andere Lösung nach.

Doch was selbergestricktes?

Könnte ja eigentlich so schwer nicht sein. Ich muss ja noch nicht mal unbedingt eine E-Mail schicken, mir würde es reichen wenn die Rezeptbewertungen in einer eigenen Tabelle gesammelt werden. Aber eben mit dem Titel bzw. der ID des Rezeptes, sonst ist die ganze Mechanik nichts wert. Ich zieh mich mal zurück und gehe brainstormen. Schaun mer amal, was dabei herauskommt.

Wir fangen mal mit dem Sourcerer an

Ein kleines HTML-Formular ist schnell gestrickt, das sieht jetzt mal im ersten Ansatz so aus:

<form action=“#“ method=“post“>
Ihre Meinung ist mir wichtig – bitte bewerten Sie dieses Rezept!<br>
<!– Radio buttons für Wahl der Bewertung –>
<input type=“radio“ name=“Bewertung“ value=“War nix“>War nix<br>
<input type=“radio“ name=“Bewertung“ value=“War prima“>War prima<br>
<input type=“submit“ name=“absenden“ value=“Bewertung absenden“>
</form>

Einfach zwei Radiobuttons und ein „absenden“-Button, mehr ist nicht dran. Der erste Ansatz zum Auswerten wäre dann dieser:

<?php

if (isset($_POST[‚absenden‘])){
if (isset($_POST[‚Bewertung‘])){
$note = $_POST[‚Bewertung‘];
echo „Vielen Dank für Ihre Bewertung: „.$note;

 //hier kommt die Mechanik fürs Wegschreiben hin

}
else{
echo „Bitte eine Bewertung auswählen!“;
}
}
?>

Was brauchen wir noch?

Die ID/den Titel des Rezepts. Na dann mal ran, das hatten wir alles so oder ähnlich schonmal, wir holen uns aus der JFactory die aktuelle Artikel-ID, und aus der Query das Feld title:

$article_id = JFactory::getApplication()->input->get(‚id‘);
$db=JFactory::getDbo();
$db->setQuery(’select title from #__content where id= ‚.$article_id.“);
$title=$db->loadResult();

Bitte, da haben wir doch alles was wir brauchen!

Jetzt könnte man zum Beispiel schon eine Mail zusammenbasteln:

// Die Nachricht
$nachricht = $article_id.“ „.$title.“ „.$note.“ „.$datum;
$nachricht = wordwrap($nachricht, 70);

// Send
mail(‚benutzer@example.com‘, ‚Rezeptbewertung‘, $nachricht);

Und das wars auch schon! In meinem Mailtodisk-Output kommt dabei Folgendes an:

To: benutzer@example.com
Subject: Rezeptbewertung
X-PHP-Originating-Script: 0:sourcerer_php_640dafb7b21f4d69df5677f28a8472eb

31 Schweinsbarten mit Sourcerer War prima 22-02-2018

Das ist genau das, was ich wollte, mir reicht dieser spartanische Output völlig, steht alles drin was ich wissen muss.

Wollen wirs noch in die Datenbank schreiben?

Klar wollen wir! Wir legen erstmal eine kleine Tabelle namens rezeptbewertungen an:

CREATE TABLE `jsite`.`rezeptbewertungen` ( `Datum` TEXT NULL , `artikelid` TEXT NULL , `artikeltitel` TEXT NULL , `note` TEXT NULL ) ENGINE = InnoDB;

Viele Wege führen nach Rom, ich hab mich für insertObject() entschieden. Dafür holen wir uns ein neues Objekt der stdClass und belegen es mit unseren Feldnamen aus der Tabelle (Groß- und Kleinschreibung beachten!). Die Values für den Insert haben wir ja schon parat, die liegen alle auf Variablen, die schreiben wir einfach rein:

$data = new stdClass();
//Objekt füllen
$data->Datum = $datum;
$data->artikelid = $article_id;
$data->artikeltitel = $title;
$data->note = $note;

$tablename = „rezeptbewertungen“;

//Insert ausführen
$db = JFactory::getDBO();
$db->insertObject($tablename, $data);
$result = $db->query();

//Erfolgsmeldung ausgeben
if ($result == true) {
echo ‚Datensatz erfolgreich gespeichert!‘;
}else{
echo ‚Fehler beim Insert!‘;
}

Das wars schon!

Bilanz

Wir haben jetzt eine Mechanik, die eine E-Mail aus den relevanten Daten des Bewertungsformulars baut und diese auch in unsere eigene Tabelle wegschreibt. Was noch fehlt: jetzt müsste man das ganze Spiel noch in den Override für die Artikel einbauen, und zwar mit der Abfrage, ob wir denn ein Rezept sind, weil nur dann das Bewertungsformular auch angezeigt werden soll. Dafür denke ich sollte man ein Modul basteln, in das das Formular mit der ganzen Mail- und Insert-Logik eingebaut wird, und dieses dann im default-Override an geeigneter Stelle einfügen. Wie das geht, habe ich im vorigen Artikel anhand des BreezingForm-Moduls schon beschrieben, das sollte ganz genau so funktionieren. Viel Spaß beim Ausprobieren!

Anmerkung zur E-Mail-Verschleierung

Falls in ihrem Mailoutput so etwas ankommt, hat die eingebaute E-Mail-Verschleierung von Joomla gegriffen:

To: Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!     document.getElementById(‚cloakc1626f4c69a0a7ef61ea78b168523217‘).innerHTML = “;     var prefix = ‚ma‘ + ‚il‘ + ‚to‘;     var path = ‚hr‘ + ‚ef‘ + ‚=‘;     var addyc1626f4c69a0a7ef61ea78b168523217 = ‚benutzer‘ + ‚@‘;     addyc1626f4c69a0a7ef61ea78b168523217 = addyc1626f4c69a0a7ef61ea78b168523217 + ‚example‘ + ‚.‘ + ‚com‘;     var addy_textc1626f4c69a0a7ef61ea78b168523217 = ‚benutzer‘ + ‚@‘ + ‚example‘ + ‚.‘ + ‚com‘;document.getElementById(‚cloakc1626f4c69a0a7ef61ea78b168523217‘).innerHTML += ‚<a ‚ + path + ‚\“ + prefix + ‚:‘ + addyc1626f4c69a0a7ef61ea78b168523217 + ‚\‘>’+addy_textc1626f4c69a0a7ef61ea78b168523217+'<\/a>‘;
Subject: Rezeptbewertung
X-PHP-Originating-Script: 0:sourcerer_php_ec1d4b855a01f009702bc4b9389cf683

33 Kartofflebrei mit Title War nix 23.02.2018

Kann man auch ausschalten, dafür das Plugin Inhalt – E-Mail – Verschleierung deaktivieren, dann kommts wieder im Klartext, wenn man das will:

To: benutzer@example.com
Subject: Rezeptbewertung
X-PHP-Originating-Script: 0:sourcerer_php_71e38a5d68ac0a27865028e9085819ea

33 Kartofflebrei mit Title War prima 23.02.2018

Inhalt – E-Mail-Verschleierung

 

Hätt ich doch beinah übersehen: das Bewertungsformular

Das wär mir doch beinah durch die Lappen gegangen, wir brauchen natürlich noch das Feedback-Formular bei den Rezepten. Im Original-Inselfischkochbuch sieht das so aus:

bewertungsformular_screenshot

bewertungsformular_screenshot

Ich habs in WordPress natürlich mit dem Contact Form 7 angelegt, und dann das Plugin WP Post Signature genutzt, um das Formular automatisch am Ende jedes Beitrags einzufügen. Wollen mal sehen, was sich in Joomla daraus machen läßt. Ich hab mal einen ersten Versuch mit BreezingForms gestartet, schauen wir mal wie weit wir kommen.

Die Anforderungen im Klartext:

  1. das Formular soll am Ende jedes Rezeptes automatisch angehängt werden
  2. damit ich weiß, welches Rezept bewertet wurde, muss der Titel (oder wenigstens die ID) aus der E-Mail hervorgehen

Zu 1.: das hört sich einfacher an als es ist, denn eigentlich sind es zwei Anforderungen, wenn nicht sogar drei.

Das BreezingForms-Plugin

Um ein BreezingForms-Formular überhaupt in einem Artikel anzeigen zu können, muss man das BreezingForms-Plugin installieren. Das findet man, wenn man das BreezingForms-ZIP entpackt hat, unter plg_breezingforms.zip . Nach der Installation muss es noch aktiviert werden, das geht unter Erweiterunge->Module-> Suche nach „breezing“, Pluginname BreezingForms aktivieren. Erst jetzt hat man die Möglichkeit, ein BreezingForms Formular auch in einen Artikel einbinden zu können, und zwar  mit der Syntax:

{ BreezingForms : formularname }

Das fett markierte ist der Name des Formulars. Aber damit haben wir noch nicht viel gewonnen, schließlich müßte man diesen Code am Ende jedes Rezeptes manuell einfügen, und das war nicht die Anforderung.

Wie wärs stattdessen mit einem Modul?

Dafür braucht man laut crosstec noch das extra-Modul, mod_breezingforms.zip. Nach der Installation freigeben nicht vergessen! Dann kann man ein neues Modul vom Typ „BreezingForms“ anlegen und das gewünschte Formular angeben. Falls es nicht angezeigt wird kann es sein dass in den Moduloptionen in der Menüzuweisung „auf keinen Seiten“ steht, das muss man natürlich auf „auf allen Seiten“ ändern.

So, und was hat uns das gebracht?

Auch das Modul muss man erstmal per Hand am Ende jedes Rezeptes einfügen, das ist noch kein grosser Fortschritt aber gemach, gemach, das wird schon noch.

Warum nicht einen Override anlegen?

Weil bei einer Änderung der default.php das Bewertungsformular unter allen Artikeln angezeigt werden würde, nicht nur unter den Rezepten. Wir machen stattdessen mal ein alternatives Layout, das ist mir sympathischer.  Dafür kopiert man sich in den Ordner /templates/dein-templates/dein-template/html/com_content/article die Datei default.php unter einem eigenen Namen, ich nenns mal meins.php. In dieser Datei sucht man dann nach dem Eintrag

<?php echo $this->item->text; ?>

Darunter kopiert man die folgende Zeile:

<?php echo JHtml::_(‚content.prepare‘, ‚{loadposition bewertungsformular}‘); ?>

Jetzt geht man nochmal in das Modul für das Bewertungsformular zurück und ändert die Modulposition in bewertungsformular – Schreibweise genau einhalten!

Daraufhin muss man noch in einem beliebigen Rezept unter dem Reiter Optionen das Layout auf „meins“ stellen, dann wird das Formular nach dem Rezepttext auch angezeigt.

Das war jetzt aber auch noch nicht der Bringer?

Schließlich muss man auch das alternative Layout per Hand anwählen, ich geb zu, da ist noch nicht viel gewonnen, genauso schnell hat man ein Modul eingefügt. Das hängt jetzt alles daran, dass ich keinen Weg gefunden habe, ein Modul nur für eine Kategorie anzeigen zu lassen, und ich hab ein paar Stunden lang danach gesucht, bislang ohne Erfolg.

Ich hätte da noch so’ne Idee

Man könnte ja einen Override auf die default.php anlegen und abfragen, welche Kategorie der aktuelle Artikel hat, und abhängig vom Ergebnis das Modul anzeigen lassen oder nicht. Mal sehen, wie weit wir damit kommen.

In unserem Override der default.php für den Artikel suchen wir nach der Stelle:

<div itemprop=“articleBody“>
<?php echo $this->item->text; ?>
</div>

Danach schubsen wir eine Datenbankabfrage rein:

<?php
    $db=JFactory::getDbo();
    $article_id = JFactory::getApplication()->input->get(‚id‚);
    echo „aktuelle Artikelid= „.$article_id;
    
    $db->setQuery(’select catid from #__content where id= ‚.$article_id.“);
    $catid=$db->loadResult();
    echo „aktuelle catid= „.$catid;
    
    if ($catid == 8){
        echo JHtml::_(‚content.prepare‘, ‚{loadposition bewertungsformular}‘);
    }
    else {
        echo „Dies ist kein Rezept, Kategorie: „.$catid;
        }
    ?>

Was hab ich gemacht? Mir die ID des aktuellen Artikels geholt, diese in den Select auf die #__content reingeschubst und mir so die ID der Kategorie geholt. Rezepte sind bei mir Kategorie 8, das frage ich mit dem If ab und zeige das Formular nur an, wenn dies zutrifft. Das wars schon! Die Debug-Ausgaben kann man noch rausschmeissen, aber so funktionierts. Endlich wieder mal ein bisschen Spaß auf der Datenbank!

Was jetzt noch fehlt: die Übermittlung der Rezept-ID oder des Titels

Weil mir die ganze Menage nichts hilft, wenn ich nicht weiss zu welchem Rezept die Bewertung abgeschickt wurde. Da hilft nur Nachforschen beim BreezingForms-Support, aber dazu gibts einen neuen Beitrag.

Benutzerdefinierte Felder – im Beitrag anzeigen

Analog zu den Custom Fields in WordPress kann man auch in Joomla selbstdefinierte Felder zu den Artikeln anlegen. Ich bin da gleich mal reingerasselt, weil ich zu den Kochbüchern ein Feld „Unverbindliche Preisempfehlung“ haben wollte. Schauen sie mal unter Inhalte/Felder/Neu die Liste der Feldtypen an – da ist alles dabei, bloß keine Währung, der einzige verfügbare Zahlentyp ist Integer. Ein Datumsfeld sucht man auch erstmal, es versteckt sich hinter dem Feldtyp „Kalender“. Ansonsten stecken in den Feldtypen jede Menge Angaben, die ich für Spielerei halte, unter anderem auch ein Feldtyp „Farbe“, man sehe und staune:

rgb_feld

rgb_feld

Damit kann man sich im Frontend den RGB-Code der gewählten Farbe anzeigen lassen, wofür das auch immer gut sein soll.

Wenigstens kann man beim Anlegen der Felder angeben ob es sich um ein Pflichtfeld handelt, dann kriegt man beim Ausfüllen der Beiträge einen Hinweis, wenn was fehlt. Wo sich die Felder und die Feldinhalte wiederfinden lassen, dazu gibts einen:

Blick auf die Datenbank

Man kann auch noch Feldgruppen anlegen, aber das lassen wir mal aussen vor, sonst wirds zu kompliziert. Ich möchte jetzt einfach in einem Artikel den Wert eines benutzerdefinierten Feldes anzeigen, und zwar an beliebiger Stelle im Inhalt. Und – da bin ich ein bißchen eigen – ich möchte das auf relativ einfachen Weg in einem Sourcerer Codesnippet tun, ohne Overrides  und ohne eigens programmierte Module. Kann ja wohl nicht so schwer sein!

Dafür tun wir mal einen beherzten Griff in die Datenbank. Die relevanten Tabellen sind die #__contents, aus der brauchen wir die ID des Artikels, die Tabelle #__fields, aus der holen wir uns den Feldnamen und nach Belieben auch die Feld-ID, und schliesslich die #__fields_values, in der stecken schlussendlich die Werte der Felder. Ich habs mal kurz nach Access reingeschubst, da stellen sich die Beziehungen so dar:

fields_beziehungen

fields_beziehungen

Also, frisch auf, einen Join über alle drei Tabellen gebastelt, wichtig ist hier die item_id, die identifiziert unseren Artikel:

felder_join_alle_drei

felder_join_alle_drei

Der Select dazu sieht so aus und ist relativ straightforward:

SELECT Fields_values.field_id, Fields.name, Fields_values.item_id, Fields_values.value
FROM Fields INNER JOIN (Content INNER JOIN Fields_values ON Content.[id] = Fields_values.[item_id]) ON Fields.[id] = Fields_values.[field_id];

Wir wollen aber nur die Feldinhalte zum aktuellen Artikel ausgeben, ich nehm mal den mit der ID 22 und klemm noch eine Where-Klausel dran. Dann können wir uns auch den Join auf die #__contents noch sparen, die ID des Artikels ist ja erstmal fest.

SELECT Fields_values.field_id, Fields.name, Fields_values.item_id, Fields_values.value
FROM Fields INNER JOIN Fields_values ON Fields.[id] = Fields_values.[field_id]
WHERE (((Fields_values.item_id)=“22″));

Ergebnis wie erwartet:

felder_nur22

felder_nur22

Und jetzt kann man sich anhand der field_id oder dem field name ein benutzerdefiniertes Feld herauspicken, ich nehm mal den Namen:

SELECT Fields_values.field_id, Fields.name, Fields_values.item_id, Fields_values.value
FROM Fields INNER JOIN Fields_values ON Fields.[id] = Fields_values.[field_id]
WHERE (((Fields.name) Like „farbtest“) AND ((Fields_values.item_id)=“22„));

Schon haben wir den Datensatz mit dem richtigen Feldwert eingekreist:

felder_nurfarbtest

felder_nurfarbtest

Na bitte, geht doch!

Und wo kriegen wir jetzt die aktuelle Artikel-ID her?

Das ist eine berechtigte Frage, man kommt ihr aber mit folgender Konstruktion bei:

$article_id = JFactory::getApplication()->input->get('id');

Jetzt basteln wir uns wie gehabt ein neues Datenzugriffsobjekt in der Variablen $db, dazu muss man noch ein bisschen bei den doppelten und einfachen Hochkommata aufpassen und das Präfix #__ nicht vergessen, aber das sollte eigentlich problemlos klappen.:

$db = JFactory::getDBO();

$query = „SELECT #__fields_values.field_id, #__fields.name, #__fields_values.item_id, #__fields_values.value
FROM #__fields INNER JOIN #__fields_values ON #__fields.id = #__fields_values.field_id
WHERE (#__fields_values.item_id = „.$article_id.“ ) and (#__fields.name like ‚farbtest‚);“;

Die Variable $article_id schubsen wir in unsere Where-Klausel rein, den Namen des benutzerdefinierten Feldes auch, das kann man auch auf eine Variable legen, oder man verdrahtet es fest wie ich es getan habe. Das war schon der ganze Zauber!

Query zuweisen und ausführen:

$db->setQuery($query);
$result = $db->execute();

Objektliste laden, auch wenn wir nur 1 Zeile im result haben:

$results = $db->loadObjectList();

Ausgabe des Wertes aus dem Feld value mit foreach(), :

foreach ($results as $zeile) :
echo $zeile->value;
endforeach;

Das wars, da haben wir den Wert unseres benutzerdefinierten Feldes, und der kann an beliebiger Stelle im Artikel positioniert werden. Die anderen benutzerdefinierten Felder kann man natürlich auch mit dem Namen ansprechen, das geht ganz genau so wie hier vorgeführt.

Kleine Spielerei am Schluss: wenn wir schon einen RGB-Farbwert haben, möchten wir die Farbe auch sehen, ich mach mal ein Quadrat von 300 px Kantenlänge. Dann kommt in der foreach-Schleife noch eine Variablenzuweisung mit:

foreach ($results as $zeile) :
$aktuelle_farbe = $zeile->value;
echo $zeile->value.“<br>“;
endforeach;

Die verwenden wir jetzt in einer Div für das Farbquadrat:

echo „<div style=’background-color:“.$aktuelle_farbe.“; width: 300px; height: 300px;‘><h2>Aktuelle Farbe</h2></div>“;

farbquadrat

farbquadrat

Na siehste, haben wir doch noch eine Anwendung für den Feldtyp „Farbe“ gefunden!

Nachtrag

Ich bin gefragt worden, warum ich denn nicht ein alternatives Layout für die Anzeige der Felder angelegt habe und dann einfach dort an der richtigen Stelle mit:

<?php echo $this->item->jcfields[x]->value; ?>

das Feld mit der Nummer x einfüge. Ganz einfach: ich wollte den Feldinhalt innerhalb des Beitragstextes ausgeben, das ist der ganze Witz an der Sache. Das geht im Template nicht, da dort der Beitragstext als Ganzes mit <?php echo $this->item->text; ?> hereingeholt wird, und man keine Chance hat da innerhalb etwas zu positionieren. Ist der Sinn und Zweck der Übung jetzt etwas klarer?

Noch ein paar lose Endchen

Haupteinträge

Das fällt einem im Beitragseditor relativ gross ins Auge, aber was bedeutet eigentlich Haupteintrag ja/nein? Ist eigentlich ganz einfach. Man kann beliebige Artikel als Haupteinträge kennzeichnen, das ist so etwas wie eine übergeordnete Kategorie. Eine Anwendung: wenn man alle Haupteinträge unter einem Menüpunkt sehen will, erstellt man einen neuen Menüeintrag mit dem Menüeintragstyp Beiträge->Haupteinträge. Hier kann man auch einstellen, ob Artikel aus allen Kategorien oder nur aus einer bzw. mehreren ausgewählten Kategorien angezeigt werden sollen. Somit hat man eine Möglichkeit, besonders wichtige oder interessante Artikel auch kategorieübergreifend in einer Blogansicht zusammenzufassen.

Navigationspfad einfügen

Unter Erweiterungen->Module-> Neu -> Navigation – Navigationspfad versteckt sich ein Modul, das einen „Wo bin ich“-Navigationspfad an der gewünschten Layoutposition ausgibt. Nützlich bei verschachtelten Untermenüs!

Blanko-Modul für eigene Inhalte

Man hat unter Module->Neu->Eigenes Modul die Möglichkeit, ein Modul mit selbstdefinierten Inhalten zu erstellen. Ich finde, hier ist eigener PHP-Code, mit Hilfe des Sourcerer eingefügt, gut aufgehoben. Man kann es aber auch für öfter genutzte Texte, z.B. Copyrightvermerke oder dergleichen gut gebrauchen.