2015-07-28 22:44:53 +02:00
|
|
|
|
# Copyright (C) 2012–2013 Alex Schroeder <alex@gnu.org>
|
2012-11-16 17:12:12 +01:00
|
|
|
|
#
|
|
|
|
|
|
# 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/>.
|
|
|
|
|
|
|
2015-03-27 03:01:01 +02:00
|
|
|
|
use strict;
|
2015-08-18 10:48:03 +02:00
|
|
|
|
use v5.10;
|
2015-03-27 03:01:01 +02:00
|
|
|
|
|
2012-11-16 17:12:12 +01:00
|
|
|
|
=head1 Private Pages Extension
|
|
|
|
|
|
|
|
|
|
|
|
This module allows you to hide the content of particular pages in Oddmuse.
|
|
|
|
|
|
Unlike the I<Hidden Pages Extension>, this is not based on the user's role of
|
|
|
|
|
|
editor or administrator. Instead, every page can have a different password by
|
|
|
|
|
|
beginning it with #PASSWORD XYZZY where XYZZY is the password required to read
|
|
|
|
|
|
it. Multiple passwords can be supplied, separated by spaces.
|
|
|
|
|
|
|
|
|
|
|
|
Note that all the meta information of the private page remains public: The
|
|
|
|
|
|
I<name> of the page, the fact that is has been edited, the author, the
|
|
|
|
|
|
revision, the content of past revisions that have not been protected by a
|
|
|
|
|
|
password all remain visible to other users.
|
|
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
|
|
|
|
=item * If you're protecting a comment page, people can still leave comments
|
|
|
|
|
|
-- they just can't read the resulting page.
|
|
|
|
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
|
|
|
|
=cut
|
|
|
|
|
|
|
2014-08-21 22:23:23 +02:00
|
|
|
|
AddModuleDescription('private-pages.pl', 'Private Pages Extension');
|
2012-11-16 17:12:12 +01:00
|
|
|
|
|
2015-07-03 11:57:29 +02:00
|
|
|
|
our (%IndexHash, %Page, $OpenPageName, $Now, @MyRules);
|
2015-03-27 03:01:01 +02:00
|
|
|
|
|
2012-11-16 17:12:12 +01:00
|
|
|
|
sub PrivatePageLocked {
|
|
|
|
|
|
my $text = shift;
|
|
|
|
|
|
my ($line) = split(/\n/, $text, 1);
|
|
|
|
|
|
my @token = split(/\s+/, $line);
|
|
|
|
|
|
my $lock = 0;
|
|
|
|
|
|
if (shift(@token) eq '#PASSWORD') {
|
|
|
|
|
|
my $pwd = GetParam('pwd', '');
|
|
|
|
|
|
$lock = 1;
|
|
|
|
|
|
foreach (@token) {
|
|
|
|
|
|
if ($pwd eq $_) {
|
|
|
|
|
|
$lock = 0;
|
2015-03-27 03:01:01 +02:00
|
|
|
|
last;
|
2012-11-16 17:12:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return $lock;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-11 23:41:33 +03:00
|
|
|
|
*OldPrivatePagesUserCanEdit = \&UserCanEdit;
|
|
|
|
|
|
*UserCanEdit = \&NewPrivatePagesUserCanEdit;
|
2012-11-16 17:12:12 +01:00
|
|
|
|
|
|
|
|
|
|
sub NewPrivatePagesUserCanEdit {
|
|
|
|
|
|
my ($id, $editing, @rest) = @_;
|
|
|
|
|
|
my $result = OldPrivatePagesUserCanEdit($id, $editing, @rest);
|
2012-11-16 18:38:27 +01:00
|
|
|
|
# bypass OpenPage and GetPageContent (these are redefined below)
|
|
|
|
|
|
if ($result > 0 and $editing and $IndexHash{$id}) {
|
2015-09-04 04:55:48 +03:00
|
|
|
|
my $data = ParseData(ReadFileOrDie(GetPageFile($id)));
|
|
|
|
|
|
if (PrivatePageLocked($data->{text})) {
|
2012-11-16 18:38:27 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
2012-11-16 17:12:12 +01:00
|
|
|
|
}
|
2012-11-16 18:38:27 +01:00
|
|
|
|
return $result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-03 11:57:29 +02:00
|
|
|
|
*OldPrivatePageNewText = \&NewText;
|
|
|
|
|
|
|
|
|
|
|
|
sub NewPrivatePageNewText {
|
2012-11-16 18:38:27 +01:00
|
|
|
|
return Ts('This page is password protected. If you know the password, you can %s. Once you have done that, return and reload this page.',
|
|
|
|
|
|
'[' . ScriptUrl('action=password') . ' '
|
|
|
|
|
|
. T('supply the password now') . ']');
|
2012-11-16 17:12:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-30 16:15:01 +02:00
|
|
|
|
# prevent unauthorized reading
|
|
|
|
|
|
|
2015-07-03 11:57:29 +02:00
|
|
|
|
# If we leave $Page{revision} set, PrintWikiToHTML will save the new
|
|
|
|
|
|
# PrivatePageMessage as the new page content. If we don't set $Page{revision},
|
|
|
|
|
|
# BrowsePage() will show NewText(). Therefore we need to override NewText(). If
|
|
|
|
|
|
# we have no $Page{ts}, PageDeletable will return 1. As a workaround, we set a
|
|
|
|
|
|
# timestamp. Aging of the page doesn't matter since the text starts with
|
|
|
|
|
|
# #PASSWORD and therefore cannot be the empty string or $DeletedPage.
|
2013-05-30 16:15:01 +02:00
|
|
|
|
|
2015-04-11 23:41:33 +03:00
|
|
|
|
*OldPrivatePagesOpenPage = \&OpenPage;
|
|
|
|
|
|
*OpenPage = \&NewPrivatePagesOpenPage;
|
2012-11-16 17:12:12 +01:00
|
|
|
|
|
|
|
|
|
|
sub NewPrivatePagesOpenPage {
|
|
|
|
|
|
OldPrivatePagesOpenPage(@_);
|
|
|
|
|
|
if (PrivatePageLocked($Page{text})) {
|
|
|
|
|
|
%Page = (); # reset everything
|
2013-05-30 16:15:01 +02:00
|
|
|
|
$Page{ts} = $Now;
|
2015-07-03 11:57:29 +02:00
|
|
|
|
*NewText = \&NewPrivatePageNewText;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
*NewText = \&OldPrivatePageNewText;
|
2012-11-16 17:12:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
return $OpenPageName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-30 16:15:01 +02:00
|
|
|
|
# prevent reading of page content by other code
|
|
|
|
|
|
|
2015-04-11 23:41:33 +03:00
|
|
|
|
*OldPrivatePagesGetPageContent = \&GetPageContent;
|
|
|
|
|
|
*GetPageContent = \&NewPrivatePagesGetPageContent;
|
2012-11-16 18:38:27 +01:00
|
|
|
|
|
|
|
|
|
|
sub NewPrivatePagesGetPageContent {
|
|
|
|
|
|
my $text = OldPrivatePagesGetPageContent(@_);
|
|
|
|
|
|
if (PrivatePageLocked($text)) {
|
2015-07-03 11:57:29 +02:00
|
|
|
|
return NewPrivatePageNewText();
|
2012-11-16 18:38:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
return $text;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-30 16:15:01 +02:00
|
|
|
|
# prevent reading of old revisions
|
|
|
|
|
|
|
2015-04-11 23:41:33 +03:00
|
|
|
|
*OldPrivatePagesGetTextRevision = \&GetTextRevision;
|
|
|
|
|
|
*GetTextRevision = \&NewPrivatePagesGetTextRevision;
|
2012-11-21 21:11:49 +01:00
|
|
|
|
|
|
|
|
|
|
sub NewPrivatePagesGetTextRevision {
|
2015-09-04 04:55:48 +03:00
|
|
|
|
my ($page, $revision) = OldPrivatePagesGetTextRevision(@_);
|
|
|
|
|
|
if (PrivatePageLocked($page->{text})) {
|
|
|
|
|
|
return ({text => NewPrivatePageNewText()}, $revision); # XXX faking a page object like this is not good
|
2012-11-21 21:11:49 +01:00
|
|
|
|
}
|
2015-09-04 04:55:48 +03:00
|
|
|
|
return wantarray ? ($page, $revision) : $page;
|
2012-11-21 21:11:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-30 16:15:01 +02:00
|
|
|
|
# hide #PASSWORD
|
|
|
|
|
|
|
2012-11-16 17:12:12 +01:00
|
|
|
|
push(@MyRules, \&PrivatePageRule);
|
|
|
|
|
|
|
|
|
|
|
|
sub PrivatePageRule {
|
2015-08-23 21:22:12 +03:00
|
|
|
|
if (pos == 0 && m/\G#PASSWORD.*\n/cg) {
|
2012-11-16 17:12:12 +01:00
|
|
|
|
return '';
|
|
|
|
|
|
}
|
2015-02-27 12:10:18 +02:00
|
|
|
|
return;
|
2012-11-16 17:12:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-30 16:15:01 +02:00
|
|
|
|
# prevent leaking of edit summary
|
|
|
|
|
|
|
2015-04-11 23:41:33 +03:00
|
|
|
|
*OldPrivatePagesGetSummary = \&GetSummary;
|
|
|
|
|
|
*GetSummary = \&NewPrivatePagesGetSummary;
|
2012-11-16 17:12:12 +01:00
|
|
|
|
|
|
|
|
|
|
sub NewPrivatePagesGetSummary {
|
|
|
|
|
|
my $text = GetParam('text', '');
|
|
|
|
|
|
if ($text and $text =~ /^#PASSWORD\b/
|
|
|
|
|
|
# no text means aftertext is set (leaving a comment)
|
|
|
|
|
|
or $Page{text} =~ /^#PASSWORD\b/) {
|
|
|
|
|
|
# if no summary was set, set something in order to avoid the default
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
return OldPrivatePagesGetSummary();
|
|
|
|
|
|
}
|