# # # add_file "templates/revisionfilebin.html" # content [ed073e34de5e16ad7ed0d14ce01bff80b0d78970] # # add_file "templates/revisionfileimg.html" # content [cd246dd75333b2ced5d8dd9216e0d50a16769dfd] # # add_file "templates/revisionfiletxt.html" # content [8abafa4c4189b5c596ac59e6061d0ad116909393] # # patch "fdo/sharedmimeinfo.py" # from [fd8b931942384ac8627bebe9d05226f9db27611a] # to [d55219697c05e208ba20248969b5239be6b16479] # # patch "syntax.py" # from [44f5b93cb1df829e1ca98c8c1b21dc4e024f9751] # to [510d8085a40d9e86afbb71cafcbc6cf6a1205566] # # patch "templates/revisionfile.html" # from [1d6c59527444f4efd35f210837e0c64a6375f480] # to [652180e381d9e181020c3a4a52f5bc85cef8397d] # # patch "viewmtn.py" # from [41ee82ea67af893f58bbd2d99f2913d96890f4a5] # to [25ef3613dbdc3d4bbf01f2924a3955f6713e7647] # ============================================================ --- templates/revisionfilebin.html ed073e34de5e16ad7ed0d14ce01bff80b0d78970 +++ templates/revisionfilebin.html ed073e34de5e16ad7ed0d14ce01bff80b0d78970 @@ -0,0 +1,10 @@ +#extends revisionfile + +#def filecontents +

+Unfortunately, this ViewMTN has determined that this file (with +MIME type $mimetype) is not suitable for display inside the web +browser. If you feel this file could have been better displayed +please inform the author. +

+#end def ============================================================ --- templates/revisionfileimg.html cd246dd75333b2ced5d8dd9216e0d50a16769dfd +++ templates/revisionfileimg.html cd246dd75333b2ced5d8dd9216e0d50a16769dfd @@ -0,0 +1,5 @@ +#extends revisionfile + +#def filecontents + +#end def ============================================================ --- templates/revisionfiletxt.html 8abafa4c4189b5c596ac59e6061d0ad116909393 +++ templates/revisionfiletxt.html 8abafa4c4189b5c596ac59e6061d0ad116909393 @@ -0,0 +1,12 @@ +#extends revisionfile + +#def filecontents +
+#filter Filter
+#for $line in $contents
+$line
+#end for
+#filter WebSafe
+
+#end def + ============================================================ --- fdo/sharedmimeinfo.py fd8b931942384ac8627bebe9d05226f9db27611a +++ fdo/sharedmimeinfo.py d55219697c05e208ba20248969b5239be6b16479 @@ -198,7 +198,7 @@ class MagicLookup(object): line = read_line() current_header.append(line) - def lookup(self, data, min_priority=None): + def lookup(self, data, priorities=None): def match_line(line): data_size = len(data) value_size = len(line['value']) @@ -237,23 +237,59 @@ class MagicLookup(object): elif indent <= depth: depth = indent - 1 - # do a lookup, until we reach min_priority - priorities = self.headers.keys() - priorities.sort() - priorities.reverse() + if not priorities: + priorities = self.priorities() for priority in priorities: - if priority < min_priority: - break for mime_type, lines in self.headers[priority]: if match_lines(lines): return mime_type return None + def priorities(self): + rv = self.headers.keys() + rv.sort() + rv.reverse() + return rv + +class LookupHelper: + def __init__(self): + self.glob_lookup = GlobLookup() + self.magic_lookup = MagicLookup() + nontext_chars = "\x01\x02\x03\x04\x05\x06\x0e\x0f"\ + "\x10\x11\x12\x13\x14\x15\x16\x17"\ + "\x18\x19\x1a\x1c\x1d\x1e\x1f" + self.nontext = {} + for char in nontext_chars: + self.nontext[char] = True + + def is_binary(self, str): + for char in str: + if self.nontext.has_key(char): + return True + return False + + def lookup(self, filename, data): + # spec says we try >= 80 priority magic matchers, then filename, then the other matchers + threshold = 80 + priorities = self.magic_lookup.priorities() + rv = self.magic_lookup.lookup(data, [t for t in priorities if t >= threshold]) + if rv != None: + return rv + # then try guessing from filename + rv = self.glob_lookup.lookup(filename) + if rv != None: + return rv + # then try the other magic matchers + rv = self.magic_lookup.lookup(data, [t for t in priorities if t < threshold]) + if rv != None: + return rv + # okay; fall-back behaviour is to return text/plain iff ! is_binary, + # otherwise application/octet-stream + if is_binary(data): + return 'application/octet-stream' + else: + return 'text/plain' + if __name__ == '__main__': + c = LookupHelper() + print c.lookup('test.tar', '') - a = GlobLookup() - b = MagicLookup() - print b.lookup(open('/Users/grahame/Desktop/Network Servers V1_00.xls').read()) - sys.exit(0) - for line in sys.stdin: - line = line.strip() - print line, a.lookup(line), b.lookup(line) ============================================================ --- syntax.py 44f5b93cb1df829e1ca98c8c1b21dc4e024f9751 +++ syntax.py 510d8085a40d9e86afbb71cafcbc6cf6a1205566 @@ -6,6 +6,10 @@ import select import popen2 import select +mime_to_lang = { + +} + def highlight(lines, language='py'): """A generator which will read lines from the given input stream, and yield them back in syntax highlighted, HTML format. """ ============================================================ --- templates/revisionfile.html 1d6c59527444f4efd35f210837e0c64a6375f480 +++ templates/revisionfile.html 652180e381d9e181020c3a4a52f5bc85cef8397d @@ -9,12 +9,7 @@ $link($filename, for_download=True).html #filter WebSafe

-
-#filter Filter
-#for $line in $contents
-$line
-#end for
-#filter WebSafe
-
+#block filecontents +#end block #end def ============================================================ --- viewmtn.py 41ee82ea67af893f58bbd2d99f2913d96890f4a5 +++ viewmtn.py 25ef3613dbdc3d4bbf01f2924a3955f6713e7647 @@ -19,6 +19,7 @@ from colorsys import hls_to_rgb import datetime import cStringIO from colorsys import hls_to_rgb +from fdo import sharedmimeinfo hq = cgi.escape import web @@ -281,7 +282,8 @@ class Renderer: # any templates that can be inherited from, should be added to the list here self.templates = [ ('base.html', 'base'), ('revision.html', 'revision'), - ('branch.html', 'branch') ] + ('branch.html', 'branch'), + ('revisionfile.html', 'revisionfile') ] self._templates_loaded = False # these variables will be available to any template @@ -308,6 +310,7 @@ ops = mtn.Operations([config.monotone, c renderer = Renderer() ops = mtn.Operations([config.monotone, config.dbfile]) +mimehelp = sharedmimeinfo.LookupHelper() class Index: def GET(self): @@ -510,10 +513,23 @@ class RevisionFile(RevisionPage): if not fileid: return web.notfound() contents = ops.get_file(fileid) - renderer.render('revisionfile.html', + mimetype = mimehelp.lookup(filename, '') + mime_to_template = { + 'image/jpeg' : 'revisionfileimg.html', + 'image/png' : 'revisionfileimg.html', + 'image/gif' : 'revisionfileimg.html' + } + template = mime_to_template.get(mimetype, None) + if not template: + if mimetype.startswith('text/'): + template = 'revisionfiletxt.html' + else: + template = 'revisionfilebin.html' + renderer.render(template, filename=mtn.File(filename, revision), page_title="File %s in revision %s" % (filename, revision.abbrev()), revision=revision, + mimetype=mimetype, contents=syntax.highlight(contents, language)) class RevisionDownloadFile(RevisionPage): @@ -523,7 +539,10 @@ class RevisionDownloadFile(RevisionPage) fileid = RevisionPage.get_fileid(self, revision, filename) if not fileid: return web.notfound() - for data in ops.get_file(fileid): + for idx, data in enumerate(ops.get_file(fileid)): + if idx == 0: + mimetype = mimehelp.lookup(filename, data) + web.header('Content-Type', mimetype) sys.stdout.write(data) sys.stdout.flush() @@ -533,6 +552,7 @@ class RevisionTar: # http://en.wikipedia.org/wiki/Tar_%28file_format%29 revision = mtn.Revision(revision) web.header('Content-Disposition', 'attachment; filename=%s.tar' % revision) + web.header('Content-Type', 'application/x-tar') manifest = [stanza for stanza in ops.get_manifest_of(revision)] # for now; we might want to come up with something more interesting; # maybe the branch name (but there might be multiple branches?)