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