Compare commits

...

26 Commits

Author SHA1 Message Date
Alex Schroeder
9238e42cfd banned-regexps: updated copyright year 2018-09-04 09:54:04 +02:00
Alex Schroeder
836f3abecb Fixed bug in banned-regexps
BannedRegexps needs to check for BannedContent before it does
anything. BannedContent checks the URLs, and so BannedRegexps wants to
check everything except for the URLs. The order is important: the old
code used to remove the URLs before handing the text to BannedContent,
so it never found a thing. Changing the order fixed this.
2018-09-04 09:45:47 +02:00
Alex Schroeder
d669f783bb t/ban: test for homepage field 2018-09-04 09:37:49 +02:00
Alex Schroeder
c18279ca4c alex-2018.css: bigger fonts, wrapping pre 2018-08-24 11:29:23 +02:00
Alex Schroeder
bbd280abf9 ban-mixed-script: better fix for encoding issue 2018-08-14 15:01:01 +02:00
Alex Schroeder
c15e6041ef ban-mixed-scripts: silence meta.t warning 2018-08-14 11:57:26 +02:00
Alex Schroeder
e4c84d9b3a ban-mixed-scripts 2018-08-12 19:43:58 +02:00
Alex Schroeder
a49afca3f7 gopher-server: fix regular expressions for links
The regular expression now also matches mardown style external links.
Now using /x and named groups. This makes the entire thing much more
readable.
2018-08-10 20:45:11 +02:00
Alex Schroeder
883f07f926 Fix URL in Makefile 2018-08-06 12:07:18 +02:00
Alex Schroeder
b5d182582f Fix URLs in AddModuleDescription 2018-08-06 11:19:45 +02:00
Alex Schroeder
fe26471abb wiki.css: update for All Modules
Although I must say, the more I see of Div Foo, the less I like it. I
can't wrap a div around the <titles ...> rule and so now I use the
following on the All_Modules page:

<list>
<titles ...>

This introduces an empty div before the div I care about and the CSS
then does .foo_list + .journal etc.
2018-07-25 17:21:01 +02:00
Alex Schroeder
96a3cbbb22 tags: fix double quote bug
The old code removed double quotes before passing the search string on
to SearchString. Thus, "a b" would become a search for "a" and "b".
2018-07-24 12:56:08 +02:00
Alex Schroeder
d303e5e955 journal-rss: switch to full page feed always 2018-07-19 16:18:17 +02:00
Alex Schroeder
dd6198ffca journal-rss.t: simplify tests accordingly
Once the journal RSS just uses the latest edits, we don't have to care
about all the stuff involving major and minor versions.
2018-07-19 16:15:29 +02:00
Alex Schroeder
ae646290ed journal-rss: fix handling of new offset parameter 2018-07-19 16:02:24 +02:00
Alex Schroeder
1a74dc9605 journal-rss: don't ignore minor changes
If a page was created by just minor edits, then it is visible in a
journal. We shouldn't skip it from the journal RSS. Furthermore:
what's the point in showing the text from the last major version? Show
the latest version!
2018-07-19 15:52:17 +02:00
Alex Schroeder
0c19bd13cd tags.t: some more tests for filtering RC 2018-07-19 15:38:14 +02:00
Alex Schroeder
42bb4888a2 rss-journal: support feed pagination 2018-07-19 14:36:27 +02:00
Alex Schroeder
9a5e44aba3 Make feed pagination possible for journal-rss.pl
This works without changes to journal-rss.pl itself. All we do is make
sure that RcSelfAction, RcPreviousAction, and RcLastAction don't get
the action (rc vs. rss) from their arguments but determine it by
looking at the script parameters. Since journal-rss simply uses the
journal action, this will then get picked up correctly.
2018-07-19 13:04:31 +02:00
Alex Schroeder
78b5dd3737 rss.t: add more tests for RSS pagination 2018-07-19 11:43:07 +02:00
Alex Schroeder
b8717c4084 rss.t: add tests for RSS pagination 2018-07-19 11:39:06 +02:00
Alex Schroeder
42f4816ae5 gopher-server: surge protection disabled for tests
Make sure the gopher server reads the config file and skips surge
protection if $SurgeProtection is set to 0. The tests do this. If we
don't do this, tests start failing after a while.
2018-07-19 00:10:24 +02:00
Alex Schroeder
ff8d671787 wiki: first draft at implementing RFC 5005
We can easily provide the latest and the previous feed URL in addition
to the current (self) URL.
2018-07-18 23:53:18 +02:00
Alex Schroeder
e16cda46c1 gopher-server: rewrote surge protection
Instead of calling DoSurgeProtection as part of process_request and
faking username and overwriting ReportError, I've moved the essential
code to allow_deny_hook which seems to be the place dedicated to this
sort of code. Sadly, push(@{$self->{server}->{deny}}, $peeraddr) seems
to have no effect. When the next request comes around, the deny list
is empty again. I guess we would have to persist this somehow. For
now, we're simply using allow_deny_hook and that seems to be just as
well.
2018-07-16 21:34:11 +02:00
Alex Schroeder
431e792e52 gopher-server: add surge protection 2018-07-16 09:37:39 +02:00
Alex Schroeder
c9f3060e42 release: accept non-numeric versions like 2.3.11a 2018-07-13 00:52:35 +02:00
16 changed files with 453 additions and 126 deletions

View File

@@ -25,7 +25,7 @@ release:
perl stuff/release ~/oddmuse.org/releases 2.3.3
build/wiki.pl: wiki.pl
perl -lne "s/(\\\$$q->a\(\{-href=>'https:\/\/www.oddmuse.org\/'\}, 'Oddmuse'\))/\\\$$q->a({-href=>'https:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . \$$1/; print" < $< > $@
perl -lne "s/(\\\$$q->a\(\{-href=>'https:\/\/www.oddmuse.org\/'\}, 'Oddmuse'\))/\\\$$q->a({-href=>'https:\/\/alexschroeder.ch\/cgit\/oddmuse\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . \$$1/; print" < $< > $@
build/%-utf8.pl: modules/translations/%-utf8.pl
perl -lne "s/(AddModuleDescription\('[^']+', '[^']+')\)/\$$1, 'translations\/', '$(VERSION_NO)')/; print" < $< > $@

View File

@@ -2,9 +2,9 @@
html{ text-align: center; }
body, rss {
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
font-family: "DejaVu Serif", Palatino, serif;
font-style: normal;
font-size: 14pt;
font-size: 16pt;
padding: 1em 3em;
max-width: 72ex;
display: inline-block;
@@ -13,10 +13,31 @@ body, rss {
background-color: #fff;
}
@media only screen and (max-device-width: 480px) {
@media only screen and (max-device-width: 600px) {
body {
padding: 1ex;
}
textarea {
font-size: inherit;
}
}
/* code */
textarea, pre, code, tt {
font-family: "DejaVu Mono", "Andale Mono", Monaco, "Courier New", Courier, monospace;
}
pre, code, tt {
font-size: 12pt; /* fits 80ex */
}
pre {
overflow:hidden;
white-space: pre-wrap; /* CSS 3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
@import url(file:///home/alex/alexschroeder.ch/css/alex-2017.css) print;
@@ -191,22 +212,6 @@ input[type="text"] {
line-height: 125%;
}
/* code */
textarea, pre, code, tt {
font-family: "Andale Mono", Monaco, "Courier New", Courier, monospace, "Symbola";
font-size: 75%; /* fits 80ex */
}
pre {
overflow:hidden;
white-space: pre-wrap; /* CSS 3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
/* styling for divs that will be invisible when printing
when printing. */
@@ -223,6 +228,9 @@ div.header, div.footer, div.near, div.definition, p.comment, a.tag {
div.footer form.search {
display: none;
}
/* Recent Changes */
div.rc {
overflow: hidden;
}
@@ -233,12 +241,57 @@ div.rc li strong, table.history strong, strong.description {
font-family: inherit;
font-weight: inherit;
}
.red {
background: red;
color: red;
}
.orange {
background: orange;
color: orange;
}
.yellow {
background: yellow;
color: yellow;
}
.green {
background: green;
color: green;
}
.blue {
background: blue;
color: blue;
}
.indigo {
background: indigo;
color: indigo;
}
.violet {
background: violet;
color: violet;
}
.white {
background: white;
color: white;
}
.ip-code {
border: 1px solid #666;
}
/* Diff */
div.diff {
padding-left: 5%;
padding-right: 5%;
font-size: 12pt;
color: #000;
}
div.old {
background-color: #ffffaf;

View File

@@ -263,3 +263,19 @@ code {
background: #eee;
white-space: pre-wrap;
}
/* for https://oddmuse.org/wiki/All_Modules */
.foo_list + .journal h1 {
font: inherit;
border: none;
display: list-item;
margin-top: 0;
margin-left: 1em;
}
.foo_list + .journal a {
font: inherit;
border: none;
text-decoration: none;
color: #a00;
}

View File

@@ -0,0 +1,72 @@
# Copyright (C) 2018 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/>.
=encoding utf8
=head1 Mixed Scripts
This module disallows ordinary users from posting words that consist of multiple
scripts. Stuff like this: "It's diffіcult to find knowledgeable people on this
topic, but youu sound like you know wgat you're takіng аboսt!" Did you notice
the confusable characters? The sentence contains the following:
ARMENIAN SMALL LETTER SEH
CYRILLIC SMALL LETTER A
CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
SMALL ROMAN NUMERAL FIFTY
=cut
use strict;
use v5.10;
use Unicode::UCD qw(charprop);
AddModuleDescription('ban-mixed-scripts.pl', 'Ban Mixed Scripts Extension');
*OldBanMixedScriptsBannedContent = \&BannedContent;
*BannedContent = \&NewBanMixedScriptsBannedContent;
sub NewBanMixedScriptsBannedContent {
my $rule = OldBanMixedScriptsBannedContent(@_);
$rule ||= BanMixedScript(@_);
return $rule;
}
sub BanMixedScript {
my $str = shift;
my @words = $str =~ m/\w+/g;
my %seen;
my %prop;
for my $word (@words) {
next if $seen{$word};
$seen{$word} = 1;
my $script;
for my $char (split(//, $word)) {
my $s = $prop{$char};
if (not $s) {
$s = charprop(ord($char), "Script_Extensions");
if ($s eq 'Hiragana') {
$s = 'Han'; # this mixing is ok
}
$prop{$char} = $s;
}
next if $s eq "Common";
if (not $script) {
$script = $s;
} elsif ($script ne $s) {
return "Mixed scripts in $word ($script and $s, if not more)";
}
}
}
}

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2012-2018 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
@@ -62,9 +62,10 @@ push(@MyInitVariables, sub {
sub RegexpNewBannedContent {
my $str = shift;
# remove URLs as they are controlled by $BannedContent
$str =~ s/$FullUrlPattern//g;
# check whether Banned Content complains
my $rule = RegexpOldBannedContent($str, @_);
# remove URLs as they have been checked by $BannedContent
$str =~ s/$FullUrlPattern//g;
if (not $rule) {
foreach (split(/\n/, GetPageContent($BannedRegexps))) {
next unless m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/;

View File

@@ -1,4 +1,4 @@
# Copyright (C) 20042014 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20042018 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
@@ -30,15 +30,43 @@ sub DoJournalRss {
local $CollectingJournal = 1;
# Fake the result of GetRcLines()
local *GetRcLines = \&JournalRssGetRcLines;
local *RcSelfAction = \&JournalRssSelfAction;
local *RcPreviousAction = \&JournalRssPreviousAction;
local *RcLastAction = \&JournalRssLastAction;
SetParam('full', 1);
print GetHttpHeader('application/xml') . GetRcRss();
}
sub JournalRssParameters {
my $more = '';
foreach (@_, qw(rsslimit match search reverse monthly)) {
my $val = GetParam($_, '');
$more .= ";$_=$val" if $val;
}
return $more;
}
sub JournalRssSelfAction {
return "action=journal" . JournalRssParameters(qw(offset));
}
sub JournalRssPreviousAction {
my $num = GetParam('rsslimit', 10);
my $offset = GetParam('offset', 0) + $num;
return "action=journal;offset=$offset" . JournalRssParameters();
}
sub JournalRssLastAction {
return "action=journal" . JournalRssParameters();
}
sub JournalRssGetRcLines {
my $num = GetParam('rsslimit', 10);
my $match = GetParam('match', '^\d\d\d\d-\d\d-\d\d');
my $search = GetParam('search', '');
my $reverse = GetParam('reverse', 0);
my $monthly = GetParam('monthly', 0);
my $offset = GetParam('offset', 0);
my @pages = sort JournalSort (grep(/$match/, $search ? SearchTitleAndBody($search) : AllPagesList()));
if ($monthly and not $match) {
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime();
@@ -48,28 +76,19 @@ sub JournalRssGetRcLines {
@pages = reverse @pages;
}
# FIXME: Missing 'future' and 'past' keywords.
# FIXME: Do we need 'offset'? I don't think so.
my @result = ();
my $n = 0;
foreach my $id (@pages) {
# Now save information required for saving the cache of the current page.
local %Page;
local $OpenPageName = '';
OpenPage($id);
# If this is a minor edit, ignore it. Load the last major revision
# instead, if you can.
if ($Page{minor}) {
# Perhaps the old kept revision is gone due to $KeepMajor=0 or
# admin.pl or because a page was created as a minor change and
# never edited. Reading kept revisions in this case results in
# an error.
eval {
%Page = GetKeptRevision($Page{lastmajor});
};
next if $@;
}
next if $Page{text} =~ /^\s*$/; # only whitespace is also to be deleted
next if $DeletedPage && substr($Page{text}, 0, length($DeletedPage))
eq $DeletedPage; # no regexp
eq $DeletedPage; # no regexp
# OK, this is a candidate page
$n++;
next if $n <= $offset;
# Generate artifical rows in the list to pass to GetRcRss. We need
# to open every single page, because the meta-data ordinarily
# available in the rc.log file is not available to us. This is why
@@ -79,7 +98,7 @@ sub JournalRssGetRcLines {
push (@result, [$Page{ts}, $id, $Page{minor}, $Page{summary}, $Page{host},
$Page{username}, $Page{revision}, \@languages,
GetCluster($Page{text})]);
last if $num ne 'all' and $#result + 1 >= $num;
last if @result >= $num;
}
return @result;
}

View File

@@ -239,6 +239,11 @@ sub TagFind {
return @result;
}
sub TagsTerms {
my $string = shift;
return grep(/./, $string =~ /\"([^\"]+)\"|(\S+)/g);
}
*OldTagFiltered = \&Filtered;
*Filtered = \&NewTagFiltered;
@@ -246,7 +251,7 @@ sub NewTagFiltered { # called within a lock!
my ($string, @pages) = @_;
my %page = map { $_ => 1 } @pages;
# looking at all the "tag:SOME TERMS" and and tag:TERM
my @tagterms = map { FreeToNormal($_) } grep(/^-?tag:/, shift =~ /\"([^\"]+)\"|(\S+)/g);
my @tagterms = map { FreeToNormal($_) } grep(/^-?tag:/, TagsTerms($string));
my @positives = map {substr($_, 4)} grep(/^tag:/, @tagterms);
my @negatives = map {substr($_, 5)} grep(/^-tag:/, @tagterms);
if (@positives) {
@@ -260,8 +265,9 @@ sub NewTagFiltered { # called within a lock!
foreach my $id (TagFind(@negatives)) {
delete $page{$id};
}
# filter out the tags from the search string
$string = join(' ', grep(!/^-?tag:/, $string =~ /\"([^\"]+)\"|(\S+)/g));
# filter out the tags from the search string, and add quotes which might have
# been stripped
$string = join(' ', map { qq{"$_"} } grep(!/^-?tag:/, TagsTerms($string)));
# run the old code for any remaining search terms
return OldTagFiltered($string, sort keys %page);
}
@@ -279,11 +285,12 @@ We're need to remove all tag terms (again) in order to not confuse it.
*SearchString = \&NewTagSearchString;
sub NewTagSearchString {
# filter out the negative tags from the search string
my $string = join(' ', map { NormalToFree($_) }
grep(!/^-tag:/, shift =~ /\"([^\"]+)\"|(\S+)/g));
my ($string, @rest) = @_;
# filter out the negative tags from the search string, and add quotes which
# might have been stripped
$string = join(' ', map { NormalToFree($_) } map { qq{"$_"} } grep(!/^-tag:/, TagsTerms($string)));
return 1 unless $string;
return OldTagSearchString($string, @_);
return OldTagSearchString($string, @rest);
}
=pod

View File

@@ -20,10 +20,13 @@ use base qw(Net::Server::Fork); # any personality will do
use MIME::Base64;
use Text::Wrap;
use List::Util qw(first);
use Socket;
our($RunCGI, $DataDir, %IndexHash, @IndexList, $IndexFile, $TagFile, $q,
%Page, $OpenPageName, $MaxPost, $ShowEdits, %Locks, $CommentsPattern,
$CommentsPrefix, $EditAllowed, $NoEditFile, $SiteName, $ScriptName);
our($RunCGI, $DataDir, %IndexHash, @IndexList, $IndexFile, $TagFile, $q, %Page,
$OpenPageName, $MaxPost, $ShowEdits, %Locks, $CommentsPattern,
$CommentsPrefix, $EditAllowed, $NoEditFile, $SiteName, $ScriptName, $Now,
%RecentVisitors, $SurgeProtection, $SurgeProtectionTime,
$SurgeProtectionViews);
my $external_image_path = '/home/alex/alexschroeder.ch/pics/';
@@ -93,6 +96,12 @@ sub post_configure_hook {
# make sure search is sorted newest first because NewTagFiltered resorts
*OldGopherFiltered = \&Filtered;
*Filtered = \&NewGopherFiltered;
*ReportError = sub {
my ($error, $status, $log, @html) = @_;
$self->print_error("Error: $error");
map { ReleaseLockDir($_); } keys %Locks;
exit 2;
};
}
my $usage = << 'EOT';
@@ -435,9 +444,21 @@ sub serve_text_page_menu {
$self->serve_page_history_link($id, $revision);
my $first = 1;
while ($page->{text} =~ /\[\[([^\]|]*)(?:\|([^\]]*))?\]\]|\[(https?:\/\/\S+)\s+([^\]]*)\]|\[gopher:\/\/([^:\/]*)(?::(\d+))?(?:\/(\d)(\S+))?\s+([^\]]+)\]/g) {
my ($title, $text, $url, $hostname, $port, $type, $selector)
= ($1, $2||$4||$9, $3, $5, $6||70, $7||1, $8);
while ($page->{text} =~ /
\[\[ (?<title>[^\]|]*) (?:\|(?<text>[^\]]*))? \]\]
| \[ (?<url>https?:\/\/\S+) \s+ (?<text>[^\]]*) \]
| \[ (?<text>[^\]]*) \] \( (?<url>https?:\/\/\S+) \)
| \[ gopher:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
(?:\/(?<type>\d) (?<selector>\S+))?
\s+ (?<text>[^\]]+)\]
| \[ (?<text>[^\]]+) \]
\( gopher:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
(?:\/(?<type>\d) (?<selector>\S+))? \)
/xg) {
my ($title, $text, $url, $hostname,
$port, $type, $selector)
= ($+{title}, $+{text}, $+{url}, $+{hostname},
$+{port}||70, $+{type}||1, $+{selector});
if ($first) {
$self->print_info("");
$self->print_info("Links leaving " . normal_to_free($id) . ":");
@@ -801,13 +822,41 @@ sub read_text {
return $buf;
}
sub process_request {
sub allow_deny_hook {
my $self = shift;
my $client = shift;
# clear cookie and all that
# clear cookie, read config file
$q = undef;
Init();
# don't do surge protection if we're testing
return 1 unless $SurgeProtection;
# get the client IP number
my $peeraddr = $self->{server}->{'peeraddr'};
# implement standard surge protection using Oddmuse tools but without using
# ReportError and all that
$self->log(4, "Adding visitor $peeraddr");
ReadRecentVisitors();
AddRecentVisitor($peeraddr);
if (RequestLockDir('visitors')) { # not fatal
WriteRecentVisitors();
ReleaseLockDir('visitors');
my @entries = @{$RecentVisitors{$peeraddr}};
my $ts = $entries[$SurgeProtectionViews];
if (($Now - $ts) < $SurgeProtectionTime) {
$self->log(2, "Too many requests by $peeraddr");
return 0;
}
}
return 1;
}
sub process_request {
my $self = shift;
# refresh list of pages
if (IsFile($IndexFile) and ReadIndex()) {
# we're good

View File

@@ -33,7 +33,7 @@ EOT
my $min = version->parse(shift || "2.3.0");
my @tags = grep { /\d+\.\d+\.\d+/ and version->parse($_) >= $min }
my @tags = grep { /(\d+\.\d+\.\d+)/ and version->parse($1) >= $min }
split(/\n/, qx{git tag --list});
unless (@tags) {

33
t/ban-mixed-scripts.t Normal file
View File

@@ -0,0 +1,33 @@
# Copyright (C) 2018 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 => 4;
use utf8; # tests contain UTF-8 characters and it matters
add_module('ban-mixed-scripts.pl');
# ordinary page editing still works
test_page(update_page('Test', 'This is a test'),
'This is a test');
test_page(update_page('Test', '🙇🏽‍ 本当にごめんね I am really sorry.'),
'I am really sorry');
# mixed scripts are not ok
test_page(update_page('Test', "It's diffіcult to find knowledgeable people on this topic, but youu sound like you know wgat you're takіng аboսt!"),
'I am really sorry');
# error message is shown
test_page($redirect, "Mixed scripts");

24
t/ban.t
View File

@@ -1,4 +1,4 @@
# Copyright (C) 2006, 2007, 2010 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2006-2018 Alex Schroeder <alex@gnu.org>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
@@ -14,7 +14,7 @@
require './t/test.pl';
package OddMuse;
use Test::More tests => 23;
use Test::More tests => 27;
$localhost = 'confusibombus';
$ENV{'REMOTE_ADDR'} = $localhost;
@@ -96,3 +96,23 @@ test_page(get_page("action=rollback to=$ts id=CriminalPage username=Alex"),
test_page(get_page("action=rollback to=$ts id=CriminalPage pwd=foo"),
'Rolling back changes',
'CriminalPage</a> rolled back');
# make sure it also doesn't work in the homepage field for comments
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
# mafia is still banned
test_page(get_page(join(' ', 'title=Comments_on_CriminalPage',
'aftertext=Innocent',
'username=Alex',
'homepage=http://mafia.example.com')),
'Reason: crime');
# but it still works!
test_page(get_page(join(' ', 'title=Comments_on_CriminalPage',
'aftertext=Innocent',
'username=Alex',
'homepage=http://police.example.com')),
'Status: 302');
test_page(get_page('Comments_on_CriminalPage'),
'Innocent',
'http://police.example.com');

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2017 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2017-2018 Alex Schroeder <alex@gnu.org>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
@@ -14,7 +14,7 @@
require './t/test.pl';
package OddMuse;
use Test::More tests => 13;
use Test::More tests => 15;
add_module('banned-regexps.pl');
@@ -63,3 +63,13 @@ test_page(update_page('Test2', 'Voldemort', 'one banned word'),
# Make sure the underscores don't show up in the page link
test_page(get_page('action=admin'), 'Local Banned Regexps');
# Make sure it doesn't break BannedContent!
# mafia is banned
update_page('BannedContent', 'mafia', 'one banned word', 0, 1);
test_page(update_page('CriminalPage', 'This is about http://mafia.example.com'),
'This page does not exist');
# error message is shown
test_page($redirect, 'Edit Denied');

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2008, 2009 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2008-2018 Alex Schroeder <alex@gnu.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,33 +15,26 @@
require './t/test.pl';
package OddMuse;
use Test::More tests => 48;
use Test::More tests => 41;
add_module('journal-rss.pl');
# summaries eq page content since no summaries are provided
update_page('2008-09-21', 'first page');
update_page('2008-09-22', 'second page'); # major
sleep(1);
update_page('2008-09-22', 'third edit', 'third edit', 1); # minor
update_page('2008-09-22', 'third edit content', 'third edit summary', 1); # minor
update_page('unrelated', 'wrong page');
my $page = get_page('action=journal');
test_page($page,
'2008-09-21', 'first page',
# ignore minor edits are ignored: show last major edit
# instead
'2008-09-22', 'second page',
# make sure we're showing full page content, not summaries
'2008-09-22', 'third edit content',
# reverse sort is the default
'2008-09-22(.*\n)+.*2008-09-21');
# make sure unrelated pages and minor edits don't show up
test_page_negative($page, 'unrelated', 'wrong page',
'third edit');
# verify the order of pages
test_page(get_page('action=journal'),
'2008-09-22(.*\n)+.*2008-09-21');
test_page_negative($page, 'unrelated', 'wrong page');
# reverse the order
test_page(get_page('action=journal reverse=1'),
@@ -52,21 +45,6 @@ $page = get_page('action=journal match=21');
test_page($page, '2008-09-21', 'first page');
test_page_negative($page, '2008-09-22', 'second page');
# search parameter
$page = get_page('action=journal search=second');
# no pages found, since this is for an old revision!
test_page_negative($page,
'2008-09-21', 'first page',
'2008-09-22', 'second page',
'third edit');
# strange but true: search returns a page based on the minor edit but
# shows the latest major revision that doesn't actually match.
$page = get_page('action=journal search=third');
test_page($page, '2008-09-22', 'second page');
test_page_negative($page, '2008-09-21', 'first page', 'third edit');
# testing the limit default
update_page('2008-09-05', 'page');
update_page('2008-09-06', 'page');
@@ -98,17 +76,23 @@ $page = get_page('action=journal rsslimit=1');
test_page($page, '2008-09-22');
test_page_negative($page, '2008-09-21');
$page = get_page('action=journal rsslimit=all');
test_page($page, '2008-09-22', '2008-09-05');
# make sure we start from a well-known point in time
AppendStringToFile($ConfigFile, "push(\@MyInitVariables, sub { \$Now = '$Now' });\n");
# Now let's show that we're using the timestamp of the last major
# change if possible.
# check default RSS
xpath_test(get_page('action=journal'),
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal"]',
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal"]',
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=10"]');
my @dates = get_page('action=rss showedit=1 all=1 match=2008-09-22')
=~ m!<pubDate>(.*?)</pubDate>!g;
# $dates[0] is the channel pubDate
my $date2 = $dates[1]; # revision 2 comes first
my $date1 = $dates[2]; # revision 1 comes second
my ($item) = $page =~ m!(<item>\n<title>2008-09-22</title>\n(.*\n)+?</item>\n)!;
test_page($item, "<pubDate>$date1</pubDate>");
test_page_negative($item, "<pubDate>$date2</pubDate>");
# check next page
xpath_test(get_page('action=journal offset=10'),
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal;offset=10"]',
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal"]',
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=20"]');
# check next page but with a tag search
xpath_test(get_page('action=journal search=tag:oddmuse'),
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal;search=tag:oddmuse"]',
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal;search=tag:oddmuse"]',
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=10;search=tag:oddmuse"]');

34
t/rss.t
View File

@@ -1,4 +1,4 @@
# Copyright (C) 20062015 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20062018 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,7 +15,7 @@
require './t/test.pl';
package OddMuse;
use Test::More tests => 126;
use Test::More tests => 135;
use utf8; # tests contain UTF-8 characters and it matters
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
@@ -107,6 +107,36 @@ update_page('big', "mee too\n" x 2 . 'x' x 55000);
test_page(get_page('action=rss full=1'), 'too big to send over RSS');
test_page(get_page('action=rss full=1 diff=1'), 'mee too', 'too big to send over RSS');
# pagination
my $interval = $RcDefault * 24 * 60 * 60;
my $t1 = $Now - $interval;
my $t2 = $Now - 2 * $interval;
my $t3 = $Now - 3 * $interval;
my $action1 = " from=$t2 upto=$t1";
my $window1 = ";from=$t2;upto=$t1";
my $window2 = ";from=$t3;upto=$t2";
# make sure we start from a well-known point in time
AppendStringToFile($ConfigFile, "push(\@MyInitVariables, sub { \$Now = '$Now' });\n");
# check default RSS
xpath_test(get_page('action=rss'),
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=rss"]',
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=rss"]',
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=rss' . $window1 . '"]');
# check next page
xpath_test(get_page('action=rss' . $action1),
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=rss' . $window1 . '"]',
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=rss"]',
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=rss' . $window2 . '"]');
# check next page but with full pages
xpath_test(get_page('action=rss full=1' . $action1),
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=rss' . $window1 . ';full=1"]',
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=rss;full=1"]',
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=rss' . $window2 . ';full=1"]');
SKIP: {
eval {

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2006, 2009 Alex Schroeder <alex@gnu.org>
# Copyright (C) 2006-2018 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,12 +15,27 @@
require './t/test.pl';
package OddMuse;
use Test::More tests => 70;
use Test::More tests => 94;
use utf8;
add_module('tags.pl');
InitVariables();
# first, let's make sure the old search still works
test_page(join(':', TagsTerms('a b "c d"')), 'a:b:c d');
update_page('test', 'foo bar baz');
test_page(get_page('search=foo'), 'test', 'Search for: foo', '<strong>foo</strong>');
test_page(get_page('search=foo+bar'), 'test', 'Search for: foo bar', '<strong>foo</strong> <strong>bar</strong>');
test_page(get_page('search=bar+foo'), 'test', 'Search for: bar foo', '<strong>foo</strong> <strong>bar</strong>');
test_page(get_page('search=foo+baz'), 'test', 'Search for: foo baz', '<strong>foo</strong> bar <strong>baz</strong>');
$page = get_page('search=%22foo+baz%22');
test_page($page, 'Search for: "foo baz"');
test_page_negative($page, 'test');
test_page(get_page('search=%22foo+bar%22'), 'test', 'Search for: "foo bar"', '<strong>foo bar</strong>');
# testing other stuff
$TagFeedIcon = 'http://www.example.org/pics/rss.png';
xpath_run_tests(split('\n',<<'EOT'));
@@ -160,6 +175,12 @@ $page = update_page('Podcasts', '<journal "." search tag:podcast>');
test_page($page, qw(Pödgecäst´s Brilliant Sons));
test_page_negative($page, qw(Alex Foo));
# check recent changes
test_page(get_page('action=rc'), qw(Brilliant Alex Jeff));
$page = get_page('action=rc rcfilteronly=tag:Podcast');
test_page($page, qw(Brilliant));
test_page_negative($page, qw(Alex Jeff));
# check the tag cloud
xpath_test(get_page('action=tagcloud'),
'//h1[text()="Tag Cloud"]',

56
wiki.pl
View File

@@ -1700,15 +1700,35 @@ sub RcHeader {
return $html;
}
sub GetScriptUrlWithRcParameters {
my $url = "$ScriptName?action=rss";
foreach my $param (qw(from upto days all showedit rollback rcidonly rcuseronly
rchostonly rcclusteronly rcfilteronly match lang
followup page diff full)) {
my $val = GetParam($param, undef);
$url .= ";$param=$val" if defined $val;
sub RcOtherParameters {
my $more = '';
foreach (@_, qw(page diff full all showedit rollback rcidonly rcuseronly rchostonly rcclusteronly rcfilteronly match lang followup)) {
my $val = GetParam($_, '');
$more .= ";$_=$val" if $val;
}
return $url;
return $more;
}
sub RcSelfAction {
my $action = GetParam('action', 'rc');
return "action=$action" . RcOtherParameters(qw(from upto days));
}
sub RcPreviousAction {
my $action = GetParam('action', 'rc');
my $interval = GetParam('days', $RcDefault) * 86400;
# use delta between from and upto, or use days, whichever is available
my $to = GetParam('from', GetParam('upto', $Now - $interval));
my $from = $to - (GetParam('upto') ? GetParam('upto') - GetParam('from') : $interval);
return "action=$action;from=$from;upto=$to" . RcOtherParameters();
}
sub RcLastAction {
my $action = GetParam('action', 'rc');
my $more = "action=$action";
my $days = GetParam('days', $RcDefault);
$more .= ";days=$days" if $days != $RcDefault;
return $more . RcOtherParameters();
}
sub GetFilterForm {
@@ -1798,16 +1818,7 @@ sub RcHtml {
};
ProcessRcLines($printDailyTear, $printRCLine);
$html .= '</ul>' if $inlist;
# use delta between from and upto, or use days, whichever is available
my $to = GetParam('from', GetParam('upto', $Now - GetParam('days', $RcDefault) * 86400));
my $from = $to - (GetParam('upto') ? GetParam('upto') - GetParam('from') : GetParam('days', $RcDefault) * 86400);
my $more = "action=rc;from=$from;upto=$to";
foreach (qw(all showedit rollback rcidonly rcuseronly rchostonly
rcclusteronly rcfilteronly match lang followup)) {
my $val = GetParam($_, '');
$more .= ";$_=$val" if $val;
}
$html .= $q->p({-class=>'more'}, ScriptLink($more, T('More...'), 'more'));
$html .= $q->p({-class=>'more'}, ScriptLink(RcPreviousAction(), T('More...'), 'more'));
return GetFormStart(undef, 'get', 'rc') . $html . $q->end_form;
}
@@ -1884,8 +1895,9 @@ sub GetRcRss {
my $title = QuoteHtml($SiteName) . ': ' . GetParam('title', QuoteHtml(NormalToFree($HomePage)));
$rss .= "<title>$title</title>\n";
$rss .= "<link>" . ScriptUrl($HomePage) . "</link>\n";
$rss .= qq{<atom:link href="} . GetScriptUrlWithRcParameters()
. qq{" rel="self" type="application/rss+xml" />\n};
$rss .= qq{<atom:link href="$ScriptName?} . RcSelfAction() . qq{" rel="self" type="application/rss+xml" />\n};
$rss .= qq{<atom:link href="$ScriptName?} . RcPreviousAction() . qq{" rel="previous" type="application/rss+xml" />\n};
$rss .= qq{<atom:link href="$ScriptName?} . RcLastAction() . qq{" rel="last" type="application/rss+xml" />\n};
if ($SiteDescription) {
$rss .= "<description>" . QuoteHtml($SiteDescription) . "</description>\n"
}
@@ -4100,8 +4112,8 @@ sub TextIsFile { $_[0] =~ /^#FILE (\S+) ?(\S+)?\n/; }
sub AddModuleDescription { # cannot use $q here because this is module init time
my ($filename, $page, $dir, $tag) = @_;
my $src = "http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/$dir" . UrlEncode($filename) . ($tag ? '?' . $tag : '');
my $doc = 'https://www.oddmuse.org/cgi-bin/oddmuse/' . UrlEncode(FreeToNormal($page));
my $src = "https://alexschroeder.ch/cgit/oddmuse/tree/modules/$dir" . UrlEncode($filename) . ($tag ? '?' . $tag : '');
my $doc = 'https://www.oddmuse.org/wiki/' . UrlEncode(FreeToNormal($page));
$ModulesDescription .= "<p><a href=\"$src\">" . QuoteHtml($filename) . "</a>" . ($tag ? " ($tag)" : '');
$ModulesDescription .= T(', see') . " <a href=\"$doc\">" . QuoteHtml($page) . "</a>" if $page;
$ModulesDescription .= "</p>";