Compare commits
240 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0974b7bbd8 | ||
|
|
f73d420957 | ||
|
|
17ef2aaf88 | ||
|
|
b70c8e8def | ||
|
|
f8752e69bc | ||
|
|
9d48f875a2 | ||
|
|
39e9cea7b0 | ||
|
|
e7b718f610 | ||
|
|
261aeccb3f | ||
|
|
a09c846700 | ||
|
|
8dbede3813 | ||
|
|
89d9f27b2a | ||
|
|
f21f257c1b | ||
|
|
48916943a1 | ||
|
|
3b185e5521 | ||
|
|
612af8f7fb | ||
|
|
dc9131e600 | ||
|
|
99af4d984d | ||
|
|
88f4fe3b89 | ||
|
|
851f2f77e8 | ||
|
|
975e15c9f8 | ||
|
|
d235d6ac47 | ||
|
|
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 | ||
|
|
244d06ca3b | ||
|
|
1a59075b51 | ||
|
|
e0b3c18499 | ||
|
|
5434136a4d | ||
|
|
aeeb182dad | ||
|
|
4d6882ffc7 | ||
|
|
828482f439 | ||
|
|
2b6f2dfa0c | ||
|
|
413b43174c | ||
|
|
9709c87185 | ||
|
|
0cca358de2 | ||
|
|
acff0cb69f | ||
|
|
8d5956cb7f | ||
|
|
e9773ea694 | ||
|
|
7094ec098b | ||
|
|
04fe4d6991 | ||
|
|
570a6b1f07 | ||
|
|
4211d6aa03 | ||
|
|
5941fcd7e6 | ||
|
|
f3df2eb289 | ||
|
|
8b975cd5e5 | ||
|
|
850f292260 | ||
|
|
ced1252b9d | ||
|
|
c0a6bfebf2 | ||
|
|
36815767f3 | ||
|
|
2cba2d30b7 | ||
|
|
fded17520c | ||
|
|
46580a3958 | ||
|
|
d584899447 | ||
|
|
79c0a9fd02 | ||
|
|
c3f21d60c2 | ||
|
|
43221ea8a9 | ||
|
|
25350e93cb | ||
|
|
415a8aa9a8 | ||
|
|
a3d740aa67 | ||
|
|
1956335640 | ||
|
|
b89c8b99e8 | ||
|
|
4e4d8ee784 | ||
|
|
7ecd7b784e | ||
|
|
0d9764b0de | ||
|
|
5d7964977c | ||
|
|
d50cda9cea | ||
|
|
7257bfb6d5 | ||
|
|
f2470256ae | ||
|
|
3c7a56ba5a | ||
|
|
07d68b4400 | ||
|
|
51d2d3e0ca | ||
|
|
0a361873de | ||
|
|
6af02a8a1e | ||
|
|
dc283ea828 | ||
|
|
8e3917b933 | ||
|
|
5a2209a0c2 | ||
|
|
73b6949b95 | ||
|
|
771b07e661 | ||
|
|
629693e353 | ||
|
|
a45e13f74c | ||
|
|
a919f4b8fd | ||
|
|
6d37baabd9 | ||
|
|
41f18d0b3e | ||
|
|
8a5cce103e | ||
|
|
65cfd93de9 | ||
|
|
a67cac80f5 | ||
|
|
c73f224f2d | ||
|
|
f241d7cd77 | ||
|
|
a5f5607667 | ||
|
|
2d7451402f | ||
|
|
7e13816d40 | ||
|
|
c2231b9e39 | ||
|
|
58ecb96975 | ||
|
|
d24b7d8bd3 | ||
|
|
91e054cea4 | ||
|
|
be2abffde2 | ||
|
|
93b706dd28 | ||
|
|
8f43c818df | ||
|
|
8194d6bb81 | ||
|
|
dc203917b9 | ||
|
|
e7818ef43e | ||
|
|
37c4a19f1f | ||
|
|
864f294761 | ||
|
|
1534d6dd8f | ||
|
|
9ae728bf13 | ||
|
|
768244860f | ||
|
|
95e566ae97 | ||
|
|
040545269a | ||
|
|
c6b7d952bd | ||
|
|
06067dd1c1 | ||
|
|
6ee6b384cf | ||
|
|
7cdf2abe2d | ||
|
|
942d77f599 | ||
|
|
71baa51a54 | ||
|
|
8819183be3 | ||
|
|
0941a09c19 | ||
|
|
eae48212be | ||
|
|
4f9641c43f | ||
|
|
32fa6057c7 | ||
|
|
025907f65a | ||
|
|
a259edc0bb | ||
|
|
94fd9c0c2a | ||
|
|
0affd5a1b7 | ||
|
|
f27ab9a8a0 | ||
|
|
086a992b5a | ||
|
|
54cae9bc1d | ||
|
|
9d9c196441 | ||
|
|
9238e42cfd | ||
|
|
836f3abecb | ||
|
|
d669f783bb | ||
|
|
c18279ca4c | ||
|
|
bbd280abf9 | ||
|
|
c15e6041ef | ||
|
|
e4c84d9b3a | ||
|
|
a49afca3f7 | ||
|
|
883f07f926 | ||
|
|
b5d182582f | ||
|
|
fe26471abb | ||
|
|
96a3cbbb22 | ||
|
|
d303e5e955 | ||
|
|
dd6198ffca | ||
|
|
ae646290ed | ||
|
|
1a74dc9605 | ||
|
|
0c19bd13cd | ||
|
|
42bb4888a2 | ||
|
|
9a5e44aba3 | ||
|
|
78b5dd3737 | ||
|
|
b8717c4084 | ||
|
|
42f4816ae5 | ||
|
|
ff8d671787 | ||
|
|
e16cda46c1 | ||
|
|
431e792e52 | ||
|
|
c9f3060e42 | ||
|
|
ec4987e817 | ||
|
|
655d3b47c6 | ||
|
|
8b85c6e9dd | ||
|
|
ef35e4eda6 | ||
|
|
7bb1ca239b | ||
|
|
a7625b35f4 | ||
|
|
1d239aa9a6 | ||
|
|
3f1e5caaad | ||
|
|
bdbd6c8ba7 | ||
|
|
1a898ee589 | ||
|
|
9609adb575 | ||
|
|
34c10202b0 | ||
|
|
1457fad242 | ||
|
|
ce355499c7 | ||
|
|
2bb3d2f967 | ||
|
|
a301ab5e1c | ||
|
|
d74d29fa13 | ||
|
|
96c54d9d66 | ||
|
|
9a0700cac1 | ||
|
|
864890c168 | ||
|
|
a93873d1d4 | ||
|
|
b0a8fa2141 |
24
Makefile
@@ -3,8 +3,8 @@
|
||||
# subdirectory.
|
||||
|
||||
VERSION_NO=$(shell git describe --tags)
|
||||
TRANSLATIONS=$(wildcard modules/translations/[a-z]*-utf8.pl$)
|
||||
MODULES=$(wildcard modules/*.pl)
|
||||
TRANSLATIONS=$(wildcard modules/translations/*-utf8.pl)
|
||||
MODULES=$(sort $(wildcard modules/*.pl))
|
||||
BUILD=build/wiki.pl $(foreach file, $(notdir $(MODULES)) $(notdir $(TRANSLATIONS)), build/$(file))
|
||||
|
||||
# PREPARE/BUILD: this creates copies of wiki.pl and all the modules
|
||||
@@ -25,7 +25,7 @@ release:
|
||||
perl stuff/release ~/oddmuse.org/releases 2.3.3
|
||||
|
||||
build/wiki.pl: wiki.pl
|
||||
perl -lne "s/(\\\$$q->a\(\{-href=>'https:\/\/www.oddmuse.org\/'\}, 'Oddmuse'\))/\\\$$q->a({-href=>'https:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . \$$1/; print" < $< > $@
|
||||
perl -lne "s/(\\\$$q->a\(\{-href=>'https:\/\/www.oddmuse.org\/'\}, 'Oddmuse'\))/\\\$$q->a({-href=>'https:\/\/alexschroeder.ch\/cgit\/oddmuse\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . \$$1/; print" < $< > $@
|
||||
|
||||
build/%-utf8.pl: modules/translations/%-utf8.pl
|
||||
perl -lne "s/(AddModuleDescription\('[^']+', '[^']+')\)/\$$1, 'translations\/', '$(VERSION_NO)')/; print" < $< > $@
|
||||
@@ -60,3 +60,21 @@ jobs ?= 4
|
||||
test:
|
||||
prove t/setup.pl
|
||||
prove --jobs=$(jobs) --state=slow,save t
|
||||
|
||||
# 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
|
||||
|
||||
%.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
|
||||
|
||||
@@ -156,8 +156,11 @@ WikiDataDir=$HOME/oddmuse perl server.pl daemon
|
||||
}}}
|
||||
|
||||
This makes the server available on {{{http://localhost:3000/wiki}}}.
|
||||
Make sure you create the directory before starting the server!
|
||||
If you don't, you'll get a strange error:
|
||||
`STDERR: : No such file or directory at ... perl5/Mojolicious/Plugin/CGI.pm`.
|
||||
|
||||
If it works for you, feel free to upgrade to Hypnotoad.
|
||||
If it works, feel free to upgrade to Hypnotoad.
|
||||
|
||||
{{{
|
||||
WikiDataDir=$HOME/oddmuse hypnotoad server.pl
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
# Copyright (C) 2011–2015 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2011–2018 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -23,7 +23,8 @@ use utf8;
|
||||
|
||||
# load Oddmuse core
|
||||
$RunCGI = 0;
|
||||
do "wiki.pl";
|
||||
$DataDir = '/home/alex/campaignwiki';
|
||||
do "/home/alex/farm/wiki.pl";
|
||||
|
||||
# globals depending on the name of the script
|
||||
my ($self, $name, $wiki);
|
||||
@@ -35,6 +36,9 @@ if ($0 eq '/home/alex/campaignwiki.org/add-link.pl') {
|
||||
$self = "https://campaignwiki.org/add-adventure";
|
||||
$name = "OSR Links to Adventures";
|
||||
$wiki = 'Adventures';
|
||||
} elsif ($0 eq '/home/alex/campaignwiki.org/add-sf-link.pl') {
|
||||
$name = "OSRSF House Rules Wiki: Uplinked Intelligence";
|
||||
$wiki = 'UplinkedIntelligence';
|
||||
} else {
|
||||
ReportError('Cannot determine wiki!', '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
@@ -164,7 +168,7 @@ sub is_duplicate {
|
||||
" already links to the URL you submitted:",
|
||||
GetUrl($link->[0], $link->[1]));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -288,11 +292,13 @@ sub print_end_of_page {
|
||||
|
||||
sub main {
|
||||
$ConfigFile = "$DataDir/config"; # read the global config file
|
||||
$ModuleDir = "$DataDir/modules"; # global modules
|
||||
$DataDir = "$DataDir/$wiki"; # but link to the local pages
|
||||
Init(); # read config file (no modules!)
|
||||
Init(); # read config file
|
||||
$ScriptName = $site; # undo setting in the config file
|
||||
$FullUrl = $site; #
|
||||
InitPageVariables(); # call again: $ScriptName was wrong
|
||||
$HomePage = 'HomePage'; # $HomePage must not be translated
|
||||
binmode(STDOUT,':utf8');
|
||||
$q->charset('utf8');
|
||||
if ($q->path_info eq '/source') {
|
||||
|
||||
93
contrib/emacswiki/bootstrap.js
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Public Domain
|
||||
// initial version by Alex Schroeder <alex@gnu.org>
|
||||
// with many improvements by Evgkeni Sampelnikof
|
||||
|
||||
$(function(){
|
||||
|
||||
// add fancy classes
|
||||
$('div.header' ).addClass('container');
|
||||
$('div.wrapper').addClass('container');
|
||||
$('div.footer' ).addClass('container');
|
||||
$('div.footer > .navbar' ).remove();
|
||||
$('.message > p' ).addClass('alert');
|
||||
$('img.portrait').addClass('img-polaroid');
|
||||
|
||||
$('input:text').addClass('input-medium search-query');
|
||||
$('textarea').addClass('span12');
|
||||
$('input:submit').addClass('btn');
|
||||
$('.download a').addClass('btn btn-success');
|
||||
|
||||
$('.footer .gotobar').remove();
|
||||
$('.footer br').first().remove();
|
||||
var $gotobar = $('.gotobar')
|
||||
.after($('<div>').attr('class','navbar')
|
||||
.append($('<div>').attr('class','navbar-inner')
|
||||
.append($('<ul>').attr('class', 'nav'))));
|
||||
var $id = $('h1 a').first().text();
|
||||
var $list = $('.nav')
|
||||
.append($('<li>')
|
||||
.append($('<a>').attr('class', 'brand').attr('href', 'http://www.emacswiki.org/')
|
||||
.append('Emacs Wiki')));
|
||||
$('.gotobar a').each(function() {
|
||||
var $item = $('<li>');
|
||||
$(this).appendTo($item);
|
||||
$item.appendTo($list);
|
||||
if ($(this).text() == $id) {
|
||||
$item.addClass('active');
|
||||
}
|
||||
});
|
||||
$gotobar.remove();
|
||||
|
||||
// search without labels, without button, without language field
|
||||
$('form.search input[type=submit]').remove();
|
||||
$('form.search label').remove();
|
||||
$('form.search input#searchlang').remove();
|
||||
$('form.search')
|
||||
.css({'float': 'right',
|
||||
'margin-top': '10px'});
|
||||
$('.navbar').append($('form.search'));
|
||||
|
||||
// add button style to some links
|
||||
$('.edit.bar a').addClass('btn');
|
||||
|
||||
// add color to Talk button for a non-existing page
|
||||
$('a.btn.comment.edit').addClass('btn-warning');
|
||||
|
||||
// move article link and talk link below title
|
||||
var $link = $('a.original').add('a.comment');
|
||||
if ($link) {
|
||||
$('.header h1').after($('<p>').append($link));
|
||||
}
|
||||
|
||||
// toc
|
||||
if ($('title').text() == "EmacsWiki: Wikified Emacs Lisp List") {
|
||||
$('.content').addClass('ell');
|
||||
}
|
||||
|
||||
// tables
|
||||
$('table').addClass('table');
|
||||
|
||||
// minor edit checkbox
|
||||
$('input[type=checkbox]').addClass('checkbox');
|
||||
$('input[type=checkbox]').parent().addClass('checkbox');
|
||||
|
||||
// clean up admin page
|
||||
$('li a.clear').parent().remove();
|
||||
$('li a.index').parent().remove();
|
||||
|
||||
$('a[href="http://creativecommons.org/licenses/GPL/2.0/"]')
|
||||
.parent()
|
||||
.css({'margin-right': '120px',
|
||||
'opacity': 0.3,
|
||||
'padding-top': '1em'});
|
||||
$('.footer .bar')
|
||||
.after('<hr />');
|
||||
var footer_wrapper = $('<div/>')
|
||||
.addClass('footer_wrapper');
|
||||
var footer = $('.footer.container');
|
||||
footer.after(footer_wrapper);
|
||||
footer_wrapper.append(footer);
|
||||
var logo_image = $('<img />')
|
||||
.attr('src', 'http://emacswiki.org/ew_logo.png');
|
||||
$('.header .navbar .brand').html(logo_image);
|
||||
});
|
||||
@@ -2,9 +2,9 @@
|
||||
html{ text-align: center; }
|
||||
|
||||
body, rss {
|
||||
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
|
||||
font-family: "DejaVu Serif", Palatino, serif;
|
||||
font-style: normal;
|
||||
font-size: 14pt;
|
||||
font-size: 16pt;
|
||||
padding: 1em 3em;
|
||||
max-width: 72ex;
|
||||
display: inline-block;
|
||||
@@ -13,10 +13,31 @@ body, rss {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
@media only screen and (max-device-width: 600px) {
|
||||
body {
|
||||
padding: 1ex;
|
||||
}
|
||||
textarea {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
/* code */
|
||||
|
||||
textarea, pre, code, tt {
|
||||
font-family: "DejaVu Mono", "Andale Mono", Monaco, "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
pre, code, tt {
|
||||
font-size: 12pt; /* fits 80ex */
|
||||
}
|
||||
pre {
|
||||
overflow:hidden;
|
||||
white-space: pre-wrap; /* CSS 3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
@import url(file:///home/alex/alexschroeder.ch/css/alex-2017.css) print;
|
||||
@@ -191,22 +212,6 @@ input[type="text"] {
|
||||
line-height: 125%;
|
||||
}
|
||||
|
||||
/* code */
|
||||
|
||||
textarea, pre, code, tt {
|
||||
font-family: "Andale Mono", Monaco, "Courier New", Courier, monospace, "Symbola";
|
||||
font-size: 75%; /* fits 80ex */
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow:hidden;
|
||||
white-space: pre-wrap; /* CSS 3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
/* styling for divs that will be invisible when printing
|
||||
when printing. */
|
||||
|
||||
@@ -223,6 +228,9 @@ div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
div.footer form.search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Recent Changes */
|
||||
|
||||
div.rc {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -233,12 +241,57 @@ div.rc li strong, table.history strong, strong.description {
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
.red {
|
||||
background: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.orange {
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
background: yellow;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.green {
|
||||
background: green;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.blue {
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.indigo {
|
||||
background: indigo;
|
||||
color: indigo;
|
||||
}
|
||||
|
||||
.violet {
|
||||
background: violet;
|
||||
color: violet;
|
||||
}
|
||||
|
||||
.white {
|
||||
background: white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ip-code {
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
/* Diff */
|
||||
|
||||
div.diff {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
font-size: 12pt;
|
||||
color: #000;
|
||||
|
||||
}
|
||||
div.old {
|
||||
background-color: #ffffaf;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
60
css/wiki.css
@@ -172,6 +172,50 @@ span.bar a {
|
||||
padding: 1ex 0;
|
||||
}
|
||||
|
||||
.red {
|
||||
background: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.orange {
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
background: yellow;
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.green {
|
||||
background: green;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.blue {
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.indigo {
|
||||
background: indigo;
|
||||
color: indigo;
|
||||
}
|
||||
|
||||
.violet {
|
||||
background: violet;
|
||||
color: violet;
|
||||
}
|
||||
|
||||
.white {
|
||||
background: white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ip-code {
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
color: black;
|
||||
@@ -219,3 +263,19 @@ code {
|
||||
background: #eee;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* for https://oddmuse.org/wiki/All_Modules */
|
||||
.foo_list + .journal h1 {
|
||||
font: inherit;
|
||||
border: none;
|
||||
display: list-item;
|
||||
margin-top: 0;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.foo_list + .journal a {
|
||||
font: inherit;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
color: #a00;
|
||||
}
|
||||
|
||||
BIN
logo/falky/Neris/Neris-Black.otf
Normal file
BIN
logo/falky/Neris/Neris-BlackItalic.otf
Normal file
BIN
logo/falky/Neris/Neris-Light.otf
Normal file
BIN
logo/falky/Neris/Neris-LightItalic.otf
Normal file
BIN
logo/falky/Neris/Neris-SemiBold.otf
Normal file
BIN
logo/falky/Neris/Neris-SemiBoldItalic.otf
Normal file
BIN
logo/falky/Neris/Neris-Thin.otf
Normal file
BIN
logo/falky/Neris/Neris-ThinItalic.otf
Normal file
15
logo/falky/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
Falky schreibt:
|
||||
|
||||
Zum Thema Rechte am Logo möchte ich erstmal sagen, dass es mir primär
|
||||
darum ging den Auftritt von oddmuse.org aufzuhübschen. Also du/ihr
|
||||
sollt durch das Logo unterstützt werden, nicht eingeschränkt.
|
||||
|
||||
Welche Lizenz das ab besten leisten, da hab ich keinen Schimmer.
|
||||
|
||||
Schön wäre, wenn irgendwo ein verweis auf "Falky [falky.de]"
|
||||
auftaucht, der soll aber nicht bei jeder Verwendung auftauchen,
|
||||
sondern irgendwo 'auffindbar' an 'zentraler' Stelle - halt irgendwo
|
||||
auf der Homepage, dass ich darauf verweisen kann und dass niemand was
|
||||
anderes behaupten kann. Das wäre es schon. Und ja, falls jemand das
|
||||
logo für ein anderes Projekt verwenden möchte, kann man ja höflich
|
||||
nochmal fragen oder so.
|
||||
BIN
logo/falky/oddmuse.banner.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
25
logo/falky/oddmuse.banner.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-61 -1 177 62">
|
||||
|
||||
<rect fill="#FFF" x="-60" y="0" width="175" height="60"/>
|
||||
|
||||
<text style="font:400 6px Neris Black" fill="#000" x="2" y="18">powered by</text>
|
||||
<text style="font:400 16px Neris Thin" fill="#000" x="2" y="34">Oddmuse<tspan style="font-family:Neris Black">.org</tspan></text>
|
||||
|
||||
<g id="new" transform="translate(-30,30) scale(.18,.18)" >
|
||||
<circle fill="#FFF" stroke="#000" stroke-width="3" cx="0" cy="0" r="160.5"/>
|
||||
<g id="new" transform="rotate(207.2)" >
|
||||
|
||||
<path fill="#000" d="m42 -77 q24 0 38 20 c14 -1 42 -18 52 -29 c-1 50 -26 68 -44 70 c0 90 -44 141 -88 141 c-44 0 -88 -51 -88 -141 c-18 -2 -43 -20 -44 -70 c10 11 38 28 52 29 q14 -20 38 -20z"/>
|
||||
<path fill="#000" transform="translate(0,60) rotate(-14)" d="m -22 46q1 50 22 50q21 0 22 -50z"/>
|
||||
|
||||
<path stroke="#FFF" stroke-width="4" fill="#000" d="m0 17 c 30 0 36 -2 46 -2 c 18 0 32 16 32 51 c 0 34 -12 50 -32 50 c-16 0 -26 -5 -46 -5 c-20 0 -30 5 -46 5 c-20 0 -33 -18 -32 -50 c0 -36 16 -52 32 -51 c10 0 16 2 46 2z"/>
|
||||
<circle fill="#FFF" cx="44" cy="49" r="12"/> <circle fill="#FFF" cx="-44" cy="49" r="12"/>
|
||||
|
||||
<circle fill="#FFF" cx="42" cy="-31" r="40"/> <circle fill="#000" cx="42" cy="-31" r="20.5"/>
|
||||
<circle fill="#FFF" cx="-42" cy="-31" r="40"/> <path fill="#000" transform="translate(-42,-31) rotate(55)" d="m-7 7 v20 q7 2 14 0 v-20 h20 q2 -7 0 -14 h-20 v-20 q -7 -2 -14 0 v20 h-20 q-2 7 0 14z"/>
|
||||
|
||||
</g></g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
logo/falky/oddmuse.icon.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
19
logo/falky/oddmuse.icon.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-100 -100 200 200" >
|
||||
|
||||
<rect fill="#D88" x="-100" y="-100" width="200" height="200"/>
|
||||
|
||||
<g id="new" transform="rotate(180) translate(0,-19)" >
|
||||
|
||||
<path fill="#A00" d="m42 -77 q18 0 33 14 q 12 -6 23 -16 q-2 25 -10 43 c0 110 -44 155 -88 155 c-44 0 -88 -44 -88 -155 q-8 -18 -10 -43 q11 10 23 16 q15 -14 33 -14z"/>
|
||||
|
||||
<path stroke="#FFF" stroke-width="6" fill="#A00" d="m0 16 c30 0 38 -2 48 -2 c17 0 33 16 33 52 c0 34 -12 52 -31 52 c-15 0 -30 -7 -50 -7 c-20 0 -35 7 -50 7 c-19 0 -31 -18 -31 -52 c0 -36 16 -52 33 -52 c10 0 18 2 48 2z"/>
|
||||
<circle fill="#FFF" cx="45" cy="49" r="12"/> <circle fill="#FFF" cx="-45" cy="49" r="12"/>
|
||||
|
||||
<circle fill="#FFF" cx="42" cy="-31" r="40"/> <circle fill="#000" cx="42" cy="-31" r="20.5"/>
|
||||
<circle fill="#FFF" cx="-42" cy="-31" r="40"/> <path fill="#666" transform="translate(-42,-31) rotate(55)" d="m-7 7 v20 q7 2 14 0 v-20 h20 q2 -7 0 -14 h-20 v-20 q -7 -2 -14 0 v20 h-20 q-2 7 0 14z"/>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
logo/falky/oddmuse.logo.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
22
logo/falky/oddmuse.logo.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-162 -162 324 324">
|
||||
|
||||
<circle fill="#FEE" stroke="#000" stroke-width="3" cx="0" cy="0" r="160.5"/>
|
||||
|
||||
<g id="new" transform="rotate(210)" >
|
||||
|
||||
<path fill="#A00" d="m42 -77 q24 0 38 20 c14 -1 42 -18 52 -29 c-1 50 -26 68 -44 70 c0 90 -44 141 -88 141 c-44 0 -88 -51 -88 -141 c-18 -2 -43 -20 -44 -70 c10 11 38 28 52 29 q14 -20 38 -20z"/>
|
||||
<path fill="#A00" transform="translate(0,60) rotate(-14)" d="m -22 46q1 50 22 50q21 0 22 -50z"/>
|
||||
|
||||
<path stroke="#FFF" stroke-width="4" fill="#D88" d="m0 17 c 30 0 36 -2 46 -2 c 18 0 32 16 32 51 c 0 34 -12 50 -32 50 c-16 0 -26 -5 -46 -5 c-20 0 -30 5 -46 5 c-20 0 -33 -18 -32 -50 c0 -36 16 -52 32 -51 c10 0 16 2 46 2z"/>
|
||||
<circle fill="#FFF" cx="44" cy="49" r="12"/> <circle fill="#FFF" cx="-44" cy="49" r="12"/>
|
||||
|
||||
<circle fill="#FFF" cx="42" cy="-31" r="40"/> <circle fill="#000" cx="42" cy="-31" r="20.5"/>
|
||||
<circle fill="#FFF" cx="-42" cy="-31" r="40"/> <path fill="#666" transform="translate(-42,-31) rotate(55)" d="m-7 7 v20 q7 2 14 0 v-20 h20 q2 -7 0 -14 h-20 v-20 q -7 -2 -14 0 v20 h-20 q-2 7 0 14z"/>
|
||||
|
||||
</g>
|
||||
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
24
logo/falky/oddmuse.siegel.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-162 -162 324 324">
|
||||
|
||||
<circle fill="#FFF" stroke="#000" stroke-width="3" cx="0" cy="0" r="160.5"/>
|
||||
|
||||
<text style="font:400 27px Neris Black" fill="#000" text-anchor="middle" x="-5" y="137">Oddmuse</text>
|
||||
|
||||
<g id="new" transform="rotate(210)" >
|
||||
|
||||
<path fill="#000" d="m42 -77 q24 0 38 20 c14 -1 42 -18 52 -29 c-1 50 -26 68 -44 70 c0 90 -44 141 -88 141 c-44 0 -88 -51 -88 -141 c-18 -2 -43 -20 -44 -70 c10 11 38 28 52 29 q14 -20 38 -20z"/>
|
||||
<path fill="#000" transform="translate(0,60) rotate(-14)" d="m -22 46q1 50 22 50q21 0 22 -50z"/>
|
||||
|
||||
<path stroke="#FFF" stroke-width="4" fill="#000" d="m0 17 c 30 0 36 -2 46 -2 c 18 0 32 16 32 51 c 0 34 -12 50 -32 50 c-16 0 -26 -5 -46 -5 c-20 0 -30 5 -46 5 c-20 0 -33 -18 -32 -50 c0 -36 16 -52 32 -51 c10 0 16 2 46 2z"/>
|
||||
<circle fill="#FFF" cx="44" cy="49" r="12"/> <circle fill="#FFF" cx="-44" cy="49" r="12"/>
|
||||
|
||||
<circle fill="#FFF" cx="42" cy="-31" r="40"/> <circle fill="#000" cx="42" cy="-31" r="20.5"/>
|
||||
<circle fill="#FFF" cx="-42" cy="-31" r="40"/> <path fill="#000" transform="translate(-42,-31) rotate(55)" d="m-7 7 v20 q7 2 14 0 v-20 h20 q2 -7 0 -14 h-20 v-20 q -7 -2 -14 0 v20 h-20 q-2 7 0 14z"/>
|
||||
|
||||
</g>
|
||||
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
logo/falky/preview/oddmuse-preview.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
logo/falky/preview/oddmuse.icon-preview.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
logo/falky/preview/oddmuse.org-preview.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
logo/falky/testmonial.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
4
logo/murray/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
The logo is licensed under the [GNU Free Documentation
|
||||
License](http://www.emacswiki.org/FDL) as well as the [CC BY
|
||||
2.0](http://creativecommons.org/licenses/by/2.0/) license. The author
|
||||
is [Murray Altheim](http://www.altheim.com/murray/).
|
||||
BIN
logo/murray/oddmuse-logo.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
100
logo/murray/oddmuse-logo.svg
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:version="0.41"
|
||||
sodipodi:docname="oddmuse-logo.svg"
|
||||
sodipodi:docbase="/Users/alex/Pictures/Oddmuse"
|
||||
height="1000pt"
|
||||
width="1000pt"
|
||||
sodipodi:version="0.32"
|
||||
id="svg1">
|
||||
<metadata
|
||||
id="metadata32">
|
||||
<rdf:RDF
|
||||
id="RDF34">
|
||||
<cc:Work
|
||||
id="Work36"
|
||||
rdf:about="">
|
||||
<dc:format
|
||||
id="format38">image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
|
||||
id="type40" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs3" />
|
||||
<sodipodi:namedview
|
||||
inkscape:current-layer="svg1"
|
||||
inkscape:window-y="22"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-height="535"
|
||||
inkscape:window-width="640"
|
||||
inkscape:cy="625.00000"
|
||||
inkscape:cx="625.00000"
|
||||
inkscape:zoom="0.28960000"
|
||||
id="base" />
|
||||
<g
|
||||
transform="translate(-155.7935,22.13440)"
|
||||
id="g176">
|
||||
<path
|
||||
transform="matrix(1.021740,0.000000,0.000000,1.021740,-299.4160,85.75640)"
|
||||
sodipodi:ry="148.93437"
|
||||
sodipodi:rx="142.30524"
|
||||
sodipodi:cy="475.27606"
|
||||
sodipodi:cx="1066.8474"
|
||||
d="M 1209.1526 475.27606 A 142.30524 148.93437 0 1 1 924.54218,475.27606 A 142.30524 148.93437 0 1 1 1209.1526 475.27606 z"
|
||||
id="path166"
|
||||
style="fill:#ffde00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:22.021299;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.591973,0.000000,0.000000,0.523224,159.0800,309.0390)"
|
||||
sodipodi:ry="148.93437"
|
||||
sodipodi:rx="142.30524"
|
||||
sodipodi:cy="475.27606"
|
||||
sodipodi:cx="1066.8474"
|
||||
d="M 1209.1526 475.27606 A 142.30524 148.93437 0 1 1 924.54218,475.27606 A 142.30524 148.93437 0 1 1 1209.1526 475.27606 z"
|
||||
id="path168"
|
||||
style="fill:#ffde00;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:38.609402;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
sodipodi:type="arc" />
|
||||
<rect
|
||||
transform="translate(-1.416020,0.000000)"
|
||||
y="561.16870"
|
||||
x="694.39026"
|
||||
height="88.691116"
|
||||
width="195.30150"
|
||||
id="rect170"
|
||||
style="font-size:12.000000;fill:#ffde00;fill-opacity:1.0000000;fill-rule:evenodd;stroke-width:1.0000000pt" />
|
||||
<g
|
||||
id="g173">
|
||||
<path
|
||||
transform="translate(185.0000,-158.7500)"
|
||||
sodipodi:ry="31.250000"
|
||||
sodipodi:rx="25.000000"
|
||||
sodipodi:cy="783.75000"
|
||||
sodipodi:cx="565.00000"
|
||||
d="M 590.00000 783.75000 A 25.000000 31.250000 0 1 1 540.00000,783.75000 A 25.000000 31.250000 0 1 1 590.00000 783.75000 z"
|
||||
id="path171"
|
||||
style="fill:#000000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="translate(266.2500,-158.7500)"
|
||||
sodipodi:ry="31.250000"
|
||||
sodipodi:rx="25.000000"
|
||||
sodipodi:cy="783.75000"
|
||||
sodipodi:cx="565.00000"
|
||||
d="M 590.00000 783.75000 A 25.000000 31.250000 0 1 1 540.00000,783.75000 A 25.000000 31.250000 0 1 1 590.00000 783.75000 z"
|
||||
id="path172"
|
||||
style="fill:#000000;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -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;
|
||||
|
||||
|
||||
72
modules/ban-mixed-scripts.pl
Normal file
@@ -0,0 +1,72 @@
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 Mixed Scripts
|
||||
|
||||
This module disallows ordinary users from posting words that consist of multiple
|
||||
scripts. Stuff like this: "It's diffіcult to find knowledgeable people on this
|
||||
topic, but youu sound like you know wgat you're taⅼkіng аboսt!" Did you notice
|
||||
the confusable characters? The sentence contains the following:
|
||||
ARMENIAN SMALL LETTER SEH
|
||||
CYRILLIC SMALL LETTER A
|
||||
CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
|
||||
SMALL ROMAN NUMERAL FIFTY
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use v5.10;
|
||||
|
||||
use Unicode::UCD qw(charprop);
|
||||
|
||||
AddModuleDescription('ban-mixed-scripts.pl', 'Ban Mixed Scripts Extension');
|
||||
|
||||
*OldBanMixedScriptsBannedContent = \&BannedContent;
|
||||
*BannedContent = \&NewBanMixedScriptsBannedContent;
|
||||
|
||||
sub NewBanMixedScriptsBannedContent {
|
||||
my $rule = OldBanMixedScriptsBannedContent(@_);
|
||||
$rule ||= BanMixedScript(@_);
|
||||
return $rule;
|
||||
}
|
||||
|
||||
sub BanMixedScript {
|
||||
my $str = shift;
|
||||
my @words = $str =~ m/\w+/g;
|
||||
my %seen;
|
||||
my %prop;
|
||||
for my $word (@words) {
|
||||
next if $seen{$word};
|
||||
$seen{$word} = 1;
|
||||
my $script;
|
||||
for my $char (split(//, $word)) {
|
||||
my $s = $prop{$char};
|
||||
if (not $s) {
|
||||
$s = charprop(ord($char), "Script_Extensions");
|
||||
if ($s eq 'Hiragana') {
|
||||
$s = 'Han'; # this mixing is ok
|
||||
}
|
||||
$prop{$char} = $s;
|
||||
}
|
||||
next if $s eq "Common";
|
||||
if (not $script) {
|
||||
$script = $s;
|
||||
} elsif ($script ne $s) {
|
||||
return "Mixed scripts in $word ($script and $s, if not more)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2012-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -62,14 +62,13 @@ push(@MyInitVariables, sub {
|
||||
|
||||
sub RegexpNewBannedContent {
|
||||
my $str = shift;
|
||||
# remove URLs as they are controlled by $BannedContent
|
||||
$str =~ s/$FullUrlPattern//g;
|
||||
# check whether Banned Content complains
|
||||
my $rule = RegexpOldBannedContent($str, @_);
|
||||
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
|
||||
|
||||
@@ -32,7 +32,9 @@ sub bbCodeRule {
|
||||
my $bbcode = $1;
|
||||
my $tag = lc($2);
|
||||
my $option = $3; # sanitize?
|
||||
if ($tag eq 'b') {
|
||||
if ($tag eq 'br') {
|
||||
return $q->br(); }
|
||||
elsif ($tag eq 'b') {
|
||||
return AddHtmlEnvironment('b'); }
|
||||
elsif ($tag eq 'i') {
|
||||
return AddHtmlEnvironment('i'); }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004, 2005, 2006 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2004–2023 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006 Ingo Belka
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -112,9 +112,7 @@ sub DoCollect {
|
||||
my $search = GetParam('search', '');
|
||||
ReportError(T('The match parameter is missing.')) unless $match or $search;
|
||||
print GetHeader('', Ts('Page Collection for %s', $match||$search), '');
|
||||
my @pages = (grep(/$match/, $search
|
||||
? SearchTitleAndBody($search)
|
||||
: AllPagesList()));
|
||||
my @pages = Matched($match, $search ? SearchTitleAndBody($search) : AllPagesList());
|
||||
if (!$CollectingJournal) {
|
||||
$CollectingJournal = 1;
|
||||
# Now save information required for saving the cache of the current page.
|
||||
|
||||
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) {
|
||||
|
||||
@@ -217,7 +217,7 @@ sub PrintPageContentCrossbar {
|
||||
|
||||
# If the crossbar div is placed immediately after the content div, place it
|
||||
# immediately before the content div.
|
||||
if (not ($html =~ s~(<div class="content browse">)$crossbar_pattern~$2$1~)) {
|
||||
if (not ($html =~ s~(<div class="content browse" lang="[a-z]*">)$crossbar_pattern~$2$1~)) {
|
||||
# Otherwise, if the crossbar div is placed immediately before the end of the
|
||||
# content div, place it immediately after the end of the content div.
|
||||
$html =~
|
||||
|
||||
37
modules/definition-lists.pl
Normal file
@@ -0,0 +1,37 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 2019 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('definition-lists.pl', 'Definition Lists Extension');
|
||||
|
||||
our ($q, $bol, @MyRules, @HtmlStack, $Fragment);
|
||||
|
||||
push(@MyRules, \&DefinitionListsRule);
|
||||
|
||||
# term
|
||||
# : definition
|
||||
|
||||
sub DefinitionListsRule {
|
||||
if ($bol and /\G(?:\s*\n)*(\S.*)\n[ \t]*:[ \t]*/cg) {
|
||||
return OpenHtmlEnvironment('dl', 1) . "<dt>$1</dt>" . AddHtmlEnvironment('dd');
|
||||
} elsif (InElement('dd') and /\G(?:\s*\n)+(\S.*)\n[ \t]*:[ \t]*/cg) {
|
||||
return OpenHtmlEnvironment('dl', 1) . "<dt>$1</dt>" . AddHtmlEnvironment('dd');
|
||||
} elsif (InElement('dd') and /\G(\s*\n)+[ \t]*:[ \t]*/cg) {
|
||||
return OpenHtmlEnvironment('dl', 1) . AddHtmlEnvironment('dd');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -35,6 +35,8 @@ push(@MyInitVariables, \&GotobarInit);
|
||||
sub GotobarInit {
|
||||
$GotobarName = FreeToNormal($GotobarName); # spaces to underscores
|
||||
$AdminPages{$GotobarName} = 1;
|
||||
my $id = GetId();
|
||||
my $encoded = UrlEncode($id);
|
||||
if ($IndexHash{$GotobarName}) {
|
||||
OpenPage($GotobarName);
|
||||
return if PageMarkedForDeletion();
|
||||
@@ -42,8 +44,11 @@ sub GotobarInit {
|
||||
# links for unsuspecting users.
|
||||
@UserGotoBarPages = ();
|
||||
$UserGotoBar = '';
|
||||
my $text = $Page{text};
|
||||
$text =~ s/\$\$id\b/$encoded/g;
|
||||
$text =~ s/\$id\b/$id/g;
|
||||
my $count = 0;
|
||||
while ($Page{text} =~ m/($LinkPattern|\[\[$FreeLinkPattern\]\]|\[\[$FreeLinkPattern\|([^\]]+)\]\]|\[$InterLinkPattern\s+([^\]]+?)\]|\[$FullUrlPattern[|[:space:]]([^\]]+?)\])/g) {
|
||||
while ($text =~ m/($LinkPattern|\[\[$FreeLinkPattern\]\]|\[\[$FreeLinkPattern\|([^\]]+)\]\]|\[$InterLinkPattern\s+([^\]]+?)\]|\[$FullUrlPattern[|[:space:]]([^\]]+?)\])/g) {
|
||||
my $page = $2||$3||$4||$6||$8;
|
||||
my $text = $5||$7||$9;
|
||||
$UserGotoBar .= ' ' if $UserGotoBar;
|
||||
@@ -57,10 +62,10 @@ sub GotobarInit {
|
||||
# is the list of recent changes.
|
||||
$count++;
|
||||
if ($count == 1) {
|
||||
unless ($GotobarSetHome) {$HomePage = FreeToNormal($page)};
|
||||
} elsif ($count == 2) {
|
||||
unless ($GotobarSetRC) {$RCName = FreeToNormal($page);}
|
||||
}
|
||||
unless ($GotobarSetHome) {$HomePage = FreeToNormal($page)};
|
||||
} elsif ($count == 2) {
|
||||
unless ($GotobarSetRC) {$RCName = FreeToNormal($page);}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
modules/grep-filtered.pl
Normal file
@@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2020 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2020 Daniel MacKay <daniel@bonmot.ca>
|
||||
#
|
||||
# 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('grep-filtered.pl', 'Grep Filtered');
|
||||
|
||||
our ($PageDir);
|
||||
our ($UseGrep);
|
||||
|
||||
$UseGrep = 1;
|
||||
|
||||
*OldGrepFiltered = \&Filtered;
|
||||
*Filtered = \&NewGrepFiltered;
|
||||
|
||||
sub NewGrepFiltered {
|
||||
my ($string, @pages) = @_;
|
||||
my @pages = OldGrepFiltered(@_);
|
||||
my $regexp = SearchRegexp($string);
|
||||
return @pages unless GetParam('grep', $UseGrep) and $regexp;
|
||||
my @result = grep(/$regexp/i, @pages); # search parameter for page titles
|
||||
my %found = map {$_ => 1} @result;
|
||||
$regexp =~ s/\\n(\)*)$/\$$1/g; # sometimes \n can be replaced with $
|
||||
$regexp =~ s/([?+{|()])/\\$1/g; # basic regular expressions from man grep
|
||||
# if we know of any remaining grep incompatibilities we should
|
||||
# return @pages here!
|
||||
$regexp = quotemeta($regexp);
|
||||
open(F, '-|:encoding(UTF-8)', "find $PageDir -type f -print0 | xargs -0 -n10 -P4 grep --ignore-case -l '$regexp'") ;
|
||||
while (<F>) {
|
||||
my ($pageName) = m/.*\/(.*)\.pg$/ ;
|
||||
push(@result, $pageName) if not $found{$pageName};
|
||||
} close(F);
|
||||
return sort @result;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ push(@MyRules, \&ImageSupportRule);
|
||||
|
||||
sub ImageSupportRule {
|
||||
my $result = undef;
|
||||
if (m!\G\[\[image((/[a-z]+)*)( external)?:\s*([^]|]+?)\s*(\|[^]|]+?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*\]\](\{([^}]+)\})?!cg) {
|
||||
if (m!\G\[\[image((/[-a-z]+)*)( external)?:\s*([^]|]+?)\s*(\|[^]|]+?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*(\|[^]|]*?)?\s*\]\](\{([^}]+)\})?!cg) {
|
||||
my $oldpos = pos;
|
||||
my $class = 'image' . $1;
|
||||
my $external = $3;
|
||||
@@ -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, 2007 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2004–2023 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
|
||||
@@ -47,8 +47,5 @@ sub PrintableIndexPages {
|
||||
push(@pages, AllPagesList()) if GetParam('pages', 1);
|
||||
push(@pages, keys %PermanentAnchors) if GetParam('permanentanchors', 1);
|
||||
push(@pages, keys %NearSource) if GetParam('near', 0);
|
||||
my $match = GetParam('match', '');
|
||||
@pages = grep /$match/i, @pages if $match;
|
||||
@pages = sort @pages;
|
||||
return @pages;
|
||||
return sort Matched(GetParam('match'), @pages);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004–2014 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004–2023 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
|
||||
@@ -30,7 +31,52 @@ sub DoJournalRss {
|
||||
local $CollectingJournal = 1;
|
||||
# Fake the result of GetRcLines()
|
||||
local *GetRcLines = \&JournalRssGetRcLines;
|
||||
print GetHttpHeader('application/xml') . GetRcRss();
|
||||
local *RcSelfWebsite = \&JournalRssSelfWebsite;
|
||||
local *RcSelfAction = \&JournalRssSelfAction;
|
||||
local *RcPreviousAction = \&JournalRssPreviousAction;
|
||||
local *RcLastAction = \&JournalRssLastAction;
|
||||
SetParam('full', 1);
|
||||
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 {
|
||||
my $more = '';
|
||||
foreach (@_, qw(rsslimit match search reverse monthly)) {
|
||||
my $val = GetParam($_, '');
|
||||
$more .= ";$_=" . UrlEncode($val) if $val;
|
||||
}
|
||||
return $more;
|
||||
}
|
||||
|
||||
sub JournalRssSelfWebsite {
|
||||
my $more = '';
|
||||
my $search = GetParam('rcfilteronly', '');
|
||||
$more .= ";search=" . UrlEncode($search) if $search;
|
||||
my $match = GetParam('match', '');
|
||||
$more .= ";match=" . UrlEncode($match) if $match;
|
||||
return $more;
|
||||
}
|
||||
|
||||
sub JournalRssSelfAction {
|
||||
return "action=journal" . JournalRssParameters(qw(offset));
|
||||
}
|
||||
|
||||
sub JournalRssPreviousAction {
|
||||
my $num = GetParam('rsslimit', 10);
|
||||
my $offset = GetParam('offset', 0) + $num;
|
||||
return "action=journal;offset=$offset" . JournalRssParameters();
|
||||
}
|
||||
|
||||
sub JournalRssLastAction {
|
||||
return "action=journal" . JournalRssParameters();
|
||||
}
|
||||
|
||||
sub JournalRssGetRcLines {
|
||||
@@ -39,7 +85,8 @@ sub JournalRssGetRcLines {
|
||||
my $search = GetParam('search', '');
|
||||
my $reverse = GetParam('reverse', 0);
|
||||
my $monthly = GetParam('monthly', 0);
|
||||
my @pages = sort JournalSort (grep(/$match/, $search ? SearchTitleAndBody($search) : AllPagesList()));
|
||||
my $offset = GetParam('offset', 0);
|
||||
my @pages = sort JournalSort (Matched($match, $search ? SearchTitleAndBody($search) : AllPagesList()));
|
||||
if ($monthly and not $match) {
|
||||
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime();
|
||||
$match = '^' . sprintf("%04d-%02d", $year+1900, $mon+1) . '-\d\d';
|
||||
@@ -48,28 +95,27 @@ sub JournalRssGetRcLines {
|
||||
@pages = reverse @pages;
|
||||
}
|
||||
# FIXME: Missing 'future' and 'past' keywords.
|
||||
# FIXME: Do we need 'offset'? I don't think so.
|
||||
my @result = ();
|
||||
my $n = 0;
|
||||
foreach my $id (@pages) {
|
||||
# Now save information required for saving the cache of the current page.
|
||||
local %Page;
|
||||
local $OpenPageName = '';
|
||||
OpenPage($id);
|
||||
# If this is a minor edit, ignore it. Load the last major revision
|
||||
# instead, if you can.
|
||||
if ($Page{minor}) {
|
||||
# Perhaps the old kept revision is gone due to $KeepMajor=0 or
|
||||
# admin.pl or because a page was created as a minor change and
|
||||
# never edited. Reading kept revisions in this case results in
|
||||
# an error.
|
||||
eval {
|
||||
%Page = GetKeptRevision($Page{lastmajor});
|
||||
};
|
||||
next if $@;
|
||||
# If this is a minor edit, let's keep everything as it is, but show the date
|
||||
# of the last major change, if possible. This is important for blogs that
|
||||
# get added to a Planet. A minor change doesn't mean that the page needs to
|
||||
# go to the front of the Planet.
|
||||
if ($Page{minor} and $Page{lastmajor}) {
|
||||
my %major = GetKeptRevision($Page{lastmajor});
|
||||
$Page{ts} = $major{ts} if $major{ts};
|
||||
}
|
||||
next if $Page{text} =~ /^\s*$/; # only whitespace is also to be deleted
|
||||
next if $DeletedPage && substr($Page{text}, 0, length($DeletedPage))
|
||||
eq $DeletedPage; # no regexp
|
||||
eq $DeletedPage; # no regexp
|
||||
# OK, this is a candidate page
|
||||
$n++;
|
||||
next if $n <= $offset;
|
||||
# Generate artifical rows in the list to pass to GetRcRss. We need
|
||||
# to open every single page, because the meta-data ordinarily
|
||||
# available in the rc.log file is not available to us. This is why
|
||||
@@ -79,7 +125,7 @@ sub JournalRssGetRcLines {
|
||||
push (@result, [$Page{ts}, $id, $Page{minor}, $Page{summary}, $Page{host},
|
||||
$Page{username}, $Page{revision}, \@languages,
|
||||
GetCluster($Page{text})]);
|
||||
last if $num ne 'all' and $#result + 1 >= $num;
|
||||
last if @result >= $num;
|
||||
}
|
||||
return @result;
|
||||
}
|
||||
|
||||
@@ -198,8 +198,7 @@ sub MakeLaTeX {
|
||||
}
|
||||
|
||||
if (not $error and IsFile('srender1.png') and not ZeroSize('srender1.png')) {
|
||||
my $png = ReadFileOrDie("srender1.png");
|
||||
WriteStringToFile ("$LatexDir/$hash.png", $png);
|
||||
Rename("srender1.png", "$LatexDir/$hash.png");
|
||||
} else {
|
||||
$error = "[Error retrieving image for $latex]";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2009–2015 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2009–2022 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2015 Aleks-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
@@ -230,6 +230,10 @@ sub MailMenu {
|
||||
ScriptLink('action=subscriptionlist',
|
||||
T('All mail subscriptions'),
|
||||
'subscriptionlist')) if UserIsAdmin();
|
||||
push(@$menuref,
|
||||
ScriptLink('action=subscribers',
|
||||
T('All mail subscribers'),
|
||||
'subscribers')) if UserIsAdmin();
|
||||
}
|
||||
|
||||
=head1 Your subscriptions
|
||||
@@ -283,9 +287,9 @@ sub MailSubscription {
|
||||
|
||||
=head1 Administrator Access
|
||||
|
||||
The subscriptionlist action will show you the subscription database,
|
||||
if you're an administrator. It's a plain text file of the data, which
|
||||
you can use for debugging and scripting purposes.
|
||||
The C<subscriptionlist> action will show you the subscription database, if
|
||||
you're an administrator. With the C<raw> parameter set it's a plain text file of
|
||||
the data, which you can use for debugging and scripting purposes.
|
||||
|
||||
=cut
|
||||
|
||||
@@ -327,6 +331,41 @@ sub MailLink {
|
||||
. join(';', map { "pages=$_" } @pages), $str);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
The C<subscribers> action lists each unique email address for easier mass
|
||||
unsubscribing of email addresses after a wave of wiki spam.
|
||||
|
||||
=cut
|
||||
|
||||
$Action{subscribers} = \&DoMailSubscribers;
|
||||
|
||||
sub DoMailSubscribers {
|
||||
UserIsAdminOrError();
|
||||
my $raw = GetParam('raw', 0);
|
||||
if ($raw) {
|
||||
print GetHttpHeader('text/plain');
|
||||
} else {
|
||||
print GetHeader('', T('Subscriptions')),
|
||||
$q->start_div({-class=>'content subscribtionlist'}),
|
||||
$q->p(T('Mail addresses are linked to unsubscription links.')),
|
||||
'<ul>';
|
||||
}
|
||||
my %authors;
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
for my $author (sort grep /\@/, map { UrlDecode($_) } keys %h) {
|
||||
if ($raw) {
|
||||
print "$author\n";
|
||||
} else {
|
||||
print $q->li(ScriptLink("action=unsubscribe;who=$author", $author));
|
||||
}
|
||||
}
|
||||
print '</ul></div>' unless $raw;
|
||||
PrintFooter() unless $raw;
|
||||
untie %h;
|
||||
}
|
||||
|
||||
=head1 Subscription
|
||||
|
||||
The subscribe action will subscribe you to pages. The mail parameter
|
||||
@@ -402,13 +441,13 @@ sub MailSubscribe {
|
||||
|
||||
=head1 Unsubscription
|
||||
|
||||
The unsubscribe action will unsubscribe you from pages. The mail
|
||||
parameter contains the mail address to use and defaults to the value
|
||||
store in your cookie. Multiple pages parameters contain the pages to
|
||||
unsubscribe.
|
||||
The unsubscribe action will unsubscribe you from pages. The mail parameter
|
||||
contains the mail address to use and defaults to the value store in your cookie.
|
||||
Multiple pages parameters contain the pages to unsubscribe. Without naming
|
||||
pages, you will be unsubscribed from all pages.
|
||||
|
||||
The who parameter overrides the mail parameter and is used for
|
||||
administrator unsubscription from the subscriptionlist action.
|
||||
The who parameter overrides the mail parameter and is used for administrator
|
||||
unsubscription from the subscriptionlist action.
|
||||
|
||||
=cut
|
||||
|
||||
@@ -416,21 +455,20 @@ $Action{unsubscribe} = \&DoMailUnsubscribe;
|
||||
|
||||
sub DoMailUnsubscribe {
|
||||
my $mail = GetParam('who', GetParam('mail', ''));
|
||||
return DoMailSubscriptions(@_) unless $mail;
|
||||
local $CGI::LIST_CONTEXT_WARN = 0;
|
||||
my @pages = $q->param('pages');
|
||||
return DoMailSubscriptions(@_) unless $mail;
|
||||
my @real = ();
|
||||
foreach my $id (@pages) {
|
||||
push @real, $id if $IndexHash{$id};
|
||||
}
|
||||
MailUnsubscribe($mail, @real);
|
||||
MailUnsubscribe($mail, @pages);
|
||||
# MailUnsubscribe will set a parameter and must run before printing
|
||||
# the header.
|
||||
print GetHeader('', T('Subscriptions')),
|
||||
$q->start_div({-class=>'content unsubscribe'});
|
||||
print $q->p(Ts('Unsubscribed %s from the following pages:', $mail));
|
||||
print $q->ul($q->li([map { GetPageLink($_) } @real]));
|
||||
print $q->p(T('The remaining pages do not exist.')) if $#real < $#pages;
|
||||
$q->start_div({-class=>'content unsubscribe'});
|
||||
if (@pages) {
|
||||
print $q->p(Ts('Unsubscribed %s from the following pages:', $mail));
|
||||
print $q->ul($q->li([map { GetPageLink($_) } @pages]));
|
||||
} else {
|
||||
print $q->p(Ts('Unsubscribed %s from all pages.', $mail));
|
||||
}
|
||||
print $q->p(ScriptLink('action=subscriptions', T('Your mail subscriptions'),
|
||||
'subscriptions') . '.');
|
||||
print $q->end_div();
|
||||
@@ -439,10 +477,11 @@ sub DoMailUnsubscribe {
|
||||
|
||||
sub MailUnsubscribe {
|
||||
my ($mail, @pages) = @_;
|
||||
return unless $mail and @pages;
|
||||
return unless $mail;
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
|
||||
@pages = keys %subscriptions unless @pages;
|
||||
foreach my $id (@pages) {
|
||||
delete $subscriptions{$id};
|
||||
# take care of reverse lookup
|
||||
@@ -466,9 +505,9 @@ sub MailUnsubscribe {
|
||||
|
||||
=head1 Migrate
|
||||
|
||||
The mailmigrate action will migrate your subscription list from the
|
||||
old format to the new format. This is necessary because these days
|
||||
because the keys and values of the DB_File are URL encoded.
|
||||
The mailmigrate action will migrate your subscription list from the old format
|
||||
to the new format. This is necessary because these days the keys and values of
|
||||
the DB_File are URL encoded.
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
108
modules/markdown-converter.pl
Normal file
@@ -0,0 +1,108 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use strict;
|
||||
use v5.10;
|
||||
use utf8;
|
||||
|
||||
AddModuleDescription('markdown-converter.pl', 'Markdown Convert');
|
||||
|
||||
our (%Action, @MyAdminCode, $q, $OpenPageName, %Page, @MyRules);
|
||||
|
||||
push(@MyAdminCode, \&MarkdownConvertMenu);
|
||||
|
||||
sub MarkdownConvertMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
my $name = $id;
|
||||
$name =~ s/_/ /g;
|
||||
if ($id) {
|
||||
push(@$menuref, ScriptLink('action=convert;id=' . $id, Ts('Help convert %s to Markdown', $name), 'convert'));
|
||||
}
|
||||
push(@$menuref, ScriptLink('action=conversion-candidates', Ts('List all non-Markdown pages'), 'convert'));
|
||||
}
|
||||
|
||||
$Action{convert} = \&MarkdownConvert;
|
||||
|
||||
# some text that doesn't start and end with a space, or just one non-space
|
||||
sub MarkdownConvertString {
|
||||
my $c = shift;
|
||||
return qr"([^\\$c \n][^\\$c\n]*[^\\$c \n]|[^\\$c \n])";
|
||||
}
|
||||
|
||||
sub MarkdownConvert {
|
||||
my $id = GetParam('id', '');
|
||||
ValidIdOrDie($id);
|
||||
print GetHeader('', Ts('Converting %s', $id), '');
|
||||
$_ = GetPageContent($id);
|
||||
|
||||
s/^\{\{\{((?:.*\n)+?)\}\}\}$/```$1```/gm;
|
||||
s/\{\{\{(.+?)\}\}\}/`$1`/gm;
|
||||
|
||||
my $s = MarkdownConvertString('*');
|
||||
s/\*$s\*/**$1**/g;
|
||||
|
||||
# avoid URL schemas like http://example.org
|
||||
$s = MarkdownConvertString('/');
|
||||
s#(?<!:/)/$s/#*$1*#g;
|
||||
s#(?<!:)//$s//#*$1*#g;
|
||||
|
||||
|
||||
s/^# /1. /gm;
|
||||
|
||||
s/##(.*?)##/`$1`/g;
|
||||
|
||||
s/^(=+) *(.*?) ?=*$/'#' x length($1) . ' ' . $2/gme;
|
||||
|
||||
s!\[(https?://\S+) (.*?)\]!g;
|
||||
|
||||
return DoEdit($id, "#MARKDOWN\n" . $_, 1); # preview
|
||||
}
|
||||
|
||||
$Action{'conversion-candidates'} = \&MarkdownConversionCandidates;
|
||||
|
||||
sub MarkdownConversionCandidates {
|
||||
# from Search
|
||||
print GetHeader('', Ts('Candidates for Conversion to Markdown'));
|
||||
print $q->start_div({-class=>'content'});
|
||||
print $q->start_ol();
|
||||
# from SearchTitleAndBody
|
||||
my $regex = qr'^(?!#MARKDOWN)';
|
||||
foreach my $id (Filtered($regex, AllPagesList())) {
|
||||
my $name = NormalToFree($id);
|
||||
my ($text) = PageIsUploadedFile($id); # set to mime-type if this is an uploaded file
|
||||
local ($OpenPageName, %Page); # this is local!
|
||||
if (not $text) { # not uploaded file, therefore allow searching of page body
|
||||
OpenPage($id); # this opens a page twice if it is not uploaded, but that's ok
|
||||
if ($Page{text} =~ /$regex/) {
|
||||
my $action = 'action=convert;id=' . UrlEncode($id);
|
||||
my $name = NormalToFree($id);
|
||||
print $q->li(GetPageLink($id, $name) . ' – '
|
||||
. ScriptLink($action, Ts('Help convert %s to Markdown', $name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
print $q->end_ol();
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
push(@MyRules, \&MarkdownConvertRule);
|
||||
|
||||
sub MarkdownConvertRule {
|
||||
if (pos == 0 and /\G#MARKDOWN\n/gc) {
|
||||
return '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 2014–2017 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2014–2022 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
|
||||
@@ -18,7 +18,7 @@ use v5.10;
|
||||
|
||||
AddModuleDescription('markdown-rule.pl', 'Markdown Rule Extension');
|
||||
|
||||
our ($q, $bol, %RuleOrder, @MyRules, $UrlProtocols, $FullUrlPattern, @HtmlStack);
|
||||
our ($q, $bol, %RuleOrder, @MyRules, $UrlProtocols, $FullUrlPattern, @HtmlStack, $Fragment);
|
||||
|
||||
push(@MyRules, \&MarkdownRule);
|
||||
# Since we want this package to be a simple add-on, we try and avoid
|
||||
@@ -31,6 +31,7 @@ $RuleOrder{\&MarkdownRule} = 200;
|
||||
# https://help.github.com/articles/github-flavored-markdown
|
||||
|
||||
sub MarkdownRule {
|
||||
my $alignment;
|
||||
# \escape
|
||||
if (m/\G\\([-#>*`=])/cg) {
|
||||
return $1;
|
||||
@@ -50,9 +51,28 @@ sub MarkdownRule {
|
||||
}
|
||||
# > blockquote
|
||||
# with continuation
|
||||
elsif ($bol and m/\G>/cg) {
|
||||
return CloseHtmlEnvironments()
|
||||
. AddHtmlEnvironment('blockquote');
|
||||
elsif ($bol and m/\G((?:>.*\n?)+)/cg) {
|
||||
Clean(CloseHtmlEnvironments());
|
||||
Dirty($1);
|
||||
my $text = $1;
|
||||
my ($oldpos, $old_) = ((pos), $_);
|
||||
print '<blockquote>';
|
||||
$text =~ s/^> ?//gm;
|
||||
ApplyRules($text, 1, 1, undef, 'p'); # local links, anchors, no revision, start with p
|
||||
print '</blockquote>';
|
||||
Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
|
||||
($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
|
||||
}
|
||||
# """ = blockquote, too
|
||||
elsif ($bol and m/\G("""[ \t]*\n(.*?)\n"""[ \t]*(?:\n|$))/cgs) {
|
||||
Clean(CloseHtmlEnvironments());
|
||||
Dirty($1);
|
||||
my ($oldpos, $old_) = ((pos), $_);
|
||||
print '<blockquote>';
|
||||
ApplyRules($2, 1, 1, undef, 'p'); # local links, anchors, no revision, start with p
|
||||
print '</blockquote>';
|
||||
Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
|
||||
($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
|
||||
}
|
||||
# ``` = code
|
||||
elsif ($bol and m/\G```[ \t]*\n(.*?)\n```[ \t]*(\n|$)/cgs) {
|
||||
@@ -123,34 +143,47 @@ sub MarkdownRule {
|
||||
# beginning of a table
|
||||
elsif ($bol and !InElement('table') and m/\G\|/cg) {
|
||||
# warn pos . " beginning of a table";
|
||||
$alignment = 'style="text-align: right"' if m/\G([ \t]+)/cg;
|
||||
$alignment = 'style="text-align: center"' if $alignment and m/\G(?=[^|]+[ \t]+\|)/cg;
|
||||
$Fragment =~ s/[ \t]+$//; # cleanup trailing whitespace if previous column was centered
|
||||
return OpenHtmlEnvironment('table',1)
|
||||
. AddHtmlEnvironment('tr')
|
||||
. AddHtmlEnvironment('th');
|
||||
. AddHtmlEnvironment('th', $alignment);
|
||||
}
|
||||
# end of a row and beginning of a new row
|
||||
elsif (InElement('table') and m/\G\|?\n\|/cg) {
|
||||
# warn pos . " end of a row and beginning of a new row";
|
||||
$alignment = 'style="text-align: right"' if m/\G([ \t]+)/cg;
|
||||
$alignment = 'style="text-align: center"' if $alignment and m/\G(?=[^|]+[ \t]+\|)/cg;
|
||||
$Fragment =~ s/[ \t]+$//; # cleanup trailing whitespace if previous column was centered
|
||||
return CloseHtmlEnvironment('tr')
|
||||
. AddHtmlEnvironment('tr')
|
||||
. AddHtmlEnvironment('td');
|
||||
. AddHtmlEnvironment('td', $alignment);
|
||||
}
|
||||
# otherwise the table ends
|
||||
elsif (InElement('table') and m/\G\|?(\n|$)/cg) {
|
||||
# warn pos . " otherwise the table ends";
|
||||
$Fragment =~ s/[ \t]+$//; # cleanup trailing whitespace if previous column was centered
|
||||
return CloseHtmlEnvironment('table')
|
||||
. AddHtmlEnvironment('p');
|
||||
}
|
||||
# continuation of the first row
|
||||
elsif (InElement('th') and m/\G\|/cg) {
|
||||
# warn pos . " continuation of the first row";
|
||||
$alignment = 'style="text-align: right"' if m/\G([ \t]+)/cg;
|
||||
$alignment = 'style="text-align: center"' if $alignment and m/\G(?=[^|]+[ \t]+\|)/cg;
|
||||
$Fragment =~ s/[ \t]+$//; # cleanup trailing whitespace if previous column was centered
|
||||
return CloseHtmlEnvironment('th')
|
||||
. AddHtmlEnvironment('th');
|
||||
. AddHtmlEnvironment('th', $alignment);
|
||||
}
|
||||
# continuation of other rows
|
||||
elsif (InElement('td') and m/\G\|/cg) {
|
||||
# warn pos . " continuation of other rows";
|
||||
$alignment = 'style="text-align: right"' if m/\G([ \t]+)/cg;
|
||||
$alignment = 'style="text-align: center"' if $alignment and m/\G(?=[^|]+[ \t]+\|)/cg;
|
||||
$Fragment =~ s/[ \t]+$//; # cleanup trailing whitespace if previous column was centered
|
||||
return CloseHtmlEnvironment('td')
|
||||
. AddHtmlEnvironment('td');
|
||||
. AddHtmlEnvironment('td', $alignment);
|
||||
}
|
||||
# whitespace indentation = code
|
||||
elsif ($bol and m/\G(\s*\n)*( .+)\n?/cg) {
|
||||
@@ -160,13 +193,21 @@ sub MarkdownRule {
|
||||
}
|
||||
return OpenHtmlEnvironment('pre',1) . $str; # always level 1
|
||||
}
|
||||
# [an example](http://example.com/ "Title")
|
||||
elsif (m/\G\[(.+?)\]\($FullUrlPattern(\s+"(.+?)")?\)/cg) {
|
||||
# link: [an example](http://example.com/ "Title")
|
||||
elsif (m/\G\[((?:[^]\n]+\n?)+)\]\((\S+)(\s+"(.+?)")?\)/cg) {
|
||||
my ($text, $url, $title) = ($1, $2, $4);
|
||||
$url =~ /^($UrlProtocols)/;
|
||||
my %params;
|
||||
$params{-href} = $url;
|
||||
$params{-class} = "url $1";
|
||||
$params{-class} = "url";
|
||||
$params{-title} = $title if $title;
|
||||
return $q->a(\%params, $text);
|
||||
}
|
||||
# link: [an example](#foo "Title")
|
||||
elsif (m/\G\[((?:[^]\n]+\n?)+)\]\((#\S)+(\s+"(.+?)")?\)/cg) {
|
||||
my ($text, $url, $title) = ($1, $2, $4);
|
||||
my %params;
|
||||
$params{-href} = $url;
|
||||
$params{-class} = "named-anchor";
|
||||
$params{-title} = $title if $title;
|
||||
return $q->a(\%params, $text);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004–2022 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
|
||||
@@ -42,10 +42,18 @@ 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);
|
||||
|
||||
$NamespacesMain = 'Main'; # to get back to the main namespace
|
||||
$NamespacesSelf = 'Self'; # for your own namespace
|
||||
@@ -110,6 +118,7 @@ sub NamespacesInitVariables {
|
||||
}
|
||||
}
|
||||
$NamespaceRoot = $ScriptName; # $ScriptName may be changed below
|
||||
$NamespacesRootDataDir = $DataDir; # $DataDir may be chanegd below
|
||||
$NamespaceCurrent = '';
|
||||
my $ns = GetNamespace();
|
||||
if ($ns
|
||||
@@ -117,7 +126,7 @@ 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";
|
||||
@@ -162,6 +171,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
|
||||
|
||||
@@ -312,9 +312,7 @@ resolved to the same target (the local page), which is unexpected.
|
||||
# IndexOptions must be set in MyInitVariables for translations to
|
||||
# work.
|
||||
push(@MyInitVariables, sub {
|
||||
push(@IndexOptions, ['near', T('Include near pages'), 0,
|
||||
\&ListNearPages]);
|
||||
});
|
||||
push(@IndexOptions, ['near', T('Include near pages'), 0, \&ListNearPages])});
|
||||
|
||||
sub ListNearPages {
|
||||
my %pages = %NearSource;
|
||||
|
||||
198
modules/network-blocker.pl
Normal file
@@ -0,0 +1,198 @@
|
||||
# -*- mode: perl -*-
|
||||
# Copyright (C) 2023 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero 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 Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Oddmuse Network Blocker
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module hooks into regular Oddmuse Surge Protection. It adds the following
|
||||
features:
|
||||
|
||||
Repeated offenders are blocked for increasingly longer times.
|
||||
|
||||
For every offender, we record the CIDR their IP number belongs to. Everytime an
|
||||
IP number is blocked, all the CIDRs of the other blocked IPs are checked: if
|
||||
there are three or more blocked IP numbers sharing the same CIDRs, the CIDR
|
||||
itself is blocked.
|
||||
|
||||
CIDR blocking works the same way: Repeated offenders are blocked for
|
||||
increasingly longer times.
|
||||
|
||||
=head2 Behind a reverse proxy
|
||||
|
||||
Make sure your config file copies the IP number to the correct environment
|
||||
variable:
|
||||
|
||||
$ENV{REMOTE_ADDR} = $ENV{HTTP_X_FORWARDED_FOR};
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
<Oddmuse Surge Protection|https://oddmuse.org/wiki/Surge_Protection>
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use v5.10;
|
||||
use Net::IP qw(:PROC);
|
||||
use Net::DNS qw(rr);
|
||||
|
||||
AddModuleDescription('network-blocker.pl', 'Network Blocker Extension');
|
||||
|
||||
our ($Now, $DataDir, $SurgeProtectionViews, $SurgeProtectionTime);
|
||||
|
||||
{
|
||||
no warnings 'redefine';
|
||||
*OldNetworkBlockerDelayRequired = \&DelayRequired;
|
||||
*DelayRequired = \&NewNetworkBlockerDelayRequired;
|
||||
}
|
||||
|
||||
# Block for at least this many seconds.
|
||||
my $NetworkBlockerMinimumPeriod = 30;
|
||||
|
||||
# Every violation doubles the current period until this maximum is reached (four weeks).
|
||||
my $NetworkBlockerMaximumPeriod = 60 * 60 * 24 * 7 * 4;
|
||||
|
||||
# All the blocked networks. Maps CIDR to an array [expiry timestamp, expiry
|
||||
# period].
|
||||
my %NetworkBlockerList;
|
||||
|
||||
# Candidates are remembered for this many seconds.
|
||||
my $NetworkBlockerCachePeriod = 600;
|
||||
|
||||
# All the candidate networks for a block. Maps IP to an array [ts, cidr, ...].
|
||||
# Candidates are removed after $NetworkBlockerCachePeriod.
|
||||
my %NetworkBlockerCandidates;
|
||||
|
||||
sub NetworkBlockerRead {
|
||||
my ($status, $data) = ReadFile("$DataDir/network-blocks");
|
||||
return unless $status;
|
||||
my @lines = split(/\n/, $data);
|
||||
while ($_ = shift(@lines)) {
|
||||
my @items = split(/,/);
|
||||
$NetworkBlockerList{shift(@items)} = \@items;
|
||||
}
|
||||
# an empty line separates the two sections
|
||||
while ($_ = shift(@lines)) {
|
||||
my @items = split(/,/);
|
||||
$NetworkBlockerCandidates{shift(@items)} = \@items;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub NetworkBlockerWrite {
|
||||
RequestLockDir('network-blocks') or return '';
|
||||
WriteStringToFile(
|
||||
"$DataDir/network-blocks",
|
||||
join("\n\n",
|
||||
join("\n", map {
|
||||
join(",", $_, @{$NetworkBlockerList{$_}})
|
||||
} keys %NetworkBlockerList),
|
||||
join("\n", map {
|
||||
join(",", $_, @{$NetworkBlockerCandidates{$_}})
|
||||
} keys %NetworkBlockerCandidates)));
|
||||
ReleaseLockDir('network-blocks');
|
||||
}
|
||||
|
||||
sub NewNetworkBlockerDelayRequired {
|
||||
my $ip = shift;
|
||||
# If $ip is a name and not an IP number, parsing fails. In this case, run the
|
||||
# regular code.
|
||||
my $ob = new Net::IP($ip);
|
||||
return OldNetworkBlockerDelayRequired($ip) unless $ob;
|
||||
# Read the file. If the file does not exist, no problem.
|
||||
NetworkBlockerRead();
|
||||
# See if the current IP number is one of the blocked CIDR ranges.
|
||||
for my $cidr (keys %NetworkBlockerList) {
|
||||
# Perhaps this CIDR block can be expired.
|
||||
if ($NetworkBlockerList{$cidr}->[0] < $Now) {
|
||||
delete $NetworkBlockerList{$cidr};
|
||||
next;
|
||||
}
|
||||
# Forget the CIDR if it cannot be turned into a range.
|
||||
my $range = new Net::IP($cidr);
|
||||
if (not $range) {
|
||||
warn "CIDR $cidr is blocked but has no range: " . Net::IP::Error();
|
||||
delete $NetworkBlockerList{$cidr};
|
||||
next;
|
||||
}
|
||||
# If the CIDR overlaps with the remote IP number, it's a block.
|
||||
warn "Checking whether $ip is in $cidr\n";
|
||||
my $overlap = $range->overlaps($ob);
|
||||
# $IP_PARTIAL_OVERLAP (ranges overlap) $IP_NO_OVERLAP (no overlap)
|
||||
# $IP_A_IN_B_OVERLAP (range2 contains range1) $IP_B_IN_A_OVERLAP (range1
|
||||
# contains range2) $IP_IDENTICAL (ranges are identical) undef (problem)
|
||||
if (defined $overlap and $overlap != $IP_NO_OVERLAP) {
|
||||
# Double the block period unless it has reached $NetworkBlockerMaximumPeriod.
|
||||
if ($NetworkBlockerList{$cidr}->[1] < $NetworkBlockerMaximumPeriod / 2) {
|
||||
$NetworkBlockerList{$cidr}->[1] *= 2;
|
||||
} else {
|
||||
$NetworkBlockerList{$cidr}->[1] = $NetworkBlockerMaximumPeriod;
|
||||
}
|
||||
$NetworkBlockerList{$cidr}->[0] = $Now + $NetworkBlockerList{$cidr}->[1];
|
||||
# And we're done!
|
||||
NetworkBlockerWrite();
|
||||
ReportError(Ts('Too many connections by %s', $cidr)
|
||||
. ': ' . Tss('Please do not fetch more than %1 pages in %2 seconds.',
|
||||
$SurgeProtectionViews, $SurgeProtectionTime),
|
||||
'503 SERVICE UNAVAILABLE');
|
||||
}
|
||||
}
|
||||
# If the CIDR isn't blocked, let's see if Surge Protection wants to block it.
|
||||
my $result = OldNetworkBlockerDelayRequired($ip);
|
||||
warn "$ip was blocked\n" if $result;
|
||||
# If the IP is to be blocked, determine its CIDRs and put them on a list. Sadly,
|
||||
# routeviews does not support IPv6 at the moment!
|
||||
if ($result and not ip_is_ipv6($ip) and not $NetworkBlockerCandidates{$ip}) {
|
||||
my $reverse = $ob->reverse_ip();
|
||||
$reverse =~ s/in-addr\.arpa\.$/asn.routeviews.org/;
|
||||
my @candidates;
|
||||
for my $rr (rr($reverse, "TXT")) {
|
||||
next unless $rr->type eq "TXT";
|
||||
my @data = $rr->txtdata;
|
||||
push(@candidates, join("/", @data[1..2]));
|
||||
}
|
||||
warn "$ip is in @candidates\n";
|
||||
$NetworkBlockerCandidates{$ip} = [$Now, @candidates];
|
||||
# Expire any of the other candidates
|
||||
for my $other_ip (keys %NetworkBlockerCandidates) {
|
||||
if ($NetworkBlockerCandidates{$other_ip}->[0] < $Now - $NetworkBlockerCachePeriod) {
|
||||
delete $NetworkBlockerCandidates{$other_ip};
|
||||
}
|
||||
}
|
||||
# Determine if any of the CIDRs is to be blocked.
|
||||
my $save;
|
||||
for my $cidr (@candidates) {
|
||||
# Count how often the candidate CIDRs show up for other IP numbers.
|
||||
my $count = 0;
|
||||
for my $other_ip (keys %NetworkBlockerCandidates) {
|
||||
my @data = $NetworkBlockerCandidates{$other_ip};
|
||||
for my $other_cidr (@data[1 .. $#data]) {
|
||||
$count++ if $cidr eq $other_cidr;
|
||||
}
|
||||
}
|
||||
if ($count >= 3) {
|
||||
$NetworkBlockerList{$cidr} = [$Now + $NetworkBlockerMinimumPeriod, $NetworkBlockerMinimumPeriod];
|
||||
$save = 1;
|
||||
}
|
||||
}
|
||||
NetworkBlockerWrite() if $save;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
# Copyright (C) 2005 Flavio Poletti <flavio@polettix.it>
|
||||
# Copyright (C) 2005 Flavio Poletti <flavio@polettix.it>
|
||||
# Copyright (C) 2014-2015 Alex Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2014-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -48,7 +50,7 @@ AddModuleDescription('olocalmap.pl');
|
||||
push(@MyInitVariables, \&InitLocalMap);
|
||||
|
||||
sub InitLocalMap {
|
||||
my $id = GetCurrentPageName();
|
||||
my $id = GetId();
|
||||
my $action = lc(GetParam('action', ''));
|
||||
AllPagesList(); # Build %IndexHash
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ sub NewPermanentAnchorsDeletePage {
|
||||
my $status = OldPermanentAnchorsDeletePage(@_);
|
||||
return $status if $status; # this would be the error message
|
||||
DeletePermanentAnchors(@_); # the only parameter is $id
|
||||
return ''; # no errors
|
||||
}
|
||||
|
||||
*OldPermanentAnchorsSave = \&Save;
|
||||
@@ -261,12 +262,15 @@ Some user interface changes are required as well.
|
||||
=item *
|
||||
|
||||
Allow the page index to list permanent anchors or not by setting
|
||||
C<@IndexOptions>.
|
||||
C<@IndexOptions>. Note that we need to delay setting this option until we're
|
||||
sure that translations have loaded correctly, which is why we're setting
|
||||
C<@IndexOptions> as part of running C<@MyInitVariables>.
|
||||
|
||||
=cut
|
||||
|
||||
push(@IndexOptions, ['permanentanchors', T('Include permanent anchors'),
|
||||
1, sub { keys %PermanentAnchors }]);
|
||||
push(@MyInitVariables, sub {
|
||||
push(@IndexOptions, ['permanentanchors', T('Include permanent anchors'),
|
||||
1, sub { keys %PermanentAnchors }])});
|
||||
|
||||
=item *
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2004 Brock Wilcox <awwaiid@thelackthereof.org>
|
||||
# Copyright (C) 2019 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -16,152 +17,124 @@
|
||||
use strict;
|
||||
use v5.10;
|
||||
|
||||
use LWP::UserAgent; # This one will one day be eliminated! Hopefully!
|
||||
|
||||
# Need these to do pingback
|
||||
use RPC::XML;
|
||||
use LWP::UserAgent;
|
||||
use RPC::XML::Parser;
|
||||
use RPC::XML;
|
||||
|
||||
AddModuleDescription('pingback-server.pl', 'Pingback Server Extension');
|
||||
|
||||
our ($CommentsPrefix);
|
||||
# Specification: http://www.hixie.ch/specs/pingback/pingback
|
||||
# XML-RPC errors: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
|
||||
|
||||
*OldPingbackServerGetHtmlHeader = \&GetHtmlHeader;
|
||||
*GetHtmlHeader = \&NewPingbackServerGetHtmlHeader;
|
||||
our ($CommentsPrefix, $q, $HtmlHeaders, %Action, $QuestionaskerSecretKey,
|
||||
@MyInitVariables, %IndexHash);
|
||||
|
||||
# Add the <link ...> to the header
|
||||
sub NewPingbackServerGetHtmlHeader {
|
||||
my ($title, $id) = @_;
|
||||
my $header = OldPingbackServerGetHtmlHeader($title,$id);
|
||||
my $pingbackLink =
|
||||
'<link rel="pingback" '
|
||||
. 'href="http://thelackthereof.org/wiki.pl?action=pingback;id='
|
||||
. $id . '">';
|
||||
$header =~ s/<head>/<head>$pingbackLink/;
|
||||
return $header;
|
||||
push(@MyInitVariables, \&PingbackServerAddLink);
|
||||
|
||||
sub PingbackServerAddLink {
|
||||
SetParam('action', 'pingback') if $q->path_info =~ m|/pingback\b|;
|
||||
my $id = GetId();
|
||||
return unless $id;
|
||||
return if $id =~ /^$CommentsPrefix/;
|
||||
my $link = '<link rel="alternate" type="application/wiki" href="'
|
||||
. ScriptUrl('pingback/' . UrlEncode($id)) . '" />';
|
||||
$HtmlHeaders .= $link unless index($HtmlHeaders, /$link/) != -1;
|
||||
}
|
||||
|
||||
*OldPingbackServerInitRequest = \&InitRequest;
|
||||
*InitRequest = \&NewPingbackServerInitRequest;
|
||||
|
||||
sub NewPingbackServerInitRequest {
|
||||
if($ENV{'QUERY_STRING'} =~ /action=pingback;id=(.*)/) {
|
||||
my $id = $1;
|
||||
DoPingbackServer($id);
|
||||
exit 0;
|
||||
} else {
|
||||
return OldPingbackServerInitRequest(@_);
|
||||
}
|
||||
}
|
||||
$Action{pingback} = \&DoPingbackServer;
|
||||
|
||||
sub DoPingbackServer {
|
||||
my $id = FreeToNormal(shift);
|
||||
|
||||
|
||||
if ($ENV{'REQUEST_METHOD'} ne 'POST') {
|
||||
result('405 Method Not Allowed', -32300,
|
||||
'Only XML-RPC POST requests recognised.', 'Allow: POST');
|
||||
# some sanity checks for the request
|
||||
if ($q->request_method() ne 'POST') {
|
||||
ReportError(T('Only XML-RPC POST requests recognised'), '405 METHOD NOT ALLOWED');
|
||||
}
|
||||
if ($q->content_type() ne 'text/xml') {
|
||||
ReportError(T('Only XML-RPC POST requests recognised'), '415 UNSUPPORTED MEDIA TYPE');
|
||||
}
|
||||
|
||||
if ($ENV{'CONTENT_TYPE'} ne 'text/xml') {
|
||||
result('415 Unsupported Media Type', -32300,
|
||||
'Only XML-RPC POST requests recognised.');
|
||||
# some sanity checks for the target page name
|
||||
if (not $id) {
|
||||
PingbackServerFault('400 NO ID', 33, "No page specified");
|
||||
}
|
||||
my $error = ValidId($id);
|
||||
if ($error) {
|
||||
PingbackServerFault('400 INVALID ID', 33, "Invalid page name: $id");
|
||||
}
|
||||
|
||||
local $/ = undef;
|
||||
my $input = <STDIN>;
|
||||
# check the IP number for bans
|
||||
my $rule = UserIsBanned();
|
||||
if ($rule) {
|
||||
PingbackServerFault('403 FORBIDDEN', 49, "Your IP number is blocked");
|
||||
}
|
||||
|
||||
# parse it
|
||||
# check that the target page exists
|
||||
AllPagesList();
|
||||
if (not $IndexHash{$id}) {
|
||||
PingbackServerFault('404 NOT FOUND', 32, "Page does not exist: $id");
|
||||
}
|
||||
|
||||
# parse the remote procedure call
|
||||
my $data = $q->param('POSTDATA');
|
||||
my $parser = RPC::XML::Parser->new();
|
||||
my $request = $parser->parse($input);
|
||||
my $request = $parser->parse($data);
|
||||
if (not ref($request)) {
|
||||
result('400 Bad Request', -32700, $request);
|
||||
PingbackServerFault('400 NO DATA', -32700, "Could not parse XML-RPC");
|
||||
}
|
||||
|
||||
# handle it
|
||||
# sanity check the function and argument number
|
||||
my $name = $request->name;
|
||||
my $arguments = $request->args;
|
||||
if ($name ne 'pingback.ping') {
|
||||
result('501 Not Implemented', -32601, "Method $name not supported");
|
||||
PingbackServerFault('501 NOT IMPLEMENTED', -32601, "Method $name not supported");
|
||||
}
|
||||
if (@$arguments != 2) {
|
||||
result('400 Bad Request', -32602,
|
||||
"Wrong number of arguments (arguments must be in the form 'from', 'to')");
|
||||
PingbackServerFault('400 WRONG NUMBER OF ARGS', -32602, "Wrong number of arguments");
|
||||
}
|
||||
|
||||
# extract the two arguments
|
||||
my $source = $arguments->[0]->value;
|
||||
my $target = $arguments->[1]->value;
|
||||
|
||||
# verify that the source isn't banned
|
||||
$rule = BannedContent($source);
|
||||
if ($rule) {
|
||||
PingbackServerFault('403 FORBIDDEN', 49, "The URL is blocked");
|
||||
}
|
||||
|
||||
# TODO: Since we are _inside_ the wiki seems like we shouldn't have to use LWP
|
||||
# So comment out all the LWP stuff once the DoPost thingie works
|
||||
# DoPost($id);
|
||||
|
||||
# verify that the pingback is legit
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->agent("OddmusePingbackServer/0.1 ");
|
||||
|
||||
# Create a request
|
||||
my $req = HTTP::Request->new(POST => 'http://thelackthereof.org/wiki.pl');
|
||||
$req->content_type('application/x-www-form-urlencoded');
|
||||
$req->content("title=$CommentsPrefix$id"
|
||||
. "&summary=new%20comment"
|
||||
. "&aftertext=Pingback:%20$source"
|
||||
. "&save=save"
|
||||
. "&username=pingback");
|
||||
my $res = $ua->request($req);
|
||||
|
||||
my $out = '';
|
||||
# Check the outcome of the response
|
||||
if ($res->is_success) {
|
||||
$out = $res->content;
|
||||
} else {
|
||||
$out = $res->status_line . "\n";
|
||||
my $response = $ua->get($source);
|
||||
if (not $response->is_success) {
|
||||
PingbackServerFault('400 NO SOURCE', 16, "Cannot retrieve $source");
|
||||
}
|
||||
my $self = ScriptUrl(UrlEncode($id));
|
||||
if ($response->decoded_content !~ /$self/) {
|
||||
PingbackServerFault('403 FORBIDDEN', "$source does not link to $self");
|
||||
}
|
||||
$id = $CommentsPrefix . $id;
|
||||
if (GetPageContent($id) =~ /$source/) {
|
||||
PingbackServerFault('400 ALREADY REGISTERED', 48, "$source has already been registered");
|
||||
}
|
||||
|
||||
result('200 OK', 0, "Oddmuse PingbackServer! $id OK");
|
||||
# post a comment without redirect at the end
|
||||
SetParam('aftertext', 'Pingback: ' . $source);
|
||||
SetParam('summary', 'Pingback');
|
||||
SetParam('username', T('Anonymous'));
|
||||
SetParam($QuestionaskerSecretKey, 1) if $QuestionaskerSecretKey;
|
||||
local *ReBrowsePage = sub {};
|
||||
DoPost($id);
|
||||
|
||||
# response
|
||||
my $message = "Oddmuse PingbackServer! $id OK";
|
||||
my $response = RPC::XML::response->new(RPC::XML::string->new($message));
|
||||
print GetHttpHeader('text/xml', 'nocache', '200 OK'), $response->as_string, "\n\n";
|
||||
}
|
||||
|
||||
sub result {
|
||||
my($status, $error, $data, $extra) = @_;
|
||||
my $response;
|
||||
if ($error) {
|
||||
$response = RPC::XML::response->new(
|
||||
RPC::XML::fault->new($error, $data));
|
||||
} else {
|
||||
$response = RPC::XML::response->new(RPC::XML::string->new($data));
|
||||
}
|
||||
print "Status: $status\n";
|
||||
if (defined($extra)) {
|
||||
print "$extra\n";
|
||||
}
|
||||
print "Content-Type: text/xml\n\n";
|
||||
print $response->as_string;
|
||||
exit;
|
||||
sub PingbackServerFault {
|
||||
my($status, $error, $data) = @_;
|
||||
my $fault = RPC::XML::response->new(RPC::XML::fault->new($error, $data));
|
||||
print GetHttpHeader('text/xml', 'nocache', $status), $fault->as_string, "\n\n";
|
||||
exit 2;
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
# This doesn't work... but might be a basis for an in-wiki update system
|
||||
|
||||
sub DoPost {
|
||||
my $id = FreeToNormal(shift);
|
||||
my $source = shift;
|
||||
ValidIdOrDie($id);
|
||||
# Lock before getting old page to prevent races
|
||||
RequestLockOrError(); # fatal
|
||||
OpenPage($id);
|
||||
my $string = $Page{text};
|
||||
my $comment = "Pingback: $source";
|
||||
$comment =~ s/\r//g; # Remove "\r"-s (0x0d) from the string
|
||||
$comment =~ s/\s+$//g; # Remove whitespace at the end
|
||||
$string .= "----\n" if $string and $string ne "\n";
|
||||
$string .= $comment . "\n\n-- Pingback"
|
||||
. ' ' . TimeToText(time) . "\n\n";
|
||||
my $summary = "new pingback"
|
||||
$Page{summary} = $summary;
|
||||
$Page{username} = $user;
|
||||
$Page{text} = $string;
|
||||
SavePage();
|
||||
ReleaseLock();
|
||||
}
|
||||
|
||||
=cut
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -171,7 +171,7 @@ sub ReCaptchaGetQuestion {
|
||||
# if (defined $ReCaptchaTabIndex) { $recaptcha_options{tabindex} = $ReCaptchaTabIndex; }
|
||||
|
||||
eval "use Captcha::reCAPTCHA";
|
||||
my $captcha_html = Captcha::reCAPTCHA->new()->get_html(
|
||||
my $captcha_html = Captcha::reCAPTCHA->new()->get_html_v2(
|
||||
$ReCaptchaPublicKey, undef, $ENV{'HTTPS'} eq 'on', undef);
|
||||
my $submit_html = $need_button ? $q->submit(-value=> T('Go!')) : '';
|
||||
my $options_html = '
|
||||
@@ -239,11 +239,12 @@ sub NewReCaptchaDoPost {
|
||||
|
||||
sub ReCaptchaCheckAnswer {
|
||||
eval "use Captcha::reCAPTCHA";
|
||||
my $result = Captcha::reCAPTCHA->new()->check_answer(
|
||||
my $answer = GetParam('g-recaptcha-response');
|
||||
return 0 unless $answer;
|
||||
my $result = Captcha::reCAPTCHA->new()->check_answer_v2(
|
||||
$ReCaptchaPrivateKey,
|
||||
$q->remote_addr(),
|
||||
GetParam('recaptcha_challenge_field'),
|
||||
GetParam('recaptcha_response_field')
|
||||
$answer,
|
||||
$q->remote_addr()
|
||||
);
|
||||
return $result->{is_valid};
|
||||
}
|
||||
|
||||
71
modules/rename-pages.pl
Normal file
@@ -0,0 +1,71 @@
|
||||
# Copyright (C) 2019–2023 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;
|
||||
|
||||
our ($q, %Page, %Action, %IndexHash, @MyAdminCode);
|
||||
|
||||
AddModuleDescription('rename-pages.pl', 'Rename Pages Extension');
|
||||
|
||||
$Action{'rename-page'} = \&RenamePage;
|
||||
|
||||
sub RenamePage {
|
||||
my $id = shift;
|
||||
my $to = GetParam('to', '');
|
||||
# check target
|
||||
ValidIdOrDie($to);
|
||||
OpenPage($to);
|
||||
ReportError(T('Target page already exists.'), '400 BAD REQUEST')
|
||||
if $IndexHash{$to} and not PageMarkedForDeletion();
|
||||
# check the source
|
||||
ValidIdOrDie($id);
|
||||
OpenPage($id);
|
||||
ReportError(T('Source page does not exist.'), '400 BAD REQUEST')
|
||||
if not $IndexHash{$id} or PageMarkedForDeletion();
|
||||
{
|
||||
# prevent posting from browsing the target right away
|
||||
local *ReBrowsePage = sub {};
|
||||
# renaming is a minor change
|
||||
SetParam('recent_edit', 'on');
|
||||
# copy text
|
||||
SetParam('text', $Page{text});
|
||||
SetParam('summary', Ts('Copied from %s', FreeToNormal($id)));
|
||||
DoPost($to);
|
||||
# create redirect
|
||||
SetParam('text', "#REDIRECT [[$to]]");
|
||||
SetParam('summary', Ts('Moved to %s', FreeToNormal($to)));
|
||||
DoPost($id);
|
||||
}
|
||||
# and now that we're done, go to the target
|
||||
ReBrowsePage($to);
|
||||
}
|
||||
|
||||
push(@MyAdminCode, \&RenamePageMenu);
|
||||
|
||||
sub RenamePageMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
my $name = FreeToNormal($id);
|
||||
if ($id) {
|
||||
push(@$menuref, GetFormStart()
|
||||
. $q->label({-for=>'to'}, Ts('Rename %s to:', $name) . ' ')
|
||||
. GetHiddenValue('action', 'rename-page')
|
||||
. GetHiddenValue('id', $id)
|
||||
. $q->textfield(-name=>'to', -size=>20)
|
||||
. ' '
|
||||
. $q->submit('Do it')
|
||||
. $q->end_form());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006, 2007, 2008 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2006–2023 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
|
||||
@@ -36,18 +36,12 @@ sub SearchListRule {
|
||||
$term = GetId();
|
||||
}
|
||||
local ($OpenPageName, %Page);
|
||||
my %hash = ();
|
||||
my @found;
|
||||
if ($variation eq 'list') {
|
||||
foreach my $id (SearchTitleAndBody($term)) {
|
||||
$hash{$id} = 1 unless $id eq $original; # skip the page with the query
|
||||
}
|
||||
@found = grep { $_ ne $original } SearchTitleAndBody($term);
|
||||
} elsif ($variation eq 'titlelist') {
|
||||
@found = grep { $_ ne $original } Matched($term, AllPagesList());
|
||||
}
|
||||
if ($variation eq 'titlelist') {
|
||||
foreach my $id (grep(/$term/, AllPagesList())) {
|
||||
$hash{$id} = 1 unless $id eq $original; # skip the page with the query
|
||||
}
|
||||
}
|
||||
my @found = keys %hash;
|
||||
if (defined &PageSort) {
|
||||
@found = sort PageSort @found;
|
||||
} else {
|
||||
@@ -63,32 +57,24 @@ sub SearchListRule {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
# Add a new action list
|
||||
|
||||
$Action{list} = \&DoList;
|
||||
|
||||
sub DoList {
|
||||
my $id = shift;
|
||||
my $match = GetParam('match', '');
|
||||
my $search = GetParam('search', '');
|
||||
my $id = shift;
|
||||
my $match = GetParam('match', '');
|
||||
my $search = GetParam('search', '');
|
||||
ReportError(T('The search parameter is missing.')) unless $match or $search;
|
||||
print GetHeader('', Ts('Page list for %s', $match||$search), '');
|
||||
local (%Page, $OpenPageName);
|
||||
my %hash = ();
|
||||
foreach my $id (grep(/$match/, $search
|
||||
? SearchTitleAndBody($search)
|
||||
: AllPagesList())) {
|
||||
$hash{$id} = 1;
|
||||
}
|
||||
my @found = keys %hash;
|
||||
if (defined &PageSort) {
|
||||
@found = sort PageSort @found;
|
||||
} else {
|
||||
@found = sort(@found);
|
||||
}
|
||||
@found = map { $q->li(GetPageLink($_)) } @found;
|
||||
print $q->start_div({-class=>'search list'}),
|
||||
$q->ul(@found), $q->end_div;
|
||||
my @found = Matched($match, $search ? SearchTitleAndBody($search) : AllPagesList());
|
||||
if (defined &PageSort) {
|
||||
@found = sort PageSort @found;
|
||||
} else {
|
||||
@found = sort(@found);
|
||||
}
|
||||
@found = map { $q->li(GetPageLink($_)) } @found;
|
||||
print $q->start_div({-class=>'search list'}), $q->ul(@found), $q->end_div;
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2007–2014 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2007–2023 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
|
||||
@@ -36,8 +36,7 @@ sub SisterPages {
|
||||
push(@pages, AllPagesList()) if GetParam('pages', 1);
|
||||
push(@pages, keys %PermanentAnchors) if GetParam('permanentanchors', 1);
|
||||
push(@pages, keys %NearSource) if GetParam('near', 0);
|
||||
my $match = GetParam('match', '');
|
||||
@pages = grep /$match/i, @pages if $match;
|
||||
@pages = Matched(GetParam('match', ''), @pages);
|
||||
@pages = sort @pages;
|
||||
return @pages;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -62,16 +62,26 @@ Example:
|
||||
|
||||
$TagFeedIcon = 'http://www.example.org/pics/rss.png';
|
||||
|
||||
=head2 $TagCloudSize
|
||||
|
||||
The number of most used tags when looking at the tag cloud. The
|
||||
default is 50.
|
||||
|
||||
Example:
|
||||
|
||||
$TagCloudSize = 20;
|
||||
|
||||
=cut
|
||||
|
||||
our ($q, $Now, %Action, %Page, $FreeLinkPattern, @MyInitVariables, @MyRules, @MyAdminCode, $DataDir, $ScriptName);
|
||||
our ($TagUrl, $TagFeed, $TagFeedIcon, $TagFile);
|
||||
our ($TagUrl, $TagFeed, $TagFeedIcon, $TagFile, $TagCloudSize);
|
||||
|
||||
push(@MyInitVariables, \&TagsInit);
|
||||
|
||||
sub TagsInit {
|
||||
$TagUrl = ScriptUrl('action=rc;rcfilteronly=tag:%s') unless $TagUrl;
|
||||
$TagFeed = ScriptUrl('action=rss;rcfilteronly=tag:%s') unless $TagFeed;
|
||||
$TagCloudSize = 50 unless $TagCloudSize;
|
||||
$TagFile = "$DataDir/tag.db";
|
||||
}
|
||||
|
||||
@@ -113,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;
|
||||
}
|
||||
@@ -239,6 +250,11 @@ sub TagFind {
|
||||
return @result;
|
||||
}
|
||||
|
||||
sub TagsTerms {
|
||||
my $string = shift;
|
||||
return grep(/./, $string =~ /\"([^\"]+)\"|(\S+)/g);
|
||||
}
|
||||
|
||||
*OldTagFiltered = \&Filtered;
|
||||
*Filtered = \&NewTagFiltered;
|
||||
|
||||
@@ -246,7 +262,7 @@ sub NewTagFiltered { # called within a lock!
|
||||
my ($string, @pages) = @_;
|
||||
my %page = map { $_ => 1 } @pages;
|
||||
# looking at all the "tag:SOME TERMS" and and tag:TERM
|
||||
my @tagterms = map { FreeToNormal($_) } grep(/^-?tag:/, shift =~ /\"([^\"]+)\"|(\S+)/g);
|
||||
my @tagterms = map { FreeToNormal($_) } grep(/^-?tag:/, TagsTerms($string));
|
||||
my @positives = map {substr($_, 4)} grep(/^tag:/, @tagterms);
|
||||
my @negatives = map {substr($_, 5)} grep(/^-tag:/, @tagterms);
|
||||
if (@positives) {
|
||||
@@ -260,8 +276,9 @@ sub NewTagFiltered { # called within a lock!
|
||||
foreach my $id (TagFind(@negatives)) {
|
||||
delete $page{$id};
|
||||
}
|
||||
# filter out the tags from the search string
|
||||
$string = join(' ', grep(!/^-?tag:/, $string =~ /\"([^\"]+)\"|(\S+)/g));
|
||||
# filter out the tags from the search string, and add quotes which might have
|
||||
# been stripped
|
||||
$string = join(' ', map { qq{"$_"} } grep(!/^-?tag:/, TagsTerms($string)));
|
||||
# run the old code for any remaining search terms
|
||||
return OldTagFiltered($string, sort keys %page);
|
||||
}
|
||||
@@ -279,11 +296,12 @@ We're need to remove all tag terms (again) in order to not confuse it.
|
||||
*SearchString = \&NewTagSearchString;
|
||||
|
||||
sub NewTagSearchString {
|
||||
# filter out the negative tags from the search string
|
||||
my $string = join(' ', map { NormalToFree($_) }
|
||||
grep(!/^-tag:/, shift =~ /\"([^\"]+)\"|(\S+)/g));
|
||||
my ($string, @rest) = @_;
|
||||
# filter out the negative tags from the search string, and add quotes which
|
||||
# might have been stripped
|
||||
$string = join(' ', map { NormalToFree($_) } map { qq{"$_"} } grep(!/^-tag:/, TagsTerms($string)));
|
||||
return 1 unless $string;
|
||||
return OldTagSearchString($string, @_);
|
||||
return OldTagSearchString($string, @rest);
|
||||
}
|
||||
|
||||
=pod
|
||||
@@ -298,27 +316,16 @@ $Action{tagcloud} = \&TagCloud;
|
||||
|
||||
sub TagCloud {
|
||||
print GetHeader('', T('Tag Cloud'), ''),
|
||||
$q->start_div({-class=>'content cloud'}) . '<p>';
|
||||
$q->start_div({-class=>'content cloud'});
|
||||
require HTML::TagCloud;
|
||||
my $cloud = HTML::TagCloud->new;
|
||||
# open the DB file
|
||||
my %h = TagReadHash();
|
||||
my $max = 0;
|
||||
my $min = 0;
|
||||
my %count = ();
|
||||
foreach my $tag (grep !/^_/, keys %h) {
|
||||
$count{$tag} = @{$h{$tag}};
|
||||
$max = $count{$tag} if $count{$tag} > $max;
|
||||
$min = $count{$tag} if not $min or $count{$tag} < $min;
|
||||
$cloud->add(NormalToFree($tag), "$ScriptName?search=tag:" . UrlEncode($tag), scalar @{$h{$tag}});
|
||||
}
|
||||
foreach my $tag (sort keys %count) {
|
||||
my $n = $count{$tag};
|
||||
print $q->a({-href => "$ScriptName?search=tag:" . UrlEncode($tag),
|
||||
-title => $n,
|
||||
-style => 'font-size: '
|
||||
. int(80+120*($max == $min ? 1 : ($n-$min)/($max-$min)))
|
||||
. '%;',
|
||||
}, NormalToFree($tag)), ' ... ';
|
||||
}
|
||||
print '</p></div>';
|
||||
print $cloud->html_and_css($TagCloudSize);
|
||||
print '</div>';
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
@@ -418,13 +425,12 @@ sub TagsMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
push(@$menuref,
|
||||
ScriptLink('action=reindex', T('Rebuild tag index'), 'reindex')
|
||||
. ', ' . ScriptLink('action=taglist', T('list tags'), 'taglist')
|
||||
. ', ' . ScriptLink('action=tagcloud', T('tag cloud'), 'tagcloud'));
|
||||
}
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2005–2015 Alex Schroeder <alex@gnu.org>
|
||||
Copyright (C) 2005–2019 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
|
||||
|
||||
@@ -229,8 +229,8 @@ Important pages:
|
||||
Páginas importantes:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Para marcar uma página para exclusão, coloque <strong>%s</strong> na primeira linha
|
||||
from %s
|
||||
de %s
|
||||
Anonymous
|
||||
Anônimo
|
||||
redirected from %s
|
||||
redirecionado de %s
|
||||
%s:
|
||||
@@ -469,8 +469,6 @@ The changes conflict. Please check the page again.
|
||||
As alterações são conflitantes. Por favor, verifique essa página novamente.
|
||||
Please check whether you overwrote those changes.
|
||||
Por favor, verifique se você sobrescreveu aquelas alterações.
|
||||
Anonymous
|
||||
Anônimo
|
||||
Cannot delete the index file %s.
|
||||
Não foi possível excluir o arquivo de índice %s.
|
||||
Please check the directory permissions.
|
||||
@@ -505,6 +503,8 @@ Note: This error is normal if no changes have been made.
|
||||
Observação: Esse erro é normal se nenhuma alteração foi feita.
|
||||
Moving %s log entries.
|
||||
Movendo %s entradas de log.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Marcar ou Remover o bloqueio global de edição
|
||||
Edit lock created.
|
||||
@@ -1236,6 +1236,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -229,8 +229,8 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
от %s
|
||||
Anonymous
|
||||
Анонимен
|
||||
redirected from %s
|
||||
Препратка от %s
|
||||
%s:
|
||||
@@ -469,8 +469,6 @@ The changes conflict. Please check the page again.
|
||||
Промените се застъпват. Моля провери страницата още веднъж.
|
||||
Please check whether you overwrote those changes.
|
||||
Моля провери, дали няма конфликт между промените.
|
||||
Anonymous
|
||||
Анонимен
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -505,6 +503,8 @@ Note: This error is normal if no changes have been made.
|
||||
Забележка: Тази грешка е нормална, ако не са направени промени.
|
||||
Moving %s log entries.
|
||||
Преместване на %s лог-записа.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Налагане или отмяна на глобална защита от редакции.
|
||||
Edit lock created.
|
||||
@@ -1236,6 +1236,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -228,8 +228,8 @@ Important pages:
|
||||
Pàgines importants:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Per marcar una pàgina per esborrar, escriure <strong>%s</strong> en la primera línia.
|
||||
from %s
|
||||
des de %s
|
||||
Anonymous
|
||||
Anònim
|
||||
redirected from %s
|
||||
redirigit des de %s
|
||||
%s:
|
||||
@@ -468,8 +468,6 @@ The changes conflict. Please check the page again.
|
||||
Els canvis entren en conflicte. Si us plau, comprova la pàgina un altre cop.
|
||||
Please check whether you overwrote those changes.
|
||||
Si us plau, comprova si sobreescrius aquests canvis.
|
||||
Anonymous
|
||||
Anònim
|
||||
Cannot delete the index file %s.
|
||||
No es pot esborrar el arxiu índex de %s.
|
||||
Please check the directory permissions.
|
||||
@@ -504,6 +502,8 @@ Note: This error is normal if no changes have been made.
|
||||
Nota: Aquest error és normal si no s'han fet canvis.
|
||||
Moving %s log entries.
|
||||
Movent %s entrades del registre.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
(Des)activar bloqueig global de edició
|
||||
Edit lock created.
|
||||
@@ -1235,6 +1235,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -229,8 +229,8 @@ Important pages:
|
||||
重要頁面:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
在該頁首行加入 <strong>%s</strong> 可將頁面標記為刪除
|
||||
from %s
|
||||
自 %s
|
||||
Anonymous
|
||||
匿名者
|
||||
redirected from %s
|
||||
由 %s 轉址
|
||||
%s:
|
||||
@@ -469,8 +469,6 @@ The changes conflict. Please check the page again.
|
||||
你的修改和他人發生衝突。請再次確認。
|
||||
Please check whether you overwrote those changes.
|
||||
請確認一下是否你要覆蓋這些修改。
|
||||
Anonymous
|
||||
匿名者
|
||||
Cannot delete the index file %s.
|
||||
無法刪除索引檔 %s 。
|
||||
Please check the directory permissions.
|
||||
@@ -505,6 +503,8 @@ Note: This error is normal if no changes have been made.
|
||||
如果還沒有做過任何修改的話,則不用理會這個錯誤訊息。
|
||||
Moving %s log entries.
|
||||
移除了 %s 個記錄項目。
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
設定或移除整個網站的編輯鎖定
|
||||
Edit lock created.
|
||||
@@ -1236,6 +1236,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -245,8 +245,8 @@ Important pages:
|
||||
重要页面:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
在首行加入 <strong>%s</strong>以将页面标记为删除。
|
||||
from %s
|
||||
自 %s
|
||||
Anonymous
|
||||
匿名者
|
||||
redirected from %s
|
||||
自%s重定向
|
||||
%s:
|
||||
@@ -485,8 +485,6 @@ The changes conflict. Please check the page again.
|
||||
您的修改和他人发生冲突。请再次确认。
|
||||
Please check whether you overwrote those changes.
|
||||
请您确认一下是否要覆盖这些修改。
|
||||
Anonymous
|
||||
匿名者
|
||||
Cannot delete the index file %s.
|
||||
无法删除索引档%s。
|
||||
Please check the directory permissions.
|
||||
@@ -521,6 +519,8 @@ Note: This error is normal if no changes have been made.
|
||||
如果还没有做过任何修改,请不用理会这个错误讯息。
|
||||
Moving %s log entries.
|
||||
移除了%s个日志项目。
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
设定或移除整个网站的编辑锁定
|
||||
Edit lock created.
|
||||
@@ -1252,6 +1252,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -235,8 +235,8 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
vanaf %s
|
||||
Anonymous
|
||||
Anoniem
|
||||
redirected from %s
|
||||
doorverwezen vanaf %s
|
||||
%s:
|
||||
@@ -475,8 +475,6 @@ The changes conflict. Please check the page again.
|
||||
De wijzigingen zijn strijdig met elkaar. Controleer de pagina a.u.b. opnieuw.
|
||||
Please check whether you overwrote those changes.
|
||||
Controleer a.u.b. of u die wijzigingen overschreven heeft.
|
||||
Anonymous
|
||||
Anoniem
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -511,6 +509,8 @@ Note: This error is normal if no changes have been made.
|
||||
NB: Deze foutmelding is gebruikelijk als er geen wijzigingen zijn gemaakt.
|
||||
Moving %s log entries.
|
||||
Bezig met verplaatsen van %s items.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Ver-/ontgrendelen van de gehele site
|
||||
Edit lock created.
|
||||
@@ -1242,6 +1242,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
Tähtsad leheküljed:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
|
||||
Anonymous
|
||||
Anonüümne
|
||||
redirected from %s
|
||||
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
|
||||
Please check whether you overwrote those changes.
|
||||
|
||||
Anonymous
|
||||
Anonüümne
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
|
||||
Moving %s log entries.
|
||||
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
%s:stä
|
||||
Anonymous
|
||||
Nimetön
|
||||
redirected from %s
|
||||
uudelleenohjattu sivulta %s
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
Muutokset ovat ristiriidassa. Tarkista sivu uudelleen.
|
||||
Please check whether you overwrote those changes.
|
||||
Tarkista, ylikirjoititko joitakin noista muutoksista.
|
||||
Anonymous
|
||||
Nimetön
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
Huom: Tämä virhe on normaali, jos muutoksia ei ole tehty.
|
||||
Moving %s log entries.
|
||||
Siirretään %s kirjausta.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Aseta tai poista sivuston muokkauslukitus
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -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
|
||||
@@ -232,8 +232,8 @@ Important pages:
|
||||
Pages importantes :
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Pour marquer une page comme étant à supprimer, ajoutez <strong>%s</strong> à la première ligne
|
||||
from %s
|
||||
depuis %s
|
||||
Anonymous
|
||||
Anonyme
|
||||
redirected from %s
|
||||
redirigé(e) à partir de %s
|
||||
%s:
|
||||
@@ -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
|
||||
@@ -472,8 +472,6 @@ The changes conflict. Please check the page again.
|
||||
Conflit sur les modifications. SVP vérifiez la page à nouveau.
|
||||
Please check whether you overwrote those changes.
|
||||
SVP vérifiez si vous avez écrasé ces modifications.
|
||||
Anonymous
|
||||
Anonyme
|
||||
Cannot delete the index file %s.
|
||||
Impossible de supprimer le fichier index %s.
|
||||
Please check the directory permissions.
|
||||
@@ -508,6 +506,8 @@ Note: This error is normal if no changes have been made.
|
||||
Remarque : Cette erreur est normale si aucune modification n’a été effectuée.
|
||||
Moving %s log entries.
|
||||
Déplace %s entrées du log.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Positionne ou Retire le verrou global d’édition
|
||||
Edit lock created.
|
||||
@@ -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:
|
||||
@@ -1239,27 +1239,38 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
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! ;)
|
||||
@@ -1267,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
|
||||
################################################################################
|
||||
@@ -1346,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.
|
||||
|
||||
################################################################################
|
||||
@@ -1413,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
|
||||
################################################################################
|
||||
@@ -1482,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
|
||||
################################################################################
|
||||
@@ -1570,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.
|
||||
|
||||
################################################################################
|
||||
@@ -1604,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:
|
||||
@@ -1636,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
|
||||
################################################################################
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
Wichtige Seiten:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Um eine Seite zu löschen, setzt man <strong>%s</strong> auf die erste Zeile.
|
||||
from %s
|
||||
von %s
|
||||
Anonymous
|
||||
Unbekannt
|
||||
redirected from %s
|
||||
Umgeleitet von %s
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
Die Änderungen beeinträchtigen sich gegenseitig. Bitte überprüfen Sie die Seite nochmals.
|
||||
Please check whether you overwrote those changes.
|
||||
Bitte überprüfen Sie, ob diese Änderungen überschrieben worden sind.
|
||||
Anonymous
|
||||
Unbekannt
|
||||
Cannot delete the index file %s.
|
||||
Die Index Datei %s kann nicht gelöscht werden.
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
HINWEIS: Dieser Fehler ist normal, wenn keine Änderungen durchgeführt wurden.
|
||||
Moving %s log entries.
|
||||
Verschiebe %s Log-Einträge.
|
||||
Removing IP numbers from %s log entries.
|
||||
IP Nummbern wurden as %s Log Einträgen entfernt.
|
||||
Set or Remove global edit lock
|
||||
Erzeugen oder Aufheben der globalen Bearbeitungssperre
|
||||
Edit lock created.
|
||||
@@ -1203,8 +1203,8 @@ All mail subscriptions
|
||||
Alle Email Abonnements
|
||||
Subscriptions
|
||||
Abonnements
|
||||
Email:
|
||||
|
||||
Email:
|
||||
E-Mail:
|
||||
Show
|
||||
Zeigen
|
||||
Subscriptions for %s:
|
||||
@@ -1234,6 +1234,17 @@ Es wurden keine Email Adressen gefundne, die zu migrieren wären.
|
||||
Migrated %s rows.
|
||||
%s Zeilen wurden migriert.
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
%s zu Markdown convertieren
|
||||
List all non-Markdown pages
|
||||
Liste aller Seiten, die noch nicht zu Markdown konvertiert wurden
|
||||
Converting %s
|
||||
%s konvertieren
|
||||
Candidates for Conversion to Markdown
|
||||
Kandidaten für die Konvertierung zu Mardown
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
Bisect modules
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
Σημαντικές σελίδες:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Για να σημειωθεί μια σελίδα προς διαγραφή, εισάγετε το κείμενο <strong>%s</strong> στην πρώτη γραμμή της σελίδας.
|
||||
from %s
|
||||
από %s
|
||||
Anonymous
|
||||
Ανώνυμος
|
||||
redirected from %s
|
||||
προώθηση από το %s
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
Οι αλλαγές έρχονται σε σύγκρουση. Παρακαλώ δείτε τη σελίδα πάλι.
|
||||
Please check whether you overwrote those changes.
|
||||
Παρακαλώ ελέγξτε ότι δεν σβήσατε κάποιες από τις αλλαγές.
|
||||
Anonymous
|
||||
Ανώνυμος
|
||||
Cannot delete the index file %s.
|
||||
Σφάλμα διαγραφής του αρχείου %s.
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
Σημείωση: Αυτό το σφάλμα δεν είναι σημαντικό αν δεν έχουν γίνει αλλαγές.
|
||||
Moving %s log entries.
|
||||
Μετακίνηση %s εγγραφών καταγραφής.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Κλείδωμα ή Ξεκλείδωμα του κεντρικού κλειδώματος εκδότη
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -228,8 +228,8 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
מ %s
|
||||
Anonymous
|
||||
אנונימי
|
||||
redirected from %s
|
||||
הוכוון מחדש מ %s
|
||||
%s:
|
||||
@@ -468,8 +468,6 @@ The changes conflict. Please check the page again.
|
||||
|
||||
Please check whether you overwrote those changes.
|
||||
|
||||
Anonymous
|
||||
אנונימי
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -504,6 +502,8 @@ Note: This error is normal if no changes have been made.
|
||||
הערה: שגיאה זו היא רגילה אם לא בוצעו שינויים.
|
||||
Moving %s log entries.
|
||||
מזיז %s ערכי יומן.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
קבע או הסר נעילת עריכה גלובלית
|
||||
Edit lock created.
|
||||
@@ -1235,6 +1235,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
Pagine importanti
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Per impostare una pagine per la cancellazione, inserire <strong>%s</strong> sulla prima riga.
|
||||
from %s
|
||||
da %s
|
||||
Anonymous
|
||||
Anonimo
|
||||
redirected from %s
|
||||
rediretto da %s
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
Le modifiche sono in conflitto. Prego ricontrollare la pagina.
|
||||
Please check whether you overwrote those changes.
|
||||
Prego controllate se avete sovrascritto quelle modifiche.
|
||||
Anonymous
|
||||
Anonimo
|
||||
Cannot delete the index file %s.
|
||||
Impossibile cancellare il file di indice %s.
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
Nota: questo errore è normale se non sono stati apportati cambiamenti.
|
||||
Moving %s log entries.
|
||||
Spostamento di %s elementi di log.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Imposta o rilascia il blocco globale di modifica
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,7 +226,7 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
Anonymous
|
||||
|
||||
redirected from %s
|
||||
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
|
||||
Please check whether you overwrote those changes.
|
||||
|
||||
Anonymous
|
||||
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
|
||||
Moving %s log entries.
|
||||
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
중요한 페이지들:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
특정 페이지를 삭제 요청페이지로 하려면, <strong>%s</strong> 를 첫줄에 입력하세요.
|
||||
from %s
|
||||
%s 로 부터
|
||||
Anonymous
|
||||
익명
|
||||
redirected from %s
|
||||
%s 에서 재전송됨
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
이 페이지들이 충돌합니다. 페이지를 다시 확인하세요.
|
||||
Please check whether you overwrote those changes.
|
||||
이 변경사항들에 덮어쓰기 하지 않았는지 확인하세요.
|
||||
Anonymous
|
||||
익명
|
||||
Cannot delete the index file %s.
|
||||
%s 인덱스 파일을 삭제 할 수 없습니다.
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
주의: 변경이 전혀 없었다면 이 오류는 정상적인 것입니다.
|
||||
Moving %s log entries.
|
||||
%s 로그 엔트리를 옭김.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
전역 편집 잠금을 설정하거나 제거
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,7 +226,7 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
Anonymous
|
||||
|
||||
redirected from %s
|
||||
|
||||
@@ -296,6 +296,8 @@ Delete
|
||||
|
||||
Filter:
|
||||
|
||||
Summary:
|
||||
|
||||
Last edit
|
||||
|
||||
revision %s
|
||||
@@ -310,8 +312,6 @@ later minor edits
|
||||
|
||||
No diff available.
|
||||
|
||||
Summary:
|
||||
|
||||
Old revision:
|
||||
|
||||
Changed:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
|
||||
Please check whether you overwrote those changes.
|
||||
|
||||
Anonymous
|
||||
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
|
||||
Moving %s log entries.
|
||||
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
|
||||
Edit lock created.
|
||||
@@ -1201,6 +1201,8 @@ Your mail subscriptions
|
||||
|
||||
All mail subscriptions
|
||||
|
||||
All mail subscribers
|
||||
|
||||
Subscriptions
|
||||
|
||||
Email:
|
||||
@@ -1227,12 +1229,25 @@ 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.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
@@ -1398,6 +1413,11 @@ Click to search for references to this permanent anchor
|
||||
|
||||
Include permanent anchors
|
||||
|
||||
################################################################################
|
||||
# modules/pingback-server.pl
|
||||
################################################################################
|
||||
Only XML-RPC POST requests recognised
|
||||
|
||||
################################################################################
|
||||
# modules/portrait-support.pl
|
||||
################################################################################
|
||||
@@ -1462,6 +1482,17 @@ Referrers
|
||||
################################################################################
|
||||
All Referrers
|
||||
|
||||
################################################################################
|
||||
# modules/rename-pages.pl
|
||||
################################################################################
|
||||
Target page already exists.
|
||||
|
||||
Source page does not exist.
|
||||
|
||||
Copied from %s
|
||||
|
||||
Moved to %s
|
||||
|
||||
################################################################################
|
||||
# modules/search-list.pl
|
||||
################################################################################
|
||||
@@ -1528,8 +1559,6 @@ Rebuilding index not done.
|
||||
|
||||
Rebuild tag index
|
||||
|
||||
list tags
|
||||
|
||||
tag cloud
|
||||
|
||||
################################################################################
|
||||
@@ -1686,5 +1715,56 @@ Edit %s.
|
||||
################################################################################
|
||||
Tags:
|
||||
|
||||
################################################################################
|
||||
# modules/webmention.pl
|
||||
################################################################################
|
||||
Webmention module requires $CommentsPrefix to be set
|
||||
|
||||
Webmention requires a POST request
|
||||
|
||||
Webmention requires x-www-form-urlencoded requests
|
||||
|
||||
Webmention must mention a specific page
|
||||
|
||||
Webmention must mention a valid page
|
||||
|
||||
Your IP number is blocked: %s
|
||||
|
||||
Webmention must mention an existing page
|
||||
|
||||
Webmention must mention source
|
||||
|
||||
Webmention must mention target
|
||||
|
||||
The URL is blocked: %s
|
||||
|
||||
Webmention source cannot be verified: %1 returns %2 %3
|
||||
|
||||
Webmention source does not link to %s
|
||||
|
||||
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
|
||||
|
||||
@@ -228,8 +228,8 @@ Important pages:
|
||||
Ważne strony:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Aby zaznaczyć stronę do usunięcia, wpisz <strong>%s</strong> w pierwszej linii.
|
||||
from %s
|
||||
z %s
|
||||
Anonymous
|
||||
Anonimowy
|
||||
redirected from %s
|
||||
przekierowano z %s
|
||||
%s:
|
||||
@@ -468,8 +468,6 @@ The changes conflict. Please check the page again.
|
||||
Wystąpił konflikt zmian. Proszę sprawdzić stronę ponownie.
|
||||
Please check whether you overwrote those changes.
|
||||
Proszę sprawdzić, czy nadpisałeś te zmiany.
|
||||
Anonymous
|
||||
Anonimowy
|
||||
Cannot delete the index file %s.
|
||||
Nie można skasować pliku indeksu %s.
|
||||
Please check the directory permissions.
|
||||
@@ -504,6 +502,8 @@ Note: This error is normal if no changes have been made.
|
||||
Uwaga: Ten błąd jest normalny, jeśli nie było żadnych zmian.
|
||||
Moving %s log entries.
|
||||
Przenoszenie %s wpisów logów.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Ustaw lub zwolnij globalną blokadę edycji
|
||||
Edit lock created.
|
||||
@@ -1235,6 +1235,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -231,8 +231,8 @@ Important pages:
|
||||
Páginas importantes:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Para marcar uma página a ser apagada, ponha <strong>%s</strong> na primeira linha.
|
||||
from %s
|
||||
de %s
|
||||
Anonymous
|
||||
Anónimo
|
||||
redirected from %s
|
||||
redireccionado de %s
|
||||
%s:
|
||||
@@ -471,8 +471,6 @@ The changes conflict. Please check the page again.
|
||||
Conflito entre alterações. Por favor reverifique esta página.
|
||||
Please check whether you overwrote those changes.
|
||||
Por favor verifique se sobrescreveu essas alterações.
|
||||
Anonymous
|
||||
Anónimo
|
||||
Cannot delete the index file %s.
|
||||
Impossível apagar o ficheiro de índice %s.
|
||||
Please check the directory permissions.
|
||||
@@ -507,6 +505,8 @@ Note: This error is normal if no changes have been made.
|
||||
NOTA: Este erro é normal se nenhuma alteração foi feita.
|
||||
Moving %s log entries.
|
||||
Movendo entradas de log %s.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Marque ou Remova bloqueio global de edição
|
||||
Edit lock created.
|
||||
@@ -1238,6 +1238,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
Pagini importante:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
de la %s
|
||||
Anonymous
|
||||
Anonim
|
||||
redirected from %s
|
||||
redirecţionat de la %s
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
|
||||
Please check whether you overwrote those changes.
|
||||
|
||||
Anonymous
|
||||
Anonim
|
||||
Cannot delete the index file %s.
|
||||
Nu am putut şterge fişierul index %s.
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
|
||||
Moving %s log entries.
|
||||
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -232,8 +232,8 @@ Important pages:
|
||||
Служебные страницы:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Чтобы пометить любую страницу к удалению, поместите <strong>%s</strong> первой строкой страницы.
|
||||
from %s
|
||||
с %s
|
||||
Anonymous
|
||||
Аноним
|
||||
redirected from %s
|
||||
перенаправлено с %s
|
||||
%s:
|
||||
@@ -472,8 +472,6 @@ The changes conflict. Please check the page again.
|
||||
Изменения конфликтуют. Проверьте страницу снова.
|
||||
Please check whether you overwrote those changes.
|
||||
Пожалуйста удостоверьтесь, что вы не перезаписали чужие изменения.
|
||||
Anonymous
|
||||
Аноним
|
||||
Cannot delete the index file %s.
|
||||
Не могу удалить индекс-файл %s.
|
||||
Please check the directory permissions.
|
||||
@@ -508,6 +506,8 @@ Note: This error is normal if no changes have been made.
|
||||
Примечание: Эта ошибка - нормально, если не было сделано изменений.
|
||||
Moving %s log entries.
|
||||
Перемещаю %s лог записей.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Установка или снятие глобальной блокировки на редактирование
|
||||
Edit lock created.
|
||||
@@ -1239,6 +1239,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -228,8 +228,8 @@ Important pages:
|
||||
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
|
||||
from %s
|
||||
од %s
|
||||
Anonymous
|
||||
Анонимони
|
||||
redirected from %s
|
||||
преусмерено са %s
|
||||
%s:
|
||||
@@ -468,8 +468,6 @@ The changes conflict. Please check the page again.
|
||||
Измене су у сукобу. Проверите страну поново.
|
||||
Please check whether you overwrote those changes.
|
||||
Проверите да нисте пребрисали ове те измене.
|
||||
Anonymous
|
||||
Анонимони
|
||||
Cannot delete the index file %s.
|
||||
|
||||
Please check the directory permissions.
|
||||
@@ -504,6 +502,8 @@ Note: This error is normal if no changes have been made.
|
||||
Пажња: Ова грешка је уобичајена уколико ништа нисте изменили.
|
||||
Moving %s log entries.
|
||||
Премештам %s дневничких ставки.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Постави или уклони глобалну браву за уређивање
|
||||
Edit lock created.
|
||||
@@ -1235,6 +1235,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -230,8 +230,8 @@ Important pages:
|
||||
Páginas importantes:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Para marcar una página para su eliminación, escribir <strong>%s</strong> en la primera línea.
|
||||
from %s
|
||||
desde %s
|
||||
Anonymous
|
||||
Anónimo
|
||||
redirected from %s
|
||||
redirigido desde %s
|
||||
%s:
|
||||
@@ -470,8 +470,6 @@ The changes conflict. Please check the page again.
|
||||
Los cambios entran en conflicto. Por favor, comprueba la página otra vez.
|
||||
Please check whether you overwrote those changes.
|
||||
Por favor, comprueba si sobreescribes esos cambios.
|
||||
Anonymous
|
||||
Anónimo
|
||||
Cannot delete the index file %s.
|
||||
No se puede borrar el archivo índice %s.
|
||||
Please check the directory permissions.
|
||||
@@ -506,6 +504,8 @@ Note: This error is normal if no changes have been made.
|
||||
Nota: Este error es normal si no se han hecho cambios.
|
||||
Moving %s log entries.
|
||||
Moviendo %s entradas del registro.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Establecer o quitar bloqueo global de edición
|
||||
Edit lock created.
|
||||
@@ -1237,6 +1237,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
No se encontraron direcciones de correo no migradas, la migración no es necesaria.
|
||||
Migrated %s rows.
|
||||
Migrado %s filas.
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
@@ -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,
|
||||
@@ -230,8 +231,8 @@ Important pages:
|
||||
Viktiga sidor:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
För att markera en sida för radering, skriv <strong>%s</strong> på första raden.
|
||||
from %s
|
||||
från %s
|
||||
Anonymous
|
||||
Anonym
|
||||
redirected from %s
|
||||
omdirigerad från %s
|
||||
%s:
|
||||
@@ -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:
|
||||
@@ -470,8 +471,6 @@ The changes conflict. Please check the page again.
|
||||
Ändringarna står i konflikt. Kontrollera sidan igen.
|
||||
Please check whether you overwrote those changes.
|
||||
Kontrollera om du skrev över de ändringarna.
|
||||
Anonymous
|
||||
Anonym
|
||||
Cannot delete the index file %s.
|
||||
Kan inte radera index-filen %s.
|
||||
Please check the directory permissions.
|
||||
@@ -506,6 +505,8 @@ Note: This error is normal if no changes have been made.
|
||||
Observera: Det här felet är normalt om inga ändringar gjorts.
|
||||
Moving %s log entries.
|
||||
Flyttar %s loggnotering.
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Slå på eller av globalt redigeringslås
|
||||
Edit lock created.
|
||||
@@ -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,12 +1234,25 @@ 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.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
@@ -1293,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
|
||||
################################################################################
|
||||
@@ -1312,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
|
||||
################################################################################
|
||||
@@ -1402,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
|
||||
################################################################################
|
||||
@@ -1466,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
|
||||
################################################################################
|
||||
@@ -1475,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
|
||||
################################################################################
|
||||
@@ -1507,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
|
||||
################################################################################
|
||||
@@ -1545,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
|
||||
################################################################################
|
||||
@@ -1589,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
|
||||
################################################################################
|
||||
@@ -1630,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
|
||||
################################################################################
|
||||
@@ -1665,7 +1695,7 @@ Wanted Pages
|
||||
# modules/webapp.pl
|
||||
################################################################################
|
||||
Web application for offline browsing
|
||||
|
||||
Webbapplikation för off-line visning
|
||||
################################################################################
|
||||
# modules/webdav.pl
|
||||
################################################################################
|
||||
@@ -1680,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
|
||||
|
||||
@@ -226,8 +226,8 @@ Important pages:
|
||||
Важливі сторінки:
|
||||
To mark a page for deletion, put <strong>%s</strong> on the first line.
|
||||
Для того, щоб помітити сторінку для видалення, вставте <strong>%s</strong> на першу строчку.
|
||||
from %s
|
||||
від %s
|
||||
Anonymous
|
||||
Анонім
|
||||
redirected from %s
|
||||
перенаправлено з %s
|
||||
%s:
|
||||
@@ -466,8 +466,6 @@ The changes conflict. Please check the page again.
|
||||
Зміни конфліктують. Будь ласка, перевірте цю сторінку ще раз.
|
||||
Please check whether you overwrote those changes.
|
||||
Будь ласка, перевірте, чи перезаписали ви ці зміни.
|
||||
Anonymous
|
||||
Анонім
|
||||
Cannot delete the index file %s.
|
||||
Не можу видалити файл з покажчиком %s.
|
||||
Please check the directory permissions.
|
||||
@@ -502,6 +500,8 @@ Note: This error is normal if no changes have been made.
|
||||
Примітка: Ця помилка припустима, якщо не було зроблено жодних змін.
|
||||
Moving %s log entries.
|
||||
|
||||
Removing IP numbers from %s log entries.
|
||||
|
||||
Set or Remove global edit lock
|
||||
Встановити або Видалити глобальний блок редагування
|
||||
Edit lock created.
|
||||
@@ -1233,6 +1233,17 @@ No non-migrated email addresses found, migration not necessary.
|
||||
|
||||
Migrated %s rows.
|
||||
|
||||
################################################################################
|
||||
# modules/markdown-converter.pl
|
||||
################################################################################
|
||||
Help convert %s to Markdown
|
||||
|
||||
List all non-Markdown pages
|
||||
|
||||
Converting %s
|
||||
|
||||
Candidates for Conversion to Markdown
|
||||
|
||||
################################################################################
|
||||
# modules/module-bisect.pl
|
||||
################################################################################
|
||||
|
||||
227
modules/webmention.pl
Normal file
@@ -0,0 +1,227 @@
|
||||
# Copyright (C) 2004 Brock Wilcox <awwaiid@thelackthereof.org>
|
||||
# Copyright (C) 2019 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;
|
||||
|
||||
use LWP::UserAgent;
|
||||
use Modern::Perl;
|
||||
use XML::LibXML;
|
||||
|
||||
AddModuleDescription('webmention.pl', 'Webmention Server Extension');
|
||||
|
||||
# Specification: https://www.w3.org/TR/webmention/
|
||||
|
||||
our ($CommentsPrefix, $q, $HtmlHeaders, %Action, $QuestionaskerSecretKey,
|
||||
@MyInitVariables, %IndexHash, $BannedContent, $UsePathInfo, $HomePage,
|
||||
$Message, @MyAdminCode, $FullUrlPattern);
|
||||
|
||||
push(@MyInitVariables, \&WebmentionServerAddLink, \&WebmentionAddAction);
|
||||
|
||||
# Add webmentions metadata to our pages
|
||||
|
||||
sub WebmentionServerAddLink {
|
||||
$Message .= T('Webmention module requires $CommentsPrefix to be set') unless $CommentsPrefix;
|
||||
# only allow linking to reasonable pages: no URL parameters!
|
||||
my @params = $q->param;
|
||||
return unless GetParam('action', 'browse') eq 'browse';
|
||||
return if GetParam('revision');
|
||||
my $id = GetId() || $HomePage;
|
||||
return if $id =~ /^$CommentsPrefix/;
|
||||
my $link = '<link rel="webmention" type="application/wiki" href="'
|
||||
. ScriptUrl('webmention/' . UrlEncode($id)) . '" />';
|
||||
$HtmlHeaders .= $link unless $HtmlHeaders =~ /rel="webmention"/;
|
||||
}
|
||||
|
||||
sub WebmentionAddAction {
|
||||
SetParam('action', 'webmention') if $q->path_info =~ m|/webmention\b|;
|
||||
}
|
||||
|
||||
# Process incoming webmentions
|
||||
|
||||
$Action{webmention} = \&DoWebmentionServer;
|
||||
|
||||
sub DoWebmentionServer {
|
||||
my $id = FreeToNormal(shift);
|
||||
|
||||
# some sanity checks for the request
|
||||
if ($q->request_method() ne 'POST') {
|
||||
ReportError(T('Webmention requires a POST request'), '400 BAD REQUEST');
|
||||
}
|
||||
if ($q->content_type() ne 'application/x-www-form-urlencoded') {
|
||||
ReportError(T('Webmention requires x-www-form-urlencoded requests'), '400 BAD REQUEST');
|
||||
}
|
||||
|
||||
# some sanity checks for the target page name
|
||||
if (not $id) {
|
||||
ReportError(T('Webmention must mention a specific page'), '400 BAD REQUEST');
|
||||
}
|
||||
my $error = ValidId($id);
|
||||
if ($error) {
|
||||
ReportError(T('Webmention must mention a valid page'), '400 BAD REQUEST');
|
||||
}
|
||||
|
||||
# check the IP number for bans
|
||||
my $rule = UserIsBanned();
|
||||
if ($rule) {
|
||||
ReportError(Ts('Your IP number is blocked: %s', $rule), '403 FORBIDDEN');
|
||||
}
|
||||
|
||||
# check that the target page exists
|
||||
AllPagesList();
|
||||
if (not $IndexHash{$id}) {
|
||||
ReportError(T('Webmention must mention an existing page'), '404 NOT FOUND');
|
||||
}
|
||||
|
||||
# verify parameters
|
||||
my $source = GetParam('source', undef) or ReportError(T('Webmention must mention source'), '400 BAD REQUEST');
|
||||
my $target = GetParam('target', undef) or ReportError(T('Webmention must mention target'), '400 BAD REQUEST');
|
||||
|
||||
# verify that the source isn't banned
|
||||
$rule = BannedContent($source);
|
||||
if ($rule) {
|
||||
ReportError(Ts('The URL is blocked: %s', $rule), '403 FORBIDDEN');
|
||||
}
|
||||
|
||||
# verify that the webmention is legit
|
||||
my $ua = LWP::UserAgent->new(agent => 'Oddmuse Webmention Server/0.1');
|
||||
my $response = $ua->get($source);
|
||||
if (not $response->is_success) {
|
||||
ReportError(Tss('Webmention source cannot be verified: %1 returns %2 %3',
|
||||
$source, $response->code, $response->message), '400 BAD REQUEST');
|
||||
}
|
||||
my $self = ScriptUrl(UrlEncode($id));
|
||||
if ($response->decoded_content !~ /$self/) {
|
||||
ReportError(Ts('Webmention source does not link to %s', $self), '400 BAD REQUEST');
|
||||
}
|
||||
$id = $CommentsPrefix . $id;
|
||||
if (GetPageContent($id) =~ /$source/) {
|
||||
ReportError(Ts('Webmention for %s already exists', $source), '400 BAD REQUEST');
|
||||
}
|
||||
|
||||
# try to determine a name and a link
|
||||
my ($username, $homepage);
|
||||
my $parser = XML::LibXML->new(recover => 2);
|
||||
my $dom = $parser->load_html(string => $response->decoded_content);
|
||||
my @nodes = $dom->findnodes('//*[@rel="author"]');
|
||||
if (@nodes) {
|
||||
my $node = shift @nodes;
|
||||
$username = $node->textContent;
|
||||
$homepage = $node->getAttribute('href');
|
||||
}
|
||||
|
||||
# post a comment without redirect at the end
|
||||
SetParam('aftertext', 'Webmention: ' . $source);
|
||||
SetParam('summary', 'Webmention');
|
||||
SetParam('username', $username || T('Anonymous'));
|
||||
SetParam('homepage', $homepage);
|
||||
SetParam($QuestionaskerSecretKey, 1) if $QuestionaskerSecretKey;
|
||||
local *ReBrowsePage = sub {};
|
||||
DoPost($id);
|
||||
|
||||
# response
|
||||
print GetHeader('', T('Webmention OK!'));
|
||||
print $q->start_div({-class=>'content webmention'}),
|
||||
$q->p(GetPageLink($BannedContent)),
|
||||
$q->end_div;
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
# Allow user to webmention other sites
|
||||
|
||||
push(@MyAdminCode, \&WebmentionMenu);
|
||||
|
||||
sub WebmentionMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
if ($id) {
|
||||
push(@$menuref, ScriptLink('action=webmentions;id=' . $id, T('Add webmentions'), 'webmentions'));
|
||||
}
|
||||
}
|
||||
|
||||
$Action{webmentions} = \&DoWebmentionMenu;
|
||||
|
||||
sub DoWebmentionMenu {
|
||||
my $id = GetId();
|
||||
ValidIdOrDie($id);
|
||||
print GetHeader('', Ts('Webmentioning others from %s', NormalToFree($id)), '');
|
||||
my $text = GetPageContent($id);
|
||||
my @urls = $text =~ /$FullUrlPattern/g;
|
||||
if (@urls) {
|
||||
print GetFormStart();
|
||||
print GetHiddenValue('action', 'webmentioning');
|
||||
print GetHiddenValue('from', UrlEncode($id));
|
||||
print '<p>';
|
||||
print $q->checkbox_group('to', \@urls, undef, 'true');
|
||||
print '</p>';
|
||||
print $q->submit('go', T('Webmention!'));
|
||||
} else {
|
||||
print $q->p(T('No links found.'));
|
||||
}
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
$Action{webmentioning} = \&DoWebmention;
|
||||
|
||||
sub DoWebmention {
|
||||
my $id = GetParam('from');
|
||||
ValidIdOrDie($id);
|
||||
my $from = ScriptUrl($id);
|
||||
my @to = $q->multi_param('to');
|
||||
ReportError('Missing target') unless @to;
|
||||
print GetHeader('', Ts('Webmentioning somebody from %s', NormalToFree($id)), '');
|
||||
for my $to (@to) {
|
||||
Webmention($from, $to);
|
||||
}
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
sub Webmention {
|
||||
my ($from, $to) = @_;
|
||||
ReportError('Target must be an URL', '400 BAD REQUEST', 0, $q->p($to)) unless $to =~ /$FullUrlPattern/;
|
||||
my $ua = LWP::UserAgent->new(agent => "Oddmuse Webmention Client/0.1");
|
||||
|
||||
print $q->p(Ts('Contacting %s', $to));
|
||||
my $response = $ua->get($to);
|
||||
if (!$response->is_success) {
|
||||
print $q->p(Ts('Target reports an error: %s', $response->status_line));
|
||||
return;
|
||||
}
|
||||
|
||||
print $q->p("Parsing response");
|
||||
my $data = $response->decoded_content;
|
||||
my $parser = XML::LibXML->new(recover => 2);
|
||||
my $dom = $parser->load_html(string => $data);
|
||||
my $webmention = $dom->findvalue('//link[@rel="webmention"]/@href');
|
||||
|
||||
if (!$webmention) {
|
||||
print $q->p(T('No Webmention URL found'));
|
||||
return;
|
||||
}
|
||||
|
||||
print $q->p("Webmention URL is $webmention");
|
||||
|
||||
$response = $ua->post($webmention, { source => $from, target => $to });
|
||||
my $message = $response->code . " " . $response->message;
|
||||
if ($response->is_success) {
|
||||
print $q->p(Ts("Success: %s", $message));
|
||||
} else {
|
||||
print $q->p(Ts("Failure: %s", $message));
|
||||
$dom = $parser->load_html(string => $response->decoded_content());
|
||||
for my $node ($dom->getElementsByTagName('script')) { $node->parentNode->removeChild($node) };
|
||||
for my $node ($dom->getElementsByTagName('style')) { $node->parentNode->removeChild($node) };
|
||||
print $q->p($dom->textContent);
|
||||
}
|
||||
}
|
||||
61
modules/wordcount.pl
Normal file
@@ -0,0 +1,61 @@
|
||||
# Copyright (C) 2005 Robin V. Stacey (robin@greywulf.net)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc.
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
# This module adds a wordcount to the bottom of edit boxes. The javascript code is munged from
|
||||
# Richard Livsey's Textarea Tools page: http://livsey.org/experiments/textareatools/
|
||||
# Though I've stripped it down to it's barest necessities
|
||||
|
||||
use strict;
|
||||
use v5.10;
|
||||
|
||||
our (@MyInitVariables, $HtmlHeaders, $EditNote);
|
||||
|
||||
AddModuleDescription('wordcount.pl', 'Word Count Extension');
|
||||
|
||||
push(@MyInitVariables, \&WordcountAddScript);
|
||||
|
||||
sub WordcountAddScript {
|
||||
$HtmlHeaders .= "<script type='text/javascript'>
|
||||
function addEvent(obj, evType, fn) {
|
||||
if (obj.addEventListener) {
|
||||
obj.addEventListener(evType, fn, true);
|
||||
return true;
|
||||
} else if (obj.attachEvent) {
|
||||
var r = obj.attachEvent('on'+evType, fn);
|
||||
return r;
|
||||
} else { return false; }
|
||||
}
|
||||
|
||||
addEvent(window, 'load', function() {
|
||||
document.getElementById('textWordCount').innerHTML = numWords(document.getElementById('text').value);
|
||||
document.getElementById('text').onkeyup = function() {
|
||||
document.getElementById('textWordCount').innerHTML = numWords(document.getElementById('text').value);
|
||||
}
|
||||
});
|
||||
|
||||
function numWords(string) {
|
||||
string = string + ' ';
|
||||
string = string.replace(/^[^A-Za-z0-9]+/gi, '');
|
||||
string = string.replace(/[^A-Za-z0-9]+/gi, ' ');
|
||||
var items = string.split(' ');
|
||||
return items.length -1;
|
||||
}
|
||||
</script>";
|
||||
}
|
||||
|
||||
$EditNote = "Words: <span id='textWordCount'></span>" . $EditNote;
|
||||
94
scripts/ip-to-regexp.pl
Normal file
@@ -0,0 +1,94 @@
|
||||
use Modern::Perl;
|
||||
use Net::Whois::Parser qw/parse_whois/;
|
||||
|
||||
sub main {
|
||||
my $ip = shift(@ARGV);
|
||||
die "Provide an IP number as argument.\n" unless $ip;
|
||||
print get_regexp_ip(get_range($ip)), "\n";
|
||||
}
|
||||
|
||||
sub get_range {
|
||||
my $ip = shift;
|
||||
my $response = parse_whois(domain => $ip);
|
||||
my $re = '(?:[0-9]{1,3}\.){3}[0-9]{1,3}';
|
||||
my ($start, $end) = $response->{inetnum} =~ /($re) *- *($re)/;
|
||||
return $start, $end;
|
||||
}
|
||||
|
||||
sub get_groups {
|
||||
my ($from, $to) = @_;
|
||||
my @groups;
|
||||
if ($from < 10) {
|
||||
my $to = $to >= 10 ? 9 : $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
|
||||
}
|
||||
}
|
||||
return \@groups;
|
||||
}
|
||||
|
||||
sub get_regexp_range {
|
||||
my @chars;
|
||||
for my $group (@{get_groups(@_)}) {
|
||||
my ($from, $to) = @$group;
|
||||
my $char;
|
||||
for (my $i = length($from); $i >= 1; $i--) {
|
||||
if (substr($from, - $i, 1) eq substr($to, - $i, 1)) {
|
||||
$char .= substr($from, - $i, 1);
|
||||
} else {
|
||||
$char .= '[' . substr($from, - $i, 1) . '-' . substr($to, - $i, 1). ']';
|
||||
}
|
||||
}
|
||||
push(@chars, $char);
|
||||
}
|
||||
return join('|', @chars);
|
||||
}
|
||||
|
||||
sub get_regexp_ip {
|
||||
my ($from, $to) = @_;
|
||||
my @start = split(/\./, $from);
|
||||
my @end = split(/\./, $to);
|
||||
my $regexp = "^";
|
||||
for my $i (0 .. 3) {
|
||||
if ($start[$i] eq $end[$i]) {
|
||||
$regexp .= $start[$i];
|
||||
} elsif ($start[$i] eq '0' and $end[$i] eq '255') {
|
||||
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 .= '\.';
|
||||
last;
|
||||
} else {
|
||||
$regexp .= '(' . get_regexp_range($start[$i], $end[$i]) . ')$';
|
||||
last;
|
||||
}
|
||||
$regexp .= '\.' if $i < 3;
|
||||
}
|
||||
return $regexp;
|
||||
}
|
||||
|
||||
main();
|
||||
71
scripts/pingback-client.pl
Executable file
@@ -0,0 +1,71 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 Pingback from one site to another
|
||||
|
||||
If you link from page A on your site to page B on some other site, you can
|
||||
invoke this script with the command-line arguments A and B. In theory, this will
|
||||
create a link back from B to A, letting them and all their visitors know that
|
||||
you wrote something in response.
|
||||
|
||||
=cut
|
||||
|
||||
use Modern::Perl;
|
||||
use RPC::XML;
|
||||
use RPC::XML::Client;
|
||||
use XML::LibXML;
|
||||
use LWP::UserAgent;
|
||||
use Data::Dumper;
|
||||
|
||||
if (@ARGV != 2) {
|
||||
die "Usage: pingback-client FROM TO\n";
|
||||
}
|
||||
|
||||
my ($from, $to) = @ARGV;
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->agent("OddmusePingbackClient/0.1");
|
||||
|
||||
print "Getting $to\n";
|
||||
my $response = $ua->get($to);
|
||||
|
||||
if (!$response->is_success) {
|
||||
die $response->status_line;
|
||||
}
|
||||
|
||||
print "Parsing $to\n";
|
||||
my $data = $response->decoded_content;
|
||||
|
||||
my $parser = XML::LibXML->new(recover => 2);
|
||||
my $dom = $parser->load_html(string => $data);
|
||||
my $pingback = $dom->findvalue('//link[@rel="pingback"]/@href');
|
||||
|
||||
if (!$pingback) {
|
||||
die "Pingback URL not found in $to\n";
|
||||
}
|
||||
|
||||
print "Pingback URL is $pingback\n";
|
||||
|
||||
my $request = RPC::XML::request->new(
|
||||
'pingback.ping', $from, $to);
|
||||
my $client = RPC::XML::Client->new($pingback);
|
||||
$response = $client->send_request($request);
|
||||
|
||||
if (!ref($response)) {
|
||||
die $response;
|
||||
}
|
||||
|
||||
print Dumper($response->value);
|
||||
@@ -1,5 +1,5 @@
|
||||
#! /usr/bin/perl
|
||||
# Copyright (C) 2010 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2010–2019 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
|
||||
@@ -22,6 +22,8 @@ use MIME::Entity;
|
||||
use File::Temp;
|
||||
use File::Basename;
|
||||
use File::Path;
|
||||
use Net::SMTP;
|
||||
use Authen::SASL qw(Perl);
|
||||
|
||||
# This script can be invoked as follows:
|
||||
# perl rc2mail.pl -r http://localhost/cgi-bin/wiki \
|
||||
@@ -36,7 +38,8 @@ use File::Path;
|
||||
# gets http://localhost/cgi-bin/wiki?action=rss;days=1;full=1;short=0
|
||||
# And http://localhost/cgi-bin/wiki?action=subscriptionlist;raw=1;pwd=foo
|
||||
# -m user:password@mailhost for sending email using SMTP Auth. Without this
|
||||
# information, the script will send mail to localhost.
|
||||
# information, the script will send mail to localhost. The host can end
|
||||
# in a port number, e.g. "kensanata:*secret*@smtp.migadu.com:587"
|
||||
# -f email address to use as the sender.
|
||||
# -t timestamp file; it's last modified date is used to determine when the
|
||||
# the last run was and an appropriate URL is used. Instead of days=1 it
|
||||
@@ -137,7 +140,7 @@ sub send_files {
|
||||
$sent += @subscribers;
|
||||
send_file($id, $title, $item, @subscribers);
|
||||
}
|
||||
print "$sent messages sent\n" if $sent;
|
||||
print "$sent messages sent\n" if $sent and not $quiet;
|
||||
}
|
||||
|
||||
sub send_file {
|
||||
@@ -170,46 +173,31 @@ sub send_mail {
|
||||
Path => $fh,
|
||||
Type=> "text/html");
|
||||
if ($host) {
|
||||
print "Sending $title to $subscriber using ${user}\@${host}\n" if $verbose;
|
||||
eval {
|
||||
require Net::SMTP::TLS;
|
||||
my $smtp = Net::SMTP::TLS->new($host,
|
||||
User => $user,
|
||||
Password => $password);
|
||||
$smtp->mail($from);
|
||||
$smtp->to($subscriber);
|
||||
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!
|
||||
$smtp->auth($user, $password);
|
||||
$smtp->mail($from);
|
||||
if ($smtp->to($subscriber)) {
|
||||
$smtp->data;
|
||||
$smtp->datasend($mail->stringify);
|
||||
$smtp->dataend;
|
||||
$smtp->quit;
|
||||
};
|
||||
if ($@) {
|
||||
require Net::SMTP::SSL;
|
||||
my $smtp = Net::SMTP::SSL->new($host, Port => 465);
|
||||
$smtp->auth($user, $password);
|
||||
$smtp->mail($from);
|
||||
$smtp->to($subscriber);
|
||||
$smtp->data;
|
||||
$smtp->datasend($mail->stringify);
|
||||
$smtp->dataend;
|
||||
$smtp->quit;
|
||||
}
|
||||
} else {
|
||||
my @recipients = $mail->smtpsend();
|
||||
if (@recipients) {
|
||||
print "Sent $title to ", join(', ', @recipients), "\n" unless $quiet;
|
||||
} else {
|
||||
print "Failed to send $title to $subscriber\n" unless $quiet;
|
||||
warn "Error: ", $smtp->message();
|
||||
}
|
||||
$smtp->quit;
|
||||
}
|
||||
}
|
||||
|
||||
sub main {
|
||||
my $rss = get_rss();
|
||||
return unless @{$rss->{items}};
|
||||
my $subscribers = get_subscribers();
|
||||
return unless %{$subscribers};
|
||||
send_files($rss, $subscribers);
|
||||
if (@{$rss->{items}}) {
|
||||
my $subscribers = get_subscribers();
|
||||
if (%{$subscribers}) {
|
||||
send_files($rss, $subscribers);
|
||||
}
|
||||
}
|
||||
update_timestamp();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
87
scripts/strip-host.pl
Executable file
@@ -0,0 +1,87 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 EncodePage {
|
||||
my @data = @_;
|
||||
my $result = '';
|
||||
$result .= (shift @data) . ': ' . EscapeNewlines(shift @data) . "\n" while (@data);
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub EscapeNewlines {
|
||||
$_[0] =~ s/\n/\n\t/g; # modify original instead of copying
|
||||
return $_[0];
|
||||
}
|
||||
|
||||
sub main {
|
||||
die "There is no temp directory, here.\n"
|
||||
. "Perhaps this isn't an Oddmuse data directory?\n"
|
||||
unless -d 'temp';
|
||||
die "The main lock already exists in the temp directory.\n"
|
||||
if -d "temp/lockmain";
|
||||
mkdir "temp/lockmain" or die "Cannot create main lock in temp: $!\n";
|
||||
local $/ = undef; # Read complete files
|
||||
foreach my $dir (qw/keep page/) {
|
||||
warn "Did not find the $dir directory.\n" unless -d $dir;
|
||||
}
|
||||
# include dotfiles!
|
||||
my $t = 0;
|
||||
my $n = 0;
|
||||
foreach my $file (glob("page/*.pg page/.*.pg"),
|
||||
glob("keep/*/*.kp keep/.*.kp")) {
|
||||
$t++;
|
||||
open(my $fh, '<', $file) or die "Cannot read $file file: $!\n";
|
||||
my $data = <$fh>;
|
||||
close($fh);
|
||||
next unless $data;
|
||||
my %result = ParseData($data);
|
||||
if (exists($result{host}) or exists($result{ip})) {
|
||||
delete($result{host});
|
||||
delete($result{ip});
|
||||
open($fh,'>', "$file~") or die "Cannot $file~: $!\n";
|
||||
print $fh EncodePage(%result);
|
||||
close($fh);
|
||||
rename("$file~", $file) or die "Cannot rename $file~ to $file: $!\n";
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
rmdir "temp/lockmain" or die "Cannot remove main lock: $!\n";
|
||||
print "I looked at $t files and found $n host or ip keys which I removed.\n";
|
||||
}
|
||||
|
||||
if (@ARGV) {
|
||||
print qq{
|
||||
Usage: $0 [--page DIR]
|
||||
|
||||
Goes through the wiki and removes the hostname or IP number from page and
|
||||
keep files. Make a backup before running this script! Run this script in
|
||||
your data directory.
|
||||
}
|
||||
} else {
|
||||
main ();
|
||||
}
|
||||
@@ -1,24 +1,41 @@
|
||||
#!/usr/bin/env perl
|
||||
use Modern::Perl;
|
||||
use Mojolicious::Lite;
|
||||
use Mojo::Cache;
|
||||
use Archive::Tar;
|
||||
use File::Basename;
|
||||
use Sort::Versions;
|
||||
use Encode qw(decode_utf8);
|
||||
my $dir = "/home/alex/oddmuse.org/releases";
|
||||
my $cache = Mojo::Cache->new(max_keys => 50);
|
||||
|
||||
get '/' => sub {
|
||||
my $c = shift;
|
||||
my @tarballs = sort map {
|
||||
sub tarballs {
|
||||
my @tarballs = reverse sort versioncmp map {
|
||||
my ($name, $path, $suffix) = fileparse($_, '.tar.gz');
|
||||
$name;
|
||||
} <$dir/*.tar.gz>;
|
||||
$c->render(template => 'index', tarballs => \@tarballs);
|
||||
return \@tarballs;
|
||||
}
|
||||
|
||||
sub tarball {
|
||||
my $tarball = shift;
|
||||
if ($tarball eq 'latest') {
|
||||
my $tarballs = tarballs();
|
||||
$tarball = shift @$tarballs;
|
||||
}
|
||||
return $tarball;
|
||||
}
|
||||
|
||||
get '/' => sub {
|
||||
my $c = shift;
|
||||
my $tarballs = tarballs();
|
||||
unshift @$tarballs, 'latest';
|
||||
$c->render(template => 'index', tarballs => $tarballs);
|
||||
} => 'main';
|
||||
|
||||
get '/#tarball' => sub {
|
||||
my $c = shift;
|
||||
my $tarball = $c->param('tarball');
|
||||
my $tarball = tarball $c->param('tarball');
|
||||
my $files = $cache->get($tarball);
|
||||
if (not $files) {
|
||||
$c->app->log->info("Reading $tarball.tar.gz");
|
||||
@@ -36,7 +53,7 @@ get '/#tarball' => sub {
|
||||
|
||||
get '/#tarball/#file' => sub {
|
||||
my $c = shift;
|
||||
my $tarball = $c->param('tarball');
|
||||
my $tarball = tarball $c->param('tarball');
|
||||
my $file = $c->param('file');
|
||||
my $text = $cache->get("$tarball/$file");
|
||||
if (not $text) {
|
||||
@@ -68,7 +85,9 @@ versions of Oddmuse.</p>
|
||||
<ul>
|
||||
% for my $tarball (@$tarballs) {
|
||||
<li>
|
||||
% if ($tarball ne 'latest') {
|
||||
<a href="https://oddmuse.org/releases/<%= $tarball %>.tar.gz"><%= $tarball %>.tar.gz</a>
|
||||
% }
|
||||
(files for <%= link_to release => {tarball => $tarball} => begin %>\
|
||||
<%= $tarball =%><%= end %>)
|
||||
</li>
|
||||
|
||||
@@ -30,7 +30,7 @@ if (@ARGV == 1 and $ARGV[0] eq '--help') {
|
||||
sub AddModuleDescription { print $_[0], ' ' };
|
||||
our %Translate;
|
||||
|
||||
my @files = <modules/translations/*-utf8.pl>;
|
||||
my @files = <./modules/translations/*-utf8.pl>;
|
||||
for (@files) {
|
||||
if (@ARGV) { # some specific modules
|
||||
my $files = join ' ', map { quotemeta } @ARGV; # quick and dirty
|
||||
|
||||
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;
|
||||
84
scripts/webmention.pl
Normal file
@@ -0,0 +1,84 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2019 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 Webmention from one site to another
|
||||
|
||||
If you link from page A on your site to page B on some other site, you can
|
||||
invoke this script with the command-line arguments A and B. In theory, this will
|
||||
create a link back from B to A, letting them and all their visitors know that
|
||||
you wrote something in response.
|
||||
|
||||
=cut
|
||||
|
||||
use Modern::Perl;
|
||||
use XML::LibXML;
|
||||
use LWP::UserAgent;
|
||||
use Data::Dumper;
|
||||
|
||||
if (@ARGV != 2) {
|
||||
die "Usage: webmention FROM TO\n";
|
||||
}
|
||||
|
||||
my $parser = XML::LibXML->new(recover => 2);
|
||||
|
||||
my ($from, $to) = @ARGV;
|
||||
my $ua = LWP::UserAgent->new(agent => "Oddmuse Webmention Client/0.1");
|
||||
|
||||
print "Getting $from\n";
|
||||
my $response = $ua->get($from);
|
||||
|
||||
if (!$response->is_success) {
|
||||
die $response->status_line;
|
||||
}
|
||||
|
||||
print "Parsing $from\n";
|
||||
my ($username, $homepage);
|
||||
my $dom = $parser->load_html(string => $response->decoded_content);
|
||||
my @nodes = $dom->findnodes('//*[@rel="author"]');
|
||||
if (@nodes) {
|
||||
my $node = shift @nodes;
|
||||
$username = $node->textContent;
|
||||
$homepage = $node->getAttribute('href');
|
||||
}
|
||||
print "Webmention from " . join(", ", $username, $homepage) . "\n"
|
||||
if $username or $homepage;
|
||||
|
||||
print "Getting $to\n";
|
||||
$response = $ua->get($to);
|
||||
|
||||
if (!$response->is_success) {
|
||||
die $response->status_line;
|
||||
}
|
||||
|
||||
print "Parsing $to\n";
|
||||
$dom = $parser->load_html(string => $response->decoded_content);
|
||||
my $webmention = $dom->findvalue('//link[@rel="webmention"]/@href');
|
||||
|
||||
if (!$webmention) {
|
||||
die "Webmention URL not found in $to\n";
|
||||
}
|
||||
|
||||
print "Webmention URL is $webmention\n";
|
||||
|
||||
$response = $ua->post($webmention, { source => $from, target => $to });
|
||||
|
||||
my $message = $response->code . " " . $response->message . "\n";
|
||||
if ($response->is_success) {
|
||||
print $message;
|
||||
} else {
|
||||
die $message;
|
||||
}
|
||||