{"id":754,"date":"2011-07-10T10:21:51","date_gmt":"2011-07-10T08:21:51","guid":{"rendered":"http:\/\/robert.kolatzek.org\/wblog\/?p=754"},"modified":"2011-07-10T10:21:51","modified_gmt":"2011-07-10T08:21:51","slug":"typo3-v-4-5-die-fortsetzung","status":"publish","type":"post","link":"https:\/\/blog.kolatzek.org\/wblog\/754\/typo3-v-4-5-die-fortsetzung","title":{"rendered":"Typo3 (v 4.5) &#8211; die Fortsetzung"},"content":{"rendered":"<p>Wie angek\u00fcndigt, kommt hier der leidvollen Erfahrungen mit Typo3 (in Version 4.5) Teil 2. Diesmal mit Hacks im Code der Plugins rund um RSS und HTML-Metadaten. Fangen wir mit einem RSS-Reader an.<br \/>\n<!--more--><\/p>\n<h3>RSS-Reader mit konfigurierbarer Ausgabe (Einbindung von RSS-Feeds in eigene Seite).<\/h3>\n<p>Einen recht gelungenen (on-demand)-RSS-Reader f\u00fcr Typo3 stellt die Erweiterung tw_rssfeeds dar. Wer das Datum in der Auflistung vermisst, patcht einfach typo3conf\/ext\/tw_rssfeeds\/pi1\/class.tx_twrssfeeds_pi1.php in Zeile 280 und macht daraus 2 Zeilen.<br \/>\n<code>$content .= '&lt;div class=\"twrss_bodytext twrss_item_link\"&gt;&lt;span class=\"twrss_item_date\"&gt;'.date('d. m. Y:',strtotime($this-&gt;data['ITEM'][$i]['PUBDATE'])).'&lt;\/span&gt; ';<br \/>\n$content .= '&lt;a href=\"'.$this-&gt;data['ITEM'][$i]['LINK'].'\" target=\"'.$get_theLinkTarget.'\" &gt;'.$go_the_head.'&lt;\/a&gt;&lt;\/div&gt;';<\/code><br \/>\nDer Abstand zwischen Tag und Monat sowie Monat und Jahr kann man mit einem negativen &#8222;word-spacing: -0.2ex;&#8220; auf die Klasse &#8222;twrss_item_date&#8220; fixieren. Ohne Leerzeichen sieht es bei uns etwas gepresst aus&#8230;<\/p>\n<h3>RSS generieren mit ecorss<\/h3>\n<p>Nachdem tt_news mit der Version 4.5 nicht mehr nutzbar ist, muss man alternativen \u00fcberlegen. Nach l\u00e4ngerer Suche fand ich etwas schicken und einfaches &#8211; mit einigen Macken. So erlaubt die Konfiguration die \u00dcbergabe von &#8222;page:uid&#8220; an pidRootline (Elternelement f\u00fcr einen Auszug aus \u00c4nderungen dieser und ihrer Unterseiten). Schrecklich! L\u00e4sst man pidRootline leer, werden alle Seiten (egal welcher Dom\u00e4ne) ausgegeben &#8211; das kann ja auch nicht der Sinn der Sache sein. Setzt man keine Ganzzahl (Seiten-ID) ein, bekommt man gar nichts. So hei\u00dft es: Selbst ist der Webmaster!<br \/>\n<code>if($pidRootline == 'page:uid')<br \/>\n{<br \/>\n\t$pidRootline = intval($GLOBALS['TSFE']-&gt;id);<br \/>\n}<\/code><br \/>\nin Funktion load() &#8211; gegenw\u00e4rtig <strong>Zeile 60<\/strong> der Datei <strong>class.tx_ecorss_models_feed.php<\/strong> im Ordner <strong>models<\/strong> &#8211; einf\u00fcgen behebt diesen Mangel. Man kann auf diesem Wege zumindest page:uid nutzen und mit einem Template f\u00fcr jede Seite (und Unterseiten) ein RSS erzeugen lassen.<\/p>\n<h3>RSS-Link im HTML-head einf\u00fcgen<\/h3>\n<p>Das Einf\u00fcgen von link-Elementen zu RSS\/Atom-Feeds der aktuellen Seite kann zwar automatisiert werden, doch wirkt die URL etwas antiquarisch. Mann kann trotz RealURL auf \u00dcbergabeparameter nicht verzichten. Es ist unsch\u00f6n, funktioniert aber.<br \/>\n<code>### ATOM-Feed als PAGE des Typs 105 definieren<br \/>\natom_thispage = PAGE<br \/>\natom_thispage {<br \/>\n\ttypeNum = 105<br \/>\n        config {<br \/>\n            disableAllHeaderCode = 1<br \/>\n            disableCharsetHeader = 1<br \/>\n            additionalHeaders = Content-type:text\/xml<br \/>\n            no_cache = 1<br \/>\n            xhtml_cleaning = 0<br \/>\n            admPanel = 0<br \/>\n         }<br \/>\n\t10 &gt;<br \/>\n\t10 = USER<br \/>\n\t10.userFunc = tx_ecorss_controllers_feed-&gt;display<br \/>\n\t10{<br \/>\n\t\tcache_period = 3600<br \/>\n\t\tnumberItems = 10<br \/>\n\t\tfeed = atom<br \/>\n\t\tpidRootline = page:uid<br \/>\n  \t  \tselect {<br \/>\n\t\t\t01 {<br \/>\n\t\t\t\ttable = tt_content<br \/>\n\t\t\t\ttitle = header<br \/>\n\t\t\t\tsummary = bodytext<br \/>\n\t\t\t\tpublished = tstamp<br \/>\n\t\t\t\tupdated = tstamp<br \/>\n\t\t\t\tdebug = 0<br \/>\n\t\t\t}<br \/>\n\t}<br \/>\n}<br \/>\n### \u00c4hnlich RSS-Feed, nur eben als Typ = 106<br \/>\nrss_thispage &lt; atom_thispage<br \/>\nrss_thispage.typeNum = 106<br \/>\nrss_thispage.10.feed = rss<br \/>\n### \u00dcbersetzungen f\u00fcr Titel und Untertitel......<br \/>\npage.headerData.12 = USER<br \/>\npage.headerData.12.userFunc = tx_ecorss_controllers_feed-&gt;add<br \/>\npage.headerData.12.atom {<br \/>\n  typeNum = 105<br \/>\n  feed = atom<br \/>\n  # aus der konfigurierten Seite des Typs 105 den entsprechend \u00fcbersetzten Titel \u00fcbernehmen<br \/>\n  title &lt; atom_thispage.10.title<br \/>\n  # Und bitte f\u00fcr jede Seite (samt ihren Unterseiten) anbieten! -&gt; Code-Hack unten<br \/>\n  pidRootline = page:uid<br \/>\n}<br \/>\npage.headerData.12.rss {<br \/>\n  typeNum = 106<br \/>\n  feed = rss<br \/>\n  # aus der konfigurierten Seite des Typs 105 den entsprechend \u00fcbersetzten Titel \u00fcbernehmen<br \/>\n  title &lt; rss_thispage.10.title<br \/>\n  # Und bitte f\u00fcr jede Seite (samt ihren Unterseiten) anbieten! -&gt; Code-Hack unten<br \/>\n  pidRootline = page:uid<br \/>\n}<\/code><br \/>\nDie \u00dcbersetzungen geht man im \u00dcbrigen immer so an:  Den Namen der Get\/Post-Variablen der Sprache (bei uns &#8222;L&#8220;) auf \u00dcbereinstimmung mit der ID einer Sprache pr\u00fcfen, dann die Variablen neu belegen.<br \/>\n<code>[globalVar = GP:L = 2] # Die 2 entspricht bei uns en-GB, kann bei Ihnen aber anders sein...<br \/>\n#englitsch<br \/>\natom_thispage.10 {<br \/>\n  title = My page title<br \/>\n  subtitle = Updates on this page<br \/>\n  lang = en-GB<br \/>\n  sysLanguageUid = 2<br \/>\n}<br \/>\nrss_thispage.10 {<br \/>\n  title = My page title<br \/>\n  subtitle = Updates on this page<br \/>\n  lang = en-GB<br \/>\n  sysLanguageUid = 2<br \/>\n}<br \/>\n[global]<\/code><br \/>\nWer statt &#8222;?type=105&#8220; (o. \u00c4.) einen Pseudo-Dateinamen haben will, muss zwei Sachen angehen:<\/p>\n<h4>Code anpassen<\/h4>\n<p>Das Problem ist das Plugin selbst: es ist auf die Anzeige ganzer Zweige ausgelegt, mi\u00dfachtet aber page:uid. Das m\u00fcssen wir angehen!<br \/>\n<strong>class.tx_ecorss_controllers_feed.php<\/strong> im Verzeichnis <strong>controllers <\/strong>des Plugins diese Zeilen in der Funktion <strong>add<\/strong> ein:<br \/>\n<code>if($config['pidRootline'] == 'page:uid')<br \/>\n{<br \/>\n\t$rootPid = intval($GLOBALS['TSFE']-&gt;id);<br \/>\n}<br \/>\nelseif($config['pidRootline']+0 == $config['pidRootline'])<br \/>\n{<br \/>\n\t$rootPid = intval($config['pidRootline']);<br \/>\n}<br \/>\nif( !empty($rootPid) )<br \/>\n{<br \/>\n\t$feedURL = $this-&gt;cObj-&gt;getTypoLink_URL($rootPid, array(\"type\" =&gt; $config['typeNum']));<br \/>\n}<\/code><br \/>\n(bei mir so um die Zeile 82) &#8230;da das Original einige Zeilen h\u00f6her immer(!) die Hauptseite (leveluid:0) annimmt. Da wir auf ein globales RSS f\u00fcr die ganze Dom\u00e4ne (leveluid:0) verzichten wollen und page:uid immer vorgeben werden (&#8222;diese Seite mit ihren Unterseiten&#8220;), m\u00fcssen wir page:uid auch bedienen &#8211; so \u00fcberschreiben wir $rootPid, die auf den leveluid:0 zeigt. Danach gibt es die drei M\u00f6glichkeiten<\/p>\n<ul>\n<li>Das RSS bekommt gar keine $rootPid (pidRootline in Konfig) vorgegeben -&gt; die gesamte Dom\u00e4ne wird ber\u00fccksichtigt (leveluid:0 = Standardverhalten)<\/li>\n<li>Eine pidRootline wurde als Zahl angegeben (ein bestimmter Zweig) &#8211; dieser Zweig wird mit allen Unterseiten im RSS angezeigt (fest dieser ID zugeordnet)<\/li>\n<li>page:uid wurde angegeben -&gt; Immer die aktuelle und die Unterseiten anzeigen<\/li>\n<\/ul>\n<h4>RealURL zur Zusammenarbeit bewegen und sch\u00f6ne URL f\u00fcr Atom und RSS erzeugen<\/h4>\n<p>Entgegen der \u00fcblichen \u00dcberzeugung, dass man &#8222;fileName&#8220;-Array ben\u00f6tigt, werden unsere Definitionen im &#8222;postVarSets&#8220; stehen. Hier kann man definieren, dass ein bestimmter PAGE-Typ einem Pseudo-Dateinamen zugeordnet sein soll.<br \/>\n<code>\"postVarSets\" =&gt; array(<br \/>\n\t\t\"_DEFAULT\" =&gt; array (<br \/>\n\t\t\t\"atom.xml\" =&gt; array(<br \/>\n\t\t\t\t'type' =&gt; 'single',<br \/>\n\t\t\t\t'keyValues' =&gt; array (\"type\" =&gt; 106)<br \/>\n\t\t\t),<br \/>\n\t\t\t\"rss.xml\" =&gt; array(<br \/>\n\t\t\t\t'type' =&gt; 'single',<br \/>\n\t\t\t\t'keyValues' =&gt; array (\"type\" =&gt; 105)<br \/>\n\t\t\t)<br \/>\n\t\t)<br \/>\n\t),<\/code><\/p>\n<h4>Das Problem mit dem Cache in ecorss<\/h4>\n<p>Jetzt d\u00fcrfte alles so weit gehen: link-Elemente im HTML-Kopfbereich, die RSS\/Atom-Ausgabe selbst&#8230; Doch da ist was faul. Man bekommt Inhalte anderer Seiten angezeigt. Die Funktion defaultAction hat eine kleine Macke: Sie erstellt den Dateinamen f\u00fcr die gecachte RSS-cache-Datei aus unserer Konfiguration (die f\u00fcr alle Seiten gleich ist) und aus dem Seitentyp (der ja auch f\u00fcr RSS bzw. Atom immer gleich bleibt). F\u00fcgt man die ID der aktuellen Seite hinzu, wird es eindeutig:<\/p>\n<p>Aus:<br \/>\n<code>$hash = md5(serialize($this-&gt;configurations) . $GLOBALS['TSFE']-&gt;type);<\/code><br \/>\nmach<br \/>\n<code>$hash = md5(serialize($this-&gt;configurations) . $GLOBALS['TSFE']-&gt;type);<br \/>\nif($this-&gt;configurations-&gt;_iterator-&gt;array['pidRootline'] == 'page:uid')<br \/>\n{<br \/>\n\t$hash = md5(serialize($this-&gt;configurations) . $GLOBALS['TSFE']-&gt;type . $GLOBALS['TSFE']-&gt;id);<br \/>\n}<\/code><\/p>\n<h3>Die wunderbare Welt der Head-Elemente<\/h3>\n<p>Sollte der Leser den Autor bis jetzt nicht gekannt haben, w\u00e4re es an der Zeit, ihm ein kleines offenes Geheimnis zu verraten: Der Autor ist zwar kein Bibliothekar, dennoch lebt er in einer Welt voller Metadaten. Die Metadaten kann man f\u00fcr Suchmaschinenoptimierung verwenden &#8211; wenn auch gegenw\u00e4rtig in einem nur sehr beschr\u00e4nkten Ma\u00dfe. Die Welt besteht nicht nur aus Suchmaschinen. Harvester &#8211; wie der der Deutschen Nationalbibliothek &#8211; grasen nicht nur f\u00fcr Google &amp; Co. Auch Archive haben ein berechtigtes Interesse daran, die vom Menschen vergebenen Beschreibungen f\u00fcr einen k\u00fcnftigen Benutzer an stelle des Inhalts anzuzeigen und somit eine grobe Vorauswahl aus einer Treffermenge zu erm\u00f6glichen. Dies geschieht in den meta-Elementen des HTML-head-Elementes. Damit es aber lustiger wird, gibt es davon zwei Arten: die alten Meta-Tags und die DublinCore-Metatags.<\/p>\n<p>In der erweiterten Optionspalette der Seiten findet der Typo3-Redakteur Felder wie: Autor, Beschreibung, Schlagworte, Zusammenfassung. Diese Eingabefelder nutzt ein Typo-Skript von <a title=\"Meta-Tags nach Typo3blogger.de\" href=\"http:\/\/typo3blogger.de\/title-und-meta-tags-bequem-setzen\/\" target=\"_blank\">typo3blogger.de<\/a>, um die Daten in beiden Formaten auszugeben. Da wir das Feld &#8222;Autor&#8220; so gut wie nie nutzen und keine spezielle Lizenz einsetzen wollen, lassen wir diese zwei Sachen aus. (Von uns ungenutzte Felder wurden auskommentiert.) Fehlen Daten zum Einf\u00fcgen, werden dank required = 1 bzw. Vererbung keine (leeren) Tags generiert.<br \/>\n<code>lib.title = TEXT<br \/>\nlib.title.data = field:subtitle \/\/ leveltitle :-1,slide<br \/>\nlib.description = TEXT<br \/>\nlib.description.data = levelfield :-1, description, slide \/\/ levelfield :-1, subtitle, slide \/\/ leveltitle :-1,slide<br \/>\nlib.keywords = TEXT<br \/>\nlib.keywords.data = levelfield :-1, keywords, slide \/\/ levelfield :-1, subtitle, slide \/\/ leveltitle :-1,slide<br \/>\n#lib.author = TEXT<br \/>\n#lib.author.data = levelfield :-1, author, slide<br \/>\n#lib.copyright = TEXT<br \/>\n#lib.copyright.data = levelfield: 0, author<\/p>\n<p>##### Zeilenumbruch (ist verzichtbar) ######<br \/>\nlib.emptyLine = TEXT<br \/>\nlib.emptyLine.value (<\/p>\n<p>)<\/p>\n<p>############## Meta ###################<br \/>\nlib.meta.description =&lt; lib.description<br \/>\nlib.meta.description  {<br \/>\n     required = 1<br \/>\n     outerWrap = &lt;meta name=\"description\" content=\"|\"\/&gt;<br \/>\n}<br \/>\nlib.meta.keywords =&lt; lib.keywords<br \/>\nlib.meta.keywords  {<br \/>\n     required = 1<br \/>\n     outerWrap = &lt;meta name=\"keywords\" content=\"|\"\/&gt;<br \/>\n}<br \/>\n#lib.meta.author =&lt; lib.author<br \/>\n#lib.meta.author  {<br \/>\n#     required = 1<br \/>\n#     outerWrap = &lt;meta name=\"author\" content=\"|\"\/&gt;<br \/>\n#}<br \/>\n#lib.meat.copyright =&lt; lib.copyright<br \/>\n#lib.meta.copyright  {<br \/>\n#     required = 1<br \/>\n#     outerWrap = &lt;meta name=\"copyright\" content=\"|\"\/&gt;<br \/>\n#}<\/p>\n<p>############# DublinCore-Meta ################<br \/>\nlib.meta.dc.title  =&lt; lib.title<br \/>\nlib.meta.dc.title  {<br \/>\n     required = 1<br \/>\n     outerWrap = &lt;meta name=\"DC.Title\" content=\"|\"\/&gt;<br \/>\n}<br \/>\nlib.meta.dc.description =&lt; lib.description<br \/>\nlib.meta.dc.description  {<br \/>\n     required = 1<br \/>\n     outerWrap = &lt;meta name=\"DC.Description\" content=\"|\"\/&gt;<br \/>\n}<br \/>\nlib.meta.dc.subject =&lt; lib.keywords<br \/>\nlib.meta.dc.subject  {<br \/>\n     required = 1<br \/>\n     outerWrap = &lt;meta name=\"DC.Subject\" content=\"|\"\/&gt;<br \/>\n}<br \/>\n#lib.meta.dc.creator =&lt; lib.author<br \/>\n#lib.meta.dc.creator  {<br \/>\n#     required = 1<br \/>\n#     outerWrap = &lt;meta name=\"DC.Creator\" content=\"|\"\/&gt;<br \/>\n#}<br \/>\n#lib.meat.dc.right =&lt; lib.copyright<br \/>\n#lib.meta.dc.rights  {<br \/>\n#     required = 1<br \/>\n#     outerWrap = &lt;meta name=\"DC.Rights\" content=\"|\"\/&gt;<br \/>\n#}<\/p>\n<p>###########################<br \/>\n# Header zusammenbauen ####<br \/>\n###########################<br \/>\npage.headerData.999 = COA<br \/>\npage.headerData.999 {<br \/>\n     5 =&lt; lib.emptyLine<br \/>\n    10 =&lt; lib.meta.title<br \/>\n    15 =&lt; lib.emptyLine<br \/>\n    20 =&lt; lib.meta.description<br \/>\n    25 =&lt; lib.emptyLine<br \/>\n    30 =&lt; lib.meta.keywords<br \/>\n    35 =&lt; lib.emptyLine<br \/>\n    #40 =&lt; lib.meta.author<br \/>\n    #45 =&lt; lib.emptyLine<br \/>\n    #50 =&lt; lib.meta.copyright<br \/>\n    #55 =&lt; lib.emptyLine<br \/>\n    #60 =&lt; lib.meta.robots<br \/>\n    #65 =&lt; lib.emptyLine<br \/>\n    70 =&lt; lib.meta.dc.title<br \/>\n    75 =&lt; lib.emptyLine<br \/>\n    80 =&lt; lib.meta.dc.description<br \/>\n    85 =&lt; lib.emptyLine<br \/>\n    90 =&lt; lib.meta.dc.subject<br \/>\n    95 =&lt; lib.emptyLine<br \/>\n   #100 =&lt; lib.meta.dc.creator<br \/>\n   #105 =&lt; lib.emptyLine<br \/>\n   #110 =&lt; lib.meta.dc.rights<br \/>\n   #115 =&lt; lib.emptyLine<br \/>\n}<\/p>\n<p>#### Vorausgesetzt, diese Zeile steht in der localconf.php!<br \/>\n<em>$GLOBALS['TYPO3_CONF_VARS']['FE']['addRootLineFields'] .= \u2018,subtitle,author,keywords,description\u2019;<\/em><\/code><br \/>\n<strong>Robots-Anweisungen<\/strong> kann man entweder wie oben f\u00fcr jede Seite einzeln aus dem Reiter &#8222;<em>Erweitert<\/em>&#8220; auslesen lassen oder <strong>die Datenbank in Ruhe lassen und per Hand global definieren<\/strong>:<br \/>\n<code>page.meta.robots = INDEX,FOLLOW<\/code><br \/>\nIn einigen F\u00e4llen will es mit dem <strong>CSS<\/strong>-Stylen im <strong>IE<\/strong> nicht so richtig gehen. Es braucht einen <strong>Kompatibilit\u00e4ts-Tag<\/strong>:<br \/>\n<code>page.headTag =&lt;head&gt;&lt;meta http-equiv=\"X-UA-Compatible\" content='IE=9' \/&gt;<\/code><br \/>\noder sch\u00f6ner so:<br \/>\n<code>page.headerData.1 = HTML<br \/>\npage.headerData.1.value = &lt;meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\" \/&gt;<\/code><\/p>\n<h4>Sitemaps f\u00fcr die Google &amp; Co.<\/h4>\n<p>Eine alte Wei\u00dfheit besagt, dass man sich um seinen Ruf selber k\u00fcmmern muss &#8211; noch bevor es andere tun. Auch im Internet kann es nicht schaden, die eigenen Webseiten den Suchmaschinen vorzuwerfen. Es ist immer besser, es selber steuern zu k\u00f6nnen, als zu hoffen und zu beten, dass sich Google und Bing erbarmen&#8230; Daf\u00fcr hat man ja die Sitemaps erschaffen. F\u00fcr Typo3 gibt es sogar ein gutes Extension: mc_googlesitemap. Man erstellt eine neue Seite mit einem Namen wie &#8222;sitemap.xml&#8220; und f\u00fcgt ihr als Inhaltselement ein Sitemap vom Typ &#8222;Sitemap for Sites&#8220; (das dritte blaue &#8222;G&#8220;). Ab sofort hat man eine Sitemap, die sogar f\u00fcr einzelne Seiten steuerbar ist: Wie oft soll der Roboter vorbeikommen? Wie wichtig ist diese Seite? Wann wurde sie zuletzt ge\u00e4ndert?<\/p>\n<p>Schade nur, dass das Plugin nicht mit RealURL zurecht kommt. Oder eher: mit dem Slash am Ende der sprechenden URLs. Aber auch hier kann man einen kleinen Hack anwenden. Man \u00e4ndert im Hauptverzeichnis des Plugins mc_googlesitemap die Datei class.tx_mcgooglesitemap_base.php in der Zeile 63 von<br \/>\n<code><br \/>\n$tmp=explode(\"\/\",$GLOBALS['_SERVER']['PHP_SELF']);<br \/>\n<\/code><br \/>\nnach<br \/>\n<code><br \/>\n$tmp=explode(\"\/\",preg_replace('#(\/)$#', '', $GLOBALS['_SERVER']['PHP_SELF']));<br \/>\n<\/code><\/p>\n<p>Ab jetzt macht der Slash hinter sitemap.xml keine Probleme mehr. Der Pfad wird des Anh\u00e4ngsels &#8222;sitemap.xml\/&#8220; beraubt und richtig ausgegeben. Es funktioniert auch f\u00fcr unterschiedliche Sprachversionen wie z.B. \/de\/sitemap.xml -&gt; http:\/\/&#8230;.\/de\/Seite1\/ bzw. \/en\/sitemap.xml -&gt; http:\/\/&#8230;.\/en\/Site1\/.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wie angek\u00fcndigt, kommt hier der leidvollen Erfahrungen mit Typo3 (in Version 4.5) Teil 2. Diesmal mit Hacks im Code der Plugins rund um RSS und HTML-Metadaten. Fangen wir mit einem RSS-Reader an.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_sitemap_exclude":false,"_sitemap_priority":"","_sitemap_frequency":"","ocean_post_layout":"","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"","ocean_second_sidebar":"","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"","ocean_custom_header_template":"","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"","ocean_menu_typo_font_family":"","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"","ocean_post_oembed":"","ocean_post_self_hosted_media":"","ocean_post_video_embed":"","ocean_link_format":"","ocean_link_format_target":"self","ocean_quote_format":"","ocean_quote_format_link":"post","ocean_gallery_link_images":"on","ocean_gallery_id":[],"footnotes":""},"categories":[8],"tags":[46,101,113],"class_list":["post-754","post","type-post","status-publish","format-standard","hentry","category-software","tag-google","tag-software-co","tag-typo3","entry"],"_links":{"self":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/posts\/754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/comments?post=754"}],"version-history":[{"count":0,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/posts\/754\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/media?parent=754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/categories?post=754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.kolatzek.org\/wblog\/wp-json\/wp\/v2\/tags?post=754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}