When I tried out Drupal blogging with TextMate, I discovered that the TextMate Blogging Bundle couldn’t deal with Drupal’s way of posting with categories as implemented by the BlogAPI module, so I decided to see if I could figure out how to add this support.

I didn’t know much about XML-RPC, so I did some research on it to get an idea of what was getting sent back and forth between the blog server and TextMate. XML-RPC Client was most helpful in this process. Between that and examining Drupal’s BlogAPI module and xmlrpc files I started to get the picture of what was going on. Most of the changes I would need to make would occur in the Blogging.tmbundle/Support/lib/blogging.rb file and involve setting or getting categories after a post is published or fetched.

The first issue I found was in the Blogging->Category Command ruby script. Drupal has a problem with the endpoint being specified as cred.endpoint in the XML-RPC call. Essentially, this messes up the endpoint path as it was already specified in the @path variable of cred.client. WordPress, interestingly, works with or without the extra endpoint. I don’t know about other blogs’ XML-RPC implementations, but with Drupal, the endpoint should be blank in the metaWeblog.getCategories call.

In order to make the change specific to only drupal blogs I wanted to add the change as an if statement with a ‘drupal’ blog mode. However, as you can see, I also am using WordPress as my public blog, so I couldn’t just use a TM_BLOG_MODE variable as then my WordPress blogging might get messed up. The endpoint guessing wasn’t accurate in this case as both Drupal and WordPress endpoints are xmlrpc.php. I decided to add an extra and optional mode column to the configuration. This mode gets picked up in read_endpoints:

def read_endpoints
	@endpoints = {}
	@modes = {}
	if File.exist?(BLOG_ACCOUNTS_FILE)
		IO.readlines(BLOG_ACCOUNTS_FILE).each do | line |
			next if line =~ /^\s*#/
			if line =~ /^(.+?)\s+(https?:\/\/.+?)\s(\w+)?/ # Match Blog URL Mode(optional)
				@endpoints[$1] = $2 # endpoints[Blog]=URL
				@endpoints[$2] = $1 # endpoints[URL]=Blog
				if ! $3.nil?
       				@modes[$2] = $3 # modes[URL]=Mode
				end
			end
		end
	end
	@endpoints
end

I moved all the mode determination to def mode … end, with an adjustment to use the mode indicated in the BLOG_ACCOUNTS_FILE when specified. I added a definition for modes to insure the endpoints and corresponding modes, get read. Now that I could specify a ‘drupal’ mode I adjusted various other spots in the code to check the mode as required for Drupal blogging.

The category command code in the Bundle Editor is the only change outside of blogging.rb. Only the changed section is shown here as an image (to test file uploading):
Blogging Category Command Code
The extra username variable is optional. I just wanted to keep things consistent with how the endpoint and password were specified.

The next problem occurs in the fetching of an existing post. It turns out that Drupal doesn’t include categories as part of the post called with metaWeblog.getPost, but they can be fetched separately using the mt.getPostCategories call. A good place to add this call is in the post_to_document method. I am including it as an elsif after the wp mode and existence of post['category'] is tested:

# Drupal's categories are not kept in the post
# but can be fetched with mt.getPostCategories
elsif (self.mode == 'drupal')
	cats = self.client.call("mt.getPostCategories", self.post_id, self.username, self.password)
	cats.each { | cat | doc += "Category: #{cat['categoryName']}\n" }
end

That takes care of category fetching. The next step is posting. This happens in post_or_update. In this case the problem is that Drupal won’t pick up any categories sent as part of the post, but they can be set separately by calling mt.setPostCategories. A further complication is that the categories need to be set as an array of categoryIds rather then categoryNames. I did this by first creating a hash of all blog categoryNames and Ids and then looking up the correct Id for the categories listed before sending them to the published post.

# Setting post categories with drupal blogs
if (self.mode == 'drupal') && (! @post['categories'].nil?)
  # Get the blog's categories: Category => Id
  blog_cats = Hash.new
  client.call("metaWeblog.getCategories", "", self.username, current_password).map do |cat|
    blog_cats[cat['categoryId']] = cat['categoryName']
  end
  # Convert the category strings to an array of categoryIds
  post_cats = @post['categories']
  post_cats.each_index do |cat|
    post_id = blog_cats.index(post_cats[cat])
    # Don't try to add categories that the blog doesn't know about!
    if ! post_id.nil?
      post_cats[cat] = Hash['categoryId' => post_id]
    end
  end
  # Set the post categories.
  client.call("mt.setPostCategories", "#{self.post_id}", self.username, current_password, post_cats)
end

That’s it for the changes, for now, and everything still seems to work correctly for both WordPress and Drupal posting.

As for file uploads, I tried uploading to my experimental Drupal blogs, and the upload itself works just fine. The files end up in the correct specified directory. However, there is an incompatibility with how Drupal handles files with its Upload module and how the BlogAPI handles them. When posting with Drupal’s web-browser UI, files are uploaded, and then, upon posting, the uploaded file path is added to a FILES table in the blog’s database along with the post’s Id so that Drupal can manage the uploaded files. This table is necessary for accessing files linked to in posts when using the private files (access control) option in the file system. If files are kept publicly accessible, this FILE table info is not strictly required, but the files won’t be editable with Drupal as Drupal won’t be aware of the files’ presence. The files won’t show up as attachments when subsequently loaded for editing in the Drupal UI. This issue would probably need to be addressed by Drupal’s BlogAPI. A possible solution might be to check for a post’s linked files in the upload directory and add the paths for any that are present but unaccounted for to the FILES table. It is something quite beyond me at the moment, as I don’t know Drupal or PHP well enough yet to attempt it.

Actually, I hardly knew any Ruby before attempting this category issue fix either, but the Blogging.tmbundle is small enough that I was able to get the general idea of what was going wrong in blogging.rb after examining the system.listMethods available and the output of metaWeblog.getPost from Drupal. I must say that Ruby code is quite readable in most cases although I did need to consult a reference for some syntax and methods help.

If anyone is interested in the complete changes to blogging.rb and updated documentation for the changes to the Setup Blogs file, you may view them in blogging.rb.diff and help.markdown.diff.

Update:
I’m not sure what changed exactly, but the latest TextMate Version 1.5.5 (1372) update has caused my changes to the Blogging.tmbundle to stop working. I tried patching the latest bundle available in the subversion repository as well, but with both I get a “Wrong type NilClass. Not allowed!” exception with the category command. Also, the category command keeps asking to select the blog twice even without any of my changes for some reason. Part of the trouble is probably my limited Ruby knowledge at work. Anyway, since I don’t use Drupal other than to just experiment with it occasionally, it is not a priority for me to figure out the problem. The default bundle works just fine with WordPress (other than the 2 times blog selection that is!).

5 Responses to “TextMate Blogging Bundle and Drupal”

  1. on 03 Feb 2007 at 8:40 pmdavidvogt

    Your article is very informative and helped me further.

    Thanks, David

  2. on 18 Feb 2007 at 12:27 amGeoff

    Thanks for the nice post. I’m working on porting the Blogging bundle to e (http://www.e-texteditor.com) and mostly have it working with Drupal. (The code is here: http://ebundles.googlecode.com/svn/trunk/Bundles/Blogging.tmbundle/) I’m wondering if you’re using Drupal 5?

    Thanks again!

  3. on 18 Feb 2007 at 9:37 amK. Liisi Linask

    Yes, I am using Drupal 5.

  4. on 03 Jun 2007 at 2:45 pmthemegarden.org

    Nice article.
    I’m wondering do you know some alternative for the TextMate (for another platforms, and maybe open source)

  5. on 05 Sep 2008 at 6:08 pmgavri

    what about building a drupal module instead of hacking the code of textmate blogging bundle.
    i played with it a bit and got some nice results:
    i duplicated the blogapi.module and played with it a bit
    i’ll post my resaults after ill organize my code