sunflower 0.4.5 → 0.5
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/README +1 -1
- data/example-bot.rb +7 -8
- data/lib/sunflower/commontasks.rb +3 -77
- data/lib/sunflower/core.rb +257 -78
- data/lib/sunflower/list.rb +255 -0
- data/lib/sunflower.rb +1 -1
- metadata +3 -12
- data/lib/sunflower/listmaker.rb +0 -160
- data/scripts/ZDBOT.rb +0 -62
- data/scripts/aktualizacjapilkarzy.rb +0 -339
- data/scripts/author-list.rb +0 -36
- data/scripts/changeimage.rb +0 -42
- data/scripts/fix-multiple-same-refs.rb +0 -102
- data/scripts/insight.rb +0 -133
- data/scripts/make-id2team-list.rb +0 -32
- data/scripts/wanted.rb +0 -72
- data/use-easy-bot.rb +0 -54
data/README
CHANGED
data/example-bot.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
# This is the most basic bot possible.
|
2
3
|
|
3
|
-
require 'sunflower
|
4
|
+
require 'sunflower'
|
4
5
|
|
5
|
-
s=Sunflower.new
|
6
|
-
s.
|
6
|
+
s = Sunflower.new.login
|
7
|
+
s.summary = 'test summary'
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
p
|
11
|
-
p.write p.text+"\n\ntest"
|
12
|
-
p.save
|
9
|
+
p = Sunflower::Page.new 'Test'
|
10
|
+
p.text += "\n\ntest"
|
11
|
+
p.save
|
@@ -1,80 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
# extends Page with some methods letting easily perform common tasks
|
3
2
|
|
4
|
-
class Page
|
5
|
-
# executes methods on self
|
6
|
-
# "commands" is array of arrays
|
7
|
-
# page.execute([
|
8
|
-
# [:replace, 'something', 'whatever'],
|
9
|
-
# [:append, 'some things']
|
10
|
-
# ])
|
11
|
-
# equals to
|
12
|
-
# page.replace('something', 'whatever')
|
13
|
-
# page.append('some things')
|
14
|
-
|
15
|
-
# allowed modifiers:
|
16
|
-
# r, required
|
17
|
-
# oi:module, only-if:module
|
18
|
-
# !oi:module, only-if-not:module
|
19
|
-
# s:append to summary, summary:append to summary
|
20
|
-
def execute commands
|
21
|
-
originalText = self.text.dup
|
22
|
-
|
23
|
-
commands.each do |cmd|
|
24
|
-
f=cmd.shift
|
25
|
-
if f.class==Array
|
26
|
-
methodName=f.shift
|
27
|
-
modifiers=f.map{|i|
|
28
|
-
i+=':' if !i.include? ':'
|
29
|
-
i=i.split(':',-1)
|
30
|
-
i[0]=i[0].downcase.gsub(/[^a-z!]/,'')
|
31
|
-
|
32
|
-
i[0]='r' if i[0]=='required'
|
33
|
-
i[0]='oi' if i[0]=='onlyif'
|
34
|
-
i[0]='!oi' if i[0]=='onlyifnot'
|
35
|
-
i[0]='s' if i[0]=='summary'
|
36
|
-
|
37
|
-
type=i.shift
|
38
|
-
i=i.join(':')
|
39
|
-
|
40
|
-
[type,i]
|
41
|
-
}
|
42
|
-
modifiers=Hash[*(modifiers.flatten)]
|
43
|
-
else
|
44
|
-
methodName=f
|
45
|
-
modifiers={}
|
46
|
-
end
|
47
|
-
|
48
|
-
if modifiers['oi']
|
49
|
-
if !@modulesExecd.index(modifiers['oi'].strip)
|
50
|
-
next #skip this command
|
51
|
-
end
|
52
|
-
end
|
53
|
-
if modifiers['!oi']
|
54
|
-
if @modulesExecd.index(modifiers['oi'].strip)
|
55
|
-
next #skip this command
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
oldText=self.text
|
60
|
-
self.method(methodName).call(*cmd)
|
61
|
-
newText=self.text
|
62
|
-
|
63
|
-
@modulesExecd<<methodName if oldText!=newText
|
64
|
-
|
65
|
-
if modifiers['s'] && oldText!=newText
|
66
|
-
@summaryAppend<<modifiers['s'].strip
|
67
|
-
end
|
68
|
-
|
69
|
-
if modifiers['r'] && oldText==newText
|
70
|
-
self.text = originalText
|
71
|
-
break #reset text and stop executing commands
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
|
3
|
+
class Sunflower::Page
|
78
4
|
# replaces "from" with "to" in page text
|
79
5
|
# "from" may be regex
|
80
6
|
def replace from, to, once=false
|
@@ -152,7 +78,7 @@ class Page
|
|
152
78
|
|
153
79
|
str.gsub!(/\[\[([^\|\]]+)(\||\]\])/){
|
154
80
|
name, rest = $1, $2
|
155
|
-
"[[#{self.sunflower.cleanup_title name}#{rest}"
|
81
|
+
"[[#{self.sunflower.cleanup_title name, true, true}#{rest}"
|
156
82
|
}
|
157
83
|
|
158
84
|
# headings
|
@@ -164,7 +90,7 @@ class Page
|
|
164
90
|
|
165
91
|
if wikiid = self.sunflower.siteinfo['general']['wikiid']
|
166
92
|
if self.respond_to? :"code_cleanup_#{wikiid}"
|
167
|
-
str = self.
|
93
|
+
str = self.send :"code_cleanup_#{wikiid}", str
|
168
94
|
end
|
169
95
|
end
|
170
96
|
|
data/lib/sunflower/core.rb
CHANGED
@@ -3,8 +3,6 @@ require 'rest-client'
|
|
3
3
|
require 'json'
|
4
4
|
require 'cgi'
|
5
5
|
|
6
|
-
class SunflowerError < StandardError; end
|
7
|
-
|
8
6
|
# Main class. To start working, you have to create new Sunflower:
|
9
7
|
# s = Sunflower.new('en.wikipedia.org')
|
10
8
|
# And then log in:
|
@@ -19,7 +17,7 @@ class SunflowerError < StandardError; end
|
|
19
17
|
#
|
20
18
|
# You can use multiple Sunflowers at once, to work on multiple wikis.
|
21
19
|
class Sunflower
|
22
|
-
VERSION = '0.
|
20
|
+
VERSION = '0.5'
|
23
21
|
|
24
22
|
INVALID_CHARS = %w(# < > [ ] | { })
|
25
23
|
INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
|
@@ -28,7 +26,19 @@ class Sunflower
|
|
28
26
|
def self.path
|
29
27
|
File.join(ENV['HOME'], 'sunflower-userdata')
|
30
28
|
end
|
31
|
-
|
29
|
+
|
30
|
+
# Returns array of [url, username, password], or nil if userdata is unavailable or invalid.
|
31
|
+
def self.read_userdata
|
32
|
+
data = nil
|
33
|
+
data = File.read(Sunflower.path).split(/\r?\n/).map{|i| i.strip} rescue nil
|
34
|
+
|
35
|
+
if data && data.length==3 && data.all?{|a| a and a != ''}
|
36
|
+
return data
|
37
|
+
else
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
32
42
|
# Summary used when saving edits with this Sunflower.
|
33
43
|
attr_accessor :summary
|
34
44
|
# Whether to run #code_cleanup when calling #save.
|
@@ -38,6 +48,11 @@ class Sunflower
|
|
38
48
|
# Siteinfo, as returned by API call.
|
39
49
|
attr_accessor :siteinfo
|
40
50
|
|
51
|
+
# Whether we are logged in.
|
52
|
+
def logged_in?; @loggedin; end
|
53
|
+
# Username if logged in; nil otherwise.
|
54
|
+
attr_reader :username
|
55
|
+
|
41
56
|
# Whether this user (if logged in) has bot rights.
|
42
57
|
def is_bot?; @is_bot; end
|
43
58
|
|
@@ -49,29 +64,72 @@ class Sunflower
|
|
49
64
|
attr_writer :log
|
50
65
|
def log?; @log; end
|
51
66
|
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
# Used by #initialize to convert short identifiers such as "b:pl" to domains such as "pl.wikibooks.org".
|
68
|
+
# Identifier is of the format "type:lang" or "lang:type" (see below for valid values).
|
69
|
+
#
|
70
|
+
# Either or both parts can be ommitted; default type is "w", default lang is "en".
|
71
|
+
# (Since clashes are impossible, the colon can be ommitted in such cases as well.)
|
72
|
+
#
|
73
|
+
# lang can be any valid language code. It is ignored for type == "meta" or "commons".
|
74
|
+
#
|
75
|
+
# Valid values for type are the same as used for inter-wiki links, that is:
|
76
|
+
# [w] Wikipedia
|
77
|
+
# [b] Wikibooks
|
78
|
+
# [n] Wikinews
|
79
|
+
# [q] Wikiquote
|
80
|
+
# [s] Wikisource
|
81
|
+
# [v] Wikiversity
|
82
|
+
# [wikt] Wiktionary
|
83
|
+
# [species] Wikispecies
|
84
|
+
# [commons] Wikimedia Commons
|
85
|
+
# [meta] Wikimedia Meta-Wiki
|
86
|
+
def self.resolve_wikimedia_id id
|
87
|
+
keys = id.split(':').select{|a| a and !a.empty? }
|
88
|
+
|
89
|
+
raise ArgumentError, 'invalid format' if keys.length > 2
|
90
|
+
|
91
|
+
type_map = {
|
92
|
+
'b' => 'XX.wikibooks.org',
|
93
|
+
'q' => 'XX.wikiquote.org',
|
94
|
+
'n' => 'XX.wikinews.org',
|
95
|
+
'w' => 'XX.wikipedia.org',
|
96
|
+
'wikt' => 'XX.wiktionary.org',
|
97
|
+
'species' => 'XX.wikispecies.org',
|
98
|
+
'v' => 'XX.wikiversity.org',
|
99
|
+
's' => 'XX.wikisource.org',
|
100
|
+
'commons' => 'commons.wikimedia.org',
|
101
|
+
'meta' => 'meta.wikimedia.org',
|
102
|
+
}
|
103
|
+
|
104
|
+
types, langs = keys.partition{|a| type_map.keys.include? a }
|
105
|
+
type = types.first || 'w'
|
106
|
+
lang = langs.first || 'en'
|
60
107
|
|
108
|
+
return type_map[type].sub 'XX', lang
|
109
|
+
end
|
110
|
+
|
111
|
+
# Initialize a new Sunflower working on a wiki with given URL, for ex. "pl.wikipedia.org". url can also be a shorthand identifier such as "b:pl" - see Sunflower.resolve_wikimedia_id for details.
|
112
|
+
def initialize url=nil
|
61
113
|
if !url
|
62
|
-
|
63
|
-
|
114
|
+
userdata = Sunflower.read_userdata()
|
115
|
+
|
116
|
+
if userdata
|
117
|
+
url = userdata[0]
|
64
118
|
else
|
65
|
-
raise
|
119
|
+
raise Sunflower::Error, 'initialize: no URL supplied and no userdata found!'
|
66
120
|
end
|
67
121
|
end
|
68
122
|
|
69
|
-
@
|
70
|
-
|
123
|
+
@wikiURL = (url.include?('.') ? url : Sunflower.resolve_wikimedia_id(url))
|
124
|
+
|
125
|
+
@warnings = true
|
126
|
+
@log = false
|
71
127
|
|
72
|
-
@
|
128
|
+
@loggedin = false
|
129
|
+
@username = nil
|
130
|
+
@is_bot = false
|
73
131
|
|
74
|
-
@
|
132
|
+
@cookies = {}
|
75
133
|
|
76
134
|
siprop = 'general|namespaces|namespacealiases|specialpagealiases|magicwords|interwikimap|dbrepllag|statistics|usergroups|extensions|fileextensions|rightsinfo|languages|skins|extensiontags|functionhooks|showhooks|variables'
|
77
135
|
@siteinfo = self.API(action: 'query', meta: 'siteinfo', siprop: siprop)['query']
|
@@ -79,6 +137,10 @@ class Sunflower
|
|
79
137
|
_build_ns_map
|
80
138
|
end
|
81
139
|
|
140
|
+
def inspect
|
141
|
+
"#<Sunflower #{@loggedin ? @username : "[anon]"}@#{@wikiURL}#{@is_bot ? ' [bot]' : ''}>"
|
142
|
+
end
|
143
|
+
|
82
144
|
# Private. Massages data from siteinfo to be used for recognizing namespaces.
|
83
145
|
def _build_ns_map
|
84
146
|
@namespace_to_id = {} # all keys lowercase
|
@@ -99,6 +161,7 @@ class Sunflower
|
|
99
161
|
@namespace_to_id[ h['*'].downcase ] = h['id'].to_i
|
100
162
|
end
|
101
163
|
end
|
164
|
+
private :_build_ns_map
|
102
165
|
|
103
166
|
# Call the API. Returns a hash of JSON response. Request can be a HTTP request string or a hash.
|
104
167
|
def API request
|
@@ -116,25 +179,69 @@ class Sunflower
|
|
116
179
|
JSON.parse resp.to_str
|
117
180
|
end
|
118
181
|
|
182
|
+
# Call the API. While more results are available via the xxcontinue parameter, call it again.
|
183
|
+
#
|
184
|
+
# Assumes action=query.
|
185
|
+
#
|
186
|
+
# By default returns an array of all API responses. Attempts to merge the responses
|
187
|
+
# into a response that would have been returned if the limit was infinite. merge_on is
|
188
|
+
# the key of response['query'] to merge consecutive responses on.
|
189
|
+
#
|
190
|
+
# If limit given, will perform no more than this many API calls before returning.
|
191
|
+
# If limit is 1, behaves exactly like #API.
|
192
|
+
#
|
193
|
+
# Example: get list of all pages linking to Main Page:
|
194
|
+
#
|
195
|
+
# sunflower.API_continued "action=query&list=backlinks&bllimit=max&bltitle=Main_Page", 'backlinks', 'blcontinue'
|
196
|
+
def API_continued request, merge_on, xxcontinue, limit=nil
|
197
|
+
out = []
|
198
|
+
|
199
|
+
# gather
|
200
|
+
res = self.API(request)
|
201
|
+
out << res
|
202
|
+
while res['query-continue'] and (!limit || out.length < limit)
|
203
|
+
api_endpoint = if request.is_a? String
|
204
|
+
request + "&#{xxcontinue}=#{res["query-continue"][merge_on][xxcontinue]}"
|
205
|
+
elsif request.is_a? Hash
|
206
|
+
request.merge({xxcontinue => res["query-continue"][merge_on][xxcontinue]})
|
207
|
+
end
|
208
|
+
|
209
|
+
res = self.API(api_endpoint)
|
210
|
+
out << res
|
211
|
+
end
|
212
|
+
|
213
|
+
# merge
|
214
|
+
merged = out[0]
|
215
|
+
meth = (merged['query'][merge_on].is_a?(Hash) ? :merge! : :concat)
|
216
|
+
|
217
|
+
out.drop(1).each do |cur|
|
218
|
+
merged['query'][merge_on].send meth, cur['query'][merge_on]
|
219
|
+
end
|
220
|
+
|
221
|
+
return merged
|
222
|
+
end
|
223
|
+
|
119
224
|
# Log in using given info.
|
120
225
|
def login user='', password=''
|
121
226
|
if user=='' || password==''
|
122
|
-
|
123
|
-
|
124
|
-
|
227
|
+
userdata = Sunflower.read_userdata()
|
228
|
+
|
229
|
+
if userdata
|
230
|
+
user = userdata[1] if user==''
|
231
|
+
password = userdata[2] if password==''
|
125
232
|
else
|
126
|
-
raise
|
233
|
+
raise Sunflower::Error, 'login: no user/pass supplied and no userdata found!'
|
127
234
|
end
|
128
235
|
end
|
129
236
|
|
130
|
-
raise
|
237
|
+
raise Sunflower::Error, 'bad username!' if user =~ INVALID_CHARS_REGEX
|
131
238
|
|
132
239
|
|
133
240
|
# 1. get the login token
|
134
241
|
response = RestClient.post(
|
135
242
|
'http://'+@wikiURL+'/w/api.php?'+"action=login&lgname=#{CGI.escape user}&lgpassword=#{CGI.escape password}"+'&format=json',
|
136
243
|
nil,
|
137
|
-
{:user_agent =>
|
244
|
+
{:user_agent => "Sunflower #{VERSION} alpha"}
|
138
245
|
)
|
139
246
|
|
140
247
|
@cookies = response.cookies
|
@@ -146,7 +253,7 @@ class Sunflower
|
|
146
253
|
response = RestClient.post(
|
147
254
|
'http://'+@wikiURL+'/w/api.php?'+"action=login&lgname=#{CGI.escape user}&lgpassword=#{CGI.escape password}&lgtoken=#{token}"+'&format=json',
|
148
255
|
nil,
|
149
|
-
{:user_agent =>
|
256
|
+
{:user_agent => "Sunflower #{VERSION} alpha", :cookies => @cookies}
|
150
257
|
)
|
151
258
|
|
152
259
|
json = JSON.parse response.to_str
|
@@ -158,7 +265,7 @@ class Sunflower
|
|
158
265
|
})
|
159
266
|
|
160
267
|
|
161
|
-
raise
|
268
|
+
raise Sunflower::Error, 'unable to log in (no cookies received)!' if !@cookies
|
162
269
|
|
163
270
|
|
164
271
|
# 3. confirm you did log in by checking the watchlist.
|
@@ -166,9 +273,12 @@ class Sunflower
|
|
166
273
|
r=self.API('action=query&list=watchlistraw')
|
167
274
|
if r['error'] && r['error']['code']=='wrnotloggedin'
|
168
275
|
@loggedin=false
|
169
|
-
raise
|
276
|
+
raise Sunflower::Error, 'unable to log in!'
|
170
277
|
end
|
171
278
|
|
279
|
+
# set the username
|
280
|
+
@username = user
|
281
|
+
|
172
282
|
# 4. check bot rights
|
173
283
|
r=self.API('action=query&list=allusers&aulimit=1&augroup=bot&aufrom='+(CGI.escape user))
|
174
284
|
unless r['query']['allusers'][0]['name']==user
|
@@ -187,7 +297,9 @@ class Sunflower
|
|
187
297
|
end
|
188
298
|
|
189
299
|
# Cleans up underscores, percent-encoding and title-casing in title (with optional anchor).
|
190
|
-
def cleanup_title title
|
300
|
+
def cleanup_title title, preserve_case=false, preserve_colon=false
|
301
|
+
return '' if title.strip == ''
|
302
|
+
|
191
303
|
name, anchor = title.split '#', 2
|
192
304
|
|
193
305
|
# CGI.unescape also changes pluses to spaces; code borrowed from there
|
@@ -197,6 +309,10 @@ class Sunflower
|
|
197
309
|
name = unescape.call(name).gsub(/[ _]+/, ' ').strip
|
198
310
|
anchor = unescape.call(anchor.gsub(/\.([0-9a-fA-F]{2})/, '%\1')).gsub(/[ _]+/, ' ').strip if anchor
|
199
311
|
|
312
|
+
leading_colon = name[0]==':'
|
313
|
+
name = name.sub(/^:\s*/, '') if leading_colon
|
314
|
+
leading_colon = false if !preserve_colon
|
315
|
+
|
200
316
|
# FIXME unicode? downcase, upcase
|
201
317
|
|
202
318
|
if name.include? ':'
|
@@ -206,9 +322,9 @@ class Sunflower
|
|
206
322
|
end
|
207
323
|
end
|
208
324
|
|
209
|
-
name[0] = name[0].upcase if @siteinfo["general"]["case"] == "first-letter"
|
325
|
+
name[0] = name[0].upcase if !preserve_case and @siteinfo["general"]["case"] == "first-letter"
|
210
326
|
|
211
|
-
return [ns ? "#{ns}:" : nil, name, anchor ? "##{anchor}" : nil].join ''
|
327
|
+
return [leading_colon ? ':' : nil, ns ? "#{ns}:" : nil, name, anchor ? "##{anchor}" : nil].join ''
|
212
328
|
end
|
213
329
|
|
214
330
|
# Returns the localized namespace name for ns, which may be namespace number, canonical name, or any namespace alias.
|
@@ -245,76 +361,103 @@ class Sunflower
|
|
245
361
|
end
|
246
362
|
|
247
363
|
# Class representing a single Wiki page. To load specified page, use #new. To save it back, use #save.
|
248
|
-
class Page
|
364
|
+
class Sunflower::Page
|
249
365
|
# Characters which MediaWiki does not permit in page title.
|
250
366
|
INVALID_CHARS = %w(# < > [ ] | { })
|
251
367
|
# Regex matching characters which MediaWiki does not permit in page title.
|
252
368
|
INVALID_CHARS_REGEX = Regexp.union *INVALID_CHARS
|
253
369
|
|
254
|
-
# The
|
370
|
+
# The Sunflower instance this page belongs to.
|
371
|
+
attr_reader :sunflower
|
372
|
+
|
373
|
+
# The current text of the page. Lazy-loaded.
|
255
374
|
attr_accessor :text
|
256
|
-
# The text of the page, as of when it was loaded.
|
375
|
+
# The text of the page, as of when it was loaded. Lazy-loaded.
|
257
376
|
attr_reader :orig_text
|
258
377
|
|
259
|
-
#
|
260
|
-
|
378
|
+
# Page title, as passed to #initialize and cleaned by Sunflower#cleanup_title.
|
379
|
+
# Real page title as canonicalized by MediaWiki software can be accessed via #real_title
|
380
|
+
# (but it should always be the same).
|
381
|
+
attr_reader :title
|
261
382
|
|
262
|
-
#
|
263
|
-
|
264
|
-
#
|
265
|
-
attr_reader :
|
266
|
-
|
383
|
+
# Value of given attribute, as returned by API call prop=info for this page. Lazy-loaded.
|
384
|
+
attr_reader :pageid, :ns, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection
|
385
|
+
# Value of `title` attribute, as returned by API call prop=info for this page. Lazy-loaded. See #title.
|
386
|
+
attr_reader :real_title
|
387
|
+
|
388
|
+
# Whether this datum is already loaded. Can be set to true to suppress loading
|
389
|
+
# (used e.g. by Sunflower::List#pages_preloaded)
|
390
|
+
attr_accessor :preloaded_text, :preloaded_attrs
|
267
391
|
|
268
392
|
# calling any of these accessors will fetch the data.
|
269
|
-
|
393
|
+
# getters...
|
394
|
+
[:pageid, :ns, :real_title, :touched, :lastrevid, :counter, :length, :starttimestamp, :edittoken, :protection].each do |meth|
|
395
|
+
remove_method meth # to avoid warnings when running with ruby -w
|
270
396
|
define_method meth do
|
271
397
|
preload_attrs unless @preloaded_attrs
|
272
398
|
instance_variable_get "@#{meth}"
|
273
399
|
end
|
274
400
|
end
|
401
|
+
[:text, :orig_text].each do |meth|
|
402
|
+
remove_method meth # to avoid warnings when running with ruby -w
|
403
|
+
define_method meth do
|
404
|
+
preload_text unless @preloaded_text
|
405
|
+
instance_variable_get "@#{meth}"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
# setters...
|
409
|
+
[:text=].each do |meth|
|
410
|
+
remove_method meth # to avoid warnings when running with ruby -w
|
411
|
+
define_method meth do |a|
|
412
|
+
preload_text unless @preloaded_text
|
413
|
+
instance_variable_set "@#{meth.to_s.chop}", a
|
414
|
+
end
|
415
|
+
end
|
275
416
|
|
276
|
-
# Load the specified page.
|
277
|
-
#
|
278
|
-
#
|
279
|
-
|
280
|
-
|
417
|
+
# Load the specified page.
|
418
|
+
# Only the text will be immediately loaded - attributes and edit token will be loaded when needed, or when you call #preload_attrs.
|
419
|
+
#
|
420
|
+
# If you are using multiple Sunflowers, you have to specify which one this page belongs to using the second argument of function.
|
421
|
+
# You can pass either a Sunflower object, wiki URL, or a shorthand id as specified in Sunflower.resolve_wikimedia_id.
|
422
|
+
def initialize title='', url=''
|
423
|
+
raise Sunflower::Error, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
|
281
424
|
|
282
425
|
@modulesExecd=[] #used by sunflower-commontasks.rb
|
283
426
|
@summaryAppend=[] #used by sunflower-commontasks.rb
|
284
427
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
raise
|
428
|
+
case url
|
429
|
+
when Sunflower
|
430
|
+
@sunflower = url
|
431
|
+
when '', nil
|
432
|
+
count = ObjectSpace.each_object(Sunflower){|o| @sunflower=o}
|
433
|
+
raise Sunflower::Error, 'no Sunflowers present' if count==0
|
434
|
+
raise Sunflower::Error, 'you must pass wiki name if using multiple Sunflowers at once' if count>1
|
291
435
|
else
|
292
|
-
|
436
|
+
url = (url.include?('.') ? url : Sunflower.resolve_wikimedia_id(url))
|
437
|
+
ObjectSpace.each_object(Sunflower){|o| @sunflower=o if o.wikiURL==url}
|
438
|
+
raise Sunflower::Error, "no Sunflower for #{url}" if !@sunflower
|
293
439
|
end
|
294
440
|
|
295
|
-
raise SunflowerError, "no Sunflower for #{wiki}" if !@sunflower
|
296
|
-
|
297
441
|
@title = @sunflower.cleanup_title title
|
298
442
|
|
299
|
-
|
300
|
-
|
301
|
-
@orig_text=''
|
302
|
-
return
|
303
|
-
end
|
304
|
-
|
305
|
-
preload_text
|
443
|
+
@preloaded_text = false
|
444
|
+
@preloaded_attrs = false
|
306
445
|
end
|
307
446
|
|
308
447
|
# Load the text of this page. Semi-private.
|
309
448
|
def preload_text
|
310
|
-
|
311
|
-
r = r['query']['pages'].first
|
312
|
-
if r['missing']
|
449
|
+
if title == ''
|
313
450
|
@text = ''
|
314
|
-
elsif r['invalid']
|
315
|
-
raise SunflowerError, 'title invalid: '+@title
|
316
451
|
else
|
317
|
-
|
452
|
+
r = @sunflower.API('action=query&prop=revisions&rvprop=content&titles='+CGI.escape(@title))
|
453
|
+
r = r['query']['pages'].values.first
|
454
|
+
if r['missing']
|
455
|
+
@text = ''
|
456
|
+
elsif r['invalid']
|
457
|
+
raise Sunflower::Error, 'title invalid: '+@title
|
458
|
+
else
|
459
|
+
@text = r['revisions'][0]['*']
|
460
|
+
end
|
318
461
|
end
|
319
462
|
|
320
463
|
@orig_text = @text.dup
|
@@ -325,8 +468,9 @@ class Page
|
|
325
468
|
# Load the metadata associated with this page. Semi-private.
|
326
469
|
def preload_attrs
|
327
470
|
r = @sunflower.API('action=query&prop=info&inprop=protection&intoken=edit&titles='+CGI.escape(@title))
|
328
|
-
r = r['query']['pages'].first
|
471
|
+
r = r['query']['pages'].values.first
|
329
472
|
r.each{|key, value|
|
473
|
+
key = 'real_title' if key == 'title'
|
330
474
|
self.instance_variable_set('@'+key, value)
|
331
475
|
}
|
332
476
|
|
@@ -359,8 +503,8 @@ class Page
|
|
359
503
|
|
360
504
|
summary = @sunflower.summary if !summary
|
361
505
|
|
362
|
-
raise
|
363
|
-
raise
|
506
|
+
raise Sunflower::Error, 'title invalid: '+title if title =~ INVALID_CHARS_REGEX
|
507
|
+
raise Sunflower::Error, 'no summary!' if (!summary or summary=='') && @summaryAppend.empty?
|
364
508
|
|
365
509
|
unless @summaryAppend.empty?
|
366
510
|
if !summary or summary==''
|
@@ -383,18 +527,53 @@ class Page
|
|
383
527
|
alias :put :save
|
384
528
|
|
385
529
|
def self.get title, wiki=''
|
386
|
-
|
530
|
+
self.new(title, wiki)
|
387
531
|
end
|
388
532
|
|
389
533
|
def self.load title, wiki=''
|
390
|
-
|
534
|
+
self.new(title, wiki)
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# For backwards compatibility. Deprecated.
|
539
|
+
class Page # :nodoc:
|
540
|
+
class << self
|
541
|
+
def new *a
|
542
|
+
warn "warning: toplevel Page class has been renamed to Sunflower::Page, this alias will be removed in v0.6"
|
543
|
+
Sunflower::Page.new *a
|
544
|
+
end
|
545
|
+
alias get new
|
546
|
+
alias load new
|
391
547
|
end
|
392
548
|
end
|
393
549
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
550
|
+
# For backwards compatibility. Deprecated.
|
551
|
+
#
|
552
|
+
# We use inheritance shenanigans to keep the usage in "begin ... rescue ... end" working.
|
553
|
+
class SunflowerError < StandardError # :nodoc:
|
554
|
+
%w[== backtrace exception inspect message set_backtrace to_s].each do |meth|
|
555
|
+
define_method meth.to_sym do |*a, &b|
|
556
|
+
if self.class == Sunflower::Error and !@warned
|
557
|
+
warn "warning: toplevel SunflowerError class has been renamed to Sunflower::Error, this alias will be removed in v0.6"
|
558
|
+
@warned = true
|
559
|
+
end
|
560
|
+
|
561
|
+
super *a, &b
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
class << self
|
566
|
+
def new *a
|
567
|
+
warn "warning: toplevel SunflowerError class has been renamed to Sunflower::Error, this alias will be removed in v0.6" unless self == Sunflower::Error
|
568
|
+
super
|
569
|
+
end
|
570
|
+
def exception *a
|
571
|
+
warn "warning: toplevel SunflowerError class has been renamed to Sunflower::Error, this alias will be removed in v0.6" unless self == Sunflower::Error
|
572
|
+
super
|
573
|
+
end
|
398
574
|
end
|
399
575
|
end
|
400
576
|
|
577
|
+
|
578
|
+
# Represents an error raised by Sunflower.
|
579
|
+
class Sunflower::Error < SunflowerError; end
|