[xiph-cvs] r6796 - in websites-ngen: . news news/2004 news/2004/04 news/2004/04/21 news/2004/05 news/2004/05/12 news/2004/05/29 xiph.org xiph.org/speex xiph.org/speex/Error xiph.org/speex/Error/404 xiph.org/vorbis

comatoast at xiph.org comatoast at xiph.org
Tue Jun 1 10:33:40 PDT 2004



Author: comatoast
Date: 2004-06-01 13:32:59 -0400 (Tue, 01 Jun 2004)
New Revision: 6796

Added:
   websites-ngen/news/2004/
   websites-ngen/news/2004/04/
   websites-ngen/news/2004/04/21/
   websites-ngen/news/2004/04/21/speex115.markdown
   websites-ngen/news/2004/05/
   websites-ngen/news/2004/05/12/
   websites-ngen/news/2004/05/12/icecast201Released.markdown
   websites-ngen/news/2004/05/29/
   websites-ngen/news/2004/05/29/postfish.markdown
   websites-ngen/time.py
   websites-ngen/xiph.org/speex/.htaccess
   websites-ngen/xiph.org/speex/Error/
   websites-ngen/xiph.org/speex/Error/404/
   websites-ngen/xiph.org/speex/Error/404/index.markdown
   websites-ngen/xiph.org/vorbis/.htaccess
Removed:
   websites-ngen/news/on2004-04-19at0149.txt
   websites-ngen/news/on2004-04-21at0000.txt
Modified:
   websites-ngen/
   websites-ngen/news/options.ini
   websites-ngen/style.txt
   websites-ngen/todo.txt
   websites-ngen/wrapup.py
   websites-ngen/xiph.org/
   websites-ngen/xiph.org/aftermaintext.inherit
   websites-ngen/xiph.org/index.markdown
Log:
* News stuff implemented. Not completely, but we seem to have real archives and permalinks.
* time.py added, because most normal humans don't know what time it is in Greenwich.
* /postfish/ stubbed out.
* style.txt updates.
* Custom ErrorDocuments for /vorbis/ and /speex/.

<p><p>Property changes on: websites-ngen
___________________________________________________________________
Name: svn:ignore
   - *index.html
commit.txt

   + *index.html
commit.txt
tags

<p>Copied: websites-ngen/news/2004/04/21/speex115.markdown (from rev 6779, websites-ngen/news/on2004-04-21at0000.txt)

Added: websites-ngen/news/2004/05/12/icecast201Released.markdown
===================================================================
--- websites-ngen/news/2004/05/12/icecast201Released.markdown	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/news/2004/05/12/icecast201Released.markdown	2004-06-01 17:32:59 UTC (rev 6796)
@@ -0,0 +1,3 @@
+This patch release fixes a overflow buffer which can cause server crashes under certain circumstances. This release contains **only** the fix for this issue. We are still targeting a 2.1.0 release with new features and functionality in the near future.
+
+Download Icecast 2.0.1 from the [Download page](http://www.xiph.org/icecast/download/).

Added: websites-ngen/news/2004/05/29/postfish.markdown
===================================================================
--- websites-ngen/news/2004/05/29/postfish.markdown	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/news/2004/05/29/postfish.markdown	2004-06-01 17:32:59 UTC (rev 6796)
@@ -0,0 +1,5 @@
+Monty put together a prerelease of [Postfish][] so he could get back to work on [OggFile][].
+
+	[postfish]:	http://www.xiph.org/postfish/
+	[oggfile]:	http://www.xiph.org/oggfile/
+

Deleted: websites-ngen/news/on2004-04-19at0149.txt
===================================================================
--- websites-ngen/news/on2004-04-19at0149.txt	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/news/on2004-04-19at0149.txt	2004-06-01 17:32:59 UTC (rev 6796)
@@ -1,3 +0,0 @@
-This is a news post. With a [link to our new website][l].
-
-	[l]: http://www.xiph.org/

Deleted: websites-ngen/news/on2004-04-21at0000.txt
===================================================================
--- websites-ngen/news/on2004-04-21at0000.txt	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/news/on2004-04-21at0000.txt	2004-06-01 17:32:59 UTC (rev 6796)
@@ -1,6 +0,0 @@
-The main change in this release is that the 1.1.5
-<abbr title='application programming interface'>API</abbr> and
-<abbr title='application binary interface'>ABI</abbr> are now compatible with Speex
-1.0.<var>x</var>.
-The versions of the functions taking a `short*` now have an “_int” suffix, as in
-`speex_encode_int()`.

Modified: websites-ngen/news/options.ini
===================================================================
--- websites-ngen/news/options.ini	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/news/options.ini	2004-06-01 17:32:59 UTC (rev 6796)
@@ -1,30 +1,23 @@
 ;[Template]
 
 ;; One or more of the following in a space-separated list:
-;;	general
-;;	vorbis
-;;	flac
-;;	theora
-;;	icecast
+;;	general vorbis flac theora icecast postfish
+;;	security (for security-critical updates)
 
-; required:
-;	Title
-;	Tag Suffix
-;	
-;	
+[2004-05-29:postfish]
+title = Postfish Prerelease
+sections = postfish
+summary = Monty makes a prerelease version of Postfish, a digital audio post-processing, restoration, filtering, and mixdown tool.
+created = 2004-05-29T10:43:01Z
 
-[on2004-04-21at0000]
-Title = Speex 1.1.5 Released
-Tag Suffix = Speex115
-Sections = speex
-Issued = 2004-05-21T04:09:33Z
-; Modified = (optional)
-Summary = The 1.1.x branch of Speex now has a 1.0.x-compatible API and ABI.
+[2004-05-12:icecast201Released]
+title = Icecast 2.0.1 Released
+sections = icecast
+summary = This update fixes a remote crash bug.
+created = 2004-05-12T01:04:00Z
 
-[on2004-04-19at0149]
-Title = Vorbis-Tools Released (fake post)
-Tag Suffix = VorbisToolsReleased
-Sections = vorbis flac
-Summary = A new version of vorbis-tools was released; oggenc now has a FLAC input.
-
-
+[2004-04-21:speex115]
+title = Speex 1.1.5 Released
+sections = speex
+created = 2004-04-21T04:09:33Z
+summary = The 1.1.x branch of Speex now has a 1.0.x-compatible API and ABI.

Modified: websites-ngen/style.txt
===================================================================
--- websites-ngen/style.txt	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/style.txt	2004-06-01 17:32:59 UTC (rev 6796)
@@ -30,9 +30,9 @@
 
 *	Prefer ## to -------- for second-level headings, unlike in this file and in todo.txt.
 
-*	Directories generally not shown to users should start with a capital letter.
-	Prime examples of this are /CSS/, /Error/, and /JS/. However, their subdirectories
-	should start with lowercase letters, like /CSS/sidebars/.
+*	Directories generally not shown to users should start with a capital letter, such as
+	/CSS/, /Error/, and /JS/. However, their subdirectories should start with lowercase letters,
+	like /CSS/sidebars/.
 
 *	Don’t have multiple files or directories that differ only by case; this will cause problems
         on case-insensitive file systems like NTFS and HFS+.
@@ -45,7 +45,10 @@
         elsewhere in the page. To avoid getting caught by this snag, use lower case consistently
         in the part with the actual URL in it (and the optional title attribute).
 
+*	Keep as much as possible in the top-level .htaccess file. As far as I can tell, the
+	ErrorDocument directive is the only thing that needs to go in sudirectories’ .htaccess files.
 
+
 Spelling, Grammar, Punctuation, and Terminology
 -----------------------------------------------
 
@@ -78,8 +81,14 @@
         For more information, see the Apache 2 information at
         <http://httpd.apache.org/docs-2.0/mod/mod_alias.html#redirect>.
 
-*	Redirect to <http://www.xiph.org/>, not <http://xiph.org/>.
+*	Always refer to <http://www.xiph.org/>, not <http://xiph.org/>.
 
+*	Avoid full URLs with the "http://www.xiph.org/" part in it as they won’t link to development
+	versions of the site when run on development machines.
+
+*	Always use full URLs in news posts as the snippet of HTML could be displayed
+	anywhere from /news/ to /vorbis/news/ to some user’s news aggregator application.
+
 *	Do not abbreviate directory names. For example, make URLs so they look like
         “/vorbis/documentation/”, not “/vorbis/doc/”, with the exception of “/faq/”.
 

Added: websites-ngen/time.py
===================================================================
--- websites-ngen/time.py	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/time.py	2004-06-01 17:32:59 UTC (rev 6796)
@@ -0,0 +1,6 @@
+import time
+import datetime
+
+USUAL_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
+dt = datetime.datetime.utcnow()
+print "It is currently %s in W3C Date-Time-ese. Have a nice day." % dt.strftime(USUAL_TIME_FORMAT)

Modified: websites-ngen/todo.txt
===================================================================
--- websites-ngen/todo.txt	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/todo.txt	2004-06-01 17:32:59 UTC (rev 6796)
@@ -27,6 +27,7 @@
 
 *	I got one vote by j^ in #vorbis that the colors for the captioned buttons were too dark.
 
+
 Open Questions
 --------------
 

Modified: websites-ngen/wrapup.py
===================================================================
--- websites-ngen/wrapup.py	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/wrapup.py	2004-06-01 17:32:59 UTC (rev 6796)
@@ -24,19 +24,17 @@
 import pdb
 from ConfigParser import SafeConfigParser
 from sets import Set
+import profile
 
 MARKDOWN_COMMAND = "perl Markdown.pl --htmltags"
 TITLE_REGEX = re.compile(r"<h1.*>(.*)</h1>")
 LANGUAGE_EXTRACTOR = re.compile(r"^.*\.(.{2,5})\..*$") # anything from “.es.” to “.zh-TW.”
+USUAL_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" # W3C Date-Time format, so I'm told
 
-class MissingOptionError(Exception):
-	def __init__(self, option, section):
-		self.option = option
-		self.section = section
+####################################################################################################
+## Markdown Stuff
+##
 
-	def __str__(self):
-		return "Must define a '%s' in section %s" % (self.option, self.section)
-
 class MarkdownWrapper(object):
         """
         Important attributes:
@@ -82,8 +80,8 @@
                         self.destFilename = destFilename
                 else:
                         self.destFilename = os.path.splitext(sourceFilename)[0] + ".html"
-		self.doctype = '<!DOCTYPE HTML PUBLIC ' + \
-					   '"-//W3C//DTD HTML 4.01//EN" ' + \
+		self.doctype = '<!DOCTYPE HTML PUBLIC '  \
+					   '"-//W3C//DTD HTML 4.01//EN" '  \
                                            '"http://www.w3.org/TR/html4/strict.dtd">'
                 
                 self.mainText = markdown(sourceFilename)
@@ -123,15 +121,6 @@
         
         def __loadOptions(self):
                 
-		def getBaseName(path):
-			"""
-				'index.html'			-> 'index'
-				'index.es.markdown'		-> 'index.es'
-				'/foo/bar/baz.es.txt'	-> 'baz.es'
-			"""
-			filename = os.path.split(path)[1]
-			filenameWithoutExtension = os.path.splitext(filename)[0]
-			return filenameWithoutExtension
 
                 configFilename = getParallelFile(self.sourceFilename, 'options.ini')
                 if not os.path.isfile(configFilename):
@@ -259,93 +248,10 @@
                 ret += "</html>\n"
                 return ret
 
+###################################################################################################
+## News Item stuff
+##
 
-def markdown(filename):
-	"""Returns an HTML snippet from a filename in Markdown format."""
-	ret = ""
-	fh = os.popen(MARKDOWN_COMMAND + ' ' + filename)
-	try:
-		ret = fh.read()
-	finally:
-		fh.close()
-	
-	return snipBom(ret)
-
-def snipBom(s):
-	BOM = '\xef\xbb\xbf'
-	ret = ''
-	bomBegin = s.find(BOM)
-	if bomBegin > -1:
-		ret = s[:bomBegin] + s[bomBegin+3:]
-	else:
-		return s
-	return ret
-		
-
-def getParallelFile(where, what):
-	"""
-		given f("/home/foo.txt", "bar.html"), returns "/home/bar.html".
-	"""
-	return os.path.join(os.path.split(where)[0], what)
-
-def hasTwoDirseps(path):
-	firstIndex = path.find(os.sep)
-	if firstIndex == -1: return False
-
-	secondIndex = path.find(os.sep, firstIndex+1)
-	if secondIndex == -1: return False
-
-	return True
-
-def getParentedFile(filename):
-	r"""
-	given a path to a file, say, /meals/breakfast/sausage/index.html, tries to return
-	the first valid file of:
-		/meals/breakfast/sausage/index.html
-		/meals/breakfast/index.html
-		/meals/index.html
-		/index.html
-	Or given .\meals\breakfast\eggs\index.html:
-		.\meals\breakfast\eggs\index.html
-		.\meals\breakfast\index.html
-		.\meals\index.html
-		.\index.html
-	Or even given cheeses\france\brie.html:
-		cheeses\france\brie.html
-		cheeses\brie.html
-		brie.html
-	
-	Returns "" if no such file is found.
-	"""
-	while True:
-		# okay, we need to abort out on two conditions:
-		# 1) we found a valid path (return it)
-		# 2) we've gone as far as we can, and there is no valid path (return "")
-		if os.path.isfile(filename): return filename
-
-		# There is no parented file if there's only one separator in the path
-		if filename.find(os.sep) == -1:
-			return ""
-
-		# if the full string has two directory separators in it or more, then
-		# there are still intermediate directories to back up through.
-		# We need to chop out the thing just before the last dirsep,
-		# but end just before the penultimate dirsep.
-		if hasTwoDirseps(filename):
-			indexOfLastDirsep = filename.rfind(os.sep)
-			indexOfPenultimateDirsep = filename.rfind(os.sep, 0, indexOfLastDirsep)
-			filename = filename[:indexOfPenultimateDirsep] + filename[indexOfLastDirsep:]
-			if os.path.isfile(filename): return filename
-		elif filename.find(os.sep) > -1:
-			# If it has only one dirsep, then it's one of the following forms (modulo / vs. \)
-			#	* /foo.html
-			#	* ./foo.html
-			if os.path.isfile(filename): return filename
-			else: return ""
-		else:
-			if os.path.isfile(filename): return filename
-			else: return ""
-
 class NewsItem(object):
         def __init__(self):
                 self.post = ''
@@ -356,30 +262,36 @@
                 self.dateIssued = None
                 self.dateModified = None
                 self.sections = Set()
+		self.url = ''
         
-	def __cmp__(self, other):
-		return cmp(self.dateCreated, other.dateCreated)
+	def __str__(self):
+		return '<NewsItem object created at %s entitled "%s">' % \
+				(strftime(USUAL_TIME_FORMAT, self.dateCreated), self.title)
+	def __repr__(self):
+		return str(self)
+	def __getTagSuffix(self):
+		return self.tag.split(':')[-1]
+	tagSuffix = property(__getTagSuffix)
+	def __cmp__(self, other): return NewsItem.compareByDateCreated(self, other)
+	def compareByDateCreated(self, other):	return cmp(self.dateCreated,  other.dateCreated)
+	def compareByDateIssued(self, other):	return cmp(self.dateIssued,   other.dateIssued)
+	def compareByDateModified(self, other):	return cmp(self.dateModified, other.dateModified)
 
-def reverser(lhs, rhs):	# primarily to sort NewsItems newest-first
-	return -cmp(lhs, rhs)
-	
+def reverser(lhs, rhs, comparator=cmp):	# primarily to sort NewsItems newest-first
+	return -comparator(lhs, rhs)
 
 class NewsDispenser(object):
         def __init__(self, optionsFile):
-		"""Make a very simple list of news posts. This is not final code."""
                 config = SafeConfigParser({'sections': 'general'})
                 config.read(os.path.join("news", "options.ini"))
 
-		FILENAME_DATE_FORMAT = "on%Y-%m-%dat%H%M"
-		USUAL_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
                 
                 self.newsItems = []
                 # strftime: format as string; strptime: parse string and get a struct_time
                 # okay, now we want to go through the sections and associate each with their newspost.
                 for section in config.sections(): # section is string
                         ni = NewsItem()
-			ni.dateCreated = time.strptime(section, FILENAME_DATE_FORMAT)
-			#ni.dateModified = ni.dateIssued = ni.dateCreated #XXX
+			ni.tag = 'tag:xiph.org,' + section
                         for key, value in config.items(section):
                                 if key == 'title':
                                         ni.title = value
@@ -394,6 +306,9 @@
                                 if key == 'sections':
                                         ni.sections = Set(value.split())
 
+				if key == 'created':
+					ni.dateCreated = time.strptime(value, USUAL_TIME_FORMAT)
+					
                                 if key == 'issued':
                                         ni.dateIssued = time.strptime(value, USUAL_TIME_FORMAT)
                                         
@@ -404,100 +319,332 @@
                         # now for some error checking…
                         if not ni.title:	raise MissingOptionError('Title', section)
                         if not ni.summary:	raise MissingOptionError('Summary', section)
+			if len(ni.sections) == 0:
+				raise MissingOptionError('Sections', section)
 
-				
-			if not ni.tag:		raise MissingOptionError('Tag Suffix', section)
-
                         # …and some error covering…
+			# "date created" absolutely must be there. Everything else can be generated from that.
+			if not ni.dateCreated:
+				raise MissingOptionError('Created', section)
                         if not ni.dateModified:
                                 ni.dateModified = ni.dateCreated
                         if not ni.dateIssued:
                                 ni.dateIssued = ni.dateCreated
-			filename = os.path.join('news', section + '.txt')
+			
+			# sections look like [2004-04-21:speex115]
+			tagBits = re.compile(
+				r"^(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d):(?P<tag>\w*)$")
+			m = tagBits.match(section)
+			if not m:
+				raise "the tag bits regex didn't match"
+			
+			# make sure the two required dates match
+			ymdFromDateCreated = list(ni.dateCreated[:3])
+			ymdFromRegex = [int(m.group('year')), int(m.group('month')), int(m.group('day'))]
+			if ymdFromDateCreated != ymdFromRegex:
+				raise	"The date in section %s does not match its Created key %s, " \
+						"when was it _really_ created? " % \
+						(section, "%s-%s-%s" % ni.dateCreated[:3])
+
+			ni.url = "http://www.xiph.org/news/%s/%s/%s/%s/" % (
+					 m.group('year'),
+					 m.group('month'),
+					 m.group('day'),
+					 m.group('tag'))
+
+			filename = os.path.join('news',
+			                        m.group('year'),
+									m.group('month'),
+									m.group('day'),
+									m.group('tag') + '.markdown')
                         s = markdown(filename)
                         ni.post = s
                         self.newsItems.append(ni)
                 self.newsItems.sort(reverser)
         
-	def dump(self):
-		return self.newsItems[:]
+	def items(self):
+		return self.newsItems
         
-	def getItems(startDate, endDate):
+	def __itemsByTimespan(self, unitCode):
                 """
-			Return news items from
+			unitCode == 1? By year.
+			unitCode == 2? By month.
+			unitCode == 3? By day.
                 """
+		allDates = [] # list of `unitCode`-tuples in (year[, month[, day]]) format
+		ret = []
+		for date in [item.dateCreated[:unitCode] for item in self.newsItems]:
+			if date not in allDates:
+				allDates.append(date)
+		
+		for date in allDates:
+			itemsOfTheTimespan = [item for item in self.newsItems \
+							 if item.dateCreated[:unitCode] == date]
+			ret.append(itemsOfTheTimespan)
 
+		return ret
+	def itemsByDay(self):
+		"Returns a list of lists of items all with the same day."
+		return self.__itemsByTimespan(3)
+	
+	def itemsByMonth(self):
+		return self.__itemsByTimespan(2)
+	
+	def itemsByYear(self):
+		return self.__itemsByTimespan(1)
+
+		
+	
+
+###################################################################################################
+## News Formatters
+##
+	
 class NewsFormatter(object):
-	def __init__(self, newsItems): pass
+	def __init__(self, dispenser):
+		self.dispenser = dispenser
+	
+	def format(self, numberOfItems=0):
+		"""
+			Returns all news items as a formatted string.
+			Override this.
+		"""
+	def formatByIndex(self, index): pass
         def formatOne(self, newsItem): pass
-	def formatAll(self): pass
 
 class HTMLNewsFormatter(NewsFormatter):
         """ This is just one HTML news formatter of possibly many, really."""
         def __init__(self, newsItems, dateFormat="%A, %B %d, %Y"):
+		NewsFormatter.__init__(self, newsItems)
                 self.newsItems = newsItems
                 self.dateFormat = dateFormat
         
-	def formatOne(self, newsItem):
-		ret =  "<h1>" + newsItem.title + "</h1>\n"
-		ret += newsItem.post[:]
-		ret += "<p>Posted %s by %s</p>" %  \
-			   (time.strftime(self.dateFormat, newsItem.date), newsItem.author)
-		ret += "\n<hr>\n\n"
+	def formatForDay(self, year, month, day):
+		"return a formatted string for the day in question."
+		loloni = self.dispenser.itemsByDay() # list of lists of NewsItems
+		ret = ''
+		
+		matchingDayGroups = [x for x in loloni if x[0].dateCreated[:3] == (year, month, day)]
+		for dayGroup in matchingDayGroups:
+			ret +=	'<h1>News for %s</h1>\n' % strftime(self.dateFormat, dayGroup[0].dateIssued)
+			for item in dayGroup:
+				ret += self.formatOne(item, headingLevel=2)
 
                 return ret
         
-	def formatAll(self):
+	def formatForMonth(self, year, month):
+		loloni = self.dispenser.itemsByMonth()
                 ret = ''
+
+		for monthGroup in [x for x in loloni if x[0].dateCreated[:2] == (year, month)]:
+			ret += strftime("<h1>News for %B %Y</h1>\n", monthGroup[0].dateIssued)
+			for item in monthGroup:
+				ret += self.formatOne(item, headingLevel=2)
+		return ret
+
+	def formatForYear(self, year):
+		loloni = self.dispenser.itemsByYear()
+		ret = ''
+		for yearGroup in [x for x in loloni if x[0].dateCreated[:1] == (year,)]:
+			ret += strftime("<h1>News for %Y</h1>\n", yearGroup[0].dateIssued)
+			for item in yearGroup:
+				ret += self.formatOne(item, headingLevel=2)
+		return ret
+		
+		
+	def formatOne(self, newsItem, headingLevel=1):
+		ret =  "<div class='newsEntry'>\n"
+		ret += " <h%s>%s</h%s>\n" % (headingLevel, newsItem.title, headingLevel)
+		ret +=  newsItem.post[:]
+		ret += " <div class='postInfo'>Posted %s (<a href='%s'>permalink</a>)</div>\n" % \
+			    (time.strftime(self.dateFormat, newsItem.dateIssued), newsItem.url)
+		ret += "</div>\n\n"
+
+		return ret
+	
+	def format(self):
+		ret = ''
                 for ni in self.newsItems:
                         ret += self.formatOne(ni)
                 return ret
 
 class AtomNewsFormatter(NewsFormatter):
-
-	# Tag URIs: http://www.taguri.org/ (used for atom:id)
-
         def __init__(self, newsItems):
-		self.newsItems = newsItems
+		NewsFormatter.__init__(self, newsItems)
                 self.dateFormat = "%Y-%m-%dT%H:%M:%SZ" # lie and say it's at UTC (...Z)
         
-	def formatOne(self, newsItem, index=0):
+	def formatOne(self, newsItem):
+		def formatDate(date):
+			return strftime(self.dateFormat, date)
+
                 ret =  "<entry>\n"
-		ret += "<title>" + newsItem.title + "</title>\n"
-		if newsItem.dateCreated:
-			ret +=	"<created>%s</created>\n" % strftime(self.dateFormat, newsItem.dateCreated)
-		if newsItem.dateIssued:
-			ret +=	"<issued>%s</issued>\n" % strftime(self.dateFormat, newsItem.dateIssued)
-		if newsItem.dateModified:
-			ret +=	"<modified>%s</modified>\n" % strftime(self.dateFormat, newsItem.dateModified)
-		ret +=		"<id>%s</id>\n" % newsItem.tag
-		ret +=		"<link rel='alternate' type='text/html' href='http://www.xiph.org/news/'/>\n"
-		ret +=		"<content type='text/html' mode='escaped'><![CDATA["
-		ret +=		newsItem.post
-		ret +=		"]]></content>\n"
-		ret +=		"</entry>"
+		ret += " <title>" + newsItem.title + "</title>\n"
+		ret += " <created>%s</created>\n"	%	formatDate(newsItem.dateCreated)
+		ret += " <issued>%s</issued>\n"		%	formatDate(newsItem.dateIssued)
+		ret += " <modified>%s</modified>\n"	%	formatDate(newsItem.dateModified)
+		ret += " <id>%s</id>\n"				%	newsItem.tag
+		ret += " <link rel='alternate' type='text/html' href='%s'/>\n" % newsItem.url
+		ret += " <content type='text/html' mode='escaped'><![CDATA["
+		ret += newsItem.post
+		ret += "]]></content>\n"
+		ret += "</entry>"
                 return ret
-	def formatAll(self):
+	
+	def format(self):
                 ret = ''
                 ret +=	"<?xml version='1.0' encoding='utf-8'?>\n" \
                                 "<feed version='0.3' xmlns='http://purl.org/atom/ns#' xml:lang='en-US'>\n" \
                                 "<title>Xiph.Org News</title>\n" \
                                 "<link rel='alternate' type='text/html' href='http://www.xiph.org/news/'/>\n" \
                                 "<modified>%s</modified>\n" % \
-					strftime(self.dateFormat, self.newsItems[0].dateModified)
-		ret +=	"<generator url='http://svn.xiph.org/websites-ngen/wrapup.py'>Wrapup</generator>\n" \
+					strftime(self.dateFormat, self.dispenser.items()[0].dateModified)
+		ret +=	"<generator url='http://svn.xiph.org/websites-ngen/wrapup.py'>" \
+				"Wrapup" \
+				"</generator>\n" \
                                 "<copyright>© %s Xiph.Org Foundation</copyright>\n" % time.gmtime()[0] + \
                                 "<author><name>Xiph.Org Foundation</name></author>\n\n"
 
-		for index, newsItem in enumerate(self.newsItems):
-			ret += self.formatOne(newsItem, index) + "\n\n"
+		for index, newsItem in enumerate(self.dispenser.items()):
+			ret += self.formatOne(newsItem) + "\n\n"
                 
                 ret += "</feed>"
                 return ret
 
+###################################################################################################
+## Miscellaneous Functions and Exception Classes
+##
+
+def passPrint(x):
+	print x
+	return x
+
+class MissingOptionError(Exception):
+	def __init__(self, option, section):
+		self.option = option
+		self.section = section
+
+	def __str__(self):
+		return "Must define a '%s' in section %s" % (self.option, self.section)
+
+
+def markdown(filename):
+	"""Returns an HTML snippet from a filename in Markdown format."""
+	ret = ""
+
+	if not os.path.isfile(filename):
+		raise RuntimeError("Can not markdown non-existent file " + filename)
+
+	fh = os.popen(MARKDOWN_COMMAND + ' ' + filename)
+	try:
+		ret = fh.read()
+	finally:
+		fh.close()
+	
+	return snipBom(ret)
+
+
+def snipBom(s):
+	BOM = '\xef\xbb\xbf'
+	ret = ''
+	bomBegin = s.find(BOM)
+	if bomBegin > -1:
+		ret = s[:bomBegin] + s[bomBegin+3:]
+	else:
+		return s
+	return ret
+		
+
+def hasTwoDirseps(path):
+	firstIndex = path.find(os.sep)
+	if firstIndex == -1: return False
+
+	secondIndex = path.find(os.sep, firstIndex+1)
+	if secondIndex == -1: return False
+
+	return True
+
+
+def getParallelFile(where, what):
+	"""
+		given f("/home/foo.txt", "bar.html"), returns "/home/bar.html".
+	"""
+	return os.path.join(os.path.split(where)[0], what)
+
+
+def getParentedFile(filename):
+	r"""
+	given a path to a file, say, /meals/breakfast/sausage/index.html, tries to return
+	the first valid file of:
+		/meals/breakfast/sausage/index.html
+		/meals/breakfast/index.html
+		/meals/index.html
+		/index.html
+	Or given .\meals\breakfast\eggs\index.html:
+		.\meals\breakfast\eggs\index.html
+		.\meals\breakfast\index.html
+		.\meals\index.html
+		.\index.html
+	Or even given cheeses\france\brie.html:
+		cheeses\france\brie.html
+		cheeses\brie.html
+		brie.html
+	
+	Returns "" if no such file is found.
+	"""
+	while True:
+		# okay, we need to abort out on two conditions:
+		# 1) we found a valid path (return it)
+		# 2) we've gone as far as we can, and there is no valid path (return "")
+		if os.path.isfile(filename): return filename
+
+		# There is no parented file if there's only one separator in the path
+		if filename.find(os.sep) == -1:
+			return ""
+
+		# if the full string has two directory separators in it or more, then
+		# there are still intermediate directories to back up through.
+		# We need to chop out the thing just before the last dirsep,
+		# but end just before the penultimate dirsep.
+		if hasTwoDirseps(filename):
+			indexOfLastDirsep = filename.rfind(os.sep)
+			indexOfPenultimateDirsep = filename.rfind(os.sep, 0, indexOfLastDirsep)
+			filename = filename[:indexOfPenultimateDirsep] + filename[indexOfLastDirsep:]
+			if os.path.isfile(filename): return filename
+		elif filename.find(os.sep) > -1:
+			# If it has only one dirsep, then it's one of the following forms (modulo / vs. \)
+			#	* /foo.html
+			#	* ./foo.html
+			if os.path.isfile(filename): return filename
+			else: return ""
+		else:
+			if os.path.isfile(filename): return filename
+			else: return ""
+
+
+def getBaseName(path):
+	"""
+		'index.html'			-> 'index'
+		'index.es.markdown'		-> 'index.es'
+		'/foo/bar/baz.es.txt'	-> 'baz.es'
+	"""
+	filename = os.path.split(path)[1]
+	filenameWithoutExtension = os.path.splitext(filename)[0]
+	return filenameWithoutExtension
+
+def padto2(s):
+	if len(s) > 1:
+		return s
+	else:
+		return "0" + s
+
+
+###################################################################################################
+## Driver Functions
+##
+
 def doSite():
         for dirpath, dirnames, filenames in os.walk('xiph.org'):
-		for filename in filter(lambda x: x.endswith(".markdown"), filenames):
+		for filename in [x for x in filenames if x.endswith('.markdown')]:
                         srcPath = os.path.join(dirpath, filename)
                         destFile = os.path.splitext(filename)[0] + ".html"
                         destPath = getParallelFile(srcPath, destFile)
@@ -508,13 +655,76 @@
                         dirnames.remove('.svn') # don't transform things in .svn directories
 
 def doNews():
+	def doNewsInAtom(newsDispenser):
+		s = AtomNewsFormatter(newsDispenser).format()
+		fh = open(os.path.join('xiph.org', 'feeds', 'atom.xml'), 'w')
+		fh.write(s)
+		fh.close()
+	def doNewsInHTML(newsDispenser):
+		def entrywise(formatter):
+			for item in formatter.dispenser.items():
+				s = formatter.formatOne(item)
+				(year, month, day) = [padto2(str(x)) for x in item.dateCreated[:3]]
+				destPath = os.path.join('xiph.org', 'news', year, month, day, item.tagSuffix)
+				try: os.makedirs(destPath)
+				except OSError: pass # the directory was already there
+				fh = open(os.path.join(destPath, 'index.markdown'), 'w')
+				try:
+					fh.write(s)
+				finally:
+					fh.close()
+		
+
+		def daily(formatter):
+			for itemGroup in formatter.dispenser.itemsByDay():
+				(year, month, day) = [padto2(str(x)) for x in itemGroup[0].dateCreated[:3]]
+				destPath = os.path.join('xiph.org', 'news', year, month, day, 'index.markdown')
+				fh = open(destPath, 'w')
+				try:
+					fh.write(formatter.formatForDay(int(year), int(month), int(day)))
+				finally:
+					fh.close()
+
+
+		def monthly(formatter):
+			for itemGroup in formatter.dispenser.itemsByMonth():
+				(year, month) = [padto2(str(x)) for x in itemGroup[0].dateCreated[:2]]
+				destPath = os.path.join('xiph.org', 'news', year, month, 'index.markdown')
+				fh = open(destPath, 'w')
+				try:
+					fh.write(formatter.formatForMonth(int(year), int(month)))
+				finally:
+					fh.close()
+					
+		def yearly(formatter):
+			for itemGroup in formatter.dispenser.itemsByYear():
+				(year,) = [padto2(str(x)) for x in itemGroup[0].dateCreated[:1]]
+				destPath = os.path.join('xiph.org', 'news', year, 'index.markdown')
+				fh = open(destPath, 'w')
+				try:
+					fh.write(formatter.formatForYear(int(year)))
+				finally:
+					fh.close()
+
+		def toplevel(formatter):
+			pass
+
+		formatter = HTMLNewsFormatter(newsDispenser)
+		
+		for f in [entrywise, daily, monthly, yearly, toplevel]:
+			f(formatter)
+
+
         nd = NewsDispenser(os.path.join('news', 'options.ini'))
-	#print HTMLNewsFormatter(nd.dump()).formatAll()
-	s = AtomNewsFormatter(nd.dump()).formatAll()
-	fh = open(os.path.join('xiph.org', 'feeds', 'atom.xml'), 'w')
-	fh.write(s)
-	fh.close()
+	doNewsInAtom(nd)
+	doNewsInHTML(nd)
+		
+def main():
+	doNews()
+	doSite()
 
 if __name__ == '__main__':
-	#doNews()
-	doSite()
+	if len(sys.argv) > 1 and sys.argv[1] == '--profile':
+		profile.run('main()')
+	else:
+		main()

<p>Property changes on: websites-ngen/xiph.org
___________________________________________________________________
Name: svn:ignore
   - index.html
index.es.html
index.de.html

   + index.html
index.es.html
index.de.html
news

<p>Modified: websites-ngen/xiph.org/aftermaintext.inherit
===================================================================
--- websites-ngen/xiph.org/aftermaintext.inherit	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/xiph.org/aftermaintext.inherit	2004-06-01 17:32:59 UTC (rev 6796)
@@ -3,6 +3,8 @@
            title='About us'>About</a> |
         <a href='/press/'
            title='Read our press releases'>Press</a> |
+	<a href='/news/'
+	   title='Read what’s been happening'>News</a> |
         <!-- 
         <a href='/donate/'
            title='Give us money'>Donate</a> |

Modified: websites-ngen/xiph.org/index.markdown
===================================================================
--- websites-ngen/xiph.org/index.markdown	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/xiph.org/index.markdown	2004-06-01 17:32:59 UTC (rev 6796)
@@ -6,6 +6,7 @@
 
 <!-- Communicate “How can we help you with our codecs?” as effectively as possible -->
 
+<!-- quoth mother: “what’s a codec?” -->
 
 Xiph.org develops multimedia codecs and related tools to ensure that the foundations of Internet
 multimedia remain free and open. By using our codecs, you can save money on software- and

Added: websites-ngen/xiph.org/speex/.htaccess
===================================================================
--- websites-ngen/xiph.org/speex/.htaccess	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/xiph.org/speex/.htaccess	2004-06-01 17:32:59 UTC (rev 6796)
@@ -0,0 +1 @@
+ErrorDocument 404 /speex/Error/404/

Added: websites-ngen/xiph.org/speex/Error/404/index.markdown
===================================================================
--- websites-ngen/xiph.org/speex/Error/404/index.markdown	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/xiph.org/speex/Error/404/index.markdown	2004-06-01 17:32:59 UTC (rev 6796)
@@ -0,0 +1,4 @@
+404
+===
+
+Did you mis-type something?

Added: websites-ngen/xiph.org/vorbis/.htaccess
===================================================================
--- websites-ngen/xiph.org/vorbis/.htaccess	2004-05-30 13:56:29 UTC (rev 6795)
+++ websites-ngen/xiph.org/vorbis/.htaccess	2004-06-01 17:32:59 UTC (rev 6796)
@@ -0,0 +1,2 @@
+ErrorDocument 404 /vorbis/Error/404/
+ErrorDocument 410 /vorbis/Error/410/

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list