Compare commits

...

117 Commits
2.2.1 ... 2.2.4

Author SHA1 Message Date
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
Alex Schroeder
ca3740ca86 Add RuleOrder to prevent conflict with markup.pl. 2013-05-19 14:43:23 +02:00
Alex Schroeder
7a69437443 Italy: removed Festa della Repubblica, added Liberation Day instead. 2013-05-17 10:38:51 +02:00
Alex Schroeder
671f00701b Anniversary of the Unification of Italy 2013-05-17 00:42:48 +02:00
Alex Schroeder
af28957796 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-05-16 23:20:13 +02:00
Alex Schroeder
28c56373f6 DuckDuckGo module.
duckduckgo-search.pl and duckduckgo-search.t based on google-search.pl
and google-search.t to use DuckDuckGo for the search action via a
redirect.
2013-05-16 23:18:23 +02:00
Alex Schroeder
d5fa00f1e2 The admin menu should only list links for actions that are actually
defined. This makes it easier to undefine and hide them.
2013-05-10 00:20:55 +02:00
Alex Schroeder
66fe91efed Expire keep files and delete pages during maintenance without main lock. 2013-05-09 23:46:06 +02:00
Alex Schroeder
3d07062e1f Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-05-08 22:19:12 +02:00
Alex Schroeder
f7b94272bf Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-05-08 22:16:48 +02:00
Alex Schroeder
9e2353aebc Add access keys for articles and comments, if enabled.
If $CommentsPrefix is set, the wiki knows about article pages and
comment pages. The link in GetFooterLinks now uses 'a' and 'c' as
access keys. The access key is passed to GetPageLink and to
ScriptLink.
2013-05-08 22:16:22 +02:00
Alex Schroeder
bf83cc5ca1 Don't use a global $form. 2013-05-07 10:18:12 +02:00
Alex Schroeder
d5e7d58d7e Use EB Garamond hosted by Google if necessary 2013-04-15 03:40:21 -04:00
Alex Schroeder
806a8ba89b DEL no longer uses grey. 2013-04-12 23:09:25 +02:00
Alex Schroeder
8602dfb324 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-04-12 23:03:08 +02:00
Alex Schroeder
6647d52e88 Handle markup singles before handling forced pairs. 2013-04-12 22:59:50 +02:00
Alex Schroeder
3dcf08a850 Fixed %s error in one message reported by Juanma MP. 2013-04-03 11:18:20 +02:00
Alex Schroeder
2a5454a732 New: wikipipe 2013-03-25 14:03:23 +01:00
Alex Schroeder
ee239428d9 Update copyright year. 2013-03-13 18:46:09 +01:00
Alex Schroeder
dd731569d3 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse
Conflicts:
	t/diff.t
2013-03-10 00:52:22 +01:00
Alex Schroeder
d9640c2ef7 Display summary of the change when displaying a diff.
GetTextRevision returns a third parameter (the summary). PrintHtmlDiff
takes a fifth parameter (the summary) and prints it. Test added.
2013-03-10 00:46:49 +01:00
Alex Schroeder
a57d26f520 Whitespace. 2013-03-05 23:13:04 +01:00
Alex Schroeder
98f5b48ceb Add support for -q, -a and -z options.
Rewrote the help message to list the options instead of having
multiple paragraphs of text. Renamed PostRaw to post and added support
for the new options. Pass the new options through copy.
2013-03-05 23:10:35 +01:00
Alex Schroeder
4e790f7847 Fix justification of cells.
The code used to detect whitespace in sibling cells. Thus, if any of
the remaining cells on this line was centered or right justified, this
cell was also getting right justified. If the current cell was both
left and right justified, the result was that it got centered. Added a
test to check for this.
2013-03-05 23:07:23 +01:00
Alex Schroeder
355874edad Add git action to call GitCleanup directly. Add more print statements explaining what git is doing. 2013-02-28 10:44:26 +01:00
Alex Schroeder
91cdb9888a Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-02-14 16:25:47 +01:00
Alex Schroeder
ff28c5f79e no background color for sister site logos 2013-02-14 16:25:28 +01:00
Alex Schroeder
004b0c0831 URL encode keys and values in the tag database.
Depending on your version of the Berkley DB, non-ASCII or non-Latin-1 characters could crash Oddmuse.
2013-02-12 06:00:06 -05:00
Alex Schroeder
01d9cdf4e3 Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-02-12 04:14:08 -05:00
Alex Schroeder
0226a82dca Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-02-01 15:37:44 +01:00
Alex Schroeder
31fcd5dc99 Avoid a javascript error. 2013-02-01 15:30:31 +01:00
Alex Schroeder
2c69716295 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-02-01 15:29:42 +01:00
Alex Schroeder
e772254293 Make sure we don't create an empty TOC element if there aren't enough sections. 2013-02-01 15:26:02 +01:00
Alex Schroeder
f8df77d1a6 Remove the TOC if we don't have enough sections. 2013-02-01 15:17:52 +01:00
Alex Schroeder
de4af94e89 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-02-01 15:16:32 +01:00
Alex Schroeder
cdee73b859 At least two siblings or parent and child nodes required. 2013-02-01 15:12:54 +01:00
Alex Schroeder
70895ed631 Only print outline when there is more than one element. 2013-02-01 15:08:54 +01:00
Alex Schroeder
14a6cc4e2f Find existing TOC from toc.pl using the class attribute. 2013-02-01 15:05:04 +01:00
Alex Schroeder
83eaa45077 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-02-01 14:21:28 +01:00
Alex Schroeder
3a9b92f4a3 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-02-01 14:17:46 +01:00
Alex Schroeder
6e82239616 An extension to generate a table of content. 2013-02-01 14:09:59 +01:00
Alex Schroeder
8e2da8a1a9 Save $1 to prevent it from being overwritten by Tss. 2013-01-31 09:50:11 +01:00
Alex Schroeder
872b914c90 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-01-31 09:43:43 +01:00
Alex Schroeder
1e6f732fa9 Use LogWrite. 2013-01-31 09:43:05 +01:00
Alex Schroeder
925f0788fb Add $1 to the explanation if such a group is part of the regular
expression.
2013-01-31 09:29:25 +01:00
Alex Schroeder
3c0c79a526 logbannedcontent.pl: Logging BannedHosts as well.
Renamed wrappers to make sure all have the Log prefix.

Moved log writing to a separate sub. Provide a little wrapper text for
banned hosts.

Using TimeToW3 to use a standard date and time format.

Use the id if no page has been opened yet (since DoEdit calls
UserIsBanned and DoPost calls UserCanEdit before either calls
OpenPage).
2013-01-31 09:21:54 +01:00
Alex Schroeder
88e66e825e Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-01-30 17:04:24 -05:00
Alex Schroeder
fb7566ae53 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-01-30 16:04:47 +01:00
Alex Schroeder
9b05ea62c5 Protect TZget against crashes if the timestamp is undefined. 2013-01-30 16:03:40 +01:00
Alex Schroeder
4feccd6484 Testing for encoding problems in diff output. 2013-01-25 19:07:11 +01:00
Alex Schroeder
7d166842f0 Make sure banning happens without logging in RegexpNewBannedContent. 2013-01-25 00:03:51 +01:00
Alex Schroeder
c6943cad7b Package Oddmuse. 2013-01-24 23:51:12 +01:00
Alex Schroeder
c29037a9d6 Delete code that doesn't belong. 2013-01-24 23:50:31 +01:00
Alex Schroeder
e1b429c3b7 New 2013-01-24 23:45:24 +01:00
Alex Schroeder
c17c622c97 Intro 2013-01-24 23:31:11 +01:00
Alex Schroeder
9d11d42e5e New 2013-01-24 23:30:32 +01:00
Alex Schroeder
270e0f4932 Invert the loop when scanning for banned content. 2013-01-24 23:04:35 +01:00
Alex Schroeder
d1f6e1bb37 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-01-24 22:46:57 +01:00
Alex Schroeder
47e4ad5e41 New. 2013-01-24 22:46:18 +01:00
Alex Schroeder
78dd013fc0 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-01-23 23:58:06 +01:00
Alex Schroeder
c04403ca66 Download style for a.download. 2013-01-23 23:57:42 +01:00
Alex Schroeder
8c8e23b21a Fix comment. 2013-01-14 23:06:19 +01:00
Alex Schroeder
fd9a715634 Another fix for mail.pl... 2013-01-13 01:04:19 +01:00
Alex Schroeder
957729fd5d Fix subscription migration for mail.pl 2013-01-13 00:57:58 +01:00
Alex Schroeder
23fb0cf18b Added migration of subscriptions to mail.pl and tests. 2013-01-13 00:45:58 +01:00
Alex Schroeder
8e72af0a45 Fix encoding issues with the use of DB_File in mail.pl.
This also fixes the tests. Also get rid of wide character in print by
Test::Builder by adding the fix mentioned in the Test::More manpage.

The DB_File issue was necessary because a page name with an EN DASH
caused the script to crash, thus not printing the footer.
Unfortunately, this is solved by URL-encoding keys and values. This
means that your old mail.db is going to be invalid!
2013-01-12 23:55:08 +01:00
Alex Schroeder
26135820e1 UrlDecode doesn't need utf8::decode. 2013-01-12 23:50:59 +01:00
Alex Schroeder
2c3abffd2e Removed $Id$ from info-ref. 2013-01-12 23:50:11 +01:00
Alex Schroeder
4b46c5385e Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-01-11 04:45:30 -05:00
Alex Schroeder
5b7fdbdea4 Reading files using :utf8 instead of :encoding(utf-8).
This is discouraged because :utf8 does not validate the input. The
problem is that in some cases you can end up with invalid UTF-8 if
your wiki was created with a copy of Oddmuse that allowed raw bytes.
There, we requested users to provide UTF-8 input and printed it back
claiming that it was UTF-8, but in the end it was just a convention.
Spammers and vandals could upload anything they liked. This is why
your rc.log (and all other sorts of files) may contain invalid UTF-8
bytes. This is particularly troublesome in the case of your rc.log
files as these will never go away and they are read very often. The
resulting warnings will fill up your web server logs.
2013-01-11 10:40:35 +01:00
Alex Schroeder
61dae58368 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-01-11 10:34:18 +01:00
Alex Schroeder
6958b66bc5 twitter is a CGI script to process RSS feeds.
It strips the description of an item if it contains the same info as
the title, and it strips the username from the title, if you want.
2013-01-11 10:30:57 +01:00
Alex Schroeder
512cbf4ae9 Get rid of a warning.
Use response->decoded_content.
Add more sources.
Get rid of $Id.
2013-01-11 10:10:19 +01:00
Alex Schroeder
e188665a9b No longer validate the UTF-8 from rc.log files in GetRcLinesFor.
If the rc.log file contained invalid UTF-8, this would fill the server
logs with thousands of warnings. There is nothing we can do about
these (they probably originated from an older copy of the script).
Therefore, <:utf8 is now used instead of <:encoding(UTF-8).
2013-01-10 14:25:56 +01:00
Alex Schroeder
4898f970b0 Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-01-07 07:53:12 -05:00
Alex Schroeder
d9a2db5b8d Added support for sub and sup tags. 2013-01-07 13:51:39 +01:00
Alex Schroeder
7b518f14f0 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2013-01-07 13:41:23 +01:00
Alex Schroeder
ff4b889f1c Merge branch 'master' of ssh://as@git.sv.gnu.org/srv/git/oddmuse 2013-01-06 12:34:17 -05:00
Alex Schroeder
b4b6435826 Fix entities for superscripts and vulgar fractions. 2013-01-06 18:18:16 +01:00
Alex Schroeder
011953370a Print fractions. 2013-01-06 18:13:53 +01:00
Alex Schroeder
40a0b7104a Fixing encoding for RSS feed inclusion using HTTP::Response->decoded_content. 2013-01-01 14:24:44 +01:00
Alex Schroeder
964f8c38c0 Typo in a comment. 2013-01-01 14:13:06 +01:00
Alex Schroeder
e46c89e90f rc2mail now adds some newlines to the HTML mail being sent. 2012-12-21 11:14:09 +01:00
Alex Schroeder
99d8ff2b01 GetRaw uses HTTP::Response's decoded_content to fix encoding issues. 2012-12-11 04:53:51 -05:00
Alex Schroeder
dfbd5ad47e Fix encoding menu disappears if page is missing. 2012-11-28 07:39:14 +01:00
Alex Schroeder
68ea223940 No encoding to fix for non-existing pages
Add more tests to show that fix-encoding does not create a page if it
is missing.
2012-11-28 07:33:20 +01:00
Alex Schroeder
4fc84fa623 Make sure fix encoding doesn't save if nothing has changed 2012-11-21 21:39:13 +01:00
Alex Schroeder
33aa81d9c9 Fix encoding issues with translation links. 2012-11-21 21:29:41 +01:00
Alex Schroeder
58690662df Private pages no longer accessible using revision parameter 2012-11-21 21:11:49 +01:00
Alex Schroeder
b2194ebdac Added some tests for translation links. 2012-11-21 21:01:06 +01:00
Alex Schroeder
d584c4dc68 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2012-11-20 19:46:45 +01:00
Alex Schroeder
105ccdf323 Add header back to bulgarian-utf8.pl and chinese-utf8.pl 2012-11-20 15:47:03 +01:00
Alex Schröder
cb6d1cc17d Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse 2012-11-20 15:38:51 +01:00
Alex Schroeder
be4bddab30 No longer recreate -utf8 files. 2012-11-16 21:49:56 +01:00
Alex Schroeder
465b278303 Fix a loophole where using <include "..."> allowed you to see hidden pages. 2012-11-16 18:38:27 +01:00
Alex Schroeder
a52bebdcd2 Fix docstring. 2012-11-16 17:56:25 +01:00
Alex Schroeder
1caa4c55c0 Private pages! 2012-11-16 17:12:12 +01:00
Alex Schroeder
9c77e56568 Whitespace. 2012-11-16 15:51:56 +01:00
Alex Schroeder
f5e86f4ddc Prevent double "Showing revision X" by passing QUIET to GetTextRevision. 2012-11-16 15:51:19 +01:00
Alex Schröder
ff64a0ed82 Update by Hervé Robin 2012-11-05 13:56:03 +01:00
53 changed files with 1863 additions and 470 deletions

View File

@@ -24,8 +24,8 @@ build/%-utf8.pl: modules/translations/%-utf8.pl
# Currently oddtrans introduces encoding errors!
%-utf8.pl: wiki.pl $(MODULES)
perl oddtrans -l $@ $^ > $@-new && mv $@-new $@
# %-utf8.pl: wiki.pl $(MODULES)
# perl oddtrans -l $@ $^ > $@-new && mv $@-new $@
# from: http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/namespaces.pl
# to: http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/namespaces.pl?id=2.1-11-gd4f1e27

View File

@@ -1,21 +1,17 @@
#!/usr/bin/perl
# Copyright (C) 2005, 2006, 2007 Alex Schroeder <alex@emacswiki.org>
# Copyright (C) 2005, 2006, 2007, 2012 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# 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/>.
use strict;
use warnings;
@@ -38,6 +34,42 @@ my %indexes = (
=> 'GNU Emacs Lisp reference manual, Index',
'http://www.gnu.org/software/emacs/manual/html_node/elisp/index.html'
=> 'GNU Emacs Lisp reference manual, Top Menu',
'http://www.gnu.org/software/emacs/manual/html_node/message/Index.html'
=> 'Message Manual, Index',
'http://www.gnu.org/software/emacs/manual/html_node/gnus/Index.html'
=> 'The Gnus Newsreader, Index',
'http://www.gnu.org/software/emacs/manual/html_node/cl/Function-Index.html'
=> 'Common Lisp Extensions, Function Index',
'http://www.gnu.org/software/emacs/manual/html_node/ccmode/Variable-Index.html'
=> 'CC Mode Manual, Variable Index',
'http://www.gnu.org/software/emacs/manual/html_node/ccmode/Concept-and-Key-Index.html'
=> 'CC Mode Manual, Command and Function Index',
'http://www.gnu.org/software/emacs/manual/html_node/org/Index.html'
=> 'Org Mode Manual, Index',
'http://www.gnu.org/software/auctex/manual/auctex/Function-Index.html'
=> 'AUCTeX Manual, Function Index',
'http://www.gnu.org/software/auctex/manual/auctex/Variable-Index.html'
=> 'AUCTeX Manual, Variable Index',
'http://www.gnu.org/software/auctex/manual/auctex/Concept-Index.html'
=> 'AUCTeX Manual, Concept Index',
'http://www.gnu.org/software/texinfo/manual/texinfo/html_node/index.html'
=> 'Texinfo, Command and Variable Index',
'http://www.gnu.org/software/texinfo/manual/texinfo/html_node/General-Index.html'
=> 'Texinfo, General Index',
'http://www.gnu.org/software/texinfo/manual/info/html_node/Index.html'
=> 'Info, Index',
'http://www.gnu.org/software/emacs/manual/html_node/dired-x/Command-Index.html'
=> 'Dired Extra, Function Index',
'http://www.gnu.org/software/emacs/manual/html_node/dired-x/Variable-Index.html'
=> 'Dired Extra, Variable Index',
'http://www.gnu.org/software/coreutils/manual/html_node/Concept-index.html'
=> 'Coreutils, Index',
'http://www.gnu.org/software/diffutils/manual/html_node/Index.html'
=> 'Diffutils, Index',
'http://www.gnu.org/software/findutils/manual/html_node/find_html/Primary-Index.html'
=> 'Findutils, Primary Index',
'http://www.gnu.org/software/emacs/manual/html_node/ediff/Index.html'
=> 'Edfiff, Index',
);
my $db = '/org/org.emacswiki/htdocs/emacs/info-ref.dat';
@@ -63,7 +95,9 @@ sub ProcessRequest {
sub ShowForm {
print $q->header, $q->start_html,
$q->start_form, "Index entry: ", $q->textfield('find'), $q->submit, $q->end_form,
$q->p('$Id: info-ref,v 1.1 2007/07/13 23:20:57 as Exp $'),
$q->p($q->a({-href=>"http://www.emacswiki.org/scripts/info-ref"}, "Source"), $q->br(),
'Last DB update: ', TimeToText((stat($db))[9]),
' (' . $q->a({-href=>$q->url . '?init=1'}, "update") . ')'),
$q->end_html;
}
@@ -74,9 +108,11 @@ sub Find {
foreach my $line (split(/$nl/, $data)) {
my ($key, $rest) = split(/$fs/, $line);
$map{$key} = ();
foreach my $a (split(/$gs/, $rest)) {
my ($link, $label) = split(/$rs/, $a);
$map{$key}{$link} = $label;
if ($rest) {
foreach my $a (split(/$gs/, $rest)) {
my ($link, $label) = split(/$rs/, $a);
$map{$key}{$link} = $label;
}
}
}
my @links = keys %{$map{$str}};
@@ -150,7 +186,7 @@ sub GetRaw {
return unless eval { require LWP::UserAgent; };
my $ua = LWP::UserAgent->new;
my $response = $ua->get($uri);
return $response->content;
return $response->decoded_content;
}
sub ReadFile {
@@ -189,3 +225,18 @@ sub ReportError { # fatal!
print $q->start_html, $q->h2($errmsg), $q->end_html;
exit (1);
}
sub CalcDay {
my ($sec, $min, $hour, $mday, $mon, $year) = gmtime(shift);
return sprintf('%4d-%02d-%02d', $year+1900, $mon+1, $mday);
}
sub CalcTime {
my ($sec, $min, $hour, $mday, $mon, $year) = gmtime(shift);
return sprintf('%02d:%02d UTC', $hour, $min);
}
sub TimeToText {
my $t = shift;
return CalcDay($t) . ' ' . CalcTime($t);
}

51
contrib/twitter Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/perl
# Copyright (C) 2009, 2012 Alex Schroeder <alex@gnu.org>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by 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 CGI qw/:standard/;
use CGI::Carp qw(fatalsToBrowser);
use LWP::UserAgent;
use XML::RSS;
if (not param('feed')) {
print header(),
start_html('Description Stripping'),
h1('Description Stripping'),
p('Removes the description of an article if it matches the title. This is most useful for Twitter and other microblogging services.'),
p('Example input:', code('http://api.twitter.com/1/statuses/user_timeline.rss?screen_name=kensanata')),
start_form(-method=>'GET'),
p('Feed: ', textfield('feed', '', 40), checkbox('Strip username'),
submit()),
end_form(),
end_html();
exit;
}
$ua = LWP::UserAgent->new;
$request = HTTP::Request->new('GET', param('feed'));
$response = $ua->request($request);
$data = $response->content;
exit unless $data;
print header(-type=>$response->content_type);
$rss = new XML::RSS;
$rss->parse($data);
foreach my $i (@{$rss->{items}}) {
$i->{description} = undef if $i->{description} eq $i->{title};
$i->{title} =~ s/^.*?: // if param('Strip username');
}
print $rss->as_string;

View File

@@ -1,15 +1,18 @@
@font-face {
font-family: 'Garamond';
font-style: normal;
font-weight: 400;
src: local('Garamond'), local('GaramondNo8'), local('EB Garamond'), local('EBGaramond'), url(https://themes.googleusercontent.com/static/fonts/ebgaramond/v4/kYZt1bJ8UsGAPRGnkXPeFdIh4imgI8P11RFo6YPCPC0.woff) format('woff');
}
body, rss {
font-family: Garamond, GaramondNo8, "Bookman Old Style", Cochin, Baskerville, serif;
font-family: Garamond, serif;
font-size: 16pt;
line-height: 20pt;
margin:1em 3em;
padding:0;
}
body.sans {
font-family: Franklin Gothic Book, Corbel, Arial, sans-serif;
}
/* headings: we can use larger sizes if we use a lighter color.
we cannot inherit the font-family because header and footer use a narrow font. */
@@ -176,7 +179,6 @@ div.sister hr {
}
div.sister img {
border:none;
background-color:#ffe;
}
div.near, div.definition {
@@ -194,9 +196,6 @@ div.sidebar ul {
/* replacements, features */
del {
color: #666;
}
ins {
color: #b33;
text-decoration: none;
@@ -390,69 +389,69 @@ p.table + p { clear:both; }
@media print {
body {
font-size: 12pt;
line-height: 13pt;
color: #000;
background-color: #fff;
}
font-size: 12pt;
line-height: 13pt;
color: #000;
background-color: #fff;
}
/* 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 {
display:none;
div.rc form, form.tiny, p.comment, p#plus1, div.g-plusone {
display:none;
}
a,
a:visited,
div.content a.near:link,
div.content a.near:visited,
div.content a.near:active {
color:inherit;
font-weight: bold;
color:inherit;
font-weight: bold;
}
div.content a.feed {
display: none;
display: none;
}
div.content a.book,
div.content a.movie {
text-decoration: none;
text-decoration: none;
}
a cite {
font-style: italic;
font-style: italic;
}
/* no difference */
pre, code, tt {
font-size: inherit;
line-height: inherit;
font-size: inherit;
line-height: inherit;
}
/* no dotted underlines */
acronym, abbr {
border: none;
text-decoration: none;
border: none;
text-decoration: none;
}
/* headings */
h1 {
color: inherit;
margin-top: 2em;
}
h2 {
color:inherit;
margin: 1em 0;
font-variant: small-caps;
}
h3 {
font-weight:inherit;
font-style:italic;
color:inherit;
margin: 1em 0;
}
h1 a, h2 a, h3 a {
color: inherit;
}
div.journal h1 a:visited,
div.journal h2 a:visited,
div.journal h3 a:visited {
color: inherit;
color: inherit;
margin-top: 2em;
}
h2 {
color:inherit;
margin: 1em 0;
font-variant: small-caps;
}
h3 {
font-weight:inherit;
font-style:italic;
color:inherit;
margin: 1em 0;
}
h1 a, h2 a, h3 a {
color: inherit;
}
div.journal h1 a:visited,
div.journal h2 a:visited,
div.journal h3 a:visited {
color: inherit;
}
}

View File

@@ -249,7 +249,7 @@ dd, li {
/* elisp files and other scripts for download */
p.download a {
p.download a, a.download {
padding: 0.5em;
color: black;
text-decoration: none;

156
modules/ban-contributors.pl Normal file
View File

@@ -0,0 +1,156 @@
# 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);
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', $ENV{'REMOTE_ADDR'});
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;
}

84
modules/banned-regexps.pl Normal file
View File

@@ -0,0 +1,84 @@
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
package OddMuse;
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/banned-regexps.pl">banned-regexps.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Banning_Regular_Expressions">Banning Regular Expressions</a></p>';
=h1 Compatibility
This extension works with logbannedcontent.pl.
=h1 Example content for the BannedRegexps page:
# This page lists regular expressions that prevent the saving of a page.
# The regexps are matched against any page or comment submitted.
# The format is simple: # comments to the end of the line. Empty lines are ignored.
# Everything else is a regular expression. If the regular expression is followed by
# a comment, this is used as the explanation when a user is denied editing. If the
# comment starts with a date, this date is not included in the explanation.
# The date could help us decide which regular expressions to delete in the future.
# In other words:
# ^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$
# Group 1 is the regular expression to use.
# Group 4 is the explanation to use.
порно # 2012-12-31 Russian porn
<a\s+href=["']?http # 2012-12-31 HTML anchor tags usually mean spam
\[url= # 2012-12-31 bbCode links usually mean spam
\s+https?:\S+[ .\r\n]*$ # 2012-12-31 ending with a link usually means spam
(?s)\s+https?:\S+.*\s+https?:\S+.*\s+https?:\S+.* # 2012-12-31 three naked links usually mean spam
=cut
use vars qw($BannedRegexps);
$BannedRegexps = 'BannedRegexps';
push(@MyInitVariables, sub {
$AdminPages{$BannedRegexps} = 1;
$LockOnCreation{$BannedRegexps} = 1;
$PlainTextPages{$BannedRegexps} = 1;
});
*RegexpOldBannedContent = *BannedContent;
*BannedContent = *RegexpNewBannedContent;
# the above also changes the mapping for the variable!
$BannedContent = $RegexpOldBannedContent;
sub RegexpNewBannedContent {
my $str = shift;
my $rule = RegexpOldBannedContent($str, @_);
if (not $rule) {
foreach (split(/\n/, GetPageContent($BannedRegexps))) {
next unless m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/;
my ($regexp, $comment, $re) = ($1, $4, undef);
eval { $re = qr/$regexp/i; };
if (defined($re) && $str =~ $re) {
my $group1 = $1;
my $explanation = ($group1
? Tss('Regular expression "%1" matched "%2" on this page.', QuoteHtml($regexp), $group1)
: Ts('Regular expression "%s" matched on this page.', QuoteHtml($regexp)));
$rule = $explanation . ' '
. ($comment ? Ts('Reason: %s.', $comment) : T('Reason unknown.')) . ' '
. Ts('See %s for more information.', GetPageLink($BannedRegexps));
last;
}
}
}
LogWrite($rule) if $rule and $BannedFile;
return $rule if $rule;
return 0;
}

View File

@@ -1,17 +1,16 @@
# Copyright (C) 2007, 2008 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2007, 2008, 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 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/>.
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/bbcode.pl">bbcode.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/bbCode_Extension">bbCode Extension</a></p>';
@@ -37,6 +36,10 @@ sub bbCodeRule {
. qq{font-style: normal;"}); }
elsif ($tag eq 's' or $tag eq 'strike') {
return AddHtmlEnvironment('del'); }
elsif ($tag eq 'sub') {
return AddHtmlEnvironment('sub'); }
elsif ($tag eq 'sup') {
return AddHtmlEnvironment('sup'); }
elsif ($tag eq 'color') {
return AddHtmlEnvironment('em', qq{style="color: $option; }
. qq{font-style: normal;"}); }
@@ -96,7 +99,7 @@ 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 highlight strong};
s del strike del sub sub sup sup highlight strong};
# closing a block level element closes all elements
if ($bbBlock eq $translate{$tag}) {
/\G([ \t]*\n)*/cg; # eat whitespace after closing block level element

View File

@@ -391,7 +391,7 @@ sub CreoleRule {
my $is_right_justified = $3;
# Now that we've retrieved all numbered matches, match another lookahead.
my $is_left_justified = m/\G(?=.*?[ \t]+\|)/;
my $is_left_justified = m/\G(?=[^\n|]*?[ \t]+\|)/;
my $attributes = $column_span == 1 ? '' : qq{colspan="$column_span"};
if ($is_left_justified and

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

@@ -0,0 +1,49 @@
# Copyright (C) 20072013 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/duckduckgo-search.pl">duckduckgo-search.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Use_DuckDuckGo_For_Searches">Use DuckDuckGo For Searches</a></p>';
use vars qw($DuckDuckGoSearchDomain);
$DuckDuckGoSearchDomain = undef;
$Action{search} = \&DoDuckDuckGoSearch;
push(@MyInitVariables, \&DuckDuckGoSearchInit);
sub DuckDuckGoSearchInit {
# If $ScriptName does not contain a hostname, this extension will
# have no effect. Domain regexp based on RFC 2396 section 3.2.2.
if (!$DuckDuckGoSearchDomain) {
my $alpha = '[a-zA-Z]';
my $alphanum = '[a-zA-Z0-9]';
my $alphanumdash = '[-a-zA-Z0-9]';
my $domainlabel = "$alphanum($alphanumdash*$alphanum)?";
my $toplabel = "$alpha($alphanumdash*$alphanum)?";
if ($ScriptName =~ m!^(https?://)?([^/]+\.)?($domainlabel\.$toplabel)\.?(:|/|\z)!) {
$DuckDuckGoSearchDomain = $3;
}
}
if ($DuckDuckGoSearchDomain
and GetParam('search', undef)
and not GetParam('action', undef)
and not GetParam('old', 0)) {
SetParam('action', 'search');
}
}
sub DoDuckDuckGoSearch {
my $search = 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

@@ -13,7 +13,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/fix-encoding.pl">fix-encoding.pl</a></p>';
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/fix-encoding.pl">fix-encoding.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Fix_Encoding">Fix Encoding</a></p>';
$Action{'fix-encoding'} = \&FixEncoding;
@@ -24,7 +24,7 @@ sub FixEncoding {
OpenPage($id);
my $text = $Page{text};
utf8::decode($text);
Save($id, $text, 'fix encoding', 1);
Save($id, $text, 'fix encoding', 1) if $text ne $Page{text};
ReleaseLock();
ReBrowsePage($id);
}

60
modules/fractions.pl Normal file
View File

@@ -0,0 +1,60 @@
# 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/>.
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/fractions.pl">fractions.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Fractions">Fractions</a></p>';
push(@MyRules, \&FractionsRule);
# usage: ^1/32
sub FractionsRule {
if (/\G\^([0-9]+)\/([0-9]+)/cg) {
if ($1 == 1 and $2 == 4) { return "\&#x00bc;"; }
elsif ($1 == 1 and $2 == 2) { return "\&#x00bd;"; }
elsif ($1 == 3 and $2 == 4) { return "\&#x00be;"; }
elsif ($1 == 1 and $2 == 7) { return "\&#x2150;"; }
elsif ($1 == 1 and $2 == 9) { return "\&#x2151;"; }
elsif ($1 == 1 and $2 == 10) { return "\&#x2152;"; }
elsif ($1 == 1 and $2 == 3) { return "\&#x2153;"; }
elsif ($1 == 2 and $2 == 3) { return "\&#x2154;"; }
elsif ($1 == 1 and $2 == 5) { return "\&#x2155;"; }
elsif ($1 == 2 and $2 == 5) { return "\&#x2156;"; }
elsif ($1 == 3 and $2 == 5) { return "\&#x2157;"; }
elsif ($1 == 4 and $2 == 5) { return "\&#x2158;"; }
elsif ($1 == 1 and $2 == 6) { return "\&#x2159;"; }
elsif ($1 == 5 and $2 == 6) { return "\&#x215a;"; }
elsif ($1 == 1 and $2 == 8) { return "\&#x215b;"; }
elsif ($1 == 3 and $2 == 8) { return "\&#x215c;"; }
elsif ($1 == 5 and $2 == 8) { return "\&#x215d;"; }
elsif ($1 == 7 and $2 == 8) { return "\&#x215e;"; }
else {
my $html;
# superscripts
for my $char (split(//, $1)) {
if ($char eq '1') { $html .= "\&#x00b9;"; }
elsif ($char eq '2') { $html .= "\&#x00b2;"; }
elsif ($char eq '3') { $html .= "\&#x00b3;"; }
else { $html .= "\&#x207$char;"; }
}
# fraction slash
$html .= '&#x2044;';
# subscripts
for my $char (split(//, $2)) {
$html .= "\&#x208$char;";
}
return $html;
}
}
return undef;
}

View File

@@ -133,26 +133,50 @@ sub GitNewDeletePage {
push(@MyMaintenance, \&GitCleanup);
$Action{git} = \&DoGitCleanup;
sub DoGitCleanup {
UserIsAdminOrError();
print GetHeader('', 'Git', '');
print $q->start_div({-class=>'content git'});
RequestLockOrError();
print $q->p(T('Main lock obtained.')), '<p>',
T('Cleaning up git repository');
GitCleanup();
ReleaseLock();
print $q->p(T('Main lock released.')), $q->end_div();
PrintFooter();
}
sub GitCleanup {
if (-d $GitRepo) {
print $q->p("Git cleanup starting");
AllPagesList();
# delete all the files including all the files starting with a dot
opendir(DIR, $GitRepo) or ReportError("cannot open directory $GitRepo: $!");
foreach my $file (readdir(DIR)) {
next if $file eq '.git' or $file eq '.' or $file eq '..';
unlink "$GitRepo/$file" or ReportError("cannot delete $GitRepo/$file: $!");
my $name = $file;
utf8::decode($name); # filenames are bytes
next if $file eq '.git' or $file eq '.' or $file eq '..' or $IndexHash{$name};
print $q->p("Deleting left over file $name");
unlink "$GitRepo/$file" or ReportError("cannot delete $GitRepo/$name: $!");
}
closedir DIR;
# write all the files again
foreach my $id (AllPagesList()) {
# write all the files again, just to be sure
print $q->p("Rewriting all the files, just to be sure");
foreach my $id (@IndexList) {
OpenPage($id);
WriteStringToFile("$GitRepo/$id", $Page{text});
}
# run git!
chdir($GitRepo); # important for all the git commands that follow!
# add any new files
print $q->p("Adding new files, if any");
GitRun('add', '.');
# commit the new state
GitRun('commit', '--quiet', '-a', '-m', 'maintenance job',
"--author=Oddmuse <$GitMail>");
print $q->p("Committing changes, if any");
# try to protect against mysterious crashes
print $q->pre(`$GitBinary commit --quiet -a -m 'maintenance job' --author=Oddmuse <$GitMail>` . ' ');
print $q->p("Git done");
}
}

View File

@@ -50,7 +50,7 @@ sub NewGoogleCustomGetSearchLink {
sub NewGoogleCustomGetHeader {
my $html = OldGoogleCustomGetHeader(@_);
$form .= qq {
my $form = qq {
<!-- Google CSE Search Box Begins -->
<form class="tiny" action="http://www.google.com/cse" id="searchbox_004774160799092323420:6-ff2s0o6yi"><p>
<input type="hidden" name="cx" value="004774160799092323420:6-ff2s0o6yi" />

View File

@@ -0,0 +1,63 @@
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by 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/list-banned-content.pl">list-banned-content.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/BannedContent">Index Extension</a></p>';
$Action{'list-banned-content'} = \&DoListBannedContent;
sub DoListBannedContent {
print GetHeader('', T('Banned Content'), '');
my @pages = AllPagesList();
my %url_regexps;
my %text_regexps;
foreach (split(/\n/, GetPageContent($BannedContent))) {
next unless m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/;
$url_regexps{qr($1)} = $4;
}
foreach (split(/\n/, GetPageContent($BannedRegexps))) {
next unless m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/;
$text_regexps{qr($1)} = $4;
}
print '<div class="content banned"><p>';
print $BannedContent . ': ' . scalar(keys(%url_regexps)) . $q->br() . "\n";
print $BannedRegexps . ': ' . scalar(keys(%text_regexps)) . $q->br() . "\n";
PAGE: foreach my $id (@pages) {
OpenPage($id);
my @urls = $str =~ /$FullUrlPattern/go;
foreach my $url (@urls) {
foreach my $re (keys %url_regexps) {
if ($url =~ $re) {
print GetPageLink($id) . ': '
. Tss('Rule "%1" matched "%2" on this page.', $re, $url) . ' '
. ($url_regexps{$re}
? Ts('Reason: %s.', $url_regexps{$re})
: T('Reason unknown.')) . $q->br() . "\n";
next PAGE;
}
}
}
foreach my $re (keys %text_regexps) {
if ($Page{text} =~ $re) {
print GetPageLink($id) . ': '
. Tss('Rule "%1" matched on this page.', $re) . ' '
. ($text_regexps{$re}
? Ts('Reason: %s.', $text_regexps{$re})
: T('Reason unknown.')) . $q->br() . "\n";
next PAGE;
}
}
}
print '</p></div>';
PrintFooter();
}

View File

@@ -20,25 +20,31 @@ use vars qw($BannedFile);
$BannedFile = "$DataDir/spammer.log" unless defined $BannedFile;
*OldBannedContent = *BannedContent;
*BannedContent = *LogBannedContent;
$BannedContent = $OldBannedContent; # copy variable
*LogOldBannedContent = *BannedContent;
*BannedContent = *LogNewBannedContent;
$BannedContent = $LogOldBannedContent; # copy variable
sub LogBannedContent {
sub LogNewBannedContent {
my $str = shift;
my $rule = OldBannedContent($str);
if ($rule) {
my $visitor = $ENV{'REMOTE_ADDR'};
($sec, $min, $hr, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
$year=$year+1900;
$mon += 1;
# Fix for 0's
$mon = sprintf("%02d", $mon);
$mday = sprintf("%02d", $mday);
$sec = sprintf("%02d", $sec);
$min = sprintf("%02d", $min);
$hr = sprintf("%02d", $hr);
AppendStringToFile($BannedFile, "$year/$mon/$mday\t$hr:$min:$sec\t$visitor: $OpenPageName - $rule\n");
}
my $rule = LogOldBannedContent($str);
LogWrite($rule) if $rule;
return $rule;
}
*LogOldUserIsBanned = *UserIsBanned;
*UserIsBanned = *LogNewUserIsBanned;
sub LogNewUserIsBanned {
my $str = shift;
my $rule = LogOldUserIsBanned($str);
LogWrite(Ts('Host or IP matched %s', $rule)) if $rule;
return $rule;
}
sub LogWrite {
my $rule = shift;
my $id = $OpenPageName || GetId();
AppendStringToFile($BannedFile,
join("\t", TimeToW3($Now), $ENV{'REMOTE_ADDR'}, $id, $rule)
. "\n");
}

View File

@@ -102,7 +102,7 @@ sub MailIsSubscribed {
# open the DB file
require DB_File;
tie %h, "DB_File", $MailFile;
my %subscribers = map {$_=>1} split(/$FS/, $h{$id});
my %subscribers = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($id)}));
untie %h;
return $subscribers{$mail};
}
@@ -179,13 +179,13 @@ sub MailDeletePage {
my $id = shift;
require DB_File;
tie %h, "DB_File", $MailFile;
foreach my $mail (split(/$FS/, delete $h{$id})) {
my %subscriptions = map {$_=>1} split(/$FS/, $h{$mail});
foreach my $mail (split(/$FS/, UrlDecode(delete $h{UrlEncode($id)}))) {
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
delete $subscriptions{$id};
if (%subscriptions) {
$h{$mail} = join($FS, keys %subscriptions);
$h{UrlEncode($mail)} = UrlEncode(join($FS, keys %subscriptions));
} else {
delete $h{$mail};
delete $h{UrlEncode($mail)};
}
}
untie %h;
@@ -256,7 +256,7 @@ sub MailSubscription {
return unless $mail;
require DB_File;
tie %h, "DB_File", $MailFile;
my @result = split(/$FS/, $h{$mail});
my @result = split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
untie %h;
return sort @result;
}
@@ -283,15 +283,16 @@ sub DoMailSubscriptionList {
'<ul>';
}
require DB_File;
tie %h, "DB_File", $MailFile;
foreach my $key (sort keys %h) {
my @values = sort split(/$FS/, $h{$key});
foreach my $encodedkey (sort keys %h) {
my @values = sort split(/$FS/, UrlDecode($h{$encodedkey}));
my $key = UrlDecode($encodedkey);
if ($raw) {
print join(' ', $key, @values) . "\n";
} else {
print $q->li(Ts('%s: ', MailLink($key, @values)),
join(' ', map { MailLink($_, $key) }
sort split(/$FS/, $h{$key})));
join(' ', map { MailLink($_, $key) } @values));
}
}
print '</ul></div>' unless $raw;
@@ -363,16 +364,16 @@ sub MailSubscribe {
require DB_File;
tie %h, "DB_File", $MailFile;
# add to the mail entry
my %subscriptions = map {$_=>1} split(/$FS/, $h{$mail});
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
for my $id (@pages) {
$subscriptions{$id} = 1;
}
$h{$mail} = join($FS, keys %subscriptions);
$h{UrlEncode($mail)} = UrlEncode(join($FS, keys %subscriptions));
# add to the page entries
for my $id (@pages) {
my %subscribers = map {$_=>1} split(/$FS/, $h{$id});
my %subscribers = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($id)}));
$subscribers{$mail} = 1;
$h{$id} = join($FS, keys %subscribers);
$h{UrlEncode($id)} = UrlEncode(join($FS, keys %subscribers));
}
untie %h;
# changes made will affect how pages look
@@ -420,53 +421,67 @@ sub MailUnsubscribe {
return unless $mail and @pages;
require DB_File;
tie %h, "DB_File", $MailFile;
my %subscriptions = map {$_=>1} split(/$FS/, $h{$mail});
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
foreach my $id (@pages) {
delete $subscriptions{$id};
# take care of reverse lookup
my %subscribers = map {$_=>1} split(/$FS/, $h{$id});
my %subscribers = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($id)}));
delete $subscribers{$mail};
if (%subscribers) {
$h{$id} = join($FS, keys %subscribers);
$h{UrlEncode($id)} = UrlEncode(join($FS, keys %subscribers));
} else {
delete $h{$id};
delete $h{UrlEncode($id)};
}
}
if (%subscriptions) {
$h{$mail} = join($FS, keys %subscriptions);
$h{UrlEncode($mail)} = UrlEncode(join($FS, keys %subscriptions));
} else {
delete $h{$mail} unless %subscriptions;
delete $h{UrlEncode($mail)} unless %subscriptions;
}
untie %h;
# changes made will affect how pages look
SetParam('sub', GetParam('sub', 0) + 1);
}
=head1 Sending Mail
=head1 Migrate
The actual sending of emails depends on setting the appropriate
options.
C<$MailServer>: This defaults to localhost. If you have Oddmuse
installed on a full server that includes an SMTP server such as
sendmail or postfix, you might get away with not setting any of the
variables.
C<$MailUser>: Chances are you will need to authenticate before you can
send an email via your mail server. Specify the name here.
C<$MailPassword>: Specify the password here.
C<$MailFrom>: Often mail servers know which email addresses they
serve. If somebody else tries to use them, they'll return an error
saying that "relaying is not allowed". If you are allowed to use the
mail server, use this option to set the appropriate sender address.
Example setup:
$MailServer = 'smtp.google.com';
$MailUser = 'kensanata';
$MailPassword = '*secret*';
$MailFrom = 'kensanata@gmail.com';
The mailmigrate action will migrate your subscription list from the
old format to the new format. This is necessary because these days
because the keys and values of the DB_File are URL encoded.
=cut
$Action{'migrate-subscriptions'} = \&DoMailMigration;
sub DoMailMigration {
UserIsAdminOrError();
print GetHeader('', T('Migrating Subscriptions')),
$q->start_div({-class=>'content mailmigrate'});
require DB_File;
tie %h, "DB_File", $MailFile;
my $found = 0;
foreach my $key (keys %h) {
if (index($key, '@') != -1) {
$found = 1;
last;
}
}
if (not $found) {
print $q->p(T('No non-migrated email addresses found, migration not necessary.'));
} else {
my %n;
foreach my $key (sort keys %h) {
my $value = $h{$key};
my @values = sort split(/$FS/, $value);
$n{UrlEncode($key)} = join($FS, map { UrlEncode($_) } @values);
}
%h = %n;
print $q->p(Ts('Migrated %s rows.', scalar(keys %n)));
}
print '</div>';
untie %h;
PrintFooter();
}

View File

@@ -144,6 +144,8 @@ sub MarkupRule {
return CloseHtmlEnvironments()
. MarkupTag($MarkupLines{UnquoteHtml($tag)}, $str)
. AddHtmlEnvironment('p');
} elsif (%MarkupSingles and m/$markup_singles_re/gc) {
return $MarkupSingles{UnquoteHtml($1)};
} elsif (%MarkupForcedPairs and m/$markup_forced_pairs_re/gc) {
my $tag = $1;
my $start = $tag;
@@ -170,8 +172,6 @@ sub MarkupRule {
}
} elsif (%MarkupPairs and m/$markup_pairs_re/gc) {
return MarkupTag($MarkupPairs{UnquoteHtml($1)}, $2);
} elsif (%MarkupSingles and m/$markup_singles_re/gc) {
return $MarkupSingles{UnquoteHtml($1)};
} elsif ($MarkupPairs{'/'} and m|\G~/|gc) {
return '~/'; # fix ~/elisp/ example
} elsif ($MarkupPairs{'/'} and m|\G(/[-A-Za-z0-9\x{0080}-\x{fffd}/]+/$words/)|gc) {

View File

@@ -376,7 +376,7 @@ sub NewNamespaceBrowsePage {
#REDIRECT into different namespaces
my ($id, $raw, $comment, $status) = @_;
OpenPage($id);
my ($text, $revision) = GetTextRevision(GetParam('revision', ''));
my ($text, $revision) = GetTextRevision(GetParam('revision', ''), 1);
my $oldId = GetParam('oldid', '');
if (not $oldId and not $revision and (substr($text, 0, 10) eq '#REDIRECT ')
and (($WikiLinks and $text =~ /^\#REDIRECT\s+(($InterSitePattern:)?$InterLinkPattern)/)

View File

@@ -64,6 +64,7 @@ $ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git
'4-17' => 'Syrien: Abzug der letzten französischen Mandatstruppen 1946',
'4-18' => 'Simbabwe: Erlangung der Unabhängigkeit',
'4-19' => 'Jahrestag der Papstwahl (Benedikt XVI.)',
'4-25' => 'Italien: Tag der Befreiung 1945',
'4-26' => 'Tansania: Jahrestag der Vereinigung von Tanganjika und Sansibar 1964',
'4-27' => 'Afghanistan: Tag der Revolution, Sierra Leone: Erlangung der Unabhängigkeit 1961, Südafrika: Tag der ersten freien Wahlen 1994, Togo: Erlangung der Unabhängigkeit 1960',
'4-30' => 'Niederlande: Königinnentag',

View File

@@ -51,6 +51,7 @@ $ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git
'4-17' => 'Syria, National Day',
'4-18' => 'Zimbabwe, National Day',
'4-19' => 'Sierra Leone, Republic Day',
'4-25' => 'Italy, Liberation Day',
'4-26' => 'Tanzania, Union Day, Israel, Independence Day',
'4-27' => 'Federal Republic of Yugoslavia, National Day, Togo, Togolais National Day, South Africa, Freedom Day',
'4-30' => 'The Netherlands, Official Birthday of Her Majesty Queen Beatrix',
@@ -66,7 +67,7 @@ $ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git
'5-26' => 'Georgia, National Day',
'5-28' => 'Ethiopia, National Day, Azerbaijan, National Day',
'6-1' => 'Samoa, Independence Day',
'6-2' => 'Italy, Foundation of the Republic',
'6-2' => 'Italy, Republic Day',
'6-4' => 'Tonga, Emancipation Day',
'6-6' => 'Sweden, National Day',
'6-10' => 'Portugal, Portugal Day, Camões Day and Day of Portuguese Communities',

158
modules/private-pages.pl Normal file
View File

@@ -0,0 +1,158 @@
# 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
# 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 Private Pages Extension
This module allows you to hide the content of particular pages in Oddmuse.
Unlike the I<Hidden Pages Extension>, this is not based on the user's role of
editor or administrator. Instead, every page can have a different password by
beginning it with #PASSWORD XYZZY where XYZZY is the password required to read
it. Multiple passwords can be supplied, separated by spaces.
Note that all the meta information of the private page remains public: The
I<name> of the page, the fact that is has been edited, the author, the
revision, the content of past revisions that have not been protected by a
password all remain visible to other users.
Notes:
=over
=item * This extension might not work in a mod_perl environment because it
sets C<$NewText> without ever resetting it.
=item * If you're protecting a comment page, people can still leave comments
-- they just can't read the resulting page.
=back
=cut
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/private-pages.pl">private-pages.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Private_Pages_Extension">Private Pages Extension</a></p>';
sub PrivatePageLocked {
my $text = shift;
my ($line) = split(/\n/, $text, 1);
my @token = split(/\s+/, $line);
my $lock = 0;
if (shift(@token) eq '#PASSWORD') {
my $pwd = GetParam('pwd', '');
$lock = 1;
foreach (@token) {
if ($pwd eq $_) {
$lock = 0;
break;
}
}
}
return $lock;
}
*OldPrivatePagesUserCanEdit = *UserCanEdit;
*UserCanEdit = *NewPrivatePagesUserCanEdit;
sub NewPrivatePagesUserCanEdit {
my ($id, $editing, @rest) = @_;
my $result = OldPrivatePagesUserCanEdit($id, $editing, @rest);
# bypass OpenPage and GetPageContent (these are redefined below)
if ($result > 0 and $editing and $IndexHash{$id}) {
my %data = ParseData(ReadFileOrDie(GetPageFile($id)));
if (PrivatePageLocked($data{text})) {
return 0;
}
}
return $result;
}
sub PrivatePageMessage {
return Ts('This page is password protected. If you know the password, you can %s. Once you have done that, return and reload this page.',
'[' . ScriptUrl('action=password') . ' '
. 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;
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;
sub NewPrivatePagesGetPageContent {
my $text = OldPrivatePagesGetPageContent(@_);
if (PrivatePageLocked($text)) {
return PrivatePageMessage();
}
return $text;
}
# prevent reading of old revisions
*OldPrivatePagesGetTextRevision = *GetTextRevision;
*GetTextRevision = *NewPrivatePagesGetTextRevision;
sub NewPrivatePagesGetTextRevision {
my ($text, $revision) = OldPrivatePagesGetTextRevision(@_);
if (PrivatePageLocked($text)) {
return (PrivatePageMessage(), $revision);
}
return ($text, $revision);
}
# hide #PASSWORD
push(@MyRules, \&PrivatePageRule);
sub PrivatePageRule {
if (pos == 0 && m/\G#PASSWORD.*\n/gc) {
return '';
}
return undef;
}
# prevent leaking of edit summary
*OldPrivatePagesGetSummary = *GetSummary;
*GetSummary = *NewPrivatePagesGetSummary;
sub NewPrivatePagesGetSummary {
my $text = GetParam('text', '');
if ($text and $text =~ /^#PASSWORD\b/
# no text means aftertext is set (leaving a comment)
or $Page{text} =~ /^#PASSWORD\b/) {
# if no summary was set, set something in order to avoid the default
return '';
}
return OldPrivatePagesGetSummary();
}

View File

@@ -127,10 +127,10 @@ sub NewTagSave { # called within a lock!
# an encoded string; the alternative would be to use a form of
# freeze and thaw.
foreach my $tag (keys %tag) {
my %file = map {$_=>1} split(/$FS/, $h{$tag});
my %file = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($tag)}));
if (not $file{$id}) {
$file{$id} = 1;
$h{$tag} = join($FS, keys %file);
$h{UrlEncode($tag)} = UrlEncode(join($FS, keys %file));
}
}
@@ -138,16 +138,16 @@ sub NewTagSave { # called within a lock!
# tags used. This allows us to delete the references that no longer
# show up without looping through them all. The files are indexed
# with a starting underscore because this is an illegal tag name.
foreach my $tag (split (/$FS/, $h{"_$id"})) {
foreach my $tag (split (/$FS/, UrlDecode($h{UrlEncode("_$id")}))) {
# If the tag we're looking at is no longer listed, we have work to
# do.
if (!$tag{$tag}) {
my %file = map {$_=>1} split(/$FS/, $h{$tag});
my %file = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($tag)}));
delete $file{$id};
if (%file) {
$h{$tag} = join($FS, keys %file);
$h{UrlEncode($tag)} = UrlEncode(join($FS, keys %file));
} else {
delete $h{$tag};
delete $h{UrlEncode($tag)};
}
}
}
@@ -155,9 +155,9 @@ sub NewTagSave { # called within a lock!
# Store the new reverse lookup of all the tags used on the current
# page. If no more tags appear on this page, delete the entry.
if (%tag) {
$h{"_$id"} = join($FS, keys %tag);
$h{UrlEncode("_$id")} = UrlEncode(join($FS, keys %tag));
} else {
delete $h{"_$id"};
delete $h{UrlEncode("_$id")};
}
untie %h;
@@ -183,18 +183,18 @@ sub NewTagDeletePage { # called within a lock!
# For each file in our hash, we have a reverse lookup of all the
# tags used. This allows us to delete the references that no longer
# show up without looping through them all.
foreach my $tag (split (/$FS/, $h{"_$id"})) {
my %file = map {$_=>1} split(/$FS/, $h{$tag});
foreach my $tag (split (/$FS/, UrlDecode($h{UrlEncode("_$id")}))) {
my %file = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($tag)}));
delete $file{$id};
if (%file) {
$h{$tag} = join($FS, keys %file);
$h{UrlEncode($tag)} = UrlEncode(join($FS, keys %file));
} else {
delete $h{$tag};
delete $h{UrlEncode($tag)};
}
}
# Delete reverse lookup entry.
delete $h{"_$id"};
delete $h{UrlEncode("_$id")};
untie %h;
# Return any error codes?
@@ -217,8 +217,7 @@ sub TagFind {
tie %h, "DB_File", $TagFile;
my %page;
foreach my $tag (@tags) {
foreach my $id (split(/$FS/, $h{lc($tag)})) {
utf8::decode($id);
foreach my $id (split(/$FS/, UrlDecode($h{UrlEncode(lc($tag))}))) {
$page{$id} = 1;
}
}
@@ -292,20 +291,20 @@ sub TagCloud {
my $max = 0;
my $min = 0;
my %count = ();
foreach my $tag (grep !/^_/, keys %h) {
$count{$tag} = split(/$FS/, $h{$tag});
$max = $count{$tag} if $count{$tag} > $max;
$min = $count{$tag} if not $min or $count{$tag} < $min;
foreach my $encoded_tag (grep !/^_/, keys %h) {
$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;
}
untie %h;
foreach my $tag (sort keys %count) {
my $n = $count{$tag};
print $q->a({-href => "$ScriptName?search=tag:" . UrlEncode($tag),
foreach my $encoded_tag (sort keys %count) {
my $n = $count{$encoded_tag};
print $q->a({-href => "$ScriptName?search=tag:" . $encoded_tag,
-title => $n,
-style => 'font-size: '
. int(80+120*($max == $min ? 1 : ($n-$min)/($max-$min)))
. '%;',
}, NormalToFree($tag)), T(' ... ');
}, NormalToFree(UrlDecode($encoded_tag))), T(' ... ');
}
print '</p></div>';
PrintFooter();
@@ -352,21 +351,18 @@ sub DoTagsReindex {
$Page{text} =~ m/\[\[tag:$FreeLinkPattern\|([^]|]+)\]\]/g);
next unless %tag;
# utf8::encode($id);
# back to bytes for the following installation:
# This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi
# (with 56 registered patches, see perl -V for more detail)
# (FIXME: get rid of this call, or explain why no UTF-8 can be stored in %h)
# For each tag we list the files tagged. Add the current file for
# all tags.
foreach my $tag (keys %tag) {
$h{$tag} = $h{$tag} ? $h{$tag} . $FS . $id : $id;
my $encoded_tag = UrlEncode($tag);
$h{$encoded_tag} = $h{$encoded_tag}
? $h{$encoded_tag} . UrlEncode($FS . $id)
: UrlEncode($id);
}
# Store the reverse lookup of all the tags used on the current
# page.
$h{"_$id"} = join($FS, keys %tag);
$h{UrlEncode("_$id")} = UrlEncode(join($FS, keys %tag));
}
untie %h;
@@ -391,8 +387,8 @@ sub TagList {
# open the DB file
require DB_File;
tie %h, "DB_File", $TagFile;
foreach my $id (sort keys %h) {
print "$id: " . join(', ', split(/$FS/, $h{$id})) . "\n";
foreach my $id (sort map { UrlDecode($_) } keys %h) {
print "$id: " . join(', ', split(/$FS/, UrlDecode($h{UrlEncode($id)}))) . "\n";
}
untie %h;
}
@@ -416,7 +412,7 @@ sub TagsMenu {
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2005, 2009 Alex Schroeder <alex@gnu.org>
Copyright (C) 2005, 2009, 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

View File

@@ -170,6 +170,10 @@ $TexRe = qr{$TexRe};
push(@MyRules, \&TexRule);
# use of -- conflicts with MarkupRules such as the following:
# $MarkupForcedPairs{'--'} = 'del';
$RuleOrder{\&TexRule} = 160;
sub TexRule {
if (m/\G$TexRe/goc) {
return $Tex{$1};

View File

@@ -25,7 +25,9 @@ $defaultTZ = 'UTC';
$CookieParameters{time} = '';
sub TZget {
my $dt = DateTime->from_epoch(epoch=>shift);
my $ts = shift;
$ts = 0 if not defined($ts);
my $dt = DateTime->from_epoch(epoch=>$ts);
my $tz = GetParam('time', '');
# setting time= will use the (defined) empty string, so avoid that
$tz = $defaultTZ unless $tz;

98
modules/toc-js.pl Normal file
View File

@@ -0,0 +1,98 @@
# 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/>.
package OddMuse;
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/toc-js.pl">toc-js.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Javascript_Table_of_Contents_Extension">Javascript Table of Contents Extension</a></p>';
use vars qw($TocOutlineLibrary);
$TocOutlineLibrary = 'http://h5o.googlecode.com/files/outliner.0.5.0.62.js';
# Add the dojo script to edit pages.
push (@MyInitVariables, \&TocScript);
sub TocScript {
# cookie is not initialized yet so we cannot use GetParam
# Cross browser compatibility: http://www.tek-tips.com/faqs.cfm?fid=4862
# HTML5 Outlines: http://blog.tremily.us/posts/HTML5_outlines/
# Required library: http://code.google.com/p/h5o/
if (GetParam('action', 'browse') eq 'browse') {
$HtmlHeaders .= qq{
<script type="text/javascript" src="$TocOutlineLibrary"></script>
<script type="text/javascript">
function addOnloadEvent(fnc) {
if ( typeof window.addEventListener != "undefined" )
window.addEventListener( "load", fnc, false );
else if ( typeof window.attachEvent != "undefined" ) {
window.attachEvent( "onload", fnc );
}
else {
if ( window.onload != null ) {
var oldOnload = window.onload;
window.onload = function ( e ) {
oldOnload( e );
window[fnc]();
};
}
else
window.onload = fnc;
}
}
var initToc=function() {
var outline = HTML5Outline(document.body);
if (outline.sections.length == 1) {
outline.sections = outline.sections[0].sections;
}
if (outline.sections.length > 1
|| outline.sections.length == 1
&& outline.sections[0].sections.length > 0) {
var toc = document.getElementById('toc');
if (!toc) {
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
if (divs[i].getAttribute('class') == 'toc') {
toc = divs[i];
break;
}
}
}
if (!toc) {
var h2 = document.getElementsByTagName('h2')[0];
if (h2) {
toc = document.createElement('div');
toc.setAttribute('class', 'toc');
h2.parentNode.insertBefore(toc, h2);
}
}
if (toc) {
var html = outline.asHTML(true);
toc.innerHTML = html;
}
}
}
addOnloadEvent(initToc);
</script>
};
}
}

View File

@@ -200,9 +200,13 @@ sub DoTranslationLink {
}
print $q->p($q->label({-for=>'target'}, T('Translated page: ')),
$q->textfield('target', '', 40),
$q->hidden('action', 'translate'),
$q->hidden('id', $source),
$q->hidden('missing', GetParam('missing', '')),
# don't use $q->hidden or you'll get encoding errors
$q->input({-type=>'hidden', -name=>'id',
-value=>$source}),
$q->input({-type=>'hidden', -name=>'action',
-value=>'translate'}),
$q->input({-type=>'hidden', -name=>'missing',
-value=>GetParam('missing', '')}),
$q->submit('dotranslate', T('Go!')));
print $q->endform, $q->end_div();
PrintFooter();

View File

@@ -1,3 +1,25 @@
# UTF-8 encoded Bulgarian translation file for use with Oddmuse
#
# Copyright (c) 2004 Stanislav Traykov <stanislav@tortoises.org>
# Copyright (c) 2003, 2004 Alex Schröder <alex@emacswiki.org>
#
# Permission is granted to copy, distribute and/or modify this
# document under the terms of the GNU Free Documentation License,
# Version 1.2 or any later version published by the Free Software
# Foundation; with no Invariant Sections, no Front-Cover Texts, and no
# Back-Cover Texts. A copy of the license could be found at:
# http://www.gnu.org/licenses/fdl.txt .
#
# Installation:
# =============
#
# Create a modules subdirectory in your data directory, and put the
# file in there. It will be loaded automatically.
#
# This translation was updated for Oddmuse 1.354.
#
use utf8;
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/translations/bulgarian-utf8.pl">bulgarian-utf8.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Bulgarian">Bulgarian</a></p>';
%Translate = split(/\n/,<<END_OF_TRANSLATION);
Include normal pages

View File

@@ -1,3 +1,25 @@
# UTF-8 encoded Traditional Chinese language file for use with Oddmuse
#
# Copyright (c) 2003, 2004 wctang <wctang@csie.nctu.edu.tw>
# Copyright (c) 2007 Wei Ren Wang <weithenn@gmail.com>
#
# Permission is granted to copy, distribute and/or modify this
# document under the terms of the GNU Free Documentation License,
# Version 1.2 or any later version published by the Free Software
# Foundation; with no Invariant Sections, no Front-Cover Texts, and no
# Back-Cover Texts. A copy of the license could be found at:
# http://www.gnu.org/licenses/fdl.txt.
#
# Installation:
# =============
#
# Create a modules subdirectory in your data directory, and put the
# file in there. It will be loaded automatically.
#
# This translation was last checked for Oddmuse version 1.504.
#
use utf8;
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/translations/chinese-utf8.pl">chinese-utf8.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Chinese">Chinese</a></p>';
%Translate = split(/\n/,<<END_OF_TRANSLATION);
Include normal pages
包含正常頁面

View File

@@ -4,6 +4,7 @@
# Copyright (c) 2004, 2005 Christophe Ducamp
# Copyright (c) 2010 Alex Schroeder
# Copyright (c) 2012 Aurélien Desbrières
# Copyright (c) 2012 Hervé Robin
#
# Permission is granted to copy, distribute and/or modify this
# document under the terms of the GNU Free Documentation License,
@@ -94,7 +95,7 @@ Pas de redirection pour les versions anciennes
Invalid link pattern for #REDIRECT
Syntaxe invalide pour le lien de redirection (#REDIRECT)
Please go on to %s.
SVP aller à %s.
SVP allez à %s.
Updates since %s
Mises à jour depuis %s
up to %s
@@ -160,7 +161,7 @@ Historique de %s
Compare
Comparer
Deleted
Suprimée
Supprimé(e)
Mark this page for deletion
Marquer cette page comme étant à supprimer
No other revisions available
@@ -180,11 +181,11 @@ Un nom dutilisateur est nécessaire pour les utilisateurs normaux
Rolling back changes
Réinitialisation en cours
The two revisions are the same.
Les deux versions sont indentiques.
Les deux versions sont identiques.
Editing not allowed for %s.
Modification non autorisée pour %s.
Rollback of %s would restore banned content.
Un retour à %s restaurera du texte interdit.
Un retour à %s restaurera du contenu interdit.
Rollback to %s
Retour à %s
%s rolled back
@@ -196,13 +197,13 @@ Index de toutes les pages
Wiki Version
Affiche la version du wiki
Unlock Wiki
Supression du verrou
Suppression du verrou
Password
Mot de passe
Run maintenance
Lancer la maintenance
Unlock site
Déverouiller le site
Déverrouiller le site
Lock site
Verouiller le site
Install CSS
@@ -218,11 +219,11 @@ Actions :
Important pages:
Pages importantes :
To mark a page for deletion, put <strong>%s</strong> on the first line.
Pour marquer une page comme étant à supprimée, ajoutez <strong>%s</strong> à la première ligne
Pour marquer une page comme étant à supprimer, ajoutez <strong>%s</strong> à la première ligne
[Home]
[Accueil]
redirected from %s
redirigée à partir de %s
redirigé(e) à partir de %s
%s:
%s :
Click to search for references to this page
@@ -244,7 +245,7 @@ La base de données est stockée dans le répertoire temporaire %s
Last edited
Dernière modification
Edited
Modifié
Modifié(e)
by %s
par %s
(diff)
@@ -264,7 +265,7 @@ Voir toutes les modifications
View contributors
Voir les contributeurs
Homepage URL:
Adresse(URL) du site perso
Adresse(URL) du site personnel
s
s
Save
@@ -302,17 +303,17 @@ Pas de diff disponible.
Old revision:
Ancienne révision :
Changed:
Modifiée :
Modifié(e) :
Deleted:
Suprimé :
Supprimé(e) :
Added:
Ajoutée :
Ajouté(e) :
to
à
Revision %s not available
La version %s nest pas disponible
showing current revision instead
présentation à la place de la version en cours.
présentation à la place de la version en cours
Showing revision %s
Présentation de la version %s
Cannot save a nameless page.
@@ -330,11 +331,11 @@ Ne peut obtenir un verrouillage %s
The lock was created %s.
Le verrouillage a été créé %s.
Maybe the user running this script is no longer allowed to remove the lock directory?
Peut-être lutilisateur éxecutant le logiciel nest plus autorisé à effacer le répertoire utilisé pour le verrouillage ?
Peut-être lutilisateur exécutant le logiciel nest-il plus autorisé à effacer le répertoire utilisé pour le verrouillage ?
This operation may take several seconds...
Cette opération peut prendre quelques secondes...
Forced unlock of %s lock.
Supression forcée du verrou %s.
Suppression forcée du verrou %s.
No unlock required.
La suppression du verrou nest pas nécessaire.
%s hours ago
@@ -354,11 +355,11 @@ just now
Edit Denied
Modification interdite
Editing not allowed: user, ip, or network is blocked.
Modification interdite : lutilisateur, ladresse ip, ou le reseau est bloqué.
Modification interdite : lutilisateur, ladresse ip, ou le réseau est bloqué.
Contact the wiki administrator for more information.
Contactez ladministrateur du wiki pour plus dinformation.
The rule %s matched for you.
La règle %s a fonctionné pour vous.
La règle %s a été appliquée pour vous.
See %s for more information.
Voir %s pour plus dinformation.
Editing not allowed: %s is read-only.
@@ -378,7 +379,7 @@ Résumé :
This change is a minor edit.
Cette modification est une édition mineure.
Cancel
Anuler
Annuler
Replace this file with text
Remplacer ce fichier par un texte
Replace this text with a file
@@ -396,7 +397,7 @@ Vous êtes actuellement éditeur de ce site.
You are a normal user on this site.
Vous êtes un utilisateur normal de ce site.
Your password does not match any of the administrator or editor passwords.
Vote mot de passe ne corespond ni au mot de passe administrateur ni au mot de passe éditeur.
Vote mot de passe ne correspond ni au mot de passe administrateur ni au mot de passe éditeur.
Password:
Mot de passe :
This site does not use admin or editor passwords.
@@ -414,7 +415,7 @@ La règle "%1" correspondait à "%2" sur cette page.
Reason: %s.
Raison : %s.
Reason unknown.
Raison inconnu.
Raison inconnue.
Filter:
Filtre :
(for %s)
@@ -422,9 +423,9 @@ Filtre :
%s pages found.
%s pages trouvées.
Malformed regular expression in %s
Expression régulière malformée dans %s
Expression rationnelle mal formulée dans %s
Replaced: %s
Remplacé : %s
Remplacé(e) : %s
Search for: %s
Rechercher : %s
View changes for these pages
@@ -452,7 +453,7 @@ SVP vérifiez si vous avez écrasé ces modifications.
Anonymous
Anonyme
Cannot delete the index file %s.
Impossible de supprimer le fichier index %s.
Impossible de supprimer le fichier index %s.
Please check the directory permissions.
Veuillez vérifier les permissions des répertoires.
Your changes were not saved.
@@ -476,11 +477,11 @@ Enlevez le fichier "maintain" ou patientez.
Expiring keep files and deleting pages marked for deletion
Expiration des fichiers de cache et suppression des pages marquées pour la suppression
not deleted:
non supprimée :
non supprimé(e) :
deleted
supprimé
supprimé(e)
Moving part of the %s log file.
Deplace une partie du fichier de log %s.
Déplace une partie du fichier de log %s.
Could not open %s log file
Impossible douvrir le fichier de log %
Error was
@@ -510,15 +511,15 @@ Trop de connexions par %s
Please do not fetch more than %1 pages in %2 seconds.
Veuillez ne pas télécharger plus de %1 pages toutes les %2 secondes
Check whether the web server can create the directory %s and whether it can create files in it.
Verifiez si le serveur web peut créer le répertoire %s et sil peut créer des fichiers dedans.
Vérifiez si le serveur web peut créer le répertoire %s et sil peut créer des fichiers dedans.
Copy one of the following stylesheets to %s:
Copier une des feuilles de style suivantes sur %s.
Deleting %s
Supression de %s
Suppression de %s
Deleted %s
%s supprimée
%s supprimé(e)
Renaming %1 to %2.
Change %1 en %2.
Renomme %1 en %2.
The page %s does not exist
La page %s n'existe pas
The page %s already exists
@@ -526,11 +527,11 @@ La page %s existe déjà
Cannot rename %1 to %2
Impossible de renommer %1 en %2
Renamed to %s
Renommé en %s
Renommé(e) en %s
Renamed from %s
Renommé à partir de %s
Renommé(e) à partir de %s
Renamed %1 to %2.
%1 a été renommée en %2.
%1 a été renommé(e) en %2.
Immediately delete %s
Supprimer immédiatement %s
Rename %s to:
@@ -542,9 +543,9 @@ Contenu Complet
The main page is %s.
La page principale est %s.
Archive:
Archive:
Archive :
Rebuild BackLink database
Rebâtir les liens de la base de donnée
Rebâtir les liens de la base de données
Internal Page:
Page Interne :
Pages that link to this page
@@ -590,29 +591,29 @@ Ve
Sa
Sa
January
Janvier
janvier
February
Février
février
March
Mars
mars
April
Avril
avril
May
Mai
mai
June
Juin
juin
July
Juillet
juillet
August
Août
août
September
Septembre
septembre
October
Octobre
octobre
November
Novembre
novembre
December
Décembre
décembre
set %s
paramétrer %s
unset %s
@@ -634,11 +635,11 @@ Extraire toutes les dates depuis la base de données
Dates
Dates
No dates found.
Aucune dates trouvées
Aucune date trouvée
Inter links:
InterLiens :
List spammed pages
Lister les pages spammer
Lister les pages spammées
Despamming pages
Suppression des textes indésirables sur les pages.
Spammed pages
@@ -648,27 +649,27 @@ Impossible de trouver la version %s.
Revert to revision %1: %2
Retour à la version %1 : %2
Marked as %s.
Marqué comme %s.
Marqué(e) comme %s.
Cannot find unspammed revision.
Impossible de trouver une version sans texte indésirable.
Recover Draft
Récupérer le brouillon
No text to save
Pas de texte à sauvegarder
Aucun texte à sauvegarder
Draft saved
Brouillon sauvegardé
Draft recovered
Brouillon récupéré
No draft available to recover
Pas de brouillon à récupérer
Aucun brouillon à récupérer
Save Draft
Sauvegarder le Brouillon
Draft Cleanup
Brouillon Nettoyer
Nettoyer le Brouillon
%1 was last modified %2 and was kept
%1 ont été les dernier modifier et %2 ont été gardé
%1 a été modifié(e) en dernier et %2 a été conservé(e)
%1 was last modified %2 and was deleted
%1 ont été modifié et %2 on été effacés
%1 a été modifié(e) en dernier et %2 a été effacé(e)
Unable to delete draft %s
Impossible d'effacer le brouillon %s
Add Comment
@@ -678,11 +679,11 @@ modifications ordinaires
Matching page names:
Pages correspondant aux noms :
Email:
Email :
E-mail :
Could not find %1.html template in %2
Impossible de trouver le modèle %1.html dans %2
Only Editors are allowed to see this hidden page.
Seuls les Editeurs ont l'autorisation de voir cette page cachée.
Seuls les Éditeurs ont l'autorisation de voir cette page cachée.
Only Admins are allowed to see this hidden page.
Seuls les Administrateurs ont l'autorisation de voir cette page cachée.
Index
@@ -696,7 +697,7 @@ Définir
Full Link List
Liste Complète des Liens
List of locked pages
Liste des pages vérrouillés
Liste des pages verrouillées
Pages tagged with %s
Pages taguées avec %s
Template without parameters
@@ -704,11 +705,11 @@ Modèle sans paramètres
The template %s is either empty or does not exist.
Le modèle %s est soit vide soit n'existe pas.
-- defined on %s
-- défini sur %s
-- défini(e) sur %s
Local names defined on %1: %2
Noms locaux définis sur %1 : %2
Locked Pages
Pages Vérouillées
Pages Verrouillées
Register for %s
Enregistrer pour %s
Please choose a username of the form "FirstLast" using your real name.
@@ -726,7 +727,7 @@ Votre enregistrement pour %s a été soumis.
Please allow time for the webmaster to approve your request.
SVP accordez un peu de temps au webmestre pour valider votre demande.
An email has been sent to "%s" with further instructions.
Un email a été envoyé à "%s" pour de plus amples instructions.
Un e-mail a été envoyé à "%s" pour de plus amples instructions.
There was an error saving your registration.
Il y a eu une erreur au moment de sauvegarder votre enregistrement.
An account was created for %s.
@@ -736,15 +737,15 @@ Se connecter sur %s
Username and/or password are incorrect.
Le nom d'utilisateur et/ou le mot de passe sont incorrects.
Logged in as %s.
Connecté sous %s.
Connecté(e) sous %s.
Logout of %s
Déconnexion de %s
Logout of %s?
Déconnexion de %s ?
Logged out of %s
Déconnecté de %s
Déconnecté(e) de %s
You are now logged out.
Vous êtes maintenant déconnecté.
Vous êtes maintenant déconnecté(e).
Register a new account
Enregistrer un nouveau compte.
Logout
@@ -762,17 +763,17 @@ Confirmation d'Enregistrement pour %s
%s, your registration has been approved. You can now use your password to login and edit this wiki.
%s, votre enregistrement a été accepté. Vous pouvez désormais utiliser votre mot de passe pour vous connecter et éditer ce wiki.
Confirmation failed. Please email %s for help.
Echec sur la confirmation. SVP envoyez un email à %s pour de l'aide.
Échec sur la confirmation. SVP envoyez un e-mail à %s pour obtenir de l'aide.
Who Am I?
Qui suis-je ?
You are logged in as %s.
Vous êtes connecté en tant que %s.
Vous êtes connecté(e) en tant que %s.
You are not logged in.
Vous n'êtes pas connecté.
Vous n'êtes pas connecté(e).
Reset Password
Réinitialiser le mot de passe.
The password for %s was reset. It has been emailed to the address on file.
Le mot de passe pour %s a été réinitialisé. Il a été envoyé à l'adresse sur le fichier.
Le mot de passe pour %s a été réinitialisé. Il a été envoyé à l'adresse spécifiée sur le fichier.
There was an error resetting the password for %s.
Il y a eu une erreur de réinitialisation du mot de passe pour %s.
The username "%s" does not exist.
@@ -780,11 +781,11 @@ Le nom d'utilisateur "%s" n'existe pas.
Reset Password for %s
Réinitialiser le mot de passe pour %s
Reset Password?
Réinitialisation Mot de Passe ?
Réinitialisation du Mot de Passe ?
Change Password for %s
Modification Mot de Passe pour %s
Modification du Mot de Passe pour %s
Change Password?
Modification Mot de Passe ?
Modification du Mot de Passe ?
Your current password is incorrect.
Votre Mot de Passe est incorrect.
Your password has been changed.
@@ -792,9 +793,9 @@ Votre mot de passe a été modifié.
Approve Pending Registrations for %s
Accepter les Enregistrements en Attente pour %s
%s has been approved.
%s a été accepté.
%s a été accepté(e).
There was an error approving %s.
Il y a eu une erreur à accepter %s.
Il y a eu une erreur en acceptant %s.
<ul>
<ul>
<li>%1 - %2</li>
@@ -806,15 +807,15 @@ Il n'y a pas d'enregistrements en attente.
Invalid Mail %s: not saved.
Ladresse e-mail %s nest pas valide
unsubscribe
désabonner
se désabonner
subscribe
abonner
s'abonner
%s appears to be an invalid mail address
Ladresse e-mail %s nest pas valide
Your mail subscriptions
Vôtre abonnements
Votre abonnements
All mail subscriptions
Tous les abonnements
Tous les abonnements e-mail
Subscriptions
Abonnements
Show
@@ -822,7 +823,7 @@ Voir
Subscriptions for %s:
Abonnements pour %s :
Unsubscribe
Désabonner
Se désabonner
There are no subscriptions for %s.
Il ny a pas dabonnements pour %s.
Change email address
@@ -830,21 +831,21 @@ Changer ladresse e-mail
Mail addresses are linked to unsubscription links.
Les adresses e-mail sont liées au désabonnement.
Subscribe to %s.
Abonner %s.
S'abonner %s.
Subscribe
Abonner
S'abonner
Subscribed %s to the following pages:
%s est abonné aux pages suivantes :
The remaining pages do not exist.
Les pages restantes nexistent pas (ou plus).
Unsubscribed %s from the following pages:
%s a désabonné les pages suivantes :
%s est désabonné aux pages suivantes :
You linked more than %s times to the same domain. It would seem that only a spammer would do this. Your edit is refused.
Vous avez liés plus de %s le même domaine. Il semble que seul les spammeurs font cela. Votre édition est donc refusée.
Vous avez créé plus de %s liens vers le même domaine. Il semble que seuls les spammeurs font cela. Votre édition est donc refusée.
%s is not a legal name for a namespace
%s nest pas un nom valide pour un espace de noms
Namespaces
Espace de noms
Espaces de noms
Getting page index file for %s.
Récupération du fichier d'index de %s.
Near links:
@@ -858,11 +859,11 @@ Pages à proximité :
Include near pages
Inclure les pages de proximité
EditNearLinks
EditerLiensDeProximité
ÉditerLiensDeProximité
The same page on other sites:
La même page sur d'autres sites :
(create locally)
(créée locallement)
(créer localement)
image
image
download
@@ -884,7 +885,7 @@ Aucune page id pour actionner la carte locale
Requested page %s does not exist
La page demandée %s n'existe pas
Local Map for %s
CarteLocale pour %s
Carte Locale pour %s
view
voir
Self-ban by %s
@@ -894,11 +895,11 @@ Vous avez banni votre propre IP.
OpenID Login
Identification OpenID
Your identity is saved in a cookie, if you have cookies enabled. Cookies may get lost if you connect from another machine, from another account, or using another software.
Votre identité est sauvegardée dans un cookie, si vous avez activé les cookies. Vos cookies seront perdue si vous vous connecté depuis une autre machine, sur un autre compte ou si vous utiliser un autre navigateur.
Votre identité est sauvegardée dans un cookie, si vous avez activé les cookies. Vos cookies seront perdus si vous vous connectez depuis une autre machine, sur un autre compte ou si vous utilisez un autre navigateur.
Your homepage is set to %s.
Votre page d'accueil est ajusté sur %s.
Votre page d'accueil est établie sur %s.
You have no homepage set.
Votre page d'acceuil n'est pas ajustée.
Votre page d'acceuil n'est pas établie.
Homepage:
Page d'accueil :
Homepage is missing
@@ -918,9 +919,9 @@ Lien permanent vers "%s"
anchor first defined here: %s
première ancre définie ici : %s
the page %s also exists
la page %s existe aussi
la page %s existe également
There was an error generating the pdf for %s. Please report this to webmaster, but do not try to download again as it will not work.
Il y a eu une erreur en générant le pdf pour %s. SVP, rendez-compte de cela au webmestre, mais n'essayez pas de télécharger à nouveau, car cela ne fonctionnera pas.
Il y a eu une erreur en générant le pdf pour %s. SVP, rendez compte de cela au webmestre, mais n'essayez pas de télécharger à nouveau, car cela ne fonctionnera pas.
Someone else is generating a pdf for %s. Please wait a minute and then try again.
Quelqu'un d'autre est en train de générer un pdf pour %S. SVP, attendez une minute puis essayez de nouveau.
Download this page as PDF
@@ -936,21 +937,21 @@ Publier %s
No target wiki was specified in the config file.
La cible du wiki n'est pas spécifiée dans le fichier de configuration.
The target wiki was misconfigured.
La cible du wiki à été mal configurée.
La cible du wiki a été mal configurée.
Upload is limited to %s bytes
Le téléversement est limité à %s bytes
You did not answer correctly.
Vous navez pas répondu correctement.
To save this page you must answer this question:
Vous devez répondre a cette question pour sauvegarder la page :
Vous devez répondre à cette question pour sauvegarder la page :
Please type the following two words:
Taper s'il vous plait les deux mots suivant :
Tapez s'il vous plaît les deux mots suivants :
Please answer this captcha:
Répondez à ce captcha s'il vous plais :
Répondez à ce captcha s'il vous plaît :
Referrers
Introducteurs
Référants
All Referrers
Tous les Introducteurs
Tous les Référants
Tag
Tag
Rebuild index for searching
@@ -960,13 +961,13 @@ Nuage de Tags
Search::FreeText is not available on this system.
Search::FreeText n'est pas disponible sur ce système.
Rebuilding index not done.
Reconstruction index non effectuée.
Reconstruction de l'index non effectuée.
(Rebuilding the index can only be done once every 12 hours.)
(La reconstruction de l'index ne peut être faite qu'une fois toutes les 12 heures.)
(La reconstruction de l'index ne peut être effectuée qu'une fois toutes les 12 heures.)
New Pages for Indexed Search
Nouvelle pages pour indexer la recherche
List changes since %s
Changement depuis %s
Changements dans la liste depuis %s
...
...
Search term missing.
@@ -986,27 +987,27 @@ Liste des pages pour %s
Slideshow:%s
Diaporama : %s
Index of all small pages
Indexe de toutes les petites pages
Index de toutes les pages de petite taille
Static Copy
Copie Statique
Back to %s
Retour à %s
Edit image in the browser
Éditer l'image du navigateur
Éditer l'image dans le navigateur
Summary of your changes:
Sommaire des changements :
Résumé de tous vos changements :
Copy to %1 succeeded: %2.
Copie vers %1 réussie : %2.
Copy to %1 failed: %2.
Copie vers %1 échouée : %2.
Feed for this tag
Graines pour ce tag
Flux pour ce tag
Rebuild tag index
Rebâtir votre index de tag
Rebâtir votre index de tags
list tags
liste de tags
tag cloud
tag du nuage
nuage de tags
Alternatively, use one of the following templates:
Alternativement, utilisez un des modèles suivants :
Thread: %s
@@ -1016,7 +1017,7 @@ Le paramètre ID est manquant.
Thread %s does not exist.
Le fil %s n'existe pas.
Page %s does not contain a thread.
La page %s ne contient pas de fil.
La page %s ne contient aucun fil.
Add
Ajouter
URL parameter is missing.
@@ -1032,11 +1033,11 @@ Nom :
Too many instances. Only %s allowed.
Trop d'instances. %s seulement est autorisée
Please try again later. Perhaps somebody is running maintenance or doing a long search. Unfortunately the site has limited resources, and so we must ask you for a bit of patience.
ressources, nous vous demandons de faire preuve d'un peu de patience.
Essayez plus tard s'il vous plaît. Peut-être que quelqu'un effectue une maintenance ou une recherche volumineuse. Malheureusement le site a des ressources limitées, nous vous demandons de faire preuve d'un peu de patience.
Timezone
Fuseau horaire
Pick your timezone:
Sélectionner votre fuseau horaire
Sélectionnez votre fuseau horaire
Set
Ajusté
Contents
@@ -1046,23 +1047,23 @@ Ajouter une page nouvelle pour aujourdhui
Add Translation
Ajouter une traduction
Please provide a different page name for the translation.
Donné s'il vous plait un nom différent à votre traduction
Donnez s'il vous plait un nom différent à votre traduction
Added translation: %1 (%2)
Traduction ajoutée: %1 (%2)
Traduction ajoutée : %1 (%2)
Translate %s
Traduire %s
Thank you for writing a translation of %s.
Merci pour la traduction de %s.
Please indicate what language you will be using.
Merci d'indiquer quel langage vous allez utiliser.
Merci d'indiquer quelle langue vous allez utiliser.
Language is missing
Le langage est manquant
La langue est manquante
Suggested languages:
Langages suggérés
Langues suggérées
Please indicate a page name for the translation of %s.
Indiqué s'il vous plait votre traduction pour %s.
Indiquez s'il vous plaît un nom de page pour la traduction de %s.
More help may be available here: %s.
Plus d'aide disponible ici: %s.
Plus d'aide disponible ici : %s.
Translated page:
Page traduite :
This page is a translation of %s.
@@ -1070,7 +1071,7 @@ Cette page est une traduction de %s.
The translation is up to date.
La traduction est à jour.
The translation is outdated.
La traduction n'est pas à jour.
La traduction n'est plus à jour.
The page does not exist.
La page n'existe pas.
http://search.barnesandnoble.com/booksearch/isbninquiry.asp?ISBN=%s
@@ -1088,7 +1089,7 @@ Pages recherchées
%s pages
%s pages
%s, referenced from:
%s, référencé depuis :
%s, référencé(e) depuis :
Upload of %s file
Téléversement du fichier %s
Blog
@@ -1096,9 +1097,9 @@ Blog
Matching pages:
Pages correspondantes :
New
Nouveau
Nouveau
Edit %s.
Editer %s.
Éditer %s.
Title:
Titre :
Tags:

View File

@@ -498,7 +498,7 @@ Interlinks:
Too many connections by %s
Demasiadas conexiones por %s
Please do not fetch more than %1 pages in %2 seconds.
Por favor, no visites más de %1 páginas en %s segundos.
Por favor, no visites más de %1 páginas en %2 segundos.
Check whether the web server can create the directory %s and whether it can create files in it.
Comprueba si el servidor web puede crear el directorio %s y si puede crear archivos en él.
Copy one of the following stylesheets to %s:

View File

@@ -148,9 +148,12 @@ sub send_file {
warn "No content for $title\n" unless $item->{description};
my $link = $item->{link};
my $sub = "$root?action=subscriptions";
print $fh qq(<p>Visit <a href="$link">$title</a>)
my $text = qq(<p>Visit <a href="$link">$title</a>)
. qq( or <a href="$sub">manage your subscriptions</a>.</p><hr />)
. $item->{description};
# prevent 501 Syntax error - line too long
$text =~ s/<(p|h[1-6]|[duo]l|pre|li|form|div|blockquote|hr|table|tr)>/\r\n<$1>/gi;
print $fh $text;
$fh->close;
foreach my $subscriber (@subscribers) {
send_mail($subscriber, $title, $fh);

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,5 +1,5 @@
#!/usr/bin/env perl
# Copyright (C) 2006, 2007, 2008, 2009 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2006-2013 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 107;
use Test::More tests => 108;
clear_pages();
add_module('creole.pl');
@@ -159,6 +159,8 @@ CamelCaseLink
~ does not escape whitespace
foo ~bar
foo bar
| 1|foo |
<table class="user"><tr><td align="right">1</td><td>foo </td></tr></table>
EOT
xpath_run_tests(split('\n',<<'EOT'));

View File

@@ -1,27 +1,31 @@
# 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/>.
require 't/test.pl';
package OddMuse;
use Test::More tests => 21;
use Test::More tests => 25;
use utf8;
clear_pages();
# encoding stuff
update_page('dash', "- en dash\n");
update_page('dash', "- en dash\n— em “dash”\n");
test_page(get_page('action=browse diff=1 id=dash'),
'<div class="new"><p>&gt; — em “dash”</p></div>',
'- en dash — em “dash”');
# Highlighting differences
update_page('xah', "When we judge people in society, often, we can see people's true nature not by the official defenses and behaviors, but by looking at the statistics (past records) of their behavior and the circumstances it happens.\n"
. "For example, when we look at the leader in human history. Great many of them have caused thousands and millions of intentional deaths. Some of these leaders are hated by many, yet great many of them are adored and admired and respected... (ok, i'm digressing...)\n");
@@ -54,11 +58,13 @@ test_page(get_page('action=history id=david'),
'Revision 4', 'fourth revision');
# using diffrevision=1 will make sure that the third revision is not shown
xpath_test(get_page('action=browse diff=1 id=david revision=2 diffrevision=1'),
'//p[@class="summary"][text()="Summary: second revision"]',
'//div[@class="old"]/p/strong[text()="first"]',
'//div[@class="new"]/p/strong[text()="second"]',
'//div[@class="content browse"]/p[text()="this is the second revision"]');
# check with cache = 0
xpath_test(get_page('action=browse diff=1 id=david revision=2 diffrevision=1 cache=0'),
'//p[@class="summary"][text()="Summary: second revision"]',
'//div[@class="old"]/p/strong[text()="first"]',
'//div[@class="new"]/p/strong[text()="second"]',
'//div[@class="content browse"]/p[text()="this is the second revision"]');

55
t/duckduckgo-search.t Normal file
View File

@@ -0,0 +1,55 @@
# Copyright (C) 20072013 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 => 9;
clear_pages();
add_module('duckduckgo-search.pl');
DuckDuckGoSearchInit();
is($DuckDuckGoSearchDomain, undef, 'No $ScriptName');
$DuckDuckGoSearchDomain = undef;
$ScriptName = 'http://www.communitywiki.org/en';
DuckDuckGoSearchInit();
is($DuckDuckGoSearchDomain, 'communitywiki.org', $ScriptName);
$DuckDuckGoSearchDomain = undef;
$ScriptName = 'http://www.community.org:80/';
DuckDuckGoSearchInit();
is($DuckDuckGoSearchDomain, 'community.org', $ScriptName);
$DuckDuckGoSearchDomain = undef;
$ScriptName = 'http://www.communitywiki.org';
DuckDuckGoSearchInit();
is($DuckDuckGoSearchDomain, 'communitywiki.org', $ScriptName);
$DuckDuckGoSearchDomain = undef;
$ScriptName = 'http://emacswiki.org/cgi-bin/emacs';
DuckDuckGoSearchInit();
is($DuckDuckGoSearchDomain, 'emacswiki.org', $ScriptName);
$DuckDuckGoSearchDomain = undef;
$ScriptName = 'http://localhost/wiki.pl';
DuckDuckGoSearchInit();
isnt($DuckDuckGoSearchDomain, 'localhost', $ScriptName);
test_page(get_page('search=alex'),
'<title>Wiki: Search for: alex</title>');
AppendStringToFile($ConfigFile, "\$ScriptName = 'http://emacswiki.org/';\n");
test_page(get_page('search=alex'),
'Status: 302',
'Location: https://www.duckduckgo.com/\?q=alex\+site%3Aemacswiki\.org');

View File

@@ -84,7 +84,7 @@ AppendStringToFile($ConfigFile, "use utf8;\n\$CookieParameters{ärger} = 1;\n");
test_page(get_page('action=browse id=Test %C3%A4rger=hallo'),
'Set-Cookie: Wiki=%C3%A4rger%251ehallo');
# this causes wide character in print somehow? otherwise harmless
# create a test page to test the output in various ways
test_page(update_page("Russian", "Русский Hello"),
"Русский");

View File

@@ -14,16 +14,46 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 5;
use Test::More tests => 12;
use utf8; # tests contain UTF-8 characters and it matters
clear_pages();
add_module('fix-encoding.pl');
# make sure no menu shows if no page is provided
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(update_page('Example', 'Pilgerstätte für die Göttin'),
# 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');
# make sure nothing is saved if there is no change
test_page(update_page('Example', 'Pilgerstätte für die Göttin'),
'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');
# the menu shows up if the page exists
test_page(get_page('action=admin id=Example'),
'action=fix-encoding;id=Example');
# here is an actual page you need to fix
test_page(update_page('Example', 'Pilgerstätte für die Göttin',
'borked encoding'),
'Pilgerstätte für die Göttin');
test_page(get_page('action=fix-encoding id=Example'),
@@ -31,3 +61,5 @@ 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');

View File

@@ -15,13 +15,40 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 45;
use utf8; # tests contain UTF-8 characters and it matters
use Test::More tests => 52;
clear_pages();
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
add_module('mail.pl');
Init(); # set $MailFile
# tests migration
require DB_File;
tie %h, "DB_File", $MailFile;
$h{'alex@gnu.org'} = 'Unregelmässige_Spieler';
$h{'Unregelmässige_Spieler'} = 'alex@gnu.org';
untie %h;
test_page(get_page('action=migrate-subscriptions pwd=foo'),
'Migrated 2 rows');
test_page(get_page('action=migrate-subscriptions pwd=foo'),
'migration not necessary');
test_page(get_page('action=subscriptionlist pwd=foo raw=1'),
'alex@gnu.org Unregelmässige_Spieler',
'Unregelmässige_Spieler alex@gnu.org');
# make a test with a character that cannot be Latin-1 encoded
# ★ #x2605 => xE2 #x98 #x85 in UTF-8
test_page(get_page('title=Comments_on_%e2%98%85 aftertext=test username=Alex '
. 'mail=berta@example.com notify=1'),
'Set-Cookie:.*mail%251eberta%40example.com');
test_page(get_page('action=subscriptionlist pwd=foo raw=1'),
'Comments_on_★ berta@example.com',
'berta@example.com Comments_on_★');
# edit page
$page = get_page('Comments_on_Foo');

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

101
t/private-pages.t Normal file
View File

@@ -0,0 +1,101 @@
# 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
# 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 => 29;
clear_pages();
add_module('private-pages.pl');
# create password protected page: can't read it without password!
test_page(update_page('Privat', "#PASSWORD foo\nSo many secrets remain untold.\n"),
'This page is password protected');
# can't update password protected page
update_page('Privat', "#PASSWORD foo\nCats have secrets.\n");
test_page($redirect, 'Status: 403');
# use password foo to update protected page: can't read it without password!
test_page_negative(update_page('Privat', "#PASSWORD foo\nCats have secrets.\n", undef, undef, 1),
'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');
test_page($page, 'Cats have secrets');
# a keep file was created as well
ok(-f GetKeepFile('Privat', 1), 'Keep file exists');
# can't read old revisions without a password
test_page_negative(get_page('action=browse id=Privat revision=1'),
'Cats have secrets');
# read old revisions with password
test_page(get_page('action=browse id=Privat revision=1 pwd=foo'),
'So many secrets remain untold');
# can't read old revisions without password
test_page(get_page('action=browse id=Privat revision=1'),
'This page is password protected');
# can't see secrets when printing raw pages
my $page = get_page('action=browse raw=1 id=Privat pwd=foo');
test_page_negative($page, 'This page is password protected');
test_page($page, 'Cats have secrets');
# can't see summaries with secrets
my $page = get_page('action=rc raw=1 all=1');
test_page($page, 'Privat');
test_page_negative($page, 'secret');
# can't search for secrets without a password
my $page = get_page('search=cats');
test_page($page, '0 pages found');
test_page_negative($page, "Privat");
# search finds secrets with password
my $page = get_page('search=cats pwd=foo');
test_page($page, '1 pages? found',
'Privat', '<strong>Cats</strong> have secrets');
# can't edit a private page without a password
my $page = get_page('action=edit id=Privat');
test_page($page, 'Editing not allowed');
test_page_negative($page, 'Cats have secrets');
# can edit a private page with a password
my $page = get_page('action=edit id=Privat pwd=foo');
test_page_negative($page, 'This page is password protected');
test_page($page, 'Cats have secrets');
# can't edit an old revision of a private page without a password
my $page = get_page('action=edit id=Privat revision=1');
test_page($page, 'Editing not allowed');
test_page_negative($page, 'secret');
# can't just post changes to a private page without a password
my $page = get_page('title=Privat text=YaddaYadda revision=1');
test_page($page, 'Editing not allowed');
test_page_negative($page, 'secret');
# can't include them
test_page_negative(update_page('Publik', '<include "Privat">'),
'Cats have secrets');

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

@@ -45,24 +45,24 @@ xpath_run_tests(split('\n',<<'EOT'));
EOT
update_page('Brilliant', 'Gameologists [[tag:podcast]] [[tag:mag]]');
update_page('Pödgecäst', 'Another [[tag:podcast]]');
update_page('Pödgecäst´s', 'Another [[tag:podcast]]');
update_page('Alex', 'Me! [[tag:Old School]]');
# open the DB file
require DB_File;
tie %h, "DB_File", $TagFile;
%tag = map {$_=>1} split($FS, $h{"_Brilliant"});
%tag = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("_Brilliant")}));
ok($tag{podcast}, 'Brilliant page tagged podcast');
ok($tag{mag}, 'Brilliant page tagged mag');
%tag = map {$_=>1} split($FS, $h{"_Pödgecäst"});
ok($tag{podcast}, 'Pödgecäst page tagged podcast');
%file = map {$_=>1} split($FS, $h{"podcast"});
%tag = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("_Pödgecäst´s")}));
ok($tag{podcast}, 'Pödgecäst´s page tagged podcast');
%file = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("podcast")}));
ok($file{Brilliant}, 'Tag podcast applies to page Brilliant');
ok($file{Pödgecäst}, 'Tag podcast applies to page Pödgecäst');
%file = map {$_=>1} split($FS, $h{"mag"});
ok($file{"Pödgecäst´s"}, 'Tag podcast applies to page Pödgecäst´s');
%file = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("mag")}));
ok($file{Brilliant}, 'Tag mag applies to page Brilliant');
%file = map {$_=>1} split($FS, $h{"old_school"});
%file = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("old_school")}));
ok($file{Alex}, 'Tag Old School applies to page Alex');
# close the DB file before making changes via the wiki!
@@ -73,12 +73,12 @@ update_page('Brilliant', 'Gameologists [[tag:mag]]');
# reopen changed file
tie %h, "DB_File", $TagFile;
%tag = map {$_=>1} split($FS, $h{"_Brilliant"});
%tag = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("_Brilliant")}));
ok(!$tag{podcast}, 'Brilliant page no longer tagged podcast');
ok($tag{mag}, 'Brilliant page still tagged mag');
%file = map {$_=>1} split($FS, $h{"podcast"});
%file = map {$_=>1} split($FS, UrlDecode($h{UrlEncode("podcast")}));
ok(!$file{Brilliant}, 'Tag podcast no longer applies to page Brilliant');
ok($file{Pödgecäst}, 'Tag podcast still applies to page Pödgecäst');
ok($file{"Pödgecäst´s"}, 'Tag podcast still applies to page Pödgecäst´s');
# close the DB file before making changes via the wiki!
untie %h;
@@ -88,8 +88,8 @@ DeletePage('Brilliant');
# reopen changed file
tie %h, "DB_File", $TagFile;
ok(!$h{_Brilliant}, 'Brilliant page no longer exists');
ok(!exists($h{mag}), 'No page tagged mag exists');
ok(!$h{UrlEncode("_Brilliant")}, 'Brilliant page no longer exists');
ok(!exists($h{UrlEncode("mag")}), 'No page tagged mag exists');
# close the DB file before making changes via the wiki!
untie %h;
@@ -101,46 +101,46 @@ update_page('Jeff', 'a blog [[tag:Old School]]');
# ordinary search finds Alex
$page = get_page('search=podcast raw=1');
test_page($page, qw(Pödgecäst Brilliant Sons Alex));
test_page($page, qw(Pödgecäst´s Brilliant Sons Alex));
# tag search skips Alex
$page = get_page('search=tag:podcast raw=1');
test_page($page, qw(Pödgecäst Brilliant Sons));
test_page($page, qw(Pödgecäst´s Brilliant Sons));
test_page_negative($page, qw(Alex));
# tag search is case insensitive
$page = get_page('search=tag:PODCAST raw=1');
test_page($page, qw(Pödgecäst Brilliant Sons));
test_page($page, qw(Pödgecäst´s Brilliant Sons));
test_page_negative($page, qw(Alex));
# exclude tag search skips Brilliant
$page = get_page('search=-tag:mag raw=1');
test_page($page, qw(Pödgecäst Sons Alex));
test_page($page, qw(Pödgecäst´s Sons Alex));
test_page_negative($page, qw(Brilliant));
# combine include and exclude tag search to exclude both Alex and
# Brilliant
$page = get_page('search=tag:podcast%20-tag:mag raw=1');
test_page($page, qw(Pödgecäst Sons));
test_page($page, qw(Pödgecäst´s Sons));
test_page_negative($page, qw(Brilliant Alex));
# combine ordinary search with include and exclude tag search to
# exclude both Alex and Brilliant
$page = get_page('search=kryos%20tag:podcast%20-tag:mag raw=1');
test_page($page, qw(Sons));
test_page_negative($page, qw(Pödgecäst Brilliant Alex));
test_page_negative($page, qw(Pödgecäst´s Brilliant Alex));
# search for a tag containing spaces
$page = get_page('search=tag:old_school raw=1');
test_page($page, qw(Jeff));
test_page_negative($page, qw(Sons Pödgecäst Brilliant Alex));
test_page_negative($page, qw(Sons Pödgecäst´s Brilliant Alex));
test_page(get_page('action=reindex pwd=foo'),
qw(Pödgecäst Brilliant Sons Alex));
qw(Pödgecäst´s Brilliant Sons Alex));
# tag search skips Alex -- repeat test after reindexing
$page = get_page('search=tag:podcast raw=1');
test_page($page, qw(Pödgecäst Brilliant Sons));
test_page($page, qw(Pödgecäst´s Brilliant Sons));
test_page_negative($page, qw(Alex));
add_module('near-links.pl');
@@ -160,7 +160,7 @@ test_page_negative($page, qw(AlexSchroeder Foo));
# check journal pages
$page = update_page('Podcasts', '<journal "." search tag:podcast>');
test_page($page, qw(Pödgecäst Brilliant Sons));
test_page($page, qw(Pödgecäst´s Brilliant Sons));
test_page_negative($page, qw(Alex Foo));
# check the tag cloud
@@ -177,4 +177,4 @@ AppendStringToFile($ConfigFile, "\$LocalNamesCollect = 1;\n");
update_page('LocalNames', 'test');
update_page('Alex', 'is a [[tag:podcast]] after all');
$page = get_page('search=tag:podcast raw=1');
test_page($page, qw(Pödgecäst Brilliant Sons Alex));
test_page($page, qw(Pödgecäst´s Brilliant Sons Alex));

View File

@@ -18,6 +18,12 @@ use XML::LibXML;
use utf8;
use vars qw($raw);
# Test::More explains how to fix wide character in print issues
my $builder = Test::More->builder;
binmode $builder->output, ":utf8";
binmode $builder->failure_output, ":utf8";
binmode $builder->todo_output, ":utf8";
# Import the functions
$raw = 0; # capture utf8 is the default
@@ -94,7 +100,6 @@ sub name {
$_ = shift;
s/\n/\\n/g;
$_ = '...' . substr($_, -60) if length > 63;
utf8::encode($_);
return $_;
}
@@ -138,8 +143,8 @@ sub run_macro_tests {
# one string, many tests
sub test_page {
my $page = shift;
foreach my $test (@_) {
my ($page, @tests) = @_;
foreach my $test (@tests) {
like($page, qr($test), name($test));
}
}

View File

@@ -15,7 +15,7 @@
require 't/test.pl';
package OddMuse;
use Test::More tests => 20;
use Test::More tests => 33;
use utf8; # tests contain UTF-8 characters and it matters
clear_pages();
@@ -76,3 +76,43 @@ test_page(get_page('action=translate id=HomePage target=abc'),
test_page(get_page('action=translate id=HomePage target=abc translation=fr'),
'Editing abc');
# encoding issues
# first check the from ASCII to something that can be encoded in Latin-1
test_page(update_page('SiteMap', 'Hello'), 'Hello');
test_page(get_page('action=translate id=SiteMap target=Übersicht translation=de'),
'Editing Übersicht');
xpath_test(get_page('SiteMap'),
'//a[@class="translation de"][text()="Deutsch"]');
xpath_test(get_page('action=rc showedit=1'),
'//li/a[@class="local"][text()="SiteMap"]/following-sibling::strong[text()="Added translation: Übersicht (Deutsch)"]');
# now check the other way around
test_page(get_page('action=translate id=Übersicht target=SiteMap translation=en'),
'Editing SiteMap');
xpath_test(get_page('Übersicht'),
'//a[@class="translation en"][text()="English"]');
xpath_test(get_page('action=rc showedit=1'),
'//li/a[@class="local"][text()="Übersicht"]/following-sibling::strong[text()="Added translation: SiteMap (English)"]');
AppendStringToFile($ConfigFile, q{
$Languages{ja} = '(ま|す|ん|し|ょ|う|の|は)';
$Translate{ja} = '日本語';
});
# Repeat it with Unicode!
test_page(get_page('action=translate id=SiteMap target=サイトマップ translation=ja'),
'Editing サイトマップ');
xpath_test(get_page('SiteMap'),
'//a[@class="translation ja"][text()="日本語"]');
xpath_test(get_page('action=rc showedit=1'),
'//li/a[@class="local"][text()="SiteMap"]/following-sibling::strong[text()="Added translation: サイトマップ (日本語)"]');
# and again, check the other way around
test_page(get_page('action=translate id=サイトマップ target=SiteMap translation=en'),
'Editing SiteMap');
xpath_test(get_page('サイトマップ'),
'//a[@class="translation en"][text()="English"]');
xpath_test(get_page('action=rc showedit=1'),
'//li/a[@class="local"][text()="サイトマップ"]/following-sibling::strong[text()="Added translation: SiteMap (English)"]');

117
wiki.pl
View File

@@ -1,5 +1,5 @@
#! /usr/bin/perl
# Copyright (C) 2001-2012
# Copyright (C) 2001-2013
# Alex Schroeder <alex@gnu.org>
# Copyleft 2008 Brian Curry <http://www.raiazome.com>
# ... including lots of patches from the UseModWiki site
@@ -797,6 +797,7 @@ sub UrlEncode {
sub UrlDecode {
my $str = shift;
$str =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/ge;
utf8::decode($str); # make internal string
return $str;
}
@@ -811,7 +812,7 @@ sub GetRaw {
return unless eval { require LWP::UserAgent; };
my $ua = LWP::UserAgent->new;
my $response = $ua->get($uri);
return $response->content if $response->is_success;
return $response->decoded_content if $response->is_success;
}
sub DoJournal {
@@ -1022,7 +1023,7 @@ sub GetRss {
}
my @need_cache = keys %todo;
if (keys %todo > 1) { # try parallel access if available
eval { # see code example in LWP::Parallel, not LWP::Parllel::UserAgent (no callbacks here)
eval { # see code example in LWP::Parallel, not LWP::Parallel::UserAgent (no callbacks here)
require LWP::Parallel::UserAgent;
my $pua = LWP::Parallel::UserAgent->new();
foreach my $uri (keys %todo) {
@@ -1034,7 +1035,7 @@ sub GetRss {
my $entries = $pua->wait();
foreach (keys %$entries) {
my $uri = $entries->{$_}->request->uri;
$data{$uri} = $entries->{$_}->response->content;
$data{$uri} = $entries->{$_}->response->decoded_content;
}
}
}
@@ -1161,11 +1162,12 @@ sub GetPageOrEditLink { # use GetPageLink and GetEditLink if you know the result
}
sub GetPageLink { # use if you want to force a link to local pages, whether it exists or not
my ($id, $name, $class) = @_;
my ($id, $name, $class, $accesskey) = @_;
$id = FreeToNormal($id);
$name = $id unless $name;
$class .= ' ' if $class;
return ScriptLink(UrlEncode($id), NormalToFree($name), $class . 'local');
return ScriptLink(UrlEncode($id), NormalToFree($name), $class . 'local',
undef, undef, $accesskey);
}
sub GetEditLink { # shortcut
@@ -1389,7 +1391,7 @@ sub BrowseResolvedPage {
sub BrowsePage {
my ($id, $raw, $comment, $status) = @_;
OpenPage($id);
my ($text, $revision) = GetTextRevision(GetParam('revision', ''));
my ($text, $revision, $summary) = GetTextRevision(GetParam('revision', ''));
$text = $NewText unless $revision or $Page{revision}; # new text for new pages
# handle a single-level redirect
my $oldId = GetParam('oldid', '');
@@ -1421,7 +1423,7 @@ sub BrowsePage {
print GetHeader($id, NormalToFree($id), $oldId, undef, $status);
my $showDiff = GetParam('diff', 0);
if ($UseDiff && $showDiff) {
PrintHtmlDiff($showDiff, GetParam('diffrevision', $revision), $revision, $text);
PrintHtmlDiff($showDiff, GetParam('diffrevision', $revision), $revision, $text, $summary);
print $q->hr();
}
PrintPageContent($text, $revision, $comment);
@@ -1500,7 +1502,7 @@ sub GetRcLines { # starttime, hash of seen pages to use as a second return value
my %following = ();
my @result = ();
# check the first timestamp in the default file, maybe read old log file
open(F, '<:encoding(UTF-8)', $RcFile);
open(F, '<:utf8', $RcFile);
my $line = <F>;
my ($ts) = split(/$FS/o, $line); # the first timestamp in the regular rc file
if (not $ts or $ts > $starttime) { # we need to read the old rc file, too
@@ -1586,7 +1588,7 @@ sub GetRcLinesFor {
rcclusteronly rcfilteronly match lang followup);
# parsing and filtering
my @result = ();
open(F, '<:encoding(UTF-8)', $file) or return ();
open(F, '<:utf8', $file) or return ();
while (my $line = <F>) {
chomp($line);
my ($ts, $id, $minor, $summary, $host, $username, $revision,
@@ -1967,7 +1969,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});
@@ -1995,8 +1997,7 @@ sub DoHistory {
}
@html = (GetFormStart(undef, 'get', 'history'),
$q->p($q->submit({-name=>T('Compare')}),
# don't use $q->hidden here, the sticky action
# value will be used instead
# don't use $q->hidden here!
$q->input({-type=>'hidden',-name=>'action',-value=>'browse'}),
$q->input({-type=>'hidden', -name=>'diff', -value=>'1'}),
$q->input({-type=>'hidden', -name=>'id', -value=>$id})),
@@ -2103,31 +2104,52 @@ sub DoRollback {
WriteRcLog('[[rollback]]', $page, $to); # leave marker
print $q->end_p() . $q->end_div();
ReleaseLock();
PrintFooter();
PrintFooter($page);
}
sub DoAdminPage {
my ($id, @rest) = @_;
my @menu = (ScriptLink('action=index', T('Index of all pages'), 'index'),
ScriptLink('action=version', T('Wiki Version'), 'version'),
ScriptLink('action=unlock', T('Unlock Wiki'), 'unlock'),
ScriptLink('action=password', T('Password'), 'password'),
ScriptLink('action=maintain', T('Run maintenance'), 'maintain'));
my @menu = ();
push(@menu, ScriptLink('action=index',
T('Index of all pages'), 'index'))
if $Action{index};
push(@menu, ScriptLink('action=version',
T('Wiki Version'), 'version'))
if $Action{version};
push(@menu, ScriptLink('action=unlock',
T('Unlock Wiki'), 'unlock'))
if $Action{unlock};
push(@menu, ScriptLink('action=password',
T('Password'), 'password'))
if $Action{password};
push(@menu, ScriptLink('action=maintain',
T('Run maintenance'), 'maintain'))
if $Action{maintain};
if (UserIsAdmin()) {
push(@menu, ScriptLink('action=clear', T('Clear Cache'), 'clear'));
if (-f "$DataDir/noedit") {
push(@menu, ScriptLink('action=editlock;set=0', T('Unlock site'), 'editlock 0'));
} else {
push(@menu, ScriptLink('action=editlock;set=1', T('Lock site'), 'editlock 1'));
push(@menu, ScriptLink('action=clear',
T('Clear Cache'), 'clear'))
if $Action{clear};
if ($Action{editlock}) {
if (-f "$DataDir/noedit") {
push(@menu, ScriptLink('action=editlock;set=0',
T('Unlock site'), 'editlock 0'));
} else {
push(@menu, ScriptLink('action=editlock;set=1',
T('Lock site'), 'editlock 1'));
}
}
if ($id) {
if ($id and $Action{pagelock}) {
my $title = NormalToFree($id);
if (-f GetLockedPageFile($id)) {
push(@menu, ScriptLink('action=pagelock;set=0;id=' . UrlEncode($id),
Ts('Unlock %s', $title), 'pagelock 0'));
push(@menu, ScriptLink('action=pagelock;set=0;id='
. UrlEncode($id),
Ts('Unlock %s', $title),
'pagelock 0'));
} else {
push(@menu, ScriptLink('action=pagelock;set=1;id=' . UrlEncode($id),
Ts('Lock %s', $title), 'pagelock 1'));
push(@menu, ScriptLink('action=pagelock;set=1;id='
. UrlEncode($id),
Ts('Lock %s', $title),
'pagelock 1'));
}
}
}
@@ -2334,7 +2356,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);
}
@@ -2398,9 +2420,9 @@ sub GetFooterLinks {
if ($id and $rev ne 'history' and $rev ne 'edit') {
if ($CommentsPrefix) {
if ($id =~ /^$CommentsPrefix(.*)/o) {
push(@elements, GetPageLink($1, undef, 'original'));
push(@elements, GetPageLink($1, undef, 'original', T('a')));
} else {
push(@elements, GetPageLink($CommentsPrefix . $id, undef, 'comment'));
push(@elements, GetPageLink($CommentsPrefix . $id, undef, 'comment', T('c')));
}
}
if (UserCanEdit($id, 0)) {
@@ -2487,21 +2509,22 @@ sub GetGotoBar { # ignore $id parameter
}
sub PrintHtmlDiff {
my ($type, $old, $new, $text) = @_;
my ($type, $old, $new, $text, $summary) = @_;
my $intro = T('Last edit');
my $diff = GetCacheDiff($type == 1 ? 'major' : 'minor');
# compute old revision if cache is disabled or no cached diff is available
if (not $old and (not $diff or GetParam('cache', $UseCache) < 1)) {
if ($type == 1) {
$old = $Page{lastmajor} - 1;
($text, $new) = GetTextRevision($Page{lastmajor}, 1)
unless $new or $Page{lastmajor} == $Page{revision};
($text, $new, $summary) = GetTextRevision($Page{lastmajor}, 1)
unless $new or $Page{lastmajor} == $Page{revision};
} elsif ($new) {
$old = $new - 1;
} else {
$old = $Page{revision} - 1;
}
}
$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);
$intro = Tss('Difference between revision %1 and %2', $old,
@@ -2512,7 +2535,7 @@ sub PrintHtmlDiff {
}
$diff =~ s!<p><strong>(.*?)</strong></p>!'<p><strong>' . T($1) . '</strong></p>'!ge;
$diff = T('No diff available.') unless $diff;
print $q->div({-class=>'diff'}, $q->p($q->b($intro)), $diff);
print $q->div({-class=>'diff'}, $q->p($q->b($intro)), $summary, $diff);
}
sub GetCacheDiff {
@@ -2667,8 +2690,8 @@ sub OpenPage { # Sets global variables
$Page{ts} = $Now;
$Page{revision} = 0;
if ($id eq $HomePage
and (open(F, '<:encoding(UTF-8)', $ReadMe)
or open(F, '<:encoding(UTF-8)', 'README'))) {
and (open(F, '<:utf8', $ReadMe)
or open(F, '<:utf8', 'README'))) {
local $/ = undef;
$Page{text} = <F>;
close F;
@@ -2696,15 +2719,15 @@ sub GetTextAtTime { # call with opened page, return $minor if all pages between
sub GetTextRevision {
my ($revision, $quiet) = @_;
$revision =~ s/\D//g; # Remove non-numeric chars
return ($Page{text}, $revision) unless $revision and $revision ne $Page{revision};
return ($Page{text}, $revision, $Page{summary}) unless $revision and $revision ne $Page{revision};
my %keep = GetKeptRevision($revision);
if (not %keep) {
$Message .= $q->p(Ts('Revision %s not available', $revision)
. ' (' . T('showing current revision instead') . ')') unless $quiet;
return ($Page{text}, '');
return ($Page{text}, '', '');
}
$Message .= $q->p(Ts('Showing revision %s', $revision)) unless $quiet;
return ($keep{text}, $revision);
return ($keep{text}, $revision, $keep{summary});
}
sub GetPageContent {
@@ -2798,7 +2821,7 @@ sub ExpireKeepFiles { # call with opened page
sub ReadFile {
my $file = shift;
utf8::encode($file); # filenames are bytes!
if (open(IN, '<:encoding(UTF-8)', $file)) {
if (open(IN, '<:utf8', $file)) {
local $/ = undef; # Read complete files
my $data=<IN>;
close IN;
@@ -3372,7 +3395,7 @@ sub PageIsUploadedFile {
if ($IndexHash{$id}) {
my $file = GetPageFile($id);
utf8::encode($file); # filenames are bytes!
open(FILE, '<:encoding(UTF-8)', $file)
open(FILE, '<:utf8', $file)
or ReportError(Ts('Cannot open %s', $file) . ": $!", '500 INTERNAL SERVER ERROR');
while (defined($_ = <FILE>) and $_ !~ /^text: /) {
} # read lines until we get to the text key
@@ -3794,8 +3817,7 @@ sub DoMaintain {
return;
}
}
RequestLockOrError();
print $q->p(T('Main lock obtained.')), '<p>', T('Expiring keep files and deleting pages marked for deletion');
print '<p>', T('Expiring keep files and deleting pages marked for deletion');
# Expire all keep files
foreach my $name (AllPagesList()) {
print $q->br(), GetPageLink($name);
@@ -3808,7 +3830,10 @@ sub DoMaintain {
ExpireKeepFiles();
}
}
print '</p>', $q->p(Ts('Moving part of the %s log file.', $RCName));
print '</p>';
RequestLockOrError();
print $q->p(T('Main lock obtained.'));
print $q->p(Ts('Moving part of the %s log file.', $RCName));
# Determine the number of days to go back
my $days = 0;
foreach (@RcDays) {

View File

@@ -42,6 +42,7 @@ our ($opt_v, $opt_w);
my $usage = qq{$0 [-i URL] [-d STRING] [-t SECONDS]
\t[-u USERNAME] [-p PASSWORD] [-w USERNAME:PASSWORD]
\t[-q QUESTION] [-a ANSWER] [-z SECRET]
\t[SOURCE] TARGET
SOURCE and TARGET are the base URLs for the two wikis. Visiting these
@@ -61,18 +62,15 @@ If you use -d instead of providing a SOURCE, all the pages will be
replaced with STRING. This is useful when replacing the page content
with "DeletedPage", for example.
Your copies on the target wiki will show up on the list of changes as
anonymous edits. If you want to provide a username, you can use -u to
do so.
If you want to copy pages to a locked wiki or if you need to overwrite
locked target pages, you need to provide a password using -p.
On the other hand, if your wiki is protected by so-called "basic
authentication" -- that is, if you need to provide a username and
password before you can even view the site -- then you can pass
those along using the -w option. Separate username and password
using a colon.
-d Delete target pages instead of providing SOURCE (default: none)
-s The summary for RecentChanges (default: none)
-u The username for RecentChanges (default: none)
-p The password to use for locked pages (default: none)
-w The username:password combo for basic authentication (default:none)
-q The question number to answer (default: 0, ie. the first question)
-a The answer to the question (default: none)
-z Alternatively, the secret key (default: question)
-v Verbose output for debugging (default: none)
Examples:
@@ -111,17 +109,31 @@ sub GetRaw {
return $response->content if $response->is_success;
}
sub PostRaw {
my ($uri, $id, $data, $username, $password) = @_;
sub post {
my ($uri, $id, $data, $minor, $summary, $username, $password,
$question, $answer, $secret) = @_;
my $ua = RequestAgent->new;
my $response = $ua->post($uri, {title=>$id, text=>$data, raw=>1,
username=>$username, pwd=>$password});
my %params = (title=>$id, text=>$data, raw=>1,
username=>$username, pwd=>$password,
summary=>$summary, question_num=>$question,
answer=>$answer, $secret=>1,
recent_edit=>$minor);
if ($opt_v) {
foreach my $key (keys %params) {
my $value = $params{$key} || '(none)';
$value = substr($value,0,50) . '...'
if $key eq 'text' and length($value) > 53;
warn "$key: $value\n";
}
}
my $response = $ua->post($uri, \%params);
my $status = $response->code . ' ' . $response->message;
warn "POST $id failed: $status.\n" unless $response->is_success;
}
sub copy {
my ($source, $replacement, $target, $interval, $username, $password,
my ($source, $replacement, $target, $interval, $minor, $summary,
$username, $password, $question, $answer, $secret,
@pages) = @_;
foreach my $id (@pages) {
print "$id\n";
@@ -129,18 +141,18 @@ sub copy {
# fix URL for other wikis
my $data = $replacement || GetRaw("$source?action=browse;id=$page;raw=1");
next unless $data;
PostRaw($target, $id, $data, $username, $password);
post($target, $id, $data, $minor, $summary, $username, $password,
$question, $answer, $secret);
sleep($interval);
}
}
sub main {
our($opt_i, $opt_t, $opt_d, $opt_u, $opt_p);
getopts('i:t:d:u:p:w:v');
our($opt_m, $opt_i, $opt_t, $opt_d, $opt_s, $opt_u, $opt_p,
$opt_q, $opt_a, $opt_z);
getopts('mi:t:d:s:u:p:q:a:z:w:v');
my $interval = $opt_t ? $opt_t : 5;
my $replacement = $opt_d;
my $username = $opt_u;
my $password = $opt_p;
my ($source, $target);
$source = shift(@ARGV) unless $replacement;
$target = shift(@ARGV);
@@ -157,7 +169,8 @@ sub main {
}
}
die "The list of pages is missing. Did you use -i?\n" unless @pages;
copy($source, $replacement, $target, $interval, $username, $password,
copy($source, $replacement, $target, $interval, $opt_m ? 'on' : '', $opt_s,
$opt_u, $opt_p, $opt_q, $opt_a, $opt_z||'question',
@pages);
}

35
wikipipe Executable file
View File

@@ -0,0 +1,35 @@
#! /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;

View File

@@ -84,7 +84,8 @@ sub post {
if ($opt_v) {
foreach my $key (keys %params) {
my $value = $params{$key} || '(none)';
$value = substr($value,0,50) . '...' if $key eq 'text' and length($value) > 53;
$value = substr($value,0,50) . '...'
if $key eq 'text' and length($value) > 53;
warn "$key: $value\n";
}
}