FTP Files to WordPress by Email

Yes, another hack to make this site easier for me to manage.

I post a lot from Simplenote now. While this is very efficient for making link-posts, it makes it rather challenging to create a post with images. Images are stored on the Macdrifter host in the standard WordPress content directories but I need a URL to create the image link reference in Simplenote.

I have used GoodReader in the past to upload an image to the server, but I have to manually create the URL for the image. That's no fun.

So I created this insult to everyone that appreciates clean code.2 The script is attached to a mail rule on my primary Mac. When I send an email message with an attached image1 and a subject prefix "iii", the script is triggered. It saves the image to a specific directory on my Mac and then uploads it to Macdrifter.com by FTP using Transmit3. Upon successful upload, it sends a reply email with the URL to the image.

The Abomination



(*

Author: Gabe Weatherhead

date: 2012-03-16_090533

Script for FTP upload to WordPress via Apple Mail rule processing.



This work is Creative Commons-Attribution licensed. Basically, do what you want with it, but don't be dick.

http://creativecommons.org/licenses/



Mesaage subjects are expected to be prepended with "iii"

Transmit is expected a favorite to exist for the destiantion. Mine is named "Macdrifter_WP"

*)



using terms from application "Mail"

    -- Uploads file using a saved favorite in Transmit

    on upload_file(theFile)

        try

            -- Creates base URL for remote file based on standard WordPress content structure

            set urlPath to "http://www.macdrifter.com/uploads.html"

            set myMonth to do shell script "date +\"%m\""

            set myYear to 1 * (year of (current date))

            set clipContents to {}

            tell application "Transmit"

                -- Stored Transmit favorite

                set myFav to item 1 of (favorites whose name is "Macdrifter_WP")



                tell current tab of (make new document at end)

                    connect to myFav

                    -- tempMSG is used for Growl reporting of errors.

                    set tempMSG to "Transmit attmepting to connect"

                    set this_item to theFile

                    -- Update Error reporting

                    set tempMSG to (this_item as text)

                    -- Get the File's path

                    set this_info to info for this_item

                    set myPath to (POSIX path of this_item)

                    -- Update Error Reporting

                    set tempMSG to "Attempting to locate local file" & (myPath as text)

                    set myFileName to name of this_info as text

                    -- Transmit must open the local location that holds the file

                    change location of local browser to path (POSIX path of ("Macintosh HD 2:Dropbox:Todo Files:Blog:"))

                    -- It's just easier to encode URL safe characters with Python

                    set myURLFileName to do shell script "/usr/bin/python -c 'import sys, urllib; print urllib.quote(sys.argv[1])' " & ¬

                        quoted form of myFileName

                    set myURLFileName to urlPath & myYear & "/" & myMonth & "/" & myURLFileName

                    set end of clipContents to "

" & myURLFileName



                    -- Tell Transmit to do the upload

                    tell remote browser

                        upload item at path myPath to "webapps/wp/wp-content/uploads/" & myYear & "/" & ¬

                            myMonth & "/" with resume mode overwrite with continue after error

                    end tell

                    -- I like to have the URL put on my clipboard as well

                    set the clipboard to myURLFileName



                    close remote browser



                end tell

                close front window

            end tell

            set AppleScript's text item delimiters to "

"

            set clipContents to clipContents as string

            set AppleScript's text item delimiters to ""

            return clipContents

        on error errmesg number errn

            set AppleScript's text item delimiters to ""

            return errmesg & return & tempMSG & "error number: " & (errn as rich text)

        end try

    end upload_file



    -- I prefer Growl messaging for error reporting since I also use Prowl for iOS

    on growl_message(theMessage)

        tell application "System Events"

            set isRunning to (count of (every process whose bundle identifier is "com.Growl.GrowlHelperApp")) > 0



        end tell

        if isRunning then

            tell application id "com.Growl.GrowlHelperApp"

                set the allNotificationsList to ¬

                    {"FTP Mail Handler Script", "Script Success", "Script Error.app"}

                set the enabledNotificationsList to ¬

                    {"FTP Mail Handler Script"}

                register as application ¬

                    "FTP Mail Rule Script" all notifications allNotificationsList ¬

                    default notifications enabledNotificationsList ¬

                    icon of application "Script Editor"

                notify with name "FTP Mail Handler Script" title ¬

                    "FTP Mail Handler" description "FTP Upload: " & ¬

                    theMessage application name "FTP Mail Rule Script"



            end tell

        end if

    end growl_message



    -- Mail Rule starts processing the messages that match

    on perform mail action with messages theMessages for rule theRule

        try

            -- Just another sanity check. Only messages that begin with this prefix get processed.

            set custom_prefix to "iii"

            -- All of my WordPress images start in the same local location

            set localPath to "/Volumes/Macintosh HD 2/Dropbox/Todo Files/Blog/"

            repeat with oneMessage in theMessages

                set receivedSubject to subject of oneMessage

                set outgoingSubject to "Re: " & receivedSubject

                set theSender to extract address from sender of oneMessage

                if (receivedSubject starts with custom_prefix) then

                    set {mail attachment:theAttachments} to oneMessage

                    repeat with oneAttachment in mail attachments of oneMessage

                        set newFilePath to POSIX path of (("Macintosh HD 2:Dropbox:Todo Files:Blog:") & (name of oneAttachment))

                        --growl_message(newFilePath)

                        save oneAttachment in ("Macintosh HD 2:Dropbox:Todo Files:Blog:") & (name of oneAttachment)

                        set fileURL to upload_file(newFilePath)

                        growl_message(fileURL)

                        set mailBody to "File Uploaded to Server: " & return & return & (fileURL as rich text)

                        set AppleScript's text item delimiters to {""}

                        set theNewMessage to make new outgoing message with properties ¬

                            {visible:true, subject:outgoingSubject, content:mailBody, sender:"gabe@macdrifter.com"}

                        tell theNewMessage

                            make new recipient at end of to recipients with properties {address:theSender}

                        end tell

                        send theNewMessage

                        set AppleScript's text item delimiters to ""

                    end repeat

                end if

            end repeat

        on error errmesg number errn

            set AppleScript's text item delimiters to ""

            growl_message(errmesg & return & return & "error number: " & (errn as rich text))

        end try

    end perform mail action with messages



end using terms from

Commentary

It might not be obvious, but this method requires an always-on Mac with Mail running. That's not a problem for me.

This is written explicitly for my WordPress content structure. This is pretty standard stuff but it's possible to use another file structure. Just change the urlPath variable appropriately.

I use Growl for notifications. About 1/3 of this script is for error handling and Growl messaging. I like this method because I also use Prowl. Any errors are displayed on my iOS devices almost immediately. I can also look over the Growl logs to see the history. Good stuff.

The script also inserts the link on my Macs clipboard. Since I use Launchbar, that means when I come back to my desk, all the links I generated remotely are right there in my history, ready to paste.

The script replies back to the same email address I sent from. I am not using the AppleScript "reply" method for Mail. It doesn't work well. Seriously, this is why we can't have nice AppleScripts.

Usage

This will work from any app that can send an image by email. That covers most of them, including the new iPhoto for iOS.

I wish there was an Acorn for iOS, but iPhoto does some nice stuff with images. It's not perfect, but it is pretty good for basic image enhancements.


  1. Technically, it will work with any attachment. It should handle multiple attachments as well, but I haven't been using it that way. 

  2. It's bad. I've tried to provide comments, but that will not make the code any better. I'd clean it up, but I'm planning a total rewrite using AppleScriptObjC. That will take a long time. This works now. 

  3. I use Transmit because it's the best FTP client around. But it also has some nice features that make scripting easy. For example, Transmit can save my credentials with the FTP connection. The script can then login without ever needing to know the credentials. When I change my FTP credentials, I only need to change them in Transmit, not 25 different scripts.