1# -*- coding: utf-8 -*-
2# Copyright 2009 Mr.Z-man
3
4# This file is part of wikitools.
5# wikitools is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9
10# wikitools is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14
15# You should have received a copy of the GNU General Public License
16# along with wikitools.  If not, see <http://www.gnu.org/licenses/>.
17
18import wiki
19import page
20import api
21import urllib2
22
23class FileDimensionError(wiki.WikiError):
24	"""Invalid dimensions"""
25
26class UploadError(wiki.WikiError):
27	"""Error during uploading"""
28
29class File(page.Page):
30	"""A file on the wiki"""
31	def __init__(self, wiki, title, check=True, followRedir=False, section=False, sectionnumber=False):
32		"""
33		wiki - A wiki object
34		title - The page title, as a string or unicode object
35		check - Checks for existence, normalizes title, required for most things
36		followRedir - follow redirects (check must be true)
37		section - the section name
38		sectionnumber - the section number
39		pageid - pageid, can be in place of title
40		"""
41		page.Page.__init__(self, wiki, title, check, followRedir, section, sectionnumber)
42		if self.namespace != 6:
43			self.setNamespace(6, check)
44		self.usage = []
45		self.history = []
46
47	def getHistory(self, force=False):
48		if self.history and not force:
49			return self.history
50		if self.pageid == 0 and not self.title:
51			self.setPageInfo()
52		if not self.exists:
53			raise NoPage
54		params = {
55			'action': 'query',
56			'prop': 'imageinfo',
57			'iilimit': self.site.limit,
58		}
59		if self.pageid > 0:
60			params['pageids'] = self.pageid
61		else:
62			params['titles'] = self.title
63		req = api.APIRequest(self.site, params)
64		response = req.query()
65		self.history = response['query']['pages'][str(self.pageid)]['imageinfo']
66		return self.history
67
68	def getUsage(self, titleonly=False, force=False, namespaces=False):
69		"""Gets a list of pages that use the file
70
71		titleonly - set to True to only create a list of strings,
72		else it will be a list of Page objects
73		force - reload the list even if it was generated before
74		namespaces - List of namespaces to restrict to (queries with this option will not be cached)
75
76		"""
77		if self.usage and not reload:
78			if titleonly:
79				if namespaces is not False:
80					return [p.title for p in self.usage if p.namespace in namespaces]
81				else:
82					return [p.title for p in self.usage]
83			if namespaces is False:
84				return self.usage
85			else:
86				return [p for p in self.usage if p.namespace in namespaces]
87		else:
88			ret = []
89			usage = []
90			for title in self.__getUsageInternal(namespaces):
91				usage.append(title)
92				if titleonly:
93					ret.append(title.title)
94			if titleonly:
95				return ret
96			if namespaces is False:
97				self.usage = usage
98			return usage
99
100	def getUsageGen(self, titleonly=False, force=False, namespaces=False):
101		"""Generator function for pages that use the file
102
103		titleonly - set to True to return strings,
104		else it will return Page objects
105		force - reload the list even if it was generated before
106		namespaces - List of namespaces to restrict to (queries with this option will not be cached)
107
108		"""
109		if self.usage and not reload:
110			for title in self.usage:
111				if namespaces is False or title.namespace in namespaces:
112					if titleonly:
113						yield title.title
114					else:
115						yield title
116		else:
117			if namespaces is False:
118				self.usage = []
119			for title in self.__getUsageInternal():
120				if namespaces is False:
121					self.usage.append(title)
122				if titleonly:
123					yield title.title
124				else:
125					yield title
126
127	def __getUsageInternal(self, namespaces=False):
128		params = {'action':'query',
129			'list':'imageusage',
130			'iutitle':self.title,
131			'iulimit':self.site.limit,
132		}
133		if namespaces is not False:
134			params['iunamespace'] = '|'.join([str(ns) for ns in namespaces])
135		while True:
136			req = api.APIRequest(self.site, params)
137			data = req.query(False)
138			for item in data['query']['imageusage']:
139				yield page.Page(self.site, item['title'], check=False, followRedir=False)
140			try:
141				params['iucontinue'] = data['query-continue']['imageusage']['iucontinue']
142			except:
143				break
144
145	def __extractToList(self, json, stuff):
146		list = []
147		if stuff in json['query']:
148			for item in json['query'][stuff]:
149				list.append(item['title'])
150		return list
151
152	def download(self, width=False, height=False, location=False):
153		"""Download the image to a local file
154
155		width/height - set width OR height of the downloaded image
156		location - set the filename to save to. If not set, the page title
157		minus the namespace prefix will be used and saved to the current directory
158
159		"""
160		if self.pageid == 0:
161			self.setPageInfo()
162		params = {'action':'query',
163			'prop':'imageinfo',
164			'iiprop':'url'
165		}
166		if width and height:
167			raise DimensionError("Can't specify both width and height")
168		if width:
169			params['iiurlwidth'] = width
170		if height:
171			params['iiurlheight'] = height
172		if self.pageid != 0:
173			params['pageids'] = self.pageid
174		elif self.title:
175			params['titles'] = self.title
176		else:
177			self.setPageInfo()
178			if not self.exists: # Non-existant files may be on a shared repo (e.g. commons)
179				params['titles'] = self.title
180			else:
181				params['pageids'] = self.pageid
182		req = api.APIRequest(self.site, params)
183		res = req.query(False)
184		key = res['query']['pages'].keys()[0]
185		url = res['query']['pages'][key]['imageinfo'][0]['url']
186		if not location:
187			location = self.title.split(':', 1)[1]
188		opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.site.cookies))
189		headers = { "User-agent": self.site.useragent }
190		request = urllib2.Request(url, None, headers)
191		data = opener.open(request)
192		f = open(location, 'wb', 0)
193		f.write(data.read())
194		f.close()
195		return location
196
197	def upload(self, fileobj=None, comment='', url=None, ignorewarnings=False, watch=False):
198		"""Upload a file, requires the "poster" module
199
200		fileobj - A file object opened for reading
201		comment - The log comment, used as the inital page content if the file
202		doesn't already exist on the wiki
203		url - A URL to upload the file from, if allowed on the wiki
204		ignorewarnings - Ignore warnings about duplicate files, etc.
205		watch - Add the page to your watchlist
206
207		"""
208		if not api.canupload and fileobj:
209			raise UploadError("The poster module is required for file uploading")
210		if not fileobj and not url:
211			raise UploadError("Must give either a file object or a URL")
212		if fileobj and url:
213			raise UploadError("Cannot give a file and a URL")
214		params = {'action':'upload',
215			'comment':comment,
216			'filename':self.unprefixedtitle,
217			'token':self.getToken('edit') # There's no specific "upload" token
218		}
219		if url:
220			params['url'] = url
221		else:
222			params['file'] = fileobj
223		if ignorewarnings:
224			params['ignorewarnings'] = ''
225		if watch:
226			params['watch'] = ''
227		req = api.APIRequest(self.site, params, write=True, multipart=bool(fileobj))
228		res = req.query()
229		if 'upload' in res and res['upload']['result'] == 'Success':
230			self.wikitext = ''
231			self.links = []
232			self.templates = []
233			self.exists = True
234		return res
235
236
237