Class | MediaWiki::Gateway |
In: |
lib/media_wiki/gateway.rb
|
Parent: | Object |
API_MAX_LIMIT | = | 500 |
base_url | [R] |
Set up a MediaWiki::Gateway for a given MediaWiki installation
# 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
Get a list of pages that link to a target page
Returns array of page titles (empty if no matches)
# 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
Options:
# 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.)
# 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.
# 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
Returns MediaWiki XML dump
# 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)
# 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
Returns content of page as string, nil if the page does not exist
# 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:
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"
# 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
Returns XML array <api><import><page/><page/>… <page revisions="1"> (or more) means successfully imported <page revisions="0"> means duplicate, not imported
# 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
Returns array of page titles (empty if no matches)
# 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
Throws error if login fails
# 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
Options:
# 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)
# 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
Options:
Returns rendered page as string, or nil if the page does not exist
# 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
Returns array of page titles (empty if no matches)
# 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
Returns result as an HTML string
# 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.
Returns number of revisions undeleted, or zero if nothing to undelete
# 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:
Requires Mediawiki 1.16+
Arguments:
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:
Deprecated but still supported options:
Examples:
mw.upload('/path/to/local/file.jpg', 'filename' => "RemoteFile.jpg") mw.upload(nil, 'filename' => "RemoteFile2.jpg", 'url' => 'http://remote.com/server/file.jpg')
# 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
Get API XML response If there are errors, raise exception Otherwise return XML root
# 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’)
# 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
# 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
Returns XML document
# 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