September 3, 2010 0

Resize all Images on a WordPress Blog

By matthias in useful

Mein Schatz hat vor einiger Zeit ihr WordPress Theme gewechselt, Nachteil daran war allerdings das ihre Bilder (und sie hat viele davon auf ihrem Blog) zu groß waren um schön dargestellt zu werden. Ich hab mich natürlich als braver Freund an die Arbeit gemacht um eine Lösung zu finden. Das ist allerdings gar nicht so einfach weil WordPress die Blog-Einträge samt HTML in die Datenbank haut. Das einzige was mir einfiel war das manuelle editieren aller Einträge, das war mir dann aber doch zu blöd.

Also hab ich meine Ruby-Künste ein wenig abgestaubt, eine MySQL Library und ein HTML-Parser haben brav geholfen :)

#!/usr/bin/env ruby
# resizing all images in a wordpress blog to 70% their original size
# author: Matthias Fassl <mf@x1598.at>
# license: MIT http://www.opensource.org/licenses/mit-license.php
require "mysql"
require "hpricot"

begin
        dbh = Mysql.real_connect("localhost", "user", "password", "wordpress")

        res = dbh.query("SELECT ID, post_content FROM wp_posts")
        while row = res.fetch_row do
                doc = Hpricot(row[1])
                elements = doc.search("img")
                elements.each { |item|
                        item.attributes["width"] = (item.attributes["width"].to_i*7/10).to_i.to_s
                        item.attributes["height"] = (item.attributes["height"].to_i*7/10).to_i.to_s
                }  
                dbh.query("UPDATE wp_posts SET post_content=’#{dbh.escape_string(doc.to_html)}’ WHERE ID=#{row[0]}")
        end
        res.free
rescue Mysql::Error => e
        puts "Error code: #{e.errno}"
        puts "Error message: #{e.error}"
        puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
ensure
        dbh.close if dbh
end

Das ganze is eher dreckig und hardcoded, und verändert natürlich nicht die wahre Größe vom Bild sondern nur die Größe der Darstellung. Aber die einfachsten Lösungen funktionieren doch meistens am Besten.

Tags: , , , ,

September 2, 2010 0

Automatic Secure Backups für den faulen SysAdmin

By matthias in useful

Irgendwie hatte ich in den letzten Jahren das Gefühl das Informatike zwar sehr gut Wissen das Backups wichtig sind (und das anderen Leuten auch vorhalten wenn ihnen Daten verloren gehen) aber es selbst nicht so ernst damit nehmen. Die Gründe dafür sind sicher vielfältig, aber einen davon kann ich persönlich bestätigen: Faulheit. Backups machen ist stinklangweilig und sie nehmen auch noch viel Zeit weg die man für bessere Sachen verwenden könnte.
Ich hab jetzt beschlossen das sich diese Situation bei mir ändern muss, bei 1und1 stehen mir immerhin 160GB FTP Backup Space zur Verfügung! Und wenn ich schon an einer automatisierten Backup-Lösung arbeite kann ich die Daten gleich sicher verschlüsseln, den Datenklau passiert bevorzugt über die Backups (dort sind die Sicherheitsvorkehrungen meist nicht so streng).
Ich hab einfach mal begonnen das ganze für meine MySQL Datenbank zu machen, das Backup der Home-Verzeichnisse werde ich später auch noch integrieren.
Ich hab mittlerweile das Backup der gesamten MySQL Datenbank, des /home Verzeichnisses und des /etc Verzeichnisses integriert, das sind die wichtigsten Daten. Je nach Bedarf ist es vielleicht auch sinnvoll ein Backup von /var zu machen.

#!/bin/bash
# backup /home, /etc and MySQL Datbases, encrypt them with gpg and send them to backup storage (ftp)
# author: Matthias Fassl <mf@x1598.at>

#MySQL Access Data
MYSQLUSER="user"
MYSQLPASSWORD="password"

#FTP Access Data
FTPUSER="user"
FTPPASSWORD="password"
FTPHOST="ftp.backup.com"

GPGRECIPIENT="user@example.com"
MYSQLBACKUP="$(date +’%Y%m%d’)-mysql-backup.dump"
HOMEBACKUP="$(date +’%Y%m%d’)-home-backup.tar.gz"
ETCBACKUP="$(date +’%Y%m%d’)-etc-backup.tar.gz"
WORKDIR="/home"

#tar all home directories
tar cvzf $WORKDIR/$HOMEBACKUP /home/*

#tar all config files in /etc
tar cvzf $WORKDIR/$ETCBACKUP /etc

#copy & compress data
mysqldump –user=$MYSQLUSERpassword=$MYSQLPASSWORD –all-databases > $WORKDIR/$MYSQLBACKUP

#encrypt data
gpg –recipient $GPGRECIPIENT –encrypt $WORKDIR/$HOMEBACKUP
gpg –recipient $GPGRECIPIENT –encrypt $WORKDIR/$ETCBACKUP
gpg –recipient $GPGRECIPIENT –encrypt $WORKDIR/$MYSQLBACKUP
rm $WORKDIR/$MYSQLBACKUP
rm $WORKDIR/$HOMEBACKUP
rm $WORKDIR/$ETCBACKUP

#send data to backup storage
(cat <<EOF
open $FTPHOST
user $FTPUSER $FTPPASSWORD
binary
put $WORKDIR/$HOMEBACKUP.gpg $HOMEBACKUP.gpg
put $WORKDIR/$ETCBACKUP.gpg $ETCBACKUP.gpg
put $WORKDIR/$MYSQLBACKUP.gpg $MYSQLBACKUP.gpg
bye
EOF
)|ftp -n

rm $WORKDIR/$HOMEBACKUP.gpg
rm $WORKDIR/$ETCBACKUP.gpg
rm $WORKDIR/$MYSQLBACKUP.gpg

Fertig wäre das Skript, nun kann man es in /etc/cron.weekly/ verschieben damit es jede Woche ausgeführt wird (üblicherweise is das Sonntag um 00:00). Es ist sinnvoll das Skript auszuprobieren bevor ihr es eurem cron daemon anvertraut, man weiß nie was im Ernstfall nicht funktioniert.

So jetzt kann ich etwas entspannter Schlafen, obwohl…vielleicht sollte ich jetzt lieber eine Sicherungskopie meines GPG Schlüssels anfertigen :(

Jemand der inkrementelle Backups braucht sollte sich vielleicht mal duplicity anschauen. Das sieht ganz brauchbar aus.

Tags: , , , ,

September 1, 2010 0

StatusNet Updates

By matthias in Idea, Playground

Die API des mittlerweile recht populären Microblogging Dienstes identi.ca kann simpler kaum noch sein. Ein einfacher HTTP POST mit Basic Authentication reicht um einen neuen Status senden zu können.

Hier ein kleiner Versuch mit Ruby:


#!/usr/bin/env ruby
require ‘net/http’
require ‘uri’

username = "username"
password = "password"
statusnet = "http://identi.ca/api/statuses/update.xml"

#generate new http post and set authentication
url = URI.parse(statusnet)
req = Net::HTTP::Post.new(url.path)
req.basic_auth username,password

#set new status and send request
req.set_form_data({‘status’ => ARGV[0]},‘;’)
res = Net::HTTP.new(url.host,url.port).start { |http|
        http.request(req)
}
puts res

Geht aber auch einfacher kürzer wie auf der Seite der API Beschreibung steht:

curl -u username:password http://example.com/api/statuses/update.xml -d status=‘Howdy!’ -d lat=’30.468′ -d long=‘-94.743′

Je nachdem welchen Verwendungszweck man dafür hat dürfte das eine oder andere sinnvoller erscheinen. Als Antwort kommt man übrigens einen großen Patzen XML vor die Nase geworfen:

<?xml version="1.0" encoding="UTF-8"?>
<status xmlns:statusnet="http://status.net/schema/api/1/">
 <text>api test number 2</text>
 <truncated>false</truncated>
 <created_at>Fri Aug 27 19:41:52 +0000 2010</created_at>
 <in_reply_to_status_id></in_reply_to_status_id>
 <source>api</source>
 <id>47704838</id>
 <in_reply_to_user_id></in_reply_to_user_id>
 <in_reply_to_screen_name></in_reply_to_screen_name>
 <geo xmlns:georss="http://www.georss.org/georss">
  <georss:point>47.3333333 13.3333333</georss:point>
</geo>
 <favorited>false</favorited>
 <user>
  <id>55243</id>
  <name>Matthias F.</name>
  <screen_name>x1598</screen_name>
  <location>Austria</location>
  <description>tech geek.</description>
  <profile_image_url>http://avatar.identi.ca/55243-48-20091221203714.jpeg</profile_image_url>
  <url>http://x1598.at</url>
  <protected>false</protected>
  <followers_count>69</followers_count>
  <profile_background_color>#F0F2F5</profile_background_color>
  <profile_text_color>#867E7E</profile_text_color>
  <profile_link_color>#DF9D11</profile_link_color>
  <profile_sidebar_fill_color>#363126</profile_sidebar_fill_color>
  <profile_sidebar_border_color></profile_sidebar_border_color>
  <friends_count>143</friends_count>
  <created_at>Sun May 17 03:50:17 +0000 2009</created_at>
  <favourites_count>24</favourites_count>
  <utc_offset>7200</utc_offset>
  <time_zone>MET</time_zone>
  <profile_background_image_url>http://background.status.net/identica/8657-20100213212008.jpeg</profile_background_image_url>
  <profile_background_tile>true</profile_background_tile>
  <statuses_count>607</statuses_count>
  <following>true</following>
  <statusnet:blocking>false</statusnet:blocking>
  <notifications>true</notifications>
  <statusnet:profile_url>http://identi.ca/x1598</statusnet:profile_url>
</user>
 <statusnet:html>api test number 2</statusnet:html>
</status>

Das würde ich ja fast fahrlässige Bandbreiten Verschwendung nennen :)

Wenn man einfach nur an der Timeline der Freunde oder and der gesamten Timeline interessiert hat man es ein wenig einfacher:

curl http://identi.ca/api/statuses/friends_timeline/x1598.xml?count=10

Wenn man das jetzt schön anzeigen will dann würde ich empfehlen ein XSLT dafür zu basteln. Wenn man es weiter verarbeiten will würde ich einen guten XML Parser empfehlen. Ich hab bis jetzt gute Erfahrungen mit REXML gemacht:

#!/usr/bin/env ruby
require ‘net/http’
require ‘uri’
require ‘rexml/document’

uri = "http://identi.ca/api/statuses/friends_timeline/x1598.xml?count=10"
url = URI.parse(uri)
res = Net::HTTP.start(url.host, url.port) { |http|
  http.get(url.path)
}

doc = REXML::Document.new res.body
doc.elements.each("statuses/status") { |status|
  puts status.elements["user"].elements["name"].text
  puts status.elements["text"].text
}

Ich weiß zwar noch nicht was ich mit den gewonnenen Erkenntnissen machen werde, aber irgendwann werd ich ja wohl noch StatusNet Unterstützung irgendwo einbauen können :P

Achja, bei der Abfrage der Timeline kann man auch andere Formate wählen indem man statt dem “xml” in der url entweder json, rss oder atom angibt.

Tags: , , , ,

August 31, 2010 0

Nautilus Extensions mit Python

By matthias in Project, useful

Ich habe ja schon letzte Woche geschrieben das ich versuchen will meine XML-RPC Erkenntnisse mit einer Nautilus Extension zu verbinden damit mein Schatz es leichter hat, heute hab ich mich endlich darüber gewagt.
Mithilfe der nautilus library habe ich einen MenuProvider erstellt der wenn nur JPG,PNG oder GIFs (die von WordPress akzeptieren Medien) markiert sind einen Eintrag “Upload Images to WordPress” im Kontextmenü anzeigt. Dafür muss man die Methode “get_file_names” überschreiben.

#!/usr/bin/env python
import xmlrpclib, nautilus, ConfigParser, os

SUPPORTED_FORMATS = ‘image/jpeg’, ‘image/png’, ‘image/gif’

class WordPressImageUploadMenuExtension(nautilus.MenuProvider):

    def get_file_items(self, window, files):
        for filename in files:
            if not filename.get_mime_type() in SUPPORTED_FORMATS:
                return
        item = nautilus.MenuItem(
            "WordpressImageUploadMenuExtension::Upload_Images",
            "Upload Images to WordPress",
            "Upload Images to WordPress"
        )  
        item.connect(‘activate’,self.menu_activate_cb, files)

        return [item]

Es ist eigentlich ganz leicht, zuerst wird überprüft ob alle markierten Dateien ein unterstütztes Format haben, dann wird ein Menüeintrag erstellt und zum Schluss binden wir diesen Menüeintrag an eine weitere Methode.
Man darf aber nicht naiv sein und glauben das die “files” die hier übergeben werden ein Array von Typ String ist, nein der Typ dieser files ist NautilusVFSFile. Hat mich einiges an Sucharbeit gekostet um dahinter zukommen.

In der zweiten Funktion verrichten wir dann einfach die XML-RPC Arbeit nachdem wir die benötigten Parameter aus dem Config-File eingelesen haben.

   def menu_activate_cb(self, menu, files):
        cfg = ConfigParser.ConfigParser()
        cfg.read(os.path.expanduser("~/.wordpress"))
        xmlrpc_url = cfg.get("wordpress","xmlrpc")
        username = cfg.get("wordpress","username")
        password = cfg.get("wordpress","password")
        blog_id = cfg.get("wordpress","blogid")

        xmlrpc_client = xmlrpclib.ServerProxy(xmlrpc_url)
        xmlrpc_multicall = xmlrpclib.MultiCall(xmlrpc_client)
        for filename in files:
            num = len(filename.get_uri_scheme())+3
            filehandle = open(filename.get_uri()[num:], "rb")
            data = filehandle.read()
            filehandle.close()
            mediaStruct = {
                ‘name’ : filename.get_name(),
                ‘type’ : filename.get_mime_type(),
                ‘bits’ : xmlrpclib.Binary(data)
            }  
            xmlrpc_multicall.wp.uploadFile(blog_id,username,password,mediaStruct)
        for result in xmlrpc_multicall():
            print result

Ich bin noch nicht vollkommen glücklich damit, ich könnte die Zugangsdaten für WordPress im Gnome-Keyring abspeichern und eine Rückmeldung über den Status des Uploads wäre auch toll. Im Moment friert Nautilus ein bis er fertig ist, das Skript sagt ja noch nicht mal ob es erfolgreich war oder nicht :)

Aber für den Anfang reicht es einmal, wenn ich wieder Lust habe werde ich die fehlenden Sachen einbauen und ein Debian-Paket daraus machen.

Für alle die Nautilus nicht verwenden hab ich auch ein brauchbares CLI Tool

#!/usr/bin/env python
# author: Matthias Fassl <mf@x1598.at>
# license: MIT http://www.opensource.org/licenses/mit-license.php
# version: 2010-08-27
import xmlrpclib, ConfigParser, sys, os, mimetypes

SUPPORTED_FORMATS = ‘image/jpeg’, ‘image/png’, ‘image/gif’

cfg = ConfigParser.ConfigParser()
cfg.read(os.path.expanduser("~/.wordpress"))
xmlrpc_url = cfg.get("wordpress","xmlrpc")
username = cfg.get("wordpress","username")
password = cfg.get("wordpress","password")
blog_id = cfg.get("wordpress","blogid")

xmlrpc_client = xmlrpclib.ServerProxy(xmlrpc_url)
xmlrpc_multicall = xmlrpclib.MultiCall(xmlrpc_client)
for filename in sys.argv[1:]:
    filehandle = open(filename, "rb")
    data = filehandle.read()
    filehandle.close()
    basename = os.path.basename(filename)
    mediaStruct = {
        ‘name’ : basename,
        ‘type’ : mimetypes.guess_type(basename)[0],
        ‘bits’ : xmlrpclib.Binary(data)
    }  
    xmlrpc_multicall.wp.uploadFile(blog_id,username,password,mediaStruct)
for result in xmlrpc_multicall():
    print result[‘url’]

Das Config-File dazu hat folgendes Format:

[wordpress]
xmlrpc: http://example.com/xmlrpc.php
username: admin
password: password1
blogid: 0

Tags: , , ,

August 30, 2010 0

Prevent Hotlinking

By matthias in Idea, Rant, useful

Gegen die “Bilder-Klauer” die sich die Frechheit herausnehmen nicht einmal die eigene Bandbreite verbrauchen zu wollen gibt es ein einfaches Mittel. Eine simple UrlRewrite Regel die überprüft ob der Referer von der lokalen Webseite kommt und in jeden anderen Fall einen ungültigen Link produziert :)

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://.*milktea.at [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]

Das ganze einfach in eine .htaccess Datei im entsprechend zu schützenden Ordner. Fertig.

Ich sollte am Besten auf ein Bild umleiten wo erklärt wird das man das Bild kopieren soll und einen Link auf das Blog setzen soll. Für alle die solche Hotlink Prevention uncool finden kann ich Privoxy empfehlen, dieser Anonymisierungsproxy filtert Sachen aus dem HTTP Stream die nicth unbedingt notwendig sind um die Privatsphäre zu schützen.

Tags: , , ,

August 29, 2010 0

Dynamic loading in C

By matthias in Project

Für meinen Bittorrent tracker racker wollte ich austauschbare Datenbank-Module erstellen. Benützer können sich auf diese Art aussuchen ob sie lieber MySQL, Postgres, SQLite, oder doch lieber etwas eigenes programmieren wollen (da hat ja doch jeder Geek seine eigenen Präferenzen). Ich hab mich einmal damit beschäftigt was man machen muss um so eine Shared Library zu erstellen, und wie man diese Library im Code dynamisch nachlädt.

Die erste kleine Test-Library sieht äußerst unspektakulär aus:

#include <stdio.h>

void test_function(FILE* out, FILE* in) {
        fprintf(out,"Hello World!\n");
}

Wichtig ist in diesem Zusammenhang eigentlich nur wie wir diese Library compilen

gcc -fPIC -c testsharedlib.c
gcc -shared -Wl,-soname,testsharedlib.so.1 -o testsharedlib.so.1 testsharedlib.o

Danach sitzen wir auf einer testsharedlib.so.1 die wir während der runtime eines Prozesses explizit laden wollen. So könnte man vielleicht im Config-File einen Eintrag machen “database=”foobar-sql.so” und der Prozess verwendet für alle Datenbankzugriffe die foobar-sql Library. Im folgenden Beispiel gehe ich aber nicht darauf ein wie man Config-Files in C verarbeitet, das füllt ein paar andere Posts.

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char* argv[]) {
        //declare function pointer
        void (*test_function)(FILE* out, FILE* in);
        //Open Library
        void *handle = dlopen("./testsharedlib.so.1", RTLD_LAZY);
        //Look for Function and return function pointer
        http_get = dlsym(handle, "http_get");
        //execute
        (*test_function)(stdout,stdin);
        //close library
        dlclose(handle);
        return EXIT_SUCCESS;
}

Wichtig ist hier das man nicht vergisst die dlfcn.h zu includen und beim compilen “-ldl” anzugeben. Eine gute Beschreibung von dlopen, dlsym bieten die entsprechenden Man-Pages. In diesem kleinen Beispiel habe ich aus Gründen der Lesbarkeit auf eine Fehlerbehandlung verzichtet, davon rate ich ab.

Tags: , , , , , ,

August 28, 2010 0

Exploring GPX

By matthias in Playground, Project

Seit circa einem Monat versuche ich regelmäßig laufen zu gehen, sprich ungefähr alle 2-3 Tage, einerseits um mir fehlende Endorphine zu holen die mir zeit meiner letzten Zigarette fehlen, aber auch um zu sehen um wieviel ich mich steigern kann wenn ich nicht rauche. Es ist für mich also Überlebenshilfe und Motivation in einem. Um zu sehen um wieviel ich mich verbessere brauche ich aber Daten, am Besten wäre natürlich Lungenvolumen, Puls, Geschwindigkeit, Atmungsregelmäßigkeit, etc. Allerdings übersteigt das etwas meine Mittel also muss der GPS-Empfänger auf meinem Android Smartphone reichen.

Auf meinen heutigen Lauf bin ich zugegeben etwas Stolz, heute hab ich zum ersten Mal die 10km/h Durchschnittsgeschwindigkeit gebrochen.


20100826 Atzgersdorf-Schwarzenhaidestraße auf einer größeren Karte anzeigen

Als Informatiker gebe ich mich allerdings nicht damit zufrieden meine Tracks mitzuschneiden und sie mir nachher auf Karten anzusehen, die meiste Zeit laufe ich sowieso dieselbe Strecke! Interessant wäre Geschwindigkeitsprofil, Gesamtlaufzeit, Gesamtstrecke, Zeit die ich für einen Kilometer brauche, etc.
Ich habe also wieder ein bisschen mit Ruby herumgespielt und versuche nun aussagekräftige Daten zu bekommen, im Idealfall würde ich zum Jahresabschluss gerne ein Motivationsbuch mit Latex schaffen wo jeder Lauf enthalten ist.

Zu Beginn wird mal ein Blick in ein GPX File reingeworfen:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="details.xsl"?>
<gpx
 version="1.0"
 creator="My Tracks for the G1 running Android"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://www.topografix.com/GPX/1/0"
 xmlns:topografix="http://www.topografix.com/GPX/Private/TopoGrafix/0/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd">

<trk>
<name><![CDATA[Atzgersdorf-Schwarzenhaidestraße]]></name>
<desc><![CDATA[Gemütlich. Nicht außer Atem]]></desc>
<number>4</number>
<topografix:color>c0c0c0</topografix:color>
<trkseg>
<trkpt lat="48.145564" lon="16.294216">
<ele>236.6999969482422</ele>
<time>2010-07-23T16:14:23Z</time>
</trkpt&gt,
<trkpt lat="48.1456" lon="16.29428">

<ele>245.60000610351563</ele>
<time>2010-07-23T16:14:24Z</time>
</trkpt>
<trkpt lat="48.145566" lon="16.294354">
<ele>250.0</ele>
<time>2010-07-23T16:14:25Z</time>
</trkpt>

Das kann ja meine Oma parsen! :)

Öffnen einen GPX Files und Ausgabe von Name und Beschreibung mit ein bisschen Ruby-Liebe:

require ‘rexml/document’

file = File.open("track1.gpx","r")
  doc = REXML::Document.new(file)
  doc.elements.each(‘gpx/trk’) { |track|
      puts track.elements["name"].text
      puts track.elements["desc"].text
  }

Wenn man durch alle Trackpoints iterieren will (wird notwendig sein um Streckenlänge und Geschwindigkeit zu berechnen):

doc.elements.each(‘gpx/trk/trkseg/trkpt’) { |point|
    currLat = point.attributes["lat"].to_f #breitengrad
    currLon = point.attributes["lon"].to_f #laengengrad
    currTime = Time.parse(point.elements["time"].text) #zeitpunkt
    currElevation = point.elements["ele"].text.to_i #hoehe
  }

So, spätestens jetzt haben auch die unaufmerksamen Leser bemerkt das die Daten die man bekommt relativ unhandlich sind für so simple Fragen wie “Wie weit bin ich gelaufen?”. Da wir nur Breiten- und Längengrade zur Verfügung haben muss man für eine Auswertung ein bisschen in der Mathematik-Kiste graben. Da wünscht man sich ja glatt das die Erde eine Scheibe wäre, oder zumindestens rund! Nein, sie muss ja ein rotationssymetrisches Ellipsoid sein :(
Naja, nach ein wenig googlen hab ich eine Formel gefunden mit der man den Abstand zwischen zwei Koordinationpuntken berechnen kann (tut mir ehrlich leid ich finde die Quelle nicht mehr):

def haversine_distance(lat1, lon1, lat2, lon2)
  earthRadius = 6371
  radPerDegree = Math::PI/180
 
  deltaLatRad = (lat2 – lat1) * radPerDegree
  deltaLonRad = (lon2 – lon1) * radPerDegree

  lat1Rad = lat1 * radPerDegree
  lat2Rad = lat2 * radPerDegree
  lon1Rad = lon1 * radPerDegree
  lon2Rad = lon2 * radPerDegree

  a = (Math.sin(deltaLatRad/2))**2 + Math.cos(lat1Rad) * Math.cos(lat2Rad) * (Math.sin(deltaLonRad/2))**2
  c = 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a))

  return c * earthRadius * 1000
end

Damit konnte ich jetzt schon Daten aus den GPX Files extrahieren, ich habe auch schon versucht das Höhen- und Geschwindigkeitsprofile zu plotten, allerdings ist dabei noch nichts schönes rausgekommen. :(

Für die Numeriker unter uns hab ich auch ein kleines XSL File das so nebenbei entstanden ist, das macht das GPX File vielleicht ein bisschen lesbarer.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gpx="http://www.topografix.com/GPX/1/0">
        <xsl:template match="/">
        <html>
        <head>
                <title><xsl:value-of select="gpx:gpx/gpx:trk/gpx:name"/></title>
        </head>
        <body>
                <h1><xsl:value-of select="gpx:gpx/gpx:trk/gpx:name"/></h1>
                <p><xsl:value-of select="gpx:gpx/gpx:trk/gpx:desc"/></p>
                <table>
                <tr>
                        <th>Longitude</th>
                        <th>Latitude</th>
                        <th>Elevation</th>
                        <th>Time</th>
                </tr>
                <xsl:for-each select="gpx:gpx/gpx:trk/gpx:trkseg/gpx:trkpt">
                <tr>
                        <td><xsl:value-of select="@lon"/></td>
                        <td><xsl:value-of select="@lat"/></td>
                        <td><xsl:value-of select="gpx:ele"/></td>
                        <td><xsl:value-of select="gpx:time"/></td>
                </tr>
                </xsl:for-each>
                </table>
        </body>
        </html>
        </xsl:template>
</xsl:stylesheet>

Tags: , , , , ,

August 27, 2010 0

WordPress File Upload mit XML-RPC

By matthias in Playground, Project, useful

Ich habe euch ja über meine Faszination der XML-RPC Schnittstelle von WordPress berichtet, heute hab ich versucht etwas nützliches damit zu machen. Ich kann jetzt Bilder mit diesem kleinen Python Skript hochladen.

def wp_uploadFile(xmlrpc_url, username, password, filename,blog_id = 0):
    xmlrpc_client = xmlrpclib.ServerProxy(xmlrpc_url)
    filehandle = open(filename, "rb")
    data = filehandle.read()
    filehandle.close()
    mediaStruct = {
        ‘name’ : os.path.basename(filename),
        ‘bits’ : xmlrpclib.Binary(data)
    }
    return xmlrpc_client.wp.uploadFile(blog_id,username,password,mediaStruct)

Python ist für mich eine eher ungewöhnliche Wahl, aber ich wollte daraus eine Nautilus Extension machen, mein Schatz hätte vielleicht Freude wenn Sie in Zukunft nur mehr Markieren-RechtsKlick muss um einen Haufen Bilder auf Ihr Blog zu laden :)

Tags: , , ,

August 26, 2010 0

Elgg Development

By matthias in Project, useful

Kennt Ihr Elgg? Das ist eine ziemliche coole Open Source Social Networking Plattform. Wenn man “the usual” haben will ist man damit gut bedient. Profile, Freundschaften, Messages, Blogs, Event Kalender, Gruppen und Status Nachrichten ala Twitter sind damit kein Problem. Das ganze präsentiert sich schön heraus geputzt mit ein bisschen Ajax :)

Aber das wirklich erstaunliche verbirgt sicher hinter der Fassade. Ich hatte in den letzten Wochen die Ehre mich näher mit den Innereien von Elgg auseinander setzen zu dürfen (Freundin plant eine Community aufzuziehen), und muss sagen das ich noch selten soviel Freude an Web-Entwicklung hatte.Der Aufwand bei der Entwicklung einer neuen Funktion hält sich nämlich schön in Grenzen, für mich eine ganz neue Erfahrung ca. 20-30 Lines of Code zu investieren und das ganze als neues Feature verkaufen zu können.
Die Internationalisierung war gleich das erste an dem ich mich probieren durfte, die verhält sich genauso wie man es erwartet und wird konsequent eingesetzt. Für die ElggCore und jedes Plugin gibt es ein languages Vereichnis in dem für jede Sprache ein PHP File drinnen ist. Dort wird einfach ein riesiges Array definiert wo Strings auf übersetzte Strings abgebildet werden.
Ausgegeben wird das dann in der Form

elgg_echo("plugin:widget:name");

.
Als nächstes hab ich kleine Widgets gebaut, für die legt man im Plugin Ordner unter “mod/pluginname/views/default/widgets/” einfach einen neuen Ordner an in dem sich zwei Files befinden “edit.php” und “view.php”. In der “edit.php” kann man ein User-Interface für irgendwelche Einstellungen basteln, das übliche wäre zum Beispiel ein Drop-Down Menü wo man auswählen kann wieviele Elemente im Widget angezeigt werden sollen. In der “view.php” gibt man einfach die richtige Anzahl an Elementen aus, dank des View-Systems geht das schön leicht von der Hand.

$num = $vars[‘entity’]->num_display;
if (!$num) {
        $num = 4;
}
echo  list_user_friends_objects($vars[‘entity’]->owner_guid,‘thewire’,$num,0);

Wenn man den Code für das Widget fertig hat muss man dieses Widget noch registrieren damit Elgg davon weiß und einem bei der Auswahl der Widgets anzeigt.

add_widget_type(‘latestwireposts’,elgg_echo("community:widget:latestwireposts:name"),
elgg_echo("community:widget:latestwireposts:description"),‘dashboard’);

Das waren alle Zeilen die nötig waren um die neuesten Status Nachrichten deiner Freunde anzuzeigen, nützlich für die Programmierung is die Referenz. Manchmal muss man ein wenig stöbern bis man was brauchbares findet, aber bis jetzt hab ich noch alles gefunden.

Aber ich muss auch sagen das obwohl die meisten Änderungen wirklich spielend von der Hand gehen tut man sich schwerer wenn man neue Daten anlegen will, denn das DataModel ist eher unüblich. Ich hab es immer noch nicht ganz verstanden, aber es spart auf alle Fälle das herumpfuschen an der Datenbank :)

Also was ich euch damit sagen will: Wenn ihr eine brauchbare Social Networking Plattform sucht dann ist Elgg vielleicht das was Ihr sucht. Vielleicht werd ich dazu mal einen kleinen Vortrag halten, das Linuxwochenende 2010 würde sich dafür irgendwie anbieten.

Tags: , , , ,

August 25, 2010 0

WordPress automatic upgrade using SSH2

By matthias in useful

Bis vor kurzem ging mir das updaten irgendeines Teiles von WordPress gehörig gegen den Strich, denn manuell wird das bei einer gewissen Update-Häufigkeit zur Qual. Dann kam das Auto-Upgrade Feature dessen Nutzen mir nicht gleich einleuchtete weil da nur FTP angeboten wurde. Als ob ich sowas archaisches auf meinem Server dulden würde :)

Aber eine kurze Recherche hat ergeben das dieses Update auch über SSH2 funktioniert, man muss einfach nur die PHP extension dafür installieren!

#install necessary stuff
aptitude install php-pear php5-dev libssl-dev
#download and compile libssh2-1.1
wget http://tinyurl.com/389hhnp
tar xvzf libssh2-1.1.tar.gz && cd libssh2-1.1
./configure && make all install
#make a php extension for ssh2 and add it to the php.ini file
pecl install -f ssh2
echo "extension=ssh2.so" > /etc/php5/conf.d/ssh2.ini
#restart apache
/etc/init.d/apache2 restart

(Zu diesem Codeblock muss ich anmerken: Das ist ein komprimierter Auszug aus meinem History-File, ich habe nicht überprüft ob diese Zeilen so wie sie hier stehen funktionieren. )

Danach erscheint beim Auto-Upgrade auch eine Option für SSH2. Ich hatte noch ein Problem das ich mich nicht einloggen konnte, das lag aber daran das libssh2 offensichtlich aes-ctr nicht unterstützt. Nachdem ich blowfisch-cbc in meiner /etc/ssh/sshd_config aktiviert hatte gings auch gleich.

Wenn man nicht ständig das Passwort eingeben will beim Upgrade helfen folgende Zeilen in der wp-config.php:

define(‘FTP_USER’,‘username’);
define(‘FTP_PASS’,‘password’);
define(‘FTP_HOST’,‘localhost:22′);

Tags: , ,