DSIW

Alles was interessant ist... (Linux, Programmierung, Datenschutz, Medien, uvm.)

Umzug von Wordpress nach Octopress

| Comments

Nachfolgend möchte hier auf das Migrieren meines alten Wordpress-Blogs eingehen und warum ich diesen Schritt getan habe. Die einfache Installation von Octopress habe ich schon in einem anderen Artikel beschrieben.

Beweggründe

In diesem Abschnitt möchte ich meine Gründe darlegen, warum ich nicht mehr Wordpress nutze und zu Octopress umgestiegen bin.

Zuerst wurde ich durch den Podcast Binärgewitter auf Octopress aufmerksam. Ich finde das Konzept einer Webseite, die kompiliert wird, interessant. Bei Wordpress störte mich das sehr groß und mächtig gewordene CMS. Dadurch war das Frontend sehr träge, sodass es immer ein paar Denksekunden brauchte, um die Wirkung einer Aktion zu sehen. Das Arbeiten, z.B. das Erstellen und Ändern von Posts, ging mir langsam auf den Zeiger.

Außerdem nutze ich den Editor vim für meine Artikel, sodass ich meine Artikel sowieso schon lokal gespeichert habe. Dazu kommt, dass ich vor hatte, meine Artikel nicht mehr in HTML zu schreiben, sondern auf Markdown umzusteigen. Dann bot es sich ja gerade an, gleich ein Framework zu nutzen, das die Artikel lokal verwaltet und Markdown nutzt!

Ich nutze nicht gerne Datenbanken, um einfach nur ein bisschen Text, Bilder und Kommentare zu speichern, da es nicht einfach ist, Änderungen vorzunehmen. Sei es einfach nur das Ändern von Links, wenn sich zum Beispiel mein Bilder-Ordner geändert hat oder ich eine andere Subdomain dafür nutzen würde. Nun kann ich es sehr einfach mittels sed tun.

Wie man an meinem Blog erkennen kann, lege ich viel Wert auf Sicherheit. Wordpress entwickelte sich in der Vergangenheit zu einem sehr mächtigen Werkzeug, das auch Cracker anzieht. Ich musste Plugins nutzen, die zur Sicherheit des CMS beitrugen. Zum Beispiel setzte ich Login LockDown ein, das nur eine begrenzte Anzahl von Logins erlaubt. Diese Plugins muss ich nunu nicht mehr einsetzen, sondern kann man voll und ganz auf den Content konzentrieren.

Apropos Plugins: Ich hatte manchmal größere Probleme mit diesen. Das ein oder andere konnte nicht mit anderen Plugins zusammenarbeiten und zeitaufwändiges Suchen nach dem Verursacher war die Folge. Das UNIX-Konzept, das auch bei Jekyll genutzt wird und „ein Plug-in für eine Aufgabe -- aber dafür gut“ verspricht, finde ich gut. Bei Wordpress gab es Plugins, die sich zu einer eierlegenden Wollmilchsau entwickelten.

Probleme

Bei der ganzen Migration meines alten Blogs unter Wordpress mussten verschiedene Probleme gelöst werden.

Syntax-Highlighting

Durch das Plug-in pygments ist das Highlighten von Code einfach möglich. Wer mag, kann sich eine Test-Seite mit den Ergebnisse anschauen. Als „Highlight Scheme“ wird das schöne Solarized genutzt und es werden viele Sprachen unterstützt.
In meinem Fall funktionierte das Highlighten leider nicht. Es kam zu Generierungsfehlern. Da ich Arch Linux nutze und dort schon Python in der Version 3 genutzt wird, aber pygments die Version 2.x benötigt, gibt es einen Workaround:

Dazu wird die folgende Datei in den Plugin-Ordner von Octopress erstellt:

ruby ruby_python_arch_linux_fix.rb http://blog.gonzih.org/blog/2011/09/21/fix-octopress-pygments-error-on-arch-linux/ Quell-Artikel RubyPython.configure :python_exe => 'python2.7'

Migrieren der alten WP-Blogposts

Ich habe mir ein Skript zur Hilfe genommen, das aus dem Wordpress-XML-Export die Artikel parst. Es erstellt dabei automatisch die passenden Dateien und wandelt das HTML in Markdown um. Ich musste das Skript an meine eigenen Bedürfnisse anpassen.

Bevor ihr es einsetzt. Testet das Ergebnis ausgiebig. Nicht, dass ihr einen Fehler findet und die ganze Arbeit nochmals erledigt werden muss.

Getestet habe ich zum Beispiel:

  • Überschriften
  • Links
  • Code-Fragmente <code> oder <pre>
  • Zitate
  • Bilder
  • Eingebundene Videos
  • u. a.

Das Skript zum Umwandeln habe ich vom Repository von melwin (via). Auch kamen Code-Schnippsel von diesem Artikel zu meinem Skript hinzu.

Skript zum Umwandeln (wordpress-octopress-migration.rb) download
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# encoding: utf-8
require 'fileutils'
require 'date'
require 'yaml'
require 'rexml/document'
require './downmark_it'
include REXML

CURRENT_IMG_HOST="blog.dsiw-it.de"

doc = Document.new File.new(ARGV[0])

FileUtils.mkdir_p "_posts"

doc.elements.each("rss/channel/item[wp:status = 'publish' and wp:post_type = 'post']") do |e|
    post = e.elements
    slug = post['wp:post_name'].text
    date = DateTime.parse(post['wp:post_date'].text)
    name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day, slug]
    date_string = date.year.to_s + "-" + date.month.to_s + "-" + date.day.to_s + " " + date.hour.to_s + ":" + date.minute.to_s

    categories = Array.new
    XPath.each(e, "category").select{ |c| categories << c.text.downcase if c.attributes['domain'] == 'category'}
    tags = Array.new
    XPath.each(e, "category").each{ |c| tags << c.text.downcase if c.attributes['domain'] == 'post_tag'}

    content_text = post['content:encoded'].text
    content = content_text.encode("UTF-8")

    # escape Markdown chars (see http://daringfireball.net/projects/markdown/syntax#backslash )
    #content = content.gsub(/\\/, '\\')
    content = content.gsub(/\*/, '\*')
    #content = content.gsub(/#/, '\#')
    #content = content.gsub(/`/, '\`')
    #content = content.gsub(/\*/, '\*')
    #content = content.gsub(/_/, '\_')
    #content = content.gsub(/\+/, '\+')
    #content = content.gsub(/-/, '\-')
    #content = content.gsub(/\./, '\.')
    #content = content.gsub(/!/, '\!')
    #content = content.gsub(/\(/, '\(')
    #content = content.gsub(/\)/, '\)')
    #content = content.gsub(/\{/, '\{')
    #content = content.gsub(/\}/, '\}')
    #content = content.gsub(/\[/, '\[')
    #content = content.gsub(/\]/, '\]')

    content = content.gsub(/<a href="[^"]*">[ ]*<img src="(http[s]?:\/\/#{CURRENT_IMG_HOST}[^"]*)" alt="[^"]*" title="([^"]*)" width="([^"]*)" height="([^"]*)".*\/>[ ]*<\/a>/m, "\n{% img \\1 \\3 \\4 \\2 %}\n")

    content = content.gsub(/<img src="(http[s]?:\/\/#{CURRENT_IMG_HOST}[^"]*)" alt="[^"]*" title="([^"]*)" width="([^"]*)" height="([^"]*)".*\/>/m, "\n{% img \\1 \\3 \\4 \\2 %}\n")

    # convert <code></code> blocks to {% codeblock %}{% encodeblock %}
    content = content.gsub(/<code>(.*?)<\/code>/, '`\1`')
    #content = content.gsub(/<code>/, '{% codeblock %}')
    #content = content.gsub(/<\/code>/, '{% endcodeblock %}')

    # correct quoted code
    content = content.gsub(/\'`(.*?)`\'/m, '`\1`')
    content = content.gsub(/"`(.*?)`"/m, '`\1`')

    # convert <pre></pre> blocks to {% codeblock %}{% encodeblock %}
    #content = content.gsub(/<pre lang="([^"]*)">(.*?)<\/pre>/m, '`\1`')
    content = content.gsub(/<pre>/, '{% codeblock %}')
    content = content.gsub(/<pre lang="([^"]*)">/, '{% codeblock %}')
    content = content.gsub(/<\/pre>/m, '{% endcodeblock %}')

    # get content of [caption]-tag of wordpress
    #content = content.gsub(/\[caption .*\](.*?)\[\/caption\]/, "\\1")
    content = content.gsub(/\[caption.*\]/, "")
    content = content.gsub(/\[\/caption\]/, "")

    # remove gallery from Wordpress
    content = content.gsub(/\[gallery.*\]/, "")
    content = content.gsub(/\[slideshow.*\]/, "")

    # get videos
    content = content.gsub(/\[youtube=(.*?),.*\]/, "\n[Video](https://www.youtube.com/watch?v=\\1) {{ oembed https://www.youtube.com/watch?v=\\1 }}\n")
    content = content.gsub(/\[vimeo.*\]/, "")

    # correct headers
    content = content.gsub(/<h1>([^<]*)<\/h1>/m, '## \1')
    (2..3).each do |i|
        content = content.gsub(/<h#{i}>([^<]*)<\/h#{i}>/, ('#'*i) + ' \1')
    end

    content = content.gsub(/(<[u|o]l>)/m, "§%§%§%§%§%§%§%§%§\n\\1")

    # correct special characters to UTF-8
    content = content.gsub(/&gt;/m, '>')
    content = content.gsub(/&lt;/m, '<')
    content = content.gsub(/&amp;/m, '&')
    content = content.gsub(/&ndash;/m, '–')
    #content = content.gsub(/&bdquo;/m, '„')
    #content = content.gsub(/&ldquo;/m, '“')
    content = content.gsub(/&laquo;/m, '«')
    content = content.gsub(/&nbsp;/m, ' ')
    content = content.gsub(/&#039;/m, "'")
    #content = content.gsub(/&quot;/m, '"')

    # correct quotes for german articles
    #content = content.gsub(/(\s+)"/m, '\1„')
    #content = content.gsub(/(„.+?)"/m, '\1“')
    #content = content.gsub(/<p>\s*"(.+?)"\s*<\/p>/m, '„\1“')

    # convert to Markdown
    content = DownmarkIt.to_markdown(content)

    # correct convertion to Markdown
    content = content.gsub(/^\s*- (.*?)/m, " - \\1")
    content = content.gsub(/^\s*(\d+). (.*?)/m, " \\1. \\2")
    content = content.gsub(/`\[(.*?)\]\((.*?)\)`/m, "[`\\1`](\\2)")

    content = content.gsub(/§%§%§%§%§%§%§%§%§/m, '')

    puts "Converting: #{name}"

    data = {
        'layout' => 'post',
        'title' => post['title'].text,
        'date' => date_string,
        'comments' => true,
        'categories' => categories,
        'tags' => tags,
        'link' => true
     }.delete_if { |k,v| v.nil? || v == ''}.to_yaml

     File.open("_posts/#{name}", "w") do |f|
         f.puts data
         f.puts "---"
         f.puts content
     end
end

Da es kein Gem mit dem Namen downmark_it gibt, habe ich es manuell heruntergeladen und lokal in meinem Skript (Zeile 6) verlinkt.

Download: downmark_it.rb

Leider funktionierte das korrekte Setzen der Umlaute nicht richtig, obwohl das Wordpress-XML in UTF-8 kodiert war und ich im Skript es auch in UTF-8 umwandele (Zeile 28). Deshalb habe ich mir ein kleines Skript geschrieben, das alle meine Umlaute und andere Sonderzeichen in UTF-8-Zeichen umwandelt:

Skript zum Umwandeln der Umlaute (convert-umlaute.sh) download
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
cd $1
for f in *.markdown; do
  sed -i 's/\\xC3\\xA4/ä/g' "$f"
  sed -i 's/\\xC3\\xB6/ö/g' "$f"
  sed -i 's/\\xC3\\xBC/ü/g' "$f"
  sed -i 's/\\xC3\\x9F/ß/g' "$f"
  sed -i 's/\\xC3\\x9C/Ü/g' "$f"
  sed -i 's/\\xC3\\x84/Ä/g' "$f"
  sed -i 's/\\xC3\\x96/Ö/g' "$f"
done
cd -

Ich muss gerade feststellen, dass diese Umwandlung eigentlich auch im Ruby-Skript gemacht werden könnte. Gerne nehme ich Änderungen entgegen. Bei mir hatte es so funktioniert, und deswegen nutzte ich diese Möglichkeit.

Das Gemfile ist eigentlich nicht sehr interessant. Es ist nur wichtig, dass hier die Abhängigkeiten der Skripte mit drin stehen. Zum Beispiel musste ich hpricot hinzufügen, damit downmark_it funktioniert. Zur Sicherheit mal mein Gemfile:

Benötigte Gems (Gemfile) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
source "http://rubygems.org"

group :development do
  gem 'rake'
  gem 'rack'
  gem 'jekyll'
  gem 'rdiscount'
  gem 'pygments.rb'
  gem 'RedCloth'
  gem 'haml', '>= 3.1'
  gem 'compass', '>= 0.11'
  gem 'rubypants'
  gem 'rb-fsevent'
  gem 'stringex'
  gem 'liquid', '2.2.2'
end

gem 'sinatra', '1.2.6'
gem 'ruby-oembed'
gem 'indextank'
gem 'nokogiri'
gem 'hpricot'
#gem 'jekyll-tagging'

Da diese Skripte alle zusammen genutzt und sequentiell abgearbeitet werden, habe ich ein kleines Skript geschrieben, das dies erledigt. Dabei ist es wichtig, dass sich dies und die anderen Skripte im octopress-Verzeichnis befinden, sonst passen die Pfade nicht mehr!

Skript, das alles vereint (migrate.sh) download
1
2
3
4
5
6
7
#!/bin/sh
ruby wordpress-octopress-migration.rb "$1" &&
#ruby wp-op-convert.rb "$1" && 
rm source/_posts/*
mv _posts/* source/_posts &&
rmdir _posts
./convert-umlaute.sh source/_posts

Weitere Variante

Es gibt zu dieser Variante auch noch weitere. Zum Beispiel Skripte, die direkt auf der Datenbank arbeiten. Weitere Informationen zum Migrieren ist im Wiki von Jekyll beschrieben. Außerdem hat noch Sven von Ganz-Sicher!Net einen Artikel über seine Erfahrungen des Umstiegs von Wordpress nach Jekyll geschrieben. Jemand anderes hat auch einen sehr langen Artikel von seinem Umstieg nach Jekyll geschrieben und hat gleich ein bisschen gecodet.

Ausblick

In Zukunft werde ich mir noch ein paar Plugins anschauen und versuchen meine ToDo-Liste abzuarbeiten.

Fazit

Im Großen und Ganzen bin ich mit Octopress sehr zufrieden. Zumal es nun viel einfacher ist Blogposts zu erstellen, da ich nur eine neue Datei anlegen muss. Das langsame und träge Dashboard von Wordpress ist nun Vergangenheit. Außerdem ist die Seite um einiges sicherer, da sie nur statischen Content enthält. Durch diese statischen Seiten kann ein kostengünstiger, minimaler Webspace genutzt werden, da keine Skriptsprachen benötigt werden.

Wie findet ihr mein neues Design bzw. würdet ihr (durch diesen Artikel) näher auf Octopress eingehen?

Comments