# Copyright (C) 2014 Alex Schroeder # 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 . use strict; use v5.10; AddModuleDescription('edit-paragraphs.pl', 'Edit Paragraphs Extension'); our ($q, $OpenPageName, $Fragment, %Page, %Action, @MyRules, $LastUpdate); # edit icon # http://publicdomainvectors.org/en/free-clipart/Pencil-vector-icon/9221.html # q{edit}; our $EditParagraphPencil = '✎'; # Allow editing of substrings $Action{'edit-paragraph'} = \&DoEditParagraph; sub DoEditParagraph { my $id = UnquoteHtml(GetParam('title', '')); UserCanEditOrDie($id); my $old = UnquoteHtml(GetParam('paragraph', '')); $old =~ s/\r//g; return DoEdit($id) unless $old; my $around = GetParam('around', undef); # Find text to edit my $new = GetParam('text', ''); OpenPage($id); if ($new) { my $myoldtime = GetParam('oldtime', ''); # maybe empty! my $text; if ($myoldtime and $myoldtime != $LastUpdate) { ($text) = GetTextAtTime($myoldtime); } else { $text = $Page{text}; } my $done; if ($around) { # The tricky part is that the numbers refer to the HTML quoted text. What a pain. my $qold = QuoteHtml($old); my $qtext = QuoteHtml($text); if (substr($qtext, $around - length($qold), length($qold)) eq $qold) { $text = UnquoteHtml(substr($qtext, 0, $around - length($qold)) . QuoteHtml($new) . substr($qtext, $around)); $done = 1; } } else { # simple case, just do it my $search_term = quotemeta($old); $done = $text =~ s/$search_term/$new/; } if ($done) { SetParam('text', UnquoteHtml($text)); return DoPost($id); } else { $text = substr($text, 0, $around) . "\n### around here ###\n" . substr($text, $around) if $around; ReportError(T('Could not identify the paragraph you were editing'), '500 INTERNAL SERVER ERROR', undef, $q->p(T('This is the section you edited:')) . $q->pre(QuoteHtml($old)) . $q->p(T('This is the current page:')) . $q->pre($text)); } } print GetHeader('', Ts('Editing %s', NormalToFree($id))); print $q->start_div({-class=>'content edit paragraph'}); my $form = GetEditForm($id, undef, $old); my $param = GetHiddenValue('paragraph', $old); $param .= GetHiddenValue('action', 'edit-paragraph'); # add action $param .= GetHiddenValue('around', $around); # add around position $form =~ s!!$param!; print $form; print $q->end_div(); PrintFooter($id, 'edit'); } # When PrintWikiToHTML is called for the current revision of a page we # initialize our data structure. The data structure simply divides the # page up into blocks based on what one would like to edit. By # default, that's just paragraph breaks and list items. When using # Creole, ordered list items and table rows are added. my @EditParagraphs = (); *EditParagraphOldPrintWikiToHTML = \&PrintWikiToHTML; *PrintWikiToHTML = \&EditParagraphNewPrintWikiToHTML; sub EditParagraphNewPrintWikiToHTML { my ($text, $is_saving_cache, $revision, $is_locked) = @_; # We need to use quoted HTML because that's what the rules will applied to! my $quoted_text = QuoteHtml($text); if ($quoted_text and not $revision) { my ($start, $end) = (0, 0); # This grouping with zero-width positive look-ahead assertion makes sure that this chunk of text does not include # markup need for the next chunk of text. my $regexp; if (grep { $_ eq \&CreoleRule } @MyRules) { $regexp = "\n+(\n|(?=[*#-=|]))"; } else { $regexp = "\n+(\n|(?=[*]))"; } while ($quoted_text =~ /$regexp/g) { $end = pos($quoted_text); push(@EditParagraphs, [$start, $end, substr($quoted_text, $start, $end - $start)]); $start = $end; } # Only do this if we have at least two paragraphs and the end isn't just some empty lines. if (@EditParagraphs and $start and $start < length($quoted_text)) { push(@EditParagraphs, [$start, length($quoted_text), substr($quoted_text, $start)]); } } # warn join('', '', map { $_->[0] . "-" . $_->[1] .": " . $_->[2]; } @EditParagraphs); return EditParagraphOldPrintWikiToHTML(@_); } # Whenever an important element is closed, we try to add a link. *EditParagraphOldCloseHtmlEnvironments = \&CloseHtmlEnvironments; *CloseHtmlEnvironments = \&EditParagraphNewCloseHtmlEnvironments; sub EditParagraphNewCloseHtmlEnvironments { EditParagraph(); return EditParagraphOldCloseHtmlEnvironments(@_); } *EditParagraphOldCloseHtmlEnvironmentUntil = \&CloseHtmlEnvironmentUntil; *CloseHtmlEnvironmentUntil = \&EditParagraphNewCloseHtmlEnvironmentUntil; sub EditParagraphNewCloseHtmlEnvironmentUntil { my $tag = $_[0]; if ($tag =~ /^(p|li|table|h[1-6])$/i) { EditParagraph(); } return EditParagraphOldCloseHtmlEnvironmentUntil(@_); } sub EditParagraph { my $text; my $pos = pos; # pos is empty for the last link if (@EditParagraphs) { if ($pos) { while (@EditParagraphs and $EditParagraphs[0]->[1] <= $pos) { $pos = $EditParagraphs[0]->[1]; # just in case we're overshooting $text .= $EditParagraphs[0]->[2]; shift(@EditParagraphs); } } else { # the last one $text = $EditParagraphs[-1]->[2]; } } if ($text) { # Huge Hack Alert: We are appending to $Fragment, which is what Clean appends to. We do this so that we can handle # headers and other block elements. Without this fix we'd see something like this: #

...

# Usually this would look as follows: #

...

# This is eliminated in Dirty. But it won't be eliminated if we leave the link in there. What we want is this: #

...

# # The same issue arises for other block level elements. What happens at the end of a table? Without this fix we'd # see something like this: #
...

# What we want, I guess, is this: #
...

$pos = $pos || length(QuoteHtml($Page{text})); # make sure we have an around value my $title = UrlEncode($OpenPageName); my $paragraph = UrlEncode(UnquoteHtml($text)); my $link = ScriptLink("action=edit-paragraph;title=$title;around=$pos;paragraph=$paragraph", $EditParagraphPencil, 'pencil'); if ($Fragment =~ s!((:?||)

)$!!) { # $Fragment .= ''; $Fragment .= $link . $1; } elsif ($Fragment =~ s!(

\s*)$!!) { # $Fragment .= ''; # Since anything can appear in raw HTML tags, there is no one-size fits all rule. # I usually use the tags to embed forms, and forms need to contain a

. # so that's what I'm handling. $Fragment .= $link . $1; } elsif ($pos and $Fragment =~ /<(p|tr)>$/) { # Do nothing: this is either an empty paragraph and will be # eliminated, or an empty row which will not be shown. # $Fragment .= ''; } else { # This is the default: add the link. # $Fragment .= ''; $Fragment .= $link; } } }