# # $Id: ZReST.py,v 1.6.12.7 2004/08/04 11:35:30 andreasjung Exp $ # ''' ReStructuredText Product for Zope This Product stores two texts - a "source" text in ReStructureText format, and a HTML "formatted" version of that text. ''' import docutils.core, docutils.io from Globals import InitializeClass, DTMLFile from OFS.SimpleItem import Item from OFS.PropertyManager import PropertyManager from OFS.History import Historical, html_diff from Acquisition import Implicit from Persistence import Persistent from AccessControl import ClassSecurityInfo from AccessControl import ModuleSecurityInfo from DateTime.DateTime import DateTime from App.config import getConfiguration from webdav.Lockable import ResourceLockedError from webdav.WriteLockInterface import WriteLockInterface import sys modulesecurity = ModuleSecurityInfo() modulesecurity.declareProtected('View management screens', 'manage_addZReSTForm') manage_addZReSTForm = DTMLFile('dtml/manage_addZReSTForm', globals()) modulesecurity.declareProtected('Add Z Roundups', 'manage_addZReST') def manage_addZReST(self, id, file='', REQUEST=None): """Add a ZReST product """ # validate the instance_home self._setObject(id, ZReST(id)) self._getOb(id).manage_upload(file) if REQUEST: return self.manage_main(self, REQUEST) class Warnings: def __init__(self): self.messages = [] def write(self, message): self.messages.append(message) class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): '''An instance of this class provides an interface between Zope and ReStructuredText for one text. ''' meta_type = 'ReStructuredText Document' security = ClassSecurityInfo() __implements__ = (WriteLockInterface,) def __init__(self, id,output_encoding=None, input_encoding=None): self.id = id self.title = id self.stylesheet = 'default.css' self.report_level = '2' self.source = self.formatted = '' from reStructuredText import default_output_encoding, \ default_input_encoding self.input_encoding = input_encoding or \ default_input_encoding self.output_encoding = output_encoding or \ default_output_encoding # define the properties that define this object _properties = ( {'id':'stylesheet', 'type': 'string', 'mode': 'w', 'default': 'default.css'}, {'id':'report_level', 'type': 'string', 'mode': 'w', 'default': '2'}, {'id':'input_encoding', 'type': 'string', 'mode': 'w', 'default': 'iso-8859-15'}, {'id':'output_encoding', 'type': 'string', 'mode': 'w', 'default': 'iso-8859-15'}, ) property_extensible_schema__ = 0 # define the tabs for the management interface manage_options= ( {'label': 'Edit', 'action':'manage_main'}, {'label': 'View', 'action':'index_html'}, {'label': 'Source', 'action':'source_txt'}, ) \ + PropertyManager.manage_options \ + Historical.manage_options \ + Item.manage_options # access to the source text and formatted text security.declareProtected('View', 'index_html') def index_html(self, REQUEST=None): ''' Getting the formatted text ''' REQUEST.RESPONSE.setHeader('content-type', 'text/html; charset=%s' % self.output_encoding) return self.formatted security.declareProtected('View', 'source_txt') def source_txt(self, REQUEST=None): ''' Getting the source text ''' REQUEST.RESPONSE.setHeader('content-type', 'text/plain; charset=%s' % self.input_encoding) return self.source # edit form, which is also the primary interface security.declareProtected('Edit ReStructuredText', 'manage_main') manage_main = DTMLFile('dtml/manage_editForm', globals()) # edit action security.declareProtected('Edit ReStructuredText', 'manage_edit') def manage_edit(self, data, SUBMIT='Change',dtpref_cols='50', dtpref_rows='20', REQUEST=None): '''Alias index_html to roundup's index ''' if self._size_changes.has_key(SUBMIT): return self._er(data, SUBMIT, dtpref_cols, dtpref_rows, REQUEST) if self.wl_isLocked(): raise ResourceLockedError, "ReStructuredText Document is locked via WebDAV" if data != self.source: self.source = data self.render() if REQUEST: message="Saved changes." return self.manage_main(self, REQUEST, manage_tabs_message=message) # handle edit window size changes _size_changes = { 'Bigger': (5,5), 'Smaller': (-5,-5), 'Narrower': (0,-5), 'Wider': (0,5), 'Taller': (5,0), 'Shorter': (-5,0), } def _er(self, data, SUBMIT, dtpref_cols, dtpref_rows, REQUEST): dr,dc = self._size_changes[SUBMIT] rows = str(max(1, int(dtpref_rows) + dr)) cols = str(dtpref_cols) if cols.endswith('%'): cols = str(min(100, max(25, int(cols[:-1]) + dc))) + '%' else: cols = str(max(35, int(cols) + dc)) e = (DateTime("GMT") + 365).rfc822() setCookie = REQUEST["RESPONSE"].setCookie setCookie("dtpref_rows", rows, path='/', expires=e) setCookie("dtpref_cols", cols, path='/', expires=e) REQUEST.other.update({"dtpref_cols":cols, "dtpref_rows":rows}) return self.manage_main(self, REQUEST, __str__=self.quotedHTML(data)) security.declarePrivate('quotedHTML') def quotedHTML(self, text=None, character_entities=( (('&'), '&'), (("<"), '<' ), ((">"), '>' ), (('"'), '"'))): #" if text is None: text=self.read_raw() for re,name in character_entities: if text.find(re) >= 0: text=name.join(text.split(re)) return text # handle uploads too security.declareProtected('Edit ReStructuredText', 'manage_upload') def manage_upload(self, file='', REQUEST=None): ''' Replaces the current source with the upload. ''' if self.wl_isLocked(): raise ResourceLockedError, "ReStructuredText Document is locked via WebDAV" if isinstance(file, type('')): self.source = file else: self.source = file.read() self.render() if REQUEST: message="Saved changes." return self.manage_main(self, REQUEST, manage_tabs_message=message) security.declarePrivate('render') def render(self): ''' Render the source to HTML ''' # format with strings pub = docutils.core.Publisher() pub.set_reader('standalone', None, 'restructuredtext') pub.set_writer('html') # go with the defaults pub.get_settings() # this is needed, but doesn't seem to do anything pub.settings._destination = '' # use the stylesheet chosen by the user pub.settings.stylesheet = self.stylesheet # set the reporting level to something sane pub.settings.report_level = int(self.report_level) # don't break if we get errors pub.settings.halt_level = 6 # remember warnings pub.settings.warning_stream = Warnings() pub.source = docutils.io.StringInput( source=self.source, encoding=self.input_encoding) # output - not that it's needed pub.settings.output_encoding = self.output_encoding pub.destination = docutils.io.StringOutput( encoding=self.output_encoding) # parse! document = pub.reader.read(pub.source, pub.parser, pub.settings) # transform pub.apply_transforms(document) self.warnings = ''.join(pub.settings.warning_stream.messages) if document.children: item = document.children[0] if item.tagname == 'title': self.title = str(item.children[0]) # do the format self.formatted = pub.writer.write(document, pub.destination) security.declareProtected('Edit ReStructuredText', 'PUT', 'manage_FTPput') def PUT(self, REQUEST, RESPONSE): ''' Handle HTTP PUT requests ''' self.dav__init(REQUEST, RESPONSE) self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) data = REQUEST.get('BODY', '') if data != self.source: if data.startswith('.. '): data = data.splitlines() new = [] for i in range(len(data)): line = data[i] if not line.startswith('.. '): break if line.startswith('.. stylesheet='): self.stylesheet = line.split('=')[1] elif line.startswith('.. report_level='): self.report_level = line.split('=')[1] else: pass # ignore data = '\n'.join(new) + '\n'.join(data[i:]) self.source = data self.render() RESPONSE.setStatus(204) return RESPONSE manage_FTPput = PUT def manage_FTPget(self): ''' Get source for FTP download ''' self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain') s = [ '.. This is a ReStructuredText Document. Initial comment lines ' '(".. ") will be stripped.', '.. stylesheet='+self.stylesheet, '.. report_level='+self.report_level ] if self.warnings: s.append('.. ') s.append('.. ' + '\n.. '.join(self.warnings.splitlines())) s.append('.. ') return '\n'.join(s) + '\n' + self.source def __str__(self): ''' Stringfy .. return the source ''' return self.quotedHTML(self.source) __call__ = __str__ def PrincipiaSearchSource(self): ''' Support for searching - the document's contents are searched. ''' return self.source def manage_historyCompare(self, rev1, rev2, REQUEST, historyComparisonResults=''): return ZReST.inheritedAttribute('manage_historyCompare')( self, rev1, rev2, REQUEST, historyComparisonResults=html_diff(rev1.source, rev2.source)) def manage_editProperties(self, REQUEST): """ re-render the page after changing the properties (encodings!!!) """ result = PropertyManager.manage_editProperties(self, REQUEST) self.render() return result InitializeClass(ZReST) modulesecurity.apply(globals()) # vim: set filetype=python ts=4 sw=4 et si