forked from github/kensanata.oddmuse
Compare commits
68 Commits
common-mar
...
namespaces
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16053e26d7 | ||
|
|
f0d0942bfb | ||
|
|
cd9246ebed | ||
|
|
f7b23d854f | ||
|
|
104a1395e7 | ||
|
|
ceb4c3a9cc | ||
|
|
536757e8e2 | ||
|
|
00af1aa638 | ||
|
|
c2cf3e7b43 | ||
|
|
05c14d37b2 | ||
|
|
fc3614f291 | ||
|
|
e201c77696 | ||
|
|
f280cb5df4 | ||
|
|
29863d7109 | ||
|
|
b514ea7846 | ||
|
|
eeaf615d3b | ||
|
|
f003481c5e | ||
|
|
4d10ef389a | ||
|
|
726ffdced1 | ||
|
|
18c4071da8 | ||
|
|
fd7fa0c3ab | ||
|
|
2ba5b72242 | ||
|
|
fa5a2f7a1a | ||
|
|
ad042630b6 | ||
|
|
9cf35b9b52 | ||
|
|
4f69103b8c | ||
|
|
37c882780a | ||
|
|
6d5f97e1ba | ||
|
|
4b1063c699 | ||
|
|
b891674a6f | ||
|
|
1a65df6e36 | ||
|
|
6043be852c | ||
|
|
bb11bdf789 | ||
|
|
540fd588c9 | ||
|
|
278fad1f43 | ||
|
|
eadeb460f5 | ||
|
|
5da9ce64c0 | ||
|
|
40498b53f7 | ||
|
|
eaf97602ff | ||
|
|
987c262425 | ||
|
|
c33ee0a9e6 | ||
|
|
eb7665661f | ||
|
|
72ae1bf56f | ||
|
|
8f30ed8109 | ||
|
|
19e71f1180 | ||
|
|
9397a38394 | ||
|
|
17bd2d08cd | ||
|
|
47a5e81000 | ||
|
|
7bfe740fb2 | ||
|
|
6a324b59b9 | ||
|
|
23545006a5 | ||
|
|
65012eacbb | ||
|
|
91107143f3 | ||
|
|
cafda90555 | ||
|
|
32dfec102d | ||
|
|
c1cdca5f95 | ||
|
|
61dc928e33 | ||
|
|
d43fe3fab9 | ||
|
|
3acb572c0d | ||
|
|
0f6787d349 | ||
|
|
af287a1279 | ||
|
|
6bbd43f8a3 | ||
|
|
364d7c695b | ||
|
|
871af41881 | ||
|
|
4648bfbd83 | ||
|
|
129d02850b | ||
|
|
ee23ef509c | ||
|
|
7e865696b0 |
17
Makefile
17
Makefile
@@ -3,7 +3,7 @@
|
||||
# subdirectory.
|
||||
|
||||
VERSION_NO=$(shell git describe --tags)
|
||||
TRANSLATIONS=$(wildcard modules/translations/[a-z]*-utf8.pl$)
|
||||
TRANSLATIONS=$(wildcard modules/translations/*-utf8.pl)
|
||||
MODULES=$(sort $(wildcard modules/*.pl))
|
||||
BUILD=build/wiki.pl $(foreach file, $(notdir $(MODULES)) $(notdir $(TRANSLATIONS)), build/$(file))
|
||||
|
||||
@@ -64,6 +64,17 @@ test:
|
||||
# Spin up a quick test
|
||||
|
||||
development:
|
||||
@if grep --quiet 'ScriptName = "http://127.0.0.1:8080";' test-data/config; then \
|
||||
echo Not overwriting \$$ScriptName in test-data/config; \
|
||||
else \
|
||||
echo '$ScriptName = "http://127.0.0.1:8080";' >> test-data/config; \
|
||||
fi
|
||||
morbo --listen http://*:8080 \
|
||||
--watch wiki.pl --watch test-data/config --watch test-data/modules/ \
|
||||
stuff/mojolicious-app.pl
|
||||
--watch wiki.pl --watch test-data/config --watch test-data/modules/ \
|
||||
stuff/mojolicious-app.pl
|
||||
|
||||
%.pem:
|
||||
openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem
|
||||
|
||||
gemini: cert.pem key.pem
|
||||
perl stuff/gemini-server.pl --wiki_cert_file=cert.pem --wiki_key_file=key.pem
|
||||
|
||||
425
css/latex.css
Normal file
425
css/latex.css
Normal file
@@ -0,0 +1,425 @@
|
||||
/*!
|
||||
* LaTeX.css (https://latex.now.sh/)
|
||||
*
|
||||
* Source: https://github.com/vincentdoerig/latex-css
|
||||
* Licensed under MIT (https://github.com/vincentdoerig/latex-css/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'Latin Modern';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-display: swap;
|
||||
src: url('/style/fonts/LM-regular.woff2') format('woff2'),
|
||||
url('/style/fonts/LM-regular.woff') format('woff'),
|
||||
url('/style/fonts/LM-regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Latin Modern';
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
font-display: swap;
|
||||
src: url('/style/fonts/LM-italic.woff2') format('woff2'),
|
||||
url('/style/fonts/LM-italic.woff') format('woff'),
|
||||
url('/style/fonts/LM-italic.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Latin Modern';
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-display: swap;
|
||||
src: url('/style/fonts/LM-bold.woff2') format('woff2'),
|
||||
url('/style/fonts/LM-bold.woff') format('woff'),
|
||||
url('/style/fonts/LM-bold.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Latin Modern';
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
font-display: swap;
|
||||
src: url('/style/fonts/LM-bold-italic.woff2') format('woff2'),
|
||||
url('/style/fonts/LM-bold-italic.woff') format('woff'),
|
||||
url('/style/fonts/LM-bold-italic.ttf') format('truetype');
|
||||
}
|
||||
|
||||
/* Box sizing rules */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Remove default margin */
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
p,
|
||||
ul[class],
|
||||
ol[class],
|
||||
li,
|
||||
figure,
|
||||
figcaption,
|
||||
dl,
|
||||
dd {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Make default font-size 1rem and add smooth scrolling to anchors */
|
||||
html {
|
||||
font-size: 1.4rem;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Latin Modern', Georgia, Cambria, 'DejaVu Serif', 'Times New Roman', Times, serif;
|
||||
line-height: 1.4;
|
||||
max-width: 80ch;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.25rem;
|
||||
|
||||
counter-reset: theorem;
|
||||
counter-reset: definition;
|
||||
|
||||
color: hsl(0, 5%, 10%);
|
||||
background-color: hsl(210, 20%, 98%);
|
||||
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
/* Justify and hyphenate all paragraphs */
|
||||
p {
|
||||
text-align: justify;
|
||||
hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* A elements that don't have a class get default styles */
|
||||
a:not([class]) {
|
||||
text-decoration-skip-ink: auto;
|
||||
}
|
||||
|
||||
/* Make links red */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #a00;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
color: #800;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid hsl(220, 90%, 52%);
|
||||
}
|
||||
|
||||
|
||||
/* Ueberschriften mit Links nur dezent einfärben */
|
||||
h1 a, h1 a:visited,
|
||||
h2 a, h2 a:visited,
|
||||
h3 a, h3 a:visited,
|
||||
h4 a, h4 a:visited,
|
||||
h5 a, h5 a:visited,
|
||||
h6 a, h6 a:visited {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* goto bar */
|
||||
div.menu form.search {
|
||||
font-size:75%;
|
||||
margin-top:2em;
|
||||
margin-bottom:3em;
|
||||
}
|
||||
|
||||
div.menu span.gotobar a.local,
|
||||
div.menu span.gotobar a.local:visited {
|
||||
text-decoration: none;
|
||||
color: #1e133c87;
|
||||
margin-right:1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Make images easier to work with */
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Inherit fonts for inputs and buttons */
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* Prevent textarea from overflowing */
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Natural flow and rhythm in articles by default */
|
||||
article > * + * {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* Styles for inline code or code snippets */
|
||||
code,
|
||||
pre,
|
||||
kbd {
|
||||
font-family: Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
font-size: 85%;
|
||||
}
|
||||
pre {
|
||||
padding: 1rem 1.4rem;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
border-radius: 4px;
|
||||
background: hsl(210, 28%, 93%);
|
||||
}
|
||||
pre code {
|
||||
font-size: 95%;
|
||||
position: relative;
|
||||
}
|
||||
kbd {
|
||||
background: hsl(210, 5%, 100%);
|
||||
border: 1px solid hsl(210, 5%, 70%);
|
||||
border-radius: 2px;
|
||||
padding: 2px 4px;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
/* Make table 100% width, add borders between rows */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
td {
|
||||
border-bottom: 1px solid hsl(0, 0%, 85%);
|
||||
}
|
||||
thead th {
|
||||
border-bottom: 2px solid hsl(0, 0%, 70%);
|
||||
}
|
||||
tfoot th {
|
||||
border-top: 2px solid hsl(0, 0%, 70%);
|
||||
}
|
||||
|
||||
/* Center align the title */
|
||||
h1:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Nested ordered list for ToC */
|
||||
nav ol {
|
||||
counter-reset: item;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
nav li {
|
||||
display: block;
|
||||
}
|
||||
nav li:before {
|
||||
content: counters(item, '.') ' ';
|
||||
counter-increment: item;
|
||||
padding-right: 0.85rem;
|
||||
}
|
||||
|
||||
/* Center definitions (most useful for display equations) */
|
||||
dl dd {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Theorem */
|
||||
.theorem {
|
||||
counter-increment: theorem;
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
font-style: italic;
|
||||
}
|
||||
.theorem::before {
|
||||
content: 'Satz ' counter(theorem) '. ';
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Lemma */
|
||||
.lemma {
|
||||
counter-increment: theorem;
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
font-style: italic;
|
||||
}
|
||||
.lemma::before {
|
||||
content: 'Lemma ' counter(theorem) '. ';
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Proof */
|
||||
.proof {
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
font-style: normal;
|
||||
position: relative;
|
||||
}
|
||||
.proof::before {
|
||||
content: 'Beweis. ' attr(title);
|
||||
font-style: italic;
|
||||
}
|
||||
.proof:after {
|
||||
content: '◾️';
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
bottom: -2px;
|
||||
}
|
||||
|
||||
/* Definition */
|
||||
.definition {
|
||||
counter-increment: definition;
|
||||
display: block;
|
||||
margin: 12px 0;
|
||||
font-style: normal;
|
||||
}
|
||||
.definition::before {
|
||||
content: 'Definition ' counter(definition) '. ';
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Center align author name, use small caps and add vertical spacing */
|
||||
.author {
|
||||
margin: 0.85rem 0;
|
||||
font-variant-caps: small-caps;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Make footnote text smaller and left align it (looks bad with long URLs) */
|
||||
.footnotes p {
|
||||
text-align: left;
|
||||
line-height: 1.5;
|
||||
font-size: 85%;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.footnotes {
|
||||
border-top: 1px solid hsl(0, 0%, 39%);
|
||||
}
|
||||
|
||||
/* Center title and paragraph */
|
||||
.abstract,
|
||||
.abstract p {
|
||||
text-align: center;
|
||||
}
|
||||
.abstract {
|
||||
margin: 2.25rem 0;
|
||||
}
|
||||
|
||||
/* Format the LaTeX symbol correctly (a higher up, e lower) */
|
||||
.latex span:nth-child(1) {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75em;
|
||||
vertical-align: 0.28em;
|
||||
margin-left: -0.48em;
|
||||
margin-right: -0.15em;
|
||||
line-height: 1ex;
|
||||
}
|
||||
|
||||
.latex span:nth-child(2) {
|
||||
text-transform: uppercase;
|
||||
vertical-align: -0.5ex;
|
||||
margin-left: -0.1667em;
|
||||
margin-right: -0.125em;
|
||||
line-height: 1ex;
|
||||
}
|
||||
|
||||
/* Heading typography */
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
line-height: 3.25rem;
|
||||
margin-bottom: 1.625rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.7rem;
|
||||
line-height: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.4rem;
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
margin-top: 1.8rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
line-height: 1.625rem;
|
||||
}
|
||||
|
||||
h1 + h2 {
|
||||
margin-top: 1.625rem;
|
||||
}
|
||||
|
||||
h2 + h3,
|
||||
h3 + h4,
|
||||
h4 + h5 {
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
|
||||
h5 + h6 {
|
||||
margin-top: -0.8rem;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
div.diff div.old {
|
||||
background-color: #FFFFAF;
|
||||
}
|
||||
|
||||
div.diff div.new {
|
||||
background-color: #CFFFCF;
|
||||
}
|
||||
|
||||
div.content blockquote {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2013-2016 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2013-2021 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
|
||||
@@ -96,16 +96,20 @@ sub DoBanHosts {
|
||||
if (IsItBanned($_, \@regexps)) {
|
||||
print $q->p(Ts("%s is banned", $name));
|
||||
} else {
|
||||
my ($start, $end) = BanContributors::get_range($_);
|
||||
$range = "[$start - $end]";
|
||||
$name .= " " . $range;
|
||||
print GetFormStart(undef, 'get', 'ban'),
|
||||
GetHiddenValue('action', 'ban'),
|
||||
GetHiddenValue('id', $id),
|
||||
GetHiddenValue('range', $range),
|
||||
GetHiddenValue('regexp', BanContributors::get_regexp_ip($start, $end)),
|
||||
GetHiddenValue('recent_edit', 'on'),
|
||||
$q->p($name, $q->submit(T('Ban!'))), $q->end_form();
|
||||
my @pairs = BanContributors::get_range($_);
|
||||
while (@pairs) {
|
||||
my $start = shift(@pairs);
|
||||
my $end = shift(@pairs);
|
||||
$range = "[$start - $end]";
|
||||
$name .= " " . $range;
|
||||
print GetFormStart(undef, 'get', 'ban'),
|
||||
GetHiddenValue('action', 'ban'),
|
||||
GetHiddenValue('id', $id),
|
||||
GetHiddenValue('range', $range),
|
||||
GetHiddenValue('regexp', BanContributors::get_regexp_ip($start, $end)),
|
||||
GetHiddenValue('recent_edit', 'on'),
|
||||
$q->p($name, $q->submit(T('Ban!'))), $q->end_form();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,47 +171,73 @@ sub NewBanContributorsWriteRcLog {
|
||||
|
||||
package BanContributors;
|
||||
use Net::Whois::Parser qw/parse_whois/;
|
||||
use Net::IP;
|
||||
|
||||
sub get_range {
|
||||
my $ip = shift;
|
||||
my $response = parse_whois(domain => $ip);
|
||||
my ($start, $end);
|
||||
my $re = '(?:[0-9]{1,3}\.){3}[0-9]{1,3}';
|
||||
my ($start, $end) = $response->{inetnum} =~ /($re) *- *($re)/;
|
||||
return $start, $end;
|
||||
# Just try all the keys and see whether there is a range match.
|
||||
for (keys %$response) {
|
||||
my @result;
|
||||
$_ = $response->{$_};
|
||||
for (ref eq 'ARRAY' ? @$_ : $_) {
|
||||
$ip = Net::IP->new($_);
|
||||
push(@result, $ip->ip, $ip->last_ip) if $ip;
|
||||
}
|
||||
return @result if @result;
|
||||
}
|
||||
# Fallback
|
||||
return $ip, $ip;
|
||||
}
|
||||
|
||||
sub get_groups {
|
||||
my ($from, $to) = @_;
|
||||
my @groups;
|
||||
if ($from < 10) {
|
||||
my $to = $to >= 10 ? 9 : $to;
|
||||
if ($from == $to) {
|
||||
return [$from, $to];
|
||||
}
|
||||
# ones up to the nearest ten
|
||||
if ($from < $to and ($from % 10 or $from < 10)) {
|
||||
# from 5-7: as is
|
||||
# from 5-17: 5 + 9 - 5 = 9 thus 5-9, set $from to 10
|
||||
my $to2 = int($to/10) > int($from/10) ? $from + 9 - $from % 10 : $to;
|
||||
push(@groups, [$from, $to2]);
|
||||
$from = $to2 + 1;
|
||||
}
|
||||
# tens up to the nearest hundred
|
||||
if ($from < $to and $from % 100) {
|
||||
# 10-17: as is
|
||||
# 10-82: 10 to 79, set $from to 80 (8*10-1)
|
||||
# 10-182: 10 to 99, set $from to 100 (10+99=10=99)
|
||||
# 110-182: 110 to 179, set $from to 180 (170)
|
||||
# 110-222: 110 to 199, set $from to 200 (110+99-10 = 199)
|
||||
my $to2 = int($to/100) > int($from/100) ? $from + 99 - $from % 100
|
||||
: int($to/10) > int($from/10) ? int($to / 10) * 10 - 1
|
||||
: $to;
|
||||
push(@groups, [$from, $to2]);
|
||||
$from = $to2 + 1;
|
||||
}
|
||||
# up to the next hundred
|
||||
if (int($to/100) > int($from/100)) {
|
||||
# from 100 to 223: set $from to 200 (2*100-1)
|
||||
my $to2 = int($to/100) * 100 - 1;
|
||||
push(@groups, [$from, $to2]);
|
||||
$from = $to2 + 1;
|
||||
}
|
||||
# up to the next ten
|
||||
if (int($to/10) > int($from/10)) {
|
||||
# 10 to 17: skip
|
||||
# 100 to 143: set $from to 140 (14*10-1)
|
||||
my $to2 = int($to / 10) * 10 - 1;
|
||||
push(@groups, [$from, $to2]);
|
||||
$from = $to2 + 1;
|
||||
}
|
||||
# up to the next one
|
||||
if ($from <= $to) {
|
||||
push(@groups, [$from, $to]);
|
||||
$from = $to + 1;
|
||||
}
|
||||
while ($from < $to) {
|
||||
my $to = int($from/100) < int($to/100) ? $from + 99 - $from % 100 : $to;
|
||||
if ($from % 10) {
|
||||
push(@groups, [$from, $from + 9 - $from % 10]);
|
||||
$from += 10 - $from % 10;
|
||||
}
|
||||
if (int($from/10) < int($to/10)) {
|
||||
if ($to % 10 == 9) {
|
||||
push(@groups, [$from, $to]);
|
||||
$from = 1 + $to;
|
||||
} else {
|
||||
push(@groups, [$from, $to - 1 - $to % 10]);
|
||||
$from = $to - $to % 10;
|
||||
}
|
||||
} else {
|
||||
push(@groups, [$from - $from % 10, $to]);
|
||||
last;
|
||||
}
|
||||
if ($to % 10 != 9) {
|
||||
push(@groups, [$from, $to]);
|
||||
$from = 1 + $to; # jump from 99 to 100
|
||||
}
|
||||
}
|
||||
# warn join("; ", map { "@$_" } @groups);
|
||||
return \@groups;
|
||||
}
|
||||
|
||||
@@ -235,24 +265,42 @@ sub get_regexp_ip {
|
||||
my $regexp = "^";
|
||||
for my $i (0 .. 3) {
|
||||
if ($start[$i] eq $end[$i]) {
|
||||
# if the byte is the same, use it as is
|
||||
$regexp .= $start[$i];
|
||||
} elsif ($start[$i] eq '0' and $end[$i] eq '255') {
|
||||
$regexp .= '\.' if $i < 3;
|
||||
} elsif ($start[$i] == 0 and $end[$i] == 255) {
|
||||
# the starting byte is 0 and the end byte is 255, then anything goes:
|
||||
# we're done, e.g. 185.244.214.0 - 185.244.214.255 results in 185\.244\.214\.
|
||||
last;
|
||||
} elsif ($start[$i + 1] > 0) {
|
||||
$regexp .= '(' . $start[$i] . '\.('
|
||||
. get_regexp_range($start[$i + 1], '255') . ')|'
|
||||
. get_regexp_range($start[$i] + 1, $end[$i + 1]) . ')';
|
||||
$regexp .= '\.';
|
||||
} elsif ($i == 3 and $start[$i] != $end[$i]) {
|
||||
# example 45.87.2.128 - 45.87.2.255: the last bytes differ
|
||||
$regexp .= '(' . get_regexp_range($start[$i], $end[$i]) . ')';
|
||||
last;
|
||||
} elsif ($start[$i + 1] == 0 and $end[$i + 1] == 255) {
|
||||
# if we're here, we already know that the start byte and the end byte are
|
||||
# not the same; if the next bytes are from 0 to 255, we know that
|
||||
# everything else doesn't matter, e.g. 42.118.48.0 - 42.118.63.255
|
||||
$regexp .= '(' . get_regexp_range($start[$i], $end[$i]) . ')';
|
||||
$regexp .= '\.' if $i < 3;
|
||||
last;
|
||||
} elsif ($end[$i] - $start[$i] == 1 and $start[$i + 1] > 0 and $end[$i + 1] < 255) {
|
||||
# if we're here, we already know that the start byte and the end byte are
|
||||
# not the same; if the starting byte of the next (!) byte is bigger than
|
||||
# zero, then we need groups: in the case 77.56.180.0 - 77.57.70.255 for
|
||||
# example,
|
||||
$regexp .= '(' . $start[$i] . '\.(' . get_regexp_range($start[$i + 1], 255) . ')|'
|
||||
. $end[$i] . '\.(' . get_regexp_range(0, $end[$i + 1]) . ')';
|
||||
$regexp .= '\.' if $i < 3;
|
||||
last;
|
||||
} else {
|
||||
$regexp .= '(' . get_regexp_range($start[$i], $end[$i]) . ')$';
|
||||
warn "Unhandled regexp: $from - $to ($i)";
|
||||
$regexp .= 'XXX';
|
||||
$regexp .= '\.' if $i < 3;
|
||||
last;
|
||||
}
|
||||
$regexp .= '\.' if $i < 3;
|
||||
}
|
||||
return $regexp;
|
||||
}
|
||||
|
||||
# this is required in case we concatenate other modules to this one
|
||||
package OddMuse;
|
||||
|
||||
|
||||
@@ -64,13 +64,11 @@ sub RegexpNewBannedContent {
|
||||
my $str = shift;
|
||||
# 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*)?(.*))?$/;
|
||||
my ($regexp, $comment, $re) = ($1, $4, undef);
|
||||
eval { $re = qr/$regexp/i; };
|
||||
eval { $re = qr/($regexp)/i; };
|
||||
if (defined($re) && $str =~ $re) {
|
||||
my $group1 = $1;
|
||||
my $explanation = ($group1
|
||||
|
||||
66
modules/cook-lang.pl
Normal file
66
modules/cook-lang.pl
Normal file
@@ -0,0 +1,66 @@
|
||||
# Copyright (C) 2021 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 strict;
|
||||
use v5.10;
|
||||
|
||||
AddModuleDescription('cook-lang.pl', 'Cooklang Extension');
|
||||
|
||||
our ($q, $bol, @MyRules);
|
||||
|
||||
push(@MyRules, \&CookLangRule);
|
||||
|
||||
sub CookLangRule {
|
||||
if (/\G#([^\n#\@\{\}]+)\{(?:([^\n%\}]+)(?:%([^\n\}]+))?)?\}/cg) {
|
||||
# #canning funnel{}
|
||||
my $html = "";
|
||||
$html .= $q->strong({-title=>"number"}, $2) if $2;
|
||||
$html .= " " if $2 and $3;
|
||||
$html .= $q->strong({-title=>"unit"}, $3) if $3;
|
||||
$html .= " " if $1 and ($2 or $3);
|
||||
$html .= $q->strong({-title=>"cookware"}, $1);
|
||||
return $html;
|
||||
} elsif (/\G#(\w+)/cg) {
|
||||
# #pot
|
||||
return $q->strong({-title=>"cookware"}, $1);
|
||||
} elsif (/\G\@([^\n#\@\{\}]+)\{(?:([^\n%\}]+)(?:%([^\n\}]+))?)?\}/cg) {
|
||||
# @ground black pepper{}
|
||||
my $html = "";
|
||||
$html .= $q->strong({-title=>"number"}, $2) if $2;
|
||||
$html .= " " if $2 and $3;
|
||||
$html .= $q->strong({-title=>"unit"}, $3) if $3;
|
||||
$html .= " " if $1 and ($2 or $3);
|
||||
$html .= $q->strong({-title=>"ingredient"}, $1);
|
||||
return $html;
|
||||
} elsif (/\G\@(\w+)/cg) {
|
||||
# @salt
|
||||
return $q->strong({-title=>"ingredient"}, $1);
|
||||
} elsif (/\G\~\{([^\n%\}]+)(?:%([^\n\}]+))?\}/cg) {
|
||||
# ~{25%minutes}
|
||||
my $html = $q->strong({-title=>"number"}, $1);
|
||||
$html .= " " if $1 and $2;
|
||||
$html .= $q->strong({-title=>"unit"}, $2) if $2;
|
||||
return $html;
|
||||
} elsif (/\G\/\/\s*(.*)/cg) {
|
||||
# // Don't burn the roux!
|
||||
return $q->em({-title=>"comment"}, $1);
|
||||
} elsif ($bol and /\G>>\s*(.*)/cg) {
|
||||
# // Don't burn the roux!
|
||||
return CloseHtmlEnvironments()
|
||||
. $q->blockquote({-title=>"meta"}, $1)
|
||||
. AddHtmlEnvironment('p');
|
||||
}
|
||||
# no match
|
||||
return;
|
||||
}
|
||||
@@ -246,7 +246,8 @@ sub CreoleRule {
|
||||
$q->img({-src=> UnquoteHtml($1),
|
||||
-alt=> UnquoteHtml($3),
|
||||
-title=> UnquoteHtml($3),
|
||||
-class=> 'url outside'})));
|
||||
-class=> 'url outside',
|
||||
-loading=>'lazy'})));
|
||||
}
|
||||
# image link: [[link|{{pic}}]] and [[link|{{pic|text}}]]
|
||||
elsif (m/\G(\[\[$FreeLinkPattern$CreoleLinkPipePattern
|
||||
@@ -257,7 +258,8 @@ sub CreoleRule {
|
||||
$q->img({-src=> GetDownloadLink(FreeToNormal($3), 2),
|
||||
-alt=> UnquoteHtml($text),
|
||||
-title=> UnquoteHtml($text),
|
||||
-class=> 'upload'}), 'image')), $text);
|
||||
-class=> 'upload',
|
||||
-loading=>'lazy'}), 'image')), $text);
|
||||
}
|
||||
# image link: [[link|{{url}}]] and [[link|{{url|text}}]]
|
||||
elsif (m/\G(\[\[$FreeLinkPattern$CreoleLinkPipePattern
|
||||
@@ -268,7 +270,8 @@ sub CreoleRule {
|
||||
$q->img({-src=> UnquoteHtml($3),
|
||||
-alt=> UnquoteHtml($text),
|
||||
-title=> UnquoteHtml($text),
|
||||
-class=> 'url outside'}), 'image')), $text);
|
||||
-class=> 'url outside',
|
||||
-loading=>'lazy'}), 'image')), $text);
|
||||
}
|
||||
# image link: [[url|{{pic}}]] and [[url|{{pic|text}}]]
|
||||
elsif (m/\G(\[\[$FullUrlPattern$CreoleLinkPipePattern
|
||||
@@ -279,7 +282,8 @@ sub CreoleRule {
|
||||
$q->img({-src=> GetDownloadLink(FreeToNormal($3), 2),
|
||||
-alt=> UnquoteHtml($text),
|
||||
-title=> UnquoteHtml($text),
|
||||
-class=> 'upload'}))), $text);
|
||||
-class=> 'upload',
|
||||
-loading=>'lazy'}))), $text);
|
||||
}
|
||||
# image link: [[url|{{url}}]] and [[url|{{url|text}}]]
|
||||
elsif (m/\G\[\[$FullUrlPattern$CreoleLinkPipePattern
|
||||
@@ -289,7 +293,8 @@ sub CreoleRule {
|
||||
$q->img({-src=> UnquoteHtml($2),
|
||||
-alt=> UnquoteHtml($4),
|
||||
-title=> UnquoteHtml($4),
|
||||
-class=> 'url outside'})));
|
||||
-class=> 'url outside',
|
||||
-loading=>'lazy'})));
|
||||
}
|
||||
# link: [[url]] and [[url|text]]
|
||||
elsif (m/\G\[\[$FullUrlPattern$CreoleLinkTextPattern\]\]/cgs) {
|
||||
|
||||
@@ -43,7 +43,7 @@ sub DitaaRule {
|
||||
my $data = MIME::Base64::encode_base64($image);
|
||||
my $url = "data:image/png;base64,$data";
|
||||
return CloseHtmlEnvironments()
|
||||
. "<div$style>" . $q->img({-src=>$url, -alt=>$map}) . "</div>";
|
||||
. "<div$style>" . $q->img({-src=>$url, -alt=>$map, -loading=>'lazy'}) . "</div>";
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@ sub DuckDuckGoSearchInit {
|
||||
|
||||
sub DoDuckDuckGoSearch {
|
||||
my $search = UrlEncode(GetParam('search', undef));
|
||||
print $q->redirect({-uri=>"https://www.duckduckgo.com/?q=$search+site%3A$DuckDuckGoSearchDomain"});
|
||||
print $q->redirect({-uri=>"https://duckduckgo.com/?q=$search+site%3A$DuckDuckGoSearchDomain"});
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ sub EmojiRule {
|
||||
} elsif (/\G>:-?\(/cg) {
|
||||
# 😠 1F620 ANGRY FACE
|
||||
return '😠';
|
||||
} elsif (/\G:-?[Ppb]/cg) {
|
||||
} elsif (/\G:-?[Ppb]\b/cg) {
|
||||
# 😝 1F61D FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES
|
||||
return '😝';
|
||||
} elsif (/\G<3/cg) {
|
||||
|
||||
@@ -66,7 +66,7 @@ sub ImageSupportRule {
|
||||
$src = $ImageUrlPath . '/' . ImageUrlEncode($name);
|
||||
}
|
||||
if ($found) {
|
||||
$result = $q->img({-src=>$src, -alt=>$alt, -title=>$alt, -class=>'upload'});
|
||||
$result = $q->img({-src=>$src, -alt=>$alt, -title=>$alt, -class=>'upload', -loading=>'lazy'});
|
||||
$result = $q->a({-href=>$link, -class=>$linkclass}, $result);
|
||||
if ($comments) {
|
||||
for (split '\n', $comments) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004–2018 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004–2021 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
|
||||
@@ -18,7 +18,8 @@ use v5.10;
|
||||
|
||||
AddModuleDescription('journal-rss.pl', 'Journal RSS Extension');
|
||||
|
||||
our ($OpenPageName, $CollectingJournal, %Page, %Action, @MyInitVariables, $DeletedPage, %NearLinksException);
|
||||
our ($OpenPageName, $CollectingJournal, %Page, %Action, @MyInitVariables, $DeletedPage, %NearLinksException,
|
||||
$RecentLink, $SiteName, $SiteDescription, $ScriptName, $RssRights);
|
||||
$Action{journal} = \&DoJournalRss;
|
||||
|
||||
# Currently RSS works like RecentChanges, which is not what bloggers
|
||||
@@ -34,7 +35,15 @@ sub DoJournalRss {
|
||||
local *RcPreviousAction = \&JournalRssPreviousAction;
|
||||
local *RcLastAction = \&JournalRssLastAction;
|
||||
SetParam('full', 1);
|
||||
print GetHttpHeader('application/xml') . GetRcRss();
|
||||
if (GetParam('raw', 0)) {
|
||||
print GetHttpHeader('text/plain');
|
||||
print RcTextItem('title', $SiteName),
|
||||
RcTextItem('description', $SiteDescription), RcTextItem('link', $ScriptName),
|
||||
RcTextItem('generator', 'Oddmuse'), RcTextItem('rights', $RssRights);
|
||||
ProcessRcLines(sub {}, \&RcTextRevision);
|
||||
} else {
|
||||
print GetHttpHeader('application/xml') . GetRcRss();
|
||||
}
|
||||
}
|
||||
|
||||
sub JournalRssParameters {
|
||||
|
||||
@@ -42,10 +42,20 @@ AddModuleDescription('namespaces.pl', 'Namespaces Extension');
|
||||
|
||||
use File::Glob ':glob';
|
||||
|
||||
our ($q, %Action, %Page, @IndexList, $Now, %InterSite, $SiteName, $ScriptName, $UsePathInfo, $DataDir, $HomePage, @MyInitVariables, @MyAdminCode, $FullUrl, $LinkPattern, $InterSitePattern, $FreeLinks, $FreeLinkPattern, $InterLinkPattern, $FreeInterLinkPattern, $UrlProtocols, $WikiLinks, $FS, $RcFile, $RcOldFile, $RcDefault, $PageDir, $KeepDir, $LockDir, $TempDir, $IndexFile, $VisitorFile, $NoEditFile, $WikiDescription, $LastUpdate, $StaticDir, $StaticUrl, $InterWikiMoniker, $RefererDir, $PermanentAnchorsFile);
|
||||
our ($q, %Action, %Page, @IndexList, $Now, %InterSite, $SiteName, $ScriptName,
|
||||
$UsePathInfo, $DataDir, $HomePage, @MyInitVariables, @MyAdminCode, $FullUrl,
|
||||
$LinkPattern, $InterSitePattern, $FreeLinks, $FreeLinkPattern,
|
||||
$InterLinkPattern, $FreeInterLinkPattern, $UrlProtocols, $WikiLinks, $FS,
|
||||
$BannedContent, $BannedHosts, $RcFile, $RcOldFile, $RcDefault, $PageDir,
|
||||
$KeepDir, $LockDir, $TempDir, $IndexFile, $VisitorFile, $NoEditFile,
|
||||
$WikiDescription, $LastUpdate, $StaticDir, $StaticUrl, $InterWikiMoniker,
|
||||
$RefererDir, $PermanentAnchorsFile, @IndexList, %IndexHash);
|
||||
|
||||
our ($NamespacesMain, $NamespacesSelf, $NamespaceCurrent,
|
||||
$NamespaceRoot, $NamespaceSlashing, @NamespaceParameters,
|
||||
%Namespaces);
|
||||
$NamespaceRoot, $NamespaceSlashing, @NamespaceParameters,
|
||||
%Namespaces, $NamespacesRootDataDir);
|
||||
|
||||
our ($OriginalSiteName, $OriginalInterWikiMoniker, $OriginalDataDir, $OriginalScriptName, $OriginalFullUrl, $OriginalStaticDir, $OriginalStaticUrl, $OriginalWikiDescription);
|
||||
|
||||
$NamespacesMain = 'Main'; # to get back to the main namespace
|
||||
$NamespacesSelf = 'Self'; # for your own namespace
|
||||
@@ -96,6 +106,23 @@ sub GetNamespace {
|
||||
}
|
||||
|
||||
sub NamespacesInitVariables {
|
||||
$OriginalSiteName //= $SiteName;
|
||||
$SiteName = $OriginalSiteName;
|
||||
$OriginalInterWikiMoniker //= $InterWikiMoniker;
|
||||
$InterWikiMoniker = $OriginalInterWikiMoniker;
|
||||
$OriginalDataDir //= $DataDir;
|
||||
$DataDir = $OriginalDataDir;
|
||||
$OriginalScriptName //= $ScriptName;
|
||||
$ScriptName = $OriginalScriptName;
|
||||
$OriginalFullUrl //= $FullUrl;
|
||||
$FullUrl = $OriginalFullUrl;
|
||||
$OriginalStaticDir //= $StaticDir;
|
||||
$StaticDir = $OriginalStaticDir;
|
||||
$OriginalStaticUrl //= $StaticUrl;
|
||||
$StaticUrl = $OriginalStaticUrl;
|
||||
$OriginalWikiDescription //= $WikiDescription;
|
||||
$WikiDescription = $OriginalWikiDescription;
|
||||
|
||||
%Namespaces = ();
|
||||
# Do this before changing the $DataDir and $ScriptName
|
||||
if ($UsePathInfo) {
|
||||
@@ -110,6 +137,7 @@ sub NamespacesInitVariables {
|
||||
}
|
||||
}
|
||||
$NamespaceRoot = $ScriptName; # $ScriptName may be changed below
|
||||
$NamespacesRootDataDir = $DataDir; # $DataDir may be chanegd below
|
||||
$NamespaceCurrent = '';
|
||||
my $ns = GetNamespace();
|
||||
if ($ns
|
||||
@@ -117,23 +145,27 @@ sub NamespacesInitVariables {
|
||||
and $ns ne $NamespacesSelf) {
|
||||
$NamespaceCurrent = $ns;
|
||||
# Change some stuff from the original InitVariables call:
|
||||
$SiteName .= ' ' . $NamespaceCurrent;
|
||||
$SiteName .= ' ' . NormalToFree($NamespaceCurrent);
|
||||
$InterWikiMoniker = $NamespaceCurrent;
|
||||
$DataDir .= '/' . $NamespaceCurrent;
|
||||
$PageDir = "$DataDir/page";
|
||||
$KeepDir = "$DataDir/keep";
|
||||
$RefererDir = "$DataDir/referer";
|
||||
$TempDir = "$DataDir/temp";
|
||||
$LockDir = "$TempDir/lock";
|
||||
$NoEditFile = "$DataDir/noedit";
|
||||
$RcFile = "$DataDir/rc.log";
|
||||
$RcOldFile = "$DataDir/oldrc.log";
|
||||
$IndexFile = "$DataDir/pageidx";
|
||||
$VisitorFile = "$DataDir/visitors.log";
|
||||
$PermanentAnchorsFile = "$DataDir/permanentanchors";
|
||||
# $ConfigFile -- shared
|
||||
# $ModuleDir -- shared
|
||||
# $NearDir -- shared
|
||||
}
|
||||
$PageDir = "$DataDir/page";
|
||||
$KeepDir = "$DataDir/keep";
|
||||
$RefererDir = "$DataDir/referer";
|
||||
$TempDir = "$DataDir/temp";
|
||||
$LockDir = "$TempDir/lock";
|
||||
$NoEditFile = "$DataDir/noedit";
|
||||
$RcFile = "$DataDir/rc.log";
|
||||
$RcOldFile = "$DataDir/oldrc.log";
|
||||
$IndexFile = "$DataDir/pageidx";
|
||||
$VisitorFile = "$DataDir/visitors.log";
|
||||
$PermanentAnchorsFile = "$DataDir/permanentanchors";
|
||||
# $ConfigFile -- shared
|
||||
# $ModuleDir -- shared
|
||||
# $NearDir -- shared
|
||||
if ($ns
|
||||
and $ns ne $NamespacesMain
|
||||
and $ns ne $NamespacesSelf) {
|
||||
$ScriptName .= '/' . UrlEncode($NamespaceCurrent);
|
||||
$FullUrl .= '/' . UrlEncode($NamespaceCurrent);
|
||||
$StaticDir .= '/' . $NamespaceCurrent; # from static-copy.pl
|
||||
@@ -162,6 +194,56 @@ sub NamespaceRequiredByParameter {
|
||||
}
|
||||
}
|
||||
|
||||
=head Spam fighting
|
||||
|
||||
We want to share C<BannedContent> and C<BannedHosts> between all the wiki
|
||||
namespaces. Therefore, we need to handle a number of cases:
|
||||
|
||||
C<UserIsBanned> uses C<GetPageContent($BannedHosts)> and C<BannedContent> uses
|
||||
C<GetPageContent($BannedContent)>, therefore C<GetPageContent> is going to get
|
||||
modified.
|
||||
|
||||
C<DoBanHosts> in F<ban-contributors.pl> uses C<DoPost($BannedContent)> and
|
||||
C<DoPost($BannedHosts)>, therefore C<DoPost> is going to get modified.
|
||||
|
||||
=cut
|
||||
|
||||
*OldNamespaceGetPageContent = \&GetPageContent;
|
||||
*GetPageContent = \&NewNamespaceGetPageContent;
|
||||
|
||||
sub NewNamespaceGetPageContent {
|
||||
my ($id) = @_;
|
||||
if ($NamespaceCurrent and ($id eq $BannedContent or $id eq $BannedHosts)) {
|
||||
local $PageDir = "$NamespacesRootDataDir/page";
|
||||
# we cannot use ReadFileOrDie because our $IndexHash{$id} does not reflect the existence of the root file
|
||||
my ($status, $data) = ReadFile(GetPageFile($id));
|
||||
return ParseData($data)->{text} if $status;
|
||||
return '';
|
||||
}
|
||||
return OldNamespaceGetPageContent(@_);
|
||||
}
|
||||
|
||||
*OldNamespaceDoPost = \&DoPost;
|
||||
*DoPost = \&NewNamespaceDoPost;
|
||||
|
||||
sub NewNamespaceDoPost {
|
||||
my ($id) = @_;
|
||||
if ($NamespaceCurrent and ($id eq $BannedContent or $id eq $BannedHosts)) {
|
||||
local $DataDir = $NamespacesRootDataDir;
|
||||
local $PageDir = "$DataDir/page";
|
||||
local $KeepDir = "$DataDir/keep";
|
||||
local $LockDir = "$TempDir/lock";
|
||||
local $NoEditFile = "$DataDir/noedit";
|
||||
local $RcFile = "$DataDir/rc.log";
|
||||
local $RcOldFile = "$DataDir/oldrc.log";
|
||||
local $IndexFile = "$DataDir/pageidx";
|
||||
@IndexList = %IndexHash = ();
|
||||
AllPagesList(); # reload from new pageidx
|
||||
return OldNamespaceDoPost(@_);
|
||||
}
|
||||
return OldNamespaceDoPost(@_);
|
||||
}
|
||||
|
||||
=head2 RecentChanges
|
||||
|
||||
RecentChanges in the main namespace will list changes to all the
|
||||
|
||||
@@ -42,7 +42,7 @@ sub PortraitSupportRule {
|
||||
$PortraitSupportColorDiv = 0;
|
||||
return $html;
|
||||
} elsif ($bol && m/\Gportrait:$UrlPattern/cg) {
|
||||
return $q->img({-src=>$1, -alt=>T("Portrait"), -class=>'portrait'});
|
||||
return $q->img({-src=>$1, -alt=>T("Portrait"), -class=>'portrait', -loading=>'lazy'});
|
||||
} elsif ($bol && m/\G(:*)\[new(.*)\]/cg) {
|
||||
my $portrait = '';
|
||||
my $depth = length($1);
|
||||
|
||||
@@ -100,7 +100,7 @@ sub StaticGetDownloadLink {
|
||||
return '[' . ($image ? 'image' : 'link') . ':' . $name . ']' unless $IndexHash{$id};
|
||||
if ($image) {
|
||||
return StaticFileName($id) if $image == 2;
|
||||
my $result = $q->img({-src=>StaticFileName($id), -alt=>$alt, -class=>'upload'});
|
||||
my $result = $q->img({-src=>StaticFileName($id), -alt=>$alt, -class=>'upload', -loading=>'lazy'});
|
||||
$result = ScriptLink($id, $result, 'image');
|
||||
return $result;
|
||||
} else {
|
||||
@@ -198,7 +198,7 @@ EOT
|
||||
my $logo = $LogoUrl;
|
||||
$logo =~ s|.*/||; # just the filename
|
||||
my $alt = T('[Home]');
|
||||
$header .= $q->img({-src=>$logo, -alt=>$alt, -class=>'logo'}) if $logo;
|
||||
$header .= $q->img({-src=>$logo, -alt=>$alt, -class=>'logo', -loading=>'lazy'}) if $logo;
|
||||
}
|
||||
# top toolbar
|
||||
local $UserGotoBar = ''; # only allow @UserGotoBarPages
|
||||
@@ -317,7 +317,7 @@ sub GetDownloadLink {
|
||||
$action = $ScriptName . '?' . $action;
|
||||
}
|
||||
return $action if $image == 2;
|
||||
my $result = $q->img({-src=>$action, -alt=>$alt, -class=>'upload'});
|
||||
my $result = $q->img({-src=>$action, -alt=>$alt, -class=>'upload', -loading=>'lazy'});
|
||||
$result = ScriptLink(UrlEncode($id), $result, 'image') unless $id eq $OpenPageName;
|
||||
return $result;
|
||||
} else {
|
||||
|
||||
@@ -95,7 +95,7 @@ sub StaticGetDownloadLink {
|
||||
# if the page does not exist
|
||||
return '[' . ($image ? 'image' : 'link') . ':' . $name . ']' unless $IndexHash{$id};
|
||||
if ($image) {
|
||||
my $result = $q->img({-src=>StaticFileName($id), -alt=>$alt, -class=>'upload'});
|
||||
my $result = $q->img({-src=>StaticFileName($id), -alt=>$alt, -class=>'upload', -loading=>'lazy'});
|
||||
$result = ScriptLink($id, $result, 'image');
|
||||
return $result;
|
||||
} else {
|
||||
@@ -271,7 +271,7 @@ sub GetDownloadLink {
|
||||
} else {
|
||||
$action = $ScriptName . '?' . $action;
|
||||
}
|
||||
my $result = $q->img({-src=>$action, -alt=>$alt, -class=>'upload'});
|
||||
my $result = $q->img({-src=>$action, -alt=>$alt, -class=>'upload', -loading=>'lazy'});
|
||||
$result = ScriptLink(UrlEncode($id), $result, 'image') unless $id eq $OpenPageName;
|
||||
return $result;
|
||||
} else {
|
||||
|
||||
@@ -123,7 +123,8 @@ sub TagsRule {
|
||||
-title=>T('Feed for this tag'),
|
||||
-rel=>'feed'
|
||||
}, $q->img({-src=>$TagFeedIcon,
|
||||
-alt=>T('RSS')}));
|
||||
-alt=>T('RSS'),
|
||||
-loading=>'lazy'}));
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ Le nom d’utilisateur ne doit pas dépasser 50 caractères : non sauvegardé
|
||||
This page contains an uploaded file:
|
||||
Cette page contient un fichier téléversé :
|
||||
No summary was provided for this file.
|
||||
|
||||
Aucun résumé fourni pour ce fichier.
|
||||
Recursive include of %s!
|
||||
Inclusion par récursivité de %s !
|
||||
Clear Cache
|
||||
@@ -95,13 +95,13 @@ Page non valide %s (ne doit pas se terminer par .lck)
|
||||
Invalid Page %s
|
||||
Page non valide %s
|
||||
There are no comments, yet. Be the first to leave a comment!
|
||||
|
||||
Pas encore de commentaires. Soyez le premier à laisser un commentaire !
|
||||
Welcome!
|
||||
|
||||
Bienvenue !
|
||||
This page does not exist, but you can %s.
|
||||
|
||||
Cette page n’existe pas, mais vous pouvez %s.
|
||||
create it now
|
||||
|
||||
la créer maintenant
|
||||
Too many redirections
|
||||
Trop de redirections
|
||||
No redirection for old revisions
|
||||
@@ -113,7 +113,7 @@ SVP allez à %s.
|
||||
Updates since %s
|
||||
Mises à jour depuis %s
|
||||
up to %s
|
||||
jusqu’à
|
||||
jusqu’à %s
|
||||
Updates in the last %s days
|
||||
Mises à jour durant les derniers %s jours
|
||||
Updates in the last day
|
||||
@@ -133,7 +133,7 @@ Lister seulement les modifications majeures
|
||||
Include minor changes
|
||||
Inclure les modifications mineures
|
||||
days
|
||||
|
||||
jours
|
||||
List later changes
|
||||
Lister les modifications plus récentes
|
||||
RSS
|
||||
@@ -263,9 +263,9 @@ par %s
|
||||
(diff)
|
||||
(diff)
|
||||
a
|
||||
|
||||
a
|
||||
c
|
||||
c
|
||||
|
||||
Edit revision %s of this page
|
||||
Modifier la version %s de cette page
|
||||
e
|
||||
@@ -409,13 +409,13 @@ Vous êtes actuellement éditeur de ce site.
|
||||
You are a normal user on this site.
|
||||
Vous êtes un utilisateur normal de ce site.
|
||||
You do not have a password set.
|
||||
|
||||
Vous n’avez pas défini de mot de passe.
|
||||
Your password does not match any of the administrator or editor passwords.
|
||||
Vote mot de passe ne correspond ni au mot de passe administrateur ni au mot de passe éditeur.
|
||||
Password:
|
||||
Mot de passe :
|
||||
Return to %s
|
||||
|
||||
Retour à %s
|
||||
This operation is restricted to site editors only...
|
||||
Cette opération est réservée aux éditeurs du site seulement...
|
||||
This operation is restricted to administrators only...
|
||||
@@ -445,7 +445,7 @@ Raison inconnue.
|
||||
%s pages found.
|
||||
%s pages trouvées.
|
||||
Preview: %s
|
||||
|
||||
Prévisualiser: %s
|
||||
Replaced: %s
|
||||
Remplacé(e) : %s
|
||||
Search for: %s
|
||||
@@ -544,7 +544,7 @@ Deleted %s
|
||||
Renaming %1 to %2.
|
||||
Renomme %1 en %2.
|
||||
The page %s does not exist
|
||||
La page %s n'existe pas
|
||||
La page %s n’existe pas
|
||||
The page %s already exists
|
||||
La page %s existe déjà
|
||||
Cannot rename %1 to %2
|
||||
@@ -563,9 +563,9 @@ Renommer %s en :
|
||||
# modules/advanced-uploads.pl
|
||||
################################################################################
|
||||
Attach file:
|
||||
|
||||
Joindre un fichier:
|
||||
Upload
|
||||
|
||||
Uploader
|
||||
################################################################################
|
||||
# modules/aggregate.pl
|
||||
################################################################################
|
||||
@@ -609,7 +609,7 @@ Ban Contributors to %s
|
||||
Ban!
|
||||
|
||||
Regular expression:
|
||||
|
||||
Expression régulière :
|
||||
%s is banned
|
||||
|
||||
These URLs were rolled back. Perhaps you want to add a regular expression to %s?
|
||||
@@ -620,9 +620,9 @@ Consider banning the IP number as well:
|
||||
# modules/banned-regexps.pl
|
||||
################################################################################
|
||||
Regular expression "%1" matched "%2" on this page.
|
||||
|
||||
Expression régulière "%1" correspond à "%2" sur cette page.
|
||||
Regular expression "%s" matched on this page.
|
||||
|
||||
Expression régulière "%s" correspond à cette page.
|
||||
################################################################################
|
||||
# modules/big-brother.pl
|
||||
################################################################################
|
||||
@@ -724,7 +724,7 @@ Une expression régulière manque au tag de compilation.
|
||||
# modules/creationdate.pl
|
||||
################################################################################
|
||||
Add creation date to page files
|
||||
|
||||
Ajouter une date de création aux fichiers des pages
|
||||
################################################################################
|
||||
# modules/css-install.pl
|
||||
################################################################################
|
||||
@@ -733,7 +733,7 @@ Installer CSS
|
||||
Copy one of the following stylesheets to %s:
|
||||
Copier une des feuilles de style suivantes sur %s.
|
||||
Reset
|
||||
|
||||
Réinitialiser
|
||||
################################################################################
|
||||
# modules/dates.pl
|
||||
################################################################################
|
||||
@@ -764,9 +764,9 @@ Impossible de trouver une version sans texte indésirable.
|
||||
# modules/diff.pl
|
||||
################################################################################
|
||||
Page diff
|
||||
|
||||
Page diff
|
||||
Diff
|
||||
Diff
|
||||
|
||||
################################################################################
|
||||
# modules/drafts.pl
|
||||
################################################################################
|
||||
@@ -806,11 +806,11 @@ modifications ordinaires
|
||||
# modules/edit-paragraphs.pl
|
||||
################################################################################
|
||||
Could not identify the paragraph you were editing
|
||||
|
||||
Impossible d'identifier le paragraphe que vous avez édité
|
||||
This is the section you edited:
|
||||
|
||||
C’est la section que vous avez éditée :
|
||||
This is the current page:
|
||||
|
||||
C’est la page actuelle
|
||||
################################################################################
|
||||
# modules/find.pl
|
||||
################################################################################
|
||||
@@ -820,53 +820,53 @@ Pages correspondant aux noms :
|
||||
# modules/fix-encoding.pl
|
||||
################################################################################
|
||||
Fix character encoding
|
||||
|
||||
Corriger l’encodage des caractères
|
||||
Fix HTML escapes
|
||||
|
||||
Corriger les caractères d’échappement HTML
|
||||
################################################################################
|
||||
# modules/form_timeout.pl
|
||||
################################################################################
|
||||
Set $FormTimeoutSalt.
|
||||
|
||||
Définir $FormTimeoutSalt.
|
||||
Form Timeout
|
||||
|
||||
################################################################################
|
||||
# modules/gd_security_image.pl
|
||||
################################################################################
|
||||
GD or Image::Magick modules not available.
|
||||
|
||||
modules GD ou Image::Magick non disponibles.
|
||||
GD::SecurityImage module not available.
|
||||
|
||||
module GD::SecurityImage non disponible.
|
||||
Image storing failed. (%s)
|
||||
|
||||
Erreur d’enregistrement de l’image. (%s)
|
||||
Bad gd_security_image_id.
|
||||
|
||||
Invalide gd_security_image_id.
|
||||
Please type the six characters from the anti-spam image
|
||||
|
||||
Entrez les six caractères de l’image anti-spam
|
||||
Submit
|
||||
|
||||
Soumettre
|
||||
CAPTCHA
|
||||
CAPTCHA
|
||||
|
||||
You did not answer correctly.
|
||||
Vous n’avez pas répondu correctement.
|
||||
$GdSecurityImageFont is not set.
|
||||
|
||||
$GdSecurityImageFont n’est pas défini.
|
||||
################################################################################
|
||||
# modules/git-another.pl
|
||||
################################################################################
|
||||
No summary provided
|
||||
|
||||
Aucun résumé fourni
|
||||
################################################################################
|
||||
# modules/git.pl
|
||||
################################################################################
|
||||
no summary available
|
||||
|
||||
aucun résumé disponible
|
||||
page was marked for deletion
|
||||
|
||||
page marquée pour suppression
|
||||
Oddmuse
|
||||
Oddmuse
|
||||
|
||||
Cleaning up git repository
|
||||
|
||||
Nettoyage du dépôt git
|
||||
################################################################################
|
||||
# modules/google-plus-one.pl
|
||||
################################################################################
|
||||
@@ -880,7 +880,7 @@ This page lists the twenty last diary entries and their +1 buttons.
|
||||
# modules/gravatar.pl
|
||||
################################################################################
|
||||
Email:
|
||||
|
||||
Courriel :
|
||||
################################################################################
|
||||
# modules/header-and-footer-templates.pl
|
||||
################################################################################
|
||||
@@ -902,53 +902,53 @@ Index
|
||||
# modules/joiner.pl
|
||||
################################################################################
|
||||
The username %s already exists.
|
||||
|
||||
Le nom d’utilisateur %s existe déjà.
|
||||
The email address %s has already been used.
|
||||
|
||||
L’adresse courriel %s a déjà été utilisée.
|
||||
Wait %s minutes before try again.
|
||||
|
||||
Attendez %s minutes avant de réessayer.
|
||||
Registration Confirmation
|
||||
|
||||
Confirmation de l’enregistrement
|
||||
Visit the link below to confirm registration.
|
||||
|
||||
Visitez le lien ci-dessous pour confirmer l’enregistrement.
|
||||
Recover Account
|
||||
|
||||
Restaurer le compte
|
||||
You can login by following the link below. Then set new password.
|
||||
|
||||
Change Email Address
|
||||
|
||||
Changer l’adresse courriel
|
||||
To confirm changing email address, follow the link below.
|
||||
|
||||
To submit this form you must answer this question:
|
||||
|
||||
Question:
|
||||
|
||||
Question:
|
||||
CAPTCHA:
|
||||
CAPTCHA:
|
||||
|
||||
Registration
|
||||
|
||||
Enregistrement
|
||||
The username must be valid page name.
|
||||
|
||||
Le nom d’utilisateur doit être un nom de page valide.
|
||||
Confirmation email will be sent to the email address.
|
||||
|
||||
Un courriel de confirmation sera envoyé à l’adresse courriel.
|
||||
Repeat Password:
|
||||
|
||||
Répétez le mot de passse :
|
||||
Bad email address format.
|
||||
|
||||
Format d’adresse courriel invalide.
|
||||
Password needs to have at least %s characters.
|
||||
|
||||
Le mot de passe doit avoir au moins %s caractères.
|
||||
Passwords differ.
|
||||
|
||||
Mots de passe différents.
|
||||
Email Sent
|
||||
|
||||
Courriel envoyé
|
||||
Confirmation email has been sent to %s. Visit the link on the mail to confirm registration.
|
||||
|
||||
Courriel de confirmation envoyé à %s. Visitez le lien du courriel de confirmation d'enregistrement.
|
||||
Failed to Confirm Registration
|
||||
|
||||
Echec de confirmation d'enregistrement.
|
||||
Invalid key.
|
||||
|
||||
Clé non valide.
|
||||
The key expired.
|
||||
|
||||
Clé expirée.
|
||||
Registration Confirmed
|
||||
|
||||
Now, you can login by using username and password.
|
||||
@@ -956,65 +956,65 @@ Now, you can login by using username and password.
|
||||
Forgot your password?
|
||||
Mot de passe oublié ?
|
||||
Login failed.
|
||||
|
||||
Connexion échouée.
|
||||
You are banned.
|
||||
|
||||
You must confirm email address.
|
||||
|
||||
Vous devez confirmer l'adresse courriel.
|
||||
Logged in
|
||||
|
||||
Connecté
|
||||
%s has logged in.
|
||||
|
||||
%s est connecté
|
||||
You should set new password immediately.
|
||||
|
||||
Vous devriez définir un nouveau mot de passe immédiatement.
|
||||
Change Password
|
||||
|
||||
Changer le mot de passe
|
||||
Logged out
|
||||
|
||||
Déconnecté
|
||||
%s has logged out.
|
||||
|
||||
%s s’est déconnecté
|
||||
Account Settings
|
||||
|
||||
Paramètres de compte
|
||||
Logout
|
||||
Se déconnecter
|
||||
Current Password:
|
||||
|
||||
Mot de passe actuel:
|
||||
New Password:
|
||||
|
||||
Nouveau mot de passe:
|
||||
Repeat New Password:
|
||||
|
||||
Répétez le mot de passe:
|
||||
Password is wrong.
|
||||
|
||||
Mot de passe incorrect.
|
||||
Password Changed
|
||||
|
||||
Mot de passe modifié
|
||||
Your password has been changed.
|
||||
Votre mot de passe a été modifié.
|
||||
Forgot Password
|
||||
|
||||
Mot de passe oublié
|
||||
Enter email address, and recovery login ticket will be sent.
|
||||
|
||||
Entrez une adresse courriel, un ticket de récupération de connexion sera envoyé.
|
||||
Not found.
|
||||
|
||||
Non trouvé.
|
||||
The mail address is not valid anymore.
|
||||
|
||||
L’adresse courriel n’est plus valide.
|
||||
An email has been sent to %s with further instructions.
|
||||
|
||||
Un courriel a été envoyé à %s avec les instructions complémentaires.
|
||||
New Email Address:
|
||||
|
||||
Nouvelle adresse courriel:
|
||||
Failed to load account.
|
||||
|
||||
Echec du chargement du compte.
|
||||
An email has been sent to %s with a login ticket.
|
||||
|
||||
Un courriel a été envoyé à %s avec un ticket de connexion.
|
||||
Confirmation Failed
|
||||
|
||||
Echec de confirmation
|
||||
Failed to confirm.
|
||||
|
||||
Echec de confirmation
|
||||
Email Address Changed
|
||||
|
||||
Adresse courriel modifiée
|
||||
Email address for %1 has been changed to %2.
|
||||
|
||||
Adresse courriel pour %1 modifiée en %2.
|
||||
Account Management
|
||||
|
||||
Gestion des comptes
|
||||
Ban Account
|
||||
|
||||
Enter username of the account to ban:
|
||||
@@ -1034,7 +1034,7 @@ Unban
|
||||
%s has been unbanned.
|
||||
|
||||
Register
|
||||
|
||||
Enregistrement
|
||||
################################################################################
|
||||
# modules/lang.pl
|
||||
################################################################################
|
||||
@@ -1052,7 +1052,7 @@ Voir !
|
||||
====1 person liked this====
|
||||
|
||||
I like this!
|
||||
|
||||
J’aime!
|
||||
################################################################################
|
||||
# modules/link-all.pl
|
||||
################################################################################
|
||||
@@ -1091,9 +1091,9 @@ Le modèle %s est soit vide soit n'existe pas.
|
||||
# modules/localnames.pl
|
||||
################################################################################
|
||||
Name:
|
||||
|
||||
Nom :
|
||||
URL:
|
||||
URL:
|
||||
|
||||
Define Local Names
|
||||
|
||||
Define external redirect:
|
||||
@@ -1106,7 +1106,7 @@ Noms locaux définis sur %1 : %2
|
||||
# modules/logbannedcontent.pl
|
||||
################################################################################
|
||||
IP number matched %s
|
||||
|
||||
Numéro IP correspond à %s
|
||||
################################################################################
|
||||
# modules/login.pl
|
||||
################################################################################
|
||||
@@ -1209,8 +1209,8 @@ All mail subscriptions
|
||||
Tous les abonnements e-mail
|
||||
Subscriptions
|
||||
Abonnements
|
||||
Email:
|
||||
|
||||
Email:
|
||||
Courriel :
|
||||
Show
|
||||
Voir
|
||||
Subscriptions for %s:
|
||||
@@ -1247,30 +1247,30 @@ Help convert %s to Markdown
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Conversion de %s
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
Bisect modules
|
||||
|
||||
Modules Bisect
|
||||
Module Bisect
|
||||
Module Bisect
|
||||
|
||||
All modules enabled now!
|
||||
|
||||
Tous les modules activés maintenant !
|
||||
Go back
|
||||
|
||||
Retour
|
||||
Test / Always enabled / Always disabled
|
||||
|
||||
Start
|
||||
|
||||
Démarrer
|
||||
Bisecting proccess is already active.
|
||||
|
||||
Stop
|
||||
|
||||
Stop
|
||||
It seems like module %s is causing your problem.
|
||||
|
||||
Le module %s sembler causer votre problème.
|
||||
Please note that this module does not handle situations when your problem is caused by a combination of specific modules (which is rare anyway).
|
||||
|
||||
Good luck fixing your problem! ;)
|
||||
@@ -1278,13 +1278,13 @@ Good luck fixing your problem! ;)
|
||||
Module count (only testable modules):
|
||||
|
||||
Current module statuses:
|
||||
|
||||
Statuts du module courant :
|
||||
Good
|
||||
|
||||
Bon
|
||||
Bad
|
||||
|
||||
Mauvais
|
||||
Enabling %s
|
||||
|
||||
Activer %s
|
||||
################################################################################
|
||||
# modules/module-updater.pl
|
||||
################################################################################
|
||||
@@ -1357,9 +1357,9 @@ L'extension "404 handler" nécessite une base de données de liens (links.pl).
|
||||
# modules/offline.pl
|
||||
################################################################################
|
||||
Make available offline
|
||||
|
||||
Rendre disponible hors ligne
|
||||
Offline
|
||||
|
||||
Hors ligne
|
||||
You are currently offline and what you requested is not part of the offline application. You need to be online to do this.
|
||||
|
||||
################################################################################
|
||||
@@ -1424,25 +1424,25 @@ Portrait
|
||||
# modules/preview.pl
|
||||
################################################################################
|
||||
Pages with changed HTML
|
||||
|
||||
Pages avec HTML modifié
|
||||
Preview changes in HTML output
|
||||
|
||||
Visualiser les changenements de la sortie HTML
|
||||
################################################################################
|
||||
# modules/private-pages.pl
|
||||
################################################################################
|
||||
This page is password protected. If you know the password, you can %s. Once you have done that, return and reload this page.
|
||||
|
||||
supply the password now
|
||||
|
||||
donner le mot de passe maintenant
|
||||
################################################################################
|
||||
# modules/private-wiki.pl
|
||||
################################################################################
|
||||
This error should not happen. If your password is set correctly and you are still seeing this message, then it is a bug, please report it. If you are just a stranger and trying to get unsolicited access, then keep in mind that all of the data is encrypted with AES-256 and the key is not stored on the server, good luck.
|
||||
|
||||
Attempt to read encrypted data without a password.
|
||||
|
||||
Tentative de lire des données cryptés sans mot de passe.
|
||||
Cannot refresh index.
|
||||
|
||||
Impossible de mettre à jour l'index
|
||||
################################################################################
|
||||
# modules/publish.pl
|
||||
################################################################################
|
||||
@@ -1493,17 +1493,17 @@ Index de toutes les pages de petite taille
|
||||
# modules/sort.pl
|
||||
################################################################################
|
||||
Sort alphabetically
|
||||
|
||||
Trier alphabétiquement
|
||||
Sorted alphabetically
|
||||
|
||||
Trié alphabétiquement
|
||||
Sorted by last update first
|
||||
|
||||
Trié par dernière modification en premier
|
||||
Sort by last update
|
||||
|
||||
Trier par dernière modification
|
||||
Sorted by creation date
|
||||
|
||||
Trié par date de création
|
||||
Sort by creation date
|
||||
|
||||
Trier par date de création
|
||||
################################################################################
|
||||
# modules/static-copy.pl
|
||||
################################################################################
|
||||
@@ -1581,7 +1581,7 @@ Failed to run %1 to create thumbnail: %2
|
||||
%s ran into an error
|
||||
|
||||
%s produced no output
|
||||
|
||||
%s n’a produit aucun résultat
|
||||
Failed to parse %s.
|
||||
|
||||
################################################################################
|
||||
@@ -1615,13 +1615,13 @@ 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 quelle langue vous allez utiliser.
|
||||
Merci d’indiquer quelle langue vous allez utiliser.
|
||||
Language is missing
|
||||
La langue est manquante
|
||||
Suggested languages:
|
||||
Langues suggérées
|
||||
Please indicate a page name for the translation of %s.
|
||||
Indiquez s'il vous plaît un nom de page pour la traduction de %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.
|
||||
Translated page:
|
||||
@@ -1647,11 +1647,11 @@ Upgrading Database
|
||||
Did the previous upgrade end with an error? A lock was left behind.
|
||||
|
||||
Unlock wiki
|
||||
|
||||
Déverrouiller le wiki
|
||||
Upgrade complete.
|
||||
|
||||
Mise à jour terminée
|
||||
Upgrade complete. Please remove $ModuleDir/upgade.pl, now.
|
||||
|
||||
Mise à jour terminée. SVP, supprimez $ModuleDir/upgade.pl maintenant.
|
||||
################################################################################
|
||||
# modules/usemod.pl
|
||||
################################################################################
|
||||
|
||||
@@ -1201,6 +1201,8 @@ Your mail subscriptions
|
||||
|
||||
All mail subscriptions
|
||||
|
||||
All mail subscribers
|
||||
|
||||
Subscriptions
|
||||
|
||||
Email:
|
||||
@@ -1227,6 +1229,8 @@ The remaining pages do not exist.
|
||||
|
||||
Unsubscribed %s from the following pages:
|
||||
|
||||
Unsubscribed %s from all pages.
|
||||
|
||||
Migrating Subscriptions
|
||||
|
||||
No non-migrated email addresses found, migration not necessary.
|
||||
@@ -1714,6 +1718,8 @@ Tags:
|
||||
################################################################################
|
||||
# modules/webmention.pl
|
||||
################################################################################
|
||||
Webmention module requires $CommentsPrefix to be set
|
||||
|
||||
Webmention requires a POST request
|
||||
|
||||
Webmention requires x-www-form-urlencoded requests
|
||||
@@ -1740,5 +1746,25 @@ Webmention for %s already exists
|
||||
|
||||
Webmention OK!
|
||||
|
||||
Add webmentions
|
||||
|
||||
Webmentioning others from %s
|
||||
|
||||
Webmention!
|
||||
|
||||
No links found.
|
||||
|
||||
Webmentioning somebody from %s
|
||||
|
||||
Contacting %s
|
||||
|
||||
Target reports an error: %s
|
||||
|
||||
No Webmention URL found
|
||||
|
||||
Success: %s
|
||||
|
||||
Failure: %s
|
||||
|
||||
#
|
||||
END_OF_TRANSLATION
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# Zrajm C Akfohg <zrajm@klingonska.org>
|
||||
# Copyright (c) 2004-06 Johan Adler <alltid@nyfiken.org>
|
||||
# Copyright (c) 2004 Zrajm C Akfohg <zrajm@klingonska.org>
|
||||
# Copyright (c) 2021 Tobias Fendin
|
||||
#
|
||||
# Permission is granted to copy, distribute and/or modify this
|
||||
# document under the terms of the GNU Free Documentation License,
|
||||
@@ -300,6 +301,8 @@ Delete
|
||||
|
||||
Filter:
|
||||
|
||||
Summary:
|
||||
Sammanfattning:
|
||||
Last edit
|
||||
|
||||
revision %s
|
||||
@@ -314,8 +317,6 @@ later minor edits
|
||||
|
||||
No diff available.
|
||||
Information om ändring är inte tillgänglig.
|
||||
Summary:
|
||||
Sammanfattning:
|
||||
Old revision:
|
||||
Gammal version:
|
||||
Changed:
|
||||
@@ -699,11 +700,6 @@ Clustermap
|
||||
Klusterkarta
|
||||
Pages without a Cluster
|
||||
Sidor utan kluster
|
||||
################################################################################
|
||||
# modules/comment-div-wrapper.pl
|
||||
################################################################################
|
||||
Comments:
|
||||
|
||||
################################################################################
|
||||
# modules/commentcount.pl
|
||||
################################################################################
|
||||
@@ -711,6 +707,11 @@ Comments on
|
||||
Kommentarer till
|
||||
Comment on
|
||||
Kommentar till
|
||||
################################################################################
|
||||
# modules/comment-div-wrapper.pl
|
||||
################################################################################
|
||||
Comments:
|
||||
|
||||
################################################################################
|
||||
# modules/compilation.pl
|
||||
################################################################################
|
||||
@@ -1205,6 +1206,8 @@ Your mail subscriptions
|
||||
|
||||
All mail subscriptions
|
||||
|
||||
All mail subscribers
|
||||
|
||||
Subscriptions
|
||||
|
||||
Email:
|
||||
@@ -1231,6 +1234,8 @@ The remaining pages do not exist.
|
||||
|
||||
Unsubscribed %s from the following pages:
|
||||
|
||||
Unsubscribed %s from all pages.
|
||||
|
||||
Migrating Subscriptions
|
||||
|
||||
No non-migrated email addresses found, migration not necessary.
|
||||
@@ -1304,6 +1309,11 @@ You linked more than %s times to the same domain. It would seem that only a spam
|
||||
|
||||
Namespaces
|
||||
|
||||
################################################################################
|
||||
# modules/nearlink-create.pl
|
||||
################################################################################
|
||||
(create locally)
|
||||
|
||||
################################################################################
|
||||
# modules/near-links.pl
|
||||
################################################################################
|
||||
@@ -1323,11 +1333,6 @@ EditNearLinks
|
||||
Redigera närlänkar
|
||||
The same page on other sites:
|
||||
Samma sida på andra siter:
|
||||
################################################################################
|
||||
# modules/nearlink-create.pl
|
||||
################################################################################
|
||||
(create locally)
|
||||
|
||||
################################################################################
|
||||
# modules/no-question-mark.pl
|
||||
################################################################################
|
||||
@@ -1413,6 +1418,11 @@ Click to search for references to this permanent anchor
|
||||
Klicka för att söka efter referenser till det här permanenta ankaret
|
||||
Include permanent anchors
|
||||
Med permanenta ankare
|
||||
################################################################################
|
||||
# modules/pingback-server.pl
|
||||
################################################################################
|
||||
Only XML-RPC POST requests recognised
|
||||
|
||||
################################################################################
|
||||
# modules/portrait-support.pl
|
||||
################################################################################
|
||||
@@ -1477,6 +1487,17 @@ Sidor som länkat hit
|
||||
################################################################################
|
||||
All Referrers
|
||||
Alla som länkat hit
|
||||
################################################################################
|
||||
# modules/rename-pages.pl
|
||||
################################################################################
|
||||
Target page already exists.
|
||||
|
||||
Source page does not exist.
|
||||
|
||||
Copied from %s
|
||||
|
||||
Moved to %s
|
||||
|
||||
################################################################################
|
||||
# modules/search-list.pl
|
||||
################################################################################
|
||||
@@ -1486,22 +1507,22 @@ Page list for %s
|
||||
# modules/small.pl
|
||||
################################################################################
|
||||
Index of all small pages
|
||||
|
||||
Index av alla små sidor
|
||||
################################################################################
|
||||
# modules/sort.pl
|
||||
################################################################################
|
||||
Sort alphabetically
|
||||
|
||||
Sortera alfabetiskt
|
||||
Sorted alphabetically
|
||||
|
||||
Sorterad alfabetiskt
|
||||
Sorted by last update first
|
||||
|
||||
Sorterad med senast uppdaterad först
|
||||
Sort by last update
|
||||
|
||||
Sortera på uppdateringstid
|
||||
Sorted by creation date
|
||||
|
||||
Sorterad på skapande datum
|
||||
Sort by creation date
|
||||
|
||||
Sortera på skapande datum
|
||||
################################################################################
|
||||
# modules/static-copy.pl
|
||||
################################################################################
|
||||
@@ -1518,35 +1539,33 @@ Editing not allowed for %s.
|
||||
# modules/svg-edit.pl
|
||||
################################################################################
|
||||
Edit image in the browser
|
||||
|
||||
Redigera bilden i webbläsaren
|
||||
Summary of your changes:
|
||||
|
||||
Sammanställning av dina ändringar:
|
||||
################################################################################
|
||||
# modules/sync.pl
|
||||
################################################################################
|
||||
Copy to %1 succeeded: %2.
|
||||
|
||||
Kopiering till %1 lyckades: %2.
|
||||
Copy to %1 failed: %2.
|
||||
|
||||
Kopiering till %1 misslyckades: %2.
|
||||
################################################################################
|
||||
# modules/tags.pl
|
||||
################################################################################
|
||||
Tag
|
||||
|
||||
Tagg
|
||||
Feed for this tag
|
||||
|
||||
Flöde för denna tagg
|
||||
Tag Cloud
|
||||
|
||||
Taggmoln
|
||||
Rebuilding index not done.
|
||||
|
||||
Ombyggnad av index inte klart.
|
||||
(Rebuilding the index can only be done once every 12 hours.)
|
||||
|
||||
(Ombyggnad av indexet kan endast göras var 12:e timme.)
|
||||
Rebuild tag index
|
||||
|
||||
list tags
|
||||
|
||||
Bygg om tagg index
|
||||
tag cloud
|
||||
|
||||
taggmoln
|
||||
################################################################################
|
||||
# modules/templates.pl
|
||||
################################################################################
|
||||
@@ -1556,41 +1575,41 @@ Eller använd en av följande mallar:
|
||||
# modules/throttle.pl
|
||||
################################################################################
|
||||
Too many instances. Only %s allowed.
|
||||
|
||||
För många instanser. Endast %s tillåtna.
|
||||
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.
|
||||
|
||||
Vänligen försök senare. Kanske pågår ett underhåll eller så har någon gjort en lång sökning. Dessvärre har denna sida begränsade resurser, så vi ber dig ha tålamod.
|
||||
################################################################################
|
||||
# modules/thumbs.pl
|
||||
################################################################################
|
||||
thumb
|
||||
|
||||
Error creating thumbnail from nonexisting page %s.
|
||||
|
||||
Misslyckades med att skapa miniatyr av en icke existerande sida %s.
|
||||
Can not create thumbnail for file type %s.
|
||||
|
||||
Kan inte skapa miniatyr för filtypen %s.
|
||||
Can not create thumbnail for a text document
|
||||
|
||||
Kan inte skapa en miniatyr av ett textdokument
|
||||
Can not create path for thumbnail - %s
|
||||
|
||||
Kan inte skapa sökväg för miniatyr - %s
|
||||
Could not open %s for writing whilst trying to save image before creating thumbnail. Check write permissions.
|
||||
|
||||
Kan inte öppna %s för skrivning före skapande av miniatyrbilden. Kontrollera skrivrättigheter.
|
||||
Failed to run %1 to create thumbnail: %2
|
||||
|
||||
Kunde inte köra %1 för att skapa miniatyren: %2
|
||||
%s ran into an error
|
||||
|
||||
%s misslyckades
|
||||
%s produced no output
|
||||
|
||||
%s producerade ingen utdata
|
||||
Failed to parse %s.
|
||||
|
||||
Misslyckades med att tolka %s.
|
||||
################################################################################
|
||||
# modules/timezone.pl
|
||||
################################################################################
|
||||
Timezone
|
||||
|
||||
Tidszon
|
||||
Pick your timezone:
|
||||
|
||||
Välj din tidszon:
|
||||
Set
|
||||
|
||||
Sätt
|
||||
################################################################################
|
||||
# modules/toc-headers.pl
|
||||
################################################################################
|
||||
@@ -1600,7 +1619,7 @@ Innehåll
|
||||
# modules/today.pl
|
||||
################################################################################
|
||||
Create a new page for today
|
||||
|
||||
Skapa en ny sida för idag
|
||||
################################################################################
|
||||
# modules/translation-links.pl
|
||||
################################################################################
|
||||
@@ -1641,15 +1660,15 @@ Sidan finns inte.
|
||||
# modules/upgrade.pl
|
||||
################################################################################
|
||||
Upgrading Database
|
||||
|
||||
Uppgradering av databas
|
||||
Did the previous upgrade end with an error? A lock was left behind.
|
||||
|
||||
Misslyckades den senaste uppgraderingen med ett fel? Ett lås finns kvar.
|
||||
Unlock wiki
|
||||
|
||||
Lås upp wiki
|
||||
Upgrade complete.
|
||||
|
||||
Uppgradering klar.
|
||||
Upgrade complete. Please remove $ModuleDir/upgade.pl, now.
|
||||
|
||||
Uppgradering klar. Vänlingen ta bort $ModuleDir/upgade.pl nu.
|
||||
################################################################################
|
||||
# modules/usemod.pl
|
||||
################################################################################
|
||||
@@ -1676,7 +1695,7 @@ Wanted Pages
|
||||
# modules/webapp.pl
|
||||
################################################################################
|
||||
Web application for offline browsing
|
||||
|
||||
Webbapplikation för off-line visning
|
||||
################################################################################
|
||||
# modules/webdav.pl
|
||||
################################################################################
|
||||
@@ -1691,15 +1710,68 @@ Blogg
|
||||
# modules/weblog-3.pl
|
||||
################################################################################
|
||||
Matching pages:
|
||||
|
||||
Matchande sidor:
|
||||
New
|
||||
|
||||
Nytt
|
||||
Edit %s.
|
||||
|
||||
Redigera %s.
|
||||
################################################################################
|
||||
# modules/weblog-4.pl
|
||||
################################################################################
|
||||
Tags:
|
||||
Taggar:
|
||||
|
||||
################################################################################
|
||||
# modules/webmention.pl
|
||||
################################################################################
|
||||
Webmention module requires $CommentsPrefix to be set
|
||||
Webmention modulen kräver att $CommentsPrefix är satt
|
||||
Webmention requires a POST request
|
||||
Webmention kräver en POST förfrågan
|
||||
Webmention requires x-www-form-urlencoded requests
|
||||
Webmention kräver x-www-form-urlencoded förfrågan
|
||||
Webmention must mention a specific page
|
||||
Webmention måste ange en specifik sida
|
||||
Webmention must mention a valid page
|
||||
Webmention måste ange en giltig sida
|
||||
Your IP number is blocked: %s
|
||||
Ditt IP-nummer är blockerat: %s
|
||||
Webmention must mention an existing page
|
||||
Webmention måste ange en existerande sida
|
||||
Webmention must mention source
|
||||
Webmention måste ange en källa
|
||||
Webmention must mention target
|
||||
Webmention måste ange ett mål
|
||||
The URL is blocked: %s
|
||||
URL:en är blockerad: %s
|
||||
Webmention source cannot be verified: %1 returns %2 %3
|
||||
Webmention källan kan inte verifieras: %1 returnerar %2 %3
|
||||
Webmention source does not link to %s
|
||||
Webmention källan länkar inte till %s
|
||||
Webmention for %s already exists
|
||||
Webmention för %s finns redan
|
||||
Webmention OK!
|
||||
|
||||
Add webmentions
|
||||
Lägg till webmention
|
||||
# Could not find a good translation of "Webmentioning"
|
||||
Webmentioning others from %s
|
||||
Webmentioning andra från %s
|
||||
Webmention!
|
||||
|
||||
No links found.
|
||||
Inga länkar hittade
|
||||
Webmentioning somebody from %s
|
||||
Webmentioning någon från %s
|
||||
Contacting %s
|
||||
Kontakta %s
|
||||
Target reports an error: %s
|
||||
Målet rapporterar ett fel: %s
|
||||
No Webmention URL found
|
||||
Ingen Webmention URL hittad
|
||||
Success: %s
|
||||
Lyckat: %s
|
||||
Failure: %s
|
||||
Misslyckat: %s
|
||||
#
|
||||
END_OF_TRANSLATION
|
||||
|
||||
@@ -173,7 +173,7 @@ sub send_mail {
|
||||
Path => $fh,
|
||||
Type=> "text/html");
|
||||
if ($host) {
|
||||
print "Sending $title to $subscriber using ${user}\@${host}\n" if $verbose;
|
||||
print "$root\nSending $title to $subscriber using ${user}\@${host}\n" if $verbose;
|
||||
my $smtp = Net::SMTP->new($host, Debug => $debug);
|
||||
$smtp->starttls();
|
||||
# the following requires Authen::SASL!
|
||||
|
||||
91
scripts/stats.pl
Executable file
91
scripts/stats.pl
Executable file
@@ -0,0 +1,91 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2005, 2007, 2021 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 Modern::Perl;
|
||||
|
||||
sub ParseData {
|
||||
my $data = shift;
|
||||
my %result;
|
||||
while ($data =~ /(\S+?): (.*?)(?=\n[^ \t]|\Z)/sg) {
|
||||
my ($key, $value) = ($1, $2);
|
||||
$value =~ s/\n\t/\n/g;
|
||||
$result{$key} = $value;
|
||||
}
|
||||
return %result;
|
||||
}
|
||||
|
||||
sub main {
|
||||
my ($PageDir) = @_;
|
||||
my $pages = 0;
|
||||
my $texts = 0;
|
||||
my $redirects = 0;
|
||||
my $files = 0;
|
||||
my $big = 0;
|
||||
# include dotfiles!
|
||||
local $/ = undef; # Read complete files
|
||||
say "Reading files...";
|
||||
my @files = glob("$PageDir/*.pg $PageDir/.*.pg");
|
||||
my $n = @files;
|
||||
local $| = 1; # flush!
|
||||
foreach my $file (@files) {
|
||||
if (not --$n % 10) {
|
||||
printf("\r%06d files to go", $n);
|
||||
}
|
||||
next unless $file =~ m|.*/(.+)\.pg$|;
|
||||
my $page = $1;
|
||||
open(F, $file) or die "Cannot read $page file: $!";
|
||||
my $data = <F>;
|
||||
close(F);
|
||||
my %result = ParseData($data);
|
||||
$pages++;
|
||||
if ($result{text} =~ /^#FILE /) {
|
||||
$files++;
|
||||
} elsif ($result{text} =~ /^#REDIRECT /) {
|
||||
$redirects++;
|
||||
} else {
|
||||
$texts++;
|
||||
$big++ if length($result{text}) > 15000;
|
||||
}
|
||||
}
|
||||
printf("\r%06d files to go\n", 0);
|
||||
printf("Pages: %7d\n", $pages);
|
||||
printf("Files: %7d\n", $files);
|
||||
printf("Redirects: %6d\n", $redirects);
|
||||
printf("Texts: %7d\n", $texts);
|
||||
printf("Big: %7d\n", $big);
|
||||
}
|
||||
|
||||
use Getopt::Long;
|
||||
my $regexp = undef;
|
||||
my $page = 'page';
|
||||
my $help;
|
||||
GetOptions ("page=s" => \$page,
|
||||
"help" => \$help);
|
||||
|
||||
if ($help) {
|
||||
print qq{
|
||||
Usage: $0 [--page DIR]
|
||||
|
||||
Prints some stats about the pages in DIR.
|
||||
|
||||
--page designates the page directory. By default this is 'page' in the
|
||||
current directory. If you run this script in your data directory,
|
||||
the default should be fine.
|
||||
}
|
||||
} else {
|
||||
main ($page);
|
||||
}
|
||||
120
scripts/unsubscribe.pl
Normal file
120
scripts/unsubscribe.pl
Normal file
@@ -0,0 +1,120 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 2010–2021 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 NAME
|
||||
|
||||
unsubscribe.pl - mass unsubscribe from Oddmuse
|
||||
|
||||
=head2 SYNOPSIS
|
||||
|
||||
B<perl unsubscribe.pl> F<MAILDB> [B<--regexp=>I<REGEXP>]
|
||||
|
||||
B<perl unsubscribe.pl> F<MAILDB> [B<--dump>]
|
||||
|
||||
=head2 DESCRIPTION
|
||||
|
||||
If you use the Mail Extension to Oddmuse, you end up with subscriptions to very
|
||||
old pages. This script helps you unsubsribe people from old pages.
|
||||
|
||||
C<--regexp> indicates a regular expression matching pages names
|
||||
|
||||
The mandatory F<MAILDB> argument is the file containing all the mail
|
||||
subscriptions.
|
||||
|
||||
=head2 EXAMPLES
|
||||
|
||||
Make a copy, unsubscribe people, check a dump of the remaining subscriptions,
|
||||
and move the file back to the wiki data directory.
|
||||
|
||||
cp ~/alexschroeder/mail.db copy.db
|
||||
perl ~/src/oddmuse/scripts/unsubscribe.pl copy.db --regexp='20[01][0-9]'
|
||||
perl ~/src/oddmuse/scripts/unsubscribe.pl copy.db --dump
|
||||
mv copy.db ~/alexschroeder/mail.db
|
||||
|
||||
=cut;
|
||||
|
||||
use Modern::Perl;
|
||||
use Getopt::Long;
|
||||
use Encode qw(encode_utf8 decode_utf8);
|
||||
use DB_File;
|
||||
|
||||
binmode(STDOUT, ":utf8");
|
||||
|
||||
my $re = "";
|
||||
my $confirm;
|
||||
my $dump;
|
||||
|
||||
GetOptions ("regexp=s" => \$re,
|
||||
"dump" => \$dump,
|
||||
"confirm" => \$confirm, );
|
||||
|
||||
my $file = shift;
|
||||
|
||||
die "Not a file: $file" unless -f $file;
|
||||
die "Unknown arguments: @ARGV" if @ARGV;
|
||||
|
||||
sub UrlEncode {
|
||||
my $str = shift;
|
||||
return '' unless $str;
|
||||
my @letters = split(//, encode_utf8($str));
|
||||
my %safe = map {$_ => 1} ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.', '!', '~', '*', "'", '(', ')', '#');
|
||||
foreach my $letter (@letters) {
|
||||
$letter = sprintf("%%%02x", ord($letter)) unless $safe{$letter};
|
||||
}
|
||||
return join('', @letters);
|
||||
}
|
||||
|
||||
sub UrlDecode {
|
||||
my $str = shift;
|
||||
return '' unless $str;
|
||||
$str =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eig;
|
||||
return decode_utf8($str);
|
||||
}
|
||||
|
||||
tie my %h, "DB_File", $file;
|
||||
my $FS = "\x1e";
|
||||
|
||||
if ($dump) {
|
||||
for my $key (keys %h) {
|
||||
my @value = split /$FS/, UrlDecode($h{$key});
|
||||
say UrlDecode($key), ": @value";
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
for my $raw (keys %h) {
|
||||
if ($raw =~ /@/) {
|
||||
# email address
|
||||
my $mail = UrlDecode($raw);
|
||||
my $value = $h{$raw};
|
||||
my @subscriptions = grep !/$re/, map { UrlDecode($_) } split /$FS/, $value;
|
||||
if (@subscriptions) {
|
||||
$h{$raw} = join $FS, map { UrlEncode($_) } @subscriptions if $confirm;
|
||||
say "> $mail: remains subscribed to @subscriptions";
|
||||
} else {
|
||||
delete $h{$raw} if $confirm;
|
||||
say "> $mail: unsubscribe from all pages";
|
||||
}
|
||||
} else {
|
||||
my $id = UrlDecode($raw);
|
||||
next unless $id =~ /$re/;
|
||||
delete $h{$raw} if $confirm;
|
||||
say "Delete $id";
|
||||
}
|
||||
}
|
||||
|
||||
untie %h;
|
||||
|
||||
say "Use --confirm to actually do it" unless $confirm;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ EOT
|
||||
|
||||
my $min = version->parse(shift || "2.3.0");
|
||||
|
||||
my @tags = grep { /(\d+\.\d+\.\d+)/ and version->parse($1) >= $min }
|
||||
my @tags = grep { /(\d+\.\d+\.\d+)/ and version->parse($1) >= $min }
|
||||
split(/\n/, qx{git tag --list});
|
||||
|
||||
unless (@tags) {
|
||||
@@ -47,7 +47,7 @@ for my $tag (@tags) {
|
||||
next;
|
||||
}
|
||||
print "Preparing $tag\n";
|
||||
|
||||
|
||||
system("git", "checkout", $tag) == 0
|
||||
or die "Failed to git checkout $tag\n";
|
||||
system("make", "prepare") == 0
|
||||
@@ -62,5 +62,5 @@ for my $tag (@tags) {
|
||||
or die "Failed to remove the directory oddmuse-$tag\n";
|
||||
}
|
||||
|
||||
system("git", "checkout", "master") == 0
|
||||
or die "Failed to git checkout master\n";
|
||||
system("git", "checkout", "main") == 0
|
||||
or die "Failed to git checkout main\n";
|
||||
|
||||
@@ -15,13 +15,42 @@
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More;
|
||||
use Net::IP;
|
||||
|
||||
add_module('ban-contributors.pl');
|
||||
|
||||
# 0-255
|
||||
is(BanContributors::get_regexp_ip('185.244.214.0', '185.244.214.255'),
|
||||
'^185\.244\.214\.',
|
||||
'185.244.214.0 - 185.244.214.255');
|
||||
|
||||
# 48.0-63.255
|
||||
is(BanContributors::get_regexp_ip('42.118.48.0', '42.118.63.255'),
|
||||
'^42\.118\.(4[8-9]|5[0-9]|6[0-3])\.',
|
||||
'42.118.48.0 - 42.118.63.255');
|
||||
|
||||
# 192.0-223.255
|
||||
is(BanContributors::get_regexp_ip('118.71.192.0', '118.71.223.255'),
|
||||
'^118\.71\.(19[2-9]|2[0-1][0-9]|22[0-3])\.',
|
||||
'118.71.192.0 - 118.71.223.255');
|
||||
|
||||
# 56.180-57.70
|
||||
is(BanContributors::get_regexp_ip('77.56.180.0', '77.57.70.255'),
|
||||
'^77\.(56\.(1[8-9][0-9]|2[0-4][0-9]|25[0-5])|5[7-9]|6[0-9]|70)\.',
|
||||
'^77\.(56\.(1[8-9][0-9]|2[0-4][0-9]|25[0-5])|57\.([0-9]|[1-6][0-9]|70)\.',
|
||||
'77.56.180.0 - 77.57.70.255');
|
||||
|
||||
# 45.87.2.128 - 45.87.2.255
|
||||
is(BanContributors::get_regexp_ip('45.87.2.128', '45.87.2.255'),
|
||||
'^45\.87\.2\.(12[8-9]|1[3-9][0-9]|2[0-4][0-9]|25[0-5])',
|
||||
'45.87.2.128 - 45.87.2.255');
|
||||
|
||||
# 191.101.0.0/16
|
||||
# verify that Net::IP works as intended
|
||||
my $ip = Net::IP->new('191.101.0.0/16');
|
||||
ok($ip, 'Net::IP parsed CIDR');
|
||||
is($ip->ip, '191.101.0.0', 'First IP in range');
|
||||
is($ip->last_ip, '191.101.255.255', 'Last IP in range');
|
||||
|
||||
$localhost = '127.0.0.1';
|
||||
$ENV{'REMOTE_ADDR'} = $localhost;
|
||||
|
||||
@@ -57,10 +86,9 @@ test_page($page, 'Rolling back changes', 'These URLs were rolled back',
|
||||
'doxycycline');
|
||||
test_page_negative($page, 'amoxil');
|
||||
|
||||
# 127.0.0.1 has no inetnum
|
||||
test_page(get_page("action=ban id=Test"),
|
||||
'Ban Contributors to Test',
|
||||
quotemeta('127.0.0.1 () [ - ]'));
|
||||
quotemeta('127.0.0.1 () [127.0.0.0 - 127.255.255.255]'));
|
||||
|
||||
SKIP: {
|
||||
skip "Net::Whois::Parser doesn't always return the same result", 4;
|
||||
@@ -73,7 +101,7 @@ SKIP: {
|
||||
|
||||
test_page(get_page('action=ban id=Test regexp="^46\.101\.([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-7])" range="[46.101.0.0 - 46.101.127.255]" recent_edit=on pwd=foo'),
|
||||
'Location: http://localhost/wiki.pl/BannedHosts');
|
||||
|
||||
|
||||
test_page(get_page('BannedHosts'),
|
||||
quotemeta('^46\.101\.([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-7]) # '
|
||||
. CalcDay($Now)
|
||||
|
||||
@@ -31,15 +31,15 @@ add_module('calendar.pl');
|
||||
|
||||
test_page(update_page("with_cal", "zulu\n\ncalendar:2006\n\nwarrior\n"),
|
||||
'<p>zulu</p><p class="nav">',
|
||||
'</pre></div><p>warrior</p></div><div class="wrapper close"></div></div><div class="footer">');
|
||||
'</pre></div><p>warrior</p></div><div class="wrapper close"></div></div><footer>');
|
||||
|
||||
test_page(update_page("with_cal", "zulu\n\nmonth:2006-09\n\nwarrior\n"),
|
||||
'<p>zulu</p><div class="cal"><div class="month"><pre>',
|
||||
'</pre></div></div><p>warrior</p></div><div class="wrapper close"></div></div><div class="footer">');
|
||||
'</pre></div></div><p>warrior</p></div><div class="wrapper close"></div></div><footer>');
|
||||
|
||||
test_page(update_page("with_cal", "zulu\n\nmonth:+0\n\nwarrior\n"),
|
||||
'<p>zulu</p><div class="cal"><div class="month"><pre>',
|
||||
'</pre></div></div><p>warrior</p></div><div class="wrapper close"></div></div><div class="footer">');
|
||||
'</pre></div></div><p>warrior</p></div><div class="wrapper close"></div></div><footer>');
|
||||
|
||||
xpath_test(get_page('action=calendar'),
|
||||
# yearly navigation
|
||||
|
||||
@@ -21,4 +21,4 @@ AppendStringToFile($ConfigFile, "\$ConfigPage = 'Config';\n");
|
||||
|
||||
xpath_test(update_page('Config', '@UserGotoBarPages = ("Foo", "Bar");',
|
||||
'config', 0, 1),
|
||||
'//div[@class="header"]/div[@class="menu"]/span[@class="gotobar bar"]/a[@class="local"][text()="Foo"]/following-sibling::a[@class="local"][text()="Bar"]');
|
||||
'//header/nav/span[@class="gotobar bar"]/a[@class="local"][text()="Foo"]/following-sibling::a[@class="local"][text()="Bar"]');
|
||||
|
||||
@@ -51,4 +51,4 @@ test_page(get_page('search=alex'),
|
||||
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');
|
||||
'Location: https://duckduckgo.com/\?q=alex\+site%3Aemacswiki\.org');
|
||||
|
||||
@@ -23,18 +23,18 @@ test_page(update_page('2011-07-06', 'Hallo'),
|
||||
'Comments_on_2011-07-06');
|
||||
xpath_test(update_page('Hi', '<journal>'),
|
||||
'//h1/a[text()="2011-07-06"]',
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[text()="Comments on this page"]');
|
||||
'//div[@class="journal h-feed"]/article[@class="h-entry"]/p[@class="comment"]/a[text()="Comments on this page"]');
|
||||
|
||||
add_module('dynamic-comments.pl');
|
||||
|
||||
xpath_test(get_page('Hi'),
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[@href="http://localhost/wiki.pl/Comments_on_2011-07-06"][text()="Add Comment"]');
|
||||
'//div[@class="journal h-feed"]/article[@class="h-entry"]/p[@class="comment"]/a[@href="http://localhost/wiki.pl/Comments_on_2011-07-06"][text()="Add Comment"]');
|
||||
|
||||
test_page(update_page('Comments_on_2011-07-06', 'Yo'),
|
||||
'Yo');
|
||||
|
||||
xpath_test(get_page('Hi'),
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"][text()="Comments on 2011-07-06"]');
|
||||
'//div[@class="journal h-feed"]/article[@class="h-entry"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"][text()="Comments on 2011-07-06"]');
|
||||
|
||||
# encoding basics
|
||||
$page = update_page('2011-07-06_(…)_Dü', 'Hallo Dü');
|
||||
@@ -44,6 +44,6 @@ xpath_test($page, '//p[contains(text(), "Dü")]');
|
||||
update_page('Comments_on_2011-07-06_(…)_Dü', 'Yo');
|
||||
|
||||
xpath_test(update_page('Hi', '<journal>'),
|
||||
'//h1/a[text()="2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[text()="Comments on 2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"]');
|
||||
'//h1/a[text()="2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal h-feed"]/article[@class="h-entry"]/p[@class="comment"]/a[text()="Comments on 2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal h-feed"]/article[@class="h-entry"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"]');
|
||||
|
||||
2
t/edit.t
2
t/edit.t
@@ -21,5 +21,5 @@ xpath_test(get_page('action=edit id=NewPage'),
|
||||
'//textarea[@name="text"][@id="text"][not(boolean(text()))]',
|
||||
'//div[@class="wrapper"]/div[@class="content edit"]',
|
||||
'//div[@class="content edit"]/following-sibling::div[@class="wrapper close"]',
|
||||
'//div[@class="wrapper"]/following-sibling::div[@class="footer"]',
|
||||
'//div[@class="wrapper"]/following-sibling::footer',
|
||||
);
|
||||
|
||||
340
t/gemini-server.t
Normal file
340
t/gemini-server.t
Normal file
@@ -0,0 +1,340 @@
|
||||
# Copyright (C) 2017–2020 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;
|
||||
use strict;
|
||||
use 5.10.0;
|
||||
use Test::More;
|
||||
use IO::Socket::SSL;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
use Modern::Perl;
|
||||
use XML::RSS;
|
||||
use XML::LibXML;
|
||||
|
||||
require './t/test.pl';
|
||||
require './stuff/gemini-server.pl';
|
||||
|
||||
add_module('tags.pl');
|
||||
|
||||
# enable uploads and filtering by language
|
||||
our($ConfigFile);
|
||||
AppendStringToFile($ConfigFile, <<'EOT');
|
||||
$UploadAllowed = 1;
|
||||
%Languages = (
|
||||
'de' => '\b(der|die|das|und|oder)\b',
|
||||
'en' => '\b(i|he|she|it|we|they|this|that|a|is|was)\b', );
|
||||
EOT
|
||||
|
||||
# enable comments
|
||||
our($CommentsPrefix);
|
||||
$CommentsPrefix = 'Comments_on_';
|
||||
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments_on_';\n");
|
||||
AppendStringToFile($ConfigFile, "\@QuestionaskerQuestions = (['Who rules in Rivendell?' => sub { shift =~ /^Elrond/i }]);\n");
|
||||
|
||||
# write a gemini-only extension
|
||||
our($DataDir);
|
||||
WriteStringToFile("$DataDir/gemini_config", <<'EOT');
|
||||
package OddMuse;
|
||||
use Modern::Perl;
|
||||
our (@extensions, @main_menu_links);
|
||||
push(@extensions, \&serve_cert);
|
||||
sub serve_cert {
|
||||
my $self = shift;
|
||||
my $url = shift;
|
||||
my $selector = shift;
|
||||
my $base = $self->base();
|
||||
if ($selector =~ m!^do/test!) {
|
||||
say "20 text/plain\r";
|
||||
say "Test";
|
||||
return 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
1;
|
||||
EOT
|
||||
|
||||
my $host = "127.0.0.1";
|
||||
my $port = random_port();
|
||||
my $pid = fork();
|
||||
|
||||
END {
|
||||
# kill server
|
||||
if ($pid) {
|
||||
kill 'KILL', $pid or warn "Could not kill server $pid";
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $pid) {
|
||||
die "Cannot fork: $!";
|
||||
} elsif ($pid == 0) {
|
||||
use Config;
|
||||
my $secure_perl_path = $Config{perlpath};
|
||||
exec($secure_perl_path,
|
||||
"stuff/gemini-server.pl",
|
||||
"--host=$host",
|
||||
"--port=$port",
|
||||
"--wiki_cert_file=t/cert.pem",
|
||||
"--wiki_key_file=t/key.pem",
|
||||
"--log_level=0", # set to 4 for verbose logging
|
||||
"--wiki=./wiki.pl",
|
||||
"--wiki_dir=$DataDir",
|
||||
"--wiki_pages=Alex",
|
||||
"--wiki_pages=Berta",
|
||||
"--wiki_pages=Chris")
|
||||
or die "Cannot exec: $!";
|
||||
}
|
||||
|
||||
# Sorting
|
||||
is(sub{$a="Alex"; $b="Berta"; newest_first()}->(), -1, "Alex before Berta");
|
||||
is(sub{$a="Alex"; $b="Comments_on_Alex"; newest_first()}->(), -1, "Alex before Comments_on_Alex");
|
||||
is(sub{$a="Chris"; $b="Comments_on_Alex"; newest_first()}->(), 1, "Chris after Comments_on_A");
|
||||
is(sub{$a="Image_1_for_Alex"; $b="Image_10_for_Alex"; newest_first()}->(), -1, "Image_1_for_Alex before Image_10_for_Alex");
|
||||
is(sub{$a="Comments_on_Alex"; $b="Image_1_for_Alex"; newest_first()}->(), -1, "Comments_on_Alex before Image_1_for_Alex");
|
||||
is(join(" ", sort newest_first qw(Alex Berta Chris)), "Alex Berta Chris", "Sort alphabetically");
|
||||
is(join(" ", sort newest_first qw(2017-12-25 2017-12-26 2017-12-27)), "2017-12-27 2017-12-26 2017-12-25", "Sort by date descending");
|
||||
is(join(" ", sort newest_first qw(Alex Comments_on_Alex Berta Chris)), "Alex Comments_on_Alex Berta Chris", "Comments after pages");
|
||||
is(join(" ", sort newest_first qw(2017-12-25 2017-12-26 Comments_on_2017-12-26 2017-12-27)), "2017-12-27 2017-12-26 Comments_on_2017-12-26 2017-12-25", "Comments after date pages");
|
||||
is(join(" ", sort newest_first qw(Alex Comments_on_Alex Image_1_for_Alex Image_2_for_Alex Image_10_for_Alex Berta Chris)), "Alex Comments_on_Alex Image_1_for_Alex Image_2_for_Alex Image_10_for_Alex Berta Chris", "Images sorted numerically");
|
||||
|
||||
update_page('Alex', "My best friend is [[Berta]].\n\nTags: [[tag:Friends]]\n");
|
||||
update_page('Berta', "This is me.\n\nTags: [[tag:Friends]]\n");
|
||||
update_page('Chris', "I'm Chris.\n\nTags: [[tag:Friends]]\n");
|
||||
update_page('Friends', "Some friends.\n");
|
||||
update_page('2017-12-25', 'It was a Monday.\n\nTags: [[tag:Day]]');
|
||||
update_page('2017-12-26', 'It was a Tuesday.\n\nTags: [[tag:Day]]');
|
||||
update_page('2017-12-27', 'It was a Wednesday.\n\nTags: [[tag:Day]]');
|
||||
update_page('Friends', "News about friends.\n", 'rewrite', 1); # minor change
|
||||
update_page('Friends', "News about friends:\n\n<journal search tag:friends>\n",
|
||||
'add journal tag', 1); # minor change
|
||||
|
||||
# file created using convert NULL: test.png && base64 test.png
|
||||
update_page('Picture',
|
||||
"#FILE image/png\niVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bv"
|
||||
. "kkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg==");
|
||||
|
||||
sub query_gemini {
|
||||
my $query = shift;
|
||||
my $text = shift;
|
||||
|
||||
# create client
|
||||
my $socket = IO::Socket::SSL->new(
|
||||
PeerHost => "localhost",
|
||||
PeerService => $port,
|
||||
SSL_cert_file => 'cert.pem',
|
||||
SSL_key_file => 'key.pem',
|
||||
SSL_verify_mode => SSL_VERIFY_NONE)
|
||||
or die "Cannot construct client socket: $@";
|
||||
|
||||
$socket->print("$query\r\n");
|
||||
$socket->print($text);
|
||||
|
||||
undef $/; # slurp
|
||||
return <$socket>;
|
||||
}
|
||||
|
||||
my $base = "gemini://$host:$port";
|
||||
|
||||
# main menu
|
||||
my $page = query_gemini("$base/");
|
||||
|
||||
for my $item(qw(Alex Berta Chris 2017-12-25 2017-12-26 2017-12-27)) {
|
||||
like($page, qr/^=> $base\/$item $item/m, "main menu contains $item");
|
||||
}
|
||||
|
||||
unlike($page, qr/^=> .*\/$/m, "No empty links in the menu");
|
||||
|
||||
$page = query_gemini("$base/Alex");
|
||||
|
||||
like($page, qr/^My best friend is Berta\.$/m, "Local free link (text)");
|
||||
like($page, qr/=> $base\/Berta Berta$/m, "Local free link (link)");
|
||||
like($page, qr/^Tags:$/m, "Tags footer");
|
||||
like($page, qr/^Tags:$/m, "Tags footer");
|
||||
like($page, qr/=> $base\/tag\/Friends Friends$/m, "Tag link");
|
||||
like($page, qr/^=> $base\/raw\/Alex Raw text$/m, "Raw text link");
|
||||
like($page, qr/^=> $base\/history\/Alex History$/m, "History");
|
||||
like($page, qr/^=> $base\/Comments_on_Alex Comments on this page$/m, "Comment link");
|
||||
|
||||
# language tag
|
||||
$page = query_gemini("$base\/2017-12-25");
|
||||
like($page, qr/^20 text\/gemini; charset=UTF-8; lang=en\r\n/, "Result 20 with MIME type and language");
|
||||
|
||||
# plain text
|
||||
$page = query_gemini("$base\/raw\/Alex");
|
||||
like($page, qr/^My best friend is \[\[Berta\]\]\.$/m, "Raw text");
|
||||
|
||||
# history
|
||||
$page = query_gemini("$base/history/Friends");
|
||||
like($page, qr/^=> $base\/Friends\/1 Friends \(1\)/m, "Revision 1 is listed");
|
||||
like($page, qr/^=> $base\/Friends\/2 Friends \(2\)/m, "Revision 2 is listed");
|
||||
like($page, qr/^=> $base\/diff\/Friends\/1 Diff between revision 1 and the current one/m, "Diff 1 link");
|
||||
like($page, qr/^=> $base\/diff\/Friends\/2 Diff between revision 2 and the current one/m, "Diff 2 link");
|
||||
like($page, qr/^=> $base\/Friends Friends \(current\)/m, "Current revision is listed");
|
||||
$page = query_gemini("$base/Friends/1");
|
||||
like($page, qr/^Some friends\.$/m, "Revision 1 content");
|
||||
$page = query_gemini("$base/Friends/2");
|
||||
like($page, qr/^News about friends\.$/m, "Revision 2 content");
|
||||
|
||||
#diffs
|
||||
$page = query_gemini("$base/diff/Friends/1");
|
||||
like($page, qr/^< Some friends\.\n-+\n> News about friends:\n> \n> <journal search tag:friends>\n$/m, "Diff 1 content");
|
||||
$page = query_gemini("$base/diff/Friends/2");
|
||||
like($page, qr/^< News about friends\.\n-+\n> News about friends:\n> \n> <journal search tag:friends>\n$/m, "Diff 1 content");
|
||||
|
||||
# tags
|
||||
$page = query_gemini("$base\/tag\/Friends");
|
||||
like($page, qr/^This page is about the tag Friends\.$/m, "tag menu intro");
|
||||
for my $item(qw(Friends Alex Berta Chris)) {
|
||||
like($page, qr/^=> $base\/$item $item$/m, "tag menu contains $item");
|
||||
}
|
||||
|
||||
# tags
|
||||
$page = query_gemini("$base\/tag\/Day");
|
||||
like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
|
||||
"tag menu sorted newest first");
|
||||
|
||||
# match
|
||||
$page = query_gemini("$base\/do/match?2017");
|
||||
for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
|
||||
like($page, qr/^=> $base\/$item $item$/m, "match menu contains $item");
|
||||
}
|
||||
like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
|
||||
"match menu sorted newest first");
|
||||
|
||||
# search
|
||||
$page = query_gemini("$base\/do/search?tag:day");
|
||||
for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
|
||||
like($page, qr/^=> $base\/$item $item/m, "search menu contains $item");
|
||||
}
|
||||
like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
|
||||
"search menu sorted newest first");
|
||||
|
||||
# rc
|
||||
$page = query_gemini("$base\/do/rc");
|
||||
my $re = join(".*", "Picture", "2017-12-27", "2017-12-26", "2017-12-25",
|
||||
"Friends", "Chris", "Berta", "Alex");
|
||||
like($page, qr/$re/s, "rc in the right order");
|
||||
|
||||
$page = query_gemini("$base\/do/rc/minor");
|
||||
|
||||
$re = join(".*", "Friends", "2017-12-27", "2017-12-26", "2017-12-25");
|
||||
like($page, qr/$re/s, "minor rc in the right order");
|
||||
|
||||
# feeds
|
||||
my $xpc = XML::LibXML::XPathContext->new;
|
||||
$xpc->registerNs('atom', 'http://www.w3.org/2005/Atom');
|
||||
|
||||
# rss with regular pages
|
||||
my $feed = new XML::RSS;
|
||||
$page = query_gemini("$base\/do/rss");
|
||||
ok($page =~ s!^20 application/rss\+xml\r\n!!, "RSS header OK");
|
||||
ok($feed->parse($page), "RSS parse OK");
|
||||
for my $item(qw(Alex Berta Chris 2017-12-25 2017-12-26 2017-12-27)) {
|
||||
ok(grep(/$item/, map { $_->{title} } @{$feed->{items}}), "$item found in RSS feed");
|
||||
}
|
||||
|
||||
# atom with regular pages
|
||||
$page = query_gemini("$base\/do/atom");
|
||||
ok($page =~ s!^20 application/atom\+xml\r\n!!, "Atom header OK");
|
||||
# $feed->parse($page) results in warnings that I can't get rid of
|
||||
ok(my $doc = XML::LibXML->load_xml(string => $page), "Atom parse OK");
|
||||
for my $item(qw(Alex Berta Chris 2017-12-25 2017-12-26 2017-12-27)) {
|
||||
ok($xpc->find("//atom:entry/atom:title[text()='$item']", $doc), "$item found in Atom feed");
|
||||
}
|
||||
|
||||
add_module('journal-rss.pl');
|
||||
|
||||
# rss with just the journal
|
||||
$page = query_gemini("$base\/do/rss");
|
||||
ok($page =~ s!^20 application/rss\+xml\r\n!!, "RSS header OK");
|
||||
ok($feed->parse($page), "RSS parse OK");
|
||||
for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
|
||||
ok(grep(/$item/, map { $_->{title} } @{$feed->{items}}), "$item found in RSS feed");
|
||||
}
|
||||
for my $item(qw(Alex Berta Chris)) {
|
||||
ok(!grep(/$item/, map { $_->{title} } @{$feed->{items}}), "$item not found in RSS feed");
|
||||
}
|
||||
my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime;
|
||||
$year += 1900;
|
||||
# Fri, 19 Jun 2020 20:41:55 GMT
|
||||
my $today = sprintf("%s, %02d %s %d",
|
||||
qw(Sun Mon Tue Wed Thu Fri Sat)[$wday], $mday,
|
||||
qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$mon], $year);
|
||||
like($page, qr!<pubDate>$today \d\d:\d\d:\d\d GMT</pubDate>!, "Update timestamp for today");
|
||||
|
||||
# atom with just the journal
|
||||
$page = query_gemini("$base\/do/atom");
|
||||
ok($page =~ s!^20 application/atom\+xml\r\n!!, "Atom header OK");
|
||||
# $feed->parse($page) results in warnings that I can't get rid of
|
||||
ok($doc = XML::LibXML->load_xml(string => $page), "Atom parse OK");
|
||||
for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
|
||||
ok($xpc->find("//atom:entry/atom:title[text()='$item']", $doc), "$item found in Atom feed");
|
||||
}
|
||||
for my $item(qw(Alex Berta Chris)) {
|
||||
ok(!$xpc->find("//atom:entry/atom:title[text()='$item']", $doc), "$item not found in Atom feed");
|
||||
}
|
||||
$today = sprintf("%d-%02d-%02d", $year, $mon+1, $mday);
|
||||
like($page, qr!<updated>${today}T\d\d:\d\d:\d\dZ</updated>!, "Update timestamp for today");
|
||||
|
||||
# upload text
|
||||
|
||||
my $titan = "titan://$host:$port";
|
||||
|
||||
my $haiku = <<EOT;
|
||||
Quiet disk ratling
|
||||
Keyboard clicking, then it stops.
|
||||
Rain falls and I think
|
||||
EOT
|
||||
|
||||
$page = query_gemini("$titan/raw/Haiku;size=76;mime=text/plain", $haiku);
|
||||
like($page, qr/^30 $base\/Haiku\r$/, "Titan Haiku");
|
||||
|
||||
my $haiku_re = $haiku;
|
||||
$haiku_re =~ s/\s+/ /g; # lines get wrapped
|
||||
$haiku_re =~ s/\s+$//g;
|
||||
$haiku_re = quotemeta($haiku_re);
|
||||
$page = query_gemini("$base/Haiku");
|
||||
like($page, qr/^$haiku_re/m, "Haiku saved");
|
||||
|
||||
# comment
|
||||
|
||||
like($page, qr/^=> $base\/Comments_on_Haiku Comments on this page$/m, "Comment page link");
|
||||
|
||||
$page = query_gemini("$base/Comments_on_Haiku");
|
||||
like($page, qr/^=> $base\/do\/comment\/Comments_on_Haiku Leave a comment$/m, "Leave comment link");
|
||||
|
||||
$page = query_gemini("$base/do/comment/Comments_on_Haiku");
|
||||
like($page, qr/^30 $base\/do\/comment\/Comments_on_Haiku\/0\r$/, "Redirect to a question");
|
||||
|
||||
$page = query_gemini("$base/do/comment/Comments_on_Haiku/0");
|
||||
like($page, qr/^10 Who rules in Rivendell\?\r$/, "Ask security question");
|
||||
|
||||
$page = query_gemini("$base/do/comment/Comments_on_Haiku/0?elrond");
|
||||
like($page, qr/^30 $base\/do\/comment\/Comments_on_Haiku\/0\/elrond\r$/, "Redirect to comment prompt");
|
||||
|
||||
$page = query_gemini("$base/do/comment/Comments_on_Haiku/0/elrond");
|
||||
like($page, qr/^10 Comment\r$/, "Ask for comment");
|
||||
|
||||
$page = query_gemini("$base/do/comment/Comments_on_Haiku/0/elrond?Give%20me%20the%20ring!");
|
||||
like($page, qr/^30 $base\/Comments_on_Haiku\r$/, "Redirect back to the main page");
|
||||
|
||||
$page = query_gemini("$base/Comments_on_Haiku");
|
||||
like($page, qr/^Give me the ring!\n\n-- Anonymous/m, "Comment saved");
|
||||
|
||||
# extension
|
||||
|
||||
$page = query_gemini("$base/do/test");
|
||||
like($page, qr/^Test\n/m, "Extension runs");
|
||||
|
||||
done_testing();
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2008-2019 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2008-2021 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 => 43;
|
||||
use Test::More tests => 45;
|
||||
|
||||
add_module('journal-rss.pl');
|
||||
|
||||
@@ -113,3 +113,7 @@ xpath_test(get_page('action=journal search=tag:oddmuse'),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal;search=tag%3aoddmuse"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal;search=tag%3aoddmuse"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=10;search=tag%3aoddmuse"]');
|
||||
|
||||
# check raw
|
||||
$page = get_page('action=journal raw=1 rsslimit=1');
|
||||
test_page($page, 'generator: Oddmuse', 'title: 2008-09-22');
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 67;
|
||||
use Test::More tests => 68;
|
||||
|
||||
add_module('markdown-rule.pl');
|
||||
add_module('bbcode.pl');
|
||||
@@ -99,6 +99,8 @@ foo\n=\nbar
|
||||
<h2>foo ##</h2>
|
||||
bar\n##foo\nbar
|
||||
bar <h2>foo</h2><p>bar</p>
|
||||
this is #foo tag
|
||||
this is #foo tag
|
||||
```\nfoo\n```\nbar
|
||||
<pre>foo</pre><p>bar</p>
|
||||
```\nfoo\n```
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 82;
|
||||
use Test::More tests => 89;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
add_module('namespaces.pl');
|
||||
@@ -202,3 +202,14 @@ test_page(update_page('Bond', 'My name is Bond', '007 ns', undef, undef,
|
||||
test_page(get_page('action=browse id=Bond ns=007'),
|
||||
'<title>Wiki 007: Bond</title>',
|
||||
'<p>My name is Bond</p>');
|
||||
|
||||
# BannedHosts are shared! Editing the BannedHosts in a namespace results in the root BannedHosts getting written.
|
||||
|
||||
test_page(update_page('BannedHosts', '^127\.0\.0\.1', 'ban myself', undef, 1, 'ns=007', 'username=James'),
|
||||
'<title>Wiki 007: Banned Hosts</title>', 'This page does not exist');
|
||||
test_page(get_page('BannedHosts'), quotemeta('^127\.0\.0\.1'));
|
||||
test_page(update_page('Mr._Q', 'Hello'), 'This page does not exist');
|
||||
test_page(update_page('Mr._Q', 'Hello', undef, undef, undef, 'ns=007'), 'This page does not exist');
|
||||
test_page(update_page('Mr._Q', 'Hello', undef, undef, undef, 'ns=008'), 'This page does not exist');
|
||||
update_page('BannedHosts', '', 'unban myself', undef, 1);
|
||||
test_page(update_page('Mr._Q', 'Hello'), 'Hello');
|
||||
|
||||
@@ -22,9 +22,9 @@ add_module('page-trail.pl');
|
||||
my $page = get_page('FirstPage');
|
||||
|
||||
xpath_test($page,
|
||||
'//div[@class="header"]/div[@class="menu"]/span[@class="gotobar bar"]/following-sibling::span[@class="trail"]',
|
||||
'//span[@class="trail"][contains(text(),"Trail: ")]/br',
|
||||
'//span[@class="trail"]/a[@class="local"][@href="http://localhost/wiki.pl/FirstPage"][text()="FirstPage"]');
|
||||
'//header/nav/span[@class="gotobar bar"]/following-sibling::span[@class="trail"]',
|
||||
'//span[@class="trail"][contains(text(),"Trail: ")]/br',
|
||||
'//span[@class="trail"]/a[@class="local"][@href="http://localhost/wiki.pl/FirstPage"][text()="FirstPage"]');
|
||||
|
||||
# verify cookie
|
||||
test_page($page, 'Set-Cookie: Wiki=trail%251eFirstPage');
|
||||
|
||||
2
t/rc.t
2
t/rc.t
@@ -51,7 +51,7 @@ test_page(get_page('action=rc raw=1'), 'title: Wiki');
|
||||
WriteStringToFile($RcFile, "1${FS}test${FS}${FS}test${FS}127.0.0.1${FS}${FS}1${FS}${FS}\n");
|
||||
test_page_negative(get_page('action=rc raw=1'), 'title: test');
|
||||
test_page(get_page('action=rc raw=1 from=1'), 'title: Wiki', 'title: test',
|
||||
'description: test', 'generator: Anonymous',
|
||||
'description: test', 'generator: \d\d\d\d',
|
||||
'link: http://localhost/wiki.pl/test',
|
||||
'last-modified: 1970-01-01T00:00Z', 'revision: 1');
|
||||
|
||||
|
||||
2
t/rss.t
2
t/rss.t
@@ -70,7 +70,7 @@ test_page(get_page('action=rss full=1'),
|
||||
'<title>12h50 Forget It</title>', # wrong
|
||||
'<title>2008-08-08</title>',
|
||||
'<title>Comments on New Hope</title>',
|
||||
'<description><div class="page" lang="en"><p>foo foo</p></div></description>');
|
||||
'<description><div class="e-content" lang="en"><p>foo foo</p></div></description>');
|
||||
|
||||
# no stripping of dates
|
||||
test_page(get_page('action=rss short=0'),
|
||||
|
||||
24
t/test.pl
24
t/test.pl
@@ -42,7 +42,7 @@ require 'wiki.pl';
|
||||
# our $ENV{PATH} is set to /bin:/usr/bin in order to find diff and
|
||||
# grep.
|
||||
if ($ENV{PERLBREW_PATH}) {
|
||||
$ENV{PATH} = $ENV{PERLBREW_PATH} . ':' . $ENV{PATH};
|
||||
$ENV{PATH} = join(':', split(/ /, $ENV{PERLBREW_PATH}), $ENV{PATH});
|
||||
} elsif (-f '/usr/local/bin/perl') {
|
||||
$ENV{PATH} = '/usr/local/bin:' . $ENV{PATH};
|
||||
}
|
||||
@@ -197,16 +197,24 @@ sub test_page_negative {
|
||||
|
||||
sub xpath_do {
|
||||
my ($check, $message, $page, @tests) = @_;
|
||||
$page =~ s/^.*?(<html)/$1/s; # strip headers
|
||||
$page =~ s/^.*?<\?xml.*?>\s*//s; # strip xml processing
|
||||
$page =~ s/^(.+\r\n)*\r\n//; # strip headers
|
||||
my $xml = $page =~ s/^.*?<\?xml.*?>\s*//s; # strip xml processing
|
||||
my $page_shown = 0;
|
||||
my $parser = XML::LibXML->new();
|
||||
my $parser = XML::LibXML->new(recover => 1, suppress_errors => 1); # allow HTML5 tags
|
||||
my $doc;
|
||||
my @result;
|
||||
SKIP: {
|
||||
eval { $doc = $parser->parse_html_string($page) };
|
||||
eval { $doc = $parser->parse_string($page) } if $@;
|
||||
skip("Cannot parse ".name($page).": $@", $#tests + 1) if $@;
|
||||
SKIP:
|
||||
{
|
||||
if ($xml) {
|
||||
eval { $doc = $parser->parse_string($page) };
|
||||
} else {
|
||||
eval { $doc = $parser->parse_html_string($page) };
|
||||
}
|
||||
if ($@) {
|
||||
skip("Cannot parse ".name($page).": $@", $#tests + 1);
|
||||
return;
|
||||
}
|
||||
# warn "Doc: '$doc'\n";
|
||||
foreach my $test (@tests) {
|
||||
my $nodelist;
|
||||
# libxml2 is not aware of UTF8 flag
|
||||
|
||||
@@ -33,7 +33,7 @@ $page = update_page('HomePage', 'This is the homepage. [[de:HauptSeite]] [[fr:Pa
|
||||
test_page($page, 'This is the homepage.', 'fr:PagePrincipale',
|
||||
'action=translate;id=HomePage;missing=en', 'Add Translation');
|
||||
test_page_negative($page, 'de:HauptSeite');
|
||||
xpath_test($page, '//div[@class="footer"]/span[@class="translation bar"]/a[@class="translation de"][@href="http://localhost/wiki.pl/HauptSeite"][text()="Deutsch"]');
|
||||
xpath_test($page, '//footer/span[@class="translation bar"]/a[@class="translation de"][@href="http://localhost/wiki.pl/HauptSeite"][text()="Deutsch"]');
|
||||
|
||||
AppendStringToFile($ConfigFile, q{
|
||||
%Languages = ('de' => '\b(der|die|das|und|oder)\b',
|
||||
@@ -45,7 +45,7 @@ $Translate{en} = 'English';
|
||||
});
|
||||
|
||||
xpath_test(update_page('HomePage', 'Simple test. [[de:HauptSeite]]'),
|
||||
'//div[@class="footer"]/span[@class="translation bar"]/a[@class="translation new"][text()="Add Translation"][@href="http://localhost/wiki.pl?action=translate;id=HomePage;missing=en_fr"]');
|
||||
'//footer/span[@class="translation bar"]/a[@class="translation new"][text()="Add Translation"][@href="http://localhost/wiki.pl?action=translate;id=HomePage;missing=en_fr"]');
|
||||
|
||||
$page = get_page('action=translate id=HomePage missing=en_fr');
|
||||
test_page($page, 'Français', 'English');
|
||||
@@ -53,7 +53,7 @@ test_page_negative($page, 'Deutsch');
|
||||
|
||||
# the page is now autoidentified as English, therefore French is the only one that is missing!
|
||||
xpath_test(update_page('HomePage', 'The the the the test. [[de:HauptSeite]]'),
|
||||
'//div[@class="footer"]/span[@class="translation bar"]/a[@class="translation new"][text()="Add Translation"][@href="http://localhost/wiki.pl?action=translate;id=HomePage;missing=fr"]');
|
||||
'//footer/span[@class="translation bar"]/a[@class="translation new"][text()="Add Translation"][@href="http://localhost/wiki.pl?action=translate;id=HomePage;missing=fr"]');
|
||||
|
||||
test_page(get_page('action=translate id=HomePage target=PagePrincipale translation=fr'),
|
||||
'Editing PagePrincipale');
|
||||
|
||||
77
wiki.pl
77
wiki.pl
@@ -1,5 +1,5 @@
|
||||
#! /usr/bin/env perl
|
||||
# Copyright (C) 2001-2019
|
||||
# Copyright (C) 2001-2020
|
||||
# Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2014-2015
|
||||
# Alex Jakimenko <alex.jakimenko@gmail.com>
|
||||
@@ -161,9 +161,7 @@ our $CurrentLanguage = 'en'; # Language of error messages etc
|
||||
our $LanguageLimit = 3; # Number of matches req. for each language
|
||||
our $JournalLimit = 200; # how many pages can be collected in one go?
|
||||
our $PageNameLimit = 120; # max length of page name in bytes
|
||||
$DocumentHeader = qq(<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN")
|
||||
. qq( "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n)
|
||||
. qq(<html xmlns="http://www.w3.org/1999/xhtml">);
|
||||
$DocumentHeader = "<!DOCTYPE html>\n<html>";
|
||||
our @MyFooters = (\&GetCommentForm, \&WrapperEnd, \&DefaultFooter);
|
||||
# Checkboxes at the end of the index.
|
||||
our @IndexOptions = ();
|
||||
@@ -210,7 +208,7 @@ sub ReportError { # fatal!
|
||||
my ($errmsg, $status, $log, @html) = @_;
|
||||
InitRequest(); # make sure we can report errors before InitRequest
|
||||
print GetHttpHeader('text/html', 'nocache', $status), GetHtmlHeader(T('Error')),
|
||||
$q->start_div({class=>"error"}), $q->h1(QuoteHtml($errmsg)), @html, $q->end_div,
|
||||
$q->start_div({class=>'error'}), $q->h1(QuoteHtml($errmsg)), @html, $q->end_div,
|
||||
$q->end_html, "\n\n"; # newlines for FCGI because of exit()
|
||||
WriteStringToFile("$TempDir/error", '<body>' . $q->h1("$status $errmsg") . $q->Dump) if $log;
|
||||
map { ReleaseLockDir($_); } keys %Locks;
|
||||
@@ -450,7 +448,7 @@ sub ApplyRules {
|
||||
if ($type eq 'text') {
|
||||
print $q->pre({class=>"include $uri"}, QuoteHtml(GetRaw($uri)));
|
||||
} else { # never use local links for remote pages, with a starting tag
|
||||
print $q->start_div({class=>"include"});
|
||||
print $q->start_div({class=>'include'});
|
||||
ApplyRules(QuoteHtml(GetRaw($uri)), 0, ($type eq 'with-anchors'), undef, 'p');
|
||||
print $q->end_div();
|
||||
}
|
||||
@@ -666,7 +664,7 @@ sub OpenHtmlEnvironment { # close the previous $html_tag and open a new one
|
||||
@HtmlStack = @stack if $found; # if not starting a new list
|
||||
$depth = $IndentLimit if $depth > $IndentLimit; # requested depth 0 makes no sense
|
||||
$html_tag_attr = qq/class="$html_tag_attr"/ # backwards-compatibility hack: classically, the third argument to this function was a single CSS class, rather than string of HTML tag attributes as in the second argument to the "AddHtmlEnvironment" function. To allow both sorts, we conditionally change this string to 'class="$html_tag_attr"' when this string is a single CSS class.
|
||||
if $html_tag_attr and $html_tag_attr !~ m/^\s*[[:alpha:]]@@+\s*=\s*('|").+\1/;
|
||||
if $html_tag_attr and $html_tag_attr !~ m/=/;
|
||||
splice(@HtmlAttrStack, 0, @HtmlAttrStack - @HtmlStack); # truncate to size of @HtmlStack
|
||||
foreach ($found .. $depth - 1) {
|
||||
unshift(@HtmlStack, $html_tag);
|
||||
@@ -802,7 +800,7 @@ sub UrlEncode {
|
||||
|
||||
sub UrlDecode {
|
||||
my $str = shift;
|
||||
return decode_utf8($str) if $str =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eg;
|
||||
return decode_utf8($str) if $str =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eig;
|
||||
return $str;
|
||||
}
|
||||
|
||||
@@ -877,17 +875,13 @@ sub PrintAllPages {
|
||||
next if $lang and @languages and not grep(/$lang/, @languages);
|
||||
next if PageMarkedForDeletion();
|
||||
next if substr($Page{text}, 0, 10) eq '#REDIRECT ';
|
||||
print $q->start_div({-class=>'page h-entry'}),
|
||||
$q->h1({-class => 'entry-title'},
|
||||
$links ? GetPageLink($id) : $q->a({-name=>$id}, UrlEncode(FreeToNormal($id))));
|
||||
print '<article class="h-entry">', $q->h1({-class => 'p-name'},
|
||||
$links ? GetPageLink($id) : $q->a({-name=>$id}, UrlEncode(FreeToNormal($id))));
|
||||
if ($variation ne 'titles') {
|
||||
my $lang = (split /,/, $Page{languages})[0] || $CurrentLanguage;
|
||||
print $q->start_div({-class=>'entry-content', -lang=>$lang});
|
||||
PrintPageHtml();
|
||||
print $q->end_div();
|
||||
PrintPageCommentsLink($id, $comments);
|
||||
}
|
||||
print $q->end_div();
|
||||
print '</article>';
|
||||
$n++; # pages actually printed
|
||||
}
|
||||
return $i;
|
||||
@@ -1135,7 +1129,7 @@ sub GetUrl {
|
||||
}
|
||||
$url = UnquoteHtml($url); # links should be unquoted again
|
||||
if ($images and $url =~ /^(http:|https:|ftp:).+\.$ImageExtensions$/i) {
|
||||
return $q->img({-src=>$url, -alt=>$url, -class=>$class});
|
||||
return $q->img({-src=>$url, -alt=>$url, -class=>$class, -loading=>'lazy'});
|
||||
} else {
|
||||
return $q->a({-href=>$url, -class=>$class}, $text);
|
||||
}
|
||||
@@ -1224,7 +1218,8 @@ sub GetDownloadLink {
|
||||
if ($image) {
|
||||
$action = $ScriptName . (($UsePathInfo and not $revision) ? '/' : '?') . $action;
|
||||
return $action if $image == 2;
|
||||
my $result = $q->img({-src=>$action, -alt=>UnquoteHtml($alt), -title=>UnquoteHtml($alt), -class=>'upload'});
|
||||
my $result = $q->img({-src=>$action, -alt=>UnquoteHtml($alt), -title=>UnquoteHtml($alt),
|
||||
-class=>'upload', -loading=>'lazy'});
|
||||
$result = ScriptLink(UrlEncode($id), $result, 'image') unless $id eq $OpenPageName;
|
||||
return $result;
|
||||
} else {
|
||||
@@ -1246,12 +1241,15 @@ sub PrintCache { # Use after OpenPage!
|
||||
}
|
||||
|
||||
sub PrintPageHtml { # print an open page
|
||||
return unless GetParam('page', 1);
|
||||
return unless GetParam('page', 1) and $Page{text};
|
||||
my $lang = (split /,/, $Page{languages})[0] || $CurrentLanguage;
|
||||
print qq{<div class="e-content" lang="$lang">};
|
||||
if ($Page{blocks} and defined $Page{flags} and GetParam('cache', $UseCache) > 0) {
|
||||
PrintCache();
|
||||
} else {
|
||||
PrintWikiToHTML($Page{text}, 1); # save cache, current revision, no main lock
|
||||
}
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
sub PrintPageDiff { # print diff for open page
|
||||
@@ -1280,7 +1278,6 @@ sub PageHtml {
|
||||
return $error if $limit and length($diff) > $limit;
|
||||
my $lang = (split /,/, $Page{languages})[0] // $CurrentLanguage;
|
||||
my $page .= ToString \&PrintPageHtml;
|
||||
$page = qq{<div class="page" lang="$lang">$page</div>} if $page;
|
||||
return $diff . $q->p($error) if $limit and length($diff . $page) > $limit;
|
||||
return $diff . $page;
|
||||
}
|
||||
@@ -1860,7 +1857,7 @@ sub RcTextRevision {
|
||||
$summary = GetPageContent($id) if GetParam('full', 0);
|
||||
print "\n", RcTextItem('title', NormalToFree($id)),
|
||||
RcTextItem('description', $summary),
|
||||
RcTextItem('generator', GetAuthor($username)),
|
||||
RcTextItem('generator', GetAuthor($username, $host)),
|
||||
RcTextItem('language', join(', ', @{$languages})), RcTextItem('link', $link),
|
||||
RcTextItem('last-modified', TimeToW3($ts)),
|
||||
RcTextItem('revision', $revision),
|
||||
@@ -1877,11 +1874,11 @@ sub PrintRcText { # print text rss header and call ProcessRcLines
|
||||
|
||||
sub GetRcRss {
|
||||
my $date = TimeToRFC822($LastUpdate);
|
||||
my %excluded = ();
|
||||
my @excluded = ();
|
||||
if (GetParam("exclude", 1)) {
|
||||
foreach (split(/\n/, GetPageContent($RssExclude))) {
|
||||
if (/^ ([^ ]+)[ \t]*$/) { # only read lines with one word after one space
|
||||
$excluded{$1} = 1;
|
||||
push(@excluded, $1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1928,7 +1925,7 @@ sub GetRcRss {
|
||||
my $count = 0;
|
||||
ProcessRcLines(sub {}, sub {
|
||||
my $id = shift;
|
||||
return if $excluded{$id} or ($limit ne 'all' and $count++ >= $limit);
|
||||
return if grep { $id =~ /$_/ } @excluded or ($limit ne 'all' and $count++ >= $limit);
|
||||
$rss .= "\n" . RssItem($id, @_);
|
||||
});
|
||||
$rss .= "</channel>\n</rss>\n";
|
||||
@@ -2216,11 +2213,16 @@ sub ScriptLinkDiff {
|
||||
return ScriptLink($action, $text, 'diff');
|
||||
}
|
||||
|
||||
sub ColorCode {
|
||||
sub Code {
|
||||
my ($str) = @_;
|
||||
my $num = unpack("L",B::hash($str)); # 32-bit integer
|
||||
my $code = sprintf("%o", $num); # octal is 0-7
|
||||
my @indexes = split(//, substr($code, 0, 4)); # four numbers
|
||||
return substr($code, 0, 4); # four numbers
|
||||
}
|
||||
|
||||
sub ColorCode {
|
||||
my $code = Code(@_);
|
||||
my @indexes = split(//, $code); # four numbers
|
||||
my @colors = qw/red orange yellow green blue indigo violet white/;
|
||||
return $q->span({-class => 'ip-code', -title => T('Anonymous')},
|
||||
join('', map { $q->span({-class => $colors[$_]}, $_) }
|
||||
@@ -2228,9 +2230,10 @@ sub ColorCode {
|
||||
}
|
||||
|
||||
sub GetAuthor {
|
||||
my ($username) = @_;
|
||||
my ($username, $host) = @_;
|
||||
return $username if $username;
|
||||
return T('Anonymous');
|
||||
return T('Anonymous') if $host eq 'Anonymous';
|
||||
return Code($host);
|
||||
}
|
||||
|
||||
sub GetAuthorLink {
|
||||
@@ -2279,12 +2282,12 @@ sub GetHeader {
|
||||
|
||||
sub GetHeaderDiv {
|
||||
my ($id, $title, $oldId, $embed) = @_;
|
||||
my $result .= $q->start_div({-class=>'header'});
|
||||
my $result .= '<header>';
|
||||
if (not $embed and $LogoUrl) {
|
||||
my $url = $IndexHash{$LogoUrl} ? GetDownloadLink($LogoUrl, 2) : $LogoUrl;
|
||||
$result .= ScriptLink(UrlEncode($HomePage), $q->img({-src=>$url, -alt=>T('[Home]'), -class=>'logo'}), 'logo');
|
||||
}
|
||||
$result .= $q->start_div({-class=>'menu'});
|
||||
$result .= '<nav>';
|
||||
if (GetParam('toplinkbar', $TopLinkBar) != 2) {
|
||||
$result .= GetGotoBar($id);
|
||||
if (%SpecialDays) {
|
||||
@@ -2296,10 +2299,10 @@ sub GetHeaderDiv {
|
||||
}
|
||||
}
|
||||
$result .= GetSearchForm() if GetParam('topsearchform', $TopSearchForm) != 2;
|
||||
$result .= $q->end_div();
|
||||
$result .= '</nav>';
|
||||
$result .= $q->div({-class=>'message'}, $Message) if $Message;
|
||||
$result .= GetHeaderTitle($id, $title, $oldId);
|
||||
$result .= $q->end_div();
|
||||
$result .= '</header>';
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -2447,7 +2450,7 @@ sub WrapperEnd { # called via @MyFooters
|
||||
|
||||
sub DefaultFooter { # called via @MyFooters
|
||||
my ($id, $rev, $comment, $page) = @_;
|
||||
my $html = $q->start_div({-class=>'footer'}) . $q->hr();
|
||||
my $html = $q->hr();
|
||||
$html .= GetGotoBar($id) if GetParam('toplinkbar', $TopLinkBar) != 1;
|
||||
$html .= GetFooterLinks($id, $rev);
|
||||
$html .= GetFooterTimestamp($id, $rev, $page);
|
||||
@@ -2458,8 +2461,7 @@ sub DefaultFooter { # called via @MyFooters
|
||||
}
|
||||
$html .= T($FooterNote) if $FooterNote;
|
||||
$html .= $q->p(Ts('%s seconds', (time - $Now))) if GetParam('timing', 0);
|
||||
$html .= $q->end_div();
|
||||
return $html;
|
||||
return "<footer>$html</footer>";
|
||||
}
|
||||
|
||||
sub GetFooterTimestamp {
|
||||
@@ -2572,9 +2574,8 @@ sub GetSearchForm {
|
||||
return $html;
|
||||
}
|
||||
|
||||
sub GetGotoBar { # ignore $id parameter
|
||||
return $q->span({-class=>'gotobar bar'}, (map { GetPageLink($_) }
|
||||
@UserGotoBarPages), $UserGotoBar);
|
||||
sub GetGotoBar { # ignore $id parameter
|
||||
return $q->span({-class=>'gotobar bar'}, (map { GetPageLink($_) } @UserGotoBarPages), $UserGotoBar);
|
||||
}
|
||||
|
||||
# return list of summaries between two revisions, assuming the open page is the upper one
|
||||
@@ -3356,6 +3357,7 @@ sub SortIndex {
|
||||
sub DoIndex {
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $match = GetParam('match', '');
|
||||
my $limit = GetParam('n', '');
|
||||
my @pages = ();
|
||||
my @menu = ($q->label({-for=>'indexmatch'}, T('Filter:')) . ' '
|
||||
. $q->textfield(-name=>'match', -id=>'indexmatch', -size=>20));
|
||||
@@ -3368,6 +3370,7 @@ sub DoIndex {
|
||||
}
|
||||
@pages = grep /$match/i, @pages if $match;
|
||||
@pages = sort SortIndex @pages;
|
||||
@pages = @pages[0 .. $limit - 1] if $limit;
|
||||
if ($raw) {
|
||||
print GetHttpHeader('text/plain'); # and ignore @menu
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user