Permalink
Browse files

Add HTML rendering for internal links.

This includes rendering for categories, interwiki links and media files.

Also: add a full test: parsing an article from the French Wikipedia. To
run it, launch "python parser.py" and look at article.htm
  • Loading branch information...
1 parent e2b6293 commit 4f58bb3314918d6432bde0839dc8828dbdb07e5f @peter17 peter17 committed Jul 30, 2011
View
@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
+<head><title>Test!</title></head><body>
+<p>Contenu soumis à la licence CC-BY-SA 3.0 (<a href="http://creativecommons.org/licenses/by-sa/3.0/deed.fr)">http://creativecommons.org/licenses/by-sa/3.0/deed.fr)</a> Source : Article Berceuse de Wikipédia en français (<a href="http://fr.wikipedia.org/wiki/Berceuse).">http://fr.wikipedia.org/wiki/Berceuse).</a> </p>
+<p><a href="Template:Voir homonymes">Template:Voir homonymes</a><a href="Template:ébauche">Template:ébauche</a><div class="thumbnail"><img src="William-Adolphe Bouguereau (1825-1905) - Lullaby (1875).jpg" style="float:right;width:250px;" /><p><em>La Berceuse</em> (œuvre de <a href="William Bouguereau">William Bouguereau</a> - <a href="Template:XIXe siècle">Template:XIXe siècle</a>)</p></div>
+Une <strong>berceuse</strong> est une <a href="chanson">chanson</a> douce destinée à aider les <a href="enfant">enfant</a>s à <a href="Sommeil">s'endormir</a>. Il existe des berceuses chantées dans toutes les langues du monde.</p>
+<h2>Berceuses françaises</h2>
+<p>Les berceuses françaises traditionnelles les plus connues sont « Dodo, l'enfant do / l'enfant dormira peut-être » et « Fais dodo, Colas mon p'tit frère ». Une berceuse du Nord, « <a href="P'tit Quinquin (chanson)">P'tit Quinquin</a> » est également devenue célèbre.</p>
+<h2>Dans la musique classique</h2>
+<p>Les berceuses en musique classique obéissent souvent à une forme nommée aussi "berceuse" dans les langues étrangères. Elles sont généralement à trois <a href="Temps (solfège)">temps</a>, à <a href="tonalité">tonalité</a> simple, alternant des harmonies <a href="dominante">dominante</a> et <a href="Tonique (musique)">tonique</a>.</p>
+<p><strong>Berceuses célèbres classées par compositeurs :</strong></p>
+{| style="text-align:center; background: #f9f9f9; color: #000;font-size:90%; line-height:1.1em; float:right;clear:right; margin:1em 1.5em 1em 1em; width:300px; border: 1px solid #aaa; padding: 0.1em;" cellspacing="7"<p>! class="media audio" style="background-color:#ccf; line-height:3.1em" | Fichier audio|-|<span style="height:20px; width:100%; padding:4pt; padding-left:0.3em; line-height:2em;"><strong>[[Media:{{{filename|{{{nomfichier|{<a href="Template:2">Template:2</a>}}}}}}}|{{{title|{{{titre|{<a href="Template:1">Template:1</a>}}}}}}}]]</strong> <em>([[:Fichier:{{{filename|{{{nomfichier|{<a href="Template:2">Template:2</a>}}}}}}}|info]])</em><br />&lt;small&gt;{{{suitetexte|{<a href="Template:description">Template:description</a>}}}}&lt;/small&gt;&lt;center&gt;[[Fichier:{{{filename|{{{nomfichier|{<a href="Template:2">Template:2</a>}}}}}}}|noicon]]&lt;/center&gt;</span><br /><span style="height:20px; width:100%; padding-left:0.3em;"><span><img src="Circle question mark.png" style="width:14px;" /> <em><a href="Aide:Écouter des sons ogg">Des problèmes pour écouter le fichier ?</a></em></span>|}</span></p>
+<ul>
+ <li> <a href="Jean-Sébastien Bach">Bach</a><ul>
+ <li> Menuet pour clavier Nº1 en sol majeur (Carnets intimes d'Anna Magdalena II/1), BWV Anh. 114</li>
+ <li> « <a href="Jésus que ma joie demeure">Jésus, que ma joie demeure</a> », (<a href="Liste des cantates de Johann Sebastian Bach">Cantate Nº147</a>), BWV 147</li>
+ <li> Suite pour orchestre n°3 en re majeur, BWV 1068</li>
+</ul>
+</li>
+ <li> <a href="Ludwig van Beethoven">Beethoven</a><ul>
+ <li> « <a href="Sonate pour piano nº 14 de Beethoven">Sonate au clair de lune</a> » (Sonate pour piano n° 14 en do dièse mineur), op. 27/2</li>
+ <li> « <a href="La Lettre à Élise">Lettre à Élise</a> » (La Bagatelle en la mineur), WoO 59</li>
+ <li> « <a href="Sonate pour piano nº 8 de Beethoven">Sonate Pathétique</a> » (Sonate pour piano nº 8 de Beethoven), op. 13/2, Adagio cantabile</li>
+</ul>
+</li>
+ <li> <a href="Johannes Brahms">Brahms</a><ul>
+ <li> « Bonsoir et bonne nuit », berceuse ("Guten Abend, gut Nacht", Wiegenlied), op. 49/4</li>
+</ul>
+</li>
+ <li> <a href="Frédéric Chopin">Chopin</a><ul>
+ <li> « <a href="Berceuse (Chopin)">Berceuse en ré bémol majeur</a> », op.57</li>
+</ul>
+</li>
+ <li> <a href="Claude Debussy">Debussy</a><ul>
+ <li> « <a href="Suite Bergamasque">Clair de lune</a> » ("Suite Bergamasque" pour piano), L. 75/3</li>
+</ul>
+</li>
+ <li> <a href="Antonín Dvořák">Dvořák</a><ul>
+ <li> « <a href="Symphonie nº 9 de Dvořák">Sonate du Nouveau Monde</a> » (Symphonie n° 9 en mi mineur, B. 178), op. 95</li>
+</ul>
+</li>
+ <li> <a href="Gabriel Fauré">Fauré</a><ul>
+ <li> « <a href="Dolly (Fauré)">Berceuse</a> » (première pièce de la suite Dolly, pour piano à quatre mains), op. 56</li>
+</ul>
+</li>
+ <li> <a href="Felix Mendelssohn Bartholdy">Mendelssohn</a><ul>
+ <li> « <a href="Romances sans paroles (Mendelssohn)">Chant du printemps</a> » (Romances sans paroles pour piano), op. 62/6</li>
+</ul>
+</li>
+ <li> <a href="Wolfgang Amadeus Mozart">Mozart</a><ul>
+ <li> « <a href="Ah ! vous dirai-je, maman">Ah ! vous dirai-je, maman</a> » (Douze variations), K. 265</li>
+ <li> « <a href="Sonate pour piano n° 16 de Mozart">Sonate facile</a> » (Sonate pour piano nº 16 en do majeur), K. 545</li>
+ <li> « La Chasse » (Sonate pour piano n° 18 en ré majeur), K. 576, 3ème mouvement</li>
+</ul>
+</li>
+ <li> <a href="Johann Pachelbel">Pachelbel</a><ul>
+ <li> « <a href="Canon de Pachelbel">Canon en ré majeur sur une basse obstinée</a> »</li>
+</ul>
+</li>
+ <li> <a href="Robert Schumann">Schumann</a><ul>
+ <li> « <a href="Scènes d'enfants (Schumann)">Gens et pays étrangers</a> » (Les Scènes d'enfants, "Kinderszenen"), op. 15</li>
+</ul>
+</li>
+ <li> <a href="Bedřich Smetana">Smetana</a><ul>
+ <li> « <a href="Má Vlast">La Moldau</a> » (Second poèmes symphoniques de "Ma Patrie")</li>
+</ul>
+</li>
+ <li> <a href="Antonio Vivaldi">Vivaldi</a><ul>
+ <li> « <a href="Les Quatre Saisons">L'Hiver</a> » (Les Quatre Saisons, Opus 8/4)</li>
+</ul>
+</li>
+</ul>
+<p>Les plus connues sont la berceuse « <em>Bonsoir et bonne nuit</em> » de Brahms et la « <em>Berceuse en ré bémol majeur</em> » de <a href="Chopin">Chopin</a>. Des compositeurs comme <a href="Franz Liszt">Franz Liszt</a>, <a href="Gabriel Fauré">Gabriel Fauré</a>, <a href="Maurice Ravel">Maurice Ravel</a>, <a href="Mili Balakirev">Mili Balakirev</a>, <a href="Igor Stravinski">Igor Stravinski</a> (<em><a href="Berceuses du chat">Berceuses du chat</a></em>) et <a href="George Gershwin">George Gershwin</a> ont aussi écrit des berceuses.</p>
+<h2>Dans la musique populaire</h2>
+<h3>Dans la chanson française</h3>
+<ul>
+ <li> <a href="Bénabar">Bénabar</a> : « La berceuse », de l'album <em><a href="Reprise des négociations">Reprise des négociations</a></em>, 2005</li>
+ <li> <a href="Henri Salvador">Henri Salvador</a> : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album <em>Rigolo</em>, 1968</li>
+</ul>
+<h3>Dans la musique pop</h3>
+<ul>
+ <li> <a href="Beatles">Beatles</a> : « <a href="Good Night (chanson)">Good Night</a> », de l'<em><a href="The Beatles (album)">album blanc</a></em>, 1968</li>
+</ul>
+<h3>Dans le jazz</h3>
+<ul>
+ <li> <a href="Baden Powell de Aquino">Baden Powell</a> : « Berceuse a Jussara », de l'album <em>Le Monde Musical</em>, 1964</li>
+ <li> <a href="Henri Salvador">Henri Salvador</a> : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album <em>Rigolo</em>, 1968</li>
+</ul>
+<h2> Voir aussi </h2>
+<ul>
+ <li> <a href="Lullaby">Lullaby</a> dans la section anglaise et italienne où il y a une description d'une bonne partie des berceuses européennes.</li>
+</ul>
+<p><a href="Template:Wiktionnaire">Template:Wiktionnaire</a></p>
+<h3>Bibliographie</h3>
+<ul>
+ <li>Claudine Antoine, <em>La Berceuse populaire dans le contexte de la petite enfance</em>, Université Strasbourg 2, 1988, 2 vol., 358 + 333 p. (thèse de Musiocologie)</li>
+ <li>Marina Altmann de Litvan (dir.), <em>La berceuse : jeux d'amour et de magie</em>, Erès, Ramonville Saint-Agne, 2008, 151 p. <a href="Template:ISBN">Template:ISBN</a></li>
+</ul>
+<p><a href="Template:portail musique">Template:portail musique</a></p>
+<p>Categories: <a href="Berceuse">Berceuse</a>, <a href="Musique par genre">Musique par genre</a>, <a href="Musique traditionnelle">Musique traditionnelle</a></p>
+<p>Interwiki: <a href="http://ar.wikipedia.org/wiki/تهويدة">تهويدة</a>, <a href="http://az.wikipedia.org/wiki/Layla">Layla</a>, <a href="http://br.wikipedia.org/wiki/Luskellerez">Luskellerez</a>, <a href="http://ca.wikipedia.org/wiki/Cançó de bressol">Cançó de bressol</a>, <a href="http://cs.wikipedia.org/wiki/Ukolébavka">Ukolébavka</a>, <a href="http://da.wikipedia.org/wiki/Berceuse">Berceuse</a>, <a href="http://de.wikipedia.org/wiki/Wiegenlied">Wiegenlied</a>, <a href="http://en.wikipedia.org/wiki/Lullaby">Lullaby</a>, <a href="http://eo.wikipedia.org/wiki/Lulkanto">Lulkanto</a>, <a href="http://es.wikipedia.org/wiki/Nana (canción de cuna)">Nana (canción de cuna)</a></p>
+</body></html>
View
133 html.py
@@ -1,8 +1,9 @@
from constants import html_entities
from pijnu.library.node import Nil, Nodes
from mediawiki_parser import wikitextParser
+from mutagen import Metadata
-def toolset(allowed_tags, allowed_autoclose_tags, allowed_attributes):
+def toolset(allowed_tags, allowed_autoclose_tags, allowed_attributes, interwiki, namespaces):
tags_stack = []
external_autonumber = []
@@ -11,6 +12,33 @@ def toolset(allowed_tags, allowed_autoclose_tags, allowed_attributes):
is rendered as: "<a href="...">[1]</a> <a href="...">[2]</a>
"""
+ category_links = []
+ """ This will contain the links to the categories of the article. """
+ interwiki_links = []
+ """ This will contain the links to the foreign versions of the article. """
+
+ for namespace, value in namespaces.iteritems():
+ assert value in range(16), "Incorrect value for namespaces"
+ """
+ Predefined namespaces; source: includes/Defines.php of MediaWiki-1.17.0
+ 'NS_MAIN', 0
+ 'NS_TALK', 1
+ 'NS_USER', 2
+ 'NS_USER_TALK', 3
+ 'NS_PROJECT', 4
+ 'NS_PROJECT_TALK', 5
+ 'NS_FILE', 6
+ 'NS_FILE_TALK', 7
+ 'NS_MEDIAWIKI', 8
+ 'NS_MEDIAWIKI_TALK', 9
+ 'NS_TEMPLATE', 10
+ 'NS_TEMPLATE_TALK', 11
+ 'NS_HELP', 12
+ 'NS_HELP_TALK', 13
+ 'NS_CATEGORY', 14
+ 'NS_CATEGORY_TALK', 15
+ """
+
def balance_tags(tag=None):
i = 0
if tag is not None:
@@ -48,11 +76,18 @@ def render_raw_text(node):
node.value = "%s" % node.leaf()
def render_paragraph(node):
- node.value = '<p>' + content(node) + '</p>\n'
+ value = content(node)
+ if value != '':
+ node.value = '<p>' + value + '</p>\n'
def render_body(node):
from apostrophes import parseAllQuotes
- node.value = '<body>\n' + parseAllQuotes(content(node)) + '</body>'
+ metadata = ''
+ if category_links != []:
+ metadata += '<p>Categories: ' + ', '.join(category_links) + '</p>\n'
+ if interwiki_links != []:
+ metadata += '<p>Interwiki: ' + ', '.join(interwiki_links) + '</p>\n'
+ node.value = '<body>\n' + parseAllQuotes(content(node)) + metadata + '</body>'
def render_entity(node):
value = '%s' % node.leaf()
@@ -269,9 +304,91 @@ def render_external_link(node):
text = node.value[1].leaf()
node.value = '<a href="%s">%s</a>' % (node.value[0].leaf(), text)
+ def render_interwiki(prefix, page):
+ link = '<a href="%s">%s</a>' % (interwiki[prefix] + page, page)
+ if link not in interwiki_links:
+ interwiki_links.append(link)
+
+ def render_category(category_name):
+ link = '<a href="%s">%s</a>' % (category_name, category_name)
+ if link not in category_links:
+ category_links.append(link)
+
+ def render_file(file_name, arguments):
+ """ This implements a basic handling of images.
+ MediaWiki supports much more parameters (see includes/Parser.php).
+ """
+ style = ''
+ thumbnail = False
+ legend = ''
+ if arguments != []:
+ parameters = arguments[0].value
+ for parameter in parameters:
+ parameter = '%s' % parameter.leaf()
+ if parameter[-2:] == 'px':
+ size = parameter[0:-2]
+ if 'x' in size:
+ size_x, size_y = size.split('x', 1)
+ try:
+ size_x = int(size_x)
+ size_y = int(size_y)
+ style += 'width:%spx;height:%spx' % (size_x, size_y)
+ except:
+ legend = parameter
+ else:
+ try:
+ size_x = int(size)
+ style += 'width:%spx;' % size_x
+ except:
+ legend = parameter
+ elif parameter in ['left', 'right', 'center']:
+ style += 'float:%s;' % parameter
+ elif parameter in ['thumb', 'thumbnail']:
+ thumbnail = True
+ elif parameter == 'border':
+ style += 'border:1px solid grey'
+ else:
+ legend = parameter
+ result = '<img src="%s" style="%s" />' % (file_name, style)
+ if thumbnail:
+ result = '<div class="thumbnail">%s<p>%s</p></div>\n' % (result, legend)
+ return result
+
+ def render_internal_link(node):
+ force_link = False
+ url = ''
+ page_name = node.value.pop(0).value
+ if page_name[0] == ':':
+ force_link = True
+ page_name = page_name[1:]
+ if ':' in page_name:
+ namespace, page_name = page_name.split(':', 1)
+ if namespace in interwiki and not force_link:
+ render_interwiki(namespace, page_name)
+ node.value = ''
+ return
+ elif namespace in interwiki:
+ url = interwiki[namespace]
+ namespace = ''
+ if namespace in namespaces:
+ if namespaces[namespace] == 6 and not force_link: # File
+ node.value = render_file(page_name, node.value)
+ return
+ elif namespaces[namespace] == 14 and not force_link: # Category
+ render_category(page_name)
+ node.value = ''
+ return
+ if namespace:
+ page_name = namespace + ':' + page_name
+ if len(node.value) == 0:
+ text = page_name
+ else:
+ text = '|'.join('%s' % item.leaf() for item in node.value[0])
+ node.value = '<a href="%s%s">%s</a>' % (url, page_name, text)
+
return locals()
-def make_parser(allowed_tags=[], allowed_autoclose_tags=[], allowed_attributes=[]):
+def make_parser(allowed_tags=[], allowed_autoclose_tags=[], allowed_attributes=[], interwiki={}, namespaces={}):
"""Constructs the parser for the HTML backend.
:arg allowed_tags: List of the HTML tags that should be allowed in the parsed wikitext.
@@ -281,7 +398,11 @@ def make_parser(allowed_tags=[], allowed_autoclose_tags=[], allowed_attributes=[
parsed wikitext. All the other self-closing tags will be output as &lt;tag /&gt;
:arg allowed_attributes: List of the HTML attributes that should be allowed in the parsed
tags (e.g.: class="", style=""). All the other attributes (e.g.: onclick="") will
- be removed.
+ be removed.
+ :arg interwiki: List of the allowed interwiki prefixes (en, fr, es, commons, etc.)
+ :arg namespaces: List of the namespaces of the wiki (File, Category, Template, etc.),
+ including the localized version of those strings (Modele, Categorie, etc.),
+ associated to the corresponding namespace code.
"""
- tools = toolset(allowed_tags, allowed_autoclose_tags, allowed_attributes)
+ tools = toolset(allowed_tags, allowed_autoclose_tags, allowed_attributes, interwiki, namespaces)
return wikitextParser.make_parser(tools)
View
@@ -113,14 +113,15 @@ def replace_by_space(node):
# TODO: allow IPv6 addresses (http://[::1]/etc)
address : (!(QUOTE/R_BRACKET) [\x21..\xff])+ : liftValue
url : protocol address : join
+ inline_url : url{1} : render_url
# Links
allowed_in_link : (!(R_BRACKET/PIPE) ESC_CHAR)+ : restore join
link_text : (clean_inline / allowed_in_link)* : liftValue
link_argument : PIPE link_text : liftValue
link_arguments : link_argument*
- internal_link : LINK_BEGIN page_name link_arguments LINK_END : liftValue
+ internal_link : LINK_BEGIN page_name link_arguments LINK_END : render_internal_link
optional_link_text : SPACETAB+ link_text : liftValue
external_link : L_BRACKET url optional_link_text? R_BRACKET : render_external_link
link : internal_link / external_link
@@ -137,7 +138,6 @@ def replace_by_space(node):
# Text types
- inline_url : url{1} : render_url
styled_text : link / inline_url / html_comment / tag / entity
not_styled_text : preformatted / nowiki
allowed_char : ESC_CHAR{1} : restore liftValue
Oops, something went wrong.

0 comments on commit 4f58bb3

Please sign in to comment.