/shabti/templates/pyblosxom/data/blog/plugins/xmlrpc_metaweblog.py
https://bitbucket.org/gawel/shabti · Python · 537 lines · 435 code · 20 blank · 82 comment · 4 complexity · dbdf9e468a9aae2e1e6c795e9c81c3a8 MD5 · raw file
- """
- Copyright (c) 2003-2005 Ted Leung
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- options for config.py:
- if py['xmlrpc_metaweblog_ping'] == 'True' then autoping will be invoked to
- generate trackbacks and pingbacks
- Implements the metaWeblog XML-RPC interface.
- See spec here: http://www.xmlrpc.com/metaWeblogApi
- Implemented methods:
- - metaWeblog.newPost
- - metaWeblog.editPost
- - metaWeblog.getPost
- - metaWeblog.getCategories
- - metaWeblog.getRecentPosts
- """
- import os, xmlrpclib, re, time, os.path
- from Pyblosxom import tools, plugin_utils
- from Pyblosxom.entries.fileentry import FileEntry
- def cb_start(args):
- request = args["request"]
- config = request.getConfiguration()
- #
- # Pyblosxom callback API functions
- #
-
- def verify_installation(request):
- config = request.getConfiguration()
- retval = 1
- # all config properties are optional
- if not config.has_key('xmlrpc_metaweblog_ping'):
- print("missing optional property: 'xmlrpc_metaweblog_ping'")
- return retval
- def cb_xmlrpc_register(args):
- """
- Binds the methods that we handle with the function instances.
- Part of the pyblosxom callback api
- @param args: the callback arguments
- @type args: dict
- """
- args["methods"].update(
- {'metaWeblog.newPost':metaWeblog_newPost,
- 'metaWeblog.editPost':metaWeblog_editPost,
- 'metaWeblog.getPost':metaWeblog_getPost,
- 'metaWeblog.newMediaObject':metaWeblog_newMediaObject,
- 'metaWeblog.getCategories':metaWeblog_getCategories,
- 'metaWeblog.getRecentPosts':metaWeblog_getRecentPosts})
-
- return args
- def authenticate(request, username, password):
- """
- Handles authentication. This works by getting the xmlrpc dispatcher
- plugin Python module and then calling authenticate on that.
-
- @param request: the pyblosxom Request instance
- @type request: Request
-
- @param username: the username
- @type username: string
-
- @param password: the password
- @type password: string
- """
- xmlrpc_plugin = plugin_utils.get_plugin_by_name("xmlrpc")
- xmlrpc_plugin.authenticate(request, username, password)
- #
- # metaWeblog API functions
- #
- def metaWeblog_editPost(request, postid, username, password, struct, publish):
- """
- Edit an existing post
- Part of the metaWeblog API
- @param request: the pyblosxom Request instance
- @type request: Request
- @param username: the username
- @type username: string
- @param password: the password
- @type password: string
- @param struct: the metaWeblog api struct
- @type struct: dict
- @param publish: to publish (true) or not
- @type publish: boolean
- @returns an xmlrpclib boolean -- true if the edit was successful, false otherwise
- """
- logger = tools.getLogger()
- logger.debug("editPost %s %s %s" % (postid, struct, publish))
- authenticate(request, username, password)
- config = request.getConfiguration()
- ping = config.get('xmlrpc_metaweblog_ping',0)
- return xmlrpclib.Boolean(_writePost(config, username, postid, struct, publish, ping))
- def metaWeblog_newPost(request, blogid, username, password, struct, publish):
- """
- Create a new entry on the server
- Part of the metaWeblog API
- if py['xmlrpc_metaweblog_ping'] == 'True' then autoping will be invoked to
- generate trackbacks and pingbacks
- @param request: the pyblosxom Request instance
- @type request: Request
- @param username: the username
- @type username: string
- @param password: the password
- @type password: string
- @param struct: the metaWeblog API struct
- @type struct: dict
- @param publish: to publish (true) or not
- @type publish: boolean
- """
- logger = tools.getLogger()
- logger.debug("newPost %s %s %s" % (blogid, struct, publish))
- authenticate(request, username, password)
- config = request.getConfiguration()
- ping = config.get('xmlrpc_metaweblog_ping',0)
- postId = _buildPostId(request, blogid, struct)
- result = _writePost(config, username, postId, struct, publish, ping)
-
- if result:
- return postId
- else:
- return xmlrpclib.Boolean(False)
-
- def metaWeblog_getPost(request, postid, username, password):
- """
- Get a single post from the server
- Part of the metaWeblog API
- @param request: the pyblosxom Request instance
- @type request: Request
- @param postid: the id of the post
- @type postid: string
- @param username: the username
- @type username: string
- @param password: the password
- @type password: string
- @returns the post whose id is postid
- @rtype dict
- """
- logger = tools.getLogger()
- logger.debug("getPost: postid: %s" % (postid,))
- authenticate(request, username, password)
- config = request.getConfiguration()
- logger.debug("datadir = %s, file = %s.txt" % (config['datadir'], postid))
- entry = FileEntry(request, os.path.join(config['datadir'],"%s.txt" % postid), config['datadir'])
- post = { 'permaLink': "%s/%s/%s/%s#%s" % (config['base_url'], entry['yr'],entry['mo_num'],entry['da'],entry['fn']),
- 'title':entry['title'],
- 'description':entry['body'],
- 'postid':re.sub(r'^/', '', "%s/%s"% (entry['absolute_path'], entry['fn'])),
- 'categories':[entry['absolute_path']],
- 'dateCreated':xmlrpclib.DateTime(entry['w3cdate']) }
- return post
- def metaWeblog_getCategories(request, blogid, username, password):
- """
- Get the available categories
- Part of the metaWeblog API
- @param request: the pyblosxom Request instance
- @type request: Request
- @param blogid: the id of the blog
- @type blogid: string
- @param username: the username
- @type username: string
- @param password: the password
- @type password: string
- @returns list of categories (each category is a string)
- @rtype list
- """
- logger = tools.getLogger()
- logger.debug("getCategories blogid: %s" % blogid)
- authenticate(request, username, password)
- config = request.getConfiguration()
- clist = _getCategories(request)
- return clist
- def metaWeblog_getRecentPosts(request, blogid, username, password, numberOfPosts):
- """
- Get the most recent posts
- Part of the metaWeblog API
- @param request: the pyblosxom Request instance
- @type request: Request
- @param blogid: the id of the blog
- @type blogid: string
- @param username: the username
- @type username: string
- @param password: the password
- @type password: string
- @param numberOfPosts: the number of posts to retreive
- @type numberOfPosts: int
- @returns list of dicts, one per post
- @rtype list
- """
- logger = tools.getLogger()
- logger.debug("getRecentPosts blogid:%s count:%s" % (blogid, numberOfPosts))
- authenticate(request, username, password)
- config = request.getConfiguration()
- filelist = tools.Walk(request, config['datadir'], int(config['depth']), pattern=_allEntriesPattern(request))
- entryList = []
- for f in filelist:
- entry = FileEntry(request, f, config['datadir'])
- entryList.append((entry._mtime, entry))
- entryList.sort()
- entryList.reverse()
- try:
- numberOfPosts = int(numberOfPosts)
- except:
- logger.error("Couldn't convert numberOfPosts")
- numberOfPosts = 5
- entryList = [ x[1] for x in entryList ][: numberOfPosts]
- def fix_path(path):
- if path == "":
- return '/'
- else:
- return path
- posts = [ { 'permaLink': "%s/%s/%s/%s#%s" % (config['base_url'], x['yr'],x['mo_num'],x['da'],x['fn']),
- 'title':x['title'],
- 'description':x['body'],
- 'postid':re.sub(r'^/', '', "%s/%s"% (x['absolute_path'], x['fn'])),
- 'categories':[ fix_path(x['absolute_path'])],
- 'dateCreated':xmlrpclib.DateTime(x['w3cdate']) } for x in entryList ]
- return posts
- def metaWeblog_newMediaObject(request, blogid, username, password, struct):
- """
- Create a new media object
- Part of the metaWeblog API
- @param request: the pyblosxom Request instance
- @type request: Request
- @param blogid: the id of the blog
- @type blogid: string
- @param username: the username
- @type username: string
- @param password: the password
- @type password: string
- @param struct: the metaWeblog API struct
- @type struct: dict
- """
- logger = tools.getLogger()
- logger.debug("newMediaObject")
- authenticate(request, username, password)
- config = request.getConfiguration()
- name = struct['name']
- mimeType = struct['type']
- bits = struct['bits']
-
- root = config['xmlrpc_metaweblog_image_dir']
- path = os.path.join("%s/%s" % (root, name))
- logger.debug("newMediaObject: %s,%s, %s, %s " % (name, path, mimeType, bits))
- f = None
- try:
- f = open(path, 'wb')
- f.write(bits.data)
- f.close()
- except:
- if f is not None:
- f.close()
- return 0
- # return { 'url': "%s/%s%s" % (config['base_url'],config['xmlrpc_metaweblog_image_prefix'],name) }
- return { 'url': "%s/%s" % (config['base_url'],name) }
- #
- # helper functions
- #
- def _allEntriesPattern(request):
- """
- Return a regular expression pattern that includes all valid entry
- extensions and their unpublished (suffexed with '-') versions. This
- allows these entries to be displayed in getRecentPosts, and to be counted
- in _getEntryCount()
- @param request: the HTTP Request
- @type request: Request
- @returns a regular expression pattern
- @rtype string
- """
- # make sure to count file with extensions ending in -
- ext = request.getData()['extensions']
- pats = list(ext.keys()) # copy it
- for p in ext.keys():
- pats.append(p+"-")
- entryPattern = re.compile(r'.*\.(' + '|'.join(pats) + r')$')
- return entryPattern
- def _buildPostId(request, blogid, struct):
- """
- Construct the id for the post
- The algorithm used for constructing the post id is to concatenate the
- pyblosxom category (directory path, with the datadir prefix removed) with
- the count of entries. This means that postids are increasing integers.
- @param request: the HTTP Request
- @type request: Request
- @param blogid: the id of the blog
- @type blogid: string
- @param struct: the metaWeblog API struct
- @type struct: dict
- @return the post id
- @rtype string
- """
- config = request.getConfiguration()
- category = ''
- try:
- category = struct['categories'][0]
- except:
- pass
- count = _getEntryCount(request)
- if not category == '':
- postId = os.path.join(category, "%d" % count)
- else:
- postId = os.path.join("%d" % count)
- logger = tools.getLogger()
- logger.debug(postId)
- return postId
- def _getEntryCount(request):
- """
- Return a count of the number of published and unpublished
- (suffixed with '-') entries
- @param request: the HTTP Request
- @type request: Request
- @return the number of published an unpublished entries
- @rtype int
- """
- config = request.getConfiguration()
- root = config['datadir']
- elist = tools.Walk(request, root, pattern=_allEntriesPattern(request))
- elist = [mem[len(root)+1:] for mem in elist]
-
- elistmap = {}
- for mem in elist:
- mem = os.path.dirname(mem)
- elistmap[mem] = 1 + elistmap.get(mem, 0)
- mem = mem.split(os.sep)
- return len(elist)
- def _getCategories(request):
- """
- Return a list of all the categories in this pyblosxom blog,
- excluding CVS and comments directories, but including category directories
- which have no entries (newly created categories).
- Each item in the list contains the following items:
- - categoryId
- - description
- - categoryName
- - htmlUrl
- - rssUrl
- @param request: the HTTP Request
- @type request: Request
- @return a list of dicts
- @rtype list of dicts
- """
- # now make a list of all the categories
- # bypass tools.walk in order to pickup empty categories
- config = request.getConfiguration()
- root = config['datadir']
- def walk_filter(arg, dirname, files):
- if dirname==root:
- return
- if not dirname.endswith('CVS') and not dirname.startswith(root+'/comments'):
- cleanDirName = dirname.replace(root + '/', '')
- dirDescriptor = {}
- dirDescriptor["categoryId"] = cleanDirName
- dirDescriptor["description"] = cleanDirName
- dirDescriptor["categoryName"] = cleanDirName
- dirDescriptor["htmlUrl"] = config["base_url"] + "/" + cleanDirName
- dirDescriptor["rssURL"] = config["base_url"] + "/" + cleanDirName +"/?flavor=rss"
- arg.append(dirDescriptor)
-
- clist = []
- os.path.walk(root, walk_filter, clist)
- clist.append("/")
- clist.sort()
- return clist
- def _writePost(config, username, postid, struct, publish=True, ping=False):
- """
- Write a post file into pyblosxom
- @param config: the pyblosxom configuration
- @type config: config
- @param username: the username of the poster
- @type username: string
- @param postid: the id of the post to be written
- @type postid: string
- @param struct: the metaWeblog API struct
- @type struct: dict
- @param publish: to publish (true) or not
- @type publish: boolean
- @param ping: whether or not to invoke autoping (true) or not
- @type ping: boolean
- """
- root = config['datadir']
- path = os.path.join(root,"%s.txt" % postid)
- logger = tools.getLogger()
- logger.debug("path = "+path)
- if not publish:
- path += '-'
- content = "%s\n#author %s\n%s" % (struct['title'], username, struct['description'])
- try:
- atime, mtime = (0, 0)
- if os.path.isfile(path):
- atime, mtime = os.stat(path)[7:9]
- if struct.has_key('dateCreated'):
- import types
- dc = struct['dateCreated']
- if type(dc) == types.StringType:
- mtime = time.mktime(time.strptime(dc, '%Y%m%dT%H:%M:%S'))
- elif type(dc) == types.InstanceType:
- mtime = time.mktime(time.strptime(str(dc), '%Y%m%dT%H:%M:%SZ'))
-
- f = open(path,'w')
- f.write(content)
- f.close()
- if atime != 0 and mtime != 0:
- try:
- os.utime(path, (atime, mtime))
- except:
- pass
- except:
- if f is not None:
- f.close()
- return 0
- if ping:
- try:
- import autoping
- os.chdir(root)
- autoping.autoping("%s.txt" % postid)
- except OSError:
- logger.error("autoping failed for %s with OSError %" % postid)
- pass
- except:
- logger.error("autoping failed for %s" % path)
- pass
- return 1