# Copyright (C) 2014 Alex Schroeder };
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:
# ... ...✎
)$!!) { # $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; } } }