forked from github/kensanata.oddmuse
Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25989f78a5 | ||
|
|
afdb7a9dcb | ||
|
|
2e79a843c8 | ||
|
|
54370da235 | ||
|
|
43839ac1aa | ||
|
|
3c2f96250b | ||
|
|
9def2d2eb2 | ||
|
|
3ad40b84fb | ||
|
|
ecda4c3d98 | ||
|
|
74a0576c5d | ||
|
|
a6e07a9886 | ||
|
|
b76b61dc86 | ||
|
|
c3cb434973 | ||
|
|
62f82c2af2 | ||
|
|
d454973294 | ||
|
|
cba29c8981 | ||
|
|
4a812931c8 | ||
|
|
093a6da63d | ||
|
|
0ab5261bc6 | ||
|
|
1d4f3e4a28 | ||
|
|
6babcffd00 | ||
|
|
977cbba251 | ||
|
|
2fc4f4b054 | ||
|
|
53566c8434 | ||
|
|
563e5cd9c6 | ||
|
|
365d33b602 | ||
|
|
eef56e435d | ||
|
|
2044564981 | ||
|
|
50c9b79858 | ||
|
|
d99f62ea7e | ||
|
|
c11188fd3e | ||
|
|
dd22a852eb | ||
|
|
62b2e22da8 | ||
|
|
5483bbf386 | ||
|
|
8608464863 | ||
|
|
b0d983c817 | ||
|
|
5f58256543 | ||
|
|
c5c088deb1 | ||
|
|
a5b5af9c07 | ||
|
|
0dcf49e2cf | ||
|
|
f3885aa213 | ||
|
|
6136b399a6 | ||
|
|
5cc7d55152 | ||
|
|
4112d2acc4 | ||
|
|
f270a3ced4 | ||
|
|
7f74d3c211 | ||
|
|
375c844e37 | ||
|
|
efce35e250 | ||
|
|
cff4f1fd28 | ||
|
|
6f9ded7e41 | ||
|
|
40c01683fd | ||
|
|
08a4861dc3 | ||
|
|
d7c40d4dbe | ||
|
|
f8360bebad | ||
|
|
45a0558fcc | ||
|
|
f4ff56e69f | ||
|
|
0d7236c047 | ||
|
|
686f24251b | ||
|
|
0841c834b9 | ||
|
|
5225bded01 | ||
|
|
e0d18c31e2 | ||
|
|
670b69c118 | ||
|
|
f4d0f300e6 | ||
|
|
53a7a9a80c | ||
|
|
4f675de687 | ||
|
|
dffe5e3053 | ||
|
|
201970ba0b | ||
|
|
7e9137c6f8 | ||
|
|
9d81a1e3d2 | ||
|
|
2f58de9aa4 | ||
|
|
5ca2bf3efb | ||
|
|
96bc4e14fa | ||
|
|
ad1059dbb2 | ||
|
|
508396d1d1 | ||
|
|
6d457ff87b | ||
|
|
860cb15324 | ||
|
|
56e76a4883 | ||
|
|
3fa8e0a6b0 | ||
|
|
4c4ab98d47 | ||
|
|
ca62cbf446 | ||
|
|
ef3bde90ac | ||
|
|
7771c541bb | ||
|
|
6adabedefe | ||
|
|
a776c67cd6 | ||
|
|
0ddc1770a3 | ||
|
|
44fa8cfb5a | ||
|
|
96c21c2240 | ||
|
|
1c25325257 | ||
|
|
fd5b4e84b1 | ||
|
|
9beff3748b | ||
|
|
87dedeab85 | ||
|
|
1e73ae22d3 | ||
|
|
5e9b02b5b1 | ||
|
|
deec99c353 | ||
|
|
d1b0ac4ccb | ||
|
|
06881768c3 | ||
|
|
8e1f6c92e3 | ||
|
|
1ebc5192ff | ||
|
|
7c52b7b4c2 | ||
|
|
2936ace022 | ||
|
|
4504ef43ac | ||
|
|
50b71adf2d | ||
|
|
8bb0475ba2 | ||
|
|
0e66af495b | ||
|
|
be6752116b | ||
|
|
36577490a7 | ||
|
|
8e4dcc2240 | ||
|
|
dc4de8212a | ||
|
|
ba2de753dd | ||
|
|
6dd1b7e125 | ||
|
|
aec6e9fb30 | ||
|
|
7b7d90f9f9 | ||
|
|
c937258922 | ||
|
|
08aa098203 | ||
|
|
b0fc1e4cc0 | ||
|
|
ca9eef8c09 | ||
|
|
b90b6e9651 | ||
|
|
f10bbb4f81 | ||
|
|
0116618e36 | ||
|
|
d864045815 | ||
|
|
294e5745e7 | ||
|
|
afc4f7ecba | ||
|
|
d249792866 | ||
|
|
59cad086e7 | ||
|
|
cfac228f57 | ||
|
|
a4bd6383a2 | ||
|
|
df0f470998 | ||
|
|
d61bf19b15 | ||
|
|
e0659c4d60 | ||
|
|
70baed8088 | ||
|
|
ab3e187354 | ||
|
|
f17a67d817 | ||
|
|
601218c0b1 | ||
|
|
8af5095ff5 | ||
|
|
0a6cbfa20d | ||
|
|
1630b64fa5 | ||
|
|
ff4ad6e151 | ||
|
|
cc07341463 | ||
|
|
9fd20a9e93 | ||
|
|
1a561c3cb1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
||||
/Mac/pkg/
|
||||
*.dmg
|
||||
*.pkg
|
||||
.DS_Store
|
||||
|
||||
90
contrib/anonymize.pl
Normal file
90
contrib/anonymize.pl
Normal file
@@ -0,0 +1,90 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# 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 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/>.
|
||||
|
||||
=head1 Anonymize oldrc.log Files
|
||||
|
||||
This script will read your oldrc.log file and replace the host field
|
||||
with 'Anonymous'. This is what the main script started doing
|
||||
2013-11-30.
|
||||
|
||||
When you run this script, it sets the main lock to prevent maintenance
|
||||
from running. You can therefore run it on a live system.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
|
||||
sub verify_setup {
|
||||
if (not -f 'oldrc.log') {
|
||||
die "Run this script in your data directory.\n"
|
||||
. "The oldrc.log file should be in the same directory.\n";
|
||||
}
|
||||
if (not -d 'temp') {
|
||||
die "Run this script in your data directory.\n"
|
||||
. "The temp directory should be in the same directory.\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub request_lock {
|
||||
if (-d 'temp/lockmain') {
|
||||
die "The wiki is currently locked.\n"
|
||||
. "Rerun this script later.\n";
|
||||
}
|
||||
mkdir('temp/lockmain') or die "Could not create 'temp/lockmain'.\n"
|
||||
. "You probably don't have the file permissions necessary.\n";
|
||||
}
|
||||
|
||||
sub release_lock {
|
||||
rmdir('temp/lockmain') or die "Could not remove 'temp/lockmain'.\n"
|
||||
}
|
||||
|
||||
sub anonymize {
|
||||
open(F, 'oldrc.log') or die "Could not open 'oldrc.log' for reading.\n";
|
||||
open(B, '>oldrc.log~') or die "Could not open 'oldrc.log~' for writing.\n"
|
||||
. "I will not continue without having a backup available.\n";
|
||||
my $FS = "\x1e"; # The FS character is the RECORD SEPARATOR control char
|
||||
my @lines = ();
|
||||
while (my $line = <F>) {
|
||||
next if $line eq "\n"; # some rc.log files are broken and contain empty lines
|
||||
my ($ts, $id, $minor, $summary, $host, @rest) = split(/$FS/o, $line);
|
||||
if ($id eq '[[rollback]]') {
|
||||
# rollback markers are very different
|
||||
push(@lines, $line);
|
||||
} else {
|
||||
# anonymize
|
||||
push(@lines, join($FS, $ts, $id, $minor, $summary, 'Anonymous', @rest));
|
||||
}
|
||||
print B $line;
|
||||
}
|
||||
close(F);
|
||||
open(F, '>', 'oldrc.log') or die "Could not open 'oldrc.log' for writing.\n";
|
||||
for my $line (@lines) {
|
||||
print F $line; # @rest ends with a newline
|
||||
}
|
||||
close(F);
|
||||
print "Wrote anonymized 'oldrc.log'.\n";
|
||||
print "Saved a backup as 'oldrc.log~'\n";
|
||||
}
|
||||
|
||||
sub main {
|
||||
verify_setup();
|
||||
request_lock();
|
||||
anonymize();
|
||||
release_lock();
|
||||
}
|
||||
|
||||
main();
|
||||
871
contrib/oddmuse-curl.el
Normal file
871
contrib/oddmuse-curl.el
Normal file
@@ -0,0 +1,871 @@
|
||||
;;; oddmuse-curl.el -- edit pages on an Oddmuse wiki using curl
|
||||
;;
|
||||
;; Copyright (C) 2006–2014 Alex Schroeder <alex@gnu.org>
|
||||
;; (C) 2007 rubikitch <rubikitch@ruby-lang.org>
|
||||
;;
|
||||
;; Latest version:
|
||||
;; http://git.savannah.gnu.org/cgit/oddmuse.git/plain/contrib/oddmuse-curl.el
|
||||
;; Discussion, feedback:
|
||||
;; http://www.emacswiki.org/wiki/OddmuseCurl
|
||||
;;
|
||||
;; 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; A simple mode to edit pages on Oddmuse wikis using Emacs and the command-line
|
||||
;; HTTP client `curl'.
|
||||
;;
|
||||
;; Since text formatting rules depend on the wiki you're writing for, the
|
||||
;; font-locking can only be an approximation.
|
||||
;;
|
||||
;; Put this file in a directory on your `load-path' and
|
||||
;; add this to your init file:
|
||||
;; (require 'oddmuse)
|
||||
;; (oddmuse-mode-initialize)
|
||||
;; And then use M-x oddmuse-edit to start editing.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cl)
|
||||
(require 'sgml-mode)
|
||||
(require 'skeleton))
|
||||
|
||||
(require 'goto-addr); URL regexp
|
||||
(require 'info); link face
|
||||
(require 'shr); preview
|
||||
(require 'xml); preview munging
|
||||
|
||||
(defcustom oddmuse-directory "~/.emacs.d/oddmuse"
|
||||
"Directory to store oddmuse pages."
|
||||
:type '(string)
|
||||
:group 'oddmuse)
|
||||
|
||||
(defcustom oddmuse-wikis
|
||||
'(("EmacsWiki" "http://www.emacswiki.org/cgi-bin/emacs"
|
||||
utf-8 "uihnscuskc" nil)
|
||||
("OddmuseWiki" "http://www.oddmuse.org/cgi-bin/oddmuse"
|
||||
utf-8 "question" nil))
|
||||
"Alist mapping wiki names to URLs.
|
||||
|
||||
The elements in this list are:
|
||||
|
||||
NAME, the name of the wiki you provide when calling `oddmuse-edit'.
|
||||
|
||||
URL, the base URL of the script used when posting. If the site
|
||||
uses URL rewriting, then you need to extract the URL from the
|
||||
edit page. Emacs Wiki, for example, usually shows an URL such as
|
||||
http://www.emacswiki.org/emacs/Foo, but when you edit the page
|
||||
and examine the page source, you'll find this:
|
||||
|
||||
<form method=\"post\" action=\"http://www.emacswiki.org/cgi-bin/emacs\"
|
||||
enctype=\"multipart/form-data\" accept-charset=\"utf-8\"
|
||||
class=\"edit text\">...</form>
|
||||
|
||||
Thus, the correct value for URL is
|
||||
http://www.emacswiki.org/cgi-bin/emacs.
|
||||
|
||||
ENCODING, a symbol naming a coding-system.
|
||||
|
||||
SECRET, the secret the wiki uses if it has the Question Asker
|
||||
extension enabled. If you're getting 403 responses (edit denied)
|
||||
eventhough you can do it from a browser, examine your cookie in
|
||||
the browser. For Emacs Wiki, for example, my cookie says:
|
||||
|
||||
euihnscuskc%251e1%251eusername%251eAlexSchroeder
|
||||
|
||||
Use `split-string' and split by \"%251e\" and you'll see that
|
||||
\"euihnscuskc\" is the odd one out. The parameter name is the
|
||||
relevant string (its value is always 1).
|
||||
|
||||
USERNAME, your optional username to provide. It defaults to
|
||||
`oddmuse-username'."
|
||||
:type '(repeat (list (string :tag "Wiki")
|
||||
(string :tag "URL")
|
||||
(choice :tag "Coding System"
|
||||
(const :tag "default" utf-8)
|
||||
(symbol :tag "specify"
|
||||
:validate (lambda (widget)
|
||||
(unless (coding-system-p
|
||||
(widget-value widget))
|
||||
(widget-put widget :error
|
||||
"Not a valid coding system")))))
|
||||
(choice :tag "Secret"
|
||||
(const :tag "default" "question")
|
||||
(string :tag "specify"))
|
||||
(choice :tag "Username"
|
||||
(const :tag "default" nil)
|
||||
(string :tag "specify"))))
|
||||
:group 'oddmuse)
|
||||
|
||||
(defcustom oddmuse-username user-full-name
|
||||
"Username to use when posting.
|
||||
Setting a username is the polite thing to do."
|
||||
:type '(string)
|
||||
:group 'oddmuse)
|
||||
|
||||
(defcustom oddmuse-password ""
|
||||
"Password to use when posting.
|
||||
You only need this if you want to edit locked pages and you
|
||||
know an administrator password."
|
||||
:type '(string)
|
||||
:group 'oddmuse)
|
||||
|
||||
(defcustom oddmuse-use-always-minor nil
|
||||
"When t, set all the minor mode bit to all editions.
|
||||
This can be changed for each edition using `oddmuse-toggle-minor'."
|
||||
:type '(boolean)
|
||||
:group 'oddmuse)
|
||||
|
||||
(defvar oddmuse-get-command
|
||||
"curl --silent %w\"?action=browse;raw=2;\"id=%t"
|
||||
"Command to use for publishing pages.
|
||||
It must print the page to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'
|
||||
%t URL encoded pagename, eg. HowTo, How_To, or How%20To")
|
||||
|
||||
(defvar oddmuse-history-command
|
||||
"curl --silent %w\"?action=history;raw=1;\"id=%t"
|
||||
"Command to use for reading the history of a page.
|
||||
It must print the history to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'
|
||||
%t URL encoded pagename, eg. HowTo, How_To, or How%20To")
|
||||
|
||||
(defvar oddmuse-rc-command
|
||||
"curl --silent %w\"?action=rc;raw=1\""
|
||||
"Command to use for Recent Changes.
|
||||
It must print the RSS 3.0 text format to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'")
|
||||
|
||||
(defvar oddmuse-post-command
|
||||
(concat "curl --silent --write-out '%{http_code}'"
|
||||
" --form title='%t'"
|
||||
" --form summary='%s'"
|
||||
" --form username='%u'"
|
||||
" --form password='%p'"
|
||||
" --form %q=1"
|
||||
" --form recent_edit=%m"
|
||||
" --form oldtime=%o"
|
||||
" --form text='<-'"
|
||||
" '%w'")
|
||||
"Command to use for publishing pages.
|
||||
It must accept the page on stdin and print the HTTP status code
|
||||
on stdout.
|
||||
|
||||
%? '?' character
|
||||
%t pagename
|
||||
%s summary
|
||||
%u username
|
||||
%p password
|
||||
%q question-asker cookie
|
||||
%m minor edit
|
||||
%o oldtime, a timestamp provided by Oddmuse
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'")
|
||||
|
||||
(defvar oddmuse-preview-command
|
||||
(concat "curl --silent"
|
||||
" --form title='%t'"
|
||||
" --form username='%u'"
|
||||
" --form password='%p'"
|
||||
" --form %q=1"
|
||||
" --form recent_edit=%m"
|
||||
" --form oldtime=%o"
|
||||
" --form Preview=Preview"; the only difference
|
||||
" --form text='<-'"
|
||||
" '%w'")
|
||||
"Command to use for previewing pages.
|
||||
It must accept the page on stdin and print the HTML on stdout.
|
||||
|
||||
%? '?' character
|
||||
%t pagename
|
||||
%u username
|
||||
%p password
|
||||
%q question-asker cookie
|
||||
%m minor edit
|
||||
%o oldtime, a timestamp provided by Oddmuse
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'")
|
||||
|
||||
(defvar oddmuse-link-pattern
|
||||
"\\<[[:upper:]]+[[:lower:]]+\\([[:upper:]]+[[:lower:]]*\\)+\\>"
|
||||
"The pattern used for finding WikiName.")
|
||||
|
||||
(defvar oddmuse-wiki nil
|
||||
"The current wiki.
|
||||
Must match a key from `oddmuse-wikis'.")
|
||||
|
||||
(defvar oddmuse-page-name nil
|
||||
"Pagename of the current buffer.")
|
||||
|
||||
(defvar oddmuse-pages-hash (make-hash-table :test 'equal)
|
||||
"The wiki-name / pages pairs.")
|
||||
|
||||
(defvar oddmuse-index-get-command
|
||||
"curl --silent %w\"?action=index;raw=1\""
|
||||
"Command to use for publishing index pages.
|
||||
It must print the page to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'
|
||||
")
|
||||
|
||||
(defvar oddmuse-minor nil
|
||||
"Is this edit a minor change?")
|
||||
|
||||
(defvar oddmuse-revision nil
|
||||
"The ancestor of the current page.
|
||||
This is used by Oddmuse to merge changes.")
|
||||
|
||||
(defun oddmuse-mode-initialize ()
|
||||
(add-to-list 'auto-mode-alist
|
||||
`(,(expand-file-name oddmuse-directory) . oddmuse-mode)))
|
||||
|
||||
(defun oddmuse-creole-markup ()
|
||||
"Implement markup rules for the Creole markup extension."
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("^=[^=\n]+"
|
||||
0 '(face info-title-1
|
||||
help-echo "Creole H1")); = h1
|
||||
("^==[^=\n]+"
|
||||
0 '(face info-title-2
|
||||
help-echo "Creole H2")); == h2
|
||||
("^===[^=\n]+"
|
||||
0 '(face info-title-3
|
||||
help-echo "Creole H3")); === h3
|
||||
("^====+[^=\n]+"
|
||||
0 '(face info-title-4
|
||||
help-echo "Creole H4")); ====h4
|
||||
("\\_<//\\(.*\n\\)*?.*?//"
|
||||
0 '(face italic
|
||||
help-echo "Creole italic")); //italic//
|
||||
("\\*\\*\\(.*\n\\)*?.*?\\*\\*"
|
||||
0 '(face bold
|
||||
help-echo "Creole bold")); **bold**
|
||||
("__\\(.*\n\\)*?.*?__"
|
||||
0 '(face underline
|
||||
help-echo "Creole underline")); __underline__
|
||||
("|+=?"
|
||||
0 '(face font-lock-string-face
|
||||
help-echo "Creole table cell"))
|
||||
("\\\\\\\\[ \t]+"
|
||||
0 '(face font-lock-warning-face
|
||||
help-echo "Creole line break"))
|
||||
("^#+ "
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Creole ordered list"))
|
||||
("^- "
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Creole ordered list")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-bbcode-markup ()
|
||||
"Implement markup rules for the bbcode markup extension."
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
`(("\\[b\\]\\(.*\n\\)*?.*?\\[/b\\]"
|
||||
0 '(face bold
|
||||
help-echo "BB code bold"))
|
||||
("\\[i\\]\\(.*\n\\)*?.*?\\[/i\\]"
|
||||
0 '(face italic
|
||||
help-echo "BB code italic"))
|
||||
("\\[u\\]\\(.*\n\\)*?.*?\\[/u\\]"
|
||||
0 '(face underline
|
||||
help-echo "BB code underline"))
|
||||
(,(concat "\\[url=" goto-address-url-regexp "\\]")
|
||||
0 '(face font-lock-builtin-face
|
||||
help-echo "BB code url"))
|
||||
("\\[/?\\(img\\|url\\)\\]"
|
||||
0 '(face font-lock-builtin-face
|
||||
help-echo "BB code url or img"))
|
||||
("\\[s\\(trike\\)?\\]\\(.*\n\\)*?.*?\\[/s\\(trike\\)?\\]"
|
||||
0 '(face strike
|
||||
help-echo "BB code strike"))
|
||||
("\\[/?\\(left\\|right\\|center\\)\\]"
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "BB code alignment")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-usemod-markup ()
|
||||
"Implement markup rules for the Usemod markup extension."
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("^=[^=\n]+=$"
|
||||
0 '(face info-title-1
|
||||
help-echo "Usemod H1"))
|
||||
("^==[^=\n]+==$"
|
||||
0 '(face info-title-2
|
||||
help-echo "Usemod H2"))
|
||||
("^===[^=\n]+===$"
|
||||
0 '(face info-title-3
|
||||
help-echo "Usemod H3"))
|
||||
("^====+[^=\n]+====$"
|
||||
0 '(face info-title-4
|
||||
help-echo "Usemod H4"))
|
||||
("^ .+?$"
|
||||
0 '(face font-lock-comment-face
|
||||
help-echo "Usemod block"))
|
||||
("^[#]+ "
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Usemod ordered list")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-usemod-html-markup ()
|
||||
"Implement markup rules for the HTML option in the Usemod markup extension."
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("<\\(/?[a-z]+\\)"
|
||||
1 '(face font-lock-function-name-face
|
||||
help-echo "Usemod HTML")))
|
||||
(car font-lock-defaults)))
|
||||
(set (make-local-variable 'sgml-tag-alist)
|
||||
`(("b") ("code") ("em") ("i") ("strong") ("nowiki")
|
||||
("pre" \n) ("tt") ("u")))
|
||||
(set (make-local-variable 'skeleton-transformation) 'identity))
|
||||
|
||||
(defun oddmuse-extended-markup ()
|
||||
"Implement markup rules for the Markup extension."
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("\\*\\w+[[:word:]-%.,:;\'\"!? ]*\\*"
|
||||
0 '(face bold
|
||||
help-echo "Markup bold"))
|
||||
("\\_</\\w+[[:word:]-%.,:;\'\"!? ]*/"
|
||||
0 '(face italic
|
||||
help-echo "Markup italic"))
|
||||
("_\\w+[[:word:]-%.,:;\'\"!? ]*_"
|
||||
0 '(face underline
|
||||
help-echo "Markup underline")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-basic-markup ()
|
||||
"Implement markup rules for the basic Oddmuse setup without extensions.
|
||||
This function should come come last in `oddmuse-markup-functions'
|
||||
because of such basic patterns as [.*] which are very generic."
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
`((,oddmuse-link-pattern
|
||||
0 '(face link
|
||||
help-echo "Basic wiki name"))
|
||||
("\\[\\[.*?\\]\\]"
|
||||
0 '(face link
|
||||
help-echo "Basic free link"))
|
||||
(,(concat "\\[" goto-address-url-regexp "\\( .+?\\)?\\]")
|
||||
0 '(face link
|
||||
help-echo "Basic external free link"))
|
||||
("^\\([*]+\\)"
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Basic bullet list")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
;; Should determine this automatically based on the version? And cache
|
||||
;; it per wiki? http://emacswiki.org/wiki?action=version
|
||||
(defvar oddmuse-markup-functions
|
||||
'(oddmuse-creole-markup
|
||||
oddmuse-usemod-markup
|
||||
oddmuse-bbcode-markup
|
||||
oddmuse-extended-markup
|
||||
oddmuse-basic-markup
|
||||
goto-address)
|
||||
"The list of functions to call when `oddmuse-mode' runs.
|
||||
If these functions add font-locking, they should modify
|
||||
`font-lock-defaults'. See `font-lock-keywords' for documentation.
|
||||
If these functions all prepend their keywords, you should list
|
||||
the most important function last.
|
||||
|
||||
Here's a template for your code:
|
||||
|
||||
\(setcar font-lock-defaults
|
||||
(append
|
||||
'((REGEXP
|
||||
0 '(face FACE
|
||||
help-echo DOCSTRING)))
|
||||
(car font-lock-defaults)))")
|
||||
|
||||
(defun oddmuse-nobreak-p ()
|
||||
"Prevent line break of links.
|
||||
This depends on the `link' face."
|
||||
(let ((face (get-text-property (point) 'face)))
|
||||
(if (listp face)
|
||||
(memq 'link face)
|
||||
(eq 'link face))))
|
||||
|
||||
(define-derived-mode oddmuse-mode text-mode "Odd"
|
||||
"Simple mode to edit wiki pages.
|
||||
|
||||
Use \\[oddmuse-follow] to follow links. With prefix, allows you
|
||||
to specify the target page yourself.
|
||||
|
||||
Use \\[oddmuse-post] to post changes. With prefix, allows you to
|
||||
post the page to a different wiki.
|
||||
|
||||
Use \\[oddmuse-edit] to edit a different page. With prefix,
|
||||
forces a reload of the page instead of just popping to the buffer
|
||||
if you are already editing the page.
|
||||
|
||||
Customize `oddmuse-wikis' to add more wikis to the list.
|
||||
|
||||
Font-locking is controlled by `oddmuse-markup-functions'.
|
||||
|
||||
\\{oddmuse-mode-map}"
|
||||
(setq font-lock-defaults '(nil))
|
||||
(mapc 'funcall oddmuse-markup-functions)
|
||||
(font-lock-mode 1)
|
||||
(when buffer-file-name
|
||||
(set (make-local-variable 'oddmuse-wiki)
|
||||
(file-name-nondirectory
|
||||
(substring (file-name-directory buffer-file-name) 0 -1)))
|
||||
(set (make-local-variable 'oddmuse-page-name)
|
||||
(file-name-nondirectory buffer-file-name)))
|
||||
(set (make-local-variable 'oddmuse-minor)
|
||||
oddmuse-use-always-minor)
|
||||
(set (make-local-variable 'oddmuse-revision)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(if (looking-at
|
||||
"\\([0-9]+\\) # Do not delete this line when editing!\n")
|
||||
(prog1 (match-string 1)
|
||||
(replace-match "")
|
||||
(set-buffer-modified-p nil)))))
|
||||
(set (make-local-variable 'fill-nobreak-predicate)
|
||||
'(oddmuse-nobreak-p))
|
||||
(setq indent-tabs-mode nil))
|
||||
|
||||
(autoload 'sgml-tag "sgml-mode" t)
|
||||
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-t") 'sgml-tag)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-o") 'oddmuse-follow)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-n") 'oddmuse-new)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-m") 'oddmuse-toggle-minor)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-c") 'oddmuse-post)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-p") 'oddmuse-preview)
|
||||
(define-key oddmuse-mode-map (kbd "C-x C-v") 'oddmuse-revert)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-f") 'oddmuse-edit)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-i") 'oddmuse-insert-pagename)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-h") 'oddmuse-history)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-r") 'oddmuse-rc)
|
||||
|
||||
;; This has been stolen from simple-wiki-edit
|
||||
;;;###autoload
|
||||
(defun oddmuse-toggle-minor (&optional arg)
|
||||
"Toggle minor mode state."
|
||||
(interactive)
|
||||
(let ((num (prefix-numeric-value arg)))
|
||||
(cond
|
||||
((or (not arg) (equal num 0))
|
||||
(setq oddmuse-minor (not oddmuse-minor)))
|
||||
((> num 0) (set 'oddmuse-minor t))
|
||||
((< num 0) (set 'oddmuse-minor nil)))
|
||||
(message "Oddmuse Minor set to %S" oddmuse-minor)
|
||||
oddmuse-minor))
|
||||
|
||||
(add-to-list 'minor-mode-alist
|
||||
'(oddmuse-minor " [MINOR]"))
|
||||
|
||||
(defun oddmuse-format-command (command)
|
||||
"Internal: Substitute oddmuse format flags according to `url',
|
||||
`oddmuse-page-name', `summary', `oddmuse-username', `question',
|
||||
`oddmuse-password', `oddmuse-revision'."
|
||||
(let ((hatena "?"))
|
||||
(dolist (pair '(("%w" . url)
|
||||
("%t" . oddmuse-page-name)
|
||||
("%s" . summary)
|
||||
("%u" . oddmuse-username)
|
||||
("%m" . oddmuse-minor)
|
||||
("%p" . oddmuse-password)
|
||||
("%q" . question)
|
||||
("%o" . oddmuse-revision)
|
||||
("%r" . regexp)
|
||||
("%\\?" . hatena)))
|
||||
(when (and (boundp (cdr pair)) (stringp (symbol-value (cdr pair))))
|
||||
(setq command (replace-regexp-in-string (car pair)
|
||||
(symbol-value (cdr pair))
|
||||
command t t))))
|
||||
command))
|
||||
|
||||
(defun oddmuse-read-wiki-and-pagename (&optional required default)
|
||||
"Read an wikiname and a pagename of `oddmuse-wikis' with completion.
|
||||
If provided, REQUIRED and DEFAULT are passed along to `oddmuse-read-pagename'."
|
||||
(let ((wiki (completing-read "Wiki: " oddmuse-wikis nil t oddmuse-wiki)))
|
||||
(list wiki (oddmuse-read-pagename wiki required default))))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-history (wiki pagename)
|
||||
"Show a page's history on a wiki using `view-mode'.
|
||||
WIKI is the name of the wiki as defined in `oddmuse-wikis',
|
||||
PAGENAME is the pagename of the page you want the history of.
|
||||
Use a prefix argument to force a reload of the page."
|
||||
(interactive (oddmuse-read-wiki-and-pagename t oddmuse-page-name))
|
||||
(let ((name (concat wiki ":" pagename " [history]")))
|
||||
(if (and (get-buffer name)
|
||||
(not current-prefix-arg))
|
||||
(pop-to-buffer (get-buffer name))
|
||||
(let* ((wiki-data (assoc wiki oddmuse-wikis))
|
||||
(url (nth 1 wiki-data))
|
||||
(oddmuse-page-name pagename)
|
||||
(command (oddmuse-format-command oddmuse-history-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(buf (get-buffer-create name)))
|
||||
(set-buffer buf)
|
||||
(erase-buffer)
|
||||
(let ((max-mini-window-height 1))
|
||||
(shell-command command buf))
|
||||
(pop-to-buffer buf)
|
||||
(goto-address)
|
||||
(view-mode)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-edit (wiki pagename)
|
||||
"Edit a page on a wiki.
|
||||
WIKI is the name of the wiki as defined in `oddmuse-wikis',
|
||||
PAGENAME is the pagename of the page you want to edit.
|
||||
Use a prefix argument to force a reload of the page."
|
||||
(interactive (oddmuse-read-wiki-and-pagename))
|
||||
(make-directory (concat oddmuse-directory "/" wiki) t)
|
||||
(let ((name (concat wiki ":" pagename)))
|
||||
(if (and (get-buffer name)
|
||||
(not current-prefix-arg))
|
||||
(pop-to-buffer (get-buffer name))
|
||||
(let* ((wiki-data (assoc wiki oddmuse-wikis))
|
||||
(url (nth 1 wiki-data))
|
||||
(oddmuse-page-name pagename)
|
||||
(command (oddmuse-format-command oddmuse-get-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(buf (find-file-noselect (concat oddmuse-directory "/" wiki "/"
|
||||
pagename)))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding))
|
||||
;; don't use let for dynamically bound variable
|
||||
(set-buffer buf)
|
||||
(unless (equal name (buffer-name)) (rename-buffer name))
|
||||
(erase-buffer)
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run "Loading" command buf))
|
||||
(pop-to-buffer buf)
|
||||
(oddmuse-mode)))))
|
||||
|
||||
(defalias 'oddmuse-go 'oddmuse-edit)
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-new (wiki pagename)
|
||||
"Create a new page on a wiki.
|
||||
WIKI is the name of the wiki as defined in `oddmuse-wikis'.
|
||||
The pagename begins with the current date."
|
||||
(interactive
|
||||
(list (completing-read "Wiki: " oddmuse-wikis nil t oddmuse-wiki)
|
||||
(replace-regexp-in-string
|
||||
" +" "_"
|
||||
(read-from-minibuffer "Pagename: "
|
||||
(format-time-string "%Y-%m-%d ")))))
|
||||
(oddmuse-edit wiki pagename))
|
||||
|
||||
(autoload 'word-at-point "thingatpt")
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-follow (arg)
|
||||
"Figure out what page we need to visit
|
||||
and call `oddmuse-edit' on it."
|
||||
(interactive "P")
|
||||
(let ((pagename (or (and arg (oddmuse-read-pagename oddmuse-wiki))
|
||||
(oddmuse-pagename-at-point)
|
||||
(oddmuse-read-pagename oddmuse-wiki))))
|
||||
(oddmuse-edit (or oddmuse-wiki
|
||||
(read-from-minibuffer "URL: "))
|
||||
pagename)))
|
||||
|
||||
(defun oddmuse-current-free-link-contents ()
|
||||
"Free link contents if the point is between [[ and ]]."
|
||||
(save-excursion
|
||||
(let* ((pos (point))
|
||||
(start (search-backward "[[" nil t))
|
||||
(end (search-forward "]]" nil t)))
|
||||
(and start end (>= end pos)
|
||||
(replace-regexp-in-string
|
||||
" " "_"
|
||||
(buffer-substring (+ start 2) (- end 2)))))))
|
||||
|
||||
(defun oddmuse-pagename-at-point ()
|
||||
"Page name at point."
|
||||
(let ((pagename (word-at-point)))
|
||||
(or (oddmuse-current-free-link-contents)
|
||||
(oddmuse-wikiname-p pagename))))
|
||||
|
||||
(defun oddmuse-wikiname-p (pagename)
|
||||
"Whether PAGENAME is WikiName or not."
|
||||
(when pagename
|
||||
(let (case-fold-search)
|
||||
(when (string-match (concat "^" oddmuse-link-pattern "$") pagename)
|
||||
pagename))))
|
||||
|
||||
;; (oddmuse-wikiname-p nil)
|
||||
;; (oddmuse-wikiname-p "WikiName")
|
||||
;; (oddmuse-wikiname-p "not-wikiname")
|
||||
;; (oddmuse-wikiname-p "notWikiName")
|
||||
|
||||
(defun oddmuse-run (mesg command buf &optional on-region expected-code)
|
||||
"Print MESG and run COMMAND on the current buffer.
|
||||
MESG should be appropriate for the following uses:
|
||||
\"MESG...\"
|
||||
\"MESG...done\"
|
||||
\"MESG failed: REASON\"
|
||||
Save outpout in BUF and report an appropriate error.
|
||||
|
||||
ON-REGION indicates whether the commands runs on the region
|
||||
such as when posting, or whether it just runs by itself such
|
||||
as when loading a page.
|
||||
|
||||
If ON-REGION is not nil, the command output is compared to
|
||||
EXPECTED-CODE. The command is supposed to print the HTTP status
|
||||
code on stdout, so usually we want to provide either 302 or 200
|
||||
as EXPECTED-CODE."
|
||||
(message "%s using %s..." mesg command)
|
||||
(when (numberp expected-code)
|
||||
(setq expected-code (number-to-string expected-code)))
|
||||
;; If ON-REGION, the resulting HTTP CODE is found in BUF, so check
|
||||
;; that, too.
|
||||
(if (and (= 0 (if on-region
|
||||
(shell-command-on-region (point-min) (point-max)
|
||||
command buf)
|
||||
(shell-command command buf)))
|
||||
(or (not on-region)
|
||||
(not expected-code)
|
||||
(string= expected-code
|
||||
(with-current-buffer buf
|
||||
(buffer-string)))))
|
||||
(message "%s...done" mesg)
|
||||
(let ((err "Unknown error"))
|
||||
(with-current-buffer buf
|
||||
(when (re-search-forward "<h1>\\(.*?\\)\\.?</h1>" nil t)
|
||||
(setq err (match-string 1))))
|
||||
(error "Error %s: %s" mesg err))))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-post (summary)
|
||||
"Post the current buffer to the current wiki.
|
||||
The current wiki is taken from `oddmuse-wiki'."
|
||||
(interactive "sSummary: ")
|
||||
;; when using prefix or on a buffer that is not in oddmuse-mode
|
||||
(when (or (not oddmuse-wiki) current-prefix-arg)
|
||||
(set (make-local-variable 'oddmuse-wiki)
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(when (not oddmuse-page-name)
|
||||
(set (make-local-variable 'oddmuse-page-name)
|
||||
(read-from-minibuffer "Pagename: " (buffer-name))))
|
||||
(let* ((list (assoc oddmuse-wiki oddmuse-wikis))
|
||||
(url (nth 1 list))
|
||||
(oddmuse-minor (if oddmuse-minor "on" "off"))
|
||||
(coding (nth 2 list))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding)
|
||||
(question (nth 3 list))
|
||||
(oddmuse-username (or (nth 4 list)
|
||||
oddmuse-username))
|
||||
(command (oddmuse-format-command oddmuse-post-command))
|
||||
(buf (get-buffer-create " *oddmuse-response*"))
|
||||
(text (buffer-string)))
|
||||
(and buffer-file-name (basic-save-buffer))
|
||||
(oddmuse-run "Posting" command buf t 302)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-preview ()
|
||||
"Preview the current buffer for the current wiki.
|
||||
The current wiki is taken from `oddmuse-wiki'."
|
||||
(interactive)
|
||||
;; when using prefix or on a buffer that is not in oddmuse-mode
|
||||
(when (or (not oddmuse-wiki) current-prefix-arg)
|
||||
(set (make-local-variable 'oddmuse-wiki)
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(when (not oddmuse-page-name)
|
||||
(set (make-local-variable 'oddmuse-page-name)
|
||||
(read-from-minibuffer "Pagename: " (buffer-name))))
|
||||
(let* ((list (assoc oddmuse-wiki oddmuse-wikis))
|
||||
(url (nth 1 list))
|
||||
(oddmuse-minor (if oddmuse-minor "on" "off"))
|
||||
(coding (nth 2 list))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding)
|
||||
(question (nth 3 list))
|
||||
(oddmuse-username (or (nth 4 list)
|
||||
oddmuse-username))
|
||||
(command (oddmuse-format-command oddmuse-preview-command))
|
||||
(buf (get-buffer-create " *oddmuse-response*"))
|
||||
(text (buffer-string)))
|
||||
(and buffer-file-name (basic-save-buffer))
|
||||
(oddmuse-run "Previewing" command buf t); no status code on stdout
|
||||
(message "Rendering...")
|
||||
(pop-to-buffer "*Preview*")
|
||||
(erase-buffer)
|
||||
(shr-insert-document
|
||||
(with-current-buffer (get-buffer " *oddmuse-response*")
|
||||
(let ((html (libxml-parse-html-region (point-min) (point-max))))
|
||||
(oddmuse-find-node
|
||||
(lambda (node)
|
||||
(and (eq (xml-node-name node) 'div)
|
||||
(string= (xml-get-attribute node 'class) "preview")))
|
||||
html))))
|
||||
(goto-char (point-min))
|
||||
(message "Rendering...done")))
|
||||
|
||||
(defun oddmuse-find-node (test node)
|
||||
"Return the child of NODE that satisfies TEST.
|
||||
TEST is a function that takes a node as an argument. NODE is a
|
||||
node as returned by `libxml-parse-html-region' or
|
||||
`xml-parse-region'. The function recurses through the node tree."
|
||||
(if (funcall test node)
|
||||
node
|
||||
(dolist (child (xml-node-children node))
|
||||
(when (listp child)
|
||||
(let ((result (oddmuse-find-node test child)))
|
||||
(when result
|
||||
(return result)))))))
|
||||
|
||||
(defun oddmuse-make-completion-table (wiki)
|
||||
"Create pagename completion table for WIKI.
|
||||
If available, return precomputed one."
|
||||
(or (gethash wiki oddmuse-pages-hash)
|
||||
(oddmuse-compute-pagename-completion-table wiki)))
|
||||
|
||||
(defun oddmuse-compute-pagename-completion-table (&optional wiki-arg)
|
||||
"Really fetch the list of pagenames from WIKI.
|
||||
This command is used to reflect new pages to `oddmuse-pages-hash'."
|
||||
(interactive)
|
||||
(let* ((wiki (or wiki-arg
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t oddmuse-wiki)))
|
||||
(url (cadr (assoc wiki oddmuse-wikis)))
|
||||
(command (oddmuse-format-command oddmuse-index-get-command))
|
||||
table)
|
||||
(message "Getting index of all pages...")
|
||||
(prog1
|
||||
(setq table (split-string (shell-command-to-string command)))
|
||||
(puthash wiki table oddmuse-pages-hash)
|
||||
(message "Getting index of all pages...done"))))
|
||||
|
||||
(defun oddmuse-read-pagename (wiki &optional require default)
|
||||
"Read a pagename of WIKI with completion.
|
||||
Optional arguments REQUIRE and DEFAULT are passed on to `completing-read'.
|
||||
Typically you would use t and a `oddmuse-page-name', if that makes sense."
|
||||
(completing-read (if default
|
||||
(concat "Pagename [" default "]: ")
|
||||
"Pagename: ")
|
||||
(oddmuse-make-completion-table wiki)
|
||||
nil require nil nil default))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-rc (&optional include-minor-edits)
|
||||
"Show Recent Changes.
|
||||
With universal argument, reload."
|
||||
(interactive "P")
|
||||
(let* ((wiki (or oddmuse-wiki
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(name (concat "*" wiki " RC*")))
|
||||
(if (and (get-buffer name)
|
||||
(not current-prefix-arg))
|
||||
(pop-to-buffer (get-buffer name))
|
||||
(let* ((wiki-data (assoc wiki oddmuse-wikis))
|
||||
(url (nth 1 wiki-data))
|
||||
(command (oddmuse-format-command oddmuse-rc-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(buf (get-buffer-create name))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding))
|
||||
(set-buffer buf)
|
||||
(unless (equal name (buffer-name)) (rename-buffer name))
|
||||
(erase-buffer)
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run "Load recent changes" command buf))
|
||||
(oddmuse-rc-buffer)
|
||||
(set (make-local-variable 'oddmuse-wiki) wiki)))))
|
||||
|
||||
(defun oddmuse-rc-buffer ()
|
||||
"Parse current buffer as RSS 3.0 and display it correctly."
|
||||
(let (result)
|
||||
(dolist (item (cdr (split-string (buffer-string) "\n\n")));; skip first item
|
||||
(let ((data (mapcar (lambda (line)
|
||||
(when (string-match "^\\(.*?\\): \\(.*\\)" line)
|
||||
(cons (match-string 1 line)
|
||||
(match-string 2 line))))
|
||||
(split-string item "\n"))))
|
||||
(setq result (cons data result))))
|
||||
(erase-buffer)
|
||||
(dolist (item (nreverse result))
|
||||
(insert "[[" (cdr (assoc "title" item)) "]] – "
|
||||
(cdr (assoc "generator" item)) "\n"))
|
||||
(goto-char (point-min))
|
||||
(oddmuse-mode)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-revert ()
|
||||
"Revert this oddmuse page."
|
||||
(interactive)
|
||||
(let ((current-prefix-arg 4))
|
||||
(oddmuse-edit oddmuse-wiki oddmuse-page-name)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-insert-pagename (pagename)
|
||||
"Insert a PAGENAME of current wiki with completion."
|
||||
(interactive (list (oddmuse-read-pagename oddmuse-wiki)))
|
||||
(insert pagename))
|
||||
|
||||
;;;###autoload
|
||||
(defun emacswiki-post (&optional pagename summary)
|
||||
"Post the current buffer to the EmacsWiki.
|
||||
If this command is invoked interactively: with prefix argument,
|
||||
prompts for pagename, otherwise set pagename as basename of
|
||||
`buffer-file-name'.
|
||||
|
||||
This command is intended to post current EmacsLisp program easily."
|
||||
(interactive)
|
||||
(let* ((oddmuse-wiki "EmacsWiki")
|
||||
(oddmuse-page-name (or pagename
|
||||
(and (not current-prefix-arg)
|
||||
buffer-file-name
|
||||
(file-name-nondirectory buffer-file-name))
|
||||
(oddmuse-read-pagename oddmuse-wiki)))
|
||||
(summary (or summary (read-string "Summary: "))))
|
||||
(oddmuse-post summary)))
|
||||
|
||||
(defun oddmuse-url (wiki pagename)
|
||||
"Get the URL of oddmuse wiki."
|
||||
(condition-case v
|
||||
(concat (or (cadr (assoc wiki oddmuse-wikis)) (error)) "/" pagename)
|
||||
(error nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-browse-page (wiki pagename)
|
||||
"Ask a WWW browser to load an Oddmuse page.
|
||||
WIKI is the name of the wiki as defined in `oddmuse-wikis',
|
||||
PAGENAME is the pagename of the page you want to browse."
|
||||
(interactive (oddmuse-read-wiki-and-pagename))
|
||||
(browse-url (oddmuse-url wiki pagename)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-browse-this-page ()
|
||||
"Ask a WWW browser to load current oddmuse page."
|
||||
(interactive)
|
||||
(oddmuse-browse-page oddmuse-wiki oddmuse-page-name))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-kill-url ()
|
||||
"Make the URL of current oddmuse page the latest kill in the kill ring."
|
||||
(interactive)
|
||||
(kill-new (oddmuse-url oddmuse-wiki oddmuse-page-name)))
|
||||
|
||||
(provide 'oddmuse)
|
||||
|
||||
;;; oddmuse-curl.el ends here
|
||||
187
contrib/vc-oddmuse.el
Normal file
187
contrib/vc-oddmuse.el
Normal file
@@ -0,0 +1,187 @@
|
||||
;;; vc-oddmuse.el -- add VC support to oddmuse-curl
|
||||
;;
|
||||
;; Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
;;
|
||||
;; Latest version:
|
||||
;; http://git.savannah.gnu.org/cgit/oddmuse.git/plain/contrib/vc-oddmuse.el
|
||||
;; Discussion, feedback:
|
||||
;; http://www.emacswiki.org/cgi-bin/wiki/OddmuseCurl
|
||||
;;
|
||||
;; 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Add the following to your init file:
|
||||
;;
|
||||
;; (add-to-list 'vc-handled-backends 'oddmuse)
|
||||
|
||||
(add-to-list 'vc-handled-backends 'oddmuse)
|
||||
|
||||
(require 'oddmuse)
|
||||
(require 'diff)
|
||||
|
||||
(defun vc-oddmuse-revision-granularity () 'file)
|
||||
|
||||
(defun vc-oddmuse-registered (file)
|
||||
"Handle files in `oddmuse-directory'."
|
||||
(string-match (concat "^" (expand-file-name oddmuse-directory))
|
||||
(file-name-directory file)))
|
||||
|
||||
(defun vc-oddmuse-state (file)
|
||||
"No idea."
|
||||
'up-to-date)
|
||||
|
||||
(defun vc-oddmuse-working-revision (file)
|
||||
"No idea")
|
||||
|
||||
(defun vc-oddmuse-checkout-model (files)
|
||||
"No locking."
|
||||
'implicit)
|
||||
|
||||
(defun vc-oddmuse-create-repo (file)
|
||||
(error "You cannot create Oddmuse wikis using Emacs."))
|
||||
|
||||
(defun vc-oddmuse-register (files &optional rev comment)
|
||||
"This always works.")
|
||||
|
||||
(defun vc-oddmuse-revert (file &optional contents-done)
|
||||
"No idea"
|
||||
nil)
|
||||
|
||||
(defvar vc-oddmuse-log-command
|
||||
"curl --silent %w\"?action=rc;showedit=1;all=1;from=1;raw=1;match=%r\""
|
||||
"Command to use for publishing index pages.
|
||||
It must print the page to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'
|
||||
%r Regular expression, URL encoded, of the pages to limit ourselves to.
|
||||
This uses the free variable `regexp'.")
|
||||
|
||||
(defun vc-oddmuse-print-log (files buffer &optional shortlog
|
||||
start-revision limit)
|
||||
"Load complete recent changes for the files."
|
||||
(let* ((wiki (or oddmuse-wiki
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(wiki-data (assoc wiki oddmuse-wikis))
|
||||
(url (nth 1 wiki-data))
|
||||
(regexp (concat
|
||||
"^(" ;; Perl regular expression!
|
||||
(mapconcat 'file-name-nondirectory files "|")
|
||||
")$"))
|
||||
(command (oddmuse-format-command vc-oddmuse-log-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding)
|
||||
(max-mini-window-height 1))
|
||||
(oddmuse-run "Getting recent changes" command buffer nil))
|
||||
;; Parse current buffer as RSS 3.0 and display it correctly.
|
||||
(save-excursion
|
||||
(with-current-buffer buffer
|
||||
(let (result)
|
||||
(dolist (item (cdr (split-string (buffer-string) "\n\n")));; skip first item
|
||||
(let ((data (mapcar (lambda (line)
|
||||
(when (string-match "^\\(.*?\\): \\(.*\\)" line)
|
||||
(cons (match-string 1 line)
|
||||
(match-string 2 line))))
|
||||
(split-string item "\n"))))
|
||||
(setq result (cons data result))))
|
||||
(dolist (item (nreverse result))
|
||||
(insert "title: " (cdr (assoc "title" item)) "\n"
|
||||
"version: " (cdr (assoc "revision" item)) "\n"
|
||||
"generator: " (cdr (assoc "generator" item)) "\n"
|
||||
"timestamp: " (cdr (assoc "last-modified" item)) "\n\n"
|
||||
" " (or (cdr (assoc "description" item)) ""))
|
||||
(fill-paragraph)
|
||||
(insert "\n\n"))
|
||||
(goto-char (point-min))))))
|
||||
|
||||
(defun vc-oddmuse-log-outgoing ()
|
||||
(error "This is not supported."))
|
||||
|
||||
(defun vc-oddmuse-log-incoming ()
|
||||
(error "This is not supported."))
|
||||
|
||||
(defvar vc-oddmuse-get-revision-command
|
||||
"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'
|
||||
%o Revision to retrieve as provided by `oddmuse-revision'")
|
||||
|
||||
(defvar vc-oddmuse-get-history-command
|
||||
"curl --silent %w\"?action=history;id=%t;raw=1\""
|
||||
"Command to use to get the history 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'")
|
||||
|
||||
(defun vc-oddmuse-diff (files &optional rev1 rev2 buffer)
|
||||
"Report the differences for FILES."
|
||||
(setq buffer (or buffer (get-buffer-create "*vc-diff*")))
|
||||
(dolist (file files)
|
||||
(setq oddmuse-page-name (file-name-nondirectory file)
|
||||
oddmuse-wiki (or oddmuse-wiki
|
||||
(file-name-nondirectory
|
||||
(directory-file-name
|
||||
(file-name-directory file)))))
|
||||
(let* ((wiki-data (or (assoc oddmuse-wiki oddmuse-wikis)
|
||||
(error "Cannot find data for wiki %s" oddmuse-wiki)))
|
||||
(url (nth 1 wiki-data)))
|
||||
(unless rev1
|
||||
;; Since we don't know the most recent revision we have to fetch
|
||||
;; it from the server every time.
|
||||
(with-temp-buffer
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run "Determining latest revision"
|
||||
(oddmuse-format-command vc-oddmuse-get-history-command)
|
||||
(current-buffer) nil))
|
||||
(if (re-search-forward "^revision: \\([0-9]+\\)$" nil t)
|
||||
(setq rev1 (match-string 1))
|
||||
(error "Cannot determine the latest revision from the page history"))))
|
||||
(dolist (rev (list rev1 rev2))
|
||||
(when (and rev
|
||||
(not (file-readable-p (concat oddmuse-directory
|
||||
"/" oddmuse-wiki "/"
|
||||
oddmuse-page-name
|
||||
".~" rev "~"))))
|
||||
(let* ((oddmuse-revision rev)
|
||||
(command (oddmuse-format-command vc-oddmuse-get-revision-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(filename (concat oddmuse-directory "/" oddmuse-wiki "/"
|
||||
oddmuse-page-name ".~" rev "~"))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding))
|
||||
(with-temp-buffer
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run (concat "Downloading revision " rev)
|
||||
command (current-buffer) nil))
|
||||
(write-file filename)))))
|
||||
(diff-no-select
|
||||
(if rev1
|
||||
(concat oddmuse-directory "/" oddmuse-wiki "/" oddmuse-page-name ".~" rev1 "~")
|
||||
file)
|
||||
(if rev2
|
||||
(concat oddmuse-directory "/" oddmuse-wiki "/" oddmuse-page-name ".~" rev2 "~")
|
||||
file)
|
||||
nil
|
||||
(vc-switches 'oddmuse 'diff)
|
||||
buffer))))
|
||||
|
||||
(provide 'vc-oddmuse)
|
||||
@@ -228,6 +228,12 @@ div.commentshown {
|
||||
p.comment {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.comment {
|
||||
font-size: 14pt;
|
||||
}
|
||||
div.comment h2 {
|
||||
margin-top: 5em;
|
||||
}
|
||||
/* comment pages with username, homepage, and email subscription */
|
||||
.comment span { display: block; }
|
||||
.comment span label { display: inline-block; width: 10em; }
|
||||
@@ -380,12 +386,12 @@ div.left p + p { display:table-caption; caption-side:bottom; }
|
||||
p.table a { float:left; width:20ex; }
|
||||
p.table + p { clear:both; }
|
||||
|
||||
/* no bleeding
|
||||
@media screen {
|
||||
/* no bleeding */
|
||||
div.content, div.rc {
|
||||
overflow:hidden;
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
@media print {
|
||||
body {
|
||||
@@ -398,7 +404,7 @@ p.table + p { clear:both; }
|
||||
/* hide all the crap */
|
||||
div.diff, div.diff+hr, div.refer, div.near, div.definition, div.sister,
|
||||
div.cal, div.footer, span.specialdays, span.gotobar, a.edit, a.number span,
|
||||
div.rc form, form.tiny, p.comment {
|
||||
div.rc form, form.tiny, p.comment, p#plus1, div.g-plusone {
|
||||
display:none;
|
||||
}
|
||||
a,
|
||||
|
||||
@@ -108,6 +108,35 @@ a:active {
|
||||
color:#a41;
|
||||
background-color: inherit;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 150%;
|
||||
cursor: pointer;
|
||||
padding: 0.3em 0.5em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset,
|
||||
0px 1px 3px black;
|
||||
}
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* table of contents */
|
||||
.toc {
|
||||
font-size: smaller;
|
||||
border-left: 1em solid #886;
|
||||
}
|
||||
.toc ol {
|
||||
list-style-type: none;
|
||||
padding-left: 1em;
|
||||
}
|
||||
.toc a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* images with links, captions, etc */
|
||||
div.image { display: inline; margin: 1em; font-size: 90%; text-align: center; }
|
||||
|
||||
@@ -237,9 +237,6 @@ a.near:link {
|
||||
a.near:visited {
|
||||
color:#550;
|
||||
}
|
||||
a.tag:before {
|
||||
content:"\2601\ ";
|
||||
}
|
||||
ol, ul, dl {
|
||||
padding-top:0.5em;
|
||||
}
|
||||
|
||||
213
css/oddmuse-2013.css
Normal file
213
css/oddmuse-2013.css
Normal file
@@ -0,0 +1,213 @@
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic'), local('GentiumBasic'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/KCktj43blvLkhOTolFn-MVhr3SzZVY8L1R-AhaesIwA.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold'), local('GentiumBasic-Bold'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/2qL6yulgGf0wwgOp-UqGyKuvVGpDTHxx0YlM6XbRIFE.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic Italic'), local('GentiumBasic-Italic'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/qoFz4NSMaYC2UmsMAG3lyajIwExuvJl80GezUi4i-sM.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold Italic'), local('GentiumBasic-BoldItalic'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/8N9-c_aQDJ8LbI1NGVMrwjBWbH-5CKom31QWlI8zOIM.woff) format('woff');
|
||||
}
|
||||
|
||||
body {
|
||||
background:#fff;
|
||||
padding:2% 5%;
|
||||
margin:0;
|
||||
font-family: "Gentium Basic", "Bookman Old Style", "Times New Roman", serif;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
div.header h1 {
|
||||
margin-top:2ex;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #a00;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #d88;
|
||||
}
|
||||
|
||||
div.header h1 a:hover, h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover,
|
||||
a:hover, span.caption a.image:hover {
|
||||
background:#fee;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
clear: right;
|
||||
border-style:none;
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
img {
|
||||
padding: 0.5em;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
a.image:hover {
|
||||
background:inherit;
|
||||
}
|
||||
|
||||
a.image:hover img {
|
||||
background:#fee;
|
||||
}
|
||||
|
||||
/* a.definition soll aussehen wie h2 */
|
||||
h2, p a.definition {
|
||||
display:block;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
/* Such Link im h1 soll nicht auffallen. */
|
||||
h1, h2, h3, h4, h1 a, h1 a:visited, p a.definition {
|
||||
color:#666;
|
||||
font-size: 30pt;
|
||||
font-weight: normal;
|
||||
margin: 4ex 0 1ex 0;
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
h3, h4 {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
div.diff {
|
||||
padding: 1em 3em;
|
||||
}
|
||||
div.old {
|
||||
background-color:#FFFFAF;
|
||||
}
|
||||
div.new {
|
||||
background-color:#CFFFCF;
|
||||
}
|
||||
div.old p, div.new p {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
div.refer { padding-left:5%; padding-right:5%; font-size:smaller; }
|
||||
div[class="content refer"] p { margin-top:2em; }
|
||||
div.content div.refer hr { display:none; }
|
||||
div.content div.refer { padding:0; font-size:medium; }
|
||||
div.content div.refer p { margin:0; }
|
||||
div.refer a { display:block; }
|
||||
table.history { border-style:none; }
|
||||
td.history { border-style:none; }
|
||||
|
||||
table.user {
|
||||
border-style: none;
|
||||
margin-left: 3em;
|
||||
}
|
||||
table.user tr td {
|
||||
border-style: none;
|
||||
padding:0.5ex 1ex;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight:bold;
|
||||
}
|
||||
dd {
|
||||
margin-bottom:1ex;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width:100%;
|
||||
height:80%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
textarea#summary { height: 3em; }
|
||||
input {
|
||||
font-size: 12pt;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
li img, img.smiley, .noborder img {
|
||||
border:none;
|
||||
padding:0;
|
||||
margin:0;
|
||||
background:#fff;
|
||||
color:#000;
|
||||
}
|
||||
/* Google +1 */
|
||||
a#plus1 img {
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.header img, div.footer img { border:0; padding:0; margin:0; }
|
||||
|
||||
.left { float:left; }
|
||||
.right { float:right; }
|
||||
div.left .left, div.right .right {
|
||||
float:none;
|
||||
}
|
||||
.center { text-align:center; }
|
||||
|
||||
span.author {
|
||||
color: #501;
|
||||
}
|
||||
span.bar a {
|
||||
padding-right:1ex;
|
||||
}
|
||||
|
||||
.rc .author {
|
||||
color: #655;
|
||||
}
|
||||
|
||||
.rc strong {
|
||||
font-weight: normal;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.rc li {
|
||||
position:relative;
|
||||
padding: 1ex 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border:none;
|
||||
color:black;
|
||||
background-color:#000;
|
||||
height:2px;
|
||||
margin-top:2ex;
|
||||
}
|
||||
|
||||
div.footer hr {
|
||||
height:4px;
|
||||
margin: 2em 0 1ex 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0.5em;
|
||||
margin-left: 1em;
|
||||
margin-right: 2em;
|
||||
white-space: pre;
|
||||
overflow:hidden;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
div.footer hr {
|
||||
clear:both;
|
||||
}
|
||||
0
modules/aawrapperdiv.pl
Executable file → Normal file
0
modules/aawrapperdiv.pl
Executable file → Normal file
@@ -12,7 +12,7 @@
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/anchors.pl">anchors.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Comments_on_Local_Anchor_Extension">Comments on Local Anchor Extension</a></p>';
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/anchors.pl">anchors.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Local_Anchor_Extension">Local Anchor Extension</a></p>';
|
||||
|
||||
push(@MyRules, \&AnchorsRule);
|
||||
|
||||
|
||||
0
modules/antispam.pl
Executable file → Normal file
0
modules/antispam.pl
Executable file → Normal file
64
modules/askpage.pl
Normal file
64
modules/askpage.pl
Normal file
@@ -0,0 +1,64 @@
|
||||
# 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/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/askpage.pl">askpage.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Ask_Page_Extension">Ask Page Extension</a></p>';
|
||||
|
||||
use Fcntl qw(:DEFAULT :flock);
|
||||
|
||||
use vars qw($AskPage $QuestionPage $NewQuestion);
|
||||
# Don't forget to set your $CommentsPattern to include both $AskPage and $QuestionPage
|
||||
$AskPage = 'Ask';
|
||||
$QuestionPage = 'Question_';
|
||||
$NewQuestion = 'Write your question here:';
|
||||
|
||||
sub IncrementInFile {
|
||||
my $filename = shift;
|
||||
sysopen my $fh, $filename, O_RDWR|O_CREAT or die "can't open $filename: $!";
|
||||
flock $fh, LOCK_EX or die "can't flock $filename: $!";
|
||||
my $num = <$fh> || 1;
|
||||
seek $fh, 0, 0 or die "can't rewind $filename: $!";
|
||||
truncate $fh, 0 or die "can't truncate $filename: $!";
|
||||
(print $fh $num+1, "\n") or die "can't write $filename: $!";
|
||||
close $fh or die "can't close $filename: $!";
|
||||
return $num;
|
||||
}
|
||||
|
||||
*OldAskPageDoPost=*DoPost;
|
||||
*DoPost=*NewAskPageDoPost;
|
||||
sub NewAskPageDoPost {
|
||||
my $id = FreeToNormal(shift);
|
||||
if ($id eq $AskPage and not GetParam('text', undef)) {
|
||||
my $currentId = IncrementInFile("$DataDir/curquestion");
|
||||
$currentQuestion =~ s/[\s\n]//g;
|
||||
return OldAskPageDoPost($QuestionPage . $currentQuestion, @_);
|
||||
} else {
|
||||
return OldAskPageDoPost($id, @_);
|
||||
}
|
||||
}
|
||||
|
||||
*OldAskPageGetCommentForm=*GetCommentForm;
|
||||
*GetCommentForm=*NewAskPageGetCommentForm;
|
||||
sub NewAskPageGetCommentForm {
|
||||
my ($id, $rev, $comment) = @_;
|
||||
$NewComment = $NewQuestion if $id eq $AskPage;
|
||||
return OldAskPageGetCommentForm(@_);
|
||||
}
|
||||
|
||||
*OldAskPageJournalSort=*JournalSort;
|
||||
*JournalSort=NewAskPageJournalSort;
|
||||
sub NewAskPageJournalSort {
|
||||
return OldAskPageJournalSort() unless $a =~ m/^$QuestionPage\d+$/ and $b =~ m/^$QuestionPage\d+$/;
|
||||
($b =~ m/$QuestionPage(\d+)/)[0] <=> ($a =~ m/$QuestionPage(\d+)/)[0];
|
||||
}
|
||||
0
modules/backlinks.pl
Executable file → Normal file
0
modules/backlinks.pl
Executable file → Normal file
175
modules/balanced-page-directories.pl
Normal file
175
modules/balanced-page-directories.pl
Normal file
@@ -0,0 +1,175 @@
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2014 Aki Goto <tyatsumi@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/>.
|
||||
|
||||
=head1 Balanced Page Directories
|
||||
|
||||
B<WARNING: This module is deprecated.> Oddmuse no longer disperses
|
||||
page data files into 27 directories based on the first character of
|
||||
the page name. The directories used to be "A" to "Z", and "other". If
|
||||
you uses your wiki as a blog, all the pages starting with a date ended
|
||||
up in "other". If your page names started with letters other than "A"
|
||||
to "Z", all the pages ended up in "other". If you were using comment
|
||||
pages, all your comment pages ended in "C". This module was intended
|
||||
to create more subdirectories and spread them more evenly. This is no
|
||||
longer necessary, as the typical filesystem's performance no longer
|
||||
degrades with tens of thousands of files in a directory. I'm assuming
|
||||
most Oddmuse hosts to use some form of GNU/Linux with ext3 or ext4
|
||||
with dir_index option.
|
||||
|
||||
The remaining info for this module is all deprecated.
|
||||
|
||||
=over
|
||||
|
||||
The ext2 inode specification allows for over 100 trillion files to
|
||||
reside in a single directory, however because of the current
|
||||
linked-list directory implementation, only about 10-15 thousand files
|
||||
can realistically be stored in a single directory. – L<haversian-ga on
|
||||
09 Dec 2002 22:56
|
||||
PST|http://answers.google.com/answers/threadview?id=122241>
|
||||
|
||||
=back
|
||||
|
||||
CAUTION: When this extension is installed, your data structure I<must>
|
||||
change. Make sure you have a backup of your data directory somewhere.
|
||||
|
||||
=head2 Finding the right directory
|
||||
|
||||
On the command line, finding the right subdirectory can be a problem.
|
||||
Here's how to use md5sum. Note that the -n option to echo prevents the
|
||||
trailing newline. Its inclusion would change the checksum.
|
||||
|
||||
echo -n HomePage | md5sum | cut -c 1-2
|
||||
c1
|
||||
echo -n ホームページ | md5sum | cut -c 1-2
|
||||
10
|
||||
|
||||
=head2 $BalancedPageDirectoriesSize
|
||||
|
||||
If you have more than 2560000 pages (w00t!) you might want to set
|
||||
$BalancedPageDirectoriesSize to 3. This will give you 16× more
|
||||
directories, which should let you have 40960000 pages. Also, please
|
||||
let us know about your wiki. :)
|
||||
|
||||
=head2 Migration
|
||||
|
||||
Once you install the code, reload any page. This should trigger
|
||||
migration. No output is produced during migration. Migration is
|
||||
triggered whenever a page file isn't found but a page is found at the
|
||||
default old location. If, for example, $PageDir/c1/HomePage.pg doesn't
|
||||
exist but $PageDir/h/HomePage.pg does, and the wiki can be locked, the
|
||||
wiki is locked and migration is started.
|
||||
|
||||
=cut
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/balanced-page-directories.pl">balanced-page-directories.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Balanced_Page_Directories_Extension">Balanced Page Directories Extension</a>';
|
||||
|
||||
use Digest::MD5 qw(md5_hex);
|
||||
use File::Find qw(finddepth);
|
||||
use vars qw($BalancedPageDirectoriesSize);
|
||||
|
||||
$BalancedPageDirectoriesSize = 2;
|
||||
|
||||
*OldBalancedPageDirectoriesGetPageDirectory = *GetPageDirectory;
|
||||
*GetPageDirectory = *NewBalancedPageDirectoriesGetPageDirectory;
|
||||
|
||||
sub NewBalancedPageDirectoriesGetPageDirectory {
|
||||
my $id = shift;
|
||||
utf8::encode($id);
|
||||
return substr(md5_hex($id), 0, $BalancedPageDirectoriesSize);
|
||||
}
|
||||
|
||||
*OldBalancedPageDirectoriesOpenPage = *OpenPage;
|
||||
*OpenPage = *NewBalancedPageDirectoriesOpenPage;
|
||||
|
||||
sub NewBalancedPageDirectoriesOpenPage {
|
||||
my $id = shift;
|
||||
if (! -f GetPageFile($id)) {
|
||||
BalancedPageDirectoriesMigrate($id);
|
||||
}
|
||||
return OldBalancedPageDirectoriesOpenPage($id, @_);
|
||||
}
|
||||
|
||||
sub BalancedPageDirectoriesMigrate {
|
||||
my $id = shift;
|
||||
|
||||
# This code is called if the page file does not exist. Perhaps we
|
||||
# need to migrate? Check if the old page file exists. If it does
|
||||
# not, there is no point in migration.
|
||||
*GetPageDirectory = *OldBalancedPageDirectoriesGetPageDirectory;
|
||||
if (not -f GetPageFile($id)) {
|
||||
*GetPageDirectory = *NewBalancedPageDirectoriesGetPageDirectory;
|
||||
return;
|
||||
}
|
||||
|
||||
# Make sure we can change the data structure now.
|
||||
RequestLockOrError();
|
||||
|
||||
# Now we know that we need to migrate. The list of pages is scanned
|
||||
# using globbing.
|
||||
SetParam('refresh', 1);
|
||||
|
||||
for $id (AllPagesList()) {
|
||||
|
||||
*GetPageDirectory = *OldBalancedPageDirectoriesGetPageDirectory;
|
||||
my $page_from = GetPageFile($id);
|
||||
my $keep_from = GetKeepDir($id);
|
||||
my $lock_from = GetLockedPageFile($id);
|
||||
my $joiner_from = $JoinerDir . '/' . GetPageDirectory($username) if $JoinerDir;
|
||||
my $joiner_email_from = $JoinerEmailDir . '/' . GetPageDirectory($username) if $JoinerEmailDir;
|
||||
my $referrer_from = $RefererDir . '/' . GetPageDirectory($id) if $RefererDir;
|
||||
*GetPageDirectory = *NewBalancedPageDirectoriesGetPageDirectory;
|
||||
my $page_to = GetPageFile($id);
|
||||
my $keep_to = GetKeepDir($id);
|
||||
my $lock_to = GetLockedPageFile($id);
|
||||
my $joiner_to = $JoinerDir . '/' . GetPageDirectory($username) if $JoinerDir;
|
||||
my $joiner_email_to = $JoinerEmailDir . '/' . GetPageDirectory($username) if $JoinerEmailDir;
|
||||
my $referrer_to = $RefererDir . '/' . GetPageDirectory($id) if $RefererDir;
|
||||
|
||||
# no clobbering
|
||||
if (! -f $page_to) {
|
||||
CreatePageDir($PageDir, $id);
|
||||
rename $page_from, $page_to || ReportError("Cannot rename $page_from");
|
||||
}
|
||||
if (-f $lock_from and ! -f $lock_to) {
|
||||
rename $lock_from, $lock_to || ReportError("Cannot rename $lock_from");
|
||||
}
|
||||
if (-d $keep_from and ! -d $keep_to) {
|
||||
CreateKeepDir($KeepDir, $id);
|
||||
rename $keep_from, $keep_to || ReportError("Cannot rename $keep_from");
|
||||
}
|
||||
if ($joiner_from and -d $joiner_from and ! -d $joiner_to) {
|
||||
CreatePageDir($JoinerDir, $id);
|
||||
rename $joiner_from, $joiner_to || ReportError("Cannot rename $joiner_from");
|
||||
}
|
||||
if ($joiner_email_from and -d $joiner_email_from and ! -d $joiner_email_to) {
|
||||
CreatePageDir($JoinerEmailDir, $id);
|
||||
rename $joiner_email_from, $joiner_email_to || ReportError("Cannot rename $joiner_email_from");
|
||||
}
|
||||
if ($referrer_from and -d $referrer_from and ! -d $referrer_to) {
|
||||
CreateRefererDir($RefererDir, $id);
|
||||
rename $referrer_from, $referrer_to || ReportError("Cannot rename $referrer_from");
|
||||
}
|
||||
}
|
||||
|
||||
# Delete empty subdirectories. Actually, attempt to delete all the
|
||||
# directories, depth first. It will simply fail for the non-empty
|
||||
# directories. http://www.perlmonks.org/?node_id=520791
|
||||
for my $parent ($PageDir, $KeepDir, $JoinerDir, $JoinerEmailDir, $RefererDir) {
|
||||
next unless $parent;
|
||||
finddepth(sub { rmdir $_ if -d }, $parent);
|
||||
}
|
||||
|
||||
ReleaseLock();
|
||||
}
|
||||
157
modules/ban-contributors.pl
Normal file
157
modules/ban-contributors.pl
Normal file
@@ -0,0 +1,157 @@
|
||||
# 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
|
||||
# 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/>.
|
||||
|
||||
=head1 Ban Contributors Extension
|
||||
|
||||
This module adds "Ban contributors" to the administration page. If you
|
||||
click on it, it will list all the recent contributors to the page
|
||||
you've been looking at. Each contributor (IP or hostname) will be
|
||||
compared to the list of regular expressions on the C<BannedHosts> page
|
||||
(see C<$BannedHosts>). If the contributor is already banned, this is
|
||||
mentioned. If the contributor is not banned, you'll see a button
|
||||
allowing you to ban him or her immediately. If you click the button,
|
||||
the IP or hostname will be added to the C<BannedHosts> page for you.
|
||||
|
||||
=cut
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/ban-contributors.pl">ban-contributors.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Ban_Contributors_Extension">Ban Contributors Extension</a></p>';
|
||||
|
||||
push(@MyAdminCode, \&BanMenu);
|
||||
|
||||
sub BanMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
if ($id and UserIsAdmin()) {
|
||||
push(@$menuref, ScriptLink('action=ban;id=' . UrlEncode($id),
|
||||
T('Ban contributors')));
|
||||
}
|
||||
}
|
||||
|
||||
$Action{ban} = \&DoBanHosts;
|
||||
|
||||
sub IsItBanned {
|
||||
my ($it, $regexps) = @_;
|
||||
my $re = undef;
|
||||
foreach my $regexp (@$regexps) {
|
||||
eval { $re = qr/$regexp/i; };
|
||||
if (defined($re) && $it =~ $re) {
|
||||
return $it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub DoBanHosts {
|
||||
my $id = shift;
|
||||
my $content = GetParam('content', '');
|
||||
my $host = GetParam('host', '');
|
||||
if ($content) {
|
||||
SetParam('text', GetPageContent($BannedContent)
|
||||
. $content . " # " . CalcDay($Now) . " "
|
||||
. NormalToFree($id) . "\n");
|
||||
SetParam('summary', NormalToFree($id));
|
||||
DoPost($BannedContent);
|
||||
} elsif ($host) {
|
||||
$host =~ s/\./\\./g;
|
||||
SetParam('text', GetPageContent($BannedHosts)
|
||||
. "^" . $host . " # " . CalcDay($Now) . " "
|
||||
. NormalToFree($id) . "\n");
|
||||
SetParam('summary', NormalToFree($id));
|
||||
DoPost($BannedHosts);
|
||||
} else {
|
||||
ValidIdOrDie($id);
|
||||
print GetHeader('', Ts('Ban Contributors to %s', NormalToFree($id)));
|
||||
SetParam('rcidonly', $id);
|
||||
SetParam('all', 1);
|
||||
SetParam('showedit', 1);
|
||||
my %contrib = ();
|
||||
for my $line (GetRcLines()) {
|
||||
$contrib{$line->[4]}->{$line->[5]} = 1 if $line->[4];
|
||||
}
|
||||
my @regexps = ();
|
||||
foreach (split(/\n/, GetPageContent($BannedHosts))) {
|
||||
if (/^\s*([^#]\S+)/) { # all lines except empty lines and comments, trim whitespace
|
||||
push(@regexps, $1);
|
||||
}
|
||||
}
|
||||
print '<div class="content ban">';
|
||||
foreach (sort(keys %contrib)) {
|
||||
my $name = $_;
|
||||
delete $contrib{$_}{''};
|
||||
$name .= " (" . join(", ", sort(keys(%{$contrib{$_}}))) . ")";
|
||||
if (IsItBanned($_, \@regexps)) {
|
||||
print $q->p(Ts("%s is banned", $name));
|
||||
} else {
|
||||
print GetFormStart(undef, 'get', 'ban'),
|
||||
GetHiddenValue('action', 'ban'),
|
||||
GetHiddenValue('id', $id),
|
||||
GetHiddenValue('host', $_),
|
||||
GetHiddenValue('recent_edit', 'on'),
|
||||
$q->p($name, $q->submit(T('Ban!'))), $q->end_form();
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
=head2 Rollback
|
||||
|
||||
If you are an admin and rolled back a single page, this extension will
|
||||
list the URLs your rollback removed (assuming that those URLs are part
|
||||
of the spam) and it will allow you to provide a regular expression
|
||||
that will be added to BannedHosts.
|
||||
|
||||
=cut
|
||||
|
||||
*OldBanContributorsWriteRcLog = *WriteRcLog;
|
||||
*WriteRcLog = *NewBanContributorsWriteRcLog;
|
||||
|
||||
sub NewBanContributorsWriteRcLog {
|
||||
my ($tag, $id, $to) = @_;
|
||||
if ($tag eq '[[rollback]]' and $id and $to > 0
|
||||
and $OpenPageName eq $id and UserIsAdmin()) {
|
||||
# we currently have the clean page loaded, so we need to reload
|
||||
# the spammed revision (there is a possible race condition here)
|
||||
my ($old) = GetTextRevision($Page{revision}-1, 1);
|
||||
my %urls = map {$_ => 1 } $old =~ /$UrlPattern/og;
|
||||
# we open the file again to force a load of the despammed page
|
||||
foreach my $url ($Page{text} =~ /$UrlPattern/og) {
|
||||
delete($urls{$url});
|
||||
}
|
||||
# we also remove any candidates that are already banned
|
||||
my @regexps = ();
|
||||
foreach (split(/\n/, GetPageContent($BannedContent))) {
|
||||
if (/^\s*([^#]\S+)/) { # all lines except empty lines and comments, trim whitespace
|
||||
push(@regexps, $1);
|
||||
}
|
||||
}
|
||||
foreach my $url (keys %urls) {
|
||||
delete($urls{$url}) if IsItBanned($url, \@regexps);
|
||||
}
|
||||
if (keys %urls) {
|
||||
print $q->p(Ts("These URLs were rolled back. Perhaps you want to add a regular expression to %s?",
|
||||
GetPageLink($BannedContent)));
|
||||
print $q->pre(join("\n", sort keys %urls));
|
||||
print GetFormStart(undef, 'get', 'ban'),
|
||||
GetHiddenValue('action', 'ban'),
|
||||
GetHiddenValue('id', $id),
|
||||
GetHiddenValue('recent_edit', 'on'),
|
||||
$q->p($q->label({-for=>'content'}, T('Regular expression:')), " ",
|
||||
$q->textfield(-name=>'content', -size=>30), " ",
|
||||
$q->submit(T('Ban!'))),
|
||||
$q->end_form();
|
||||
};
|
||||
print $q->p(T("Consider banning the hostname or IP number as well: "),
|
||||
ScriptLink('action=ban;id=' . UrlEncode($id), T('Ban contributors')));
|
||||
};
|
||||
return OldBanContributorsWriteRcLog(@_);
|
||||
}
|
||||
37
modules/ban-quick-editors.pl
Normal file
37
modules/ban-quick-editors.pl
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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
|
||||
# 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/>.
|
||||
#
|
||||
# This file must load before logbannedcontent.pl such that quick
|
||||
# editors will be logged.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/ban-quick-editors.pl">ban-quick-editors.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Banning_Quick_Editors">Banning Quick Editors</a></p>';
|
||||
|
||||
*BanQuickOldUserIsBanned = *UserIsBanned;
|
||||
*UserIsBanned = *BanQuickNewUserIsBanned;
|
||||
|
||||
sub BanQuickNewUserIsBanned {
|
||||
my $rule = BanQuickOldUserIsBanned(@_);
|
||||
if (not $rule
|
||||
and $SurgeProtection # need surge protection
|
||||
and GetParam('title')) {
|
||||
my $name = GetParam('username', GetRemoteAddress());
|
||||
my @entries = @{$RecentVisitors{$name}};
|
||||
# $entry[0] is $Now after AddRecentVisitor
|
||||
my $ts = $entries[1];
|
||||
if ($Now - $ts < 5) {
|
||||
return "fast editing spam bot";
|
||||
}
|
||||
}
|
||||
return $rule;
|
||||
}
|
||||
@@ -16,6 +16,8 @@ $ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git
|
||||
|
||||
push(@MyRules, \&bbCodeRule);
|
||||
|
||||
$RuleOrder{\&bbCodeRule} = 100; # must come after PortraitSupportRule
|
||||
|
||||
use vars qw($bbBlock);
|
||||
my %bbTitle = qw(h1 1 h2 1 h3 1 h4 1 h5 1 h6 1);
|
||||
|
||||
@@ -36,6 +38,8 @@ sub bbCodeRule {
|
||||
. qq{font-style: normal;"}); }
|
||||
elsif ($tag eq 's' or $tag eq 'strike') {
|
||||
return AddHtmlEnvironment('del'); }
|
||||
elsif ($tag eq 'tt') {
|
||||
return AddHtmlEnvironment('tt'); }
|
||||
elsif ($tag eq 'sub') {
|
||||
return AddHtmlEnvironment('sub'); }
|
||||
elsif ($tag eq 'sup') {
|
||||
@@ -99,7 +103,8 @@ sub bbCodeRule {
|
||||
%translate = qw{b b i i u em color em size em font span url a
|
||||
quote blockquote h1 h1 h2 h2 h3 h3 h4 h4 h5 h5
|
||||
h6 h6 center div left div right div list ul
|
||||
s del strike del sub sub sup sup highlight strong};
|
||||
s del strike del sub sub sup sup highlight strong
|
||||
tt tt};
|
||||
# closing a block level element closes all elements
|
||||
if ($bbBlock eq $translate{$tag}) {
|
||||
/\G([ \t]*\n)*/cg; # eat whitespace after closing block level element
|
||||
|
||||
0
modules/blockquote.pl
Executable file → Normal file
0
modules/blockquote.pl
Executable file → Normal file
0
modules/clustermap.pl
Executable file → Normal file
0
modules/clustermap.pl
Executable file → Normal file
0
modules/crossbar.pl
Executable file → Normal file
0
modules/crossbar.pl
Executable file → Normal file
@@ -1,20 +1,16 @@
|
||||
# Copyright (C) 2006 Alex Schroeder <alex@emacswiki.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 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 2 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/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/delete-all.pl">delete-all.pl</a></p>';
|
||||
|
||||
@@ -27,6 +23,7 @@ $DeleteAge = 172800; # 2*24*60*60
|
||||
|
||||
# All pages will be deleted after two days of inactivity!
|
||||
sub NewDelPageDeletable {
|
||||
return 1 if $Now - $Page{ts} > $DeleteAge;
|
||||
return 1 if $Now - $Page{ts} > $DeleteAge
|
||||
and not $LockOnCreation{$OpenPageName};
|
||||
return OldDelPageDeletable(@_);
|
||||
}
|
||||
|
||||
@@ -91,14 +91,14 @@ sub DespamBannedContent {
|
||||
foreach my $url (@urls) {
|
||||
if ($url =~ /($regexp)/i) {
|
||||
return Tss('Rule "%1" matched "%2" on this page.',
|
||||
QuoteHtml($regexp), $url);
|
||||
QuoteHtml($regexp), QuoteHtml($url));
|
||||
}
|
||||
}
|
||||
}
|
||||
# depends on strange-spam.pl!
|
||||
foreach (@DespamStrangeRules) {
|
||||
my $regexp = $_;
|
||||
if ($str =~ /($regexp)/) {
|
||||
if ($str =~ /($regexp)/i) {
|
||||
my $match = $1;
|
||||
$match =~ s/\n/ /g;
|
||||
return Tss('Rule "%1" matched "%2" on this page.',
|
||||
|
||||
@@ -44,6 +44,6 @@ sub DuckDuckGoSearchInit {
|
||||
}
|
||||
|
||||
sub DoDuckDuckGoSearch {
|
||||
my $search = GetParam('search', undef);
|
||||
my $search = UrlEncode(GetParam('search', undef));
|
||||
print $q->redirect({-uri=>"https://www.duckduckgo.com/?q=$search+site%3A$DuckDuckGoSearchDomain"});
|
||||
}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
# Copyright (C) 2005 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2005-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 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/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/dynamic-comments.pl">dynamic-comments.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Dynamic_Comments_Extension">Dynamic Comments Extension</a></p>';
|
||||
|
||||
@@ -51,7 +47,7 @@ sub DynamicCommentsNewGetPageLink {
|
||||
my $anchor = "id" . $num++;
|
||||
return qq{<a href="javascript:togglecomments('$anchor')">$title</a>}
|
||||
. '</p>' # close p before opening div
|
||||
. $q->div({-class=>commenthidden, -id=>$anchor},
|
||||
. $q->div({-class=>'commenthidden', -id=>$anchor},
|
||||
$page,
|
||||
$q->p(DynamicCommentsOldGetPageLink($id, T('Add Comment'))))
|
||||
. '<p>'; # open an empty p that will be closed in PrintAllPages
|
||||
|
||||
@@ -24,7 +24,20 @@ sub FixEncoding {
|
||||
OpenPage($id);
|
||||
my $text = $Page{text};
|
||||
utf8::decode($text);
|
||||
Save($id, $text, 'fix encoding', 1) if $text ne $Page{text};
|
||||
Save($id, $text, T('Fix character encoding'), 1) if $text ne $Page{text};
|
||||
ReleaseLock();
|
||||
ReBrowsePage($id);
|
||||
}
|
||||
|
||||
$Action{'fix-escaping'} = \&FixEscaping;
|
||||
|
||||
sub FixEscaping {
|
||||
my $id = shift;
|
||||
ValidIdOrDie($id);
|
||||
RequestLockOrError();
|
||||
OpenPage($id);
|
||||
my $text = UnquoteHtml($Page{text});
|
||||
Save($id, $text, T('Fix HTML escapes'), 1) if $text ne $Page{text};
|
||||
ReleaseLock();
|
||||
ReBrowsePage($id);
|
||||
}
|
||||
@@ -33,8 +46,12 @@ push(@MyAdminCode, \&FixEncodingMenu);
|
||||
|
||||
sub FixEncodingMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
if ($id) {
|
||||
if ($id && GetParam('username')) {
|
||||
push(@$menuref,
|
||||
ScriptLink('action=fix-encoding;id=' . UrlEncode($id), T('Fix page encoding')));
|
||||
ScriptLink('action=fix-encoding;id=' . UrlEncode($id),
|
||||
T('Fix character encoding')));
|
||||
push(@$menuref,
|
||||
ScriptLink('action=fix-escaping;id=' . UrlEncode($id),
|
||||
T('Fix HTML escapes')));
|
||||
}
|
||||
}
|
||||
|
||||
106
modules/form_timeout.pl
Normal file
106
modules/form_timeout.pl
Normal file
@@ -0,0 +1,106 @@
|
||||
# form_timeout.pl - a form timeout based anti-spam module for Oddmuse
|
||||
#
|
||||
# Copyright (C) 2014 Aki Goto <tyatsumi@gmail.com>
|
||||
#
|
||||
# Original code is in PHP from http://textcaptcha.com/really
|
||||
# by Rob Tuley <hello@rob.cx>. Used with permission.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
$ModulesDescription .= '<p>form_timeout_token.pl</p>';
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is an anti-spam module for Oddmuse using form timeout method.
|
||||
Edit permission is timed out in specified duration (default is 30 minutes)
|
||||
after viewing the edit form. When edit content is posted directly by a spam bot
|
||||
without viewing the edit form, edit will be denied.
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
$FormTimeoutSalt
|
||||
Mandatory. Token hash salt. Specify arbitrary string.
|
||||
Default = undef.
|
||||
|
||||
$FormTimeoutTimeout
|
||||
The form timeout in seconds.
|
||||
Default = 60 * 30 (30 minutes).
|
||||
|
||||
=cut
|
||||
|
||||
use vars qw($FormTimeoutSalt $FormTimeoutTimeout);
|
||||
use Digest::MD5 qw(md5_hex);
|
||||
|
||||
$FormTimeoutSalt = undef;
|
||||
$FormTimeoutTimeout = 60 * 30; # 30 minutes
|
||||
|
||||
push(@MyInitVariables, \&FormTimeoutInitVariables);
|
||||
|
||||
sub FormTimeoutInitVariables {
|
||||
if (!defined($FormTimeoutSalt)) {
|
||||
ReportError(T('Set $FormTimeoutSalt.'), '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
sub FormTimeoutGetHash {
|
||||
my ($when) = @_;
|
||||
return md5_hex($FormTimeoutSalt . $when);
|
||||
}
|
||||
|
||||
sub FormTimeoutGetToken {
|
||||
return $Now . '#' . FormTimeoutGetHash($Now);
|
||||
}
|
||||
|
||||
sub FormTimeoutGetTime {
|
||||
my ($token) = @_;
|
||||
my ($when, $hash) = split /#/, $token;
|
||||
my $valid_hash = FormTimeoutGetHash($when);
|
||||
if ($hash ne $valid_hash) {
|
||||
return '';
|
||||
}
|
||||
return $when;
|
||||
}
|
||||
|
||||
sub FormTimeoutCheck {
|
||||
my $token = GetParam('form_timeout_token', '');
|
||||
my $when = FormTimeoutGetTime($token);
|
||||
if ($when eq '' || $when < $Now - $FormTimeoutTimeout) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
*OldFormTimeoutGetFormStart = *GetFormStart;
|
||||
*GetFormStart = *NewFormTimeoutGetFormStart;
|
||||
|
||||
sub NewFormTimeoutGetFormStart {
|
||||
my ($ignore, $method, $class) = @_;
|
||||
my $form = OldFormTimeoutGetFormStart($ignore, $method, $class);
|
||||
my $token = FormTimeoutGetToken();
|
||||
$form .= $q->input({-type=>'hidden', -name=>'form_timeout_token',
|
||||
-value=>$token});
|
||||
return $form;
|
||||
}
|
||||
|
||||
*OldFormTimeoutDoEdit = *DoEdit;
|
||||
*DoEdit = *NewFormTimeoutDoEdit;
|
||||
|
||||
sub NewFormTimeoutDoEdit {
|
||||
my ($id, $newText, $preview) = @_;
|
||||
if (!FormTimeoutCheck()) {
|
||||
ReportError(T('Form Timeout'), '403 FORBIDDEN', undef,
|
||||
$q->p(Ts('Editing not allowed: %s is read-only.', NormalToFree($id))));
|
||||
}
|
||||
OldFormTimeoutDoEdit($id, $newText, $preview);
|
||||
}
|
||||
0
modules/hibernal.pl
Executable file → Normal file
0
modules/hibernal.pl
Executable file → Normal file
0
modules/htmllinks.pl
Executable file → Normal file
0
modules/htmllinks.pl
Executable file → Normal file
@@ -24,7 +24,7 @@ sub DoPrintableIndex {
|
||||
print GetHeader('', T('Index'), '');
|
||||
my @pages = PrintableIndexPages();
|
||||
my %hash;
|
||||
map { push(@{$hash{GetPageDirectory($_)}}, $_); } @pages;
|
||||
map { push(@{$hash{substr($_,0,1)}}, $_); } @pages;
|
||||
print '<div class="content printable index">';
|
||||
print $q->p($q->a({-name=>"top"}),
|
||||
map { $q->a({-href=>"#$_"}, $_); } sort keys %hash);
|
||||
|
||||
1131
modules/joiner.pl
Normal file
1131
modules/joiner.pl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ $ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git
|
||||
|
||||
$CookieParameters{interface} = '';
|
||||
|
||||
use vars qw($CurrentLanguage);
|
||||
use vars qw($CurrentLanguage $LoadLanguageDir);
|
||||
|
||||
my %library= ('bg' => 'bulgarian-utf8.pl',
|
||||
'de' => 'german-utf8.pl',
|
||||
@@ -60,6 +60,7 @@ sub LoadLanguage {
|
||||
foreach $_ (@prefs) {
|
||||
last if $Lang{$_} eq 'en'; # the default
|
||||
my $file = $library{$Lang{$_}};
|
||||
$file = "$LoadLanguageDir/$file" if defined $LoadLanguageDir;
|
||||
if (-r $file) {
|
||||
do $file;
|
||||
do "$ConfigFile-$Lang{$_}" if -r "$ConfigFile-$Lang{$_}";
|
||||
|
||||
@@ -54,7 +54,7 @@ You can change this expiry time by setting C<$LnCacheHours>.
|
||||
|
||||
=cut
|
||||
|
||||
push (MyMaintenance, \&LnMaintenance);
|
||||
push (@MyMaintenance, \&LnMaintenance);
|
||||
|
||||
sub LnMaintenance {
|
||||
if (opendir(DIR, $RssDir)) { # cleanup if they should expire anyway
|
||||
|
||||
2
modules/logbannedcontent.pl
Executable file → Normal file
2
modules/logbannedcontent.pl
Executable file → Normal file
@@ -45,6 +45,6 @@ sub LogWrite {
|
||||
my $rule = shift;
|
||||
my $id = $OpenPageName || GetId();
|
||||
AppendStringToFile($BannedFile,
|
||||
join("\t", TimeToW3($Now), $ENV{'REMOTE_ADDR'}, $id, $rule)
|
||||
join("\t", TimeToW3($Now), GetRemoteAddress(), $id, $rule)
|
||||
. "\n");
|
||||
}
|
||||
|
||||
@@ -71,19 +71,21 @@ sub MacFixEncoding {
|
||||
$UseGrep = 0 if GetParam('search', '') =~ /[x{0080}-\x{fffd}]/;
|
||||
|
||||
# the rest is only necessary if using namespaces.pl
|
||||
return unless defined %Namespaces;
|
||||
while (my ($key, $value) = each %Namespaces) {
|
||||
delete $Namespaces{$key};
|
||||
return unless %Namespaces;
|
||||
my %hash = ();
|
||||
for my $key (keys %Namespaces) {
|
||||
utf8::decode($key);
|
||||
$key = NFC($key);
|
||||
$Namespaces{$key} = $NamespaceRoot . '/' . $key . '/';
|
||||
$hash{$key} = $NamespaceRoot . '/' . $key . '/';
|
||||
}
|
||||
while (my ($key, $value) = each %InterSite) {
|
||||
delete $InterSite{$key};
|
||||
%Namespaces = %hash;
|
||||
%hash = ();
|
||||
for my $key (keys %InterSite) {
|
||||
utf8::decode($key);
|
||||
$key = NFC($key);
|
||||
$InterSite{$key} = $Namespaces{$key} if $Namespaces{$key};
|
||||
$hash{$key} = $Namespaces{$key} if $Namespaces{$key};
|
||||
}
|
||||
%InterSite = %hash;
|
||||
}
|
||||
|
||||
# for drafts.pl
|
||||
|
||||
138
modules/markdown-rule.pl
Normal file
138
modules/markdown-rule.pl
Normal file
@@ -0,0 +1,138 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 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 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/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/markdown-rule.pl">markdown-rule.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Markdown_Rule_Extension">Markdown Rule Extension</a></p>';
|
||||
|
||||
push(@MyRules, \&MarkdownRule);
|
||||
# Since we want this package to be a simple add-on, we try and avoid
|
||||
# all conflicts by going *last*. The use of # for numbered lists by
|
||||
# Usemod conflicts with the use of # for headings, for example.
|
||||
$RuleOrder{\&MarkdownRule} = 200;
|
||||
|
||||
# http://daringfireball.net/projects/markdown/syntax
|
||||
# https://help.github.com/articles/markdown-basics
|
||||
# https://help.github.com/articles/github-flavored-markdown
|
||||
|
||||
sub MarkdownRule {
|
||||
# atx headers
|
||||
if ($bol and m~\G(\s*\n)*(#{1,6})[ \t]*~cg) {
|
||||
my $header_depth = length($2);
|
||||
return CloseHtmlEnvironments()
|
||||
. AddHtmlEnvironment("h" . $header_depth);
|
||||
}
|
||||
# end atx header at a newline
|
||||
elsif ((InElement('h1') or InElement('h2') or InElement('h3') or
|
||||
InElement('h4') or InElement('h5') or InElement('h6'))
|
||||
and m/\G\n/cg) {
|
||||
return CloseHtmlEnvironments()
|
||||
. AddHtmlEnvironment("p");
|
||||
}
|
||||
# setext headers
|
||||
elsif ($bol and m/\G((\s*\n)*(.+?)[ \t]*\n(-+|=+)[ \t]*\n)/gc) {
|
||||
return CloseHtmlEnvironments()
|
||||
. (substr($4,0,1) eq '=' ? $q->h2($3) : $q->h3($3))
|
||||
. AddHtmlEnvironment('p');
|
||||
}
|
||||
# > blockquote
|
||||
# with continuation
|
||||
elsif ($bol and m/\G>/gc) {
|
||||
return CloseHtmlEnvironments()
|
||||
. AddHtmlEnvironment('blockquote');
|
||||
}
|
||||
# ***bold and italic***
|
||||
elsif (not InElement('strong') and not InElement('em') and m/\G\*\*\*/cg) {
|
||||
return AddHtmlEnvironment('em') . AddHtmlEnvironment('strong');
|
||||
}
|
||||
# **bold**
|
||||
elsif (m/\G\*\*/cg) {
|
||||
return AddOrCloseHtmlEnvironment('strong');
|
||||
}
|
||||
# *italic*
|
||||
elsif (m/\G\*/cg) {
|
||||
return AddOrCloseHtmlEnvironment('em');
|
||||
}
|
||||
# ~~strikethrough~~ (deleted)
|
||||
elsif (m/\G~~/cg) {
|
||||
return AddOrCloseHtmlEnvironment('del');
|
||||
}
|
||||
# - bullet list
|
||||
elsif ($bol and m/\G(\s*\n)*-[ \t]*/cg
|
||||
or InElement('li') and m/\G(\s*\n)+-[ \t]*/cg) {
|
||||
return CloseHtmlEnvironment('li')
|
||||
. OpenHtmlEnvironment('ul',1) . AddHtmlEnvironment('li');
|
||||
}
|
||||
# 1. numbered list
|
||||
elsif ($bol and m/\G(\s*\n)*\d+\.[ \t]*/cg
|
||||
or InElement('li') and m/\G(\s*\n)+\d+\.[ \t]*/cg) {
|
||||
return CloseHtmlEnvironment('li')
|
||||
. OpenHtmlEnvironment('ol',1) . AddHtmlEnvironment('li');
|
||||
}
|
||||
# beginning of a table
|
||||
elsif ($bol and !InElement('table') and m/\G\|/cg) {
|
||||
# warn pos . " beginning of a table";
|
||||
return OpenHtmlEnvironment('table',1)
|
||||
. AddHtmlEnvironment('tr')
|
||||
. AddHtmlEnvironment('th');
|
||||
}
|
||||
# end of a row and beginning of a new row
|
||||
elsif (InElement('table') and m/\G\|?\n\|/cg) {
|
||||
# warn pos . " end of a row and beginning of a new row";
|
||||
return CloseHtmlEnvironment('tr')
|
||||
. AddHtmlEnvironment('tr')
|
||||
. AddHtmlEnvironment('td');
|
||||
}
|
||||
# otherwise the table ends
|
||||
elsif (InElement('table') and m/\G\|?(\n|$)/cg) {
|
||||
# warn pos . " otherwise the table ends";
|
||||
return CloseHtmlEnvironment('table')
|
||||
. AddHtmlEnvironment('p');
|
||||
}
|
||||
# continuation of the first row
|
||||
elsif (InElement('th') and m/\G\|/cg) {
|
||||
# warn pos . " continuation of the first row";
|
||||
return CloseHtmlEnvironment('th')
|
||||
. AddHtmlEnvironment('th');
|
||||
}
|
||||
# continuation of other rows
|
||||
elsif (InElement('td') and m/\G\|/cg) {
|
||||
# warn pos . " continuation of other rows";
|
||||
return CloseHtmlEnvironment('td')
|
||||
. AddHtmlEnvironment('td');
|
||||
}
|
||||
# whitespace indentation = code
|
||||
elsif ($bol and m/\G(\s*\n)*( .+)\n?/gc) {
|
||||
my $str = substr($2, 4);
|
||||
while (m/\G( .*)\n?/gc) {
|
||||
$str .= "\n" . substr($1, 4);
|
||||
}
|
||||
return OpenHtmlEnvironment('pre',1) . $str; # always level 1
|
||||
}
|
||||
# ``` = code
|
||||
elsif ($bol and m/\G```[ \t]*\n(.*?)\n```[ \t]*(\n|$)/gcs) {
|
||||
return CloseHtmlEnvironments() . $q->pre($1)
|
||||
. AddHtmlEnvironment("p");
|
||||
}
|
||||
# [an example](http://example.com/ "Title")
|
||||
elsif (m/\G\[(.+?)\]\($FullUrlPattern(\s+"(.+?)")?\)/goc) {
|
||||
my ($text, $url, $title) = ($1, $2, $4);
|
||||
$url =~ /^($UrlProtocols)/;
|
||||
my %params;
|
||||
$params{-href} = $url;
|
||||
$params{-class} = "url $1";
|
||||
$params{-title} = $title if $title;
|
||||
return $q->a(\%params, $text);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2003–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
|
||||
@@ -303,8 +303,12 @@ resolved to the same target (the local page), which is unexpected.
|
||||
=cut
|
||||
|
||||
|
||||
push(@IndexOptions, ['near', T('Include near pages'), 0,
|
||||
\&ListNearPages]);
|
||||
# IndexOptions must be set in MyInitVariables for translations to
|
||||
# work.
|
||||
push(@MyInitVariables, sub {
|
||||
push(@IndexOptions, ['near', T('Include near pages'), 0,
|
||||
\&ListNearPages]);
|
||||
});
|
||||
|
||||
sub ListNearPages {
|
||||
my %pages = %NearSource;
|
||||
|
||||
@@ -33,10 +33,10 @@ $Action{$SelfBan} = \&DoSelfBan;
|
||||
|
||||
sub DoSelfBan {
|
||||
my $date = &TimeToText($Now);
|
||||
my $str = '^' . quotemeta($ENV{REMOTE_ADDR});
|
||||
my $str = '^' . quotemeta(GetRemoteAddress());
|
||||
OpenPage($BannedHosts);
|
||||
Save ($BannedHosts, $Page{text} . "\n\nself-ban on $date\n $str",
|
||||
Ts("Self-ban by %s", $ENV{REMOTE_ADDR}), 1); # minor edit
|
||||
Ts("Self-ban by %s", GetRemoteAddress()), 1); # minor edit
|
||||
ReportError(T("You have banned your own IP."));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ sub OpenProxyNewDoEdit {
|
||||
|
||||
sub BanOpenProxy {
|
||||
my ($force) = @_;
|
||||
my $ip = $ENV{REMOTE_ADDR};
|
||||
my $ip = GetRemoteAddress();
|
||||
my $limit = 60*60*24*30; # rescan after 30 days
|
||||
# Only check each IP address once a month
|
||||
my %proxy = split(/\s+/, ReadFile($OpenProxies));
|
||||
|
||||
0
modules/poetry.pl
Executable file → Normal file
0
modules/poetry.pl
Executable file → Normal file
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2012–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
|
||||
@@ -81,6 +81,16 @@ sub PrivatePageMessage {
|
||||
. T('supply the password now') . ']');
|
||||
}
|
||||
|
||||
# prevent unauthorized reading
|
||||
|
||||
# If we leave $Page{revision}, PrintWikiToHTML will save the new
|
||||
# PrivatePageMessage as the new page content. If we delete
|
||||
# $Page{revision}, the text shown will be based on $NewText. If we
|
||||
# have no $Page{ts} and no $Page{text}, PageDeletable will return 1.
|
||||
# As a workaround, we set a timestamp. Aging of the page doesn't
|
||||
# matter since the text starts with #PASSWORD and therefore cannot be
|
||||
# the empty string or $DeletedPage.
|
||||
|
||||
*OldPrivatePagesOpenPage = *OpenPage;
|
||||
*OpenPage = *NewPrivatePagesOpenPage;
|
||||
|
||||
@@ -88,11 +98,14 @@ sub NewPrivatePagesOpenPage {
|
||||
OldPrivatePagesOpenPage(@_);
|
||||
if (PrivatePageLocked($Page{text})) {
|
||||
%Page = (); # reset everything
|
||||
$Page{ts} = $Now;
|
||||
$NewText = PrivatePageMessage();
|
||||
}
|
||||
return $OpenPageName;
|
||||
}
|
||||
|
||||
# prevent reading of page content by other code
|
||||
|
||||
*OldPrivatePagesGetPageContent = *GetPageContent;
|
||||
*GetPageContent = *NewPrivatePagesGetPageContent;
|
||||
|
||||
@@ -104,6 +117,8 @@ sub NewPrivatePagesGetPageContent {
|
||||
return $text;
|
||||
}
|
||||
|
||||
# prevent reading of old revisions
|
||||
|
||||
*OldPrivatePagesGetTextRevision = *GetTextRevision;
|
||||
*GetTextRevision = *NewPrivatePagesGetTextRevision;
|
||||
|
||||
@@ -115,6 +130,8 @@ sub NewPrivatePagesGetTextRevision {
|
||||
return ($text, $revision);
|
||||
}
|
||||
|
||||
# hide #PASSWORD
|
||||
|
||||
push(@MyRules, \&PrivatePageRule);
|
||||
|
||||
sub PrivatePageRule {
|
||||
@@ -124,6 +141,8 @@ sub PrivatePageRule {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# prevent leaking of edit summary
|
||||
|
||||
*OldPrivatePagesGetSummary = *GetSummary;
|
||||
*GetSummary = *NewPrivatePagesGetSummary;
|
||||
|
||||
|
||||
@@ -78,7 +78,8 @@ sub NewQuestionaskerDoPost {
|
||||
print GetHeader('', T('Edit Denied'), undef, undef, '403 FORBIDDEN');
|
||||
print $q->p(T('You did not answer correctly.'));
|
||||
print GetFormStart(), QuestionaskerGetQuestion(1),
|
||||
(map { $q->hidden($_, '') }
|
||||
(map { $q->input({-type=>'hidden', -name=>$_,
|
||||
-value=>UnquoteHtml(GetParam($_))}) }
|
||||
qw(title text oldtime summary recent_edit aftertext)), $q->end_form;
|
||||
PrintFooter();
|
||||
# logging to the error log file of the server
|
||||
|
||||
@@ -231,7 +231,8 @@ sub NewReCaptchaDoPost {
|
||||
print $q->start_div({-class=>'error'});
|
||||
print $q->p(T('You did not answer correctly.'));
|
||||
print GetFormStart(), ReCaptchaGetQuestion(1),
|
||||
(map { $q->hidden($_, '') }
|
||||
(map { $q->input({-type=>'hidden', -name=>$_,
|
||||
-value=>UnquoteHtml(GetParam($_))}) }
|
||||
qw(title text oldtime summary recent_edit aftertext)), $q->end_form;
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
@@ -251,7 +252,7 @@ sub ReCaptchaCheckAnswer {
|
||||
eval "use Captcha::reCAPTCHA";
|
||||
my $result = Captcha::reCAPTCHA->new()->check_answer(
|
||||
$ReCaptchaPrivateKey,
|
||||
$ENV{'REMOTE_ADDR'},
|
||||
GetRemoteAddress(),
|
||||
GetParam('recaptcha_challenge_field'),
|
||||
GetParam('recaptcha_response_field')
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ sub RefererNewDeletePage {
|
||||
|
||||
sub GetRefererFile {
|
||||
my $id = shift;
|
||||
return $RefererDir . '/' . GetPageDirectory($id) . "/$id.rf";
|
||||
return "$RefererDir/$id.rf";
|
||||
}
|
||||
|
||||
sub ReadReferers {
|
||||
|
||||
0
modules/sabifoo.pl
Executable file → Normal file
0
modules/sabifoo.pl
Executable file → Normal file
0
modules/slideshow.pl
Executable file → Normal file
0
modules/slideshow.pl
Executable file → Normal file
@@ -1,20 +1,16 @@
|
||||
# Copyright (C) 2004, 2005, 2006 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2004-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 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/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/static-copy.pl">static-copy.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Static_Copy_Extension">Static Copy Extension</a></p>';
|
||||
|
||||
@@ -39,6 +35,7 @@ sub DoStatic {
|
||||
}
|
||||
CreateDir($StaticDir);
|
||||
%StaticFiles = ();
|
||||
print '<p>' unless $raw;
|
||||
StaticWriteFiles();
|
||||
print '</p>' unless $raw;
|
||||
PrintFooter() unless $raw;
|
||||
@@ -60,10 +57,20 @@ sub StaticMimeTypes {
|
||||
|
||||
sub StaticWriteFiles {
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $html = GetParam('html', 0);
|
||||
local *ScriptLink = *StaticScriptLink;
|
||||
local *GetDownloadLink = *StaticGetDownloadLink;
|
||||
# get rid of subscribe link in the footer by mail.pl
|
||||
local *GetCommentForm = *MailOldGetCommentForm if defined &MailNewGetCommentForm;
|
||||
foreach my $id (AllPagesList()) {
|
||||
StaticWriteFile($id);
|
||||
if ($StaticAlways > 1
|
||||
or $html
|
||||
or PageIsUploadedFile($id)) {
|
||||
StaticWriteFile($id, $html);
|
||||
}
|
||||
}
|
||||
if ($StaticAlways > 1 or $html) {
|
||||
StaticWriteCss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,18 +127,22 @@ sub StaticFileName {
|
||||
}
|
||||
|
||||
sub StaticWriteFile {
|
||||
my $id = shift;
|
||||
my ($id, $html) = @_;
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $html = GetParam('html', 1);
|
||||
my $filename = StaticFileName($id);
|
||||
OpenPage($id);
|
||||
my ($mimetype, $encoding, $data) = $Page{text} =~ /^\#FILE ([^ \n]+) ?([^ \n]*)\n(.*)/s;
|
||||
return unless $html or $data;
|
||||
open(F,"> $StaticDir/$filename") or ReportError(Ts('Cannot write %s', $filename));
|
||||
my ($mimetype, $encoding, $data) =
|
||||
$Page{text} =~ /^\#FILE ([^ \n]+) ?([^ \n]*)\n(.*)/s;
|
||||
open(F,"> $StaticDir/$filename")
|
||||
or ReportError(Ts('Cannot write %s', $filename));
|
||||
if ($data) {
|
||||
binmode(F);
|
||||
StaticFile($id, $mimetype, $data);
|
||||
} elsif ($html) {
|
||||
binmode(F, ':utf8');
|
||||
StaticHtml($id);
|
||||
} else {
|
||||
print "no data for ";
|
||||
}
|
||||
close(F);
|
||||
chmod 0644,"$StaticDir/$filename";
|
||||
@@ -141,7 +152,6 @@ sub StaticWriteFile {
|
||||
sub StaticFile {
|
||||
my ($id, $type, $data) = @_;
|
||||
require MIME::Base64;
|
||||
binmode(F);
|
||||
print F MIME::Base64::decode($data);
|
||||
}
|
||||
|
||||
@@ -200,7 +210,8 @@ EOT
|
||||
print F $q->div({-class=>'content'}, PageHtml($id)); # this reopens the page currently open
|
||||
# footer
|
||||
my $links = '';
|
||||
if ($OpenPageName !~ /^$CommentsPrefix/) { # fails if $CommentsPrefix is empty!
|
||||
if ($OpenPageName !~ /^$CommentsPrefix/ # fails if $CommentsPrefix is empty!
|
||||
and $IndexHash{$CommentsPrefix . $OpenPageName}) {
|
||||
$links .= ScriptLink(UrlEncode($CommentsPrefix . $OpenPageName),
|
||||
T('Comments on this page'));
|
||||
}
|
||||
@@ -216,6 +227,21 @@ EOT
|
||||
print F '</body></html>';
|
||||
}
|
||||
|
||||
sub StaticWriteCss {
|
||||
my $css;
|
||||
if ($StyleSheet) {
|
||||
$css = GetRaw($StyleSheet);
|
||||
}
|
||||
if (not $css and $IndexHash{$StyleSheetPage}) {
|
||||
$css = GetPageContent($StyleSheetPage);
|
||||
}
|
||||
if (not $css) {
|
||||
$css = GetRaw('http://www.oddmuse.org/default.css');
|
||||
}
|
||||
WriteStringToFile("$StaticDir/static.css", $css) if $css;
|
||||
chmod 0644,"$StaticDir/static.css";
|
||||
}
|
||||
|
||||
*StaticFilesOldSave = *Save;
|
||||
*Save = *StaticFilesNewSave;
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ sub StaticNewDoRollback {
|
||||
} elsif (!UserCanEdit($id, 1)) {
|
||||
print Ts('Editing not allowed for %s.', $id), $q->br();
|
||||
} else {
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne $ENV{REMOTE_ADDR}));
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne GetRemoteAddress()));
|
||||
StaticDeleteFile($id);
|
||||
print Ts('%s rolled back', GetPageLink($id)), ($ts ? ' ' . Ts('to %s', TimeToText($to)) : ''), $q->br();
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ sub TagCloud {
|
||||
my $min = 0;
|
||||
my %count = ();
|
||||
foreach my $encoded_tag (grep !/^_/, keys %h) {
|
||||
$count{$encoded_tag} = split(/$FS/, $h{$encoded_tag});
|
||||
$count{$encoded_tag} = split(/$FS/, UrlDecode($h{$encoded_tag}));
|
||||
$max = $count{$encoded_tag} if $count{$encoded_tag} > $max;
|
||||
$min = $count{$encoded_tag} if not $min or $count{$encoded_tag} < $min;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ Go!
|
||||
(minor)
|
||||
(次要的)
|
||||
rollback
|
||||
回復
|
||||
回滾
|
||||
new
|
||||
新增
|
||||
All changes for %s
|
||||
@@ -168,13 +168,13 @@ Revision %s
|
||||
Contributors to %s
|
||||
編寫 %s 的作者
|
||||
Missing target for rollback.
|
||||
找不到要回復的目標
|
||||
找不到要回滾的目標
|
||||
Target for rollback is too far back.
|
||||
要回復的目標已太久以前了。
|
||||
要回滾的目標已太久以前了。
|
||||
A username is required for ordinary users.
|
||||
需使用普通用戶名稱
|
||||
Rolling back changes
|
||||
回復修改
|
||||
回滾修改
|
||||
The two revisions are the same.
|
||||
二個版本相同
|
||||
Editing not allowed for %s.
|
||||
@@ -182,9 +182,9 @@ Editing not allowed for %s.
|
||||
Rollback of %s would restore banned content.
|
||||
|
||||
Rollback to %s
|
||||
回復至 %s
|
||||
回滾至 %s
|
||||
%s rolled back
|
||||
%s 已回復
|
||||
%s 已回滾
|
||||
to %s
|
||||
到 %s
|
||||
Index of all pages
|
||||
@@ -644,7 +644,7 @@ SPAM 廣告頁面
|
||||
Cannot find revision %s.
|
||||
無法取得版本 %s 。
|
||||
Revert to revision %1: %2
|
||||
回復至版本 %1: %2
|
||||
回滾至版本 %1: %2
|
||||
Marked as %s.
|
||||
標記為 %s 。
|
||||
Cannot find unspammed revision.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -672,7 +672,7 @@ ordinary changes
|
||||
normale Änderungen
|
||||
Matching page names:
|
||||
Übereinstimmende Seitennamen:
|
||||
Fix page encoding
|
||||
Fix character encoding
|
||||
Zeichenkodierung korrigieren
|
||||
no summary available
|
||||
keine Zusammenfassug vorhanden
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
103
modules/upgrade.pl
Normal file
103
modules/upgrade.pl
Normal file
@@ -0,0 +1,103 @@
|
||||
# Copyright (C) 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 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/>.
|
||||
|
||||
use utf8;
|
||||
|
||||
# We are now running in InitModules. InitVariables will be called later.
|
||||
# We want to prevent any calls to GetPageContent and the like.
|
||||
|
||||
*UpgradeOldInitVariables = *InitVariables;
|
||||
*InitVariables = *UpgradeNewInitVariables;
|
||||
|
||||
sub UpgradeNewInitVariables {
|
||||
$InterMap = undef;
|
||||
$LocalNamesPage = undef;
|
||||
$SidebarName = undef;
|
||||
$NearMap = undef;
|
||||
UpgradeOldInitVariables(@_);
|
||||
}
|
||||
|
||||
*DoBrowseRequest = *DoUpgrade;
|
||||
|
||||
sub DoUpgrade {
|
||||
|
||||
# The only thing allowed besides upgrading is login and unlock
|
||||
my $action = lc(GetParam('action', ''));
|
||||
if (($action eq 'password' or $action eq 'unlock')
|
||||
and $Action{$action}) {
|
||||
return &{$Action{$action}}($id);
|
||||
}
|
||||
|
||||
# Only admins may upgrade
|
||||
ReportError(T('Upgrading Database'),
|
||||
'403 FORBIDDEN', 0,
|
||||
$q->p(T('This operation is restricted to administrators only...'))
|
||||
. $q->p(ScriptLink('action=password', T('Login'), 'password')))
|
||||
unless UserIsAdmin();
|
||||
|
||||
ReportError(T('Upgrading Database'),
|
||||
'403 FORBIDDEN', 0,
|
||||
$q->p(T('Did the previous upgrade end with an error? A lock was left behind.'))
|
||||
. $q->p(ScriptLink('action=unlock', T('Unlock wiki'), 'unlock')))
|
||||
unless RequestLockDir('main');
|
||||
|
||||
print GetHeader('', T('Upgrading Database')),
|
||||
$q->start_div({-class=>'content upgrade'});
|
||||
|
||||
if (-e $IndexFile) {
|
||||
unlink $IndexFile;
|
||||
}
|
||||
|
||||
print "<p>Renaming files...";
|
||||
|
||||
for my $ns ('', keys %InterSite) {
|
||||
next unless -d "$DataDir/$ns";
|
||||
print "<br />\n<strong>$ns</strong>" if $ns;
|
||||
for my $dir ($PageDir, $KeepDir, $RefererDir, $JoinerDir, $JoinerEmailDir) {
|
||||
next unless $dir;
|
||||
$dir =~ s/^$DataDir/$DataDir\/$ns/ if $ns;
|
||||
for my $old (bsd_glob("$dir/*/*", bsd_glob("$dir/*/.*"))) {
|
||||
next if $old eq '.' or $old eq '..';
|
||||
my $oldname = $old;
|
||||
utf8::decode($oldname);
|
||||
print "<br />\n$oldname";
|
||||
my $new = $old;
|
||||
$new =~ s!/([A-Z]|other)/!/!;
|
||||
if ($old eq $new) {
|
||||
print " does not fit the pattern!";
|
||||
} elsif (not rename $old, $new) {
|
||||
my $newname = $new;
|
||||
utf8::decode($newname);
|
||||
print " → $newname failed!";
|
||||
}
|
||||
}
|
||||
for my $subdir (grep(/\/([A-Z]|other)$/, bsd_glob("$dir/*"))) {
|
||||
rmdir $subdir; # ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print $q->end_p();
|
||||
|
||||
if (rename "$ModuleDir/upgrade.pl", "$ModuleDir/upgrade.done") {
|
||||
print $q->p(T("Upgrade complete."))
|
||||
} else {
|
||||
print $q->p(T("Upgrade complete. Please remove $ModuleDir/upgade.pl, now."))
|
||||
}
|
||||
|
||||
ReleaseLock();
|
||||
|
||||
print $q->end_p(), $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
44
t/balanced-page-directories.t
Normal file
44
t/balanced-page-directories.t
Normal file
@@ -0,0 +1,44 @@
|
||||
# Copyright (C) 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 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 10;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
clear_pages();
|
||||
|
||||
update_page('HomePage', 'Das ist ein Ei.');
|
||||
ok(-f GetPageFile('HomePage'), 'page file');
|
||||
|
||||
update_page('HomePage', 'This is an egg.');
|
||||
ok(-f GetKeepFile('HomePage', 1), 'keep file');
|
||||
|
||||
update_page('ホームページ', 'これが卵です。');
|
||||
ok(-f GetPageFile('ホームページ'), 'Japanese page file');
|
||||
|
||||
update_page($StyleSheetPage, '/* nothing to see */', '', 0, 1);
|
||||
ok(-f GetPageFile($StyleSheetPage), 'locked page file');
|
||||
ok(-f GetLockedPageFile($StyleSheetPage), 'page lock');
|
||||
|
||||
add_module('balanced-page-directories.pl');
|
||||
|
||||
test_page(get_page('HomePage'), 'This is an egg.');
|
||||
ok(-f GetKeepFile('HomePage', 1), 'keep file');
|
||||
test_page(get_page('ホームページ'), 'これが卵です。');
|
||||
ok(-f GetLockedPageFile($StyleSheetPage), 'page lock');
|
||||
|
||||
# create a new page
|
||||
test_page(update_page('サイトマップ', '日本語ユーザーに向けて'),
|
||||
'日本語ユーザーに向けて');
|
||||
55
t/ban-contributors.t
Normal file
55
t/ban-contributors.t
Normal file
@@ -0,0 +1,55 @@
|
||||
# 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
|
||||
# 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 21;
|
||||
|
||||
clear_pages();
|
||||
|
||||
add_module('ban-contributors.pl');
|
||||
$localhost = 'pyrobombus';
|
||||
$ENV{'REMOTE_ADDR'} = $localhost;
|
||||
|
||||
update_page('Test', 'insults');
|
||||
test_page_negative(get_page('action=admin id=Test'), 'Ban contributors');
|
||||
test_page(get_page('action=admin id=Test pwd=foo'), 'Ban contributors');
|
||||
test_page(get_page('action=ban id=Test pwd=foo'), 'pyrobombus', 'Ban!');
|
||||
test_page(get_page('action=ban id=Test host=pyrobombus pwd=foo'),
|
||||
'Location: http://localhost/wiki.pl/BannedHosts');
|
||||
test_page(get_page('BannedHosts'), 'pyrobombus', 'Test');
|
||||
|
||||
clear_pages();
|
||||
add_module('ban-contributors.pl');
|
||||
|
||||
update_page('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);
|
||||
|
||||
update_page('Test', "http://spam/amoxil/ http://spam/doxycycline/");
|
||||
test_page(get_page("action=rollback id=Test to=$to pwd=foo"),
|
||||
'Rolling back changes', 'These URLs were rolled back',
|
||||
'amoxil', 'doxycycline', 'Consider banning the hostname');
|
||||
test_page(get_page("action=ban id=Test content=amoxil pwd=foo"),
|
||||
'Location: http://localhost/wiki.pl/BannedContent');
|
||||
test_page(get_page('BannedContent'), 'amoxil', 'Test');
|
||||
update_page('Test', "http://spam/amoxil/ http://spam/doxycycline/");
|
||||
$page = get_page("action=rollback id=Test to=$to pwd=foo");
|
||||
test_page($page, 'Rolling back changes', 'These URLs were rolled back',
|
||||
'doxycycline');
|
||||
test_page_negative($page, 'amoxil');
|
||||
34
t/ban-quick-editors.t
Normal file
34
t/ban-quick-editors.t
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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
|
||||
# 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 3;
|
||||
|
||||
clear_pages();
|
||||
|
||||
# switch it back on again
|
||||
AppendStringToFile($ConfigFile, "\$SurgeProtection = 1;\n");
|
||||
# make sure the visitors.log is filled
|
||||
$ENV{'REMOTE_ADDR'} = '127.0.0.1';
|
||||
|
||||
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');
|
||||
sleep 5;
|
||||
test_page(update_page('Test', 'edit succeeded'),
|
||||
'edit succeeded');
|
||||
32
t/comments.t
32
t/comments.t
@@ -1,24 +1,20 @@
|
||||
# Copyright (C) 2006 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 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 => 35;
|
||||
use Test::More tests => 36;
|
||||
clear_pages();
|
||||
|
||||
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
|
||||
@@ -89,11 +85,17 @@ test_page(get_page('Comments_on_Yadda'), 'This is my comment\.', '-- Alex');
|
||||
test_page(get_page('action=rc raw=1'), 'title: Comments on Yadda',
|
||||
'description: This is my comment.', 'generator: Alex');
|
||||
|
||||
# homepage
|
||||
get_page('title=Comments_on_Yadda', 'aftertext=This%20is%20another%20comment.',
|
||||
'username=Alex', 'homepage=http%3a%2f%2fwww%2eoddmuse%2eorg%2f');
|
||||
xpath_test(get_page('Comments_on_Yadda'),
|
||||
'//p[contains(text(),"This is my comment.")]',
|
||||
'//a[@class="url http outside"][@href="http://www.oddmuse.org/"][text()="Alex"]');
|
||||
# variant without protocol
|
||||
get_page('title=Comments_on_Yadda', 'aftertext=This%20is%20another%20comment.',
|
||||
'username=Berta', 'homepage=alexschroeder%2ech');
|
||||
xpath_test(get_page('Comments_on_Yadda'),
|
||||
'//a[@class="url http outside"][@href="http://alexschroeder.ch"][text()="Berta"]');
|
||||
|
||||
my $textarea = '//textarea[@name="aftertext"][@id="aftertext"]';
|
||||
xpath_test(get_page('Comments_on_Yadda'), $textarea);
|
||||
|
||||
@@ -50,15 +50,15 @@ test_page(get_page('action=browse id=HomePage username=Alex'),
|
||||
SKIP: {
|
||||
|
||||
eval { require LWP::UserAgent; };
|
||||
skip "LWP::UserAgent not installed", 5 if $@;
|
||||
skip "LWP::UserAgent not installed", 7 if $@;
|
||||
|
||||
eval { require HTTP::Cookies; };
|
||||
skip "HTTP::Cookies not installed", 5 if $@;
|
||||
skip "HTTP::Cookies not installed", 7 if $@;
|
||||
|
||||
my $wiki = 'http://localhost/cgi-bin/wiki.pl';
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my $response = $ua->get("$wiki?action=version");
|
||||
skip("No wiki running at $wiki", 5)
|
||||
skip("No wiki running at $wiki", 7)
|
||||
unless $response->is_success;
|
||||
|
||||
$ua = LWP::UserAgent->new;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env perl
|
||||
# Copyright (C) 2008 Weakish Jiang <weakish@gmail.com>
|
||||
# Copyright (C) 2009 Alex Schroeder <alex@gnu.com>
|
||||
# Copyright (C) 2009-2013 Alex Schroeder <alex@gnu.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
@@ -56,8 +56,6 @@ H<sub>2</sub>O
|
||||
<dl><dt><strong>dt1</strong></dt><dd>dd1</dd></dl>
|
||||
; {{{dt1}}}\n:dd1
|
||||
<dl><dt><code>dt1</code></dt><dd>dd1</dd></dl>
|
||||
;[[http://www.toto.com|toto]] \n :Site of my friend Toto
|
||||
<dl><dt><a class="url http outside" href="http://www.toto.com">toto</a></dt><dd>Site of my friend Toto</dd></dl>
|
||||
; {{{[[http://www.toto.com|toto]]}}} \n : Site of my friend Toto
|
||||
<dl><dt><code>[[http://www.toto.com|toto]]</code></dt><dd>Site of my friend Toto</dd></dl>
|
||||
; what if we have {{{[[http://example.com]]}}} and {{{[[ftp://example.org]]}}}\n: And {{{[[http://example.net]]}}}
|
||||
@@ -75,6 +73,8 @@ H<sub>2</sub>O
|
||||
EOT
|
||||
|
||||
xpath_run_tests(split('\n',<<'EOT'));
|
||||
;[[http://www.toto.com|toto]] \n :Site of my friend Toto
|
||||
//dl/dt[a[@class="url http outside"][@href="http://www.toto.com"][text()="toto"]]/following-sibling::dd[text()="Site of my friend Toto"]
|
||||
##http://example.com##
|
||||
//code/a[@class="url http"][@href="http://example.com"][text()="http://example.com"]
|
||||
##[[wiki page]] will work##
|
||||
|
||||
8
t/css.t
8
t/css.t
@@ -24,13 +24,13 @@ clear_pages();
|
||||
|
||||
# Default
|
||||
xpath_test(get_page('HomePage'),
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/oddmuse.css"]');
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]');
|
||||
|
||||
# StyleSheetPage
|
||||
update_page('css', "em { font-weight: bold; }", 'some css', 0, 1);
|
||||
$page = get_page('HomePage');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/oddmuse.css"]');
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]');
|
||||
xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]');
|
||||
|
||||
@@ -38,7 +38,7 @@ xpath_test($page,
|
||||
AppendStringToFile($ConfigFile, "\$StyleSheet = 'http://example.org/test.css';\n");
|
||||
$page = get_page('HomePage');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/oddmuse.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]');
|
||||
xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://example.org/test.css"]');
|
||||
@@ -46,7 +46,7 @@ xpath_test($page,
|
||||
# Parameter
|
||||
$page = get_page('action=browse id=HomePage css=http://example.org/my.css');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/oddmuse.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://example.org/test.css"]');
|
||||
xpath_test($page,
|
||||
|
||||
@@ -39,7 +39,7 @@ update_page('ExpiredPage', "Still more spam from http://example.com.");
|
||||
|
||||
update_page('BannedContent', " example\\.com\n", 'required', 0, 1);
|
||||
|
||||
unlink("$DataDir/keep/E/ExpiredPage/1.kp")
|
||||
unlink("$DataDir/keep/ExpiredPage/1.kp")
|
||||
or die "Cannot delete kept revision: $!";
|
||||
|
||||
my $page = get_page('action=spam');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2012–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 => 12;
|
||||
use Test::More tests => 22;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
clear_pages();
|
||||
@@ -26,14 +26,19 @@ test_page_negative(get_page('action=admin'), 'action=fix-encoding');
|
||||
|
||||
# make sure no menu shows up if the page does not exists
|
||||
|
||||
test_page(get_page('action=admin id=foo'), 'action=fix-encoding;id=foo');
|
||||
test_page_negative(get_page('action=admin id=foo'),
|
||||
'action=fix-encoding;id=foo',
|
||||
'action=fix-escaping;id=foo');
|
||||
|
||||
# make sure nothing is saved if the page does not exist
|
||||
|
||||
test_page(get_page('action=fix-encoding id=Example'),
|
||||
'Location: http://localhost/wiki.pl/Example');
|
||||
|
||||
test_page_negative(get_page('action=rc showedit=1'), 'fix encoding');
|
||||
test_page(get_page('action=fix-escaping id=Example'),
|
||||
'Location: http://localhost/wiki.pl/Example');
|
||||
|
||||
test_page_negative(get_page('action=rc all=1 showedit=1'), 'fix');
|
||||
|
||||
# make sure nothing is saved if there is no change
|
||||
|
||||
@@ -43,14 +48,25 @@ test_page(update_page('Example', 'Pilgerstätte für die Göttin'),
|
||||
test_page(get_page('action=fix-encoding id=Example'),
|
||||
'Location: http://localhost/wiki.pl/Example');
|
||||
|
||||
test_page_negative(get_page('action=rc showedit=1'), 'fix encoding');
|
||||
test_page(get_page('action=fix-escaping id=Example'),
|
||||
'Location: http://localhost/wiki.pl/Example');
|
||||
|
||||
# the menu shows up if the page exists
|
||||
test_page_negative(get_page('action=rc all=1 showedit=1'),
|
||||
'Fix Character encoding');
|
||||
|
||||
test_page(get_page('action=admin id=Example'),
|
||||
'action=fix-encoding;id=Example');
|
||||
# the menu doesn't show up if the page exists
|
||||
|
||||
# here is an actual page you need to fix
|
||||
test_page_negative(get_page('action=admin id=Example'),
|
||||
'action=fix-encoding;id=Example',
|
||||
'action=fix-escaping;id=Example');
|
||||
|
||||
# the menu does show up if the page exists and a username is set
|
||||
|
||||
test_page(get_page('action=admin id=Example username=Alex'),
|
||||
'action=fix-encoding;id=Example',
|
||||
'action=fix-escaping;id=Example');
|
||||
|
||||
# here is an actual page with a character encoding error you need to fix
|
||||
|
||||
test_page(update_page('Example', 'Pilgerstätte für die Göttin',
|
||||
'borked encoding'),
|
||||
@@ -62,4 +78,20 @@ test_page(get_page('action=fix-encoding id=Example'),
|
||||
test_page(get_page('Example'),
|
||||
'Pilgerstätte für die Göttin');
|
||||
|
||||
test_page(get_page('action=rc showedit=1'), 'fix encoding');
|
||||
test_page(get_page('action=rc showedit=1'),
|
||||
'Fix character encoding');
|
||||
|
||||
# here is an actual page with an HTML escaping error you need to fix
|
||||
|
||||
test_page(update_page('Example', '&lt;b&gt;bold&lt;/b&gt;',
|
||||
'borked escaping'),
|
||||
'&lt;b&gt;bold&lt;/b&gt;');
|
||||
|
||||
test_page(get_page('action=fix-escaping id=Example'),
|
||||
'Location: http://localhost/wiki.pl/Example');
|
||||
|
||||
test_page(get_page('Example'),
|
||||
'<b>bold</b>');
|
||||
|
||||
test_page(get_page('action=rc showedit=1'),
|
||||
'Fix HTML escapes');
|
||||
|
||||
13
t/journal3.t
13
t/journal3.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2011 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2011–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 => 34;
|
||||
use Test::More tests => 35;
|
||||
|
||||
clear_pages();
|
||||
|
||||
@@ -38,7 +38,7 @@ test_page($page, '2011-12-17', '2011-12-16', '2011-12-15',
|
||||
test_page_negative($page, '2011-12-12', '2011-12-11', '2011-12-10',
|
||||
'2011-12-09', '2011-12-08');
|
||||
|
||||
xpath_test($page, '//a[@href="http://localhost/wiki.pl?action=more;num=5;regexp=^\d\d\d\d-\d\d-\d\d;search=;mode=;offset=5"][text()="More..."]');
|
||||
xpath_test($page, '//a[@href="http://localhost/wiki.pl?action=more;num=5;regexp=%5e%5cd%5cd%5cd%5cd-%5cd%5cd-%5cd%5cd;search=;mode=;offset=5"][text()="More..."]');
|
||||
|
||||
# check that the link for more actually works
|
||||
|
||||
@@ -60,7 +60,12 @@ test_page($page, '2011-12-13', '2011-12-12', '2011-12-11',
|
||||
'2011-12-10', '2011-12-09');
|
||||
xpath_test($page, '//a[text()="More..."]');
|
||||
|
||||
# one las check
|
||||
# one last check
|
||||
|
||||
xpath_test_negative(get_page("action=more num=5 offset=6 "),
|
||||
'//a[text()="More..."]');
|
||||
|
||||
# check for unescaped URL
|
||||
|
||||
$page = update_page('Plus', "Using a plus:\n\n<journal 5 \"^.+\">");
|
||||
xpath_test($page, '//a[text()="More..."][@href="http://localhost/wiki.pl?action=more;num=5;regexp=%5e.%2b;search=;mode=;offset=5"]');
|
||||
|
||||
97
t/markdown-rule.t
Normal file
97
t/markdown-rule.t
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env perl
|
||||
# Copyright (C) 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 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 34;
|
||||
clear_pages();
|
||||
|
||||
add_module('markdown-rule.pl');
|
||||
|
||||
# ApplyRules strips trailing newlines, so write tests accordingly.
|
||||
run_tests(split(/\n/,<<'EOT'));
|
||||
1. one
|
||||
<ol><li>one</li></ol>
|
||||
2. one
|
||||
2. one
|
||||
1. one\n2. two
|
||||
<ol><li>one</li><li>two</li></ol>
|
||||
1. one\n\n2. two
|
||||
<ol><li>one</li><li>two</li></ol>
|
||||
- one
|
||||
<ul><li>one</li></ul>
|
||||
- one\n-- Alex
|
||||
<ul><li>one</li><li>- Alex</li></ul>
|
||||
- one\n\n- Alex
|
||||
<ul><li>one</li><li>Alex</li></ul>
|
||||
this is ***bold italic*** yo!
|
||||
this is <em><strong>bold italic</strong></em> yo!
|
||||
this is **bold**
|
||||
this is <strong>bold</strong>
|
||||
**bold**
|
||||
<strong>bold</strong>
|
||||
*italic*
|
||||
<em>italic</em>
|
||||
foo\nbar
|
||||
foo bar
|
||||
foo\n===\nbar
|
||||
<h2>foo</h2><p>bar</p>
|
||||
foo\n---\nbar
|
||||
<h3>foo</h3><p>bar</p>
|
||||
foo\n=== bar
|
||||
foo === bar
|
||||
foo\n=\nbar
|
||||
<h2>foo</h2><p>bar</p>
|
||||
# foo
|
||||
<h1>foo</h1>
|
||||
## foo
|
||||
<h2>foo</h2>
|
||||
### foo
|
||||
<h3>foo</h3>
|
||||
#### foo
|
||||
<h4>foo</h4>
|
||||
##### foo
|
||||
<h5>foo</h5>
|
||||
###### foo
|
||||
<h6>foo</h6>
|
||||
####### foo
|
||||
<h6># foo</h6>
|
||||
## foo ##
|
||||
<h2>foo ##</h2>
|
||||
bar\n##foo\nbar
|
||||
bar <h2>foo</h2><p>bar</p>
|
||||
```\nfoo\n```\nbar
|
||||
<pre>foo</pre><p>bar</p>
|
||||
```\nfoo\n```
|
||||
<pre>foo</pre>
|
||||
```\nfoo\n``` bar
|
||||
``` foo ``` bar
|
||||
|a|b|\n|c|d|\nbar
|
||||
<table><tr><th>a</th><th>b</th></tr><tr><td>c</td><td>d</td></tr></table><p>bar</p>
|
||||
|a|b|\n|c|d|
|
||||
<table><tr><th>a</th><th>b</th></tr><tr><td>c</td><td>d</td></tr></table>
|
||||
|a
|
||||
<table><tr><th>a</th></tr></table>
|
||||
foo ~~bar~~
|
||||
foo <del>bar</del>
|
||||
EOT
|
||||
|
||||
xpath_run_tests(split('\n',<<'EOT'));
|
||||
[an example](http://example.com/ "Title")
|
||||
//a[@class="url http"][@href="http://example.com/"][@title="Title"][text()="an example"]
|
||||
[an example](http://example.com/)
|
||||
//a[@class="url http"][@href="http://example.com/"][text()="an example"]
|
||||
EOT
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 73;
|
||||
use Test::More tests => 77;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
clear_pages();
|
||||
@@ -41,6 +41,15 @@ test_page(get_page('action=browse id=Test ns=Muu'),
|
||||
'<title>Wiki Muu: Test</title>',
|
||||
'<p>Mooo!</p>');
|
||||
|
||||
# history
|
||||
xpath_test(get_page('action=history id=Test ns=Muu'),
|
||||
'//table[@class="history"]/tr/td/a[text()="Revision 1"]',
|
||||
'//h1[text()="History of Test"]');
|
||||
|
||||
test_page(get_page('action=history id=Test ns=Muu raw=1'),
|
||||
"link: http://localhost/wiki.pl/Muu\\?action=history;id=Test;raw=1\n",
|
||||
"link: http://localhost/wiki.pl/Muu/Test\n");
|
||||
|
||||
# search
|
||||
$page = get_page('/Muu?search=Mooo raw=1');
|
||||
test_page($page, 'description: Mooo!');
|
||||
|
||||
4042
t/oddmuse-2.2.6.pl
Normal file
4042
t/oddmuse-2.2.6.pl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2012–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 => 28;
|
||||
use Test::More tests => 29;
|
||||
|
||||
clear_pages();
|
||||
add_module('private-pages.pl');
|
||||
@@ -32,6 +32,10 @@ test_page_negative(update_page('Privat', "#PASSWORD foo\nCats have secrets.\n",
|
||||
'Cats have secrets');
|
||||
test_page($redirect, 'Status: 302');
|
||||
|
||||
# is not deleted by maintenance job
|
||||
my $page = get_page('action=maintain');
|
||||
test_page($page, 'Privat');
|
||||
|
||||
# read it with password
|
||||
my $page = get_page('action=browse id=Privat pwd=foo');
|
||||
test_page_negative($page, 'This page is password protected');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2008 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 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
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 14;
|
||||
use Test::More tests => 15;
|
||||
|
||||
clear_pages();
|
||||
add_module('questionasker.pl');
|
||||
@@ -55,3 +55,7 @@ test_page(update_page('test', 'answer new question', undef, undef, undef,
|
||||
test_page(get_page('Comments_on_test'),
|
||||
'label for="username"',
|
||||
'say hi');
|
||||
|
||||
# test for corruption of Unicode text
|
||||
update_page('Umlaute', '<Schröder>');
|
||||
test_page($redirect, '<Schröder>')
|
||||
|
||||
20
t/rc.t
20
t/rc.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006, 2007, 2008, 2009 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006–20013 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 => 83;
|
||||
use Test::More tests => 86;
|
||||
|
||||
clear_pages();
|
||||
|
||||
@@ -23,14 +23,22 @@ clear_pages();
|
||||
# with nothing appropriate in them.
|
||||
|
||||
test_page(get_page('action=rc raw=1'), 'title: Wiki');
|
||||
WriteStringToFile($RcFile, "1${FS}test${FS}${FS}test${FS}${FS}${FS}1${FS}${FS}\n");
|
||||
# ts, id, minor, summary, host, username, revision, languages, cluster
|
||||
WriteStringToFile($RcFile, "1${FS}test${FS}${FS}test${FS}127.0.0.1${FS}${FS}1${FS}${FS}\n");
|
||||
test_page_negative(get_page('action=rc raw=1'), 'title: test');
|
||||
test_page(get_page('action=rc raw=1 from=1'), 'title: Wiki', 'title: test',
|
||||
'description: test', 'link: http://localhost/wiki.pl/test',
|
||||
'description: test', 'generator: 127.0.0.1',
|
||||
'link: http://localhost/wiki.pl/test',
|
||||
'last-modified: 1970-01-01T00:00Z', 'revision: 1');
|
||||
ok(rename($RcFile, $RcOldFile), "renamed $RcFile to $RcOldFile");
|
||||
|
||||
test_page(get_page('action=maintain'),
|
||||
'Moving part of the RecentChanges log file',
|
||||
'Moving 1 log entries');
|
||||
|
||||
# make sure it was anonymized
|
||||
test_page(get_page('action=rc raw=1 from=1'), 'title: Wiki', 'title: test',
|
||||
'description: test', 'link: http://localhost/wiki.pl/test',
|
||||
'description: test', 'generator: Anonymous',
|
||||
'link: http://localhost/wiki.pl/test',
|
||||
'last-modified: 1970-01-01T00:00Z', 'revision: 1');
|
||||
|
||||
# Test that newlines are in fact stripped
|
||||
|
||||
38
t/recaptcha.t
Normal file
38
t/recaptcha.t
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 5;
|
||||
use utf8; # test data is UTF-8 and it matters
|
||||
|
||||
clear_pages();
|
||||
$ENV{'REMOTE_ADDR'}='127.0.0.1';
|
||||
add_module('recaptcha.pl');
|
||||
|
||||
# The recaptcha module used to corrupt UTF-8 encoding and HTML
|
||||
# escaping.
|
||||
|
||||
# non-existing page and no permission
|
||||
test_page(get_page('title=SandBox text="<b>K%C3%BChlschrank</b>"'),
|
||||
'Status: 403',
|
||||
'<b>Kühlschrank</b>');
|
||||
# update it as an admin
|
||||
test_page(update_page('SandBox', '<b>Kühlschrank</b>', undef, undef, 1),
|
||||
'<b>Kühlschrank</b>');
|
||||
# existing page and no permission
|
||||
test_page(get_page('title=SandBox text="<b>K%C3%BChlschrank-test</b>"'),
|
||||
'Status: 403',
|
||||
'<b>Kühlschrank-test</b>');
|
||||
50
t/rollback-extras.t
Normal file
50
t/rollback-extras.t
Normal file
@@ -0,0 +1,50 @@
|
||||
# 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
|
||||
# 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 3;
|
||||
|
||||
# simple, single page rollback
|
||||
|
||||
# ($ts, $id, $minor, $summary, $host, $username, $revision, $languages, $cluster)
|
||||
# ($ts, '[[rollback]]', $to, $page)
|
||||
|
||||
clear_pages();
|
||||
WriteStringToFile ($RcFile, "1Aone1\n"); # original
|
||||
AppendStringToFile($RcFile, "2Atwo2\n"); # to be rolled back
|
||||
AppendStringToFile($RcFile, "3A0one3\n"); # back to the original
|
||||
AppendStringToFile($RcFile, "3[[rollback]]1A\n"); # rollback marker
|
||||
|
||||
local $/ = "\n"; # undef in test.pl
|
||||
|
||||
my @lines = GetRcLines(1);
|
||||
is(scalar(@lines), 1, "starting situation contains just one line");
|
||||
is($lines[0][0], 3, "simple rollback starts with 3");
|
||||
|
||||
AppendStringToFile($RcFile, "4Athree4\n");
|
||||
|
||||
# print "GetRcLines\n";
|
||||
# for my $line (GetRcLines(1)) {
|
||||
# my ($ts, $id, $minor, $summary) = @$line;
|
||||
# print "$ts, $id, $minor, $summary\n";
|
||||
# }
|
||||
|
||||
SetParam('all', 1);
|
||||
my @lines = GetRcLines(1);
|
||||
is(scalar(@lines), 4, "using all=1, see all four major revisions");
|
||||
|
||||
|
||||
# This could be an interesting test framework.
|
||||
|
||||
@@ -70,8 +70,8 @@ update_page('NicePage', 'Evil content.', 'vandal one');
|
||||
update_page('OtherPage', 'Other evil content.', 'another vandal');
|
||||
update_page('NicePage', 'Bad content.', 'vandal two');
|
||||
update_page('EvilPage', 'Spam!', 'vandal three');
|
||||
update_page('AnotherEvilPage', 'More Spam!', 'vandal four');
|
||||
update_page('AnotherEvilPage', 'Still More Spam!', 'vandal five');
|
||||
update_page('AnotherEvilPage', 'More Minor Spam!', 'vandal four', 1);
|
||||
update_page('AnotherEvilPage', 'Still More Minor Spam!', 'vandal five', 1);
|
||||
update_page('MinorPage', 'Ramtatam', 'testerror', 1);
|
||||
|
||||
test_page(get_page('NicePage'), 'Bad content');
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
# Copyright (C) 2007, 2008, 2009 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2007–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 => 16;
|
||||
use utf8;
|
||||
use Test::More tests => 36;
|
||||
clear_pages();
|
||||
|
||||
add_module('static-copy.pl');
|
||||
@@ -111,7 +112,40 @@ xpath_test(update_page('HomePage', "Static: [[image:Trogs]]"),
|
||||
. '[@src="/static/Trogs.svgz"]'
|
||||
. '[@alt="Trogs"]');
|
||||
|
||||
# Make sure spaces are translated to underscores (fixed in image.pl)
|
||||
# delete the static pages and regenerate it
|
||||
ok(unlink("$DataDir/static/Trogs.svgz"), "Deleted $DataDir/static/Trogs.svgz");
|
||||
ok(unlink("$DataDir/static/Logo.png"), "Deleted $DataDir/static/Logo.png");
|
||||
|
||||
# StaticWriteFiles must write uploaded files only (since $StaticAlways = 1)
|
||||
$page = get_page('action=static raw=1 pwd=foo');
|
||||
test_page($page, "Trogs", "Logo"); # Remember, a rollback has restored Logo.png
|
||||
test_page_negative($page, "HomePage"); # since it an ordinary page
|
||||
|
||||
ok(-s "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz has nonzero size");
|
||||
ok(-s "$DataDir/static/Logo.png", "$DataDir/static/Logo.png has nonzero size");
|
||||
ok(! -e "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html does not exist");
|
||||
|
||||
# force generation of HomePage using html=1
|
||||
$page = get_page('action=static raw=1 pwd=foo html=1');
|
||||
test_page($page, "Trogs", "Logo", "HomePage");
|
||||
test_page_negative($page, "no data"); # must not skip HomePage!
|
||||
|
||||
ok(-s "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz has nonzero size");
|
||||
ok(-s "$DataDir/static/Logo.png", "$DataDir/static/Logo.png has nonzero size");
|
||||
ok(-s "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html has nonzero size");
|
||||
|
||||
# check that links between pages work as expected
|
||||
xpath_test(update_page("Test", "Link to HomePage. Testing Ümlaute."),
|
||||
'//a[text()="HomePage"][@href="http://localhost/wiki.pl/HomePage"]');
|
||||
test_page(get_page('action=static raw=1 pwd=foo html=1'), 'Test');
|
||||
xpath_test_file("$DataDir/static/Test.html",
|
||||
'//a[text()="HomePage"][@href="HomePage.html"]');
|
||||
test_file("$DataDir/static/Test.html",
|
||||
"Ümlaute");
|
||||
test_file("$DataDir/static/static.css",
|
||||
"body { background-color:#FFF; color:#000; margin:1em 2em; }");
|
||||
|
||||
# make sure spaces are translated to underscores (fixed in image.pl)
|
||||
add_module('image.pl');
|
||||
|
||||
# Now, create real pages. First, we'll use the ordinary image link to
|
||||
|
||||
36
t/test.pl
36
t/test.pl
@@ -32,7 +32,16 @@ $UseConfig = 0; # don't read module files
|
||||
$DataDir = 'test-data';
|
||||
$ENV{WikiDataDir} = $DataDir;
|
||||
require 'wiki.pl';
|
||||
$ENV{PATH} = '/usr/local/bin:' . $ENV{PATH}; # location of perl?
|
||||
|
||||
# Try to guess which Perl we should be using. Since we loaded wiki.pl,
|
||||
# our $ENV{PATH} is set to /bin:/usr/bin in order to find diff and
|
||||
# grep.
|
||||
if ($ENV{PERLBREW_PATH}) {
|
||||
$ENV{PATH} = $ENV{PERLBREW_PATH} . ':' . $ENV{PATH};
|
||||
} elsif (-f '/usr/local/bin/perl') {
|
||||
$ENV{PATH} = '/usr/local/bin:' . $ENV{PATH};
|
||||
}
|
||||
|
||||
Init();
|
||||
use vars qw($redirect);
|
||||
|
||||
@@ -99,7 +108,7 @@ sub get_page {
|
||||
sub name {
|
||||
$_ = shift;
|
||||
s/\n/\\n/g;
|
||||
$_ = '...' . substr($_, -60) if length > 63;
|
||||
$_ = '...' . substr($_, -67) if length > 70;
|
||||
return $_;
|
||||
}
|
||||
|
||||
@@ -149,6 +158,18 @@ sub test_page {
|
||||
}
|
||||
}
|
||||
|
||||
# one file, many tests
|
||||
sub test_file {
|
||||
my ($file, @tests) = @_;
|
||||
if (open(F, '< :utf8', $file)) {
|
||||
local $/ = undef;
|
||||
test_page(<F>, @tests);
|
||||
close(F);
|
||||
} else {
|
||||
warn "cannot open $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
# one string, many negative tests
|
||||
sub test_page_negative {
|
||||
my $page = shift;
|
||||
@@ -199,6 +220,17 @@ sub xpath_test {
|
||||
xpath_do(sub { shift > 0; }, "No Matches\n", @_);
|
||||
}
|
||||
|
||||
sub xpath_test_file {
|
||||
my ($file, @tests) = @_;
|
||||
if (open(F, '< :utf8', $file)) {
|
||||
local $/ = undef;
|
||||
xpath_test(<F>, @tests);
|
||||
close(F);
|
||||
} else {
|
||||
warn "cannot open $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub negative_xpath_test {
|
||||
xpath_do(sub { shift == 0; }, "Unexpected Matches\n", @_);
|
||||
}
|
||||
|
||||
119
t/upgrade.t
Normal file
119
t/upgrade.t
Normal file
@@ -0,0 +1,119 @@
|
||||
# Copyright (C) 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 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 37;
|
||||
|
||||
clear_pages();
|
||||
|
||||
# Create a 2.2.6 wiki first.
|
||||
$page = qx(perl t/oddmuse-2.2.6.pl title=Test text=Hello);
|
||||
test_page($page, "Status: 302 Found");
|
||||
$page = qx(perl t/oddmuse-2.2.6.pl title=Test text=Hallo);
|
||||
test_page($page, "Status: 302 Found");
|
||||
$page = qx(perl t/oddmuse-2.2.6.pl action=pagelock id=Test set=1 pwd=foo);
|
||||
test_page($page, "created");
|
||||
|
||||
ok(-d "$PageDir/T", "T page directory exists");
|
||||
ok(-d "$KeepDir/T", "T keep directory exists");
|
||||
|
||||
add_module('upgrade.pl');
|
||||
|
||||
ok(-f "$ModuleDir/upgrade.pl", "upgrade.pl was installed");
|
||||
|
||||
test_page(get_page('Test'), 'Upgrading Database', 'action=password');
|
||||
|
||||
test_page(get_page('action=password'), 'You are a normal user');
|
||||
|
||||
$page = get_page('action=upgrade pwd=foo');
|
||||
|
||||
test_page($page,
|
||||
'page/T/Test.pg',
|
||||
'page/T/Test.lck',
|
||||
'keep/T/Test',
|
||||
'Upgrade complete');
|
||||
|
||||
test_page_negative($page, 'failed',
|
||||
'does not fit the pattern',
|
||||
'Please remove');
|
||||
|
||||
ok(! -d "$PageDir/T", "T directory has disappeared");
|
||||
ok(! -d "$KeepDir/T", "T keep directory has disappeared");
|
||||
ok(! -d $LockDir . 'main', "Lock was released");
|
||||
ok(! -f "$ModuleDir/upgrade.pl", "upgrade.pl was renamed");
|
||||
|
||||
test_page(get_page('action=browse id=Test revision=1'), 'Hello');
|
||||
|
||||
test_page(get_page('Test'), 'Hallo');
|
||||
|
||||
# you cannot run it again after a successful run
|
||||
test_page(get_page('action=upgrade pwd=foo'),
|
||||
'Invalid action parameter');
|
||||
|
||||
# reinstall it and run it again
|
||||
add_module('upgrade.pl');
|
||||
|
||||
test_page(get_page('action=upgrade pwd=foo'),
|
||||
'Upgrade complete');
|
||||
|
||||
# set up a wiki with namespaces
|
||||
|
||||
clear_pages();
|
||||
|
||||
add_module('namespaces.pl');
|
||||
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=Test text=Main%20Hello),
|
||||
"Status: 302 Found");
|
||||
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=Test text=Space%20Hello ns=Space),
|
||||
"Status: 302 Found");
|
||||
|
||||
add_module('upgrade.pl');
|
||||
|
||||
$page = get_page('action=upgrade pwd=foo');
|
||||
|
||||
test_page($page,
|
||||
'<strong>Space</strong>',
|
||||
'Upgrade complete');
|
||||
|
||||
test_page_negative($page, 'failed');
|
||||
|
||||
test_page(get_page('Test'), 'Main Hello');
|
||||
test_page(get_page("'/Space/Test?'"), 'Space Hello');
|
||||
|
||||
# Install modules which use GetPageContent in their init routine.
|
||||
|
||||
clear_pages();
|
||||
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$InterMap text=$InterMap),
|
||||
$InterMap);
|
||||
|
||||
add_module('localnames.pl');
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$LocalNamesPage text=$LocalNamesPage),
|
||||
$LocalNamesPage);
|
||||
|
||||
add_module('sidebar.pl');
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$SidebarName text=$SidebarName),
|
||||
$SidebarName);
|
||||
|
||||
add_module('near-links.pl');
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$NearMap text=$NearMap),
|
||||
$NearMap);
|
||||
|
||||
add_module('upgrade.pl');
|
||||
test_page_negative(get_page('HomePage'), 'Cannot open');
|
||||
test_page(get_page('action=upgrade pwd=foo'),
|
||||
'Upgrade complete');
|
||||
@@ -54,6 +54,7 @@ sub rewrite {
|
||||
my %page = split(/$FS1/, read_file($file), -1);
|
||||
%section = split(/$FS2/, $page{text_default}, -1);
|
||||
%text = split(/$FS3/, $section{data}, -1);
|
||||
$file =~ s!/([A-Z]|other)/!/!;
|
||||
$file =~ s/\.db$/.pg/ or die "Invalid page name\n";
|
||||
print "Writing $file...\n";
|
||||
write_page_file($file);
|
||||
@@ -65,6 +66,7 @@ sub rewrite {
|
||||
print "Reading refer $file...\n";
|
||||
my $data = read_file($file);
|
||||
$data =~ s/$FS1/$NewFS/g;
|
||||
$file =~ s!/([A-Z]|other)/!/!;
|
||||
$file =~ s/\.rb$/.rf/ or die "Invalid page name\n";
|
||||
print "Writing $file...\n";
|
||||
write_file($file, $data);
|
||||
@@ -77,6 +79,7 @@ sub rewrite {
|
||||
my $data = read_file($file);
|
||||
my @list = split(/$FS1/, $data);
|
||||
my $out = $file;
|
||||
$out =~ s!/([A-Z]|other)/!/!;
|
||||
$out =~ s/\.kp$// or die "Invalid keep name\n";
|
||||
# We introduce a new variable $dir, here, instead of using $out,
|
||||
# because $out will be part of the filename later on, and the
|
||||
|
||||
221
wiki.pl
221
wiki.pl
@@ -39,7 +39,8 @@ use vars qw($RssLicense $RssCacheHours @RcDays $TempDir $LockDir $DataDir
|
||||
$KeepDir $PageDir $RcOldFile $IndexFile $BannedContent $NoEditFile $BannedHosts
|
||||
$ConfigFile $FullUrl $SiteName $HomePage $LogoUrl $RcDefault $RssDir
|
||||
$IndentLimit $RecentTop $RecentLink $EditAllowed $UseDiff $KeepDays $KeepMajor
|
||||
$EmbedWiki $BracketText $UseConfig $UseLookup $AdminPass $EditPass $NetworkFile
|
||||
$EmbedWiki $BracketText $UseConfig $UseLookup $AdminPass $EditPass
|
||||
$PassHashFunction $PassSalt $NetworkFile
|
||||
$BracketWiki $FreeLinks $WikiLinks $SummaryHours $FreeLinkPattern $RCName
|
||||
$RunCGI $ShowEdits $LinkPattern $RssExclude $InterLinkPattern $MaxPost $UseGrep
|
||||
$UrlPattern $UrlProtocols $ImageExtensions $InterSitePattern $FS $CookieName
|
||||
@@ -49,7 +50,7 @@ $RssImageUrl $ReadMe $RssRights $BannedCanRead $SurgeProtection $TopLinkBar
|
||||
$LanguageLimit $SurgeProtectionTime $SurgeProtectionViews $DeletedPage
|
||||
%Languages $InterMap $ValidatorLink %LockOnCreation $RssStyleSheet
|
||||
%CookieParameters @UserGotoBarPages $NewComment $HtmlHeaders $StyleSheetPage
|
||||
$ConfigPage $ScriptName $CommentsPrefix @UploadTypes $AllNetworkFiles
|
||||
$ConfigPage $ScriptName $CommentsPrefix $CommentsPattern @UploadTypes $AllNetworkFiles
|
||||
$UsePathInfo $UploadAllowed $LastUpdate $PageCluster %PlainTextPages
|
||||
$RssInterwikiTranslate $UseCache $Counter $ModuleDir $FullUrlPattern
|
||||
$SummaryDefaultLength $FreeInterLinkPattern %InvisibleCookieParameters
|
||||
@@ -95,12 +96,14 @@ $StyleSheetPage = 'css'; # Page for CSS sheet
|
||||
$LogoUrl = ''; # URL for site logo ('' for no logo)
|
||||
$NotFoundPg = ''; # Page for not-found links ('' for blank pg)
|
||||
|
||||
$NewText = "This page is empty.\n"; # New page text
|
||||
$NewComment = "Add your comment here.\n"; # New comment text
|
||||
$NewText = T('This page is empty.') . "\n"; # New page text
|
||||
$NewComment = T('Add your comment here:') . "\n"; # New comment text
|
||||
|
||||
$EditAllowed = 1; # 0 = no, 1 = yes, 2 = comments pages only, 3 = comments only
|
||||
$AdminPass = '' unless defined $AdminPass; # Whitespace separated passwords.
|
||||
$EditPass = '' unless defined $EditPass; # Whitespace separated passwords.
|
||||
$PassHashFunction = '' unless defined $PassHashFunction; # Name of the function to create hashes
|
||||
$PassSalt = '' unless defined $PassSalt; # Salt will be added to any password before hashing
|
||||
|
||||
$BannedHosts = 'BannedHosts'; # Page for banned hosts
|
||||
$BannedCanRead = 1; # 1 = banned cannot edit, 0 = banned cannot read
|
||||
@@ -151,6 +154,7 @@ $TopLinkBar = 1; # 1 = add a goto bar at the top of the page
|
||||
$UserGotoBar = ''; # HTML added to end of goto bar
|
||||
$ValidatorLink = 0; # 1 = Link to the W3C HTML validator service
|
||||
$CommentsPrefix = ''; # prefix for comment pages, eg. 'Comments_on_' to enable
|
||||
$CommentsPattern = undef; # regex used to match comment pages
|
||||
$HtmlHeaders = ''; # Additional stuff to put in the HTML <head> section
|
||||
$IndentLimit = 20; # Maximum depth of nested lists
|
||||
$LanguageLimit = 3; # Number of matches req. for each language
|
||||
@@ -159,7 +163,7 @@ $DocumentHeader = qq(<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN")
|
||||
. qq( "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n)
|
||||
. qq(<html xmlns="http://www.w3.org/1999/xhtml">);
|
||||
# Checkboxes at the end of the index.
|
||||
@IndexOptions = (['pages', T('Include normal pages'), 1, \&AllPagesList]);
|
||||
@IndexOptions = ();
|
||||
# Display short comments below the GotoBar for special days
|
||||
# Example: %SpecialDays = ('1-1' => 'New Year', '1-2' => 'Next Day');
|
||||
%SpecialDays = ();
|
||||
@@ -289,6 +293,8 @@ sub InitVariables { # Init global session variables for mod_perl!
|
||||
(\$HomePage, \$RCName, \$BannedHosts, \$InterMap, \$StyleSheetPage, \$CommentsPrefix,
|
||||
\$ConfigPage, \$NotFoundPg, \$RssInterwikiTranslate, \$BannedContent, \$RssExclude, );
|
||||
$CommentsPrefix .= '_' if $add_space;
|
||||
$CommentsPattern = "^$CommentsPrefix(.*)"
|
||||
unless defined $CommentsPattern or not $CommentsPrefix;
|
||||
@UserGotoBarPages = ($HomePage, $RCName) unless @UserGotoBarPages;
|
||||
my @pages = sort($BannedHosts, $StyleSheetPage, $ConfigPage, $InterMap,
|
||||
$RssInterwikiTranslate, $BannedContent);
|
||||
@@ -306,6 +312,7 @@ sub InitVariables { # Init global session variables for mod_perl!
|
||||
@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) {
|
||||
my $result = &$sub;
|
||||
$Message .= $q->p($@) if $@;
|
||||
@@ -464,12 +471,12 @@ sub ApplyRules {
|
||||
}
|
||||
Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
|
||||
($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
|
||||
} elsif ($bol && m/\G(\<journal(\s+(\d*))?(\s+"(.*?)")?(\s+(reverse|past|future))?(\s+search\s+(.*))?\>[ \t]*\n?)/cgi) {
|
||||
} elsif ($bol && m/\G(\<journal(\s+(\d*)(,(\d*))?)?(\s+"(.*?)")?(\s+(reverse|past|future))?(\s+search\s+(.*))?\>[ \t]*\n?)/cgi) {
|
||||
# <journal 10 "regexp"> includes 10 pages matching regexp
|
||||
Clean(CloseHtmlEnvironments());
|
||||
Dirty($1);
|
||||
my ($oldpos, $old_) = (pos, $_); # remember these because of the call to PrintJournal()
|
||||
PrintJournal($3, $5, $7, 0, $9); # no offset
|
||||
PrintJournal($3, $5, $7, $9, 0, $11); # no offset
|
||||
Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
|
||||
($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
|
||||
} elsif ($bol && m/\G(\<rss(\s+(\d*))?\s+(.*?)\>[ \t]*\n?)/cgis) {
|
||||
@@ -818,7 +825,7 @@ sub GetRaw {
|
||||
sub DoJournal {
|
||||
print GetHeader(undef, T('Journal'));
|
||||
print $q->start_div({-class=>'content'});
|
||||
PrintJournal(map { GetParam($_, ''); } qw(num regexp mode offset search));
|
||||
PrintJournal(map { GetParam($_, ''); } qw(num num regexp mode offset search));
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
@@ -828,9 +835,10 @@ sub JournalSort { $b cmp $a }
|
||||
sub PrintJournal {
|
||||
return if $CollectingJournal; # avoid infinite loops
|
||||
local $CollectingJournal = 1;
|
||||
my ($num, $regexp, $mode, $offset, $search) = @_;
|
||||
my ($num, $numMore, $regexp, $mode, $offset, $search) = @_;
|
||||
$regexp = '^\d\d\d\d-\d\d-\d\d' unless $regexp;
|
||||
$num = 10 unless $num;
|
||||
$numMore = $num unless $numMore;
|
||||
$offset = 0 unless $offset;
|
||||
# FIXME: Should pass filtered list of pages to SearchTitleAndBody to save time?
|
||||
my @pages = sort JournalSort (grep(/$regexp/, $search ? SearchTitleAndBody($search) : AllPagesList()));
|
||||
@@ -859,7 +867,9 @@ sub PrintJournal {
|
||||
print $q->start_div({-class=>'journal'});
|
||||
my $next = $offset + PrintAllPages(1, 1, $num, @pages[$offset .. $#pages]);
|
||||
print $q->end_div();
|
||||
print $q->p({-class=>'more'}, ScriptLink("action=more;num=$num;regexp=$regexp;search=$search;mode=$mode;offset=$next", T('More...'), 'more')) if $pages[$next];
|
||||
$regexp = UrlEncode($regexp);
|
||||
$search = UrlEncode($search);
|
||||
print $q->p({-class=>'more'}, ScriptLink("action=more;num=$numMore;regexp=$regexp;search=$search;mode=$mode;offset=$next", T('More...'), 'more')) if $pages[$next];
|
||||
}
|
||||
|
||||
sub PrintAllPages {
|
||||
@@ -879,17 +889,21 @@ sub PrintAllPages {
|
||||
$q->h1($links ? GetPageLink($id)
|
||||
: $q->a({-name=>$id}, UrlEncode(FreeToNormal($id))));
|
||||
PrintPageHtml();
|
||||
if ($comments and $id !~ /^$CommentsPrefix/o) {
|
||||
print $q->p({-class=>'comment'},
|
||||
GetPageLink($CommentsPrefix . $id,
|
||||
T('Comments on this page')));
|
||||
}
|
||||
PrintPageCommentsLink($id, $comments);
|
||||
print $q->end_div();
|
||||
$n++; # pages actually printed
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
sub PrintPageCommentsLink {
|
||||
my ($id, $comments) = @_;
|
||||
if ($comments and $CommentsPattern and $id !~ /$CommentsPattern/o) {
|
||||
print $q->p({-class=>'comment'},
|
||||
GetPageLink($CommentsPrefix . $id, T('Comments on this page')));
|
||||
}
|
||||
}
|
||||
|
||||
sub RSS {
|
||||
return if $CollectingJournal; # avoid infinite loops when using full=1
|
||||
local $CollectingJournal = 1;
|
||||
@@ -1379,7 +1393,7 @@ sub BrowseResolvedPage {
|
||||
print $q->redirect({-uri=>$resolved});
|
||||
} elsif ($class && $class eq 'alias') { # an anchor was found instead of a page
|
||||
ReBrowsePage($resolved);
|
||||
} elsif (not $resolved and $NotFoundPg and $id !~ /^$CommentsPrefix/o) { # custom page-not-found message
|
||||
} elsif (not $resolved and $NotFoundPg and $id !~ /$CommentsPattern/o) { # custom page-not-found message
|
||||
BrowsePage($NotFoundPg);
|
||||
} elsif ($resolved) { # an existing page was found
|
||||
BrowsePage($resolved, GetParam('raw', 0));
|
||||
@@ -1542,30 +1556,30 @@ sub LatestChanges {
|
||||
sub StripRollbacks {
|
||||
my @result = @_;
|
||||
if (not (GetParam('all', 0) or GetParam('rollback', 0))) { # strip rollbacks
|
||||
my ($skip_to, $end, %rollback);
|
||||
my (%rollback);
|
||||
for (my $i = $#result; $i >= 0; $i--) {
|
||||
# some fields have a different meaning if looking at rollbacks
|
||||
my $ts = $result[$i][0];
|
||||
my $id = $result[$i][1];
|
||||
my $target_ts = $result[$i][2];
|
||||
my $target_id = $result[$i][3];
|
||||
# strip global rollbacks
|
||||
if ($skip_to and $ts <= $skip_to) {
|
||||
splice(@result, $i + 1, $end - $i);
|
||||
$skip_to = 0;
|
||||
} elsif ($id eq '[[rollback]]') {
|
||||
if ($id eq '[[rollback]]') {
|
||||
if ($target_id) {
|
||||
$rollback{$target_id} = $target_ts; # single page rollback
|
||||
splice(@result, $i, 1); # strip marker
|
||||
} else {
|
||||
$end = $i unless $skip_to;
|
||||
$skip_to = $target_ts; # cumulative rollbacks!
|
||||
my $end = $i;
|
||||
while ($ts > $target_ts and $i > 0) {
|
||||
$i--; # quickly skip all these lines
|
||||
$ts = $result[$i][0];
|
||||
}
|
||||
splice(@result, $i + 1, $end - $i);
|
||||
$i++; # compensate $i-- in for loop
|
||||
}
|
||||
} elsif ($rollback{$id} and $ts > $rollback{$id}) {
|
||||
splice(@result, $i, 1); # strip rolled back single pages
|
||||
}
|
||||
}
|
||||
splice(@result, 0, $end + 1) if $skip_to; # strip rest if any
|
||||
} else { # just strip the marker left by DoRollback()
|
||||
for (my $i = $#result; $i >= 0; $i--) {
|
||||
splice(@result, $i, 1) if $result[$i][1] eq '[[rollback]]'; # id
|
||||
@@ -1849,11 +1863,10 @@ sub RcTextRevision {
|
||||
: ($UsePathInfo ? '/' : '?') . UrlEncode($id));
|
||||
print "\n", RcTextItem('title', NormalToFree($id)),
|
||||
RcTextItem('description', $summary),
|
||||
RcTextItem('generator', $username
|
||||
? $username . ' ' . Ts('from %s', $host) : $host),
|
||||
RcTextItem('generator', GetAuthor($host, $username)),
|
||||
RcTextItem('language', join(', ', @{$languages})), RcTextItem('link', $link),
|
||||
RcTextItem('last-modified', TimeToW3($ts)),
|
||||
RcTextItem('revision', $revision);
|
||||
RcTextItem('revision', $revision);
|
||||
}
|
||||
|
||||
sub PrintRcText { # print text rss header and call ProcessRcLines
|
||||
@@ -1942,7 +1955,7 @@ sub RssItem {
|
||||
$rss .= "<description>" . QuoteHtml($summary) . "</description>\n" if $summary;
|
||||
$rss .= "<pubDate>" . $date . "</pubDate>\n";
|
||||
$rss .= "<comments>" . ScriptUrl($CommentsPrefix . UrlEncode($id))
|
||||
. "</comments>\n" if $CommentsPrefix and $id !~ /^$CommentsPrefix/o;
|
||||
. "</comments>\n" if $CommentsPattern and $id !~ /$CommentsPattern/o;
|
||||
$rss .= "<dc:contributor>" . $username . "</dc:contributor>\n" if $username;
|
||||
$rss .= "<wiki:status>" . (1 == $revision ? 'new' : 'updated')
|
||||
. "</wiki:status>\n";
|
||||
@@ -1969,7 +1982,7 @@ sub DoHistory {
|
||||
print GetHttpHeader('text/plain'),
|
||||
RcTextItem('title', Ts('History of %s', NormalToFree($OpenPageName))),
|
||||
RcTextItem('date', TimeToText($Now)),
|
||||
RcTextItem('link', $q->url(-path_info=>1, -query=>1)),
|
||||
RcTextItem('link', ScriptUrl("action=history;id=$OpenPageName;raw=1")),
|
||||
RcTextItem('generator', 'Oddmuse');
|
||||
SetParam('all', 1);
|
||||
my @languages = split(/,/, $Page{languages});
|
||||
@@ -2078,6 +2091,9 @@ sub DoRollback {
|
||||
my @ids = ();
|
||||
if (not $page) { # cannot just use list length because of ('')
|
||||
return unless UserIsAdminOrError(); # only admins can do mass changes
|
||||
SetParam('showedit', 1); # make GetRcLines return minor edits as well
|
||||
SetParam('all', 1); # prevent LatestChanges from interfering
|
||||
SetParam('rollback', 1); # prevent StripRollbacks from interfering
|
||||
my %ids = map { my ($ts, $id) = @$_; $id => 1; } # make unique via hash
|
||||
GetRcLines($Now - $KeepDays * 86400); # 24*60*60
|
||||
@ids = keys %ids;
|
||||
@@ -2097,14 +2113,14 @@ sub DoRollback {
|
||||
} elsif (not UserIsEditor() and my $rule = BannedContent($text)) {
|
||||
print Ts('Rollback of %s would restore banned content.', $id), $rule, $q->br();
|
||||
} else {
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne $ENV{REMOTE_ADDR}));
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne GetRemoteAddress()));
|
||||
print Ts('%s rolled back', GetPageLink($id)), ($ts ? ' ' . Ts('to %s', TimeToText($to)) : ''), $q->br();
|
||||
}
|
||||
}
|
||||
WriteRcLog('[[rollback]]', $page, $to); # leave marker
|
||||
print $q->end_p() . $q->end_div();
|
||||
ReleaseLock();
|
||||
PrintFooter();
|
||||
PrintFooter($page);
|
||||
}
|
||||
|
||||
sub DoAdminPage {
|
||||
@@ -2198,6 +2214,17 @@ sub ScriptLinkDiff {
|
||||
return ScriptLink($action, $text, 'diff');
|
||||
}
|
||||
|
||||
sub GetRemoteAddress {
|
||||
return $ENV{REMOTE_ADDR};
|
||||
}
|
||||
|
||||
sub GetAuthor {
|
||||
my ($host, $username) = @_;
|
||||
return $username . ' ' . Ts('from %s', $host) if $username and $host;
|
||||
return $username if $username;
|
||||
return T($host); # could be 'Anonymous'
|
||||
}
|
||||
|
||||
sub GetAuthorLink {
|
||||
my ($host, $username) = @_;
|
||||
$username = FreeToNormal($username);
|
||||
@@ -2206,11 +2233,11 @@ sub GetAuthorLink {
|
||||
$username = ''; # Just pretend it isn't there.
|
||||
}
|
||||
if ($username and $RecentLink) {
|
||||
return ScriptLink(UrlEncode($username), $name, 'author', undef, Ts('from %s', $host));
|
||||
return ScriptLink(UrlEncode($username), $name, 'author', undef, $host);
|
||||
} elsif ($username) {
|
||||
return $q->span({-class=>'author'}, $name) . ' ' . Ts('from %s', $host);
|
||||
return $q->span({-class=>'author'}, $name);
|
||||
}
|
||||
return $host;
|
||||
return T($host); # could be 'Anonymous'
|
||||
}
|
||||
|
||||
sub GetHistoryLink {
|
||||
@@ -2356,7 +2383,7 @@ sub GetCss { # prevent javascript injection
|
||||
push (@css, $StyleSheet) if $StyleSheet and not @css;
|
||||
push (@css, "$ScriptName?action=browse;id=" . UrlEncode($StyleSheetPage) . ";raw=1;mime-type=text/css")
|
||||
if $IndexHash{$StyleSheetPage} and not @css;
|
||||
push (@css, 'http://www.oddmuse.org/oddmuse.css') unless @css;
|
||||
push (@css, 'http://www.oddmuse.org/default.css') unless @css;
|
||||
return join('', map { qq(<link type="text/css" rel="stylesheet" href="$_" />) } @css);
|
||||
}
|
||||
|
||||
@@ -2418,8 +2445,8 @@ sub GetFooterLinks {
|
||||
my ($id, $rev) = @_;
|
||||
my @elements;
|
||||
if ($id and $rev ne 'history' and $rev ne 'edit') {
|
||||
if ($CommentsPrefix) {
|
||||
if ($id =~ /^$CommentsPrefix(.*)/o) {
|
||||
if ($CommentsPattern) {
|
||||
if ($id =~ /$CommentsPattern/o) {
|
||||
push(@elements, GetPageLink($1, undef, 'original', T('a')));
|
||||
} else {
|
||||
push(@elements, GetPageLink($CommentsPrefix . $id, undef, 'comment', T('c')));
|
||||
@@ -2450,11 +2477,11 @@ sub GetFooterLinks {
|
||||
|
||||
sub GetCommentForm {
|
||||
my ($id, $rev, $comment) = @_;
|
||||
if ($CommentsPrefix ne '' and $id and $rev ne 'history' and $rev ne 'edit'
|
||||
and $id =~ /^$CommentsPrefix/o and UserCanEdit($id, 0, 1)) {
|
||||
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
|
||||
$q->p(GetHiddenValue('title', $id),
|
||||
GetTextArea('aftertext', $comment ? $comment : $NewComment, 10)), $EditNote,
|
||||
$q->p(GetHiddenValue('title', $id), $q->label({-for=>'aftertext', -accesskey=>T('c')}, $NewComment),
|
||||
$q->br(), GetTextArea('aftertext', $comment, 10)), $EditNote,
|
||||
$q->p($q->span({-class=>'username'},
|
||||
$q->label({-for=>'username'}, T('Username:')), ' ',
|
||||
$q->textfield(-name=>'username', -id=>'username',
|
||||
@@ -2524,6 +2551,7 @@ sub PrintHtmlDiff {
|
||||
$old = $Page{revision} - 1;
|
||||
}
|
||||
}
|
||||
$summary = $Page{summary} if not $summary and not $new;
|
||||
$summary = $q->p({-class=>'summary'}, T('Summary:') . ' ' . $summary) if $summary;
|
||||
if ($old > 0) { # generate diff if the computed old revision makes sense
|
||||
$diff = GetKeptDiff($text, $old);
|
||||
@@ -2695,7 +2723,6 @@ sub OpenPage { # Sets global variables
|
||||
local $/ = undef;
|
||||
$Page{text} = <F>;
|
||||
close F;
|
||||
} elsif ($CommentsPrefix and $id =~ /^$CommentsPrefix(.*)/o) { # do nothing
|
||||
}
|
||||
}
|
||||
$OpenPageName = $id;
|
||||
@@ -2747,17 +2774,17 @@ sub GetKeptRevision { # Call after OpenPage
|
||||
|
||||
sub GetPageFile {
|
||||
my ($id) = @_;
|
||||
return $PageDir . '/' . GetPageDirectory($id) . "/$id.pg";
|
||||
return "$PageDir/$id.pg";
|
||||
}
|
||||
|
||||
sub GetKeepFile {
|
||||
my ($id, $revision) = @_; die "No revision for $id" unless $revision; #FIXME
|
||||
return $KeepDir . '/' . GetPageDirectory($id) . "/$id/$revision.kp";
|
||||
return "$KeepDir/$id/$revision.kp";
|
||||
}
|
||||
|
||||
sub GetKeepDir {
|
||||
my $id = shift; die 'No id' unless $id; #FIXME
|
||||
return $KeepDir . '/' . GetPageDirectory($id) . '/' . $id;
|
||||
return "$KeepDir/$id";
|
||||
}
|
||||
|
||||
sub GetKeepFiles {
|
||||
@@ -2768,19 +2795,11 @@ sub GetKeepRevisions {
|
||||
return sort {$b <=> $a} map { m/([0-9]+)\.kp$/; $1; } GetKeepFiles(shift);
|
||||
}
|
||||
|
||||
sub GetPageDirectory {
|
||||
my $id = shift;
|
||||
if ($id =~ /^([a-zA-Z])/) {
|
||||
return uc($1);
|
||||
}
|
||||
return 'other';
|
||||
}
|
||||
|
||||
# Always call SavePage within a lock.
|
||||
sub SavePage { # updating the cache will not change timestamp and revision!
|
||||
ReportError(T('Cannot save a nameless page.'), '400 BAD REQUEST', 1) unless $OpenPageName;
|
||||
ReportError(T('Cannot save a page without revision.'), '400 BAD REQUEST', 1) unless $Page{revision};
|
||||
CreatePageDir($PageDir, $OpenPageName);
|
||||
CreateDir($PageDir);
|
||||
WriteStringToFile(GetPageFile($OpenPageName), EncodePage(%Page));
|
||||
}
|
||||
|
||||
@@ -2791,7 +2810,8 @@ sub SaveKeepFile {
|
||||
delete $Page{'diff-major'};
|
||||
delete $Page{'diff-minor'};
|
||||
$Page{'keep-ts'} = $Now; # expire only $KeepDays from $Now!
|
||||
CreateKeepDir($KeepDir, $OpenPageName);
|
||||
CreateDir($KeepDir);
|
||||
CreateDir("$KeepDir/$OpenPageName");
|
||||
WriteStringToFile(GetKeepFile($OpenPageName, $Page{revision}), EncodePage(%Page));
|
||||
}
|
||||
|
||||
@@ -2866,21 +2886,9 @@ sub CreateDir {
|
||||
or ReportError(Ts('Cannot create %s', $newdir) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
|
||||
sub CreatePageDir {
|
||||
my ($dir, $id) = @_;
|
||||
CreateDir($dir);
|
||||
CreateDir($dir . '/' . GetPageDirectory($id));
|
||||
}
|
||||
|
||||
sub CreateKeepDir {
|
||||
my ($dir, $id) = @_;
|
||||
CreatePageDir($dir, $id);
|
||||
CreateDir($dir . '/' . GetPageDirectory($id) . '/' . $id);
|
||||
}
|
||||
|
||||
sub GetLockedPageFile {
|
||||
my $id = shift;
|
||||
return $PageDir . '/' . GetPageDirectory($id) . "/$id.lck";
|
||||
return "$PageDir/$id.lck";
|
||||
}
|
||||
|
||||
sub RequestLockDir {
|
||||
@@ -3006,13 +3014,13 @@ sub GetHiddenValue {
|
||||
|
||||
sub GetRemoteHost { # when testing, these variables are undefined.
|
||||
my $rhost = $ENV{REMOTE_HOST}; # tests are written to avoid -w warnings.
|
||||
if (not $rhost and $UseLookup and $ENV{REMOTE_ADDR}) {
|
||||
if (not $rhost and $UseLookup and GetRemoteAddress()) {
|
||||
# Catch errors (including bad input) without aborting the script
|
||||
eval 'use Socket; my $iaddr = inet_aton($ENV{REMOTE_ADDR});'
|
||||
eval 'use Socket; my $iaddr = inet_aton(GetRemoteAddress());'
|
||||
. '$rhost = gethostbyaddr($iaddr, AF_INET) if $iaddr;';
|
||||
}
|
||||
if (not $rhost) {
|
||||
$rhost = $ENV{REMOTE_ADDR};
|
||||
$rhost = GetRemoteAddress();
|
||||
}
|
||||
return $rhost;
|
||||
}
|
||||
@@ -3021,11 +3029,9 @@ sub FreeToNormal { # trim all spaces and convert them to underlines
|
||||
my $id = shift;
|
||||
return '' unless $id;
|
||||
$id =~ s/ /_/g;
|
||||
if (index($id, '_') > -1) { # Quick check for any space/underscores
|
||||
$id =~ s/__+/_/g;
|
||||
$id =~ s/^_//;
|
||||
$id =~ s/_$//;
|
||||
}
|
||||
$id =~ s/__+/_/g;
|
||||
$id =~ s/^_//;
|
||||
$id =~ s/_$//;
|
||||
return UnquoteHtml($id);
|
||||
}
|
||||
|
||||
@@ -3210,7 +3216,7 @@ sub UserCanEdit {
|
||||
return 1 if UserIsEditor();
|
||||
return 0 if !$EditAllowed or -f $NoEditFile;
|
||||
return 0 if $editing and UserIsBanned(); # this call is more expensive
|
||||
return 0 if $EditAllowed >= 2 and (not $CommentsPrefix or $id !~ /^$CommentsPrefix/o);
|
||||
return 0 if $EditAllowed >= 2 and (not $CommentsPattern or $id !~ /$CommentsPattern/o);
|
||||
return 1 if $EditAllowed >= 3 and ($comment or (GetParam('aftertext', '') and not GetParam('text', '')));
|
||||
return 0 if $EditAllowed >= 3;
|
||||
return 1;
|
||||
@@ -3219,7 +3225,7 @@ sub UserCanEdit {
|
||||
sub UserIsBanned {
|
||||
return 0 if GetParam('action', '') eq 'password'; # login is always ok
|
||||
my ($host, $ip);
|
||||
$ip = $ENV{'REMOTE_ADDR'};
|
||||
$ip = GetRemoteAddress();
|
||||
$host = GetRemoteHost();
|
||||
foreach (split(/\n/, GetPageContent($BannedHosts))) {
|
||||
if (/^\s*([^#]\S+)/) { # all lines except empty lines and comments, trim whitespace
|
||||
@@ -3232,19 +3238,22 @@ sub UserIsBanned {
|
||||
}
|
||||
|
||||
sub UserIsAdmin {
|
||||
return 0 if $AdminPass eq '';
|
||||
my $pwd = GetParam('pwd', '');
|
||||
foreach (split(/\s+/, $AdminPass)) {
|
||||
return 1 if $pwd eq $_;
|
||||
}
|
||||
return 0;
|
||||
return UserHasPassword(GetParam('pwd', ''), $AdminPass);
|
||||
}
|
||||
|
||||
sub UserIsEditor {
|
||||
return 1 if UserIsAdmin(); # Admin includes editor
|
||||
return 0 if $EditPass eq '';
|
||||
my $pwd = GetParam('pwd', ''); # Used for both passwords
|
||||
foreach (split(/\s+/, $EditPass)) {
|
||||
return UserHasPassword(GetParam('pwd', ''), $EditPass);
|
||||
}
|
||||
|
||||
sub UserHasPassword {
|
||||
my ($pwd, $pass) = @_;
|
||||
return 0 if not $pass;
|
||||
if ($PassHashFunction ne '') {
|
||||
no strict 'refs';
|
||||
$pwd = &$PassHashFunction($pwd . $PassSalt);
|
||||
}
|
||||
foreach (split(/\s+/, $pass)) {
|
||||
return 1 if $pwd eq $_;
|
||||
}
|
||||
return 0;
|
||||
@@ -3327,7 +3336,7 @@ sub AllPagesList {
|
||||
if (not $refresh and -f $IndexFile) {
|
||||
my ($status, $rawIndex) = ReadFile($IndexFile); # not fatal
|
||||
if ($status) {
|
||||
%IndexHash = split(/\s+/, $rawIndex);
|
||||
%IndexHash = split(/ /, $rawIndex);
|
||||
@IndexList = sort(keys %IndexHash);
|
||||
return @IndexList;
|
||||
}
|
||||
@@ -3337,7 +3346,7 @@ sub AllPagesList {
|
||||
%IndexHash = ();
|
||||
# If file exists and cannot be changed, error!
|
||||
my $locked = RequestLockDir('index', undef, undef, -f $IndexFile);
|
||||
foreach (bsd_glob("$PageDir/*/*.pg"), bsd_glob("$PageDir/*/.*.pg")) {
|
||||
foreach (bsd_glob("$PageDir/*.pg"), bsd_glob("$PageDir/.*.pg")) {
|
||||
next unless m|/.*/(.+)\.pg$|;
|
||||
my $id = $1;
|
||||
utf8::decode($id);
|
||||
@@ -3489,8 +3498,7 @@ sub PrintSearchResultEntry {
|
||||
my %entry = %{(shift)}; # get value from reference
|
||||
my $regex = shift;
|
||||
if (GetParam('raw', 0)) {
|
||||
$entry{generator} = $entry{username} . ' ' if $entry{username};
|
||||
$entry{generator} .= Ts('from %s', $entry{host}) if $entry{host};
|
||||
$entry{generator} = GetAuthor($entry{host}, $entry{username});
|
||||
foreach my $key (qw(title description size last-modified generator username host)) {
|
||||
print RcTextItem($key, $entry{$key});
|
||||
}
|
||||
@@ -3560,7 +3568,7 @@ sub Replace {
|
||||
if (eval "s{$from}{$to}gi") { # allows use of backreferences
|
||||
push (@result, $id);
|
||||
Save($id, $_, $from . ' -> ' . $to, 1,
|
||||
($Page{ip} ne $ENV{REMOTE_ADDR}));
|
||||
($Page{ip} ne GetRemoteAddress()));
|
||||
}
|
||||
}
|
||||
ReleaseLock();
|
||||
@@ -3639,7 +3647,7 @@ sub DoPost {
|
||||
if ($oldrev) { # the first author (no old revision) is not considered to be "new"
|
||||
# prefer usernames for potential new author detection
|
||||
$newAuthor = 1 if not $Page{username} or $Page{username} ne GetParam('username', '');
|
||||
$newAuthor = 1 if not $ENV{REMOTE_ADDR} or not $Page{ip} or $ENV{REMOTE_ADDR} ne $Page{ip};
|
||||
$newAuthor = 1 if not GetRemoteAddress() or not $Page{ip} or GetRemoteAddress() ne $Page{ip};
|
||||
}
|
||||
my $oldtime = $Page{ts};
|
||||
my $myoldtime = GetParam('oldtime', ''); # maybe empty!
|
||||
@@ -3701,7 +3709,7 @@ sub AddComment {
|
||||
my $author = GetParam('username', T('Anonymous'));
|
||||
my $homepage = GetParam('homepage', '');
|
||||
$homepage = 'http://' . $homepage
|
||||
if $homepage and not substr($homepage,0,7) eq 'http://';
|
||||
if $homepage and $homepage !~ /^($UrlProtocols):/;
|
||||
$author = "[$homepage $author]" if $homepage;
|
||||
$string .= "\n----\n\n" if $string and $string ne "\n";
|
||||
$string .= $comment . "\n\n"
|
||||
@@ -3732,7 +3740,7 @@ sub Save { # call within lock, with opened page
|
||||
$Page{revision} = $revision;
|
||||
$Page{summary} = $summary;
|
||||
$Page{username} = $user;
|
||||
$Page{ip} = $ENV{REMOTE_ADDR};
|
||||
$Page{ip} = GetRemoteAddress();
|
||||
$Page{host} = $host;
|
||||
$Page{minor} = $minor;
|
||||
$Page{text} = $new;
|
||||
@@ -3794,9 +3802,9 @@ sub MergeRevisions { # merge change from file2 to file3 into file1
|
||||
# Note: all diff and recent-list operations should be done within locks.
|
||||
sub WriteRcLog {
|
||||
my ($id, $summary, $minor, $revision, $username, $host, $languages, $cluster) = @_;
|
||||
my $rc_line = join($FS, $Now, $id, $minor, $summary, $host,
|
||||
my $line = join($FS, $Now, $id, $minor, $summary, $host,
|
||||
$username, $revision, $languages, $cluster);
|
||||
AppendStringToFile($RcFile, $rc_line . "\n");
|
||||
AppendStringToFile($RcFile, $line . "\n");
|
||||
}
|
||||
|
||||
sub UpdateDiffs { # this could be optimized, but isn't frequent enough
|
||||
@@ -3848,17 +3856,18 @@ sub DoMaintain {
|
||||
}
|
||||
# Move the old stuff from rc to temp
|
||||
my @rc = split(/\n/, $data);
|
||||
my $i;
|
||||
for ($i = 0; $i < @rc ; $i++) {
|
||||
my ($ts) = split(/$FS/o, $rc[$i]);
|
||||
my @tmp = ();
|
||||
for my $line (@rc) {
|
||||
my ($ts, $id, $minor, $summary, $host, @rest) = split(/$FS/o, $line);
|
||||
last if ($ts >= $starttime);
|
||||
push(@tmp, join($FS, $ts, $id, $minor, $summary, 'Anonymous', @rest));
|
||||
}
|
||||
print $q->p(Ts('Moving %s log entries.', $i));
|
||||
if ($i) {
|
||||
my @temp = splice(@rc, 0, $i);
|
||||
print $q->p(Ts('Moving %s log entries.', scalar(@tmp)));
|
||||
if (@tmp) {
|
||||
# Write new files, and backups
|
||||
AppendStringToFile($RcOldFile, join("\n",@temp) . "\n");
|
||||
AppendStringToFile($RcOldFile, join("\n", @tmp) . "\n");
|
||||
WriteStringToFile($RcFile . '.old', $data);
|
||||
splice(@rc, 0, scalar(@tmp)); # strip
|
||||
WriteStringToFile($RcFile, @rc ? join("\n",@rc) . "\n" : '');
|
||||
}
|
||||
if (opendir(DIR, $RssDir)) { # cleanup if they should expire anyway
|
||||
@@ -3956,7 +3965,7 @@ sub DoDebug {
|
||||
sub DoSurgeProtection {
|
||||
return unless $SurgeProtection;
|
||||
my $name = GetParam('username','');
|
||||
$name = $ENV{'REMOTE_ADDR'} if not $name and $SurgeProtection;
|
||||
$name = GetRemoteAddress() if not $name and $SurgeProtection;
|
||||
return unless $name;
|
||||
ReadRecentVisitors();
|
||||
AddRecentVisitor($name);
|
||||
|
||||
35
wikipipe
35
wikipipe
@@ -1,35 +0,0 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 2005 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 2 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, write to the
|
||||
# Free Software Foundation, Inc.
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
use Getopt::Std;
|
||||
use LWP::UserAgent;
|
||||
|
||||
our $opt_b;
|
||||
getopt('b');
|
||||
my $base = $opt_b;
|
||||
my $url = shift;
|
||||
die "Usage: wikipipe [-b base-url] url\n" unless $url;
|
||||
undef $/;
|
||||
|
||||
my $data = <STDIN>;
|
||||
my $ua = new LWP::UserAgent;
|
||||
my %params = (action=>raw, data=>$data, base=>$base);
|
||||
my $response = $ua->post($url, \%params);
|
||||
die $response->status_line unless $response->is_success;
|
||||
print $response->content;
|
||||
Reference in New Issue
Block a user