Category: Development

WordPress: Virtuelle Seiten erstellen – Von .htaccess zu Hooks

Worpress ist praktisch, wenn man manuell Seiten (Pages) erstellt und diese im Admin Bereich editieren möchte. Allerdings gibt es keine (mitgelieferte) Möglichkeit “virtuelle Seiten” unterzubringen.

Virtuelle Seiten?!
Nun rede ich hier von virtuellen Seiten, aber was verstehe ich darunter?
Im Prinzip ist die Idee ganz einfach: Der Webseitenbesucher soll keinen Unterschied zwischen einer virtuellen und einer “realen” Seite sehen – allerdings ist die virtuelle Seite nicht in der WordPress Datenbank abgelegt.

Die Probleme
Aus dieser Aufgabenstellung ergeben sich folgende Probleme:

  1. Umleitung der Anfrage auf ein eigenes php-Skript
  2. Einbinden des WordPress Layouts
  3. Erweitern der Navigation um die Virtuellen Seiten

Umleitung der Anfrage auf ein eigenes php-Skript
Auszug aus der .htaccess

View Code PLAIN
1
2
3
4
RewriteEngine On
RewriteBase /pfad/zu/meinem/blog/
RewriteRule ^uebung/([\w,=;\-]+)\.html$ ce_exercise.php?id=$1 [L]
RewriteRule ^uebung/?$ ce_exercise.php [L]

In diesem Beispiel werden alle Anfragen auf /uebung, /uebung/, /uebung/xyz-abc-etc.html auf ce_exercise.php umgeleitet. ce_exercise.php bindet dann WordPress ein und lädt die angeforderte Seite aus der (eigenen) Datenbank.

Einbinden des WordPress Layouts
Mit

1
2
3
4
5
6
define ( 'WP_USE_THEMES', false );
 
/** hier fehlt noch was! Siehe unten */
 
/** Loads the WordPress Environment and Template */
require ('./wp-blog-header.php');

wird WordPress eingebunden (angenommen, dass wp-blog-header.php im selben Verzeichnis liegt).
define ( ‘WP_USE_THEMES’, false ); verhindert, dass WordPress selbstständig wird und Theme Dateien lädt – Das wollen wir ja selbst machen!

Dies alleine reicht allerdings nicht aus, da WordPress die url analysiert und trotzdem versucht den Inhalt der Seite zu laden. Da die angeforderte Seite virtuell ist wird WordPress immer davon ausgehen, dass ein 404 (Seite nicht gefunden) vorliegt.

Dies lässt sich wie folgt lösen:

1
2
3
/* confuse wordpress: make it think the url is something else */
preg_match ( '#(.*)/uebung/#', $_SERVER ['REQUEST_URI'], $treffer );
$_SERVER ['REQUEST_URI'] = $treffer [1] . "/uebung/";

Ich gebe zu, diese Lösung ist weder schön noch Zukunftssicher – aber sie funktioniert.

Wichtige Hinweise zu dieser Lösung:

  • Ich verwende “schöne” permalinks mit mod_rewrite
  • Es muss eine Seite angelegt werden, die auf die umgeschriebene url passt. Hier: /uebung/
  • Der Code muss vor require (‘./wp-blog-header.php’); stehen!

Der Anfang des php-Skriptes sieht dann so aus:

1
2
3
4
5
6
7
8
9
10
11
error_reporting ( E_ALL );
define ( 'WP_USE_THEMES', false );
 
/* confuse wordpress: make it think the url is something else */
preg_match ( '#(.*)/uebung/#', $_SERVER ['REQUEST_URI'], $treffer );
$_SERVER ['REQUEST_URI'] = $treffer [1] . "/uebung/";
 
$requested_page = isset ( $_GET ['id'] ) ? $_GET ['id'] : "main";
 
/** Loads the WordPress Environment and Template */
require ('./wp-blog-header.php');

Nun können die WordPress-Funktionen get_header (); get_sidebar (); und get_footer (); aufgerufen werden.

Damit ist das Problem des Layouts gelöst – bleibt noch:

Erweitern der Navigation um die Virtuellen Seiten
Wordpress stellt viele sogenannter “hooks” zur Verfügung, um die Ausgabe von WordPress Funktionen zu beeinflussen. Diese Hooks werden auf den Resultaten der WordPress Funktionen ausgeführt, nachdem die WordPress Funktion fertig ist.

Vereinfacht kann man sich das so vorstellen:

1
2
$reslut = wp_some_funtion();
$result = my_hook( $result );

Die Kunst besteht darin die richtige WordPress Funktion auszuwählen. Ich hab hier auch einen unschönen aber schnellen und effektiven Weg gewhält: Ich modifiziere die Navigation, nachdem diese als HTML generiert wurde. Konkret: Ich verwende einen Hook auf wp_list_pages.

In meinem Fall möchte ich nur die Navigation der Sidebar beeinflussen und nicht die des Headers. Deshalb wird der Hook erst nach Ausgabe des Headers aufgerufen.

Jetzt bleibt nur noch das Hinzufügen der zusätzlichen Navigationselemente im Hook. Ich suche dazu nach dem Navigationseintrag, der “uebung” enthält und füge danach die virtuellen Seiten ein.

Der gesamte (auf das Wesentliche reduzierte) Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 
error_reporting ( E_ALL );
/* verhindern, dass WP selbständig Themes lädt */
define ( 'WP_USE_THEMES', false );
 
/* confuse wordpress: make it think the url is something else */
preg_match ( '#(.*)/uebung/#', $_SERVER ['REQUEST_URI'], $treffer );
$_SERVER ['REQUEST_URI'] = $treffer [1] . "/uebung/";
 
$requested_page = isset ( $_GET ['id'] ) ? $_GET ['id'] : "main";
 
/** Loads the WordPress Environment and Template */
require ('./wp-blog-header.php');
 
 
get_header ();
 
// hier den eigentlichen Seiteninhalt ausgeben
 
// hier $added_pages_str mit den virtuellen Seiten füllen
 
add_filter ( 'wp_list_pages', 'ce_mod_pages_list', 1, 2 );
 
get_sidebar ();
 
get_footer ();
 
function ce_mod_pages_list($pages_str, $opt) {
	global $added_pages_str, $requested_page;
	$parts = preg_split ( "#</li>#", $pages_str );
	foreach ( $parts as $key => $value ) {
		if (strpos ( $value, "uebung" ) !== false) {
			if ($requested_page != "main") {
				$parts [$key] = str_replace ( "current_page_item", "", $parts [$key] );
			}
			$parts [$key] .= $added_pages_str;
			break;
		}
	}
	return implode ( "</li>", $parts );
}

Schlussbemerkungen

  • ce_ steht für CompuLearn Englisch
  • Das Deutsch / Englisch Chaos in Code und Kommentaren ist ärgerlich aber nicht zu vermeiden.
  • Oje, das ist ja ganz schön viel geworden – Ich hoffe jemand liest das und findet Anwendung dafür.
  • Die Resultate können auf compulearnenglisch.de/uebung besichtigt werden.
  • Fragen und Kommentare wie immer erwünscht.
  • Listen machen alles übersichtlicher ;-)

WordPress: XML-Sitemap: Zusätzliche Seiten hinzufügen

Auf compulearnenglisch.de/uebung finden sich viele generierte Seiten, die nicht in der WordPress Datenbank gespeichert sind. Demnach werden diese Seiten nicht automatisch in der Xml-Sitemap aufgenommen.

Ein Blick in die Dokumentation genügt allerdings um das Problem zu lösen:

Adding other pages to the sitemap via other plugins

This plugin uses the action system of WordPress to allow other plugins to add urls to the sitemap. Simply add your function with add_action to the list and the plugin will execute yours every time the sitemap is build. Use the static method “GetInstance” to get the generator and AddUrl method to add your content.

1
2
3
4
5
function your_pages() {
  $generatorObject = &GoogleSitemapGenerator::GetInstance(); //Please note the "&" sign for PHP4!
  if($generatorObject!=null) $generatorObject->AddUrl("http://blog.uri/tags/hello/",time(),"daily",0.5);
}
add_action("sm_buildmap","your_pages");

c# – eigenes Control, Keys.Up, Down, Left, Right: OnKeyDown / OnKeyPress vs. ProcessCmdKey

Wer ein eigenes Control (Steuerelement) in c# erstellt und dachte, dass er mit OnKeyDown Pfeiltasten erkennen kann ist auf dem Holzweg:

View Code CSHARP
1
2
3
4
5
6
protected override void OnKeyDown(KeyEventArgs e)
{
	/* wird für Pfeiltasten nicht aufgerufen */
	base.OnKeyDown(e);
	if (e.KeyCode == Keys.Up) MoveIndex(-1);
}

Bei Druck auf die Pfeiltasten (Keys.Up, Keys.Down, Keys.Left, Keys.Right) wird weder OnKeyDown noch OnKeyPress aufgerufen.

Die Lösung bringt ProcessCmdKey:

View Code CSHARP
1
2
3
4
5
6
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
	if (keyData == Keys.Up) MoveIndex(-1);
 
	return base.ProcessCmdKey(ref msg, keyData);
}

Hier muss allerdings beachtet werden, dass in keyData auch Strg, Shift und Alt kodiert werden. keyData == Keys.Up ist nur true, wenn keine Steuerungstaste (Strg, Shift, Alt) gleichzeitig gedrückt wurde.

Welche Tasten gedrückt sind lässt sich folgendermaßen herausfinden:

View Code CSHARP
1
2
3
4
bool shift = (keyData & Keys.Shift) != 0;
bool control = (keyData & Keys.Control) != 0;
bool alt = (keyData & Keys.Alt) != 0;
Keys unmodifiedKey = (keyData & Keys.KeyCode);

c#: Win32Exception: Fehler beim Erstellen des Fensterhandles

Wann tritt diese Exception auf? – Wenn zu viele Steuerelemente erstellt und nicht wieder aufgeräumt wurden.

Beispiel

View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
    public partial class Form1 : Form
    {
        void ForceException()
        {
            while (true)
            {
                Label l = new Label();
                this.Controls.Add(l);
                this.Controls.Clear();
            }
        }
    }

Bei jedem Schleifendurchlauf wird ein neues Label erstellt. mit this.Controls.Clear(); wird das Label zwar entfernt, aber das Fenster-Handle des Labels existiert weiterhin. Da jeder Prozess nur begrenzt viele Fenster-Handles haben kann sind diese irgendwann aufgebraucht.

Abhilfe

View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
    public partial class Form1 : Form
    {
        void ForceException()
        {
            while (true)
            {
                Label l = new Label();
                this.Controls.Add(l);
                this.Controls.Clear();
                l.Dispose();
            }
        }
    }

Mit l.Dispose(); werden alle Ressourcen, die das Label reserviert hatte freigegeben. Damit wird auch das Fenster-Handle wieder frei und kann später wieder verwendet werden. Die obige while-Schleife läuft beliebig lange ohne Fehler.

HowTo: Proftpd-lenny unter debian etch 4.0 / Filezilla ECONNABORTED beheben

Dieser Artikel richtet sich an “debian etch 4.0″-Administratoren, die Proftpd aus den Paketquellen installiert haben und folgendes Problem in Filezilla haben:

Status: Server did not properly shut down TLS connection
Error: Could not read from transfer socket: ECONNABORTED – Connection aborted

Wie im Filezilla Forum zu lesen ist:

ECONNABORTED: It’s the server’s fault!

leider hilft das nicht wirklich weiter.

Die Lösung ist ganz einfach: Proftpd aus den lenny Paketquellen laden und installieren. Dieses HowTo hilft bei der Erstellung eines Proftpd-Paketes.

Vorbereiten des Systems

Für einige der nachfolgenden Befehle muss man root sein. Am besten man wird es jetzt schon.

Zum Bauen von Debian-Paketen sind einige Pakete erforderlich.

1
aptitude install devscripts build-essential fakeroot

Laden und Vorbereiten der Sourcen

Die Sourcen gibt es unter packages.debian.org/lenny/proftpd

1
2
wget http://security.debian.org/debian-security/pool/updates/main/p/proftpd-dfsg/proftpd-dfsg_1.3.1.orig.tar.gz
wget http://security.debian.org/debian-security/pool/updates/main/p/proftpd-dfsg/proftpd-dfsg_1.3.1-17lenny4.diff.gz

Auspacken des tar.gz Archives:

1
tar -xzf proftpd-dfsg_1.3.1.orig.tar.gz

Nun werden die debian-spezifischen Patches angewendet

1
2
cd cd proftpd-dfsg-1.3.1/
zcat ../proftpd-dfsg_1.3.1-17lenny4.diff.gz | patch -p1

Bauen der Pakete

1. Versuch mit debuild

1
debuild

Dieser 1. Versuch wird aller Voraussicht nach fehlschlagen, da andere Pakete fehlen, um Proftpd zu bauen. Die Fehlermeldung ist allerdings hilfreich und verrät welche Pakete noch zu installieren sind. Diese sollten nun nacheinander installiert werden. Zwischendurch immer wieder debuild probieren. Die Liste der fehlenden Pakete sollte mit jedem installierten Paket kleiner werden.
Schließlich startet debuild das Erstellen der Pakete. Dies kann einige Zeit dauern. Nicht die Geduld verlieren.

Es kann sein, dass debuild einige Fehler am Ende ausgibt, dies soll uns aber nicht weiter stören. Wenn alles geklappt hat sollten am Ende folgende Dateien vorhanden sein (je nach System kann die Liste abweichen)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cd ..
ls -al
 
-rw-r--r--  1 root root  195024 2009-11-12 17:19 proftpd_1.3.1-17lenny4_all.deb
-rw-r--r--  1 root root  696556 2009-11-12 17:19 proftpd-basic_1.3.1-17lenny4_i386.deb
drwxr-sr-x 13 root root    4096 2009-11-12 17:19 proftpd-dfsg-1.3.1
-rw-r--r--  1 root root  102934 2009-11-12 17:16 proftpd-dfsg_1.3.1-17lenny4.diff.gz
-rw-r--r--  1 root root     653 2009-11-12 17:16 proftpd-dfsg_1.3.1-17lenny4.dsc
-rw-r--r--  1 root root       0 2009-11-12 17:19 proftpd-dfsg_1.3.1-17lenny4.dsc.asc
-rw-r--r--  1 root root  124074 2009-11-12 17:19 proftpd-dfsg_1.3.1-17lenny4_i386.build
-rw-r--r--  1 root root    1700 2009-11-12 17:19 proftpd-dfsg_1.3.1-17lenny4_i386.changes
-rw-r--r--  1 root root 2662056 2009-02-25 12:55 proftpd-dfsg_1.3.1.orig.tar.gz
-rw-r--r--  1 root root 1254386 2009-11-12 17:19 proftpd-doc_1.3.1-17lenny4_all.deb
-rw-r--r--  1 root root  214058 2009-11-12 17:19 proftpd-mod-ldap_1.3.1-17lenny4_i386.deb
-rw-r--r--  1 root root  203592 2009-11-12 17:19 proftpd-mod-mysql_1.3.1-17lenny4_i386.deb
-rw-r--r--  1 root root  203562 2009-11-12 17:19 proftpd-mod-pgsql_1.3.1-17lenny4_i386.deb

Installieren von Proftpd

Bevor der neue Proftpd installiert wird sollte der alte entfernt werden

1
aptitude remove proftpd

Schließlich kann der neue Proftpd installiert werden

1
dpkg -i *.deb

Nun sollte der selbst gebaute Proftpd seinen Dienst verrichten.

Fragen und Anregungen sind in den Kommentaren gerne willkommen!

Links
Rebuilding Debian packages debian-administration.org/articles/20
Lenny Proftpd Quellen packages.debian.org/lenny/proftpd

Dank
Vielen Dank an Matthias für das “Mini-Papier-HowTo”

if (false)

1
2
3
if (false) {
    doWork();
}

je nach Geschmack auch:

1
2
3
4
5
if (false) {
    beNice();
} else {
    beEvil();
}

HowTo: Eigenes Steuerelement, Eigene Eigenschaften definieren

In Visual Studio ist es möglich im Eigenschaftsfenster selbst definierte Eigenschaften anzeigen zu lassen.

Ich werde dies am Beispiel eines eigenen Buttons erklären, der eine zusätzliche Eigenschaft (WordWrap) erhält.

Legen wir zunächst einen neuen Button an. In diesem Beispiel heißt dieser FlatButton:

View Code CSHARP
1
2
class FlatButton : Button {
}

Bislang hat unser FlatButton alle Eigenschaften eines ‘normalen’ Buttons.

Nun fügen wir unsere eigene Eigenschaft hinzu:

View Code CSHARP
1
2
3
4
5
6
7
8
9
10
class FlatButton : Button {
    bool wordWrap = false;
    [Browsable(true)]
    [Category("Appearance")]
    public bool WordWrap
    {
        get { return wordWrap; }
        set { wordWrap = value; Invalidate(); }
    }
}

[Browsable(true)] gibt an, dass WordWrap im Eigenschaftsfenster angezeigt werden soll.
[Category("Appearance")] lässt WordWrap in der Kategorie Darstellung erscheinen.

Wenn alles geklappt hat sollte nun WordWrap im Eigenschaftsfenster angezeigt werden:
FlatButtonProperties

FlatButton mit WordWrap=true bei geeigneter Implementierung von OnPaint:
FlatButton

Weitergehende Informationen gibt es in der MSDN
system.componentmodel.browsableattribute
system.componentmodel.categoryattribute

WordPress 2.8 Update Problem: Leere Seite nach Update

wp-logoNachdem die letzte WordPress Aktualisierung auf 2.7 problemlos verlief habe ich nun versucht auf die 2.8 zu aktualisieren. Das Ergebnis war eine leere Seite sowohl auf dem Blog als auch im Admin Menü.

Mir hat bei der Problemlösung vor allem Fixing Blank, Empty or White WordPress Pages after Upgrade geholfen.

Der Trick war, zunächst alle Plugins durch umbenennen des Plugin Ordners zu deaktivieren und danach einzeln wieder zu aktivieren. Das problematische Plugin ist bei mir der “wp-options-manager”.

Außerdem scheint die 2.8 nicht vollständig übersetzt worden zu sein:
wp-2-8-uebersetzung

Das Upgrade kann also durchaus holprig verlaufen… Ein Datenbank-Backup mit mysqldump und ein Seiten-Backup mit “tar -czf” ist – wenn möglich – auf jeden Fall zu empfehlen.

C#: Strg+C, Strg+V, Strg+X

In einer benutzerfreundlichen Anwendung sollte der Benutzer wenn möglich Strg+C, Strg+V und Strg+X verwenden können.

Dies ist einfach zu realisieren:

View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
class MyForm : Form
{
	protected override void OnKeyDown(KeyEventArgs e)
	{
		if (e.Modifiers == Keys.Control)
		{
			switch (e.KeyCode)
			{
				case Keys.C:
					tryCopy();
					break;
				case Keys.V:
					tryPaste(); 
					break;
				case Keys.X:
					tryCut();
					break;
				case Keys.Delete:
					tryDelete();
					break;
				case Keys.Right:
					tryMoveNext();
					break;
				case Keys.Left:
					tryMovePrevious();
					break;
			}
		}
		base.OnKeyDown(e);
	}
}

Nun noch die tryXY-Funktionen implementieren und fertig ist die Tastaturbedienung des Programms.

∞ Gründe den Internet Explorer (IE7) zu hassen

Lieber CSS-Liebhaber,

beachte

1
2
3
4
.forumline td {
	border-bottom: 1px solid #fff;
	padding: 3px 5px;
}

wirkt sich nur auf Zellen aus, in denen Text steht. Warum sollte es sich auch auf leere Zellen auswirken – Nein das macht wirklich keinen Sinn…

Aber es gibt auch dafür einen ‘guten’ IE-Workaround:

1
2
3
4
5
aus
<tr><td></td><td>mein text</td></tr>
 
mache
<tr><td>&nbsp;</td><td>mein text</td></tr>

dann wirkt sich auch im Internet Explorer “border-bottom” aus. Es wäre einfacher gewesen ein spezial IE-Attribut einzuführen:

1
<td ie="really_apply_css"></td>

Well done Microsoft.

WordPress Themes