Setting up WordPress 3.x for SSH2 Updates

I ran into a problem I haddn’t previously encountered trying to use AutoUpdate on a WordPress 3.3 installation that required a bit more fixing than I expected.  Overall I’m having a great experience using Rackspace for various staging server needs but the minimal nature of the installs means I’m tweaking server setups and disk images more than I have in the past.

One of the latest tweaks involed getting a WordPress site to work with AutoUpdate after I had been recieving a a “Failed to Connect to FTP server..” error message at the top of the page when trying to update.  So after making sure my permissions were set correctly I looked into the connection type options for auto-updating in wordpress.

The only options I was presented with were FTP (Bad idea for security reasons) and FTPS (different than SFTP, there were a number of comments on this confusion so I thought I’d note it here for anyoen reading.), but what I wanted to to enable SSH2 for updates.

The root issue I discovered is really in the PHP setup and not in wordpress itself.  After reading Kevin van Zonneveld’s post “Make SSH connections with PHP” it was fairly straight forward to get it to work.

Essentially I had to enable ssh2 in PHP by installing the correct libraries.  On my Ubuntu installation that was just by:

sudo apt-get install libssh2-1-dev libssh2-php

which I confirm is installed correctly (as Kevin directs) with:

php -m | grep ssh2

After that I want to setup password based authentication in my case instead of key based so I modify ‘/etc/ssh/sshd_config’ and change the value for ‘PasswordAuthentication’ to ‘yes’

After that I restart ssh via:

sudo service ssh restart

Gong back to my wordpress admin page I select the link to update wordpress, use the userid and password values as needed and leave the key fields blank.

WordPress to Django: Designing Compatible URLs in urls.py

As I mentioned in my previous post, there are a few fairly easy strategies for maintaining the stable URLs for your content when migrating from WordPress to a local Django driven blog.

Django allows you a high level of control over URL formats so it's fairly simple to design them to be compatible with WordPress URLs.  Additionally WordPress has been around long enough that the standard URL re-write formats follow suggested best practices for content, so bringing your Django URLs in alignment with that is not only useful for migrating content but good practice overall.

That said the two most common formats for URLs in WordPress are:

http://<domain>/<4 digit year>/<1 or 2 digit month/<1 or 2 digit day/<slug>/

so for example the URL for the previous post linked above is…

http://www.flagonwiththedragon.com/2011/06/01/wordpress-to-django-strategies-dealing-with-WordPress-querystring-urls/

The next most common format for URLs is similar and differs mostly in how months are abbreviated:

http://<domain>/4 digit year>/<3 char month>/<1 or 2 digit day>/<slug>/

So an example of the same URL above in this format would be…

http://www.flagonwiththedragon.com/2011/jun/01/wordpress-to-django-strategies-dealing-with-WordPress-querystring-urls/

Designing urls.py in Django to accomodate this is simply:

    # URL format where month format is abbreviated character format.
    url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/(?P[0-9A-Za-z-]+)/$', 'post_detail_alt'),
    url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/$', 'post_day_alt'),
    url(r'^(?P\d{4})/(?P\w{3})/$', 'post_month_alt'),
    # URL format where month is either one or two digits.
    url(r'Word\d{4}Press\d{1,2})/(?P\d{1,2})/(?P[0-9A-Za-z-]+)/$', 'post_detail', name='post-detail'),
    url(r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/$', 'post_day', name='list-day'),
    url(r'^(?P\d{4})/(?P\d{1,2})/$', 'post_month', name='list-month'),
    url(r'^(?P\d{4})/$', 'post_year', name='list-year'),

I provide a bit more here than needed as I also include posts lists if you just use the date portion of the URL but it should be obvious.

My unscientific opinion is that digit format for all date properties in a URL is better practice and the format I see most often so I suggest using that in your own use.  Using this format is also advantagous for your Django views because casting the date pieces as an int() means it can evalutate valutes like '01' and '1' in exactly the same way.  TheWordPress a post detail view based on all digits then would simply be:

# Test conversion to number based dates
def post_detail(request, year, month, day, slug):
    """Returns an individual post."""
    date = datetime.date(int(year), int(month), int(day))
    try:
        post = Post.objects.published().get(slug=slug, pub_date__year=date.year,
                            pub_date__month=date.month, pub_date__day=date.day)
    except Post.DoesNotExist:
        raise Http404

    return render(request, 'blog/post_detail.xhtml', {
            'post': post,
            'title': post.title,
            })

The view to handle URLs where months use character abbreviations is very similar and just needs a bit more to parse the date format like so…

# Detail Views
def post_detail_alt(request, year, month, day, slug):
    """
    Returns an individual post. Alternate arguments for compatability with temporary URL pattern

    """
    tt = time.strptime('-'.join([year, month, day]), '%Y-%b-%d')
    date = datetime.date(*tt[:3])
    try:
        post = Post.objects.published().get(slug=slug, pub_date__year=date.year, 
                            pub_date__month=date.month, pub_date__day=date.day)
    except Post.DoesNotExist:
        raise Http404
    
    return render(request, 'blog/post_detail.xhtml', {
            'post': post,
            'title': post.title,                                     
            })

The above alt method is rather sloppy on my part really and violates DRY principles so at some future date I could refactor the alt method to be more lean like so…

def post_detail_alt(request, year, month, day, slug):
    """
    Returns an individual post. Alternate arguments for compatability with temporary URL pattern

    """
    tt = time.strptime('-'.join([year, month, day]), '%Y-%b-%d')
    date = datetime.date(*tt[:3])
    return post_detail(request, date.year, date.month, date.day)

That's really all you need.  If you're migrating content from WordPress and used those URL formats all your content should map.  If you're just starting out with your blog the concepts here are sound for starting right from the beginning.

WordPress to Django: Strategies Dealing with WordPress Querystring URLs

Stable URLs are the foundation of valuable information on the web.  As Tim Berners-Lee eloquently described it "Cool URIs Don't Change" and I thought I'd address a few strategies and code I'm using in MetaRho for maintaining stable URLs for content migrating from WordPress.

WordPress Querystring URLs

By default WordPress uses querystrings for accessing content, passing the internal ID number of the post through the 'p' attribute like so…

http://<domain name>/index.php?p=<post id>

So for example calling post id 1 on mydomain.com would look like.

http://www.mydomain.com/index.php?p=1

Since best practice for Cool URIs and in Django is to use real URLs instead of Querystrings this presents a small problem.  The easiest solution I found is to simply implement a decorator in Django that I put on the default index view to watch for incoming WordPress querystring URLs and query some extra field on the model for blog posts that holds the original WordPress ID number.  Note I do NOT try to maintain ID numbers between posts as the better practice is to keep these opaque from the user.

I use the common strategy of keeping a one to many Model related to my posts to contain key value pairs for extra data.  In my implementation I call that Model PostMeta and when I import content I just store the original WordPress ID number under a key 'wp_post_id'.

(view code on github)

def wp_post_redirect(view_fn):
    '''
    Checks a request for a querystring item matching a WordPress 
    post request.
    
    This is to enables url redirects for blog migrations from WordPress.
    To use just decorate the view method for your default blog location.
    
    '''
    def decorator(request, *args, **kwargs):
        wp_query = request.GET.get('p', None)
        if wp_query:
            try:
                post = Post.objects.published().get(postmeta__key='wp_post_id', 
                                                    postmeta__value=wp_query)
                ar = post.pub_date.strftime("%Y/%m/%d").split('/')
                ar.append(post.slug)
                htr = HttpResponseRedirect(reverse('blog:post-detail', args=ar))
                htr.status_code = 301 # This should reflect a 'Moved Permanently' code.
                return htr
            except Post.DoesNotExist:
                raise Http404
        return view_fn(request, *args, **kwargs)

    return decorator

The decorator is fairly simple and just queries for posts with that meta key and redirects the user to the real URL with the proper status code or issues a 404 if no post is found.

It's important to note that this decorator needs to be put on the view used at the of the blog app as that is the equivalent behavior from WordPress.

In my next update I'll discuss a bit about designing Django URLs to deal with typical WordPress URL rewrite configurations.