forked from github/kensanata.oddmuse
Compare commits
56 Commits
use-warnin
...
2.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b0d411564 | ||
|
|
6790de2d6a | ||
|
|
2784628544 | ||
|
|
dd8c687b2b | ||
|
|
9f4ceb2d72 | ||
|
|
0f8a4fa1df | ||
|
|
3b16b58880 | ||
|
|
192a902932 | ||
|
|
aedf77cff8 | ||
|
|
728547f309 | ||
|
|
33f5484441 | ||
|
|
a225486709 | ||
|
|
3f7f9ec1eb | ||
|
|
174aac5570 | ||
|
|
954232f7c8 | ||
|
|
067658fd10 | ||
|
|
b2b2b0f6cc | ||
|
|
408d169729 | ||
|
|
0eddbd5806 | ||
|
|
210a28afd4 | ||
|
|
94a16bd463 | ||
|
|
4005e246f7 | ||
|
|
6b3cd0437f | ||
|
|
c961748b49 | ||
|
|
7327eb8e0c | ||
|
|
1b0d595945 | ||
|
|
b7e2a04bb4 | ||
|
|
848eb65ad0 | ||
|
|
7ae98f4ed9 | ||
|
|
dc792691d4 | ||
|
|
0a57a8e89b | ||
|
|
f75d415322 | ||
|
|
1654562236 | ||
|
|
ba0535f39d | ||
|
|
e5b069f70b | ||
|
|
d34b9f669b | ||
|
|
ae2061fcaf | ||
|
|
65475cf2e8 | ||
|
|
ce2e63be6b | ||
|
|
007ce8db86 | ||
|
|
5997c3ea02 | ||
|
|
6895428844 | ||
|
|
2bc2d1f927 | ||
|
|
873ce10ced | ||
|
|
3855c83a7e | ||
|
|
281736a082 | ||
|
|
63d8e24c2f | ||
|
|
c66f1a6f8e | ||
|
|
6418cab98c | ||
|
|
040d51bc93 | ||
|
|
41c3245a51 | ||
|
|
ffc2a0b12f | ||
|
|
90c632f4ab | ||
|
|
9e8def306a | ||
|
|
f4e551111a | ||
|
|
1ead545561 |
@@ -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,6 +254,9 @@ 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)))
|
||||
@@ -285,10 +288,10 @@ Example:
|
||||
(file-name-directory file))))
|
||||
|
||||
(defmacro with-oddmuse-file (file &rest body)
|
||||
"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)))
|
||||
"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)))
|
||||
,@body))
|
||||
|
||||
(put 'with-oddmuse-file 'lisp-indent-function 1)
|
||||
@@ -407,6 +410,7 @@ 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)
|
||||
@@ -416,6 +420,7 @@ 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))
|
||||
@@ -477,15 +482,13 @@ well."
|
||||
(message "%s using %s..." mesg command)
|
||||
(when (numberp expected-code)
|
||||
(setq expected-code (number-to-string expected-code)))
|
||||
;; 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)))
|
||||
(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)))
|
||||
(error "Error %s: HTTP Status Code %s" mesg status))
|
||||
((string-match "<title>Error</title>" status)
|
||||
(if (string-match "<h1>\\(.*\\)</h1>" status)
|
||||
@@ -901,9 +904,10 @@ 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)."
|
||||
wiki (ie. it must use Perl syntax).
|
||||
Use a prefix argument to search a different wiki."
|
||||
(interactive "sSearch term: ")
|
||||
(let* ((wiki (or oddmuse-wiki
|
||||
(let* ((wiki (or (and (not current-prefix-arg) oddmuse-wiki)
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(name (concat "*" wiki ": search for '" regexp "'*")))
|
||||
(if (and (get-buffer name)
|
||||
@@ -921,9 +925,10 @@ wiki (ie. it must use Perl syntax)."
|
||||
(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)."
|
||||
wiki (ie. it must use Perl syntax).
|
||||
Use a prefix argument to search a different wiki."
|
||||
(interactive "sPages matching: ")
|
||||
(let* ((wiki (or oddmuse-wiki
|
||||
(let* ((wiki (or (and (not current-prefix-arg) oddmuse-wiki)
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(name (concat "*" wiki ": matches for '" regexp "'*")))
|
||||
(if (and (get-buffer name)
|
||||
|
||||
@@ -94,14 +94,19 @@ See `oddmuse-format-command' for the formatting options.")
|
||||
(error "This is not supported."))
|
||||
|
||||
(defvar vc-oddmuse-get-revision-command
|
||||
"curl --silent %w\"?action=browse;id=%t;revision=%o;raw=1\""
|
||||
(concat "curl --silent"
|
||||
" --form action=browse"
|
||||
" --form id=%t"
|
||||
" --form revision=%v"
|
||||
" --form raw=1"
|
||||
" '%w'")
|
||||
"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'
|
||||
%o Revision to retrieve as provided by `oddmuse-revision'")
|
||||
%v Revision to retrieve as provided by `oddmuse-revision'")
|
||||
|
||||
(defun oddmuse-revision-filename (rev)
|
||||
"Return filename for revision REV.
|
||||
@@ -117,14 +122,17 @@ 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)))
|
||||
(setq rev1 (or rev1 (oddmuse-get-latest-revision wiki pagename)))
|
||||
(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)
|
||||
(oddmuse-run
|
||||
(concat "Downloading revision " rev)
|
||||
command wiki)
|
||||
(write-file filename)))))
|
||||
(diff-no-select
|
||||
(if rev1 (oddmuse-revision-filename rev1) file)
|
||||
@@ -156,6 +164,6 @@ used as a check-in comment."
|
||||
(buf (get-buffer-create " *oddmuse-response*")))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(oddmuse-run "Posting" command nil nil buf t 302))))))
|
||||
(oddmuse-run "Posting" command wiki pagename buf t 302))))))
|
||||
|
||||
(provide 'vc-oddmuse)
|
||||
|
||||
@@ -197,9 +197,17 @@ hr {
|
||||
div.footer hr {
|
||||
height:4px;
|
||||
margin: 2em 0 1ex 0;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
div.comment {
|
||||
div.content > div.comment {
|
||||
border-top: none;
|
||||
padding-top: none;
|
||||
border-left: 1ex solid #bbb;
|
||||
padding-left: 1ex;
|
||||
}
|
||||
|
||||
div.wrapper > div.comment {
|
||||
border-top: 2px solid #000;
|
||||
padding-top: 2em;
|
||||
}
|
||||
@@ -210,12 +218,13 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004, 2006, 2008 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004, 2006, 2008, 2014 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) if ValidIdOrDie($title);
|
||||
my $id = FreeToNormal($title);
|
||||
UserCanEditOrDie($id);
|
||||
$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);
|
||||
|
||||
@@ -40,8 +40,8 @@ $PrintTOCAnchor = 0;
|
||||
|
||||
%ClusterMap = ();
|
||||
|
||||
*OldDoRc = *DoRc;
|
||||
*DoRc = *ClusterMapDoRc;
|
||||
*OldPrintRcHtml = *PrintRcHtml;
|
||||
*PrintRcHtml = *ClusterMapPrintRcHtml;
|
||||
|
||||
push(@MyAdminCode, \&ClusterMapAdminRule);
|
||||
|
||||
@@ -178,7 +178,7 @@ sub CreateClusterMap {
|
||||
}
|
||||
}
|
||||
|
||||
sub ClusterMapDoRc {
|
||||
sub ClusterMapPrintRcHtml {
|
||||
my ( @options ) = @_;
|
||||
my $page = "";
|
||||
my $cluster = GetParam(rcclusteronly);
|
||||
@@ -195,7 +195,7 @@ sub ClusterMapDoRc {
|
||||
print "</ul>";
|
||||
}
|
||||
|
||||
OldDoRc(@options);
|
||||
OldPrintRcHtml(@options);
|
||||
}
|
||||
|
||||
sub PrintUnclusteredMap {
|
||||
|
||||
@@ -25,14 +25,14 @@ sub CommentDivWrapper {
|
||||
if (substr($OpenPageName, 0, length($CommentsPrefix)) eq $CommentsPrefix) {
|
||||
if (pos == 0 and not $CommentDiv) {
|
||||
$CommentDiv = 1;
|
||||
return '<div class="userComment">';
|
||||
return $q->start_div({-class=>'userComment'});
|
||||
}
|
||||
}
|
||||
if ($OpenPageName =~ /$CommentsPattern/o) {
|
||||
if ($bol and m/\G(\s*\n)*----+[ \t]*\n?/cg) {
|
||||
my $html = CloseHtmlEnvironments()
|
||||
. ($CommentDiv++ > 0 ? '</div>' : '<h2 id="commentsHeading">' . T('Comments:') . '</h2>') . '<div class="userComment">'
|
||||
. AddHtmlEnvironment('p');
|
||||
. ($CommentDiv++ > 0 ? $q->end_div() : $q->h2({-class=>'commentsHeading'}, T('Comments:'))) . $q->start_div({-class=>'userComment'})
|
||||
. AddHtmlEnvironment('p');
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,8 @@ sub CommentDivWrapper {
|
||||
sub NewCommentDivApplyRules {
|
||||
my ($blocks, $flags) = OldCommentDivApplyRules(@_);
|
||||
if ($CommentDiv) {
|
||||
print '</div>';
|
||||
$blocks .= $FS . '</div>';
|
||||
print $q->end_div();
|
||||
$blocks .= $FS . $q->end_div();
|
||||
$flags .= $FS . 0;
|
||||
$CommentDiv = 0;
|
||||
}
|
||||
|
||||
@@ -242,6 +242,7 @@ 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}}]]
|
||||
@@ -252,6 +253,7 @@ 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}}]]
|
||||
@@ -262,6 +264,7 @@ 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}}]]
|
||||
@@ -272,6 +275,7 @@ 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}}]]
|
||||
@@ -281,6 +285,7 @@ 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]]
|
||||
|
||||
35
modules/div-foo.pl
Normal file
35
modules/div-foo.pl
Normal file
@@ -0,0 +1,35 @@
|
||||
# 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\<([\w ]+)\>\s*\n/cg) {
|
||||
return CloseHtmlEnvironment('p') . AddHtmlEnvironment('div', qq{class="$1"});
|
||||
}
|
||||
if (m/\G\<([\w ]+)\>/cg) {
|
||||
return AddHtmlEnvironment('span', qq{class="$1"});
|
||||
}
|
||||
if (m/\G\<\/\/\>/cg) {
|
||||
return CloseHtmlEnvironment('div') . (InElement('div') ? '' : AddHtmlEnvironment('p'));
|
||||
}
|
||||
if (m/\G\<\/\>/cg) {
|
||||
return CloseHtmlEnvironment('span');
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -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,6 +42,7 @@ 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;
|
||||
@@ -66,6 +67,16 @@ 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);
|
||||
}
|
||||
|
||||
@@ -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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
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->endform;
|
||||
print $q->end_form;
|
||||
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
|
||||
@@ -247,7 +247,7 @@ sub DoMailSubscriptions {
|
||||
print $q->p(ScriptLink('action=subscriptions;mail=', T('Change email address'),
|
||||
'change subscriptions'));
|
||||
}
|
||||
print $q->endform(), $q->end_div();
|
||||
print $q->end_form(), $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
|
||||
137
modules/module-bisect.pl
Normal file
137
modules/module-bisect.pl
Normal file
@@ -0,0 +1,137 @@
|
||||
# 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', 'Module Bisect Extension');
|
||||
|
||||
push(@MyAdminCode, \&ModuleBisectMenu);
|
||||
$Action{bisect} = \&BisectAction;
|
||||
|
||||
sub ModuleBisectMenu {
|
||||
return unless UserIsAdmin();
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
push(@$menuref, ScriptLink('action=bisect', T('Bisect modules'), 'modulebisect'));
|
||||
}
|
||||
|
||||
sub BisectAction {
|
||||
UserIsAdminOrError();
|
||||
RequestLockOrError();
|
||||
print GetHeader('', T('Module Bisect'), '', 'nocache');
|
||||
if (GetParam('stop')) {
|
||||
BisectEnableAll(1);
|
||||
print $q->br(), $q->strong(T('All modules enabled now!'));
|
||||
print GetFormStart(undef, 'get', 'bisect');
|
||||
print GetHiddenValue('action', 'bisect');
|
||||
print $q->submit(-name=>'noop', -value=>T('Go back'));
|
||||
print $q->end_form();
|
||||
} elsif (GetParam('good') or GetParam('bad')) {
|
||||
BisectProcess(GetParam('good'));
|
||||
} else {
|
||||
BisectInitialScreen();
|
||||
}
|
||||
PrintFooter();
|
||||
ReleaseLock();
|
||||
}
|
||||
|
||||
sub BisectInitialScreen {
|
||||
print GetFormStart(undef, 'get', 'bisect');
|
||||
print GetHiddenValue('action', 'bisect');
|
||||
my @disabledFiles = bsd_glob("$ModuleDir/*.p[ml].disabled");
|
||||
if (@disabledFiles == 0) {
|
||||
print T('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();
|
||||
}
|
||||
|
||||
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) - 1; # $start and $end are indexes
|
||||
my $end = GetParam('end', @files * 2) - 1;
|
||||
if ($end - $start <= 1) {
|
||||
print Ts('It seems like module %s is causing your problem.',
|
||||
$q->strong((fileparse($isGood ? $files[$end] : $files[$start]))[0])), $q->br(), $q->br();
|
||||
print T('Please note that this module does not handle situations when your problem is caused by a combination of specific modules (which is rare anyway).'), $q->br();
|
||||
print T('Good luck fixing 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 Ts('Enabling %s', (fileparse($_))[0]), '...', $q->br() if $_[0];
|
||||
move($oldName, $_);
|
||||
}
|
||||
}
|
||||
@@ -31,54 +31,78 @@ sub ModuleUpdaterMenu {
|
||||
}
|
||||
|
||||
sub ModuleUpdaterAction {
|
||||
return unless UserIsAdminOrError();
|
||||
UserIsAdminOrError();
|
||||
RequestLockOrError();
|
||||
print GetHeader('', T('Module Updater'), '');
|
||||
for (bsd_glob("$ModuleDir/*.p[ml]")) {
|
||||
my $curModule = fileparse($_);
|
||||
ProcessModule($curModule);
|
||||
print GetHeader('', T('Module Updater'), '', 'nocache');
|
||||
|
||||
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();
|
||||
}
|
||||
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 "<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/>';
|
||||
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();
|
||||
return;
|
||||
}
|
||||
my $diff = DoModuleDiff("$ModuleDir/$module", "$TempDir/newmodule");
|
||||
open my $fh, ">", "$TempDir/$module" or die("Could not open file. $!");
|
||||
print $fh $moduleData;
|
||||
close $fh;
|
||||
|
||||
my $diff = DoModuleDiff("$ModuleDir/$module", "$TempDir/$module");
|
||||
if (not $diff) {
|
||||
print '<strong>This module is up to date, there is no need to update it.</strong><br/>';
|
||||
print $q->strong('This module is up to date, there is no need to update it.'), $q->br();
|
||||
unlink "$TempDir/$module";
|
||||
return;
|
||||
}
|
||||
print '<strong>There is a newer version of this module. Here is a diff:</strong><br/>';
|
||||
print $q->strong('There is a newer version of this module. Here is a diff:'), $q->br();
|
||||
|
||||
$diff = QuoteHtml($diff);
|
||||
$diff =~ tr/\r//d; # TODO is this required? # probably not
|
||||
$diff =~ tr/\r//d; # TODO is this required? # probably not # but maybe it is there to fix problems with dos newlines?
|
||||
for (split /\n/, $diff) {
|
||||
my ($type) = /(.)/;
|
||||
if ($type eq '+') {
|
||||
print '<span class="updaternew">';
|
||||
} elsif ($type eq '-') {
|
||||
print '<span class="updaterold">';
|
||||
if ($type =~ /[+-]/) {
|
||||
my $class = $type eq '+' ? 'updaternew' : 'updaterold';
|
||||
print $q->span({-class => $class}, $q->code($_));
|
||||
} else {
|
||||
print $q->span($q->code($_));
|
||||
}
|
||||
print '<code>' . $_ . '</code>';
|
||||
print '</span>' if $type =~ /[+-]/;
|
||||
print '<br/>';
|
||||
print $q->br();
|
||||
}
|
||||
move("$TempDir/newmodule", "$ModuleDir/$module") or print "<strong>Unable to replace module: $! </strong><br/>";
|
||||
print '<strong>Module updated successfully!</strong><br/>';
|
||||
}
|
||||
|
||||
sub DoModuleDiff {
|
||||
|
||||
@@ -167,7 +167,8 @@ anchors.
|
||||
*DeletePage = *NewPermanentAnchorsDeletePage;
|
||||
|
||||
sub NewPermanentAnchorsDeletePage {
|
||||
OldPermanentAnchorsDeletePage(@_);
|
||||
my $status = OldPermanentAnchorsDeletePage(@_);
|
||||
return $status if $status; # this would be the error message
|
||||
DeletePermanentAnchors(@_); # the only parameter is $id
|
||||
}
|
||||
|
||||
|
||||
@@ -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->endform());
|
||||
$q->end_form());
|
||||
} elsif ($id and @tags) {
|
||||
print $q->div({-class=>'tags'},
|
||||
$q->p(T('Tags:'), map { $_ = "\[\[tag:$_\]\]";
|
||||
|
||||
@@ -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->endform());
|
||||
$q->end_form());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ sub ThreadAdd {
|
||||
. '</td></tr></table>'
|
||||
. '<p>'
|
||||
. $q->p($q->submit(-name=>'Save', -value=>T('Save')))
|
||||
. $q->endform());
|
||||
. $q->end_form());
|
||||
print $q->end_html;
|
||||
} else {
|
||||
my ($page, $thread) = ThreadExtract($id);
|
||||
|
||||
@@ -75,6 +75,6 @@ sub DoTZ {
|
||||
-values=>\@names,
|
||||
-default=>GetParam('time', $defaultTZ)),
|
||||
$q->submit('dotz', T('Set')));
|
||||
print $q->endform . $q->end_div();
|
||||
print $q->end_form . $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
@@ -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. ;-)
|
||||
if (defined &UsemodRule) { $header_depth--; }
|
||||
$header_depth-- if defined &UsemodRule or defined &CreoleRule;
|
||||
|
||||
# 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) {
|
||||
while ($list_depth > $header_depth and $list_depth != 1) {
|
||||
$list_depth--;
|
||||
$toc_html .= '</li></ol>';
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ sub DoTranslationLink {
|
||||
$q->input({-type=>'hidden', -name=>'missing',
|
||||
-value=>GetParam('missing', '')}),
|
||||
$q->submit('dotranslate', T('Go!')));
|
||||
print $q->endform, $q->end_div();
|
||||
print $q->end_form, $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
}
|
||||
|
||||
0
t/aggregate.t
Executable file → Normal file
0
t/aggregate.t
Executable file → Normal file
0
t/anchors.t
Executable file → Normal file
0
t/anchors.t
Executable file → Normal file
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2013-2014 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 => 3;
|
||||
use Test::More tests => 4;
|
||||
|
||||
clear_pages();
|
||||
|
||||
@@ -28,7 +28,8 @@ 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');
|
||||
test_page($redirect, 'Editing not allowed',
|
||||
'fast editing spam bot');
|
||||
sleep 5;
|
||||
test_page(update_page('Test', 'edit succeeded'),
|
||||
'edit succeeded');
|
||||
|
||||
46
t/cache.t
Executable file → Normal file
46
t/cache.t
Executable file → Normal file
@@ -1,24 +1,20 @@
|
||||
# Copyright (C) 2006, 2007 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2006, 2007 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 Foundation; either version 2 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, write to the
|
||||
# Free Software Foundation, Inc.
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
# 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 => 7;
|
||||
use Test::More tests => 8;
|
||||
|
||||
clear_pages();
|
||||
|
||||
@@ -27,23 +23,39 @@ sub get_etag {
|
||||
return $1 if $str =~ /Etag: (.*)\r\n/;
|
||||
}
|
||||
|
||||
sub get_last_modified {
|
||||
my $str = shift;
|
||||
return $1 if $str =~ /Last-Modified: (.*)\r\n/i;
|
||||
}
|
||||
|
||||
# Get the ts from the page db and compare it to the Etag
|
||||
update_page('CacheTest', 'something');
|
||||
OpenPage('CacheTest');
|
||||
my $ts1 = $Page{ts};
|
||||
my $ts2 = get_etag(get_page('CacheTest'));
|
||||
ok(abs($ts1 - $ts2) <= 1, "Latest edit of this page: $ts1 and $ts2 are close");
|
||||
|
||||
# When updating another page, that page's ts is the new Etag for all of them
|
||||
update_page('OtherPage', 'something');
|
||||
OpenPage('OtherPage');
|
||||
$ts1 = $Page{ts};
|
||||
$ts2 = get_etag(get_page('OtherPage'));
|
||||
ok(abs($ts1 - $ts2) <= 1, "Latest edit of other page: $ts1 and $ts2 are close");
|
||||
|
||||
# Getting it raw should use the original timestamp
|
||||
OpenPage('CacheTest');
|
||||
$ts1 = $Page{ts};
|
||||
$ts2 = get_etag(get_page('/raw/CacheTest?'));
|
||||
ok(abs($ts1 - $ts2) <= 1, "Latest edit of raw page: $ts1 and $ts2 are close");
|
||||
$page = get_page('/raw/CacheTest?');
|
||||
$ts2 = get_etag($page);
|
||||
ok(abs($ts1 - $ts2) <= 1, "Latest edit of raw page: $ts1 and $ts2 based on etag are close");
|
||||
|
||||
SKIP: {
|
||||
eval { require Date::Parse };
|
||||
skip ("Date::Parse not installed", 1) if $@;
|
||||
|
||||
$ts2 = Date::Parse::str2time(get_last_modified($page));
|
||||
ok(abs($ts1 - $ts2) <= 1, "Latest edit of raw page: $ts1 and $ts2 based on last-modified timestamp are close");
|
||||
}
|
||||
|
||||
$str = 'This is a WikiLink.';
|
||||
|
||||
|
||||
0
t/calendar.t
Executable file → Normal file
0
t/calendar.t
Executable file → Normal file
0
t/clusters.t
Executable file → Normal file
0
t/clusters.t
Executable file → Normal file
0
t/comments.t
Executable file → Normal file
0
t/comments.t
Executable file → Normal file
0
t/config-page.t
Executable file → Normal file
0
t/config-page.t
Executable file → Normal file
0
t/conflict.t
Executable file → Normal file
0
t/conflict.t
Executable file → Normal file
15
t/cookie.t
15
t/cookie.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2009, 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2009–2014 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,7 +46,6 @@ AppendStringToFile($ConfigFile, "\$WikiLinks = 0;\n");
|
||||
test_page(get_page('action=browse id=HomePage username=Alex'),
|
||||
'username=Alex');
|
||||
|
||||
|
||||
SKIP: {
|
||||
|
||||
eval { require LWP::UserAgent; };
|
||||
@@ -68,21 +67,23 @@ SKIP: {
|
||||
# Set the cookie
|
||||
$response = $ua->get("$wiki?action=debug;pwd=foo");
|
||||
ok($response->is_success, 'request the page');
|
||||
test_page($ua->cookie_jar->as_string, 'Set-Cookie.*: Wiki=pwd%251efoo');
|
||||
$ua->cookie_jar->as_string =~ /Set-Cookie.*: ([^=]+)=pwd%251efoo/;
|
||||
my $cookie = $1;
|
||||
ok($cookie, 'pwd was set in the cookie');
|
||||
test_page_negative($response->content, 'pwd');
|
||||
|
||||
# Change the cookie
|
||||
$response = $ua->get("$wiki?action=debug;pwd=test");
|
||||
test_page($ua->cookie_jar->as_string, 'Set-Cookie.*: Wiki=pwd%251etest');
|
||||
test_page($ua->cookie_jar->as_string, qq{Set-Cookie.*: $cookie=pwd%251etest});
|
||||
|
||||
# Delete the cookie
|
||||
$response = $ua->get("$wiki?action=debug;pwd=");
|
||||
test_page($ua->cookie_jar->as_string, 'Set-Cookie.*: Wiki=""');
|
||||
test_page($ua->cookie_jar->as_string, qq{Set-Cookie.*: $cookie=""});
|
||||
|
||||
# Encoding issues
|
||||
$response = $ua->get("$wiki?action=rc;username=Alex\%20Schr\%C3\%B6der");
|
||||
test_page($ua->cookie_jar->as_string,
|
||||
'Set-Cookie.*: Wiki=username%251eAlex%20Schr%C3%B6der');
|
||||
qq{Set-Cookie.*: $cookie=username%251eAlex%20Schr%C3%B6der});
|
||||
test_page($response->decoded_content,
|
||||
'Cookie: Wiki, username=Alex Schröder');
|
||||
qq{Cookie: $cookie, username=Alex Schröder});
|
||||
};
|
||||
|
||||
10
t/creole.t
10
t/creole.t
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env perl
|
||||
# Copyright (C) 2006-2013 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006-2014 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 => 108;
|
||||
use Test::More tests => 111;
|
||||
clear_pages();
|
||||
|
||||
add_module('creole.pl');
|
||||
@@ -217,6 +217,12 @@ 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
Normal file → Executable file
0
t/creoleaddition.t
Normal file → Executable file
1
t/crossbar.t
Normal file → Executable file
1
t/crossbar.t
Normal file → Executable file
@@ -1,4 +1,3 @@
|
||||
|
||||
#!/usr/bin/env perl
|
||||
# ====================[ crossbar.t ]====================
|
||||
|
||||
|
||||
0
t/crumbs.t
Executable file → Normal file
0
t/crumbs.t
Executable file → Normal file
6
t/default-links.t
Executable file → Normal file
6
t/default-links.t
Executable file → Normal file
@@ -15,7 +15,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 61;
|
||||
use Test::More tests => 63;
|
||||
|
||||
clear_pages();
|
||||
|
||||
@@ -34,6 +34,10 @@ 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/download.t
Executable file → Normal file
0
t/download.t
Executable file → Normal file
7
t/git.t
7
t/git.t
@@ -15,7 +15,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 15;
|
||||
use Test::More tests => 16;
|
||||
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", 15;
|
||||
skip "$GitBinary not found", 16;
|
||||
}
|
||||
GitInitVariables();
|
||||
|
||||
@@ -48,8 +48,7 @@ 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,
|
||||
|
||||
@@ -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="http://www.gravatar.com/avatar/' . $gravatar . '"]');
|
||||
'//img[@src="https://secure.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="http://www.gravatar.com/avatar/' . $gravatar . '"]');
|
||||
'//img[@src="https://secure.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="http://www.gravatar.com/avatar/' . $gravatar . '"]');
|
||||
'//img[@src="https://secure.gravatar.com/avatar/' . $gravatar . '"]');
|
||||
|
||||
0
t/history.t
Executable file → Normal file
0
t/history.t
Executable file → Normal file
0
t/include.t
Executable file → Normal file
0
t/include.t
Executable file → Normal file
0
t/indexed-search.t
Executable file → Normal file
0
t/indexed-search.t
Executable file → Normal file
0
t/journal.t
Executable file → Normal file
0
t/journal.t
Executable file → Normal file
0
t/link-all.t
Executable file → Normal file
0
t/link-all.t
Executable file → Normal file
0
t/localnames.t
Executable file → Normal file
0
t/localnames.t
Executable file → Normal file
2
t/lock.t
2
t/lock.t
@@ -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 for TestLock');
|
||||
test_page($redirect, '403 FORBIDDEN', 'Editing not allowed: TestLock is read-only');
|
||||
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
Executable file → Normal file
0
t/long-tables.t
Executable file → Normal file
18
t/maintain.t
18
t/maintain.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2009 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2009, 2014 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,9 +15,10 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 6;
|
||||
use Test::More tests => 9;
|
||||
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', '', '');
|
||||
@@ -33,3 +34,16 @@ 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/markdown-rule.t
Normal file → Executable file
0
t/markdown-rule.t
Normal file → Executable file
0
t/markup.t
Executable file → Normal file
0
t/markup.t
Executable file → Normal file
0
t/oddmuse-2.2.6.pl
Normal file → Executable file
0
t/oddmuse-2.2.6.pl
Normal file → Executable file
0
t/pagenames.t
Executable file → Normal file
0
t/pagenames.t
Executable file → Normal file
0
t/portrait.t
Normal file → Executable file
0
t/portrait.t
Normal file → Executable file
0
t/questionasker.t
Executable file → Normal file
0
t/questionasker.t
Executable file → Normal file
0
t/redirection.t
Executable file → Normal file
0
t/redirection.t
Executable file → Normal file
@@ -32,8 +32,13 @@ SKIP: {
|
||||
my $response = $ua->get("$wiki?action=version");
|
||||
skip("No wiki running at $wiki", 12)
|
||||
unless $response->is_success;
|
||||
skip("Wiki running at $wiki doesn't have the referrer-tracking extension installed", 12)
|
||||
# check that the wiki is capable of running these tests
|
||||
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
Executable file → Normal file
0
t/revisions.t
Executable file → Normal file
40
t/rollback.t
Executable file → Normal file
40
t/rollback.t
Executable file → Normal file
@@ -1,21 +1,20 @@
|
||||
# Copyright (C) 2006, 2007, 2008 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2006–2014 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 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 => 63;
|
||||
use Test::More tests => 69;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
clear_pages();
|
||||
@@ -195,3 +194,20 @@ $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');
|
||||
|
||||
7
t/search.t
Executable file → Normal file
7
t/search.t
Executable file → Normal file
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006, 2007, 2009 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006–2014 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 => 38;
|
||||
use Test::More tests => 39;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
clear_pages();
|
||||
@@ -102,6 +102,9 @@ 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
Executable file → Normal file
0
t/setext.t
Executable file → Normal file
0
t/sidebar.t
Normal file → Executable file
0
t/sidebar.t
Normal file → Executable file
0
t/subscribe.t
Executable file → Normal file
0
t/subscribe.t
Executable file → Normal file
0
t/summary.t
Executable file → Normal file
0
t/summary.t
Executable file → Normal file
@@ -87,6 +87,7 @@ 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
Executable file → Normal file
0
t/upload.t
Executable file → Normal file
0
t/usemod-1.0.4.pl
Executable file → Normal file
0
t/usemod-1.0.4.pl
Executable file → Normal file
0
t/usemod-options.t
Executable file → Normal file
0
t/usemod-options.t
Executable file → Normal file
133
wiki.pl
133
wiki.pl
@@ -29,11 +29,9 @@
|
||||
|
||||
package OddMuse;
|
||||
use strict;
|
||||
use warnings;
|
||||
#use diagnostics;
|
||||
|
||||
use CGI;
|
||||
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
|
||||
use utf8; # in case anybody ever addes UTF8 characters to the source
|
||||
use CGI qw/-utf8/;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
use File::Glob ':glob';
|
||||
local $| = 1; # Do not buffer output (localized for mod_perl)
|
||||
|
||||
@@ -217,7 +215,8 @@ sub ReportError { # fatal!
|
||||
}
|
||||
|
||||
sub Init {
|
||||
binmode(STDOUT, ':utf8');
|
||||
binmode(STDOUT, ':utf8'); # this is where the HTML gets printed
|
||||
binmode(STDERR, ':utf8'); # just in case somebody prints debug info to stderr
|
||||
InitDirConfig();
|
||||
$FS = "\x1e"; # The FS character is the RECORD SEPARATOR control char in ASCII
|
||||
$Message = ''; # Warnings and non-fatal errors.
|
||||
@@ -305,11 +304,10 @@ 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 not $LastUpdate or $LastUpdate != $ts; # reinit if another process changed files (requires $DataDir)
|
||||
ReInit() if not $ts 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);
|
||||
$RuleOrder{$_} //= 0 for @MyRules; # default is 0
|
||||
@MyRules = sort {$RuleOrder{$a} <=> $RuleOrder{$b}} @MyRules;
|
||||
@MyRules = sort {$RuleOrder{$a} <=> $RuleOrder{$b}} @MyRules; # default is 0
|
||||
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) {
|
||||
@@ -328,9 +326,8 @@ 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 { utf8::decode($_); $_ => 1 } $q->param;
|
||||
my %provided = map { $_ => 1 } $q->param;
|
||||
for my $key (keys %OldCookie) {
|
||||
SetParam($key, $OldCookie{$key}) unless $provided{$key};
|
||||
}
|
||||
@@ -366,10 +363,9 @@ sub CookieRollbackFix {
|
||||
|
||||
sub GetParam {
|
||||
my ($name, $default) = @_;
|
||||
utf8::encode($name); # may fail
|
||||
utf8::encode($name); # turn to byte string
|
||||
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>
|
||||
}
|
||||
|
||||
@@ -457,7 +453,7 @@ sub ApplyRules {
|
||||
} else {
|
||||
$Includes{$OpenPageName} = 1;
|
||||
local $OpenPageName = FreeToNormal($uri);
|
||||
if ($type and $type eq 'text') {
|
||||
if ($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"});
|
||||
@@ -527,7 +523,7 @@ sub ApplyRules {
|
||||
$bol = (substr($_, pos() - 1, 1) eq "\n");
|
||||
}
|
||||
}
|
||||
pos = length $_ if $_; # notify module functions we've completed rule handling
|
||||
pos = length $_; # 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())
|
||||
@@ -677,7 +673,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 $_ and pos($_) == length($_); # close all HTML environments if we're are at the end of this page
|
||||
return CloseHtmlEnvironmentUntil() if 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
|
||||
@@ -773,7 +769,6 @@ sub DoClearCache {
|
||||
|
||||
sub QuoteHtml {
|
||||
my $html = shift;
|
||||
return '' if not $html;
|
||||
$html =~ s/&/&/g;
|
||||
$html =~ s/</</g;
|
||||
$html =~ s/>/>/g;
|
||||
@@ -844,9 +839,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 and ($mode eq 'reverse' or $mode eq 'future');
|
||||
@pages = reverse @pages if $mode eq 'reverse' or $mode eq 'future';
|
||||
$b = $Today // CalcDay($Now);
|
||||
if ($mode and ($mode eq 'future' or $mode eq 'past')) {
|
||||
if ($mode eq 'future' || $mode eq 'past') {
|
||||
my $compare = $mode eq 'future' ? -1 : 1;
|
||||
for (my $i = 0; $i < @pages; $i++) {
|
||||
$a = $pages[$i];
|
||||
@@ -1156,7 +1151,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 $text ne $id;
|
||||
$link .= ($free ? '|' : ' ') . $text if $text and FreeToNormal($text) ne $id;
|
||||
$link = "[[$link]]" if $free;
|
||||
$link = "[$link]" if $bracket or not $free and $text;
|
||||
return $link;
|
||||
@@ -1168,7 +1163,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);
|
||||
}
|
||||
|
||||
@@ -1268,11 +1263,13 @@ sub PageHtml {
|
||||
local *STDOUT;
|
||||
OpenPage($id);
|
||||
open(STDOUT, '>', \$diff) or die "Can't open memory file: $!";
|
||||
binmode(STDOUT); # works whether STDOUT already has the UTF8 layer or not
|
||||
binmode(STDOUT, ":utf8");
|
||||
PrintPageDiff();
|
||||
utf8::decode($diff);
|
||||
return $error if $limit and length($diff) > $limit;
|
||||
open(STDOUT, '>', \$page) or die "Can't open memory file: $!";
|
||||
binmode(STDOUT); # works whether STDOUT already has the UTF8 layer or not
|
||||
binmode(STDOUT, ":utf8");
|
||||
PrintPageHtml();
|
||||
utf8::decode($page);
|
||||
@@ -1461,7 +1458,7 @@ sub PageFresh { # pages can depend on other pages (ie. last update), admin statu
|
||||
|
||||
sub PageEtag {
|
||||
my ($changed, $visible, %params) = CookieData();
|
||||
return UrlEncode(join($FS, $LastUpdate, sort(values %params))); # no CTL in field values
|
||||
return UrlEncode(join($FS, $LastUpdate||$Now, sort(values %params))); # no CTL in field values
|
||||
}
|
||||
|
||||
sub FileFresh { # old files are never stale, current files are stale when the page was modified
|
||||
@@ -1582,8 +1579,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 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 $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 $match and $id !~ /$match/i;
|
||||
next if $hostOnly and $host !~ /$hostOnly/i;
|
||||
my @languages = split(/,/, $languages);
|
||||
@@ -1721,7 +1718,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->endform;
|
||||
. $q->p($q->submit('dofilter', T('Go!'))) . $q->end_form;
|
||||
}
|
||||
|
||||
sub RcHtml {
|
||||
@@ -1796,7 +1793,7 @@ sub RcHtml {
|
||||
$more .= ";$_=$val" if $val;
|
||||
}
|
||||
$html .= $q->p({-class=>'more'}, ScriptLink($more, T('More...'), 'more'));
|
||||
return GetFormStart(undef, 'get', 'rc') . $html . $q->endform;
|
||||
return GetFormStart(undef, 'get', 'rc') . $html . $q->end_form;
|
||||
}
|
||||
|
||||
sub PrintRcHtml { # to append RC to existing page, or action=rc directly
|
||||
@@ -2075,7 +2072,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 for %s.', $id), $q->br();
|
||||
print Ts('Editing not allowed: %s is read-only.', $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 {
|
||||
@@ -2086,7 +2083,7 @@ sub DoRollback {
|
||||
WriteRcLog('[[rollback]]', $page, $to); # leave marker
|
||||
print $q->end_p() . $q->end_div();
|
||||
ReleaseLock();
|
||||
PrintFooter($page);
|
||||
PrintFooter($page, 'edit');
|
||||
}
|
||||
|
||||
sub DoAdminPage {
|
||||
@@ -2243,11 +2240,17 @@ sub GetHeaderTitle {
|
||||
sub GetHttpHeader {
|
||||
return if $PrintedHeader;
|
||||
$PrintedHeader = 1;
|
||||
my ($type, $ts, $status, $encoding) = @_; # $ts is undef, a ts, or 'nocache'
|
||||
my ($type, $ts, $status, $encoding) = @_;
|
||||
$q->charset($type =~ m!^(text/|application/xml)! ? 'utf-8' : ''); # text/plain, text/html, application/xml: UTF-8
|
||||
my %headers = (-cache_control=>($UseCache < 0 ? 'no-cache' : 'max-age=10'));
|
||||
$headers{-etag} = $ts || PageEtag() if GetParam('cache', $UseCache) >= 2;
|
||||
$headers{'-last-modified'} = TimeToRFC822($ts) if $ts and $ts ne 'nocache'; # RFC 2616 section 13.3.4
|
||||
# Set $ts when serving raw content that cannot be modified by cookie parameters; or 'nocache'; or undef. If you
|
||||
# provide a $ts, the last-modiefied header generated will by used by HTTP/1.0 clients. If you provide no $ts, the etag
|
||||
# header generated will be used by HTTP/1.1 clients. In this situation, cookie parameters can influence the look of
|
||||
# the page and we cannot rely on $LastUpdate. HTTP/1.0 clients will ignore etags. See RFC 2616 section 13.3.4.
|
||||
if (GetParam('cache', $UseCache) >= 2 and $ts ne 'nocache') {
|
||||
$headers{'-last-modified'} = TimeToRFC822($ts) if $ts;
|
||||
$headers{-etag} = PageEtag();
|
||||
}
|
||||
$headers{-type} = GetParam('mime-type', $type);
|
||||
$headers{-status} = $status if $status;
|
||||
$headers{-Content_Encoding} = $encoding if $encoding;
|
||||
@@ -2281,7 +2284,6 @@ 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 . ', '
|
||||
@@ -2384,7 +2386,6 @@ 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})));
|
||||
@@ -2396,7 +2397,6 @@ sub GetFooterTimestamp {
|
||||
|
||||
sub GetFooterLinks {
|
||||
my ($id, $rev) = @_;
|
||||
$rev //= '';
|
||||
my @elements;
|
||||
if ($id and $rev ne 'history' and $rev ne 'edit') {
|
||||
if ($CommentsPattern) {
|
||||
@@ -2418,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;
|
||||
GetRCLink($id, T('View all changes'))) if $Action{history} and $rev ne '';
|
||||
if ($Action{contrib} and $id and $rev eq 'history') {
|
||||
push(@elements, ScriptLink("action=contrib;id=" . UrlEncode($id), T('View contributors'), 'contrib'));
|
||||
}
|
||||
@@ -2432,7 +2432,6 @@ 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
|
||||
@@ -2450,7 +2449,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->endform());
|
||||
$q->end_form());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
@@ -2478,7 +2477,7 @@ sub GetSearchForm {
|
||||
-default=>GetParam('lang', '')) . ' ';
|
||||
}
|
||||
return GetFormStart(undef, 'get', 'search')
|
||||
. $q->p($form . $q->submit('dosearch', T('Go!'))) . $q->endform;
|
||||
. $q->p($form . $q->submit('dosearch', T('Go!'))) . $q->end_form;
|
||||
}
|
||||
|
||||
sub GetValidatorLink {
|
||||
@@ -2986,21 +2985,9 @@ sub UnWiki {
|
||||
|
||||
sub DoEdit {
|
||||
my ($id, $newText, $preview) = @_;
|
||||
ValidIdOrDie($id);
|
||||
UserCanEditOrDie($id);
|
||||
my $upload = GetParam('upload', undef);
|
||||
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()) {
|
||||
if ($upload and not $UploadAllowed and not UserIsAdmin()) {
|
||||
ReportError(T('Only administrators can upload files.'), '403 FORBIDDEN');
|
||||
}
|
||||
OpenPage($id);
|
||||
@@ -3061,7 +3048,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->endform();
|
||||
$html .= $q->end_form();
|
||||
return $html;
|
||||
}
|
||||
|
||||
@@ -3079,19 +3066,18 @@ sub DoDownload {
|
||||
OpenPage($id) if ValidIdOrDie($id);
|
||||
print $q->header(-status=>'304 NOT MODIFIED') and return if FileFresh(); # FileFresh needs an OpenPage!
|
||||
my ($text, $revision) = GetTextRevision(GetParam('revision', '')); # maybe revision reset!
|
||||
my $ts = $Page{ts};
|
||||
if (my ($type, $encoding) = TextIsFile($text)) {
|
||||
my ($data) = $text =~ /^[^\n]*\n(.*)/s;
|
||||
my %allowed = map {$_ => 1} @UploadTypes;
|
||||
if (@UploadTypes and not $allowed{$type}) {
|
||||
ReportError(Ts('Files of type %s are not allowed.', $type), '415 UNSUPPORTED MEDIA TYPE');
|
||||
}
|
||||
print GetHttpHeader($type, $ts, undef, $encoding);
|
||||
print GetHttpHeader($type, $Page{ts}, undef, $encoding);
|
||||
require MIME::Base64;
|
||||
binmode(STDOUT, ":pop:raw"); # need to pop utf8 for Windows users!?
|
||||
print MIME::Base64::decode($data);
|
||||
} else {
|
||||
print GetHttpHeader('text/plain', $ts);
|
||||
print GetHttpHeader('text/plain', $Page{ts});
|
||||
print $text;
|
||||
}
|
||||
}
|
||||
@@ -3113,7 +3099,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->endform;
|
||||
$q->submit(-name=>'Save', -accesskey=>T('s'), -value=>T('Save'))), $q->end_form;
|
||||
} else {
|
||||
print $q->p(T('This site does not use admin or editor passwords.'));
|
||||
}
|
||||
@@ -3133,6 +3119,24 @@ 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')
|
||||
@@ -3498,8 +3502,7 @@ sub Replace {
|
||||
|
||||
sub DoPost {
|
||||
my $id = FreeToNormal(shift);
|
||||
ValidIdOrDie($id);
|
||||
ReportError(Ts('Editing not allowed for %s.', $id), '403 FORBIDDEN') unless UserCanEdit($id, 1);
|
||||
UserCanEditOrDie($id);
|
||||
# Lock before getting old page to prevent races
|
||||
RequestLockOrError(); # fatal
|
||||
OpenPage($id);
|
||||
@@ -3903,7 +3906,7 @@ sub DelayRequired {
|
||||
my $name = shift;
|
||||
my @entries = @{$RecentVisitors{$name}};
|
||||
my $ts = $entries[$SurgeProtectionViews];
|
||||
return $Now - ($ts || 0) < $SurgeProtectionTime;
|
||||
return ($Now - $ts) < $SurgeProtectionTime;
|
||||
}
|
||||
|
||||
sub AddRecentVisitor {
|
||||
@@ -3931,20 +3934,17 @@ 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 {
|
||||
return '' unless $_[0];
|
||||
$_[0] =~ /^#FILE (\S+) ?(\S+)?\n/
|
||||
}
|
||||
sub TextIsFile { $_[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;
|
||||
@@ -3952,5 +3952,4 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user