Files
oddmuse/modules/private-pages.pl
2014-08-21 22:23:23 +02:00

159 lines
4.7 KiB
Perl
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (C) 20122013 Alex Schroeder <alex@gnu.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
=head1 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 * This extension might not work in a mod_perl environment because it
sets C<$NewText> without ever resetting it.
=item * If you're protecting a comment page, people can still leave comments
-- they just can't read the resulting page.
=back
=cut
AddModuleDescription('private-pages.pl', 'Private Pages Extension');
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;
break;
}
}
}
return $lock;
}
*OldPrivatePagesUserCanEdit = *UserCanEdit;
*UserCanEdit = *NewPrivatePagesUserCanEdit;
sub NewPrivatePagesUserCanEdit {
my ($id, $editing, @rest) = @_;
my $result = OldPrivatePagesUserCanEdit($id, $editing, @rest);
# bypass OpenPage and GetPageContent (these are redefined below)
if ($result > 0 and $editing and $IndexHash{$id}) {
my %data = ParseData(ReadFileOrDie(GetPageFile($id)));
if (PrivatePageLocked($data{text})) {
return 0;
}
}
return $result;
}
sub PrivatePageMessage {
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') . ']');
}
# prevent unauthorized reading
# If we leave $Page{revision}, PrintWikiToHTML will save the new
# PrivatePageMessage as the new page content. If we delete
# $Page{revision}, the text shown will be based on $NewText. If we
# have no $Page{ts} and no $Page{text}, 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.
*OldPrivatePagesOpenPage = *OpenPage;
*OpenPage = *NewPrivatePagesOpenPage;
sub NewPrivatePagesOpenPage {
OldPrivatePagesOpenPage(@_);
if (PrivatePageLocked($Page{text})) {
%Page = (); # reset everything
$Page{ts} = $Now;
$NewText = PrivatePageMessage();
}
return $OpenPageName;
}
# prevent reading of page content by other code
*OldPrivatePagesGetPageContent = *GetPageContent;
*GetPageContent = *NewPrivatePagesGetPageContent;
sub NewPrivatePagesGetPageContent {
my $text = OldPrivatePagesGetPageContent(@_);
if (PrivatePageLocked($text)) {
return PrivatePageMessage();
}
return $text;
}
# prevent reading of old revisions
*OldPrivatePagesGetTextRevision = *GetTextRevision;
*GetTextRevision = *NewPrivatePagesGetTextRevision;
sub NewPrivatePagesGetTextRevision {
my ($text, $revision) = OldPrivatePagesGetTextRevision(@_);
if (PrivatePageLocked($text)) {
return (PrivatePageMessage(), $revision);
}
return ($text, $revision);
}
# hide #PASSWORD
push(@MyRules, \&PrivatePageRule);
sub PrivatePageRule {
if (pos == 0 && m/\G#PASSWORD.*\n/gc) {
return '';
}
return undef;
}
# prevent leaking of edit summary
*OldPrivatePagesGetSummary = *GetSummary;
*GetSummary = *NewPrivatePagesGetSummary;
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();
}