-
Notifications
You must be signed in to change notification settings - Fork 1
/
uploader.py
executable file
·193 lines (157 loc) · 8.75 KB
/
uploader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/python2
# coding=utf-8
import six
import mimetypes
try:
import xmlrpc.client as client
except ImportError as e:
import xmlrpclib as client
from datetime import datetime
from os import path
from sys import stderr
try:
from urllib.parse import urlparse
except ImportError as e:
import urlparse
class Uploader:
def __init__( self ):
'''Do any initialization stuff common to all derived classes.'''
#nothing to do so far
def upload( self ):
'''Do stuff common to all derived classes' upload() functions. This function does not itself upload anything.'''
self.blah = 0
class DrupalUploader( Uploader ):
def __init__( self ):
Uploader.__init__( self )
def upload( self ):
self.blah = 0
class WordPressUploader( Uploader ):
def __init__( self, uri, username, password, blogID = None ):
'''Connect to the server.
Relevant WordPress docs:
https://codex.WordPress.org/XML-RPC_WordPress_API/Users#wp.getUsersBlogs
Args:
uri: The URI to connect to with XMLRPC. This should be the xmlrpc.php file in your WordPress install directory, e.g. https://www.example.com/xmlrpc.php . SECURITY WARNING: If this URI does not use encryption (e.g. starting with httpS), then USERNAMES and PASSWORDS will be transmitted in CLEARTEXT!
username: The username under which these posts are made.
password: The password for the account.
blogID: The ID number of the blog to post to. Will be auto-detected if not specified.'''
Uploader.__init__( self )
self.uri = str( uri )
#if not self.uri.startswith( "https://" ):
if urlparse( self.uri ).scheme not in [ "file", "https", "sftp", "shttp", "sips", "snews", "svn+ssh" ]:
six.print_( "SECURITY WARNING: URI does not use any known secure protocol. USERNAMES and PASSWORDS are being transmitted in CLEARTEXT!", "\nThis is the URI:", uri, file = stderr )
self.username = str( username )
self.password = str( password )
self.server = client.ServerProxy( self.uri )
try:
blogInfo = self.server.wp.getUsersBlogs( self.username, self.password )
except client.Fault as fault: #How is a fault different from an error? Beats me.
six.print_( "A fault occurred. Fault code %d." % fault.faultCode, file = stderr )
six.print_( "Fault string: %s" % fault.faultString, file = stderr )
six.print_( "Username:", self.username, "Password:", self.password )
return
except client.ProtocolError as error:
six.print_( "A protocol error occurred. URL: %s" % error.url, file = stderr )
six.print_( "HTTP(S) headers: %s" % error.headers, file = stderr )
six.print_( "Error code: %d" % error.errcode, file = stderr )
six.print_( "Error message: %s" % error.errmsg, file = stderr )
return
except client.Error as error:
six.print_( "An error occurred:", error, file = stderr )
return
if blogID is None:
for blog in blogInfo:
if blog[ "xmlrpc" ] == self.uri:
blogID = blog[ "blogid" ]
break
else:
blogID = int( blogID )
if blogID is None:
blogID = 0
self.blogID = blogID
for blog in blogInfo:
if self.blogID == blog[ "blogid" ]:
if self.uri != blog[ "xmlrpc" ]:
self.uri = blog[ "xmlrpc" ]
self.server = client.ServerProxy( self.uri )
def upload( self, inputFileName = "default out.png", shortComicTitle = "", longComicTitle = None, postCategories = None, postTime = datetime.now(), postStatus = "draft", transcript = None, originalURL = None, silence = False ):
'''Upload the comic (must be a readable image file) as a blog post.
Relevant WordPress docs:
https://codex.WordPress.org/XML-RPC_WordPress_API/Posts#wp.newPost
https://codex.WordPress.org/Function_Reference/wp_insert_post
https://codex.WordPress.org/XML-RPC_WordPress_API/Media#wp.uploadFile
https://codex.wordpress.org/Post_Formats
Args:
inputFileName: The name of the image file to upload. Defaults to "default out.png"
shortComicTitle: The title of the comic, in short form. Will be the first part of the image file's name as uploaded to the server (the local copy will not be renamed). Defaults to the empty string.
longComicTitle: The title of the comic, in long form. Will be the first part of the post's title. Defaults to whatever shortComicTitle is.
postCategories: A list of strings, each string representing a post category. Defaults to one string, longComicTitle.
postTime: The timestamp to be attached to the post. Defaults to now. The date (excluding time) will also be used as the second parts of the image file's name as uploaded to the server (the local copy will not be renamed) and of the post's title.
postStatus: A string indicating the status to assign to the post. Defaults to "draft". See the relevant WP docs for a list of acceptable values.
transcript: A string containing the text of the comic being uploaded. Will be read from "default out.txt" if not specified.
originalURL: The URL of the source comic image from which the current comic was generated. Defaults to None.
silence: A Boolean indicating whether to keep quiet (True) or output status messages to standard output (False)
Returns:
0 if everything worked, no errors.
WordPress-provided fault codes if a fault (e.g. invalid username/password) occurs.
Server-provided error codes if a protocol error (i.e. 404: file not found) occurs.
-1 if some other type of error occurs.'''
inputFileName = str( inputFileName )
shortComicTitle = str( shortComicTitle )
postStatus = str( postStatus )
if longComicTitle is None:
longComicTitle = shortComicTitle
else:
longComicTitle = str( longComicTitle )
if postCategories is None:
postCategories = [ longComicTitle ]
post = dict()
dateString = datetime.date( postTime ).isoformat()
post[ "post_title" ] = longComicTitle + " " + dateString
post[ "post_date" ] = client.DateTime( postTime ) #WordPress post 'dates' include time of day
post[ "post_status" ] = postStatus
post[ "post_format" ] = "image" #WordPress themes are not required to support every format. If posts from this program look wonky, try commenting this line out or changing to a different format string.
post[ "comment_status" ] = "open" #NOTE: Workaround for a Wordpress bug. WordPress 4.3.1 by default disallows comments on posts sent using XML PRC, even if its settings are supposed to allow them by default.
post[ "terms_names" ] = dict()
post[ "terms_names" ][ "category" ] = postCategories
fileData = dict()
fileData[ "name" ] = shortComicTitle + " " + dateString + path.splitext( inputFileName )[ 1 ]
fileType = mimetypes.guess_type( inputFileName )
if fileType[ 0 ] is None:
six.print_( 'Warning: MIME type could not be guessed. Uploading with no MIME type specified.', file = stderr )
else:
fileData[ "type" ] = fileType[ 0 ]
fileHandle = open( inputFileName, "rb" )
fileData[ "bits" ] = client.Binary( fileHandle.read() )
fileHandle.close()
if transcript is None:
transcriptFileHandle = open( "default out.txt", "rt" )
transcript = ""
for line in transcriptFileHandle:
transcript += line
transcriptFileHandle.close()
try:
fileUploadResult = self.server.wp.uploadFile( self.blogID, self.username, self.password, fileData )
if not silence:
six.print_( "File upload result:", fileUploadResult )
post[ "post_content" ] = '<a href="' + fileUploadResult[ "url" ] + '"><img class="aligncenter size-full img-zoomable wp-image-' + fileUploadResult[ "id" ] + '" src="' + fileUploadResult[ "url" ] + '" alt="' + transcript + '" /></a>Click the image for full size.<p>Transcript:</p><p class="comic-transcript">' + transcript + '</p>'
if originalURL is not None:
originalURL = str( originalURL )
post[ "post_content" ] = post[ "post_content" ] + '<p class="comic-original-url"><a href="' + originalURL + '">Original</a></p>'
postUploadResult = self.server.wp.newPost( self.blogID, self.username, self.password, post )
if not silence:
six.print_( "Post upload result:", postUploadResult )
except client.Fault as fault:
six.print_( "A fault occurred. Fault code %d." % fault.faultCode, file = stderr )
six.print_( "Fault string: %s" % fault.faultString, file = stderr )
return fault.faultCode
except client.ProtocolError as error:
six.print_( "A protocol error occurred. URL: %s" % error.url, file = stderr )
six.print_( "HTTP(S) headers: %s" % error.headers, file = stderr )
six.print_( "Error code: %d" % error.errcode, file = stderr )
six.print_( "Error message: %s" % error.errmsg, file = stderr )
return error.errcode
except client.Error as error:
six.print_( "An error occurred:", error, file = stderr )
return -1
return 0