Commit 5e5c1195 authored by Yoshinori Okuji's avatar Yoshinori Okuji

This is a preliminary wiki implementation for ERP5.

This is far from perfect at the moment, but I commit
this file not to lose the code.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3980 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent a4eb15af
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Yoshinori Okuji <yo@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Document.Document import Document
from zLOG import LOG
import cgi
import re
class Wiki( Document ):
"""
Bug means a bug report, a feature request or an issue.
"""
# Default Properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.Document
)
# CMF Type Definition
meta_type='ERP5 Wiki'
portal_type='Wiki'
add_permission = Permissions.AddPortalContent
isPortalContent = 1
isRADContent = 1
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.View)
# Compile regular expressions at load time.
table_expr = re.compile(r"^\|\|(.+)\|\|$")
heading_expr = re.compile(r"^(={1,5}) (.+) \1$")
horizontal_line_expr = re.compile(r"^-{4,}$")
enumerated_list_expr = re.compile(r"^(\s+)[0-9]+\.(.*)")
item_list_expr = re.compile(r"^(\s+)\*(.*)")
preformatted_text_expr = re.compile(r"^{{{$")
empty_line_expr = re.compile(r"^\s*$")
line_expr_map = {
'table' : table_expr,
'heading' : heading_expr,
'horizontal_line' : horizontal_line_expr,
'enumerated_list' : enumerated_list_expr,
'item_list' : item_list_expr,
'preformatted_text' : preformatted_text_expr,
'empty_line' : empty_line_expr,
}
wiki_name_expr = re.compile(r"!?([A-Z][a-z]+){2,}(/([A-Z][a-z]+){2,})*")
underscore_expr = re.compile(r"__([^_]+)__")
bold_expr = re.compile(r"'''([^']+)'''")
italic_expr = re.compile(r"(?<!')''([^']+)''(?!')")
strike_expr = re.compile(r"~~([^~]+)~~")
url_expr = re.compile(r"(!?[a-z]+://[^ ]+)")
link_expr = re.compile(r"(?<!\[)\[([^\] ]+) ([^\]]+)\](?!\])")
tales_expr = re.compile(r"\[\[([^\]]+)\]\]")
text_expr_map = {
'wiki_name' : wiki_name_expr,
'underscore': underscore_expr,
'bold' : bold_expr,
'italic' : italic_expr,
'strike' : strike_expr,
'url' : url_expr,
'link' : link_expr,
'tales' : tales_expr,
}
def _getTopLevelUrl(self):
o = self.aq_parent
while o.meta_type == 'ERP5 Wiki':
o = o.aq_parent
return o.absolute_url()
def _render_text(self, text):
"""
Render a piece of text.
"""
text_list = []
append = text_list.append
while text:
min_start = len(text)
match = None
min_key = None
for key, expr in self.text_expr_map.items():
m = expr.search(text)
if m is not None:
start = m.start(0)
if min_start > start:
min_start = start
min_key = key
match = m
if match is None:
# Nothing special.
append(cgi.escape(text))
break
else:
if min_start > 0:
append(text[:min_start])
if min_key == 'wiki_name':
name = match.string[match.start(0):match.end(0)]
if name.startswith('!'):
append(name[1:])
else:
# FIMXE: Create a new wiki instance if not present.
append('<a href="%s/%s/view">%s</a>' % (self._getTopLevelUrl(), name, name))
elif min_key == 'url':
url = match.string[match.start(1):match.end(1)]
if url.startswith('!'):
append(url[1:])
else:
# XXX Language-specific evil hack.
if url[-1] in r",.')]}":
extra = url[-1]
url = url[:-1]
else:
extra = None
append('<a href="%s">%s</a>' % (url, cgi.escape(url)))
if extra is not None:
append(extra)
elif min_key == 'tales':
append(self._render_tales(match.string[match.start(1):match.end(1)]))
elif min_key == 'link':
link, label = match.groups()
label = self._render_text(label)
if self.wiki_name_expr.search(link):
# FIXME: Create a new wiki instance if not present
append('<a href="%s/%s/view">%s</a>' % (self._getTopLevelUrl(), link, label))
else:
append('<a href="%s">%s</a>' % (link, label))
else:
word = self._render_text(match.string[match.start(1):match.end(1)])
if min_key == 'underscore':
append('<u>%s</u>' % word)
elif min_key == 'italic':
append('<i>%s</i>' % word)
elif min_key == 'bold':
append('<b>%s</b>' % word)
elif min_key == 'strike':
append('<del>%s</del>' % word)
text = text[match.end(0):]
return ''.join(text_list)
def _render_tales(self, expr):
"""
Evaluate the expression.
FIXME
"""
return ''
def _render_table_row(self, text):
"""
Render a row of a table.
"""
text_list = ['<tr>']
append = text_list.append
for cell in text.split('||'):
append('<td>')
append(self._render_text(cell))
append('</td>')
append('</tr>')
return ''.join(text_list)
security.declareProtected(Permissions.View, 'render')
def render(self, content=None, format=None):
"""
Render the contents and generate HTML.
FIXME: Very dirty. Better to rewrite this with a state machine.
"""
if content is None:
content = self.getTextContent()
if content is None:
content = ''
preformatted = False
in_table = False
in_paragraph = False
list_stack = []
line_list = []
append = line_list.append
for line in content.split('\r\n'):
if preformatted:
if line == '}}}':
preformatted = False
append('</pre>')
else:
append(cgi.escape(line))
continue
if in_table:
m = self.table_expr.search(line)
if m is not None:
append(self._render_table_row(m.string[m.start(1):m.end(1)]))
continue
else:
append('</table>')
if list_stack:
for key, expr in (('enumerated_list', self.enumerated_list_expr), ('item_list', self.item_list_expr)):
m = expr.search(line)
if m is not None:
break
else:
key = None
if key is None:
level = 0
else:
level = m.end(1) - m.start(1)
while list_stack and list_stack[-1][1] > level:
prev_key, prev_level = list_stack.pop()
if prev_key == 'enumerated_list':
append('</ol>')
else:
append('</ul>')
if list_stack and list_stack[-1][1] == level:
if key == list_stack[-1][0]:
append('<li>')
append(self._render_text(m.string[m.start(2):m.end(2)]))
continue
else:
prev_key, prev_level = list_stack.pop()
if prev_key == 'enumerated_list':
append('</ol>')
else:
append('</ul>')
if key is not None:
list_stack.append((key, level))
if key == 'enumerated_list':
append('<ol><li>')
append(self._render_text(m.string[m.start(2):m.end(2)]))
else:
append('<ul><li>')
append(self._render_text(m.string[m.start(2):m.end(2)]))
continue
for key, expr in self.line_expr_map.items():
m = expr.search(line)
if m is not None:
break
else:
key = None
if in_paragraph and key is not None:
in_paragraph = False
append('</p>')
if key == 'preformatted_text':
preformatted = True
append('<pre>')
elif key == 'horizontal_line':
append('<hr>')
elif key == 'table':
in_table = True
append('<table>')
append(self._render_table_row(m.string[m.start(1):m.end(1)]))
elif key == 'heading':
level = m.end(1) - m.start(1)
append('<h%d>' % level)
append(self._render_text(m.string[m.start(2):m.end(2)]))
append('</h%d>' % level)
elif key == 'enumerated_list':
list_stack.append((key, m.end(1) - m.start(1)))
append('<ol><li>')
append(self._render_text(m.string[m.start(2):m.end(2)]))
elif key == 'item_list':
list_stack.append((key, m.end(1) - m.start(1)))
append('<ul><li>')
append(self._render_text(m.string[m.start(2):m.end(2)]))
elif key == 'empty_line':
pass
else:
if not in_paragraph:
in_paragraph = True
append('<p>')
append(self._render_text(line))
while list_stack:
key, level = list_stack.pop()
if key == 'enumerated_list':
append('</ol>')
else:
append('</ul>')
if in_paragraph:
append('</p>')
if in_table:
append('</table>')
if preformatted:
append('</pre>')
return '\n'.join(line_list)
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment