mediawiki-gateway 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|