@@ -297,4 +297,4 @@
-
\ No newline at end of file
+
diff --git a/addons/account/report/account_balance.rml b/addons/account/report/account_balance.rml
index fac1983ef25..45385827d96 100644
--- a/addons/account/report/account_balance.rml
+++ b/addons/account/report/account_balance.rml
@@ -283,7 +283,7 @@
-
+
Code
Account
@@ -294,7 +294,7 @@
[[ repeatIn(lines(data['form']), 'a') ]][[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['code'] or removeParentNode('tr') ]]
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ '..'*(a['level']-1) ]][[ a['name'] ]]
-
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['debit']) ]][[ a['type']<>'view' and removeParentNode('font') ]] [[formatLang(a['debit']) ]]
+
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['debit']) ]][[ a['type']<>'view' and removeParentNode('font') ]] [[formatLang(a['debit']) ]]
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font')]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['credit']) ]][[ a['type']<>'view' and removeParentNode('font') ]] [[ formatLang(a['credit']) ]]
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['balance']) ]] [[ company.currency_id.symbol ]][[ a['type']<>'view' and removeParentNode('font') ]] [[ formatLang(a['balance']) ]] [[ company.currency_id.symbol ]]
@@ -303,4 +303,4 @@
-
\ No newline at end of file
+
diff --git a/addons/account/report/account_general_journal.rml b/addons/account/report/account_general_journal.rml
index 0fced27408d..84a9ee6a780 100644
--- a/addons/account/report/account_general_journal.rml
+++ b/addons/account/report/account_general_journal.rml
@@ -343,11 +343,11 @@
- [[lines(o.id) and removeParentNode('para') or removeParentNode('section') ]]
+ [[ repeatIn(lines(o.id),'line')]][[ display_currency(data) == False or removeParentNode('blockTable') ]]
- [[reduce(lambda x, line: line.date_planned>x and line.date_planned or x, o.order_line, '')]]
-
-
-
-
-
-
- Reference
-
-
- Designation
-
-
- Package
-
-
- Quantity
-
-
- S.U.
-
-
- Prepared Package
-
-
- Prepared quantity
-
-
- Batch
-
-
-
-
- [[ repeatIn(o.order_line, 'line') ]]
-
-
-
- [[line.product_id.code]]
-
-
- [[line.product_id.name]]
-
-
-
-
-
-
-
-
- [[line.number_packages]]
-
-
- [[line.product_uom_qty]]
-
-
- [[line.product_uom.name]]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [[line.product_id.ean13]]
-
-
- Logistical unit : [[line.product_packaging.qty]]
- [[line.number_packages and 'Packaging : %d package of %d P' % (line.number_packages, line.product_packaging.qty) or '']]
-
-
-
-
-
-
-
- [[line.product_uos_qty]]
-
-
- [[line.product_uos and line.product_uos.name or '']]
-
, tag='pre', attrs=[('class', 'screen')]
+ if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
+ uattrs = []
+ # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
+ for key, value in attrs:
+ if type(value) != type(u''):
+ value = unicode(value, self.encoding)
+ uattrs.append((unicode(key, self.encoding), value))
+ strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs]).encode(self.encoding)
+ if tag in self.elements_no_end_tag:
+ self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
+ else:
+ self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
+
+ def unknown_endtag(self, tag):
+ # called for each end tag, e.g. for
+% endif
+
diff --git a/addons/wiki/web/widgets/wiki.py b/addons/wiki/web/widgets/wiki.py
new file mode 100644
index 00000000000..61a81ca6260
--- /dev/null
+++ b/addons/wiki/web/widgets/wiki.py
@@ -0,0 +1,228 @@
+###############################################################################
+#
+# Copyright (C) 2007-TODAY Tiny ERP Pvt Ltd. All Rights Reserved.
+#
+# $Id$
+#
+# Developed by Tiny (http://openerp.com) and Axelor (http://axelor.com).
+#
+# The OpenERP web client is distributed under the "OpenERP Public License".
+# It's based on Mozilla Public License Version (MPL) 1.1 with following
+# restrictions:
+#
+# - All names, links and logos of Tiny, OpenERP and Axelor must be
+# kept as in original distribution without any changes in all software
+# screens, especially in start-up page and the software header, even if
+# the application source code has been changed or updated or code has been
+# added.
+#
+# - All distributions of the software must keep source code with OEPL.
+#
+# - All integrations to any other software must keep source code with OEPL.
+#
+# If you need commercial licence to remove this kind of restriction please
+# contact us.
+#
+# You can see the MPL licence at: http://www.mozilla.org/MPL/MPL-1.1.html
+#
+###############################################################################
+
+import re
+import random
+import locale
+
+from base64 import b64encode
+from base64 import b64decode
+from StringIO import StringIO
+
+import cherrypy
+import wikimarkup
+
+from openobject.widgets import JSLink, CSSLink
+
+from openerp.utils import rpc
+
+from openerp.widgets import register_widget
+from openerp.widgets.form import Text
+
+
+_image = re.compile(r'img:(.*)\.(.*)', re.UNICODE)
+_rss = re.compile(r'rss:(.*)\.(.*)', re.UNICODE)
+_attach = re.compile(r'attach:(.*)\.(.*)', re.UNICODE)
+_internalLinks = re.compile(r'\[\[.*\]\]', re.UNICODE)
+_edit = re.compile(r'edit:(.*)\|(.*)', re.UNICODE)
+_view = re.compile(r'view:(.*)\|(.*)', re.UNICODE)
+
+class WikiParser(wikimarkup.Parser):
+
+ def parse(self, text, id):
+ text = text.replace(' ', 'n-b-s-p')
+ text = text.replace('&', 'n-a-m-p')
+ text = text.replace('&','&')
+ text = text.replace('n-b-s-p', ' ')
+ text = text.replace('n-a-m-p', '&')
+ text = text.replace('', '
')
+ text = text.replace('
', '')
+
+ text = wikimarkup.to_unicode(text)
+ text = self.strip(text)
+
+ text = super(WikiParser, self).parse(text)
+ text = self.addImage(text, id)
+ text = self.attachDoc(text, id)
+ text = self.recordLink(text)
+ text = self.viewRecordLink(text)
+ text = self.addInternalLinks(text)
+ #TODO : already implemented but we will implement it later after releasing the 5.0
+ #text = self.addRss(text, id)
+ return text
+
+ def viewRecordLink(self, text):
+ def record(path):
+ record = path.group().replace('view:','').split("|")
+ model = record[0]
+ text = record[1].replace('\r','').strip()
+ label = "View Record"
+ if len(record) > 2:
+ label = record[2]
+ proxy = rpc.RPCProxy(model)
+ ids = proxy.name_search(text, [], 'ilike', {})
+ if len(ids):
+ id = ids[0][0]
+ else:
+ try:
+ id = int(text)
+ except:
+ id = 0
+ return "[[/openerp/form/view?model=%s&id=%d | %s]]" % (model, id, label)
+
+ bits = _view.sub(record, text)
+ return bits
+
+ def addRss(self, text, id):
+ def addrss(path):
+ rssurl = path.group().replace('rss:','')
+ import rss.feedparser as feedparser
+ data = feedparser.parse(rssurl)
+ values = "
%s
" % (data.feed.title)
+ values += "%s " % (data.channel.description)
+ for entry in data['entries']:
+ values += "
" % (entry.link, entry.title)
+ values += "%s " % (entry.summary)
+
+ return values
+
+ bits = _rss.sub(addrss, text)
+ return bits
+
+ def attachDoc(self, text, id):
+ def document(path):
+ file = path.group().replace('attach:','')
+ if file.startswith('http') or file.startswith('ftp'):
+ return "Download File" % (file)
+ else:
+ proxy = rpc.RPCProxy('ir.attachment')
+ ids = proxy.search([('datas_fname','=',file.strip()), ('res_model','=','wiki.wiki'), ('res_id','=',id)])
+ if len(ids) > 0:
+ return "%s" % (file, id, file)
+ else:
+ return """Attach : %s """ % (id, file)
+ bits = _attach.sub(document, text)
+ return bits
+
+ def addImage(self, text, id):
+ def image(path):
+ file = path.group().replace('img:','')
+ if file.startswith('http') or file.startswith('ftp'):
+ return "" % (file)
+ else:
+ proxy = rpc.RPCProxy('ir.attachment')
+ ids = proxy.search([('datas_fname','=',file.strip()), ('res_model','=','wiki.wiki'), ('res_id','=',id)])
+ if len(ids) > 0:
+ return "" % (file, id)
+ else:
+ return """Attach : %s """ % (id, file)
+ #"[[/attachment/?model=wiki.wiki&id=%d | Attach:%s]]" % (id, file)
+ bits = _image.sub(image, text)
+ return bits
+
+ def recordLink(self, text):
+ def record(path):
+ record = path.group().replace('edit:','').split("|")
+ model = record[0]
+ text = record[1].replace('\r','').strip()
+ label = "Edit Record"
+ if len(record) > 2:
+ label = record[2]
+ proxy = rpc.RPCProxy(model)
+ ids = proxy.name_search(text, [], '=', {})
+ if len(ids):
+ id = ids[0][0]
+ else:
+ try:
+ id = int(text)
+ except:
+ id = 0
+ return "[[/openerp/form/edit?model=%s&id=%d | %s]]" % (model, id, label)
+
+ bits = _edit.sub(record, text)
+ return bits
+
+ def addInternalLinks(self, text):
+ proxy = rpc.RPCProxy('wiki.wiki')
+
+ def link(path):
+ link = path.group().replace('[','').replace('[','').replace(']','').replace(']','').split("|")
+ name_to_search = link[0].strip()
+ mids = proxy.search([('name','ilike', name_to_search)])
+ link_str = ""
+ if mids:
+ if len(link) == 2:
+ link_str = "%s" % (mids[0], link[1])
+ elif len(link) == 1:
+ link_str = "%s" % (mids[0], link[0])
+ else:
+ if len(link) == 2:
+ link_str = "%s" % (link[0], link[1])
+ elif len(link) == 1:
+ link_str = "%s" % (link[0])
+
+ return link_str
+
+ bits = _internalLinks.sub(link, text)
+ return bits
+
+def wiki2html(text, showToc, id):
+ p = WikiParser(show_toc=showToc)
+ return p.parse(text, id)
+
+class WikiWidget(Text):
+ template = "/wiki/widgets/templates/wiki.mako"
+
+ params = ["data"]
+
+ css = [CSSLink("wiki", "css/wiki.css")]
+
+ data = None
+
+ def set_value(self, value):
+ super(WikiWidget, self).set_value(value)
+
+ if value:
+ toc = True
+ id = False
+ if hasattr(cherrypy.request, 'terp_record'):
+ params = cherrypy.request.terp_params
+ if params._terp_model == 'wiki.wiki':
+ proxy = rpc.RPCProxy('wiki.wiki')
+ toc = proxy.read([params.id], ['toc'])[0]['toc']
+ id = params.id
+
+ text = value+'\n\n'
+ html = wiki2html(text, toc, id)
+
+ self.data = html
+
+register_widget(WikiWidget, ["text_wiki"])
diff --git a/addons/wiki/web/widgets/wikimarkup/__init__.py b/addons/wiki/web/widgets/wikimarkup/__init__.py
new file mode 100644
index 00000000000..91fec4bc4fd
--- /dev/null
+++ b/addons/wiki/web/widgets/wikimarkup/__init__.py
@@ -0,0 +1,2143 @@
+# coding: latin1
+"""
+MediaWiki-style markup
+
+Copyright (C) 2008 David Cramer
+
+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 3 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, see .
+"""
+
+import re, random, locale
+from base64 import b64encode, b64decode
+
+# a few patterns we use later
+
+MW_COLON_STATE_TEXT = 0
+MW_COLON_STATE_TAG = 1
+MW_COLON_STATE_TAGSTART = 2
+MW_COLON_STATE_CLOSETAG = 3
+MW_COLON_STATE_TAGSLASH = 4
+MW_COLON_STATE_COMMENT = 5
+MW_COLON_STATE_COMMENTDASH = 6
+MW_COLON_STATE_COMMENTDASHDASH = 7
+
+_attributePat = re.compile(ur'''(?:^|\s)([A-Za-z0-9]+)(?:\s*=\s*(?:"([^<"]*)"|'([^<']*)'|([a-zA-Z0-9!#$%&()*,\-./:;<>?@[\]^_`{|}~]+)|#([0-9a-fA-F]+)))''', re.UNICODE)
+_space = re.compile(ur'\s+', re.UNICODE)
+_closePrePat = re.compile(u"]*?)(/?>)([^<]*)$', re.UNICODE)
+
+_htmlpairs = ( # Tags that must be closed
+ u'b', u'del', u'i', u'ins', u'u', u'font', u'big', u'small', u'sub', u'sup', u'h1',
+ u'h2', u'h3', u'h4', u'h5', u'h6', u'cite', u'code', u'em', u's',
+ u'strike', u'strong', u'tt', u'var', u'div', u'center',
+ u'blockquote', u'ol', u'ul', u'dl', u'table', u'caption', u'pre',
+ u'ruby', u'rt' , u'rb' , u'rp', u'p', u'span', u'u',
+)
+_htmlsingle = (
+ u'br', u'hr', u'li', u'dt', u'dd', u'img',
+)
+_htmlsingleonly = ( # Elements that cannot have close tags
+ u'br', u'hr', u'img',
+)
+_htmlnest = ( # Tags that can be nested--??
+ u'table', u'tr', u'td', u'th', u'div', u'blockquote', u'ol', u'ul',
+ u'dl', u'font', u'big', u'small', u'sub', u'sup', u'span', u'img',
+)
+_tabletags = ( # Can only appear inside table
+ u'td', u'th', u'tr',
+)
+_htmllist = ( # Tags used by list
+ u'ul', u'ol',
+)
+_listtags = ( # Tags that can appear in a list
+ u'li',
+)
+_htmlsingleallowed = _htmlsingle + _tabletags
+_htmlelements = _htmlsingle + _htmlpairs + _htmlnest
+
+_htmlEntities = {
+ u'Aacute': 193, u'aacute': 225, u'Acirc': 194, u'acirc': 226, u'acute': 180,
+ u'AElig': 198, u'aelig': 230, u'Agrave': 192, u'agrave': 224, u'alefsym': 8501,
+ u'Alpha': 913, u'alpha': 945, u'amp': 38, u'and': 8743, u'ang': 8736, u'Aring': 197,
+ u'aring': 229,
+ u'asymp': 8776,
+ u'Atilde': 195,
+ u'atilde': 227,
+ u'Auml': 196,
+ u'auml': 228,
+ u'bdquo': 8222,
+ u'Beta': 914,
+ u'beta': 946,
+ u'brvbar': 166,
+ u'bull': 8226,
+ u'cap': 8745,
+ u'Ccedil': 199,
+ u'ccedil': 231,
+ u'cedil': 184,
+ u'cent': 162,
+ u'Chi': 935,
+ u'chi': 967,
+ u'circ': 710,
+ u'clubs': 9827,
+ u'cong': 8773,
+ u'copy': 169,
+ u'crarr': 8629,
+ u'cup': 8746,
+ u'curren': 164,
+ u'dagger': 8224,
+ u'Dagger': 8225,
+ u'darr': 8595,
+ u'dArr': 8659,
+ u'deg': 176,
+ u'Delta': 916,
+ u'delta': 948,
+ u'diams': 9830,
+ u'divide': 247,
+ u'Eacute': 201,
+ u'eacute': 233,
+ u'Ecirc': 202,
+ u'ecirc': 234,
+ u'Egrave': 200,
+ u'egrave': 232,
+ u'empty': 8709,
+ u'emsp': 8195,
+ u'ensp': 8194,
+ u'Epsilon': 917,
+ u'epsilon': 949,
+ u'equiv': 8801,
+ u'Eta': 919,
+ u'eta': 951,
+ u'ETH': 208,
+ u'eth': 240,
+ u'Euml': 203,
+ u'euml': 235,
+ u'euro': 8364,
+ u'exist': 8707,
+ u'fnof': 402,
+ u'forall': 8704,
+ u'frac12': 189,
+ u'frac14': 188,
+ u'frac34': 190,
+ u'frasl': 8260,
+ u'Gamma': 915,
+ u'gamma': 947,
+ u'ge': 8805,
+ u'gt': 62,
+ u'harr': 8596,
+ u'hArr': 8660,
+ u'hearts': 9829,
+ u'hellip': 8230,
+ u'Iacute': 205,
+ u'iacute': 237,
+ u'Icirc': 206,
+ u'icirc': 238,
+ u'iexcl': 161,
+ u'Igrave': 204,
+ u'igrave': 236,
+ u'image': 8465,
+ u'infin': 8734,
+ u'int': 8747,
+ u'Iota': 921,
+ u'iota': 953,
+ u'iquest': 191,
+ u'isin': 8712,
+ u'Iuml': 207,
+ u'iuml': 239,
+ u'Kappa': 922,
+ u'kappa': 954,
+ u'Lambda': 923,
+ u'lambda': 955,
+ u'lang': 9001,
+ u'laquo': 171,
+ u'larr': 8592,
+ u'lArr': 8656,
+ u'lceil': 8968,
+ u'ldquo': 8220,
+ u'le': 8804,
+ u'lfloor': 8970,
+ u'lowast': 8727,
+ u'loz': 9674,
+ u'lrm': 8206,
+ u'lsaquo': 8249,
+ u'lsquo': 8216,
+ u'lt': 60,
+ u'macr': 175,
+ u'mdash': 8212,
+ u'micro': 181,
+ u'middot': 183,
+ u'minus': 8722,
+ u'Mu': 924,
+ u'mu': 956,
+ u'nabla': 8711,
+ u'nbsp': 160,
+ u'ndash': 8211,
+ u'ne': 8800,
+ u'ni': 8715,
+ u'not': 172,
+ u'notin': 8713,
+ u'nsub': 8836,
+ u'Ntilde': 209,
+ u'ntilde': 241,
+ u'Nu': 925,
+ u'nu': 957,
+ u'Oacute': 211,
+ u'oacute': 243,
+ u'Ocirc': 212,
+ u'ocirc': 244,
+ u'OElig': 338,
+ u'oelig': 339,
+ u'Ograve': 210,
+ u'ograve': 242,
+ u'oline': 8254,
+ u'Omega': 937,
+ u'omega': 969,
+ u'Omicron': 927,
+ u'omicron': 959,
+ u'oplus': 8853,
+ u'or': 8744,
+ u'ordf': 170,
+ u'ordm': 186,
+ u'Oslash': 216,
+ u'oslash': 248,
+ u'Otilde': 213,
+ u'otilde': 245,
+ u'otimes': 8855,
+ u'Ouml': 214,
+ u'ouml': 246,
+ u'para': 182,
+ u'part': 8706,
+ u'permil': 8240,
+ u'perp': 8869,
+ u'Phi': 934,
+ u'phi': 966,
+ u'Pi': 928,
+ u'pi': 960,
+ u'piv': 982,
+ u'plusmn': 177,
+ u'pound': 163,
+ u'prime': 8242,
+ u'Prime': 8243,
+ u'prod': 8719,
+ u'prop': 8733,
+ u'Psi': 936,
+ u'psi': 968,
+ u'quot': 34,
+ u'radic': 8730,
+ u'rang': 9002,
+ u'raquo': 187,
+ u'rarr': 8594,
+ u'rArr': 8658,
+ u'rceil': 8969,
+ u'rdquo': 8221,
+ u'real': 8476,
+ u'reg': 174,
+ u'rfloor': 8971,
+ u'Rho': 929,
+ u'rho': 961,
+ u'rlm': 8207,
+ u'rsaquo': 8250,
+ u'rsquo': 8217,
+ u'sbquo': 8218,
+ u'Scaron': 352,
+ u'scaron': 353,
+ u'sdot': 8901,
+ u'sect': 167,
+ u'shy': 173,
+ u'Sigma': 931,
+ u'sigma': 963,
+ u'sigmaf': 962,
+ u'sim': 8764,
+ u'spades': 9824,
+ u'sub': 8834,
+ u'sube': 8838,
+ u'sum': 8721,
+ u'sup': 8835,
+ u'sup1': 185,
+ u'sup2': 178,
+ u'sup3': 179,
+ u'supe': 8839,
+ u'szlig': 223,
+ u'Tau': 932,
+ u'tau': 964,
+ u'there4': 8756,
+ u'Theta': 920,
+ u'theta': 952,
+ u'thetasym': 977,
+ u'thinsp': 8201,
+ u'THORN': 222,
+ u'thorn': 254,
+ u'tilde': 732,
+ u'times': 215,
+ u'trade': 8482,
+ u'Uacute': 218,
+ u'uacute': 250,
+ u'uarr': 8593,
+ u'uArr': 8657,
+ u'Ucirc': 219,
+ u'ucirc': 251,
+ u'Ugrave': 217,
+ u'ugrave': 249,
+ u'uml': 168,
+ u'upsih': 978,
+ u'Upsilon': 933,
+ u'upsilon': 965,
+ u'Uuml': 220,
+ u'uuml': 252,
+ u'weierp': 8472,
+ u'Xi': 926,
+ u'xi': 958,
+ u'Yacute': 221,
+ u'yacute': 253,
+ u'yen': 165,
+ u'Yuml': 376,
+ u'yuml': 255,
+ u'Zeta': 918,
+ u'zeta': 950,
+ u'zwj': 8205,
+ u'zwnj': 8204
+}
+
+_charRefsPat = re.compile(ur'''(&([A-Za-z0-9]+);|([0-9]+);|[xX]([0-9A-Za-z]+);|(&))''', re.UNICODE)
+_cssCommentPat = re.compile(ur'''\*.*?\*''', re.UNICODE)
+_toUTFPat = re.compile(ur'''\\([0-9A-Fa-f]{1,6})[\s]?''', re.UNICODE)
+_hackPat = re.compile(ur'''(expression|tps*://|url\s*\().*''', re.UNICODE | re.IGNORECASE)
+_hrPat = re.compile(u'''^-----*''', re.UNICODE | re.MULTILINE)
+_h1Pat = re.compile(u'^=(.+)=\s*$', re.UNICODE | re.MULTILINE)
+_h2Pat = re.compile(u'^==(.+)==\s*$', re.UNICODE | re.MULTILINE)
+_h3Pat = re.compile(u'^===(.+)===\s*$', re.UNICODE | re.MULTILINE)
+_h4Pat = re.compile(u'^====(.+)====\s*$', re.UNICODE | re.MULTILINE)
+_h5Pat = re.compile(u'^=====(.+)=====\s*$', re.UNICODE | re.MULTILINE)
+_h6Pat = re.compile(u'^======(.+)======\s*$', re.UNICODE | re.MULTILINE)
+_quotePat = re.compile(u"""(''+)""", re.UNICODE)
+_removePat = re.compile(ur'\b(' + ur'|'.join((u"a", u"an", u"as", u"at", u"before", u"but", u"by", u"for", u"from",
+ u"is", u"in", u"into", u"like", u"of", u"off", u"on", u"onto", u"per",
+ u"since", u"than", u"the", u"this", u"that", u"to", u"up", u"via",
+ u"with")) + ur')\b', re.UNICODE | re.IGNORECASE)
+_nonWordSpaceDashPat = re.compile(ur'[^\w\s\-\./]', re.UNICODE)
+_multiSpacePat = re.compile(ur'[\s\-_\./]+', re.UNICODE)
+_spacePat = re.compile(ur' ', re.UNICODE)
+_linkPat = re.compile(ur'^(?:([A-Za-z0-9]+):)?([^\|]+)(?:\|([^\n]+?))?\]\](.*)$', re.UNICODE | re.DOTALL)
+_bracketedLinkPat = re.compile(ur'(?:\[((?:mailto:|irc://|https?://|ftp://|/)[^<>\]\[' + u"\x00-\x20\x7f" + ur']*)\s*(.*?)\])', re.UNICODE)
+_protocolPat = re.compile(ur'(\b(?:mailto:|irc://|https?://|ftp://))', re.UNICODE)
+_specialUrlPat = re.compile(ur'^([^<>\]\[' + u"\x00-\x20\x7f" + ur']+)(.*)$', re.UNICODE)
+_protocolsPat = re.compile(ur'^(mailto:|irc://|https?://|ftp://)$', re.UNICODE)
+_controlCharsPat = re.compile(ur'[\]\[<>"' + u"\\x00-\\x20\\x7F" + ur']]', re.UNICODE)
+_hostnamePat = re.compile(ur'^([^:]+:)(//[^/]+)?(.*)$', re.UNICODE)
+_stripPat = re.compile(u'\\s|\u00ad|\u1806|\u200b|\u2060|\ufeff|\u03f4|\u034f|\u180b|\u180c|\u180d|\u200c|\u200d|[\ufe00-\ufe0f]', re.UNICODE)
+_zomgPat = re.compile(ur'^(:*)\{\|(.*)$', re.UNICODE)
+_headerPat = re.compile(ur"<[Hh]([1-6])(.*?)>(.*?)[Hh][1-6] *>", re.UNICODE)
+_templateSectionPat = re.compile(ur"", re.UNICODE)
+_tagPat = re.compile(ur"<.*?>", re.UNICODE)
+_startRegexHash = {}
+_endRegexHash = {}
+_endCommentPat = re.compile(ur'(-->)', re.UNICODE)
+_extractTagsAndParams_n = 1
+_guillemetLeftPat = re.compile(ur'(.) (\?|:|;|!|\302\273)', re.UNICODE)
+_guillemetRightPat = re.compile(ur'(\302\253) ', re.UNICODE)
+
+def setupAttributeWhitelist():
+ common = ( u'id', u'class', u'lang', u'dir', u'title', u'style' )
+ block = common + (u'align',)
+ tablealign = ( u'align', u'char', u'charoff', u'valign' )
+ tablecell = ( u'abbr',
+ u'axis',
+ u'headers',
+ u'scope',
+ u'rowspan',
+ u'colspan',
+ u'nowrap', # deprecated
+ u'width', # deprecated
+ u'height', # deprecated
+ u'bgcolor' # deprecated
+ )
+ return {
+ u'div': block,
+ u'center': common, # deprecated
+ u'span': block, # ??
+ u'h1': block,
+ u'h2': block,
+ u'h3': block,
+ u'h4': block,
+ u'h5': block,
+ u'h6': block,
+ u'em': common,
+ u'strong': common,
+ u'cite': common,
+ u'code': common,
+ u'var': common,
+ u'img': common + (u'src', u'alt', u'width', u'height',),
+ u'blockquote': common + (u'cite',),
+ u'sub': common,
+ u'sup': common,
+ u'p': block,
+ u'br': (u'id', u'class', u'title', u'style', u'clear',),
+ u'pre': common + (u'width',),
+ u'ins': common + (u'cite', u'datetime'),
+ u'del': common + (u'cite', u'datetime'),
+ u'ul': common + (u'type',),
+ u'ol': common + (u'type', u'start'),
+ u'li': common + (u'type', u'value'),
+ u'dl': common,
+ u'dd': common,
+ u'dt': common,
+ u'table': common + ( u'summary', u'width', u'border', u'frame',
+ u'rules', u'cellspacing', u'cellpadding',
+ u'align', u'bgcolor',
+ ),
+ u'caption': common + (u'align',),
+ u'thead': common + tablealign,
+ u'tfoot': common + tablealign,
+ u'tbody': common + tablealign,
+ u'colgroup': common + ( u'span', u'width' ) + tablealign,
+ u'col': common + ( u'span', u'width' ) + tablealign,
+ u'tr': common + ( u'bgcolor', ) + tablealign,
+ u'td': common + tablecell + tablealign,
+ u'th': common + tablecell + tablealign,
+ u'tt': common,
+ u'b': common,
+ u'i': common,
+ u'big': common,
+ u'small': common,
+ u'strike': common,
+ u's': common,
+ u'u': common,
+ u'font': common + ( u'size', u'color', u'face' ),
+ u'hr': common + ( u'noshade', u'size', u'width' ),
+ u'ruby': common,
+ u'rb': common,
+ u'rt': common, #array_merge( $common, array( 'rbspan' ) ),
+ u'rp': common,
+ }
+_whitelist = setupAttributeWhitelist()
+_page_cache = {}
+env = {}
+
+def registerTagHook(tag, function):
+ mTagHooks[tag] = function
+
+class BaseParser(object):
+ def __init__(self):
+ self.uniq_prefix = u"\x07UNIQ" + unicode(random.randint(1, 1000000000))
+ self.strip_state = {}
+ self.arg_stack = []
+ self.env = env
+ self.keep_env = (env != {})
+
+ def __del__(self):
+ if not self.keep_env:
+ global env
+ env = {}
+
+ ''' Used to store objects in the environment
+ used to prevent recursive imports '''
+ def store_object(self, namespace, key, value=True):
+ # Store the item to not reprocess it
+ if namespace not in self.env:
+ self.env[namespace] = {}
+ self.env[namespace][key] = value
+
+ def has_object(self, namespace, key):
+ if namespace not in self.env:
+ self.env[namespace] = {}
+ if hasattr(self, 'count'):
+ data = self.env[namespace]
+ test = key in data
+ ls
+ self.count = True
+ return key in self.env[namespace]
+
+ def retrieve_object(self, namespace, key, default=None):
+ if not self.env.get(namespace):
+ self.env[namespace] = {}
+ return self.env[namespace].get(key, default)
+
+ def parse(self, text):
+ utf8 = isinstance(text, str)
+ text = to_unicode(text)
+ if text[-1:] != u'\n':
+ text = text + u'\n'
+ taggedNewline = True
+ else:
+ taggedNewline = False
+
+ text = self.strip(text)
+ text = self.removeHtmlTags(text)
+ text = self.parseHorizontalRule(text)
+ text = self.parseAllQuotes(text)
+ text = self.replaceExternalLinks(text)
+ text = self.unstrip(text)
+ text = self.fixtags(text)
+ text = self.doBlockLevels(text, True)
+ text = self.unstripNoWiki(text)
+ text = text.split(u'\n')
+ text = u'\n'.join(text)
+ if taggedNewline and text[-1:] == u'\n':
+ text = text[:-1]
+ if utf8:
+ return text.encode("utf-8")
+ return text
+
+ def strip(self, text, stripcomments=False, dontstrip=[]):
+ render = True
+
+ commentState = {}
+
+ elements = ['nowiki',] + mTagHooks.keys()
+ if True: #wgRawHtml
+ elements.append('html')
+
+ # Removing $dontstrip tags from $elements list (currently only 'gallery', fixing bug 2700)
+ for k in dontstrip:
+ if k in elements:
+ del elements[k]
+
+ matches = {}
+ text = self.extractTagsAndParams(elements, text, matches)
+
+ for marker in matches:
+ element, content, params, tag = matches[marker]
+ if render:
+ tagName = element.lower()
+ if tagName == u'!--':
+ # comment
+ output = tag
+ if tag[-3:] != u'-->':
+ output += "-->"
+ elif tagName == u'html':
+ output = content
+ elif tagName == u'nowiki':
+ output = content.replace(u'&', u'&').replace(u'<', u'<').replace(u'>', u'>')
+ else:
+ if tagName in mTagHooks:
+ output = mTagHooks[tagName](self, content, params)
+ else:
+ output = content.replace(u'&', u'&').replace(u'<', u'<').replace(u'>', u'>')
+ else:
+ # Just stripping tags; keep the source
+ output = tag
+
+ # Unstrip the output, because unstrip() is no longer recursive so
+ # it won't do it itself
+ output = self.unstrip(output)
+
+ if not stripcomments and element == u'!--':
+ commentState[marker] = output
+ elif element == u'html' or element == u'nowiki':
+ if 'nowiki' not in self.strip_state:
+ self.strip_state['nowiki'] = {}
+ self.strip_state['nowiki'][marker] = output
+ else:
+ if 'general' not in self.strip_state:
+ self.strip_state['general'] = {}
+ self.strip_state['general'][marker] = output
+
+ # Unstrip comments unless explicitly told otherwise.
+ # (The comments are always stripped prior to this point, so as to
+ # not invoke any extension tags / parser hooks contained within
+ # a comment.)
+ if not stripcomments:
+ # Put them all back and forget them
+ for k in commentState:
+ v = commentState[k]
+ text = text.replace(k, v)
+
+ return text
+
+ def removeHtmlTags(self, text):
+ """convert bad tags into HTML identities"""
+ sb = []
+ text = self.removeHtmlComments(text)
+ bits = text.split(u'<')
+ sb.append(bits.pop(0))
+ tagstack = []
+ tablestack = tagstack
+ for x in bits:
+ m = _tagPattern.match(x)
+ if not m:
+ continue
+ slash, t, params, brace, rest = m.groups()
+ t = t.lower()
+ badtag = False
+ if t in _htmlelements:
+ # Check our stack
+ if slash:
+ # Closing a tag...
+ if t in _htmlsingleonly or len(tagstack) == 0:
+ badtag = True
+ else:
+ ot = tagstack.pop()
+ if ot != t:
+ if ot in _htmlsingleallowed:
+ # Pop all elements with an optional close tag
+ # and see if we find a match below them
+ optstack = []
+ optstack.append(ot)
+ while True:
+ if len(tagstack) == 0:
+ break
+ ot = tagstack.pop()
+ if ot == t or ot not in _htmlsingleallowed:
+ break
+ optstack.append(ot)
+ if t != ot:
+ # No match. Push the optinal elements back again
+ badtag = True
+ tagstack += reversed(optstack)
+ else:
+ tagstack.append(ot)
+ #
can be nested in
or , skip those cases:
+ if ot not in _htmllist and t in _listtags:
+ badtag = True
+ elif t == u'table':
+ if len(tablestack) == 0:
+ bagtag = True
+ else:
+ tagstack = tablestack.pop()
+ newparams = u''
+ else:
+ # Keep track for later
+ if t in _tabletags and u'table' not in tagstack:
+ badtag = True
+ elif t in tagstack and t not in _htmlnest:
+ badtag = True
+ # Is it a self-closed htmlpair? (bug 5487)
+ elif brace == u'/>' and t in _htmlpairs:
+ badTag = True
+ elif t in _htmlsingleonly:
+ # Hack to force empty tag for uncloseable elements
+ brace = u'/>'
+ elif t in _htmlsingle:
+ # Hack to not close $htmlsingle tags
+ brace = None
+ else:
+ if t == u'table':
+ tablestack.append(tagstack)
+ tagstack = []
+ tagstack.append(t)
+ newparams = self.fixTagAttributes(params, t)
+ if not badtag:
+ rest = rest.replace(u'>', u'>')
+ if brace == u'/>':
+ close = u' /'
+ else:
+ close = u''
+ sb.append(u'<')
+ sb.append(slash)
+ sb.append(t)
+ sb.append(newparams)
+ sb.append(close)
+ sb.append(u'>')
+ sb.append(rest)
+ continue
+ sb.append(u'<')
+ sb.append(x.replace(u'>', u'>'))
+
+ # Close off any remaining tags
+ while tagstack:
+ t = tagstack.pop()
+ sb.append(u'')
+ sb.append(t)
+ sb.append(u'>\n')
+ if t == u'table':
+ if not tablestack:
+ break
+ tagstack = tablestack.pop()
+
+ return u''.join(sb)
+
+ def removeHtmlComments(self, text):
+ """remove comments from given text"""
+ sb = []
+ start = text.find(u'', start)
+ if end == -1:
+ break
+ end += 3
+
+ spaceStart = max(0, start-1)
+ spaceEnd = end
+ while text[spaceStart] == u' ' and spaceStart > 0:
+ spaceStart -= 1
+ while text[spaceEnd] == u' ':
+ spaceEnd += 1
+
+ if text[spaceStart] == u'\n' and text[spaceEnd] == u'\n':
+ sb.append(text[last:spaceStart])
+ sb.append(u'\n')
+ last = spaceEnd+1
+ else:
+ sb.append(text[last:spaceStart+1])
+ last = spaceEnd
+
+ start = text.find(u''
+
+ return result, mDTopen
+
+ def nextItem(self, char, mDTopen):
+ if char == u'*' or char == '#':
+ return u'
', None
+ elif char == u':' or char == u';':
+ close = u''
+ if mDTopen:
+ close = ''
+ if char == u';':
+ return close + u'
" + x[2:]
+ l = ltd.pop()
+ if not has_opened_tr.pop():
+ z = u"
" + z
+ if tr.pop():
+ z = u"
" + z
+ if td.pop():
+ z = u'' + l + u'>' + z
+ ltr.pop()
+ t[k] = z + u'
'*indent_level
+ elif u'|-' == x[0:2]: # Allows for |-------------
+ x = x[1:]
+ while x != u'' and x[0:1] == '-':
+ x = x[1:]
+ z = ''
+ l = ltd.pop()
+ has_opened_tr.pop()
+ has_opened_tr.append(True)
+ if tr.pop():
+ z = u'
' + z
+ if td.pop():
+ z = u'' + l + u'>' + z
+ ltr.pop()
+ t[k] = z
+ tr.append(False)
+ td.append(False)
+ ltd.append(u'')
+ attributes = self.unstripForHTML(x)
+ ltr.append(self.fixTagAttributes(attributes, u'tr'))
+ elif u'|' == fc or u'!' == fc or u'|+' == x[0:2]: # Caption
+ # x is a table row
+ if u'|+' == x[0:2]:
+ fc = u'+'
+ x = x[1:]
+ x = x[1:]
+ if fc == u'!':
+ x = x.replace(u'!!', u'||')
+ # Split up multiple cells on the same line.
+ # FIXME: This can result in improper nesting of tags processed
+ # by earlier parser steps, but should avoid splitting up eg
+ # attribute values containing literal "||".
+ x = x.split(u'||')
+
+ t[k] = u''
+
+ # Loop through each table cell
+ for theline in x:
+ z = ''
+ if fc != u'+':
+ tra = ltr.pop()
+ if not tr.pop():
+ z = u'
\n'
+ tr.append(True)
+ ltr.append(u'')
+ has_opened_tr.pop()
+ has_opened_tr.append(True)
+ l = ltd.pop()
+ if td.pop():
+ z = u'' + l + u'>' + z
+ if fc == u'|':
+ l = u'td'
+ elif fc == u'!':
+ l = u'th'
+ elif fc == u'+':
+ l = u'caption'
+ else:
+ l = u''
+ ltd.append(l)
+
+ #Cell parameters
+ y = theline.split(u'|', 1)
+ # Note that a '|' inside an invalid link should not
+ # be mistaken as delimiting cell parameters
+ if y[0].find(u'[[') != -1:
+ y = [theline]
+
+ if len(y) == 1:
+ y = z + u"<" + l + u">" + y[0]
+ else:
+ attributes = self.unstripForHTML(y[0])
+ y = z + u"<" + l + self.fixTagAttributes(attributes, l) + u">" + y[1]
+
+ t[k] += y
+ td.append(True)
+
+ while len(td) > 0:
+ l = ltd.pop()
+ if td.pop():
+ t.append(u'')
+ if tr.pop():
+ t.append(u'
')
+ if not has_opened_tr.pop():
+ t.append(u'
')
+ t.append(u'')
+
+ text = u'\n'.join(t)
+ # special case: don't return empty table
+ if text == u"
\n
\n
":
+ text = u''
+
+ return text
+
+ def formatHeadings(self, text, isMain):
+ """
+ This function accomplishes several tasks:
+ 1) Auto-number headings if that option is enabled
+ 2) Add an [edit] link to sections for logged in users who have enabled the option
+ 3) Add a Table of contents on the top for users who have enabled the option
+ 4) Auto-anchor headings
+
+ It loops through all headlines, collects the necessary data, then splits up the
+ string and re-inserts the newly formatted headlines.
+ """
+ doNumberHeadings = False
+ showEditLink = True # Can User Edit
+
+ if text.find(u"__NOEDITSECTION__") != -1:
+ showEditLink = False
+ text = text.replace(u"__NOEDITSECTION__", u"")
+
+ # Get all headlines for numbering them and adding funky stuff like [edit]
+ # links - this is for later, but we need the number of headlines right now
+ matches = _headerPat.findall(text)
+ numMatches = len(matches)
+
+ # if there are fewer than 4 headlines in the article, do not show TOC
+ # unless it's been explicitly enabled.
+ enoughToc = self.show_toc and (numMatches >= 4 or text.find(u"") != -1)
+
+ # Allow user to stipulate that a page should have a "new section"
+ # link added via __NEWSECTIONLINK__
+ showNewSection = False
+ if text.find(u"__NEWSECTIONLINK__") != -1:
+ showNewSection = True
+ text = text.replace(u"__NEWSECTIONLINK__", u"")
+ # if the string __FORCETOC__ (not case-sensitive) occurs in the HTML,
+ # override above conditions and always show TOC above first header
+ if text.find(u"__FORCETOC__") != -1:
+ self.show_toc = True
+ enoughToc = True
+ text = text.replace(u"__FORCETOC__", u"")
+ # Never ever show TOC if no headers
+ if numMatches < 1:
+ enoughToc = False
+
+ # headline counter
+ headlineCount = 0
+ sectionCount = 0 # headlineCount excluding template sections
+
+ # Ugh .. the TOC should have neat indentation levels which can be
+ # passed to the skin functions. These are determined here
+ toc = []
+ head = {}
+ sublevelCount = {}
+ levelCount = {}
+ toclevel = 0
+ level = 0
+ prevlevel = 0
+ toclevel = 0
+ prevtoclevel = 0
+ refers = {}
+ refcount = {}
+ wgMaxTocLevel = 5
+
+ for match in matches:
+ headline = match[2]
+ istemplate = False
+ templatetitle = u''
+ templatesection = 0
+ numbering = []
+
+ m = _templateSectionPat.search(headline)
+ if m:
+ istemplate = True
+ templatetitle = b64decode(m[0])
+ templatesection = 1 + int(b64decode(m[1]))
+ headline = _templateSectionPat.sub(u'', headline)
+
+ if toclevel:
+ prevlevel = level
+ prevtoclevel = toclevel
+
+ level = matches[headlineCount][0]
+
+ if doNumberHeadings or enoughToc:
+ if level > prevlevel:
+ toclevel += 1
+ sublevelCount[toclevel] = 0
+ if toclevel < wgMaxTocLevel:
+ toc.append(u'\n
')
+ elif level < prevlevel and toclevel > 1:
+ # Decrease TOC level, find level to jump to
+
+ if toclevel == 2 and level < levelCount[1]:
+ toclevel = 1
+ else:
+ for i in range(toclevel, 0, -1):
+ if levelCount[i] == level:
+ # Found last matching level
+ toclevel = i
+ break
+ elif levelCount[i] < level:
+ toclevel = i + 1
+ break
+ if toclevel < wgMaxTocLevel:
+ toc.append(u"\n")
+ toc.append(u"
\n\n" * max(prevtoclevel - toclevel, 0))
+ else:
+ if toclevel < wgMaxTocLevel:
+ toc.append(u"\n")
+
+ levelCount[toclevel] = level
+
+ # count number of headlines for each level
+ sublevelCount[toclevel] += 1
+ for i in range(1, toclevel+1):
+ if sublevelCount[i]:
+ numbering.append(to_unicode(sublevelCount[i]))
+
+ # The canonized header is a version of the header text safe to use for links
+ # Avoid insertion of weird stuff like