Tags

    Aperture MetaWeblog Plugin: Creating an Post

    Comments

    Creating the Post Objective-C Class

    We need a class to represent a post which contains the title, the time created, a description, summary, categories, link, keywords and more text of a blog post. To create one, in Xcode select File, then New File...

    200812202356.jpg

    Select Objective-C class and then select Next.

    200812210003.jpg

    Replace the file name with Post.m and select Finish. Replace Post.h with the following:

    #import <Cocoa/Cocoa.h>
    
    @interface Post : NSObject {
       NSString* title;
       NSCalendarDate* dateCreated;
       NSString* description;
       NSString* postId;
       NSString* blogId;
       NSString* summary;
       NSArray* categories;
       NSString* link;
       NSString* keywords;
       NSString* more;
    }
    
    @property(readwrite,copy) NSString* title;
    @property(readwrite,copy) NSCalendarDate* dateCreated;
    @property(readwrite,copy) NSString* description;
    @property(readwrite,copy) NSString* postId;
    @property(readwrite,copy) NSString* blogId;
    @property(readwrite,copy) NSString* summary;
    @property(readwrite,copy) NSArray *categories;
    @property(readwrite,copy) NSString* link;
    @property(readwrite,copy) NSString* keywords;
    @property(readwrite,copy) NSString* more;
    
    - (id) init;
    
    @end
    

    Replace Post.m with:

    #import “Post.h”
    
    @implementation Post
    
    @synthesize title;
    @synthesize dateCreated;
    @synthesize description;
    @synthesize postId;
    @synthesize blogId;
    @synthesize categories;
    @synthesize summary;
    @synthesize link;
    @synthesize keywords;
    @synthesize more;
    
    - (id) init
    {
       dateCreated = [NSCalendarDate init];
       description = @“”;
       postId = @“”;
       title = @“”;
       summary = @“”;
       categories = nil;
       link = @“”;
       keywords = @“”;
       more = @“”;
       return self;
    }
    
    @end
    

    Creating the MetaWeblog class

    Now that we have a class to represent a post, we need one that represents the blogging engine we will use to create posts. In this case, the blogging engine will need to support the MetaWeblog API. At a minimum, we need to be able to support newMediaObject and newPost methods in order to create a post for an image stored in Aperture. Select File, then New File...

    Select Objective-C class and then select Next.

    200812202357.jpg

    Enter MetaWeblog.m as the file name and select Finish. Open the MetaWeblog.h file and replace it with the following:

    #import <Cocoa/Cocoa.h>
    #import “Post.h”
    
    @interface MetaWeblog : NSObject {
       NSString *url;   
       NSString *username;
       NSString *password;
       NSString *blogId;
    }
    
    @property(readwrite, assign) NSString *url; 
    @property(readwrite, assign) NSString *username;
    @property(readwrite, assign) NSString *password;
    @property(readwrite, assign) NSString *blogId; 
    
    - (id) init;
    
    - (NSString*)newPost: (Post*) post;
    - (NSString*)newMediaObject: (NSString*)name andType: (NSString*) type andBits:(NSData*) bits;
    
    @end
    

    Replace the MetaWeblog.m file with the following:

    #import “MetaWeblog.h”
    
    @implementation MetaWeblog
    
    @synthesize url; 
    @synthesize username;
    @synthesize password;
    @synthesize blogId; 
    
    - (id) init
    {
       url = nil; 
       username  = nil;
       password = nil;
       blogId = nil;
       [super init]; 
       return self;
    }
    
    - (NSString*)newPost: (Post*) px
    {   
       NSString* title = px.title;
       NSString* summary = px.summary;
       NSString* body = px.description;
       NSDate* date = px.dateCreated;
       NSString* more = px.more;
       NSArray* categories = [[NSArray alloc] initWithArray: px.categories copyItems: YES];
       
       NSString *description = body;
       WSMethodInvocationRef rpcCall;
       NSURL *rpcURL = [NSURL URLWithString: url];
       NSString *methodName = @“metaWeblog.newPost”;
       NSArray *postKey = [NSArray arrayWithObjects: 
                       @“description”, 
                       @“dateCreated”, 
                       @“title”, 
                       @“mt_excerpt”, 
                       @“mt_text_more”, 
                       @“categories”, 
                       nil];
       NSArray *postObject = [NSArray arrayWithObjects:
                        description, 
                        date, 
                        title, 
                        summary, 
                        more, 
                        categories, 
                        nil];
       NSDictionary *post = [NSDictionary dictionaryWithObjects: postObject forKeys:postKey]; 
       NSArray *keys = [NSArray arrayWithObjects: @“blog_ID” , @“username”, @“password”, @“post”, @“publish”, nil];
       NSMutableArray *objects = [NSArray arrayWithObjects: blogId, username, password, post, kCFBooleanTrue, nil];
       NSDictionary *params = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
       NSArray *order = [NSArray arrayWithObjects: @“blog_ID”, @“username”, @“password”, @“post”, @“publish”, nil];
       rpcCall = WSMethodInvocationCreate ((CFURLRef) rpcURL, (CFStringRef) methodName, kWSXMLRPCProtocol);
       WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef) order);
       NSDictionary *result = (NSDictionary *) (WSMethodInvocationInvoke (rpcCall));
       if (WSMethodResultIsFault((CFDictionaryRef)result))
       {
         NSString* faultString = [result objectForKey: (NSString *) kWSFaultString];
         NSLog(@“[metaWeblog.newPost] failed = %@“, result);   
         NSException* theException = [NSException exceptionWithName: @“MetaWeblogException” reason: faultString userInfo:nil]; 
         @throw theException; 
       }
       
       NSLog(@“[metaWeblog.newPost] result = %@“, result);   
       return (NSString*) [result objectForKey:(NSString*)kWSMethodInvocationResult ];
    }
    
    - (NSString*)newMediaObject: (NSString*)name andType: (NSString*) type andBits:(NSData*) bits
    {   
       WSMethodInvocationRef rpcCall;
       NSURL *rpcURL = [NSURL URLWithString: url];
       NSString *methodName = @“metaWeblog.newMediaObject”;
       NSArray *postKey = [NSArray arrayWithObjects: @“name”, @“type”, @“bits”, nil];
       NSArray *postObject = [NSArray arrayWithObjects: name, type, bits, nil];
       NSDictionary *post = [NSDictionary dictionaryWithObjects:postObject forKeys:postKey]; 
       NSArray *keys = [NSArray arrayWithObjects: @“blog_ID” , @“username”, @“password”, @“post”, nil];
       NSMutableArray *objects = [NSArray arrayWithObjects: blogId, username, password, post, nil];
       NSDictionary *params = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
       NSArray *order = [NSArray arrayWithObjects: @“blog_ID”, @“username”, @“password”, @“post”, nil];
       rpcCall = WSMethodInvocationCreate ((CFURLRef) rpcURL, (CFStringRef) methodName, kWSXMLRPCProtocol);
       WSMethodInvocationSetParameters (rpcCall, (CFDictionaryRef) params, (CFArrayRef) order);
       
       NSDictionary *result = (NSDictionary *) (WSMethodInvocationInvoke (rpcCall));
       NSLog(@“[metaWeblog.newMediaObject] result = %@“, result);
       
       NSDictionary *values = [result objectForKey:(NSString*)kWSMethodInvocationResult ];
       return (NSString*) [values objectForKey: @“url”];
    }
    
    @end
    

    Modifying the Plug-In

    Now that we have a class to create a post and upload a media object (aka an image), we need to modify the plug-in. First, we need to define an instance of MetaWeblog class in the MetaWeblogPlugIn header.

    #import <Cocoa/Cocoa.h>
    #import <Foundation/Foundation.h>
    #import “ApertureExportManager.h”
    #import “ApertureExportPlugIn.h”
    #import “MetaWeblog.h”
    
    @interface MetaWeblogPlugIn : NSObject <ApertureExportPlugIn>
    {
     id _apiManager; 
     NSObject<ApertureExportManager, PROAPIObject> *_exportManager; 
     NSLock *_progressLock;
     NSArray *_topLevelNibObjects;
     ApertureExportProgress exportProgress;
     IBOutlet NSView *settingsView;
     IBOutlet NSView *firstView;
     IBOutlet NSView *lastView;
     IBOutlet NSTextField *blogAccessPointTextField;
     IBOutlet NSTextField *blogUserNameTextField;
     IBOutlet NSSecureTextField *blogPasswordTextField;
     IBOutlet NSTextField *blogIdTextField;
    
        // the blogging engine
     MetaWeblog* _blog;
    }
    
    @end
    

    Next, when the export process is about to begin (aka exportManagerShouldBeginExport is being called), we need to initialize the instance and set its properties to the values entered by the UI. Find exportManagerShouldBeginExport in MetaWeblogPlugIn.m and modify it so it looks as follows:

    - (void)exportManagerShouldBeginExport
    {
      _blog = [[MetaWeblog alloc] init];
      [_blog setUrl: [blogAccessPointTextField stringValue]];
      [_blog setBlogId: [blogIdTextField stringValue]];
      [_blog setUsername: [blogUserNameTextField stringValue]];
      [_blog setPassword: [blogPasswordTextField stringValue]];
      [_exportManager shouldBeginExport];
    }
    

    When we are done exporting, we should free the blog instance we created. Find the exportManagerDidFinishExport method in MetaWeblogPlugIn.m and replace it with the following:

    - (void)exportManagerDidFinishExport
    {
      [_blog release];
      _blog = nil;
      [_exportManager shouldFinishExport];
    }
    

    We should do the same when the user cancelled the export. Find the exportManagerShouldCancelExport method in MetaWeblogPlugIn.m and replace it with the following:

    - (void)exportManagerShouldCancelExport
    {
      [_blog release];
      _blog = nil;
      [_exportManager shouldCancelExport];
    }
    

    The next step will be to upload the exportManagerShouldWriteImageData to upload the image and create a post. The assumption for now is that the image will always be JPEG. We’ll deal with supporting other image formats at a later date. We’ll also use the version name as the post title. In a later installment, I’ll use the actual metadata from the image to set the title. Replace the contents of the method to the following:

    - (BOOL)exportManagerShouldWriteImageData:(NSData *)imageData toRelativePath:(NSString *)path forImageAtIndex:(unsigned)index
    {
      // memory management
      NSAutoreleasePool *pool =  [[NSAutoreleasePool alloc] init];
      
      // get the properties for the image
      NSDictionary* properties = [_exportManager propertiesForImageAtIndex: index];
    
      // get the version name from the properties 
      NSString* versionName = [properties objectForKey:@“kExportKeyVersionName”];
    
      // get the actual file name from the path without a directory
      NSString* name = [path lastPathComponent];
     
      // upload the image
      NSString* url = [_blog newMediaObject: name andType: @“image/jpeg” andBits: imageData];
      if(url != nil)
      {
        NSLog(@“Uploaded image %@“, url);
    
        // create a post
        Post* post = [[Post alloc] init];
        post.title = versionName;
        post.description = [NSString stringWithFormat: @“<img src=“%@“ />“, url];
        post.dateCreated = [NSCalendarDate init];
        
        // create the post
        NSString* postId = [_blog newPost: post];
        NSLog(@“Created post with id %@“, postId);
        [post release];
      }
      else
      {
        NSLog(@“Failed to upload the image ‘%@‘“, path);
      }
      [pool release];
    
      // don’t write anything to disk
      return FALSE; 
    }
    

    Testing the plugin

    Quit Aperture if its running. Rebuild and deploy the plug-in. Execute the plug-in, by running Aperture, select a few images, then select File, then Export and then Blog. Enter the details of your blog and select Export. Once the plug-in is done, you should see a few entries for the selected images in your blog.

    Conclusion

    In this post we modified the plug-in to post the image to a MetaWeblog compatible blogging system. There are several things that require attention, including saving your blog’s details, improved error handling (which is non-existant) and reusing metadata we already defined in Aperture.

    .