Page MenuHomePhabricator

DoubleWiki.php

Authored By
bzimport
Nov 21 2014, 8:57 PM
Size
10 KB
Referenced Files
None
Subscribers
None

DoubleWiki.php

<?php
# 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.
# http://www.gnu.org/copyleft/gpl.html
#
#
# This extension displays an article and its
# translation on two columns of the same page.
# The translation comes from another wiki
# that can be accessed through interlanguage links
$wgHooks['OutputPageBeforeHTML'][] = 'addMatchedText' ;
function addMatchedText ( &$parserOutput , &$text ) {
global $wgContLang, $wgRequest, $wgLang, $wgContLanguageCode, $wgTitle;
$match_request = $wgRequest->getText( 'match' );
if ( $match_request === '' ) {
return ;
}
foreach( $parserOutput->mLanguageLinks as $l ) {
$nt = Title::newFromText( $l );
$iw = $nt->getInterwiki();
if( $iw === $match_request ){
$url = $nt->getFullURL();
$myURL = $wgTitle -> getLocalURL() ;
$languageName = $wgContLang->getLanguageName( $nt->getInterwiki() );
$myLanguage = $wgLang->getLanguageName( $wgContLanguageCode );
$sep = ( in_string( '?', $url ) ) ? '&' : '?';
$translation = wfGetHttp( $url.$sep.'action=render' );
if ( $translation !== null ) {
#first find all links that have no 'class' parameter.
#these links are local so we add '?match=xx' to their url,
#unless it already contains a '?'
$translation = preg_replace(
"/<a href=\"http:\/\/([^\"\?]*)\"(([\s]+)(c(?!lass=)|[^c\>\s])([^\>\s]*))*\>/i",
"<a href=\"http://\\1?match={$wgContLanguageCode}\"\\2>", $translation );
#now add class='extiw' to these links
$translation = preg_replace(
"/<a href=\"http:\/\/([^\"]*)\"(([\s]+)(c(?!lass=)|[^c\>\s])([^\>\s]*))*\>/i",
"<a href=\"http://\\1\" class=\"extiw\"\\3>", $translation );
#use class='extiw' for images too
$translation = preg_replace(
"/<a href=\"http:\/\/([^\"]*)\"([^\>]*)class=\"image\"([^\>]*)\>/i",
"<a href=\"http://\\1\"\\2class=\"extiw\"\\3>", $translation );
#add prefixes to internal links, in order to prevent duplicates
$translation = preg_replace("/<a href=\"#(.*?)\"/i","<a href=\"#l_\\1\"",
$translation );
$translation = preg_replace("/<font id=\"(.*?)\"/i","<font id=\"l_\\1\"",
$translation );
$text = preg_replace("/<a href=\"#(.*?)\"/i","<a href=\"#r_\\1\"", $text );
$text = preg_replace("/<font id=\"(.*?)\"/i","<font id=\"r_\\1\"", $text );
#add tags before h2 and h3 sections
$translation = preg_replace("/<h2>/i","<div title=\"@@h2\"></div>\n<h2>",
$translation );
$translation = preg_replace("/<h3>/i","<div title=\"@@h3\"></div>\n<h3>",
$translation );
$text = preg_replace("/<h2>/i","<div title=\"@@h2\"></div>\n<h2>", $text );
$text = preg_replace("/<h3>/i","<div title=\"@@h3\"></div>\n<h3>", $text );
#add ?match= to local links of the local wiki
$text = preg_replace( "/<a href=\"\/([^\"\?]*)\"/i",
"<a href=\"/\\1?match={$match_request}\"", $text );
#do the job
$text = matchColumns ( $text, $myLanguage, $myURL ,
$translation, $languageName, $url );
}
return true;
}
}
return true;
}
/**
* Return table with two columns of text
* Text is split into slices based on title tags
*/
function matchColumns( $left_text, $left_title, $left_url, $right_text, $right_title, $right_url ){
# note about emdedding:
# text is split only at a single level.
# initially we assume that this level is zero
# if nesting is encountered before the
# first paragraph, then this split level is increased
# we keep track of the current nesting level during processing
# if (current level != split level) then we do not split the text
# the current level of embedding (stack depth)
$left_nesting = 0;
$right_nesting = 0;
#the level of embedding where the text is split
#initial value is -1 until actual value is known
$left_splitlevel = -1;
$right_splitlevel = -1;
# split text
$tag_pattern = "/<div title=\"([^\"]*)\"><\/div>/i";
$left_slices = preg_split( $tag_pattern, $left_text );
$right_slices = preg_split( $tag_pattern, $right_text );
preg_match_all( $tag_pattern, $left_text, $left_tags, PREG_PATTERN_ORDER );
preg_match_all( $tag_pattern, $right_text, $right_tags, PREG_PATTERN_ORDER );
/**
* Order slices in a two-column array.
* slices that are surrounded by the same tag belong in the same line
* $i indexes the left column, $j the right column.
*/
$body = '';
$left_chunk = '';
$right_chunk = '';
$j=0;
$max_i = count( $left_slices );
for ( $i=0 ; $i < $max_i ; $i++ ) {
$found = false;
$left_chunk .= $left_slices[$i];
$max_k = count( $right_slices );
# if we are at the end of the loop, finish quickly
if ( $i==$max_i - 1 ) {
for ( $k=$j ; $k < $max_k ; $k++ ) $right_chunk .= $right_slices[$k];
$found = true;
}
else for ( $k=$j ; $k < $max_k ; $k++ ) {
#look for requested tag in the text
$a = strpos ( $right_slices[$k], $left_tags[1][$i] );
if( $a ) {
#go to beginning of paragraph
#this regexp matches the rightmost delimiter
$sub = substr( $right_slices[$k], 0, $a);
if ( preg_match("/(.*)<(p|dl)>/is", $sub, $matches ) ){
$right_chunk .= $matches[1];
$right_slices[$k] = substr( $right_slices[$k], strlen($matches[1]) );
}
else {
$right_chunk .= $sub;
$right_slices[$k] = substr( $right_slices[$k], $a );
}
$found = true;
$j = $k;
break;
}
$right_chunk .= $right_slices[$k];
if( $k < $max_k - 1 ) {
if( $left_tags[0][$i] == $right_tags[0][$k] ) {
$found = true;
$j = $k+1;
break;
}
}
}
if( $found ) {
#split chunks into smaller units (paragraphs)
$paragraph_tags = "/<(p|dl)>/i";
$left_bits = preg_split( $paragraph_tags, $left_chunk );
$right_bits = preg_split( $paragraph_tags, $right_chunk );
preg_match_all( $paragraph_tags, $left_chunk, $left_seps, PREG_PATTERN_ORDER );
preg_match_all( $paragraph_tags, $right_chunk, $right_seps, PREG_PATTERN_ORDER );
$left_chunk = '';
$right_chunk = '';
# add separators that were cut off
for($l=1; $l < count( $left_bits ); $l++ ) {
$left_bits[$l] = $left_seps[0][$l-1].$left_bits[$l];
}
for($l=1; $l < count( $right_bits ); $l++ ) {
$right_bits[$l] = $right_seps[0][$l-1].$right_bits[$l];
}
$max = max( count( $left_bits ) , count( $right_bits ));
# initialize missing elements
for($l= count( $left_bits ); $l<$max; $l++) $left_bits[$l]='';
for($l= count( $right_bits ); $l<$max; $l++) $right_bits[$l]='';
for($l=0; $l < $max; $l++ ) {
list($left_delta,$left_o,$left_c) = nesting_delta( $left_bits[$l] );
list($right_delta,$right_o,$right_c) = nesting_delta( $right_bits[$l] );
$left_nesting = $left_nesting + $left_delta;
$right_nesting = $right_nesting + $right_delta;
#are we at the end?
$the_end = ($l == $max-1) && ($i == $max_i -1 );
if(( $left_splitlevel == -1) && ($right_splitlevel == -1)) {
$left_splitlevel = $left_nesting;
$right_splitlevel = $right_nesting;
$left_opening = $left_o;
$right_opening = $right_o;
$left_closure = $left_c;
$right_closure = $right_c;
$left_prefix = '';
$right_prefix = '';
$left_suffix = $left_closure;
$right_suffix = $right_closure;
}
else if($the_end) {
$left_prefix = $left_opening;
$right_prefix = $right_opening;
$left_suffix = '';
$right_suffix = '';
}
else {
$left_prefix = $left_opening;
$right_prefix = $right_opening;
$left_suffix = $left_closure;
$right_suffix = $right_closure;
}
if( ( ($left_nesting == $left_splitlevel)
&& ($right_nesting == $right_splitlevel) ) || $the_end) {
$body .=
"<tr><td valign=\"top\" style=\"padding-right: 0.5em\">"
."<div style=\"width:35em; margin:0px auto\">\n"
.$left_prefix.$left_bits[$l].$left_suffix
."</div>"
."</td>\n<td valign=\"top\" style=\"padding-left: 0.5em\">"
."<div style=\"width:35em; margin:0px auto\">\n"
.$right_prefix.$right_bits[$l].$right_suffix
."</div>"
."</td></tr>\n";
}
else {
# procrastinate
$left_nesting = $left_nesting - $left_delta;
$right_nesting = $right_nesting - $right_delta;
if ($l < $max-1) {
$left_bits[$l+1] = $left_bits[$l] . $left_bits[$l+1];
$right_bits[$l+1] = $right_bits[$l] . $right_bits[$l+1];
} else {
$left_chunk = $left_bits[$l] ;
$right_chunk = $right_bits[$l];
}
}
}
}
else{ $right_chunk='';}
}
# format table head and return results
$left_url = htmlspecialchars( $left_url );
$right_url = htmlspecialchars( $right_url );
$head =
"<table width=\"100%\" border=\"0\" bgcolor=\"white\" rules=\"cols\" cellpadding=\"0\">
<colgroup><col width=\"50%\"/><col width=\"50%\"/></colgroup><thead>
<tr><td bgcolor=\"#cfcfff\" align=\"center\">
<a href=\"{$left_url}\">{$left_title}</a></td>
<td bgcolor=\"#cfcfff\" align=\"center\">
<a href=\"{$right_url}\" class='extiw'>{$right_title}</a>
</td></tr></thead>\n";
return $head.$body."</table>" ;
}
/*
* returns how much the stack is changed
* also returns opening and closing sequences of tag
*/
function nesting_delta ( $text ) {
#tags that must be closed. (list copied from Sanitizer.php)
$tags = "/<\/?(b|del|i|ins|u|font|big|small|sub|sup|h1|h2|h3|h4|h5|h6|"
."cite|code|em|s|strike|strong|tt|tr|td|var|div|center|blockquote|ol|ul|dl|"
."table|caption|pre|ruby|rt|rb|rp|p|span)([\s](.*?)>|>)/i";
preg_match_all( $tags, $text, $m, PREG_SET_ORDER);
$stack = array();
$counter = 0;
$opening = '';
$closure = '';
for($i=0; $i < count($m); $i++){
$t = $m[$i];
if( substr( $t[0], 0, 2) != "</" ){
$counter++;
array_push($stack, $t);
} else {
$tt = array_pop($stack);
$counter--;
#if( ($tt != null) && ($tt[1] != $t[1]) ) {
# #input html is buggy...
# echo "Warning: ".$t[1]." encountered, expected ".$tt[1]."<br>\n";
#}
}
}
for($i=0; $i<$counter; $i++){
$opening .= $stack[$i][0];
$closure = "</".$stack[$i][1].">".$closure;
}
return array($counter, $opening, $closure);
}
?>

File Metadata

Mime Type
text/x-php
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2047
Default Alt Text
DoubleWiki.php (10 KB)

Event Timeline