Class MediaWiki::Gateway
In: lib/media_wiki/gateway.rb
Parent: Object

Methods

Constants

API_MAX_LIMIT = 500

Attributes

base_url  [R] 

Public Class methods

Set up a MediaWiki::Gateway for a given MediaWiki installation

url
Path to API of target MediaWiki (eg. "en.wikipedia.org/w/api.php")
loglevel
Log level to use (optional, defaults to Logger::WARN)

[Source]

    # File lib/media_wiki/gateway.rb, line 16
16:     def initialize(url, loglevel = Logger::WARN)
17:       @log = Logger.new(STDERR)
18:       @log.level = loglevel
19:       @wiki_url = url
20:       @headers = { "User-Agent" => "MediaWiki::Gateway/#{MediaWiki.version}" }
21:       @cookies = {}
22:     end

Public Instance methods

Get a list of pages that link to a target page

title
Link target page
filter
"all" links (default), "redirects" only, or "nonredirects" (plain links only)

Returns array of page titles (empty if no matches)

[Source]

     # File lib/media_wiki/gateway.rb, line 180
180:     def backlinks(title, filter = "all")
181:       titles = []
182:       blcontinue = nil
183:       begin
184:         form_data =
185:           {'action' => 'query',
186:           'list' => 'backlinks',
187:           'bltitle' => title,
188:           'blfilterredir' => filter,
189:           'bllimit' => API_MAX_LIMIT }
190:         form_data['blcontinue'] = blcontinue if blcontinue
191:         res = make_api_request(form_data)
192:         blcontinue = res.elements['query-continue'] ? res.elements['query-continue/backlinks'].attributes['blcontinue'] : nil
193:         titles += REXML::XPath.match(res, "//bl").map { |x| x.attributes["title"] }
194:       end while blcontinue
195:       titles
196:     end

Create a new page, or overwrite an existing one

title
Page title to create or overwrite, string
content
Content for the page, string
options
Hash of additional options

Options:

  • [overwrite] Allow overwriting existing pages
  • [summary] Edit summary for history, string
  • [token] Use this existing edit token instead requesting a new one (useful for bulk loads)

[Source]

     # File lib/media_wiki/gateway.rb, line 102
102:     def create(title, content, options={})
103:       form_data = {'action' => 'edit', 'title' => title, 'text' => content, 'summary' => (options[:summary] || ""), 'token' => get_token('edit', title)}
104:       form_data['createonly'] = "" unless options[:overwrite]
105:       make_api_request(form_data)
106:     end

Delete one page. (MediaWiki API does not support deleting multiple pages at a time.)

title
Title of page to delete

[Source]

     # File lib/media_wiki/gateway.rb, line 128
128:     def delete(title)
129:       form_data = {'action' => 'delete', 'title' => title, 'token' => get_token('delete', title)}
130:       make_api_request(form_data)
131:     end

Download file_name. Returns file contents. All options are passed to image_info however options[‘iiprop’] is forced to url. You can still set other options to control what file you want to download.

[Source]

     # File lib/media_wiki/gateway.rb, line 344
344:     def download(file_name, options={})
345:       options['iiprop'] = 'url'
346:   
347:       attributes = image_info(file_name, options)
348:       if attributes
349:         RestClient.get attributes['url']
350:       else
351:         nil
352:       end
353:     end

Exports a page or set of pages

page_titles
String or array of page titles to fetch

Returns MediaWiki XML dump

[Source]

     # File lib/media_wiki/gateway.rb, line 375
375:     def export(page_titles)
376:       form_data = {'action' => 'query', 'titles' => [page_titles].join('|'), 'export' => nil, 'exportnowrap' => nil}
377:       return make_api_request(form_data)
378:     end

Get a list of all installed (and registered) extensions

Returns array of extensions (name => version)

[Source]

     # File lib/media_wiki/gateway.rb, line 396
396:     def extensions
397:       form_data = { 'action' => 'query', 'meta' => 'siteinfo', 'siprop' => 'extensions' }
398:       res = make_api_request(form_data)
399:       REXML::XPath.match(res, "//ext").inject(Hash.new) do |extensions, extension|
400:         name = extension.attributes["name"] || ""
401:         extensions[name] = extension.attributes["version"]
402:         extensions
403:       end
404:     end

Fetch MediaWiki page in MediaWiki format

page_title
Page title to fetch

Returns content of page as string, nil if the page does not exist

[Source]

    # File lib/media_wiki/gateway.rb, line 45
45:     def get(page_title)
46:       form_data = {'action' => 'query', 'prop' => 'revisions', 'rvprop' => 'content', 'titles' => page_title}
47:       page = make_api_request(form_data).elements["query/pages/page"]
48:       if ! page or page.attributes["missing"]
49:         nil
50:       else
51:         page.elements["revisions/rev"].text || ""
52:       end
53:     end

Requests image info from MediaWiki. Follows redirects.

file_name_or_page_id should be either:

  • a file name (String) you want info about without File: prefix.
  • or a Fixnum page id you of the file.

options is Hash passed as query arguments. See www.mediawiki.org/wiki/API:Query_-_Properties#imageinfo_.2F_ii for more information.

options[‘iiprop’] should be either a string of properties joined by ’|’ or an Array (or more precisely something that responds to join).

Hash like object is returned where keys are image properties.

Example:

  mw.image_info(
    "Trooper.jpg", 'iiprop' => ['timestamp', 'user']
  ).each do |key, value|
    puts "#{key.inspect} => #{value.inspect}"
  end

Output:

  "timestamp" => "2009-10-31T12:59:11Z"
  "user" => "Valdas"

[Source]

     # File lib/media_wiki/gateway.rb, line 313
313:     def image_info(file_name_or_page_id, options={})
314:       options['iiprop'] = options['iiprop'].join('|') \
315:         if options['iiprop'].respond_to?(:join)
316:       form_data = options.merge(
317:         'action' => 'query',
318:         'prop' => 'imageinfo',
319:         'redirects' => true
320:       )
321: 
322:       case file_name_or_page_id
323:       when Fixnum
324:         form_data['pageids'] = file_name_or_page_id
325:       else
326:         form_data['titles'] = "File:#{file_name_or_page_id}"
327:       end
328: 
329:       xml = make_api_request(form_data)
330:       page = xml.elements["query/pages/page"]
331:       if ! page or page.attributes["missing"]
332:         nil
333:       elsif xml.elements["query/redirects/r"]
334:         # We're dealing with redirect here.
335:         image_info(page.attributes["pageid"].to_i, options)
336:       else
337:         page.elements["imageinfo/ii"].attributes
338:       end
339:     end

Imports a MediaWiki XML dump

xml
String or array of page names to fetch

Returns XML array <api><import><page/><page/>… <page revisions="1"> (or more) means successfully imported <page revisions="0"> means duplicate, not imported

[Source]

     # File lib/media_wiki/gateway.rb, line 362
362:     def import(xmlfile)
363:       form_data = { "action"  => "import",
364:         "xml"     => File.new(xmlfile),
365:         "token"   => get_token('import', 'Main Page'), # NB: dummy page name
366:         "format"  => 'xml' }
367:       make_api_request(form_data)
368:     end

Get a list of matching page titles in a namespace

key
Search key, matched as a prefix (^key.*). May contain or equal a namespace, defaults to main (namespace 0) if none given.

Returns array of page titles (empty if no matches)

[Source]

     # File lib/media_wiki/gateway.rb, line 154
154:     def list(key)
155:       titles = []
156:       apfrom = nil
157:       key, namespace = key.split(":", 2).reverse
158:       namespace = namespaces_by_prefix[namespace] || 0
159:       begin
160:         form_data =
161:           {'action' => 'query',
162:           'list' => 'allpages',
163:           'apfrom' => apfrom,
164:           'apprefix' => key,
165:           'aplimit' => API_MAX_LIMIT,
166:           'apnamespace' => namespace}
167:         res = make_api_request(form_data)
168:         apfrom = res.elements['query-continue'] ? res.elements['query-continue/allpages'].attributes['apfrom'] : nil
169:         titles += REXML::XPath.match(res, "//p").map { |x| x.attributes["title"] }
170:       end while apfrom
171:       titles
172:     end

Login to MediaWiki

username
Username
password
Password
domain
Domain for authentication plugin logins (eg. LDAP), optional — defaults to ‘local’ if not given

Throws error if login fails

[Source]

    # File lib/media_wiki/gateway.rb, line 33
33:     def login(username, password, domain = 'local')
34:       form_data = {'action' => 'login', 'lgname' => username, 'lgpassword' => password, 'lgdomain' => domain}
35:       make_api_request(form_data)
36:       @password = password
37:       @username = username
38:     end

Move a page to a new title

from
Old page name
to
New page name
options
Hash of additional options

Options:

  • [movesubpages] Move associated subpages
  • [movetalk] Move associated talkpages

[Source]

     # File lib/media_wiki/gateway.rb, line 117
117:     def move(from, to, options={})
118:       valid_options = %w(movesubpages movetalk)
119:       options.keys.each{|opt| raise ArgumentError.new("Unknown option '#{opt}'") unless valid_options.include?(opt.to_s)}
120:       
121:       form_data = options.merge({'action' => 'move', 'from' => from, 'to' => to, 'token' => get_token('edit', from)})
122:       make_api_request(form_data)
123:     end

Get a list of all known namespaces

Returns array of namespaces (name => id)

[Source]

     # File lib/media_wiki/gateway.rb, line 383
383:     def namespaces_by_prefix
384:       form_data = { 'action' => 'query', 'meta' => 'siteinfo', 'siprop' => 'namespaces' }
385:       res = make_api_request(form_data)
386:       REXML::XPath.match(res, "//ns").inject(Hash.new) do |namespaces, namespace|
387:         prefix = namespace.attributes["canonical"] || ""
388:         namespaces[prefix] = namespace.attributes["id"].to_i
389:         namespaces
390:       end
391:     end

Render a MediaWiki page as HTML

page_title
Page title to fetch
options
Hash of additional options

Options:

  • [linkbase] supply a String to prefix all internal (relative) links with. ’/wiki/’ is assumed to be the base of a relative link
  • [noeditsections] strips all edit-links if set to true
  • [noimages] strips all img tags from the rendered text if set to true

Returns rendered page as string, or nil if the page does not exist

[Source]

    # File lib/media_wiki/gateway.rb, line 66
66:     def render(page_title, options = {})
67:       form_data = {'action' => 'parse', 'page' => page_title}
68: 
69:       valid_options = %w(linkbase noeditsections noimages)
70:       # Check options
71:       options.keys.each{|opt| raise ArgumentError.new("Unknown option '#{opt}'") unless valid_options.include?(opt.to_s)}
72: 
73:       rendered = nil
74:       parsed = make_api_request(form_data).elements["parse"]
75:       if parsed.attributes["revid"] != '0'
76:         rendered = parsed.elements["text"].text.gsub(/<!--(.|\s)*?-->/, '')
77:         # OPTIMIZE: unifiy the keys in +options+ like symbolize_keys! but w/o
78:         if options["linkbase"] or options[:linkbase]
79:           linkbase = options["linkbase"] || options[:linkbase]
80:           rendered = rendered.gsub(/\shref="\/wiki\/([\w\(\)_\-\.%\d:,]*)"/, ' href="' + linkbase + '/wiki/\1"')
81:         end
82:         if options["noeditsections"] or options[:noeditsections]
83:           rendered = rendered.gsub(/<span class="editsection">\[.+\]<\/span>/, '')
84:         end
85:         if options["noimages"] or options[:noimages]
86:           rendered = rendered.gsub(/<img.*\/>/, '')
87:         end
88:       end
89:       rendered
90:     end

Get a list of pages with matching content in given namespaces

key
Search key
namespaces
Array of namespace names to search (defaults to NS_MAIN only)
limit
Max number of hits to return

Returns array of page titles (empty if no matches)

[Source]

     # File lib/media_wiki/gateway.rb, line 205
205:     def search(key, namespaces=nil, limit=10)
206:       titles = []
207:       form_data = { 'action' => 'query',
208:         'list' => 'search',
209:         'srwhat' => 'text',
210:         'srsearch' => key,
211:         'srlimit' => limit}
212:       if namespaces
213:         namespaces = [ namespaces ] unless namespaces.kind_of? Array
214:         form_data['srnamespace'] = namespaces.map! do |ns| namespaces_by_prefix[ns] end.join('|')
215:       end
216:       titles += REXML::XPath.match(make_api_request(form_data), "//p").map { |x| x.attributes["title"] }
217:     end

Execute Semantic Mediawiki query

query
Semantic Mediawiki query
params
Array of additional parameters or options, eg. mainlabel=Foo or ?Place (optional)

Returns result as an HTML string

[Source]

     # File lib/media_wiki/gateway.rb, line 412
412:     def semantic_query(query, params = [])
413:       params << "format=list"
414:       form_data = { 'action' => 'parse', 'prop' => 'text', 'text' => "{{#ask:#{query}|#{params.join('|')}}}" }
415:       xml = make_api_request(form_data)
416:       return xml.elements["parse/text"].text
417:     end

Undelete all revisions of one page.

title
Title of page to undelete

Returns number of revisions undeleted, or zero if nothing to undelete

[Source]

     # File lib/media_wiki/gateway.rb, line 138
138:     def undelete(title)
139:       token = get_undelete_token(title)
140:       if token
141:         form_data = {'action' => 'undelete', 'title' => title, 'token' => token }
142:         xml = make_api_request(form_data)
143:         xml.elements["undelete"].attributes["revisions"].to_i
144:       else
145:         0 # No revisions to undelete
146:       end
147:     end

Upload a file, or get the status of pending uploads. Several methods are available:

  • Upload file contents directly.
  • Have the MediaWiki server fetch a file from a URL, using the "url" parameter

Requires Mediawiki 1.16+

Arguments:

  • [path] Path to file to upload. Set to nil if uploading from URL.
  • [options] Hash of additional options

Note that queries using session keys must be done in the same login session as the query that originally returned the key (i.e. do not log out and then log back in).

Options:

  • ‘filename’ - Target filename (defaults to local name if not given), options[:target] is alias for this.
  • ‘comment’ - Upload comment. Also used as the initial page text for new files if "text" is not specified.
  • ‘text’ - Initial page text for new files
  • ‘watch’ - Watch the page
  • ‘ignorewarnings’ - Ignore any warnings
  • ‘url’ - Url to fetch the file from. Set path to nil if you want to use this.

Deprecated but still supported options:

  • :description - Description of this file. Used as ‘text’.
  • :target - Target filename, same as ‘filename’.
  • :summary - Edit summary for history. Used as ‘comment’. Also used as ‘text’ if neither it or :description is specified.

Examples:

  mw.upload('/path/to/local/file.jpg', 'filename' => "RemoteFile.jpg")
  mw.upload(nil, 'filename' => "RemoteFile2.jpg", 'url' => 'http://remote.com/server/file.jpg')

[Source]

     # File lib/media_wiki/gateway.rb, line 253
253:     def upload(path, options={})
254:       if options[:description]
255:         options['text'] = options[:description]
256:         options.delete(:description)
257:       end
258: 
259:       if options[:target]
260:         options['filename'] = options[:target]
261:         options.delete(:target)
262:       end
263: 
264:       if options[:summary]
265:         options['text'] ||= options[:summary]
266:         options['comment'] = options[:summary]
267:         options.delete(:summary)
268:       end
269: 
270:       options['comment'] ||= "Uploaded by MediaWiki::Gateway"
271:       options['file'] = File.new(path) if path
272:       full_name = path || options['url']
273:       options['filename'] ||= File.basename(full_name) if full_name
274: 
275:       raise ArgumentError.new(
276:         "One of the 'file', 'url' or 'sessionkey' options must be specified!"
277:       ) unless options['file'] || options['url'] || options['sessionkey']
278: 
279:       form_data = options.merge(
280:         'action' => 'upload',
281:         'token' => get_token('edit', options['filename'])
282:       )
283: 
284:       make_api_request(form_data)
285:     end

Private Instance methods

Get API XML response If there are errors, raise exception Otherwise return XML root

[Source]

     # File lib/media_wiki/gateway.rb, line 471
471:     def get_response(res)
472:       begin
473:         doc = REXML::Document.new(res).root
474:       rescue REXML::ParseException => e
475:         raise "Response is not XML.  Are you sure you are pointing to api.php?"
476:       end
477:       @log.debug("RES: #{doc}")
478:       raise "Response does not contain Mediawiki API XML: #{res}" unless [ "api", "mediawiki" ].include? doc.name
479:       if doc.elements["error"]
480:         code = doc.elements["error"].attributes["code"]
481:         info = doc.elements["error"].attributes["info"]
482:         raise "API error: code '#{code}', info '#{info}'"
483:       end
484:       doc
485:     end

Fetch token (type ‘delete’, ‘edit’, ‘import’)

[Source]

     # File lib/media_wiki/gateway.rb, line 422
422:     def get_token(type, page_titles)
423:       form_data = {'action' => 'query', 'prop' => 'info', 'intoken' => type, 'titles' => page_titles}
424:       res = make_api_request(form_data)
425:       token = res.elements["query/pages/page"].attributes[type + "token"]
426:       raise "User is not permitted to perform this operation: #{type}" if token.nil?
427:       token
428:     end

[Source]

     # File lib/media_wiki/gateway.rb, line 430
430:     def get_undelete_token(page_titles)
431:       form_data = {'action' => 'query', 'list' => 'deletedrevs', 'prop' => 'info', 'drprop' => 'token', 'titles' => page_titles}
432:       res = make_api_request(form_data)
433:       if res.elements["query/deletedrevs/page"]
434:         token = res.elements["query/deletedrevs/page"].attributes["token"]
435:         raise "User is not permitted to perform this operation: #{type}" if token.nil?
436:         token
437:       else
438:         nil
439:       end
440:     end

Make generic request to API

form_data
hash or string of attributes to post

Returns XML document

[Source]

     # File lib/media_wiki/gateway.rb, line 447
447:     def make_api_request(form_data)
448:       form_data['format'] = 'xml' if form_data.kind_of? Hash
449:       @log.debug("REQ: #{form_data.inspect}, #{@cookies.inspect}")
450:       RestClient.post(@wiki_url, form_data, @headers.merge({:cookies => @cookies})) do |response, &block| 
451:         # Check response for errors and return XML
452:         raise "API error, bad response: #{response}" unless response.code >= 200 and response.code < 300 
453:         doc = get_response(response.dup)
454:         if(form_data['action'] == 'login')
455:           login_result = doc.elements["login"].attributes['result']
456:           @cookies.merge!(response.cookies)
457:           case login_result
458:             when "Success" then # do nothing
459:             when "NeedToken" then make_api_request(form_data.merge('lgtoken' => doc.elements["login"].attributes["token"]))
460:             else raise "Login failed: " + login_result
461:           end
462:         end
463:         return doc
464:       end
465: 
466:     end

[Validate]