diff --git a/css/alex-2012.css b/css/alex-2012.css index 3d6fbb8d..264edae7 100644 --- a/css/alex-2012.css +++ b/css/alex-2012.css @@ -149,9 +149,6 @@ div.refer { line-height: 13pt; } -div.refer a:first-child:before { content: "" } -div.refer a:before { content: "• " } - div.message { background-color:#fee; color:#000; diff --git a/modules/fix-encoding.pl b/modules/fix-encoding.pl index be82d00a..28f2d932 100644 --- a/modules/fix-encoding.pl +++ b/modules/fix-encoding.pl @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -$ModulesDescription .= '

fix-encoding.pl

'; +$ModulesDescription .= '

fix-encoding.pl, see Fix Encoding

'; $Action{'fix-encoding'} = \&FixEncoding; diff --git a/modules/namespaces.pl b/modules/namespaces.pl index fbb9cfb5..2d75b0c8 100644 --- a/modules/namespaces.pl +++ b/modules/namespaces.pl @@ -376,7 +376,7 @@ sub NewNamespaceBrowsePage { #REDIRECT into different namespaces my ($id, $raw, $comment, $status) = @_; OpenPage($id); - my ($text, $revision) = GetTextRevision(GetParam('revision', '')); + my ($text, $revision) = GetTextRevision(GetParam('revision', ''), 1); my $oldId = GetParam('oldid', ''); if (not $oldId and not $revision and (substr($text, 0, 10) eq '#REDIRECT ') and (($WikiLinks and $text =~ /^\#REDIRECT\s+(($InterSitePattern:)?$InterLinkPattern)/) diff --git a/modules/private-pages.pl b/modules/private-pages.pl new file mode 100644 index 00000000..894b2c32 --- /dev/null +++ b/modules/private-pages.pl @@ -0,0 +1,128 @@ +# Copyright (C) 2012 Alex Schroeder +# +# 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 . + +=head1 Private Pages Extension + +This module allows you to hide the content of particular pages in Oddmuse. +Unlike the I, 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 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 + +$ModulesDescription .= '

private-pages.pl, see 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') . ']'); +} + +*OldPrivatePagesOpenPage = *OpenPage; +*OpenPage = *NewPrivatePagesOpenPage; + +sub NewPrivatePagesOpenPage { + OldPrivatePagesOpenPage(@_); + if (PrivatePageLocked($Page{text})) { + %Page = (); # reset everything + $NewText = PrivatePageMessage(); + } + return $OpenPageName; +} + +*OldPrivatePagesGetPageContent = *GetPageContent; +*GetPageContent = *NewPrivatePagesGetPageContent; + +sub NewPrivatePagesGetPageContent { + my $text = OldPrivatePagesGetPageContent(@_); + if (PrivatePageLocked($text)) { + return PrivatePageMessage(); + } + return $text; +} + +push(@MyRules, \&PrivatePageRule); + +sub PrivatePageRule { + if (pos == 0 && m/\G#PASSWORD.*\n/gc) { + return ''; + } + return undef; +} + +*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(); +} diff --git a/t/private-pages.t b/t/private-pages.t new file mode 100644 index 00000000..664b8aac --- /dev/null +++ b/t/private-pages.t @@ -0,0 +1,93 @@ +# Copyright (C) 2012 Alex Schroeder +# +# 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 . + +require 't/test.pl'; +package OddMuse; +use Test::More tests => 27; + +clear_pages(); +add_module('private-pages.pl'); + +# create password protected page: can't read it without password! +test_page(update_page('Privat', "#PASSWORD foo\nSo many secrets remain untold.\n"), + 'This page is password protected'); + +# can't update password protected page +update_page('Privat', "#PASSWORD foo\nCats have secrets.\n"); +test_page($redirect, 'Status: 403'); + +# use password foo to update protected page: can't read it without password! +test_page_negative(update_page('Privat', "#PASSWORD foo\nCats have secrets.\n", undef, undef, 1), + 'Cats have secrets'); +test_page($redirect, 'Status: 302'); + +# read it with password +my $page = get_page('action=browse id=Privat pwd=foo'); +test_page_negative($page, 'This page is password protected'); +test_page($page, 'Cats have secrets'); + +# a keep file was created as well +ok(-f GetKeepFile('Privat', 1), 'Keep file exists'); + +# can't read old revisions without a password +test_page_negative(get_page('action=browse id=Privat revision=1'), + 'Cats have secrets'); + +# read old revisions with password +test_page(get_page('action=browse id=Privat revision=1 pwd=foo'), + 'So many secrets remain untold'); + +# can't see secrets when printing raw pages +my $page = get_page('action=browse raw=1 id=Privat pwd=foo'); +test_page_negative($page, 'This page is password protected'); +test_page($page, 'Cats have secrets'); + +# can't see summaries with secrets +my $page = get_page('action=rc raw=1 all=1'); +test_page($page, 'Privat'); +test_page_negative($page, 'secret'); + +# can't search for secrets without a password +my $page = get_page('search=cats'); +test_page($page, '0 pages found'); +test_page_negative($page, "Privat"); + +# search finds secrets with password +my $page = get_page('search=cats pwd=foo'); +test_page($page, '1 pages? found', + 'Privat', 'Cats have secrets'); + +# can't edit a private page without a password +my $page = get_page('action=edit id=Privat'); +test_page($page, 'Editing not allowed'); +test_page_negative($page, 'Cats have secrets'); + +# can edit a private page with a password +my $page = get_page('action=edit id=Privat pwd=foo'); +test_page_negative($page, 'This page is password protected'); +test_page($page, 'Cats have secrets'); + +# can't edit an old revision of a private page without a password +my $page = get_page('action=edit id=Privat revision=1'); +test_page($page, 'Editing not allowed'); +test_page_negative($page, 'secret'); + +# can't just post changes to a private page without a password +my $page = get_page('title=Privat text=YaddaYadda revision=1'); +test_page($page, 'Editing not allowed'); +test_page_negative($page, 'secret'); + +# can't include them +test_page_negative(update_page('Publik', ''), + 'Cats have secrets'); diff --git a/wiki.pl b/wiki.pl index aa8c4192..465b1b5f 100755 --- a/wiki.pl +++ b/wiki.pl @@ -2495,7 +2495,7 @@ sub PrintHtmlDiff { if ($type == 1) { $old = $Page{lastmajor} - 1; ($text, $new) = GetTextRevision($Page{lastmajor}, 1) - unless $new or $Page{lastmajor} == $Page{revision}; + unless $new or $Page{lastmajor} == $Page{revision}; } elsif ($new) { $old = $new - 1; } else {