[a-zA-Z]+)$',
- 'week': r'^Week-of-Mon-' + yre + mre + dre,
- 'day': '^' + yre + mre + dre + '$'
- }
-
- def _makeArticle(self, msg, sequence):
- return Article(msg, sequence,
- lang=self.maillist.preferred_language,
- mlist=self.maillist)
-
- def html_foot(self):
- # avoid i18n side-effects
- mlist = self.maillist
- # Convenience
- def quotetime(s):
- return html_quote(i18n.ctime(s), self.lang)
- with i18n.using_language(mlist.preferred_language):
- d = {"lastdate": quotetime(self.lastdate),
- "archivedate": quotetime(self.archivedate),
- "listinfo": mlist.script_url('listinfo'),
- "version": self.version,
- }
- i = {"thread": _("thread"),
- "subject": _("subject"),
- "author": _("author"),
- "date": _("date")
- }
- for t in i.keys():
- cap = t[0].upper() + t[1:]
- if self.type == cap:
- d["%s_ref" % (t)] = ""
- else:
- d["%s_ref" % (t)] = ('[ %s ]'
- % (t, i[t]))
- return quick_maketext(
- 'archidxfoot.html', d,
- mlist=mlist)
-
- def html_head(self):
- # avoid i18n side-effects
- mlist = self.maillist
- # Convenience
- def quotetime(s):
- return html_quote(i18n.ctime(s), self.lang)
- with i18n.using_language(mlist.preferred_language):
- d = {"listname": html_quote(mlist.real_name, self.lang),
- "archtype": self.type,
- "archive": self.volNameToDesc(self.archive),
- "listinfo": mlist.script_url('listinfo'),
- "firstdate": quotetime(self.firstdate),
- "lastdate": quotetime(self.lastdate),
- "size": self.size,
- }
- i = {"thread": _("thread"),
- "subject": _("subject"),
- "author": _("author"),
- "date": _("date"),
- }
- for t in i.keys():
- cap = t[0].upper() + t[1:]
- if self.type == cap:
- d["%s_ref" % (t)] = ""
- d["archtype"] = i[t]
- else:
- d["%s_ref" % (t)] = ('[ %s ]'
- % (t, i[t]))
- if self.charset:
- d["encoding"] = html_charset % self.charset
- else:
- d["encoding"] = ""
- return quick_maketext(
- 'archidxhead.html', d,
- mlist=mlist)
-
- def html_TOC(self):
- mlist = self.maillist
- listname = mlist.fqdn_listname
- mbox = os.path.join(mlist.archive_dir()+'.mbox', listname+'.mbox')
- d = {"listname": mlist.real_name,
- "listinfo": mlist.script_url('listinfo'),
- "fullarch": '../%s.mbox/%s.mbox' % (listname, listname),
- "size": sizeof(mbox, mlist.preferred_language),
- 'meta': '',
- }
- # Avoid i18n side-effects
- with i18n.using_language(mlist.preferred_language):
- if not self.archives:
- d["noarchive_msg"] = _(
- 'Currently, there are no archives.
')
- d["archive_listing_start"] = ""
- d["archive_listing_end"] = ""
- d["archive_listing"] = ""
- else:
- d["noarchive_msg"] = ""
- d["archive_listing_start"] = quick_maketext(
- 'archliststart.html',
- lang=mlist.preferred_language,
- mlist=mlist)
- d["archive_listing_end"] = quick_maketext(
- 'archlistend.html',
- mlist=mlist)
-
- accum = []
- for a in self.archives:
- accum.append(self.html_TOC_entry(a))
- d["archive_listing"] = EMPTYSTRING.join(accum)
- # The TOC is always in the charset of the list's preferred language
- d['meta'] += html_charset % Utils.GetCharSet(mlist.preferred_language)
- # The site can disable public access to the mbox file.
- if as_boolean(config.archiver.pipermail.public_mbox):
- template = 'archtoc.html'
- else:
- template = 'archtocnombox.html'
- return quick_maketext(template, d, mlist=mlist)
-
- def html_TOC_entry(self, arch):
- # Check to see if the archive is gzip'd or not
- txtfile = os.path.join(self.maillist.archive_dir(), arch + '.txt')
- gzfile = txtfile + '.gz'
- # which exists? .txt.gz first, then .txt
- if os.path.exists(gzfile):
- file = gzfile
- url = arch + '.txt.gz'
- templ = '[ ' + _('Gzip\'d Text%(sz)s') \
- + '] | '
- elif os.path.exists(txtfile):
- file = txtfile
- url = arch + '.txt'
- templ = '[ ' + _('Text%(sz)s') + '] | '
- else:
- # neither found?
- file = None
- # in Python 1.5.2 we have an easy way to get the size
- if file:
- textlink = templ % {
- 'url': url,
- 'sz' : sizeof(file, self.maillist.preferred_language)
- }
- else:
- # there's no archive file at all... hmmm.
- textlink = ''
- return quick_maketext(
- 'archtocentry.html',
- {'archive': arch,
- 'archivelabel': self.volNameToDesc(arch),
- 'textlink': textlink
- },
- mlist=self.maillist)
-
- def GetArchLock(self):
- if self._lock_file:
- return 1
- self._lock_file = Lock(
- os.path.join(config.LOCK_DIR,
- self.maillist.fqdn_listname + '-arch.lock'))
- try:
- self._lock_file.lock(timeout=0.5)
- except lockfile.TimeOutError:
- return 0
- return 1
-
- def DropArchLock(self):
- if self._lock_file:
- self._lock_file.unlock(unconditionally=1)
- self._lock_file = None
-
- def processListArch(self):
- name = self.maillist.ArchiveFileName()
- wname= name+'.working'
- ename= name+'.err_unarchived'
- try:
- os.stat(name)
- except (IOError,os.error):
- #no archive file, nothin to do -ddm
- return
-
- #see if arch is locked here -ddm
- if not self.GetArchLock():
- #another archiver is running, nothing to do. -ddm
- return
-
- #if the working file is still here, the archiver may have
- # crashed during archiving. Save it, log an error, and move on.
- try:
- wf = open(wname)
- log.error('Archive working file %s present. '
- 'Check %s for possibly unarchived msgs',
- wname, ename)
- omask = os.umask(007)
- try:
- ef = open(ename, 'a+')
- finally:
- os.umask(omask)
- ef.seek(1,2)
- if ef.read(1) <> '\n':
- ef.write('\n')
- ef.write(wf.read())
- ef.close()
- wf.close()
- os.unlink(wname)
- except IOError:
- pass
- os.rename(name,wname)
- archfile = open(wname)
- self.processUnixMailbox(archfile)
- archfile.close()
- os.unlink(wname)
- self.DropArchLock()
-
- def get_filename(self, article):
- return '%06i.html' % (article.sequence,)
-
- def get_archives(self, article):
- """Return a list of indexes where the article should be filed.
- A string can be returned if the list only contains one entry,
- and the empty list is legal."""
- res = self.dateToVolName(float(article.date))
- self.message(_("figuring article archives\n"))
- self.message(res + "\n")
- return res
-
- def volNameToDesc(self, volname):
- volname = volname.strip()
- # Don't make these module global constants since we have to runtime
- # translate them anyway.
- monthdict = [
- '',
- _('January'), _('February'), _('March'), _('April'),
- _('May'), _('June'), _('July'), _('August'),
- _('September'), _('October'), _('November'), _('December')
- ]
- for each in self._volre.keys():
- match = re.match(self._volre[each], volname)
- # Let ValueErrors percolate up
- if match:
- year = int(match.group('year'))
- if each == 'quarter':
- d =["", _("First"), _("Second"), _("Third"), _("Fourth") ]
- ord = d[int(match.group('quarter'))]
- return _("%(ord)s quarter %(year)i")
- elif each == 'month':
- monthstr = match.group('month').lower()
- for i in range(1, 13):
- monthname = time.strftime("%B", (1999,i,1,0,0,0,0,1,0))
- if monthstr.lower() == monthname.lower():
- month = monthdict[i]
- return _("%(month)s %(year)i")
- raise ValueError, "%s is not a month!" % monthstr
- elif each == 'week':
- month = monthdict[int(match.group("month"))]
- day = int(match.group("day"))
- return _("The Week Of Monday %(day)i %(month)s %(year)i")
- elif each == 'day':
- month = monthdict[int(match.group("month"))]
- day = int(match.group("day"))
- return _("%(day)i %(month)s %(year)i")
- else:
- return match.group('year')
- raise ValueError, "%s is not a valid volname" % volname
-
-# The following two methods should be inverses of each other. -ddm
-
- def dateToVolName(self,date):
- datetuple=time.localtime(date)
- if self.ARCHIVE_PERIOD=='year':
- return time.strftime("%Y",datetuple)
- elif self.ARCHIVE_PERIOD=='quarter':
- if datetuple[1] in [1,2,3]:
- return time.strftime("%Yq1",datetuple)
- elif datetuple[1] in [4,5,6]:
- return time.strftime("%Yq2",datetuple)
- elif datetuple[1] in [7,8,9]:
- return time.strftime("%Yq3",datetuple)
- else:
- return time.strftime("%Yq4",datetuple)
- elif self.ARCHIVE_PERIOD == 'day':
- return time.strftime("%Y%m%d", datetuple)
- elif self.ARCHIVE_PERIOD == 'week':
- # Reconstruct "seconds since epoch", and subtract weekday
- # multiplied by the number of seconds in a day.
- monday = time.mktime(datetuple) - datetuple[6] * 24 * 60 * 60
- # Build a new datetuple from this "seconds since epoch" value
- datetuple = time.localtime(monday)
- return time.strftime("Week-of-Mon-%Y%m%d", datetuple)
- # month. -ddm
- else:
- return time.strftime("%Y-%B",datetuple)
-
-
- def volNameToDate(self, volname):
- volname = volname.strip()
- for each in self._volre.keys():
- match = re.match(self._volre[each],volname)
- if match:
- year = int(match.group('year'))
- month = 1
- day = 1
- if each == 'quarter':
- q = int(match.group('quarter'))
- month = (q * 3) - 2
- elif each == 'month':
- monthstr = match.group('month').lower()
- m = []
- for i in range(1,13):
- m.append(
- time.strftime("%B",(1999,i,1,0,0,0,0,1,0)).lower())
- try:
- month = m.index(monthstr) + 1
- except ValueError:
- pass
- elif each == 'week' or each == 'day':
- month = int(match.group("month"))
- day = int(match.group("day"))
- try:
- return time.mktime((year,month,1,0,0,0,0,1,-1))
- except OverflowError:
- return 0.0
- return 0.0
-
- def sortarchives(self):
- def sf(a, b):
- al = self.volNameToDate(a)
- bl = self.volNameToDate(b)
- if al > bl:
- return 1
- elif al < bl:
- return -1
- else:
- return 0
- if self.ARCHIVE_PERIOD in ('month','year','quarter'):
- self.archives.sort(sf)
- else:
- self.archives.sort()
- self.archives.reverse()
-
- def message(self, msg):
- if self.VERBOSE:
- f = sys.stderr
- f.write(msg)
- if msg[-1:] != '\n':
- f.write('\n')
- f.flush()
-
- def open_new_archive(self, archive, archivedir):
- index_html = os.path.join(archivedir, 'index.html')
- try:
- os.unlink(index_html)
- except:
- pass
- os.symlink(self.DEFAULTINDEX+'.html',index_html)
-
- def write_index_header(self):
- self.depth=0
- print self.html_head()
- if not self.THREADLAZY and self.type=='Thread':
- self.message(_("Computing threaded index\n"))
- self.updateThreadedIndex()
-
- def write_index_footer(self):
- for i in range(self.depth):
- print ''
- print self.html_foot()
-
- def write_index_entry(self, article):
- subject = self.get_header("subject", article)
- author = self.get_header("author", article)
- if as_boolean(config.archiver.pipermail.obscure_email_addresses):
- try:
- author = re.sub('@', _(' at '), author)
- except UnicodeError:
- # Non-ASCII author contains '@' ... no valid email anyway
- pass
- subject = CGIescape(subject, self.lang)
- author = CGIescape(author, self.lang)
-
- d = {
- 'filename': urllib.quote(article.filename),
- 'subject': subject,
- 'sequence': article.sequence,
- 'author': author
- }
- print quick_maketext(
- 'archidxentry.html', d,
- mlist=self.maillist)
-
- def get_header(self, field, article):
- # if we have no decoded header, return the encoded one
- result = article.decoded.get(field)
- if result is None:
- return getattr(article, field)
- # otherwise, the decoded one will be Unicode
- return result
-
- def write_threadindex_entry(self, article, depth):
- if depth < 0:
- self.message('depth<0')
- depth = 0
- if depth > self.THREADLEVELS:
- depth = self.THREADLEVELS
- if depth < self.depth:
- for i in range(self.depth-depth):
- print ''
- elif depth > self.depth:
- for i in range(depth-self.depth):
- print ''
- print '' % (depth, article.threadKey)
- self.depth = depth
- self.write_index_entry(article)
-
- def write_TOC(self):
- self.sortarchives()
- omask = os.umask(002)
- try:
- toc = open(os.path.join(self.basedir, 'index.html'), 'w')
- finally:
- os.umask(omask)
- toc.write(self.html_TOC())
- toc.close()
-
- def write_article(self, index, article, path):
- # called by add_article
- omask = os.umask(002)
- try:
- f = open(path, 'w')
- finally:
- os.umask(omask)
- f.write(article.as_html())
- f.close()
-
- # Write the text article to the text archive.
- path = os.path.join(self.basedir, "%s.txt" % index)
- omask = os.umask(002)
- try:
- f = open(path, 'a+')
- finally:
- os.umask(omask)
- f.write(article.as_text())
- f.close()
-
- def update_archive(self, archive):
- self.__super_update_archive(archive)
- # only do this if the gzip module was imported globally, and
- # gzip'ing was enabled via Defaults.GZIP_ARCHIVE_TXT_FILES. See
- # above.
- if gzip:
- archz = None
- archt = None
- txtfile = os.path.join(self.basedir, '%s.txt' % archive)
- gzipfile = os.path.join(self.basedir, '%s.txt.gz' % archive)
- oldgzip = os.path.join(self.basedir, '%s.old.txt.gz' % archive)
- try:
- # open the plain text file
- archt = open(txtfile)
- except IOError:
- return
- try:
- os.rename(gzipfile, oldgzip)
- archz = gzip.open(oldgzip)
- except (IOError, RuntimeError, os.error):
- pass
- try:
- ou = os.umask(002)
- newz = gzip.open(gzipfile, 'w')
- finally:
- # XXX why is this a finally?
- os.umask(ou)
- if archz:
- newz.write(archz.read())
- archz.close()
- os.unlink(oldgzip)
- # XXX do we really need all this in a try/except?
- try:
- newz.write(archt.read())
- newz.close()
- archt.close()
- except IOError:
- pass
- os.unlink(txtfile)
-
- _skip_attrs = ('maillist', '_lock_file', 'charset')
-
- def getstate(self):
- d={}
- for each in self.__dict__.keys():
- if not (each in self._skip_attrs
- or each.upper() == each):
- d[each] = self.__dict__[each]
- return d
-
- # Add tags around URLs and e-mail addresses.
-
- def __processbody_URLquote(self, lines):
- # XXX a lot to do here:
- # 1. use lines directly, rather than source and dest
- # 2. make it clearer
- # 3. make it faster
- # TK: Prepare for unicode obscure.
- atmark = _(' at ')
- if lines and isinstance(lines[0], unicode):
- atmark = unicode(atmark, Utils.GetCharSet(self.lang), 'replace')
- source = lines[:]
- dest = lines
- last_line_was_quoted = 0
- for i in xrange(0, len(source)):
- Lorig = L = source[i]
- prefix = suffix = ""
- if L is None:
- continue
- # Italicise quoted text
- if self.IQUOTES:
- quoted = quotedpat.match(L)
- if quoted is None:
- last_line_was_quoted = 0
- else:
- quoted = quoted.end(0)
- prefix = CGIescape(L[:quoted], self.lang) + ''
- suffix = ''
- if self.SHOWHTML:
- suffix += '
'
- if not last_line_was_quoted:
- prefix = '
' + prefix
- L = L[quoted:]
- last_line_was_quoted = 1
- # Check for an e-mail address
- L2 = ""
- jr = emailpat.search(L)
- kr = urlpat.search(L)
- while jr is not None or kr is not None:
- if jr == None:
- j = -1
- else:
- j = jr.start(0)
- if kr is None:
- k = -1
- else:
- k = kr.start(0)
- if j != -1 and (j < k or k == -1):
- text = jr.group(1)
- length = len(text)
- if as_boolean(
- config.archiver.pipermail.obscure_email_addresses):
- text = re.sub('@', atmark, text)
- URL = self.maillist.script_url('listinfo')
- else:
- URL = 'mailto:' + text
- pos = j
- elif k != -1 and (j > k or j == -1):
- text = URL = kr.group(1)
- length = len(text)
- pos = k
- else: # j==k
- raise ValueError, "j==k: This can't happen!"
- #length = len(text)
- #self.message("URL: %s %s %s \n"
- # % (CGIescape(L[:pos]), URL, CGIescape(text)))
- L2 += '%s%s' % (
- CGIescape(L[:pos], self.lang),
- html_quote(URL), CGIescape(text, self.lang))
- L = L[pos+length:]
- jr = emailpat.search(L)
- kr = urlpat.search(L)
- if jr is None and kr is None:
- L = CGIescape(L, self.lang)
- L = prefix + L2 + L + suffix
- source[i] = None
- dest[i] = L
-
- # Perform Hypermail-style processing of directives
- # in message bodies. Lines between and will be written
- # out precisely as they are; other lines will be passed to func2
- # for further processing .
-
- def __processbody_HTML(self, lines):
- # XXX need to make this method modify in place
- source = lines[:]
- dest = lines
- l = len(source)
- i = 0
- while i < l:
- while i < l and htmlpat.match(source[i]) is None:
- i = i + 1
- if i < l:
- source[i] = None
- i = i + 1
- while i < l and nohtmlpat.match(source[i]) is None:
- dest[i], source[i] = source[i], None
- i = i + 1
- if i < l:
- source[i] = None
- i = i + 1
-
- def format_article(self, article):
- # called from add_article
- # TBD: Why do the HTML formatting here and keep it in the
- # pipermail database? It makes more sense to do the html
- # formatting as the article is being written as html and toss
- # the data after it has been written to the archive file.
- lines = filter(None, article.body)
- # Handle directives
- if self.ALLOWHTML:
- self.__processbody_HTML(lines)
- self.__processbody_URLquote(lines)
- if not self.SHOWHTML and lines:
- lines.insert(0, '
')
- lines.append('')
- else:
- # Do fancy formatting here
- if self.SHOWBR:
- lines = map(lambda x:x + "
", lines)
- else:
- for i in range(0, len(lines)):
- s = lines[i]
- if s[0:1] in ' \t\n':
- lines[i] = '' + s
- article.html_body = lines
- return article
-
- def update_article(self, arcdir, article, prev, next):
- seq = article.sequence
- filename = os.path.join(arcdir, article.filename)
- self.message(_('Updating HTML for article %(seq)s'))
- try:
- f = open(filename)
- article.loadbody_fromHTML(f)
- f.close()
- except IOError, e:
- if e.errno <> errno.ENOENT: raise
- self.message(_('article file %(filename)s is missing!'))
- article.prev = prev
- article.next = next
- omask = os.umask(002)
- try:
- f = open(filename, 'w')
- finally:
- os.umask(omask)
- f.write(article.as_html())
- f.close()
--
cgit v1.2.3-70-g09d2