GoogleCL przestało działać, bo Google przestało obsługiwać wersję OAuth 1.0. Ponadto, wygląda na to, że dostosowanie tego użytecznego narzędzia do wersji OAuth 2.0 bynajmniej nie jest trywialne na co wskazują liczne (ale do tej pory bezskuteczne) prośby i wołania o aktualizację GoogleCL, które można znaleźć w Internecie.
Ponieważ poszukiwania
w miarę podobnego zamiennika zakończyły
się niepowodzeniem, nie pozostało nic innego zmajstrować coś samodzielnie.
Autoryzację OAuth 2.0 mam już opanową -- obsługuje ją Pythonowy skrypt oauth2picasa.py
.
(Skrypt jest (zapożyczonym) fragmentem z projektu
picasawebsync
).
Wystarczyło
dorobić następujący prosty skrypt Perlowy (por. także:
Publishing
a blog post):
#!/usr/bin/perl # *** Wyslanie posta na blogger.com *** use strict; use LWP::UserAgent; use XML::LibXML; use Getopt::Long; my $profileID="default"; my $blogID = '1928418645181504144'; # Identyfikator bloga my $blog_entry ; ## Na wypadek gdy ktoś ma kilka blogów moża podać na któr ## ma być wysłany post używając opcji -blog GetOptions( "blog=s" => \$blogID, "post=s" => \$blog_entry) ; if ( $blog_entry eq '' ) { print STDERR "*** USAGE: $0 -b blog -p message (-b is optional) ***\n" } ## sprawdź czy post jest well formed: my $parser = XML::LibXML->new(); eval {my $res_ = $parser->parse_string($blog_entry) }; if ($@) { die "*** Error parsing post message! \n"; } my $ACCESS_TOKEN=`oauth2blogger.py`; # pobierz ACCESS_TOKEN print STDERR "*** AccessToken: $ACCESS_TOKEN ***\n"; my $req = HTTP::Request->new( POST => "https://www.blogger.com/feeds/$blogID/posts/default"); $req->header( 'Content-Type' => 'application/atom+xml' ); $req->header( 'Authorization' => "Bearer $ACCESS_TOKEN" ); $req->header( 'GData-Version' => '2' ); $req->content($blog_entry); my $ua = LWP::UserAgent->new; my $res = $ua->request($req); # Jeżeli coś jest nie tak poniższe drukuje verbatim: # http://www.perlmonks.org/bare/?node_id=464442 # $ua->prepare_request($req); print($req->as_string); exit ; if ($res->is_success) { my $decoded_response = $res->decoded_content; print STDERR "*** OK *** $decoded_response\n"; } else { die $res->status_line; }
Wykorzystanie:
perl blogger_upload.pl -p 'treść-posta'
Treść posta musi być oczywiście w formacie xHTML i zawierać
się wewnątrz elementu content
, który z kolei
jest wewnątrz elementu entry
.
Element entry
zawiera także title
określający tytuł posta, oraz elementy category
zawierające
tagi. Przykładem może być coś takiego:
<entry xmlns='http://www.w3.org/2005/Atom'> <title type='text'>Marriage!</title> <content type='xhtml'> <div xmlns="http://www.w3.org/1999/xhtml"> <p>Mr. Darcy has proposed marriage to me!</p> <p>He is the last man on earth I would ever desire to marry.</p> <p>Whatever shall I do?</p> </div> </content> <category scheme="http://www.blogger.com/atom/ns#" term="marriage" /> <category scheme="http://www.blogger.com/atom/ns#" term="Mr. Darcy" /> </entry>
Opisany skrypt jest tutaj:
blogger_upload.pl
.
Kol. DM pisze sobie bloga używając WordPressa. Trochę go to kosztuje więc pojawiła się koncepcja żeby przeszedł na bezpłatnego Bloggera. Początkiem tej koncepcji jest oczywiście konwersja WP do formatu Bloggera.
Wpisy kol. DM zawierają tekst i dużo zdjęć oraz innych rysunków. Nie mam bladego pojęcia nt. WordPressa, ale dość szybko ustaliłem, że można wyeksportować treść posługując się stosowną funkcją dostępną z Kokpit→Narzędzia→Eksport (powstaje plik w formacie WordPress eXtended RSS -- WXR).
Plik WXR zapisujemy na dysku. Można go zamienić do formatu Bloggera korzystując z konwertera dostępnego tutaj. Jest wprawdzie napisane, że konwerter obsługuje pliki nie większe niż 1Mb, ale mój miał 4Mb i też poszło.
Plik kolegi DM zawiera tekst oraz prawidłowe linki do niektórych rysunków. Inne rysunki są wstawiane sprytnym czymś co nazywane jest shortcode (cf Shortcode API.) Nie wchodząc w szczegóły, zamiast rysunków w treści postu jest umieszczone np. coś takiego:
[nggallery id=506]
506 z kolei jest identyfikatorem zbioru rysunków, które fizycznie są przechowywane w katalogu:
wp-content/gallery
Każda galeria jest w oddzielnym katalogu, ale nazwami katalogów nie
są identyfikatory typu 506
ale coś innego. Logując się
do phpMyAdmin
byłem w stanie ściągnąć całą bazę (w formacie SQL),
w której siedzi WordPress.
W jednej z tabel bazy znalazłem
przypisanie id_galerii
→nazwa-katalogu-z-plikami
INSERT INTO `wp_ngg_gallery` (`gid`, `name`, `slug`, `path`, `title`, `galdesc`, `pageid`, `previewpic`, `author`) VALUES (17, 'gottardo_2', '', 'wp-content/gallery/gottardo_2', '', '', 0, 0, 1), (16, 'gottardo_1', '', 'wp-content/gallery/gottardo_1', '', '', 0, 0, 1), (15, 'nufenen', '', 'wp-content/gallery/nufenen', NULL, NULL, 0, 0, 1), ... itd ...
Czyli zawartość czegoś, co w treści wygląda jak:
[nggallery id=15]
Znajduje się w katalogu wp-content/gallery/nufenen
.
Teraz ściągnąłem cały katalog wp-content
na dysk lokalny
wykorzystując ncftp
ncftp -u USER -p PASS HOST get -R -T wp-content
Następnie zamieniłem nazwy plików w następujący sposób:
nr_galerii__nazwa_pliku
Pliki wysłałem na google za pomocą skryptu. Istotne jest to, że skrypt po załadowaniu, zwraca URL zdjęcia pod którym jest ono dostępne na koncie googla:
100__fra_07063.jpg http://lh5.ggpht.com/-26SgLqsS1vM/UhdwT-Q62CI/AAAAAAAAABQ/k_ipaT4SNsE/100__fra_07063.jpg 100__fra_07064.jpg http://lh4.ggpht.com/-1kWivWwiZW4/UhdwU4vZWPI/AAAAAAAAABY/XxuIGrIPj8Q/100__fra_07064.jpg itd...
Czyli zdjęcie 100__fra_07063.jpg (oryginalnie należące go galerii o identyfikatorze równym '100') jest dostępne pod adresem:
http://lh5.ggpht.com/-26SgLqsS1vM/UhdwT-Q62CI/AAAAAAAAABQ/k_ipaT4SNsE/100__fra_07063.jpg
Uwaga: Album ze zdjęciami na koncie googla może zawierać maksymalnie 1000 zdjęć. Jeżeli zdjęć jest więcej trzeba utworzyć więcej albumów.
Skryptem Perla (wyrażenia regularne/regułowe) zamieniam każde [nggallery id=506] na stosowny ekwiwalent. Przykładowo:
<div id='gid_g509'> <span>< a href="http://lh5.ggpht.com/-NZ_dEAq8qZI/Uhf_OPG9jiI/AAAAAAAADp8/BtBx9DwVgs4/509__1106_016.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"> <img border="0" src="http://lh5.ggpht.com/-NZ_dEAq8qZI/Uhf_OPG9jiI/AAAAAAAADp8/BtBx9DwVgs4/s128/509__1106_016.jpg" height='85' /></a> </span> ... itd ...
Pozostałe zdjęcia mają `prawdziwe URLe' (a nie jakieś
shortcody
), ale oczywiście URLe te są złe bo wskazują na
starego hosta.
Zdjęcia te (z `prawdziwymi URLami') są przechowywane
w katalogu
./wp-content/uploads
.
Ze zdjeciami postępuję, tak jak
w przypadku zdjęć z galerii: 1) wysyłam na konto google skryptem;
2) zmieniam oryginalne
URLe na URLe z konta google (skryptem Perla).
Przed importem do Bloggera warto sprawdzić czy plik, który ma być zaimportowany jest well-formed:
xmllint plik-do-zaimportowania.xml
Uwaga: w tym wpisie przykłady ilustrujące działanie Lightboksa NIE DZIAŁAJĄ, bo
na blogu pinkaccordions.homelinux.org
nie ma zainstalowanego Lightboksa.
(Działają tutaj).
Kliknięcie zdjęcia lub obrazu zamieszczonego na blogu powoduje wyświetlenie go w nakładce na stronie. Jest to tzw. widok Lightbox. (cf. Widok Lightbox w Bloggerze):
<div> <span> <a href="https://lh4.googleusercontent.com/-cKBvbosMsII/Ua4zYPs99kI/AAAAAAAAB90/NLTG1cMbI88/epl313_6040342.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" src="https://lh4.googleusercontent.com/-cKBvbosMsII/Ua4zYPs99kI/AAAAAAAAB90/NLTG1cMbI88/s128/epl313_6040342.jpg" /></a> </span> <span> <a href="http://lh3.ggpht.com/-3POvL9Uv478/UhXICIpp_3I/AAAAAAAAB-w/K1rRrCU2D9c/epl313_6040338.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" src="http://lh3.ggpht.com/-3POvL9Uv478/UhXICIpp_3I/AAAAAAAAB-w/K1rRrCU2D9c/s128/epl313_6040338.jpg" /></a> </span> <span> <a href="http://lh6.ggpht.com/-nmAnV2_TD8U/UhXIIoYKraI/AAAAAAAAB-4/mWAJWpsOUZU/epl313_6040339.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" src="http://lh6.ggpht.com/-nmAnV2_TD8U/UhXIIoYKraI/AAAAAAAAB-4/mWAJWpsOUZU/s128/epl313_6040339.jpg" /></a> </span> <span> <a href="http://lh5.ggpht.com/-wO5_E5_jK14/UhXG7Rd-bSI/AAAAAAAAB-c/x76Ee7I9tgU/epl313_6040332.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" src="http://lh5.ggpht.com/-wO5_E5_jK14/UhXG7Rd-bSI/AAAAAAAAB-c/x76Ee7I9tgU/s128/epl313_6040332.jpg" /></a> </span> </div>
Wynik jest następujący (kliknij w zdjęcie aby przejść do widoku Lightboksa):
W powyższym przykładzie zawartość atrybutów href
i src
jest prawie identyczna, bo różni się wyłącznie
fragmentem s128/
.
Jest to przykład zastosowania sprytnego URLa, który spowoduje automagiczne przeskalowanie
zdjęcia, tak aby dłuższy wymiar miał maksymalnie 128 pikseli. Jeżeli zamiast s128/
wstawimy
s128-c/
albo s128-p/
, to
spowoduje to automagiczne wycięcie kwadratu o wymiarach maksymalnych 128 pikseli (crop).
Przykład:
<div> <span> <a href="http://lh3.ggpht.com/.../epl313_6040338.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" src="http://lh3.ggpht.com/.../s128-c/epl313_6040338.jpg" /></a> </span> <span> <a href="http://lh3.ggpht.com/.../epl313_6040338.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: .1em;"><img border="0" src="http://lh3.ggpht.com/.../s128-p/epl313_6040338.jpg" /></a> </span>
Symbolem ...
oznaczono pominiętą część URLa (żeby nie wystawał na margines).
Wynik (kliknięcie w zdjęcie nie powoduje przejścia do widoku Lightboksa):
BTW: Liczba 128 nie jest magiczna, można przeskalować obrazek na inny wymiar.
Jeżeli zdjęcia są różnych wymiarów (portret/pejzaż),
to s128-c/
(albo s128-p/
) jest lepsze niż zwykłe s128
,
bo minaturki są jednakowej wielkości,
tyle że Lightbox takie zdjęcia ignoruje. Czemu -- nie wiem... Nie wiem też jak to zmienić
Jako obejście problemu można przeskalować miniaturkę do jednakowej wysokości. Przykład:
<div> <span> <a href="https://lh4.googleusercontent.com/.../epl313_6040342.jpg" imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85' src="https://lh4.googleusercontent.com/.../s128/epl313_6040342.jpg" /></a> </span> <span> <a href="http://lh3.ggpht.com/.../epl313_6040338.jpg" imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85' src="http://lh3.ggpht.com/.../s128/epl313_6040338.jpg" /></a> </span> <span> <a href="http://lh6.ggpht.com/.../epl313_6040339.jpg" imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85' src="http://lh6.ggpht.com/.../s128/epl313_6040339.jpg" /></a> </span> <span> <a href="http://lh5.ggpht.com/.../epl313_6040332.jpg" imageanchor="1" style="margin-bottom: 1em;"><img border="0" height='85' src="http://lh5.ggpht.com/.../s128/epl313_6040332.jpg" /></a> </span> </div>
Symbolem ...
oznaczono pominiętą część URLa (żeby nie wystawał na margines).
Wynik jest następujący:
Kliknięcie w zdjęcie powoduje przejście do widoku Lightboksa.
Być może za czas jakiś problem zniknie. Wyświetlanie dla tego wpisu 10 zamiast 8 obrazków w widoku Lightboksa będzie tego dowodem:-)
I have decided to give a try to Google Blogger service. I am an old dinosaur used to command line and tired with mouse and menus but as there is GoogleCL I am not scare. The problem is with my old posts---there is no way to post backdated blog entries with GoogleCL. A problem...
Fortunately there is export/import features on Blogger: one can backup blog content and/or
upload it back to Google. In particular to import posts (and comments) into a blog, one have to
click Import Blog
from the blog's Settings
. Next one have to
select appropriate file and fill out the word verification beneath.
The Blogger data format
is Atom.
So, to successfully
import my old Blosxom entries I have to convert them to Atom.
I have made a few test entries and export them to check how the data looks like. Pretty wired but most of the content is irrelevant as it is concerned with formatting (css styles and such stuff is included). Also as I had comments disabled at my previous blog the problem is further simplified.
I have consulted Atom schema and tried with the following:
<?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0">'; <id>tag:blogger.com,1999:blog-1928418645181504144.archive</id> <updated>2011-10-22T12:34:14.746-07:00</updated> <title type='text'>pinkaccordions.blogspot.com</title> <generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>
The meaning of the elements should be obvious.
The last element (generator
) is required
by Blogger import facility, otherwise error message is returned.
According to the schema inside feed
element
there is zero or more entry
elements:
<entry> <id>ID</id> <published>DATE</published> <updated>DATE</updated> <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/blogger/2008/kind#post"/> <!-- tags, each as value of attribute `term' of element category --> <category scheme='http://www.blogger.com/atom/ns#' term='tag1'/> <category scheme='http://www.blogger.com/atom/ns#' term='tag2'/> <title type='text'>title</title> <content type='html'>post content ... </content> </entry>
There is a final </feed>
to guarantee that XML file is well formatted.
I have assumed the only important feature of id
element is that it's content
should be unique. I have decided
to use MD5sum of the post content as IDs to guarantee that.
Finally, my old Blosxom-compatible entries looks similar to the example below:
<?xml version='1.0' encoding='iso-8859-2' ?> <html xmlns="http://www.w3.org/1999/xhtml"><head> <title>Przed finałami RWC 2011</title> <!-- Tags: rwc2011,rugby,francja,polsat--> </head><body><!-- ##Published : 2011-10-20T07:20:26CEST ##--> <p>W RWC 2011 zostały już tylko dwa mecze: jutro (piątek), o trzecie miejsce oraz
So it was extremly easy to extract title, tags and publication date and format Atom-compliant XML file with the following Perl script:
#!/usr/bin/perl # Variant of Blosxom to Blogger conversion # 2011/10 t.przechlewski # use Digest::MD5 qw(md5_hex); print '<?xml version="1.0" encoding="UTF-8"?> <!-- id, title/updated jest wymagane w elementach feed/entry reszta opcjonalna --> <!-- wyglada na minimalne oznakowanie --> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0">'; print "<id>tag:blogger.com,1999:blog-1928418645181504144.archive</id>"; print "<updated>2011-10-22T12:34:14.746-07:00</updated>"; print "<title type='text'>pinkaccordions.blogspot.com</title>"; print "<generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>\n"; foreach $post_file (@ARGV) { my $post_title = $post_content = $md5sum = $published = ''; my @post_kws = (); my $body = $in_pre = 0; my $rel_URLs = 0; print STDERR "\n$post_file opened!\n"; open POST, "$post_file" || die "*** cannot open $post_file ***\n"; while (<POST>) { chomp(); if (/<title>(.+)<\/title>/) {$post_title = $1 ; next ; } if (/<!--[ \t]*Tags:[ \t]*(.+)[ \t]*-->/) {$tags = $1 ; next ; } if (/<\/head><body>/) { $body = 1 ; ## </head><body><!-- ##Published : 2011-10-20T07:20:26CEST ##--> if (/##Published[ \t]+:[ \t]+([0-9T\-\:]+).+##/) { $published = $1; } print STDERR "Published: $published\n"; next; } if (/<\/body><\/html>/) { $body = 0 ; next } if ( $body ) { ## sprawdzam `przenosnosc URLi': if (/src[ \t]*=/) { if (/pinkaccordions.homelinux.org/ || !(/http:\/\// ) ) { $rel_URLs = 1; } } ## zawartość pre nie powinna być składana w jednym wierszu: if (/<pre>/) { $in_pre = 1; $post_content .= "$_\n"; next ; } if (/<\/pre>/) { $in_pre = 0; $post_content .= "$_ "; next ; } if ( $in_pre ) { $post_content .= "$_\n"; } else { $post_content .= "$_ "; # ** musi być ze spacją ** } } } ### ### ### if ($published eq '') { warn "*** something wrong with: $post_file. Not published? Skipping....\n" ; close(POST); next ; } if ( $tags eq '' || $post_title eq '' ) { die "*** something wrong with: $post_file (tags: $tags/title: $post_title)\n"; } if ($rel_URLs) { die "*** suspicious relative URIs: $post_file\n"; } $post_content =~ s/\&/&/g; $post_content =~ s/</</g; $post_content =~ s/>/>/g; print STDERR "Title: $post_title Tags: $tags\n"; @post_kws = split /,/, $tags; $md5sum = md5_hex($post_content); print STDERR "MD5sum: $md5sum\n"; print "<entry>"; print "<id>tag:blogger.com,1999:post-$md5sum</id>"; print "<published>$published</published>"; print "<updated>$published</updated>"; print '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/blogger/2008/kind#post"/>'; ## tags: foreach $k (@post_kws) { print "<category scheme='http://www.blogger.com/atom/ns#' term='$k'/>"; } print "<title type='text'>$post_title</title>"; print "<content type='html'>$post_content</content></entry>"; close(POST); } print "</feed>";
The minor problem was the default formatting of <pre>...</pre>
which I use
to show code snippets.
I have to preserve line breaks (cf. $in_pre
in the above Perl script) of pre
element
content as well as
have to add the following to the default CSS styles (it is possible to modify CSS via
Project →Template Designer →Advanced →Add CSS1)
pre { white-space:nowrap; font-size: 80%; }
To convert simply run script as follows:
perl blogspot-import.pl post1 post2 post3.... > converted-posts.xml
The above described script can be downloaded from here.
1In Polish: Projekt →Projektant szablonów →Advanced →Dodaj Arkusz CSS