lib/media_wiki/gateway.rb in mediawiki-gateway-0.6.0 vs lib/media_wiki/gateway.rb in mediawiki-gateway-0.6.1
- old
+ new
@@ -12,21 +12,22 @@
# Set up a MediaWiki::Gateway for a given MediaWiki installation
#
# [url] Path to API of target MediaWiki (eg. "http://en.wikipedia.org/w/api.php")
# [options] Hash of options
+ # [http_options] Hash of options for RestClient::Request (via http_send)
#
# Options:
# [:bot] When set to true, executes API queries with the bot parameter (see http://www.mediawiki.org/wiki/API:Edit#Parameters). Defaults to false.
# [:ignorewarnings] Log API warnings and invalid page titles, instead throwing MediaWiki::APIError
# [: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.
# [:logdevice] Log device to use. Defaults to STDERR
# [:loglevel] Log level to use, defaults to Logger::WARN. Set to Logger::DEBUG to dump every request and response to the log.
# [:maxlag] Maximum allowed server lag (see http://www.mediawiki.org/wiki/Manual:Maxlag_parameter), defaults to 5 seconds.
# [:retry_count] Number of times to try before giving up if MediaWiki returns 503 Service Unavailable, defaults to 3 (original request plus two retries).
# [:retry_delay] Seconds to wait before retry if MediaWiki returns 503 Service Unavailable, defaults to 10 seconds.
- def initialize(url, options={})
+ def initialize(url, options={}, http_options={})
default_options = {
:bot => false,
:limit => 500,
:logdevice => STDERR,
:loglevel => Logger::WARN,
@@ -34,10 +35,11 @@
:retry_count => 3,
:retry_delay => 10,
:max_results => 500
}
@options = default_options.merge(options)
+ @http_options = http_options
@wiki_url = url
@log = Logger.new(@options[:logdevice])
@log.level = @options[:loglevel]
@headers = { "User-Agent" => "MediaWiki::Gateway/#{MediaWiki::VERSION}", "Accept-Encoding" => "gzip" }
@cookies = {}
@@ -48,43 +50,58 @@
# Login to MediaWiki
#
# [username] Username
# [password] Password
# [domain] Domain for authentication plugin logins (eg. LDAP), optional -- defaults to 'local' if not given
+ # [options] Hash of additional options
#
# Throws MediaWiki::Unauthorized if login fails
- def login(username, password, domain = 'local')
- form_data = {'action' => 'login', 'lgname' => username, 'lgpassword' => password, 'lgdomain' => domain}
- make_api_request(form_data)
+ def login(username, password, domain = 'local', options = {})
+ make_api_request(options.merge(
+ 'action' => 'login',
+ 'lgname' => username,
+ 'lgpassword' => password,
+ 'lgdomain' => domain
+ ))
+
@password = password
@username = username
end
# Fetch MediaWiki page in MediaWiki format. Does not follow redirects.
#
# [page_title] Page title to fetch
+ # [options] Hash of additional options
#
# Returns content of page as string, nil if the page does not exist.
- def get(page_title)
- form_data = {'action' => 'query', 'prop' => 'revisions', 'rvprop' => 'content', 'titles' => page_title}
- page = make_api_request(form_data).first.elements["query/pages/page"]
- if valid_page? page
- page.elements["revisions/rev"].text || ""
- end
+ def get(page_title, options = {})
+ page = make_api_request(options.merge(
+ 'action' => 'query',
+ 'prop' => 'revisions',
+ 'rvprop' => 'content',
+ 'titles' => page_title
+ )).first.elements['query/pages/page']
+
+ page.elements['revisions/rev'].text || '' if valid_page?(page)
end
# Fetch latest revision ID of a MediaWiki page. Does not follow redirects.
#
# [page_title] Page title to fetch
+ # [options] Hash of additional options
#
# Returns revision ID as a string, nil if the page does not exist.
- def revision(page_title)
- form_data = {'action' => 'query', 'prop' => 'revisions', 'rvprop' => 'ids', 'rvlimit' => 1, 'titles' => page_title}
- page = make_api_request(form_data).first.elements["query/pages/page"]
- if valid_page? page
- page.elements["revisions/rev"].attributes["revid"]
- end
+ def revision(page_title, options = {})
+ page = make_api_request(options.merge(
+ 'action' => 'query',
+ 'prop' => 'revisions',
+ 'rvprop' => 'ids',
+ 'rvlimit' => 1,
+ 'titles' => page_title
+ )).first.elements['query/pages/page']
+
+ page.elements['revisions/rev'].attributes['revid'] if valid_page?(page)
end
# Render a MediaWiki page as HTML
#
# [page_title] Page title to fetch
@@ -108,11 +125,11 @@
if parsed.attributes["revid"] != '0'
rendered = parsed.elements["text"].text.gsub(/<!--(.|\s)*?-->/, '')
# OPTIMIZE: unifiy the keys in +options+ like symbolize_keys! but w/o
if options["linkbase"] or options[:linkbase]
linkbase = options["linkbase"] || options[:linkbase]
- rendered = rendered.gsub(/\shref="\/wiki\/([\w\(\)_\-\.%\d:,]*)"/, ' href="' + linkbase + '/wiki/\1"')
+ rendered = rendered.gsub(/\shref="\/wiki\/([\w\(\)\-\.%:,]*)"/, ' href="' + linkbase + '/wiki/\1"')
end
if options["noeditsections"] or options[:noeditsections]
rendered = rendered.gsub(/<span class="editsection">\[.+\]<\/span>/, '')
end
if options["noimages"] or options[:noimages]
@@ -238,25 +255,32 @@
end
# Delete one page. (MediaWiki API does not support deleting multiple pages at a time.)
#
# [title] Title of page to delete
- def delete(title)
- form_data = {'action' => 'delete', 'title' => title, 'token' => get_token('delete', title)}
- make_api_request(form_data)
+ # [options] Hash of additional options
+ def delete(title, options = {})
+ make_api_request(options.merge(
+ 'action' => 'delete',
+ 'title' => title,
+ 'token' => get_token('delete', title)
+ ))
end
# Undelete all revisions of one page.
#
# [title] Title of page to undelete
+ # [options] Hash of additional options
#
# Returns number of revisions undeleted, or zero if nothing to undelete
- def undelete(title)
- token = get_undelete_token(title)
- if token
- form_data = {'action' => 'undelete', 'title' => title, 'token' => token }
- make_api_request(form_data).first.elements["undelete"].attributes["revisions"].to_i
+ def undelete(title, options = {})
+ if token = get_undelete_token(title)
+ make_api_request(options.merge(
+ 'action' => 'undelete',
+ 'title' => title,
+ 'token' => token
+ )).first.elements['undelete'].attributes['revisions'].to_i
else
0 # No revisions to undelete
end
end
@@ -265,123 +289,94 @@
# [key] Search key, matched as a prefix (^key.*). May contain or equal a namespace, defaults to main (namespace 0) if none given.
# [options] Optional hash of additional options, eg. { 'apfilterredir' => 'nonredirects' }. See http://www.mediawiki.org/wiki/API:Allpages
#
# Returns array of page titles (empty if no matches)
def list(key, options = {})
- titles = []
- apfrom = nil
- key, namespace = key.split(":", 2).reverse
+ key, namespace = key.split(':', 2).reverse
namespace = namespaces_by_prefix[namespace] || 0
- begin
- form_data = options.merge(
- {'action' => 'query',
- 'list' => 'allpages',
- 'apfrom' => apfrom,
- 'apprefix' => key,
- 'aplimit' => @options[:limit],
- 'apnamespace' => namespace})
- res, apfrom = make_api_request(form_data, '//query-continue/allpages/@apfrom')
- titles += REXML::XPath.match(res, "//p").map { |x| x.attributes["title"] }
- end while apfrom
- titles
+
+ iterate_query('allpages', '//p', 'title', 'apfrom', options.merge(
+ 'list' => 'allpages',
+ 'apprefix' => key,
+ 'apnamespace' => namespace,
+ 'aplimit' => @options[:limit]
+ ))
end
# Get a list of pages that are members of a category
#
# [category] Name of the category
# [options] Optional hash of additional options. See http://www.mediawiki.org/wiki/API:Categorymembers
#
# Returns array of page titles (empty if no matches)
def category_members(category, options = {})
- titles = []
- apfrom = nil
- begin
- form_data = options.merge(
- {'action' => 'query',
- 'list' => 'categorymembers',
- 'apfrom' => apfrom,
- 'cmtitle' => category,
- 'cmlimit' => @options[:limit]})
- res, apfrom = make_api_request(form_data, '//query-continue/categorymembers/@apfrom')
- titles += REXML::XPath.match(res, "//cm").map { |x| x.attributes["title"] }
- end while apfrom
- titles
+ iterate_query('categorymembers', '//cm', 'title', 'cmcontinue', options.merge(
+ 'cmtitle' => category,
+ 'cmlimit' => @options[:limit]
+ ))
end
# 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)
+ # [options] Hash of additional options
#
# Returns array of page titles (empty if no matches)
- def backlinks(title, filter = "all")
- titles = []
- blcontinue = nil
- begin
- form_data =
- {'action' => 'query',
- 'list' => 'backlinks',
- 'bltitle' => title,
- 'blfilterredir' => filter,
- 'bllimit' => @options[:limit] }
- form_data['blcontinue'] = blcontinue if blcontinue
- res, blcontinue = make_api_request(form_data, '//query-continue/backlinks/@blcontinue')
- titles += REXML::XPath.match(res, "//bl").map { |x| x.attributes["title"] }
- end while blcontinue
- titles
+ def backlinks(title, filter = 'all', options = {})
+ iterate_query('backlinks', '//bl', 'title', 'blcontinue', options.merge(
+ 'bltitle' => title,
+ 'blfilterredir' => filter,
+ 'bllimit' => @options[:limit]
+ ))
end
# Get a list of pages with matching content in given namespaces
#
# [key] Search key
# [namespaces] Array of namespace names to search (defaults to main only)
# [limit] Maximum number of hits to ask for (defaults to 500; note that Wikimedia Foundation wikis allow only 50 for normal users)
# [max_results] Maximum total number of results to return
+ # [options] Hash of additional options
#
# Returns array of page titles (empty if no matches)
- def search(key, namespaces=nil, limit=@options[:limit], max_results=@options[:max_results])
+ def search(key, namespaces = nil, limit = @options[:limit], max_results = @options[:max_results], options = {})
titles = []
offset = 0
- in_progress = true
- form_data = { 'action' => 'query',
- 'list' => 'search',
- 'srwhat' => 'text',
+ form_data = options.merge(
+ 'action' => 'query',
+ 'list' => 'search',
+ 'srwhat' => 'text',
'srsearch' => key,
- 'srlimit' => limit
- }
+ 'srlimit' => limit
+ )
+
if namespaces
namespaces = [ namespaces ] unless namespaces.kind_of? Array
form_data['srnamespace'] = namespaces.map! do |ns| namespaces_by_prefix[ns] end.join('|')
end
+
begin
form_data['sroffset'] = offset if offset
form_data['srlimit'] = [limit, max_results - offset.to_i].min
res, offset = make_api_request(form_data, '//query-continue/search/@sroffset')
titles += REXML::XPath.match(res, "//p").map { |x| x.attributes["title"] }
end while offset && offset.to_i < max_results.to_i
+
titles
end
# Get a list of users
#
# [options] Optional hash of options, eg. { 'augroup' => 'sysop' }. See http://www.mediawiki.org/wiki/API:Allusers
#
# Returns array of user names (empty if no matches)
def users(options = {})
- names = []
- aufrom = nil
- begin
- form_data = options.merge(
- {'action' => 'query',
- 'list' => 'allusers',
- 'aufrom' => aufrom,
- 'aulimit' => @options[:limit]})
- res, aufrom = make_api_request(form_data, '//query-continue/allusers/@aufrom')
- names += REXML::XPath.match(res, "//u").map { |x| x.attributes["name"] }
- end while aufrom
- names
+ iterate_query('allusers', '//u', 'name', 'aufrom', options.merge(
+ 'aulimit' => @options[:limit]
+ ))
end
# Get user contributions
#
# user: The user name
@@ -389,24 +384,20 @@
# [options] Optional hash of options, eg. { 'ucnamespace' => 4 }. See http://www.mediawiki.org/wiki/API:Usercontribs
#
# Returns array of hashes containing the "item" attributes defined here: http://www.mediawiki.org/wiki/API:Usercontribs
def contributions(user, count = nil, options = {})
result = []
- ucstart = options[:ucstart] or nil
- begin
- limit = [count, @options[:limit]].compact.min
- form_data = options.merge(
- {'action' => 'query',
- 'list' => 'usercontribs',
- 'ucuser' => user,
- 'ucstart' => ucstart,
- 'uclimit' => limit})
- res, ucstart = make_api_request(form_data, '//query-continue/usercontribs/@ucstart')
- result += REXML::XPath.match(res, "//item").map { |x| x.attributes.inject({}) { |hash, data| hash[data.first] = data.last; hash } }
- break if count and result.size >= count
- end while ucstart
- result
+
+ iterate_query('usercontribs', '//item', nil, 'uccontinue', options.merge(
+ 'ucuser' => user,
+ 'uclimit' => @options[:limit]
+ )) { |element|
+ result << hash = {}
+ element.attributes.each { |key, value| hash[key] = value }
+ }
+
+ count ? result.take(count) : result
end
# Upload a file, or get the status of pending uploads. Several
# methods are available:
#
@@ -488,53 +479,66 @@
# Get image list for given article[s]. Follows redirects.
#
# _article_or_pageid_ is the title or pageid of a single article
# _imlimit_ is the maximum number of images to return (defaults to 200)
+ # _options_ is the hash of additional options
#
# Example:
# images = mw.images('Gaborone')
# _images_ would contain ['File:Gaborone at night.jpg', 'File:Gaborone2.png', ...]
+ def images(article_or_pageid, imlimit = 200, options = {})
+ form_data = options.merge(
+ 'action' => 'query',
+ 'prop' => 'images',
+ 'imlimit' => imlimit,
+ 'redirects' => true
+ )
- def images(article_or_pageid, imlimit = 200)
- form_data = {'action' => 'query', 'prop' => 'images', 'imlimit' => imlimit, 'redirects' => true}
case article_or_pageid
when Fixnum
form_data['pageids'] = article_or_pageid
else
form_data['titles'] = article_or_pageid
end
- xml, dummy = make_api_request(form_data)
+ xml, _ = make_api_request(form_data)
page = xml.elements["query/pages/page"]
if valid_page? page
if xml.elements["query/redirects/r"]
# We're dealing with redirect here.
images(page.attributes["pageid"].to_i, imlimit)
else
- imgs = REXML::XPath.match(page, "images/im").map { |x| x.attributes["title"] }
+ REXML::XPath.match(page, "images/im").map { |x| x.attributes["title"] }
end
else
nil
end
end
# Get list of interlanguage links for given article[s]. Follows redirects. Returns a hash like { 'id' => 'Yerusalem', 'en' => 'Jerusalem', ... }
#
# _article_or_pageid_ is the title or pageid of a single article
# _lllimit_ is the maximum number of langlinks to return (defaults to 500, the maximum)
+ # _options_ is the hash of additional options
#
# Example:
# langlinks = mw.langlinks('Jerusalem')
- def langlinks(article_or_pageid, lllimit = 500)
- form_data = {'action' => 'query', 'prop' => 'langlinks', 'lllimit' => lllimit, 'redirects' => true}
+ def langlinks(article_or_pageid, lllimit = 500, options = {})
+ form_data = options.merge(
+ 'action' => 'query',
+ 'prop' => 'langlinks',
+ 'lllimit' => lllimit,
+ 'redirects' => true
+ )
+
case article_or_pageid
when Fixnum
form_data['pageids'] = article_or_pageid
else
form_data['titles'] = article_or_pageid
end
- xml, dummy = make_api_request(form_data)
+ xml, _ = make_api_request(form_data)
page = xml.elements["query/pages/page"]
if valid_page? page
if xml.elements["query/redirects/r"]
# We're dealing with the redirect here.
langlinks(page.attributes["pageid"].to_i, lllimit)
@@ -602,11 +606,11 @@
form_data['pageids'] = file_name_or_page_id
else
form_data['titles'] = "File:#{file_name_or_page_id}"
end
- xml, dummy = make_api_request(form_data)
+ xml, _ = make_api_request(form_data)
page = xml.elements["query/pages/page"]
if valid_page? page
if xml.elements["query/redirects/r"]
# We're dealing with redirect here.
image_info(page.attributes["pageid"].to_i, options)
@@ -633,51 +637,70 @@
end
# Imports a MediaWiki XML dump
#
# [xml] String or array of page names to fetch
+ # [options] Hash of additional options
#
# Returns XML array <api><import><page/><page/>...
# <page revisions="1"> (or more) means successfully imported
# <page revisions="0"> means duplicate, not imported
- def import(xmlfile)
- form_data = { "action" => "import",
- "xml" => File.new(xmlfile),
- "token" => get_token('import', 'Main Page'), # NB: dummy page name
- "format" => 'xml' }
- make_api_request(form_data)
+ def import(xmlfile, options = {})
+ make_api_request(options.merge(
+ 'action' => 'import',
+ 'xml' => File.new(xmlfile),
+ 'token' => get_token('import', 'Main Page'), # NB: dummy page name
+ 'format' => 'xml'
+ ))
end
# Exports a page or set of pages
#
# [page_titles] String or array of page titles to fetch
+ # [options] Hash of additional options
#
# Returns MediaWiki XML dump
- def export(page_titles)
- form_data = {'action' => 'query', 'titles' => [page_titles].join('|'), 'export' => nil, 'exportnowrap' => nil}
- make_api_request(form_data).first
+ def export(page_titles, options = {})
+ make_api_request(options.merge(
+ 'action' => 'query',
+ 'titles' => Array(page_titles).join('|'),
+ 'export' => nil,
+ 'exportnowrap' => nil
+ )).first
end
# Get a list of all known namespaces
#
+ # [options] Hash of additional options
+ #
# Returns array of namespaces (name => id)
- def namespaces_by_prefix
- form_data = { 'action' => 'query', 'meta' => 'siteinfo', 'siprop' => 'namespaces' }
- res = make_api_request(form_data)
+ def namespaces_by_prefix(options = {})
+ res = make_api_request(options.merge(
+ 'action' => 'query',
+ 'meta' => 'siteinfo',
+ 'siprop' => 'namespaces'
+ )).first
+
REXML::XPath.match(res, "//ns").inject(Hash.new) do |namespaces, namespace|
prefix = namespace.attributes["canonical"] || ""
namespaces[prefix] = namespace.attributes["id"].to_i
namespaces
end
end
# Get a list of all installed (and registered) extensions
#
+ # [options] Hash of additional options
+ #
# Returns array of extensions (name => version)
- def extensions
- form_data = { 'action' => 'query', 'meta' => 'siteinfo', 'siprop' => 'extensions' }
- res = make_api_request(form_data)
+ def extensions(options = {})
+ res = make_api_request(options.merge(
+ 'action' => 'query',
+ 'meta' => 'siteinfo',
+ 'siprop' => 'extensions'
+ )).first
+
REXML::XPath.match(res, "//ext").inject(Hash.new) do |extensions, extension|
name = extension.attributes["name"] || ""
extensions[name] = extension.attributes["version"]
extensions
end
@@ -686,59 +709,70 @@
# Sends e-mail to a user
#
# [user] Username to send mail to (name only: eg. 'Bob', not 'User:Bob')
# [subject] Subject of message
# [content] Content of message
+ # [options] Hash of additional options
#
# Will raise a 'noemail' APIError if the target user does not have a confirmed email address, see http://www.mediawiki.org/wiki/API:E-mail for details.
- def email_user(user, subject, text)
- form_data = { 'action' => 'emailuser', 'target' => user, 'subject' => subject, 'text' => text, 'token' => get_token('email', "User:" + user) }
- res, dummy = make_api_request(form_data)
+ def email_user(user, subject, text, options = {})
+ res = make_api_request(options.merge(
+ 'action' => 'emailuser',
+ 'target' => user,
+ 'subject' => subject,
+ 'text' => text,
+ 'token' => get_token('email', "User:#{user}")
+ )).first
+
res.elements['emailuser'].attributes['result'] == 'Success'
end
# Execute Semantic Mediawiki query
#
# [query] Semantic Mediawiki query
# [params] Array of additional parameters or options, eg. mainlabel=Foo or ?Place (optional)
+ # [options] Hash of additional options
#
# Returns result as an HTML string
- def semantic_query(query, params = [])
- if extensions.include? 'Semantic MediaWiki'
- smw_version = extensions['Semantic MediaWiki'].to_f
- if smw_version >= 1.7
- form_data = { 'action' => 'ask', 'query' => "#{query}|#{params.join('|')}"}
- xml, dummy = make_api_request(form_data)
- return xml
- else
- params << "format=list"
- form_data = { 'action' => 'parse', 'prop' => 'text', 'text' => "{{#ask:#{query}|#{params.join('|')}}}" }
- xml, dummy = make_api_request(form_data)
- return xml.elements["parse/text"].text
- end
- else
- raise MediaWiki::Exception.new "Semantic MediaWiki extension not installed."
- end
+ def semantic_query(query, params = [], options = {})
+ unless smw_version = extensions['Semantic MediaWiki']
+ raise MediaWiki::Exception, 'Semantic MediaWiki extension not installed.'
+ end
+
+ if smw_version.to_f >= 1.7
+ make_api_request(options.merge(
+ 'action' => 'ask',
+ 'query' => "#{query}|#{params.join('|')}"
+ )).first
+ else
+ make_api_request(options.merge(
+ 'action' => 'parse',
+ 'prop' => 'text',
+ 'text' => "{{#ask:#{query}|#{params.push('format=list').join('|')}}}"
+ )).first.elements['parse/text'].text
+ end
end
# Create a new account
#
# [options] is +Hash+ passed as query arguments. See https://www.mediawiki.org/wiki/API:Account_creation#Parameters for more information.
def create_account(options)
- form_data = options.merge({ 'action' => 'createaccount' })
- res, dummy = make_api_request(form_data)
- res
+ make_api_request(options.merge('action' => 'createaccount')).first
end
# Sets options for currenlty logged in user
#
# [changes] a +Hash+ that will be transformed into an equal sign and pipe-separated key value parameter
# [optionname] a +String+ indicating which option to change (optional)
# [optionvalue] the new value for optionname - allows pipe characters (optional)
# [reset] a +Boolean+ indicating if all preferences should be reset to site defaults (optional)
- def options(changes = {}, optionname = nil, optionvalue = nil, reset = false)
- form_data = { 'action' => 'options', 'token' => get_options_token }
+ # [options] Hash of additional options
+ def options(changes = {}, optionname = nil, optionvalue = nil, reset = false, options = {})
+ form_data = options.merge(
+ 'action' => 'options',
+ 'token' => get_options_token
+ )
if changes.present?
form_data['change'] = changes.map { |key, value| "#{key}=#{value}" }.join('|')
end
@@ -748,51 +782,59 @@
if reset
form_data['reset'] = true
end
- res, dummy = make_api_request(form_data)
- res
+ make_api_request(form_data).first
end
# Set groups for a user
#
# [user] Username of user to modify
# [groups_to_add] Groups to add user to, as an array or a string if a single group (optional)
# [groups_to_remove] Groups to remove user from, as an array or a string if a single group (optional)
- def set_groups(user, groups_to_add = [], groups_to_remove = [], comment = '')
+ # [options] Hash of additional options
+ def set_groups(user, groups_to_add = [], groups_to_remove = [], comment = '', options = {})
token = get_userrights_token(user)
- userrights(user, token, groups_to_add, groups_to_remove, comment)
+ userrights(user, token, groups_to_add, groups_to_remove, comment, options)
end
# Review current revision of an article (requires FlaggedRevisions extension, see http://www.mediawiki.org/wiki/Extension:FlaggedRevs)
#
# [title] Title of article to review
# [flags] Hash of flags and values to set, eg. { "accuracy" => "1", "depth" => "2" }
# [comment] Comment to add to review (optional)
- def review(title, flags, comment = "Reviewed by MediaWiki::Gateway")
+ # [options] Hash of additional options
+ def review(title, flags, comment = "Reviewed by MediaWiki::Gateway", options = {})
raise APIError.new('missingtitle', "Article #{title} not found") unless revid = revision(title)
- form_data = {'action' => 'review', 'revid' => revid, 'token' => get_token('edit', title), 'comment' => comment}
- form_data.merge!( Hash[flags.map {|k,v| ["flag_#{k}", v]}] )
- res, dummy = make_api_request(form_data)
- res
+
+ form_data = options.merge(
+ 'action' => 'review',
+ 'revid' => revid,
+ 'token' => get_token('edit', title),
+ 'comment' => comment
+ )
+
+ flags.each { |k, v| form_data["flag_#{k}"] = v }
+
+ make_api_request(form_data).first
end
private
# Fetch token (type 'delete', 'edit', 'email', 'import', 'move', 'protect')
def get_token(type, page_titles)
form_data = {'action' => 'query', 'prop' => 'info', 'intoken' => type, 'titles' => page_titles}
- res, dummy = make_api_request(form_data)
+ res, _ = make_api_request(form_data)
token = res.elements["query/pages/page"].attributes[type + "token"]
raise Unauthorized.new "User is not permitted to perform this operation: #{type}" if token.nil?
token
end
def get_undelete_token(page_titles)
form_data = {'action' => 'query', 'list' => 'deletedrevs', 'prop' => 'info', 'drprop' => 'token', 'titles' => page_titles}
- res, dummy = make_api_request(form_data)
+ res, _ = make_api_request(form_data)
if res.elements["query/deletedrevs/page"]
token = res.elements["query/deletedrevs/page"].attributes["token"]
raise Unauthorized.new "User is not permitted to perform this operation: #{type}" if token.nil?
token
else
@@ -801,11 +843,11 @@
end
# User rights management (aka group assignment)
def get_userrights_token(user)
form_data = {'action' => 'query', 'list' => 'users', 'ustoken' => 'userrights', 'ususers' => user}
- res, dummy = make_api_request(form_data)
+ res, _ = make_api_request(form_data)
token = res.elements["query/users/user"].attributes["userrightstoken"]
@log.debug("RESPONSE: #{res.to_s}")
if token.nil?
if res.elements["query/users/user"].attributes["missing"]
@@ -818,30 +860,32 @@
token
end
def get_options_token
form_data = { 'action' => 'tokens', 'type' => 'options' }
- res, dummy = make_api_request(form_data)
+ res, _ = make_api_request(form_data)
res.elements['tokens'].attributes['optionstoken']
end
- def userrights(user, token, groups_to_add, groups_to_remove, reason)
+ def userrights(user, token, groups_to_add, groups_to_remove, reason, options = {})
# groups_to_add and groups_to_remove can be a string or an array. Turn them into MediaWiki's pipe-delimited list format.
if groups_to_add.is_a? Array
groups_to_add = groups_to_add.join('|')
end
+
if groups_to_remove.is_a? Array
groups_to_remove = groups_to_remove.join('|')
end
- form_data = {'action' => 'userrights', 'user' => user, 'token' => token,
- 'add' => groups_to_add,
+ make_api_request(options.merge(
+ 'action' => 'userrights',
+ 'user' => user,
+ 'token' => token,
+ 'add' => groups_to_add,
'remove' => groups_to_remove,
'reason' => reason
- }
- res, dummy = make_api_request(form_data)
- res
+ )).first
end
# Make a custom query
#
@@ -865,11 +909,44 @@
options.each {|k,v| form_data[k.to_s] = v.to_s }
form_data['action'] = 'query'
make_api_request(form_data).first.elements['query']
end
+ # Iterate over query results
+ #
+ # [list] list name to query
+ # [res_xpath] XPath selector for results
+ # [attr] attribute name to extract, if any
+ # [param] parameter name to continue query
+ # [options] additional query options
+ #
+ # Yields each attribute value, or, if +attr+ is nil, each REXML::Element.
+ def iterate_query(list, res_xpath, attr, param, options, &block)
+ items, block = [], lambda { |item| items << item } unless block
+ attribute_names = %w[from continue].map { |name|
+ "name()='#{param[0, 2]}#{name}'"
+ }
+
+ req_xpath = "//query-continue/#{list}/@*[#{attribute_names.join(' or ')}]"
+ res_xpath = "//query/#{list}/#{res_xpath}" unless res_xpath.start_with?('/')
+
+ options, continue = options.merge('action' => 'query', 'list' => list), nil
+
+ loop {
+ res, continue = make_api_request(options, req_xpath)
+
+ REXML::XPath.match(res, res_xpath).each { |element|
+ block[attr ? element.attributes[attr] : element]
+ }
+
+ continue ? options[param] = continue : break
+ }
+
+ items
+ end
+
# Make generic request to API
#
# [form_data] hash or string of attributes to post
# [continue_xpath] XPath selector for query continue parameter
# [retry_count] Counter for retries
@@ -910,37 +987,38 @@
if action == 'login'
raise Unauthorized.new("Login failed: #{action_result}")
elsif action == 'createaccount'
raise Unauthorized.new("Account creation failed: #{action_result}")
end
- end
+ end
end
-
- continue = (continue_xpath and doc.elements['query-continue']) ? REXML::XPath.first(doc, continue_xpath).value : nil
+ continue = (continue_xpath and doc.elements['query-continue']) ? REXML::XPath.first(doc, continue_xpath) : nil
return [doc, continue]
end
end
# Execute the HTTP request using either GET or POST as appropriate
def http_send url, form_data, headers, &block
+ opts = @http_options.merge(:url => url, :headers => headers)
+
if form_data['action'] == 'query'
log.debug("GET: #{form_data.inspect}, #{@cookies.inspect}")
headers[:params] = form_data
- RestClient.get url, headers, &block
+ RestClient::Request.execute(opts.update(:method => :get), &block)
else
log.debug("POST: #{form_data.inspect}, #{@cookies.inspect}")
- RestClient.post url, form_data, headers, &block
+ RestClient::Request.execute(opts.update(:method => :post, :payload => form_data), &block)
end
end
# Get API XML response
# If there are errors or warnings, raise APIError
# Otherwise return XML root
def get_response(res)
begin
res = res.force_encoding("UTF-8") if res.respond_to?(:force_encoding)
doc = REXML::Document.new(res).root
- rescue REXML::ParseException => e
+ rescue REXML::ParseException
raise MediaWiki::Exception.new "Response is not XML. Are you sure you are pointing to api.php?"
end
log.debug("RES: #{doc}")
raise MediaWiki::Exception.new "Response does not contain Mediawiki API XML: #{res}" unless [ "api", "mediawiki" ].include? doc.name
if doc.elements["error"]