def get_comment(self, page, comment): if not self.comments_on(): return None if page.type != 'file' or not page.displayable(): return None compath = utils.pjoin(page.path, comment) po = self._commentpage(compath) if not po or po.type != "file" or not po.displayable(): raise derrors.IntErr("missing or undisplayable comment '%s' on page '%s'" % (comment, page)) c = model_comment.loadcomment(po, comment) if c is None: raise derrors.IntErr("misformatted comment '%s' on '%s'" % (comment, page.path)) return c
def get_template(self, tname, fail_on_error = True): to = self.tstore.get(tname) if not to and fail_on_error: raise derrors.IntErr("request for fully bogus template: "+tname) elif not to: return None return validate_template(to, fail_on_error, tname)
def newblob(self, where, blobstr): if not self.validname(where): raise derrors.IntErr("bad commentstore name: '%s'" % where) loc = join2(self.root, where) try: if not os.path.isdir(loc): makedirs(loc) except EnvironmentError as e: raise derrors.IOErr("could not make directory '%s': %s" % (loc, str(e))) objname = self.blobname(blobstr) pname = join2(loc, objname) phase = "create" try: fd = os.open(pname, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0644) phase = "write" os.write(fd, blobstr) os.close(fd) except EnvironmentError as e: # It exists already, so we 'succeeded' as it were. if phase == "create" and os.path.exists(pname): return True raise derrors.IOErr("could not %s file '%s': %s" % (phase, pname, str(e))) return True
def blogview(context): """Generate a Blog rendering of the current directory: all descendant real pages, from most recent to oldest, possibly truncated at a day boundary if there's 'too many', and sets up information for blog navigation renderers. Each displayed page is rendered with the _blog/blogentry.tmpl_ template. Supports VirtualDirectory restrictions.""" if context.page.type != "dir": return '' if not context.page.displayable(): raise derrors.IntErr("undisplayable directory page") # This automatically applies restrictions. dl = context.cache_page_children(context.page) if not dl: return '' dl = clipDown(context, dl) # For each file, clone the context, set the current page to # it (!), and render it with the blogentry template. to = context.model.get_template("blog/blogentry.tmpl") # Set up our rolling storage. We can't hook directly onto # the context, because we drop the context after every # entry. So we write our own dictionary into the context. context.setvar(rollvar, {}) res = [] rootpath = context.page.me().path dupDict = {} for ts, path in dl: # Skip redirects and other magic files. # We go whole hog and drop anything that is not a real page, # which subsumes both redirect pages and non-displayable # pages. Since attempting to render a non-displayable page # is a fatal internal error, we must drop them before we # go to the template. np = context.model.get_page(path) if not np.realpage() or np.is_util(): continue # Suppress multiple occurrences of the same page as # may happen with, eg, hardlinks. Note that this is # slightly questionable; macros mean that a file's # rendering output may depend on not just its contents # but its position in the file hierarchy. We don't # care. pageid = np.identity() if pageid in dupDict: continue else: dupDict[pageid] = True # Note: we do not reset the view type, because we do # not render through the interface that cares; we go # straight to template. nc = context.clone_to_page(np) nc.setvar('relname', path[len(rootpath) + 1:]) res.append(template.Template(to).render(nc)) context.newtime(nc.modtime) return ''.join(res)
def genScopeRange(ctuple): if ctuple[1]: sday, daynum = calendar.monthrange(ctuple[0], ctuple[1]) return [("%d" % x, (ctuple[0], ctuple[1], x)) for x in range(1, daynum+1)] elif ctuple[0]: return [(months[x][:3], (ctuple[0], x+1, None)) for x in range(0, 12)] else: raise derrors.IntErr("genScopeRange doesn't do years")
def subst(self, mo): what = mo.group(1)[0] if what == '$': return self.variable(mo) elif what == '@': return self.renderer(mo) elif what == '%': return self.cond_renderer(mo) elif what == '#': return self.include(mo) else: raise derrors.IntErr("unknown template operation: "+mo.group(1))
def timeof(self, zone, host, path, key="default"): self.validate_quad(zone, host, path, key) key = key + '~' dname = os.path.sep.join([self.root, zone, host, path]) fname = join2(dname, key) st = self.getStat(fname) if not st: return None elif not stat.S_ISREG(st.st_mode): raise derrors.IntErr( "not a regular file in cachestore: '%s' '%s' '%s' '%s'" % (zone, host, path, key)) else: return st.st_mtime
def restrict(context, suffix): for k, rex in restMatches.items(): mo = rex.match(suffix) if mo: break if not mo: return False # We found something. The only tricky bit is setting up the # parameters. context.setvar(rest_type, k) if k == 'VirtStems': context.setvar(rest_type, mo.group(1)) context.setvar(rest_val, defVals[mo.group(1)]) elif k == 'latest' or k == 'oldest': context.setvar(rest_val, int(mo.group(1))) elif k == 'calendar': cv = [] for g in mo.groups(): if g: cv.append(int(g)) else: cv.append(None) # reject bad month or days; the latter is approximate. # if we don't do this we can run into exceptions in # the calendar module later. if (cv[1] is not None and (cv[1] == 0 or cv[1] > 12)) or \ (cv[2] is not None and (cv[2] == 0 or cv[2] > 31)): context.delvar(rest_type) return False context.setvar(rest_val, tuple(cv)) elif k == 'range': # We're easy on this one; you can specify anything you # like. one = int(mo.group(1)) two = int(mo.group(2)) if one > two: two, one = one, two if one == 0: # This guarantees both one and two are positive, # since two is >= one. one += 1 two += 1 context.setvar(rest_val, (one, two)) else: raise derrors.IntErr("unhandled restriction match: "+k) return True
def fromstore(self, fileobj, name): blob = fileobj.contents() if not blob: raise derrors.IntErr("CommentV1 fromstore blob is empty") mo = commentv1_re.match(blob) if not mo: return False self.user = mo.group(1) self.anon = mo.group(2) self.username = mo.group(3).strip() self.userurl = mo.group(4).strip() self.ip = mo.group(5) self.data = mo.group(6) self.time = fileobj.timestamp() self.name = name return True
def get_commentlist(self, page): if not self.comments_on(): return [] # An undisplayable page turns off its comments. if page.type != "file" or not page.displayable(): return [] po = self._commentpage(page.path) if not po: return [] # Safety check: if po.type != "dir": raise derrors.IntErr("comment fileobj for '%s' not a directory" % page) # This is safe by our axioms; we know that this must be # only have files, so we will get a list of comments that # the page has. return po.contents()
def blogdir(context): """Generate a BlogDir rendering of the current directory: display all real pages in the current directory from most recent to oldest, rendering each with the template _blog/blogdirpage.tmpl_. Supports VirtualDirectory restrictions.""" if context.page.type != "dir": return '' if not context.page.displayable(): raise derrors.IntErr("undisplayable directory page") dl = context.page.children("file") if not dl: return '' # Boil it down to just files, in modtime order. # (Technically this is real files: displayable, non-redirect.) dl = [z for z in dl if z.realpage() and not z.is_util()] # This directory might have nothing. if not dl: return '' # Apply restrictions, backwardly. # Because restrictions are designed to be hugely scalable, they # want the output we'd get from page.descendants(). if pageranges.is_restriction(context): tl = pageranges.filter_files(context, [(z.timestamp, z.path) for z in dl]) if not tl: return '' dl = [context.model.get_page(z[1]) for z in tl] else: dl.sort(key=lambda x: x.timestamp, reverse=True) # For each file, clone the context, set the current page to # it (!), and render it with the blogentry template. to = context.model.get_template("blog/blogdirpage.tmpl") context.setvar(rollvar, {}) res = [] for page in dl: # Note: we do not reset the view type, because we do # not render through the interface that cares; we go # straight to template. nc = context.clone_to_page(page) res.append(template.Template(to).render(nc)) context.newtime(nc.modtime) return ''.join(res)
def fetch(self, zone, host, path, key="default", TTL=None): self.validate_quad(zone, host, path, key) key = key + '~' fname = os.path.sep.join([self.root, zone, host, path, key]) st = self.getStat(fname) now = time.time() if not st: return None elif not stat.S_ISREG(st.st_mode): raise derrors.IntErr( "not a regular file in cachestore: '%s' '%s' '%s' '%s'" % (zone, host, path, key)) elif TTL and (st.st_mtime + TTL) < now: return None # Finally we can get and retrieve the cached file. # Cached file objects are always depickled before # getting returned. (We use FileObjs because they # handle a number of details for us.) # # Rather than cache the depickled objects, which may # be mutated by later people, we cache the file object # data and depickle each time. if fname not in self.cache: fo = FileObj(fname, st, "rb") data = fo.contents() else: data = self.cache[fname] if not data: # This might be: empty file, no-permissions file, # and probably others. None are severe enough to # kill us. return None ro = cPickle.loads(data) if self.cache_on: self.cache[fname] = data return ro
def loadcomment(fileobj, name): blob = fileobj.contents() if not blob: return None mo = commentver_re.match(blob) # might be a version zero comment. if not mo: mo = commentbody_re.match(blob) if mo: c = Comment() else: return None elif mo.group(1) == "1": c = CommentV1() else: raise derrors.IntErr("Uknown comment format version: '%s' in %s" % (mo.group(1), name)) # Load: if c.fromstore(fileobj, name): return c else: return None
def titleindex(context): """Like _blog::blog_, except that instead of rendering entries through a template, it just displays a table of dates and entry titles (or relative paths for entries without titles), linking to entries and to the day pages. Respects VirtualDirectory restrictions. Unlike _blog::blog_, it always displays information for all applicable entries.""" if context.page.type != "dir": return '' if not context.page.displayable(): raise derrors.IntErr("undisplayable directory page") # This automatically applies restrictions. dl = context.cache_page_children(context.page) if not dl: return '' # Building a table is unfortunately much more complicated # than a <dl> would be, because we have to use <br> to separate # multiple entries for the same day instead of <td>, which means # that we have to keep track of when we need to generate one and # so on. rl = [ '<table class="blogtitles">\n', ] lday = None dupDict = {} rootpath = context.page.me().path # Rather than directly use a wikirend routine by importing # it, we indirect through the renderer registration. Since # either way we know a wikirend name, I figure this is no # worse. rfunc = htmlrends.get_renderer("wikitext:title:nolinks") for ts, path in dl: # FIXME: this duplication is code smell. np = context.model.get_page(path) if not np.realpage() or np.is_util() or \ not np.access_ok(context): continue pageid = np.identity() if pageid in dupDict: continue else: dupDict[pageid] = None # Do we need to generate a new row for a new day? # Our basic running state is that we are always in # a <td> for page links (except right at the start), # so we must close it off et cetera and then reopen # it. t = time.localtime(ts) plain = "%d-%02d-%02d" % (t.tm_year, t.tm_mon, t.tm_mday) if plain != lday: if lday: # Not first entry ever, so close off # the last day table row. rl.append("\n</td> </tr>\n") rl.append("<tr> <td> %s: </td> <td>\n" % \ link_to_tm(context, t, plain)) lday = plain else: # If we are the second or later entry for a # given day, we must put a <br> between ourselves # and the previous entry. rl.append("<br>\n") # As usual, we must work in a new context. nc = context.clone_to_page(np) ltitle = rfunc(nc) if not ltitle: ltitle = httputil.quotehtml(path[len(rootpath) + 1:]) # We can't use htmlrends.makelink() because that would # quote the live HTML in real titles. rl.append(' <a href="%s">%s</a>' % \ (context.nurl(np), ltitle)) context.newtime(nc.modtime) # Done all; close off the <table> rl.append('\n</td></tr></table>\n') return ''.join(rl)
def get(self, relname, missIsNone=False): __pychecker__ = "no-argsused" raise derrors.IntErr("using get on a cachestore")