mediawiki-gateway 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/media_wiki.rb +1 -1
- data/lib/media_wiki/gateway.rb +66 -31
- data/mediawiki-gateway.gemspec +3 -3
- data/spec/fake_media_wiki/app.rb +28 -20
- metadata +5 -5
data/lib/media_wiki.rb
CHANGED
data/lib/media_wiki/gateway.rb
CHANGED
@@ -6,10 +6,10 @@ require 'uri'
|
|
6
6
|
require 'active_support'
|
7
7
|
|
8
8
|
module MediaWiki
|
9
|
-
|
9
|
+
|
10
10
|
class Gateway
|
11
11
|
attr_reader :log
|
12
|
-
|
12
|
+
|
13
13
|
# Set up a MediaWiki::Gateway for a given MediaWiki installation
|
14
14
|
#
|
15
15
|
# [url] Path to API of target MediaWiki (eg. "http://en.wikipedia.org/w/api.php")
|
@@ -19,29 +19,31 @@ module MediaWiki
|
|
19
19
|
# [:bot] When set to true, executes API queries with the bot parameter (see http://www.mediawiki.org/wiki/API:Edit#Parameters). Defaults to false.
|
20
20
|
# [:ignorewarnings] Log API warnings and invalid page titles, instead throwing MediaWiki::APIError
|
21
21
|
# [:limit] Maximum number of results returned per search (see http://www.mediawiki.org/wiki/API:Query_-_Lists#Limits), defaults to the MediaWiki default of 500.
|
22
|
+
# [:logdevice] Log device to use. Defaults to STDERR
|
22
23
|
# [:loglevel] Log level to use, defaults to Logger::WARN. Set to Logger::DEBUG to dump every request and response to the log.
|
23
24
|
# [:maxlag] Maximum allowed server lag (see http://www.mediawiki.org/wiki/Manual:Maxlag_parameter), defaults to 5 seconds.
|
24
25
|
# [:retry_count] Number of times to try before giving up if MediaWiki returns 503 Service Unavailable, defaults to 3 (original request plus two retries).
|
25
26
|
# [:retry_delay] Seconds to wait before retry if MediaWiki returns 503 Service Unavailable, defaults to 10 seconds.
|
26
27
|
def initialize(url, options={})
|
27
28
|
default_options = {
|
29
|
+
:bot => false,
|
28
30
|
:limit => 500,
|
31
|
+
:logdevice => STDERR,
|
29
32
|
:loglevel => Logger::WARN,
|
30
33
|
:maxlag => 5,
|
31
34
|
:retry_count => 3,
|
32
35
|
:retry_delay => 10,
|
33
|
-
:bot => false
|
34
36
|
}
|
35
37
|
@options = default_options.merge(options)
|
36
38
|
@wiki_url = url
|
37
|
-
@log = Logger.new(
|
39
|
+
@log = Logger.new(@options[:logdevice])
|
38
40
|
@log.level = @options[:loglevel]
|
39
41
|
@headers = { "User-Agent" => "MediaWiki::Gateway/#{MediaWiki::VERSION}" }
|
40
42
|
@cookies = {}
|
41
43
|
end
|
42
|
-
|
44
|
+
|
43
45
|
attr_reader :base_url, :cookies
|
44
|
-
|
46
|
+
|
45
47
|
# Login to MediaWiki
|
46
48
|
#
|
47
49
|
# [username] Username
|
@@ -55,7 +57,7 @@ module MediaWiki
|
|
55
57
|
@password = password
|
56
58
|
@username = username
|
57
59
|
end
|
58
|
-
|
60
|
+
|
59
61
|
# Fetch MediaWiki page in MediaWiki format. Does not follow redirects.
|
60
62
|
#
|
61
63
|
# [page_title] Page title to fetch
|
@@ -91,7 +93,7 @@ module MediaWiki
|
|
91
93
|
# * [:linkbase] supply a String to prefix all internal (relative) links with. '/wiki/' is assumed to be the base of a relative link
|
92
94
|
# * [:noeditsections] strips all edit-links if set to +true+
|
93
95
|
# * [:noimages] strips all +img+ tags from the rendered text if set to +true+
|
94
|
-
#
|
96
|
+
#
|
95
97
|
# Returns rendered page as string, or nil if the page does not exist
|
96
98
|
def render(page_title, options = {})
|
97
99
|
form_data = {'action' => 'parse', 'page' => page_title}
|
@@ -118,7 +120,7 @@ module MediaWiki
|
|
118
120
|
end
|
119
121
|
rendered
|
120
122
|
end
|
121
|
-
|
123
|
+
|
122
124
|
# Create a new page, or overwrite an existing one
|
123
125
|
#
|
124
126
|
# [title] Page title to create or overwrite, string
|
@@ -220,11 +222,11 @@ module MediaWiki
|
|
220
222
|
def move(from, to, options={})
|
221
223
|
valid_options = %w(movesubpages movetalk noredirect reason watch unwatch)
|
222
224
|
options.keys.each{|opt| raise ArgumentError.new("Unknown option '#{opt}'") unless valid_options.include?(opt.to_s)}
|
223
|
-
|
225
|
+
|
224
226
|
form_data = options.merge({'action' => 'move', 'from' => from, 'to' => to, 'token' => get_token('move', from)})
|
225
227
|
make_api_request(form_data)
|
226
228
|
end
|
227
|
-
|
229
|
+
|
228
230
|
# Delete one page. (MediaWiki API does not support deleting multiple pages at a time.)
|
229
231
|
#
|
230
232
|
# [title] Title of page to delete
|
@@ -273,6 +275,28 @@ module MediaWiki
|
|
273
275
|
titles
|
274
276
|
end
|
275
277
|
|
278
|
+
# Get a list of pages that are members of a category
|
279
|
+
#
|
280
|
+
# [category] Name of the category
|
281
|
+
# [options] Optional hash of additional options. See http://www.mediawiki.org/wiki/API:Categorymembers
|
282
|
+
#
|
283
|
+
# Returns array of page titles (empty if no matches)
|
284
|
+
def category_members(category, options = {})
|
285
|
+
titles = []
|
286
|
+
apfrom = nil
|
287
|
+
begin
|
288
|
+
form_data = options.merge(
|
289
|
+
{'action' => 'query',
|
290
|
+
'list' => 'categorymembers',
|
291
|
+
'apfrom' => apfrom,
|
292
|
+
'cmtitle' => category,
|
293
|
+
'cmlimit' => @options[:limit]})
|
294
|
+
res, apfrom = make_api_request(form_data, '//query-continue/categorymembers/@apfrom')
|
295
|
+
titles += REXML::XPath.match(res, "//cm").map { |x| x.attributes["title"] }
|
296
|
+
end while apfrom
|
297
|
+
titles
|
298
|
+
end
|
299
|
+
|
276
300
|
# Get a list of pages that link to a target page
|
277
301
|
#
|
278
302
|
# [title] Link target page
|
@@ -326,11 +350,11 @@ module MediaWiki
|
|
326
350
|
titles
|
327
351
|
end
|
328
352
|
|
329
|
-
# Upload a file, or get the status of pending uploads. Several
|
353
|
+
# Upload a file, or get the status of pending uploads. Several
|
330
354
|
# methods are available:
|
331
355
|
#
|
332
356
|
# * Upload file contents directly.
|
333
|
-
# * Have the MediaWiki server fetch a file from a URL, using the
|
357
|
+
# * Have the MediaWiki server fetch a file from a URL, using the
|
334
358
|
# "url" parameter
|
335
359
|
#
|
336
360
|
# Requires Mediawiki 1.16+
|
@@ -338,8 +362,8 @@ module MediaWiki
|
|
338
362
|
# Arguments:
|
339
363
|
# * [path] Path to file to upload. Set to nil if uploading from URL.
|
340
364
|
# * [options] Hash of additional options
|
341
|
-
#
|
342
|
-
# Note that queries using session keys must be done in the same login
|
365
|
+
#
|
366
|
+
# Note that queries using session keys must be done in the same login
|
343
367
|
# session as the query that originally returned the key (i.e. do not
|
344
368
|
# log out and then log back in).
|
345
369
|
#
|
@@ -355,7 +379,7 @@ module MediaWiki
|
|
355
379
|
# * :description - Description of this file. Used as 'text'.
|
356
380
|
# * :target - Target filename, same as 'filename'.
|
357
381
|
# * :summary - Edit summary for history. Used as 'comment'. Also used as 'text' if neither it or :description is specified.
|
358
|
-
#
|
382
|
+
#
|
359
383
|
# Examples:
|
360
384
|
# mw.upload('/path/to/local/file.jpg', 'filename' => "RemoteFile.jpg")
|
361
385
|
# mw.upload(nil, 'filename' => "RemoteFile2.jpg", 'url' => 'http://remote.com/server/file.jpg')
|
@@ -404,7 +428,7 @@ module MediaWiki
|
|
404
428
|
page = make_api_request(form_data).first.elements["query/pages/page"]
|
405
429
|
!!(valid_page?(page) and page.attributes["redirect"])
|
406
430
|
end
|
407
|
-
|
431
|
+
|
408
432
|
# Requests image info from MediaWiki. Follows redirects.
|
409
433
|
#
|
410
434
|
# _file_name_or_page_id_ should be either:
|
@@ -461,12 +485,12 @@ module MediaWiki
|
|
461
485
|
end
|
462
486
|
end
|
463
487
|
|
464
|
-
# Download _file_name_ (without "File:" or "Image:" prefix). Returns file contents. All options are passed to
|
488
|
+
# Download _file_name_ (without "File:" or "Image:" prefix). Returns file contents. All options are passed to
|
465
489
|
# #image_info however options['iiprop'] is forced to url. You can still
|
466
490
|
# set other options to control what file you want to download.
|
467
491
|
def download(file_name, options={})
|
468
492
|
options['iiprop'] = 'url'
|
469
|
-
|
493
|
+
|
470
494
|
attributes = image_info(file_name, options)
|
471
495
|
if attributes
|
472
496
|
RestClient.get attributes['url']
|
@@ -479,7 +503,7 @@ module MediaWiki
|
|
479
503
|
#
|
480
504
|
# [xml] String or array of page names to fetch
|
481
505
|
#
|
482
|
-
# Returns XML array <api><import><page/><page/>...
|
506
|
+
# Returns XML array <api><import><page/><page/>...
|
483
507
|
# <page revisions="1"> (or more) means successfully imported
|
484
508
|
# <page revisions="0"> means duplicate, not imported
|
485
509
|
def import(xmlfile)
|
@@ -525,7 +549,7 @@ module MediaWiki
|
|
525
549
|
extensions
|
526
550
|
end
|
527
551
|
end
|
528
|
-
|
552
|
+
|
529
553
|
# Execute Semantic Mediawiki query
|
530
554
|
#
|
531
555
|
# [query] Semantic Mediawiki query
|
@@ -538,7 +562,7 @@ module MediaWiki
|
|
538
562
|
xml, dummy = make_api_request(form_data)
|
539
563
|
return xml.elements["parse/text"].text
|
540
564
|
end
|
541
|
-
|
565
|
+
|
542
566
|
# Set groups for a user
|
543
567
|
#
|
544
568
|
# [user] Username of user to modify
|
@@ -599,10 +623,10 @@ module MediaWiki
|
|
599
623
|
raise Unauthorized.new "User '#{@username}' is not permitted to perform this operation: get_userrights_token"
|
600
624
|
end
|
601
625
|
end
|
602
|
-
|
626
|
+
|
603
627
|
token
|
604
628
|
end
|
605
|
-
|
629
|
+
|
606
630
|
def userrights(user, token, groups_to_add, groups_to_remove, reason)
|
607
631
|
# groups_to_add and groups_to_remove can be a string or an array. Turn them into MediaWiki's pipe-delimited list format.
|
608
632
|
if groups_to_add.is_a? Array
|
@@ -620,7 +644,7 @@ module MediaWiki
|
|
620
644
|
res, dummy = make_api_request(form_data)
|
621
645
|
res
|
622
646
|
end
|
623
|
-
|
647
|
+
|
624
648
|
# Make generic request to API
|
625
649
|
#
|
626
650
|
# [form_data] hash or string of attributes to post
|
@@ -634,15 +658,14 @@ module MediaWiki
|
|
634
658
|
form_data['maxlag'] = @options[:maxlag]
|
635
659
|
form_data['bot']="1" if @options[:bot]
|
636
660
|
end
|
637
|
-
|
638
|
-
RestClient.post(@wiki_url, form_data, @headers.merge({:cookies => @cookies})) do |response, &block|
|
661
|
+
http_send(@wiki_url, form_data, @headers.merge({:cookies => @cookies})) do |response, &block|
|
639
662
|
if response.code == 503 and retry_count < @options[:retry_count]
|
640
663
|
log.warn("503 Service Unavailable: #{response.body}. Retry in #{@options[:retry_delay]} seconds.")
|
641
664
|
sleep @options[:retry_delay]
|
642
665
|
make_api_request(form_data, continue_xpath, retry_count + 1)
|
643
666
|
end
|
644
667
|
# Check response for errors and return XML
|
645
|
-
raise MediaWiki::Exception.new "Bad response: #{response}" unless response.code >= 200 and response.code < 300
|
668
|
+
raise MediaWiki::Exception.new "Bad response: #{response}" unless response.code >= 200 and response.code < 300
|
646
669
|
doc = get_response(response.dup)
|
647
670
|
if(form_data['action'] == 'login')
|
648
671
|
login_result = doc.elements["login"].attributes['result']
|
@@ -657,7 +680,19 @@ module MediaWiki
|
|
657
680
|
return [doc, continue]
|
658
681
|
end
|
659
682
|
end
|
660
|
-
|
683
|
+
|
684
|
+
# Execute the HTTP request using either GET or POST as appropriate
|
685
|
+
def http_send url, form_data, headers, &block
|
686
|
+
if form_data['action'] == 'query'
|
687
|
+
log.debug("GET: #{form_data.inspect}, #{@cookies.inspect}")
|
688
|
+
headers[:params] = form_data
|
689
|
+
RestClient.get url, headers, &block
|
690
|
+
else
|
691
|
+
log.debug("POST: #{form_data.inspect}, #{@cookies.inspect}")
|
692
|
+
RestClient.post url, form_data, headers, &block
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
661
696
|
# Get API XML response
|
662
697
|
# If there are errors or warnings, raise APIError
|
663
698
|
# Otherwise return XML root
|
@@ -680,7 +715,7 @@ module MediaWiki
|
|
680
715
|
end
|
681
716
|
doc
|
682
717
|
end
|
683
|
-
|
718
|
+
|
684
719
|
def valid_page?(page)
|
685
720
|
return false unless page
|
686
721
|
return false if page.attributes["missing"]
|
@@ -690,7 +725,7 @@ module MediaWiki
|
|
690
725
|
true
|
691
726
|
end
|
692
727
|
end
|
693
|
-
|
728
|
+
|
694
729
|
def warning(msg)
|
695
730
|
if @options[:ignorewarnings]
|
696
731
|
log.warn(msg)
|
data/mediawiki-gateway.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "mediawiki-gateway"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jani Patokallio"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-06-01"
|
13
13
|
s.description = ""
|
14
14
|
s.email = "jpatokal@iki.fi"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -46,7 +46,7 @@ Gem::Specification.new do |s|
|
|
46
46
|
]
|
47
47
|
s.homepage = "http://github.com/jpatokal/mediawiki-gateway"
|
48
48
|
s.require_paths = ["lib"]
|
49
|
-
s.rubygems_version = "1.8.
|
49
|
+
s.rubygems_version = "1.8.24"
|
50
50
|
s.summary = "Connect to the mediawiki API"
|
51
51
|
|
52
52
|
if s.respond_to? :specification_version then
|
data/spec/fake_media_wiki/app.rb
CHANGED
@@ -8,7 +8,7 @@ require 'spec/fake_media_wiki/query_handling'
|
|
8
8
|
module FakeMediaWiki
|
9
9
|
|
10
10
|
class App < Sinatra::Base
|
11
|
-
|
11
|
+
|
12
12
|
set :show_exceptions, false
|
13
13
|
set :environment, :development
|
14
14
|
|
@@ -16,7 +16,7 @@ module FakeMediaWiki
|
|
16
16
|
reset
|
17
17
|
super
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def reset
|
21
21
|
@sequence_id = 0
|
22
22
|
|
@@ -24,7 +24,7 @@ module FakeMediaWiki
|
|
24
24
|
add_user('atlasmw', 'wombat', 'local', true)
|
25
25
|
add_user('nonadmin', 'sekrit', 'local', false)
|
26
26
|
add_user('ldapuser', 'ldappass', 'ldapdomain', false)
|
27
|
-
|
27
|
+
|
28
28
|
@pages = ApiPages.new
|
29
29
|
@pages.add('Main Page', 'Content')
|
30
30
|
@pages.add('Main 2', 'Content')
|
@@ -37,7 +37,7 @@ module FakeMediaWiki
|
|
37
37
|
@pages.add('Redirect', '#REDIRECT', true)
|
38
38
|
|
39
39
|
@extensions = { 'FooExtension' => 'r1', 'BarExtension' => 'r2' }
|
40
|
-
|
40
|
+
|
41
41
|
@logged_in_users = []
|
42
42
|
end
|
43
43
|
|
@@ -54,14 +54,22 @@ module FakeMediaWiki
|
|
54
54
|
:is_admin => is_admin
|
55
55
|
}
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
def logged_in(username)
|
59
59
|
@logged_in_users.include?(username)
|
60
60
|
end
|
61
61
|
|
62
|
+
get "/w/api.php" do
|
63
|
+
handle_request if params[:action] == 'query'
|
64
|
+
end
|
65
|
+
|
62
66
|
post "/w/api.php" do
|
67
|
+
handle_request
|
68
|
+
end
|
69
|
+
|
70
|
+
def handle_request
|
63
71
|
begin
|
64
|
-
halt(503, "Maxlag exceeded") if params[:maxlag].to_i < 0
|
72
|
+
halt(503, "Maxlag exceeded") if params[:maxlag].to_i < 0
|
65
73
|
|
66
74
|
@token = ApiToken.new(params)
|
67
75
|
action = params[:action]
|
@@ -69,7 +77,7 @@ module FakeMediaWiki
|
|
69
77
|
content_type "application/xml"
|
70
78
|
return send(action)
|
71
79
|
end
|
72
|
-
|
80
|
+
|
73
81
|
halt(404, "Page not found")
|
74
82
|
rescue FakeMediaWiki::ApiError => e
|
75
83
|
return api_error_response(e.code, e.message)
|
@@ -127,24 +135,24 @@ module FakeMediaWiki
|
|
127
135
|
|
128
136
|
def undelete
|
129
137
|
@token.validate_admin
|
130
|
-
|
138
|
+
|
131
139
|
title = params[:title]
|
132
140
|
revisions = @pages.undelete(title)
|
133
141
|
api_response do |_|
|
134
142
|
_.undelete(nil, {:title => title, :revisions => revisions})
|
135
143
|
end
|
136
144
|
end
|
137
|
-
|
145
|
+
|
138
146
|
def upload
|
139
147
|
@token.validate
|
140
|
-
|
148
|
+
|
141
149
|
filename = params[:filename]
|
142
150
|
@pages.add(filename, params[:file])
|
143
151
|
api_response do |_|
|
144
152
|
_.upload(nil, {:filename => filename, :result => "Success"})
|
145
153
|
end
|
146
154
|
end
|
147
|
-
|
155
|
+
|
148
156
|
def parse
|
149
157
|
page = @pages.get(params[:page])
|
150
158
|
api_response do |_|
|
@@ -161,7 +169,7 @@ module FakeMediaWiki
|
|
161
169
|
end
|
162
170
|
end
|
163
171
|
end
|
164
|
-
|
172
|
+
|
165
173
|
include QueryHandling
|
166
174
|
|
167
175
|
def api_response
|
@@ -203,7 +211,7 @@ module FakeMediaWiki
|
|
203
211
|
_.page(nil, attributes)
|
204
212
|
end
|
205
213
|
end
|
206
|
-
|
214
|
+
|
207
215
|
def get_revisions
|
208
216
|
query_pages do |_, title, page|
|
209
217
|
if page.nil?
|
@@ -224,11 +232,11 @@ module FakeMediaWiki
|
|
224
232
|
username = request.cookies['login']
|
225
233
|
@users[username] if logged_in(username)
|
226
234
|
end
|
227
|
-
|
235
|
+
|
228
236
|
def requested_page_titles
|
229
237
|
params[:titles].split("|")
|
230
238
|
end
|
231
|
-
|
239
|
+
|
232
240
|
def get_token
|
233
241
|
token_str = @token.request(user)
|
234
242
|
query_pages do |_, title, page|
|
@@ -255,9 +263,9 @@ module FakeMediaWiki
|
|
255
263
|
def get_userrights_token(username)
|
256
264
|
@token.set_type 'userrights'
|
257
265
|
token_str = @token.request(user)
|
258
|
-
|
266
|
+
|
259
267
|
user_to_manage = @users[username]
|
260
|
-
|
268
|
+
|
261
269
|
if user_to_manage
|
262
270
|
api_response do |_|
|
263
271
|
_.query do
|
@@ -271,7 +279,7 @@ module FakeMediaWiki
|
|
271
279
|
_.error(nil, { :code => 'nosuchuser', :info => "The user '#{params[:ususer].to_s}' does not exist"} )
|
272
280
|
end
|
273
281
|
end
|
274
|
-
end
|
282
|
+
end
|
275
283
|
|
276
284
|
def login
|
277
285
|
user = @users[params[:lgname]]
|
@@ -312,11 +320,11 @@ module FakeMediaWiki
|
|
312
320
|
end
|
313
321
|
|
314
322
|
class WikiPage
|
315
|
-
|
323
|
+
|
316
324
|
def initialize(options={})
|
317
325
|
options.each { |k, v| send("#{k}=", v) }
|
318
326
|
end
|
319
|
-
|
327
|
+
|
320
328
|
attr_accessor :content, :author
|
321
329
|
|
322
330
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mediawiki-gateway
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 4
|
9
8
|
- 5
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jani Patokallio
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-06-01 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rest-client
|
@@ -225,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
225
|
requirements: []
|
226
226
|
|
227
227
|
rubyforge_project:
|
228
|
-
rubygems_version: 1.8.
|
228
|
+
rubygems_version: 1.8.24
|
229
229
|
signing_key:
|
230
230
|
specification_version: 3
|
231
231
|
summary: Connect to the mediawiki API
|