Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
88799b3ebc | ||
|
|
4032aa563c | ||
|
|
2b9fd67dbd | ||
|
|
62056409de | ||
|
|
f45f4eb49f | ||
|
|
acd4d42561 | ||
|
|
8375c3a842 | ||
|
|
54138b7998 | ||
|
|
3816567543 | ||
|
|
c6954437ea | ||
|
|
f3725a4938 | ||
|
|
fc8f6b4a42 | ||
|
|
b6109e37ad | ||
|
|
b3b98e2b82 | ||
|
|
74f6a4b314 | ||
|
|
19a9ad3da0 | ||
|
|
2d99025024 | ||
|
|
63370f31d7 | ||
|
|
752daa81b5 | ||
|
|
429ead8c69 | ||
|
|
52f4aad356 | ||
|
|
4abd0a26cf | ||
|
|
c8173cac04 | ||
|
|
be4b141c43 | ||
|
|
f9d6258744 | ||
|
|
66234d7785 | ||
|
|
c67b4a7f12 | ||
|
|
4a94023be2 | ||
|
|
9535f45647 | ||
|
|
8f585bcd29 | ||
|
|
d95d7b0674 | ||
|
|
50fcd7eb0b | ||
|
|
e5b46fe1a4 | ||
|
|
28872646d0 | ||
|
|
d059e09104 | ||
|
|
545cd78805 | ||
|
|
1e7a7d2fa7 | ||
|
|
1b540fc294 | ||
|
|
493ddc233c | ||
|
|
07b3169c5b | ||
|
|
e73707a16f | ||
|
|
5323399bc8 | ||
|
|
13ac083542 | ||
|
|
3dee191328 | ||
|
|
e13524c1d3 | ||
|
|
dc82b7d64f | ||
|
|
d68163ee90 | ||
|
|
349ed2722c | ||
|
|
e01b39edf6 | ||
|
|
783325509a | ||
|
|
2db3736a70 | ||
|
|
22cf00c28f | ||
|
|
3fe2736ad4 | ||
|
|
03b38673f7 | ||
|
|
3590bb96dd | ||
|
|
5051b9602a | ||
|
|
af9da2be34 | ||
|
|
0c17454a0c | ||
|
|
d789bc40f0 | ||
|
|
3a97171320 | ||
|
|
110970f310 | ||
|
|
9986552ffb | ||
|
|
64277e26ed | ||
|
|
b35e867b55 | ||
|
|
67c6db4b03 | ||
|
|
eec5307bc3 | ||
|
|
6a3b9a9916 | ||
|
|
e6880ae469 | ||
|
|
40de3ea9a1 | ||
|
|
93d40b022f | ||
|
|
dec6acf354 | ||
|
|
062cd9b5b9 | ||
|
|
31574e3606 | ||
|
|
ca01d9d3d6 | ||
|
|
9c69322289 | ||
|
|
c13841e30a | ||
|
|
9ae1ff22c7 | ||
|
|
8db5a45dcd | ||
|
|
b3e2485cd0 | ||
|
|
6c135be248 | ||
|
|
b5a4af9656 | ||
|
|
b23f1d777b | ||
|
|
2f49adf605 | ||
|
|
76f9eb7945 | ||
|
|
0c2718ca8c | ||
|
|
7109c5be9c | ||
|
|
e9fad88a10 |
4
Makefile
@@ -4,7 +4,7 @@
|
||||
|
||||
VERSION_NO=$(shell git describe --tags)
|
||||
TRANSLATIONS=$(wildcard modules/translations/[a-z]*-utf8.pl$)
|
||||
MODULES=$(wildcard modules/*.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" < $< > $@
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
544
css/alex-2017.css
Normal file
@@ -0,0 +1,544 @@
|
||||
/* This file is in the public domain. */
|
||||
html{ text-align: center; }
|
||||
|
||||
body, rss {
|
||||
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
|
||||
font-style: normal;
|
||||
font-size: 14pt;
|
||||
padding: 1em 3em;
|
||||
max-width: 72ex;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@import url(file:///home/alex/alexschroeder.ch/css/alex-2017.css) print;
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
/* hide all the crap */
|
||||
div.diff, div.diff+hr, div.refer, div.near, div.definition, div.sister,
|
||||
div.cal, div.footer, span.specialdays, span.gotobar, a.edit, a.number span,
|
||||
div.rc form, form.tiny, p.comment, p#plus1, div.g-plusone, div.content a.feed {
|
||||
display:none;
|
||||
}
|
||||
div.content a.book,
|
||||
div.content a.movie {
|
||||
text-decoration: none;
|
||||
}
|
||||
a cite {
|
||||
font-style: italic;
|
||||
}
|
||||
img[alt="RSS"] { display: none }
|
||||
a.rss { font-size: 8pt }
|
||||
}
|
||||
|
||||
/* headings: we can use larger sizes if we use a lighter color.
|
||||
we cannot inherit the font-family because header and footer use a narrow font. */
|
||||
|
||||
h1, h2, h3, title {
|
||||
font-family: inherit;
|
||||
font-weight: normal;
|
||||
}
|
||||
h1, channel title {
|
||||
font-size: 32pt;
|
||||
margin: 1em 0 0.5em 0;
|
||||
padding: 0.4em 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18pt;
|
||||
margin: 2em 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
h3 {
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
margin: 1em 0 0 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* headers in the journal are smaller */
|
||||
|
||||
div.journal h1, item title {
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
clear: both;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
div.journal h2 {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
div.journal h3 {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
font-style: italic;
|
||||
}
|
||||
div.journal hr {
|
||||
visibility: hidden;
|
||||
}
|
||||
p.more {
|
||||
margin-top: 3em;
|
||||
}
|
||||
/* Links in headings appear on journal pages. */
|
||||
|
||||
h1 a, h2 a, h3 a {
|
||||
color:inherit;
|
||||
text-decoration:none;
|
||||
font-weight: normal;
|
||||
}
|
||||
h1 a:visited, h2 a:visited, h3 a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* for download buttons and the like */
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 120%;
|
||||
cursor: pointer;
|
||||
padding: 0.4em 0.6em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset, 0px 1px 3px black;
|
||||
}
|
||||
|
||||
.button .icon {
|
||||
color: #363;
|
||||
text-shadow: 0px -1px 1px white, 0px 1px 3px #666;
|
||||
}
|
||||
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* links */
|
||||
|
||||
a.pencil {
|
||||
padding-left: 1ex;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
visibility: hidden;
|
||||
transition: visibility 0s 1s, opacity 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
*:hover > a.pencil {
|
||||
visibility: visible;
|
||||
transition: opacity .5s linear;
|
||||
opacity: 1;
|
||||
}
|
||||
@media print {
|
||||
a.pencil {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.number {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* stop floating content from flowing over the footer */
|
||||
|
||||
hr {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* the distance between links in the navigation bars */
|
||||
|
||||
span.bar a {
|
||||
margin-right: 1ex;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* search box in the top bar */
|
||||
|
||||
.header form, .header p {
|
||||
display: inline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
label[for="searchlang"], #searchlang, .header input[type="submit"] {
|
||||
/* don't use display: none! http://stackoverflow.com/questions/5665203/getting-iphone-go-button-to-submit-form */
|
||||
visibility: hidden; position: absolute;
|
||||
}
|
||||
/* wrap on the iphone */
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
}
|
||||
|
||||
.header input {
|
||||
width: 10ex;
|
||||
}
|
||||
|
||||
/* other form fields */
|
||||
|
||||
input[type="text"] {
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
line-height: 125%;
|
||||
}
|
||||
|
||||
/* code */
|
||||
|
||||
textarea, pre, code, tt {
|
||||
font-family: "Andale Mono", Monaco, "Courier New", Courier, monospace, "Symbola";
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
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. */
|
||||
|
||||
div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
|
||||
font-size: 14pt;
|
||||
}
|
||||
@media print {
|
||||
div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
font-size: 8pt;
|
||||
}
|
||||
}
|
||||
|
||||
div.footer form.search {
|
||||
display: none;
|
||||
}
|
||||
div.rc li + li {
|
||||
margin-top: 1em;
|
||||
}
|
||||
div.rc li strong, table.history strong, strong.description {
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
div.diff {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
font-size: 12pt;
|
||||
color: #000;
|
||||
|
||||
}
|
||||
div.old {
|
||||
background-color: #ffffaf;
|
||||
}
|
||||
div.new {
|
||||
background-color: #cfffcf;
|
||||
}
|
||||
|
||||
div.refer {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
div.message {
|
||||
background-color:#fee;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
img.xml {
|
||||
border:none;
|
||||
padding:1px;
|
||||
}
|
||||
a.small img {
|
||||
max-width:300px;
|
||||
}
|
||||
a.large img {
|
||||
max-width:600px;
|
||||
}
|
||||
div.sister {
|
||||
margin-right:1ex;
|
||||
background-color:inherit;
|
||||
}
|
||||
div.sister p {
|
||||
margin-top:0;
|
||||
}
|
||||
div.sister hr {
|
||||
display:none;
|
||||
}
|
||||
div.sister img {
|
||||
border:none;
|
||||
}
|
||||
|
||||
div.near, div.definition {
|
||||
background-color:#efe;
|
||||
}
|
||||
|
||||
div.sidebar {
|
||||
float:right;
|
||||
border:1px dotted #000;
|
||||
padding:0 1em;
|
||||
}
|
||||
div.sidebar ul {
|
||||
padding-left:1em;
|
||||
}
|
||||
|
||||
/* replacements, features */
|
||||
|
||||
ins {
|
||||
font-style: italic;
|
||||
text-decoration: none;
|
||||
}
|
||||
acronym, abbr {
|
||||
letter-spacing:0.1em;
|
||||
font-variant:small-caps;
|
||||
}
|
||||
|
||||
/* Interlink prefix not shown */
|
||||
a .site, a .separator {
|
||||
display: none;
|
||||
}
|
||||
a cite { font:inherit; }
|
||||
/* browser borkage */
|
||||
textarea[name="text"] { width:97%; height:80%; }
|
||||
textarea[name="summary"] { width:97%; height:3em; }
|
||||
/* comments */
|
||||
textarea[name="aftertext"] { width:97%; height:10em; }
|
||||
div.commentshown {
|
||||
font-size: 12pt;
|
||||
padding: 2em 0;
|
||||
}
|
||||
div.commenthidden {
|
||||
display:none;
|
||||
}
|
||||
div.commentshown {
|
||||
display:block;
|
||||
}
|
||||
p.comment {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.comment {
|
||||
font-size: 14pt;
|
||||
}
|
||||
div.comment h2 {
|
||||
margin-top: 5em;
|
||||
}
|
||||
/* comment pages with username, homepage, and email subscription */
|
||||
.comment form span { display: block; }
|
||||
.comment form span label { display: inline-block; width: 10em; }
|
||||
/* IE sucks */
|
||||
.comment input#username,
|
||||
.comment input#homepage,
|
||||
.comment input#mail { width: 20em; }
|
||||
|
||||
/* cal */
|
||||
div.month { padding:0; margin:0 2ex; }
|
||||
body > div.month {
|
||||
float:right;
|
||||
background-color: inherit;
|
||||
border:solid thin;
|
||||
padding:0 1ex;
|
||||
}
|
||||
.year > .month {
|
||||
float:left;
|
||||
}
|
||||
.footer {
|
||||
clear:both;
|
||||
}
|
||||
.month .title a.local {
|
||||
background-color: inherit;
|
||||
}
|
||||
.month a.local {
|
||||
background-color: #ddf;
|
||||
}
|
||||
.month a.today {
|
||||
background-color: #fdd;
|
||||
}
|
||||
.month a {
|
||||
color:inherit;
|
||||
font-weight:inherit;
|
||||
text-decoration: none;
|
||||
background-color: #eee;
|
||||
}
|
||||
/* history tables and other tables */
|
||||
table.history {
|
||||
border: none;
|
||||
}
|
||||
td.history {
|
||||
border: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border: none;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
margin: 1em 2em;
|
||||
}
|
||||
table tr td, table tr th {
|
||||
border: none;
|
||||
padding: 0.2em 0.5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.arab tr th {
|
||||
font-weight:normal;
|
||||
text-align:left;
|
||||
vertical-align:top;
|
||||
}
|
||||
table.arab, table.arab tr th, table.arab tr td {
|
||||
border:none;
|
||||
}
|
||||
th.nobreak {
|
||||
white-space:nowrap;
|
||||
}
|
||||
table.full { width:99%; margin-left:1px; }
|
||||
table.j td, table.j th, table tr td.j, table tr th.j, .j { text-align:justify; }
|
||||
table.l td, table.l th, table tr td.l, table tr th.l, .l { text-align:left; }
|
||||
table.r td, table.r th, table tr td.r, table tr th.r, .r { text-align:right; }
|
||||
table.c td, table.c th, table tr td.c, table tr th.c, .c { text-align:center; }
|
||||
table.t td { vertical-align: top; }
|
||||
td.half { width:50%; }
|
||||
td.third { width:33%; }
|
||||
|
||||
form table td { padding:5px; }
|
||||
|
||||
/* lists */
|
||||
dd { padding-bottom:0.5ex; }
|
||||
dl.inside dt { float:left; }
|
||||
/* search */
|
||||
div.search span.result { font-size:larger; }
|
||||
div.search span.info { font-size:smaller; font-style:italic; }
|
||||
div.search p.result { display:none; }
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
margin: 0 0 0 1ex;
|
||||
padding: 0;
|
||||
border: 1px solid #000;
|
||||
opacity: 0.3;
|
||||
background-color:#ffe;
|
||||
}
|
||||
|
||||
/* images */
|
||||
|
||||
div.content a.feed img, div.journal a.feed img,
|
||||
div.content a img.smiley, div.journal a img.smiley, img.smiley,
|
||||
div.content a.inline img, div.journal a.inline img,
|
||||
div.content li a.image img, div.journal li a.image img {
|
||||
margin: 0; padding: 0; border: none;
|
||||
}
|
||||
div.image a img {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.left { float:left; margin-right: 1em; }
|
||||
.right { float:right; margin-left: 1em; }
|
||||
.half img { height: 50%; width: 50%; }
|
||||
.face img { width: 200px; }
|
||||
div.left .left, div.right .right {
|
||||
float:none;
|
||||
}
|
||||
.center { text-align:center; }
|
||||
table.aside {
|
||||
float:right;
|
||||
width:40%;
|
||||
margin-left: 1em;
|
||||
padding: 1ex;
|
||||
border: 1px dotted #666;
|
||||
}
|
||||
table.aside td {
|
||||
text-align:left;
|
||||
}
|
||||
div.sidebar {
|
||||
float:right; width: 250px;
|
||||
text-align: right;
|
||||
border: none;
|
||||
margin: 1ex;
|
||||
}
|
||||
|
||||
.bigsidebar {
|
||||
float:right;
|
||||
width: 500px;
|
||||
border: none;
|
||||
margin-left: 1ex;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
dl.irc dt { width:20ex; float:left; text-align:right; clear:left; }
|
||||
dl.irc dt span.time { float:left; }
|
||||
dl.irc dd { margin-left:22ex; }
|
||||
|
||||
/* portrait */
|
||||
|
||||
div.footer, div.comment, hr { clear: both; }
|
||||
.portrait { float: left; font-size: small; margin-right: 1em; }
|
||||
.portrait a { color: #999; }
|
||||
|
||||
div.left { float:left; margin:1em; padding: 0.5em; }
|
||||
div.left p { display:table-cell; }
|
||||
div.left p + p { display:table-caption; caption-side:bottom; }
|
||||
|
||||
p.table a { float:left; width:20ex; }
|
||||
p.table + p { clear:both; }
|
||||
|
||||
/* mastodon */
|
||||
|
||||
div.mastodon { padding: 0 2em }
|
||||
div.mastodon .status {padding-top: 1ex; border-bottom: 1px solid grey;}
|
||||
div.mastodon .status:first-child {border-top: 1px solid grey;}
|
||||
|
||||
/* rss */
|
||||
|
||||
channel * { display: block; }
|
||||
|
||||
channel title {
|
||||
margin-top: 30pt;
|
||||
}
|
||||
copyright {
|
||||
font-size: 14pt;
|
||||
margin-top: 1em;
|
||||
}
|
||||
channel:before {
|
||||
font-size: 14pt;
|
||||
display: block;
|
||||
margin: 1em;
|
||||
padding: 0.5em;
|
||||
content: "This document is to be read in a feed reader. The item content is escaped HTML, which makes it hard to read for humans. Sorry!";
|
||||
color: red;
|
||||
border: 1px solid red;
|
||||
}
|
||||
license {
|
||||
font-size: 11pt;
|
||||
margin-bottom: 9pt;
|
||||
}
|
||||
contributor:before { content: "Last edited by "; }
|
||||
contributor:after { content: "."; }
|
||||
generator:before { content: "Feed generated by "; }
|
||||
generator:after { content: "."; }
|
||||
channel description {
|
||||
font-weight: bold;
|
||||
}
|
||||
item description {
|
||||
font-weight: normal;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
link, managingEditor, webMaster, license, url,
|
||||
docs, language,
|
||||
pubDate, lastBuildDate, ttl, guid, category, comments,
|
||||
docs, image title, image link,
|
||||
status, version, diff, history, importance {
|
||||
display: none;
|
||||
}
|
||||
615
css/alex-2018.css
Normal file
@@ -0,0 +1,615 @@
|
||||
/* This file is in the public domain. */
|
||||
html{ text-align: center; }
|
||||
|
||||
body, rss {
|
||||
font-family: "DejaVu Serif", Palatino, serif;
|
||||
font-style: normal;
|
||||
font-size: 16pt;
|
||||
padding: 1em 3em;
|
||||
max-width: 72ex;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
/* hide all the crap */
|
||||
div.diff, div.diff+hr, div.refer, div.near, div.definition, div.sister,
|
||||
div.cal, div.footer, span.specialdays, span.gotobar, a.edit, a.number span,
|
||||
div.rc form, form.tiny, p.comment, p#plus1, div.g-plusone, div.content a.feed {
|
||||
display:none;
|
||||
}
|
||||
div.content a.book,
|
||||
div.content a.movie {
|
||||
text-decoration: none;
|
||||
}
|
||||
a cite {
|
||||
font-style: italic;
|
||||
}
|
||||
img[alt="RSS"] { display: none }
|
||||
a.rss { font-size: 8pt }
|
||||
}
|
||||
|
||||
/* headings: we can use larger sizes if we use a lighter color.
|
||||
we cannot inherit the font-family because header and footer use a narrow font. */
|
||||
|
||||
h1, h2, h3, title {
|
||||
font-family: inherit;
|
||||
font-weight: normal;
|
||||
}
|
||||
h1, channel title {
|
||||
font-size: 32pt;
|
||||
margin: 1em 0 0.5em 0;
|
||||
padding: 0.4em 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18pt;
|
||||
margin: 2em 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
h3 {
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
margin: 1em 0 0 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* headers in the journal are smaller */
|
||||
|
||||
div.journal h1, item title {
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
clear: both;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
div.journal h2 {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
div.journal h3 {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
font-style: italic;
|
||||
}
|
||||
div.journal hr {
|
||||
visibility: hidden;
|
||||
}
|
||||
p.more {
|
||||
margin-top: 3em;
|
||||
}
|
||||
/* Links in headings appear on journal pages. */
|
||||
|
||||
h1 a, h2 a, h3 a {
|
||||
color:inherit;
|
||||
text-decoration:none;
|
||||
font-weight: normal;
|
||||
}
|
||||
h1 a:visited, h2 a:visited, h3 a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* for download buttons and the like */
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 120%;
|
||||
cursor: pointer;
|
||||
padding: 0.4em 0.6em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset, 0px 1px 3px black;
|
||||
}
|
||||
|
||||
.button .icon {
|
||||
color: #363;
|
||||
text-shadow: 0px -1px 1px white, 0px 1px 3px #666;
|
||||
}
|
||||
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* links */
|
||||
|
||||
a.pencil {
|
||||
padding-left: 1ex;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
visibility: hidden;
|
||||
transition: visibility 0s 1s, opacity 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
*:hover > a.pencil {
|
||||
visibility: visible;
|
||||
transition: opacity .5s linear;
|
||||
opacity: 1;
|
||||
}
|
||||
@media print {
|
||||
a.pencil {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.number {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* stop floating content from flowing over the footer */
|
||||
|
||||
hr {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* the distance between links in the navigation bars */
|
||||
|
||||
span.bar a {
|
||||
margin-right: 1ex;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* search box in the top bar */
|
||||
|
||||
.header form, .header p {
|
||||
display: inline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
label[for="searchlang"], #searchlang, .header input[type="submit"] {
|
||||
/* don't use display: none! http://stackoverflow.com/questions/5665203/getting-iphone-go-button-to-submit-form */
|
||||
visibility: hidden; position: absolute;
|
||||
}
|
||||
|
||||
.header input {
|
||||
width: 10ex;
|
||||
}
|
||||
|
||||
/* other form fields */
|
||||
|
||||
input[type="text"] {
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
line-height: 125%;
|
||||
}
|
||||
|
||||
/* styling for divs that will be invisible when printing
|
||||
when printing. */
|
||||
|
||||
div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
|
||||
font-size: 14pt;
|
||||
}
|
||||
@media print {
|
||||
div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
font-size: 8pt;
|
||||
}
|
||||
}
|
||||
|
||||
div.footer form.search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Recent Changes */
|
||||
|
||||
div.rc {
|
||||
overflow: hidden;
|
||||
}
|
||||
div.rc li + li {
|
||||
margin-top: 1em;
|
||||
}
|
||||
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;
|
||||
}
|
||||
div.new {
|
||||
background-color: #cfffcf;
|
||||
}
|
||||
|
||||
div.refer {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
div.message {
|
||||
background-color:#fee;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
img.xml {
|
||||
border:none;
|
||||
padding:1px;
|
||||
}
|
||||
a.small img {
|
||||
max-width:300px;
|
||||
}
|
||||
a.large img {
|
||||
max-width:600px;
|
||||
}
|
||||
div.sister {
|
||||
margin-right:1ex;
|
||||
background-color:inherit;
|
||||
}
|
||||
div.sister p {
|
||||
margin-top:0;
|
||||
}
|
||||
div.sister hr {
|
||||
display:none;
|
||||
}
|
||||
div.sister img {
|
||||
border:none;
|
||||
}
|
||||
|
||||
div.near, div.definition {
|
||||
background-color:#efe;
|
||||
}
|
||||
|
||||
div.sidebar {
|
||||
float:right;
|
||||
border:1px dotted #000;
|
||||
padding:0 1em;
|
||||
}
|
||||
div.sidebar ul {
|
||||
padding-left:1em;
|
||||
}
|
||||
|
||||
/* replacements, features */
|
||||
|
||||
ins {
|
||||
font-style: italic;
|
||||
text-decoration: none;
|
||||
}
|
||||
acronym, abbr {
|
||||
letter-spacing:0.1em;
|
||||
font-variant:small-caps;
|
||||
}
|
||||
|
||||
/* Interlink prefix not shown */
|
||||
a .site, a .separator {
|
||||
display: none;
|
||||
}
|
||||
a cite { font:inherit; }
|
||||
/* browser borkage */
|
||||
textarea[name="text"] { width:97%; height:80%; }
|
||||
textarea[name="summary"] { width:97%; height:3em; }
|
||||
/* comments */
|
||||
textarea[name="aftertext"] { width:97%; height:10em; }
|
||||
div.commentshown {
|
||||
font-size: 12pt;
|
||||
padding: 2em 0;
|
||||
}
|
||||
div.commenthidden {
|
||||
display:none;
|
||||
}
|
||||
div.commentshown {
|
||||
display:block;
|
||||
}
|
||||
p.comment {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.comment {
|
||||
font-size: 14pt;
|
||||
}
|
||||
div.comment h2 {
|
||||
margin-top: 5em;
|
||||
}
|
||||
/* comment pages with username, homepage, and email subscription */
|
||||
.comment form span { display: block; }
|
||||
.comment form span label { display: inline-block; width: 10em; }
|
||||
/* IE sucks */
|
||||
.comment input#username,
|
||||
.comment input#homepage,
|
||||
.comment input#mail { width: 20em; }
|
||||
|
||||
/* cal */
|
||||
div.month { padding:0; margin:0 2ex; }
|
||||
body > div.month {
|
||||
float:right;
|
||||
background-color: inherit;
|
||||
border:solid thin;
|
||||
padding:0 1ex;
|
||||
}
|
||||
.year > .month {
|
||||
float:left;
|
||||
}
|
||||
.footer {
|
||||
clear:both;
|
||||
}
|
||||
.month .title a.local {
|
||||
background-color: inherit;
|
||||
}
|
||||
.month a.local {
|
||||
background-color: #ddf;
|
||||
}
|
||||
.month a.today {
|
||||
background-color: #fdd;
|
||||
}
|
||||
.month a {
|
||||
color:inherit;
|
||||
font-weight:inherit;
|
||||
text-decoration: none;
|
||||
background-color: #eee;
|
||||
}
|
||||
/* history tables and other tables */
|
||||
table.history {
|
||||
border: none;
|
||||
}
|
||||
td.history {
|
||||
border: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border: none;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
margin: 1em 2em;
|
||||
}
|
||||
table tr td, table tr th {
|
||||
border: none;
|
||||
padding: 0.2em 0.5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.arab tr th {
|
||||
font-weight:normal;
|
||||
text-align:left;
|
||||
vertical-align:top;
|
||||
}
|
||||
table.arab, table.arab tr th, table.arab tr td {
|
||||
border:none;
|
||||
}
|
||||
th.nobreak {
|
||||
white-space:nowrap;
|
||||
}
|
||||
table.full { width:99%; margin-left:1px; }
|
||||
table.j td, table.j th, table tr td.j, table tr th.j, .j { text-align:justify; }
|
||||
table.l td, table.l th, table tr td.l, table tr th.l, .l { text-align:left; }
|
||||
table.r td, table.r th, table tr td.r, table tr th.r, .r { text-align:right; }
|
||||
table.c td, table.c th, table tr td.c, table tr th.c, .c { text-align:center; }
|
||||
table.t td { vertical-align: top; }
|
||||
td.half { width:50%; }
|
||||
td.third { width:33%; }
|
||||
|
||||
form table td { padding:5px; }
|
||||
|
||||
/* lists */
|
||||
dd { padding-bottom:0.5ex; }
|
||||
dl.inside dt { float:left; }
|
||||
/* search */
|
||||
div.search span.result { font-size:larger; }
|
||||
div.search span.info { font-size:smaller; font-style:italic; }
|
||||
div.search p.result { display:none; }
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
margin: 0 0 0 1ex;
|
||||
padding: 0;
|
||||
border: 1px solid #000;
|
||||
opacity: 0.3;
|
||||
background-color:#ffe;
|
||||
}
|
||||
|
||||
/* images */
|
||||
|
||||
div.content a.feed img, div.journal a.feed img,
|
||||
div.content a img.smiley, div.journal a img.smiley, img.smiley,
|
||||
div.content a.inline img, div.journal a.inline img,
|
||||
div.content li a.image img, div.journal li a.image img {
|
||||
margin: 0; padding: 0; border: none;
|
||||
}
|
||||
div.image a img {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.left { float:left; margin-right: 1em; }
|
||||
.right { float:right; margin-left: 1em; }
|
||||
.half img { height: 50%; width: 50%; }
|
||||
.face img { width: 200px; }
|
||||
div.left .left, div.right .right {
|
||||
float:none;
|
||||
}
|
||||
.center { text-align:center; }
|
||||
table.aside {
|
||||
float:right;
|
||||
width:40%;
|
||||
margin-left: 1em;
|
||||
padding: 1ex;
|
||||
border: 1px dotted #666;
|
||||
}
|
||||
table.aside td {
|
||||
text-align:left;
|
||||
}
|
||||
div.sidebar {
|
||||
float:right; width: 250px;
|
||||
text-align: right;
|
||||
border: none;
|
||||
margin: 1ex;
|
||||
}
|
||||
|
||||
.bigsidebar {
|
||||
float:right;
|
||||
width: 500px;
|
||||
border: none;
|
||||
margin-left: 1ex;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
dl.irc dt { width:20ex; float:left; text-align:right; clear:left; }
|
||||
dl.irc dt span.time { float:left; }
|
||||
dl.irc dd { margin-left:22ex; }
|
||||
|
||||
/* portrait */
|
||||
|
||||
div.footer, div.comment, hr { clear: both; }
|
||||
.portrait { float: left; font-size: small; margin-right: 1em; }
|
||||
.portrait a { color: #999; }
|
||||
|
||||
div.left { float:left; margin:1em; padding: 0.5em; }
|
||||
div.left p { display:table-cell; }
|
||||
div.left p + p { display:table-caption; caption-side:bottom; }
|
||||
|
||||
p.table a { float:left; width:20ex; }
|
||||
p.table + p { clear:both; }
|
||||
|
||||
/* mastodon */
|
||||
|
||||
div.mastodon { padding: 0 2em }
|
||||
div.mastodon .status {padding-top: 1ex; border-bottom: 1px solid grey;}
|
||||
div.mastodon .status:first-child {border-top: 1px solid grey;}
|
||||
|
||||
/* terminal "screenshots" */
|
||||
|
||||
.terminal {
|
||||
width: 80%;
|
||||
margin: 50px auto 100px auto;
|
||||
padding: 5px;
|
||||
font-size: 62%; /* fits 80ex */
|
||||
border: 1px solid #999;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 25px 50px #999;
|
||||
}
|
||||
|
||||
/* rss */
|
||||
|
||||
channel * { display: block; }
|
||||
|
||||
channel title {
|
||||
margin-top: 30pt;
|
||||
}
|
||||
copyright {
|
||||
font-size: 14pt;
|
||||
margin-top: 1em;
|
||||
}
|
||||
channel:before {
|
||||
font-size: 14pt;
|
||||
display: block;
|
||||
margin: 1em;
|
||||
padding: 0.5em;
|
||||
content: "This document is to be read in a feed reader. The item content is escaped HTML, which makes it hard to read for humans. Sorry!";
|
||||
color: red;
|
||||
border: 1px solid red;
|
||||
}
|
||||
license {
|
||||
font-size: 11pt;
|
||||
margin-bottom: 9pt;
|
||||
}
|
||||
contributor:before { content: "Last edited by "; }
|
||||
contributor:after { content: "."; }
|
||||
generator:before { content: "Feed generated by "; }
|
||||
generator:after { content: "."; }
|
||||
channel description {
|
||||
font-weight: bold;
|
||||
}
|
||||
item description {
|
||||
font-weight: normal;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
link, managingEditor, webMaster, license, url,
|
||||
docs, language,
|
||||
pubDate, lastBuildDate, ttl, guid, category, comments,
|
||||
docs, image title, image link,
|
||||
status, version, diff, history, importance {
|
||||
display: none;
|
||||
}
|
||||
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 |
72
modules/ban-mixed-scripts.pl
Normal file
@@ -0,0 +1,72 @@
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 Mixed Scripts
|
||||
|
||||
This module disallows ordinary users from posting words that consist of multiple
|
||||
scripts. Stuff like this: "It's diffіcult to find knowledgeable people on this
|
||||
topic, but youu sound like you know wgat you're taⅼkіng аboսt!" Did you notice
|
||||
the confusable characters? The sentence contains the following:
|
||||
ARMENIAN SMALL LETTER SEH
|
||||
CYRILLIC SMALL LETTER A
|
||||
CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
|
||||
SMALL ROMAN NUMERAL FIFTY
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use v5.10;
|
||||
|
||||
use Unicode::UCD qw(charprop);
|
||||
|
||||
AddModuleDescription('ban-mixed-scripts.pl', 'Ban Mixed Scripts Extension');
|
||||
|
||||
*OldBanMixedScriptsBannedContent = \&BannedContent;
|
||||
*BannedContent = \&NewBanMixedScriptsBannedContent;
|
||||
|
||||
sub NewBanMixedScriptsBannedContent {
|
||||
my $rule = OldBanMixedScriptsBannedContent(@_);
|
||||
$rule ||= BanMixedScript(@_);
|
||||
return $rule;
|
||||
}
|
||||
|
||||
sub BanMixedScript {
|
||||
my $str = shift;
|
||||
my @words = $str =~ m/\w+/g;
|
||||
my %seen;
|
||||
my %prop;
|
||||
for my $word (@words) {
|
||||
next if $seen{$word};
|
||||
$seen{$word} = 1;
|
||||
my $script;
|
||||
for my $char (split(//, $word)) {
|
||||
my $s = $prop{$char};
|
||||
if (not $s) {
|
||||
$s = charprop(ord($char), "Script_Extensions");
|
||||
if ($s eq 'Hiragana') {
|
||||
$s = 'Han'; # this mixing is ok
|
||||
}
|
||||
$prop{$char} = $s;
|
||||
}
|
||||
next if $s eq "Common";
|
||||
if (not $script) {
|
||||
$script = $s;
|
||||
} elsif ($script ne $s) {
|
||||
return "Mixed scripts in $word ($script and $s, if not more)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2012-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -62,9 +62,10 @@ push(@MyInitVariables, sub {
|
||||
|
||||
sub RegexpNewBannedContent {
|
||||
my $str = shift;
|
||||
# remove URLs as they are controlled by $BannedContent
|
||||
$str =~ s/$FullUrlPattern//g;
|
||||
# check whether Banned Content complains
|
||||
my $rule = RegexpOldBannedContent($str, @_);
|
||||
# remove URLs as they have been checked by $BannedContent
|
||||
$str =~ s/$FullUrlPattern//g;
|
||||
if (not $rule) {
|
||||
foreach (split(/\n/, GetPageContent($BannedRegexps))) {
|
||||
next unless m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/;
|
||||
|
||||
@@ -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'); }
|
||||
|
||||
@@ -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);}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004–2014 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004–2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -30,15 +30,43 @@ sub DoJournalRss {
|
||||
local $CollectingJournal = 1;
|
||||
# Fake the result of GetRcLines()
|
||||
local *GetRcLines = \&JournalRssGetRcLines;
|
||||
local *RcSelfAction = \&JournalRssSelfAction;
|
||||
local *RcPreviousAction = \&JournalRssPreviousAction;
|
||||
local *RcLastAction = \&JournalRssLastAction;
|
||||
SetParam('full', 1);
|
||||
print GetHttpHeader('application/xml') . GetRcRss();
|
||||
}
|
||||
|
||||
sub JournalRssParameters {
|
||||
my $more = '';
|
||||
foreach (@_, qw(rsslimit match search reverse monthly)) {
|
||||
my $val = GetParam($_, '');
|
||||
$more .= ";$_=$val" if $val;
|
||||
}
|
||||
return $more;
|
||||
}
|
||||
|
||||
sub JournalRssSelfAction {
|
||||
return "action=journal" . JournalRssParameters(qw(offset));
|
||||
}
|
||||
|
||||
sub JournalRssPreviousAction {
|
||||
my $num = GetParam('rsslimit', 10);
|
||||
my $offset = GetParam('offset', 0) + $num;
|
||||
return "action=journal;offset=$offset" . JournalRssParameters();
|
||||
}
|
||||
|
||||
sub JournalRssLastAction {
|
||||
return "action=journal" . JournalRssParameters();
|
||||
}
|
||||
|
||||
sub JournalRssGetRcLines {
|
||||
my $num = GetParam('rsslimit', 10);
|
||||
my $match = GetParam('match', '^\d\d\d\d-\d\d-\d\d');
|
||||
my $search = GetParam('search', '');
|
||||
my $reverse = GetParam('reverse', 0);
|
||||
my $monthly = GetParam('monthly', 0);
|
||||
my $offset = GetParam('offset', 0);
|
||||
my @pages = sort JournalSort (grep(/$match/, $search ? SearchTitleAndBody($search) : AllPagesList()));
|
||||
if ($monthly and not $match) {
|
||||
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime();
|
||||
@@ -48,28 +76,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 +106,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]";
|
||||
}
|
||||
|
||||
@@ -416,21 +416,16 @@ $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;
|
||||
print $q->ul($q->li([map { GetPageLink($_) } @pages]));
|
||||
print $q->p(ScriptLink('action=subscriptions', T('Your mail subscriptions'),
|
||||
'subscriptions') . '.');
|
||||
print $q->end_div();
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -48,12 +48,6 @@ sub MarkdownRule {
|
||||
return CloseHtmlEnvironments()
|
||||
. AddHtmlEnvironment("p");
|
||||
}
|
||||
# setext headers
|
||||
elsif ($bol and m/\G((\s*\n)*(.+?)[ \t]*\n(-+|=+)[ \t]*\n)/cg) {
|
||||
return CloseHtmlEnvironments()
|
||||
. (substr($4,0,1) eq '=' ? $q->h2($3) : $q->h3($3))
|
||||
. AddHtmlEnvironment('p');
|
||||
}
|
||||
# > blockquote
|
||||
# with continuation
|
||||
elsif ($bol and m/\G>/cg) {
|
||||
@@ -65,8 +59,8 @@ sub MarkdownRule {
|
||||
return CloseHtmlEnvironments() . $q->pre($1)
|
||||
. AddHtmlEnvironment("p");
|
||||
}
|
||||
# ` = code
|
||||
elsif (m/\G`([^`].*?)`/cg) {
|
||||
# ` = code may not start with a newline
|
||||
elsif (m/\G`([^\n`][^`]*)`/cg) {
|
||||
return $q->code($1);
|
||||
}
|
||||
# ***bold and italic***
|
||||
@@ -92,8 +86,8 @@ sub MarkdownRule {
|
||||
return AddOrCloseHtmlEnvironment('del');
|
||||
}
|
||||
# indented lists = nested lists
|
||||
elsif ($bol and m/\G(\s*\n)*()([*-]|\d+\.)[ \t]*/cg
|
||||
or InElement('li') && m/\G(\s*\n)+( *)([*-]|\d+\.)[ \t]*/cg) {
|
||||
elsif ($bol and m/\G(\s*\n)*()([*-]|\d+\.)[ \t]+/cg
|
||||
or InElement('li') && m/\G(\s*\n)+( *)([*-]|\d+\.)[ \t]+/cg) {
|
||||
my $nesting_goal = int(length($2)/4) + 1;
|
||||
my $tag = ($3 eq '*' or $3 eq '-') ? 'ul' : 'ol';
|
||||
my $nesting_current = 0;
|
||||
@@ -176,6 +170,12 @@ sub MarkdownRule {
|
||||
$params{-title} = $title if $title;
|
||||
return $q->a(\%params, $text);
|
||||
}
|
||||
# setext headers (must come after block quotes)
|
||||
elsif ($bol and m/\G((\s*\n)*(.+?)[ \t]*\n(-+|=+)[ \t]*\n)/cg) {
|
||||
return CloseHtmlEnvironments()
|
||||
. (substr($4,0,1) eq '=' ? $q->h2($3) : $q->h3($3))
|
||||
. AddHtmlEnvironment('p');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ sub MarkdownExtraRule {
|
||||
elsif (InElement('em', 'style="font-style: normal; text-decoration: underline"') and m/\G_/cg) {
|
||||
return CloseHtmlEnvironment('em');
|
||||
}
|
||||
elsif ($bol and m/\G_/cg or m/\G(?<=\P{Word})_/cg) {
|
||||
elsif ($bol and m/\G_/cg or m/\G(?<=\P{Word})_(?=\S)/cg) {
|
||||
return AddHtmlEnvironment('em', 'style="font-style: normal; text-decoration: underline"');
|
||||
}
|
||||
# //italic//
|
||||
@@ -201,7 +201,7 @@ sub MarkdownExtraRule {
|
||||
elsif (InElement('em') and m/\G\//cg) {
|
||||
return CloseHtmlEnvironment('em');
|
||||
}
|
||||
elsif ($bol and m/\G\//cg or m/\G(?<=\P{Word})\//cg) {
|
||||
elsif ($bol and m/\G\//cg or m/\G(?<=[|[:space:]])\/(?=\S)/cg) {
|
||||
return AddHtmlEnvironment('em');
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
70
modules/rename-pages.pl
Normal file
@@ -0,0 +1,70 @@
|
||||
# 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;
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
@@ -239,6 +239,11 @@ sub TagFind {
|
||||
return @result;
|
||||
}
|
||||
|
||||
sub TagsTerms {
|
||||
my $string = shift;
|
||||
return grep(/./, $string =~ /\"([^\"]+)\"|(\S+)/g);
|
||||
}
|
||||
|
||||
*OldTagFiltered = \&Filtered;
|
||||
*Filtered = \&NewTagFiltered;
|
||||
|
||||
@@ -246,7 +251,7 @@ sub NewTagFiltered { # called within a lock!
|
||||
my ($string, @pages) = @_;
|
||||
my %page = map { $_ => 1 } @pages;
|
||||
# looking at all the "tag:SOME TERMS" and and tag:TERM
|
||||
my @tagterms = map { FreeToNormal($_) } grep(/^-?tag:/, shift =~ /\"([^\"]+)\"|(\S+)/g);
|
||||
my @tagterms = map { FreeToNormal($_) } grep(/^-?tag:/, TagsTerms($string));
|
||||
my @positives = map {substr($_, 4)} grep(/^tag:/, @tagterms);
|
||||
my @negatives = map {substr($_, 5)} grep(/^-tag:/, @tagterms);
|
||||
if (@positives) {
|
||||
@@ -260,8 +265,9 @@ sub NewTagFiltered { # called within a lock!
|
||||
foreach my $id (TagFind(@negatives)) {
|
||||
delete $page{$id};
|
||||
}
|
||||
# filter out the tags from the search string
|
||||
$string = join(' ', grep(!/^-?tag:/, $string =~ /\"([^\"]+)\"|(\S+)/g));
|
||||
# filter out the tags from the search string, and add quotes which might have
|
||||
# been stripped
|
||||
$string = join(' ', map { qq{"$_"} } grep(!/^-?tag:/, TagsTerms($string)));
|
||||
# run the old code for any remaining search terms
|
||||
return OldTagFiltered($string, sort keys %page);
|
||||
}
|
||||
@@ -279,11 +285,12 @@ We're need to remove all tag terms (again) in order to not confuse it.
|
||||
*SearchString = \&NewTagSearchString;
|
||||
|
||||
sub NewTagSearchString {
|
||||
# filter out the negative tags from the search string
|
||||
my $string = join(' ', map { NormalToFree($_) }
|
||||
grep(!/^-tag:/, shift =~ /\"([^\"]+)\"|(\S+)/g));
|
||||
my ($string, @rest) = @_;
|
||||
# filter out the negative tags from the search string, and add quotes which
|
||||
# might have been stripped
|
||||
$string = join(' ', map { NormalToFree($_) } map { qq{"$_"} } grep(!/^-tag:/, TagsTerms($string)));
|
||||
return 1 unless $string;
|
||||
return OldTagSearchString($string, @_);
|
||||
return OldTagSearchString($string, @rest);
|
||||
}
|
||||
|
||||
=pod
|
||||
|
||||
@@ -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
|
||||
################################################################################
|
||||
|
||||
@@ -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:
|
||||
@@ -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.
|
||||
@@ -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
|
||||
################################################################################
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
################################################################################
|
||||
|
||||
@@ -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
|
||||
################################################################################
|
||||
|
||||
@@ -230,8 +230,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:
|
||||
@@ -470,8 +470,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 +504,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.
|
||||
@@ -1237,6 +1237,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.
|
||||
|
||||
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
|
||||
################################################################################
|
||||
|
||||
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–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
|
||||
@@ -137,7 +137,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 {
|
||||
@@ -206,10 +206,12 @@ sub send_mail {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
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 ();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
933
stuff/gopher-server.pl
Executable file
@@ -0,0 +1,933 @@
|
||||
#!/usr/bin/env perl
|
||||
# Copyright (C) 2017–2018 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
use strict;
|
||||
use 5.10.0;
|
||||
use base qw(Net::Server::Fork); # any personality will do
|
||||
use MIME::Base64;
|
||||
use Text::Wrap;
|
||||
use List::Util qw(first);
|
||||
use Socket;
|
||||
|
||||
our($RunCGI, $DataDir, %IndexHash, @IndexList, $IndexFile, $TagFile, $q,
|
||||
%Page, $OpenPageName, $MaxPost, $ShowEdits, %Locks, $CommentsPattern,
|
||||
$CommentsPrefix, $EditAllowed, $NoEditFile, $SiteName, $ScriptName,
|
||||
$Now, %RecentVisitors, $SurgeProtectionTime, $SurgeProtectionViews,
|
||||
$SurgeProtection);
|
||||
|
||||
my $external_image_path = '/home/alex/alexschroeder.ch/pics/';
|
||||
|
||||
# Sadly, we need this information before doing anything else
|
||||
my %args = (proto => 'ssl');
|
||||
for (grep(/--wiki_(key|cert)_file=/, @ARGV)) {
|
||||
$args{SSL_cert_file} = $1 if /--wiki_cert_file=(.*)/;
|
||||
$args{SSL_key_file} = $1 if /--wiki_key_file=(.*)/;
|
||||
}
|
||||
if ($args{SSL_cert_file} and not $args{SSL_key_file}
|
||||
or not $args{SSL_cert_file} and $args{SSL_key_file}) {
|
||||
die "I must have both --wiki_key_file and --wiki_cert_file\n";
|
||||
} elsif ($args{SSL_cert_file} and $args{SSL_key_file}) {
|
||||
OddMuse->run(%args);
|
||||
} else {
|
||||
OddMuse->run;
|
||||
}
|
||||
|
||||
sub options {
|
||||
my $self = shift;
|
||||
my $prop = $self->{'server'};
|
||||
my $template = shift;
|
||||
|
||||
# setup options in the parent classes
|
||||
$self->SUPER::options($template);
|
||||
|
||||
# add a single value option
|
||||
$prop->{wiki} ||= undef;
|
||||
$template->{wiki} = \$prop->{wiki};
|
||||
|
||||
$prop->{wiki_dir} ||= undef;
|
||||
$template->{wiki_dir} = \$prop->{wiki_dir};
|
||||
|
||||
$prop->{wiki_pages} ||= [];
|
||||
$template->{wiki_pages} = $prop->{wiki_pages};
|
||||
|
||||
$prop->{menu} ||= [];
|
||||
$template->{menu} = $prop->{menu};
|
||||
|
||||
$prop->{menu_file} ||= [];
|
||||
$template->{menu_file} = $prop->{menu_file};
|
||||
|
||||
# $prop->{wiki_pem_file} ||= undef;
|
||||
# $template->{wiki_pem_file} = $prop->{wiki_pem_file};
|
||||
}
|
||||
|
||||
sub post_configure_hook {
|
||||
my $self = shift;
|
||||
$self->write_help if $ARGV[0] eq '--help';
|
||||
|
||||
$DataDir = $self->{server}->{wiki_dir} || $ENV{WikiDataDir} || '/tmp/oddmuse';
|
||||
|
||||
$self->log(3, "PID $$");
|
||||
$self->log(3, "Host " . ("@{$self->{server}->{host}}" || "*"));
|
||||
$self->log(3, "Port @{$self->{server}->{port}}");
|
||||
$self->log(3, "Wiki data dir is $DataDir\n");
|
||||
|
||||
$RunCGI = 0;
|
||||
my $wiki = $self->{server}->{wiki} || "./wiki.pl";
|
||||
$self->log(1, "Running $wiki\n");
|
||||
unless (my $return = do $wiki) {
|
||||
$self->log(1, "couldn't parse wiki library $wiki: $@") if $@;
|
||||
$self->log(1, "couldn't do wiki library $wiki: $!") unless defined $return;
|
||||
$self->log(1, "couldn't run wiki library $wiki") unless $return;
|
||||
}
|
||||
|
||||
# make sure search is sorted newest first because NewTagFiltered resorts
|
||||
*OldGopherFiltered = \&Filtered;
|
||||
*Filtered = \&NewGopherFiltered;
|
||||
*ReportError = sub {
|
||||
my ($error, $status, $log, @html) = @_;
|
||||
$self->print_error("Error: $error");
|
||||
map { ReleaseLockDir($_); } keys %Locks;
|
||||
exit 2;
|
||||
};
|
||||
}
|
||||
|
||||
my $usage = << 'EOT';
|
||||
This server serves a wiki as a gopher site.
|
||||
|
||||
It implements Net::Server and thus all the options available to
|
||||
Net::Server are also available here. Additional options are available:
|
||||
|
||||
wiki - this is the path to the Oddmuse script
|
||||
wiki_dir - this is the path to the Oddmuse data directory
|
||||
wiki_pages - this is a page to show on the entry menu
|
||||
menu - this is the description of a gopher menu to prepend
|
||||
menu_file - this is the filename of the gopher menu to prepend
|
||||
wiki_cert_file - the filename containing a certificate in PEM format
|
||||
wiki_key_file - the filename containing a private key in PEM format
|
||||
|
||||
For many of the options, more information can be had in the Net::Server
|
||||
documentation. This is important if you want to daemonize the server. You'll
|
||||
need to use --pid_file so that you can stop it using a script, --setsid to
|
||||
daemonize it, --log_file to write keep logs, and you'll net to set the user or
|
||||
group using --user or --group such that the server has write access to the data
|
||||
directory.
|
||||
|
||||
For testing purposes, you can start with the following:
|
||||
|
||||
--port=7070
|
||||
The port to listen to, defaults to a random port.
|
||||
--log_level=4
|
||||
The log level to use, defaults to 2.
|
||||
--wiki_dir=/var/oddmuse
|
||||
The wiki directory, defaults to the value of the "WikiDataDir" environment
|
||||
variable or "/tmp/oddmuse".
|
||||
--wiki_lib=/home/alex/src/oddmuse/wiki.pl
|
||||
The Oddmuse main script, defaults to "./wiki.pl".
|
||||
--wiki_pages=SiteMap
|
||||
This adds a page to the main index. Can be used multiple times.
|
||||
--help
|
||||
Prints this message.
|
||||
|
||||
Example invocation:
|
||||
|
||||
/home/alex/src/oddmuse/stuff/gopher-server.pl \
|
||||
--port=7070 \
|
||||
--wiki=/home/alex/src/oddmuse/wiki.pl \
|
||||
--pid_file=/tmp/oddmuse/gopher.pid \
|
||||
--wiki_dir=/tmp/oddmuse \
|
||||
--wiki_pages=Homepage \
|
||||
--wiki_pages=Gopher
|
||||
|
||||
Run the script and test it:
|
||||
|
||||
echo | nc localhost 7070
|
||||
lynx gopher://localhost:7070
|
||||
|
||||
If you want to use SSL, you need to provide PEM files containing certificate and
|
||||
private key. To create self-signed files, for example:
|
||||
|
||||
openssl req -new -x509 -days 365 -nodes -out \
|
||||
gopher-server-cert.pem -keyout gopher-server-key.pem
|
||||
|
||||
Make sure the common name you provide matches your domain name!
|
||||
|
||||
Note that parameters should not contain spaces. Thus:
|
||||
|
||||
/home/alex/src/oddmuse/stuff/gopher-server.pl \
|
||||
--port=7070 \
|
||||
--log_level=3 \
|
||||
--wiki=/home/alex/src/oddmuse/wiki.pl \
|
||||
--wiki_dir=/home/alex/alexschroeder \
|
||||
--menu=Moku_Pona_Updates \
|
||||
--menu_file=~/.moku-pona/updates.txt \
|
||||
--menu=Moku_Pona_Sites \
|
||||
--menu_file=~/.moku-pona/sites.txt
|
||||
|
||||
EOT
|
||||
|
||||
run();
|
||||
|
||||
sub NewGopherFiltered {
|
||||
my @pages = OldGopherFiltered(@_);
|
||||
@pages = sort newest_first @pages;
|
||||
return @pages;
|
||||
}
|
||||
|
||||
sub normal_to_free {
|
||||
my $title = shift;
|
||||
$title =~ s/_/ /g;
|
||||
return $title;
|
||||
}
|
||||
|
||||
sub print_text {
|
||||
my $self = shift;
|
||||
my $text = shift;
|
||||
print($text); # bytes
|
||||
}
|
||||
|
||||
sub print_menu {
|
||||
my $self = shift;
|
||||
my $display = shift;
|
||||
my $selector = shift;
|
||||
my $host = shift
|
||||
|| $self->{server}->{host}->[0]
|
||||
|| $self->{server}->{sockaddr};
|
||||
my $port = shift
|
||||
|| $self->{server}->{port}->[0]
|
||||
|| $self->{server}->{sockport};
|
||||
my $encoded = shift;
|
||||
|
||||
$selector = join('/', map { UrlEncode($_) } split(/\//, $selector)) unless $encoded;
|
||||
$self->print_text(join("\t", $display, $selector, $host, $port)
|
||||
. "\r\n");
|
||||
}
|
||||
|
||||
sub print_info {
|
||||
my $self = shift;
|
||||
my $info = shift;
|
||||
$self->print_menu("i$info", "");
|
||||
}
|
||||
|
||||
sub print_error {
|
||||
my $self = shift;
|
||||
my $error = shift;
|
||||
$self->print_menu("3$error", "");
|
||||
}
|
||||
|
||||
sub serve_main_menu {
|
||||
my $self = shift;
|
||||
my $more = shift;
|
||||
$self->log(3, "Serving main menu");
|
||||
$self->print_info("Welcome to the Gopher version of this wiki.");
|
||||
$self->print_info("");
|
||||
|
||||
$self->print_info("Phlog:");
|
||||
my @pages = sort { $b cmp $a } grep(/^\d\d\d\d-\d\d-\d\d/, @IndexList);
|
||||
# we should check for pages marked for deletion!
|
||||
for my $id (@pages[0..9]) {
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
$self->print_menu("1" . "More...", "do/more");
|
||||
$self->print_info("");
|
||||
|
||||
for my $id (@{$self->{server}->{wiki_pages}}) {
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
|
||||
for my $id (@{$self->{server}->{menu}}) {
|
||||
$self->print_menu("1" . normal_to_free($id), "map/$id");
|
||||
}
|
||||
|
||||
$self->print_menu("1" . "Recent Changes", "do/rc");
|
||||
$self->print_menu("0" . "Gopher RSS", "do/rss");
|
||||
$self->print_menu("7" . "Find matching page titles", "do/match");
|
||||
$self->print_menu("7" . "Full text search", "do/search");
|
||||
$self->print_menu("1" . "Index of all pages", "do/index");
|
||||
|
||||
if ($TagFile) {
|
||||
$self->print_menu("1" . "Index of all tags", "do/tags");
|
||||
}
|
||||
|
||||
if ($EditAllowed and not IsFile($NoEditFile)) {
|
||||
$self->print_menu("w" . "New page", "do/new");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_phlog_archive {
|
||||
my $self = shift;
|
||||
$self->log(3, "Serving phlog archive");
|
||||
my @pages = sort { $b cmp $a } grep(/^\d\d\d\d-\d\d-\d\d/, @IndexList);
|
||||
for my $id (@pages) {
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_index {
|
||||
my $self = shift;
|
||||
$self->log(3, "Serving index of all pages");
|
||||
for my $id (sort newest_first @IndexList) {
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_match {
|
||||
my $self = shift;
|
||||
my $match = shift;
|
||||
$self->log(3, "Serving pages matching " . UrlEncode($match));
|
||||
$self->print_info("Use a regular expression to match page titles.");
|
||||
$self->print_info("Spaces in page titles are underlines, '_'.");
|
||||
for my $id (sort newest_first grep(/$match/i, @IndexList)) {
|
||||
$self->print_menu( "1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_search {
|
||||
my $self = shift;
|
||||
my $str = shift;
|
||||
$self->log(3, "Serving search result for " . UrlEncode($str));
|
||||
$self->print_info("Use regular expressions separated by spaces.");
|
||||
SearchTitleAndBody($str, sub {
|
||||
my $id = shift;
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
});
|
||||
}
|
||||
|
||||
sub serve_tags {
|
||||
my $self = shift;
|
||||
$self->log(3, "Serving tag cloud");
|
||||
# open the DB file
|
||||
my %h = TagReadHash();
|
||||
my %count = ();
|
||||
foreach my $tag (grep !/^_/, keys %h) {
|
||||
$count{$tag} = @{$h{$tag}};
|
||||
}
|
||||
foreach my $id (sort { $count{$b} <=> $count{$a} } keys %count) {
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/tag");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_rc {
|
||||
my $self = shift;
|
||||
my $showedit = $ShowEdits = shift;
|
||||
$self->log(3, "Serving recent changes"
|
||||
. ($showedit ? " including minor changes" : ""));
|
||||
|
||||
$self->print_info("Recent Changes");
|
||||
if ($showedit) {
|
||||
$self->print_menu("1" . "Skip minor edits", "do/rc");
|
||||
} else {
|
||||
$self->print_menu("1" . "Show minor edits", "do/rc/showedits");
|
||||
}
|
||||
|
||||
ProcessRcLines(
|
||||
sub {
|
||||
my $date = shift;
|
||||
$self->print_info("");
|
||||
$self->print_info("$date");
|
||||
$self->print_info("");
|
||||
},
|
||||
sub {
|
||||
my($id, $ts, $author_host, $username, $summary, $minor, $revision,
|
||||
$languages, $cluster, $last) = @_;
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
for my $line (split(/\n/, wrap(' ', ' ', $summary))) {
|
||||
$self->print_info($line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sub serve_rss {
|
||||
my $self = shift;
|
||||
$self->log(3, "Serving Gopher RSS");
|
||||
my $host = shift
|
||||
|| $self->{server}->{host}->[0]
|
||||
|| $self->{server}->{sockaddr};
|
||||
my $port = shift
|
||||
|| $self->{server}->{port}->[0]
|
||||
|| $self->{server}->{sockport};
|
||||
my $gopher = "gopher://$host:$port/"; # use gophers for TLS?
|
||||
local $ScriptName = $gopher;
|
||||
my $rss = GetRcRss();
|
||||
$rss =~ s!$ScriptName\?action=rss!${gopher}1do/rss!g;
|
||||
$rss =~ s!$ScriptName\?action=history;id=([^[:space:]<]*)!${gopher}1$1/history!g;
|
||||
$rss =~ s!$ScriptName/([^[:space:]<]*)!${gopher}0$1!g;
|
||||
$rss =~ s!<wiki:diff>.*</wiki:diff>\n!!g;
|
||||
print $rss;
|
||||
}
|
||||
|
||||
sub serve_map {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
$self->log(3, "Serving map " . UrlEncode($id));
|
||||
my @menu = @{$self->{server}->{menu}};
|
||||
my $i = first { $id eq $menu[$_] } 0..$#menu;
|
||||
my $file = $self->{server}->{menu_file}->[$i];
|
||||
if (-f $file and open(my $fh, '<:encoding(UTF-8)', $file)) {
|
||||
local $/ = undef;
|
||||
my $text = <$fh>;
|
||||
$self->log(4, "Map has " . length($text) . " characters");
|
||||
$self->print_text($text);
|
||||
} else {
|
||||
$self->log(1, "Error reading $file");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_page_comment_link {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $revision = shift;
|
||||
if (not $revision and $CommentsPattern) {
|
||||
if ($id =~ /$CommentsPattern/) {
|
||||
my $original = $1;
|
||||
# sometimes we are on a comment page and cannot derive the original
|
||||
$self->print_menu("1" . "Back to the original page",
|
||||
"$original/menu") if $original;
|
||||
$self->print_menu("w" . "Add a comment", "$id/append/text");
|
||||
} else {
|
||||
my $comments = $CommentsPrefix . $id;
|
||||
$self->print_menu("1" . "Comments on this page", "$comments/menu");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_page_history_link {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $revision = shift;
|
||||
if (not $revision) {
|
||||
$self->print_menu("1" . "Page History", "$id/history");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_file_page_menu {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $type = shift;
|
||||
my $revision = shift;
|
||||
my $code = substr($type, 0, 6) eq 'image/' ? 'I' : '9';
|
||||
$self->log(3, "Serving file page menu for " . UrlEncode($id));
|
||||
$self->print_menu($code . normal_to_free($id)
|
||||
. ($revision ? "/$revision" : ""), $id);
|
||||
$self->serve_page_comment_link($id, $revision);
|
||||
$self->serve_page_history_link($id, $revision);
|
||||
}
|
||||
|
||||
sub serve_text_page_menu {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $page = shift;
|
||||
my $revision = shift;
|
||||
$self->log(3, "Serving text page menu for " . UrlEncode($id)
|
||||
. ($revision ? "/$revision" : ""));
|
||||
|
||||
$self->print_info("The text of this page:");
|
||||
$self->print_menu("0" . normal_to_free($id),
|
||||
$id . ($revision ? "/$revision" : ""));
|
||||
$self->print_menu("h" . normal_to_free($id),
|
||||
$id . ($revision ? "/$revision" : "") . "/html");
|
||||
$self->print_menu("w" . "Replace " . normal_to_free($id),
|
||||
$id . "/write/text");
|
||||
|
||||
$self->serve_page_comment_link($id, $revision);
|
||||
$self->serve_page_history_link($id, $revision);
|
||||
|
||||
my $first = 1;
|
||||
while ($page->{text} =~ /
|
||||
\[\[ (?<title>[^\]|]*) (?:\|(?<text>[^\]]*))? \]\]
|
||||
| \[ (?<url>https?:\/\/\S+) \s+ (?<text>[^\]]*) \]
|
||||
| \[ (?<text>[^\]]*) \] \( (?<url>https?:\/\/\S+) \)
|
||||
| \[ gopher:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
|
||||
(?:\/(?<type>\d) (?<selector>\S+))?
|
||||
\s+ (?<text>[^\]]+)\]
|
||||
| \[ (?<text>[^\]]+) \]
|
||||
\( gopher:\/\/ (?<hostname>[^:\/]*) (?::(?<port>\d+))?
|
||||
(?:\/(?<type>\d) (?<selector>\S+))? \)
|
||||
/xg) {
|
||||
my ($title, $text, $url, $hostname,
|
||||
$port, $type, $selector)
|
||||
= ($+{title}, $+{text}, $+{url}, $+{hostname},
|
||||
$+{port}||70, $+{type}||1, $+{selector});
|
||||
if ($first) {
|
||||
$self->print_info("");
|
||||
$self->print_info("Links leaving " . normal_to_free($id) . ":");
|
||||
$first = 0;
|
||||
}
|
||||
if ($hostname) {
|
||||
$self->print_text(join("\t", $type . $text, $selector, $hostname, $port) . "\r\n");
|
||||
} elsif ($url) {
|
||||
$self->print_menu("h$text", "URL:" . $url, undef, undef, 1);
|
||||
} elsif ($title and substr($title, 0, 4) eq 'tag:') {
|
||||
$self->print_menu("1" . ($text||substr($title, 4)),
|
||||
substr($title, 4) . "/tag");
|
||||
} elsif ($title =~ s!^image[/a-z]* external:!pics/!) {
|
||||
$self->print_menu("I" . $text||$title, $title);
|
||||
} elsif ($title) {
|
||||
$title =~ s!^image[/a-z]*:!!i;
|
||||
$self->print_menu("1" . ($text||$title), $title . "/menu");
|
||||
}
|
||||
}
|
||||
|
||||
$first = 1;
|
||||
while ($page->{text} =~ /\[https?:\/\/gopher\.floodgap\.com\/gopher\/gw\?a=gopher%3a%2f%2f(.*?)(?:%3a(\d+))?%2f(.)(\S+)\s+([^\]]+)\]/gi) {
|
||||
my ($hostname, $port, $type, $selector, $text) = ($1, $2||"70", $3, $4, $5);
|
||||
if ($first) {
|
||||
$self->print_info("");
|
||||
$self->print_info("Gopher links (via Floodgap):");
|
||||
$first = 0;
|
||||
}
|
||||
$selector =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eig; # url unescape
|
||||
$self->print_text(join("\t", $type . $text, $selector, $hostname, $port)
|
||||
. "\r\n");
|
||||
}
|
||||
|
||||
if ($page->{text} =~ m/<journal search tag:(\S+)>\s*/) {
|
||||
my $tag = $1;
|
||||
$self->print_info("");
|
||||
$self->serve_tag_list($tag);
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_page_history {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
$self->log(3, "Serving history of " . UrlEncode($id));
|
||||
OpenPage($id);
|
||||
|
||||
$self->print_menu("1" . normal_to_free($id) . " (current)", "$id/menu");
|
||||
$self->print_info(CalcTime($Page{ts})
|
||||
. " by " . GetAuthor($Page{username})
|
||||
. ($Page{summary} ? ": $Page{summary}" : "")
|
||||
. ($Page{minor} ? " (minor)" : ""));
|
||||
|
||||
foreach my $revision (GetKeepRevisions($OpenPageName)) {
|
||||
my $keep = GetKeptRevision($revision);
|
||||
$self->print_menu("1" . normal_to_free($id) . " ($keep->{revision})",
|
||||
"$id/$keep->{revision}/menu");
|
||||
$self->print_info(CalcTime($keep->{ts})
|
||||
. " by " . GetAuthor($keep->{username})
|
||||
. ($keep->{summary} ? ": $keep->{summary}" : "")
|
||||
. ($keep->{minor} ? " (minor)" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
sub get_page {
|
||||
my $id = shift;
|
||||
my $revision = shift;
|
||||
my $page;
|
||||
|
||||
if ($revision) {
|
||||
$OpenPageName = $id;
|
||||
$page = GetKeptRevision($revision);
|
||||
} else {
|
||||
OpenPage($id);
|
||||
$page = \%Page;
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
sub serve_page_menu {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $revision = shift;
|
||||
my $page = get_page($id, $revision);
|
||||
|
||||
if (my ($type) = TextIsFile($page->{text})) {
|
||||
$self->serve_file_page_menu($id, $type, $revision);
|
||||
} else {
|
||||
$self->serve_text_page_menu($id, $page, $revision);
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_file_page {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $page = shift;
|
||||
$self->log(3, "Serving " . UrlEncode($id) . " as file");
|
||||
my ($encoded) = $page->{text} =~ /^[^\n]*\n(.*)/s;
|
||||
$self->log(4, UrlEncode($id) . " has " . length($encoded)
|
||||
. " bytes of MIME encoded data");
|
||||
my $data = decode_base64($encoded);
|
||||
$self->log(4, UrlEncode($id) . " has " . length($data)
|
||||
. " bytes of binary data");
|
||||
binmode(STDOUT, ":raw");
|
||||
print($data);
|
||||
}
|
||||
|
||||
sub serve_text_page {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $page = shift;
|
||||
my $text = $page->{text};
|
||||
$text =~ s/^\./../mg;
|
||||
$text =~ s/\[\[tag:([^]]+)\]\]/'#' . join('_', split(' ', $1))/mge;
|
||||
$self->log(3, "Serving " . UrlEncode($id) . " as " . length($text)
|
||||
. " bytes of text");
|
||||
$self->print_text($text);
|
||||
}
|
||||
|
||||
sub serve_page {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $revision = shift;
|
||||
my $page = get_page($id, $revision);
|
||||
if (my ($type) = TextIsFile($page->{text})) {
|
||||
$self->serve_file_page($id, $page);
|
||||
} else {
|
||||
$self->serve_text_page($id, $page);
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_page_html {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $revision = shift;
|
||||
my $page = get_page($id, $revision);
|
||||
|
||||
$self->log(3, "Serving " . UrlEncode($id) . " as HTML");
|
||||
|
||||
my $title = normal_to_free($id);
|
||||
print GetHtmlHeader(Ts('%s:', $SiteName) . ' ' . UnWiki($title), $id);
|
||||
print GetHeaderDiv($id, $title);
|
||||
print $q->start_div({-class=>'wrapper'});
|
||||
|
||||
if ($revision) {
|
||||
# no locking of the file, no updating of the cache
|
||||
PrintWikiToHTML($page->{text});
|
||||
} else {
|
||||
PrintPageHtml();
|
||||
}
|
||||
PrintFooter($id, $revision);
|
||||
}
|
||||
|
||||
sub serve_redirect {
|
||||
my $self = shift;
|
||||
my $url = shift;
|
||||
print qq{<!DOCTYPE HTML>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url=$url">
|
||||
<title>Redirection</title>
|
||||
</head>
|
||||
<body>
|
||||
If you are not redirected automatically, follow this <a href='$url'>link</a>.
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
}
|
||||
|
||||
sub serve_image {
|
||||
my $self = shift;
|
||||
my $pic = shift;
|
||||
my $file = $external_image_path . $pic;
|
||||
# no tricks
|
||||
if ($file !~ /\.\./ and $file !~ /\/\//
|
||||
and -f $file and open(my $fh, "<", $file)) {
|
||||
local $/ = undef;
|
||||
my $data = <$fh>;
|
||||
$self->log(4, $pic . " has " . length($data)
|
||||
. " bytes of binary data");
|
||||
binmode(STDOUT, ":raw");
|
||||
print($data);
|
||||
} else {
|
||||
$self->log(1, "Error reading $file: $!");
|
||||
}
|
||||
}
|
||||
|
||||
sub newest_first {
|
||||
my ($A, $B) = ($a, $b);
|
||||
if ($A =~ /^\d\d\d\d-\d\d-\d\d/ and $B =~ /^\d\d\d\d-\d\d-\d\d/) {
|
||||
return $B cmp $A;
|
||||
}
|
||||
$A cmp $B;
|
||||
}
|
||||
|
||||
sub serve_tag_list {
|
||||
my $self = shift;
|
||||
my $tag = shift;
|
||||
$self->print_info("Search result for tag $tag:");
|
||||
for my $id (sort newest_first TagFind($tag)) {
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
}
|
||||
|
||||
sub serve_tag {
|
||||
my $self = shift;
|
||||
my $tag = shift;
|
||||
$self->log(3, "Serving tag " . UrlEncode($tag));
|
||||
if ($IndexHash{$tag}) {
|
||||
$self->print_info("This page is about the tag $tag.");
|
||||
$self->print_menu("1" . normal_to_free($tag), "$tag/menu");
|
||||
$self->print_info("");
|
||||
}
|
||||
$self->serve_tag_list($tag);
|
||||
}
|
||||
|
||||
sub serve_error {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $error = shift;
|
||||
$self->log(3, "Error ('" . UrlEncode($id) . "'): $error");
|
||||
$self->print_error("Error ('" . UrlEncode($id) . "'): $error");
|
||||
}
|
||||
|
||||
sub write_help {
|
||||
my $self = shift;
|
||||
my @lines = split(/\n/, <<"EOF");
|
||||
This is how your document should start:
|
||||
```
|
||||
username: Alex Schroeder
|
||||
summary: typo fixed
|
||||
```
|
||||
This is the text of your document.
|
||||
Just write whatever.
|
||||
|
||||
Note the space after the colon for metadata fields.
|
||||
More metadata fields are allowed:
|
||||
`minor` is 1 if this is a minor edit. The default is 0.
|
||||
EOF
|
||||
for my $line (@lines) {
|
||||
$self->print_info($line);
|
||||
}
|
||||
}
|
||||
|
||||
sub write_page_ok {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
$self->print_info("Page was saved.");
|
||||
$self->print_menu("1" . normal_to_free($id), "$id/menu");
|
||||
}
|
||||
|
||||
sub write_page_error {
|
||||
my $self = shift;
|
||||
my $error = shift;
|
||||
$self->log(4, "Not saved: $error");
|
||||
$self->print_error("Page was not saved: $error");
|
||||
map { ReleaseLockDir($_); } keys %Locks;
|
||||
}
|
||||
|
||||
sub write_data {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $data = shift;
|
||||
my $param = shift||'text';
|
||||
SetParam($param, $data);
|
||||
my $error;
|
||||
eval {
|
||||
local *ReBrowsePage = sub {};
|
||||
local *ReportError = sub { $error = shift };
|
||||
DoPost($id);
|
||||
};
|
||||
if ($error) {
|
||||
$self->write_page_error($error);
|
||||
} else {
|
||||
$self->write_page_ok($id);
|
||||
}
|
||||
}
|
||||
|
||||
sub write_file_page {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $data = shift;
|
||||
my $type = shift || 'application/octet-stream';
|
||||
$self->write_page_error("page title is missing") unless $id;
|
||||
$self->log(3, "Posting " . length($data) . " bytes of $type to page "
|
||||
. UrlEncode($id));
|
||||
# no metadata
|
||||
$self->write_data($id, "#FILE $type\n" . encode_base64($data));
|
||||
}
|
||||
|
||||
sub write_text {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $data = shift;
|
||||
my $param = shift;
|
||||
|
||||
utf8::decode($data);
|
||||
|
||||
my ($lead, $meta, $text) = split(/^```\s*(?:meta)?\n/m, $data, 3);
|
||||
|
||||
if (not $lead and $meta) {
|
||||
while ($meta =~ /^([a-z-]+): (.*)/mg) {
|
||||
if ($1 eq 'minor' and $2) {
|
||||
SetParam('recent_edit', 'on'); # legacy UseMod parameter name
|
||||
} else {
|
||||
SetParam($1, $2);
|
||||
if ($1 eq "title") {
|
||||
$id = $2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->log(3, ($param eq 'text' ? "Posting" : "Appending")
|
||||
. " " . length($text) . " characters (with metadata) to page $id");
|
||||
$self->write_data($id, $text, $param);
|
||||
} else {
|
||||
# no meta data
|
||||
$self->log(3, ($param eq 'text' ? "Posting" : "Appending")
|
||||
. " " . length($data) . " characters to page $id") if $id;
|
||||
$self->write_data($id, $data, $param);
|
||||
}
|
||||
}
|
||||
|
||||
sub write_text_page {
|
||||
my $self = shift;
|
||||
$self->write_text(@_, 'text');
|
||||
}
|
||||
|
||||
sub append_text_page {
|
||||
my $self = shift;
|
||||
$self->write_text(@_, 'aftertext');
|
||||
}
|
||||
|
||||
sub read_file {
|
||||
my $self = shift;
|
||||
my $length = shift;
|
||||
$length = $MaxPost if $length > $MaxPost;
|
||||
local $/ = \$length;
|
||||
my $buf .= <STDIN>;
|
||||
$self->log(4, "Received " . length($buf) . " bytes (max is $MaxPost)");
|
||||
return $buf;
|
||||
}
|
||||
|
||||
sub read_text {
|
||||
my $self = shift;
|
||||
my $buf;
|
||||
while (1) {
|
||||
my $line = <STDIN>;
|
||||
if (length($line) == 0) {
|
||||
sleep(1); # wait for input
|
||||
next;
|
||||
}
|
||||
last if $line =~ /^.\r?\n/m;
|
||||
$buf .= $line;
|
||||
if (length($buf) > $MaxPost) {
|
||||
$buf = substr($buf, 0, $MaxPost);
|
||||
last;
|
||||
}
|
||||
}
|
||||
$self->log(4, "Received " . length($buf) . " bytes (max is $MaxPost)");
|
||||
utf8::decode($buf);
|
||||
$self->log(4, "Received " . length($buf) . " characters");
|
||||
return $buf;
|
||||
}
|
||||
|
||||
sub allow_deny_hook {
|
||||
my $self = shift;
|
||||
my $client = shift;
|
||||
|
||||
# clear cookie, read config file
|
||||
$q = undef;
|
||||
Init();
|
||||
|
||||
# don't do surge protection if we're testing
|
||||
return 1 unless $SurgeProtection;
|
||||
|
||||
# get the client IP number
|
||||
my $peeraddr = $self->{server}->{'peeraddr'};
|
||||
|
||||
# implement standard surge protection using Oddmuse tools but without using
|
||||
# ReportError and all that
|
||||
$self->log(4, "Adding visitor $peeraddr");
|
||||
ReadRecentVisitors();
|
||||
AddRecentVisitor($peeraddr);
|
||||
if (RequestLockDir('visitors')) { # not fatal
|
||||
WriteRecentVisitors();
|
||||
ReleaseLockDir('visitors');
|
||||
my @entries = @{$RecentVisitors{$peeraddr}};
|
||||
my $ts = $entries[$SurgeProtectionViews];
|
||||
if (($Now - $ts) < $SurgeProtectionTime) {
|
||||
$self->log(2, "Too many requests by $peeraddr");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub process_request {
|
||||
my $self = shift;
|
||||
|
||||
# refresh list of pages
|
||||
if (IsFile($IndexFile) and ReadIndex()) {
|
||||
# we're good
|
||||
} else {
|
||||
RefreshIndex();
|
||||
}
|
||||
|
||||
eval {
|
||||
local $SIG{'ALRM'} = sub {
|
||||
$self->log(1, "Timeout!");
|
||||
die "Timed Out!\n";
|
||||
};
|
||||
alarm(10); # timeout
|
||||
my $selector = <STDIN>; # no loop
|
||||
$selector = UrlDecode($selector); # assuming URL-encoded UTF-8
|
||||
$selector =~ s/\s+$//g; # no trailing whitespace
|
||||
|
||||
if (not $selector or $selector eq "/") {
|
||||
$self->serve_main_menu();
|
||||
} elsif ($selector eq "do/more") {
|
||||
$self->serve_phlog_archive();
|
||||
} elsif ($selector eq "do/index") {
|
||||
$self->serve_index();
|
||||
} elsif (substr($selector, 0, 9) eq "do/match\t") {
|
||||
$self->serve_match(substr($selector, 9));
|
||||
} elsif (substr($selector, 0, 10) eq "do/search\t") {
|
||||
$self->serve_search(substr($selector, 10));
|
||||
} elsif ($selector eq "do/tags") {
|
||||
$self->serve_tags();
|
||||
} elsif ($selector eq "do/rc") {
|
||||
$self->serve_rc(0);
|
||||
} elsif ($selector eq "do/rss") {
|
||||
$self->serve_rss(0);
|
||||
} elsif ($selector eq "do/rc/showedits") {
|
||||
$self->serve_rc(1);
|
||||
} elsif ($selector eq "do/new") {
|
||||
my $data = $self->read_text();
|
||||
$self->write_text_page(undef, $data);
|
||||
} elsif ($selector =~ m!^([^/]*)/(\d+)/menu$!) {
|
||||
$self->serve_page_menu($1, $2);
|
||||
} elsif ($selector =~ m!^map/(.*)!) {
|
||||
$self->serve_map($1);
|
||||
} elsif (substr($selector, -5) eq '/menu') {
|
||||
$self->serve_page_menu(substr($selector, 0, -5));
|
||||
} elsif ($selector =~ m!^([^/]*)/tag$!) {
|
||||
$self->serve_tag($1);
|
||||
} elsif ($selector =~ m!^([^/]*)(?:/(\d+))?/html!) {
|
||||
$self->serve_page_html($1, $2);
|
||||
} elsif ($selector =~ m!^([^/]*)/history$!) {
|
||||
$self->serve_page_history($1);
|
||||
} elsif ($selector =~ m!^([^/]*)/write/text$!) {
|
||||
my $data = $self->read_text();
|
||||
$self->write_text_page($1, $data);
|
||||
} elsif ($selector =~ m!^([^/]*)/append/text$!) {
|
||||
my $data = $self->read_text();
|
||||
$self->append_text_page($1, $data);
|
||||
} elsif ($selector =~ m!^([^/]*)(?:/([a-z]+/[-a-z]+))?/write/file(?:\t(\d+))?$!) {
|
||||
my $data = $self->read_file($3);
|
||||
$self->write_file_page($1, $data, $2);
|
||||
} elsif ($selector =~ m!^([^/]*)(?:/(\d+))?(?:/text)?$!) {
|
||||
$self->serve_page($1, $2);
|
||||
} elsif ($selector =~ m!^URL:(.*)!i) {
|
||||
$self->serve_redirect(UrlDecode($1));
|
||||
} elsif ($selector =~ m!^pics/(.*)!i) {
|
||||
$self->serve_image(UrlDecode($1));
|
||||
} else {
|
||||
$self->serve_error($selector, ValidId($selector)||'Cause unknown');
|
||||
}
|
||||
|
||||
$self->log(4, "Done");
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ while ($arg =~ /^-l/) {
|
||||
die $help unless -f $file;
|
||||
my %backup = %Translate;
|
||||
header_info_extract($file); # keep the header information of the translation files
|
||||
do $file or die "Cannot do $file";
|
||||
do "./$file" or die "Cannot do $file";
|
||||
foreach my $key (keys %Translate) {
|
||||
$backup{$key} = $Translate{$key};
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ EOT
|
||||
|
||||
my $min = version->parse(shift || "2.3.0");
|
||||
|
||||
my @tags = grep { /\d+\.\d+\.\d+/ and version->parse($_) >= $min }
|
||||
my @tags = grep { /(\d+\.\d+\.\d+)/ and version->parse($1) >= $min }
|
||||
split(/\n/, qx{git tag --list});
|
||||
|
||||
unless (@tags) {
|
||||
|
||||
3
t/atom.t
@@ -26,9 +26,6 @@ add_module('atom.pl');
|
||||
|
||||
start_server();
|
||||
|
||||
# Give the child time to start
|
||||
sleep 1;
|
||||
|
||||
# Check whether the child is up and running
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my $response = $ua->get("$ScriptName?action=version");
|
||||
|
||||
33
t/ban-mixed-scripts.t
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 4;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
add_module('ban-mixed-scripts.pl');
|
||||
|
||||
# ordinary page editing still works
|
||||
test_page(update_page('Test', 'This is a test'),
|
||||
'This is a test');
|
||||
test_page(update_page('Test', '🙇🏽 本当にごめんね – I am really sorry.'),
|
||||
'I am really sorry');
|
||||
|
||||
# mixed scripts are not ok
|
||||
test_page(update_page('Test', "It's diffіcult to find knowledgeable people on this topic, but youu sound like you know wgat you're taⅼkіng аboսt!"),
|
||||
'I am really sorry');
|
||||
|
||||
# error message is shown
|
||||
test_page($redirect, "Mixed scripts");
|
||||
24
t/ban.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006, 2007, 2010 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 23;
|
||||
use Test::More tests => 27;
|
||||
|
||||
$localhost = 'confusibombus';
|
||||
$ENV{'REMOTE_ADDR'} = $localhost;
|
||||
@@ -96,3 +96,23 @@ test_page(get_page("action=rollback to=$ts id=CriminalPage username=Alex"),
|
||||
test_page(get_page("action=rollback to=$ts id=CriminalPage pwd=foo"),
|
||||
'Rolling back changes',
|
||||
'CriminalPage</a> rolled back');
|
||||
|
||||
# make sure it also doesn't work in the homepage field for comments
|
||||
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
|
||||
|
||||
# mafia is still banned
|
||||
test_page(get_page(join(' ', 'title=Comments_on_CriminalPage',
|
||||
'aftertext=Innocent',
|
||||
'username=Alex',
|
||||
'homepage=http://mafia.example.com')),
|
||||
'Reason: crime');
|
||||
|
||||
# but it still works!
|
||||
test_page(get_page(join(' ', 'title=Comments_on_CriminalPage',
|
||||
'aftertext=Innocent',
|
||||
'username=Alex',
|
||||
'homepage=http://police.example.com')),
|
||||
'Status: 302');
|
||||
test_page(get_page('Comments_on_CriminalPage'),
|
||||
'Innocent',
|
||||
'http://police.example.com');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2017 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2017-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 13;
|
||||
use Test::More tests => 15;
|
||||
|
||||
add_module('banned-regexps.pl');
|
||||
|
||||
@@ -63,3 +63,13 @@ test_page(update_page('Test2', 'Voldemort', 'one banned word'),
|
||||
|
||||
# Make sure the underscores don't show up in the page link
|
||||
test_page(get_page('action=admin'), 'Local Banned Regexps');
|
||||
|
||||
# Make sure it doesn't break BannedContent!
|
||||
|
||||
# mafia is banned
|
||||
update_page('BannedContent', 'mafia', 'one banned word', 0, 1);
|
||||
test_page(update_page('CriminalPage', 'This is about http://mafia.example.com'),
|
||||
'This page does not exist');
|
||||
|
||||
# error message is shown
|
||||
test_page($redirect, 'Edit Denied');
|
||||
|
||||
40
t/checkbox.t
Normal file
@@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 5;
|
||||
|
||||
add_module('checkbox.pl');
|
||||
|
||||
my $text = q{
|
||||
There's some stuff I want to work on:
|
||||
|
||||
[[ :something to do]]
|
||||
[[x:something done]]
|
||||
[[save:update the list]]
|
||||
|
||||
Let's do this!
|
||||
};
|
||||
|
||||
$page = update_page('TODO', $text, 'saving it');
|
||||
|
||||
xpath_test(
|
||||
$page,
|
||||
'//p[text()="There\'s some stuff I want to work on:"]',
|
||||
'//form[@class="checkboxes"]/p/label/input[@type="checkbox"][@name="something_to_do"]/following-sibling::text()[string()="something to do"]',
|
||||
'//form[@class="checkboxes"]/p/label/input[@type="checkbox"][@name="something_done"][@checked="checked"]/following-sibling::text()[string()="something done"]',
|
||||
'//form[@class="checkboxes"]/p/input[@type="submit"][@name="update the list"]',
|
||||
'//p[text()="Let\'s do this!"]',);
|
||||
18
t/diff.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006-2013 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006-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
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 25;
|
||||
use Test::More tests => 30;
|
||||
use utf8;
|
||||
|
||||
# encoding stuff
|
||||
@@ -46,14 +46,15 @@ test_page(get_page('action=browse diff=1 id=code'),
|
||||
update_page('david', 'this is the first revision', 'first revision');
|
||||
update_page('david', 'this is the second revision', 'second revision');
|
||||
update_page('david', 'this is the third revision', 'third revision');
|
||||
update_page('david', 'this is the fourth revision', 'fourth revision');
|
||||
update_page('david', 'this is the fourth revision', ' ');
|
||||
update_page('david', 'this is the fifth revision', '');
|
||||
# first make sure the history page shows the appropriate labels and
|
||||
# summaries
|
||||
test_page(get_page('action=history id=david'),
|
||||
'Revision 1', 'first revision',
|
||||
'Revision 2', 'second revision',
|
||||
'Revision 3', 'third revision',
|
||||
'Revision 4', 'fourth revision');
|
||||
'Revision 4');
|
||||
# using diffrevision=1 will make sure that the third revision is not shown
|
||||
xpath_test(get_page('action=browse diff=1 id=david revision=2 diffrevision=1'),
|
||||
'//p[@class="summary"][text()="Summary: second revision"]',
|
||||
@@ -66,3 +67,12 @@ xpath_test(get_page('action=browse diff=1 id=david revision=2 diffrevision=1 cac
|
||||
'//div[@class="old"]/p/strong[text()="first"]',
|
||||
'//div[@class="new"]/p/strong[text()="second"]',
|
||||
'//div[@class="content browse"]/p[text()="this is the second revision"]');
|
||||
# check summaries
|
||||
$page = get_page('action=browse diff=1 id=david revision=5 diffrevision=1 cache=0');
|
||||
xpath_test($page,
|
||||
'//div[@class="summary"]/ul/li[text()="second revision"]',
|
||||
'//div[@class="summary"]/ul/li[text()="third revision"]',
|
||||
'//div[@class="summary"]/ul[count(li)=2]',
|
||||
'//div[@class="old"]/p/strong[text()="first"]',
|
||||
'//div[@class="new"]/p/strong[text()="fifth"]',
|
||||
'//div[@class="content browse"]/p[text()="this is the fifth revision"]');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2011 Alex Schroeder <alex@gnu.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
|
||||
@@ -23,18 +23,18 @@ test_page(update_page('2011-07-06', 'Hallo'),
|
||||
'Comments_on_2011-07-06');
|
||||
xpath_test(update_page('Hi', '<journal>'),
|
||||
'//h1/a[text()="2011-07-06"]',
|
||||
'//div[@class="journal"]/div[@class="page"]/p[@class="comment"]/a[text()="Comments on this page"]');
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[text()="Comments on this page"]');
|
||||
|
||||
add_module('dynamic-comments.pl');
|
||||
|
||||
xpath_test(get_page('Hi'),
|
||||
'//div[@class="journal"]/div[@class="page"]/p[@class="comment"]/a[@href="http://localhost/wiki.pl/Comments_on_2011-07-06"][text()="Add Comment"]');
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[@href="http://localhost/wiki.pl/Comments_on_2011-07-06"][text()="Add Comment"]');
|
||||
|
||||
test_page(update_page('Comments_on_2011-07-06', 'Yo'),
|
||||
'Yo');
|
||||
|
||||
xpath_test(get_page('Hi'),
|
||||
'//div[@class="journal"]/div[@class="page"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"][text()="Comments on 2011-07-06"]');
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"][text()="Comments on 2011-07-06"]');
|
||||
|
||||
# encoding basics
|
||||
$page = update_page('2011-07-06_(…)_Dü', 'Hallo Dü');
|
||||
@@ -45,5 +45,5 @@ update_page('Comments_on_2011-07-06_(…)_Dü', 'Yo');
|
||||
|
||||
xpath_test(update_page('Hi', '<journal>'),
|
||||
'//h1/a[text()="2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal"]/div[@class="page"]/p[@class="comment"]/a[text()="Comments on 2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal"]/div[@class="page"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"]');
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[text()="Comments on 2011-07-06 (…) Dü"]',
|
||||
'//div[@class="journal h-feed"]/div[@class="page h-entry"]/p[@class="comment"]/a[@href="javascript:togglecomments(\'id0\')"]');
|
||||
|
||||
315
t/gopher-server.t
Normal file
@@ -0,0 +1,315 @@
|
||||
# Copyright (C) 2017–2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
use strict;
|
||||
use 5.10.0;
|
||||
use Test::More;
|
||||
use IO::Socket::IP;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
require './t/test.pl';
|
||||
|
||||
add_module('tags.pl');
|
||||
|
||||
# enable uploads
|
||||
our($ConfigFile);
|
||||
AppendStringToFile($ConfigFile, "\$UploadAllowed = 1;\n");
|
||||
|
||||
my $port = random_port();
|
||||
my $pid = fork();
|
||||
|
||||
END {
|
||||
# kill server
|
||||
if ($pid) {
|
||||
kill 'KILL', $pid or warn "Could not kill server $pid";
|
||||
}
|
||||
}
|
||||
|
||||
our ($DataDir);
|
||||
if (!defined $pid) {
|
||||
die "Cannot fork: $!";
|
||||
} elsif ($pid == 0) {
|
||||
use Config;
|
||||
my $secure_perl_path = $Config{perlpath};
|
||||
exec($secure_perl_path,
|
||||
"stuff/gopher-server.pl",
|
||||
"--port=$port",
|
||||
"--log_level=0", # set to 4 for verbose logging
|
||||
"--wiki=./wiki.pl",
|
||||
"--wiki_dir=$DataDir",
|
||||
"--wiki_pages=Alex",
|
||||
"--wiki_pages=Berta",
|
||||
"--wiki_pages=Chris")
|
||||
or die "Cannot exec: $!";
|
||||
}
|
||||
|
||||
update_page('Alex', "My best friend is [[Berta]].\n\nTags: [[tag:Friends]]\n");
|
||||
update_page('Berta', "This is me.\n\nTags: [[tag:Friends]]\n");
|
||||
update_page('Chris', "I'm Chris.\n\nTags: [[tag:Friends]]\n");
|
||||
update_page('Friends', "Some friends.\n");
|
||||
update_page('2017-12-25', 'It was a Monday.\n\nTags: [[tag:Day]]');
|
||||
update_page('2017-12-26', 'It was a Tuesday.\n\nTags: [[tag:Day]]');
|
||||
update_page('2017-12-27', 'It was a Wednesday.\n\nTags: [[tag:Day]]');
|
||||
update_page('Friends', "News about friends.\n", 'rewrite', 1); # minor change
|
||||
update_page('Friends', "News about friends:\n\n<journal search tag:friends>\n",
|
||||
'add journal tag', 1); # minor change
|
||||
|
||||
# file created using convert NULL: test.png && base64 test.png
|
||||
update_page('Picture',
|
||||
"#FILE image/png\niVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bv"
|
||||
. "kkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg==");
|
||||
|
||||
sub query_gopher {
|
||||
my $query = shift;
|
||||
my $text = shift;
|
||||
|
||||
# create client
|
||||
my $socket = IO::Socket::IP->new(
|
||||
PeerHost => "localhost",
|
||||
PeerPort => $port,
|
||||
Type => SOCK_STREAM, )
|
||||
or die "Cannot construct client socket: $@";
|
||||
|
||||
$socket->print("$query\r\n");
|
||||
$socket->print($text);
|
||||
|
||||
undef $/; # slurp
|
||||
return <$socket>;
|
||||
}
|
||||
|
||||
# main menu
|
||||
my $page = query_gopher("");
|
||||
for my $item(qw(Alex Berta Chris 2017-12-25 2017-12-26 2017-12-27)) {
|
||||
like($page, qr/^1$item\t$item\/menu\t/m, "main menu contains $item");
|
||||
}
|
||||
|
||||
# page menu
|
||||
$page = query_gopher("Alex/menu");
|
||||
like($page, qr/^0Alex\tAlex\t/m,
|
||||
"Alex menu links to plain text");
|
||||
like($page, qr/^hAlex\tAlex\/html\t/m,
|
||||
"Alex menu links to HTML");
|
||||
like($page, qr/^1Page History\tAlex\/history\t/m,
|
||||
"Alex menu links to page history");
|
||||
like($page, qr/^1Berta\tBerta\/menu\t/m,
|
||||
"Alex menu links to Berta menu");
|
||||
like($page, qr/^1Friends\tFriends\/tag\t/m,
|
||||
"Alex menu links to Friends tag");
|
||||
|
||||
# plain text
|
||||
$page = query_gopher("Alex");
|
||||
like($page, qr/^My best friend is \[\[Berta\]\]/, "Alex plain text");
|
||||
|
||||
# HTML
|
||||
$page = query_gopher("Alex/html");
|
||||
like($page, qr/<p>My best friend is <a.*?>Berta<\/a>/, "Alex HTML");
|
||||
|
||||
# tags
|
||||
$page = query_gopher("Friends/tag");
|
||||
like($page, qr/iThis page is about the tag Friends/, "tag menu intro");
|
||||
for my $item(qw(Friends Alex Berta Chris)) {
|
||||
like($page, qr/^1$item\t$item\/menu\t/m, "tag menu contains $item");
|
||||
}
|
||||
|
||||
# tags
|
||||
$page = query_gopher("Day/tag");
|
||||
like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
|
||||
"tag menu sorted newest first");
|
||||
|
||||
# match
|
||||
$page = query_gopher("do/match\t2017");
|
||||
for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
|
||||
like($page, qr/^1$item\t$item\/menu\t/m, "match menu contains $item");
|
||||
}
|
||||
like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
|
||||
"match menu sorted newest first");
|
||||
|
||||
# search
|
||||
$page = query_gopher("do/search\ttag:day");
|
||||
for my $item(qw(2017-12-25 2017-12-26 2017-12-27)) {
|
||||
like($page, qr/^1$item\t$item\/menu\t/m, "serch menu contains $item");
|
||||
}
|
||||
like($page, qr/2017-12-27.*2017-12-26.*2017-12-25/s,
|
||||
"search menu sorted newest first");
|
||||
|
||||
# rc
|
||||
$page = query_gopher("do/rc");
|
||||
my $re = join(".*", "Picture", "2017-12-27", "2017-12-26", "2017-12-25",
|
||||
"Friends", "Chris", "Berta", "Alex");
|
||||
like($page, qr/$re/s, "rc in the right order");
|
||||
|
||||
$page = query_gopher("do/rc/showedits");
|
||||
|
||||
$re = join(".*", "Friends", "2017-12-27", "2017-12-26", "2017-12-25");
|
||||
like($page, qr/$re/s, "rc in the right order");
|
||||
|
||||
# history
|
||||
$page = query_gopher("Friends/history");
|
||||
like($page, qr/^1Friends \(1\)\tFriends\/1\/menu\t/m,
|
||||
"Friends (1)");
|
||||
like($page, qr/^1Friends \(2\)\tFriends\/2\/menu\t/m,
|
||||
"Friends (2)");
|
||||
like($page, qr/^1Friends \(current\)\tFriends\/menu\t/m,
|
||||
"Friends (current)");
|
||||
like($page, qr/Friends\/menu.*Friends\/2\/menu.*Friends\/1\/menu/s,
|
||||
"history in the right order");
|
||||
|
||||
# revision menu
|
||||
$page = query_gopher("Friends/1/menu");
|
||||
like($page, qr/^0Friends\tFriends\/1\t/m,
|
||||
"Friends/1 menu links to plain text");
|
||||
like($page, qr/^hFriends\tFriends\/1\/html\t/m,
|
||||
"Friends/1 menu links to HTML");
|
||||
unlike($page, qr/Search result for tag/,
|
||||
"Friends/1 has no journal and thus no tag search");
|
||||
|
||||
# revision plain text
|
||||
$page = query_gopher("Friends/1");
|
||||
like($page, qr/^Some friends/m, "Friends/1 plain text");
|
||||
|
||||
# revision html
|
||||
$page = query_gopher("Friends/1/html");
|
||||
like($page, qr/<p>Some friends/m, "Friends/1 html");
|
||||
|
||||
# upload text
|
||||
my $haiku = <<EOT;
|
||||
Quiet disk ratling
|
||||
Keyboard clicking, then it stops.
|
||||
Rain falls and I think
|
||||
.
|
||||
EOT
|
||||
|
||||
$page = query_gopher("Haiku/write/text", "$haiku");
|
||||
like($page, qr/^iPage was saved./m, "Write Haiku");
|
||||
like($page, qr/^1Haiku\tHaiku\/menu/m, "Link back to Haiku");
|
||||
|
||||
my $haiku_re = quotemeta(substr($haiku, 0, -2)); # strip period and \n
|
||||
$page = query_gopher("Haiku");
|
||||
like($page, qr/^$haiku_re/, "Haiku saved");
|
||||
|
||||
$haiku = <<"EOT";
|
||||
```
|
||||
username: Alex
|
||||
minor: 1
|
||||
summary: typos
|
||||
```
|
||||
Quiet disk rattling
|
||||
Keyboard clicking, then it stops.
|
||||
Rain falls and I think.
|
||||
.
|
||||
EOT
|
||||
|
||||
$page = query_gopher("Haiku/write/text", "$haiku");
|
||||
like($page, qr/^iPage was saved./m, "Write haiku");
|
||||
|
||||
$haiku_re = quotemeta(<<"EOT");
|
||||
Quiet disk rattling
|
||||
Keyboard clicking, then it stops.
|
||||
Rain falls and I think.
|
||||
EOT
|
||||
|
||||
$page = query_gopher("Haiku");
|
||||
like($page, qr/^$haiku_re/, "Haiku updated");
|
||||
|
||||
$page = query_gopher("Haiku/history");
|
||||
like($page, qr/^1Haiku \(current\)\tHaiku\/menu\t/m, "Haiku (current)");
|
||||
like($page, qr/^i\d\d:\d\d UTC by Alex: typos \(minor\)/m,
|
||||
"Metadata recorded");
|
||||
like($page, qr/^1Haiku \(1\)\tHaiku\/1\/menu\t/m, "Haiku (1)");
|
||||
|
||||
# new page
|
||||
$page = query_gopher("do/new", <<"EOT");
|
||||
```
|
||||
username: Alex
|
||||
summary: copy
|
||||
title: Haiku_Copy
|
||||
```
|
||||
Quiet disk rattling
|
||||
Keyboard clicking, then it stops.
|
||||
Rain falls and I think.
|
||||
.
|
||||
EOT
|
||||
like($page, qr/^iPage was saved./m, "Write copy of haiku");
|
||||
$page = query_gopher("Haiku_Copy");
|
||||
like($page, qr/^$haiku_re/, "New copy of haiku created");
|
||||
|
||||
# append
|
||||
$page = query_gopher("Haiku_Copy/append/text", "This is a comment by me!\n.\n");
|
||||
like($page, qr/^iPage was saved./m, "Append to copy of haiku");
|
||||
$page = query_gopher("Haiku_Copy");
|
||||
like($page, qr/^$haiku_re/, "Copy of haiku still there");
|
||||
like($page, qr/\n\n----\n\nThis is a comment by me!\n\n-- Anonymous/,
|
||||
"Comment is also there");
|
||||
|
||||
# Image download
|
||||
my $image = query_gopher("Picture");
|
||||
like($image, qr/\211PNG\r\n/, "Image download");
|
||||
|
||||
# Image upload
|
||||
$page = query_gopher("PictureCopy/write/file\t" . length($image), "$image");
|
||||
like($page, qr/Files of type application\/octet-stream are not allowed/m,
|
||||
"MIME type check");
|
||||
|
||||
$page = query_gopher("PictureCopy/image/png/write/file\t" . length($image), "$image");
|
||||
like($page, qr/^iPage was saved./m, "Image upload");
|
||||
unlike($page, qr/^3Page was not saved/, "Messages are correct");
|
||||
|
||||
my $copy = query_gopher("PictureCopy");
|
||||
like($copy, qr/\211PNG\r\n/, "Image copy download");
|
||||
|
||||
is($copy, $image, "Image and copy are identical");
|
||||
|
||||
# image:link
|
||||
$page = query_gopher("Test/write/text", "[[image:Picture]]\n.\n");
|
||||
like($page, qr/^iPage was saved./m, "Saved test page containing image link");
|
||||
$page = query_gopher("Test/menu");
|
||||
like($page, qr/^1Picture\tPicture\/menu/m, "Link to image page looks good");
|
||||
$page = query_gopher("Picture/menu");
|
||||
like($page, qr/^IPicture\tPicture/, "Link to image file looks good");
|
||||
|
||||
# Test upload of large page (but note $MaxPost: 1024 * 210 > (10 * 8 + 1) * 2600)
|
||||
my $garbage = (("0123456789" x 8) . "\n") x 2600 . "Last Line\n";
|
||||
$page = query_gopher("Large/write/text", "$garbage.\n");
|
||||
like($page, qr/^iPage was saved./m, "Write page with "
|
||||
. length($garbage) . " bytes");
|
||||
$page = query_gopher("Large");
|
||||
like(substr($page, -20), qr/Last Line/, "All of large page was saved");
|
||||
|
||||
# Test of Umlauts in the selector
|
||||
test_page(update_page('Zürich♥', '[[Üetliberg♥]]'), 'Zürich♥', 'Üetliberg♥');
|
||||
$page = query_gopher("Z%c3%bcrich%e2%99%a5");
|
||||
utf8::decode($page);
|
||||
like($page, qr/Üetliberg♥/, "UTF-8 encoded page names");
|
||||
|
||||
$page = query_gopher("Z%c3%bcrich%e2%99%a5/menu");
|
||||
utf8::decode($page);
|
||||
like($page, qr/^0Zürich♥\tZ%c3%bcrich%e2%99%a5\t/m, "UTF-8 encoded text link");
|
||||
like($page, qr/^1Üetliberg♥\t%c3%9cetliberg%e2%99%a5\/menu\t/m,
|
||||
"UTF-8 encoded links");
|
||||
|
||||
# gopher links
|
||||
update_page('Gopher', '[http://gopher.floodgap.com/gopher/gw?a=gopher%3A%2F%2Fsdf.org%3A70%2F0%2Fusers%2Fsolderpunk%2Fphlog%2Fintroducing-vf1.txt VF-1], [gopher://sdf.org:70/1/phlogs/ Phlogs]');
|
||||
$page = query_gopher("Gopher/menu");
|
||||
like($page, qr/^1Phlogs\t\/phlogs\/\tsdf\.org\t70/m, "Direct Gopher link");
|
||||
like($page, qr/^0VF-1\t\/users\/solderpunk\/phlog\/introducing-vf1.txt\tsdf\.org\t70/m, "Floodgap proxy link");
|
||||
|
||||
# gopher tags
|
||||
update_page('Gopher', 'Tags: [[tag:Gopher]] [[tag:Perl 6]]');
|
||||
$page = query_gopher("Gopher");
|
||||
like($page, qr/#Gopher/m, "Gopher tag");
|
||||
like($page, qr/#Perl_6/m, "Gopher multi-word tag");
|
||||
|
||||
done_testing();
|
||||
25
t/gotobar.t
@@ -15,19 +15,38 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 7;
|
||||
use Test::More tests => 17;
|
||||
use utf8;
|
||||
|
||||
add_module('gotobar.pl');
|
||||
|
||||
test_page(update_page('InterMap', q{
|
||||
Edit http://emacswiki.org/wiki?action=edit;id=%s
|
||||
Browse http://emacswiki.org/wiki/%s
|
||||
}, 0, 0, 1), 'Edit', 'Browse');
|
||||
|
||||
test_page(update_page('GotoBar', q{
|
||||
[[Hauptseite]]
|
||||
[[Letzte Änderungen]]
|
||||
[Browse:SiteMap Emacs Wiki]
|
||||
[http://example.org/ Example]
|
||||
}), 'Hauptseite', 'Letzte Änderungen', 'Example');
|
||||
}), 'Hauptseite', 'Letzte Änderungen', 'Emacs Wiki', 'Example');
|
||||
|
||||
test_page(get_page('Hauptseite'),
|
||||
'Hauptseite', 'Letzte Änderungen', 'Example');
|
||||
'Hauptseite', 'Letzte Änderungen', 'Emacs Wiki', 'Example');
|
||||
|
||||
test_page(get_page('Letzte_%C3%84nderungen'),
|
||||
'GotoBar');
|
||||
|
||||
|
||||
update_page('GotoBar', q{
|
||||
[[Comments on $id]]
|
||||
[[Comments on $id|Comments]]
|
||||
[[Edit:$id Edit $id]]
|
||||
[http://example.org/$$id Example]
|
||||
});
|
||||
|
||||
test_page(get_page('Tëst'),
|
||||
'Comments on Tëst', 'Comments_on_T%c3%abst',
|
||||
'http://emacswiki.org/wiki\?action=edit;id=Tëst', 'Edit Tëst',
|
||||
'http://example.org/T%c3%abst', 'Example');
|
||||
|
||||
84
t/index.t
Normal file
@@ -0,0 +1,84 @@
|
||||
# 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/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 23;
|
||||
|
||||
update_page('Test', 'Mu');
|
||||
test_page(get_page('action=index'),
|
||||
'Include normal pages', 'Test');
|
||||
|
||||
add_module('permanent-anchors.pl');
|
||||
|
||||
# create a page with a permanent anchor
|
||||
update_page('Fix', '[::Moo]');
|
||||
|
||||
# the default is to include permanent anchors
|
||||
$page = get_page('action=index');
|
||||
test_page($page,
|
||||
'Include normal pages',
|
||||
'Include permanent anchors');
|
||||
xpath_test($page, "//a[text()='Test']",
|
||||
"//a[text()='Fix']",
|
||||
"//a[text()='Moo']");
|
||||
|
||||
# we can exclude permanent anchors
|
||||
$page = get_page('action=index permanentanchors=0');
|
||||
xpath_test($page, "//a[text()='Test']",
|
||||
"//a[text()='Fix']");
|
||||
negative_xpath_test($page, "//a[text()='Moo']");
|
||||
|
||||
# or include them specifically
|
||||
$page = get_page('action=index permanentanchors=1');
|
||||
xpath_test($page, "//a[text()='Moo']");
|
||||
|
||||
# and exclude normal pages
|
||||
$page = get_page('action=index pages=0 permanentanchors=1');
|
||||
xpath_test($page, "//a[text()='Moo']");
|
||||
negative_xpath_test($page,
|
||||
"//a[text()='Test']",
|
||||
"//a[text()='Fix']");
|
||||
|
||||
add_module('near-links.pl');
|
||||
|
||||
CreateDir($NearDir);
|
||||
WriteStringToFile("$NearDir/EmacsWiki",
|
||||
"Alex\n");
|
||||
|
||||
update_page('InterMap', " EmacsWiki http://www.emacswiki.org/wiki/%s\n",
|
||||
'required', 0, 1);
|
||||
update_page('NearMap', " EmacsWiki"
|
||||
. " http://www.emacswiki.org/wiki?action=index;raw=1"
|
||||
. " http://www.emacswiki.org/wiki?search=%s;raw=1;near=0\n",
|
||||
'required', 0, 1);
|
||||
|
||||
# the default is to not include near links
|
||||
$page = get_page('action=index');
|
||||
test_page($page,
|
||||
'Include normal pages',
|
||||
'Include permanent anchors',
|
||||
'Include near pages');
|
||||
xpath_test($page, "//a[text()='Test']",
|
||||
"//a[text()='Fix']",
|
||||
"//a[text()='Moo']");
|
||||
negative_xpath_test($page, "//a[text()='Alex']");
|
||||
|
||||
# we need to specifically include near links
|
||||
$page = get_page('action=index near=1');
|
||||
xpath_test($page, "//a[text()='Alex']");
|
||||
|
||||
# or we can specifically exclude near links
|
||||
$page = get_page('action=index near=0');
|
||||
negative_xpath_test($page, "//a[text()='Alex']");
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2008, 2009 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2008-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -15,33 +15,43 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 48;
|
||||
use Test::More tests => 43;
|
||||
|
||||
add_module('journal-rss.pl');
|
||||
|
||||
# summaries eq page content since no summaries are provided
|
||||
update_page('2008-09-21', 'first page');
|
||||
update_page('2008-09-21', 'first page', '', 1); # minor
|
||||
update_page('2008-09-22', 'second page'); # major
|
||||
|
||||
OpenPage('2008-09-22');
|
||||
my $ts1 = $Page{ts};
|
||||
|
||||
sleep(1);
|
||||
update_page('2008-09-22', 'third edit', 'third edit', 1); # minor
|
||||
|
||||
update_page('2008-09-22', 'third edit content', 'third edit summary', 1); # minor
|
||||
|
||||
$OpenPageName = ''; # force OpenPage to reopen the page
|
||||
OpenPage('2008-09-22');
|
||||
my $ts2 = $Page{ts};
|
||||
|
||||
isnt($ts1, $ts2, "timestamps are different");
|
||||
|
||||
update_page('unrelated', 'wrong page');
|
||||
|
||||
my $page = get_page('action=journal');
|
||||
test_page($page,
|
||||
# make sure pages with only minor edits get shown as well
|
||||
'2008-09-21', 'first page',
|
||||
# ignore minor edits are ignored: show last major edit
|
||||
# instead
|
||||
'2008-09-22', 'second page',
|
||||
# make sure we're showing full page content, not summaries
|
||||
'2008-09-22', 'third edit content',
|
||||
# reverse sort is the default
|
||||
'2008-09-22(.*\n)+.*2008-09-21');
|
||||
|
||||
# make sure unrelated pages and minor edits don't show up
|
||||
test_page_negative($page, 'unrelated', 'wrong page',
|
||||
'third edit');
|
||||
# make sure unrelated pages don't show up
|
||||
test_page_negative($page, 'unrelated', 'wrong page');
|
||||
|
||||
# verify the order of pages
|
||||
test_page(get_page('action=journal'),
|
||||
'2008-09-22(.*\n)+.*2008-09-21');
|
||||
# make sure the minor change doesn't affect the timestamp
|
||||
my $date = quotemeta("<pubDate>" . TimeToRFC822($ts1) . "</pubDate>");
|
||||
like($page, qr/$date/, "minor don't change the timestamp");
|
||||
|
||||
# reverse the order
|
||||
test_page(get_page('action=journal reverse=1'),
|
||||
@@ -52,21 +62,6 @@ $page = get_page('action=journal match=21');
|
||||
test_page($page, '2008-09-21', 'first page');
|
||||
test_page_negative($page, '2008-09-22', 'second page');
|
||||
|
||||
# search parameter
|
||||
$page = get_page('action=journal search=second');
|
||||
|
||||
# no pages found, since this is for an old revision!
|
||||
test_page_negative($page,
|
||||
'2008-09-21', 'first page',
|
||||
'2008-09-22', 'second page',
|
||||
'third edit');
|
||||
|
||||
# strange but true: search returns a page based on the minor edit but
|
||||
# shows the latest major revision that doesn't actually match.
|
||||
$page = get_page('action=journal search=third');
|
||||
test_page($page, '2008-09-22', 'second page');
|
||||
test_page_negative($page, '2008-09-21', 'first page', 'third edit');
|
||||
|
||||
# testing the limit default
|
||||
update_page('2008-09-05', 'page');
|
||||
update_page('2008-09-06', 'page');
|
||||
@@ -98,17 +93,23 @@ $page = get_page('action=journal rsslimit=1');
|
||||
test_page($page, '2008-09-22');
|
||||
test_page_negative($page, '2008-09-21');
|
||||
|
||||
$page = get_page('action=journal rsslimit=all');
|
||||
test_page($page, '2008-09-22', '2008-09-05');
|
||||
# make sure we start from a well-known point in time
|
||||
AppendStringToFile($ConfigFile, "push(\@MyInitVariables, sub { \$Now = '$Now' });\n");
|
||||
|
||||
# Now let's show that we're using the timestamp of the last major
|
||||
# change if possible.
|
||||
# check default RSS
|
||||
xpath_test(get_page('action=journal'),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=10"]');
|
||||
|
||||
my @dates = get_page('action=rss showedit=1 all=1 match=2008-09-22')
|
||||
=~ m!<pubDate>(.*?)</pubDate>!g;
|
||||
# $dates[0] is the channel pubDate
|
||||
my $date2 = $dates[1]; # revision 2 comes first
|
||||
my $date1 = $dates[2]; # revision 1 comes second
|
||||
my ($item) = $page =~ m!(<item>\n<title>2008-09-22</title>\n(.*\n)+?</item>\n)!;
|
||||
test_page($item, "<pubDate>$date1</pubDate>");
|
||||
test_page_negative($item, "<pubDate>$date2</pubDate>");
|
||||
# check next page
|
||||
xpath_test(get_page('action=journal offset=10'),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal;offset=10"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=20"]');
|
||||
|
||||
# check next page but with a tag search
|
||||
xpath_test(get_page('action=journal search=tag:oddmuse'),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=journal;search=tag:oddmuse"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=journal;search=tag:oddmuse"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=journal;offset=10;search=tag:oddmuse"]');
|
||||
|
||||
73
t/maintain.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2009–2015 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2009–2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -15,18 +15,66 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 10;
|
||||
use Test::More tests => 20;
|
||||
|
||||
my $hundred_days_ago = $Now - 100 * 24 * 60 * 60;
|
||||
my $ten_days_ago = $Now - 10 * 24 * 60 * 60;
|
||||
my $two_days_ago = $Now - 2 * 24 * 60 * 60;
|
||||
my $ip = '127.0.0.1';
|
||||
# $ts, $id, $minor, $summary, $host, @rest
|
||||
|
||||
# First, make sure that moving all the entries from rc.log leaves no newline in
|
||||
# the file.
|
||||
|
||||
my $log = join("\n",
|
||||
join($FS, $hundred_days_ago, 'Two_Hundred_Days_Ago', '',
|
||||
'Boring', $ip,
|
||||
'Alex', '1', '', ''),
|
||||
'');
|
||||
WriteStringToFile($RcFile, $log);
|
||||
|
||||
# old log entry to be moved
|
||||
my $log = join($FS, '1235079422', 'Ganz_und_Gar', '',
|
||||
'Ladenbeschreibung und Preisliste', '62.12.165.34',
|
||||
'Alex', '1', '', '');
|
||||
WriteStringToFile($RcFile, $log . "\n");
|
||||
test_page(get_page('action=maintain pwd=foo'),
|
||||
'Moving 1 log entries');
|
||||
test_page(ReadFileOrDie($RcOldFile),
|
||||
"^1235079422$FS");
|
||||
is(ReadFileOrDie($RcFile), '', 'empty rc.log');
|
||||
'Moving 1 log entries',
|
||||
'Removing IP numbers from 0 log entries');
|
||||
$log = ReadFileOrDie($RcOldFile);
|
||||
test_page($log, "Hundred_Days_Ago.*Anonymous");
|
||||
test_page_negative($log, $ip);
|
||||
$log = ReadFileOrDie($RcFile);
|
||||
is($log, '', 'rc.log is empty');
|
||||
|
||||
# Now let's make sure that an old entry get anonymized and moved (like the
|
||||
# previous test), and that those that are not moved care anonymized if they are
|
||||
# old enough.
|
||||
|
||||
$log = join("\n",
|
||||
join($FS, $hundred_days_ago, 'One_Hundred_Days_Ago', '',
|
||||
'Boring', $ip,
|
||||
'Alex', '1', '', ''),
|
||||
join($FS, $ten_days_ago, 'Ten_Days_Ago', '',
|
||||
'Boring', $ip,
|
||||
'Alex', '1', '', ''),
|
||||
join($FS, $two_days_ago, 'Two_Days_Ago', '',
|
||||
'Boring', $ip,
|
||||
'Alex', '1', '', ''),
|
||||
'');
|
||||
WriteStringToFile($RcFile, $log);
|
||||
|
||||
test_page(get_page('action=maintain pwd=foo'),
|
||||
'Moving 1 log entries',
|
||||
'Removing IP numbers from 1 log entries');
|
||||
$log = ReadFileOrDie($RcOldFile);
|
||||
test_page($log,
|
||||
"Two_Hundred_Days_Ago.*Anonymous",
|
||||
"One_Hundred_Days_Ago.*Anonymous");
|
||||
test_page_negative($log, $ip);
|
||||
$log = ReadFileOrDie($RcFile);
|
||||
test_page($log,
|
||||
"Ten_Days_Ago.*Anonymous",
|
||||
"Two_Days_Ago.*$ip");
|
||||
test_page_negative($log, "Hundred_Days_Ago");
|
||||
|
||||
# Let's make sure that updating pages write the right rc lines.
|
||||
|
||||
update_page('test', 'this is a test');
|
||||
my $log = ReadFileOrDie($RcFile);
|
||||
test_page($log,
|
||||
@@ -34,7 +82,8 @@ test_page($log,
|
||||
"${FS}this is a test${FS}");
|
||||
test_page_negative($log, "^\n");
|
||||
|
||||
# old page to be deleted
|
||||
# Make sure that pages to be deleted are in fact deleted.
|
||||
|
||||
OpenPage('test');
|
||||
$Page{ts} = 1;
|
||||
$Page{revision} = 1;
|
||||
|
||||
101
t/markdown-converter.t
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env 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/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use utf8;
|
||||
use Test::More tests => 24;
|
||||
|
||||
add_module('markdown-converter.pl');
|
||||
|
||||
my $input = qq{
|
||||
# mu
|
||||
1 * 2 * 3
|
||||
*foo*
|
||||
**bar**
|
||||
*foo bar*
|
||||
/baz/
|
||||
//quux//
|
||||
##oort##
|
||||
[http://example.com/ example]
|
||||
{{{
|
||||
code
|
||||
}}}
|
||||
{{{also code}}}
|
||||
};
|
||||
|
||||
update_page('test', $input);
|
||||
|
||||
my $output = get_page('action=convert id=test');
|
||||
unlike $output, qr'<p>#MARKDOWN</p>', 'No Markdown marker in the HTML';
|
||||
like $output, qr'#MARKDOWN\n', 'Markdown marker';
|
||||
like $output, qr'1\. mu', 'list item';
|
||||
like $output, qr'1 \* 2 \* 3', 'lone asterisk';
|
||||
like $output, qr'\*\*foo\*\*', 'short strong emphasis';
|
||||
like $output, qr'\*\*bar\*\*', 'long strong emphasis';
|
||||
like $output, qr'\*\*foo bar\*\*', 'spaces ok';
|
||||
like $output, qr'\*baz\*', 'short emphasis';
|
||||
like $output, qr'\*quux\*', 'long emphasis';
|
||||
like $output, qr'`oort`', 'code';
|
||||
like $output, qr'\[example\]\(http://example.com/\)', 'link';
|
||||
like $output, qr'```\ncode\n```', 'fenced code';
|
||||
like $output, qr'`also code`', 'inline code';
|
||||
|
||||
# Errors found and fixed at a later date
|
||||
$input = qq{
|
||||
/Toe’s Reach/
|
||||
|
||||
{{{
|
||||
one
|
||||
}}}
|
||||
|
||||
== heading
|
||||
|
||||
{{{
|
||||
two
|
||||
}}}
|
||||
|
||||
nothing but {{{#REDIRECT [[Rhysalis Eina]]}}}
|
||||
|
||||
##eins## und ##zwei##
|
||||
|
||||
};
|
||||
|
||||
update_page('test', $input);
|
||||
|
||||
my $output = get_page('action=convert id=test');
|
||||
|
||||
like $output, qr'\*Toe’s Reach\*', 'Toe’s Reach';
|
||||
like $output, qr'^```\none\n```$'m, 'code block one';
|
||||
like $output, qr'^```\none\n```$'m, 'code block two';
|
||||
like $output, qr'^## heading$'m, 'heading';
|
||||
like $output, qr'`#REDIRECT \[\[Rhysalis Eina\]\]`', 'inline code again';
|
||||
like $output, qr'`eins`', 'eins';
|
||||
like $output, qr'`zwei`', 'zwei';
|
||||
|
||||
# check whether the candidates are listed correctly
|
||||
|
||||
test_page(get_page('action=conversion-candidates'), 'test');
|
||||
|
||||
# convert the file so it isn't listed anymore
|
||||
update_page('test', "#MARKDOWN\nhello\n");
|
||||
|
||||
# add an image which cannot be converted
|
||||
AppendStringToFile($ConfigFile, "\$UploadAllowed = 1;\n");
|
||||
$page = update_page('pic', "#FILE image/png\niVBORw0KGgoAAAA");
|
||||
test_page($page, 'This page contains an uploaded file:');
|
||||
|
||||
test_page_negative(get_page('action=conversion-candidates'), 'test', 'pic');
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 52;
|
||||
use Test::More tests => 53;
|
||||
|
||||
add_module('markdown-rule.pl');
|
||||
add_module('bbcode.pl');
|
||||
@@ -38,7 +38,7 @@ run_tests(split(/\n/,<<'EOT'));
|
||||
- one
|
||||
<ul><li>one</li></ul>
|
||||
- one\n-- Alex
|
||||
<ul><li>one</li><li>- Alex</li></ul>
|
||||
<ul><li>one -- Alex</li></ul>
|
||||
- one\n\n- Alex
|
||||
<ul><li>one</li><li>Alex</li></ul>
|
||||
* one\n * two
|
||||
@@ -121,6 +121,8 @@ bar <h2>foo</h2><p>bar</p>
|
||||
<table><tr><th><em style="font-style: normal; text-decoration: underline">foo</em></th></tr></table>
|
||||
foo ~~bar~~
|
||||
foo <del>bar</del>
|
||||
pay 1.-/month
|
||||
pay 1.-/month
|
||||
EOT
|
||||
|
||||
xpath_run_tests(split('\n',<<'EOT'));
|
||||
|
||||
1
t/meta.t
@@ -147,6 +147,7 @@ for my $re (sort keys %changes) {
|
||||
$text =~s/Tss?\([^\)]+//g; # getting rid of "rename" in strings
|
||||
$text =~s/\{\w+\}//g; # getting rid of "rename" in $Action{rename}
|
||||
$text =~s/'\w+'//g; # getting rid of "rename" in 'rename'
|
||||
$text =~s/rename-//g; # rename-page is OK
|
||||
not ($_ eq 'modules/pygmentize.pl' and $re eq '-f'
|
||||
or $_ eq 'modules/static-copy.pl' and $re eq 'chmod'
|
||||
or $_ eq 'modules/static-hybrid.pl' and $re eq 'chmod')
|
||||
|
||||
@@ -67,7 +67,6 @@ add_module('namespaces.pl');
|
||||
# EOF
|
||||
|
||||
start_mojolicious_server();
|
||||
sleep(1);
|
||||
|
||||
my $t = Test::Mojo->new;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ use Test::Mojo;
|
||||
require './t/test.pl';
|
||||
|
||||
start_mojolicious_server();
|
||||
sleep(1);
|
||||
|
||||
my $t = Test::Mojo->new;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006, 2007 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006-2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 27;
|
||||
use Test::More tests => 30;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
add_module('permanent-anchors.pl');
|
||||
@@ -90,3 +90,10 @@ run_tests(split('\n',<<'EOT'));
|
||||
**[::bold]** and **bold**
|
||||
<strong><span class="permanentanchor">bold</span></strong> and <strong>bold</strong>
|
||||
EOT
|
||||
|
||||
# delete a page
|
||||
update_page('TheTest', $DeletedPage);
|
||||
OpenPage('TheTest');
|
||||
is(-f GetPageFile($OpenPageName), 1, 'Page file exists');
|
||||
is(DeletePage($OpenPageName), '', 'No error deleting a page');
|
||||
is(-e GetPageFile($OpenPageName), undef, 'Page file no longer exists');
|
||||
|
||||
82
t/pingback-server.t
Normal file
@@ -0,0 +1,82 @@
|
||||
# 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/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 13;
|
||||
use LWP::UserAgent;
|
||||
use RPC::XML::Client;
|
||||
use RPC::XML;
|
||||
|
||||
add_module('pingback-server.pl');
|
||||
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments_on_';\n");
|
||||
|
||||
# This test is is going to use two servers in addition to this script, but in
|
||||
# actual fact we are all going to share the data directory.
|
||||
|
||||
test_page(update_page('Target', 'This is the test page.'), 'This is the test page');
|
||||
|
||||
# Server 1 is going to be the pingback server.
|
||||
|
||||
start_server();
|
||||
|
||||
# Check whether the child is up and running
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my $response = $ua->get("$ScriptName?action=version");
|
||||
ok($response->is_success, "There is a wiki running at $ScriptName");
|
||||
like($response->decoded_content, qr/\bpingback-server\.pl/, "The server has the pingback-server extension installed");
|
||||
|
||||
# Now that we have the pingback server running, we need to get the URL of the
|
||||
# test page, including its port.
|
||||
|
||||
my $target_url = ScriptUrl('Target');
|
||||
my $pingback_url = ScriptUrl('pingback/Target');
|
||||
|
||||
# Verify that the target exists via external request
|
||||
$response = $ua->get($target_url);
|
||||
ok($response->is_success, "Target URL response");
|
||||
like($response->decoded_content, qr/This is the test page/, "Target URL decoded");
|
||||
|
||||
# Create the Source page before starting the next server (so that it knows about
|
||||
# the new page)
|
||||
test_page(update_page('Source', "Link to $target_url"), 'Link to');
|
||||
|
||||
# Server 2 is going to be the source server.
|
||||
start_server(2);
|
||||
|
||||
# Check whether the child is up and running (with a new $ScriptName!)
|
||||
$response = $ua->get("$ScriptName?action=version");
|
||||
ok($response->is_success, "There is a wiki running at $ScriptName");
|
||||
|
||||
# New script name means we can now get the source_url.
|
||||
my $source_url = ScriptUrl('Source');
|
||||
|
||||
# Verify that the source exists via external request
|
||||
$response = $ua->get($source_url);
|
||||
ok($response->is_success, "Source URL response");
|
||||
like($response->decoded_content, qr/Link to/, "Source URL decoded");
|
||||
like($response->decoded_content, qr/$target_url/, "Source page links to Target page");
|
||||
|
||||
my $client = RPC::XML::Client->new($pingback_url);
|
||||
my $source = RPC::XML::string->new($source_url);
|
||||
my $target = RPC::XML::string->new($target_url);
|
||||
my $request = RPC::XML::request->new('pingback.ping', $source, $target);
|
||||
my $response = $client->send_request($request);
|
||||
|
||||
ok(ref($response), 'Got a response reference');
|
||||
|
||||
test_page(get_page('Comments_on_Target'),
|
||||
'Pingback:',
|
||||
$source_url);
|
||||
4
t/rc.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006–20015 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006–2015 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
|
||||
@@ -51,7 +51,7 @@ test_page(get_page('action=rc raw=1'), 'title: Wiki');
|
||||
WriteStringToFile($RcFile, "1${FS}test${FS}${FS}test${FS}127.0.0.1${FS}${FS}1${FS}${FS}\n");
|
||||
test_page_negative(get_page('action=rc raw=1'), 'title: test');
|
||||
test_page(get_page('action=rc raw=1 from=1'), 'title: Wiki', 'title: test',
|
||||
'description: test', 'generator: 127.0.0.1',
|
||||
'description: test', 'generator: Anonymous',
|
||||
'link: http://localhost/wiki.pl/test',
|
||||
'last-modified: 1970-01-01T00:00Z', 'revision: 1');
|
||||
|
||||
|
||||
31
t/rename-pages.t
Normal file
@@ -0,0 +1,31 @@
|
||||
# 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/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 9;
|
||||
|
||||
add_module('rename-pages.pl');
|
||||
|
||||
test_page(update_page('A', 'Alpha'), 'Alpha');
|
||||
test_page(get_page('action=rename-page'), 'Page name is missing');
|
||||
test_page(get_page('action=rename-page id=X to=Y'), 'Source page does not exist');
|
||||
test_page(get_page('action=rename-page id=A to=A'), 'Target page already exists');
|
||||
test_page(get_page('action=rename-page id=A to=B'),
|
||||
'Status: 302',
|
||||
'Location: .*wiki.pl/B');
|
||||
test_page(get_page('A'),
|
||||
'Status: 302',
|
||||
'Location: .*wiki.pl\?action=browse;oldid=A;id=B');
|
||||
test_page(get_page('B'), 'Alpha');
|
||||
34
t/rss.t
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2006–2015 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006–2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 126;
|
||||
use Test::More tests => 135;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
|
||||
@@ -107,6 +107,36 @@ update_page('big', "mee too\n" x 2 . 'x' x 55000);
|
||||
test_page(get_page('action=rss full=1'), 'too big to send over RSS');
|
||||
test_page(get_page('action=rss full=1 diff=1'), 'mee too', 'too big to send over RSS');
|
||||
|
||||
# pagination
|
||||
my $interval = $RcDefault * 24 * 60 * 60;
|
||||
my $t1 = $Now - $interval;
|
||||
my $t2 = $Now - 2 * $interval;
|
||||
my $t3 = $Now - 3 * $interval;
|
||||
my $action1 = " from=$t2 upto=$t1";
|
||||
my $window1 = ";from=$t2;upto=$t1";
|
||||
my $window2 = ";from=$t3;upto=$t2";
|
||||
|
||||
# make sure we start from a well-known point in time
|
||||
AppendStringToFile($ConfigFile, "push(\@MyInitVariables, sub { \$Now = '$Now' });\n");
|
||||
|
||||
# check default RSS
|
||||
xpath_test(get_page('action=rss'),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=rss"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=rss"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=rss' . $window1 . '"]');
|
||||
|
||||
# check next page
|
||||
xpath_test(get_page('action=rss' . $action1),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=rss' . $window1 . '"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=rss"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=rss' . $window2 . '"]');
|
||||
|
||||
# check next page but with full pages
|
||||
xpath_test(get_page('action=rss full=1' . $action1),
|
||||
'//atom:link[@rel="self"][@href="http://localhost/wiki.pl?action=rss' . $window1 . ';full=1"]',
|
||||
'//atom:link[@rel="last"][@href="http://localhost/wiki.pl?action=rss;full=1"]',
|
||||
'//atom:link[@rel="previous"][@href="http://localhost/wiki.pl?action=rss' . $window2 . ';full=1"]');
|
||||
|
||||
SKIP: {
|
||||
|
||||
eval {
|
||||
|
||||
23
t/search-inclusion.t
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2018 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require './t/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 3;
|
||||
|
||||
test_page(update_page("Omega", "last page"), "last page");
|
||||
test_page(update_page("Alpha", "first page\n<search page>"),
|
||||
"Omega");
|
||||
test_page(get_page('Omega'), 'last page');
|
||||