def visit_moinpage_h(self, element): """ There is not really heading in DocBook, but rather section with title. The section is a root tag for all the elements which in the dom tree will be between two heading tags. So we need to process child manually to determine correctly the children of each section. A section is closed when we have a new heading with an equal or higher level. """ depth = element.get(moin_page('outline-level')) # We will have a new section # under another section if depth > self.current_section: self.parent_section = self.current_section self.current_section = int(depth) self.section_children[self.current_section] = [] # NB : Error with docbook.title title = ET.Element(docbook('title'), attrib={}, children=element[0]) self.section_children[self.current_section].append(title) # We will close a section before starting a new one # Need more test elif depth < self.current_section: if self.parent_section != 0: section_tag = 'sect{0}'.format(self.parent_section) section = ET.Element(docbook(section_tag), attrib={}, children=self.section_children[self.current_section]) self.section_children[self.parent_section].append(section) self.current_section = int(depth)
def visit_qandaentry_number(self, element, depth): """ Convert:: <question>Q</question><answer>A</answer> to:: <list-item> <list-item-body><p>Q</p><p>A</p></list-item-body> </list-item> """ items = [] for child in element: if isinstance(child, ET.Element): if child.tag.name == 'question' or child.tag.name == 'answer': r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) items.extend(r) else: items.append(child) item_body = ET.Element(moin_page('list-item-body'), attrib={}, children=items) return ET.Element(moin_page('list-item'), attrib={}, children=[item_body])
def visit_simple_list(self, moin_page_tag, attrib, element, depth): """ There is different list element in DocBook with different semantic meaning, but with an unique result in the DOM Tree. Here we handle the conversion of such of list. """ list_item_tags = set(['listitem', 'step', 'stepalternatives', 'member']) items = [] for child in element: if isinstance(child, ET.Element): if child.tag.name in list_item_tags: children = self.visit(child, depth) list_item_body = ET.Element(moin_page('list-item-body'), attrib={}, children=children) tag = ET.Element(moin_page('list-item'), attrib={}, children=[list_item_body]) tag = (tag, ) items.extend(tag) else: r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) items.extend(r) else: items.append(child) return ET.Element(moin_page.list, attrib=attrib, children=items)
def visit_docbook_seglistitem(self, element, labels, depth): """ A seglistitem is a list-item for a segmented list. It is quite special because it act list definition with label, but the labels are predetermined in the labels list. So we generate label/body couple according to the content in labels """ new = [] counter = 0 for child in element: if isinstance(child, ET.Element): if child.tag.name == 'seg': label_tag = ET.Element(moin_page('list-item-label'), attrib={}, children=labels[counter % len(labels)]) body_tag = ET.Element(moin_page('list-item-body'), attrib={}, children=self.visit(child, depth)) item_tag = ET.Element(moin_page('list-item'), attrib={}, children=[label_tag, body_tag]) item_tag = (item_tag, ) new.extend(item_tag) counter += 1 else: r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) new.extend(r) else: new.append(child) return new
def visit_xhtml_dl(self, element): """ Convert a list of definition. The starting structure:: <dl> <dt>Label 1</dt><dd>Text 1</dd> <dt>Label 2</dt><dd>Text 2</dd> </dl> will be converted to:: <list> <list-item> <list-item-label>Label 1</list-item-label> <list-item-body>Text 1</list-item-body> </list-item> <list-item> <list-item-label>Label 2</list-item-label> <list-item-body>Text 2</list-item-body> </list-item> </list> """ list_item = [] pair = [] number_pair = 0 # We will browse the child, and try to catch all the pair # of <dt><dd> for child in element: # We need one dt tag, and one dd tag, a have a pair if child.tag.name == 'dt' or child.tag.name == 'dd': number_pair += 1 # The following code is similar to do_children method if isinstance(child, ET.Element): r = self.visit(child) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) pair.extend(r) else: pair.append(r) if number_pair == 2: # We have two elements of the pair # So we can put it into a <list-item> element list_item_element = ET.Element(moin_page.list_item, attrib={}, children=pair) list_item.append(list_item_element) pair = [] number_pair = 0 # we return the <list> with all the list item element return ET.Element(moin_page.list, attrib={}, children=list_item)
def new(self, tag, attrib, children): """ Return a new element in the DocBook tree. """ if self.standard_attribute: attrib.update(self.standard_attribute) self.standard_attribute = {} if self.current_section > 0: self.section_children[self.current_section].append( ET.Element(tag, attrib=attrib, children=children)) else: return ET.Element(tag, attrib=attrib, children=children)
def visit_xhtml_li(self, element): """ NB : A list item (<li>) is like the following snippet:: <list-item> <list-item-label>label</list-item-label> <list-item-body>Body</list-item-body> </list-item> For <li> element, there is no label """ list_item_body = ET.Element(moin_page.list_item_body, attrib={}, children=self.do_children(element)) return ET.Element(moin_page.list_item, attrib={}, children=[list_item_body])
def visit_moinpage_list_item_body(self, element): items = [] for child in element: if isinstance(child, ET.Element): r = self.visit(child) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) items.extend(r) else: an_item = ET.Element(docbook.simpara, attrib={}, children=child) items.append(an_item) return ET.Element(docbook.listitem, attrib={}, children=items)
def visit_qandaentry_qanda(self, element, depth): """ Convert:: <question>Q body</question><answer>A Body</answer> to:: <list-item> <list-item-label>Q:</list-item-label> <list-item-body>Q Body</list-item-body> </list-item> <list-item> <list-item-label>A:</list-item-label> <list-item-body>A Body</list-item-body> </list-item> """ items = [] for child in element: if isinstance(child, ET.Element): r = () item_label = None if child.tag.name == 'question': item_label = ET.Element(moin_page('list-item-label'), attrib={}, children="Q:") elif child.tag.name == 'answer': item_label = ET.Element(moin_page('list-item-label'), attrib={}, children="A:") else: r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) items.extend(r) if item_label is not None: item_body = ET.Element(moin_page('list-item-body'), attrib={}, children=self.visit(child, depth)) r = (item_label, item_body) list_item = ET.Element(moin_page('list-item'), attrib={}, children=r) items.append(list_item) else: items.append(child) return items
def visit_list(self, element): """ Convert a list of item (whatever the type : ordered or unordered) So we have html code like:: <ul> <li>Item 1</li> <li>Item 2</li> </ul> Which will be converted to:: <list> <list-item> <list-item-body>Item 1</list-item-body> </list-item> <list-item> <list-item-body>Item 2</list-item-body> </list-item> </list> """ # We will define the appropriate attribute # according to the type of the list attrib = {} if element.tag == "ul" or element.tag == "dir": attrib[moin_page('item-label-generate')] = 'unordered' elif element.tag == "ol": attrib[moin_page('item-label-generate')] = 'ordered' return ET.Element(moin_page.list, attrib=attrib, children=self.do_children(element))
def visit_docbook_segmentedlist(self, element, depth): """ A segmented list is a like a list of definition, but the label are defined at the start with <segtitle> tag and then for each definition, we repeat the label. So to convert such list, we will first determine and save the labels. Then we will iterate over the object to get the definition. """ labels = [] new = [] for child in element: if isinstance(child, ET.Element): r = None if child.tag.name == 'segtitle': r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) labels.extend(r) else: if child.tag.name == 'seglistitem': r = self.visit_docbook_seglistitem(child, labels, depth) else: r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) new.extend(r) else: new.append(child) return ET.Element(moin_page.list, attrib={}, children=new)
def visit_data_element(self, element, depth, object_data, text_object, caption): """ We will try to return an object element based on the object_data. If it is not possible, we return a paragraph with the content of text_object. """ attrib = {} preferred_format, data_tag, mimetype = self.media_tags[element.tag.name] if not object_data: if not text_object: return else: children = self.do_children(element, depth + 1) return self.new(moin_page.p, attrib={}, children=children) # We try to determine the best object to show for obj in object_data: format = obj.get('format') # format is optional: <imagedata format="jpeg" fileref="jpeg.jpg"/> if format: format = format.lower() if format in preferred_format: object_to_show = obj break else: # unsupported format object_to_show = None else: # XXX: Maybe we could add some verification over the extension of the file object_to_show = obj if object_to_show is None: # we could not find any suitable object, return the text_object replacement. children = self.do_children(text_object, depth + 1) return self.new(moin_page.p, attrib={}, children=children) href = object_to_show.get('fileref') if not href: # We could probably try to use entityref, # but at this time we won't support it. return attrib[html.alt] = href attrib[xlink.href] = '+get/' + href format = object_to_show.get('format') if format: format = format.lower() attrib[moin_page('type')] = ''.join([mimetype, format]) else: attrib[moin_page('type')] = mimetype align = object_to_show.get('align') if align and align in set(['left', 'center', 'right', 'top', 'middle', 'bottom']): attrib[html.class_] = align # return object tag, html_out.py will convert to img, audio, or video based on type attr ret = ET.Element(moin_page.object, attrib=attrib) ret = mark_item_as_transclusion(ret, href) if caption: caption = self.new(moin_page.span, attrib={moin_page.class_: 'db-caption'}, children=[caption]) return self.new(moin_page.span, attrib={}, children=[ret, caption]) else: return ret
def new(self, tag, attrib, children): """ Return a new element for the DocBook Tree. """ if self.standard_attribute: attrib.update(self.standard_attribute) self.standard_attribute = {} return ET.Element(tag, attrib=attrib, children=children)
def visit_moinpage_table(self, element): # TODO: Attributes conversion title = element.get(html('title')) if not title: # TODO: Translation title = "Table {0}".format(self.table_counter) self.table_counter += 1 caption = ET.Element(docbook('caption'), attrib={}, children=[title]) children = [caption] children.extend(self.do_children(element)) return self.new(docbook.table, attrib={}, children=children)
def visit_moinpage_page(self, element): title = ET.Element(docbook('title'), attrib={}, children=[self.title]) info = ET.Element(docbook.info, attrib={}, children=[title]) for item in element: if item.tag.uri == moin_page and item.tag.name == 'body': c = self.do_children(item) if not c: self.section_children = sorted(self.section_children.items(), reverse=True) section = None for k, v in self.section_children: if section: section_tag = 'sect{0}'.format(k) v.append(section) section = ET.Element(docbook(section_tag), attrib={}, children=v) else: section_tag = 'sect{0}'.format(k) section = ET.Element(docbook(section_tag), attrib={}, children=v) return ET.Element(docbook.article, attrib={}, children=[info, section]) else: c.insert(0, info) return ET.Element(docbook.article, attrib={}, children=c) raise RuntimeError('page:page need to contain exactly one page body tag, got {0!r}'.format(element[:]))
def visit_xhtml_table(self, element): attrib = self.convert_attributes(element) # we should not have any strings in the child list_table_elements = [] for child in element: if isinstance(child, ET.Element): r = self.visit(child) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) list_table_elements.extend(r) return ET.Element(moin_page.table, attrib=attrib, children=list_table_elements)
def visit_xhtml_list(self, element): """ Convert a list of items (whatever the type : ordered or unordered) So we have html code like:: <ul> <li>Item 1</li> <li>Item 2</li> </ul> Which will be converted to:: <list> <list-item> <list-item-body>Item 1</list-item-body> </list-item> <list-item> <list-item-body>Item 2</list-item-body> </list-item> </list> """ # We will define the appropriate attribute # according to the type of the list attrib = self.convert_attributes(element) if element.tag.name == "ul" or element.tag.name == "dir": attrib[moin_page('item-label-generate')] = 'unordered' elif element.tag.name == "ol": attrib[moin_page('item-label-generate')] = 'ordered' # We check which kind of style we have style = element.get(html.type) if 'A' == style: attrib[moin_page('list-style-type')] = 'upper-alpha' elif 'I' == style: attrib[moin_page('list-style-type')] = 'upper-roman' elif 'a' == style: attrib[moin_page('list-style-type')] = 'lower-alpha' elif 'i' == style: attrib[moin_page('list-style-type')] = 'lower-roman' # we should not have any strings in the child list_items = [] for child in element: if isinstance(child, ET.Element): r = self.visit(child) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) list_items.extend(r) return ET.Element(moin_page.list, attrib=attrib, children=list_items)
def visit_data_element(self, element, depth, object_data, text_object, caption): """ We will try to return an object element based on the object_data. If it is not possible, we return a paragraph with the content of text_object. """ attrib = {} prefered_format, data_tag, mimetype = self.media_tags[element.tag.name] if not object_data: if not text_object: return else: children = self.do_children(child, depth+1)[0] return self.new(moin_page.p, attrib={}, children=children) # We try to determine the best object to show object_to_show = None for obj in object_data: format = obj.get('format') if format: format = format.lower() if format in prefered_format: object_to_show = obj break else: #XXX: Maybe we could add some verification over the # extension of the file object_to_show = obj # If we could not find any suitable object, we return # the text replacement. if not object_to_show: children = self.do_children(child, depth+1)[0] return self.new(moin_page.p, attrib={}, children=children) href = object_to_show.get('fileref') if not href: # We could probably try to use entityref, # but at this time we won't support it. return attrib[xlink.href] = href format = object_to_show.get('format') if format: format = format.lower() attrib[moin_page('type')] = ''.join([mimetype, format]) else: attrib[moin_page('type')] = mimetype return ET.Element(moin_page.object, attrib=attrib)
def visit_docbook_table(self, element, depth): """ <table> --> <table> """ # we should not have any strings in the child list_table_elements = [] for child in element: if isinstance(child, ET.Element): r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) list_table_elements.extend(r) return ET.Element(moin_page.table, attrib={}, children=list_table_elements)
def replace_smiley(self, text): """ Replace a given string by the appropriate element if the string is exactly a smiley. Otherwise return the string without any change. """ # Remove the space of the smiley_text if any smiley_markup = text.strip() if smiley_markup in self.smileys: smiley_name = self.smileys[smiley_markup] attrib = {moin_page('class'): 'moin-text-icon moin-' + smiley_name} return ET.Element(moin_page.span, attrib=attrib, children=[smiley_markup]) else: # if the text was not a smiley, just return the markup without any transformations return text
def handle_simple_list(self, docbook_tag, element, attrib): list_items = [] for child in element: if isinstance(child, ET.Element): # We do not care about <list-item> if child.tag.name != 'list-item': r = self.visit(child) else: r = self.do_children(child) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) list_items.extend(r) return ET.Element(docbook_tag, attrib=attrib, children=list_items)
def visit_qandaset_qanda(self, element, depth): """ <qandaset defaultlabel="qanda"> --> <list> """ items = [] for child in element: if isinstance(child, ET.Element): r = () if child.tag.name == 'qandaentry': r = self.visit_qandaentry_qanda(child, depth) else: r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) items.extend(r) else: items.append(child) return ET.Element(moin_page('list'), attrib={}, children=items)
def visit_qandaset_number(self, element, depth): """ <qandaset defaultlabel="number"> --> <list item-label-generate='ordered'> """ attrib = {} key = moin_page('item-label-generate') attrib[key] = 'ordered' items = [] for child in element: if isinstance(child, ET.Element): if child.tag.name == 'qandaentry': r = self.visit_qandaentry_number(child, depth) else: r = self.visit(child, depth) if r is None: r = () elif not isinstance(r, (list, tuple)): r = (r, ) items.extend(r, ) else: items.append(child) return ET.Element(moin_page('list'), attrib=attrib, children=items)
def test_wikiexternal(conv, input_, output): elem = ET.Element(None) conv.handle_external_links(elem, Iri(input_)) href = elem.get(xlink.href) assert href == output
def __call__(self, attrib=None, children=(), **extra): return ET.Element(self, attrib=attrib, children=children, **extra)
def new(self, tag, attrib, children): """ Return a new element for the DOM Tree """ return ET.Element(tag, attrib=attrib, children=children)
def new(self, tag, attrib={}, children=[]): return ET.Element(tag, attrib=attrib, children=children)
def test_wikilocal(conv, input_, page, output): elem = ET.Element(None) conv.handle_wikilocal_links(elem, Iri(input_), Iri(page)) assert elem.get(xlink.href) == output
def recurse(self, elem, page_href): # on first call, elem.tag.name=='page'. # Descendants (body, div, p, include, page, etc.) are processed by recursing through DOM # stack is used to detect transclusion loops page_href_new = elem.get(moin_page.page_href) if page_href_new: page_href_new = Iri(page_href_new) if page_href_new != page_href: page_href = page_href_new self.stack.append(page_href) else: self.stack.append(None) else: self.stack.append(None) try: if elem.tag == xinclude.include: # we have already recursed several levels and found a transclusion: "{{SomePage}}" or <<Include(...)>> # process the transclusion and add it to the DOM. Subsequent recursions will traverse through # the transclusion's elements. href = elem.get(xinclude.href) xpointer = elem.get(xinclude.xpointer) xp_include_pages = None xp_include_sort = None xp_include_items = None xp_include_skipitems = None xp_include_heading = None xp_include_level = None if xpointer: # we are working on an <<Include(abc)>> macro, not a {{transclusion}} xp = XPointer(xpointer) xp_include = None xp_namespaces = {} for entry in xp: uri = None name = entry.name.split(':', 1) if len(name) > 1: prefix, name = name uri = xp_namespaces.get(prefix, False) else: name = name[0] if uri is None and name == 'xmlns': d_prefix, d_uri = entry.data.split('=', 1) xp_namespaces[d_prefix] = d_uri elif uri == moin_page.namespace and name == 'include': xp_include = XPointer(entry.data) if xp_include: for entry in xp_include: name, data = entry.name, entry.data_unescape # TODO: These do not include all parameters in moin 1.9 Include macro docs: # <<Include(pagename, heading, level, from="regex", to="regex", sort=ascending|descending, items=n, skipitems=n, titlesonly, editlink)>> # these are currently unsupported in moin 2.0: from, to, titlesonly, editlink if name == 'pages': # pages == pagename in moin 1.9 xp_include_pages = data elif name == 'sort': xp_include_sort = data elif name == 'items': xp_include_items = int(data) elif name == 'skipitems': xp_include_skipitems = int(data) elif name == 'heading': xp_include_heading = data elif name == 'level': xp_include_level = data included_elements = [] if href: # We have a single page to transclude or include href = Iri(href) link = Iri(scheme='wiki', authority='') if href.scheme == 'wiki': if href.authority: raise ValueError( "can't handle xinclude for non-local authority" ) else: path = href.path[1:] elif href.scheme == 'wiki.local': page = page_href path = href.path if path[0] == '': # /subitem tmp = page.path[1:] tmp.extend(path[1:]) path = tmp elif path[0] == '..': # ../sisteritem path = page.path[1:] + path[1:] else: raise ValueError( "can't handle xinclude for schemes other than wiki or wiki.local" ) link.path = path if flaskg.user.may.read(unicode(path)): page = Item.create(unicode(path)) pages = ((page, link), ) else: # ACLs prevent user from viewing a transclusion - show message message = moin_page.p(children=(_( 'Access Denied, transcluded content suppressed.'))) attrib = {html.class_: 'warning'} div = ET.Element(moin_page.div, attrib, children=(message, )) container = ET.Element(moin_page.body, children=(div, )) return [ container, 0 ] # replace transclusion with container's child elif xp_include_pages: # we have regex of pages to include: <<Include(^qqq)>> query = And([ Term(WIKINAME, app.cfg.interwikiname), Regex(NAME_EXACT, xp_include_pages) ]) reverse = xp_include_sort == 'descending' results = flaskg.storage.search(query, sortedby=NAME_EXACT, reverse=reverse, limit=None) pagelist = [result.name for result in results] if xp_include_skipitems is not None: pagelist = pagelist[xp_include_skipitems:] if xp_include_items is not None: pagelist = pagelist[xp_include_items + 1:] pages = ((Item.create(p), Iri(scheme='wiki', authority='', path='/' + p)) for p in pagelist) if not pagelist: msg = _( 'Error: no items found matching "<<Include({0})>>"' ).format(xp_include_pages) attrib = {html.class_: 'moin-error'} strong = ET.Element(moin_page.strong, attrib, (msg, )) included_elements.append(strong) for page, p_href in pages: if p_href.path[0] != '/': p_href.path = IriPath('/' + '/'.join(p_href.path)) if p_href in self.stack: # we have a transclusion loop, create an error message showing list of pages forming loop loop = self.stack[self.stack.index(p_href):] loop = [ u'{0}'.format(ref.path[1:]) for ref in loop if ref is not None ] + [page.name] msg = u'Error: Transclusion loop via: ' + u', '.join( loop) attrib = {html.class_: 'moin-error'} strong = ET.Element(moin_page.strong, attrib, (msg, )) included_elements.append(strong) continue if xp_include_heading is not None: attrib = {xlink.href: p_href} children = (xp_include_heading or page.name, ) elem_a = ET.Element(moin_page.a, attrib, children=children) attrib = { moin_page.outline_level: xp_include_level or '1' } elem_h = ET.Element(moin_page.h, attrib, children=(elem_a, )) included_elements.append(elem_h) page_doc = page.content.internal_representation( attributes=Arguments(keyword=elem.attrib)) if isinstance(page.rev.data, file): page.rev.data.close() self.recurse(page_doc, page_href) # The href needs to be an absolute URI, without the prefix "wiki://" page_doc = mark_item_as_transclusion(page_doc, p_href.path) included_elements.append(page_doc) if len(included_elements) > 1: # use a div as container result = ET.Element(moin_page.div) result.extend(included_elements) elif included_elements: result = included_elements[0] else: result = None # end of processing for transclusion; the "result" will get inserted into the DOM below return result # Traverse the DOM by calling self.recurse with each child of the current elem. # Starting elem.tag.name=='page'. container = [] i = 0 while i < len(elem): child = elem[i] if isinstance(child, ET.Node): ret = self.recurse(child, page_href) if ret: # Either child or a descendant of child is a transclusion. # See top of this script for notes on why these DOM adjustments are required. if isinstance(ret, ET.Node ) and elem.tag.name in NO_BLOCK_CHILDREN: body = ret[0] if len(body) == 0: # the transcluded item is empty, insert an empty span into DOM attrib = Attributes(ret).convert() elem[i] = ET.Element(moin_page.span, attrib=attrib) elif (isinstance(body[0], ET.Node) and (len(body) > 1 or body[0].tag.name not in ('p', 'object', 'a'))): # Complex case: "some text {{BlockItem}} more text" or "\n{{BlockItem}}\n" where # the BlockItem body contains multiple p's, a table, preformatted text, etc. # These block elements cannot be made a child of the current elem, so we create # a container to replace elem. # Create nodes to hold any siblings before and after current child (elem[i]) before = copy.deepcopy(elem) after = copy.deepcopy(elem) before[:] = elem[0:i] after[:] = elem[i + 1:] if len(before): # there are siblings before transclude, save them in container container.append(before) new_trans_ptr = len(container) # get attributes from page node; # we expect {class: "moin-transclusion"; data-href: "http://some.org/somepage"} attrib = Attributes(ret).convert() # current elem will likely be replaced by container so we need to copy data-lineno attr if html.data_lineno in elem.attrib: attrib[html.data_lineno] = elem.attrib[ html.data_lineno] # make new div node to hold transclusion, copy children, and save in container div = ET.Element(moin_page.div, attrib=attrib, children=body[:]) container.append( div) # new_trans_ptr is index to this if len(after): container.append(after) if elem.tag.name == 'a': # invalid input [[MyPage|{{BlockItem}}]], # best option is to retain A-tag and fail html validation # TODO: error may not be obvious to user - add error message elem[i] = div else: # move up 1 level in recursion where elem becomes the child and # is usually replaced by container return [container, new_trans_ptr] else: # default action for inline transclusions or odd things like circular transclusion error messages classes = child.attrib.get(html.class_, '').split() classes += ret.attrib.get(html.class_, '').split() ret.attrib[html.class_] = ' '.join(classes) elem[i] = ret elif isinstance(ret, types.ListType): # a container has been returned. # Note: there are multiple places where a container may be constructed ret_container, trans_ptr = ret # trans_ptr points to the transclusion within ret_container. # Here the transclusion will always contain a block level element if elem.tag.name in NO_BLOCK_CHILDREN: # Complex case, transclusion effects grand-parent, great-grand-parent, e.g.: # "/* comment {{BlockItem}} */" or "text ''italic {{BlockItem}} italic'' text" # elem is an inline element, build a bigger container to replace elem's parent, before = copy.deepcopy(elem) after = copy.deepcopy(elem) before[:] = elem[0:i] + ret_container[ 0:trans_ptr] after[:] = ret_container[trans_ptr + 1:] + elem[i + 1:] if len(before): container.append(before) new_trans_ptr = len(container) # child may have classes like "comment" that must be added to transcluded element classes = child.attrib.get( moin_page.class_, '').split() # must use moin_page.class_ above, but use html.class below per html_out.py code classes += ret_container[trans_ptr].attrib.get( html.class_, '').split() ret_container[trans_ptr].attrib[ html.class_] = ' '.join(classes) container.append(ret_container[trans_ptr] ) # the transclusion if len(after): container.append(after) return [container, new_trans_ptr] else: # elem is a block element for grandchild in child: if isinstance( grandchild, ET.Node ) and grandchild.tag.name == u'include': # the include may have classes that must be added to transcluded element classes = grandchild.attrib.get( html.class_, '').split() classes += ret_container[ trans_ptr].attrib.get( html.class_, '').split() ret_container[trans_ptr].attrib[ html.class_] = ' '.join(classes) # replace child element with the container generated in lower recursion elem[i:i + 1] = ret_container # elem[i] is the child else: # default action for any ret not fitting special cases above, # e.g. tranclusion is within a table cell elem[i] = ret # we are finished with this child, advance to next sibling i += 1 finally: self.stack.pop()
def test_wiki(app, conv, input_, output): assert 'MoinMoin' in app.cfg.interwiki_map elem = ET.Element(None) conv.handle_wiki_links(elem, Iri(input_)) assert elem.get(xlink.href) == output