Sonntag, 4. März 2012

Geburtstage von Apple AddressBook nach iCal übertragen

Damit man die gesammelten Geburtstage seiner Mitmenschen immer im Kopf behält, empfehle ich jedem Mac-Anwender die nützliche Apple Automator-Action iCalBirthdays.

Mit dieser Action ist es möglich, für alle im Apple Adressbuch enthaltenen Kontakte mit Geburtsdatum einen Alarm in iCal zu erstellen. Führt man die Automator-Action quartalsweise (oder häufiger) durch — je nachdem, wie oft neue Geburtsdaten ins Adressbuch aufgenommen werden — hat man so immer einen aktuellen Kalender und somit auch eine Erinnerungsmeldung, damit man Bekannten pünktlich gratulieren kann.

Nachdem die Action heruntergeladen und installiert ist, öffnet man Apple Automator und erstellt einen Workflow mit der iCalBirthday-Komponente in der Calendar-Library. Den Workflow speichert man anschliessend am Besten als eigenständige Applikation, die man unter /Applications ablegt.

Meine Einstellungen für den Workflow schauen folgendermassen aus:

Screen Shot 2012-03-04 at 21.55.18

Tags: , , ,
Labels: Apple

3 Kommentare | neuen Kommentar verfassen

Sonntag, 4. März 2012

Aktuell geöffnetes Fenster unter Mac OS X minütlich aufzeichnen

Seit mehreren Jahren zeichne ich jede Minute auf, welches Fenster auf meinem Mac OS X-Desktop aktiv ist. Ich nutze diese Aufzeichnungen nicht zuletzt dazu, um bei ungefähr zu wissen, wie lange ich für Kundenaufträge gearbeitet habe — und dementsprechend Rechnung zu stellen.

Das AppleScript lautet folgendermassen:

tell application "System Events"
	set app_name to name of item 1 of (every process whose frontmost is true)
end tell

tell application app_name
	try
		if (count of windows) > 0 then
			set mouseToolsPath to "usr:local:bin:MouseTools"
			set {mouseX, mouseY} to paragraphs of (do shell script quoted form of POSIX path of mouseToolsPath & " -location")
			
			set title to (app_name & "&title=" & name of window 1 & "&x=" & mouseX & "&y=" & mouseY)
		end if
	on error
		set title to app_name
	end try
end tell

Damit das Script auch gleichzeitig die Position des Mauszeigers erfasst, müssen noch die MouseTools heruntergeladen und — in meinem Fall — unter /usr/local/bin/ abgelegt werden.

Um dieses Script regelmässig auszuführen, habe ich mit der kostenpflichtigen Applikation Lingon einen launchd-Job erstellt (in der Steinzeit hat man noch auf Cron-Jobs zurückgegriffen), der jede Minute ausgeführt wird. Hierzu verwende ich ein bash-Script als Wrapper, um die Informationen an ein PHP-Script auf einem im Intranet stehenden Linux-Server weiterzuleiten:

#!/bin/sh

LOGGEDIN=`who | grep "^mario" | grep console | wc -l`
WGET="/opt/local/bin/wget"
#WGET=`which wget`

if [ $LOGGEDIN -lt 1 ];
then
        echo "User mario not logged in. Exiting."
        exit 0
fi

if [ ! -x "$WGET" ];
then
	echo "wget executable '$WGET' not found. Exiting."
	exit 1
fi

APP=`/usr/bin/osascript /usr/local/bin/FrontMostApplication.scpt`
#echo $APP

URL="http://tld/save.php?app=$APP"

#echo "Accessing URL '$URL' using $WGET"

$WGET -q -O /dev/null "$URL"

exit 0

Tags: , , , ,
Labels: Apple, Linux

Keine Kommentare | neuen Kommentar verfassen

Samstag, 3. März 2012

Bye bye, Virgin Megastore Paris — hello Apple Store!

Das waren noch Zeiten, als ich um die Jahrtausendwende zusammen mit Gymerkollegen mehrere Male in Paris weilte. Ein Besuch im Virgin Megastore an der Champs Elysées gehörte bei diesen Trips jedesmal zu einem Muss.

Wir Schweizer ergötzten uns am Verkaufstempel von CDs und Vinyls in unsereins ungewohnten Dimensionen, und auch, dass man mit Barcode-Lesern die EANs der CDs einlesen und dann über Kopfhörer probehören konnte.

Mehrere Male kehrte ich mit einigen raren, internationalen Tonträgern von damals zeitgenössischen Künstlern nach Hause, unter anderem Brian Transeau oder kurz: BT.

Wie sich die Welt mehr als 10 Jahre später geändert hat: Damals hatte ich mir gerade mein erstes Mobiltelefon geleistet, ein Nokia 8210, mit welchem man für teures Geld telefonieren und — ganz neu — auch zwischen den verschiedenen Mobilfunknetzen SMS austauschen konnte. Auf den Reisen dabei hatte ich eine Sony DSC-P1, die zwar bereits 3 Megapixel aufwies, auf den 64MB fassenden MemoryStick aber kaum genügend Bilder aufnehmen konnte. Als frischverdienender Vollzeitangestellter steckte ich zudem viel Geld in den Aufbau einer CD-Sammlung elektronischer Musik.

Heute besitze ich zwei iPhones, ein iPad, ein MacBook Air … und kann mich nicht erinnern, wann ich das letzte Mal einen physischen Tonträger erstanden habe. Die Zeit der CD-Läden ist vorbei, die digitale Distribution von Musik (und bald auch Filmen) hat die altgedienten Distributionskanäle — und damit auch Virgin — gekillt.

Deshalb ist es die grösste Ironie an der ganzen Geschichte, dass Gerüchten zu Folge nach dem Auszug von Virgin aus dem Lokal an der Champs Elysée das Gebäude an den Musikdistributionsplatzhirschen Apple abtritt:

Business Insider points to a report [Google translation] from French newspaper Le Figaro noting that Virgin will be vacating its landmark retail store on Champs-Élysées in Paris and that Apple is one of the primary candidates rumored to be interested in taking the spot.

Quelle: Rumors of Massive Apple Retail Store for Paris as Virgin Vacates Prime Location

Tags: , , ,
Labels: Apple, Wirtschaft

Keine Kommentare | neuen Kommentar verfassen

Samstag, 25. Februar 2012

youtube-dl meldet „no fmt_url_map or conn information found in video info“

Wer Youtube-Videos auf seinen Rechner herunterladen möchte, um sie später ohne Internetverbindung anschauen zu können, wird das Python-Script youtube-dl längst kennen.

Wenn das Ding aber den Fehler

$ ~/youtube-dl.sh http://www.youtube.com/watch?v=QhhFQ-3w5tE
[youtube] Setting language
[youtube] QhhFQ-3w5tE: Downloading video webpage
[youtube] QhhFQ-3w5tE: Downloading video info webpage
[youtube] QhhFQ-3w5tE: Extracting video information
ERROR: no fmt_url_map or conn information found in video info

meldet, sollte man sich den Fork von Philipp Hagemeister herunterladen, welcher den Bug behebt:

youtube-dl (Philipp Hagemeisters Fork)

Youtube-Video als MP3 herunterladen

Wenn wir gerade dabei sind: Wer obiges Tool einsetzt, sollte auch zwingend nachfolgende Web-Site kennen, welche Youtube-Videos in MP3-Dateien umwandelt:

www.youtube-mp3.org

Tags: , , , , ,
Labels: Linux

2 Kommentare | neuen Kommentar verfassen

Sonntag, 19. Februar 2012

MacPorts unter Lion

Xcode 4.3 kann nur noch über den Mac App Store bezogen werden. Applikationen, die über diesen Kanal vertrieben werden, müssen verschiedene Einschränkungen in Kauf nehmen — unter anderem können sie nicht mehr Dateien irgendwo auf der Macintosh HD ablegen.

Die ausführbaren Dateien liegen unter /Applications/Xcode.app/Contents/Developer, und dies muss man auf der Kommandozeile auch dementsprechend kundtun:

sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer

Quelle: Untitled

Damit ist es aber noch nicht getan. In Xcode muss man nun noch die Command Line Tools installieren, die den gcc-Compiler zur Verfügung stellen:

  1. Xcode installieren
  2. Xcode starten
  3. Xcode
  4. Preferences
  5. Downloads
  6. Install Command Line Tools

Unklare Wirkung

Bei der Fehlersuche habe ich auch noch folgende Anpassung an /opt/local/etc/macports/macports.conf vorgenommen, weiss aber nicht, ob sie nötig ist oder nicht:

...
# Directory containing Xcode Tools (default is to ask xcode-select)
#developer_dir       /Developer
developer_dir        /
...

Tags: , , , , , ,
Labels: Apple

Keine Kommentare | neuen Kommentar verfassen

Freitag, 17. Februar 2012

Iteration über alle in eine Word-Datei eingebette OLE-Objekte

In einem Word-Dokument befinden sich ein Dutzend OLE-Objekte, sprich in die Word-Datei eingebettete Dateien wie PDFs, TXTs und XLSs.

Folgendes Visual Basic-Script iteriert über die Shapes und InlineShapes in einem Dokument und gibt den Dateinamen aus:

Sub emeidiListEmbeddedFiles()
    Debug.Print "Shapes in ActiveDocument.Shapes.Count: " & ActiveDocument.Shapes.Count
    Debug.Print ""
    
    For Each Shape In ActiveDocument.Shapes
        With Shape
            With .OLEFormat
                'Debug.Print "    .IconLabel: " & .IconLabel
                Debug.Print .IconLabel
            End With
        End With
    Next
    
    Debug.Print "Shapes in ActiveDocument.InlineShapes.Count: " & ActiveDocument.InlineShapes.Count
    Debug.Print ""
    
    For Each Shape In ActiveDocument.InlineShapes
        With Shape
            With .OLEFormat
                'Debug.Print "    .IconLabel: " & .IconLabel
                Debug.Print .IconLabel
            End With
        End With
    Next
    
    
End Sub

Das Script kann problemlos angepasst und erweitert werden, um die Eigenschaften der entsprechenden Objekte zu verändern.

Tags: , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 12. Februar 2012

Den bash Prompt den eigenen Bedürfnissen anpassen

Wegen der im vorgängigen Post erwähnten Anpassung an der /etc/hosts schaute mein Bash Shell Prompt plötzlich folgendermassen aus:

mario@192:/ $

Ich habe mich deshalb daran gemacht, das Prompt meinen eigenen Bedürfnissen anzupassen. Folgendermassen schaut es nun aus:

[mario@192.168.0.102:/] $ 

Die Ausgabe beim root-User sieht identisch aus, ausser dass das $ durch ein # ersetzt wurde und der Prompt in roter Farbe hervorgehoben ist.

Für die Einstellungen des root-Users habe ich /etc/bash.bashrc angepasst:

...#PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
PS1="\\[$(tput setaf 1)\\]\\u@\\H:\\w # \\[$(tput sgr0)\\]"

Für die Einstellungen aller anderen User muss man sich in die /etc/profiles bemühen:

# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).

if [ "`id -u`" -eq 0 ]; then
  PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"
else
  PATH="/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games"
fi

if [ "$PS1" ]; then
  if [ "$BASH" ]; then
    #PS1='\u@\h:\w\$ '
    PS1="[\\u@\\H:\\w] $ "
  else
    if [ "`id -u`" -eq 0 ]; then
      #PS1='# '
      PS1="[\\u@\\H:\\w] # "
    else
      #PS1='$ '
      PS1="[\\u@\\H:\\w] $ "
    fi
  fi
fi

export PATH

umask 022

Damit dies bei den einzelnen Benutzern auch wirklich so ausschaut, müssen diese in ~/.bashrc und ~/.bash_profile jegliche Definition der PS1-Variable verhindern.

Fazit: Ein riesiges Chaos mit unzähligen Overrides und verschachtelten Abhängigkeiten, welches eines Linux-Betriebssystems im Grunde unwürdig ist.

Tags: ,
Labels: Linux

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 12. Februar 2012

Apache kann den FQDN des Servers nicht festlegen

Seit Jahren ärgere ich mich damit herum, dass meine Cron-Logs jedes Wochenende mit folgendem Fehler aufwarten:

apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.0.101 for ServerName

Diese Meldung taucht auch auf, wenn ich den Server mit apache2ctl graceful respektive apache2ctl restart neu starte.

Heute nun habe ich mich endlich des Problems angenommen und teile die Lösung gerne mit der ganzen Online-Welt.

Der vom lokalen BIND (DNS-Server) geliefert Hostname meines Web-Servers lautete wie folgt:

$ host 192.168.0.101
101.0.168.192.in-addr.arpa domain name pointer alpha.emeidi.local.

In meiner /etc/hosts stand aber nur folgendes:

127.0.0.1	localhost

192.168.0.101	ALPHA
...

Nach einigem Googlen wechselte ich die Zeile beginnend mit 192.168.0.101 folgendermassen aus:

192.168.0.101	ALPHA ALPHA.emeidi.local mad4you.homeip.net

Und siehe da, ab sofort kann Apache den Fully Qualified Domain Name des Servers selber erraten.

Tags: , ,
Labels: Linux

Keine Kommentare | neuen Kommentar verfassen

Samstag, 11. Februar 2012

curlftps mit Zeichensatz-Problemen

Ich ziehe mir wöchentlich ein Backup eines produktiven Web-Servers auf meinen Testserver. Da Endbenutzer Dateien auf die Web-Site laden und Ordner erstellen können, gibt es dort mittlerweile auch Dateinamen mit Sonderzeichen wie äöü.

Die Kombination aus curlftps (zur Bereistellung eines FTP-Mounts welcher wie eine lokale Festplatte angesprochen werden kann, zu Gunsten von rsync, mit welchem ich nur neu hinzugekommene Dateien übertrage) hat nun aber zu Problemen bei der Darstellung der Dateien mit Sonderzeichen geführt. Anstelle des ä bei „Bräteln“ sah ich auf der Kommandozeile mit ls nur „Br?teln“.

Seit ich curlftps mit folgenden Optionen aufrufe, klappt es auch mit der korrekten Übertragung der Dateinamen mit Sonderzeichen:

/usr/bin/curlftpfs -o codepage=latin1 -o iocharset=utf8 -r -s 'server.tld' '/mnt/ftpBackup/server.tld'

Damit wird tarsnap hoffentlich auch nicht mehr mit folgenden Fehlermeldungen auffallen:

...
tarsnap: var/www/sites/server.tld/Bilder/T�ftelwettbewerb/: Can't translate pathname 'var/www/sites/server.tld/Bilder/T�ftelwettbewerb/' to UTF-8
...

Übrigens: Meine locale-Einstellungen schauen folgender massen aus:

LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Tags: ,
Labels: Linux

Keine Kommentare | neuen Kommentar verfassen

Samstag, 11. Februar 2012

Vom Nutzen einer PHP Debug-Klasse

Seit mehreren Jahren verwende ich eine Sammlung von selber entwickelten emeidi*-PHP-Klassen, um Web-Sites und -Applikationen zu entwickeln. Leider hege ich immer noch grosse Skrupel, diese Klassen via Github als Open Source Software freizugeben — einerseits weil sich im Quellcode doch der eine oder andere unschöne Bock findet, andererseits, weil die Klassen Sicherheitslücken enthalten könnten, die bei produktiven Web-Sites ausgenützt werden könnten. Hinzu kommt mein Amazon-API-Key, den ich kurzerhand in die emeidiAmazon-Klasse festkodiert habe …

Unverzichtbares Kernstück ist dabei meine emeidiDebug-Klasse, welche für die professionelle, effiziente Web-Entwicklung unabdingbar ist. Jede andere meiner Klasse instanziert beim Laden die emeidiDebug-Klasse, mit welcher ich im ganzen Programmcode nützliche Fehlermeldungen und/oder Informationen zum Programmablauf festhalte.

Auf produktiven Web-Sites gebe ich den Inhalt des Debug-Objektes natürlich nicht aus, aber auf dem Entwicklungsserver ist es eine grosse Unterstützung, wenn ich am Ende der ausgegebenen Web-Seite noch folgenden Code hinpflanze:

...
<?php print $klasse->debug->getHtml(); ?>
</body>
</html>

Im ganzen PHP-Code finden sich zur Befüllung dieses Objekts Konstrukte wie

$this->debug->add('Not dumping descriptions because length new ' . $lenNew . ' is smaller than lenght old ' . $lenOld,'error');

Teilweise gebe ich auf produktiven Web-Sites wichtige Warnmeldungen auch in das PHP Error-Log aus. Während ich das früher direkt mit error_log(); gemacht habe, benutze ich heute ebenfalls meine Debug-Klasse dafür.

Der Grund dafür ist simpel: Nicht nur möchte ich das gesamte Debugging über meine Debug-Klasse laufen lassen, nein, meine Debug-Klasse ist auch geschwätziger und hilft mir im Falle meines Testservers, das fehlerhafte Script zu lokalisieren.

Vor einigen Tagen fand ich auf meinem Testserver folgende Einträge in der php.err-Datei:

[06-Feb-2012 00:07:43 UTC] Not dumping descriptions because length new 1778 is smaller than lenght old 1780

Mittlerweile habe ich das ausgebende Script (es wird täglich per Cron-Job aufgerufen) lokalisiert und die Programmierung angepasst. Alt hiess es:

error_log('Not dumping descriptions because length new ' . $lenNew . ' is smaller than lenght old ' . $lenOld);

Neu heisst es:

$this->debug->add('Not dumping descriptions because length new ' . $lenNew . ' is smaller than lenght old ' . $lenOld,'error',true);

Der letzte der Funktion übergebene Parameter true weist meine Debug-Klasse an, einen Eintrag in die php.err-Datei zu machen. Dieser schaut folgendermassen aus:

[11-Feb-2012 16:57:16 UTC] Not dumping descriptions because length new 1778 is smaller than lenght old 1780 in /var/www/apps/weather2ics/weather2ics.class.php on line 95 for URI </bern>

Anhand dieser Informationen kann ich nicht nur die Datei und die verantwortliche Zeile auf einen Blick lokalisieren, welche den Fehler ausgibt, sondern sehe auch noch gerade die URI, mit welcher die Web-App aufgerufen wurde.

Tags: , ,
Labels: Web

1 Kommentar | neuen Kommentar verfassen