def test_extra_mimetypes(self): """ The text/x-ini mimetype is normally not known by Trac, but Pygments supports it. """ mimeview = Mimeview(self.env) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.ini')) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.cfg')) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.text/x-ini'))
def _render_file(self, req, context, repos, node, rev=None): """ trac.versioncontrol.web_ui.browser.BrowserModule._render_file() copy with just the essentials needed for our purpose. """ req.perm(context.resource).require('FILE_VIEW') mimeview = Mimeview(self.env) # MIME type detection CHUNK_SIZE = 4096 content = node.get_content() chunk = content.read(CHUNK_SIZE) mime_type = node.content_type if not mime_type or mime_type == 'application/octet-stream': mime_type = mimeview.get_mimetype(node.name, chunk) or \ mime_type or 'text/plain' self.log.debug("Rendering ReposReadMe of node %s@%s with mime-type %s" % (node.name, str(rev), mime_type)) del content # the remainder of that content is not needed add_stylesheet(req, 'common/css/code.css') annotations = [] force_source = False raw_href = '' return mimeview.preview_data(context, node.get_content(), node.get_content_length(), mime_type, node.created_path, raw_href, annotations=annotations, force_source=force_source)
def expand_macro(self, formatter, name, content): add_stylesheet(formatter.req, 'lineno/css/lineno.css') i = 1 self._anchor = 'a1' while self._anchor in formatter._anchors: self._anchor = 'a' + str(i) i += 1 formatter._anchors[self._anchor] = True mt = 'txt' match = WikiParser._processor_re.match(content) if match: try: #Trac 0.12+ mt = match.group(2) content = content[match.end(2)+1:] except IndexError: #Trac 0.11 mt = match.group(1) content = content[match.end(1)+1:] mimeview = Mimeview(formatter.env) mimetype = mimeview.get_mimetype(mt) or mimeview.get_mimetype('txt') return mimeview.render(formatter.context, mimetype, content, annotations=['codeblock-lineno'])
def _render_file(self, req, context, repos, node, rev=None): req.perm(node.resource).require('FILE_VIEW') mimeview = Mimeview(self.env) # MIME type detection content = node.get_content() chunk = content.read(CHUNK_SIZE) mime_type = node.content_type if not mime_type or mime_type == 'application/octet-stream': mime_type = mimeview.get_mimetype(node.name, chunk) or \ mime_type or 'text/plain' # Eventually send the file directly format = req.args.get('format') if format in ('raw', 'txt'): req.send_response(200) req.send_header('Content-Type', 'text/plain' if format == 'txt' else mime_type) req.send_header('Content-Length', node.content_length) req.send_header('Last-Modified', http_date(node.last_modified)) if rev is None: req.send_header('Pragma', 'no-cache') req.send_header('Cache-Control', 'no-cache') req.send_header('Expires', 'Fri, 01 Jan 1999 00:00:00 GMT') if not self.render_unsafe_content: # Force browser to download files instead of rendering # them, since they might contain malicious code enabling # XSS attacks req.send_header('Content-Disposition', 'attachment') req.end_headers() while 1: if not chunk: raise RequestDone req.write(chunk) chunk = content.read(CHUNK_SIZE) else: # The changeset corresponding to the last change on `node` # is more interesting than the `rev` changeset. changeset = repos.get_changeset(node.created_rev) # add ''Plain Text'' alternate link if needed if not is_binary(chunk) and mime_type != 'text/plain': plain_href = req.href.browser(repos.reponame or None, node.path, rev=rev, format='txt') add_link(req, 'alternate', plain_href, _('Plain Text'), 'text/plain') # add ''Original Format'' alternate link (always) raw_href = req.href.export(rev or repos.youngest_rev, repos.reponame or None, node.path) add_link(req, 'alternate', raw_href, _('Original Format'), mime_type) self.log.debug("Rendering preview of node %s@%s with mime-type %s" % (node.name, str(rev), mime_type)) del content # the remainder of that content is not needed add_stylesheet(req, 'common/css/code.css') annotations = ['lineno'] annotate = req.args.get('annotate') if annotate: annotations.insert(0, annotate) preview_data = mimeview.preview_data(context, node.get_content(), node.get_content_length(), mime_type, node.created_path, raw_href, annotations=annotations, force_source=bool(annotate)) return { 'changeset': changeset, 'size': node.content_length, 'preview': preview_data, 'annotate': annotate, }
class FilePage(Page): def __init__(self, env, node, root, base): super(FilePage, self).__init__(env, node, root, base) self.mimeview = Mimeview(self.env) self.exists = (node is not None) self.mime_type = None self.chunk = None def get_html(self, req): """ Get the raw content from the repository and convert to html. """ mime_type, chunk = self.get_raw() if not mime_type.startswith('text'): raise TracError("Invalid mime-type: %s" % mime_type) # Hack to support images, we change the path from relative # the document being requested to absolute. # 1: Ignore http and ftp urls to allow images to be fetched # 2: Assume URLS beginning with "/" are relative to top-level # 3: Assume URLS that do not include "http/ftp" are relative to # current path. def fixup(m): text = m.group(1) if text.startswith('http:') or text.startswith('ftp:'): return m.group(0) if text.startswith('/'): text = text[1:] dir = self.dir if dir.endswith('/'): dir = dir[:-1] return '.. image:: %s/%s' % (req.href.docs(dir), text) chunk = re.sub('\.\. image\:\:\s*(\S+)', fixup, chunk, re.MULTILINE) # Assume all wiki pages are ReStructuredText documents result = self.mimeview.render(req, mime_type, chunk) if not isinstance(result, (str, unicode)): result = unicode(result) # Hack to pretty-print source code (assumes all literal-blocks # contain source code). result = result.replace('<pre class="literal-block">', '<pre class="literal-block prettyprint">') if 'prettyprint' in result: # FIXME: Add as an event listener instead? result += """ <script type="text/javascript"> var origOnLoad = window.onload; function onLoad() { if (origOnLoad) { if(typeof(origOnLoad) == "string") { eval(origOnLoad); } else { origOnLoad(); } } prettyPrint(); } window.onload = onLoad; </script> """ return Markup(result) def get_raw(self): """ Load the raw content from the repository. """ if self.mime_type is not None: return self.mime_type, self.chunk node = self.node content_length = node.get_content_length() if content_length > (1024 * 1024): raise TracError("Docs is too large: %d" % content_length) content = node.get_content() chunk = content.read(content_length) mime_type = node.content_type # Guess the mime-type when possible to be text/plain. if not mime_type or mime_type == 'application/octet-stream': mime_type = self.mimeview.get_mimetype(node.name, chunk) or \ mime_type or 'text/plain' if mime_type.startswith('text/plain'): mime_type = 'text/x-rst; charset=utf8' self.mime_type = mime_type self.chunk = chunk return mime_type, chunk def save(self, req, text, comment): """ Save the specified text into this document. """ if not isinstance(self.node.repos, SubversionRepository): raise TracError("The '%s' repository is not supported" % type(self.node.repos)) from svn import core as _core from svn import fs as _fs from svn import repos as _repos repos = self.node.repos.repos #.repos revnum = self.node._requested_rev author = req.authname message = 'Edited %s' % self.base[1:] if comment: message += ' (%s)' % comment pool = _core.Pool() fs_txn = _repos.fs_begin_txn_for_commit(repos, revnum, author, message, pool) fs_root = _fs.svn_fs_txn_root(fs_txn, pool) if hasattr(self.node, '_scoped_svn_path'): fs_path = self.node._scoped_svn_path else: fs_path = self.node._scoped_path_utf8 stream = _fs.svn_fs_apply_text(fs_root, fs_path, None, pool) _core.svn_stream_write(stream, text) _core.svn_stream_close(stream) return _repos.fs_commit_txn(repos, fs_txn, pool)
def filter_stream(self, req, method, template, stream, data): if not (template == 'browser.html' and data.get('dir')): if ((not data.get('dir')) and (data.get('path')) and (data.get('path').endswith('.md'))): # Rendering single markdown file preview stream = stream | Transformer("//head/script[not(@src)][1]").after( tag.script( Markup( "jQuery(document).ready(function($) {" " $('#preview').each(function() {" " $(this).html(marked( $(this).children('pre').first().text() ));" " });" "});" ), type = "text/javascript" ) ) return stream add_stylesheet(req, 'common/css/code.css') repos = data.get('repos') or self.env.get_repository() rev = req.args.get('rev', None) for entry in data['dir']['entries']: # Rendering all READMEs in a directory preview try: if not entry.isdir and entry.name.lower().startswith('readme'): node = repos.get_node(entry.path, rev) req.perm(data['context'].resource).require('FILE_VIEW') mimeview = Mimeview(self.env) content = node.get_content() mimetype = node.content_type divclass = 'searchable' if entry.name.lower().endswith('.wiki'): mimetype = 'text/x-trac-wiki' divclass = 'searchable wiki' elif entry.name.lower().endswith('.md'): mimetype = 'text/x-markdown' divclass = 'searchable markdown' if not mimetype or mimetype == 'application/octet-stream': mimetype = mimeview.get_mimetype(node.name, content.read(4096)) or mimetype or 'text/plain' del content self.log.debug("ReadmeRenderer: rendering node %s@%s as %s" % (node.name, str(rev), mimetype)) output = mimeview.preview_data( data['context'], node.get_content(), node.get_content_length(), mimetype, node.created_path, '', annotations = [], force_source = False ) if output: if isinstance(output['rendered'], Stream): #content = output['rendered'].select('./pre/node()') #content = output['rendered'].select('./pre') content = output['rendered'].select('.') else: self.log.debug("GOT THERE") content = output['rendered'] insert = tag.div( tag.h1(entry.name, tag.a(Markup(' ¶'), class_ = "anchor", href = '#' + entry.name, title = 'Link to file' ), id_ = entry.name ), tag.div( content, #xml:space = "preserve", class_ = divclass, title = entry.name ), class_ = "readme", style = "padding-top: 1em;" ) stream = stream | Transformer("//div[@id='content']/div[@id='help']").before(insert) except Exception, e: self.log.debug(to_unicode(e))
def send_as_email(self, req, sender, recipients, subject, text, mode, *resources): """ `authname` Trac username of sender `sender` Tuple of (real name, email address) `recipients` List of (real name, email address) recipient address tuples `subject` The e-mail subject `text` The text body of the e-mail `files` List of paths to the files to send """ assert len(resources) > 0, 'Nothing to send!' mailsys = self.distributor(self.env) from_addr = sender[1] root = MIMEMultipart('related') root.set_charset(self._make_charset()) root.preamble = 'This is a multi-part message in MIME format.' headers = {} recp = [r[1] for r in recipients] headers['Subject'] = subject headers['To'] = ', '.join(recp) headers['From'] = from_addr headers['Date'] = formatdate() authname = req.authname files = [] links = [] attachments = [] mimeview = Mimeview(self.env) for r in resources: repo = RepositoryManager(self.env).get_repository(r.parent.id) n = repo.get_node(r.id, rev=r.version) files.append(n.path) f = os.path.join(repo.repos.path, n.path) if mode in (self.LINKS_ONLY, self.LINKS_ATTACHMENTS): links.append((req.abs_href.browser(repo.reponame or None, n.path, format='raw'), os.path.basename(f))) if mode in (self.ATTACHMENTS_ONLY, self.LINKS_ATTACHMENTS): content = n.get_content().read() mtype = n.get_content_type() or mimeview.get_mimetype(f, content) if not mtype: mtype = 'application/octet-stream' if '; charset=' in mtype: # What to use encoding for? mtype, encoding = mtype.split('; charset=', 1) attachments.append(os.path.basename(f)) maintype, subtype = mtype.split('/', 1) part = MIMEBase(maintype, subtype) part.set_payload(content) part.add_header('content-disposition', 'attachment', filename=os.path.basename(f)) encode_base64(part) root.attach(part) body = self._format_email(authname, sender, recipients, subject, text, mode, links, attachments) msg = MIMEText(body, 'html', 'utf-8') root.attach(msg) del root['Content-Transfer-Encoding'] for k, v in headers.items(): set_header(root, k, v) email = (from_addr, recp, root.as_string()) # Write mail to /tmp #import logging #if self.log.isEnabledFor(logging.DEBUG): # (fd, tmpname) = mkstemp() # os.write(fd, email[2]) # os.close(fd) # self.log.debug('Wrote mail from %s to %s to %s', email[0], email[1], tmpname) self.log.info('Sending mail with items %s from %s to %s', resources, from_addr, recp) try: if using_announcer: if mailsys.use_threaded_delivery: mailsys.get_delivery_queue().put(email) else: mailsys.send(*email) else: mailsys.send_email(*email) except Exception, e: raise TracError(e.message)
def _render_readme(self, req, stream, data): add_stylesheet(req, 'common/css/code.css') repos = data.get('repos') or self.env.get_repository() rev = req.args.get('rev', None) # Rendering all READMEs in a directory preview for entry in data['dir']['entries']: try: if not entry.isdir and entry.name.lower().startswith('readme'): node = repos.get_node(entry.path, rev) req.perm(data['context'].resource).require('FILE_VIEW') mimeview = Mimeview(self.env) content = node.get_content() mimetype = node.content_type divclass = 'searchable' if entry.name.lower().endswith('.wiki'): mimetype = 'text/x-trac-wiki' divclass = 'searchable wiki' elif entry.name.lower().endswith('.md'): mimetype = 'text/x-markdown' divclass = 'searchable markdown' if not mimetype or mimetype == 'application/octet-stream': mimetype = mimeview.get_mimetype( node.name, content.read(4096)) or \ mimetype or 'text/plain' del content self.log.debug( "ReadmeRenderer: rendering node %s@%s as %s", node.name, rev, mimetype) output = mimeview.preview_data( data['context'], node.get_content(), node.get_content_length(), mimetype, node.created_path, '', annotations=[], force_source=False) if output: if isinstance(output['rendered'], Stream): content = output['rendered'].select('.') else: content = output['rendered'] insert = tag.div( tag.h1(entry.name, tag.a(Markup(' ¶'), class_="anchor", href='#' + entry.name, title='Link to file'), id_=entry.name), tag.div(content, class_=divclass, title=entry.name), class_="readme", style="padding-top: 1em;" ) xpath = "//div[@id='content']/div[@id='help']" stream |= Transformer(xpath).before(insert) except Exception, e: self.log.debug(to_unicode(e))
def _render_file(self, req, context, repos, node, rev=None): req.perm(node.resource).require("FILE_VIEW") mimeview = Mimeview(self.env) # MIME type detection content = node.get_processed_content() chunk = content.read(CHUNK_SIZE) mime_type = node.content_type if not mime_type or mime_type == "application/octet-stream": mime_type = mimeview.get_mimetype(node.name, chunk) or mime_type or "text/plain" # Eventually send the file directly format = req.args.get("format") if format in ("raw", "txt"): req.send_response(200) req.send_header("Content-Type", "text/plain" if format == "txt" else mime_type) req.send_header("Last-Modified", http_date(node.last_modified)) if rev is None: req.send_header("Pragma", "no-cache") req.send_header("Cache-Control", "no-cache") req.send_header("Expires", "Fri, 01 Jan 1999 00:00:00 GMT") if not self.render_unsafe_content: # Force browser to download files instead of rendering # them, since they might contain malicious code enabling # XSS attacks req.send_header("Content-Disposition", "attachment") req.end_headers() # Note: don't pass an iterable instance to RequestDone, instead # call req.write() with each chunk here to avoid SEGVs (#11805) while chunk: req.write(chunk) chunk = content.read(CHUNK_SIZE) raise RequestDone else: # The changeset corresponding to the last change on `node` # is more interesting than the `rev` changeset. changeset = repos.get_changeset(node.created_rev) # add ''Plain Text'' alternate link if needed if not is_binary(chunk) and mime_type != "text/plain": plain_href = req.href.browser(repos.reponame or None, node.path, rev=rev, format="txt") add_link(req, "alternate", plain_href, _("Plain Text"), "text/plain") # add ''Original Format'' alternate link (always) raw_href = req.href.export(rev or repos.youngest_rev, repos.reponame or None, node.path) add_link(req, "alternate", raw_href, _("Original Format"), mime_type) self.log.debug("Rendering preview of node %s@%s with mime-type %s", node.name, rev, mime_type) content = None # the remainder of that content is not needed add_stylesheet(req, "common/css/code.css") annotations = ["lineno"] annotate = req.args.get("annotate") if annotate: annotations.insert(0, annotate) preview_data = mimeview.preview_data( context, node.get_processed_content(), node.get_content_length(), mime_type, node.created_path, raw_href, annotations=annotations, force_source=bool(annotate), ) return {"changeset": changeset, "size": node.content_length, "preview": preview_data, "annotate": annotate}
def filter_stream(self, req, method, template, stream, data): if not (template == 'browser.html' and data.get('dir')): if ((not data.get('dir')) and (data.get('path')) and (data.get('path').endswith('.md'))): # Rendering single markdown file preview stream = stream | Transformer("//head[1]").after( tag.script( Markup( "jQuery(document).ready(function($) {" " $('#preview').each(function() {" " $(this).html(marked( $(this).children('pre').first().text() ));" " });" "});" ), type = "text/javascript" ) ) return stream add_stylesheet(req, 'common/css/code.css') repos = data.get('repos') or self.env.get_repository() rev = req.args.get('rev', None) for entry in data['dir']['entries']: # Rendering all READMEs in a directory preview try: if not entry.isdir and entry.name.lower().startswith('readme'): node = repos.get_node(entry.path, rev) req.perm(data['context'].resource).require('FILE_VIEW') mimeview = Mimeview(self.env) content = node.get_content() mimetype = node.content_type divclass = 'searchable' if entry.name.lower().endswith('.wiki'): mimetype = 'text/x-trac-wiki' divclass = 'searchable wiki' elif entry.name.lower().endswith('.md'): mimetype = 'text/x-markdown' divclass = 'searchable markdown' if not mimetype or mimetype == 'application/octet-stream': mimetype = mimeview.get_mimetype(node.name, content.read(4096)) or mimetype or 'text/plain' del content self.log.debug("ReadmeRenderer: rendering node %s@%s as %s" % (node.name, str(rev), mimetype)) output = mimeview.preview_data( data['context'], node.get_content(), node.get_content_length(), mimetype, node.created_path, '', annotations = [], force_source = False ) if output: if isinstance(output['rendered'], Stream): #content = output['rendered'].select('./pre/node()') #content = output['rendered'].select('./pre') content = output['rendered'].select('.') else: self.log.debug("GOT THERE") content = output['rendered'] insert = tag.div( tag.h1(entry.name, tag.a(Markup(' ¶'), class_ = "anchor", href = '#' + entry.name, title = 'Link to file' ), id_ = entry.name ), tag.div( content, #xml:space = "preserve", class_ = divclass, title = entry.name ), class_ = "readme", style = "padding-top: 1em;" ) stream = stream | Transformer("//div[@id='content']/div[@id='help']").before(insert) except Exception, e: self.log.debug(to_unicode(e))