5 from page_nowriteapi import OldPage
14 def __init__(self, site, name, info=None, extra_properties={}):
15 if type(name) is type(self):
16 return self.__dict__.update(name.__dict__)
23 prop = 'info|' + '|'.join(extra_properties.iterkeys())
25 [extra_props.extend(extra_prop) for extra_prop in extra_properties.itervalues()]
31 info = self.site.api('query', prop=prop, pageids=name,
32 inprop='protection', *extra_props)
34 info = self.site.api('query', prop=prop, titles=name,
35 inprop='protection', *extra_props)
36 info = info['query']['pages'].itervalues().next()
39 self.namespace = info.get('ns', 0)
40 self.name = info.get('title', u'')
42 self.page_title = self.strip_namespace(self.name)
44 self.page_title = self.name
46 self.touched = client.parse_timestamp(info.get('touched', '0000-00-00T00:00:00Z'))
47 self.revision = info.get('lastrevid', 0)
48 self.exists = 'missing' not in info
49 self.length = info.get('length')
50 self.protection = dict([(i['type'], (i['level'], i['expiry'])) for i in info.get('protection', ()) if i])
51 self.redirect = 'redirect' in info
53 self.last_rev_time = None
56 def redirects_to(self):
57 """ Returns the redirect target page, or None if the page is not a redirect page."""
58 info = self.site.api('query', prop='pageprops', titles=self.name, redirects='')['query']
59 if 'redirects' in info:
60 for page in info['redirects']:
61 if page['from'] == self.name:
62 return Page(self.site, page['to'])
67 def resolve_redirect(self):
68 """ Returns the redirect target page, or the current page if it's not a redirect page."""
69 target_page = self.redirects_to()
70 if target_page is None:
76 return "<Page object '%s' for %s>" % (self.name.encode('utf-8'), self.site)
78 def __unicode__(self):
82 def strip_namespace(title):
85 return title[title.find(':') + 1:]
88 def normalize_title(title):
89 # TODO: Make site dependent
93 title = title[0].upper() + title[1:]
94 title = title.replace(' ', '_')
97 def can(self, action):
98 level = self.protection.get(action, (action, ))[0]
100 level = compatibility.protectright(self.site.version)
102 return level in self.site.rights
104 def get_token(self, type, force=False):
105 self.site.require(1, 11)
107 if type not in self.site.tokens:
108 self.site.tokens[type] = '0'
109 if self.site.tokens.get(type, '0') == '0' or force:
110 info = self.site.api('query', titles=self.name,
111 prop='info', intoken=type)
112 for i in info['query']['pages'].itervalues():
113 if i['title'] == self.name:
114 self.site.tokens[type] = i['%stoken' % type]
115 return self.site.tokens[type]
117 def get_expanded(self):
118 self.site.require(1, 12)
120 revs = self.revisions(prop='content', limit=1, expandtemplates=True)
122 return revs.next()['*']
123 except StopIteration:
126 def edit(self, section=None, readonly=False):
127 """Returns wikitext for a specified section or for the whole page.
129 Retrieves the latest edit.
132 if not self.can('read'):
133 raise errors.InsufficientPermission(self)
137 revs = self.revisions(prop='content|timestamp', limit=1, section=section)
141 self.section = section
142 self.last_rev_time = rev['timestamp']
143 except StopIteration:
146 self.edit_time = None
147 self.edit_time = time.gmtime()
150 def save(self, text=u'', summary=u'', minor=False, bot=True, section=None, **kwargs):
151 """Save text of page."""
152 if not self.site.logged_in and self.site.force_login:
153 # Should we really check for this?
154 raise errors.LoginError(self.site)
155 if self.site.blocked:
156 raise errors.UserBlocked(self.site.blocked)
157 if not self.can('edit'):
158 raise errors.ProtectedPageError(self)
163 section = self.section
165 if not self.site.writeapi:
166 return OldPage.save(self, text=text, summary=summary, minor=False)
172 data['notminor'] = '1'
173 if self.last_rev_time:
174 data['basetimestamp'] = time.strftime('%Y%m%d%H%M%S', self.last_rev_time)
176 data['starttimestamp'] = time.strftime('%Y%m%d%H%M%S', self.edit_time)
180 data['section'] = section
185 result = self.site.api('edit', title=self.name, text=text,
186 summary=summary, token=self.get_token('edit'),
188 if result['edit'].get('result').lower() == 'failure':
189 raise errors.EditError(self, result['edit'])
193 except errors.APIError, e:
194 if e.code == 'badtoken':
195 # Retry, but only once to avoid an infinite loop
196 self.get_token('edit', force=True)
199 except errors.APIError, e:
200 self.handle_edit_error(e, summary)
202 self.handle_edit_error(e, summary)
204 if result['edit'] == 'Success':
205 self.last_rev_time = client.parse_timestamp(result['newtimestamp'])
206 return result['edit']
208 def handle_edit_error(self, e, summary):
209 if e.code == 'editconflict':
210 raise errors.EditError(self, summary, e.info)
211 elif e.code in ('protectedtitle', 'cantcreate', 'cantcreate-anon', 'noimageredirect-anon',
212 'noimageredirect', 'noedit-anon', 'noedit'):
213 raise errors.ProtectedPageError(self, e.code, e.info)
217 def get_expanded(self):
218 self.site.require(1, 12)
220 revs = self.revisions(prop='content', limit=1, expandtemplates=True)
222 return revs.next()['*']
223 except StopIteration:
226 def move(self, new_title, reason='', move_talk=True, no_redirect=False):
227 """Move (rename) page to new_title.
229 If user account is an administrator, specify no_direct as True to not
232 If user does not have permission to move page, an InsufficientPermission
236 if not self.can('move'):
237 raise errors.InsufficientPermission(self)
239 if not self.site.writeapi:
240 return OldPage.move(self, new_title=new_title,
241 reason=reason, move_talk=move_talk)
245 data['movetalk'] = '1'
247 data['noredirect'] = '1'
248 result = self.site.api('move', ('from', self.name), to=new_title,
249 token=self.get_token('move'), reason=reason, **data)
250 return result['move']
252 def delete(self, reason='', watch=False, unwatch=False, oldimage=False):
255 If user does not have permission to delete page, an InsufficientPermission
259 if not self.can('delete'):
260 raise errors.InsufficientPermission(self)
262 if not self.site.writeapi:
263 return OldPage.delete(self, reason=reason)
269 data['unwatch'] = '1'
271 data['oldimage'] = oldimage
272 result = self.site.api('delete', title=self.name,
273 token=self.get_token('delete'),
274 reason=reason, **data)
275 return result['delete']
278 """Purge server-side cache of page. This will re-render templates and other
282 self.site.raw_index('purge', title=self.name)
284 # def watch: requires 1.14
287 def backlinks(self, namespace=None, filterredir='all', redirect=False, limit=None, generator=True):
288 self.site.require(1, 9)
289 # Fix title for < 1.11 !!
290 prefix = listing.List.get_prefix('bl', generator)
291 kwargs = dict(listing.List.generate_kwargs(prefix,
292 namespace=namespace, filterredir=filterredir))
294 kwargs['%sredirect' % prefix] = '1'
295 kwargs[compatibility.title(prefix, self.site.require(1, 11, raise_error=False))] = self.name
297 return listing.List.get_list(generator)(self.site, 'backlinks', 'bl', limit=limit, return_values='title', **kwargs)
299 def categories(self, generator=True):
300 self.site.require(1, 11)
302 return listing.PagePropertyGenerator(self, 'categories', 'cl')
304 # TODO: return sortkey if wanted
305 return listing.PageProperty(self, 'categories', 'cl', return_values='title')
307 def embeddedin(self, namespace=None, filterredir='all', redirect=False, limit=None, generator=True):
308 self.site.require(1, 9)
309 # Fix title for < 1.11 !!
310 prefix = listing.List.get_prefix('ei', generator)
311 kwargs = dict(listing.List.generate_kwargs(prefix,
312 namespace=namespace, filterredir=filterredir))
314 kwargs['%sredirect' % prefix] = '1'
315 kwargs[compatibility.title(prefix, self.site.require(1, 11, raise_error=False))] = self.name
317 return listing.List.get_list(generator)(self.site, 'embeddedin', 'ei', limit=limit, return_values='title', **kwargs)
320 self.site.require(1, 11)
321 return listing.PageProperty(self, 'extlinks', 'el', return_values='*')
323 def images(self, generator=True):
324 self.site.require(1, 9)
326 return listing.PagePropertyGenerator(self, 'images', '')
328 return listing.PageProperty(self, 'images', '', return_values='title')
331 self.site.require(1, 9) # guessing...
332 return listing.PageProperty(self, 'iwlinks', 'iw', return_values=('prefix', '*'))
334 def langlinks(self, **kwargs):
335 self.site.require(1, 9)
336 return listing.PageProperty(self, 'langlinks', 'll', return_values=('lang', '*'), **kwargs)
338 def links(self, namespace=None, generator=True, redirects=False):
339 self.site.require(1, 9)
340 kwargs = dict(listing.List.generate_kwargs('pl', namespace=namespace))
342 kwargs['redirects'] = '1'
344 return listing.PagePropertyGenerator(self, 'links', 'pl', **kwargs)
346 return listing.PageProperty(self, 'links', 'pl', return_values='title', **kwargs)
348 def revisions(self, startid=None, endid=None, start=None, end=None,
349 dir='older', user=None, excludeuser=None, limit=50,
350 prop='ids|timestamp|flags|comment|user', expandtemplates=False, section=None):
351 self.site.require(1, 8)
352 kwargs = dict(listing.List.generate_kwargs('rv', startid=startid, endid=endid,
353 start=start, end=end, user=user, excludeuser=excludeuser))
354 kwargs['rvdir'] = dir
355 kwargs['rvprop'] = prop
357 kwargs['rvexpandtemplates'] = '1'
359 kwargs['rvsection'] = section
361 return listing.RevisionsIterator(self, 'revisions', 'rv', limit=limit, **kwargs)
363 def templates(self, namespace=None, generator=True):
364 self.site.require(1, 8)
365 kwargs = dict(listing.List.generate_kwargs('tl', namespace=namespace))
367 return listing.PagePropertyGenerator(self, 'templates', 'tl')
369 return listing.PageProperty(self, 'templates', 'tl', return_values='title')
374 def __init__(self, site, name, info=None):
376 Page.__init__(self, site, name, info,
377 extra_properties={'imageinfo': (('iiprop',
378 compatibility.iiprop(site.version)), )})
379 self.imagerepository = self._info.get('imagerepository', '')
380 self.imageinfo = self._info.get('imageinfo', ({}, ))[0]
382 def imagehistory(self):
383 return listing.PageProperty(self, 'imageinfo', 'ii',
384 iiprop=compatibility.iiprop(self.site.version))
386 def imageusage(self, namespace=None, filterredir='all', redirect=False,
387 limit=None, generator=True):
388 self.site.require(1, 11)
389 # TODO: Fix for versions < 1.11
390 prefix = listing.List.get_prefix('iu', generator)
391 kwargs = dict(listing.List.generate_kwargs(prefix, title=self.name,
392 namespace=namespace, filterredir=filterredir))
394 kwargs['%sredirect' % prefix] = '1'
395 return listing.List.get_list(generator)(self.site, 'imageusage', 'iu',
396 limit=limit, return_values='title', **kwargs)
398 def duplicatefiles(self, limit=None):
400 return listing.PageProperty(self, 'duplicatefiles', 'df',
404 url = self.imageinfo['url']
405 if not url.startswith('http://'):
406 url = 'http://' + self.site.host + url
407 url = urlparse.urlparse(url)
409 return self.site.connection.get(url[1], url[2])
412 return "<Image object '%s' for %s>" % (self.name.encode('utf-8'), self.site)