forked from github/kensanata.oddmuse
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eda34108e1 | ||
|
|
39a59e257f |
6
Makefile
6
Makefile
@@ -19,13 +19,9 @@ build:
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
prove t/setup.pl
|
||||
|
||||
release:
|
||||
perl stuff/release ~/oddmuse.org
|
||||
|
||||
build/wiki.pl: wiki.pl
|
||||
perl -lne "s/(\\\$$q->a\(\{-href=>'http:\/\/www.oddmuse.org\/'\}, 'Oddmuse'\))/\\\$$q->a({-href=>'http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . \$$1/; print" < $< > $@
|
||||
perl -lne "s/(\\\$$q->a\({-href=>'http:\/\/www.oddmuse.org\/'}, 'Oddmuse'\))/\\\$$q->a({-href=>'http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/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" < $< > $@
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
if test -z "$2" -o ! -z "$3"; then
|
||||
echo "Usage: delete.sh USERNAME WIKI"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
username=$1
|
||||
wiki=$2
|
||||
|
||||
for p in $(curl "https://campaignwiki.org/wiki/$wiki?action=index;raw=1"); do
|
||||
echo "Deleting: $p"
|
||||
curl -F frodo=1 -F "title=$p" -F text=DeletedPage -F summary=Deleted -F username="$username" "https://campaignwiki.org/wiki/$wiki"
|
||||
sleep 5
|
||||
done
|
||||
@@ -1,131 +0,0 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2005-2016 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use Modern::Perl;
|
||||
use LWP::UserAgent;
|
||||
use utf8;
|
||||
binmode(STDOUT, ":utf8");
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
|
||||
sub url_encode {
|
||||
my $str = shift;
|
||||
return '' unless $str;
|
||||
utf8::encode($str); # turn to byte string
|
||||
my @letters = split(//, $str);
|
||||
my %safe = map {$_ => 1} ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.', '!', '~', '*', "'", '(', ')', '#');
|
||||
foreach my $letter (@letters) {
|
||||
$letter = sprintf("%%%02x", ord($letter)) unless $safe{$letter};
|
||||
}
|
||||
return join('', @letters);
|
||||
}
|
||||
|
||||
sub get_raw {
|
||||
my $uri = shift;
|
||||
my $response = $ua->get($uri);
|
||||
return $response->content if $response->is_success;
|
||||
}
|
||||
|
||||
sub get_wiki_page {
|
||||
my ($wiki, $id, $password) = @_;
|
||||
my $parameters = [
|
||||
pwd => $password,
|
||||
action => 'browse',
|
||||
id => $id,
|
||||
raw => 1,
|
||||
];
|
||||
my $response = $ua->post($wiki, $parameters);
|
||||
return $response->decoded_content if $response->is_success;
|
||||
die "Getting $id returned " . $response->status_line;
|
||||
}
|
||||
|
||||
sub get_wiki_index {
|
||||
my $wiki = shift;
|
||||
my $parameters = [
|
||||
search => "flickr.com",
|
||||
context => 0,
|
||||
raw => 1,
|
||||
];
|
||||
my $response = $ua->post($wiki, $parameters);
|
||||
return $response->decoded_content if $response->is_success;
|
||||
die "Getting the index returned " . $response->status_line;
|
||||
}
|
||||
|
||||
sub post_wiki_page {
|
||||
my ($wiki, $id, $username, $password, $text) = @_;
|
||||
my $parameters = [
|
||||
username => $username,
|
||||
pwd => $password,
|
||||
recent_edit => 'on',
|
||||
text => $text,
|
||||
title => $id,
|
||||
];
|
||||
my $response = $ua->post($wiki, $parameters);
|
||||
die "Posting to $id returned " . $response->status_line unless $response->code == 302;
|
||||
}
|
||||
|
||||
my %seen = ();
|
||||
|
||||
sub write_flickr {
|
||||
my ($id, $flickr, $dir, $file) = @_;
|
||||
say "Found $flickr";
|
||||
warn "$file was seen before: " . $seen{$file} if $seen{$file};
|
||||
die "$file contains unknown characters" if $file =~ /[^a-z0-9_.]/;
|
||||
$seen{$file} = "$id used $flickr";
|
||||
my $bytes = get_raw($flickr) or die("No data for $id");
|
||||
open(my $fh, '>', "$dir/$file") or die "Cannot write $dir/$file";
|
||||
binmode($fh);
|
||||
print $fh $bytes;
|
||||
close($fh);
|
||||
}
|
||||
|
||||
sub convert_page {
|
||||
my ($wiki, $pics, $dir, $username, $password, $id) = @_;
|
||||
say $id;
|
||||
my $text = get_wiki_page($wiki, $id, $password);
|
||||
my $is_changed = 0;
|
||||
while ($text =~ m!(https://[a-z0-9.]+.flickr.com/(?:[a-z0-9.]+/)?([a-z0-9_]+\.(?:jpg|png)))!) {
|
||||
my $flickr = $1;
|
||||
my $file = $2;
|
||||
write_flickr($id, $flickr, $dir, $file);
|
||||
$is_changed = 1;
|
||||
my $re = quotemeta($flickr);
|
||||
$text =~ s!$flickr!$pics/$file!g;
|
||||
}
|
||||
if ($is_changed) {
|
||||
post_wiki_page($wiki, $id, $username, $password, $text);
|
||||
} else {
|
||||
# die "$id has no flickr matches?\n$text";
|
||||
}
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
sub convert_site {
|
||||
my ($wiki, $pics, $dir, $username, $password) = @_;
|
||||
my @ids = split(/\n/, get_wiki_index($wiki));
|
||||
for my $id (@ids) {
|
||||
convert_page($wiki, $pics, $dir, $username, $password, $id);
|
||||
}
|
||||
}
|
||||
|
||||
our $AdminPass;
|
||||
do "/home/alex/password.pl";
|
||||
convert_site('https://alexschroeder.ch/wiki',
|
||||
'https://alexschroeder.ch/pics',
|
||||
'/home/alex/alexschroeder.ch/pics',
|
||||
'Alex Schroeder',
|
||||
$AdminPass);
|
||||
@@ -38,10 +38,9 @@
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile
|
||||
'(progn
|
||||
(require 'cl)
|
||||
(require 'sgml-mode)
|
||||
(require 'skeleton)))
|
||||
(require 'cl)
|
||||
(require 'sgml-mode)
|
||||
(require 'skeleton))
|
||||
|
||||
(require 'goto-addr); URL regexp
|
||||
(require 'info); link face
|
||||
@@ -258,6 +257,24 @@ Example:
|
||||
(defvar oddmuse-revision nil
|
||||
"A variable to bind dynamically when calling `oddmuse-format-command'.")
|
||||
|
||||
(defun oddmuse-revision-put (wiki page rev)
|
||||
"Store REV for WIKI and PAGE in `oddmuse-revisions'."
|
||||
(let ((w (assoc wiki oddmuse-revisions)))
|
||||
(unless w
|
||||
(setq w (list wiki)
|
||||
oddmuse-revisions (cons w oddmuse-revisions)))
|
||||
(let ((p (assoc page w)))
|
||||
(unless p
|
||||
(setq p (list page))
|
||||
(setcdr w (cons p (cdr w))))
|
||||
(setcdr p rev))))
|
||||
|
||||
(defun oddmuse-revision-get (wiki page)
|
||||
"Get revision for WIKI and PAGE in `oddmuse-revisions'."
|
||||
(let ((w (assoc wiki oddmuse-revisions)))
|
||||
(when w
|
||||
(cdr (assoc page w)))))
|
||||
|
||||
;;; Helpers
|
||||
|
||||
(defsubst oddmuse-page-name (file)
|
||||
@@ -283,7 +300,7 @@ Example:
|
||||
(defun oddmuse-url (wiki pagename)
|
||||
"Get the URL of oddmuse wiki."
|
||||
(condition-case v
|
||||
(concat (or (cadr (assoc wiki oddmuse-wikis)) (error "Wiki not found in `oddmuse-wikis'")) "/"
|
||||
(concat (or (cadr (assoc wiki oddmuse-wikis)) (error)) "/"
|
||||
(url-hexify-string pagename))
|
||||
(error nil)))
|
||||
|
||||
@@ -517,7 +534,7 @@ as well."
|
||||
((string-match "<title>Error</title>" status)
|
||||
(if (string-match "<h1>\\(.*\\)</h1>" status)
|
||||
(error "Error %s: %s" mesg (match-string 1 status))
|
||||
(error "Error %s: Cause unknown" status)))
|
||||
(error "Error %s: Cause unknown")))
|
||||
(t
|
||||
(message "%s...done" mesg))))))
|
||||
|
||||
@@ -719,7 +736,7 @@ Font-locking is controlled by `oddmuse-markup-functions'.
|
||||
(set (make-local-variable 'sgml-tag-alist)
|
||||
`(("b") ("code") ("em") ("i") ("strong") ("nowiki")
|
||||
("pre" \n) ("tt") ("u")))
|
||||
(set (make-local-variable 'skeleton-transformation-function) 'identity)
|
||||
(set (make-local-variable 'skeleton-transformation) 'identity)
|
||||
|
||||
(make-local-variable 'oddmuse-wiki)
|
||||
(make-local-variable 'oddmuse-page-name)
|
||||
@@ -837,8 +854,11 @@ people have been editing the wiki in the mean time."
|
||||
(set-buffer (get-buffer-create name))
|
||||
(erase-buffer); in case of current-prefix-arg
|
||||
(oddmuse-run "Loading" oddmuse-get-command wiki pagename)
|
||||
(oddmuse-revision-put wiki pagename (oddmuse-get-latest-revision wiki pagename))
|
||||
;; fix mode-line for VC in the new buffer because this is not a vc-checkout
|
||||
(setq buffer-file-name (concat oddmuse-directory "/" wiki "/" pagename))
|
||||
(vc-working-revision buffer-file-name 'oddmuse)
|
||||
(vc-mode-line buffer-file-name 'oddmuse)
|
||||
(pop-to-buffer (current-buffer))
|
||||
;; check for a diff (this ends with display-buffer) and bury the
|
||||
;; buffer if there are no hunks
|
||||
(when (file-exists-p buffer-file-name)
|
||||
@@ -849,9 +869,7 @@ people have been editing the wiki in the mean time."
|
||||
;; this also changes the buffer name
|
||||
(basic-save-buffer)
|
||||
;; this makes sure that the buffer name is set correctly
|
||||
(oddmuse-mode)
|
||||
;; fix mode-line for VC in the new buffer because this is not a vc-checkout
|
||||
(vc-mode-line buffer-file-name 'oddmuse))))
|
||||
(oddmuse-mode))))
|
||||
|
||||
(defalias 'oddmuse-go 'oddmuse-edit)
|
||||
|
||||
@@ -891,11 +909,8 @@ Use a prefix argument to override this."
|
||||
(and buffer-file-name (basic-save-buffer))
|
||||
(oddmuse-run "Posting" oddmuse-post-command nil nil
|
||||
(get-buffer-create " *oddmuse-response*") t 302)
|
||||
;; force reload
|
||||
(vc-file-setprop buffer-file-name 'vc-working-revision
|
||||
(oddmuse-get-latest-revision oddmuse-wiki oddmuse-page-name))
|
||||
;; fix mode-line for VC in the new buffer because this is not a vc-checkout
|
||||
(vc-mode-line buffer-file-name 'oddmuse))
|
||||
(oddmuse-revision-put oddmuse-wiki oddmuse-page-name
|
||||
(oddmuse-get-latest-revision oddmuse-wiki oddmuse-page-name)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-preview (&optional arg)
|
||||
|
||||
@@ -47,8 +47,7 @@ For a list of possible values, see `vc-state'."
|
||||
|
||||
(defun vc-oddmuse-working-revision (file)
|
||||
"The current revision based on `oddmuse-revisions'."
|
||||
(with-oddmuse-file
|
||||
(oddmuse-get-latest-revision wiki pagename)))
|
||||
(oddmuse-revision-get oddmuse-wiki oddmuse-page-name))
|
||||
|
||||
(defun vc-oddmuse-checkout-model (files)
|
||||
"No locking."
|
||||
@@ -60,6 +59,10 @@ For a list of possible values, see `vc-state'."
|
||||
(defun vc-oddmuse-register (files &optional rev comment)
|
||||
"This always works.")
|
||||
|
||||
(defun vc-oddmuse-revert (file &optional contents-done)
|
||||
"No idea"
|
||||
nil)
|
||||
|
||||
(defvar vc-oddmuse-log-command
|
||||
(concat "curl --silent %w"
|
||||
" --form action=rc"
|
||||
@@ -146,7 +149,7 @@ a version backup."
|
||||
(with-oddmuse-file file
|
||||
(let ((command (oddmuse-format-command vc-oddmuse-get-revision-command)))
|
||||
(with-temp-buffer
|
||||
(oddmuse-run "Loading" command wiki pagename)
|
||||
(oddmuse-run "Loading" command)
|
||||
(write-file file))))))
|
||||
|
||||
(defun vc-oddmuse-checkin (files rev comment)
|
||||
|
||||
@@ -1,536 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@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.user {
|
||||
border: none;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
margin: 1em 2em;
|
||||
}
|
||||
table.user tr td, table.user 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; }
|
||||
|
||||
/* rss */
|
||||
|
||||
channel * { display: block; }
|
||||
|
||||
channel title {
|
||||
margin-top: 30pt;
|
||||
}
|
||||
copyright {
|
||||
font-size: 14pt;
|
||||
margin-top: 1em;
|
||||
}
|
||||
channel > link:before {
|
||||
font-size: 18pt;
|
||||
display: block;
|
||||
margin: 1em;
|
||||
padding: 0.5em;
|
||||
content: "This is an RSS feed, designed to be read in a feed reader.";
|
||||
color: red;
|
||||
border: 1px solid red;
|
||||
}
|
||||
link, license {
|
||||
font-size: 11pt;
|
||||
margin-bottom: 9pt;
|
||||
}
|
||||
username:before { content: "Last edited by "; }
|
||||
username:after { content: "."; }
|
||||
generator:before { content: "Feed generated by "; }
|
||||
generator:after { content: "."; }
|
||||
channel description {
|
||||
font-weight: bold;
|
||||
}
|
||||
item description {
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
docs, language,
|
||||
pubDate, lastBuildDate, ttl, guid, category, comments,
|
||||
docs, image title, image link,
|
||||
status, version, diff, history, importance {
|
||||
display: none;
|
||||
}
|
||||
@@ -321,6 +321,7 @@ div.sister {
|
||||
float:left;
|
||||
margin-right:1ex;
|
||||
padding-right:1ex;
|
||||
border-right:1px dashed;
|
||||
}
|
||||
div.sister p { padding:1ex; margin:0; }
|
||||
div.sister hr { display:none; }
|
||||
|
||||
@@ -47,8 +47,8 @@ textarea, pre, code, tt {
|
||||
.browse { min-height: 3em; }
|
||||
.header form, .header p { margin: 0; }
|
||||
/* hide the buttons but don't use display:none because of
|
||||
http://stackoverflow.com/questions/5665203/getting-iphone-go-button-to-submit-form
|
||||
.header input[type="submit"] { position: absolute; visibility: hidden; } */
|
||||
http://stackoverflow.com/questions/5665203/getting-iphone-go-button-to-submit-form */
|
||||
.header input[type="submit"] { position: absolute; visibility: hidden; }
|
||||
.header input { width: 5em; font-size: 80%; }
|
||||
.footer { clear:both; font-size: 90%; }
|
||||
.content input { font-size: 80%; line-height: 125%; }
|
||||
@@ -201,12 +201,10 @@ div.message {
|
||||
}
|
||||
table.history { border-style:none; }
|
||||
td.history { border-style:none; }
|
||||
div.history span.dash + strong { font-weight: normal; }
|
||||
span.result { font-size:larger; }
|
||||
span.info { font-size:smaller; font-style:italic; }
|
||||
div.rc hr { display: none; }
|
||||
div.rc li { padding-bottom: 0.5em; }
|
||||
div.rc li strong { font-weight: normal; }
|
||||
|
||||
/* Tables */
|
||||
table.user {
|
||||
|
||||
47
css/wiki.css
47
css/wiki.css
@@ -219,3 +219,50 @@ code {
|
||||
background: #eee;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold'), local('GentiumBasic-Bold'), url(/fonts/GenBasB.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic Italic'), local('GentiumBasic-Italic'), url(/fonts/GenBasI.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold Italic'), local('GentiumBasic-BoldItalic'), url(/fonts/GenBasBI.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic'), local('GentiumBasic'), url(/fonts/GenBasR.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Plus';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Plus'), local('GentiumPlus'), url(/fonts/GentiumPlus-R.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Plus';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Plus Italic'), local('GentiumPlus-Italic'), url(/fonts/GentiumPlus-I.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Symbola';
|
||||
src: local('Symbola'), url('/fonts/Symbola.woff') format('woff') url('/fonts/Symbola.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ sub AdminPowerDelete {
|
||||
GetCluster($Page{text}));
|
||||
}
|
||||
# Regenerate index on next request
|
||||
Unlink($IndexFile);
|
||||
unlink($IndexFile);
|
||||
ReleaseLock();
|
||||
print $q->p(T('Main lock released.'));
|
||||
PrintFooter();
|
||||
@@ -61,30 +61,30 @@ sub AdminPowerRename {
|
||||
print $q->p(T('Main lock obtained.'));
|
||||
# page file -- only check for existing or missing pages here
|
||||
my $fname = GetPageFile($id);
|
||||
ReportError(Ts('The page %s does not exist', $id), '400 BAD REQUEST') unless IsFile($fname);
|
||||
ReportError(Ts('The page %s does not exist', $id), '400 BAD REQUEST') unless -f $fname;
|
||||
my $newfname = GetPageFile($new);
|
||||
ReportError(Ts('The page %s already exists', $new), '400 BAD REQUEST') if IsFile($newfname);
|
||||
ReportError(Ts('The page %s already exists', $new), '400 BAD REQUEST') if -f $newfname;
|
||||
# Regenerate index on next request -- remove this before errors can occur!
|
||||
Unlink($IndexFile);
|
||||
unlink($IndexFile);
|
||||
# page file
|
||||
CreateDir($PageDir); # It might not exist yet
|
||||
Rename($fname, $newfname)
|
||||
rename($fname, $newfname)
|
||||
or ReportError(Tss('Cannot rename %1 to %2', $fname, $newfname) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
# keep directory
|
||||
my $kdir = GetKeepDir($id);
|
||||
my $newkdir = GetKeepDir($new);
|
||||
CreateDir($KeepDir); # It might not exist yet (only the parent directory!)
|
||||
Rename($kdir, $newkdir)
|
||||
rename($kdir, $newkdir)
|
||||
or ReportError(Tss('Cannot rename %1 to %2', $kdir, $newkdir) . ": $!", '500 INTERNAL SERVER ERROR')
|
||||
if IsDir($kdir);
|
||||
if -d $kdir;
|
||||
# refer file
|
||||
if (defined(&GetRefererFile)) {
|
||||
my $rdir = GetRefererFile($id);
|
||||
my $newrdir = GetRefererFile($new);
|
||||
CreateDir($RefererDir); # It might not exist yet
|
||||
Rename($rdir, $newrdir)
|
||||
rename($rdir, $newrdir)
|
||||
or ReportError(Tss('Cannot rename %1 to %2', $rdir, $newrdir) . ": $!", '500 INTERNAL SERVER ERROR')
|
||||
if IsDir($rdir);
|
||||
if -d $rdir;
|
||||
}
|
||||
# RecentChanges
|
||||
OpenPage($new);
|
||||
|
||||
@@ -29,7 +29,7 @@ $NewQuestion = 'Write your question here:';
|
||||
|
||||
sub IncrementInFile {
|
||||
my $filename = shift;
|
||||
sysopen my $fh, encode_utf8($filename), O_RDWR|O_CREAT or die "can't open $filename: $!";
|
||||
sysopen my $fh, $filename, O_RDWR|O_CREAT or die "can't open $filename: $!";
|
||||
flock $fh, LOCK_EX or die "can't flock $filename: $!";
|
||||
my $num = <$fh> || 1;
|
||||
seek $fh, 0, 0 or die "can't rewind $filename: $!";
|
||||
|
||||
@@ -171,9 +171,9 @@ sub UserCanEditAutoLockFix {
|
||||
return 0 if $id eq 'SampleUndefinedPage' or $id eq T('SampleUndefinedPage')
|
||||
or $id eq 'Sample_Undefined_Page' or $id eq T('Sample_Undefined_Page');
|
||||
return 1 if UserIsAdmin() || UserIsEditor();
|
||||
return 0 if $id ne '' and IsFile(GetLockedPageFile($id));
|
||||
return 0 if $LockOnCreation{$id} and not IsFile(GetPageFile($id)); # new page
|
||||
return 0 if !$EditAllowed or IsFile($NoEditFile);
|
||||
return 0 if $id ne '' and -f GetLockedPageFile($id);
|
||||
return 0 if $LockOnCreation{$id} and not -f GetPageFile($id); # new page
|
||||
return 0 if !$EditAllowed or -f $NoEditFile;
|
||||
return 0 if $editing and UserIsBanned(); # this call is more expensive
|
||||
return 0 if $EditAllowed >= 2 and (not $CommentsPrefix or $id !~ /^$CommentsPrefix/);
|
||||
return 1 if $EditAllowed >= 3 and ($comment or (GetParam('aftertext', '') and not GetParam('text', '')));
|
||||
|
||||
@@ -44,8 +44,8 @@ sub BacklinksMenu {
|
||||
$Action{buildback} = \&BuildBacklinkDatabase;
|
||||
sub BuildBacklinkDatabase {
|
||||
print GetHttpHeader('text/plain');
|
||||
Unlink($backfile); # Remove old database
|
||||
tie my %backhash, 'MLDBM', encode_utf8($backfile) or die "Cannot open file $backfile $!\n";
|
||||
unlink $backfile; # Remove old database
|
||||
tie my %backhash, 'MLDBM', $backfile or die "Cannot open file $backfile $!\n";
|
||||
log1("Starting Database Store Process ... please wait\n\n");
|
||||
|
||||
foreach my $name (AllPagesList()) {
|
||||
@@ -101,7 +101,7 @@ sub GetBackLink {
|
||||
|
||||
our ($BacklinkBanned);
|
||||
$BacklinkBanned = "HomePage|ScratchPad" if !$BacklinkBanned;
|
||||
tie my %backhash, 'MLDBM', encode_utf8($backfile), O_CREAT|O_RDWR, oct(644) or die "Cannot open file $backfile $!\n";
|
||||
tie my %backhash, 'MLDBM', $backfile, O_CREAT|O_RDWR, oct(644) or die "Cannot open file $backfile $!\n";
|
||||
|
||||
# Search database for matches
|
||||
while ( my ($source, $hashes) = each %backhash ) {
|
||||
|
||||
@@ -77,7 +77,8 @@ sub DoUnifiedDiff { # copied from DoDiff
|
||||
RequestLockDir('diff') or return '';
|
||||
WriteStringToFile($oldName, $_[0]);
|
||||
WriteStringToFile($newName, $_[1]);
|
||||
my $diff_out = decode_utf8(`diff -U 99999 -- \Q$oldName\E \Q$newName\E | tail -n +7`); # should be +4, but we always add extra line # TODO that workaround is ugly, fix it!
|
||||
my $diff_out = `diff -U 99999 -- \Q$oldName\E \Q$newName\E | tail -n +7`; # should be +4, but we always add extra line # TODO that workaround is ugly, fix it!
|
||||
utf8::decode($diff_out); # needs decoding
|
||||
$diff_out =~ s/\n\K\\ No newline.*\n//g; # Get rid of common complaint.
|
||||
ReleaseLockDir('diff');
|
||||
# No need to unlink temp files--next diff will just overwrite.
|
||||
|
||||
@@ -29,7 +29,7 @@ push(@MyInitVariables, \&DraftInit);
|
||||
sub DraftInit {
|
||||
if (GetParam('Draft', '')) {
|
||||
SetParam('action', 'draft') ; # Draft button used
|
||||
} elsif (IsFile("$DraftDir/" . GetParam('username', $q->remote_addr())) # draft exists
|
||||
} elsif (-f "$DraftDir/" . GetParam('username', $q->remote_addr()) # draft exists
|
||||
and $FooterNote !~ /action=draft/) { # take care of mod_perl persistence
|
||||
$FooterNote = $q->p(ScriptLink('action=draft', T('Recover Draft'))) . $FooterNote;
|
||||
}
|
||||
@@ -47,9 +47,9 @@ sub DoDraft {
|
||||
WriteStringToFile($draft, EncodePage(text=>$text, id=>$id));
|
||||
SetParam('msg', T('Draft saved')); # invalidate cache
|
||||
print GetHttpHeader('', T('Draft saved'), '204 NO CONTENT');
|
||||
} elsif (IsFile($draft)) {
|
||||
} elsif (-f $draft) {
|
||||
my $data = ParseData(ReadFileOrDie($draft));
|
||||
Unlink($draft);
|
||||
unlink ($draft);
|
||||
$Message .= $q->p(T('Draft recovered'));
|
||||
DoEdit($data->{id}, $data->{text}, 1);
|
||||
} else {
|
||||
@@ -76,19 +76,22 @@ push(@MyMaintenance, \&DraftCleanup);
|
||||
|
||||
sub DraftFiles {
|
||||
return map {
|
||||
substr($_, length($DraftDir) + 1);
|
||||
} Glob("$DraftDir/*"), Glob("$DraftDir/.*");
|
||||
my $x = $_;
|
||||
$x = substr($x, length($DraftDir) + 1);
|
||||
utf8::decode($x);
|
||||
$x;
|
||||
} bsd_glob("$DraftDir/*"), bsd_glob("$DraftDir/.*");
|
||||
}
|
||||
|
||||
sub DraftCleanup {
|
||||
print '<p>' . T('Draft Cleanup');
|
||||
foreach my $draft (DraftFiles()) {
|
||||
next if $draft eq '.' or $draft eq '..';
|
||||
my $ts = Modified("$DraftDir/$draft");
|
||||
my $ts = (stat("$DraftDir/$draft"))[9];
|
||||
if ($Now - $ts < 1209600) { # 14*24*60*60
|
||||
print $q->br(), Tss("%1 was last modified %2 and was kept",
|
||||
$draft, CalcTimeSince($Now - $ts));
|
||||
} elsif (Unlink("$DraftDir/$draft")) {
|
||||
} elsif (unlink("$DraftDir/$draft")) {
|
||||
print $q->br(), Tss("%1 was last modified %2 and was deleted",
|
||||
$draft, CalcTimeSince($Now - $ts));
|
||||
} else {
|
||||
|
||||
@@ -27,7 +27,8 @@ sub FixEncoding {
|
||||
ValidIdOrDie($id);
|
||||
RequestLockOrError();
|
||||
OpenPage($id);
|
||||
my $text = decode_utf8($Page{text});
|
||||
my $text = $Page{text};
|
||||
utf8::decode($text);
|
||||
Save($id, $text, T('Fix character encoding'), 1) if $text ne $Page{text};
|
||||
ReleaseLock();
|
||||
ReBrowsePage($id);
|
||||
|
||||
@@ -12,8 +12,8 @@ our ($q, $OpenPageName, @MyRules, $CrossbarPageName);
|
||||
push(@MyRules, \&FormsRule);
|
||||
|
||||
sub FormsRule {
|
||||
if (IsFile(GetLockedPageFile($OpenPageName)) or (InElement('div', '^class="crossbar"$') and
|
||||
IsFile(GetLockedPageFile($CrossbarPageName)))) {
|
||||
if (-f GetLockedPageFile($OpenPageName) or (InElement('div', '^class="crossbar"$') and
|
||||
-f GetLockedPageFile($CrossbarPageName))) {
|
||||
if (/\G(\<form.*?\<\/form\>)/cgs) {
|
||||
my $form = $1;
|
||||
my $oldpos = pos;
|
||||
|
||||
@@ -163,7 +163,8 @@ sub GdSecurityImageGenerate {
|
||||
my ($imgData) = $img->out(force => 'png');
|
||||
my $ticketId = Digest::MD5::md5_hex(rand());
|
||||
CreateDir($GdSecurityImageDir);
|
||||
open my $fh, ">:raw", encode_utf8(GdSecurityImageGetImageFile($ticketId))
|
||||
my $file = GdSecurityImageGetImageFile($ticketId);
|
||||
open my $fh, ">:raw", $file
|
||||
or ReportError(Ts('Image storing failed. (%s)', $!), '500 INTERNAL SERVER ERROR');
|
||||
print $fh $imgData;
|
||||
#print $fh $png; ### experimental ###
|
||||
@@ -186,7 +187,9 @@ sub GdSecurityImageIsValidId {
|
||||
}
|
||||
|
||||
sub GdSecurityImageReadImageFile {
|
||||
if (open(my $IN, '<:raw', encode_utf8(shift))) {
|
||||
my $file = shift;
|
||||
utf8::encode($file); # filenames are bytes!
|
||||
if (open(my $IN, '<:raw', $file)) {
|
||||
local $/ = undef; # Read complete files
|
||||
my $data=<$IN>;
|
||||
close $IN;
|
||||
@@ -208,7 +211,7 @@ sub GdSecurityImageDoImage {
|
||||
print $q->header(-type=>'image/png');
|
||||
print $data;
|
||||
|
||||
Unlink(GdSecurityImageGetImageFile($id));
|
||||
unlink(GdSecurityImageGetImageFile($id));
|
||||
}
|
||||
|
||||
sub GdSecurityImageCleanup {
|
||||
@@ -216,10 +219,10 @@ sub GdSecurityImageCleanup {
|
||||
if (!GdSecurityImageIsValidId($id)) {
|
||||
return;
|
||||
}
|
||||
my @files = (Glob("$GdSecurityImageDir/*.png"), Glob("$GdSecurityImageDir/*.ticket"));
|
||||
my @files = (bsd_glob("$GdSecurityImageDir/*.png"), bsd_glob("$GdSecurityImageDir/*.ticket"));
|
||||
foreach my $file (@files) {
|
||||
if ($Now - Modified($file) > $GdSecurityImageDuration) {
|
||||
Unlink($file);
|
||||
if ($Now - (stat $file)[9] > $GdSecurityImageDuration) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,7 +255,7 @@ sub GdSecurityImageCheck {
|
||||
}
|
||||
|
||||
if (GdSecurityImageIsValidId($id)) {
|
||||
Unlink(GdSecurityImageGetTicketFile($id));
|
||||
unlink(GdSecurityImageGetTicketFile($id));
|
||||
}
|
||||
|
||||
$GdSecurityImageId = GdSecurityImageGenerate();
|
||||
|
||||
@@ -30,18 +30,18 @@ $GitMail = 'unknown@oddmuse.org';
|
||||
sub GitCommit {
|
||||
my ($message, $author) = @_;
|
||||
my $oldDir = cwd;
|
||||
ChangeDir("$DataDir/page");
|
||||
chdir("$DataDir/page");
|
||||
capture {
|
||||
system($GitBinary, qw(add -A));
|
||||
system($GitBinary, qw(commit -q -m), $message, "--author=$author <$GitMail>");
|
||||
};
|
||||
ChangeDir($oldDir);
|
||||
chdir($oldDir);
|
||||
}
|
||||
|
||||
sub GitInitRepository {
|
||||
return if IsDir("$DataDir/page/.git");
|
||||
return if -d "$DataDir/page/.git";
|
||||
capture {
|
||||
system($GitBinary, qw(init -q --), encode_utf8("$DataDir/page"));
|
||||
system($GitBinary, qw(init -q --), "$DataDir/page");
|
||||
};
|
||||
GitCommit('Initial import', 'Oddmuse');
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ sub GitRun {
|
||||
my $exitStatus;
|
||||
# warn join(' ', $GitBinary, @_) . "\n";
|
||||
|
||||
ChangeDir($GitRepo);
|
||||
chdir($GitRepo);
|
||||
if ($GitDebug) {
|
||||
# TODO use ToString here
|
||||
# capture the output of the git comand in a temporary file
|
||||
@@ -99,7 +99,7 @@ sub GitRun {
|
||||
} else {
|
||||
$exitStatus = system($GitBinary, @_);
|
||||
}
|
||||
ChangeDir($oldDir);
|
||||
chdir($oldDir);
|
||||
return $exitStatus;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ sub GitInitVariables {
|
||||
}
|
||||
|
||||
sub GitInitRepository {
|
||||
return if IsDir("$GitRepo/.git");
|
||||
return if -d "$GitRepo/.git";
|
||||
my $exception = shift;
|
||||
CreateDir($GitRepo);
|
||||
GitRun(qw(init --quiet));
|
||||
@@ -187,16 +187,17 @@ sub DoGitCleanup {
|
||||
}
|
||||
|
||||
sub GitCleanup {
|
||||
if (IsDir($GitRepo)) {
|
||||
if (-d $GitRepo) {
|
||||
print $q->p('Git cleanup starting');
|
||||
AllPagesList();
|
||||
# delete all the files including all the files starting with a dot
|
||||
opendir(DIR, encode_utf8($GitRepo)) or ReportError("cannot open directory $GitRepo: $!");
|
||||
opendir(DIR, $GitRepo) or ReportError("cannot open directory $GitRepo: $!");
|
||||
foreach my $file (readdir(DIR)) {
|
||||
my $name = decode_utf8($file);
|
||||
my $name = $file;
|
||||
utf8::decode($name); # filenames are bytes
|
||||
next if $file eq '.git' or $file eq '.' or $file eq '..' or $IndexHash{$name};
|
||||
print $q->p("Deleting left over file $name");
|
||||
Unlink("$GitRepo/$file") or ReportError("cannot delete $GitRepo/$name: $!");
|
||||
unlink "$GitRepo/$file" or ReportError("cannot delete $GitRepo/$name: $!");
|
||||
}
|
||||
closedir DIR;
|
||||
# write all the files again, just to be sure
|
||||
|
||||
@@ -43,7 +43,7 @@ sub HtmlTemplate {
|
||||
my $type = shift;
|
||||
# return header.de.html, or header.html, or error.html, or report an error...
|
||||
foreach my $f ((map { "$type.$_" } HtmlTemplateLanguage()), $type, "error") {
|
||||
return "$HtmlTemplateDir/$f.html" if IsFile("$HtmlTemplateDir/$f.html");
|
||||
return "$HtmlTemplateDir/$f.html" if -r "$HtmlTemplateDir/$f.html";
|
||||
}
|
||||
ReportError(Tss('Could not find %1.html template in %2', $type, $HtmlTemplateDir),
|
||||
'500 INTERNAL SERVER ERROR');
|
||||
|
||||
@@ -87,7 +87,7 @@ sub GetActionHtmlTemplate {
|
||||
my $action = GetParam('action', 'browse');
|
||||
# return browse.de.html, or browse.html, or error.html, or report an error...
|
||||
foreach my $f ((map { "$action.$_" } HtmlTemplateLanguage()), $action, "error") {
|
||||
return "$HtmlTemplateDir/$f.html" if IsFile("$HtmlTemplateDir/$f.html");
|
||||
return "$HtmlTemplateDir/$f.html" if -r "$HtmlTemplateDir/$f.html";
|
||||
}
|
||||
ReportError(Tss('Could not find %1.html template in %2', $action, $HtmlTemplateDir),
|
||||
'500 INTERNAL SERVER ERROR');
|
||||
|
||||
@@ -31,7 +31,7 @@ push(@MyRules, \&HtmlLinksRule);
|
||||
$RuleOrder{\&HtmlLinksRule} = 105;
|
||||
|
||||
sub HtmlLinksRule {
|
||||
if (IsFile(GetLockedPageFile($OpenPageName))) {
|
||||
if (-f GetLockedPageFile($OpenPageName)) {
|
||||
$HtmlLinks = 1;
|
||||
} else {
|
||||
$HtmlLinks = 0;
|
||||
|
||||
@@ -32,7 +32,7 @@ sub DivFooRule {
|
||||
my $str = $1;
|
||||
CreateDir($ImagifyDir);
|
||||
my $fileName = sha256_hex($str) . '.' . $ImagifyFormat;
|
||||
system('convert', %ImagifyParams, "caption:$str", "$ImagifyDir/$fileName") unless IsFile("$ImagifyDir/$fileName");
|
||||
system('convert', %ImagifyParams, "caption:$str", "$ImagifyDir/$fileName") unless -e "$ImagifyDir/$fileName";
|
||||
my $src = $ScriptName . "/imagify/" . UrlEncode($fileName);
|
||||
return CloseHtmlEnvironments() . $q->img({-class => 'imagify', -src => $src, -alt => '(rendered text)'}) . AddHtmlEnvironment('p');
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ sub MakeLaTeX {
|
||||
|
||||
# Select which binary to use for conversion of dvi to images
|
||||
my $useConvert = 0;
|
||||
if (not IsFile($dvipngPath)) {
|
||||
if (not IsFile($convertPath)) {
|
||||
if (not -e $dvipngPath) {
|
||||
if (not -e $convertPath) {
|
||||
return "[Error: dvipng binary and convert binary not found at $dvipngPath or $convertPath ]";
|
||||
}
|
||||
else {
|
||||
@@ -155,13 +155,13 @@ sub MakeLaTeX {
|
||||
}
|
||||
|
||||
# check cache
|
||||
if (not IsFile("$LatexDir/$hash.png") or ZeroSize("$LatexDir/$hash.png")) {
|
||||
if (not -f "$LatexDir/$hash.png" or -z "$LatexDir/$hash.png") { #If file doesn't exist or is zero bytes
|
||||
# Then create the image
|
||||
|
||||
# read template and replace <math>
|
||||
CreateDir($LatexDir);
|
||||
if (not IsFile($LatexDefaultTemplateName)) {
|
||||
open (my $F, '>', encode_utf8($LatexDefaultTemplateName)) or return '[Unable to write template]';
|
||||
mkdir($LatexDir) unless -d $LatexDir;
|
||||
if (not -f $LatexDefaultTemplateName) {
|
||||
open (my $F, '>', $LatexDefaultTemplateName) or return '[Unable to write template]';
|
||||
print $F $LatexDefaultTemplate;
|
||||
close $F;
|
||||
}
|
||||
@@ -169,12 +169,12 @@ sub MakeLaTeX {
|
||||
$template =~ s/<math>/$latex/gi;
|
||||
#setup rendering directory
|
||||
my $dir = "$LatexDir/$hash";
|
||||
if (IsDir($dir)) {
|
||||
Unlink((Glob("$dir/*")));
|
||||
if (-d $dir) {
|
||||
unlink (bsd_glob('$dir/*'));
|
||||
} else {
|
||||
CreateDir($dir);
|
||||
mkdir($dir) or return "[Unable to create $dir]";
|
||||
}
|
||||
ChangeDir($dir) or return "[Unable to switch to $dir]";
|
||||
chdir ($dir) or return "[Unable to switch to $dir]";
|
||||
WriteStringToFile ("srender.tex", $template);
|
||||
my $errorText = qx(latex srender.tex);
|
||||
|
||||
@@ -197,16 +197,16 @@ sub MakeLaTeX {
|
||||
$error = "[dvipng error $? ($output)]" if $?;
|
||||
}
|
||||
|
||||
if (not $error and IsFile('srender1.png') and not ZeroSize('srender1.png')) {
|
||||
if (not $error and -f 'srender1.png' and not -z 'srender1.png') {
|
||||
my $png = ReadFileOrDie("srender1.png");
|
||||
WriteStringToFile ("$LatexDir/$hash.png", $png);
|
||||
} else {
|
||||
$error = "[Error retrieving image for $latex]";
|
||||
}
|
||||
}
|
||||
Unlink(glob('*'));
|
||||
ChangeDir($LatexDir);
|
||||
RemoveDir($dir);
|
||||
unlink (glob('*'));
|
||||
chdir ($LatexDir);
|
||||
rmdir ($dir);
|
||||
|
||||
return $error if $error;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ sub DoListLocked {
|
||||
print $q->start_div({-class=>'content list locked'}), $q->start_p();
|
||||
}
|
||||
foreach my $id (AllPagesList()) {
|
||||
PrintPage($id) if IsFile(GetLockedPageFile($id));
|
||||
PrintPage($id) if -f GetLockedPageFile($id);
|
||||
}
|
||||
if (not $raw) {
|
||||
print $q->end_p(), $q->end_div();
|
||||
|
||||
@@ -29,7 +29,6 @@ our %TranslationsLibrary = (
|
||||
'bg' => 'bulgarian-utf8.pl',
|
||||
'ca' => 'catalan-utf8.pl',
|
||||
'de' => 'german-utf8.pl',
|
||||
'et' => 'estonian-utf8.pl',
|
||||
'es' => 'spanish-utf8.pl',
|
||||
'fi' => 'finnish-utf8.pl',
|
||||
'fr' => 'french-utf8.pl',
|
||||
@@ -73,11 +72,10 @@ sub LoadLanguage {
|
||||
foreach (@prefs) {
|
||||
last if $Lang{$_} eq 'en'; # the default
|
||||
my $file = $TranslationsLibrary{$Lang{$_}};
|
||||
next unless $file; # file is not listed, eg. there is no file for "de-ch"
|
||||
$file = "$LoadLanguageDir/$file" if defined $LoadLanguageDir;
|
||||
if (IsFile($file)) {
|
||||
if (-r $file) {
|
||||
do $file;
|
||||
do "$ConfigFile-$Lang{$_}" if IsFile("$ConfigFile-$Lang{$_}");
|
||||
do "$ConfigFile-$Lang{$_}" if -r "$ConfigFile-$Lang{$_}";
|
||||
$CurrentLanguage = $Lang{$_};
|
||||
last;
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ You can change this expiry time by setting C<$LnCacheHours>.
|
||||
push (@MyMaintenance, \&LnMaintenance);
|
||||
|
||||
sub LnMaintenance {
|
||||
if (opendir(DIR, encode_utf8($RssDir))) { # cleanup if they should expire anyway
|
||||
foreach my $file (readdir(DIR)) {
|
||||
Unlink("$RssDir/$file") if $Now - Modified($file) > $LnCacheHours * 3600;
|
||||
if (opendir(DIR, $RssDir)) { # cleanup if they should expire anyway
|
||||
foreach (readdir(DIR)) {
|
||||
unlink "$RssDir/$_" if $Now - (stat($_))[9] > $LnCacheHours * 3600;
|
||||
}
|
||||
closedir DIR;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ sub LocalNamesInit {
|
||||
my %data = ();
|
||||
if (GetParam('cache', $UseCache) > 0) {
|
||||
foreach my $uri (keys %todo) { # read cached rss files if possible
|
||||
if ($Now - Modified($todo{$uri}) < $LnCacheHours * 3600) {
|
||||
if ($Now - (stat($todo{$uri}))[9] < $LnCacheHours * 3600) {
|
||||
$data{$uri} = ReadFile($todo{$uri});
|
||||
delete($todo{$uri}); # no need to fetch them below
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use v5.10;
|
||||
AddModuleDescription('login.pl', 'Login Module');
|
||||
|
||||
our ($q, %Action, $SiteName, @MyAdminCode, $IndexFile, $DataDir, $FullUrl);
|
||||
our ($RegistrationForm, $MinimumPasswordLength, $RegistrationsMustBeApproved, $LoginForm, $PasswordFile, $PasswordFileToUse, $PendingPasswordFile, $RequireLoginToEdit, $ConfirmEmailAddress, $UnconfirmedPasswordFile, $EmailSenderAddress, $EmailCommand, $EmailRegExp, $NotifyPendingRegistrations, $EmailConfirmationMessage, $ResetPasswordMessage, $LogoutForm, $ResetForm, $ChangePassForm, $RequireCamelUserName, $UsernameRegExp);
|
||||
our ($RegistrationForm, $MinimumPasswordLength, $RegistrationsMustBeApproved, $LoginForm, $PasswordFile, $PasswordFileToUse, $PendingPasswordFile, $RequireLoginToEdit, $ConfirmEmailAddress, $UncomfirmedPasswordFile, $EmailSenderAddress, $EmailCommand, $EmailRegExp, $NotifyPendingRegistrations, $EmailConfirmationMessage, $ResetPasswordMessage, $LogoutForm, $ResetForm, $ChangePassForm, $RequireCamelUserName, $UsernameRegExp);
|
||||
|
||||
my $EncryptedPassword = "";
|
||||
|
||||
@@ -40,7 +40,7 @@ $RegistrationsMustBeApproved = 1 unless defined $RegistrationsMustBeApproved;
|
||||
$PendingPasswordFile = "$DataDir/pending" unless defined $PendingPasswordFile;
|
||||
|
||||
$ConfirmEmailAddress = 1 unless defined $ConfirmEmailAddress;
|
||||
$UnconfirmedPasswordFile = "$DataDir/uncomfirmed" unless defined $UnconfirmedPasswordFile;
|
||||
$UncomfirmedPasswordFile = "$DataDir/uncomfirmed" unless defined $UncomfirmedPasswordFile;
|
||||
|
||||
$EmailSenderAddress = "fletcher\@freeshell.org" unless defined $EmailSenderAddress;
|
||||
$EmailCommand = "/usr/sbin/sendmail -oi -t" unless defined $EmailCommand;
|
||||
@@ -71,7 +71,7 @@ $PasswordFileToUse = $RegistrationsMustBeApproved
|
||||
? $PendingPasswordFile : $PasswordFile;
|
||||
|
||||
$PasswordFileToUse = $ConfirmEmailAddress
|
||||
? $UnconfirmedPasswordFile : $PasswordFileToUse;
|
||||
? $UncomfirmedPasswordFile : $PasswordFileToUse;
|
||||
|
||||
$RegistrationForm = <<'EOT' unless defined $RegistrationForm;
|
||||
<p>Your Username should be a CamelCase form of your real name, e.g. JohnDoe.</p>
|
||||
@@ -290,7 +290,7 @@ sub DoProcessLogin {
|
||||
ReportError(T('Username and/or password are incorrect.'))
|
||||
unless (AuthenticateUser($username,$pwd));
|
||||
|
||||
Unlink($IndexFile);
|
||||
unlink($IndexFile);
|
||||
print GetHeader('', Ts('Register for %s', $SiteName), '');
|
||||
print '<div class="content">';
|
||||
print Ts('Logged in as %s.', $username);
|
||||
@@ -318,7 +318,7 @@ $Action{process_logout} = \&DoProcessLogout;
|
||||
sub DoProcessLogout {
|
||||
SetParam('pwd','');
|
||||
SetParam('username','');
|
||||
Unlink($IndexFile); # I shouldn't have to do this...
|
||||
unlink($IndexFile); # I shouldn't have to do this...
|
||||
print GetHeader('', Ts('Logged out of %s', $SiteName), '');
|
||||
print '<div class="content">';
|
||||
print T('You are now logged out.');
|
||||
@@ -328,7 +328,7 @@ sub DoProcessLogout {
|
||||
|
||||
sub UserExists {
|
||||
my $username = shift;
|
||||
if (open (my $PASSWD, '<', encode_utf8($PasswordFile))) {
|
||||
if (open (my $PASSWD, '<', $PasswordFile)) {
|
||||
while ( <$PASSWD> ) {
|
||||
if ($_ =~ /^$username:/) {
|
||||
return 1;
|
||||
@@ -338,7 +338,7 @@ sub UserExists {
|
||||
}
|
||||
|
||||
if ($RegistrationsMustBeApproved) {
|
||||
if (open (my $PASSWD, '<', encode_utf8($PendingPasswordFile))) {
|
||||
if (open (my $PASSWD, '<', $PendingPasswordFile)) {
|
||||
while ( <$PASSWD> ) {
|
||||
if ($_ =~ /^$username:/) {
|
||||
return 1;
|
||||
@@ -349,7 +349,7 @@ sub UserExists {
|
||||
}
|
||||
|
||||
if ($ConfirmEmailAddress) {
|
||||
if (open (my $PASSWD, '<', encode_utf8($UnconfirmedPasswordFile))) {
|
||||
if (open (my $PASSWD, '<', $UncomfirmedPasswordFile)) {
|
||||
while ( <$PASSWD> ) {
|
||||
if ($_ =~ /^$username:/) {
|
||||
return 1;
|
||||
@@ -490,13 +490,14 @@ sub ConfirmUser {
|
||||
my ($username, $key) = @_;
|
||||
my $FileToUse = $RegistrationsMustBeApproved
|
||||
? $PendingPasswordFile : $PasswordFileToUse;
|
||||
if (open(my $PASSWD, '<', encode_utf8($UnconfirmedPasswordFile))) {
|
||||
|
||||
if (open(my $PASSWD, '<', $UncomfirmedPasswordFile)) {
|
||||
while (<$PASSWD>) {
|
||||
if ($_ =~ /^$username:(.*):(.*)/) {
|
||||
if (crypt($1,$key) eq $key) {
|
||||
AddUser($username,$1,$2,$FileToUse);
|
||||
close $PASSWD;
|
||||
RemoveUser($username,$UnconfirmedPasswordFile);
|
||||
RemoveUser($username,$UncomfirmedPasswordFile);
|
||||
if ($RegistrationsMustBeApproved) {
|
||||
SendNotification($username);
|
||||
}
|
||||
@@ -514,7 +515,8 @@ sub RemoveUser {
|
||||
|
||||
my %passwords = ();
|
||||
my %emails = ();
|
||||
if (open (my $PASSWD, '<', encode_utf8($FileToUse))) {
|
||||
|
||||
if (open (my $PASSWD, '<', $FileToUse)) {
|
||||
while ( <$PASSWD> ) {
|
||||
if ($_ =~ /^(.*):(.*):(.*)$/) {
|
||||
next if ($1 eq $username);
|
||||
@@ -597,7 +599,8 @@ sub ChangePassword {
|
||||
|
||||
my %passwords = ();
|
||||
my %emails = ();
|
||||
if (open (my $PASSWD, '<', encode_utf8($PasswordFile))) {
|
||||
|
||||
if (open (my $PASSWD, '<', $PasswordFile)) {
|
||||
while ( <$PASSWD> ) {
|
||||
if ($_ =~ /^(.*):(.*):(.*)$/) {
|
||||
$passwords{$1}=$2;
|
||||
@@ -609,7 +612,7 @@ sub ChangePassword {
|
||||
|
||||
$passwords{$user} = $hash;
|
||||
|
||||
open (my $PASSWD, '>', encode_utf8($PasswordFile));
|
||||
open (my $PASSWD, '>', $PasswordFile);
|
||||
foreach my $key ( sort keys(%passwords)) {
|
||||
print $PASSWD "$key:$passwords{$key}:$emails{$key}\n";
|
||||
}
|
||||
@@ -716,7 +719,7 @@ sub DoApprovePending {
|
||||
}
|
||||
} else {
|
||||
print '<ul>';
|
||||
if (open(my $PASSWD, '<', encode_utf8($PendingPasswordFile))) {
|
||||
if (open(my $PASSWD, '<', $PendingPasswordFile)) {
|
||||
while (<$PASSWD>) {
|
||||
if ($_ =~ /^(.*):(.*):(.*)$/) {
|
||||
print '<li>' . ScriptLink("action=approve_pending;user=$1;",$1) . ' - ' . $3 . '</li>';
|
||||
@@ -737,7 +740,8 @@ sub DoApprovePending {
|
||||
|
||||
sub ApproveUser {
|
||||
my ($username) = @_;
|
||||
if (open(my $PASSWD, '<', encode_utf8($PendingPasswordFile))) {
|
||||
|
||||
if (open(my $PASSWD, '<', $PendingPasswordFile)) {
|
||||
while (<$PASSWD>) {
|
||||
if ($_ =~ /^$username:(.*):(.*)/) {
|
||||
AddUser($username,$1,$2,$PasswordFile);
|
||||
|
||||
@@ -56,12 +56,14 @@ sub MacFixEncoding {
|
||||
return unless %Namespaces;
|
||||
my %hash = ();
|
||||
for my $key (keys %Namespaces) {
|
||||
utf8::decode($key);
|
||||
$key = NFC($key);
|
||||
$hash{$key} = $NamespaceRoot . '/' . $key . '/';
|
||||
}
|
||||
%Namespaces = %hash;
|
||||
%hash = ();
|
||||
for my $key (keys %InterSite) {
|
||||
utf8::decode($key);
|
||||
$key = NFC($key);
|
||||
$hash{$key} = $Namespaces{$key} if $Namespaces{$key};
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ sub MailIsSubscribed {
|
||||
return 0 unless $mail;
|
||||
# open the DB file
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
my %subscribers = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($id)}));
|
||||
untie %h;
|
||||
return $subscribers{$mail};
|
||||
@@ -197,7 +197,7 @@ sub NewMailDeletePage {
|
||||
sub MailDeletePage {
|
||||
my $id = shift;
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
foreach my $mail (split(/$FS/, UrlDecode(delete $h{UrlEncode($id)}))) {
|
||||
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
|
||||
delete $subscriptions{$id};
|
||||
@@ -274,7 +274,7 @@ sub MailSubscription {
|
||||
my $mail = shift;
|
||||
return unless $mail;
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
my @result = split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
|
||||
untie %h;
|
||||
@result = sort @result;
|
||||
@@ -303,7 +303,8 @@ sub DoMailSubscriptionList {
|
||||
'<ul>';
|
||||
}
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
foreach my $encodedkey (sort keys %h) {
|
||||
my @values = sort split(/$FS/, UrlDecode($h{$encodedkey}));
|
||||
my $key = UrlDecode($encodedkey);
|
||||
@@ -382,7 +383,7 @@ sub MailSubscribe {
|
||||
return unless $mail and @pages;
|
||||
# open the DB file
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
# add to the mail entry
|
||||
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
|
||||
for my $id (@pages) {
|
||||
@@ -441,7 +442,7 @@ sub MailUnsubscribe {
|
||||
my ($mail, @pages) = @_;
|
||||
return unless $mail and @pages;
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
my %subscriptions = map {$_=>1} split(/$FS/, UrlDecode($h{UrlEncode($mail)}));
|
||||
foreach my $id (@pages) {
|
||||
delete $subscriptions{$id};
|
||||
@@ -480,7 +481,8 @@ sub DoMailMigration {
|
||||
$q->start_div({-class=>'content mailmigrate'});
|
||||
|
||||
require DB_File;
|
||||
tie my %h, "DB_File", encode_utf8($MailFile);
|
||||
|
||||
tie my %h, "DB_File", $MailFile;
|
||||
my $found = 0;
|
||||
foreach my $key (keys %h) {
|
||||
if (index($key, '@') != -1) {
|
||||
|
||||
@@ -54,10 +54,10 @@ sub BisectAction {
|
||||
sub BisectInitialScreen {
|
||||
print GetFormStart(undef, 'get', 'bisect');
|
||||
print GetHiddenValue('action', 'bisect');
|
||||
my @disabledFiles = Glob("$ModuleDir/*.p[ml].disabled");
|
||||
my @disabledFiles = bsd_glob("$ModuleDir/*.p[ml].disabled");
|
||||
if (@disabledFiles == 0) {
|
||||
print T('Test / Always enabled / Always disabled'), $q->br();
|
||||
my @files = Glob("$ModuleDir/*.p[ml]");
|
||||
my @files = bsd_glob("$ModuleDir/*.p[ml]");
|
||||
for (my $i = 0; $i < @files; $i++) {
|
||||
my $moduleName = fileparse($files[$i]);
|
||||
my @disabled = ($moduleName eq 'module-bisect.pl' ? (-disabled=>'disabled') : ());
|
||||
@@ -78,7 +78,7 @@ sub BisectProcess {
|
||||
my ($isGood) = @_;
|
||||
my $parameterHandover = '';
|
||||
BisectEnableAll();
|
||||
my @files = Glob("$ModuleDir/*.p[ml]");
|
||||
my @files = bsd_glob("$ModuleDir/*.p[ml]");
|
||||
for (my $i = @files - 1; $i >= 0; $i--) { # handle user choices
|
||||
if (GetParam("m$i") eq 'on') {
|
||||
$parameterHandover .= GetHiddenValue("m$i", GetParam("m$i"));
|
||||
@@ -131,7 +131,7 @@ sub BisectProcess {
|
||||
}
|
||||
|
||||
sub BisectEnableAll {
|
||||
for (Glob("$ModuleDir/*.p[ml].disabled")) { # reenable all modules
|
||||
for (bsd_glob("$ModuleDir/*.p[ml].disabled")) { # reenable all modules
|
||||
my $oldName = $_;
|
||||
s/\.disabled$//;
|
||||
print Ts('Enabling %s', (fileparse($_))[0]), '...', $q->br() if $_[0];
|
||||
|
||||
@@ -40,8 +40,8 @@ sub ModuleUpdaterAction {
|
||||
if (GetParam('ok')) {
|
||||
ModuleUpdaterApply();
|
||||
} else {
|
||||
Unlink(Glob("$TempDir/*.p[ml]")); # XXX is it correct to use $TempDir for such stuff? What if something else puts .pm files there?
|
||||
for (Glob("$ModuleDir/*.p[ml]")) {
|
||||
unlink bsd_glob("$TempDir/*.p[ml]"); # XXX is it correct to use $TempDir for such stuff? What if something else puts .pm files there?
|
||||
for (bsd_glob("$ModuleDir/*.p[ml]")) {
|
||||
my $curModule = fileparse($_);
|
||||
ProcessModule($curModule);
|
||||
}
|
||||
@@ -58,7 +58,7 @@ sub ModuleUpdaterAction {
|
||||
}
|
||||
|
||||
sub ModuleUpdaterApply {
|
||||
for (Glob("$TempDir/*.p[ml]")) {
|
||||
for (bsd_glob("$TempDir/*.p[ml]")) {
|
||||
my $moduleName = fileparse($_);
|
||||
if (move($_, "$ModuleDir/$moduleName")) {
|
||||
print $q->strong("Module $moduleName updated successfully!"), $q->br();
|
||||
@@ -66,7 +66,7 @@ sub ModuleUpdaterApply {
|
||||
print $q->strong("Unable to replace module $moduleName: $!"), $q->br();
|
||||
}
|
||||
}
|
||||
Unlink(Glob("$TempDir/*.p[ml]")); # XXX same as above
|
||||
unlink bsd_glob("$TempDir/*.p[ml]"); # XXX same as above
|
||||
print $q->br(), $q->strong('Done!');
|
||||
}
|
||||
|
||||
@@ -81,14 +81,15 @@ sub ProcessModule {
|
||||
. ' If this is your own module, please contribute it to Oddmuse!'), $q->br();
|
||||
return;
|
||||
}
|
||||
open my $fh, ">:utf8", encode_utf8("$TempDir/$module") or die("Could not open file $TempDir/$module: $!");
|
||||
open my $fh, ">", "$TempDir/$module" or die("Could not open file. $!");
|
||||
utf8::encode($moduleData);
|
||||
print $fh $moduleData;
|
||||
close $fh;
|
||||
|
||||
my $diff = DoModuleDiff("$ModuleDir/$module", "$TempDir/$module");
|
||||
if (not $diff) {
|
||||
print $q->strong('This module is up to date, there is no need to update it.'), $q->br();
|
||||
Unlink("$TempDir/$module");
|
||||
unlink "$TempDir/$module";
|
||||
return;
|
||||
}
|
||||
print $q->strong('There is a newer version of this module. Here is a diff:'), $q->br();
|
||||
@@ -108,5 +109,7 @@ sub ProcessModule {
|
||||
}
|
||||
|
||||
sub DoModuleDiff {
|
||||
decode_utf8(`diff -U 3 -- \Q$_[0]\E \Q$_[1]\E`);
|
||||
my $diff = `diff -U 3 -- \Q$_[0]\E \Q$_[1]\E`;
|
||||
utf8::decode($diff); # needs decoding
|
||||
return $diff;
|
||||
}
|
||||
|
||||
@@ -85,8 +85,9 @@ sub NamespacesInitVariables {
|
||||
# Do this before changing the $DataDir and $ScriptName
|
||||
if ($UsePathInfo) {
|
||||
$Namespaces{$NamespacesMain} = $ScriptName . '/';
|
||||
foreach my $name (Glob("$DataDir/*")) {
|
||||
if (IsDir($name)
|
||||
foreach my $name (bsd_glob("$DataDir/*")) {
|
||||
utf8::decode($name);
|
||||
if (-d $name
|
||||
and $name =~ m|/($InterSitePattern)$|
|
||||
and $name ne $NamespacesMain
|
||||
and $name ne $NamespacesSelf) {
|
||||
@@ -98,7 +99,8 @@ sub NamespacesInitVariables {
|
||||
$NamespaceCurrent = '';
|
||||
my $ns = GetParam('ns', '');
|
||||
if (not $ns and $UsePathInfo) {
|
||||
my $path_info = decode_utf8($q->path_info());
|
||||
my $path_info = $q->path_info();
|
||||
utf8::decode($path_info);
|
||||
# make sure ordinary page names are not matched!
|
||||
if ($path_info =~ m|^/($InterSitePattern)(/.*)?|
|
||||
and ($2 or $q->keywords or NamespaceRequiredByParameter())) {
|
||||
@@ -135,8 +137,13 @@ sub NamespacesInitVariables {
|
||||
$StaticUrl .= UrlEncode($NamespaceCurrent) . '/'
|
||||
if substr($StaticUrl,-1) eq '/'; # from static-copy.pl
|
||||
$WikiDescription .= "<p>Current namespace: $NamespaceCurrent</p>";
|
||||
$LastUpdate = Modified($IndexFile);
|
||||
CreateDir($DataDir);
|
||||
# override LastUpdate
|
||||
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks)
|
||||
= stat($IndexFile);
|
||||
$LastUpdate = $mtime;
|
||||
CreateDir($DataDir); # Create directory if it doesn't exist
|
||||
ReportError(Ts('Cannot create %s', $DataDir) . ": $!", '500 INTERNAL SERVER ERROR')
|
||||
unless -d $DataDir;
|
||||
}
|
||||
$Namespaces{$NamespacesSelf} = $ScriptName . '?';
|
||||
# reinitialize
|
||||
@@ -217,19 +224,19 @@ sub NewNamespaceGetRcLines { # starttime, hash of seen pages to use as a second
|
||||
# opening a rcfile, compare the first timestamp with the
|
||||
# starttime. If any rcfile exists with no timestamp before the
|
||||
# starttime, we need to open its rcoldfile.
|
||||
foreach my $rcfile (@rcfiles) {
|
||||
open(my $F, '<:encoding(UTF-8)', encode_utf8($rcfile));
|
||||
foreach my $file (@rcfiles) {
|
||||
open(my $F, '<:encoding(UTF-8)', $file);
|
||||
my $line = <$F>;
|
||||
my ($ts) = split(/$FS/, $line); # the first timestamp in the regular rc file
|
||||
my @new;
|
||||
if (not $ts or $ts > $starttime) { # we need to read the old rc file, too
|
||||
push(@new, GetRcLinesFor($rcoldfiles{$rcfile}, $starttime,\%match, \%following));
|
||||
push(@new, GetRcLinesFor($rcoldfiles{$file}, $starttime,\%match, \%following));
|
||||
}
|
||||
push(@new, GetRcLinesFor($rcfile, $starttime, \%match, \%following));
|
||||
push(@new, GetRcLinesFor($file, $starttime, \%match, \%following));
|
||||
# strip rollbacks in each namespace separately
|
||||
@new = StripRollbacks(@new);
|
||||
# prepend the namespace to both pagename and author
|
||||
my $ns = $namespaces{$rcfile};
|
||||
my $ns = $namespaces{$file};
|
||||
if ($ns) {
|
||||
for (my $i = $#new; $i >= 0; $i--) {
|
||||
# page id
|
||||
@@ -434,6 +441,8 @@ sub NamespacesNewGetId {
|
||||
# In this case GetId() will have set the parameter Test to 1.
|
||||
# http://example.org/cgi-bin/wiki.pl/Test?rollback-1234=foo
|
||||
# This doesn't set the Test parameter.
|
||||
return if $id and $UsePathInfo and $id eq $NamespaceCurrent and not GetParam($id) and not GetParam('ns');
|
||||
if ($UsePathInfo and $id eq $NamespaceCurrent and not GetParam($id) and not GetParam('ns')) {
|
||||
$id = undef;
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use v5.10;
|
||||
|
||||
AddModuleDescription('near-links.pl', 'Near Links');
|
||||
|
||||
our ($q, $Now, %AdminPages, %InterSite, $CommentsPrefix, $DataDir, $UseCache, @MyFooters, @MyMaintenance, @MyInitVariables, @Debugging, $InterSitePattern, @UserGotoBarPages, @IndexOptions);
|
||||
our ($q, %AdminPages, %InterSite, $CommentsPrefix, $DataDir, $UseCache, @MyFooters, @MyMaintenance, @MyInitVariables, @Debugging, $InterSitePattern, @UserGotoBarPages, @IndexOptions);
|
||||
|
||||
=head1 Near Links
|
||||
|
||||
@@ -128,8 +128,7 @@ sub NearLinksMaintenance {
|
||||
# skip if less than 12h old and caching allowed (the default)
|
||||
foreach my $site (keys %NearSite) {
|
||||
next if GetParam('cache', $UseCache) > 0
|
||||
and IsFile("$NearDir/$site")
|
||||
and $Now - Modified("$NearDir/$site") < 0.5;
|
||||
and -f "$NearDir/$site" and -M "$NearDir/$site" < 0.5;
|
||||
print $q->p(Ts('Getting page index file for %s.', $site));
|
||||
my $data = GetRaw($NearSite{$site});
|
||||
print $q->p($q->strong(Ts('%s returned no data, or LWP::UserAgent is not available.',
|
||||
|
||||
@@ -35,7 +35,7 @@ $Action{clearcache} = \&DoClearCache;
|
||||
|
||||
sub DoClearCache {
|
||||
print GetHeader('', QuoteHtml(T('Clearing Cache')), '');
|
||||
Unlink(Glob("$NotFoundHandlerDir/*"));
|
||||
unlink(bsd_glob("$NotFoundHandlerDir/*"));
|
||||
print $q->p(T('Done.'));
|
||||
PrintFooter();
|
||||
}
|
||||
@@ -45,7 +45,7 @@ sub DoClearCache {
|
||||
sub ReadLinkDb {
|
||||
return if $LinkDbInit;
|
||||
$LinkDbInit = 1;
|
||||
return if not IsFile($LinkFile);
|
||||
return if not -f $LinkFile;
|
||||
my $data = ReadFileOrDie($LinkFile);
|
||||
map { my ($id, @links) = split; $LinkDb{$id} = \@links } split(/\n/, $data);
|
||||
}
|
||||
@@ -101,13 +101,13 @@ sub NewNotFoundHandlerSave {
|
||||
my $id = $args[0];
|
||||
OldNotFoundHandlerSave(@args);
|
||||
RefreshLinkDb(); # for the open page
|
||||
if (not IsDir($NotFoundHandlerDir)) {
|
||||
CreateDir($NotFoundHandlerDir);
|
||||
if (not -d $NotFoundHandlerDir) {
|
||||
mkdir($NotFoundHandlerDir);
|
||||
} elsif ($Page{revision} == 1) {
|
||||
NotFoundHandlerCacheUpdate($id);
|
||||
} else {
|
||||
# unlink PageName, PageName.en, PageName.de, etc.
|
||||
Unlink("$NotFoundHandlerDir/$id", Glob("$NotFoundHandlerDir/$id.[a-z][a-z]"));
|
||||
unlink("$NotFoundHandlerDir/$id", bsd_glob("$NotFoundHandlerDir/$id.[a-z][a-z]"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ sub NotFoundHandlerCacheUpdate {
|
||||
foreach my $source (keys %LinkDb) {
|
||||
warn "Examining $source\n";
|
||||
if (grep(/$target/, @{$LinkDb{$source}})) {
|
||||
Unlink("$NotFoundHandlerDir/$source", Glob("$NotFoundHandlerDir/$source.[a-z][a-z]"));
|
||||
unlink("$NotFoundHandlerDir/$source", bsd_glob("$NotFoundHandlerDir/$source.[a-z][a-z]"));
|
||||
warn "Unlinking $source\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ sub PrivateWikiInit {
|
||||
}
|
||||
}
|
||||
|
||||
sub PadTo16Bytes { # use this only on bytes (after encode_utf8)
|
||||
sub PadTo16Bytes { # use this only on UTF-X strings (after utf8::encode)
|
||||
my ($data, $minLength) = @_;
|
||||
my $endBytes = length($data) % 16;
|
||||
$data .= "\0" x (16 - $endBytes) if $endBytes != 0;
|
||||
@@ -62,7 +62,8 @@ sub NewPrivateWikiReadFile {
|
||||
$q->p($errorMessage)) if not UserIsEditor();
|
||||
PrivateWikiInit();
|
||||
my $file = shift;
|
||||
if (open(my $IN, '<', encode_utf8($file))) {
|
||||
utf8::encode($file); # filenames are bytes!
|
||||
if (open(my $IN, '<', $file)) {
|
||||
local $/ = undef; # Read complete files
|
||||
my $data = <$IN>;
|
||||
close $IN;
|
||||
@@ -71,7 +72,8 @@ sub NewPrivateWikiReadFile {
|
||||
$data = $cipher->decrypt(substr $data, 16);
|
||||
my $copy = $data; # copying is required, see https://github.com/briandfoy/crypt-rijndael/issues/5
|
||||
$copy =~ s/\0+$//;
|
||||
return (1, decode_utf8($copy));
|
||||
utf8::decode($copy);
|
||||
return (1, $copy);
|
||||
}
|
||||
return (0, '');
|
||||
}
|
||||
@@ -84,12 +86,13 @@ sub NewPrivateWikiWriteStringToFile {
|
||||
$q->p($errorMessage)) if not UserIsEditor();
|
||||
PrivateWikiInit();
|
||||
my ($file, $string) = @_;
|
||||
open(my $OUT, '>', encode_utf8($file))
|
||||
or ReportError(Ts('Cannot write %s', $file) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
utf8::encode($file);
|
||||
open(my $OUT, '>', $file) or ReportError(Ts('Cannot write %s', $file) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
utf8::encode($string);
|
||||
my $iv = $random->random_bytes(16);
|
||||
$cipher->set_iv($iv);
|
||||
print $OUT $iv;
|
||||
print $OUT $cipher->encrypt(PadTo16Bytes(encode_utf8($string)));
|
||||
print $OUT $cipher->encrypt(PadTo16Bytes $string);
|
||||
close($OUT);
|
||||
}
|
||||
|
||||
@@ -106,9 +109,9 @@ sub AppendStringToFile {
|
||||
*RefreshIndex = \&NewPrivateWikiRefreshIndex;
|
||||
|
||||
sub NewPrivateWikiRefreshIndex {
|
||||
if (not IsFile($IndexFile)) { # Index file does not exist yet, this is a new wiki
|
||||
if (not -f $IndexFile) { # Index file does not exist yet, this is a new wiki
|
||||
my $fh;
|
||||
open($fh, '>', encode_utf8($IndexFile)) or die "Unable to open file $IndexFile : $!"; # 'touch' equivalent
|
||||
open($fh, '>', $IndexFile) or die "Unable to open file $IndexFile : $!"; # 'touch' equivalent
|
||||
close($fh) or die "Unable to close file : $IndexFile $!";
|
||||
return;
|
||||
}
|
||||
@@ -160,7 +163,8 @@ sub GetPrivatePageFile {
|
||||
}
|
||||
$cipher->set_iv($iv);
|
||||
# We cannot use full byte range because of the filesystem limits
|
||||
my $returnName = unpack "H*", $iv . $cipher->encrypt(PadTo16Bytes(encode_utf8($id), 96)); # to hex string
|
||||
utf8::encode($id);
|
||||
my $returnName = unpack "H*", $iv . $cipher->encrypt(PadTo16Bytes $id, 96); # to hex string
|
||||
return $returnName;
|
||||
}
|
||||
|
||||
@@ -212,13 +216,14 @@ sub DoDiff { # Actualy call the diff program
|
||||
my $oldName = "$TempDir/old";
|
||||
my $newName = "$TempDir/new";
|
||||
RequestLockDir('diff') or return '';
|
||||
$LockCleaners{'diff'} = sub { Unlink($oldName) if IsFile($oldName); Unlink($newName) if IsFile($newName); };
|
||||
$LockCleaners{'diff'} = sub { unlink $oldName if -f $oldName; unlink $newName if -f $newName; };
|
||||
OldPrivateWikiWriteStringToFile($oldName, $_[0]); # CHANGED Here we use the old sub!
|
||||
OldPrivateWikiWriteStringToFile($newName, $_[1]); # CHANGED
|
||||
my $diff_out = decode_utf8(`diff -- \Q$oldName\E \Q$newName\E`);
|
||||
my $diff_out = `diff -- \Q$oldName\E \Q$newName\E`;
|
||||
utf8::decode($diff_out); # needs decoding
|
||||
$diff_out =~ s/\n\K\\ No newline.*\n//g; # Get rid of common complaint.
|
||||
# CHANGED We have to unlink the files because we don't want to store them in plaintext!
|
||||
Unlink($oldName, $newName); # CHANGED
|
||||
unlink $oldName, $newName; # CHANGED
|
||||
ReleaseLockDir('diff');
|
||||
return $diff_out;
|
||||
}
|
||||
@@ -232,14 +237,15 @@ sub MergeRevisions { # merge change from file2 to file3 into file1
|
||||
CreateDir($TempDir);
|
||||
RequestLockDir('merge') or return T('Could not get a lock to merge!');
|
||||
$LockCleaners{'merge'} = sub { # CHANGED
|
||||
Unlink($name1) if IsFile($name1); Unlink($name2) if IsFile($name2); Unlink($name3) if IsFile($name3);
|
||||
unlink $name1 if -f $name1; unlink $name2 if -f $name2; unlink $name3 if -f $name3;
|
||||
};
|
||||
OldPrivateWikiWriteStringToFile($name1, $file1); # CHANGED
|
||||
OldPrivateWikiWriteStringToFile($name2, $file2); # CHANGED
|
||||
OldPrivateWikiWriteStringToFile($name3, $file3); # CHANGED
|
||||
my ($you, $ancestor, $other) = (T('you'), T('ancestor'), T('other'));
|
||||
my $output = decode_utf8(`diff3 -m -L \Q$you\E -L \Q$ancestor\E -L \Q$other\E -- \Q$name1\E \Q$name2\E \Q$name3\E`);
|
||||
Unlink($name1, $name2, $name3); # CHANGED unlink temp files -- we don't want to store them in plaintext!
|
||||
my $output = `diff3 -m -L \Q$you\E -L \Q$ancestor\E -L \Q$other\E -- \Q$name1\E \Q$name2\E \Q$name3\E`;
|
||||
utf8::decode($output); # needs decoding
|
||||
unlink $name1, $name2, $name3; # CHANGED unlink temp files -- we don't want to store them in plaintext!
|
||||
ReleaseLockDir('merge');
|
||||
return $output;
|
||||
}
|
||||
|
||||
@@ -52,9 +52,11 @@ sub DoPygmentize {
|
||||
|
||||
RequestLockDir('pygmentize') or return '';
|
||||
WriteStringToFile("$TempDir/pygmentize", $contents);
|
||||
my $output = decode_utf8(`pygmentize $lexer -f html -O encoding=utf8 $args -- \Q$TempDir/pygmentize\E 2>&1`);
|
||||
my $output = `pygmentize $lexer -f html -O encoding=utf8 $args -- \Q$TempDir/pygmentize\E 2>&1`;
|
||||
ReleaseLockDir('pygmentize');
|
||||
|
||||
utf8::decode($output);
|
||||
|
||||
if ($?) {
|
||||
$output = $q->p($q->strong($output)) # "sh: pygmentize: command not found"
|
||||
. $q->pre($contents);
|
||||
|
||||
@@ -65,7 +65,7 @@ sub RefererNewDeletePage {
|
||||
return $status if $status; # this would be the error message
|
||||
my $id = shift;
|
||||
my $fname = GetRefererFile($id);
|
||||
Unlink($fname) if (IsFile($fname));
|
||||
unlink($fname) if (-f $fname);
|
||||
return ''; # no error
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ sub GetRefererFile {
|
||||
sub ReadReferers {
|
||||
my $file = GetRefererFile(shift);
|
||||
%Referers = ();
|
||||
if (IsFile($file)) {
|
||||
if (-f $file) {
|
||||
my ($status, $data) = ReadFile($file);
|
||||
%Referers = split(/$FS/, $data, -1) if $status;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ sub PageContentToTitle {
|
||||
$title =~ s!\s+! !g;
|
||||
$title =~ s!^ !!;
|
||||
$title =~ s! $!!;
|
||||
$title = substr($title, 0, $RefererTitleLimit) . "..."
|
||||
$title = substring($title, 0, $RefererTitleLimit) . "..."
|
||||
if length($title) > $RefererTitleLimit;
|
||||
return $title;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ sub WriteReferers {
|
||||
CreateDir($RefererDir);
|
||||
WriteStringToFile($file, $data);
|
||||
} else {
|
||||
Unlink($file); # just try it, doesn't matter if it fails
|
||||
unlink $file; # just try it, doesn't matter if it fails
|
||||
}
|
||||
ReleaseLockDir('refer_' . $id);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ my $dummy = RelationRead();
|
||||
|
||||
sub RelationRead {
|
||||
# return scalar(@RelationLinking) if (scalar(@RelationLinking));
|
||||
open (my $RRR, '<', encode_utf8("$DataDir/$referencefile")) || return(0);
|
||||
open (my $RRR, '<', "$DataDir/$referencefile") || return(0);
|
||||
while (<$RRR>) {
|
||||
chomp;
|
||||
my ($a,$b,$c) = split(';');
|
||||
@@ -172,7 +172,7 @@ $Action{'updaterelates'} = sub {
|
||||
else {
|
||||
print "no new source<br />\n";
|
||||
}
|
||||
open (my $RRR, '>', encode_utf8("$DataDir/$referencefile"));
|
||||
open (my $RRR, '>', "$DataDir/$referencefile");
|
||||
print "<br />\n";
|
||||
foreach my $t (@RelationLinking) {
|
||||
next unless (defined($t));
|
||||
|
||||
@@ -30,9 +30,9 @@ $SmileyUrlPath = '/pics'; # path where all the smileys can be found (URL)
|
||||
push(@MyInitVariables, \&SmileyDirInit);
|
||||
|
||||
sub SmileyDirInit {
|
||||
if (opendir(DIR, encode_utf8($SmileyDir))) {
|
||||
if (opendir(DIR, $SmileyDir)) {
|
||||
map {
|
||||
if (/^((.*)\.$ImageExtensions$)/ and IsFile("$SmileyDir/$_")) {
|
||||
if (/^((.*)\.$ImageExtensions$)/ and -f "$SmileyDir/$_") {
|
||||
my $regexp = quotemeta("{$2}");
|
||||
$Smilies{$regexp} = "$SmileyUrlPath/$1";
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ sub StaticScriptLink {
|
||||
my %params;
|
||||
if ($action !~ /=/) {
|
||||
# the page might not exist, eg. if called via GetAuthorLink
|
||||
$params{'-href'} = StaticFileName($action) if $IndexHash{UrlDecode($action)};
|
||||
$params{-href} = StaticFileName($action) if $IndexHash{$action};
|
||||
}
|
||||
$params{'-class'} = $class if $class;
|
||||
$params{'-name'} = UrlEncode($name) if $name;
|
||||
@@ -131,25 +131,23 @@ sub StaticFileName {
|
||||
sub StaticWriteFile {
|
||||
my ($id, $html) = @_;
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $filename = StaticFileName($id);
|
||||
OpenPage($id);
|
||||
my ($mimetype, $encoding, $data) =
|
||||
$Page{text} =~ /^\#FILE ([^ \n]+) ?([^ \n]*)\n(.*)/s;
|
||||
my $filename = StaticFileName($id);
|
||||
my $file = "$StaticDir/$filename";
|
||||
$Page{text} =~ /^\#FILE ([^ \n]+) ?([^ \n]*)\n(.*)/s;
|
||||
open(my $fh, '>', "$StaticDir/$filename")
|
||||
or ReportError(Ts('Cannot write %s', $filename));
|
||||
if ($data) {
|
||||
open(my $fh, '>', encode_utf8($file))
|
||||
or ReportError(Ts('Cannot write %s', $filename));
|
||||
binmode($fh);
|
||||
StaticFile($id, $fh, $mimetype, $data);
|
||||
close($fh);
|
||||
} elsif ($html) {
|
||||
open(my $fh, '>:encoding(UTF-8)', encode_utf8($file))
|
||||
or ReportError(Ts('Cannot write %s', $filename));
|
||||
binmode($fh, ':encoding(UTF-8)');
|
||||
StaticHtml($id, $fh);
|
||||
close($fh);
|
||||
} else {
|
||||
print "no data for ";
|
||||
}
|
||||
ChangeMod(0644,"$StaticDir/$filename");
|
||||
close($fh);
|
||||
chmod 0644,"$StaticDir/$filename";
|
||||
print $filename, $raw ? "\n" : $q->br();
|
||||
}
|
||||
|
||||
@@ -281,7 +279,7 @@ sub StaticDeleteFile {
|
||||
%StaticMimeTypes = StaticMimeTypes() unless %StaticMimeTypes;
|
||||
# we don't care if the files or $StaticDir don't exist -- just delete!
|
||||
for my $f (map { "$StaticDir/$id.$_" } (values %StaticMimeTypes, 'html')) {
|
||||
Unlink($f); # delete copies with different extensions
|
||||
unlink $f; # delete copies with different extensions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ $StaticUrl = '' unless defined $StaticUrl; # change this!
|
||||
$StaticAlways = 0 unless defined $StaticAlways;
|
||||
# 1 = uploaded files only, 2 = all pages
|
||||
|
||||
my $StaticMimeTypes = '/etc/http/mime.types'; # all-ASCII characters
|
||||
my $StaticMimeTypes = '/etc/http/mime.types';
|
||||
my %StaticFiles;
|
||||
|
||||
my $StaticAction = 0; # Are we doing action or not?
|
||||
@@ -133,11 +133,12 @@ sub StaticWriteFile {
|
||||
my $id = shift;
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $html = GetParam('html', 1);
|
||||
my $filename = StaticFileName($id);
|
||||
|
||||
OpenPage($id);
|
||||
my ($mimetype, $data) = $Page{text} =~ /^\#FILE ([^ \n]+)\n(.*)/s;
|
||||
return unless $html or $data;
|
||||
my $filename = StaticFileName($id);
|
||||
open(my $F, '>', encode_utf8("$StaticDir/$filename")) or ReportError(Ts('Cannot write %s', $filename));
|
||||
open(my $F, '>', "$StaticDir/$filename") or ReportError(Ts('Cannot write %s', $filename));
|
||||
if ($data) {
|
||||
StaticFile($id, $mimetype, $data, $F);
|
||||
} elsif ($html) {
|
||||
@@ -240,7 +241,7 @@ sub StaticDeleteFile {
|
||||
%StaticMimeTypes = StaticMimeTypes() unless %StaticMimeTypes;
|
||||
# we don't care if the files or $StaticDir don't exist -- just delete!
|
||||
for my $f (map { "$StaticDir/$id.$_" } (values %StaticMimeTypes, 'html')) {
|
||||
Unlink($f); # delete copies with different extensions
|
||||
unlink $f; # delete copies with different extensions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ Example:
|
||||
|
||||
=cut
|
||||
|
||||
our ($q, $Now, %Action, %Page, $FreeLinkPattern, @MyInitVariables, @MyRules, @MyAdminCode, $DataDir, $ScriptName);
|
||||
our ($q, %Action, %Page, $FreeLinkPattern, @MyInitVariables, @MyRules, @MyAdminCode, $DataDir, $ScriptName);
|
||||
our ($TagUrl, $TagFeed, $TagFeedIcon, $TagFile);
|
||||
|
||||
push(@MyInitVariables, \&TagsInit);
|
||||
@@ -84,7 +84,7 @@ sub TagsGetLink {
|
||||
|
||||
sub TagReadHash {
|
||||
require Storable;
|
||||
return %{ Storable::retrieve(encode_utf8($TagFile)) } if IsFile($TagFile);
|
||||
return %{ Storable::retrieve($TagFile) } if -f $TagFile;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ sub TagReadHash {
|
||||
sub TagWriteHash {
|
||||
my $h = shift;
|
||||
require Storable;
|
||||
return Storable::store($h, encode_utf8($TagFile));
|
||||
return Storable::store($h, $TagFile);
|
||||
}
|
||||
|
||||
push(@MyRules, \&TagsRule);
|
||||
@@ -338,9 +338,7 @@ Example:
|
||||
$Action{reindex} = \&DoTagsReindex;
|
||||
|
||||
sub DoTagsReindex {
|
||||
if (not UserIsAdmin()
|
||||
and IsFile($TagFile)
|
||||
and $Now - Modified($TagFile) < 0.5) {
|
||||
if (!UserIsAdmin() && (-f $TagFile) && ((-M $TagFile) < 0.5)) {
|
||||
ReportError(T('Rebuilding index not done.'), '403 FORBIDDEN',
|
||||
0, T('(Rebuilding the index can only be done once every 12 hours.)'));
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ sub NewDoBrowseRequest {
|
||||
|
||||
# limit the script to a maximum of $InstanceThrottleLimit instances
|
||||
sub DoInstanceThrottle {
|
||||
my @pids = Glob($InstanceThrottleDir."/*");
|
||||
my @pids = bsd_glob($InstanceThrottleDir."/*");
|
||||
# Go over all pids: validate each pid by sending signal 0, unlink
|
||||
# pidfile if pid does not exist and return 0. Count the number of
|
||||
# zeros (= removed files = zombies) with grep.
|
||||
my $zombies = grep /^0$/,
|
||||
(map {/(\d+)$/ and kill 0,$1 or Unlink($_) and 0} @pids);
|
||||
(map {/(\d+)$/ and kill 0,$1 or unlink and 0} @pids);
|
||||
if (scalar(@pids)-$zombies >= $InstanceThrottleLimit) {
|
||||
ReportError(Ts('Too many instances. Only %s allowed.',
|
||||
$InstanceThrottleLimit),
|
||||
@@ -80,5 +80,5 @@ sub CreatePidFile {
|
||||
sub RemovePidFile {
|
||||
my $file = "$InstanceThrottleDir/$$";
|
||||
# not fatal
|
||||
Unlink($file);
|
||||
unlink $file;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ sub ThumbNailSupportRule {
|
||||
{
|
||||
|
||||
|
||||
if (!IsFile("$ThumbnailCacheDir/$id/$size"))
|
||||
if (! -e "$ThumbnailCacheDir/$id/$size")
|
||||
{
|
||||
GenerateThumbNail ($id, $size);
|
||||
}
|
||||
@@ -209,8 +209,10 @@ sub GenerateThumbNail {
|
||||
|
||||
|
||||
my $filename = $ThumbnailTempDir . "/odd" . $id . "_" . $size;
|
||||
|
||||
# Decode the original image to a temp file
|
||||
open(my $FD, '>', encode_utf8($filename)) or ReportError(Ts("Could not open %s for writing whilst trying to save image before creating thumbnail. Check write permissions.",$filename), '500 INTERNAL SERVER ERROR');
|
||||
|
||||
open(my $FD, '>', $filename) or ReportError(Ts("Could not open %s for writing whilst trying to save image before creating thumbnail. Check write permissions.",$filename), '500 INTERNAL SERVER ERROR');
|
||||
binmode($FD);
|
||||
print $FD MIME::Base64::decode($data);
|
||||
close($FD);
|
||||
@@ -247,7 +249,7 @@ sub GenerateThumbNail {
|
||||
ReportError(Ts("Failed to parse %s.", $convert), '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
|
||||
Unlink($filename);
|
||||
unlink($filename);
|
||||
|
||||
# save tag to page
|
||||
#$Page{'thumbnail_' . $size} = '#FILE ' . $type . ' created=' . $Now . ' revision=' . $Page{'revision'} . ' size=' . $scaled_size_x . 'x' . $scaled_size_y . "\n" . $thumbnail_data;
|
||||
|
||||
@@ -229,10 +229,18 @@ my $TocCommentPattern = qr~\Q<!-- toc\E.*?\Q -->\E~;
|
||||
# appropriate, and then printed at the very end.
|
||||
sub NewTocApplyRules {
|
||||
my ($html, $blocks, $flags);
|
||||
$html = ToString(sub{
|
||||
# pass arguments on to OldTocApplyRules given that ToString takes a code ref
|
||||
{
|
||||
local *STDOUT;
|
||||
my $html_unfixed;
|
||||
open( STDOUT, '>', \$html_unfixed) or die "Can't open memory file: $!";
|
||||
binmode STDOUT, ":encoding(UTF-8)";
|
||||
($blocks, $flags) = OldTocApplyRules(@_);
|
||||
}, @_);
|
||||
close STDOUT;
|
||||
utf8::decode($blocks);
|
||||
# do not delete!
|
||||
$html = $html_unfixed; # this is a workarond for perl bug
|
||||
utf8::decode($html); # otherwise UTF8 characters are SOMETIMES not decoded.
|
||||
}
|
||||
# If there are at least two HTML headers on this page, insert a table of
|
||||
# contents.
|
||||
if ($TocHeaderNumber > 2) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,9 +89,9 @@ Ungültige Seite %s (Darf nicht mit .lck enden)
|
||||
Invalid Page %s
|
||||
Ungültige Seite %s
|
||||
There are no comments, yet. Be the first to leave a comment!
|
||||
Es gibt noch keine Kommentare, sei der Erste der einen hinterlässt!
|
||||
|
||||
Welcome!
|
||||
Willkommen!
|
||||
|
||||
This page does not exist, but you can %s.
|
||||
Diese Seite gibt es nicht, aber du kannst %s.
|
||||
create it now
|
||||
@@ -343,9 +343,9 @@ Die Sperre wurde %s gesetzt.
|
||||
Maybe the user running this script is no longer allowed to remove the lock directory?
|
||||
Vielleicht darf der user, welcher dieses script ausführt, das Sperr-Verzeichnis nicht löschen?
|
||||
Sometimes locks are left behind if a job crashes.
|
||||
Manchmal bleiben Sperren erhalten, wenn ein Prozess abbricht.
|
||||
|
||||
After ten minutes, you could try to unlock the wiki.
|
||||
Nach zehn Minuten kann versucht werden, das Wiki zu entsperren.
|
||||
|
||||
This operation may take several seconds...
|
||||
Das könnte einige Sekunden dauern...
|
||||
Forced unlock of %s lock.
|
||||
@@ -435,7 +435,7 @@ Grund unbekannt.
|
||||
%s pages found.
|
||||
%s Seiten gefunden.
|
||||
Preview: %s
|
||||
Vorschau: %s
|
||||
|
||||
Replaced: %s
|
||||
Ersetzt: %s
|
||||
Search for: %s
|
||||
@@ -786,7 +786,7 @@ Kommentar hinzufügen
|
||||
ordinary changes
|
||||
normale Änderungen
|
||||
%s days
|
||||
%s Tage
|
||||
|
||||
################################################################################
|
||||
# modules/edit-paragraphs.pl
|
||||
################################################################################
|
||||
@@ -1398,9 +1398,9 @@ Portrait
|
||||
# modules/preview.pl
|
||||
################################################################################
|
||||
Pages with changed HTML
|
||||
Seiten mit geändertem HTML
|
||||
|
||||
Preview changes in HTML output
|
||||
Vorschau der Änderungen der HTML-Ausgabe
|
||||
|
||||
################################################################################
|
||||
# modules/private-pages.pl
|
||||
################################################################################
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
# Copyright (C) 2016 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/>.
|
||||
#
|
||||
# The source for the data is unknown.
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
|
||||
AddModuleDescription('national-days-de.pl', 'Special Days') if defined &AddModuleDescription;
|
||||
|
||||
our %SpecialDays = (
|
||||
'1-1' => 'Sudán, Día Nacional, Haití, Día de Independencia, Cuba, Día de Liberación',
|
||||
'1-4' => 'Myanmar, Día de Independencia',
|
||||
'1-9' => 'Islas de Mariana del Norte, Día Nacional',
|
||||
'1-26' => 'Australia, Día de Australia',
|
||||
'1-31' => 'Nauru, Día Nacional',
|
||||
'2-4' => 'Sri Lanka, Día de Conmemoración de la Independencia',
|
||||
'2-6' => 'Nueva Zelanda, Día de Waitangi',
|
||||
'2-7' => 'Grenada, Día de Independencia',
|
||||
'2-11' => 'Irán, Victoria de la Revolución Islámica en Irán',
|
||||
'2-16' => 'Lituania, Día de Independencia',
|
||||
'2-18' => 'Gambia, Día Nacional',
|
||||
'2-22' => 'Santa Lucía, Día Nacional',
|
||||
'2-23' => 'Guyana, Día Nacional, Brunei Darussalam, Día Nacional',
|
||||
'2-24' => 'Estonia, Día de Independencia',
|
||||
'2-25' => 'Kuwait, Día Nacional',
|
||||
'2-27' => 'República Dominicana, Día de Independencia',
|
||||
'3-3' => 'Bulgaria, Día Nacional',
|
||||
'3-6' => 'Ghana, Día de Independencia',
|
||||
'3-12' => 'Mauritius, Día Nacional',
|
||||
'3-17' => 'Irlanda, Día de San Patricio',
|
||||
'3-20' => 'Tunisia, Aniversario de la Independencia de la República de Tunisia',
|
||||
'3-21' => 'Namibia, Día de Independencia',
|
||||
'3-23' => 'Pakistán, Día Nacional',
|
||||
'3-25' => 'Grecia, Día de Independencia',
|
||||
'3-26' => 'Bangladesh, Día de Independencia',
|
||||
'4-4' => 'Senegal, Día de Independencia',
|
||||
'4-16' => 'Dinamarca, Cumpleaños de la Reina',
|
||||
'4-17' => 'Syria, Día Nacional',
|
||||
'4-18' => 'Zimbabwe, Día Nacional',
|
||||
'4-19' => 'Sierra Leona, Día de la República',
|
||||
'4-26' => 'Tanzania, Día de la Unión, Israel, Día de Independencia',
|
||||
'4-27' => 'República Federal de Yugoslavia, Día Nacional, Togo, Togolais Día Nacional, Suráfrica, Día de la Libertad',
|
||||
'4-30' => 'Los Países Bajos, Cumpleaños Oficial de Su Majestad la Reina Beatriz',
|
||||
'5-1' => 'Islas Marshall, Día Nacional',
|
||||
'5-3' => 'Polonia, Día Nacional',
|
||||
'5-9' => 'Unión Europea, Día de Europa',
|
||||
'5-14' => 'Paraguay, Día Nacional',
|
||||
'5-17' => 'Noruega, Día de la Constitución',
|
||||
'5-20' => 'Camerún, Día Nacional',
|
||||
'5-22' => 'Yemen, Día Nacional',
|
||||
'5-24' => 'Eritrea, Día de Independencia',
|
||||
'5-25' => 'Jordania, Día de Independencia, Argentina, Día Nacional',
|
||||
'5-26' => 'Georgia, Día Nacional',
|
||||
'5-28' => 'Etiopía, Día Nacional, Azerbaijan, Día Nacional',
|
||||
'6-1' => 'Samoa, Día de Independencia',
|
||||
'6-2' => 'Italia, Fundación de la República',
|
||||
'6-4' => 'Tonga, Día de Emancipación',
|
||||
'6-6' => 'Suecia, Día Nacional',
|
||||
'6-10' => 'Portugal, Día de Portugal, Día de Camões y Día de las Comunidades Portuguesas',
|
||||
'6-12' => 'Filipinas, Día de Independencia, Rusia, Día Nacional',
|
||||
'6-17' => 'Islandia, Día Nacional',
|
||||
'6-18' => 'Seychelles, Día Nacional',
|
||||
'6-23' => 'Luxemburgo, Día Nacional y Cumpleaños Oficial de H.R.H. el Gran Duque',
|
||||
'6-25' => 'Croacia, Día Nacional, Eslovenia, Día Nacional, Mozambique, Día de Independencia',
|
||||
'6-26' => 'Madagascar, Día Nacional',
|
||||
'6-27' => 'Djibouti, Día de Independencia',
|
||||
'6-30' => 'República Democrática del Congo, Día de Independencia',
|
||||
'7-1' => 'Burundi, Día Nacional, Canadá, Día de Canadá',
|
||||
'7-3' => 'Belarus, Día Nacional',
|
||||
'7-4' => 'Estados Unidos de América, Día de Independencia',
|
||||
'7-5' => 'Rwanda, Día de Liberación, Cape Verde, Día Nacional, Venezuela, Día Nacional',
|
||||
'7-6' => 'Malawi, Día Nacional, Comoros, Día Nacional',
|
||||
'7-7' => 'Nepal, Cumpleaños del Rey y Día Nacional, Islas Solomon, Día Nacional',
|
||||
'7-10' => 'Bahamas, Día de Independencia',
|
||||
'7-11' => 'Mongolia, Aniversario de la Revolución de las Gentes de Mongolia',
|
||||
'7-12' => 'Sao Tome & Principe, Día Nacional, Kiribati, Día Nacional',
|
||||
'7-14' => 'Francia, Día de Bastillas',
|
||||
'7-17' => 'Irak, Día Nacional',
|
||||
'7-20' => 'Colombia, Día Nacional',
|
||||
'7-21' => 'Bélgica, Ascenso del Rey Leopoldo I (1831)',
|
||||
'7-23' => 'Egipto, Aniversario de la Revolución',
|
||||
'7-26' => 'Liberia, Día Nacional, Maldives, Día Nacional',
|
||||
'7-28' => 'Perú, Día de Independencia',
|
||||
'7-30' => 'Vanuatu, Día de Independencia, Marruecos, Festival del Trono',
|
||||
'8-1' => 'Benin, Día Nacional, Suiza, Fundación de la Confederación Suiza',
|
||||
'8-4' => 'Islas Cook, Día Nacional',
|
||||
'8-5' => 'Jamaica, Día Nacional',
|
||||
'8-6' => 'Bolivia, Día de Independencia',
|
||||
'8-7' => 'Cote D\'Ivoire, Día Nacional',
|
||||
'8-9' => 'Singapur, Día Nacional',
|
||||
'8-10' => 'Ecuador, Día Nacional',
|
||||
'8-11' => 'Chad, Día Nacional',
|
||||
'8-15' => 'República de Korea, Día Nacional, Liechtenstein, Día Nacional, India, Día Nacional, República del Congo, Día de Independencia',
|
||||
'8-17' => 'Indonesia, Proclamación de Independencia, Gabon, Día Nacional',
|
||||
'8-19' => 'Afganistán, Día de Independencia',
|
||||
'8-20' => 'Hungaria, Día Nacional',
|
||||
'8-24' => 'Ucrania, Día Nacional',
|
||||
'8-25' => 'Uruguay, Día de Independencia',
|
||||
'8-27' => 'Moldova, Día Nacional',
|
||||
'8-31' => 'Kyrgyzstan, Día Nacional, Trinidad y Tobago, Día Nacional, Malasia, Día Nacional',
|
||||
'9-1' => 'Uzbekistán, Día Nacional, Eslovakia, Día de la Constitución',
|
||||
'9-2' => 'Vietnam, Día Nacional, Libyan Arab Jamahiriya, Día Nacional',
|
||||
'9-3' => 'Qatar, Día Nacional, San Marino, Día de Fundación Nacional',
|
||||
'9-6' => 'Swaziland, Día Nacional',
|
||||
'9-7' => 'Brazil, Día de Independencia',
|
||||
'9-8' => 'Andorra, Día Nacional, Antigua República de Macedonia Yugoslava, Día de Independencia',
|
||||
'9-9' => 'República Democrática de Korea, Día Nacional, Tajikistán, Día Nacional',
|
||||
'9-15' => 'Guatemala, Día de Independencia, Honduras, Día de Independencia, El Salvador, Día de Independencia, Costa Rica, Día de Independencia, Nicaragua, Día de Independencia',
|
||||
'9-16' => 'Papua Nueva Guinea, Día de Independencia, Méjico, Proclamación de Independencia',
|
||||
'9-18' => 'Chile, Día Nacional',
|
||||
'9-19' => 'St Kitts Nevis, Día de Independencia',
|
||||
'9-21' => 'Belize, Día Nacional, Malta, Día de Independencia, Armenia, Día Nacional',
|
||||
'9-22' => 'República de Mali, Proclamación de Independencia',
|
||||
'9-23' => 'Arabia Saudita, Día Nacional',
|
||||
'9-24' => 'Guinea Bissau, Día de Independencia',
|
||||
'9-30' => 'Botswana, Día Nacional',
|
||||
'10-1' => 'Guinea, Día de Independencia, China, Día Nacional, Palau, Día Nacional, Tuvalu, Día de Independencia, Cyprus, Día Nacional, Nigeria, Día Nacional',
|
||||
'10-3' => 'Alemania, Día Nacional',
|
||||
'10-4' => 'Lesotho, Día Nacional',
|
||||
'10-9' => 'Uganda, Día de Independencia',
|
||||
'10-10' => 'Fiji, Día Nacional',
|
||||
'10-12' => 'Guinea Ecuatorial, Día de Independencia, España, Día Nacional',
|
||||
'10-19' => 'Niue, Día Nacional',
|
||||
'10-22' => 'Holy See, Aniversario, Ministerio Pontífice de Su Santidad el Papa Juan Pablo II',
|
||||
'10-24' => 'Zambia, Día de Independencia',
|
||||
'10-26' => 'Austria, Día Nacional',
|
||||
'10-27' => 'Turkmenistán, Día Nacional, San Vincente y las Granadinas, Día Nacional',
|
||||
'10-28' => 'República Checa, Día Nacional',
|
||||
'10-29' => 'Turquía, Día de la República',
|
||||
'11-1' => 'Antigua y Barbuda, Día Nacional, Algeria, Aniversario de la Revolución',
|
||||
'11-3' => 'Panamá, Día Nacional, Dominica, Día Nacional, Estados Federados de Micronesia, Día Nacional',
|
||||
'11-9' => 'Cambodia, Día de Independencia',
|
||||
'11-11' => 'Angola, Día Nacional',
|
||||
'11-18' => 'Omán, Día Nacional, Latvia, Proclamación de Independencia',
|
||||
'11-19' => 'Mónaco, Día Nacional',
|
||||
'11-22' => 'Lebanon, Día de Independencia',
|
||||
'11-25' => 'Bosnia y Herzegovina, Día del Estado, Surinam, Día Nacional',
|
||||
'11-28' => 'Mauritania, Día Nacional, Albania, Día Nacional',
|
||||
'11-30' => 'Barbados, Día de Independencia',
|
||||
'12-1' => 'Rumania, Día Nacional, República de África Central, Día Nacional',
|
||||
'12-2' => 'Emiratos Árabes Unidos, Día Nacional, Laos, Día Nacional',
|
||||
'12-5' => 'Tailandia, Cumpleaños del Rey',
|
||||
'12-6' => 'Finlandia, Día de Independencia',
|
||||
'12-11' => 'Burkina Faso, Día Nacional',
|
||||
'12-12' => 'Kenya, Día de Jamhuri',
|
||||
'12-16' => 'Bahrain, Día Nacional, Kazakhstán, Día Nacional',
|
||||
'12-17' => 'Bután, Día Nacional',
|
||||
'12-18' => 'Nigeria, Día Nacional',
|
||||
'12-23' => 'Japón, Cumpleaños del Emperador',
|
||||
);
|
||||
@@ -691,6 +691,11 @@ Clustermap
|
||||
|
||||
Pages without a Cluster
|
||||
|
||||
################################################################################
|
||||
# modules/comment-div-wrapper.pl
|
||||
################################################################################
|
||||
Comments:
|
||||
|
||||
################################################################################
|
||||
# modules/commentcount.pl
|
||||
################################################################################
|
||||
@@ -698,11 +703,6 @@ Comments on
|
||||
|
||||
Comment on
|
||||
|
||||
################################################################################
|
||||
# modules/comment-div-wrapper.pl
|
||||
################################################################################
|
||||
Comments:
|
||||
|
||||
################################################################################
|
||||
# modules/compilation.pl
|
||||
################################################################################
|
||||
@@ -1239,7 +1239,7 @@ Test / Always enabled / Always disabled
|
||||
|
||||
Start
|
||||
|
||||
Bisecting proccess is already active.
|
||||
Bisection proccess is already active.
|
||||
|
||||
Stop
|
||||
|
||||
@@ -1280,11 +1280,6 @@ You linked more than %s times to the same domain. It would seem that only a spam
|
||||
|
||||
Namespaces
|
||||
|
||||
################################################################################
|
||||
# modules/nearlink-create.pl
|
||||
################################################################################
|
||||
(create locally)
|
||||
|
||||
################################################################################
|
||||
# modules/near-links.pl
|
||||
################################################################################
|
||||
@@ -1304,6 +1299,11 @@ EditNearLinks
|
||||
|
||||
The same page on other sites:
|
||||
|
||||
################################################################################
|
||||
# modules/nearlink-create.pl
|
||||
################################################################################
|
||||
(create locally)
|
||||
|
||||
################################################################################
|
||||
# modules/no-question-mark.pl
|
||||
################################################################################
|
||||
@@ -1662,5 +1662,4 @@ Edit %s.
|
||||
################################################################################
|
||||
Tags:
|
||||
|
||||
#
|
||||
END_OF_TRANSLATION
|
||||
|
||||
@@ -62,40 +62,44 @@ sub DoUpgrade {
|
||||
print GetHeader('', T('Upgrading Database')),
|
||||
$q->start_div({-class=>'content upgrade'});
|
||||
|
||||
if (IsFile($IndexFile)) {
|
||||
Unlink($IndexFile);
|
||||
if (-e $IndexFile) {
|
||||
unlink $IndexFile;
|
||||
}
|
||||
|
||||
print "<p>Renaming files...";
|
||||
|
||||
for my $ns ('', keys %InterSite) {
|
||||
next unless IsDir("$DataDir/$ns");
|
||||
next unless -d "$DataDir/$ns";
|
||||
print "<br />\n<strong>$ns</strong>" if $ns;
|
||||
for my $dirname ($PageDir, $KeepDir, $RefererDir, $JoinerDir, $JoinerEmailDir) {
|
||||
next unless $dirname;
|
||||
my $dir = $dirname; # copy in order not to modify the original
|
||||
$dir =~ s/^$DataDir/$DataDir\/$ns/ if $ns;
|
||||
for my $old (Glob("$dir/*/*"), Glob("$dir/*/.*")) {
|
||||
for my $old (bsd_glob("$dir/*/*"), bsd_glob("$dir/*/.*")) {
|
||||
next if $old =~ /\/\.\.?$/;
|
||||
print "<br />\n$old";
|
||||
my $oldname = $old;
|
||||
utf8::decode($oldname);
|
||||
print "<br />\n$oldname";
|
||||
my $new = $old;
|
||||
$new =~ s!/([A-Z]|other)/!/!;
|
||||
if ($old eq $new) {
|
||||
print " does not fit the pattern!";
|
||||
} elsif (not Rename($old, $new)) {
|
||||
print " → $new failed!";
|
||||
} elsif (not rename $old, $new) {
|
||||
my $newname = $new;
|
||||
utf8::decode($newname);
|
||||
print " → $newname failed!";
|
||||
}
|
||||
}
|
||||
for my $subdir (grep(/\/([A-Z]|other)$/, Glob("$dir/*"), Glob("$dir/.*"))) {
|
||||
for my $subdir (grep(/\/([A-Z]|other)$/, bsd_glob("$dir/*"), bsd_glob("$dir/.*"))) {
|
||||
next if substr($subdir, -2) eq '/.' or substr($subdir, -3) eq '/..';
|
||||
RemoveDir($subdir); # ignore errors
|
||||
rmdir $subdir; # ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print $q->end_p();
|
||||
|
||||
if (Rename("$ModuleDir/upgrade.pl", "$ModuleDir/upgrade.done")) {
|
||||
if (rename "$ModuleDir/upgrade.pl", "$ModuleDir/upgrade.done") {
|
||||
print $q->p(T("Upgrade complete."))
|
||||
} else {
|
||||
print $q->p(T("Upgrade complete. Please remove $ModuleDir/upgade.pl, now."))
|
||||
|
||||
@@ -60,7 +60,7 @@ for (my $i=0; $q->param("fileToUpload$i"); $i++) {
|
||||
|
||||
$curFilename = $name . $extension;
|
||||
|
||||
while (IsFile("$uploadDir/$curFilename")) { # keep adding random characters until we get unique filename
|
||||
while (-e "$uploadDir/$curFilename") { # keep adding random characters until we get unique filename
|
||||
squeak 'Error: Cannot save file with such filename' if length $curFilename >= 150; # cannot find available filename after so many attempts
|
||||
$name .= $additionalChars[rand @additionalChars];
|
||||
$curFilename = $name . $extension;
|
||||
@@ -71,12 +71,14 @@ for (my $i=0; $q->param("fileToUpload$i"); $i++) {
|
||||
} else {
|
||||
squeak 'Error: Filename contains invalid characters'; # this should not happen
|
||||
}
|
||||
open(my $LOGFILE, '>>', encode_utf8($logFile)) or squeak "$!";
|
||||
|
||||
open(my $LOGFILE, '>>', $logFile) or squeak "$!";
|
||||
print $LOGFILE $q->param("key") . ' ' . $ENV{REMOTE_ADDR} . ' ' . $curFilename . "\n";
|
||||
close $LOGFILE;
|
||||
|
||||
my $uploadFileHandle = $q->upload("fileToUpload$i");
|
||||
open(my $UPLOADFILE, '>', encode_utf8("$uploadDir/$curFilename")) or squeak "$!";
|
||||
|
||||
open(my $UPLOADFILE, '>', "$uploadDir/$curFilename") or squeak "$!";
|
||||
binmode $UPLOADFILE;
|
||||
while (<$uploadFileHandle>) {
|
||||
print $UPLOADFILE;
|
||||
|
||||
@@ -394,7 +394,7 @@ sub propfind {
|
||||
|
||||
sub propfind_data {
|
||||
my %data = ();
|
||||
my $update = Modified($OddMuse::WebDavCache);
|
||||
my $update = (stat($OddMuse::WebDavCache))[9];
|
||||
if ($update and $OddMuse::LastUpdate == $update) {
|
||||
my $data = OddMuse::ReadFileOrDie($OddMuse::WebDavCache);
|
||||
map {
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
#!/usr/bin/env perl6
|
||||
use Net::IRC::Bot;
|
||||
use Net::IRC::Modules::Autoident;
|
||||
use Net::IRC::Modules::Tell;
|
||||
use Net::IRC::CommandHandler;
|
||||
|
||||
sub wikiLink($page is copy) {
|
||||
$page ~~ s:g/\s/_/; # quick and dirty
|
||||
return “https://oddmuse.org/wiki/$page”;
|
||||
}
|
||||
|
||||
class Intermap {
|
||||
has $.intermapLink is rw = ‘https://oddmuse.org/wiki/Local_Intermap?raw=1’;
|
||||
has %!intermap;
|
||||
|
||||
method update {
|
||||
# TODO https breaks HTTP::UserAgent, workaround with curl
|
||||
my $proc = run(‘curl’, $!intermapLink, :out);
|
||||
my $text = $proc.out.slurp-rest;
|
||||
$proc.out.close; # RT #126561
|
||||
return False unless $proc;
|
||||
for $text ~~ m:global〈 ^^ \h+ $<name>=\S+ \s+ $<value>=.+? $$ 〉 {
|
||||
%!intermap{~$_<name>} = ~$_<value>; # TODO map!
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
method said ($e) {
|
||||
self.update if not %!intermap or $e.what ~~ / ‘update intermap’ /; # lazy init
|
||||
for $e.what ~~ m:global〈 $<name>=<-[\s :]>+ ‘:’ $<value>=\S+ 〉 { # quick and dirty
|
||||
next unless %!intermap{.<name>}:exists;
|
||||
my $link = %!intermap{~.<name>};
|
||||
my $replacement = $_<value>;
|
||||
$link ~~ s{ \%s | $ } = $replacement;
|
||||
$e.msg: $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Pages {
|
||||
method said ($e) {
|
||||
for $e.what ~~ m:global〈 ‘[[’ $<page>=<-[ \] ]>+ ‘]]’ 〉 { # quick and dirty
|
||||
$e.msg: wikiLink ~.<page>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Sorry {
|
||||
has $.answers is rw = « ‘I'm so sorry!’ ‘Please forgive me!’
|
||||
‘I should have done better!’
|
||||
‘I promise that it won't happen again!’ »;
|
||||
|
||||
method said ($e) {
|
||||
if $e.what ~~ / ^ "{ $e.bot.nick }" [‘:’|‘,’] / {
|
||||
$e.msg: $!answers.pick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RecentChanges {
|
||||
has $.delay is rw = 30;
|
||||
has $.url is rw = ‘https://oddmuse.org/wiki?action=rss;all=0;showedit=0;rollback=1;from=’;
|
||||
has $!last = time;
|
||||
|
||||
method joined ($e) {
|
||||
start loop {
|
||||
sleep $!delay;
|
||||
self.process: $e;
|
||||
}
|
||||
}
|
||||
|
||||
method process ($e) {
|
||||
my $newLast = time;
|
||||
# TODO https breaks HTTP::UserAgent, workaround with curl
|
||||
my $proc = run(‘curl’, $!url ~ $!last, :out);
|
||||
my $xml = $proc.out.slurp-rest;
|
||||
$proc.out.close; # RT #126561
|
||||
return False unless $proc;
|
||||
$!last = $newLast;
|
||||
|
||||
use XML;
|
||||
for from-xml($xml).elements(:TAG<item>, :RECURSE) {
|
||||
my $title = ~.elements(:TAG<title>, :SINGLE).contents;
|
||||
my $desc = ~.elements(:TAG<description>, :SINGLE).contents;
|
||||
my $author = ~.elements(:TAG<dc:contributor>, :SINGLE).contents;
|
||||
$e.msg: “Wiki: [$title] <$author> – $desc ({wikiLink $title})”;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RecentCommits {
|
||||
has $.delay is rw = 30;
|
||||
has $.url = ‘https://github.com/kensanata/oddmuse.git’;
|
||||
has $.repo = ‘repo’;
|
||||
|
||||
method joined ($e) {
|
||||
start {
|
||||
if $!repo.IO !~~ :e {
|
||||
fail unless run(‘git’, ‘clone’, $!url, $!repo);
|
||||
}
|
||||
loop {
|
||||
sleep $!delay;
|
||||
self.process: $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method process ($e) {
|
||||
my $proc1 = run(‘git’, ‘--git-dir’, $!repo ~ ‘/.git’, ‘fetch’) ;
|
||||
return False unless $proc1;
|
||||
my $proc2 = run(‘git’, ‘--git-dir’, $!repo ~ ‘/.git’, ‘log’,
|
||||
‘--pretty=format:Commit: %s (https://github.com/kensanata/oddmuse/commit/%h)’,
|
||||
‘...origin’, :out);
|
||||
$e.msg: $_ for $proc2.out;
|
||||
$proc2.out.close; # RT #126561
|
||||
return False unless $proc2;
|
||||
|
||||
run(‘git’, ‘--git-dir’, $!repo ~ ‘/.git’, ‘merge’, ‘-q’);
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Backlog does Net::IRC::CommandHandler {
|
||||
has $.limit is rw = 60 * 60 * 48;
|
||||
has $.delay is rw = 30; # seconds before file deletion
|
||||
has $.path is rw = ‘backlogs/’;
|
||||
has $.link is rw = ‘http://alexine.oddmuse.org/backlogs/’; # TODO https
|
||||
has %.messages = ();
|
||||
|
||||
multi method said ($e) {
|
||||
%!messages{$e.where} = [] unless %!messages{$e.where}:exists;
|
||||
%!messages{$e.where}.push: { ‘when’ => time, ‘who’ => $e.who<nick>, ‘what’ => $e.what };
|
||||
self.clean;
|
||||
}
|
||||
|
||||
method clean {
|
||||
for %!messages.values -> $value { # each channel
|
||||
for $value.kv -> $index, $elem { # each message
|
||||
last if time - $elem<when> < $!limit;
|
||||
LAST { $value.splice(0, $index) } # at least one message will be kept
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method backlog ($e, $match) is cmd {
|
||||
self.clean;
|
||||
mkdir $!path unless $!path.IO ~~ :d;
|
||||
my $name = ^2**128 .pick.base(36);
|
||||
my $fh = open “$!path/$name”, :w;
|
||||
$fh.say(“<{.<who>}> {.<what>}”) for @(%!messages{$e.where});
|
||||
$fh.close;
|
||||
$e.msg: “$!link$name”;
|
||||
Promise.in($!delay).then: { unlink “$!path/$name” };
|
||||
}
|
||||
|
||||
method forget ($e, $match) is cmd {
|
||||
%!messages{$e.where} = [];
|
||||
$e.msg: ‘OK, we didn't have this conversation.’;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub MAIN(Str :$nick = ‘alexine’, Str :$password is copy = ‘’, Str :$channel = ‘#oddmuse’) {
|
||||
$password = prompt ‘Nickserv password: ’ unless $password;
|
||||
Net::IRC::Bot.new(
|
||||
nick => $nick,
|
||||
username => $nick,
|
||||
realname => $nick,
|
||||
server => ‘irc.freenode.org’,
|
||||
channels => [ $channel ],
|
||||
debug => True,
|
||||
|
||||
modules => (
|
||||
Intermap.new(),
|
||||
Pages.new(),
|
||||
Sorry.new(),
|
||||
RecentChanges.new(),
|
||||
#RecentCommits.new(),
|
||||
Backlog.new(prefix => ‘.’),
|
||||
Net::IRC::Modules::Tell.new(prefix => ‘.’),
|
||||
Net::IRC::Modules::Autoident.new(password => $password),
|
||||
),
|
||||
).run;
|
||||
}
|
||||
@@ -21,7 +21,6 @@ TEST_LOG="$WORKING_DIRECTORY/log"
|
||||
ODDMUSE_TEST_LOCATION="$WORKING_DIRECTORY/oddmuse-for-tests/"
|
||||
GIT_LOCATION="$WORKING_DIRECTORY/"
|
||||
LAST_COMMIT_FILE="$WORKING_DIRECTORY/last_commit"
|
||||
LAST_STATUS_FILE="$WORKING_DIRECTORY/last_status"
|
||||
FIRST_TESTABLE_COMMIT='1c0801bd6ca23de71c7c360a18a648c2b953f1da'
|
||||
RESULT_FILE="$WORKING_DIRECTORY/output"
|
||||
WIKIPUT='../config/oddmuse/scripts/cli/wikiput'
|
||||
@@ -50,9 +49,7 @@ while :; do
|
||||
"${git[@]}" reset --hard origin/master # starting our search from the last commit
|
||||
|
||||
[[ -f $LAST_COMMIT_FILE ]] || echo "$FIRST_TESTABLE_COMMIT" > "$LAST_COMMIT_FILE"
|
||||
[[ -f $LAST_STATUS_FILE ]] || echo 0 > "$LAST_STATUS_FILE"
|
||||
lastCommit=$(< "$LAST_COMMIT_FILE")
|
||||
lastStatus=$(< "$LAST_STATUS_FILE")
|
||||
|
||||
logOutput=$("${git[@]}" log --topo-order --pretty=oneline | grep --before 1 -m 1 "^$lastCommit")
|
||||
(($(wc -l <<< "$logOutput") < 2)) && exit 0 # No more commits to process, good!
|
||||
@@ -68,16 +65,14 @@ while :; do
|
||||
printf "%s\n" "$output" > "$RESULT_FILE"
|
||||
# echo "Duration: $((duration/60))m$((duration%60))s Status: $status" >> "$RESULT_FILE"
|
||||
printf "%s\n" "$currentCommit" > "$LAST_COMMIT_FILE"
|
||||
printf "%s\n" "$status" > "$LAST_STATUS_FILE"
|
||||
|
||||
"${gitRepo[@]}" add -- "$(readlink -m -- "$RESULT_FILE")" "$(readlink -m -- "$LAST_COMMIT_FILE")" "$(readlink -m -- "$LAST_STATUS_FILE")"
|
||||
"${gitRepo[@]}" add -- "$(readlink -m -- "$RESULT_FILE")" "$(readlink -m -- "$LAST_COMMIT_FILE")"
|
||||
"${gitRepo[@]}" commit -m "Test status at $currentCommit (automated commit)"
|
||||
|
||||
"${gitRepo[@]}" push
|
||||
|
||||
if (( status == 0 )); then
|
||||
(( lastStatus == 0 )) && minor='-m' || minor='' # we will use unquoted variable on purpose
|
||||
"$WIKIPUT" $minor -u "$USER_NAME" -s 'Tests PASSED' -z 'ham' "$WIKI_LOCATION/$STATUS_PAGE" <<< $'TEST STATUS – **OK**\n\n'"Commit:${currentCommit:0:7} – see [[$OUT_PAGE|test log]]"
|
||||
"$WIKIPUT" -m -u "$USER_NAME" -s 'Tests PASSED' -z 'ham' "$WIKI_LOCATION/$STATUS_PAGE" <<< $'TEST STATUS – **OK**\n\n'"Commit:${currentCommit:0:7} – see [[$OUT_PAGE|test log]]"
|
||||
else
|
||||
"$WIKIPUT" -u "$USER_NAME" -s 'Tests FAILED' -z 'ham' "$WIKI_LOCATION/$STATUS_PAGE" <<< $'TEST STATUS – **FAIL**\n\n'"Commit:${currentCommit:0:7} – see [[$OUT_PAGE|test log]]"
|
||||
fi
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
use Mojolicious::Lite;
|
||||
use Mojo::Cache;
|
||||
use Archive::Tar;
|
||||
use File::Basename;
|
||||
use Encode qw(decode_utf8);
|
||||
my $dir = "/home/alex/oddmuse.org/releases";
|
||||
my $cache = Mojo::Cache->new(max_keys => 50);
|
||||
|
||||
get '/' => sub {
|
||||
my $c = shift;
|
||||
my @tarballs = sort map {
|
||||
my ($name, $path, $suffix) = fileparse($_, '.tar.gz');
|
||||
$name;
|
||||
} <$dir/*.tar.gz>;
|
||||
$c->render(template => 'index', tarballs => \@tarballs);
|
||||
} => 'main';
|
||||
|
||||
get '/#tarball' => sub {
|
||||
my $c = shift;
|
||||
my $tarball = $c->param('tarball');
|
||||
my $files = $cache->get($tarball);
|
||||
if (not $files) {
|
||||
$c->app->log->info("Reading $tarball.tar.gz");
|
||||
my $tar = Archive::Tar->new;
|
||||
$tar->read("$dir/$tarball.tar.gz");
|
||||
my @files = sort grep /./, map {
|
||||
my @e = split('/', $_->name);
|
||||
$e[1];
|
||||
} $tar->get_files();
|
||||
$files = \@files;
|
||||
$cache->set($tarball => $files);
|
||||
}
|
||||
$c->render(template => 'release', tarball=> $tarball, files => $files);
|
||||
} => 'release';
|
||||
|
||||
get '/#tarball/#file' => sub {
|
||||
my $c = shift;
|
||||
my $tarball = $c->param('tarball');
|
||||
my $file = $c->param('file');
|
||||
my $text = $cache->get("$tarball/$file");
|
||||
if (not $text) {
|
||||
$c->app->log->info("Reading $tarball/$file");
|
||||
my $tar = Archive::Tar->new;
|
||||
$tar->read("$dir/$tarball.tar.gz");
|
||||
$text = decode_utf8($tar->get_content("$tarball/$file"));
|
||||
$cache->set("$tarball/$file" => $text);
|
||||
}
|
||||
$c->render(template => 'file', format => 'txt', content => $text);
|
||||
} => 'file';
|
||||
|
||||
app->start;
|
||||
|
||||
__DATA__
|
||||
|
||||
@@ index.html.ep
|
||||
% layout 'default';
|
||||
% title 'Oddmuse Releases';
|
||||
<h1>Oddmuse Releases</h1>
|
||||
|
||||
<p>Welcome! This is where you get access to tarballs and files in released
|
||||
versions of Oddmuse.</p>
|
||||
|
||||
<ul>
|
||||
% for my $tarball (@$tarballs) {
|
||||
<li>
|
||||
<a href="https://oddmuse.org/releases/<%= $tarball %>.tar.gz"><%= $tarball %>.tar.gz</a>
|
||||
(files for <%= link_to release => {tarball => $tarball} => begin %>\
|
||||
<%= $tarball =%><%= end %>)
|
||||
</li>
|
||||
% }
|
||||
</ul>
|
||||
|
||||
|
||||
@@ release.html.ep
|
||||
% layout 'default';
|
||||
% title 'Release';
|
||||
<h1>Files for <%= $tarball %></h1>
|
||||
<p>
|
||||
Back to the list of <%= link_to 'releases' => 'main' %>.
|
||||
Remember,
|
||||
%= link_to file => {file => 'wiki.pl'} => begin
|
||||
wiki.pl
|
||||
% end
|
||||
is the main script.
|
||||
|
||||
<ul>
|
||||
% for my $file (@$files) {
|
||||
<li>
|
||||
%= link_to file => {file => $file} => begin
|
||||
%= $file
|
||||
% end
|
||||
% }
|
||||
</ul>
|
||||
|
||||
@@ file.txt.ep
|
||||
%layout 'file';
|
||||
<%== $content %>
|
||||
|
||||
@@ layouts/default.html.ep
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><%= title %></title>
|
||||
%= stylesheet '/tarballs.css'
|
||||
%= stylesheet begin
|
||||
body {
|
||||
padding: 1em;
|
||||
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
|
||||
}
|
||||
% end
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body>
|
||||
<%= content %>
|
||||
<hr>
|
||||
<p>
|
||||
<a href="https://oddmuse.org/">Oddmuse</a> 
|
||||
<%= link_to 'Releases' => 'main' %> 
|
||||
<a href="https://alexschroeder.ch/wiki/Contact">Alex Schroeder</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -85,7 +85,7 @@ Examples
|
||||
--------
|
||||
|
||||
Journal pages only:
|
||||
$0 --match '^\\d{4}-\\d{2}-\\d{2}'
|
||||
$0 --match '^\d{4}-\d{2}-\d{2}'
|
||||
|
||||
Skip comment pages:
|
||||
$0 --match '^(?!Comments_on_)'
|
||||
|
||||
37
server.pl
37
server.pl
@@ -1,49 +1,36 @@
|
||||
#!/usr/bin/env perl
|
||||
#! /usr/bin/env perl
|
||||
|
||||
# Copyright (C) 2015-2016 Alex Schroeder <alex@gnu.org>
|
||||
# This script only works with a version of Mojolicious::Plugin::CGI better than
|
||||
# the official 0.23. One version would be my fork:
|
||||
# https://github.com/kensanata/mojolicious-plugin-cgi
|
||||
|
||||
# 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/>.
|
||||
# If you use the fork, you might want to simply add its lib directory to your
|
||||
# libraries instead of installing it?
|
||||
|
||||
# use lib '/Users/alex/src/mojolicious-plugin-cgi/lib';
|
||||
|
||||
use Mojolicious::Lite;
|
||||
|
||||
# This needs to be in a different section, sometimes?
|
||||
plugin CGI => {
|
||||
support_semicolon_in_query_string => 1,
|
||||
};
|
||||
|
||||
|
||||
plugin CGI => {
|
||||
route => '/wiki',
|
||||
# We need this for older versions of Mojolicious::Plugin::CGI
|
||||
script => 'wiki.pl',
|
||||
run => \&OddMuse::DoWikiRequest,
|
||||
before => sub {
|
||||
no warnings;
|
||||
$OddMuse::RunCGI = 0;
|
||||
# The default data directory is determined by the environment variable
|
||||
# WikiDataDir and falls back to the following
|
||||
# $OddMuse::DataDir = '/tmp/oddmuse';
|
||||
use warnings;
|
||||
$OddMuse::DataDir = '/tmp/oddmuse';
|
||||
require 'wiki.pl' unless defined &OddMuse::DoWikiRequest;
|
||||
},
|
||||
env => {},
|
||||
# path to where STDERR from cgi script goes
|
||||
errlog => ($ENV{WikiDataDir} || '/tmp/oddmuse')
|
||||
. "/wiki.log",
|
||||
errlog => 'wiki.log', # path to where STDERR from cgi script goes
|
||||
};
|
||||
|
||||
get '/' => sub {
|
||||
my $self = shift;
|
||||
$self->redirect_to('/wiki');
|
||||
};
|
||||
|
||||
|
||||
app->start;
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#! /usr/bin/perl
|
||||
my $usage = q{expire-pans.pl
|
||||
Usage: this script expects to be run in a directory with a spammer.log file as
|
||||
produced by the LogBannedContent module.
|
||||
<https://oddmuse.org/wiki/LogBannedContent_Module>
|
||||
|
||||
In the same directory, it expects at least one of BannedContent, BannedHosts or
|
||||
BannedRegexps. It will work on all three, though. These must be the raw text
|
||||
files of the wiki.
|
||||
|
||||
Here's how you might get them from Emacs Wiki, for example.
|
||||
|
||||
wget https://www.emacswiki.org/spammer.log
|
||||
wget https://www.emacswiki.org/emacs/raw/BannedContent
|
||||
wget https://www.emacswiki.org/emacs/raw/BannedHosts
|
||||
wget https://www.emacswiki.org/emacs/raw/BannedRegexps
|
||||
|
||||
};
|
||||
|
||||
die $usage if ! -f 'spammer.log'
|
||||
|| !(-f 'BannedContent' || -f 'BannedHosts' || -f 'BannedRegexps');
|
||||
|
||||
my $fh;
|
||||
my @bans;
|
||||
|
||||
warn "Reading spammer.log...\n";
|
||||
open($fh, '<:utf8', 'spammer.log') or die "Cannot read spammer.log: $!";
|
||||
for my $line (<$fh>) {
|
||||
push(@bans, $line);
|
||||
}
|
||||
close($fh);
|
||||
|
||||
for my $file (qw(BannedContent BannedHosts BannedRegexps)) {
|
||||
warn "Reading $file...\n";
|
||||
if (open($fh, '<:utf8', $file)) {
|
||||
my $count = 0;
|
||||
my $used = 0;
|
||||
my @out;
|
||||
for my $line (<$fh>) {
|
||||
if ($line =~ m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/) {
|
||||
$count++;
|
||||
my ($regexp, $comment) = ($1, $4);
|
||||
foreach my $ban (@bans) {
|
||||
if (index($ban, $regexp) > -1) {
|
||||
$used++;
|
||||
push(@out, $line);
|
||||
last;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
push(@out, $line);
|
||||
}
|
||||
}
|
||||
close ($fh);
|
||||
warn "$count regular expressions checked\n";
|
||||
warn "$used regular expressions were used\n";
|
||||
warn "Writing $file-new...\n";
|
||||
open ($fh, '>:utf8', "$file-new")
|
||||
or die "Cannot write $file-new: $!";
|
||||
print $fh join("", @out);
|
||||
close $fh;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/env perl
|
||||
# Copyright (C) 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 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 warnings;
|
||||
use version;
|
||||
|
||||
my $dir = shift;
|
||||
|
||||
unless (-d $dir) {
|
||||
die <<"EOT";
|
||||
Usage: $0 DIR [RELEASE]
|
||||
|
||||
DIR is the directory where the tarballs for each tag are created.
|
||||
It must already exist.
|
||||
|
||||
If an optional RELEASE such as 2.3.0 is provided, then only tags
|
||||
equal or greater than 2.3.0 will be considered. The default is 2.3.0.
|
||||
EOT
|
||||
}
|
||||
|
||||
my $min = version->parse(shift || "2.3.0");
|
||||
|
||||
my @tags = grep { /\d+\.\d+\.\d+/ and version->parse($_) > $min }
|
||||
split(/\n/, qx{git tag --list});
|
||||
|
||||
unless (@tags) {
|
||||
die "git tag --list produced no list of tags\n";
|
||||
}
|
||||
|
||||
for my $tag (@tags) {
|
||||
my $fname = "$dir/oddmuse-$tag.tar.gz";
|
||||
unless (-f $fname) {
|
||||
|
||||
system("git", "checkout", $tag) == 0
|
||||
or die "Failed to git checkout $tag\n";
|
||||
system("make", "prepare") == 0
|
||||
or die "Failed to run make prepare for tag $tag\n";
|
||||
system("mv", "build", "oddmuse-$tag") == 0
|
||||
or die "Failed to rename the build directory to oddmuse-$tag\n";
|
||||
system("tar", "czf", "oddmuse-$tag.tar.gz", "oddmuse-$tag") == 0
|
||||
or die "Failed to build tarball oddmuse-$tag.tar.gz\n";
|
||||
system("mv", "oddmuse-$tag.tar.gz", $fname) == 0
|
||||
or die "Failed to move the tarball oddmuse-$tag.tar.gz\n";
|
||||
system("rm", "-rf", "oddmuse-$tag") == 0
|
||||
or die "Failed to remove the directory oddmuse-$tag\n";
|
||||
}
|
||||
}
|
||||
|
||||
system("git", "checkout", "master") == 0
|
||||
or die "Failed to git checkout master\n";
|
||||
45
t/atom.t
45
t/atom.t
@@ -22,9 +22,45 @@ use XML::Atom::Client;
|
||||
use XML::Atom::Entry;
|
||||
use XML::Atom::Person;
|
||||
|
||||
sub random_port {
|
||||
use Errno qw( EADDRINUSE );
|
||||
use Socket qw( PF_INET SOCK_STREAM INADDR_ANY sockaddr_in );
|
||||
|
||||
my $family = PF_INET;
|
||||
my $type = SOCK_STREAM;
|
||||
my $proto = getprotobyname('tcp') or die "getprotobyname: $!";
|
||||
my $host = INADDR_ANY; # Use inet_aton for a specific interface
|
||||
|
||||
for my $i (1..3) {
|
||||
my $port = 1024 + int(rand(65535 - 1024));
|
||||
socket(my $sock, $family, $type, $proto) or die "socket: $!";
|
||||
my $name = sockaddr_in($port, $host) or die "sockaddr_in: $!";
|
||||
setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
bind($sock, $name)
|
||||
and close($sock)
|
||||
and return $port;
|
||||
die "bind: $!" if $! != EADDRINUSE;
|
||||
print "Port $port in use, retrying...\n";
|
||||
}
|
||||
die "Tried 3 random ports and failed.\n"
|
||||
}
|
||||
|
||||
my $port = random_port();
|
||||
$ScriptName = "http://localhost:$port";
|
||||
|
||||
AppendStringToFile($ConfigFile, "\$ScriptName = $ScriptName;\n");
|
||||
|
||||
add_module('atom.pl');
|
||||
|
||||
start_server();
|
||||
# Fork a test server with the new config file and the module
|
||||
my $pid = fork();
|
||||
if (!defined $pid) {
|
||||
die "Cannot fork: $!";
|
||||
} elsif ($pid == 0) {
|
||||
use Config;
|
||||
my $secure_perl_path = $Config{perlpath};
|
||||
exec($secure_perl_path, "stuff/server.pl", "wiki.pl", $port) or die "Cannot exec: $!";
|
||||
}
|
||||
|
||||
# Give the child time to start
|
||||
sleep 1;
|
||||
@@ -33,7 +69,7 @@ sleep 1;
|
||||
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->content, qr/\batom\.pl/, "The server has the atom extension installed");
|
||||
like($response->content, qr/\batom\.pl/, "The has the atom extension installed");
|
||||
|
||||
# Testing the Atom API
|
||||
my $api = XML::Atom::Client->new;
|
||||
@@ -143,3 +179,8 @@ sub trim {
|
||||
}
|
||||
ok(trim($result->content->body) eq ("<p>" . trim($content) . '</p>'), 'verify content');
|
||||
ok($result->author->name eq $username, 'verify author');
|
||||
|
||||
END {
|
||||
# kill server
|
||||
kill 'KILL', $pid;
|
||||
}
|
||||
|
||||
10
t/css.t
10
t/css.t
@@ -20,13 +20,13 @@ AppendStringToFile($ConfigFile, "\$StyleSheetPage = 'css';\n");
|
||||
|
||||
# Default
|
||||
xpath_test(get_page('HomePage'),
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="https://oddmuse.org/default.css"]');
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]');
|
||||
|
||||
# StyleSheetPage
|
||||
update_page('css', "em { font-weight: bold; }", 'some css', 0, 1);
|
||||
$page = get_page('HomePage');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="https://oddmuse.org/default.css"]');
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]');
|
||||
xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]');
|
||||
|
||||
@@ -34,7 +34,7 @@ xpath_test($page,
|
||||
AppendStringToFile($ConfigFile, "\$StyleSheet = 'http://example.org/test.css';\n");
|
||||
$page = get_page('HomePage');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="https://oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]');
|
||||
xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://example.org/test.css"]');
|
||||
@@ -43,7 +43,7 @@ xpath_test($page,
|
||||
AppendStringToFile($ConfigFile, "\$StyleSheet = ['http://example.org/test.css', 'http://example.org/another.css'];\n");
|
||||
$page = get_page('HomePage');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="https://oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]');
|
||||
xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://example.org/test.css"]');
|
||||
@@ -53,7 +53,7 @@ xpath_test($page,
|
||||
# Parameter
|
||||
$page = get_page('action=browse id=HomePage css=http://example.org/my.css');
|
||||
negative_xpath_test($page,
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="https://oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://www.oddmuse.org/default.css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://localhost/wiki.pl?action=browse;id=css;raw=1;mime-type=text/css"]',
|
||||
'//link[@type="text/css"][@rel="stylesheet"][@href="http://example.org/test.css"]');
|
||||
xpath_test($page,
|
||||
|
||||
2
t/git.t
2
t/git.t
@@ -23,8 +23,6 @@ SKIP: {
|
||||
|
||||
add_module('git.pl');
|
||||
|
||||
$ENV{LANG} = "en_US.UTF-8"; # test relies on English output
|
||||
|
||||
if (qx($GitBinary --version) !~ /git version/) {
|
||||
skip "$GitBinary not found", 16;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# Copyright (C) 2015 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2016 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
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 6;
|
||||
use Test::More tests => 1;
|
||||
|
||||
use File::Basename;
|
||||
|
||||
@@ -24,10 +23,9 @@ require("$ModuleDir/load-lang.pl");
|
||||
|
||||
my %choosable_translations = reverse %TranslationsLibrary;
|
||||
my @missing = (); # missing in load-lang.pl
|
||||
my $count = 0;
|
||||
|
||||
foreach (bsd_glob("modules/translations/*.p[ml]")) {
|
||||
my $filename = fileparse($_);
|
||||
$count++;
|
||||
next if exists $choosable_translations{$filename};
|
||||
next if $filename eq 'new-utf8.pl'; # placeholder
|
||||
next if $filename =~ /^month-names/; # month names are located in translations/ for whatever reason
|
||||
@@ -39,20 +37,4 @@ unless (ok(@missing == 0, 'All translations are listed')) {
|
||||
diag("$_ is not listed in load-lang.pl") for @missing;
|
||||
}
|
||||
|
||||
unless (ok($count > 0, "$count translations were found")) {
|
||||
diag("if 0 then the \$LoadLanguageDir was not set correctly");
|
||||
}
|
||||
|
||||
test_page(get_page('Test'), 'Edit this page');
|
||||
|
||||
CreateDir($LoadLanguageDir);
|
||||
WriteStringToFile("$LoadLanguageDir/german-utf8.pl", ReadFileOrDie("modules/translations/german-utf8.pl"));
|
||||
# AppendStringToFile("$LoadLanguageDir/german-utf8.pl", "warn 'reading german-utf8.pl';\n");
|
||||
|
||||
$ENV{'HTTP_ACCEPT_LANGUAGE'} = 'de-ch,de;q=0.7,en;q=0.3';
|
||||
|
||||
test_page(get_page('action=version'), 'load-lang.pl');
|
||||
|
||||
my $page = get_page('Test');
|
||||
test_page($page, 'Diese Seite bearbeiten');
|
||||
test_page_negative($page, 'Edit this page');
|
||||
# TODO test the module itself
|
||||
|
||||
70
t/meta.t
70
t/meta.t
@@ -1,5 +1,5 @@
|
||||
# Copyright (C) 2015-2016 Alex Schroeder <alex@gnu.com>
|
||||
# Copyright (C) 2015 Alex Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2015 Alex Schroeder <alex@gnu.com>
|
||||
#
|
||||
# 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
|
||||
@@ -20,14 +20,13 @@ use utf8;
|
||||
|
||||
package OddMuse;
|
||||
require 't/test.pl';
|
||||
use Test::More tests => 29;
|
||||
use Test::More tests => 11;
|
||||
use File::Basename;
|
||||
use Pod::Strip;
|
||||
use Pod::Simple::TextContent;
|
||||
|
||||
my @modules = grep { $_ ne 'modules/404handler.pl' } <modules/*.pl>;
|
||||
my @other = 'wiki.pl';
|
||||
my %text = (map { $_ => ReadFileOrDie($_) } @modules, @other);
|
||||
my @badModules;
|
||||
|
||||
@badModules = grep { (stat $_)[2] != oct '100644' } @modules;
|
||||
@@ -36,19 +35,20 @@ unless (ok(@badModules == 0, 'Consistent file permissions of modules')) {
|
||||
diag("▶▶▶ Use this command to fix it: chmod 644 @badModules");
|
||||
}
|
||||
|
||||
@badModules = grep { $text{$_} !~ / ^ use \s+ strict; /mx } @modules;
|
||||
@badModules = grep { ReadFile($_) !~ / ^ use \s+ strict; /mx } @modules;
|
||||
unless (ok(@badModules == 0, '"use strict;" in modules')) {
|
||||
diag(qq{$_ has no "use strict;"}) for @badModules;
|
||||
}
|
||||
|
||||
@badModules = grep { $text{$_} !~ / ^ use \s+ v5\.10; /mx } @modules;
|
||||
@badModules = grep { ReadFile($_) !~ / ^ use \s+ v5\.10; /mx } @modules;
|
||||
unless (ok(@badModules == 0, '"use v5.10;" in modules')) {
|
||||
diag(qq{$_ has no "use v5.10;"}) for @badModules;
|
||||
diag(q{Minimum perl version for the core is v5.10, it seems like there is no reason not to have "use v5.10;" everywhere else.});
|
||||
}
|
||||
|
||||
@badModules = grep {
|
||||
my $code = $text{$_};
|
||||
my $code = ReadFile($_);
|
||||
# warn "Looking at $_: " . length($code);
|
||||
|
||||
# check Perl source code
|
||||
my $perl;
|
||||
@@ -72,39 +72,39 @@ ok(@badModules == 0, 'utf8 in modules');
|
||||
|
||||
SKIP: {
|
||||
skip 'documentation tests, we did not try to document every module yet', 1;
|
||||
@badModules = grep { $text{$_} !~ / ^ AddModuleDescription\(' [^\']+ ', /mx } @modules;
|
||||
@badModules = grep { ReadFile($_) !~ / ^ AddModuleDescription\(' [^\']+ ', /mx } @modules;
|
||||
unless (ok(@badModules == 0, 'link to the documentation in modules')) {
|
||||
diag(qq{$_ has no link to the documentation}) for @badModules;
|
||||
}
|
||||
}
|
||||
|
||||
@badModules = grep { $text{$_} =~ / ^ package \s+ OddMuse; /imx } @modules;
|
||||
@badModules = grep { ReadFile($_) =~ / ^ package \s+ OddMuse; /imx } @modules;
|
||||
unless (ok(@badModules == 0, 'no "package OddMuse;" in modules')) {
|
||||
diag(qq{$_ has "package OddMuse;"}) for @badModules;
|
||||
diag(q{When we do "do 'somemodule.pl';" it ends up being in the same namespace of a caller, so there is no need to use "package OddMuse;"});
|
||||
}
|
||||
|
||||
@badModules = grep { $text{$_} =~ / ^ use \s+ vars /mx } @modules;
|
||||
@badModules = grep { ReadFile($_) =~ / ^ use \s+ vars /mx } @modules;
|
||||
unless (ok(@badModules == 0, 'no "use vars" in modules')) {
|
||||
diag(qq{$_ is using "use vars"}) for @badModules;
|
||||
diag('▶▶▶ Use "our ($var, ...)" instead of "use vars qw($var ...)"');
|
||||
diag(q{▶▶▶ Use this command to do automatic conversion: perl -0pi -e 's/^([\t ]*)use vars qw\s*\(\s*(.*?)\s*\);/$x = $2; $x =~ s{(?<=\w)\b(?!$)}{,}g;"$1our ($x);"/gems' } . "@badModules");
|
||||
}
|
||||
|
||||
@badModules = grep { $text{$_} =~ / [ \t]+ $ /mx } @modules, @other;
|
||||
@badModules = grep { ReadFile($_) =~ / [ \t]+ $ /mx } @modules, @other;
|
||||
unless (ok(@badModules == 0, 'no trailing whitespace in modules (and other perl files)')) {
|
||||
diag(qq{$_ has trailing whitespace}) for @badModules;
|
||||
diag(q{▶▶▶ Use this command to do automatic trailing whitespace removal: perl -pi -e 's/[ \t]+$//g' } . "@badModules");
|
||||
}
|
||||
|
||||
@badModules = grep { $text{$_} =~ / This (program|file) is free software /x } @modules;
|
||||
@badModules = grep { ReadFile($_) =~ / This (program|file) is free software /x } @modules;
|
||||
unless (ok(@badModules == 0, 'license is specified in every module')) {
|
||||
diag(qq{$_ has no license specified}) for @badModules;
|
||||
}
|
||||
|
||||
@badModules = grep {
|
||||
my ($name, $path, $suffix) = fileparse($_, '.pl');
|
||||
$text{$_} !~ /^AddModuleDescription\('$name.pl'/mx;
|
||||
ReadFile($_) !~ /^AddModuleDescription\('$name.pl'/mx;
|
||||
} @modules;
|
||||
unless (ok(@badModules == 0, 'AddModuleDescription is used in every module')) {
|
||||
diag(qq{$_ does not use AddModuleDescription}) for @badModules;
|
||||
@@ -116,49 +116,3 @@ unless (ok(@badModules == 0, 'modules are syntatically correct')) {
|
||||
diag(qq{$_ has syntax errors}) for @badModules;
|
||||
diag("▶▶▶ Use this command to see the problems: for f in @badModules; do perl -c \$f; done");
|
||||
}
|
||||
|
||||
my %changes = (
|
||||
'-f' => 'IsFile',
|
||||
'-e' => 'IsFile',
|
||||
'-r' => 'IsFile',
|
||||
'-d' => 'IsDir',
|
||||
'-z' => 'ZeroSize',
|
||||
'-M' => '$Now - Modified',
|
||||
'unlink' => 'Unlink',
|
||||
'stat(.*)[9]' => 'Modified',
|
||||
'bsd_glob' => 'Glob',
|
||||
'chmod' => 'ChangeMod',
|
||||
'rename' => 'Rename',
|
||||
'rmdir' => 'RemoveDir',
|
||||
'chdir' => 'ChangeDir',
|
||||
'mkdir' => 'CreateDir',
|
||||
);
|
||||
|
||||
for my $re (sort keys %changes) {
|
||||
@badModules = grep {
|
||||
my $text = $text{$_};
|
||||
$text =~s/#.*\n//g; # get rid of comments
|
||||
$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'
|
||||
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')
|
||||
and (substr($re, 0, 1) eq '-' and $text =~ /[ (] $re \s/x
|
||||
or $re eq 'stat(.*)[9]' and $text =~ /\b $re /x
|
||||
or $re =~ /^\w+$/ and $text =~ /\b $re \b/x);
|
||||
} @modules;
|
||||
unless (ok(@badModules == 0, "modules do not use $re")) {
|
||||
diag(qq{$_ uses $re instead of $changes{$re}}) for @badModules;
|
||||
}
|
||||
}
|
||||
|
||||
for my $fun ('open.*,.*[<>]', 'sysopen', 'tie', 'opendir') {
|
||||
@badModules = grep {
|
||||
my @lines = map { s/#.*//; $_ } split(/\n/, $text{$_});
|
||||
grep(!/encode_utf8/, grep(/\b $fun \b/x, @lines));
|
||||
} @modules;
|
||||
unless (ok(@badModules == 0, qq{modules use encode_utf8 with $fun})) {
|
||||
diag(qq{$_ does not use encode_utf8 with $fun}) for @badModules;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
# Copyright (C) 2016 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 Test::More;
|
||||
use Test::Mojo;
|
||||
|
||||
require 't/test.pl';
|
||||
|
||||
add_module('namespaces.pl');
|
||||
|
||||
start_mojolicious_server();
|
||||
sleep(1);
|
||||
|
||||
my $t = Test::Mojo->new;
|
||||
|
||||
# Installation worked
|
||||
$t->get_ok("$ScriptName?action=version")
|
||||
->content_like(qr/namespaces\.pl/);
|
||||
|
||||
# Edit a page in the Main namespace
|
||||
$t->post_ok("$ScriptName"
|
||||
=> form => {title => 'Some_Page',
|
||||
text => 'This is the Main namespace.'})
|
||||
->status_is(302);
|
||||
$t->get_ok("$ScriptName/Some_Page")
|
||||
->status_is(200)
|
||||
->content_like(qr/This is the Main namespace/);
|
||||
|
||||
# Edit a page in the Five Winds namespace
|
||||
$t->post_ok("$ScriptName/FiveWinds"
|
||||
=> form => {title => 'Some_Page',
|
||||
text => 'This is the Five Winds namespace.'})
|
||||
->status_is(302);
|
||||
$t->get_ok("$ScriptName/FiveWinds/Some_Page")
|
||||
->status_is(200)
|
||||
->content_like(qr/This is the Five Winds namespace/);
|
||||
|
||||
# This didn't overwrite the Main namespace.
|
||||
$t->get_ok("$ScriptName/Some_Page")
|
||||
->content_like(qr/This is the Main namespace/);
|
||||
|
||||
# Umlauts
|
||||
$t->post_ok("$ScriptName/F%C3%BCnfWinde"
|
||||
=> form => {title => 'Some_Page',
|
||||
text => 'Wir sind im Namensraum Fünf Winde.'})
|
||||
->status_is(302);
|
||||
$t->get_ok("$ScriptName/F%C3%BCnfWinde/Some_Page")
|
||||
->status_is(200)
|
||||
->content_like(qr/Wir sind im Namensraum Fünf Winde/);
|
||||
|
||||
done_testing();
|
||||
@@ -1,35 +0,0 @@
|
||||
# Copyright (C) 2016 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 Test::More;
|
||||
use Test::Mojo;
|
||||
|
||||
require 't/test.pl';
|
||||
|
||||
start_mojolicious_server();
|
||||
sleep(1);
|
||||
|
||||
my $t = Test::Mojo->new;
|
||||
|
||||
$t->get_ok("$ScriptName")->status_is(404)->content_like(qr/Welcome!/);
|
||||
$t->get_ok("$ScriptName?action=admin")->status_is(200);
|
||||
|
||||
$t->post_ok("$ScriptName"
|
||||
=> form => {title => 'HomePage', text => 'This is a test.'})
|
||||
->status_is(302);
|
||||
$t->get_ok("$ScriptName")->status_is(200)->content_like(qr/This is a test/);
|
||||
|
||||
done_testing();
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2015-2016 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 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 the Free Software
|
||||
@@ -18,14 +18,8 @@ use Test::More tests => 2;
|
||||
|
||||
add_module('pygmentize.pl');
|
||||
|
||||
SKIP: {
|
||||
if (qx(pygmentize -V) !~ /Pygments version/) {
|
||||
skip "pygmentize not found", 2;
|
||||
}
|
||||
|
||||
$ENV{PATH} = '.'; # pygmentize is not installed in the current directory
|
||||
$page = apply_rules(newlines('{{{\ntest\n}}}\n'));
|
||||
test_page($page,
|
||||
'\bsh\b.*\bpygmentize\b.*\bnot found\b',
|
||||
'<pre>test</pre>');
|
||||
}
|
||||
$ENV{PATH} = '.'; # pygmentize is not installed in the current directory
|
||||
$page = apply_rules(newlines('{{{\ntest\n}}}\n'));
|
||||
test_page($page,
|
||||
'\bsh\b.*\bpygmentize\b.*\bnot found\b',
|
||||
'<pre>test</pre>');
|
||||
|
||||
13
t/search.t
13
t/search.t
@@ -15,7 +15,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 89;
|
||||
use Test::More tests => 72;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
add_module('mac.pl');
|
||||
@@ -116,17 +116,6 @@ xpath_test($page, '//a[@class="more"][@href="http://localhost/wiki.pl?search=Som
|
||||
$page = get_page('search=Something preview=1 offset=10 num=10 replace=Other pwd=foo');
|
||||
test_page($page, map { "Page_$_" } ('K' .. 'M'));
|
||||
|
||||
# Now do the replacement
|
||||
|
||||
$page = get_page('search=Something replace=Other pwd=foo');
|
||||
test_page($page, 'Replaced: Something → Other', '13 pages found',
|
||||
map { "Page_$_" } ('A' .. 'M'));
|
||||
|
||||
# Verify that the change has been made
|
||||
|
||||
test_page(get_page('search=Other'), 'Search for: Other', '13 pages found');
|
||||
|
||||
|
||||
# Replace with backreferences, where the replacement pattern is no longer found.
|
||||
# Take 'fuuz and barz.' and replace ([a-z]+)z with x$1 results in 'xfuu and xbar.'
|
||||
test_page(get_page('"search=([a-z]%2b)z" replace=x%241 pwd=foo'), '1 pages found');
|
||||
|
||||
33
t/server.t
33
t/server.t
@@ -1,33 +0,0 @@
|
||||
# Copyright (C) 2016 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 LWP::UserAgent;
|
||||
|
||||
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");
|
||||
ok($response->is_success, "There is a wiki running at $ScriptName");
|
||||
like($response->content, qr/Oddmuse/, "It self-identifies as Oddmuse");
|
||||
ok($ua->get("$ScriptName?title=Test;text=Testing")->is_success, "Page saved");
|
||||
like($ua->get("$ScriptName/Test")->content, qr/Testing/, "Content verified");
|
||||
89
t/sidebar.t
89
t/sidebar.t
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env perl
|
||||
# Copyright (C) 2006–2016 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2006, 2007 Alex Schroeder <alex@emacswiki.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,71 +16,19 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 9;
|
||||
|
||||
# basic test
|
||||
use Test::More tests => 6;
|
||||
|
||||
add_module('sidebar.pl');
|
||||
|
||||
test_page(update_page($SidebarName, 'mu'), '<div class="sidebar"><p>mu</p></div>');
|
||||
test_page(get_page('HomePage'), '<div class="sidebar"><p>mu</p></div>');
|
||||
|
||||
# with images
|
||||
|
||||
add_module('image.pl');
|
||||
# enable uploads
|
||||
AppendStringToFile($ConfigFile, "\$UploadAllowed = 1;\n");
|
||||
update_page('pic', "#FILE image/png\niVBORw0KGgoAAAA");
|
||||
xpath_test(update_page($SidebarName, '[[image:pic|picture|Target]]'),
|
||||
'//div[@class="sidebar"]/p/a[@class="image"][@href="http://localhost/wiki.pl/Target"]/img[@class="upload"][@src="http://localhost/wiki.pl/download/pic"][@alt="picture"]');
|
||||
|
||||
# with static-copy
|
||||
|
||||
add_module('static-copy.pl');
|
||||
|
||||
AppendStringToFile($ConfigFile, q{
|
||||
$StaticAlways = 1;
|
||||
$StaticDir = $DataDir . '/static';
|
||||
$StaticUrl = '/static/';
|
||||
%StaticMimeTypes = ('image/png' => 'png', );
|
||||
@UploadTypes = ('image/png', );
|
||||
});
|
||||
|
||||
update_page('pic', "DeletedPage");
|
||||
update_page('pic', "#FILE image/png\niVBORw0KGgoAAAA", undef, 0, 1);
|
||||
ok(-f "$DataDir/static/pic.png", "$DataDir/static/pic.png exists");
|
||||
|
||||
xpath_test(update_page($SidebarName, '[[image:pic|a picture|Target]]'),
|
||||
'//div[@class="sidebar"]/p/a[@class="image"][@href="http://localhost/wiki.pl/Target"]/img[@class="upload"][@src="/static/pic.png"][@alt="a picture"]');
|
||||
|
||||
# with forms
|
||||
|
||||
add_module('forms.pl');
|
||||
|
||||
# Markup the sidebar page prior to locking the sidebar page. This should ensure
|
||||
# that forms on that page are not interpreted.
|
||||
test_page(update_page($SidebarName, '<form><h1>mu</h1></form>'),
|
||||
'<div class="sidebar"><p><form><h1>mu</h1></form></p></div>');
|
||||
|
||||
# Lock the sidebar page, mark it up again, and ensure that forms on that page
|
||||
# are now interpreted.
|
||||
xpath_test(get_page("action=pagelock id=$SidebarName set=1 pwd=foo"),
|
||||
'//p/text()[string()="Lock for "]/following-sibling::a[@href="http://localhost/wiki.pl/SideBar"][@class="local"][text()="SideBar"]/following-sibling::text()[string()=" created."]');
|
||||
test_page(get_page("action=browse id=$SidebarName cache=0"), #update_page($SidebarName, '<form><h1>mu</h1></form>'),
|
||||
'<div class="sidebar"><form><h1>mu</h1></form></div>');
|
||||
# While rendering the SideBar as part of the HomePage, it should still
|
||||
# be considered "locked", and therefore the form should render
|
||||
# correctly.
|
||||
test_page(get_page('HomePage'),
|
||||
'<div class="sidebar"><form><h1>mu</h1></form></div>');
|
||||
|
||||
|
||||
# FIXME: Due to the recent refactoring of the Table of Contents module, the
|
||||
# Sidebar module is now known **not** to work as expected with that module.
|
||||
# This would appear to be an unavoidable consequence of that refactoring... The
|
||||
# Sidebar module, as currently implemented, **cannot** be made to work with the
|
||||
# Table of Contents module. As such, we disable all prior tests against the
|
||||
# Table of Contents module. It's hardly ideal. (But then, what is?)
|
||||
#FIXME: Due to the recent refactoring of the Table of Contents module, the
|
||||
#Sidebar module is now known **not** to work as expected with that module.
|
||||
#This would appear to be an unavoidable consequence of that refactoring... The
|
||||
#Sidebar module, as currently implemented, **cannot** be made to work with the
|
||||
#Table of Contents module. As such, we disable all prior tests against the
|
||||
#Table of Contents module. It's hardly ideal. (But then, what is?)
|
||||
|
||||
# with toc
|
||||
|
||||
@@ -124,3 +72,24 @@ test_page(get_page('HomePage'),
|
||||
|
||||
# remove_rule(\&TocRule);
|
||||
# remove_rule(\&UsemodRule);
|
||||
|
||||
# with forms
|
||||
|
||||
add_module('forms.pl');
|
||||
|
||||
# Markup the sidebar page prior to locking the sidebar page. This should ensure
|
||||
# that forms on that page are not interpreted.
|
||||
test_page(update_page($SidebarName, '<form><h1>mu</h1></form>'),
|
||||
'<div class="sidebar"><p><form><h1>mu</h1></form></p></div>');
|
||||
|
||||
# Lock the sidebar page, mark it up again, and ensure that forms on that page
|
||||
# are now interpreted.
|
||||
xpath_test(get_page("action=pagelock id=$SidebarName set=1 pwd=foo"),
|
||||
'//p/text()[string()="Lock for "]/following-sibling::a[@href="http://localhost/wiki.pl/SideBar"][@class="local"][text()="SideBar"]/following-sibling::text()[string()=" created."]');
|
||||
test_page(get_page("action=browse id=$SidebarName cache=0"), #update_page($SidebarName, '<form><h1>mu</h1></form>'),
|
||||
'<div class="sidebar"><form><h1>mu</h1></form></div>');
|
||||
# While rendering the SideBar as part of the HomePage, it should still
|
||||
# be considered "locked", and therefore the form should render
|
||||
# correctly.
|
||||
test_page(get_page('HomePage'),
|
||||
'<div class="sidebar"><form><h1>mu</h1></form></div>');
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use utf8;
|
||||
use Test::More tests => 38;
|
||||
use Test::More tests => 36;
|
||||
|
||||
add_module('static-copy.pl');
|
||||
|
||||
@@ -167,17 +167,3 @@ xpath_test(update_page('test_image', '[[image/right:bar baz]]'),
|
||||
# Next, using a real page. The image type is used appropriately.
|
||||
xpath_test(update_page('test_image', '[[image/right:Logo]]'),
|
||||
'//a[@class="image right"][@href="http://localhost/wiki.pl/Logo"]/img[@class="upload"][@src="/static/Logo.png"][@alt="Logo"]');
|
||||
|
||||
|
||||
my $weirdPage = 'Ï_lövé_¥ǫµnĩçȯḑë';
|
||||
update_page($weirdPage, 'Some text');
|
||||
update_page('Unicode', '[[' . $weirdPage . ']]');
|
||||
|
||||
get_page('action=static raw=1 pwd=foo html=1'); # generate static files
|
||||
|
||||
my ($status, $data) = ReadFile("$DataDir/static/Unicode.html");
|
||||
|
||||
xpath_test(get_page('Unicode'),
|
||||
'//a[@class="local"]' . '[@href="http://localhost/wiki.pl/' . UrlEncode($weirdPage) . '"]');
|
||||
xpath_test($data,
|
||||
'//a[@class="local"]' . '[@href="' . UrlEncode($weirdPage) . '.html"]');
|
||||
|
||||
83
t/test.pl
83
t/test.pl
@@ -17,7 +17,6 @@ package OddMuse;
|
||||
use lib '.';
|
||||
use XML::LibXML;
|
||||
use utf8;
|
||||
use Encode qw(encode_utf8 decode_utf8);
|
||||
use vars qw($raw);
|
||||
|
||||
# Test::More explains how to fix wide character in print issues
|
||||
@@ -57,7 +56,8 @@ $| = 1; # no output buffering
|
||||
sub url_encode {
|
||||
my $str = shift;
|
||||
return '' unless $str;
|
||||
my @letters = split(//, encode_utf8($str));
|
||||
utf8::encode($str); # turn to byte string
|
||||
my @letters = split(//, $str);
|
||||
my @safe = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.'); # shell metachars are unsafe
|
||||
foreach my $letter (@letters) {
|
||||
my $pattern = quotemeta($letter);
|
||||
@@ -209,8 +209,15 @@ sub xpath_do {
|
||||
skip("Cannot parse ".name($page).": $@", $#tests + 1) if $@;
|
||||
foreach my $test (@tests) {
|
||||
my $nodelist;
|
||||
# libxml2 is not aware of UTF8 flag
|
||||
eval { $nodelist = $doc->findnodes(encode_utf8($test)) };
|
||||
my $bytes = $test;
|
||||
# utf8::encode: Converts in-place the character sequence to the
|
||||
# corresponding octet sequence in *UTF-X*. The UTF8 flag is
|
||||
# turned off, so that after this operation, the string is a byte
|
||||
# string. (I have no idea why this is necessary, but there you
|
||||
# go. See encoding.t tests and make sure the page file is
|
||||
# encoded correctly.)
|
||||
utf8::encode($bytes);
|
||||
eval { $nodelist = $doc->findnodes($bytes) };
|
||||
if ($@) {
|
||||
fail(&$check(1) ? "$test: $@" : "not $test: $@");
|
||||
} elsif (ok(&$check($nodelist->size()),
|
||||
@@ -346,73 +353,6 @@ sub clear_pages {
|
||||
write_config_file();
|
||||
}
|
||||
|
||||
# Find an unused port
|
||||
sub random_port {
|
||||
use Errno qw( EADDRINUSE );
|
||||
use Socket qw( PF_INET SOCK_STREAM INADDR_ANY sockaddr_in );
|
||||
|
||||
my $family = PF_INET;
|
||||
my $type = SOCK_STREAM;
|
||||
my $proto = getprotobyname('tcp') or die "getprotobyname: $!";
|
||||
my $host = INADDR_ANY; # Use inet_aton for a specific interface
|
||||
|
||||
for my $i (1..3) {
|
||||
my $port = 1024 + int(rand(65535 - 1024));
|
||||
socket(my $sock, $family, $type, $proto) or die "socket: $!";
|
||||
my $name = sockaddr_in($port, $host) or die "sockaddr_in: $!";
|
||||
setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
bind($sock, $name)
|
||||
and close($sock)
|
||||
and return $port;
|
||||
die "bind: $!" if $! != EADDRINUSE;
|
||||
print "Port $port in use, retrying...\n";
|
||||
}
|
||||
die "Tried 3 random ports and failed.\n"
|
||||
}
|
||||
|
||||
my $pid;
|
||||
|
||||
# Fork a simple test server
|
||||
sub start_server {
|
||||
die "A server already exists: $pid\n" if $pid;
|
||||
my $port = random_port();
|
||||
$ScriptName = "http://localhost:$port";
|
||||
AppendStringToFile($ConfigFile, "\$ScriptName = '$ScriptName';\n");
|
||||
$pid = fork();
|
||||
if (!defined $pid) {
|
||||
die "Cannot fork: $!";
|
||||
} elsif ($pid == 0) {
|
||||
use Config;
|
||||
my $secure_perl_path = $Config{perlpath};
|
||||
exec($secure_perl_path, "stuff/server.pl", "wiki.pl", $port) or die "Cannot exec: $!";
|
||||
}
|
||||
}
|
||||
|
||||
# Fork a Mojolicious server
|
||||
sub start_mojolicious_server {
|
||||
die "A server already exists: $pid\n" if $pid;
|
||||
my $port = random_port();
|
||||
my $listen = "http://127.0.0.1:$port";
|
||||
$ScriptName = "http://127.0.0.1:$port/wiki";
|
||||
AppendStringToFile($ConfigFile, "\$ScriptName = '$ScriptName';\n");
|
||||
$pid = fork();
|
||||
if (!defined $pid) {
|
||||
die "Cannot fork: $!";
|
||||
} elsif ($pid == 0) {
|
||||
use Config;
|
||||
my $secure_perl_path = $Config{perlpath};
|
||||
exec($secure_perl_path, "server.pl", "daemon", "-l", $listen)
|
||||
or die "Cannot exec: $!";
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
# kill server
|
||||
if ($pid) {
|
||||
kill 'KILL', $pid or warn "Could not kill server $pid";
|
||||
}
|
||||
}
|
||||
|
||||
sub RunAndTerminate { # runs a command for 1 second and then sends SIGTERM
|
||||
my $pid = fork();
|
||||
if (not $pid) { # child
|
||||
@@ -434,4 +374,3 @@ sub AppendToConfig {
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
181
wiki.pl
Normal file → Executable file
181
wiki.pl
Normal file → Executable file
@@ -38,7 +38,6 @@ use utf8; # in case anybody ever adds UTF8 characters to the source
|
||||
use CGI qw/-utf8/;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
use File::Glob ':glob';
|
||||
use Encode qw(encode_utf8 decode_utf8);
|
||||
use sigtrap 'handler' => \&HandleSignals, 'normal-signals', 'error-signals';
|
||||
local $| = 1; # Do not buffer output (localized for mod_perl)
|
||||
|
||||
@@ -67,7 +66,7 @@ our $UseConfig //= 1;
|
||||
|
||||
# Main wiki directory
|
||||
our $DataDir;
|
||||
$DataDir ||= decode_utf8($ENV{WikiDataDir}) if $UseConfig;
|
||||
$DataDir ||= $ENV{WikiDataDir} if $UseConfig;
|
||||
$DataDir ||= '/tmp/oddmuse'; # FIXME: /var/opt/oddmuse/wiki ?
|
||||
|
||||
our $ConfigFile;
|
||||
@@ -102,6 +101,9 @@ our $EditPass //= ''; # Whitespace separated passwords.
|
||||
our $PassHashFunction //= ''; # Name of the function to create hashes
|
||||
our $PassSalt //= ''; # Salt will be added to any password before hashing
|
||||
|
||||
our $UseCsp = 0; # 1 = enable Content Security Policy # TODO should be enabled by default
|
||||
our %CspDirectives = ('default-src' => ["'self'"], 'style-src' => ['*'], 'img-src' => ['*']); # CSP directives
|
||||
|
||||
our $BannedHosts = 'BannedHosts'; # Page for banned hosts
|
||||
our $BannedCanRead = 1; # 1 = banned cannot edit, 0 = banned cannot read
|
||||
our $BannedContent = 'BannedContent'; # Page for banned content (usually for link-ban)
|
||||
@@ -228,20 +230,17 @@ sub Init {
|
||||
}
|
||||
|
||||
sub InitModules {
|
||||
if ($UseConfig and $ModuleDir and IsDir($ModuleDir)) {
|
||||
foreach my $lib (Glob("$ModuleDir/*.p[ml]")) {
|
||||
if (not $MyInc{$lib}) {
|
||||
$MyInc{$lib} = 1; # Cannot use %INC in mod_perl settings
|
||||
my $file = encode_utf8($lib);
|
||||
do $file;
|
||||
$Message .= CGI::p("$lib: $@") if $@; # no $q exists, yet
|
||||
}
|
||||
if ($UseConfig and $ModuleDir and -d $ModuleDir) {
|
||||
foreach my $lib (bsd_glob("$ModuleDir/*.p[ml]")) {
|
||||
do $lib unless $MyInc{$lib};
|
||||
$MyInc{$lib} = 1; # Cannot use %INC in mod_perl settings
|
||||
$Message .= CGI::p("$lib: $@") if $@; # no $q exists, yet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub InitConfig {
|
||||
if ($UseConfig and $ConfigFile and not $INC{$ConfigFile} and IsFile($ConfigFile)) {
|
||||
if ($UseConfig and $ConfigFile and not $INC{$ConfigFile} and -f $ConfigFile) {
|
||||
do $ConfigFile; # these options must be set in a wrapper script or via the environment
|
||||
$Message .= CGI::p("$ConfigFile: $@") if $@; # remember, no $q exists, yet
|
||||
}
|
||||
@@ -254,6 +253,7 @@ sub InitConfig {
|
||||
}
|
||||
|
||||
sub InitDirConfig {
|
||||
utf8::decode($DataDir); # just in case, eg. "WikiDataDir=/tmp/Zürich♥ perl wiki.pl"
|
||||
$PageDir = "$DataDir/page"; # Stores page data
|
||||
$KeepDir = "$DataDir/keep"; # Stores kept (old) page data
|
||||
$TempDir = "$DataDir/temp"; # Temporary files and locks
|
||||
@@ -303,12 +303,12 @@ sub InitVariables { # Init global session variables for mod_perl!
|
||||
delete $PlainTextPages{''}; # $ConfigPage and others might be empty.
|
||||
CreateDir($DataDir); # Create directory if it doesn't exist
|
||||
$Now = time; # Reset in case script is persistent
|
||||
my $ts = Modified($IndexFile); # always stat for multiple server processes
|
||||
my $ts = (stat($IndexFile))[9]; # always stat for multiple server processes
|
||||
ReInit() if not $ts or $LastUpdate != $ts; # reinit if another process changed files (requires $DataDir)
|
||||
$LastUpdate = $ts;
|
||||
unshift(@MyRules, \&MyRules) if defined(&MyRules) && (not @MyRules or $MyRules[0] != \&MyRules);
|
||||
@MyRules = sort {$RuleOrder{$a} <=> $RuleOrder{$b}} @MyRules; # default is 0
|
||||
ReportError(Ts('Cannot create %s', $DataDir) . ": $!", '500 INTERNAL SERVER ERROR') unless IsDir($DataDir);
|
||||
ReportError(Ts('Cannot create %s', $DataDir) . ": $!", '500 INTERNAL SERVER ERROR') unless -d $DataDir;
|
||||
@IndexOptions = (['pages', T('Include normal pages'), 1, \&AllPagesList]);
|
||||
foreach my $sub (@MyInitVariables) {
|
||||
my $result = $sub->();
|
||||
@@ -363,7 +363,8 @@ sub CookieRollbackFix {
|
||||
|
||||
sub GetParam {
|
||||
my ($name, $default) = @_;
|
||||
my $result = $q->param(encode_utf8($name));
|
||||
utf8::encode($name); # turn to byte string
|
||||
my $result = $q->param($name);
|
||||
$result //= $default;
|
||||
return QuoteHtml($result); # you need to unquote anything that can have <tags>
|
||||
}
|
||||
@@ -788,7 +789,8 @@ sub UnquoteHtml {
|
||||
sub UrlEncode {
|
||||
my $str = shift;
|
||||
return '' unless $str;
|
||||
my @letters = split(//, encode_utf8($str));
|
||||
utf8::encode($str); # turn to byte string
|
||||
my @letters = split(//, $str);
|
||||
my %safe = map {$_ => 1} ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.', '!', '~', '*', "'", '(', ')', '#');
|
||||
foreach my $letter (@letters) {
|
||||
$letter = sprintf("%%%02x", ord($letter)) unless $safe{$letter};
|
||||
@@ -798,7 +800,8 @@ sub UrlEncode {
|
||||
|
||||
sub UrlDecode {
|
||||
my $str = shift;
|
||||
return decode_utf8($str) if $str =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eg;
|
||||
$str =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eg;
|
||||
utf8::decode($str); # make internal string
|
||||
return $str;
|
||||
}
|
||||
|
||||
@@ -1013,7 +1016,7 @@ sub GetRss {
|
||||
my $str = '';
|
||||
if (GetParam('cache', $UseCache) > 0) {
|
||||
foreach my $uri (keys %todo) { # read cached rss files if possible
|
||||
if ($Now - Modified($todo{$uri}) < $RssCacheHours * 3600) {
|
||||
if ($Now - (stat($todo{$uri}))[9] < $RssCacheHours * 3600) {
|
||||
$data{$uri} = ReadFile($todo{$uri});
|
||||
delete($todo{$uri}); # no need to fetch them below
|
||||
}
|
||||
@@ -1256,14 +1259,16 @@ sub PrintPageDiff { # print diff for open page
|
||||
}
|
||||
|
||||
sub ToString {
|
||||
my $sub_ref = shift;
|
||||
my ($sub_ref) = @_;
|
||||
my $output;
|
||||
open(my $outputFH, '>:encoding(UTF-8)', \$output) or die "Can't open memory file: $!";
|
||||
my $oldFH = select $outputFH;
|
||||
$sub_ref->(@_);
|
||||
$sub_ref->();
|
||||
select $oldFH;
|
||||
close $outputFH;
|
||||
return decode_utf8($output);
|
||||
my $output_fixed = $output; # do not delete!
|
||||
utf8::decode($output_fixed); # this is a workarond for perl bug
|
||||
return $output_fixed; # otherwise UTF8 characters are SOMETIMES not decoded.
|
||||
}
|
||||
|
||||
sub PageHtml {
|
||||
@@ -1297,11 +1302,17 @@ sub Tss {
|
||||
|
||||
sub GetId {
|
||||
my $id = UnquoteHtml(GetParam('id', GetParam('title', ''))); # id=x or title=x -> x
|
||||
if (not $id and $q->keywords) {
|
||||
$id = decode_utf8(join('_', $q->keywords)); # script?p+q -> p_q
|
||||
if (not $id) {
|
||||
my @keywords = $q->keywords;
|
||||
foreach my $keyword (@keywords) {
|
||||
utf8::decode($keyword);
|
||||
}
|
||||
$id ||= join('_', @keywords); # script?p+q -> p_q
|
||||
}
|
||||
if ($UsePathInfo and $q->path_info) {
|
||||
my @path = map { decode_utf8($_) } split(/\//, $q->path_info);
|
||||
if ($UsePathInfo) {
|
||||
my $path = $q->path_info;
|
||||
utf8::decode($path);
|
||||
my @path = split(/\//, $path);
|
||||
$id ||= pop(@path); # script/p/q -> q
|
||||
foreach my $p (@path) {
|
||||
SetParam($p, 1); # script/p/q -> p=1
|
||||
@@ -1496,7 +1507,7 @@ sub GetRcLines { # starttime, hash of seen pages to use as a second return value
|
||||
my @result = ();
|
||||
my $ts;
|
||||
# check the first timestamp in the default file, maybe read old log file
|
||||
if (open(my $F, '<:encoding(UTF-8)', encode_utf8($RcFile))) {
|
||||
if (open(my $F, '<:encoding(UTF-8)', $RcFile)) {
|
||||
my $line = <$F>;
|
||||
($ts) = split(/$FS/, $line); # the first timestamp in the regular rc file
|
||||
}
|
||||
@@ -1580,7 +1591,7 @@ sub GetRcLinesFor {
|
||||
rcclusteronly rcfilteronly match lang followup);
|
||||
# parsing and filtering
|
||||
my @result = ();
|
||||
open(my $F, '<:encoding(UTF-8)', encode_utf8($file)) or return ();
|
||||
open(my $F, '<:encoding(UTF-8)', $file) or return ();
|
||||
while (my $line = <$F>) {
|
||||
chomp($line);
|
||||
my ($ts, $id, $minor, $summary, $host, $username, $revision,
|
||||
@@ -2117,8 +2128,8 @@ sub DoAdminPage {
|
||||
push(@menu, ScriptLink('action=maintain', T('Run maintenance'), 'maintain')) if $Action{maintain};
|
||||
my @locks;
|
||||
for my $pattern (@KnownLocks) {
|
||||
for my $name (Glob($pattern)) {
|
||||
if (IsDir($LockDir . $name)) {
|
||||
for my $name (bsd_glob $pattern) {
|
||||
if (-d $LockDir . $name) {
|
||||
push(@locks, $name);
|
||||
}
|
||||
}
|
||||
@@ -2128,7 +2139,7 @@ sub DoAdminPage {
|
||||
};
|
||||
if (UserIsAdmin()) {
|
||||
if ($Action{editlock}) {
|
||||
if (IsFile("$DataDir/noedit")) {
|
||||
if (-f "$DataDir/noedit") {
|
||||
push(@menu, ScriptLink('action=editlock;set=0', T('Unlock site'), 'editlock 0'));
|
||||
} else {
|
||||
push(@menu, ScriptLink('action=editlock;set=1', T('Lock site'), 'editlock 1'));
|
||||
@@ -2136,7 +2147,7 @@ sub DoAdminPage {
|
||||
}
|
||||
if ($id and $Action{pagelock}) {
|
||||
my $title = NormalToFree($id);
|
||||
if (IsFile(GetLockedPageFile($id))) {
|
||||
if (-f GetLockedPageFile($id)) {
|
||||
push(@menu, ScriptLink('action=pagelock;set=0;id=' . UrlEncode($id),
|
||||
Ts('Unlock %s', $title), 'pagelock 0'));
|
||||
} else {
|
||||
@@ -2295,6 +2306,12 @@ sub GetHttpHeader {
|
||||
$headers{-Content_Encoding} = $encoding if $encoding;
|
||||
my $cookie = Cookie();
|
||||
$headers{-cookie} = $cookie if $cookie;
|
||||
if ($UseCsp) {
|
||||
my $csp = join '; ', map { join ' ', $_, @{$CspDirectives{$_}} } sort keys %CspDirectives;
|
||||
$headers{'-Content-Security-Policy'} = $csp;
|
||||
$headers{'-X-Content-Security-Policy'} = $csp; # required for IE
|
||||
$headers{'-X-Webkit-CSP'} = $csp; # required for UC browser
|
||||
}
|
||||
if ($q->request_method() eq 'HEAD') {
|
||||
print $q->header(%headers), "\n\n"; # add newlines for FCGI because of exit()
|
||||
exit; # total shortcut -- HEAD never expects anything other than the header!
|
||||
@@ -2369,7 +2386,7 @@ sub GetCss { # prevent javascript injection
|
||||
if ($IndexHash{$StyleSheetPage} and not @css) {
|
||||
push (@css, "$ScriptName?action=browse;id=" . UrlEncode($StyleSheetPage) . ";raw=1;mime-type=text/css")
|
||||
}
|
||||
push (@css, 'https://oddmuse.org/default.css') unless @css;
|
||||
push (@css, 'http://www.oddmuse.org/default.css') unless @css;
|
||||
return join('', map { qq(<link type="text/css" rel="stylesheet" href="$_" />) } @css);
|
||||
}
|
||||
|
||||
@@ -2596,9 +2613,10 @@ sub DoDiff { # Actualy call the diff program
|
||||
RequestLockDir('diff') or return '';
|
||||
WriteStringToFile($oldName, $_[0]);
|
||||
WriteStringToFile($newName, $_[1]);
|
||||
my $diff_out = decode_utf8(`diff -- \Q$oldName\E \Q$newName\E`);
|
||||
ReleaseLockDir('diff');
|
||||
my $diff_out = `diff -- \Q$oldName\E \Q$newName\E`;
|
||||
utf8::decode($diff_out); # needs decoding
|
||||
$diff_out =~ s/\n\K\\ No newline.*\n//g; # Get rid of common complaint.
|
||||
ReleaseLockDir('diff');
|
||||
# No need to unlink temp files--next diff will just overwrite.
|
||||
return $diff_out;
|
||||
}
|
||||
@@ -2773,7 +2791,7 @@ sub GetKeepDir {
|
||||
}
|
||||
|
||||
sub GetKeepFiles {
|
||||
return Glob(GetKeepDir(shift) . '/*.kp'); # files such as 1.kp, 2.kp, etc.
|
||||
return bsd_glob(GetKeepDir(shift) . '/*.kp'); # files such as 1.kp, 2.kp, etc.
|
||||
}
|
||||
|
||||
sub GetKeepRevisions {
|
||||
@@ -2834,12 +2852,14 @@ sub ExpireKeepFiles { # call with opened page
|
||||
my $keep = GetKeptRevision($revision);
|
||||
next if $keep->{'keep-ts'} >= $expirets;
|
||||
next if $KeepMajor and $keep->{revision} == $Page{lastmajor};
|
||||
Unlink(GetKeepFile($OpenPageName, $revision));
|
||||
unlink GetKeepFile($OpenPageName, $revision);
|
||||
}
|
||||
}
|
||||
|
||||
sub ReadFile {
|
||||
if (open(my $IN, '<:encoding(UTF-8)', encode_utf8(shift))) {
|
||||
my $file = shift;
|
||||
utf8::encode($file); # filenames are bytes!
|
||||
if (open(my $IN, '<:encoding(UTF-8)', $file)) {
|
||||
local $/ = undef; # Read complete files
|
||||
my $data=<$IN>;
|
||||
close $IN;
|
||||
@@ -2860,7 +2880,8 @@ sub ReadFileOrDie {
|
||||
|
||||
sub WriteStringToFile {
|
||||
my ($file, $string) = @_;
|
||||
open(my $OUT, '>:encoding(UTF-8)', encode_utf8($file))
|
||||
utf8::encode($file);
|
||||
open(my $OUT, '>:encoding(UTF-8)', $file)
|
||||
or ReportError(Ts('Cannot write %s', $file) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
print $OUT $string;
|
||||
close($OUT);
|
||||
@@ -2868,27 +2889,18 @@ sub WriteStringToFile {
|
||||
|
||||
sub AppendStringToFile {
|
||||
my ($file, $string) = @_;
|
||||
open(my $OUT, '>>:encoding(UTF-8)', encode_utf8($file))
|
||||
utf8::encode($file);
|
||||
open(my $OUT, '>>:encoding(UTF-8)', $file)
|
||||
or ReportError(Ts('Cannot write %s', $file) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
print $OUT $string;
|
||||
close($OUT);
|
||||
}
|
||||
|
||||
sub IsFile { return -f encode_utf8(shift); }
|
||||
sub IsDir { return -d encode_utf8(shift); }
|
||||
sub ZeroSize { return -z encode_utf8(shift); }
|
||||
sub Unlink { return unlink(map { encode_utf8($_) } @_); }
|
||||
sub Modified { return (stat(encode_utf8(shift)))[9]; }
|
||||
sub Glob { return map { decode_utf8($_) } bsd_glob(encode_utf8(shift)); }
|
||||
sub ChangeMod { return chmod(shift, map { encode_utf8($_) } @_); }
|
||||
sub Rename { return rename(encode_utf8($_[0]), encode_utf8($_[1])); }
|
||||
sub RemoveDir { return rmdir(encode_utf8(shift)); }
|
||||
sub ChangeDir { return chdir(encode_utf8(shift)); }
|
||||
|
||||
sub CreateDir {
|
||||
my ($newdir) = @_;
|
||||
return if IsDir($newdir);
|
||||
mkdir(encode_utf8($newdir), 0775)
|
||||
utf8::encode($newdir);
|
||||
return if -d $newdir;
|
||||
mkdir($newdir, 0775)
|
||||
or ReportError(Ts('Cannot create %s', $newdir) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
|
||||
@@ -2904,11 +2916,9 @@ sub RequestLockDir {
|
||||
CreateDir($TempDir);
|
||||
my $lock = $LockDir . $name;
|
||||
my $n = 0;
|
||||
# Cannot use CreateDir because we don't want to skip mkdir if the directory
|
||||
# already exists.
|
||||
while (mkdir(encode_utf8($lock), 0555) == 0) {
|
||||
while (mkdir($lock, 0555) == 0) {
|
||||
if ($n++ >= $tries) {
|
||||
my $ts = Modified($lock);
|
||||
my $ts = (stat($lock))[9];
|
||||
if ($Now - $ts > $LockExpiration and $LockExpires{$name} and not $retried) { # XXX should we remove this now?
|
||||
ReleaseLockDir($name); # try to expire lock (no checking)
|
||||
return 1 if RequestLockDir($name, undef, undef, undef, 1);
|
||||
@@ -2941,9 +2951,9 @@ sub CleanLock {
|
||||
}
|
||||
|
||||
sub ReleaseLockDir {
|
||||
my $name = shift; # We don't check whether we succeeded.
|
||||
RemoveDir($LockDir . $name); # Before fixing, make sure we only call this
|
||||
delete $Locks{$name}; # when we know the lock exists.
|
||||
my $name = shift; # We don't check whether we succeeded.
|
||||
rmdir($LockDir . $name); # Before fixing, make sure we only call this
|
||||
delete $Locks{$name}; # when we know the lock exists.
|
||||
}
|
||||
|
||||
sub RequestLockOrError {
|
||||
@@ -2957,7 +2967,7 @@ sub ReleaseLock {
|
||||
sub ForceReleaseLock {
|
||||
my $pattern = shift;
|
||||
my $forced;
|
||||
foreach my $name (Glob($pattern)) {
|
||||
foreach my $name (bsd_glob $pattern) {
|
||||
# First try to obtain lock (in case of normal edit lock)
|
||||
$forced = 1 unless RequestLockDir($name, 5, 3, 0);
|
||||
ReleaseLockDir($name); # Release the lock, even if we didn't get it. This should not happen.
|
||||
@@ -3226,10 +3236,10 @@ sub UserCanEdit {
|
||||
return 0 if $id eq 'SampleUndefinedPage' or $id eq T('SampleUndefinedPage')
|
||||
or $id eq 'Sample_Undefined_Page' or $id eq T('Sample_Undefined_Page');
|
||||
return 1 if UserIsAdmin();
|
||||
return 0 if $id ne '' and IsFile(GetLockedPageFile($id));
|
||||
return 0 if $LockOnCreation{$id} and not IsFile(GetPageFile($id)); # new page
|
||||
return 0 if $id ne '' and -f GetLockedPageFile($id);
|
||||
return 0 if $LockOnCreation{$id} and not -f GetPageFile($id); # new page
|
||||
return 1 if UserIsEditor();
|
||||
return 0 if not $EditAllowed or IsFile($NoEditFile);
|
||||
return 0 if not $EditAllowed or -f $NoEditFile;
|
||||
return 0 if $editing and UserIsBanned(); # this call is more expensive
|
||||
return 0 if $EditAllowed >= 2 and (not $CommentsPattern or $id !~ /$CommentsPattern/);
|
||||
return 1 if $EditAllowed >= 3 and GetParam('recent_edit', '') ne 'on' # disallow minor comments
|
||||
@@ -3352,7 +3362,7 @@ sub AllPagesList {
|
||||
my $refresh = GetParam('refresh', 0);
|
||||
return @IndexList if @IndexList and not $refresh;
|
||||
SetParam('refresh', 0) if $refresh;
|
||||
return @IndexList if not $refresh and IsFile($IndexFile) and ReadIndex();
|
||||
return @IndexList if not $refresh and -f $IndexFile and ReadIndex();
|
||||
# If open fails just refresh the index
|
||||
RefreshIndex();
|
||||
return @IndexList;
|
||||
@@ -3376,10 +3386,11 @@ sub RefreshIndex {
|
||||
@IndexList = ();
|
||||
%IndexHash = ();
|
||||
# If file exists and cannot be changed, error!
|
||||
my $locked = RequestLockDir('index', undef, undef, IsFile($IndexFile));
|
||||
foreach (Glob("$PageDir/*.pg"), Glob("$PageDir/.*.pg")) {
|
||||
my $locked = RequestLockDir('index', undef, undef, -f $IndexFile);
|
||||
foreach (bsd_glob("$PageDir/*.pg"), bsd_glob("$PageDir/.*.pg")) {
|
||||
next unless m|/.*/(.+)\.pg$|;
|
||||
my $id = $1;
|
||||
utf8::decode($id);
|
||||
push(@IndexList, $id);
|
||||
$IndexHash{$id} = 1;
|
||||
}
|
||||
@@ -3407,11 +3418,6 @@ sub DoSearch {
|
||||
if (GetParam('preview', '')) { # Preview button was used
|
||||
print GetHeader('', Ts('Preview: %s', $string . " → " . $replacement));
|
||||
print $q->start_div({-class=>'content replacement'});
|
||||
print GetFormStart(undef, 'post', 'replace');
|
||||
print GetHiddenValue('search', $string);
|
||||
print GetHiddenValue('replace', $replacement);
|
||||
print GetHiddenValue('delete', GetParam('delete', 0));
|
||||
print $q->submit(-value=>T('Go!')) . $q->end_form();
|
||||
@results = ReplaceAndDiff($re, UnquoteHtml($replacement));
|
||||
} else {
|
||||
print GetHeader('', Ts('Replaced: %s', $string . " → " . $replacement));
|
||||
@@ -3448,9 +3454,9 @@ sub PageIsUploadedFile {
|
||||
return if $OpenPageName eq $id;
|
||||
if ($IndexHash{$id}) {
|
||||
my $file = GetPageFile($id);
|
||||
open(my $FILE, '<:encoding(UTF-8)', encode_utf8($file))
|
||||
or ReportError(Ts('Cannot open %s', GetPageFile($id))
|
||||
. ": $!", '500 INTERNAL SERVER ERROR');
|
||||
utf8::encode($file); # filenames are bytes!
|
||||
open(my $FILE, '<:encoding(UTF-8)', $file)
|
||||
or ReportError(Ts('Cannot open %s', $file) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
while (defined($_ = <$FILE>) and $_ !~ /^text: /) {
|
||||
} # read lines until we get to the text key
|
||||
close $FILE;
|
||||
@@ -3583,7 +3589,7 @@ sub SearchExtract {
|
||||
sub ReplaceAndSave {
|
||||
my ($from, $to) = @_;
|
||||
RequestLockOrError(); # fatal
|
||||
my @result = Replace($from, $to, 1, sub {
|
||||
my @result = Replace($from, $to, sub {
|
||||
my ($id, $new) = @_;
|
||||
Save($id, $new, $from . ' → ' . $to, 1, ($Page{host} ne $q->remote_addr()));
|
||||
});
|
||||
@@ -3593,7 +3599,7 @@ sub ReplaceAndSave {
|
||||
|
||||
sub ReplaceAndDiff {
|
||||
my ($from, $to) = @_;
|
||||
my @found = Replace($from, $to, 0, sub {
|
||||
my @found = Replace($from, $to, sub {
|
||||
my ($id, $new) = @_;
|
||||
print $q->h2(GetPageLink($id)), $q->div({-class=>'diff'}, ImproveDiff(DoDiff($Page{text}, $new)));
|
||||
});
|
||||
@@ -3609,7 +3615,7 @@ sub ReplaceAndDiff {
|
||||
}
|
||||
|
||||
sub Replace {
|
||||
my ($from, $to, $all, $func) = @_; # $func takes $id and $new text
|
||||
my ($from, $to, $func) = @_; # $func takes $id and $new text
|
||||
my $lang = GetParam('lang', '');
|
||||
my $num = GetParam('num', 10);
|
||||
my $offset = GetParam('offset', 0);
|
||||
@@ -3629,7 +3635,7 @@ sub Replace {
|
||||
};
|
||||
if (s/$from/$replacement->()/egi) { # allows use of backreferences
|
||||
push (@result, $id);
|
||||
$func->($id, $_) if $all or @result > $offset and @result <= $offset + $num;
|
||||
$func->($id, $_) if @result > $offset and @result <= $offset + $num;
|
||||
}
|
||||
}
|
||||
return @result;
|
||||
@@ -3785,7 +3791,7 @@ sub Save { # call within lock, with opened page
|
||||
my $revision = $Page{revision} + 1;
|
||||
my $old = $Page{text};
|
||||
my $olddiff = $Page{'diff-major'} == '1' ? $Page{'diff-minor'} : $Page{'diff-major'};
|
||||
if ($revision == 1 and IsFile($IndexFile) and not Unlink($IndexFile)) { # regenerate index on next request
|
||||
if ($revision == 1 and -e $IndexFile and not unlink($IndexFile)) { # regenerate index on next request
|
||||
SetParam('msg', Ts('Cannot delete the index file %s.', $IndexFile)
|
||||
. ' ' . T('Please check the directory permissions.')
|
||||
. ' ' . T('Your changes were not saved.'));
|
||||
@@ -3845,7 +3851,8 @@ sub MergeRevisions { # merge change from file2 to file3 into file1
|
||||
WriteStringToFile($name2, $file2);
|
||||
WriteStringToFile($name3, $file3);
|
||||
my ($you, $ancestor, $other) = (T('you'), T('ancestor'), T('other'));
|
||||
my $output = decode_utf8(`diff3 -m -L \Q$you\E -L \Q$ancestor\E -L \Q$other\E -- \Q$name1\E \Q$name2\E \Q$name3\E`);
|
||||
my $output = `diff3 -m -L \Q$you\E -L \Q$ancestor\E -L \Q$other\E -- \Q$name1\E \Q$name2\E \Q$name3\E`;
|
||||
utf8::decode($output); # needs decoding
|
||||
ReleaseLockDir('merge'); # don't unlink temp files--next merge will just overwrite.
|
||||
return $output;
|
||||
}
|
||||
@@ -3869,7 +3876,7 @@ sub DoMaintain {
|
||||
print GetHeader('', T('Run Maintenance')), $q->start_div({-class=>'content maintain'});
|
||||
my $fname = "$DataDir/maintain";
|
||||
if (not UserIsAdmin()) {
|
||||
if (IsFile($fname) and $Now - Modified($fname) < 0.5) {
|
||||
if ((-f $fname) and ((-M $fname) < 0.5)) {
|
||||
print $q->p(T('Maintenance not done.') . ' ' . T('(Maintenance can only be done once every 12 hours.)')
|
||||
. ' ', T('Remove the "maintain" file or wait.')), $q->end_div();
|
||||
PrintFooter();
|
||||
@@ -3912,7 +3919,7 @@ sub DoMaintain {
|
||||
}
|
||||
if (opendir(DIR, $RssDir)) { # cleanup if they should expire anyway
|
||||
foreach (readdir(DIR)) {
|
||||
Unlink("$RssDir/$_") if $Now - Modified($_) > $RssCacheHours * 3600;
|
||||
unlink "$RssDir/$_" if $Now - (stat($_))[9] > $RssCacheHours * 3600;
|
||||
}
|
||||
closedir DIR;
|
||||
}
|
||||
@@ -3945,8 +3952,8 @@ sub DeletePage { # Delete must be done inside locks.
|
||||
ValidIdOrDie($id);
|
||||
AppendStringToFile($DeleteFile, "$id\n");
|
||||
foreach my $name (GetPageFile($id), GetKeepFiles($id), GetKeepDir($id), GetLockedPageFile($id), $IndexFile) {
|
||||
Unlink($name) if IsFile($name);
|
||||
RemoveDir($name) if IsDir($name);
|
||||
unlink $name if -f $name;
|
||||
rmdir $name if -d $name;
|
||||
}
|
||||
ReInit($id);
|
||||
delete $IndexHash{$id};
|
||||
@@ -3961,10 +3968,10 @@ sub DoEditLock {
|
||||
if (GetParam("set", 1)) {
|
||||
WriteStringToFile($fname, 'editing locked.');
|
||||
} else {
|
||||
Unlink($fname);
|
||||
unlink($fname);
|
||||
}
|
||||
utime time, time, $IndexFile; # touch index file
|
||||
print $q->p(IsFile($fname) ? T('Edit lock created.') : T('Edit lock removed.'));
|
||||
print $q->p(-f $fname ? T('Edit lock created.') : T('Edit lock removed.'));
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
@@ -3977,10 +3984,10 @@ sub DoPageLock {
|
||||
if (GetParam('set', 1)) {
|
||||
WriteStringToFile($fname, 'editing locked.');
|
||||
} else {
|
||||
Unlink($fname);
|
||||
unlink($fname);
|
||||
}
|
||||
utime time, time, $IndexFile; # touch index file
|
||||
print $q->p(IsFile($fname) ? Ts('Lock for %s created.', GetPageLink($id))
|
||||
print $q->p(-f $fname ? Ts('Lock for %s created.', GetPageLink($id))
|
||||
: Ts('Lock for %s removed.', GetPageLink($id)));
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user