This commit is contained in:
2024-08-29 17:07:22 +08:00
commit 6032bd1237
1790 changed files with 349463 additions and 0 deletions
@@ -0,0 +1,8 @@
"""
Library to interface with the WordPress XML-RPC API.
See README for usage instructions.
"""
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import *
from . import methods
@@ -0,0 +1,150 @@
import collections
import sys
from wordpress_xmlrpc.compat import xmlrpc_client, dict_type
from wordpress_xmlrpc.exceptions import ServerConnectionError, UnsupportedXmlrpcMethodError, InvalidCredentialsError, XmlrpcDisabledError
class Client(object):
"""
Connection to a WordPress XML-RPC API endpoint.
To execute XML-RPC methods, pass an instance of an
`XmlrpcMethod`-derived class to `Client`'s `call` method.
"""
def __init__(self, url, username, password, blog_id=0, transport=None):
self.url = url
self.username = username
self.password = password
self.blog_id = blog_id
try:
self.server = xmlrpc_client.ServerProxy(url, allow_none=True, transport=transport)
self.supported_methods = self.server.mt.supportedMethods()
except xmlrpc_client.ProtocolError:
e = sys.exc_info()[1]
raise ServerConnectionError(repr(e))
def call(self, method):
if method.method_name not in self.supported_methods:
raise UnsupportedXmlrpcMethodError(method.method_name)
server_method = getattr(self.server, method.method_name)
args = method.get_args(self)
try:
raw_result = server_method(*args)
except xmlrpc_client.Fault:
e = sys.exc_info()[1]
if e.faultCode == 403:
raise InvalidCredentialsError(e.faultString)
elif e.faultCode == 405:
raise XmlrpcDisabledError(e.faultString)
else:
raise
return method.process_result(raw_result)
class XmlrpcMethod(object):
"""
Base class for XML-RPC methods.
Child classes can override methods and properties to customize behavior:
Properties:
* `method_name`: XML-RPC method name (e.g., 'wp.getUserInfo')
* `method_args`: Tuple of method-specific required parameters
* `optional_args`: Tuple of method-specific optional parameters
* `results_class`: Python class which will convert an XML-RPC response dict into an object
"""
method_name = None
method_args = tuple()
optional_args = tuple()
results_class = None
def __init__(self, *args, **kwargs):
if self.method_args or self.optional_args:
if self.optional_args:
max_num_args = len(self.method_args) + len(self.optional_args)
if not (len(self.method_args) <= len(args) <= max_num_args):
raise ValueError("Invalid number of parameters to %s" % self.method_name)
else:
if len(args) != len(self.method_args):
raise ValueError("Invalid number of parameters to %s" % self.method_name)
for i, arg_name in enumerate(self.method_args):
setattr(self, arg_name, args[i])
if self.optional_args:
for i, arg_name in enumerate(self.optional_args, start=len(self.method_args)):
if i >= len(args):
break
setattr(self, arg_name, args[i])
if 'results_class' in kwargs:
self.results_class = kwargs['results_class']
def default_args(self, client):
"""
Builds set of method-non-specific arguments.
"""
return tuple()
def get_args(self, client):
"""
Builds final set of XML-RPC method arguments based on
the method's arguments, any default arguments, and their
defined respective ordering.
"""
default_args = self.default_args(client)
if self.method_args or self.optional_args:
optional_args = getattr(self, 'optional_args', tuple())
args = []
for arg in (self.method_args + optional_args):
if hasattr(self, arg):
obj = getattr(self, arg)
if hasattr(obj, 'struct'):
args.append(obj.struct)
else:
args.append(obj)
args = list(default_args) + args
else:
args = default_args
return args
def process_result(self, raw_result):
"""
Performs actions on the raw result from the XML-RPC response.
If a `results_class` is defined, the response will be converted
into one or more object instances of that class.
"""
if self.results_class and raw_result:
if isinstance(raw_result, dict_type):
return self.results_class(raw_result)
elif isinstance(raw_result, collections.Iterable):
return [self.results_class(result) for result in raw_result]
return raw_result
class AnonymousMethod(XmlrpcMethod):
"""
An XML-RPC method for which no authentication is required.
"""
pass
class AuthenticatedMethod(XmlrpcMethod):
"""
An XML-RPC method for which user authentication is required.
Blog ID, username and password details will be passed from
the `Client` instance to the method call.
"""
def default_args(self, client):
return (client.blog_id, client.username, client.password)
@@ -0,0 +1,28 @@
try:
import xmlrpclib as xmlrpc_client # py2.x
except ImportError:
from xmlrpc import client as xmlrpc_client # py3.x
try:
import httplib as http_client # py2.x
except ImportError:
from http import client as http_client #py3.x
import types
try:
dict_type = types.DictType # py2.x
except AttributeError:
dict_type = dict # py3.x
try:
from ConfigParser import ConfigParser # py2.x
except ImportError:
from configparser import ConfigParser # py3.x
try:
unicode('test')
except NameError:
def unicode(s):
return s
@@ -0,0 +1,47 @@
class ServerConnectionError(Exception):
"""
An error while attempting to connect to the XML-RPC endpoint.
"""
pass
class UnsupportedXmlrpcMethodError(Exception):
"""
An error while attempting to call a method that is not
supported by the XML-RPC server.
"""
pass
class XmlrpcDisabledError(Exception):
"""
An error when XML-RPC services are disabled in WordPress.
"""
pass
class InvalidCredentialsError(Exception):
"""
An error when the XML-RPC server rejects the user's credentials
(username/password combination).
"""
pass
class FieldConversionError(Exception):
"""
An error while converting field Python value to XML-RPC value type.
Attributes:
`field_name`: name of the field
`input_value`: value that was passed to the conversion function
"""
def __init__(self, field_name, error):
self.field_name = field_name
self.error = error
def __str__(self):
return repr(self)
def __repr__(self):
return self.field_name
@@ -0,0 +1,135 @@
import datetime
from wordpress_xmlrpc.compat import xmlrpc_client
class FieldMap(object):
"""
Container for settings mapping a WordPress XML-RPC request/response struct
to a Python, programmer-friendly class.
Parameters:
`inputName`: name of the field in XML-RPC response.
`outputNames`: (optional) list of field names to use when generating new XML-RPC request. defaults to `[inputName]`
`default`: (optional) default value to use when none is supplied in XML-RPC response. defaults to `None`
`conversion`: (optional) function to convert Python value to XML-RPC value for XML-RPC request.
"""
def __init__(self, inputName, outputNames=None, default=None, conversion=None):
self.name = inputName
self.output_names = outputNames or [inputName]
self.default = default
self.conversion = conversion
def convert_to_python(self, xmlrpc=None):
"""
Extracts a value for the field from an XML-RPC response.
"""
if xmlrpc:
return xmlrpc.get(self.name, self.default)
elif self.default:
return self.default
else:
return None
def convert_to_xmlrpc(self, input_value):
"""
Convert a Python value to the expected XML-RPC value type.
"""
if self.conversion:
return self.conversion(input_value)
else:
return input_value
def get_outputs(self, input_value):
"""
Generate a set of output values for a given input.
"""
output_value = self.convert_to_xmlrpc(input_value)
output = {}
for name in self.output_names:
output[name] = output_value
return output
class IntegerFieldMap(FieldMap):
"""
FieldMap pre-configured for handling integer fields.
"""
def __init__(self, *args, **kwargs):
if 'conversion' not in kwargs:
kwargs['conversion'] = int
super(IntegerFieldMap, self).__init__(*args, **kwargs)
class DateTimeFieldMap(FieldMap):
"""
FieldMap pre-configured for handling DateTime fields.
"""
def __init__(self, *args, **kwargs):
if 'conversion' not in kwargs:
kwargs['conversion'] = xmlrpc_client.DateTime
super(DateTimeFieldMap, self).__init__(*args, **kwargs)
def convert_to_python(self, xmlrpc=None):
if xmlrpc:
# make sure we have an `xmlrpc_client.DateTime` instance
raw_value = xmlrpc.get(self.name, self.default)
if not isinstance(raw_value, xmlrpc_client.DateTime):
raw_value = xmlrpc_client.DateTime(raw_value)
# extract its timetuple and convert to datetime
try:
tt = raw_value.timetuple()
except ValueError:
# Workaround for a combination of Python and WordPress bug
# which would return a null date for Draft posts. This is not
# the case for recent versions of WP, but drafts created a that
# time still have a null date.
# The python bug is http://bugs.python.org/issue2623 and
# affects xmlrpclib when fed a timezone aware DateTime
if str(raw_value) == "00000000T00:00:00Z":
raw_value = xmlrpc_client.DateTime("00010101T00:00:00")
tt = raw_value.timetuple()
else:
raise
return datetime.datetime(*tuple(tt)[:6])
elif self.default:
return self.default
else:
return None
class TermsListFieldMap(FieldMap):
"""
FieldMap that converts to/from WordPress objects.
"""
def __init__(self, object_class, *args, **kwargs):
self.object_class = object_class
super(TermsListFieldMap, self).__init__(*args, **kwargs)
def convert_to_python(self, xmlrpc=None):
if xmlrpc and self.name in xmlrpc:
values = []
for value in xmlrpc.get(self.name):
values.append(self.object_class(value))
return values
else:
return []
def convert_to_xmlrpc(self, input_value):
if input_value:
values = {}
for term in input_value:
if term.taxonomy not in values:
values[term.taxonomy] = []
values[term.taxonomy].append(int(term.id))
return values
else:
return None
@@ -0,0 +1,10 @@
"""
Implementations of standard WordPress XML-RPC APIs.
"""
from wordpress_xmlrpc.methods import posts
from wordpress_xmlrpc.methods import pages
from wordpress_xmlrpc.methods import demo
from wordpress_xmlrpc.methods import users
from wordpress_xmlrpc.methods import options
from wordpress_xmlrpc.methods import comments
from wordpress_xmlrpc.methods import media
@@ -0,0 +1,126 @@
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import WordPressComment
class GetComment(AuthenticatedMethod):
"""
Retrieve an individual comment.
Parameters:
`comment_id`: ID of the comment to retrieve.
Returns: :class:`WordPressPost` instance.
"""
method_name = 'wp.getComment'
method_args = ('comment_id',)
results_class = WordPressComment
class NewComment(AuthenticatedMethod):
"""
Create a new comment on a post.
Parameters:
`post_id`: The id of the post to add a comment to.
`comment`: A :class:`WordPressComment` instance with at least the `content` value set.
Returns: ID of the newly-created comment (an integer).
"""
method_name = 'wp.newComment'
method_args = ('post_id', 'comment')
class NewAnonymousComment(AnonymousMethod):
"""
Create a new comment on a post without authenticating.
NOTE: Requires support on the blog by setting the following filter in a plugin or theme:
add_filter( 'xmlrpc_allow_anonymous_comments', '__return_true' );
Parameters:
`post_id`: The id of the post to add a comment to.
`comment`: A :class:`WordPressComment` instance with at least the `content` value set.
Returns: ID of the newly-created comment (an integer).
"""
method_name = 'wp.newComment'
method_args = ('post_id', 'comment')
class EditComment(AuthenticatedMethod):
"""
Edit an existing comment.
Parameters:
`comment_id`: The id of the comment to edit.
`comment`: A :class:`WordPressComment` instance with at least the `content` value set.
Returns: `True` on successful edit.
"""
method_name = 'wp.editComment'
method_args = ('comment_id', 'comment')
class DeleteComment(AuthenticatedMethod):
"""
Delete an existing comment.
Parameters:
`comment_id`: The id of the comment to be deleted.
Returns: `True` on successful deletion.
"""
method_name = 'wp.deleteComment'
method_args = ('comment_id',)
class GetCommentStatusList(AuthenticatedMethod):
"""
Retrieve the set of possible blog comment statuses (e.g., "approve," "hold," "spam").
Parameters:
None
Returns: `dict` of values and their pretty names.
Example:
>>> client.call(GetCommentStatusList())
{'hold': 'Unapproved', 'approve': 'Approved', 'spam': 'Spam'}
"""
method_name = 'wp.getCommentStatusList'
class GetCommentCount(AuthenticatedMethod):
"""
Retrieve comment count for a specific post.
Parameters:
`post_id`: The id of the post to retrieve comment count for.
Returns: `dict` of comment counts for the post divided by comment status.
Example:
>>> client.call(GetCommentCount(1))
{'awaiting_moderation': '2', 'total_comments': 23, 'approved': '18', 'spam': 3}
"""
method_name = 'wp.getCommentCount'
method_args = ('post_id',)
class GetComments(AuthenticatedMethod):
"""
Gets a set of comments for a post.
Parameters:
`filter`: a `dict` with the following values:
* `post_id`: the id of the post to retrieve comments for
* `status`: type of comments of comments to retrieve (optional, defaults to 'approve')
* `number`: number of comments to retrieve (optional, defaults to 10)
* `offset`: retrieval offset (optional, defaults to 0)
Returns: `list` of :class:`WordPressComment` instances.
"""
method_name = 'wp.getComments'
method_args = ('filter',)
results_class = WordPressComment
@@ -0,0 +1,10 @@
from wordpress_xmlrpc.base import *
class SayHello(AnonymousMethod):
method_name = 'demo.sayHello'
class AddTwoNumbers(AnonymousMethod):
method_name = 'demo.addTwoNumbers'
method_args = ('number1', 'number2')
@@ -0,0 +1,55 @@
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import WordPressMedia
class GetMediaLibrary(AuthenticatedMethod):
"""
Retrieve filtered list of media library items.
Parameters:
`filter`: `dict` with optional keys:
* `number`: number of media items to retrieve
* `offset`: query offset
* `parent_id`: ID of post the media item is attached to.
Use empty string (default) to show all media items.
Use `0` to show unattached media items.
* `mime_type`: file mime-type to filter by (e.g., 'image/jpeg')
Returns: `list` of :class:`WordPressMedia` instances.
"""
method_name = 'wp.getMediaLibrary'
method_args = ('filter',)
results_class = WordPressMedia
class GetMediaItem(AuthenticatedMethod):
"""
Retrieve an individual media item.
Parameters:
`attachment_id`: ID of the media item.
Returns: :class:`WordPressMedia` instance.
"""
method_name = 'wp.getMediaItem'
method_args = ('attachment_id',)
results_class = WordPressMedia
class UploadFile(AuthenticatedMethod):
"""
Upload a file to the blog.
Note: the file is not attached to or inserted into any blog posts.
Parameters:
`data`: `dict` with three items:
* `name`: filename
* `type`: MIME-type of the file
* `bits`: base-64 encoded contents of the file. See xmlrpclib.Binary()
* `overwrite` (optional): flag to override an existing file with this name
Returns: `dict` with keys `id`, `file` (filename), `url` (public URL), and `type` (MIME-type).
"""
method_name = 'wp.uploadFile'
method_args = ('data',)
@@ -0,0 +1,35 @@
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import WordPressOption
class GetOptions(AuthenticatedMethod):
"""
Retrieve list of blog options.
Parameters:
`options`: `list` of option names to retrieve; if empty, all options will be retrieved
Returns: `list` of :class:`WordPressOption` instances.
"""
method_name = 'wp.getOptions'
method_args = ('options',)
def process_result(self, options_dict):
options = []
for key, value in options_dict.items():
value['name'] = key
options.append(WordPressOption(value))
return options
class SetOptions(GetOptions):
"""
Update the value of an existing blog option.
Parameters:
`options`: `dict` of key/value pairs
Returns: `list` of :class:`WordPressOption` instances representing the updated options.
"""
method_name = 'wp.setOptions'
method_args = ('options',)
@@ -0,0 +1,33 @@
from wordpress_xmlrpc.base import *
class GetPageStatusList(AuthenticatedMethod):
"""
Retrieve the set of possible blog page statuses (e.g., "draft," "private," "publish").
Parameters:
None
Returns: `dict` of values and their pretty names.
Example:
>>> client.call(GetPageStatusList())
{'draft': 'Draft', 'private': 'Private', 'publish': 'Published'}
"""
method_name = 'wp.getPageStatusList'
class GetPageTemplates(AuthenticatedMethod):
"""
Retrieve the list of blog templates.
Parameters:
None
Returns: `dict` of values and their paths.
Example:
>>> client.call(GetPageTemplates())
{'Default': 'default', 'Sidebar Template': 'sidebar-page.php', 'Showcase Template': 'showcase.php'}
"""
method_name = 'wp.getPageTemplates'
@@ -0,0 +1,177 @@
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import WordPressPost, WordPressPostType
class GetPosts(AuthenticatedMethod):
"""
Retrieve posts from the blog.
Parameters:
`filter`: optional `dict` of filters:
* `number`
* `offset`
* `orderby`
* `order`: 'ASC' or 'DESC'
* `post_type`: Defaults to 'post'
* `post_status`
Returns: `list` of :class:`WordPressPost` instances.
"""
method_name = 'wp.getPosts'
optional_args = ('filter', 'fields')
results_class = WordPressPost
class GetPost(AuthenticatedMethod):
"""
Retrieve an individual blog post.
Parameters:
`post_id`: ID of the blog post to retrieve.
Returns: :class:`WordPressPost` instance.
"""
method_name = 'wp.getPost'
method_args = ('post_id',)
optional_args = ('fields',)
results_class = WordPressPost
class NewPost(AuthenticatedMethod):
"""
Create a new post on the blog.
Parameters:
`content`: A :class:`WordPressPost` instance with at least the `title` and `content` values set.
Returns: ID of the newly-created blog post (an integer).
"""
method_name = 'wp.newPost'
method_args = ('content',)
class EditPost(AuthenticatedMethod):
"""
Edit an existing blog post.
Parameters:
`post_id`: ID of the blog post to edit.
`content`: A :class:`WordPressPost` instance with the new values for the blog post.
Returns: `True` on successful edit.
"""
method_name = 'wp.editPost'
method_args = ('post_id', 'content')
class DeletePost(AuthenticatedMethod):
"""
Delete a blog post.
Parameters:
`post_id`: ID of the blog post to delete.
Returns: `True` on successful deletion.
"""
method_name = 'wp.deletePost'
method_args = ('post_id', )
class GetPostStatusList(AuthenticatedMethod):
"""
Retrieve the set of possible blog post statuses (e.g., "draft," "private," "publish").
Parameters:
None
Returns: `dict` of values and their pretty names.
Example:
>>> client.call(GetPostStatusList())
{'draft': 'Draft', 'private': 'Private', 'pending': 'Pending Review', 'publish': 'Published'}
"""
method_name = 'wp.getPostStatusList'
class GetPostFormats(AuthenticatedMethod):
"""
Retrieve the set of post formats used by the blog.
Parameters:
None
Returns: `dict` containing a `dict` of all blog post formats (`all`)
and a list of formats `supported` by the theme.
Example:
>>> client.call(GetPostFormats())
{'all': {'status': 'Status', 'quote': 'Quote', 'image': 'Image', 'aside': 'Aside', 'standard': 'Standard', 'link': 'Link', 'chat': 'Chat', 'video': 'Video', 'audio': 'Audio', 'gallery': 'Gallery'},
'supported': ['aside', 'link', 'gallery', 'status', 'quote', 'image']}
"""
method_name = 'wp.getPostFormats'
def get_args(self, client):
args = super(GetPostFormats, self).get_args(client)
args += ({'show-supported': True},)
return args
class GetPostTypes(AuthenticatedMethod):
"""
Retrieve a list of post types used by the blog.
Parameters:
None
Returns: `dict` with names as keys and :class:`WordPressPostType` instances as values.
"""
method_name = 'wp.getPostTypes'
results_class = WordPressPostType
def process_result(self, raw_result):
result = {}
for name, raw_value in raw_result.items():
result[name] = self.results_class(raw_value)
return result
class GetPostType(AuthenticatedMethod):
"""
Retrieve an individual blog post type.
Parameters:
`post_type`: Name of the blog post type to retrieve.
Returns: :class:`WordPressPostType` instance.
"""
method_name = 'wp.getPostType'
method_args = ('post_type',)
results_class = WordPressPostType
class GetRevisions(AuthenticatedMethod):
"""
Retrieve all revisions of a post.
Parameters:
`post_id`: ID of the post.
Returns: `list` of :class:`WordPressPost` instances.
"""
method_name = 'wp.getRevisions'
method_args = ('post_id',)
optional_args = ('fields',)
results_class = WordPressPost
class RestoreRevision(AuthenticatedMethod):
"""
Restores a post to a previous revision.
Parameters:
`revision_id`: ID of the revision to revert to.
Returns: `True` on successful reversion.
"""
method_name = 'wp.restoreRevision'
method_args = ('revision_id',)
@@ -0,0 +1,111 @@
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import WordPressTaxonomy, WordPressTerm
class GetTaxonomies(AuthenticatedMethod):
"""
Retrieve the list of available taxonomies for the blog.
Parameters:
None
Returns: `list` of :class:`WordPressTaxonomy` instances.
"""
method_name = 'wp.getTaxonomies'
results_class = WordPressTaxonomy
class GetTaxonomy(AuthenticatedMethod):
"""
Retrieve an individual taxonomy.
Parameters:
`taxonomy`: name of the taxonomy
Returns: :class:`WordPressTaxonomy` instance.
"""
method_name = 'wp.getTaxonomy'
method_args = ('taxonomy',)
results_class = WordPressTaxonomy
class GetTerms(AuthenticatedMethod):
"""
Retrieve the list of available terms for a taxonomy.
Parameters:
`taxonomy`: name of the taxonomy
`filter`: optional `dict` of filters:
* `number`
* `offset`
* `orderby`
* `order`: 'ASC' or 'DESC'
* `hide_empty`: Whether to return terms with count==0
* `search`: Case-insensitive search on term names
Returns: `list` of :class:`WordPressTerm` instances.
"""
method_name = 'wp.getTerms'
method_args = ('taxonomy',)
optional_args = ('filter',)
results_class = WordPressTerm
class GetTerm(AuthenticatedMethod):
"""
Retrieve an individual term.
Parameters:
`taxonomy`: name of the taxonomy
`term_id`: ID of the term
Returns: :class:`WordPressTerm` instance.
"""
method_name = 'wp.getTerm'
method_args = ('taxonomy', 'term_id')
results_class = WordPressTerm
class NewTerm(AuthenticatedMethod):
"""
Create new term.
Parameters:
`term`: instance of :class:`WordPressTerm`
Returns: ID of newly-created term (an integer).
"""
method_name = 'wp.newTerm'
method_args = ('term',)
class EditTerm(AuthenticatedMethod):
"""
Edit an existing term.
Parameters:
`term_id`: ID of the term to edit.
`term`: A :class:`WordPressTerm` instance with the new values for the term.
Returns: `True` on successful edit.
"""
method_name = 'wp.editTerm'
method_args = ('term_id', 'term')
class DeleteTerm(AuthenticatedMethod):
"""
Delete a term.
Parameters:
`taxonomy`: name of the taxonomy
`term_id`: ID of the term to delete.
Returns: `True` on successful deletion.
"""
method_name = 'wp.deleteTerm'
method_args = ('taxonomy', 'term_id')
@@ -0,0 +1,98 @@
from wordpress_xmlrpc.base import *
from wordpress_xmlrpc.wordpress import WordPressBlog, WordPressAuthor, WordPressUser
class GetUsers(AuthenticatedMethod):
"""
Retrieve list of users in the blog.
Parameters:
`filter`: optional `dict` of filters:
* `number`
* `offset`
* `role`
`fields`: optional `list` of fields to return. Specific fields, or groups 'basic' or 'all'.
Returns: `list` of :class:`WordPressUser` instances.
"""
method_name = 'wp.getUsers'
optional_args = ('filter', 'fields')
results_class = WordPressUser
class GetUser(AuthenticatedMethod):
"""
Retrieve an individual user.
Parameters:
`user_id`: ID of the user
`fields`: (optional) `list` of fields to return. Specific fields, or groups 'basic' or 'all'.
Returns: :class:`WordPressUser` instance.
"""
method_name = 'wp.getUser'
method_args = ('user_id',)
optional_args = ('fields',)
results_class = WordPressUser
class GetProfile(AuthenticatedMethod):
"""
Retrieve information about the connected user.
Parameters:
None
Returns: instance of :class:`WordPressUser` representing the user whose credentials are being used with the XML-RPC API.
"""
method_name = 'wp.getProfile'
results_class = WordPressUser
class EditProfile(AuthenticatedMethod):
"""
Edit profile fields of the connected user.
Parameters:
`user`: `WordPressUser` instance.
Returns: `True` on successful edit.
"""
method_name = 'wp.editProfile'
method_args = ('user',)
class GetUserInfo(GetProfile):
"""Alias for GetProfile for backwards compatibility"""
pass
class GetUsersBlogs(AuthenticatedMethod):
"""
Retrieve list of blogs that this user belongs to.
Parameters:
None
Returns: `list` of :class:`WordPressBlog` instances.
"""
method_name = 'wp.getUsersBlogs'
results_class = WordPressBlog
def get_args(self, client):
# strip off first (blog_id) parameter
return super(GetUsersBlogs, self).get_args(client)[1:]
class GetAuthors(AuthenticatedMethod):
"""
Retrieve list of authors in the blog.
Parameters:
None
Returns: `list` of :class:`WordPressAuthor` instances.
"""
method_name = 'wp.getAuthors'
results_class = WordPressAuthor
@@ -0,0 +1,16 @@
from wordpress_xmlrpc.compat import xmlrpc_client, http_client
class ProxiedTransport(xmlrpc_client.Transport):
def set_proxy(self, proxy):
self.proxy = proxy
def make_connection(self, host):
self.host = host
return http_client.HTTP(self.proxy)
def send_request(self, connection, handler, request_body):
connection.putrequest("POST", "http://%s%s" % (self.host, handler))
def send_host(self, connection, host):
connection.putheader('Host', host)
@@ -0,0 +1,260 @@
import sys
from .compat import *
from .fieldmaps import FieldMap, IntegerFieldMap, DateTimeFieldMap, TermsListFieldMap
from wordpress_xmlrpc.exceptions import FieldConversionError
class WordPressBase(object):
"""
Base class for representing a WordPress object. Handles conversion
of an XML-RPC response to an object, and construction of a `struct`
to use in XML-RPC requests.
Child classes should define a `definition` property that contains
the list of fields and a `FieldMap` instance to handle conversion
for XML-RPC calls.
"""
definition = {}
def __init__(self, xmlrpc=None):
# create private variable containing all FieldMaps for the `definition`
self._def = {}
for key, value in self.definition.items():
# if the definition was not a FieldMap, create a simple FieldMap
if isinstance(value, FieldMap):
self._def[key] = value
else:
self._def[key] = FieldMap(value)
# convert and store the value on this instance if non-empty
try:
converted_value = self._def[key].convert_to_python(xmlrpc)
except Exception:
e = sys.exc_info()[1]
raise FieldConversionError(key, e)
if converted_value is not None:
setattr(self, key, converted_value)
@property
def struct(self):
"""
XML-RPC-friendly representation of the current object state
"""
data = {}
for var, fmap in self._def.items():
if hasattr(self, var):
data.update(fmap.get_outputs(getattr(self, var)))
return data
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, str(self).encode('utf-8'))
class WordPressTaxonomy(WordPressBase):
definition = {
'name': FieldMap('name', default=''),
'label': 'label',
'labels': 'labels',
'hierarchical': 'hierarchical',
'public': 'public',
'show_ui': 'show_ui',
'cap': 'cap',
'is_builtin': '_builtin',
'object_type': 'object_type'
}
def __str__(self):
if hasattr(self, 'name'):
return self.name
return unicode('')
class WordPressTerm(WordPressBase):
definition = {
'id': 'term_id',
'group': 'term_group',
'taxonomy': 'taxonomy',
'taxonomy_id': 'term_taxonomy_id',
'name': FieldMap('name', default=''),
'slug': 'slug',
'description': 'description',
'parent': 'parent',
'count': IntegerFieldMap('count')
}
def __str__(self):
if hasattr(self, 'name'):
return self.name
return unicode('')
class WordPressPost(WordPressBase):
definition = {
'id': 'post_id',
'user': 'post_author',
'date': DateTimeFieldMap('post_date_gmt'),
'date_modified': DateTimeFieldMap('post_modified_gmt'),
'slug': 'post_name',
'post_status': 'post_status',
'title': FieldMap('post_title', default='Untitled'),
'content': 'post_content',
'excerpt': 'post_excerpt',
'link': 'link',
'comment_status': 'comment_status',
'ping_status': 'ping_status',
'terms': TermsListFieldMap(WordPressTerm, 'terms'),
'terms_names': 'terms_names',
'custom_fields': 'custom_fields',
'enclosure': 'enclosure',
'password': 'post_password',
'post_format': 'post_format',
'thumbnail': 'post_thumbnail',
'sticky': 'sticky',
'post_type': FieldMap('post_type', default='post'),
'parent_id': 'post_parent',
'menu_order': IntegerFieldMap('menu_order'),
'guid': 'guid',
'mime_type': 'post_mime_type',
}
def __str__(self):
if hasattr(self, 'title'):
return self.title
return unicode('')
class WordPressPage(WordPressPost):
definition = dict(WordPressPost.definition, **{
'template': 'wp_page_template',
'post_type': FieldMap('post_type', default='page'),
})
class WordPressComment(WordPressBase):
definition = {
'id': 'comment_id',
'user': 'user_id',
'post': 'post_id',
'post_title': 'post_title',
'parent': 'comment_parent',
'date_created': DateTimeFieldMap('date_created_gmt'),
'status': 'status',
'content': FieldMap('content', default=''),
'link': 'link',
'author': 'author',
'author_url': 'author_url',
'author_email': 'author_email',
'author_ip': 'author_ip',
}
def __str__(self):
if hasattr(self, 'content'):
return self.content
return unicode('')
class WordPressBlog(WordPressBase):
definition = {
'id': 'blogid',
'name': FieldMap('blogName', default=''),
'url': 'url',
'xmlrpc': 'xmlrpc',
'is_admin': FieldMap('isAdmin', default=False),
}
def __str__(self):
if hasattr(self, 'name'):
return self.name
return unicode('')
class WordPressAuthor(WordPressBase):
definition = {
'id': 'user_id',
'user_login': 'user_login',
'display_name': FieldMap('display_name', default=''),
}
def __str__(self):
if hasattr(self, 'display_name'):
return self.display_name
return unicode('')
class WordPressUser(WordPressBase):
definition = {
'id': 'user_id',
'username': 'username',
'roles': 'roles',
'nickname': 'nickname',
'url': 'url',
'first_name': 'first_name',
'last_name': 'last_name',
'registered': DateTimeFieldMap('registered'),
'bio': 'bio',
'email': 'email',
'nicename': 'nicename',
'display_name': 'display_name',
}
def __str__(self):
if hasattr(self, 'nickname'):
return self.nickname
return unicode('')
class WordPressMedia(WordPressBase):
definition = {
'id': 'attachment_id',
'parent': 'parent',
'title': FieldMap('title', default=''),
'description': 'description',
'caption': 'caption',
'date_created': DateTimeFieldMap('date_created_gmt'),
'link': 'link',
'thumbnail': 'thumbnail',
'metadata': 'metadata',
}
def __str__(self):
if hasattr(self, 'title'):
return self.title
return unicode('')
class WordPressOption(WordPressBase):
definition = {
'name': FieldMap('name', default=''),
'description': 'desc',
'value': FieldMap('value', default=''),
'read_only': FieldMap('readonly', default=False),
}
def __str__(self):
if hasattr(self, 'name') and hasattr(self, 'value'):
return '%s="%s"' % (self.name, self.value)
return unicode('')
class WordPressPostType(WordPressBase):
definition = {
'name': 'name',
'label': FieldMap('label', default=''),
'labels': 'labels',
'cap': 'cap',
'map_meta_cap': 'map_meta_cap',
'hierarchical': 'hierarchical',
'menu_icon': 'menu_icon',
'menu_position': 'menu_position',
'public': 'public',
'show_in_menu': 'show_in_menu',
'taxonomies': 'taxonomies',
'is_builtin': '_builtin',
'supports': 'supports',
}
def __str__(self):
if hasattr(self, 'name'):
return self.name
return unicode('')