Compare commits

..

1 Commits

Author SHA1 Message Date
Alex Jakimenko
4f9e243036 Initial changes for 'use warnings' 2014-08-29 05:27:59 +03:00
78 changed files with 182 additions and 439 deletions

View File

@@ -112,7 +112,7 @@ USERNAME, your optional username to provide. It defaults to
;;; Variables
(defvar oddmuse-get-command
"curl --silent %w --form action=browse --form raw=2 --form id='%t'"
"curl --silent %w --form action=browse --form raw=2 --form id=%t"
"Command to use for publishing pages.
It must print the page to stdout.
@@ -254,9 +254,6 @@ Example:
((\"Alex\" ((\"Contact\" . \"58\"))))")
(defvar oddmuse-revision nil
"A variable to bind dynamically when calling `oddmuse-format-command'.")
(defun oddmuse-revision-put (wiki page rev)
"Store REV for WIKI and PAGE in `oddmuse-revisions'."
(let ((w (assoc wiki oddmuse-revisions)))
@@ -288,10 +285,10 @@ Example:
(file-name-directory file))))
(defmacro with-oddmuse-file (file &rest body)
"Bind `wiki' and `pagename' based on FILE and execute BODY."
(declare (debug (symbolp &rest form)))
`(let ((pagename (oddmuse-page-name ,file))
(wiki (oddmuse-wiki ,file)))
"Bind `oddmuse-wiki' and `oddmuse-page-name' based on FILE
and execute BODY."
`(let ((oddmuse-page-name (oddmuse-page-name ,file))
(oddmuse-wiki (oddmuse-wiki ,file)))
,@body))
(put 'with-oddmuse-file 'lisp-indent-function 1)
@@ -410,7 +407,6 @@ It's either a [[free link]] or a WikiWord based on
%p `oddmuse-password'
%q `question' as provided by `oddmuse-wikis'
%o `oddmuse-ts'
%v `oddmuse-revision'
%r `regexp' as provided by the user"
(dolist (pair '(("%w" . url)
("%t" . pagename)
@@ -420,7 +416,6 @@ It's either a [[free link]] or a WikiWord based on
("%p" . oddmuse-password)
("%q" . question)
("%o" . oddmuse-ts)
("%v" . oddmuse-revision)
("%r" . regexp)))
(let* ((key (car pair))
(sym (cdr pair))
@@ -482,13 +477,15 @@ well."
(message "%s using %s..." mesg command)
(when (numberp expected-code)
(setq expected-code (number-to-string expected-code)))
(if send-buffer
(shell-command-on-region (point-min) (point-max) command buf)
(shell-command command buf))
(let ((status (with-current-buffer buf (buffer-string))))
(cond ((and send-buffer
expected-code
(not (string= expected-code status)))
;; If SEND-BUFFER, the resulting HTTP CODE is found in BUF, so check
;; that, too.
(let* ((errno (if send-buffer
(shell-command-on-region (point-min) (point-max) command buf)
(shell-command command buf)))
(status (with-current-buffer buf (buffer-string))))
(cond ((not (zerop errno))
(error "Error %s: non-zero return value" mesg))
((and send-buffer expected-code (not (string= expected-code status)))
(error "Error %s: HTTP Status Code %s" mesg status))
((string-match "<title>Error</title>" status)
(if (string-match "<h1>\\(.*\\)</h1>" status)
@@ -904,10 +901,9 @@ node as returned by `libxml-parse-html-region' or
(defun oddmuse-search (regexp)
"Search the wiki for REGEXP.
REGEXP must be a regular expression understood by the
wiki (ie. it must use Perl syntax).
Use a prefix argument to search a different wiki."
wiki (ie. it must use Perl syntax)."
(interactive "sSearch term: ")
(let* ((wiki (or (and (not current-prefix-arg) oddmuse-wiki)
(let* ((wiki (or oddmuse-wiki
(completing-read "Wiki: " oddmuse-wikis nil t)))
(name (concat "*" wiki ": search for '" regexp "'*")))
(if (and (get-buffer name)
@@ -925,10 +921,9 @@ Use a prefix argument to search a different wiki."
(defun oddmuse-match (regexp)
"Search the wiki for page names matching REGEXP.
REGEXP must be a regular expression understood by the
wiki (ie. it must use Perl syntax).
Use a prefix argument to search a different wiki."
wiki (ie. it must use Perl syntax)."
(interactive "sPages matching: ")
(let* ((wiki (or (and (not current-prefix-arg) oddmuse-wiki)
(let* ((wiki (or oddmuse-wiki
(completing-read "Wiki: " oddmuse-wikis nil t)))
(name (concat "*" wiki ": matches for '" regexp "'*")))
(if (and (get-buffer name)

View File

@@ -94,19 +94,14 @@ See `oddmuse-format-command' for the formatting options.")
(error "This is not supported."))
(defvar vc-oddmuse-get-revision-command
(concat "curl --silent"
" --form action=browse"
" --form id=%t"
" --form revision=%v"
" --form raw=1"
" '%w'")
"curl --silent %w\"?action=browse;id=%t;revision=%o;raw=1\""
"Command to use to get older revisions of a page.
It must print the page to stdout.
%? '?' character
%w URL of the wiki as provided by `oddmuse-wikis'
%t Page title as provided by `oddmuse-page-name'
%v Revision to retrieve as provided by `oddmuse-revision'")
%o Revision to retrieve as provided by `oddmuse-revision'")
(defun oddmuse-revision-filename (rev)
"Return filename for revision REV.
@@ -122,17 +117,14 @@ This uses `oddmuse-directory', `oddmuse-wiki' and
(setq buffer (or buffer (get-buffer-create "*vc-diff*")))
(dolist (file files)
(with-oddmuse-file file
(setq rev1 (or rev1 (oddmuse-get-latest-revision wiki pagename)))
(setq rev1 (or rev1 (oddmuse-get-latest-revision)))
(dolist (rev (list rev1 rev2))
(when (and rev (not (file-readable-p (oddmuse-revision-filename rev))))
(let* ((oddmuse-revision rev)
(command (oddmuse-format-command
vc-oddmuse-get-revision-command))
(command (oddmuse-format-command vc-oddmuse-get-revision-command))
(filename (oddmuse-revision-filename rev)))
(with-temp-buffer
(oddmuse-run
(concat "Downloading revision " rev)
command wiki)
(oddmuse-run (concat "Downloading revision " rev) command)
(write-file filename)))))
(diff-no-select
(if rev1 (oddmuse-revision-filename rev1) file)
@@ -164,6 +156,6 @@ used as a check-in comment."
(buf (get-buffer-create " *oddmuse-response*")))
(with-temp-buffer
(insert-file-contents file)
(oddmuse-run "Posting" command wiki pagename buf t 302))))))
(oddmuse-run "Posting" command nil nil buf t 302))))))
(provide 'vc-oddmuse)

View File

@@ -197,17 +197,9 @@ hr {
div.footer hr {
height:4px;
margin: 2em 0 1ex 0;
clear:both;
}
div.content > div.comment {
border-top: none;
padding-top: none;
border-left: 1ex solid #bbb;
padding-left: 1ex;
}
div.wrapper > div.comment {
div.comment {
border-top: 2px solid #000;
padding-top: 2em;
}
@@ -218,13 +210,12 @@ pre {
margin-right: 2em;
white-space: pre;
overflow:hidden;
white-space: pre-wrap; /* CSS 3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
tt, pre, code {
font-size: 80%;
};
div.footer hr {
clear:both;
}

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2004, 2006, 2008, 2014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2004, 2006, 2008 Alex Schroeder <alex@gnu.org>
#
# 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
@@ -140,11 +140,11 @@ sub DoAtomSave {
my $title = $entry->title();
my $author = $entry->author();
SetParam('username', $author->name) if $author; # Used in Save()
my $id = FreeToNormal($title);
UserCanEditOrDie($id);
my $id = FreeToNormal($title) if ValidIdOrDie($title);
$oldid = $id unless $oldid;
ValidIdOrDie($oldid);
my $summary = $entry->summary();
ReportError(Ts('Editing not allowed for %s.', $id), '403 FORBIDDEN') unless UserCanEdit($id, 1);
# Lock before getting old page to prevent races
RequestLockOrError(); # fatal
OpenPage($oldid);

View File

@@ -40,8 +40,8 @@ $PrintTOCAnchor = 0;
%ClusterMap = ();
*OldPrintRcHtml = *PrintRcHtml;
*PrintRcHtml = *ClusterMapPrintRcHtml;
*OldDoRc = *DoRc;
*DoRc = *ClusterMapDoRc;
push(@MyAdminCode, \&ClusterMapAdminRule);
@@ -178,7 +178,7 @@ sub CreateClusterMap {
}
}
sub ClusterMapPrintRcHtml {
sub ClusterMapDoRc {
my ( @options ) = @_;
my $page = "";
my $cluster = GetParam(rcclusteronly);
@@ -195,7 +195,7 @@ sub ClusterMapPrintRcHtml {
print "</ul>";
}
OldPrintRcHtml(@options);
OldDoRc(@options);
}
sub PrintUnclusteredMap {

View File

@@ -25,14 +25,14 @@ sub CommentDivWrapper {
if (substr($OpenPageName, 0, length($CommentsPrefix)) eq $CommentsPrefix) {
if (pos == 0 and not $CommentDiv) {
$CommentDiv = 1;
return $q->start_div({-class=>'userComment'});
return '<div class="userComment">';
}
}
if ($OpenPageName =~ /$CommentsPattern/o) {
if ($bol and m/\G(\s*\n)*----+[ \t]*\n?/cg) {
my $html = CloseHtmlEnvironments()
. ($CommentDiv++ > 0 ? $q->end_div() : $q->h2({-class=>'commentsHeading'}, T('Comments:'))) . $q->start_div({-class=>'userComment'})
. AddHtmlEnvironment('p');
. ($CommentDiv++ > 0 ? '</div>' : '<h2 id="commentsHeading">' . T('Comments:') . '</h2>') . '<div class="userComment">'
. AddHtmlEnvironment('p');
return $html;
}
}
@@ -46,8 +46,8 @@ sub CommentDivWrapper {
sub NewCommentDivApplyRules {
my ($blocks, $flags) = OldCommentDivApplyRules(@_);
if ($CommentDiv) {
print $q->end_div();
$blocks .= $FS . $q->end_div();
print '</div>';
$blocks .= $FS . '</div>';
$flags .= $FS . 0;
$CommentDiv = 0;
}

View File

@@ -242,7 +242,6 @@ sub CreoleRule {
-class=> 'image outside'},
$q->img({-src=> UnquoteHtml($1),
-alt=> UnquoteHtml($3),
-title=> UnquoteHtml($3),
-class=> 'url outside'})));
}
# image link: [[link|{{pic}}]] and [[link|{{pic|text}}]]
@@ -253,7 +252,6 @@ sub CreoleRule {
ScriptLink(UrlEncode(FreeToNormal($2)),
$q->img({-src=> GetDownloadLink(FreeToNormal($3), 2),
-alt=> UnquoteHtml($text),
-title=> UnquoteHtml($text),
-class=> 'upload'}), 'image')), $text);
}
# image link: [[link|{{url}}]] and [[link|{{url|text}}]]
@@ -264,7 +262,6 @@ sub CreoleRule {
ScriptLink(UrlEncode(FreeToNormal($2)),
$q->img({-src=> UnquoteHtml($3),
-alt=> UnquoteHtml($text),
-title=> UnquoteHtml($text),
-class=> 'url outside'}), 'image')), $text);
}
# image link: [[url|{{pic}}]] and [[url|{{pic|text}}]]
@@ -275,7 +272,6 @@ sub CreoleRule {
$q->a({-href=> UnquoteHtml($2), -class=> 'image outside'},
$q->img({-src=> GetDownloadLink(FreeToNormal($3), 2),
-alt=> UnquoteHtml($text),
-title=> UnquoteHtml($text),
-class=> 'upload'}))), $text);
}
# image link: [[url|{{url}}]] and [[url|{{url|text}}]]
@@ -285,7 +281,6 @@ sub CreoleRule {
$q->a({-href=> UnquoteHtml($1), -class=> 'image outside'},
$q->img({-src=> UnquoteHtml($2),
-alt=> UnquoteHtml($4),
-title=> UnquoteHtml($4),
-class=> 'url outside'})));
}
# link: [[url]] and [[url|text]]

View File

@@ -1,35 +0,0 @@
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
package OddMuse;
AddModuleDescription('div-foo.pl', 'Div Foo Extension');
push(@MyRules, \&DivFooRule);
sub DivFooRule {
if (m/\G\&lt;([\w ]+)\&gt;\s*\n/cg) {
return CloseHtmlEnvironment('p') . AddHtmlEnvironment('div', qq{class="$1"});
}
if (m/\G\&lt;([\w ]+)\&gt;/cg) {
return AddHtmlEnvironment('span', qq{class="$1"});
}
if (m/\G\&lt;\/\/\&gt;/cg) {
return CloseHtmlEnvironment('div') . (InElement('div') ? '' : AddHtmlEnvironment('p'));
}
if (m/\G\&lt;\/\&gt;/cg) {
return CloseHtmlEnvironment('span');
}
return undef;
}

View File

@@ -28,7 +28,7 @@ push(@MyRules, \&ImageSupportRule);
sub ImageSupportRule {
my $result = undef;
if (m!\G\[\[image((/[a-z]+)*)( external)?:\s*([^]|]+?)\s*(\|[^]|]+?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*\]\](\{([^}]+)\})?!gc) {
if (m!\G\[\[image((/[a-z]+)*)( external)?:\s*([^]|]+?)\s*(\|[^]|]+?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*\]\]!gc) {
my $oldpos = pos;
my $class = 'image' . $1;
my $external = $3;
@@ -42,7 +42,6 @@ sub ImageSupportRule {
my $link = $6 ? substr($6, 1) : '';
my $caption = $7 ? substr($7, 1) : '';
my $reference = $8 ? substr($8, 1) : '';
my $comments = $10;
my $id = FreeToNormal($name);
$class =~ s!/! !g;
my $linkclass = $class;
@@ -67,16 +66,6 @@ sub ImageSupportRule {
if ($found) {
$result = $q->img({-src=>$src, -alt=>$alt, -title=>$alt, -class=>'upload'});
$result = $q->a({-href=>$link, -class=>$linkclass}, $result);
if ($comments) {
for (split '\n', $comments) {
my $valRegex = qr/(([0-9.]+[a-z]*%?)\s+)/;
if ($_ =~ /^\s*(([a-zA-Z ]+)\/)?$valRegex$valRegex$valRegex$valRegex(.*)$/) { # can't use {4} here? :(
my $commentClass = $2 ? "imagecomment $2" : 'imagecomment';
$result .= $q->div({-class=>$commentClass, -style=>"position: absolute; top: $6; left: $4; width: $8; height: $10"}, QuoteHtml($11));
}
}
$result = CloseHtmlEnvironments() . $q->div({-class=>"imageholder", -style=>"position: relative"}, $result);
}
} else {
$result = GetDownloadLink($src, 1, undef, $alt);
}

View File

@@ -366,7 +366,7 @@ sub JoinerDoRegister {
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
print $q->table($table);
print JoinerGetQuestion();
print $q->end_form;
print $q->endform;
print $q->end_div();
PrintFooter();
@@ -530,7 +530,7 @@ sub JoinerDoLogin {
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
print $q->table($table);
print JoinerGetQuestion();
print $q->end_form;
print $q->endform;
print $q->start_p();
print ScriptLink('action=joiner_forgot_password', T('Forgot your password?'));
@@ -705,7 +705,7 @@ sub JoinerDoChangePassword {
$q->td($q->password_field(-name=>'joiner_repeat_new_password', -id=>'joiner_repeat_new_password')));
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
print $q->table($table);
print $q->end_form;
print $q->endform;
print $q->end_div();
PrintFooter();
@@ -790,7 +790,7 @@ sub JoinerDoForgotPassword {
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
print $q->table($table);
print JoinerGetQuestion();
print $q->end_form;
print $q->endform;
print $q->end_div();
PrintFooter();
@@ -891,7 +891,7 @@ sub JoinerDoChangeEmail {
$q->td($q->password_field(-name=>'joiner_password', -id=>'joiner_password')));
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Submit', -value=>T('Submit'))));
print $q->table($table);
print $q->end_form;
print $q->endform;
print $q->end_div();
PrintFooter();
@@ -1082,7 +1082,7 @@ sub JoinerDoBan {
$q->td($q->textfield(-name=>'joiner_username', -id=>'joiner_username')));
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Ban', -value=>T('Ban'))));
print $q->table($table);
print $q->end_form;
print $q->endform;
print $q->start_p();
print T('Enter username of the account to unban:');
@@ -1096,7 +1096,7 @@ sub JoinerDoBan {
$q->td($q->textfield(-name=>'joiner_username', -id=>'joiner_username')));
$table .= $q->Tr($q->td(), $q->td($q->submit(-name=>'Unban', -value=>T('Unban'))));
print $q->table($table);
print $q->end_form;
print $q->endform;
print $q->end_div();
PrintFooter();

View File

@@ -247,7 +247,7 @@ sub DoMailSubscriptions {
print $q->p(ScriptLink('action=subscriptions;mail=', T('Change email address'),
'change subscriptions'));
}
print $q->end_form(), $q->end_div();
print $q->endform(), $q->end_div();
PrintFooter();
}

View File

@@ -1,121 +0,0 @@
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
package OddMuse;
use File::Basename;
use File::Copy;
AddModuleDescription('module-bisect.pl', 'Bisect Extension');
$Action{bisect} = \&BisectAction;
sub BisectAction {
UserIsAdminOrError();
RequestLockOrError();
print GetHeader('', T('Module Bisect'), '');
if (GetParam('stop')) {
BisectEnableAll(1);
print $q->br(), $q->strong(T('All modules enabled now!'));
} elsif (GetParam('good') or GetParam('bad')) {
BisectProcess(GetParam('good'));
} else {
print GetFormStart(undef, 'get', 'bisect');
print GetHiddenValue('action', 'bisect');
my @disabledFiles = bsd_glob("$ModuleDir/*.p[ml].disabled");
if (@disabledFiles == 0) {
print 'Test / Always enabled / Always disabled', $q->br();
my @files = bsd_glob("$ModuleDir/*.p[ml]");
for (my $i = 0; $i < @files; $i++) {
my $moduleName = fileparse($files[$i]);
my @disabled = ($moduleName eq 'module-bisect.pl' ? (-disabled=>'disabled') : ());
print $q->input({-type=>'radio', -name=>"m$i", -value=>'t', ($moduleName ne 'module-bisect.pl' ? (-checked=>'checked') : ()), @disabled});
print $q->input({-type=>'radio', -name=>"m$i", -value=>'on', ($moduleName eq 'module-bisect.pl' ? (-checked=>'checked') : ())});
print $q->input({-type=>'radio', -name=>"m$i", -value=>'off', @disabled});
print $moduleName, $q->br();
}
print $q->submit(-name=>'bad', -value=>T('Start'));
} else {
print T('Biscecting proccess is already active.'), $q->br();
print $q->submit(-name=>'stop', -value=>T('Stop'));
}
print $q->end_form();
}
PrintFooter();
ReleaseLock();
}
sub BisectProcess {
my ($isGood) = @_;
my $parameterHandover = '';
BisectEnableAll();
my @files = bsd_glob("$ModuleDir/*.p[ml]");
for (my $i = @files - 1; $i >= 0; $i--) { # handle user choices
if (GetParam("m$i") eq 'on') {
$parameterHandover .= GetHiddenValue("m$i", GetParam("m$i"));
splice @files, $i, 1;
} elsif (GetParam("m$i") eq 'off') {
$parameterHandover .= GetHiddenValue("m$i", GetParam("m$i"));
move($files[$i], $files[$i] . '.disabled');
splice @files, $i, 1;
}
}
my $start = GetParam('start') - 1; # $start and $end are indexes
my $end = GetParam('end') - 1;
$start = 0 if $start < 0; # not specified (probably right after Start)
$end = @files * 2 - 1 if $end < 0;
if ($end - $start <= 1) {
print 'It seems like ', $q->strong((fileparse($isGood ? $files[$end] : $files[$start]))[0]), ' is causing your problem.';
print GetFormStart(undef, 'get', 'bisect');
print GetHiddenValue('action', 'bisect');
print $q->submit(-name=>'stop', -value=>T('Stop'));
print $q->end_form();
return;
}
print T('Module count (only testable modules): '), $q->strong(scalar @files), $q->br();
print $q->br(), T('Current module statuses:'), $q->br();
my $halfsize = ($end - $start + 1) / 2.0; # + 1 because it is count
$end -= int($halfsize) unless $isGood;
$start += int($halfsize + 0.51) if $isGood; # ceil
$halfsize = ($end - $start + 1) / 2.0;
for (my $i = 0; $i < @files; $i++) {
if ($i >= $start and $i <= $end - int($halfsize)) {
print $q->strong('> + '), (fileparse($files[$i]))[0], $q->br();
} elsif ($i >= $start and $i <= $end) {
print $q->strong('> - '), (fileparse($files[$i]))[0], $q->br();
move($files[$i], $files[$i] . '.disabled');
} else {
print $q->strong('- '), (fileparse($files[$i]))[0], $q->br();
move($files[$i], $files[$i] . '.disabled');
}
}
print GetFormStart(undef, 'get', 'bisect');
print GetHiddenValue('action', 'bisect');
print GetHiddenValue('start', $start + 1);
print GetHiddenValue('end', $end + 1);
print $parameterHandover;
print $q->submit(-name=>'good', -value=>T('Good')), ' ';
print $q->submit(-name=>'bad', -value=>T('Bad')), ' ';
print $q->submit(-name=>'stop', -value=>T('Stop'));
print $q->end_form();
}
sub BisectEnableAll {
for (bsd_glob("$ModuleDir/*.p[ml].disabled")) { # reenable all modules
my $oldName = $_;
s/\.disabled$//;
print "Enabling ", (fileparse($_))[0], '...', $q->br() if $_[0];
move($oldName, $_);
}
}

View File

@@ -34,75 +34,51 @@ sub ModuleUpdaterAction {
return unless UserIsAdminOrError();
RequestLockOrError();
print GetHeader('', T('Module Updater'), '');
if (GetParam('ok')) {
ModuleUpdaterApply();
} else {
unlink bsd_glob("$TempDir/*.p[ml]"); # XXX is it correct to use $TempDir for such stuff? What if something else puts .pm files there?
for (bsd_glob("$ModuleDir/*.p[ml]")) {
my $curModule = fileparse($_);
ProcessModule($curModule);
}
print $q->br();
print GetFormStart(undef, 'get');
print GetHiddenValue('action', 'updatemodules');
print $q->submit(-name=>'ok', -value=>T('Looks good. Update modules now!'));
print $q->end_form();
for (bsd_glob("$ModuleDir/*.p[ml]")) {
my $curModule = fileparse($_);
ProcessModule($curModule);
}
print '<strong>Done!</strong>';
PrintFooter();
ReleaseLock();
}
sub ModuleUpdaterApply {
for (bsd_glob("$TempDir/*.p[ml]")) {
my $moduleName = fileparse($_);
if (move($_, "$ModuleDir/$moduleName")) {
print $q->strong("Module $moduleName updated successfully!"), $q->br();
} else {
print $q->strong("Unable to replace module $moduleName: $!"), $q->br();
}
}
unlink bsd_glob("$TempDir/*.p[ml]"); # XXX same as above
print $q->br(), $q->strong('Done!');
}
sub ProcessModule() {
my $module = shift;
CreateDir($TempDir);
print $q->hr();
print $q->strong("Diffing $module ..."), $q->br();
my $moduleData = GetRaw("$OddmuseModulesUrl/$module");
if (not $moduleData) {
print $q->strong('There was an error downloading this module.'
. ' If this is your own module, please contribute it to Oddmuse!'), $q->br();
print "<hr/>";
print "<strong>Updating $module ...</strong><br/>";
if (system('wget', '-O', "$TempDir/newmodule", '--', "$OddmuseModulesUrl/$module") != 0) {
if ($? >> 8 == 8) { # wget usually returns 8 if server response is NOT FOUND
# TODO maybe there is any better way to do this?
print '<strong>There is no such module in git repository. If this is your own module, please contribute it to Oddmuse! If it is not, then it was probably removed.</strong><br/>';
return;
}
print 'There was an error downloading this module.<br/>';
return;
}
open my $fh, ">", "$TempDir/$module" or die("Could not open file. $!");
print $fh $moduleData;
close $fh;
my $diff = DoModuleDiff("$ModuleDir/$module", "$TempDir/$module");
my $diff = DoModuleDiff("$ModuleDir/$module", "$TempDir/newmodule");
if (not $diff) {
print $q->strong('This module is up to date, there is no need to update it.'), $q->br();
unlink "$TempDir/$module";
print '<strong>This module is up to date, there is no need to update it.</strong><br/>';
return;
}
print $q->strong('There is a newer version of this module. Here is a diff:'), $q->br();
print '<strong>There is a newer version of this module. Here is a diff:</strong><br/>';
$diff = QuoteHtml($diff);
$diff =~ tr/\r//d; # TODO is this required? # probably not # but maybe it is there to fix problems with dos newlines?
$diff =~ tr/\r//d; # TODO is this required? # probably not
for (split /\n/, $diff) {
my ($type) = /(.)/;
if ($type =~ /[+-]/) {
my $class = $type eq '+' ? 'updaternew' : 'updaterold';
print $q->span({-class => $class}, $q->code($_));
} else {
print $q->span($q->code($_));
if ($type eq '+') {
print '<span class="updaternew">';
} elsif ($type eq '-') {
print '<span class="updaterold">';
}
print $q->br();
print '<code>' . $_ . '</code>';
print '</span>' if $type =~ /[+-]/;
print '<br/>';
}
move("$TempDir/newmodule", "$ModuleDir/$module") or print "<strong>Unable to replace module: $! </strong><br/>";
print '<strong>Module updated successfully!</strong><br/>';
}
sub DoModuleDiff {

View File

@@ -167,8 +167,7 @@ anchors.
*DeletePage = *NewPermanentAnchorsDeletePage;
sub NewPermanentAnchorsDeletePage {
my $status = OldPermanentAnchorsDeletePage(@_);
return $status if $status; # this would be the error message
OldPermanentAnchorsDeletePage(@_);
DeletePermanentAnchors(@_); # the only parameter is $id
}

View File

@@ -546,7 +546,7 @@ sub NewSearchFreePrintFooter {
$q->p(GetHiddenValue('id', $id), GetHiddenValue('action', 'retag'),
T('Tags:'), $q->br(), GetTextArea('tags', join(' ', @tags), 2),
$q->br(), $q->submit(-name=>'Save', -value=>T('Save'))),
$q->end_form());
$q->endform());
} elsif ($id and @tags) {
print $q->div({-class=>'tags'},
$q->p(T('Tags:'), map { $_ = "\[\[tag:$_\]\]";

View File

@@ -368,7 +368,7 @@ sub StaticGetCommentForm {
-override=>1, -size=>40, -maxlength=>100)),
$q->p($q->submit(-name=>'Save', -accesskey=>T('s'), -value=>T('Save')), ' ',
$q->submit(-name=>'Preview', -value=>T('Preview'))),
$q->end_form());
$q->endform());
}
return '';
}

View File

@@ -111,7 +111,7 @@ sub ThreadAdd {
. '</td></tr></table>'
. '<p>'
. $q->p($q->submit(-name=>'Save', -value=>T('Save')))
. $q->end_form());
. $q->endform());
print $q->end_html;
} else {
my ($page, $thread) = ThreadExtract($id);

View File

@@ -75,6 +75,6 @@ sub DoTZ {
-values=>\@names,
-default=>GetParam('time', $defaultTZ)),
$q->submit('dotz', T('Set')));
print $q->end_form . $q->end_div();
print $q->endform . $q->end_div();
PrintFooter();
}

View File

@@ -270,7 +270,7 @@ sub GetTocHtml {
# By Usemod convention, all headers begin with depth 2. This algorithm,
# however, expects headers to begin with depth 1. Thus, to "streamline"
# things, we transform it appropriately. ;-)
$header_depth-- if defined &UsemodRule or defined &CreoleRule;
if (defined &UsemodRule) { $header_depth--; }
# If this is the first header and if this header's depth is deeper than 1,
# we manually clamp this header's depth to 1 so as to ensure the first list
@@ -280,7 +280,7 @@ sub GetTocHtml {
# Close ordered lists and list items for prior headings deeper than this
# heading's depth.
while ($list_depth > $header_depth and $list_depth != 1) {
while ($list_depth > $header_depth) {
$list_depth--;
$toc_html .= '</li></ol>';
}

View File

@@ -208,7 +208,7 @@ sub DoTranslationLink {
$q->input({-type=>'hidden', -name=>'missing',
-value=>GetParam('missing', '')}),
$q->submit('dotranslate', T('Go!')));
print $q->end_form, $q->end_div();
print $q->endform, $q->end_div();
PrintFooter();
}
}

0
t/aggregate.t Normal file → Executable file
View File

0
t/all.t Normal file → Executable file
View File

0
t/anchors.t Normal file → Executable file
View File

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2013-2014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
#
# 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
@@ -14,7 +14,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 4;
use Test::More tests => 3;
clear_pages();
@@ -28,8 +28,7 @@ add_module('ban-quick-editors.pl');
get_page('Test');
test_page(update_page('Test', 'cannot edit'),
'This page is empty');
test_page($redirect, 'Editing not allowed',
'fast editing spam bot');
test_page($redirect, 'Editing not allowed');
sleep 5;
test_page(update_page('Test', 'edit succeeded'),
'edit succeeded');

0
t/ban.t Normal file → Executable file
View File

0
t/cache.t Normal file → Executable file
View File

0
t/calendar.t Normal file → Executable file
View File

0
t/clusters.t Normal file → Executable file
View File

0
t/comments.t Normal file → Executable file
View File

0
t/config-page.t Normal file → Executable file
View File

0
t/conflict.t Normal file → Executable file
View File

View File

@@ -1,4 +1,4 @@
# Copyright (C) 20092014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2009, 2012 Alex Schroeder <alex@gnu.org>
#
# 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
@@ -46,6 +46,7 @@ AppendStringToFile($ConfigFile, "\$WikiLinks = 0;\n");
test_page(get_page('action=browse id=HomePage username=Alex'),
'username=Alex');
SKIP: {
eval { require LWP::UserAgent; };
@@ -67,23 +68,21 @@ SKIP: {
# Set the cookie
$response = $ua->get("$wiki?action=debug;pwd=foo");
ok($response->is_success, 'request the page');
$ua->cookie_jar->as_string =~ /Set-Cookie.*: ([^=]+)=pwd%251efoo/;
my $cookie = $1;
ok($cookie, 'pwd was set in the cookie');
test_page($ua->cookie_jar->as_string, 'Set-Cookie.*: Wiki=pwd%251efoo');
test_page_negative($response->content, 'pwd');
# Change the cookie
$response = $ua->get("$wiki?action=debug;pwd=test");
test_page($ua->cookie_jar->as_string, qq{Set-Cookie.*: $cookie=pwd%251etest});
test_page($ua->cookie_jar->as_string, 'Set-Cookie.*: Wiki=pwd%251etest');
# Delete the cookie
$response = $ua->get("$wiki?action=debug;pwd=");
test_page($ua->cookie_jar->as_string, qq{Set-Cookie.*: $cookie=""});
test_page($ua->cookie_jar->as_string, 'Set-Cookie.*: Wiki=""');
# Encoding issues
$response = $ua->get("$wiki?action=rc;username=Alex\%20Schr\%C3\%B6der");
test_page($ua->cookie_jar->as_string,
qq{Set-Cookie.*: $cookie=username%251eAlex%20Schr%C3%B6der});
'Set-Cookie.*: Wiki=username%251eAlex%20Schr%C3%B6der');
test_page($response->decoded_content,
qq{Cookie: $cookie, username=Alex Schröder});
'Cookie: Wiki, username=Alex Schröder');
};

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env perl
# Copyright (C) 2006-2014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2006-2013 Alex Schroeder <alex@gnu.org>
#
# 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
@@ -16,7 +16,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 111;
use Test::More tests => 108;
clear_pages();
add_module('creole.pl');
@@ -217,12 +217,6 @@ http://www.wikicreole.org/.
//a[@class="url http outside"][@href="http://www.wikicreole.org/"][em[text()="Visit the WikiCreole website"]]
[[http://www.wikicreole.org/ | Visit the WikiCreole website]]
//a[@class="url http outside"][@href="http://www.wikicreole.org/"][text()="Visit the WikiCreole website"]
[[foo bar]]
//div[text()[.="[[foo_bar"]/following-sibling::a[@class="edit"][@title="Click to edit this page"][@href="http://localhost/test.pl?action=edit;id=foo_bar"][text()="?"]/following-sibling::text()[.="]]"]]
[[foo_bar]]
//div[text()[.="[[foo_bar"]/following-sibling::a[@class="edit"][@title="Click to edit this page"][@href="http://localhost/test.pl?action=edit;id=foo_bar"][text()="?"]/following-sibling::text()[.="]]"]]
[[foo bar|text]]
//div[text()[.="[[foo_bar"]/following-sibling::a[@class="edit"][@title="Click to edit this page"][@href="http://localhost/test.pl?action=edit;id=foo_bar"][text()="?"]/following-sibling::text()[.="|text]]"]]
[[link]]
//a[text()="link"]
[[link|Go to my page]]

0
t/creoleaddition.t Executable file → Normal file
View File

1
t/crossbar.t Executable file → Normal file
View File

@@ -1,3 +1,4 @@
#!/usr/bin/env perl
# ====================[ crossbar.t ]====================

0
t/crumbs.t Normal file → Executable file
View File

6
t/default-links.t Normal file → Executable file
View File

@@ -15,7 +15,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 63;
use Test::More tests => 61;
clear_pages();
@@ -34,10 +34,6 @@ xpath_run_tests(split('\n',<<'EOT'));
//div[text()="[[0]]"]
[[0a]]
//a[@class="edit"][@title="Click to edit this page"][@href="http://localhost/test.pl?action=edit;id=0a"][text()="?"]
[[foo bar]]
//div[text()[.="[[foo_bar"]/following-sibling::a[@class="edit"][@title="Click to edit this page"][@href="http://localhost/test.pl?action=edit;id=foo_bar"][text()="?"]/following-sibling::text()[.="]]"]]
[[foo_bar]]
//div[text()[.="[[foo_bar"]/following-sibling::a[@class="edit"][@title="Click to edit this page"][@href="http://localhost/test.pl?action=edit;id=foo_bar"][text()="?"]/following-sibling::text()[.="]]"]]
file://home/foo/tutorial.pdf
//a[@class="url file"][@href="file://home/foo/tutorial.pdf"][text()="file://home/foo/tutorial.pdf"]
file:///home/foo/tutorial.pdf

0
t/diff.t Normal file → Executable file
View File

0
t/download.t Normal file → Executable file
View File

View File

@@ -15,7 +15,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 16;
use Test::More tests => 15;
use utf8; # test data is UTF-8 and it matters
SKIP: {
@@ -27,7 +27,7 @@ SKIP: {
add_module('git.pl');
if (qx($GitBinary --version) !~ /git version/) {
skip "$GitBinary not found", 16;
skip "$GitBinary not found", 15;
}
GitInitVariables();
@@ -48,7 +48,8 @@ SKIP: {
$GitResult = '';
GitRun(qw(status));
test_page($GitResult, 'nothing to commit', 'working directory clean');
test_page($GitResult,
'nothing to commit, working directory clean');
GitRun(qw(log -- Test));
test_page($GitResult,

View File

@@ -35,7 +35,7 @@ xpath_test($page,
'//span[@class="portrait gravatar"]',
'//p[contains(text(),"This is my comment")]',
'//a[@href="http://oddmuse.org/"][text()="Alex Schroeder"]',
'//img[@src="https://secure.gravatar.com/avatar/' . $gravatar . '"]');
'//img[@src="http://www.gravatar.com/avatar/' . $gravatar . '"]');
# without homepage
@@ -51,7 +51,7 @@ xpath_test($page,
'//span[@class="portrait gravatar"]',
'//p[contains(text(),"This is my comment")]',
'//a[@href="http://localhost/wiki.pl/Alex_Schroeder"][text()="Alex Schroeder"]',
'//img[@src="https://secure.gravatar.com/avatar/' . $gravatar . '"]');
'//img[@src="http://www.gravatar.com/avatar/' . $gravatar . '"]');
# with homepage an no email
@@ -68,4 +68,4 @@ xpath_test($page,
'//a[@href="http://oddmuse.org/"][text()="Alex Schroeder"]');
negative_xpath_test($page,
'//span[@class="portrait gravatar"]',
'//img[@src="https://secure.gravatar.com/avatar/' . $gravatar . '"]');
'//img[@src="http://www.gravatar.com/avatar/' . $gravatar . '"]');

0
t/history.t Normal file → Executable file
View File

0
t/image.t Normal file → Executable file
View File

0
t/include.t Normal file → Executable file
View File

0
t/indexed-search.t Normal file → Executable file
View File

0
t/irc.t Normal file → Executable file
View File

0
t/journal.t Normal file → Executable file
View File

0
t/link-all.t Normal file → Executable file
View File

0
t/localnames.t Normal file → Executable file
View File

View File

@@ -25,7 +25,7 @@ test_page(get_page('action=editlock'), 'operation is restricted');
test_page(get_page('action=editlock pwd=foo'), 'Edit lock created');
xpath_test(update_page('TestLock', 'mu!'),
'//a[@href="http://localhost/wiki.pl?action=password"][@class="password"][text()="This page is read-only"]');
test_page($redirect, '403 FORBIDDEN', 'Editing not allowed: TestLock is read-only');
test_page($redirect, '403 FORBIDDEN', 'Editing not allowed for TestLock');
test_page(get_page('action=editlock set=0'), 'operation is restricted');
test_page(get_page('action=editlock set=0 pwd=foo'), 'Edit lock removed');
RequestLockDir('main');

0
t/long-tables.t Normal file → Executable file
View File

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2009, 2014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2009 Alex Schroeder <alex@gnu.org>
#
# 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
@@ -15,10 +15,9 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 9;
use Test::More tests => 6;
clear_pages();
# old log entry to be moved
my $log = join($FS, '1235079422', 'Ganz_und_Gar', '',
'Ladenbeschreibung und Preisliste', '62.12.165.34',
'Alex', '1', '', '');
@@ -34,16 +33,3 @@ test_page($log,
"${FS}test${FS}",
"${FS}this is a test${FS}");
test_page_negative($log, "^\n");
# old page to be deleted
OpenPage('test');
$Page{ts} = 1;
$Page{revision} = 1;
$Page{text} = $DeletedPage;
SavePage();
ok(-f GetPageFile($OpenPageName), GetPageFile($OpenPageName)
. " exists");
xpath_test(get_page('action=maintain pwd=foo'),
'//a[text()="test"]/following-sibling::text()[.=" deleted"]');
ok(! -e GetPageFile($OpenPageName), GetPageFile($OpenPageName)
. " was deleted");

0
t/major.t Normal file → Executable file
View File

0
t/markdown-rule.t Executable file → Normal file
View File

0
t/markup.t Normal file → Executable file
View File

0
t/moin.t Normal file → Executable file
View File

0
t/oddmuse-2.2.6.pl Executable file → Normal file
View File

0
t/pagenames.t Normal file → Executable file
View File

0
t/portrait.t Executable file → Normal file
View File

0
t/questionasker.t Normal file → Executable file
View File

0
t/redirection.t Normal file → Executable file
View File

View File

@@ -32,13 +32,8 @@ SKIP: {
my $response = $ua->get("$wiki?action=version");
skip("No wiki running at $wiki", 12)
unless $response->is_success;
# check that the wiki is capable of running these tests
skip("Wiki running at $wiki doesn't have the Referrer-Tracking Extension installed", 12)
skip("Wiki running at $wiki doesn't have the referrer-tracking extension installed", 12)
unless $response->content =~ /referrer-tracking\.pl/;
# if we're running in some random environment where localhost is not
# a wiki for us to interact with
skip("Wiki running at $wiki has the Question Asker Extension installed", 12)
if $response->content =~ /questionasker\.pl/;
my $id = 'Random' . time;
# make sure we're not being fooled by 404 errors

0
t/revisions.t Normal file → Executable file
View File

40
t/rollback.t Normal file → Executable file
View File

@@ -1,20 +1,21 @@
# Copyright (C) 20062014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2006, 2007, 2008 Alex Schroeder <alex@emacswiki.org>
#
# 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 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.
# 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 <http://www.gnu.org/licenses/>.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
require 't/test.pl';
package OddMuse;
use Test::More tests => 69;
use Test::More tests => 63;
use utf8; # tests contain UTF-8 characters and it matters
clear_pages();
@@ -194,20 +195,3 @@ $ts = $Now - $KeepDays * 86400 + 100;
get_page("action=rollback to=$ts username=Alex pwd=foo");
AppendStringToFile($ConfigFile, "\$KeepDays = 7;\n");
test_page_negative(get_page("action=rc raw=1"), '[[rollback]]');
# Avoid Save button for comments
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
update_page('Comments_on_Test', 'no spam');
ok(get_page('action=browse id=Test raw=2')
=~ /(\d+) # Do not delete this line/,
'raw=2 returns timestamp');
$to = $1;
ok($to, 'timestamp stored');
sleep(1);
get_page('title=Comments_on_Test aftertext=http://spam/amoxil/');
test_page(get_page('Comments_on_Test'), 'spam');
# rollback without password
$page = get_page("action=rollback id=Comments_on_Test to=$to username=Alex");
test_page($page, 'Rolling back changes');
test_page_negative($page, 'Add your comment here', 'Save');

0
t/rss.t Normal file → Executable file
View File

7
t/search.t Normal file → Executable file
View File

@@ -1,4 +1,4 @@
# Copyright (C) 20062014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2006, 2007, 2009 Alex Schroeder <alex@gnu.org>
#
# 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
@@ -15,7 +15,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 39;
use Test::More tests => 38;
use utf8; # tests contain UTF-8 characters and it matters
clear_pages();
@@ -102,9 +102,6 @@ xpath_test(update_page('IncludeSearch',
'//div[@class="search"]/p/span[@class="result"]/a[@class="local"][@href="http://localhost/wiki.pl/NegativeSearchTestTwo"][text()="NegativeSearchTestTwo"]',
'//p[text()=" last line"]'); # note the NL -> SPC
xpath_test(get_page('search=Schröder'),
'//input[@name="search"][@value="Schröder"]');
# Search for zero
update_page("Zero", "This is about 0 and the empty string ''.");

0
t/setext.t Normal file → Executable file
View File

0
t/sidebar.t Executable file → Normal file
View File

0
t/subscribe.t Normal file → Executable file
View File

0
t/summary.t Normal file → Executable file
View File

0
t/tags.t Normal file → Executable file
View File

0
t/test.pl Normal file → Executable file
View File

0
t/toc.t Executable file → Normal file
View File

View File

@@ -87,7 +87,6 @@ clear_pages();
my $dir = `/bin/pwd`;
chop($dir);
my $mod = 'namespaces-2.2.6.pl';
mkdir($ModuleDir);
symlink("$dir/t/$mod", "$ModuleDir/$mod");
ok(-e "$ModuleDir/$mod", "old namespaces.pl installed");

0
t/upload.t Normal file → Executable file
View File

0
t/usemod-1.0.4.pl Normal file → Executable file
View File

0
t/usemod-options.t Normal file → Executable file
View File

107
wiki.pl
View File

@@ -29,8 +29,11 @@
package OddMuse;
use strict;
use CGI qw/-utf8/;
use CGI::Carp qw(fatalsToBrowser);
use warnings;
#use diagnostics;
use CGI;
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
use File::Glob ':glob';
local $| = 1; # Do not buffer output (localized for mod_perl)
@@ -302,10 +305,11 @@ sub InitVariables { # Init global session variables for mod_perl!
CreateDir($DataDir); # Create directory if it doesn't exist
$Now = time; # Reset in case script is persistent
my $ts = (stat($IndexFile))[9]; # always stat for multiple server processes
ReInit() if not $ts or $LastUpdate != $ts; # reinit if another process changed files (requires $DataDir)
ReInit() if not $ts or not $LastUpdate or $LastUpdate != $ts; # reinit if another process changed files (requires $DataDir)
$LastUpdate = $ts;
unshift(@MyRules, \&MyRules) if defined(&MyRules) && (not @MyRules or $MyRules[0] != \&MyRules);
@MyRules = sort {$RuleOrder{$a} <=> $RuleOrder{$b}} @MyRules; # default is 0
$RuleOrder{$_} //= 0 for @MyRules; # default is 0
@MyRules = sort {$RuleOrder{$a} <=> $RuleOrder{$b}} @MyRules;
ReportError(Ts('Cannot create %s', $DataDir) . ": $!", '500 INTERNAL SERVER ERROR') unless -d $DataDir;
@IndexOptions = (['pages', T('Include normal pages'), 1, \&AllPagesList]);
foreach my $sub (@MyInitVariables) {
@@ -324,8 +328,9 @@ sub ReInit { # init everything we need if we want to link to stuff
sub InitCookie {
undef $q->{'.cookies'}; # Clear cache if it exists (for SpeedyCGI)
my $cookie = $q->cookie($CookieName);
utf8::decode($cookie); # make sure it's decoded as UTF-8
%OldCookie = split(/$FS/o, UrlDecode($cookie));
my %provided = map { $_ => 1 } $q->param;
my %provided = map { utf8::decode($_); $_ => 1 } $q->param;
for my $key (keys %OldCookie) {
SetParam($key, $OldCookie{$key}) unless $provided{$key};
}
@@ -361,8 +366,10 @@ sub CookieRollbackFix {
sub GetParam {
my ($name, $default) = @_;
utf8::encode($name); # may fail
my $result = $q->param($name);
$result //= $default;
utf8::decode($result) if defined $result; # may fail, avoid turning undef to ''
return QuoteHtml($result); # you need to unquote anything that can have <tags>
}
@@ -450,7 +457,7 @@ sub ApplyRules {
} else {
$Includes{$OpenPageName} = 1;
local $OpenPageName = FreeToNormal($uri);
if ($type eq 'text') {
if ($type and $type eq 'text') {
print $q->pre({class=>"include $OpenPageName"}, QuoteHtml(GetPageContent($OpenPageName)));
} elsif (not $Includes{$OpenPageName}) { # with a starting tag, watch out for recursion
print $q->start_div({class=>"include $OpenPageName"});
@@ -520,7 +527,7 @@ sub ApplyRules {
$bol = (substr($_, pos() - 1, 1) eq "\n");
}
}
pos = length $_; # notify module functions we've completed rule handling
pos = length $_ if $_; # notify module functions we've completed rule handling
Clean(CloseHtmlEnvironments()); # last block -- close it, cache it
if ($Fragment ne '') {
$Fragment =~ s|<p>\s*</p>||g; # clean up extra paragraphs (see end Dirty())
@@ -670,7 +677,7 @@ sub OpenHtmlEnvironment { # close the previous $html_tag and open a new one
}
sub CloseHtmlEnvironments { # close all -- remember to use AddHtmlEnvironment('p') if required!
return CloseHtmlEnvironmentUntil() if pos($_) == length($_); # close all HTML environments if we're are at the end of this page
return CloseHtmlEnvironmentUntil() if $_ and pos($_) == length($_); # close all HTML environments if we're are at the end of this page
my $html = '';
while (@HtmlStack) {
defined $HtmlEnvironmentContainers{$HtmlStack[0]} and # avoid closing block level elements
@@ -766,6 +773,7 @@ sub DoClearCache {
sub QuoteHtml {
my $html = shift;
return '' if not $html;
$html =~ s/&/&amp;/g;
$html =~ s/</&lt;/g;
$html =~ s/>/&gt;/g;
@@ -836,9 +844,9 @@ sub PrintJournal {
$offset ||= 0;
# FIXME: Should pass filtered list of pages to SearchTitleAndBody to save time?
my @pages = sort JournalSort (grep(/$regexp/, $search ? SearchTitleAndBody($search) : AllPagesList()));
@pages = reverse @pages if $mode eq 'reverse' or $mode eq 'future';
@pages = reverse @pages if $mode and ($mode eq 'reverse' or $mode eq 'future');
$b = $Today // CalcDay($Now);
if ($mode eq 'future' || $mode eq 'past') {
if ($mode and ($mode eq 'future' or $mode eq 'past')) {
my $compare = $mode eq 'future' ? -1 : 1;
for (my $i = 0; $i < @pages; $i++) {
$a = $pages[$i];
@@ -1148,7 +1156,7 @@ sub GetPageOrEditLink { # use GetPageLink and GetEditLink if you know the result
} else { # reproduce markup if $UseQuestionmark
return GetEditLink($id, UnquoteHtml($bracket ? "[$link]" : $link)) unless $UseQuestionmark;
$link = QuoteHtml($id) . GetEditLink($id, '?');
$link .= ($free ? '|' : ' ') . $text if $text and FreeToNormal($text) ne $id;
$link .= ($free ? '|' : ' ') . $text if $text and $text ne $id;
$link = "[[$link]]" if $free;
$link = "[$link]" if $bracket or not $free and $text;
return $link;
@@ -1160,7 +1168,7 @@ sub GetPageLink { # use if you want to force a link to local pages, whether it e
$id = FreeToNormal($id);
$name ||= $id;
$class .= ' ' if $class;
return ScriptLink(UrlEncode($id), NormalToFree($name), $class . 'local',
return ScriptLink(UrlEncode($id), NormalToFree($name), ($class || '') . 'local',
undef, undef, $accesskey);
}
@@ -1574,8 +1582,8 @@ sub GetRcLinesFor {
next if $idOnly and $idOnly ne $id;
next if $filterOnly and not $match{$id};
next if ($userOnly and $userOnly ne $username);
next if $minor == 1 and not $showminoredit; # skip minor edits (if [[rollback]] this is bogus)
next if not $minor and $showminoredit == 2; # skip major edits
next if $minor and $minor == 1 and not $showminoredit; # skip minor edits (if [[rollback]] this is bogus)
next if not $minor and $showminoredit and $showminoredit == 2; # skip major edits
next if $match and $id !~ /$match/i;
next if $hostOnly and $host !~ /$hostOnly/i;
my @languages = split(/,/, $languages);
@@ -1713,7 +1721,7 @@ sub GetFilterForm {
-default=>GetParam('lang', ''))));
}
return GetFormStart(undef, 'get', 'filter') . $q->p($form) . $q->table($table)
. $q->p($q->submit('dofilter', T('Go!'))) . $q->end_form;
. $q->p($q->submit('dofilter', T('Go!'))) . $q->endform;
}
sub RcHtml {
@@ -1788,7 +1796,7 @@ sub RcHtml {
$more .= ";$_=$val" if $val;
}
$html .= $q->p({-class=>'more'}, ScriptLink($more, T('More...'), 'more'));
return GetFormStart(undef, 'get', 'rc') . $html . $q->end_form;
return GetFormStart(undef, 'get', 'rc') . $html . $q->endform;
}
sub PrintRcHtml { # to append RC to existing page, or action=rc directly
@@ -2067,7 +2075,7 @@ sub DoRollback {
if ($Page{text} eq $text) {
print T("The two revisions are the same."), $q->br() if $page; # no message when doing mass revert
} elsif (not UserCanEdit($id, 1)) {
print Ts('Editing not allowed: %s is read-only.', $id), $q->br();
print Ts('Editing not allowed for %s.', $id), $q->br();
} elsif (not UserIsEditor() and my $rule = BannedContent($text)) {
print Ts('Rollback of %s would restore banned content.', $id), $rule, $q->br();
} else {
@@ -2078,7 +2086,7 @@ sub DoRollback {
WriteRcLog('[[rollback]]', $page, $to); # leave marker
print $q->end_p() . $q->end_div();
ReleaseLock();
PrintFooter($page, 'edit');
PrintFooter($page);
}
sub DoAdminPage {
@@ -2273,6 +2281,7 @@ sub Cookie {
my ($changed, $visible, %params) = CookieData(); # params are URL encoded
if ($changed) {
my $cookie = join(UrlEncode($FS), %params); # no CTL in field values
utf8::encode($cookie); # prevent casting to Latin 1
my $result = $q->cookie(-name=>$CookieName, -value=>$cookie, -expires=>'+2y');
if ($visible) {
$Message .= $q->p(T('Cookie: ') . $CookieName . ', '
@@ -2375,6 +2384,7 @@ sub PrintFooter {
sub GetFooterTimestamp {
my ($id, $rev) = @_;
$rev //= '';
if ($id and $rev ne 'history' and $rev ne 'edit' and $Page{revision}) {
my @elements = ($q->br(), ($rev eq '' ? T('Last edited') : T('Edited')), TimeToText($Page{ts}),
Ts('by %s', GetAuthorLink($Page{host}, $Page{username})));
@@ -2386,6 +2396,7 @@ sub GetFooterTimestamp {
sub GetFooterLinks {
my ($id, $rev) = @_;
$rev //= '';
my @elements;
if ($id and $rev ne 'history' and $rev ne 'edit') {
if ($CommentsPattern) {
@@ -2407,7 +2418,7 @@ sub GetFooterLinks {
}
push(@elements, GetHistoryLink($id, T('View other revisions'))) if $Action{history} and $id and $rev ne 'history';
push(@elements, GetPageLink($id, T('View current revision')),
GetRCLink($id, T('View all changes'))) if $Action{history} and $rev ne '';
GetRCLink($id, T('View all changes'))) if $Action{history} and $rev;
if ($Action{contrib} and $id and $rev eq 'history') {
push(@elements, ScriptLink("action=contrib;id=" . UrlEncode($id), T('View contributors'), 'contrib'));
}
@@ -2421,6 +2432,7 @@ sub GetFooterLinks {
sub GetCommentForm {
my ($id, $rev, $comment) = @_;
$rev //= '';
if ($CommentsPattern ne '' and $id and $rev ne 'history' and $rev ne 'edit'
and $id =~ /$CommentsPattern/o and UserCanEdit($id, 0, 1)) {
return $q->div({-class=>'comment'}, GetFormStart(undef, undef, 'comment'), # protected by questionasker
@@ -2438,7 +2450,7 @@ sub GetCommentForm {
-override=>1, -size=>40, -maxlength=>100))),
$q->p($q->submit(-name=>'Save', -accesskey=>T('s'), -value=>T('Save')), ' ',
$q->submit(-name=>'Preview', -accesskey=>T('p'), -value=>T('Preview'))),
$q->end_form());
$q->endform());
}
return '';
}
@@ -2466,7 +2478,7 @@ sub GetSearchForm {
-default=>GetParam('lang', '')) . ' ';
}
return GetFormStart(undef, 'get', 'search')
. $q->p($form . $q->submit('dosearch', T('Go!'))) . $q->end_form;
. $q->p($form . $q->submit('dosearch', T('Go!'))) . $q->endform;
}
sub GetValidatorLink {
@@ -2974,9 +2986,21 @@ sub UnWiki {
sub DoEdit {
my ($id, $newText, $preview) = @_;
UserCanEditOrDie($id);
ValidIdOrDie($id);
my $upload = GetParam('upload', undef);
if ($upload and not $UploadAllowed and not UserIsAdmin()) {
if (not UserCanEdit($id, 1)) {
my $rule = UserIsBanned();
if ($rule) {
ReportError(T('Edit Denied'), '403 FORBIDDEN', undef,
$q->p(T('Editing not allowed: user, ip, or network is blocked.')),
$q->p(T('Contact the wiki administrator for more information.')),
$q->p(Ts('The rule %s matched for you.', $rule) . ' '
. Ts('See %s for more information.', GetPageLink($BannedHosts))));
} else {
ReportError(T('Edit Denied'), '403 FORBIDDEN', undef,
$q->p(Ts('Editing not allowed: %s is read-only.', NormalToFree($id))));
}
} elsif ($upload and not $UploadAllowed and not UserIsAdmin()) {
ReportError(T('Only administrators can upload files.'), '403 FORBIDDEN');
}
OpenPage($id);
@@ -3037,7 +3061,7 @@ sub GetEditForm {
} elsif ($UploadAllowed or UserIsAdmin()) {
$html .= $q->p(ScriptLink('action=edit;upload=1;id=' . UrlEncode($page_name), T('Replace this text with a file'), 'upload'));
}
$html .= $q->end_form();
$html .= $q->endform();
return $html;
}
@@ -3089,7 +3113,7 @@ sub DoPassword {
print GetFormStart(undef, undef, 'password'),
$q->p(GetHiddenValue('action', 'password'), T('Password:'), ' ',
$q->password_field(-name=>'pwd', -size=>20, -maxlength=>50),
$q->submit(-name=>'Save', -accesskey=>T('s'), -value=>T('Save'))), $q->end_form;
$q->submit(-name=>'Save', -accesskey=>T('s'), -value=>T('Save'))), $q->endform;
} else {
print $q->p(T('This site does not use admin or editor passwords.'));
}
@@ -3109,24 +3133,6 @@ sub UserIsAdminOrError {
return 1;
}
sub UserCanEditOrDie {
my $id = shift;
ValidIdOrDie($id);
if (not UserCanEdit($id, 1)) {
my $rule = UserIsBanned();
if ($rule) {
ReportError(T('Edit Denied'), '403 FORBIDDEN', undef,
$q->p(T('Editing not allowed: user, ip, or network is blocked.')),
$q->p(T('Contact the wiki administrator for more information.')),
$q->p(Ts('The rule %s matched for you.', $rule) . ' '
. Ts('See %s for more information.', GetPageLink($BannedHosts))));
} else {
ReportError(T('Edit Denied'), '403 FORBIDDEN', undef,
$q->p(Ts('Editing not allowed: %s is read-only.', NormalToFree($id))));
}
}
}
sub UserCanEdit {
my ($id, $editing, $comment) = @_;
return 0 if $id eq 'SampleUndefinedPage' or $id eq T('SampleUndefinedPage')
@@ -3492,7 +3498,8 @@ sub Replace {
sub DoPost {
my $id = FreeToNormal(shift);
UserCanEditOrDie($id);
ValidIdOrDie($id);
ReportError(Ts('Editing not allowed for %s.', $id), '403 FORBIDDEN') unless UserCanEdit($id, 1);
# Lock before getting old page to prevent races
RequestLockOrError(); # fatal
OpenPage($id);
@@ -3896,7 +3903,7 @@ sub DelayRequired {
my $name = shift;
my @entries = @{$RecentVisitors{$name}};
my $ts = $entries[$SurgeProtectionViews];
return ($Now - $ts) < $SurgeProtectionTime;
return $Now - ($ts || 0) < $SurgeProtectionTime;
}
sub AddRecentVisitor {
@@ -3924,17 +3931,20 @@ sub WriteRecentVisitors {
foreach my $name (keys %RecentVisitors) {
my @entries = @{$RecentVisitors{$name}};
if ($entries[0] >= $limit) { # if the most recent one is too old, do not keep
$data .= join($FS, $name, @entries[0 .. $SurgeProtectionViews - 1]) . "\n";
$data .= join($FS, $name, @entries[0 .. $SurgeProtectionViews - 1]) . "\n";
}
}
WriteStringToFile($VisitorFile, $data);
}
sub TextIsFile { $_[0] =~ /^#FILE (\S+) ?(\S+)?\n/ }
sub TextIsFile {
return '' unless $_[0];
$_[0] =~ /^#FILE (\S+) ?(\S+)?\n/
}
sub AddModuleDescription { # cannot use $q here because this is module init time
my ($filename, $page, $dir, $tag) = @_;
my $src = "http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/$dir" . UrlEncode($filename) . ($tag ? '?' . $tag : '');
my $src = "http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/" . ($dir || '') . UrlEncode($filename) . ($tag ? '?' . $tag : '');
my $doc = 'http://www.oddmuse.org/cgi-bin/oddmuse/' . UrlEncode(FreeToNormal($page));
$ModulesDescription .= "<p><a href=\"$src\">" . QuoteHtml($filename) . "</a>" . ($tag ? " ($tag)" : '');
$ModulesDescription .= T(', see ') . "<a href=\"$doc\">" . QuoteHtml($page) . "</a>" if $page;
@@ -3942,4 +3952,5 @@ sub AddModuleDescription { # cannot use $q here because this is module init time
}
DoWikiRequest() if $RunCGI and not exists $ENV{MOD_PERL}; # Do everything.
warningsToBrowser(1);
1; # In case we are loaded from elsewhere