# -*- coding: utf-8 -*- """ TracFullBlogPluginRPC: RPC interface. All rights reserved. See README-LICENSE.txt in project root. Copyright (C) 2012 Samuel Degrande - CNRS, Samuel.Degrande@lifl.fr """ import os from datetime import datetime from trac.core import * from trac.resource import Resource, ResourceNotFound from trac.attachment import Attachment from trac.web.chrome import web_context from trac.wiki.formatter import format_to_html from tracrpc.api import IXMLRPCHandler, Binary from tracrpc.util import to_utimestamp from tracfullblog.model import BlogPost, BlogComment, get_blog_posts __all__ = ['BlogRPC'] __docformat__ = 'restructuredtext en' class BlogRPC(Component): """ Superset of the [http://www.jspwiki.org/Wiki.jsp?page=WikiRPCInterface2 WikiRPC API] for access to !TracFullBlog posts. """ implements(IXMLRPCHandler) # IXMLRPCHandler methods def xmlrpc_namespace(self): return 'blog' def xmlrpc_methods(self): yield ('BLOG_VIEW', ((list,),), self.getRPCVersionSupported) yield (None, ((dict, datetime),), self.getRecentChanges) yield (None, ((str, str), (str, str, int),), self.getPage) yield (None, ((str, str, int),), self.getPage, 'getPageVersion') yield (None, ((str, str), (str, str, int),), self.getPageHTML) yield (None, ((str, str, int),), self.getPageHTML, 'getPageHTMLVersion') yield (None, ((dict, str), (dict, str, int),), self.getPageInfo) yield (None, ((dict, str, int),), self.getPageInfo, 'getPageInfoVersion') yield (None, ((list, str),), self.listAttachments) yield (None, ((Binary, str),), self.getAttachment) yield (None, ((list, str),), self.listComments) yield (None, ((str, str, int),), self.getComment) yield (None, ((str, str, int),), self.getCommentHTML) # Helpers def _get_post(self, req, postname, version=None): # Get a blog post with some access checks post = BlogPost(self.env, postname, version) req.perm(post.resource).require('BLOG_VIEW') if not post.version: msg = 'Blog post "%s" does not exist' % postname if version is not None: msg += ' at version %s' % version raise ResourceNotFound(msg) return post def _get_post_comment(self, req, postname, number): # Get the blog post to check existence and access rights post = self._get_post(req, postname) comment = BlogComment(self.env, postname, number) if not comment.number: raise ResourceNotFound('Comment #%s of blog post "%s" does not exist' % (number, postname)) return comment def _post_info(self, name, when, author, version): return dict(name=name, lastModified=when, author=author, version=int(version)) # RPC calls methods def getRPCVersionSupported(self, req): """ Return 2 with this version of the Trac API. """ return 2 def getRecentChanges(self, req, since): """ Get list of changed blog posts since timestamp """ blog_realm = Resource('blog') req.perm(blog_realm).require('BLOG_VIEW') posts_infos = get_blog_posts(self.env, from_dt=since, all_versions=True) for name, version, time, author, title, body, category_list in posts_infos: if 'BLOG_VIEW' not in req.perm(blog_realm(id=name, version=version)): continue bp = BlogPost(self.env, name=name, version=version) yield self._post_info(name, bp.version_time, bp.version_author, version) def getPage(self, req, postname, version=None): """ Get the raw text of a blog post, at latest version. """ post = self._get_post(req, postname, version) return "= " + post.title + " =\n" + post.body def getPageInfo(self, req, postname, version=None): """ Return information about the given blog post, at latest version. """ post = self._get_post(req, postname, version) return self._post_info(post.name, post.version_time, post.version_author, post.version) def getPageHTML(self, req, postname, version=None): """ Return blog post in rendered HTML, latest version. """ post = self._get_post(req, postname, version) context = web_context(req, post.resource, absurls=True) html = format_to_html(self.env, context, post.body) return '\n

' + post.title + '

\n%s' % html def listAttachments(self, req, postname): """ List attachments on a blog post. """ # Check ACL on the blog post post = BlogPost(self.env, postname) req.perm(post.resource).require('BLOG_VIEW') for attachment in Attachment.select(self.env, 'blog', postname): if 'ATTACHMENT_VIEW' in req.perm(attachment.resource): yield postname + '/' + attachment.filename def getAttachment(self, req, path): """ Return the content of a blog post attachment """ postname, filename = os.path.split(path) attachment = Attachment(self.env, 'blog', postname, filename) req.perm(attachment.resource).require('ATTACHMENT_VIEW') return Binary(attachment.open().read()) def listComments(self, req, postname): """ List comments of a given blog post. """ post = self._get_post(req, postname) # If perm is granted to view the post, then it is also granted to # view the comments, so no need for additional checks comments = post.get_comments() for comment in comments: yield dict(id=comment.number, author=comment.author, time=comment.time) def getComment(self, req, postname, num): """ Get the raw text of comment #num of a blog post. """ comment = self._get_post_comment(req, postname, num) return comment.comment def getCommentHTML(self, req, postname, num): """ Return a blog post's comment in rendered HTML. """ comment = self._get_post_comment(req, postname, num) context = web_context(req, absurls=True) html = format_to_html(self.env, context, comment.comment) return '%s' % html