Compare commits

...

140 Commits
2.2.3 ... 2.3.0

Author SHA1 Message Date
Alex Schroeder
25989f78a5 Merge branch 'as/no-more-page-subdirectories' 2014-06-21 21:30:27 +02:00
Aki Goto
afdb7a9dcb form_timeout.pl: an anti-spam module for Oddmuse
This is an anti-spam module for Oddmuse using form timeout. Edit
permission times out after a specified duration (the default is 30
minutes). When edit content is posted directly by a spam bot without
viewing the edit form, edit will be denied.
2014-06-21 12:18:26 +02:00
Alex Schroeder
2e79a843c8 oddmuse-curl.el: Fix URL 2014-06-21 12:01:08 +02:00
Alex Schroeder
54370da235 oddmuse-curl.el: whitespace 2014-06-21 12:00:26 +02:00
Alex Schroeder
43839ac1aa upgrade.t: Testing for the changes to upgrade.pl 2014-06-21 10:45:21 +02:00
Alex Schroeder
3c2f96250b upgrade.pl: Use variables; print utf8.
Instead of fiddling with @MyInitVariables, set the options naming
pages to the empty string.
2014-06-21 04:44:33 -04:00
Alex Schroeder
9def2d2eb2 upgrade.pl: Disable some module initialisations
Some modules read a page file when initializing. As these cannot be
found before the upgrade, we need to disable them.
2014-06-21 04:18:16 -04:00
Alex Schroeder
3ad40b84fb .gitignore: .DS_Store 2014-06-20 22:39:37 +02:00
Alex Schroeder
ecda4c3d98 oddmuse-curl.el: Preview shows just the preview
Don't show the header, footer and textarea; show just the preview div.
2014-06-20 22:36:09 +02:00
Alex Schroeder
74a0576c5d oddmuse-curl.el: Font lock restructuring
All the font-locking functions are now modifying font-lock-defaults
instead of calling font-lock-add-keywords because that's what it says
in the elisp manual.
2014-06-20 21:23:30 +02:00
Alex Schroeder
a6e07a9886 oddmuse-curl.el: Preview command
The preview command uses shr, a built in HTML renderer.
2014-06-20 18:31:33 +02:00
Alex Schroeder
b76b61dc86 .gitignore: .DS_Store 2014-06-19 23:11:04 +02:00
Alex Schroeder
c3cb434973 upgrade.pl: handle name spaces 2014-06-19 23:00:18 +02:00
Alex Schroeder
62f82c2af2 upgrade.pl: New interface.
Don't rely on a separate upgrade action. Any request except for login
and unlock will trigger an upgrade, if you're an admin. Once the upgrade
is complete, the module will rename itself such that it will no longer
load.
2014-06-17 13:14:13 +02:00
Alex Schroeder
d454973294 oddmuse-curl.el: fix order of font-locking
oddmuse-basic-markup must come last (which prepends its rules to the
front) such that links get precedence over other markup.
2014-06-17 12:26:50 +02:00
Alex Schroeder
cba29c8981 oddmuse-curl.el: added oddmuse-new
A function to make it easy to blog by offering the current ISO date as
page name.
2014-06-17 10:39:05 +02:00
Alex Schroeder
4a812931c8 oddmuse-curl.el: do not break links
Added a fill-nobreak-predicate to prevent links from breaking. This uses
the face property and checks for the link face.
2014-06-17 10:37:57 +02:00
Alex-Daniel Jakimenko
093a6da63d askpage.pl: Don't keep old value of $NewComment
Resetting the value was dead code. Instead of fixing this, the old value
is now discarded. There is no point in keeping it around.
2014-06-16 15:40:13 +02:00
Aki Goto
0ab5261bc6 load-lang.pl: add $LoadLanguageDir
The new option $LoadLanguageDir specifies a directory for the language
files.
2014-06-16 15:33:53 +02:00
Alex Schroeder
1d4f3e4a28 Ask Page extension
This is a simple extension to create a page for asking questions. The
process of writing a question is similar to writing a comment, for the
only exception that after pressing the ‘Save’ button the user will be
redirected to the newly created page containing his question.
2014-06-16 11:25:13 +02:00
Alex Jakimenko
6babcffd00 Consistency in file permissions.
Some modules are executable where as others are not. This patch fixes
this.
2014-06-16 11:19:24 +02:00
Alex Jakimenko
977cbba251 Fix two issues with $NewComment.
Display $NewComment above comment textarea.

'c' is now an access key to focus comment textarea.
2014-06-16 11:06:20 +02:00
Alex Schroeder
2fc4f4b054 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse into as/no-more-page-subdirectories 2014-06-16 10:32:31 +02:00
Alex Schroeder
53566c8434 Use a function instead of $ENV{REMOTE_ADDR}
When the webserver is behind a reverse proxy, $ENV{REMOTE_ADDR} is not
the actual remote client's address but the reverse proxy's address. The
actual remote client's address is available from an environment
variable. The name of this variable depends on the proxy, e.g. pound
uses $ENV{HTTP_X_FORWARDED_FOR}.

As suggested by tyatsumi on the wiki, all access $ENV{REMOTE_ADDR} now
happens via a new function which allows users to override it in their
config file.
2014-06-16 09:50:05 +02:00
Alex Schroeder
563e5cd9c6 upgrade.pl: delete empty directories after upgrade 2014-06-07 17:08:18 +02:00
Alex Schroeder
365d33b602 Get rid of one letter sub-directories.
Recent GNU/Linux systems use ext3 or ext4 file systems. These use HTree
to index files. Wikipedia says: "HTree indexing improved the scalability
of Linux ext2 based filesystems from a practical limit of a few thousand
files, into the range of tens of millions of files per directory. [...]
HTree indexes are available in ext3 when the dir_index feature is
enabled. [...] HTree indexes are turned on by default in ext4."

Thus, instead of working on balanced-page-directories.pl, we decided to
get rid of these sub-directories altogether.

Unfortunately, this is backwards incompatible. Users wanting to upgrade
will need to install the upgrade.pl extension in order to upgrade the
file database.
2014-06-06 17:32:44 +02:00
Alex Schroeder
eef56e435d questionasker.t: fixed number of tests 2014-06-05 17:07:15 +02:00
Alex Schroeder
2044564981 Merge branch 'master' of ssh://git.sv.gnu.org/srv/git/oddmuse 2014-06-05 05:34:08 -04:00
Aki Goto
50c9b79858 balanced-page-directories: reset GetPageDirectory
The test for migration sets GetPageDirectory; when the test fails and
we're not migrating, we need to reset GetPageDirectory to its old value.
2014-06-04 16:14:26 +02:00
Alex Schroeder
d99f62ea7e balanced-page-directories: fix page creation
Previously, no new pages could be created because of a deadlock. The
code mistakenly attempted to start migrating when a new page was
created, and since DoPost already held the main lock, migration failed.
The check was now fixed such that migration is not started when a new
page is being created.
2014-06-04 09:58:37 +02:00
Alex Schroeder
c11188fd3e fixed documentation typo 2014-06-03 16:17:07 +02:00
Alex Schroeder
dd22a852eb balanced-page-directories: new module 2014-06-03 16:14:48 +02:00
Alex Schroeder
62b2e22da8 Questionasker: encoding issue with hidden fields
We need to get rid of $q->hidden when using Unicode, as suggested by
tyatsumi on the wiki.
2014-06-03 11:19:50 +02:00
Alex Schroeder
5483bbf386 joiner: fix URL 2014-06-02 09:37:55 +02:00
Alex Schroeder
8608464863 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2014-06-02 09:22:38 +02:00
Aki Goto
b0d983c817 joiner: user registration module 2014-06-02 09:21:15 +02:00
Aki Goto
5f58256543 Updated Japanese translation. 2014-05-27 14:45:50 +02:00
Alex Jakimenko
c5c088deb1 Do not rehash the password for every entry. 2014-04-23 17:41:44 +02:00
Alex Schroeder
a5b5af9c07 Add $PassHashFunction and $PassSalt
This allows users to have the passwords encrypted in their config
file, as suggested by Alex Jakimenko.
2014-04-23 10:32:36 +02:00
Alex Jakimenko
0dcf49e2cf Add $CommentsPattern to supplement $CommentsPrefix
$CommentsPrefix is used in the code in two contexts:

- as a string added to the original page name to create links (used as
  a plain string)

- and as a way to detect whether the page has comments or not (used as
  a regular expression)

It feels natural to split this functionality into two separate
variables. $CommentsPattern is now the regular expression. No more
CommentsPrefix='.*' hacks!

For example, now you can do some complex stuff like this:

    $CommentsPrefix = 'Comments_on_';
    $CommentsPattern = '^(?|Comments_on_(.*)|Rant_About_(.*)|\d\d\d\d-\d\d-\d\d.*|FAQ)$';

Comments_on_ , Rant_About_ and journal pages will work correctly as
comments pages, but you can also specify some other pages as well, like
FAQ. Basically it can get as complex as one wants. $1 will be used to
create a link to the original page, which means that in this particular
example both Comments_on_Test and Rant_About_Test will have a link to
Test page, while FAQ will have no link to the original page at all.

Of course, by default there will only be "$CommentsPrefix . $id" link in
the footer, so you have to provide links to Rant_About_ pages elsewhere
yourself.

If you don't want to provide a regular expression pattern, you can leave
it undefined. It will be created automatically, keeping functionality
backwards compatible. If you were using $CommentsPrefix='.*' you should
now change it to $CommentsPattern='.*'.
2014-04-21 18:43:40 +02:00
Alex Jakimenko
f3885aa213 OpenPage: Remove useless test. 2014-04-20 00:26:29 +02:00
Alex Schroeder
6136b399a6 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2014-04-15 07:45:01 +02:00
Alex Schroeder
5cc7d55152 License, dependencies. 2014-04-15 07:39:01 +02:00
Alex Schroeder
4112d2acc4 New URL, trimmed wiki list. 2014-04-15 07:38:01 +02:00
Alex Jakimenko
f270a3ced4 Two small fixes
User input needs to be HTML quoted when printed.
The regular expression matching needs the ignore case flag.
2014-04-15 07:31:50 +02:00
Alex Schroeder
7f74d3c211 Use smaller headers 2014-04-09 08:34:14 -04:00
Alex Jakimenko
375c844e37 Specifying the number of entries for More...
This patch addes a new parameter to PrintJournal
such that a journal can have a certain number of
entries but when the user clicks on the More...
link, pagination happens with a different number.
<Journal 1,5>
2014-04-08 10:43:28 +02:00
Alex Jakimenko
efce35e250 Simplify FreeToNormal and remove if statement 2014-04-06 15:14:59 +02:00
Alex Schroeder
cff4f1fd28 Enabling diff usince C-x v = 2014-04-04 18:12:04 +02:00
Alex Schroeder
6f9ded7e41 vc-oddmuse.el added with support for C-x v l 2014-04-04 15:15:00 +02:00
Alex Jakimenko
40c01683fd Split pageidx file using space
Previously, we split it using whitespace (/\s+/). This caused a problem
if somebody managed to create a page containing non-breaking whitespace.
2014-03-29 09:42:00 +01:00
Alex Schroeder
08a4861dc3 Get rid of cloud icon. 2014-03-21 07:43:24 +01:00
Alex Schroeder
d7c40d4dbe Fix dead comment links
If the comment page does not exist, the static copy contains an anchor
element with no href attribute. Better avoid this situation.
2014-03-18 13:01:27 +01:00
Alex Schroeder
f8360bebad Produce static.css as well when exporting 2014-03-18 12:47:16 +01:00
Alex Schroeder
45a0558fcc Fixing the UTF-8 encoding issues for static export 2014-03-18 11:49:28 +01:00
Alex Schroeder
f4ff56e69f Fix HTML output for static export 2014-03-18 11:21:08 +01:00
Alex Schroeder
0d7236c047 More tests regarding local links in static exports 2014-03-18 10:32:22 +01:00
Alex Schroeder
686f24251b Fix static export of HTML pages
Apparently the html=1 parameter got lost so that you could not force an
export of all the pages.
2014-03-18 09:26:04 +01:00
Alex Schroeder
0841c834b9 Escape URL of More... links
When computing the More... link at the end of a journal, we need to URL
escape all user input. In this case, that's the regexp and search
parameter.
2014-03-17 09:52:30 +01:00
Alex Schroeder
5225bded01 Revert "Homepage URL fixing works recognizes https."
I must have been hallucinating. Instead of committing the change I
wanted to commit, I added a file that wasn't ready.

This reverts commit 670b69c118.
2014-03-17 09:51:11 +01:00
Alex Schroeder
e0d18c31e2 Homepage URL fixing works recognizes https.
When leaving a comment, users are given the option of providing a
homepage to link their name to. A common error is to just provide a
domain like "oddmuse.org" instead of a real URL. The resulting markup
used to be [oddmuse.org YourName] which doesn't do what the user
expected. That's why a piece of code used to check whether the homepage
starts with "http://" and if it doesn't, it prefixes it, resulting in
"[http://oddmuse.org YourName]". If the homepage started with
"https://", however, the code did the wrong thing. That's why we're now
checking whether the homepage starts with any known URL-protocol and a
colon.
2014-03-17 09:50:32 +01:00
Alex Schroeder
670b69c118 Homepage URL fixing works recognizes https.
When leaving a comment, users are given the option of providing a
homepage to link their name to. A common error is to just provide a
domain like "oddmuse.org" instead of a real URL. The resulting markup
used to be [oddmuse.org YourName] which doesn't do what the user
expected. That's why a piece of code used to check whether the homepage
starts with "http://" and if it doesn't, it prefixes it, resulting in
"[http://oddmuse.org YourName]". If the homepage started with
"https://", however, the code did the wrong thing. That's why we're now
checking whether the homepage starts with any known URL-protocol and a
colon.
2014-03-16 08:48:36 +01:00
Alex Schroeder
f4d0f300e6 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse
Conflicts:
	modules/translations/chinese_cn-utf8.pl
2014-03-07 09:02:41 +01:00
Andy Stewart
53a7a9a80c chinese_cn-utf8.pl: Updated translation
Simplified Chinese, fixed.
2014-03-07 08:38:15 +01:00
Alex Schroeder
4f675de687 oddmuse-curl.el: Fix following page links. 2014-03-07 08:31:08 +01:00
Alex Schroeder
dffe5e3053 Updated with new texts. 2014-03-06 17:15:35 +01:00
Alex Schroeder
201970ba0b Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2014-03-06 16:28:27 +01:00
Alex Schroeder
7e9137c6f8 Changing 回復 to 回滾 after feedback from Andy Stewart. 2014-03-06 16:23:04 +01:00
Alex Schroeder
9d81a1e3d2 DoDuckDuckGoSearch uses UrlEncode on the argument.
Drew Adams reported that clicking on the title of a page with a + in
its name had the + replaced by a space. This commit fixes this issue.
2014-03-03 08:30:21 +01:00
Alex Schroeder
2f58de9aa4 $NewText and $NewComment can be translated.
As suggested by Dexasys on the wiki.
2014-02-01 23:49:48 +01:00
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
Alex Schroeder
d249792866 Changed the default CSS to oddmuse.org/default.css
I was often confused as to whether oddmuse.org/oddmuse.css referred to
the CSS used by oddmuse.org or to the CSS used by Oddmuse
installations without CSS setting.
2013-08-19 11:46:11 +02:00
Alex Schroeder
59cad086e7 Removed ban-yourself.pl because that was just an idea without any code. 2013-08-16 00:32:27 +02:00
Alex Schroeder
cfac228f57 Test for ban-quick-editors.pl 2013-08-16 00:31:28 +02:00
Alex Schroeder
a4bd6383a2 Quote the unquoted string "commenthidden". 2013-08-16 00:30:58 +02:00
Alex Schroeder
df0f470998 Don't delete pages that are "lock on creation".
This is meant to protect BannedContent and BannedHost from deletion.
2013-08-16 00:29:32 +02:00
Alex Schroeder
d61bf19b15 New module: don't allow quick editing by an IP or username. 2013-08-15 23:46:31 +02:00
Alex Schroeder
e0659c4d60 Made regular expression test more robust. 2013-08-05 10:02:06 +02:00
Alex Schroeder
70baed8088 Testing: Add tests to verify the process of banning URL fragments. 2013-08-02 17:27:50 +02:00
Alex Schroeder
ab3e187354 Bugfix: Banned URLs are added to BannedContent, not BannedHosts.
Added a separate link to add host or IP number to BannedHosts.
2013-08-02 17:13:44 +02:00
Alex Schroeder
f17a67d817 Convenience: List URLs rolled back and offer entry of regexp.
If you are an admin and rolled back a single page, this 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.
2013-08-02 16:55:49 +02:00
Alex Schroeder
601218c0b1 Added ban-contributors extension and tests. 2013-07-30 17:40:57 +02:00
Alex Schroeder
8af5095ff5 Hide Google+ stuff when printing. 2013-07-24 07:43:56 -04:00
Alex Schroeder
0a6cbfa20d Fix link given by raw history page. 2013-06-05 16:18:10 +02:00
Alex Schroeder
1630b64fa5 Fix critical bug in private-pages.pl.
Private pages were deleted whenever maintenance ran. This has been
fixed.
2013-05-30 16:15:01 +02:00
Alex Schroeder
ff4ad6e151 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-05-23 09:03:13 +02:00
Alex Schroeder
cc07341463 DoRollback prints a footer for a specific page if called for one page only.
The typical workflow when reverting spam:
1. view RecentChanges
2. click History button
3. click Rollback button
4. click Administration
5. click Ban contributors
6. click Ban!

This requires that the Administration link in #4 contains the page
id. This commit ensures it.
2013-05-23 09:02:46 +02:00
Alex Schroeder
9fd20a9e93 A new rollback testing idea. 2013-05-19 16:13:37 +02:00
Alex Schroeder
1a561c3cb1 Fix tagcloud bug in tags.pl.
The hash value wasn't being decoded before being split which resultet
in count being 1 in all cases.
2013-05-19 15:32:54 +02:00
77 changed files with 8859 additions and 1345 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
/Mac/pkg/
*.dmg
*.pkg
.DS_Store

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

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

@@ -0,0 +1,871 @@
;;; oddmuse-curl.el -- edit pages on an Oddmuse wiki using curl
;;
;; Copyright (C) 20062014 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
View 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)

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

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

View File

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

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

0
modules/antispam.pl Executable file → Normal file
View File

64
modules/askpage.pl Normal file
View 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
View File

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

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

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

0
modules/blockquote.pl Executable file → Normal file
View File

0
modules/clustermap.pl Executable file → Normal file
View File

0
modules/crossbar.pl Executable file → Normal file
View File

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

View File

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

View File

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

View File

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

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

106
modules/form_timeout.pl Normal file
View 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
View File

0
modules/htmllinks.pl Executable file → Normal file
View File

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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{$_}";

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

2
modules/logbannedcontent.pl Executable file → Normal file
View 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");
}

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

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

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

View File

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

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

View File

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

0
modules/slideshow.pl Executable file → Normal file
View File

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

View File

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

View File

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

View File

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

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

File diff suppressed because it is too large Load Diff

103
modules/upgrade.pl Normal file
View 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();
}

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

View File

@@ -1,24 +1,20 @@
# Copyright (C) 2006 Alex Schroeder <alex@emacswiki.org>
# Copyright (C) 20062014 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);

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

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

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

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2011 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20112014 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
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

View File

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

File diff suppressed because it is too large Load Diff

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

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2008 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20082014 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, '&lt;Schröder&gt;')

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

50
t/rollback-extras.t Normal file
View 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.

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

@@ -1,21 +1,22 @@
# Copyright (C) 2007, 2008, 2009 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20072014 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

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

View File

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

@@ -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(\&lt;journal(\s+(\d*))?(\s+"(.*?)")?(\s+(reverse|past|future))?(\s+search\s+(.*))?\&gt;[ \t]*\n?)/cgi) {
} elsif ($bol && m/\G(\&lt;journal(\s+(\d*)(,(\d*))?)?(\s+"(.*?)")?(\s+(reverse|past|future))?(\s+search\s+(.*))?\&gt;[ \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(\&lt;rss(\s+(\d*))?\s+(.*?)\&gt;[ \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);

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;