forked from github/kensanata.oddmuse
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9238e42cfd | ||
|
|
836f3abecb | ||
|
|
d669f783bb | ||
|
|
c18279ca4c | ||
|
|
bbd280abf9 | ||
|
|
c15e6041ef | ||
|
|
e4c84d9b3a | ||
|
|
a49afca3f7 | ||
|
|
883f07f926 | ||
|
|
b5d182582f | ||
|
|
fe26471abb | ||
|
|
96a3cbbb22 | ||
|
|
d303e5e955 | ||
|
|
dd6198ffca | ||
|
|
ae646290ed | ||
|
|
1a74dc9605 | ||
|
|
0c19bd13cd | ||
|
|
42bb4888a2 | ||
|
|
9a5e44aba3 | ||
|
|
78b5dd3737 | ||
|
|
b8717c4084 | ||
|
|
42f4816ae5 | ||
|
|
ff8d671787 | ||
|
|
e16cda46c1 | ||
|
|
431e792e52 | ||
|
|
c9f3060e42 |
2
Makefile
2
Makefile
@@ -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" < $< > $@
|
||||
|
||||
@@ -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;
|
||||
|
||||
16
css/wiki.css
16
css/wiki.css
@@ -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;
|
||||
}
|
||||
|
||||
72
modules/ban-mixed-scripts.pl
Normal file
72
modules/ban-mixed-scripts.pl
Normal 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 taⅼkі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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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*)?(.*))?$/;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004–2014 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004–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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
33
t/ban-mixed-scripts.t
Normal 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 taⅼkіng аboսt!"),
|
||||
'I am really sorry');
|
||||
|
||||
# error message is shown
|
||||
test_page($redirect, "Mixed scripts");
|
||||
24
t/ban.t
24
t/ban.t
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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
34
t/rss.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006–2015 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,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 {
|
||||
|
||||
25
t/tags.t
25
t/tags.t
@@ -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
56
wiki.pl
@@ -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>";
|
||||
|
||||
Reference in New Issue
Block a user