Node.js Notizen & Mongodb – Konfiguration + Tools

Im Moment schaue ich mir Node.js und Mongodb. Hier sammle ich für mich nützliche Informationen bzgl. der Einrichtung und Konfiguration.
Der Webspace kommt von Uberspace, entsprechend sind ein Paar Links in das ordentlich geschriebene Wiki von denen drin.

1. Einrichtung Uberspace

Anweisungen im Wiki folgen

Subdomain

Node Applikation auf Port 80 weiterleiten:

[schmimi@ara node.radikalblogger.de]$ cat .htaccess
RewriteEngine On
RewriteRule (.*) http://localhost:61667/$1 [P]

2. NPM konfigurieren

cat >  ~/.npmrc >> __EOF__
prefix = $HOME
umask = 077
__EOF__

 

3. Watch for Filechanges

Sehr praktisch zum entwickeln ist Supervisor

npm install supervisor -g
#Nun das Script mit dem Befehl supervisor starten.
[schmimi@ara ~]$ supervisor node/example.js

4. Node.js Tutorials

5. Mongo DB konfigurieren

Anleitung im Uberspace Wiki
Mongodb Handbuch

User und Datenbank anlegen

[schmimi4@octans nodejs]$ mongo admin --port 20522 -u schmimi4_mongoadmin -p
MongoDB shell version: 2.4.8
Enter password: 
connecting to: 127.0.0.1:20522/admin
> use node-mongo-blog
switched to db node-mongo-blog
use products
db.addUser( { user: "Alice",
              pwd: "Moon1234",
              roles: [ "readWrite", "dbAdmin" ]
            } )
> quit

Verbindung testen

mongo node-mongo-blog --port 20522 -u mongoblog -p

Datenbank löschen

> use mydb; 
> db.dropDatabase();

Ein (aus Anfängersicht) brauchbares Webinterface um mit der Datenbank rumzumachen scheint RockMongo zu sein. Konfiguration problemlos.

6. Auf Produktivbetriebumstellen

Interessanter Blogartikel

[schm@bla blog]export NODE_ENV=production

Led Lampen mit WLAN Anbindung

Ein Thema das mich immer wieder fasziniert ist die Vernetzung von Gegenständen des Alltags. Meine Wohnzimmerlampe glänzt nicht nur durch hässliche Form sondern auch durch kaltes Licht und Quecksilber im Leuchtmittel. Deshalb ersetze ich nach und nach Kompaktleuchtstofflampen durch LED Lampen. Der Zufall brachte mich zu diesem Lampenset mit WLAN Anbindung (Affiliate) vom Hersteller LimitlessLED. Die Lampen passen in E27 Sockel, haben 6 Watt Leistung und können mit 230V ~  betrieben werden. Die Grundidee ist die Lampen via Smartphone oder auf anderen Wegen einzuschalten, die Farben zu ändern und dimmen.Neben den 3 Lampen werden eine Fernbedienung und was viel interessanter ist einem Controller mit dokumentierter API geliefert. Apps gibt es auch! Pro Fernbedienung und Controller kann man 4 Lampen steuern. Dabei kann man die Parameter Helligkeit, Farbe regeln.

2013-10-15 16.56.18

 

Impressionen

Inbetriebnahme

Lampen

Jede Lampe muß konfiguriert werden. Dabei spielt es keine Rolle, ob man sie der Fernbedienung oder in der App konfiguriert.

Lampe in Fassung drehen und binnen 2 Sekunden nach Anlegen von Spannung auf der Fernbedienung einen Kanal aktivieren – die Lampe ist nun dem entsprechenden Kanal zugewiesen. Die erfolgreiche Zuweisung  wird durch Blinken quittiert. Um die Lampe aus dem Kanal zu lösen innerhalb von 2 Sekunden nach dem Einschalten 5 mal den entsprechenden Kanal auf der Fernbedienung aktivieren.

Controller

Der Controller wird per Micro-USB Kabel mit Spannung versorgt. Ich nutze ein Ladegerät für Smartphones. Nach dem Booten sollte man ein WLAN mit der SSID milight finden. Nun kann man der Anleitung folgen oder was ich als angenehmer per Webinterface konfigurieren. Dazu ins eigene WLAN verbinden und http://192.168.0.100/home.html aufrufen – ob die IP immer die gleiche ist kann ich nicht sagen. Hier muß in den STA Interface Settings das eigene WLAN konfiguriert werden.

App

Momentan nutze ich die in der Anleitung vorgeschlagene App Es gibt auch noch eine puristischere Version mit
Anbindung an Tasker – die  ohne Anpassungen leider nicht läuft.

LED Lampen ansteuern

Beim Hersteller gibt es eine Dokumentation.  Die Beispiele sind leider ein wenig inkonsistent, da irgendwann die Portnummer von 50000 auf 8899 umgestellt wurde.

Die Lampen werden per UDP Kommandos gesteuert. Dabei werden immer 3 Byte übertragen.

Byte 1 ist das Kommando an welches immer der Suffix 0x00  0x55 angehängt werden muß.

In PHP kann mit diesen Zeilen alle Kanäle einschalten:

<?php
define("CONTROLLER_IP", "192.168.0.100");
function executeCommand( $ip, $command, $suffix1=00, $suffix2='55'){
	fwrite(fsockopen("udp://$ip", 8899), chr(hexdec($command)) . chr(hexdec($suffix1)) . chr(hexdec($suffix2)));
}
 
executeCommand(CONTROLLER_IP, 42);

Wer php5-cli installiert hat kann das Script auch auf der Kommandozeile nutzen. Für Freunde der Bash gibt es auch ein Script (geklaut bei Smileytechadventures)

#!/bin/bash 
 
if [ -z "$1" ] ; then 
    echo "You must enter a parameter: "  
    echo "  e.g. $0 allon" 
    exit 1 
fi 
 
incmd="$1" 
ipaddress="192.168.0.201" 
portnum="50000" 
 
allon="\x35\00\x55" 
alloff="\x39\00\x55" 
zone1on="\x38\00\x55" 
zone1off="\x3B\00\x55" 
zone2on="\x3D\00\x55" 
zone2off="\x33\00\x55" 
zone3on="\x37\00\x55" 
zone3off="\x3A\00\x55" 
zone4on="\x32\00\x55" 
zone4off="\x36\00\x55" 
 
eval incmd=\$$incmd 
 
echo -n -e "$incmd" >/dev/udp/"$ipaddress"/"$portnum"

Ein wenig Heimautomatisierung ist auch schnell betrieben. Dieses Skript prüft, ob die Sonne untergegangen ist und schaltet in diesem Fall alle Lampen ein.

#!/usr/bin/php
<?php define("CONTROLLER_IP", "192.168.0.100"); define("ZEIT_FUER_BETT", "23:59"); function executeCommand( $ip, $command, $suffix1=00, $suffix2='55'){ fwrite(fsockopen("udp://$ip", 8899), chr(hexdec($command)) . chr(hexdec($suffix1)) . chr(hexdec($suffix2))); } function getIp(){ return file_get_contents("http://icanhazip.com/"); } function getLatLongByIp($ip){ $geoplugin = unserialize( file_get_contents('http://www.geoplugin.net/php.gp?ip='.$ip) ); if ( is_numeric($geoplugin['geoplugin_latitude']) && is_numeric($geoplugin['geoplugin_longitude']) ) { $ret['lat']= $geoplugin['geoplugin_latitude']; $ret['lon']=$geoplugin['geoplugin_longitude']; return $ret; } } $ret= getLatLongByIp(getIp()); $dateSunset=date_sunset(time(), SUNFUNCS_RET_TIMESTAMP,$ret['lat'], $ret['lon'], 90, 1); $dateSunrise=date_sunrise(time(), SUNFUNCS_RET_TIMESTAMP, $ret['lat'], $ret['lon'], 90, 1); //Zu einer festen Uhrzeit ausschalten if(date("H:i")>= date(ZEIT_FUER_BETT)){
echo "schlafen";
	executeCommand(CONTROLLER_IP, 41);
	exit;
}  
//Nach Sonnenuntergang und vor Aufgang
if ( time() >=$dateSunset && $dateSunset >$dateSunrise){
 
	executeCommand(CONTROLLER_IP, 42);
}else {
	executeCommand(CONTROLLER_IP, 41);
}

Das kleine Skript fühlt sich auf kleinen Linuxrechnern im eigenen Netzwerk sehr wohl und sollte per Cronjob alle 1 Minuten angestoßen werden.

*/1 *    * * *   /home/pi/sunrise.php

Inzwischen habe ich die Lampen seit einer Woche in Betrieb und viel Spaß damit. Die Lichtausbeute ist ausreichend, der Stromverbrauch in Ordnung. Der Preis ist mit 99 EUR schon sehr happig, aber es macht auch sehr sehr viel Spaß Skripte für die Lampen zu schreiben.

Google Cloud Messaging (GCM) Tutorial

In diesem Beitrag geht es darum wie man Nachrichten via GCM an Apps verschicken kann.

Viele Android Apps sind klassische Client/Server Anwendungen. Der Client tätigt Requests an einen Server und bekommt im besten Fall eine vernünftige Antwort. In anderer Richtung ist dies jedoch nicht so einfach. Wenn der Server Daten an 1 bis n Clients senden möchte ist das in diesem Szenario nicht so einfach. Mobiltelefone sind meist nicht über eine statische IP zu erreichen, es ist nicht sichergestellt, daß das Gerät mit dem Internet verbunden ist wenn eine Nachricht versendet wird. Wenn der Client regelmäßig den Server anpollt kostet das Bandbreite und elektische Leistung.

Continue reading

Und noch ein Rechner in der Bude – Raspberry Pi

Ich mag ja kleine Rechner mit und auch ohne Linux drauf, egal ob das Ding jetzt Pogoplug,Nslu2 oder mbed heißt. Inzwischen habe ich mehr Rechner – mit ARM Cpu am laufen als klassische x86. Deshalb habe ich das Erscheinen des Raspberry Pi sehr entspannt verfolgt. Es gibt hier einfach zuviele davon…

Meine Meinung änderte sich jedoch mit dem Erscheinen einer stabilen Version von Raspbmc, einer Linux Distribution mit der XBMC Software. Das wollte ich schon immer haben und wartete eigentlich nur auf den Hack der aktuellen AppleTV Generation.

Der Pi war schneller und für den Preis einer mittelschweren Zecherei  ~ 60 Eur hab ich nun so ein Teil, inklusive Netzteil, Gehäuse und Kühlrippen (wer’s denn braucht).

Gekauft habe ich beim PI3g Shop (kein subventionierter Link).

Die Installation ging leicht von der Hand – Image auf eine SD-Karte schreiben, rein in den Pi, Netzwerk dran, HDMI, Spannung anlegen und einen Kaffee trinken.

Kaffee grabben

Im Anschluss erstmal ein xvid und einen mkv (1080p DTS) Container getestet – läuft tadellos. Die ARD Mediathek ist einfach zu installieren und  läßt sich sehr gut bedienen. Youtube streikt, aber das scheint bekannt zu sein und leicht behebbar. Damit kann das Gerät schon mehr als mein jetziger Player, der mir in seiner Beschissenheit echt die Lust am Basteln geraubt hat. Bedienen tue ich den Spaß mit der Official XBMC Remote. Geht gut von der Hand. Um Texteingaben vom Mobiltelefon an das  Xbmc zu senden mußte ich Username + Passwort festelegen. Das manifestiert sich praktischerweise in einem .htaccess Zugriffsschutz, womit ich den Port sorgenlos in die Weiten des Netzes forwarden kann.  Per ssh kommt man per usr=pwd=raspberry auf den Rechner. Hier ein paar interessante Ausgaben:

 

pi@raspbmc:~$ uname -a
Linux raspbmc 3.6.11 #1 PREEMPT Sun Feb 3 04:42:21 UTC 2013 armv6l GNU/Linux
pi@raspbmc:~$ cat /etc/issue
Raspbmc by Sam Nazarko
http://www.raspbmc.com
pi@raspbmc:~$ cat /proc/cpuinfo
Processor       : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS        : 531.66
Features        : swp half thumb fastmult vfp edsp java tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xb76
CPU revision    : 7
 
Hardware        : BCM2708
Revision        : 000e
Serial          : 0000000063099a57
pi@raspbmc:~$ ssh
usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
           [-D [bind_address:]port] [-e escape_char] [-F configfile]
           [-I pkcs11] [-i identity_file]
           [-L [bind_address:]port:host:hostport]
           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
           [-R [bind_address:]port:host:hostport] [-S ctl_path]
           [-W host:port] [-w local_tun[:remote_tun]]
           [user@]hostname [command]

Ich habe noch die Skriptsprache meiner Wahl in der Cli Version installiert – dank Wheezy sogar in Version 5.4.4-12. Samba läuft bereits und gibt /home/pi sowie USB Laufwerke lesend wie schreibend frei.

Unterm Strich habe ich mit 0 Aufwand ein funktionierendes System zum Abspielen von Webinhalten und Sachen die ich aus dem Internet gezogen habe eingerichtet. Als Nächstes werde ich schauen, wie es mit DVB-* aussieht und ob die kleine Kiste auch noch Pyload schafft. Sehr interessant scheint webiopi damit lassen sich die I/O Pins des PI sehr leicht per Web ansteuern was sehr zum Basteln motiviert.

Persistenz und Reflexion mit PHP

City Refraction, City Reflection
Attribution License by lrargerich
Reflexion oder Introspektion bedeutet dass ein Objekt seine eigene Struktur kennt und diese Modifizieren kann.
Sehr praktisch ist dies, wenn es um Typsicherheit oder die Persistenz von Daten geht. PHP stellt dafür die Klasse ReflectionClass zur Verfügung. Ich möchte hier beschreiben, wie man mit Reflexion die Typen und Werte von Attributen auslesen kann, um diese z.B. persistent zu halten.
Zuerst ein Beispiel für Reflexion:

class ReflectionExample{
	protected $attribute1;
	protected $attribute2;
	protected $attribute3;
	public 	  $attribute4;

	public function method1(){}
	public function method2(){}

	public function __construct(){
		Reflection::export(new ReflectionClass($this));

	}
}//class
$reflect = new ReflectionExample();

Die Ausgabe:

Class [  class ReflectionExample ] {
  @@ /home/schmiddi/web/gloria2/Reflection.php 8-21

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [4] {
    Property [  protected $attribute1 ]
    Property [  protected $attribute2 ]
    Property [  protected $attribute3 ]
    Property [  public $attribute4 ]
  }

  - Methods [3] {
    Method [  public method method1 ] {
      @@ /home/schmiddi/web/gloria2/Reflection.php 14 - 14
    }

    Method [  public method method2 ] {
      @@ /home/schmiddi/web/gloria2/Reflection.php 15 - 15
    }

    Method [  public method __construct ] {
      @@ /home/schmiddi/web/gloria2/Reflection.php 17 - 20
    }
  }
}

Wie man sieht bekommt man jegliche Information über die Klasse geliefert. Ich möchte wie gesagt Daten bequem in einer Datenbank speichern, bearbeiten oder löschen. Der funktionale oder „PHP Tutorial“ Weg sähe so aus, dass man für jedes Objekt / Aspekt der PHP Seite die SQL Befehle INSERT, UPDATE und DELETE schreibt. Das ist nicht nur sehr Aufwändig, sondern benötigt auch große Aufmerksamkeit, wenn Attribute oder Variablen wegfallen oder neue hinzukommen.

Schauen wir uns noch einmal das Anfangsbeispiel an, um ein kleines Problem mit der Reflection Class zu verdeutlichen:

reflect

Die Klasse Reflect liest die Attribute der erbenden Klasse ReflectMe ein und macht eine Ausgabe:

abstract class Reflect{
	private $types = array();
	private $values= array();
	private $parentAttribute ;
	protected  $parentAttribute2;
	public function getAttributes(){
		$reflection = new ReflectionClass($this);
		$types=$this->types;
		$values=$this->values;
		foreach ($reflection->getProperties() as $key ){
			$value=str_replace("$",' ',$key->name);
			$type =  gettype($this->$value);
			$types[$key->name] = gettype($this->$value);
			$values[$key->name] = $this->$value;
			echo gettype($this->$value).
				" {$key->name} {$this->$value}
";
		}//each
	}//getAtrributes
}//class

Die Kindklasse ReflectMe hat nur ein paar Attribute die ich ausgeben möchte:

class ReflectMe extends Reflect{
	protected $child_attribute1= "Hallo Otto";
	protected $child_attribute2= 3.1415;
	protected $child_attribute3= 1337;
	private	  $child_attribute4= array();

	public function method1(){}
	public function method2(){}

}//class

$reflect = new ReflectMe();
$reflect->getAttributes();

Ein Blick in die Ausgabe verdeutlicht 2 Probleme:

string child_attribute1 Hallo Otto
double child_attribute2 3.1415
integer child_attribute3 1337
NULL parentAttribute2
Fatal error: Cannot access private property ReflectMe::$child_attribute4 in /home/schmiddi/web/gloria2/Reflection.php on line 30

Die ReflectionClass kann nicht auf private Attribute der Kindklasse zugreifen. Für meinen Geschmack ist dieser Grad der Kapselung ein wenig übertrieben. Mit Version 5.3 von PHP wird die Methode ReflectionMethod::setAccessible eingeführt, die den Zugriff erlaubt. Ein weiteres Problem wäre, dass wir auch die protected Werte der Elternklasse bekommen, da diese ja vererbt werden. Dies kann man Umgehen, indem man die Attribute von Reflect private deklariert. Damit schießt man sich jedoch in Sachen Vererbung ins Bein. Eleganter ist es die Elternklasse ebenfalls zu „reflektieren“ und die Schnittmenge der beiden Reflektionen zu entfernen:

/* ... */
	public function getAttributes(){
		//neues ReflectionObject für die Eltern Klasse erzeugen
		$reflectParent = new ReflectionClass(__CLASS__);
		$parentTypes=array();
		//Attribute in ein Array speichern
		foreach ($reflectParent->getProperties() as $key)
			$parentTypes[$key->name] = gettype($this->value);

		$reflection = new ReflectionClass($this);
		$types=$this->types;
		$values=$this->values;
		foreach ($reflection->getProperties() as $key ){
	//nur die Attribute der erbenden Klasse in die Array's schieben
			if(!array_key_exists($key->name, $parentTypes)){
				$value=str_replace("$",' ',$key->name);
				$type =  gettype($this->$value);
				$types[$key->name] = gettype($this->$value);
				$values[$key->name] = $this->$value;
				echo gettype($this->$value).
					" {$key->name} {$this->$value}
";
			}//if
		}//each
	}//getAtrributes

Nun können wir die Attribute der Klasse bequem auslesen, es sei denn sie sind als private deklariert. Für mein Skript möchte ich nicht PHP 5.3 voraussetzen, entsprechend muß darauf geachtet werden, dass nur gespeichert werden kann, was public oder protected ist.
Nun ist alles beisammen, was einen ordentlichen SQL Befehl ausmacht.
Zur Sache also:

private function generateInsertCommand(){
	$this->getAttributes();
		if ($this->insertCommand!=null)
			return $this->insertCommand;

		$classname =  $this->childClassName;
		$values = $this->attributeValues;

		$sql ="
		INSERT INTO  $classname (";
		$counter = 0;
		$count = count($this->attributeValues);
		foreach ($values as $key =>$value){
			$counter+=1;
			$sql.=$key;
			if($counter $count)
				$sql.=',';
		}
		$sql.=")VALUES (";
		$counter = 0;
		foreach ($values as $key =>$value){
			$counter+=1;
			$sql.='?';
			if($counter $count)
				$sql.=',';
		}//each

		$sql.=");";
		$this->insertCommand = $sql;
		return $this->insertCommand ;
	}//function

Die counts sind zwar nicht sehr schön, aber ich brauche sie für die Klammern. Wenn ich mal wieder ein wenig Zeit habe, stelle ich vielleicht den ORM Mapper hier vor.

PHP + mod_rewrite

Caps-Lock is FULL OF AWESOME!!1!
Attribution-NonCommercial-ShareAlike License by catcubed

Jeder WordPress Benutzer kennt die schönen Permalinks. Ein Link auf einen Artikel sieht meist nicht so aus

blogname/index.php?p=139

sondern so:

blogname/2010/04/07/Heute-ist-mal-wieder-was-tolles-passiert.

Die Technik dahinter ist recht simpel. Zum einen benötigt man das Apache2 Modul mod_rewrite und eine .htaccess Datei. Mod_rewrite leitet nach den Regeln, die man in der .htaccess Datei festlegt an eine Datei um. In diesem Fall eine PHP Datei, in der das $_SERVER Array ausgewertet wird.

Falls mod_rewrite im Apache deaktiviert ist, kann man das ganze recht simpel per Softlink einschalten:

schmiddi@dasgrauen:/etc/apache2$ cd /etc/apache2/
schmiddi@dasgrauen:/etc/apache2$ ls mods-available/ |grep rewrite
rewrite.load
schmiddi@dasgrauen:/etc/apache2$ ls mods-enabled/ |grep rewrite
schmiddi@dasgrauen:/etc/apache2/mods-enabled$ sudo ln -s ../mods-available/rewrite.load

Nun wird noch die Datei /etc/apache2/sites-enabled/000-default angepasst.

Ich habe lokal einen Apache Server, der nur der Programmiererei dient. Ich gehe hier nicht auf virtuelle Server oder sonst was ein.

Interessant sind nur die folgenden Zeilen:

DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>

Ich habe das AllowOverride einfach auf All gestellt und dann den Apache neu gestartet. Nun die .htaccess Datei anlegen und befüllen:

schmiddi@dasgrauen:/var/www$ touch .htaccess
touch: kann „.htaccess“ nicht berühren: Permission denied
schmiddi@dasgrauen:/var/www$ sudo !!
sudo touch .htaccess

Um auf die index.php im Serverroot umzuleiten braucht man die folgenden Zeilen:

# BEGIN WRoot

<IfModule mod_rewrite.c>

RewriteEngine On

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.php [L]

</IfModule>

Bemerkenswert ist noch, das man innerhalb von IfModule kein Hash, Teppich, Schlängeli, Nummer (#) schreiben sollte, da dies Internal Errors produziert.
Nun kann man Adressen auf dem Server aufrufen, die es gar nicht gibt und wird entsprechend an die index.php weitergeleitet. Zu den htaccess Dateien wäre noch zu sagen, dass sich das ganze rekursiv auswirkt und Hierarchien möglich sind. Wenn in einem Unterordner eine weitere Datei mit Rewrite Anweisungen liegt und dieses Verzeichniss oder ein tieferes aufgerufen wird, gilt die oberste Htaccess Datei.

Nun kann man sich die Sache unter PHP anschauen. An die Requesturl kommen wir wie gesagt mit dem $_SERVER Array:

$request = $_SERVER['REQUEST_URI'];

Wie so oft bei PHP bekommt man Probleme, wenn Sonderzeichen ins Spiel kommen:

Die Url:

http://localhost/Frühling/läßt/sein/blaues/Band

verwandelt sich in:

/Fr%C3%BChling/l%C3%A4%C3%9Ft/sein/blaues/Band

Um auswertbare Zeichenketten zu bekommen,  muß die Url dekodiert werden und der String nach UTF-8 gewandelt werden:

echo utf8_decode(urldecode($request));

Nun wird noch ein Array daraus gebastelt um die Urls bequem verarbeiten zu können:

function get_url(){
	$request = $_SERVER['REQUEST_URI'];
	$crumbs = explode('/' , utf8_decode(urldecode($request)));
	$retval=array();
	foreach ($crumbs as $crumb){
		if ( trim($crumb) !='')
			array_push($retval, $crumb);

	}//each
	return $retval;
}//function

Seehr schön. Um nun sinnvoll mit Permalinks zu arbeiten, muß man sich noch Gedanken über das Erstellen und Auswerten machen. Zuerst sollte man sich Gedanken über den Aufbau machen. Hier schauts so aus:

Jahr/Monat/Tag/Titel

Das erzeugen der Adresse erledigt die Funktion generate_url mit den Argumenten Titel und gewünschtem Format:

function generate_url($title, $format){
	$d= getdate();
	$year = $d["year"];
	$month = $d['month'];
	$day = $d['mday'];
	$msg = $format;

	$msg = str_replace ('%y', $year.'/', $msg);
	$msg = str_replace ('%m', $month.'/', $msg);
	$msg = str_replace ('%d', $day.'/', $msg);
	$msg = str_replace ('%t', $title.'/', $msg);

	return "http://" . $_SERVER['SERVER_NAME']."//$msg";
}//function

Nicht sehr elegant ist der 4fache Aufruf von str_replace. Ich habe auf die Schnelle nicht herausbekommen, wie ich das eleganter hinbekomme. Über sachdienliche Hinweise zur Aufklärung würde ich mich sehr freuen

Nun kann man das ganze mal ausprobieren:

//test generate_url
$format_string='%y%m%d%t';

$url= generate_url("PHP + mod_rewrite", $format_string);
echo "$url";

Der nächste Schritt ist nun die Auswertung. Dafür werden wir wieder den Formatstring benutzen, da er uns beschreibt, was wie aus der Persistenzschicht rausgeschaufelt werden muß. Wenn z.b nur 2010 angegeben ist, bekommt man jegliche Inhalte aus diesem Jahr, bei April den Inhalt aus Jahr und Monat, wenn der Artikel angegben ist, nur der Artikel:

function get_the_content($url_array, $format) {
	$n = count ($url_array);

	/*den Formatstring in ein Array wandeln, null values entfernen
	und den Index neu aufbauen :)*/

	$f_arr = explode('%', $format);
	$empty_element = array_keys($f_arr, "");

	foreach ($empty_element as $e)
		unset ($f_arr[$e]);
	$f_arr = array_values($f_arr);

	//titel in url vorhanden?
	$t_pos =  array_search('t', $f_arr);
	if ( $url_array[$t_pos] !="")
		return  "Select Artikel: ". $url_array[$t_pos];

	//Datum Basteln
	$year = $month = $day = 0;
 	$year = $url_array[array_search('y', $f_arr)];
	$month = $url_array[array_search('m', $f_arr)];
	$day = $url_array[ array_search('d', $f_arr)];
	//Monatsname nach Zahl
	$month_number = "";
	for($i=1;$i<=12;$i++){
		if(date("F", mktime(0, 0, 0, $i, 1, 0)) == $month){
			$month_number = $i;
			break;
		}
	}//for

	if ($month_number=="")
		return "nichts gefunden";
	//Unixtimestamp erzeugen
	$d = date('c', mktime(0, 0, 0, $month_number, $day, $year));
	return "SELECT Artikel in Datumsbereich $d";
}//function

Exemplarisch habe ich einfach mal einen Unixtimestamp erzeugt und zurückgegeben. Wenn jemand das ernsthaft verwenden will, sollte er sich ein paar Gedanken über Oo machen und manches schöner schreiben.

Notizen zur Pluginschreiberei für WordPress

spot the wordpress
Attribution-NonCommercial-ShareAlike License by miss604

Wordpressplugins selbst schreiben ist kein Problem. Die Dokumentation ist recht ordentlich, manche Dinge sind jedoch ein wenig hakelig. Aber mit ein wenig Hartnäckigkeit erzielt man rasch Ergebnisse.

Einen englischsprachigen Einstieg findet man hier:

Diese Anleitung orientiert sich an dem Plugin schmie_twitter. Download kann man es hier. Die Eigentlichen Funktionen sind soweit es geht von WordPresscode getrennt.

Das Plugin aktualisiert den Status bei Shortmessaging Diensten (Twitter und Identica), wenn ein neuer Artikel erstellt wurde, oder ein älterer aktualisiert wurde.

Näher betrachtet wird folgendes:

  • Das Erstellen einer Settingspage.
  • Lesen und Schreiben von WordPress Einstellungen
  • Das Hook- oder Action System von WordPress
  • Übersetzungen von Plugins
  • Verteilen eines Plugins über das Plugin Directory

Ich möchte an dieser Stelle keine deutsche Übersetzung der WordPressdokumentation erstellen, sondern kurz und bündig beschreiben, was wie funktioniert.

Dieses Plugin sendet keine Ausgaben an den Benutzer, abgesehen von einer Seite mit Einstellungen.

in guter Einstieg ist das Betrachten der Verzeichnisshierarchie

schmiddi@dasgrauen:~/web/schmie_twitter$ ls -R

.:

classes  img  lang  readme.txt  schmie_twitter_options.php  schmie_twitter.php  screenshot-1.png  uninstall.php

./classes:

Cschmie_twitter.php  CToIdentica.php  CTo_Twitter.php  CUrlshortener.php

./img:

logo.png

./lang:

schmie_twitter-de_DE.mo  schmie_twitter.de_DE.mo  schmie_twitter.po

Die beiden letzten Ordner sind relativ unwichtig. In img. liegt eine Bilddatei mit dem Logo und in lang sind die zur Internationalisierung nötigen Dateien abgelegt. Dazu später mehr.

In classes liegen 4 Klassen, die die Grundfuntionen bereitstellen.

schmie_twitter.php ist der Einstieg in das Plugin.

Um ein Plugin zu schreiben wird nur eine PHP Datei benötigt, die den Namen des Plugins trägt. Die Datei kann auch in einen Unterordner nach diesem Schema abgelegt werden : schmie_twitter/schmie_twitter.php

Wichtig ist die Einzigartigkeit des Namens. Leider bietet WordPress keine Namespacefunktionen oder sonstige Garantien der Einzigartigkeit. Ich wähle zur Namensvergebung einen Präfix und anschließend einen prägnanten Namen. Die Plugindatei benötigt einen speziellen Header, der von WordPress geparsed wird:

Actions & Hooks

Zumindest der Plugin Name muß gesetzt sein, damit WordPress das Plugin akzeptiert.

Was nun folgt ist normaler PHP Code:

	//Codeauszug schmie_twitter.php

	function post_twit($post_ID) {
	/**
	....
	**/
	}//function
	add_action('publish_post','post_twit');

Bemerkenswert ist der add_action Mechanismus. (http://codex.wordpress.org/Function_Reference/add_action)

Um dies zu erklären, muß auf die Hook Mechanismen eingegangen werden. Bestimmten Ereignisse sind mit Hooks (Haken) oder actions (Aktionen) verbunden. Wenn diese Ereignisse ablaufen, wird abgeprüft, welche Funktionen noch ausgeführt werden sollen. Diese werden mit der add_action Funktion angemeldet. In unserem Fall wird bei dem Ereigniss ‚publish_post‘ also bei der Veröffentlichung eines Posts die Funktion post_twit angehängt und entsprechend ausgeführt.

Ein weiteres Beispiel:

/**** Save Settings **/
	$st= new CSchmie_twitter();
	add_action( 'admin_init', 'register_mysettings' );
	function register_mysettings() {
		global $st;
		$st->register_mysettings();
	}//function

Hier wird deutlich, dass das Actionsystem nur Funktional und nicht objekt-orientiert angesteuert werden kann. In der Funktion register_mysettings wird die Methode register_mysettings des CSchmie_twitter Objekts aufgerufen. Das Schlüsselword global wird benötigt um die Variable innerhalb der Funktion bekannt zu machen, da PHP merwürdige Gültigkeitsbereiche im Vergleich zu anderen Sprachen hat.

Ein letztes Beispiel wäre das Erzeugen einer Settingspage:

/** @see schmie_twitter_options.php ***/
add_action('admin_menu', 'schmie_twitter_create_menu');
function schmie_twitter_create_menu() {
	add_options_page('schmie_Twitter Settings', 'schmie_twitter Settings', 'administrator', __FILE__, 		'schmie_twitter_settings_page', __FILE__);

}//function

function schmie_twitter_settings_page() {
	if ( file_exists ( dirname(__FILE__).'/schmie_twitter_options.php' )) {
		include( dirname(__FILE__).'/schmie_twitter_options.php' );
	} else {
		e( '

Couldn't locate the settings page.

', 'wp-to-twitter' );
	}//fi
}//function
Auch hier werden wieder Grenzen des Systems aufgezeigt. Zuerst wird die Action create_menu() bei der Generierung des Administratormenüs aufgerufen und erzeugt die Links zu der Einstellungsseite und anschließend wird die Einstellungsseite generiert. Der include Mechanismus ist analog zum Präprozessorbefehl in C.
Durch die Modularisierung mit Hooks kann dies nicht in einem Schritt geschehen. Möglich wäre nur eine Zusammenfassung innerhalb einer neuen Funktion . (@@@wirklch? Meta Hooks?)

Optionspages

Der nächste Punkt ist das Erstellen einer Einstellungsseite (Optionspage).
Zum größten Teil werden hier nur Abgleichungen mit der Datenbank erledigt.
Wichtig hierbei sind die Funktionen register_settings(‚optiongroup‘,’option‘), und get_option(‚option‘).
WordPress speichert sämtliche Einstellungen in einer Datenbanktabelle namens Options. Die Funktion register_settings prüft das Vorhandensein der Einstellung und legt sie gegebenfalls an. Bei Optiongroup handelt es sich um eine Art Namespace, um die Einzigartigkeit zu gewährleisten.
Die Verarbeitung der Einstellungen geschieht über ein HTML Formular. Zuerst wird die Optiongroup bekannt gemacht:
<?php settings_fields( ’schmie_settings_group‘ ); ?>
Der Lese / Schreibzugriff wird über die Funktion get_option(‚option‘) abgewickelt. Die Funktion ist robust, doch leider mit sehr viel HTML verbunden:
BESCHREIBUNG
<input name="OPTION NAME" type="text" value=";" />

Zum einen ergbit dies eine sehr unelegante Vermischung aus PHP un HTML, zum anderen ist es mit einigem Schreibaufwand verbunden. Generatorfunktionen sind meines Wissens nicht vorhanden. Sehr nützlich für mich waren ein paar einfache PHP Funktionen, die die Einstellungen mit einem HTML Rumpf ausgeben:

function schmie_generate_textfield($description,$option,$password=false) {
	$option_val=get_option($option);
	$type='text';
	if ($password)
		$type='password';

echo "	
        $description
        
        ";
}//function

//looks nice :)
schmie_generate_textfield(__("Twitter Password", 'schmie_twitter'), 'schmie_tw_pass',true);

Bei der Namenswahl sollte man immer recht vorsichtig sein, damit keine WordPressfunktionen überschrieben werdem. Auch hier hat sich wie bei Settings das Präfixsystem für mich bewährt.

Übersetzung

Das Thema Internationalisierung sollte direkt zu Anfang der Entwicklung betrachtet werden. Dies erspart viel Änderungen am Code. Verwendet wird die innerhalb der Sprache PHP implementierte Gettext Bibliothek. Das Softwarepaket gettext ist für Übersetzungen erforderlich.

Links:
http://www.gnu.org/software/gettext/manual/gettext.html
http://www.php.net/manual/de/intro.gettext.php
http://codex.wordpress.org/I18n_for_WordPress_Developers

Sehr empfehlenswert ist dieser Beitrag aus der Blogosphäre, der deutlich tiefer auf die Thematik eingeht. Leider habe ich den Artikel zu spät entdeckt:(:

http://www.tuxlog.de/wordpress/2007/wordpress-plugins-ubersetzen-mit-gettext-po-und-pot-dateien/

Zuerst muß im Code gekennzeichnet werden, was übersetzt werden soll. Dabei wird die Zeichenkette mit einem Funktionsaufruf umschlossen und ein Namespace für die Übersetzung angegeben:

$foo=__('Dies könnte Ihre Übersetzung sein', 'NAMESPACE') .

Eine direkte Ausgabe erfolgt mit :

_e('Hallo Welt', 'NAMESPACE');

Platzhalter sind auch möglich:

_e("$ctr Flaschen Bier an der Wand",'NAMESPACE');

oder mit

printf(__("%d Flaschen Bier an der Wand",'NAMESPACE'), $ctr);

Das Einsetzten des Namespaces kann auch durch diverse Skripte (z.B.: innerhalb der Admin Funktionen im WordPress Plugindirectory). Dann genügt auch ein $bar = __(‚Hallo W‘);

Nun wird mit xgettext mit den o.g. Admin Funktionen eine POT Datei (Portable Object Template) erzeugt.

xgettext -L PHP -k –keyword=_e –keyword=__ –from-code=utf8 –default-domain=schmie_twitter –output=schmie_twitter.pot *.php

Dabei handelt es sich um eine Liste der zu übersetzenden Strings. Nun erstellt man eine Kopie der POT Datei und pflegt die entsprechenden Übersetzungen dort ein:

cp schmie_twitter.pot schmie_twitter-de_DE.po

Dies kann per Texteditor oder speziellen Tools wie KBabel oder Poedit erfolgen.

Wenn dies erledigt ist, wird eine binäre Datei mit der Namenskonvention pluginname-<lang_string>.mo mit msgfmt erzeugt:
msgfmt -o schmie_twitter.mo de_DE.po
Den Namnesstring erfährt man in der wp-config.php. Die Datei ist im Hauptverzeichniss der WordPress installation zu finden. Der String für deutschsprachige Unterstützung sieht so aus: define (‚WPLANG‘, ‚de_DE‘);

Nun muß die Übersetzung innerhalb des PHP Codes geladen werden. Dies geschieht mit folgenden Zeilen:

$path= basename(dirname(__FILE__));
	$domain='schmie_twitter';
	load_plugin_textdomain('schmie_twitter', false, $path . '/lang');
Ich habe mich dazu entschlossen alle Übersetzungen in den Unterordner lang zu legen.

Deinstallation:

Ein Skript kann händisch entfernt werden, indem einfach alle PHP Dateien entfernt werden. Datenbankeinträge bleiben dabei jedoch vorhanden. Deswegen solle immer der Deinstallationsmechanismus von WordPress verwendet werden.
Um eine Uninstallroutine zu schreiben, gibt es 2 Möglichkeiten:
-per hook
-Uninstall.php
Bei der Methode mit Hook wird wie bei den oben beschrieben Actions vorgegangen:
register_uninstall_hook(__FILE__, 'my_uninstall_hook');
	function my_uninstall_hook() {
		delete_option('option_1');
	}//
Allerdings wurde dieser Hook erst mit der Version 2.7 von WordPress implementiert. Man kann zwar mit function_exists(callback) eine Fehlermeldung abfangen, allerdings passiert bleiben die Einstellungen in der Datenbank.
Deshalb sollte man dem Weg mit der Uninstall.php den Vorzug geben. Dabei wird im Hauptverzeichnis des Plugins die genannte Datei erzeugt. Bevor man an das entfernen der Einstellungen geht, sollte man Prüfen, ob die WP_UNINSTALL Konstante gesetzt ist:

	if(!defined('WP_UNINSTALL_PLUGIN') )
	    exit();
	delete_option( 'schmie_tw_user' );

das WordPress Plugin Directory

Da man Plugins aus dem Directory recht bequem direkt in WordPress installieren kann, empfiehlt es sich seine Werke dort zu veröffentlichen. Nach der Registrierung (Passwort aufschreiben) füllt man einen Antrag auf Aufnahme des Plugins an. Ein WordPress Mensch schaut sich das Plugin an und nach einer gewissen Zeit (Spanne: Stunden bis Tage) wird das Plugin freigeschaltet. Zuerst schaut man sich die Pluginseite an, vor allem den Adminbereich. Was jetzt noch fehlt ist die Beschreibung im Directory. Dies geschieht über ein Textfile. Ausreichende Infos gibts hier.
Es gibt auch eine sehr brauchbare Seite mit Informationen zum Repo http://wordpress.org/extend/plugins/about/svn/.

Hier die Schnellversion (auch für den Laien):
Die Adresse des Repo’s findet Ihr auf Eurer Pluginseite.
Zuerst wird in ein leeres Verzeichniss ausgecheckt (Check Out):

	svn co http://plugins.svn.wordpress.org/schmie-twitter/

Hier gibts 3 Ordner branches tags trunk. Die aktuelle Version des Plugins liegt immer im trunk.
Also das Plugin dorthin kopieren. Nun fügen wir die Dateien dem Repository hinzu:

svn add trunk/*

Sehr nett ist es ältere Versionen im Tagsverzeichnis zu sichern. Dann stehen auch diese Versionen zum Download zur Verfügung. Falls man mal in älteren Code sehen will und nicht so bewandert mit SVN ist, hat man hier alles schnell zur Hand also:

svn cp trunk tags/1.0

Eingecheckt wird so (Check In):

svn ci -m "hey du willst es hier ist es"

Anmeldung am Repository mit den Directory Benutzerdaten.
Jetzt noch ein paar Worte für diejenigen, die noch nie mit Versionierungssoftware gearbeitet haben. Löschen im Dateimanager ist recht sinnlos, wenn man mit SVN oder ähnlichem arbeitet, da die Datei(en) zwar von der Platte getilgt sind, aber nicht aus dem Repo. Das gilt auch für Verschieben oder Umbenennen von Dateien. Auf solche Details ist immer zu achten. Eine gute Dokumentation für Subversion findet sich in Version Control with Subversion.

Das wärs von mir zu diesem Zeitpunkt. Viel Freude beim skripten / programmieren. Für Tipps oder Verbesserungen bin ich wie immer sehr dankbar. Wenn ich mal wieder ein Widget schreibe oder eines besseren belehrt werde, wird der Artikel angepasst.

System Tray in Python mit GTK

Green Tree Python
Attribution-NonCommercial-ShareAlike License by Sebastian Niedlich (Grabthar)

Im Moment beschäftige ich mich ein wenig mit Python und GTK.  GTK kenn ich schon ein wenig länger, aber Python programmiere ich erst seit einer Woche und ich muß sagen, es macht wirklich Spaß und produziert im Gegensatz zu Perl sehr gut lesbaren Code:)

Nun schreibe ich gerade ein kleines Notizbuch, dass auch einen Platz im System Tray haben soll. Also mal in der GTK Referenz irgendwas in der Richtung Window.visible() gesucht aber Fehlanzeige.

Da ich auch durch googlen nichts manierliches gefunden habe hier meine kleine Interpretation des Problems in Python. HF:)

#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gtk
import egg.trayicon

class PythonTray:
	def __init__(self,
			icon,		#Pfadangabe zu einem Bild (String)
			tooltip,	#Tooltiptext (String)
			event_handler	#Eventhandler fuer das Trayzeichen mitgeben
			):
		"""
		Erzeugt das Trayicon
		"""
		tray_object = egg.trayicon.TrayIcon(tooltip)
		tray_object.set_tooltip_text(tooltip)
		#bild laden + skalieren
		image_widget = gtk.Image();
		pixbuf = gtk.gdk.pixbuf_new_from_file(icon)
		scaled_buf = pixbuf.scale_simple(32,32,gtk.gdk.INTERP_BILINEAR)
		image_widget.set_from_pixbuf(scaled_buf)

		event_box = gtk.EventBox()
		event_box.add(image_widget)
		event_box.connect('button_press_event', event_handler)
		tray_object.add(event_box)
		tray_object.show_all()

	def showWindow(self,
		"""
		Blendet ein Fenster ein und aus
		"""
			window	#handle fuer's Fenster
			):

		if not(window.is_active()):
			window.show_now()
			window.present()
		else:
			window.hide()

class Trayexample:
	tray = PythonTray
	#kill den shit
	def destroy(self, widget, data=None):
		gtk.main_quit()

	###delete event ueberschreiben
	def delete_event(self, widget, event, data =None):
		self.window.hide()
		return True

	def applet_face_click(self, window, event, *data):
		if event.button ==1:
			#haesslich
			self.tray.showWindow(window=self.window)
			#self.showWindow()
		if event.button ==3:
			self.destroy(self.window)

	def __init__(self):	#Konstruktor
		#neues Fenster erzeugen
		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		self.window.connect("delete_event", self.delete_event)	###Das X wird ignoriert :)
		self.window.set_size_request(640,480)		###fenstergroesse
		self.window.set_title('SystemTray Example')
		self.window.connect("destroy", self.destroy)
		self.window.set_icon_from_file('/usr/share/icons/gnome/scalable/apps/im-aim.svg')
		#Tray Krams
		self.tray= PythonTray ("/usr/share/icons/gnome/scalable/apps/im-aim.svg","MyTray",self.applet_face_click)
		#self.window.show()

	def main(self):

		gtk.main()

if __name__ == "__main__"	:
	hello = Trayexample()
	hello.main()

hier kann das Skript runterladen

Reblog this post [with Zemanta]