Revision: 5516
Author: russblau
Date: 2008-06-05 18:12:56 +0000 (Thu, 05 Jun 2008)
Log Message:
-----------
partial implementation of action=edit (only available for testing on
test.wikipedia.org so
far)
Modified Paths:
--------------
branches/rewrite/pywikibot/site.py
Modified: branches/rewrite/pywikibot/site.py
===================================================================
--- branches/rewrite/pywikibot/site.py 2008-06-05 16:47:43 UTC (rev 5515)
+++ branches/rewrite/pywikibot/site.py 2008-06-05 18:12:56 UTC (rev 5516)
@@ -16,11 +16,16 @@
from pywikibot.exceptions import *
import config
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import md5
import logging
import os
+import re
import sys
import threading
-import re
+import urllib
class PageInUse(pywikibot.Error):
@@ -369,11 +374,9 @@
@param sysop: if True, require sysop privileges.
"""
- if not hasattr(self, '_userinfo'):
+ if self.getuserinfo()['name'] != self._username:
return False
- if self._userinfo['name'] != self._username:
- return False
- return (not sysop) or 'sysop' in self._userinfo['groups']
+ return (not sysop) or 'sysop' in self.getuserinfo()['groups']
def loggedInAs(self, sysop = False):
"""Return the current username if logged in, otherwise return
None.
@@ -487,7 +490,7 @@
@property
def siteinfo(self):
"""Site information dict."""
- if not hasattr("_siteinfo"):
+ if not hasattr(self, "_siteinfo"):
self._getsiteinfo()
return self._siteinfo
@@ -656,6 +659,26 @@
page._revid = revision.revid
yield page
+ def token(self, page, tokentype):
+ """Return token retrieved from wiki to allow changing page
content.
+
+ @param page: the Page for which a token should be retrieved
+ @param tokentype: the type of token (e.g., "edit", "move",
"delete");
+ see API documentation for full list of types
+
+ """
+ query = api.PropertyGenerator("info|revisions", site=self,
+ titles=page.title(withSection=False),
+ intoken=tokentype)
+ for item in query:
+ if item['title'] != page.title(withSection=False):
+ raise Error(
+ u"token: Query on page %s returned data on page [[%s]]"
+ % (page.title(withSection=False, asLink=True),
+ item['title']))
+ api.update_page(page, item)
+ return item[tokentype + "token"]
+
# following group of methods map more-or-less directly to API queries
def pagebacklinks(self, page, followRedirects=False, filterRedirects=None,
@@ -1617,7 +1640,175 @@
for ns in namespaces)
return rngen
+ # catalog of editpage error codes, for use in generating messages
+ _ep_errors = {
+"noapiwrite": "API editing not enabled on %(site)s wiki",
+"writeapidenied":
+ "User %(user)s is not authorized to edit on %(site)s wiki",
+"protectedtitle":
+ "Title %(title)s is protected against creation on %(site)s",
+"cantcreate":
+ "User %(user)s not authorized to create new pages on %(site)s wiki",
+"cantcreate-anon":
+ """\
+Bot is not logged in, and anon users are not authorized to create new pages
+on %(site)s wiki""",
+"articleexists":
+ "Page %(title)s already exists on %(site)s wiki",
+"noimageredirect-anon":
+ """\
+Bot is not logged in, and anon users are not authorized to create image
+redirects on %(site)s wiki""",
+"noimageredirect":
+ "User %(user)s not authorized to create image redirects on %(site)s wiki",
+"spamdetected":
+ "Edit to page %(title)s rejected by spam filter due to content:\n",
+"filtered":
+ "%(info)s",
+"contenttoobig":
+ "%(info)s",
+"noedit-anon":
+ """\
+Bot is not logged in, and anon users are not authorized to edit on
+%(site)s wiki""",
+"noedit":
+ "User %(user)s not authorized to edit pages on %(site)s wiki",
+"pagedeleted":
+ "Page %(title)s has been deleted since last retrieved from %(site)s wiki",
+"editconflict":
+ "Page %(title)s not saved due to edit conflict.",
+ }
+
+ def editpage(self, page, summary, minor=True, notminor=False,
+ recreate=True, createonly=False, watch=False, unwatch=False):
+ """Submit an edited Page object to be saved to the wiki.
+ @param page: The Page to be saved; its .text property will be used
+ as the new text to be saved to the wiki
+ @param token: the edit token retrieved using Site.token()
+ @param summary: the edit summary (required!)
+ @param minor: if True (default), mark edit as minor
+ @param notminor: if True, override account preferences to mark edit
+ as non-minor
+ @param recreate: if True (default), create new page even if this
+ title has previously been deleted
+ @param createonly: if True, raise an error if this title already
+ exists on the wiki
+ @param watch: if True, add this Page to bot's watchlist
+ @param unwatch: if True, remove this Page from bot's watchlist if
+ possible
+
+ """
+ text = page.text
+ if not text:
+ raise Error("editpage: no text to be saved")
+ try:
+ lastrev = page.latestRevision()
+ except NoPage:
+ lastrev = None
+ if not recreate:
+ raise Error("Page %s does not exist on %s wiki."
+ % (page.title(withSection=False), self))
+ self.lock_page(page)
+ token = self.token(page, "edit")
+ if lastrev is not None and page.latestRevision() != lastrev:
+ raise Error("editpage: Edit conflict detected; saving aborted.")
+ req = api.Request(site=self, action="edit",
+ title=page.title(withSection=False),
+ text=text, token=token, summary=summary)
+## if lastrev is not None:
+## req["basetimestamp"] = page._revisions[lastrev].timestamp
+ if minor:
+ req['minor'] = ""
+ elif notminor:
+ req['notminor'] = ""
+ if 'bot' in self.getuserinfo()['groups']:
+ req['bot'] = ""
+ if recreate:
+ req['recreate'] = ""
+ if createonly:
+ req['createonly'] = ""
+ if watch:
+ req['watch'] = ""
+ elif unwatch:
+ req['unwatch'] = ""
+## FIXME: API gives 'badmd5' error
+## md5hash = md5()
+## md5hash.update(urllib.quote_plus(text.encode(self.encoding())))
+## req['md5'] = md5hash.digest()
+ while True:
+ try:
+ result = req.submit()
+ logging.debug("editpage response: %s" % result)
+ except api.APIError, err:
+ self.unlock_page(page)
+ if err.code.endswith("anon") and self.logged_in():
+ logging.debug(
+"editpage: received '%s' even though bot is logged in" % err.code)
+ errdata = {
+ 'site': self,
+ 'title': page.title(withSection=False),
+ 'user': self.user(),
+ 'info': err.info
+ }
+ if err.code == "spamdetected":
+ raise SpamfilterError(self._ep_errors[err.code] % errdata
+ + err.info[ err.info.index("fragment: ") + 9: ])
+
+ if err.code == "editconflict":
+ raise EditConflict(self._ep_errors[err.code] % errdata)
+ if err.code in self._ep_errors:
+ raise Error(self._ep_errors[err.code] % errdata)
+ logging.debug("editpage: Unexpected error code '%s'
received."
+ % err.code)
+ raise
+ assert ("edit" in result and "result" in
result["edit"]), result
+ if result["edit"]["result"] == "Success":
+ self.unlock_page(page)
+ if "nochange" in result["edit"]:
+ # null edit, page not changed
+ # TODO: do we want to notify the user of this?
+ return True
+ page._revid = result["edit"]["newrevid"]
+ page._revisions[int(page._revid)] = pywikibot.page.Revision(
+ revid=int(page._revid), timestamp='',
+ user=self.user(), anon=not self.logged_in(),
+ comment=summary, minor=minor or not notminor,
+ text=text
+ )
+ return True
+ elif result["edit"]["result"] == "Failure":
+ if "captcha" in result["edit"]:
+ captcha = result["edit"]["captcha"]
+ req['captchaid'] = captcha['id']
+ if captcha["type"] == "math":
+ req['captchaword'] =
input(captcha["question"])
+ continue
+ elif "url" in captcha:
+ webbrowser.open(url)
+ req['captchaword'] = cap_answerwikipedia.input(
+"Please view CAPTCHA in your browser, then type answer here:")
+ continue
+ else:
+ self.unlock_page(page)
+ logging.error(
+"editpage: unknown CAPTCHA response %s, page not saved"
+ % captcha)
+ return False
+ else:
+ self.unlock_page(page)
+ logging.error("editpage: unknown failure reason %s"
+ % str(result))
+ return False
+ else:
+ self.unlock_page(page)
+ logging.error(
+"editpage: Unknown result code '%s' received; page not saved"
+ % result["edit"]["result"])
+ logging.error(str(result))
+ return False
+
+
#### METHODS NOT IMPLEMENTED YET (but may be delegated to Family object) ####
class NotImplementedYet: