def cb_renderer(args): request = args['request'] config = request.getConfiguration() http = request.getHttp() form = http['form'] # intercept ajax requests with our renderer if (form.has_key('ajax') and http.get('REQUEST_METHOD', '') == 'POST'): data = '&'.join(['%s=%s' % (arg.name, arg.value) for arg in form.list]) tools.getLogger().info('AJAX request: %s' % data) return AjaxRenderer(request, request.getData())
def cb_end(args): if timestamps_to_save: datadir = args['request'].getConfiguration()['datadir'] datadir = os.path.normpath(datadir) tsfile = file(os.path.join(datadir, 'timestamps'), 'a') for filename, mtime in timestamps_to_save.items(): time_str = time.strftime('%Y-%m-%d-%H-%M', time.localtime(mtime)) # strip the datadir prefix and directory separator slash filename = filename[len(datadir) + 1:] tsfile.write('%s %s\n' % (time_str, filename)) tools.getLogger().info('Saved mtime %s for %s' % (time_str, filename)) tsfile.close() timestamps_to_save.clear()
def cb_end(args): if timestamps_to_save: datadir = args["request"].getConfiguration()["datadir"] datadir = os.path.normpath(datadir) tsfile = file(os.path.join(datadir, "timestamps"), "a") for filename, mtime in timestamps_to_save.items(): time_str = time.strftime("%Y-%m-%d-%H-%M", time.localtime(mtime)) # strip the datadir prefix and directory separator slash filename = filename[len(datadir) + 1 :] tsfile.write("%s %s\n" % (time_str, filename)) tools.getLogger().info("Saved mtime %s for %s" % (time_str, filename)) tsfile.close() timestamps_to_save.clear()
def start_openid_auth(request, openid_url): form = request.getForm() config = request.getConfiguration() data = request.getData() import_and_initialize() # Try to start OpenID verification session = Session(request) consumer = get_openid_consumer(request, session) if consumer is None: return if check_url_rejected(config, openid_url, 'identity'): tools.getLogger().info('Rejected %r' % (openid_url, )) raise OpenIDCommentError( 'That identity is not allowed to post to this blog') auth_req = consumer.begin(openid_url) # Make sure that the server and identity URL are allowed by the config server_id = auth_req.endpoint.getLocalID() server_url = auth_req.endpoint.server_url if (check_url_rejected(config, server_id, 'identity') or check_url_rejected(config, server_url, 'server')): tools.getLogger().info('Rejected %r or %r' % (server_id, server_url)) raise OpenIDCommentError( 'That identity is not allowed to post to this blog') if 'body' not in form: raise OpenIDCommentError('Comment body required') if 'openid_trust_root' in config: trust_root = config['openid_trust_root'] else: trust_root = config['base_url'] # Save the data for the return populate_comment_session(session, request, auth_req.assoc, trust_root) args = { sid_field: session.id(), 'showcomments': 'yes', } return_to = appendArgs(data['url'], args) redirect_url = auth_req.redirectURL(trust_root, return_to) renderer = data['renderer'] renderer.addHeader('Status', '302 Found') renderer.addHeader("Location", redirect_url) renderer.showHeaders() renderer.rendered = 1
def cb_handle(args): global OpenIDServer global oidserver request = args['request'] config = request.getConfiguration() data = request.getData() http = request.getHttp() form = FieldStorage_to_dict(http['form']) response = request.getResponse() trigger = config.get('openid_trigger', DEFAULT_TRIGGER) # the libs are big, and importing them incurs a performance hit, so only # import them if we're actually handling the request. if http['PATH_INFO'].startswith(trigger): if not import_and_initialize(request): return False if http['PATH_INFO'] == trigger: # handle the openid request try: oidrequest = oidserver.decodeRequest(form) if not oidrequest: # cb_filelist() will show an info page about this endpoint return False if oidrequest.mode in ['checkid_immediate', 'checkid_setup']: if has_cookie(request): tools.getLogger().info( 'Has cookie, confirming identity to ' + oidrequest.trust_root) return respond(request, oidrequest.answer(True)) elif oidrequest.immediate: oidresponse = oidrequest.answer( False, server_url=config['base_url'] + trigger) return respond(request, oidresponse) else: data['openid_login_oidrequest'] = oidrequest # cb_filelist will show a login page return False elif oidrequest.mode in ['associate', 'check_authentication']: return respond(request, oidserver.handleRequest(oidrequest)) else: # cb_filelist() will show an info page about this endpoint return False except OpenIDServer.ProtocolError, why: return respond(request, why)
def start_openid_auth(request, openid_url): form = request.getForm() config = request.getConfiguration() data = request.getData() import_and_initialize() # Try to start OpenID verification session = Session(request) consumer = get_openid_consumer(request, session) if consumer is None: return if check_url_rejected(config, openid_url, 'identity'): tools.getLogger().info('Rejected %r' % (openid_url,)) raise OpenIDCommentError( 'That identity is not allowed to post to this blog') auth_req = consumer.begin(openid_url) # Make sure that the server and identity URL are allowed by the config server_id = auth_req.endpoint.getLocalID() server_url = auth_req.endpoint.server_url if (check_url_rejected(config, server_id, 'identity') or check_url_rejected(config, server_url, 'server')): tools.getLogger().info('Rejected %r or %r' % (server_id, server_url)) raise OpenIDCommentError( 'That identity is not allowed to post to this blog') if 'body' not in form: raise OpenIDCommentError('Comment body required') if 'openid_trust_root' in config: trust_root = config['openid_trust_root'] else: trust_root = config['base_url'] # Save the data for the return populate_comment_session(session, request, auth_req.assoc, trust_root) args = {sid_field: session.id(), 'showcomments': 'yes',} return_to = appendArgs(data['url'], args) redirect_url = auth_req.redirectURL(trust_root, return_to) renderer = data['renderer'] renderer.addHeader('Status', '302 Found') renderer.addHeader("Location", redirect_url) renderer.showHeaders() renderer.rendered = 1
def get_openid_consumer(request, session): """Initialize an OpenID store for authenticating comments. @param request: Pyblosxom request object @param session: session @type session: instance of C{Session} @return: An instance of OpenIDConsumer @rtype: OpenID consumer store or C{None} """ config = request.getConfiguration() logger = tools.getLogger() store_dir = config.get('openid_store_dir') if store_dir is None: logger.error('You must define openid_store_dir in your ' 'config to enable OpenID comments.') return None try: store = filestore.FileOpenIDStore(store_dir) return openid.Consumer(session, store) except Exception: trace = traceback.format_exception(*sys.exc_info()) logger.error('Error initializing OpenID server:\n' + '\n'.join(trace)) return None
def cb_filestat(args): """Parse the entry filename looking for a date pattern. If the pattern matches and is a valid date, then override the mtime. """ from Pyblosxom import tools filepath = args['filename'] filelst = os.path.split(filepath) filename = filelst[-1] datadir = args['request'].getConfiguration()['datadir'] logger = tools.getLogger() # If we find a date pattern in the filename, load it into the args # dict and return. If a pattern is not found, or if the values # in the yyyy-mm-dd prefix do not constitute a valid date, # return args unmolested. m = filerex.match(filename) if m: try: year = int(m.group(1)) month = int(m.group(2)) day = int(m.group(3)) # Time values all set to zero in this implementation mtime = time.mktime((year,month,day,0,0,0,0,0,-1)) stattuple = args['mtime'] args['mtime'] = tuple(list(stattuple[:8]) + [mtime] + list(stattuple[9:])) except Exception as e: logger.error("%s: %s" % (type(e), e.args)) return args return args
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_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 cb_truncatelist(args): """For each entry under a named category, parse the entry filename looking for a date pattern. If the pattern does not match, delete the file from the list. """ from Pyblosxom import tools logger = tools.getLogger() request = args['request'] config = request.getConfiguration() categories = config['datedcategories'] pagesdir = config['pagesdir'] data = request.get_data() entry_list = args['entry_list'] for i in range(len(entry_list) - 1, -1, -1): entry = entry_list[i] #print entry.keys() filepath = entry['file_path'] # Check for path if filepath: # Split file path filelst = filepath.split(os.path.sep) # Check for index if filelst[-1] == "index": continue # Check for pages trigger if pagesdir and entry['filename'].startswith(pagesdir): continue # Check for dated config if categories: # Check for date pattern logger.debug("%s" % filelst[-1]) if not filerex.match(filelst[-1]): args['entry_list'].pop(i) return args['entry_list']
def cb_story(args): logger = tools.getLogger() pagedelimiter = "BREAK" continue_template = '<br /><br />::<a href="%(url)s">READ MORE</a>' continued_template = '<br /><br /><span style="color: red;">::READ HERE</span>' entry = args["entry"] if not entry.has_key("body"): return # override default breakpoint (pagedelimiter) if applicable from the # config.py file. if args["entry"].has_key("readmore_breakpoint"): readmore_breakpoint = args["entry"]["readmore_breakpoint"] if isinstance(readmore_breakpoint, type("")): pagedelimiter = readmore_breakpoint else: logger.error( "py['readmore_breakpoint'] value is not a " + "string. It's type seems to be: %s" % type(readmore_breakpoint) ) # find the delimiter in the body of the text match = re.search(pagedelimiter, entry["body"]) if match: if args["entry"].has_key("readmore_template"): readmore_template = args["entry"]["readmore_template"] if isinstance(readmore_template, type("")): continue_template = readmore_template continued_template = "" elif isinstance(readmore_template, type([])): if not readmore_template: logger.error("py['readmore_template'] is an empty list.") else: continue_template = readmore_template[0] if len(readmore_template) > 1: continued_template = readmore_template[1] else: logger.error( "py['readmore_template'] value is neither " + "a string nor a list. It's type seems to " + "be: %s" % type(readmore_template) ) if entry["bl_type"] == "file": entry["body"] = re.sub(pagedelimiter, continued_template, entry["body"]) else: base_url = entry["base_url"] file_path = entry["file_path"] flavour = entry["flavour"] m = { "url": "%s/%s.%s" % (base_url, file_path, flavour), "base_url": base_url, "file_path": file_path, "flavour": flavour, } entry["body"] = entry["body"][: match.start()] entry["body"] += continue_template % m
def cb_story(args): logger = tools.getLogger() pagedelimiter = 'BREAK' continue_template = '<br /><br />::<a href="%(url)s">READ MORE</a>' continued_template = '<br /><br /><span style="color: red;">::READ HERE</span>' entry = args['entry'] if not entry.has_key('body'): return # override default breakpoint (pagedelimiter) if applicable from the # config.py file. if args['entry'].has_key('readmore_breakpoint'): readmore_breakpoint = args['entry']['readmore_breakpoint'] if isinstance(readmore_breakpoint, type('')): pagedelimiter = readmore_breakpoint else: logger.error("py['readmore_breakpoint'] value is not a " + \ "string. It's type seems to be: %s" % \ type(readmore_breakpoint)) # find the delimiter in the body of the text match = re.search(pagedelimiter, entry['body']) if match: if args['entry'].has_key('readmore_template'): readmore_template = args['entry']['readmore_template'] if isinstance(readmore_template, type('')): continue_template = readmore_template continued_template = '' elif isinstance(readmore_template, type([])): if not readmore_template: logger.error("py['readmore_template'] is an empty list.") else: continue_template = readmore_template[0] if len(readmore_template) > 1: continued_template = readmore_template[1] else: logger.error("py['readmore_template'] value is neither " + \ "a string nor a list. It's type seems to " + \ "be: %s" % type(readmore_template)) if entry['bl_type'] == 'file': entry['body'] = re.sub(pagedelimiter, continued_template, entry['body']) else: base_url = entry['base_url'] file_path = entry['file_path'] flavour = entry['flavour'] m = { 'url': '%s/%s.%s' % (base_url, file_path, flavour), 'base_url': base_url, 'file_path': file_path, 'flavour': flavour } entry['body'] = entry['body'][:match.start()] entry['body'] += continue_template % m
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 cb_story(args): logger = tools.getLogger() pagedelimiter = 'BREAK' continue_template = '<br /><br />::<a href="%(url)s">READ MORE</a>' continued_template = '<br /><br /><span style="color: red;">::READ HERE</span>' entry = args['entry'] if not entry.has_key('body'): return # override default breakpoint (pagedelimiter) if applicable from the # config.py file. if args['entry'].has_key('readmore_breakpoint'): readmore_breakpoint = args['entry']['readmore_breakpoint'] if isinstance(readmore_breakpoint, type('')): pagedelimiter = readmore_breakpoint else: logger.error("py['readmore_breakpoint'] value is not a " + \ "string. It's type seems to be: %s" % \ type(readmore_breakpoint)) # find the delimiter in the body of the text match = re.search(pagedelimiter, entry['body']) if match: if args['entry'].has_key('readmore_template'): readmore_template = args['entry']['readmore_template'] if isinstance(readmore_template, type('')): continue_template = readmore_template continued_template = '' elif isinstance(readmore_template, type([])): if not readmore_template: logger.error("py['readmore_template'] is an empty list.") else: continue_template = readmore_template[0] if len(readmore_template) > 1: continued_template = readmore_template[1] else: logger.error("py['readmore_template'] value is neither " + \ "a string nor a list. It's type seems to " + \ "be: %s" % type(readmore_template)) if entry['bl_type' ] == 'file': entry['body'] = re.sub(pagedelimiter, continued_template, entry['body']) else: base_url = entry['base_url'] file_path = entry['file_path'] flavour = entry['flavour'] m = {'url':'%s/%s.%s' % (base_url, file_path, flavour), 'base_url':base_url, 'file_path':file_path, 'flavour':flavour} entry['body'] = entry['body'][:match.start()] entry['body'] += continue_template % m
def generate_history_entry(request, history_fn): """ Calls history_fn and handles any exceptions. Then generates an entry, merges history_fn 's return value (a dictionary) into the entry's data, and returns it. history_fn should take an Entry and a request and return a dict. """ config = request.getConfiguration() data = request.getData() filename = data['root_datadir'] # run history_fn backend_class = BACKENDS[config['history_backend']] try: versioned_entry = backend_class(filename) entryData = history_fn(versioned_entry) except (BadPath, BadVersion), msg: tools.getLogger().error(msg) return # make pyblosxom 404
def cleanup(self): """This cleans up Pyblosxom after a run. This should be called when Pyblosxom has done everything it needs to do before exiting. """ # log some useful stuff for debugging # this will only be logged if the log_level is "debug" log = tools.getLogger() response = self.get_response() log.debug("status = %s" % response.status) log.debug("headers = %s" % response.headers)
def readComment(filename, encoding, config): """ Read a comment from filename @param filename: filename containing a comment @type filename: string @param encoding: encoding of comment files @type encoding: string @param config: the pyblosxom configuration settings @type config: dictionary @returns: a comment dict """ from xml.sax import make_parser, SAXException from xml.sax.handler import feature_namespaces, ContentHandler class cmtHandler(ContentHandler): def __init__(self, cmt): self._data = "" self.cmt = cmt def startElement(self, name, atts): self._data = "" def endElement(self, name): self.cmt['cmt_'+name] = self._data def characters(self, content): self._data += content cmt = {} try: parser = make_parser() parser.setFeature(feature_namespaces, 0) handler = cmtHandler(cmt) parser.setContentHandler(handler) parser.parse(filename) cmt['cmt_time'] = float(cmt['cmt_pubDate']) #time.time() cmt['cmt_pubDate'] = time.ctime(float(cmt['cmt_pubDate'])) #pretty time cmt['cmt_w3cdate'] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(cmt['cmt_time'])) if cmt['cmt_link']: link = add_dont_follow('<a href="%s">%s</a>' % (cmt['cmt_link'], cmt['cmt_author']), config) cmt['cmt_optionally_linked_author'] = link else: cmt['cmt_optionally_linked_author'] = cmt['cmt_author'] return cmt except: #don't error out on a bad comment logger = tools.getLogger() logger.error("bad comment file: %s\nerror was: %s" % (filename, traceback.format_exception(*sys.exc_info())))
def cb_story(args): request = args['request'] http = request.getHttp() data = request.getData() config = request.getConfiguration() flavour = args['renderer'].flavour trigger = config.get('openid_trigger', DEFAULT_TRIGGER) if http['PATH_INFO'].startswith(trigger): if data.has_key('openid_login_oidrequest'): tools.getLogger().info('Login request: ' + str(data['openid_login_oidrequest'])) args['template'] = flavour.get('openid-login', DEFAULT_LOGIN_TEMPLATE) elif data.has_key('openid_error'): tools.getLogger().error('error: ' + data['openid_error']) args['template'] = flavour.get('openid-error', DEFAULT_ERROR_TEMPLATE) else: args['template'] = flavour.get('openid-info', DEFAULT_INFO_TEMPLATE) return args
def cb_truncatelist(args): """For each entry under a named category, parse the entry filename looking for a date pattern. If the pattern does not match, delete the file from the list. """ from Pyblosxom import tools logger = tools.getLogger() request = args['request'] config = request.getConfiguration() category_names = config['newslists'].keys() category_configs = config['newslists'] if config.has_key('pagesdir'): pagesdir = config['pagesdir'] else: pagesdir = False data = request.get_data() # The entry_list segment is a little funny inside # Pyblosxom. On most occasions it is double-nested when # this callback is invoked, so we don't return args # verbatim from this function. entry_list = args['entry_list'] previewing = config['preview'] for i in range(len(entry_list) - 1, -1, -1): entry = entry_list[i] filepath = entry['file_path'] # Check for path if filepath: # Split file path filelst = filepath.split(os.path.sep) # Always pass through index pages if filelst[-1] == "index": continue # Always pass through static pages if pagesdir and entry['filename'].startswith(pagesdir): continue # Check for dated config if category_configs: # Check for date pattern. This doesn't check # for date validity, only a looks-like-a-date # pattern. logger.debug("%s" % filelst[-1]) if not filerex.match(filelst[-1]): if not previewing or not filelst[-1].startswith('X'): args['entry_list'].pop(i) else: if previewing: args['entry_list'].pop(i) return args['entry_list']
def cb_filestat(args): """Parse the entry file looking for a #published metadata value. If the entry has a suitable value then override the mtime. """ from Pyblosxom import tools # Parse the entry file. # Does a plugin really have to parse the entry file itself, it can't get a # parsed object from pyblosxom? filename = args['filename'] datadir = args['request'].getConfiguration()['datadir'] logger = tools.getLogger() try: entry = parsefile(os.path.join(datadir,filename)) except IOError, e: logger.error(e) return args
def __doPing(self): pingTime = int(time.time()) # Save this data first else we'll go crazy with looping if not self.__saveResults(pingTime, "buffer", "buffer"): return # Ping both servers now. logger = tools.getLogger() try: rpc = xmlrpclib.Server("http://ping.blo.gs/") response = rpc.weblogUpdates.extendedPing(self._title, self._site, self._xml, self._xml) rpc = xmlrpclib.Server("http://rpc.weblogs.com/RPC2") response1 = rpc.weblogUpdates.ping(self._title, self._site) # save result of ping in self._file, note, no output is done self.__saveResults(pingTime, response, response1) except: logger.error("Error during ping: %s, %s" % (str(sys.exc_type), str(sys.exc_value)))
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) }
def cb_comment_reject(args): req = args["request"] comment = args["comment"] blog_config = req.getConfiguration() max_age = blog_config.get("no_old_comments_max_age", 2419200) data = req.getData() entry = data["entry_list"][0] logger = tools.getLogger() logger.debug("%s -> %s" % (entry["mtime"], comment)) if (time.time() - entry["mtime"]) >= max_age: logger.info("Entry too old, comment not posted!") return 1 logger.info("Entry ok, comment posted!") return 0
def cb_comment_reject(args): req = args["request"] comment = args["comment"] blog_config = req.getConfiguration() max_age = blog_config.get('no_old_comments_max_age', 2419200) data = req.getData() entry = data['entry_list'][0] logger = tools.getLogger() logger.debug( '%s -> %s' % (entry['mtime'], comment) ) if ( (time.time() - entry['mtime']) >= max_age): logger.info('Entry too old, comment not posted!') return 1 logger.info('Entry ok, comment posted!') return 0
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 gen_recentposts(req): log = tools.getLogger() try: config = req.getConfiguration() root = config["datadir"] def to_url(ename): # trim root directory from entry name trimmed = ename[len(root)+1:] # trim possible extension, which pyblosxom will interpret # as a flavour trimmed = os.path.splitext(trimmed)[0] return "%s/%s" % (config.get("base_url", ""), trimmed) limit = config.get("recentposts_limit", DEFAULT_LIMIT) start = config.get("recentposts_start", DEFAULT_START) end = config.get("recentposts_end", DEFAULT_END) item = config.get("recentposts_item", DEFAULT_ITEM) # sort entries by mtime and clip off most recent limit entries # (or whole list if limit > len(elist)) def cmp_mtime(a, b): return cmp(tools.filestat(req, a), tools.filestat(req, b)) elist = sorted(tools.Walk(req, root), cmp_mtime)[(limit * -1):] # reverse for most-recent-first order elist.reverse() # parse titles and map them to entry file name items = [] for ename in elist: f = open(ename, 'r') # grab title (i.e., first line of post) try: l = f.readline().rstrip() finally: f.close() d = { 'url': to_url(ename), 'title': l} items.append(item % d) return start + "%s" % '\n'.join(items) + end except Exception, e: log.exception(e)
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 _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
import re, sgmllib, sys, urllib, xmlrpclib from xml.sax import parseString, SAXParseException from xml.sax.handler import ContentHandler import cPickle, os, os.path # Get our pyblosxom specifics here from Pyblosxom import tools from Pyblosxom.pyblosxom import blosxom_entry_parser from Pyblosxom.Request import Request import config logdir = config.get("logdir", "/tmp") logfile = os.path.normpath(logdir + os.sep + "autoping.log") logger = tools.getLogger() def excerpt(filename, title, body, blogname): """ filename,title,body => url,args Excerpt the body and urlencode the trackback arguments. """ body = re.split('<div\s+class="excerpt">(.*?)<\/div>',body)[:2][-1] body = re.sub('\n',' ',body) body = re.sub(' ',' ',body) body = re.sub('^(<p>)?<a\s+href="\S+">[\w\s\.]+<\/a>:\s*','',body) body = re.sub('<em>.*?<\/em>\.?\s*','',body) body = re.sub('<.*?>','',body)
def pingback(request, source, target): logger = tools.getLogger() logger.info("pingback started") source_file = urllib.urlopen(source.split('#')[0]) if source_file.headers.get('error', '') == '404': raise Fault(0x0010, "Target %s not exists" % target) source_page = parser() source_page.feed(source_file.read()) source_file.close() if source_page.title == "": source_page.title = source if target in source_page.hrefs: target_entry = fileFor(request, target) body = '' try: from rssfinder import getFeeds from rssparser import parse baseurl=source.split("#")[0] for feed in getFeeds(baseurl): for item in parse(feed)['items']: if item['link']==source: if 'title' in item: source_page.title = item['title'] if 'content_encoded' in item: body = item['content_encoded'].strip() if 'description' in item: body = item['description'].strip() or body body=re.compile('<.*?>',re.S).sub('',body) body=re.sub('\s+',' ',body) body=body[:body.rfind(' ',0,250)][:250] + " ...<br />" except: pass cmt = {'title':source_page.title, \ 'author':'Pingback from %s' % source_page.title, 'pubDate' : str(time.time()), \ 'link': source, 'source' : '', 'description' : body} # run anti-spam plugins argdict = { "request": request, "comment": cmt } reject = tools.run_callback("trackback_reject", argdict, donefunc=lambda x:x != 0) if ((isinstance(reject, tuple) or isinstance(reject, list)) and len(reject) == 2): reject_code, reject_message = reject else: reject_code, reject_message = reject, "Pingback rejected." if reject_code == 1: raise Fault(0x0031, reject_message) from comments import writeComment config = request.getConfiguration() data = request.getData() data['entry_list'] = [ target_entry ] # TODO: Check if comment from the URL exists writeComment(request, config, data, cmt, config['blog_encoding']) return "success pinging %s from %s\n" % (target, source) else: raise Fault(0x0011, "%s does not point to %s" % (source, target))
def cb_handle(args): """ @param args: a dict of plugin arguments @type args: dict """ request = args['request'] pyhttp = request.getHttp() config = request.getConfiguration() urltrigger = config.get('trackback_urltrigger','/trackback') logger = tools.getLogger() path_info = pyhttp['PATH_INFO'] if path_info.startswith(urltrigger): response = request.getResponse() response.addHeader("Content-type", "text/xml") form = request.getForm() message = "A trackback must have at least a URL field (see http://www.sixapart.com/pronet/docs/trackback_spec )" if form.has_key("url"): from comments import decode_form encoding = config.get('blog_encoding', 'iso-8859-1') decode_form(form, encoding) import time cdict = { 'title': form.getvalue('title', ''), 'author' : form.getvalue('blog_name', ''), 'pubDate' : str(time.time()), 'link' : form['url'].value, 'source' : form.getvalue('blog_name', ''), 'description' : form.getvalue('excerpt', ''), 'ipaddress': pyhttp.get('REMOTE_ADDR', ''), 'type' : 'trackback' } argdict = { "request": request, "comment": cdict } reject = tools.run_callback("trackback_reject", argdict, donefunc=lambda x:x != 0) if ((isinstance(reject, tuple) or isinstance(reject, list)) and len(reject) == 2): reject_code, reject_message = reject else: reject_code, reject_message = reject, "Trackback rejected." if reject_code == 1: print >> response, tb_bad_response % reject_message return 1 from Pyblosxom.entries.fileentry import FileEntry from Pyblosxom.pyblosxom import Request from Pyblosxom.pyblosxom import PyBlosxom datadir = config['datadir'] from comments import writeComment try: import os pi = path_info.replace(urltrigger,'') path = os.path.join(datadir, pi[1:]) data = request.getData() ext = tools.what_ext(data['extensions'].keys(), path) entry = FileEntry(request, '%s.%s' % (path, ext), datadir ) data = {} data['entry_list'] = [ entry ] # Format Author cdict['author'] = 'Trackback from %s' % form.getvalue('blog_name', '') writeComment(request, config, data, cdict, encoding) print >> response, tb_good_response except OSError: message = 'URI '+path_info+" doesn't exist" logger.error(message) print >> response, tb_bad_response % message else: logger.error(message) print >> response, tb_bad_response % message # no further handling is needed return 1 else: return 0
def cb_filelist(args): """ This handles kicking off wbgwiki functionality if we see a url that we handle. """ req = args["request"] pyhttp = req.getHttp() config = req.getConfiguration() pathinfo = pyhttp["PATH_INFO"] if not pathinfo.startswith("/" + TRIGGER): return logger = tools.getLogger() data = req.getData() data[INIT_KEY] = 1 datadir = config["datadir"] data['root_datadir'] = config['datadir'] wikidir = config.get("wikidir", config['datadir']) # convert the / to os.sep so that we can use os.path stuff. wikidir = wikidir.replace("/", os.sep) if not wikidir.endswith(os.sep): wikidir = wikidir + os.sep page_name = pathinfo[len("/" + TRIGGER) + 1:] if not page_name: return page_name = page_name.replace("/", os.sep) if not page_name: return if page_name.endswith(os.sep): page_name = page_name[:-1] # if the page has a flavour, we use that. otherwise # we default to the wiki flavour page_name, flavour = os.path.splitext(page_name) if flavour: data["flavour"] = flavour[1:] # wikifile should hold the absolute path on the file system to # the wiki file we're looking at. if it's in a parent directory # of wikidir, then we abort. wikifile = os.path.normpath(os.path.join(wikidir, page_name)) if not wikifile.startswith(wikidir): logger.info("wiki file requested '%s' is not in wikidir." % wikifile) return [] # we build our own config dict for the fileentry to kind of # fake it into loading this file correctly rather than # one of the entries. newdatadir = wikidir ext = tools.what_ext(data["extensions"].keys(), wikifile) if not ext: logger.info("wiki file '%s' does not exist." % wikifile) return [] data['root_datadir'] = page_name + '.' + ext data['bl_type'] = 'file' wikifile = wikifile + "." + ext if not os.path.isfile(wikifile): return [] fe = FileEntry(req, wikifile, wikidir) # now we evaluate python code blocks body = fe.getData() body = eval_python_blocks(req, body) body = "<!-- STATIC PAGE START -->\n\n%s\n<!-- STATIC PAGE END -->\n" % body # now we evaluate for wikilinks body = connect_links(config["base_url"], data["extensions"].keys(), wikidir, body) fe.setData(body) fe["absolute_path"] = TRIGGER fe["fn"] = page_name fe["file_path"] = TRIGGER + "/" + page_name fe["template_name"] = "wiki" data['blog_title_with_path'] = "%s : %s" % \ (config.get("blog_title", ""), fe.get("title_escaped", "")) # set the datadir back config["datadir"] = datadir return [fe]
def cb_handle(args): """ @param args: a dict of plugin arguments @type args: dict """ request = args['request'] pyhttp = request.getHttp() config = request.getConfiguration() urltrigger = config.get('trackback_urltrigger', '/trackback') logger = tools.getLogger() path_info = pyhttp['PATH_INFO'] if path_info.startswith(urltrigger): response = request.getResponse() response.addHeader("Content-type", "text/xml") form = request.getForm() message = "A trackback must have at least a URL field (see http://www.sixapart.com/pronet/docs/trackback_spec )" if form.has_key("url"): from comments import decode_form encoding = config.get('blog_encoding', 'iso-8859-1') decode_form(form, encoding) import time cdict = { 'title': form.getvalue('title', ''), 'author': form.getvalue('blog_name', ''), 'pubDate': str(time.time()), 'link': form['url'].value, 'source': form.getvalue('blog_name', ''), 'description': form.getvalue('excerpt', ''), 'ipaddress': pyhttp.get('REMOTE_ADDR', ''), 'type': 'trackback' } argdict = {"request": request, "comment": cdict} reject = tools.run_callback("trackback_reject", argdict, donefunc=lambda x: x != 0) if ((isinstance(reject, tuple) or isinstance(reject, list)) and len(reject) == 2): reject_code, reject_message = reject else: reject_code, reject_message = reject, "Trackback rejected." if reject_code == 1: print >> response, tb_bad_response % reject_message return 1 from Pyblosxom.entries.fileentry import FileEntry from Pyblosxom.pyblosxom import Request from Pyblosxom.pyblosxom import PyBlosxom datadir = config['datadir'] from comments import writeComment try: import os pi = path_info.replace(urltrigger, '') path = os.path.join(datadir, pi[1:]) data = request.getData() ext = tools.what_ext(data['extensions'].keys(), path) entry = FileEntry(request, '%s.%s' % (path, ext), datadir) data = {} data['entry_list'] = [entry] # Format Author cdict['author'] = 'Trackback from %s' % form.getvalue( 'blog_name', '') writeComment(request, config, data, cdict, encoding) print >> response, tb_good_response except OSError: message = 'URI ' + path_info + " doesn't exist" logger.error(message) print >> response, tb_bad_response % message else: logger.error(message) print >> response, tb_bad_response % message # no further handling is needed return 1 else: return 0
def cb_start(args): request = args["request"] config = request.getConfiguration() logger = tools.getLogger() logger.info("finished config")
def cb_handle(args): req = args["request"] pyhttp = req.http pathinfo = pyhttp.get("PATH_INFO", "") logger = tools.getLogger() # breaks the pathinfo into nice pieces pieces = [urllib.unquote(p) for p in pathinfo.split("/") if p] logger.info("%s" % repr(pieces)) if len(pieces) == 0 or pieces[0] != "flavour": return cfg = req.config response = req.getResponse() flavourdir = cfg.get("flavourdir", None) if not flavourdir: return logger.info("checking safety of %s and %s" % (pieces[1], pieces[2])) if not is_piece_safe(pieces[1]) or not is_piece_safe(pieces[2]): return # FIXME - test fn for extensions fn = os.path.join(flavourdir, "%s.flav" % pieces[1], pieces[2]) logger.info("looking at %s" % fn) if not os.path.isfile(fn): return logger.info("looking good....") # this is based on the filekicker.py plugin contenttype, enc = mimetypes.guess_type(fn) logger.info("content-type: %s" % repr(contenttype)) logger.info("encoding: %s" % repr(enc)) if contenttype: response.addHeader('Content-Type', contenttype) if enc: response.addHeader('Content-Encoding', enc) length = os.stat(fn)[stat.ST_SIZE] response.addHeader('Content-Length', str(length)) logger.info("length: %s" % repr(length)) f = open(fn, "rb", 4096) while True: block = f.read(4096) if not block: break response.write(block) f.close() return 1
def cb_start(args): request = args["request"] config = request.getConfiguration() logger = tools.getLogger()
try: message = Message.fromPostArgs(form) endpoint = config['base_url'] + trigger oidrequest = OpenIDServer.CheckIDRequest.fromMessage( message, endpoint) except: exception = traceback.format_exception(*sys.exc_info()) data['openid_error'] = ('Error decoding login request:\n%s\n%s' % (str(form), '\n'.join(exception))) # cb_filelist() will show an error page return False if form.has_key('continue') and form.has_key('password'): if form['password'] == config['openid_password']: if form.get('remember', '') == 'yes': tools.getLogger().info( 'Setting cookie to remember openid login') response.addHeader('Set-Cookie', 'openid_remembered=yes') tools.getLogger().info('Logged in, confirming identity to ' + oidrequest.trust_root) return respond(request, oidrequest.answer(True)) else: data[ 'openid_error'] = 'Incorrect password. Click Back to try again.' # cb_filelist() will show an error page return False elif form.has_key('cancel'): tools.getLogger().info('Login cancelled, sending cancel to ' + oidrequest.trust_root) return respond(request, oidrequest.answer(False))