#!/usr/bin/perl # -*- mode: cperl; coding: utf-8; -*- use strict; use warnings; use utf8; use lib "/h/hamren/src/post/lib", "."; my $rval = do "common.pm" || die "$0: common.pm failed ($!) [$@]"; #--- Single-line common initializer #--- End of header my $post = tt(".post" ); my $html = tt(".html" ); my $ts = tt(".ts" ); my $postpm = tt("post.pm"); my $img = tt("img" ); my $href = tt("href" ); my $slug = '"slug"'; my $slugs = '"slugs"'; my $gt = '>'; post( header(), p("There are at least three ways in which pages can be remotely uploaded to a WordPress site."), ul("Using the modern HTML-based (REST) API v2. I almost, but not completely, got this to work. " . "Unfortunately, documentation is mostly missing, and partly incorrect, so I will skip this one, at least for now.", "Using the older XMLRPC API. There are two Perl modules, " . "«WordPress::API» and «WordPress::API::Post», that make using this API a breeze. " . "The downside of XMLRPC (and the REST API above) is speed, with each post taking more than a second to upload. " . "Using XMPLRPC is the focus of this article.", "Writing directly to the database. This is a lot faster, perhaps by a factor of 1000 on a local database.See " . post_link("creating-web-pages-using-perl-upload-2", "this article") . "."), h2("Uploading using XMLRPC"), p("Uploading is done by the script ", href({charset => "UTF-8", type => "text/plain"}, "wordpress-upload-posts.pl", "wordpress-upload-posts.pl"), " which is part of ", pub_link("post", "the archive"), "."), p("This script should be run in the directory contaning all the post directories."), p("Uploading, as done by the script, involves several steps:"), ul("Download all the posts from the server («WordPress::API->new->getRecentPosts()»).", "Create a hash that maps $slugs to IDs.", "For each downloaded post". ul("Check that there is exactly one $post file in the directory named after the $slug. The $post file can have any name", "Check that there is a corresponding $html file. The file must have the same name as the $post file, but with an «.html» extension", "Check if the $html file is newer than the timestamp ($ts) file.", "Download the post using its ID(«WordPress::API::Post->new->load()»).", "Update the content field («\$the_post-${gt}description(\$new_text)}»).", "Upload the updated post («\$the_post-${gt}description()}»).")), p("A typical filename triple:"), source_codeq(<<'EOF'), creating-web-pages-using-perl/page.post creating-web-pages-using-perl/page.html creating-web-pages-using-perl/page.ts EOF p("The timestamp file help keeping total upload time down. ", "To unconditionally upload all posts, delete all the .ts files"), h2("Key points in the source code"), p("First, a connection for reading all the posts must be set up:"), source_codeq(<<'EOF'), my $w = WordPress::API->new({ proxy => $proxy, username => $username, password => $password }); EOF p("«proxy» is the URL of the file «xmlrpc.php» in the root of the WordPress installation directory, ", "something like « https://www.sdu.se/blog/xmlrpc.php». ", "«username» and «password» are of course the username and password of a user that has read and write permissions on the site. ", "These values are passed to the script as three environment variables: «PROXY», «WUSERNAME» and «WPASSWORD». ", "The username and poassword variables get a prefix, «W» for web, to avoid conflicts with other usernames and passwords."), p("Then all active posts are downloaded, and a map from $slugs to id:s can be created:"), source_codeq(<<'EOF'), my $posts = $w->getRecentPosts(); map { $slug_to_id{$_->{'wp_slug'}} = $_->{'postid'} } @$posts; EOF p("We need the posts id when we send an update request to the server, ", "and we need the $slug to identify the directory containing the post. ", "Recall that there is one directory for each post, and that the directory containing a post must have exactly the same name as the post's $slug. ", "Why use the slug? Well, I'd rather have a directory called «creating-web-pages-using-perl» than one called «79»."), p("If there are several revisions of a post, then there will be several records for that post in the database, all with the same $slug. ", "Only the active (usually: latest) revision is return by the «getRecentPosts()» call, ", "so there is no risk of updating the wrong instance."), p("Then we loop over all the downloaded posts. ", "For each post, if the $html file is newer than the $ts file, the post is downloaded, it's contents replaced, and then uploaded again. ", "Downloading the post twice does seem wasteful, ", "but posts returned by «getRecentPosts()» are in a different format from posts downloaded indivitually. ", "In technical terms, they are represented by Perl hashes that are completely different."), p("Downloading is easy:"), source_codeq(<<'EOF'), my $p = WordPress::API::Post->new({ proxy => $proxy, username => $username, password => $password }); $p->id($id); #--- Set the id my $tmp = $p->load(); #--- Download post EOF p("Updating the body and uploading the update post is equally easy:"), source_codeq(<<'EOF'), $p->description($body); #--- Update body $tmp = $p->save(); #--- Upload modified post EOF p(pub_link("post", "Download the archive"), " or view ", href({type => "text/plain"}, "wordpress-upload-posts.pl", "wordpress-upload-posts.pl"), "."), footer() );