File Coverage

blib/lib/Labyrinth/Plugin/Wiki/Text.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             package Labyrinth::Plugin::Wiki::Text;
2              
3 2     2   6406 use warnings;
  2         4  
  2         67  
4 2     2   8 use strict;
  2         2  
  2         60  
5              
6 2     2   6 use vars qw($VERSION);
  2         2  
  2         105  
7             $VERSION = '1.06';
8              
9             =head1 NAME
10              
11             Labyrinth::Plugin::Wiki::Text - Wiki text handler for Labyrinth framework.
12              
13             =head1 DESCRIPTION
14              
15             Contains all the Wiki text rendering code for Labyrinth.
16              
17             =cut
18              
19             # -------------------------------------
20             # Library Modules
21              
22 2     2   14 use base qw(Labyrinth::Plugin::Base);
  2         3  
  2         727  
23              
24             use Labyrinth::Audit;
25             use Labyrinth::DBUtils;
26             use Labyrinth::MLUtils;
27             use Labyrinth::Support;
28             use Labyrinth::Users;
29             use Labyrinth::Variables;
30              
31             # -------------------------------------
32             # Variables
33              
34             # preset with restricted pages
35             my %wiki_links = map {$_ => 1} qw(People Login Search RecentChanges);
36              
37             my ($LinkPattern,$SitePattern,$UrlPattern,$UriPattern,$MailPattern,$SendPattern);
38              
39             # HTML tag lists
40             # Single tags (that do not require a closing /tag)
41             my @HtmlSingle = qw(br hr);
42             # Tags that must be in ... pairs:
43             my @HtmlPairs = ( qw(b i p u h1 h2 h3 h4 h5 h6 code em strike strong blockquote ol ul li dt dd tr td th),
44             @HtmlSingle); # All singles can also be pairs
45              
46             # -------------------------------------
47             # Public Methods
48              
49             =head1 PUBLIC INTERFACE METHODS
50              
51             =over 4
52              
53             =item Render
54              
55             Controls the process of rendering a given page.
56              
57             =item InitLinkPatterns
58              
59             Prepares patterns used to translate wiki links into HTML links.
60              
61             =item Wiki2HTML
62              
63             Translate WikiFormat into XHTML.
64              
65             =item CommonMarkup
66              
67             Looks for and translates common WikiFormat markup into XHTML.
68              
69             =item WikiLink
70              
71             Looks for and translates WikiFormat links into XHTML.
72              
73             =item WikiHeading
74              
75             Translate WikiFormat heading into XHTML.
76              
77             =cut
78              
79             sub Render {
80             my $self = shift;
81             my $hash = shift;
82             my $title = $cgiparams{pagename};
83             my $content = $hash->{content};
84              
85             InitLinkPatterns() unless($LinkPattern);
86              
87             $content = Wiki2HTML($content);
88              
89             # reposition top level heading
90             if($content =~ s!^

(.*?)

!!) {
91             $title = $1;
92             }
93              
94             return $title,$content;
95             }
96              
97             sub InitLinkPatterns {
98             my $UpperLetter = '[A-Z\xc0-\xde]';
99             my $LowerLetter = '[a-z\xdf-\xff]';
100             my $AnyLetter = '[A-Za-z\xc0-\xff_0-9\$]';
101             my $AnyString = '[A-Za-z\xc0-\xff_0-9 \-\&\'~.,\?\(\)\"!\$:\/]';
102              
103             # Main link pattern: lowercase between uppercase, then anything
104             my $LpA = $UpperLetter . $AnyLetter . '*';
105             my $LpB = $AnyLetter . $AnyString . '*';
106             my $LpC = $AnyLetter . '*:' . $AnyString . '*';
107              
108             $LinkPattern = qr!\[\[($LpA|$LpC)\]\]!;
109             $SitePattern = qr!\[\[($LpA|$LpC)\|($LpB)\]\]!;
110              
111             $UrlPattern = qr!\[($settings{urlregex})\]!;
112             $UriPattern = qr!\[($settings{urlregex})[ \|]($LpB)\]!;
113              
114             $MailPattern = qr!\[(?:mailto:)?($settings{emailregex})\]!;
115             $SendPattern = qr!\[(?:mailto:)?($settings{emailregex})[ |]($LpB)\]!;
116             }
117              
118             sub Wiki2HTML {
119             my ($text) = @_;
120             my (@stack, $code, $oldcode, $parse);
121             my $depth = 0;
122             my $html = '';
123              
124             $code = 'p'; # we assume a paragraph starts
125             $text =~ s/\r\n?/\n/g;
126             for (split(/\n/, $text)) { # Process lines one-at-a-time
127             $_ .= "\n";
128             $parse = 2;
129             if (s/^(\*+)/
  • /) {
  • 130             $code = "ul";
    131             $depth = length $1;
    132             } elsif (s/^(\#+)/
  • /) {
  • 133             $code = "ol";
    134             $depth = length $1;
    135             } elsif (s/^![ \t]//) {
    136             $code = "pre";
    137             $depth = 1;
    138             $parse = 0;
    139             } elsif (s/^([ \t]{2})//) {
    140             $code = "pre";
    141             $depth = 1;
    142             $parse = 1;
    143             } elsif (s/^(\" )//) {
    144             $code = "blockquote";
    145             $depth = 1;
    146             } else {
    147             $code = "p";
    148             $depth = 0;
    149             }
    150             while (@stack > $depth) { # Close tags as needed
    151             $html .= '\n";
    152             }
    153             if ($depth > 0) {
    154             # $depth = $IndentLimit if ($depth > $IndentLimit);
    155             if (@stack) { # Non-empty stack
    156             $oldcode = pop(@stack);
    157             if ($oldcode ne $code) {
    158             $html .= "<$code>\n";
    159             }
    160             push(@stack, $code);
    161             }
    162             while (@stack < $depth) {
    163             push(@stack, $code);
    164             $html .= "<$code>\n";
    165             }
    166             }
    167              
    168             if($code eq 'pre') {
    169             s!^\s*$!
    \n!; # Blank lines become new lines
    170             } else {
    171             s!^\s*$!

    !; # Blank lines become new paragraphs

    172             }
    173             $html .= CommonMarkup($_, $parse);
    174             }
    175             while (@stack > 0) { # Clear stack
    176             $html .= '\n";
    177             }
    178              
    179             $html = process_html($html,0,1);
    180              
    181             # $html =~ s!

    (.*?)\s*<(ul|ol|h[1-6]|pre|p)>!

    $1

    \n<$2>!gs; # close

    's.

    182             # $html =~ s!

    (.*?)\s*$!

    $1

    !gs; # close final

    .

    183             # $html =~ s!\s*

    \s*

    !

    !gs; # remove extra close paragraphs
    184             # $html =~ s!

    \s*

    !!gs; # remove black paragraphs
    185             # $html =~ s/(\s|

    )*

    \s*/\n

    /gs; # multiple blank lines fold into one.

    186             # $html =~ s/\s*

    \s*<(ul|ol|h[1-6]|pre)/\n<$1/gs; # remove unnecessary

    's.

    187             # $html =~ s!([^>\s]+)\s*

    !$1

    \n

    !gs; # close paragraphs.

    188             # $html =~ s!\s*

    \s*\n
    189              
    190             LogDebug("html=[$html]");
    191             return $html;
    192             }
    193              
    194             # 2 = Full parser
    195             # 1 = Link only parsing
    196             # 0 = no parsing
    197              
    198             sub CommonMarkup {
    199             my ($text, $parse) = @_;
    200             local $_ = $text;
    201              
    202             if ($parse > 1) {
    203             s!\<pre\>((.|\n)*?)\<\/pre\>!
    $1
    !ig;
    204             s!\<code\>((.|\n)*?)\<\/code\>!$1!ig;
    205              
    206             my $t;
    207             for $t (@HtmlPairs) {
    208             s!\<$t(\s[^<>]+?)?\>(.*?)\<\/$t\>!<$t$1>$2<\/$t>!gis;
    209             }
    210             for $t (@HtmlSingle) {
    211             s!\<$t(\s[^<>]+?)?\>!<$t$1>!gi;
    212             }
    213              
    214             # The quote markup patterns avoid overlapping tags (with 5 quotes)
    215             # by matching the inner quotes for the strong pattern.
    216             s/!!(.*?)!!/$1<\/code>/g; #'
    217             s/('*)'''(.*?)'''/$1$2<\/strong>/g; #'
    218             s/''(.*?)''/$1<\/em>/g;
    219             s/(^|\n)\s*(\=+)\s+([^\n]+)\s+\=+/WikiHeading($1, $2, $3)/geo;
    220              
    221             s!\<br\>!
    !g;
    222             s!----+!
    !g;
    223             s!====+!
    !g;
    224             }
    225              
    226             if($parse > 0) {
    227             s!$SitePattern!WikiLink($1,$2)!eg;
    228             s!$LinkPattern!WikiLink($1,$1)!eg;
    229              
    230             s!$UriPattern!$2!g;
    231             s!$UrlPattern!$1!g;
    232              
    233             s!$SendPattern!$2!g;
    234             s!$MailPattern!$1!g;
    235             }
    236              
    237             return $_;
    238             }
    239              
    240             sub WikiLink {
    241             my ($page,$name) = @_;
    242              
    243             if($page =~ /cpan:~(.*)/) {
    244             $page =~ s!cpan:~!!;
    245             $name =~ s!cpan:~!!;
    246             return qq!$name!;
    247             } elsif($page =~ /cpan:(.*)/) {
    248             $page =~ s!cpan:!!;
    249             $name =~ s!cpan:!!;
    250             return qq!$name!;
    251             } elsif($page =~ /perldoc:(.*)/) {
    252             $page =~ s!perldoc:!!;
    253             $name =~ s!perldoc:!!;
    254             return qq!$name!;
    255             } elsif($page =~ /user:(\d+|[\w ]+)/) {
    256             $name = undef if($page eq $name);
    257             return _mapuser($1,$name);
    258             } elsif($page =~ /image:(\d+)/) {
    259             return _mapimage(id => $1);
    260             } elsif($page =~ /image:(.*)/) {
    261             return _mapimage(name => $1);
    262             } elsif($page =~ /media:(\d+)/) {
    263             return _mapmedia(id => $1);
    264             } elsif($page =~ /media:(.*)/) {
    265             return _mapmedia(name => $1);
    266             }
    267              
    268             $wiki_links{$page} ||= do {
    269             my @rows = $dbi->GetQuery('hash','CheckWikiPage',$page);
    270             @rows ? 1 : 0;
    271             };
    272              
    273             if($wiki_links{$page}) {
    274             return qq!$name!
    275             }
    276              
    277             return qq!$name?!
    278             }
    279              
    280             sub WikiHeading {
    281             my ($pre, $depth, $text) = @_;
    282              
    283             $depth = length($depth) - 1;
    284             $depth = 6 if ($depth > 6);
    285             return $pre . "$text\n";
    286             }
    287              
    288             # -------------------------------------
    289             # Private Methods
    290              
    291             sub _mapuser {
    292             my $id = shift;
    293             my $nm = $id;
    294              
    295             if($id =~ /^\d+$/) {
    296             $nm = UserName($id);
    297             } else {
    298             $id = UserID($id);
    299             }
    300              
    301             return qq!$nm!;
    302             }
    303              
    304             sub _mapimage {
    305             my %hash = @_;
    306             my @rows;
    307              
    308             if($hash{id}) {
    309             @rows = $dbi->GetQuery('hash','GetImageByID',$hash{id});
    310             } else {
    311             @rows = $dbi->GetQuery('hash','GetImageByName',$hash{id});
    312             }
    313              
    314             return unless(@rows);
    315             return qq!$rows[0]->{tag}!;
    316             }
    317              
    318             sub _mapmedia {
    319             my %hash = @_;
    320             my @rows;
    321              
    322             if($hash{id}) {
    323             @rows = $dbi->GetQuery('hash','GetImageByID',$hash{id});
    324             } else {
    325             @rows = $dbi->GetQuery('hash','GetImageByName',$hash{id});
    326             }
    327              
    328             return unless(@rows);
    329             return qq!$rows[0]->{tag}!;
    330             }
    331              
    332             1;
    333              
    334             __END__