public
Fork of halorgium/mephisto
Description: A mirror of the mephisto code-base
Homepage: http://mephistoblog.com/
Clone URL: git://github.com/technoweenie/mephisto.git
Click here to lend your support to: mephisto and make a donation at www.pledgie.com !
Implement movable type xml-rpc API [Moritz Angermann]

git-svn-id: http://svn.techno-weenie.net/projects/mephisto/trunk@2402 
567b1171-46fb-0310-a4c9-b4bef9110e78
technoweenie (author)
Fri Oct 27 07:04:35 -0700 2006
commit  a18ee2e99080c900a8f1ef036462977d1c3ba06b
tree    da57ed82aa60b2279d699659dafcb92bb437f821
parent  2163e57bd482e414ebcc60e57e999bee0aa3ddcb
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 * SVN *
0
 
0
+* Implement movable type xml-rpc API [Moritz Angermann]
0
+
0
 * Enhance error recovery in the wordpress converter [Chris Petrilli]
0
 
0
 * Fix Site#tags query to return distinct tags [DeLynn Berry]
...
3
4
5
6
 
7
8
9
...
19
20
21
22
 
23
24
25
26
27
28
 
29
30
31
...
65
66
67
68
69
70
71
72
73
 
 
 
 
 
 
 
74
75
76
77
78
 
79
80
81
...
83
84
85
86
87
 
...
3
4
5
 
6
7
8
9
...
19
20
21
 
22
23
24
25
26
27
 
28
29
30
31
...
65
66
67
 
 
 
 
 
68
69
70
71
72
73
74
75
76
77
78
79
 
80
81
82
83
...
85
86
87
 
88
89
0
@@ -3,7 +3,7 @@ class MetaWeblogService < XmlRpcService
0
   before_invocation :authenticate
0
 
0
   def getCategories(blogid, username, password)
0
- site.sections.find(:all).collect &:name
0
+ site.sections.find(:all, :order => 'id ASC').collect &:name
0
   end
0
 
0
   def getPost(postid, username, password)
0
@@ -19,13 +19,13 @@ class MetaWeblogService < XmlRpcService
0
     article = @user.articles.build :site => site
0
     post_it(article, username, password, struct, publish)
0
   end
0
-
0
+
0
   def editPost(postid, username, password, struct, publish)
0
     article = @user.articles.find(postid)
0
     post_it(article, username, password, struct, publish)
0
     true
0
   end
0
-
0
+
0
   def deletePost(appkey, postid, username, password, publish)
0
     article = @user.articles.find(postid)
0
     article.destroy
0
@@ -65,17 +65,19 @@ class MetaWeblogService < XmlRpcService
0
     def article_url(article)
0
       article.published? && article.full_permalink
0
     end
0
-
0
- def post_it(article, user, password, struct, publish)
0
- article.attributes = {:updater => @user, :section_ids => Section.find(:all, :conditions => ['name IN (?)', struct['categories']]).collect(&:id),
0
- :body => struct['description'].to_s, :title => struct['title'].to_s, :excerpt => struct['mt_excerpt'].to_s}
0
- utc_date = Time.utc(struct['dateCreated'].year, struct['dateCreated'].month, struct['dateCreated'].day, struct['dateCreated'].hour, struct['dateCreated'].sec, struct['dateCreated'].min) rescue article.published_at
0
 
0
+ def post_it(article, user, password, struct, publish)
0
+ # make sure publish is true if it's 1 if not leave it the way it is.
0
+ publish = publish == 1 || publish
0
+ # if no categories are supplied do not attempt to set any.
0
+ article.section_ids = Section.find(:all, :conditions => ['name IN (?)', struct['categories']]).collect(&:id) if struct['categories']
0
+ article.attributes = {:updater => @user, :body => struct['description'].to_s, :title => struct['title'].to_s, :excerpt => struct['mt_excerpt'].to_s}
0
+ utc_date = Time.utc(struct['dateCreated'].year, struct['dateCreated'].month, struct['dateCreated'].day, struct['dateCreated'].hour, struct['dateCreated'].sec, struct['dateCreated'].min) rescue article.published_at || Time.now.utc
0
       article.published_at = publish == true ? utc_date : nil
0
       article.save!
0
       article.id
0
     end
0
-
0
+
0
     def guess_content_type_from(name)
0
       if name =~ /(png|gif|jpe?g)/i
0
         "image/#{$1 == 'jpg' ? 'jpeg' : $1}"
0
@@ -83,4 +85,4 @@ class MetaWeblogService < XmlRpcService
0
         'application/octet-stream'
0
       end
0
     end
0
-end
0
\ No newline at end of file
0
+end
...
1
2
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
6
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
0
@@ -1,6 +1,88 @@
0
 class MovableTypeApi < ActionWebService::API::Base
0
   inflect_names false
0
 
0
+ # Movable Type Programatic interface
0
+ # see: <http://www.movabletype.org/mt-static/docs/mtmanual_programmatic.html>
0
+ # supportedTextFilters has already been there!
0
+ # - Moritz Angermann
0
+ #
0
+ # mt.getRecentPostTitles
0
+ # Description: Returns a bandwidth-friendly list of the most recent posts in the system.
0
+ # Parameters: String blogid, String username, String password, int numberOfPosts
0
+ # Return value: on success, array of structs containing ISO.8601 dateCreated, String userid, String postid, String title; on failure, fault
0
+ # Notes: dateCreated is in the timezone of the weblog blogid
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtgetrecentposttitles.html>
0
+
0
+ #api_method :getRecentPostTitles,
0
+ # :expects => [ {:blogid => :string}, {:username => :string}, {:password => :string}, {:numberOfPosts => :int} ],
0
+ # :returns => [[MovableTypeStructs::Post]]
0
+
0
+ # mt.getCategoryList
0
+ # Description: Returns a list of all categories defined in the weblog.
0
+ # Parameters: String blogid, String username, String password
0
+ # Return value: on success, an array of structs containing String categoryId and String categoryName; on failure, fault.
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtgetcategorylist.html>
0
+ api_method :getCategoryList,
0
+ :expects => [ {:blogid => :string}, {:username => :string}, {:password => :string} ],
0
+ :returns => [[MovableTypeStructs::Category]]
0
+
0
+ # mt.getPostCategories
0
+ # Description: Returns a list of all categories to which the post is assigned.
0
+ # Parameters: String postid, String username, String password
0
+ # Return value: on success, an array of structs containing String categoryName, String categoryId, and boolean isPrimary; on failure, fault.
0
+ # Notes: isPrimary denotes whether a category is the post's primary category.
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtgetpostcategories.html>
0
+ api_method :getPostCategories,
0
+ :expects => [ {:postid => :string}, {:username => :string}, {:password => :string} ],
0
+ :returns => [[MovableTypeStructs::Category]]
0
+
0
+ # mt.setPostCategories
0
+ # Description: Sets the categories for a post.
0
+ # Parameters: String postid, String username, String password, array categories
0
+ # Return value: on success, boolean true value; on failure, fault
0
+ # Notes: the array categories is an array of structs containing String categoryId and boolean isPrimary. Using isPrimary to set the primary category is optional--in the absence of this flag, the first struct in the array will be assigned the primary category for the post.
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtsetpostcategories.html>
0
+ api_method :setPostCategories,
0
+ :expects => [ {:postid => :string}, {:username => :string}, {:password => :string}, {:categories => [MovableTypeStructs::PostCategory]} ],
0
+ :returns => [:bool]
0
+
0
+ # mt.supportedMethods
0
+ # Description: Retrieve information about the XML-RPC methods supported by the server.
0
+ # Parameters: none
0
+ # Return value: an array of method names supported by the server.
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtsupportedMethods.html>
0
+
0
+ api_method :supportedMethods,
0
+ :expects => [],
0
+ :returns => [[:string]]
0
+
0
+ # mt.supportedTextFilters
0
+ # Description: Retrieve information about the text formatting plugins supported by the server.
0
+ # Parameters: none
0
+ # Return value: an array of structs containing String key and String label. key is the unique string identifying a text formatting plugin, and label is the readable description to be displayed to a user. key is the value that should be passed in the mt_convert_breaks parameter to newPost and editPost.
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtsupportedtextfilters.html>
0
+
0
   api_method :supportedTextFilters,
0
+ :expects => [],
0
     :returns => [[MovableTypeStructs::TextFilter]]
0
-end
0
\ No newline at end of file
0
+
0
+ # mt.getTrackbackPings
0
+ # Description: Retrieve the list of TrackBack pings posted to a particular entry. This could be used to programmatically retrieve the list of pings for a particular entry, then iterate through each of those pings doing the same, until one has built up a graph of the web of entries referencing one another on a particular topic.
0
+ # Parameters: String postid
0
+ # Return value: an array of structs containing String pingTitle (the title of the entry sent in the ping), String pingURL (the URL of the entry), and String pingIP (the IP address of the host that sent the ping).
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtgettrackbackpings.html>
0
+
0
+ # api_method :getTrackbackPings,
0
+ # :expects => [ {:postid => :string} ],
0
+ # :returns => [[MovableTypeStructs::Trackback]]
0
+
0
+ # mt.publishPost
0
+ # Description: Publish (rebuild) all of the static files related to an entry from your weblog. Equivalent to saving an entry in the system (but without the ping).
0
+ # Parameters: String postid, String username, String password
0
+ # Returns value: on success, boolean true value; on failure, fault
0
+ # Reference: <http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtpublishpost.html>
0
+
0
+ #api_method :publishPost,
0
+ # :expects => [ {:postid => :string}, {:username => :string}, {:password => :string} ],
0
+ # :returns => [:bool]
0
+end
...
1
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
4
5
6
7
8
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
33
34
35
36
37
38
39
40
41
42
43
44
45
0
@@ -1,7 +1,44 @@
0
 class MovableTypeService < XmlRpcService
0
   web_service_api MovableTypeApi
0
+ before_invocation :authenticate, :except => [:supportedMethods, :supportedTextFilters]
0
+
0
+ #def getRecentPostTitles(blogid, username, password, numberOfPosts)
0
+ # # FIXME not implemented
0
+ #end
0
+
0
+ def getCategoryList(blogid, username, password)
0
+ site.sections.find(:all, :order => 'id ASC').collect { |c| MovableTypeStructs::Category.new(:categoryId => c.id, :categoryName => c.name) }
0
+ end
0
+
0
+ def getPostCategories(postid, username, password)
0
+ article = @user.articles.find(postid)
0
+ article.sections.collect { |c| MovableTypeStructs::Category.new(:categoryId => c.id, :categoryName => c.name) }
0
+ end
0
+
0
+ def setPostCategories(postid, username, password, categories)
0
+ article = @user.articles.find(postid)
0
+ article.section_ids= categories.collect { |c| c.categoryId }
0
+ article.save!
0
+ true
0
+ end
0
+
0
+ def supportedMethods
0
+ MetaWeblogService.public_instance_methods(false).collect { |m| "metaWeblog.{#m}" } \
0
+ + MovableTypeService.public_instance_methods(false).collect { |m| "mt.{#m}" }
0
+ end
0
 
0
   def supportedTextFilters
0
     FilteredColumn.filters.collect { |(key, filter)| MovableTypeStructs::TextFilter.new(:key => key, :label => filter.filter_name) }
0
   end
0
-end
0
\ No newline at end of file
0
+
0
+ # def getTrackbackPings,
0
+ # ...
0
+ # end
0
+
0
+ #def publishPost(postid, username, password)
0
+ # article = @user.articles.find(postid)
0
+ # #article. hmmm now what?
0
+ # true
0
+ #end
0
+
0
+end
...
8
9
10
11
 
12
13
14
...
16
17
18
19
 
20
21
22
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
25
26
...
84
85
86
87
 
88
89
90
...
92
93
94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
96
97
 
98
99
100
...
102
103
104
105
 
106
107
108
109
 
110
111
112
 
113
114
115
...
117
118
119
120
 
121
122
123
 
124
125
126
...
136
137
138
139
 
140
141
 
142
143
144
...
146
147
148
149
 
150
...
8
9
10
 
11
12
13
14
...
16
17
18
 
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
...
143
144
145
 
146
147
148
149
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
 
204
205
206
207
...
209
210
211
 
212
213
214
215
 
216
217
218
 
219
220
221
222
...
224
225
226
 
227
228
229
 
230
231
232
233
...
243
244
245
 
246
247
 
248
249
250
251
...
253
254
255
 
256
257
0
@@ -8,7 +8,7 @@ class BackendController; def rescue_action(e) raise e end; end
0
 
0
 class BackendControllerTest < Test::Unit::TestCase
0
   fixtures :users, :sections, :assigned_sections, :contents, :sites
0
-
0
+
0
   def setup
0
     @controller = BackendController.new
0
     @request = ActionController::TestRequest.new
0
@@ -16,11 +16,70 @@ class BackendControllerTest < Test::Unit::TestCase
0
     @protocol = :xmlrpc
0
     FileUtils.mkdir_p ASSET_PATH
0
   end
0
-
0
+
0
   def teardown
0
     FileUtils.rm_rf ASSET_PATH
0
   end
0
 
0
+ # Movable Type Backend Api Tests
0
+ def test_movable_type_get_category_list
0
+ # test test_movable_type_get_category_list
0
+ args = [ 1, 'quentin', 'test' ]
0
+
0
+ result = invoke_layered :mt, :getCategoryList, *args
0
+ assert_equal 'Home', result.first['categoryName']
0
+ end
0
+
0
+ def test_movable_type_get_post_categories
0
+ # test test_movable_type_get_post_categories
0
+ args = [ 1, 'quentin', 'test' ]
0
+
0
+ result = invoke_layered :mt, :getPostCategories, *args
0
+ assert_equal %w(About Home), result.collect { |c| c['categoryName'] }
0
+ end
0
+
0
+ def test_movable_type_set_post_categories
0
+ # test test_movable_type_set_post_categories
0
+ # this needs some work!! FIXME FIXME FIXME
0
+
0
+ c1 = MovableTypeStructs::PostCategory.new(:categoryId => 1, :categoryName => 'Home', :isPrimary => false)
0
+ c2 = MovableTypeStructs::PostCategory.new(:categoryId => 2, :categoryName => 'About', :isPrimary => false)
0
+ c3 = MovableTypeStructs::PostCategory.new(:categoryId => 9, :categoryName => 'Links', :isPrimary => false)
0
+
0
+ args = [ 1, 'quentin', 'test', [ c1, c2, c3 ]]
0
+
0
+ result = invoke_layered :mt, :setPostCategories, *args
0
+ assert_equal true, result
0
+
0
+ # see if the categories have been set
0
+ args = [ 1, 'quentin', 'test' ]
0
+
0
+ result = invoke_layered :mt, :getPostCategories, *args
0
+ assert_equal ["1","2","9"], result.collect { |c| c['categoryId'] }.sort
0
+
0
+ # set the categories back to the default
0
+ args = [ 1, 'quentin', 'test', [c1,c2] ]
0
+
0
+ result = invoke_layered :mt, :setPostCategories, *args
0
+ assert_equal true, result
0
+ end
0
+
0
+ def test_movable_type_supported_methods
0
+ # test test_movable_type_supported_methods
0
+ result = invoke_layered :mt, :supportedMethods
0
+
0
+ assert_instance_of Array, result
0
+ end
0
+
0
+ def test_movable_type_supported_text_filters
0
+ # test test_movable_type_supported_text_filters
0
+ result = invoke_layered :mt, :supportedTextFilters
0
+
0
+ assert_instance_of Array, result
0
+ end
0
+
0
+
0
+
0
   def test_meta_weblog_get_categories
0
     args = [ 1, 'quentin', 'test' ]
0
 
0
@@ -84,7 +143,7 @@ class BackendControllerTest < Test::Unit::TestCase
0
       result = invoke_layered :metaWeblog, :newPost, *args
0
       assert result
0
       new_post = Article.find(result)
0
-
0
+
0
       assert_equal "Posted via Test", new_post.title
0
       assert_equal article.body, new_post.body
0
       assert_equal "<p>body</p>", new_post.body_html
0
@@ -92,9 +151,57 @@ class BackendControllerTest < Test::Unit::TestCase
0
     end
0
   end
0
 
0
+ def test_meta_weblog_new_post_min
0
+ # This is going to test weather a post is correctly submited or not without the published_at field!
0
+ # See http://www.xmlrpc.com/metaWeblogApi#theStruct or
0
+ # See http://www.sixapart.com/developers/xmlrpc/metaweblog_api/metaweblognewpost.html for more information
0
+ assert_difference Article, :count do
0
+ article = Article.new
0
+ article.title = 'This is a title'
0
+ article.body = 'This is a post'
0
+
0
+ args = [ 1, 'quentin', 'test', MetaWeblogService.new(@controller).article_dto_from(article), 1 ]
0
+
0
+ result = invoke_layered :metaWeblog, :newPost, *args
0
+ assert result
0
+ new_post = Article.find(result)
0
+
0
+ assert_equal 'This is a post', new_post.body
0
+ assert_equal '<p>This is a post</p>', new_post.body_html
0
+ assert_equal 'This is a title', new_post.title
0
+ assert_equal :published, new_post.status
0
+ end
0
+ end
0
+
0
+ def test_meta_weblog_new_post_publish
0
+ # Test weather publish is set correctly on 1 and true to :published otherwise :pending
0
+ [{ :set => 1, :expect => :published },
0
+ { :set => 0, :expect => :pending },
0
+ { :set => true, :expect => :published},
0
+ { :set => false, :expect => :pending }].each do |c|
0
+ assert_difference Article, :count do
0
+ article = Article.new
0
+ article.title = 'This is a title'
0
+ article.body = 'This is a post'
0
+
0
+ args = [ 1, 'quentin', 'test', MetaWeblogService.new(@controller).article_dto_from(article), c[:set] ]
0
+
0
+ result = invoke_layered :metaWeblog, :newPost, *args
0
+ assert result
0
+ new_post = Article.find(result)
0
+
0
+ assert_equal 'This is a post', new_post.body
0
+ assert_equal '<p>This is a post</p>', new_post.body_html
0
+ assert_equal 'This is a title', new_post.title
0
+ assert_equal c[:expect], new_post.status
0
+ end
0
+
0
+ end
0
+ end
0
+
0
   def test_should_upload_file_via_meta_weblog_new_media_object
0
     now = Time.now.utc
0
- media_object = new_media_object
0
+ media_object = new_media_object
0
 
0
     args = [ 1, 'quentin', 'test', media_object ]
0
     result = invoke_layered :metaWeblog, :newMediaObject, *args
0
@@ -102,14 +209,14 @@ class BackendControllerTest < Test::Unit::TestCase
0
 
0
     assert_file_exists File.join(ASSET_PATH, now.year.to_s, now.month.to_s, now.day.to_s, media_object['name'])
0
   end
0
-
0
+
0
   def test_should_guess_content_type_for_gif
0
     media_object = new_media_object 'type' => nil, :name => 'filename.gif'
0
     assert_nil media_object['type']
0
-
0
+
0
     args = [ 1, 'quentin', 'test', media_object ]
0
     result = invoke_layered :metaWeblog, :newMediaObject, *args
0
-
0
+
0
     new_asset = Asset.find :first, :order => 'created_at DESC'
0
     assert_equal 'image/gif', new_asset.content_type
0
   end
0
@@ -117,10 +224,10 @@ class BackendControllerTest < Test::Unit::TestCase
0
   def test_should_guess_content_type_for_jpg
0
     media_object = new_media_object 'type' => nil
0
     assert_nil media_object['type']
0
-
0
+
0
     args = [ 1, 'quentin', 'test', media_object ]
0
     result = invoke_layered :metaWeblog, :newMediaObject, *args
0
-
0
+
0
     new_asset = Asset.find :first, :order => 'created_at DESC'
0
     assert_equal 'image/jpeg', new_asset.content_type
0
   end
0
@@ -136,9 +243,9 @@ class BackendControllerTest < Test::Unit::TestCase
0
     # This will be a little more useful with the upstream changes in [1093]
0
     assert_raise(XMLRPC::FaultException) { invoke_layered :metaWeblog, :getRecentPosts, *args }
0
   end
0
-
0
+
0
   protected
0
-
0
+
0
     def new_media_object(options = {})
0
       MetaWeblogStructs::MediaObject.new(options.reverse_merge!(
0
         'name' => Digest::SHA1.hexdigest("upload-test--#{Time.now}--") + ".jpg",
0
@@ -146,5 +253,5 @@ class BackendControllerTest < Test::Unit::TestCase
0
         'bits' => Base64.encode64(File.open(File.expand_path(RAILS_ROOT) + '/public/images/mephisto/shadow.png', 'rb') { |f| f.read })
0
       ))
0
     end
0
-
0
+
0
 end

Comments

    No one has commented yet.