Compare commits

..

52 Commits
2.2.4 ... 2.2.5

Author SHA1 Message Date
Alex Schroeder
5ca2bf3efb Fix tests for fix-encoding. 2014-01-26 11:39:06 +01:00
Alex Schroeder
96bc4e14fa Fix the number of tests. 2014-01-25 18:27:10 +01:00
Alex Schroeder
ad1059dbb2 Revert the last commit.
The output has to be encoded as well.
2014-01-25 18:16:06 +01:00
Alex Schroeder
508396d1d1 New module. 2014-01-24 23:19:58 +01:00
Alex Schroeder
6d457ff87b Fix newline handling. 2014-01-24 23:19:37 +01:00
Alex Schroeder
860cb15324 Lighter RecentChanges. More spacing. 2014-01-22 01:52:43 +01:00
Alex Schroeder
56e76a4883 Get rid of special cases for RecentChanges. 2014-01-22 01:43:44 +01:00
Alex Schroeder
3fa8e0a6b0 New. 2014-01-21 08:06:56 -05:00
Alex Schroeder
4c4ab98d47 Merge branch 'master' of ssh://git.sv.gnu.org/srv/git/oddmuse 2013-12-23 05:18:56 -05:00
Alex Schroeder
ca62cbf446 Add .toc 2013-12-23 05:18:45 -05:00
Alex Schroeder
ef3bde90ac Fix documentation URL 2013-12-21 20:01:58 +01:00
Alex Schroeder
7771c541bb oddmuse-curl.el requires info for the faces.
Fixed the regular expressions for extended markup.
Fixed the regular expression for oddmuse-link-pattern.
Use goto-address when looking at a history page.
Use a list instead of an alist for the list of pagenames.
2013-12-06 11:00:42 +01:00
Alex Schroeder
6adabedefe oddmuse-curl.el merged with the current version on Emacs Wiki. 2013-12-06 09:36:46 +01:00
Alex Schroeder
a776c67cd6 oddmuse-curl.el, an Oddmuse client for Emacs, based on version 1.3
with an improved doc-string for oddmuse-wikis.
2013-12-06 09:31:16 +01:00
Alex Schroeder
0ddc1770a3 Merge branch 'master' of ssh://git.sv.gnu.org/srv/git/oddmuse 2013-11-30 18:59:39 -05:00
Alex Schroeder
44fa8cfb5a Handle empty lines in broken oldrc.log files. 2013-11-30 18:59:31 -05:00
Alex Schroeder
96c21c2240 Removed wikipipi: No idea how this is supposed to work. 2013-12-01 00:21:20 +01:00
Alex Schroeder
1c25325257 Anonymizing older entries in the list of recent changes.
When the maintenance action runs, it copies all the older entries in
rc.log to oldrc.log. Older entries are the entries that will usually
not get used by a typical display of recent changes. Any entry older
than the largest value of @RcDays is moved to oldrc.log (defaults to
90 days).

The idea is that you would only need hostnames or IP numbers to fight
spam and vandalism: Add regular expressions matching either hostname
or IP number of spammers or vandals to $BannedHosts and prevent the
attack from continuing. After a few days, however, this information is
no longer required. In this day and age of privacy invasion, I think
software should take a pro-active stance and therefore the entries
moved to oldrc.log will have their hostname or IP number replaced by
"Anonymous".

The existing entries in oldrc.log are not changed. If you want to do
the right thing, there's a script called anonymize.pl in the contrib
directory.
2013-11-30 23:56:10 +01:00
Alex Schroeder
fd5b4e84b1 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-11-30 22:51:00 +01:00
Alex Schroeder
9beff3748b Set @IndexOptions via @MyInitVariables.
With commit deec99c353 @InitOptions can
no longer be set at load time. Setting it at load time also disables
translations unless they get loaded earlier. Thus, @MyInitVariables.
2013-11-30 22:50:31 +01:00
Alex Schroeder
87dedeab85 Add button. 2013-11-28 01:33:20 -05:00
Alex Schroeder
1e73ae22d3 Show the menu only when a username is provided. 2013-11-21 14:31:24 +01:00
Alex Schroeder
5e9b02b5b1 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2013-11-17 23:34:43 +01:00
Alex Schroeder
deec99c353 Delaying the setting of @IndexOptions.
This change was suggested by toomas on the wiki because the
%Translations hash was not set at the time that @IndexOptions was set.
It is now set in InitVariables.
2013-11-17 23:32:00 +01:00
Alex Schroeder
d1b0ac4ccb Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-10-25 03:22:47 -04:00
Alex Schroeder
06881768c3 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-10-25 08:50:51 +02:00
Alex Schroeder
8e1f6c92e3 Static action now takes $StaticAlways into account. 2013-10-25 08:49:13 +02:00
Alex Schroeder
1ebc5192ff ReCAPTCHA introduced HTML escaping problems. 2013-10-20 20:27:12 +02:00
Alex Schroeder
7c52b7b4c2 Add facility to fix HTML escaping. 2013-10-20 20:00:30 +02:00
Alex Schroeder
2936ace022 Test for an encoding bug in recaptcha.pl 2013-10-09 23:28:13 +02:00
Alex Schroeder
4504ef43ac Avoid the use of $q->hidden() and use $q->input and GetParam() instead. 2013-10-09 23:09:47 +02:00
Alex Schroeder
50b71adf2d Add a summary when showing a diff in RSS output.
When looking at an URL like action=rss;full=1;page=0;diff=1;days=1 we
expect to see a diff, and with the diff we expect a summary.  RssItem
calls PageHtml if full is set.  PageHtml opens the page and calls
PrintPageDiff.  If diff is set, it calls PrintHtmlDiff.  Since the
page is already open, PrintHtmlDiff will use GetCacheDiff and skip the
calling of GetTextRevision which would have produced a summary. That's
why we use the open page's summary if none has been set.
2013-09-23 16:44:22 +02:00
Alex Schroeder
8bb0475ba2 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-08-31 21:35:16 +02:00
Alex Schroeder
0e66af495b bbCodeRule must come after PortraitSupportRule
If bbCode is interpreted first, it tries to handle [new].
2013-08-31 21:33:42 +02:00
Alex Schroeder
be6752116b Added support for tt distinct from code. 2013-08-25 00:36:41 +02:00
Alex Schroeder
36577490a7 Fix off by one bug recently introduced by changes to rollbacks.
Rollbacks of large sections count down $i to find the right place to
stop. When the for loop continues, however, $i is decremented once
more. Added $i++ in order to compensate.
2013-08-24 16:20:41 +02:00
Alex Schroeder
8e4dcc2240 Rewrote the code that skips over multiple pages.
When we're within a large rollback, $end used to point to the end of
the block that would eventually be stripped. It's a relative pointer.
While we were escanning downwards, we would sometimes strip single
pages even if we were within a larger rollback. Every time this
happened, $end was not shortened.

The current rewrite start skipping lines immediately and does away
with $skip_to and $end.
2013-08-24 15:38:11 +02:00
Alex Schroeder
dc4de8212a Rollback must roll back previously rolled back changes as well. 2013-08-24 13:53:10 +02:00
Alex Schroeder
ba2de753dd Rollback must roll back to minor changes as well. 2013-08-24 13:39:34 +02:00
Alex Schroeder
6dd1b7e125 Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-08-22 08:02:05 -04:00
Alex Schroeder
aec6e9fb30 Add 14pt font-size for comments. 2013-08-22 14:01:38 +02:00
Alex Schroeder
7b7d90f9f9 Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-08-22 06:51:41 -04:00
Alex Schroeder
c937258922 Commented the 'no bleeding' section. 2013-08-22 06:51:36 -04:00
Alex Schroeder
08aa098203 Add whitespace above the "Comments" header. 2013-08-22 12:49:44 +02:00
Alex Schroeder
b0fc1e4cc0 Take minor edits into accounts when proposing list of contributors to ban. 2013-08-21 12:10:18 +02:00
Alex Schroeder
ca9eef8c09 Get rid of undefined behaviour as indicated by Perl 5.18. 2013-08-21 11:55:20 +02:00
Alex Schroeder
b90b6e9651 Used another XPath expression in the test to make it more robust. 2013-08-21 11:49:33 +02:00
Alex Schroeder
f10bbb4f81 Fix test for %Namespaces as suggested by a Perl 5.18 warning. 2013-08-21 10:44:42 +02:00
Alex Schroeder
0116618e36 Fix number of skipped tests if LWP::UserAgent is not available. 2013-08-21 10:42:27 +02:00
Alex Schroeder
d864045815 Use $PERLBREW_PATH if available.
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.
Prepending /usr/local/bin is one option, using $PERLBREW_PATH is
another.
2013-08-21 10:41:51 +02:00
Alex Schroeder
294e5745e7 Fixed syntax problem as warned by Perl 5.18 2013-08-21 10:19:45 +02:00
Alex Schroeder
afc4f7ecba Replaced oddmuse.css with default.css. 2013-08-21 09:52:26 +02:00
28 changed files with 1499 additions and 129 deletions

90
contrib/anonymize.pl Normal file
View 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();

698
contrib/oddmuse-curl.el Normal file
View File

@@ -0,0 +1,698 @@
;;; oddmuse-curl.el -- edit pages on an Oddmuse wiki using curl
;;
;; Copyright (C) 20062013 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/cgi-bin/wiki/OddmuseMode
;;
;; 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)
(require 'info)
(defcustom oddmuse-directory "~/emacs/oddmuse"
"Directory to store oddmuse pages."
:type '(string)
:group 'oddmuse)
(defcustom oddmuse-wikis
'(("TestWiki" "http://www.emacswiki.org/cgi-bin/test"
utf-8 "question" nil)
("EmacsWiki" "http://www.emacswiki.org/cgi-bin/emacs"
utf-8 "uihnscuskc" nil)
("CommunityWiki" "http://www.communitywiki.org/cw"
utf-8 "question" nil)
("OddmuseWiki" "http://www.oddmuse.org/cgi-bin/oddmuse"
utf-8 "question" nil)
("CampaignWiki" "http://www.campaignwiki.org/wiki/NameOfYourWiki"
utf-8 "ts" 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.
%? '?' 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-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."
(font-lock-add-keywords
nil
'(("^=[^=\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")))))
(defun oddmuse-bbcode-markup ()
"Implement markup rules for the bbcode markup extension."
(font-lock-add-keywords
nil
`(("\\[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")))))
(defun oddmuse-usemod-markup ()
"Implement markup rules for the Usemod markup extension."
(font-lock-add-keywords
nil
'(("^=[^=\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")))))
(defun oddmuse-usemod-html-markup ()
"Implement markup rules for the HTML option in the Usemod markup extension."
(font-lock-add-keywords
nil
'(("<\\(/?[a-z]+\\)" 1 '(face font-lock-function-name-face help-echo "Usemod HTML"))))
(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."
(font-lock-add-keywords
nil
'(("\\*\\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")))))
(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."
(font-lock-add-keywords
nil
`((,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"))))
(goto-address))
;; Should determine this automatically based on the version? And cache it per wiki?
;; http://emacswiki.org/wiki?action=version
(defvar oddmuse-markup-functions
'(oddmuse-basic-markup
oddmuse-extended-markup
oddmuse-usemod-markup
oddmuse-creole-markup
oddmuse-bbcode-markup)
"The list of functions to call when `oddmuse-mode' runs.
Later functions take precedence because they call `font-lock-add-keywords'
which adds the expressions to the front of the existing list.")
(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}"
(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)))))
(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-m") 'oddmuse-toggle-minor)
(define-key oddmuse-mode-map (kbd "C-c C-c") 'oddmuse-post)
(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)
("%\\?" . hatena)))
(when (and (boundp (cdr pair)) (stringp (symbol-value (cdr pair))))
(setq command (replace-regexp-in-string (car pair)
(shell-quote-argument
(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))
(set-buffer buf)
(unless (equal name (buffer-name)) (rename-buffer name))
(erase-buffer)
(let ((max-mini-window-height 1))
(oddmuse-run "Loading" command buf nil))
(pop-to-buffer buf)
(oddmuse-mode)))))
(defalias 'oddmuse-go 'oddmuse-edit)
(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 (if arg (oddmuse-read-pagename oddmuse-wiki)
(oddmuse-pagename-at-point))))
(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)))
(cond ((oddmuse-current-free-link-contents))
((oddmuse-wikiname-p pagename)
pagename)
(t
(error "No link found at point")))))
(defun oddmuse-wikiname-p (pagename)
"Whether PAGENAME is WikiName or not."
(let (case-fold-search)
(string-match (concat "^" oddmuse-link-pattern "$") pagename)))
;; (oddmuse-wikiname-p "WikiName")
;; (oddmuse-wikiname-p "not-wikiname")
;; (oddmuse-wikiname-p "notWikiName")
(defun oddmuse-run (mesg command buf on-region)
"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."
(message "%s..." mesg)
;; 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)
(string= "302" (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 "%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)))
(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 nil))
(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

View File

@@ -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 {

View File

@@ -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; }

207
css/oddmuse-2013.css Normal file
View File

@@ -0,0 +1,207 @@
@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: 18pt;
}
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%; }
textarea#summary { height: 3em; }
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;
}

View 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);

View File

@@ -72,6 +72,7 @@ sub DoBanHosts {
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];

View File

@@ -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

View File

@@ -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')));
}
}

View File

@@ -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

View File

@@ -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
View 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&gt;/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;
}

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20032013 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;

View File

@@ -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();

View File

@@ -1,20 +1,16 @@
# Copyright (C) 2004, 2005, 2006 Alex Schroeder <alex@emacswiki.org>
# Copyright (C) 2004-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/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>';
@@ -63,7 +59,11 @@ sub StaticWriteFiles {
local *ScriptLink = *StaticScriptLink;
local *GetDownloadLink = *StaticGetDownloadLink;
foreach my $id (AllPagesList()) {
StaticWriteFile($id);
if ($StaticAlways > 1
or GetParam('html', 0)
or PageIsUploadedFile($id)) {
StaticWriteFile($id);
}
}
}
@@ -122,16 +122,16 @@ sub StaticFileName {
sub StaticWriteFile {
my $id = shift;
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));
if ($data) {
StaticFile($id, $mimetype, $data);
} elsif ($html) {
StaticHtml($id);
} else {
print "no data for ";
}
close(F);
chmod 0644,"$StaticDir/$filename";

View File

@@ -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

View File

@@ -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;

View File

@@ -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##

View File

@@ -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,

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20122013 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', '&amp;lt;b&amp;gt;bold&amp;lt;/b&amp;gt;',
'borked escaping'),
'&amp;lt;b&amp;gt;bold&amp;lt;/b&amp;gt;');
test_page(get_page('action=fix-escaping id=Example'),
'Location: http://localhost/wiki.pl/Example');
test_page(get_page('Example'),
'&lt;b&gt;bold&lt;/b&gt;');
test_page(get_page('action=rc showedit=1'),
'Fix HTML escapes');

97
t/markdown-rule.t Normal file
View 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

20
t/rc.t
View File

@@ -1,4 +1,4 @@
# Copyright (C) 2006, 2007, 2008, 2009 Alex Schroeder <alex@gnu.org>
# Copyright (C) 200620013 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
View 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',
'&lt;b&gt;Kühlschrank&lt;/b&gt;');
# update it as an admin
test_page(update_page('SandBox', '<b>Kühlschrank</b>', undef, undef, 1),
'&lt;b&gt;Kühlschrank&lt;/b&gt;');
# existing page and no permission
test_page(get_page('title=SandBox text="<b>K%C3%BChlschrank-test</b>"'),
'Status: 403',
'&lt;b&gt;Kühlschrank-test&lt;/b&gt;');

View File

@@ -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');

View File

@@ -15,7 +15,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 16;
use Test::More tests => 29;
clear_pages();
add_module('static-copy.pl');
@@ -111,6 +111,18 @@ xpath_test(update_page('HomePage', "Static: [[image:Trogs]]"),
. '[@src="/static/Trogs.svgz"]'
. '[@alt="Trogs"]');
# 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");
test_page(get_page('action=static raw=1 pwd=foo'), "Trogs", "Logo");
ok(-f "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz exists");
ok(-f "$DataDir/static/Logo.png", "$DataDir/static/Logo.png exists");
ok(! -f "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html does not exist");
test_page(get_page('action=static raw=1 pwd=foo html=1'), "Trogs", "Logo", "HomePage");
ok(-f "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz exists");
ok(-f "$DataDir/static/Logo.png", "$DataDir/static/Logo.png exists");
ok(-f "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html exists");
# Make sure spaces are translated to underscores (fixed in image.pl)
add_module('image.pl');

View File

@@ -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);

65
wiki.pl
View File

@@ -159,7 +159,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 = ();
@@ -306,6 +306,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 $@;
@@ -1542,30 +1543,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 +1850,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
@@ -2078,6 +2078,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;
@@ -2198,6 +2201,13 @@ sub ScriptLinkDiff {
return ScriptLink($action, $text, 'diff');
}
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 +2216,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 {
@@ -2524,6 +2534,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);
@@ -3489,8 +3500,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});
}
@@ -3794,9 +3804,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 +3858,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

View File

@@ -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;