사용하고 말고는 자기 맘입니다만, 동작의 정확성을 보장하지 않습니다. 이 패치를 사용하고 싶다면 아래의 테스트 요청에 협조해 주세요~ :-)
페이지 숨김 기능 추가
- 특정한 페이지를 숨겨서, 관리자 권한을 갖고 있는 사용자만 열람할 수 있도록 한다.
- WikiX 에 있는 Hide/Unhide page 의 동작을 흉내냄
- 필수 요구 사항 :
- 기존에 패치했던 것들과 맞물려 있기 때문에, 이 패치만 따로 적용하기는 쉽지 않을 듯하다.
- 선택 요구 사항 : 없음
사용법
- 관리자 권한이 있는 경우, 편집 가이드 또는 전체 페이지 목록 화면에서 각 페이지 옆에 "숨김|드러냄" 링크가 표시되고, 클릭하면 각각 페이지를 숨기거나, 다시 드러낼 수 있다.
- wiki.pl?action=pagehide&set=1&id=페이지이름 의 형식으로 사용할 수도 있다. set=1 이면 숨김, 0 이면 해제이다.
- 관리자 권한이 있는 사용자에게는 편집 가이드에 "(숨겨짐)"이라고 표시되는 것을 제외하고는 아무런 차이가 없다.
- 관리자 권한이 없는 사용자가 브라우저로 볼 경우, 감춰진 페이지는 다음과 같이 동작한다. 즉 최대한 그 페이지의 내용에 대해 짐작할 수 없도록 하였다.
- 페이지 목록에 나오지 않음. 전체 페이지 숫자도 관리자가 볼 때와 다른 사람이 볼 때 서로 다르게 나옴
- 페이지 내용 보기 (wiki.pl?페이지이름 또는 wiki.pl?action=browse) 가 금지됨
- 역링크 탐색 (wiki.pl?reverse=) 이 금지됨
- 변경내역을 보는 것 (wiki.pl?action=history) 이 금지됨
- full link list 에 나오지 않음 - 감춰진 페이지 자체도 나오지 않고, 다른 페이지가 감춰진 페이지를 링크한 경우도 그 링크는 표시되지 않는다.
- 페이지 수정 또는 소스 보기 (wiki.pl?action=edit) 가 금지됨
- 최근 변경 내역에 나오지 않음
- include 류의 매크로로 삽입되지 않음
- 페이지 목록을 출력하는 각종 매크로들에서, 감춰진 페이지는 아예 없는 것처럼 간주하여 동작한다. (이 부분의 동작이 확실한지가 제일 미심쩍은 상태임)
- 단, 다음의 경우는 예외이다.
- 다른 페이지의 본문에서 감춰진 페이지를 링크한 경우 그 링크 자체는 그냥 보임
- 감춰진 페이지들의 목록은 config.pl 에서 $HiddenPageFile 로 지정된 화일에 저장된다. 이 화일은 wiki.pl 이 실행되는 순간 읽히게 된다. 기본값은 다음과 같다.
$HiddenPageFile = "$DataDir/hidden";
- 주의 사항 :
- 페이지를 편집하고 저장하는 시점에서 숨김 여부를 체크할 수는 없다. 즉 일단 저장한 후에 숨김 설정을 지정해야 한다는 얘기다. (WikiX 처럼 편집 화면에 체크 박스를 별도로 둘까 했는데, 더 이상 일을 벌리고 싶지 않아서 관뒀음) 따라서 저장한 시점과 숨기는 시점 사이의 순간에 다른 누군가가 들여다 볼 수가 있다. 해결책은 뭐... 일단 별 내용 없이 만들어서 저장하고, 숨긴 후에, 그 다음에 제대로 된 내용을 집어넣으면 된다. ;-)
- 숨겨진 페이지를 삭제할 경우, rclog 화일에서도 삭제되고 keep 화일도 삭제된다. 즉 /페이지삭제시keep화일보존 패치의 동작이 적용되지 않는다. 삭제하면 되살릴 수 없으니 주의.
- 부작용 : 아직 모름
- 위에서 언급했듯이, 사용자가 관리자 권한이 있는 경우와 없는 경우에, 기존의 매크로들이 각각 정확하게 동작하는지 확신을 하지 못하고 있다. 주인장이 테스트하는 동안에는 별 문제가 없어 보였다.
- 정말로 감춰진 페이지의 내용을 볼 수 없는지도 사실 확신이 서지 않는다. 아래 Notes 란에 있는 "테스트 요청"을 읽어 볼 것
- 페이지 목록을 다루는 많은 루틴에서 숨김 여부를 일일이 확인하기 때문에 성능에 저하가 있다. (검사를 할 때 화일을 읽는 게 아니라 해쉬변수값을 보는 것이기 때문에 크게 저하되지는 않을 것으로 기대하지만...)
config.pl 에 추가
$HiddenPageFile = "$DataDir/hidden";
wiki.pl 수정
필요한 환경 변수와 전역 변수 추가
...
use vars qw(
$UserGotoBar $UserGotoBar2 $UserGotoBar3 $UserGotoBar4
$ConfigFile $SOURCEHIGHLIGHT @SRCHIGHLANG $LinkFirstChar
$EditGuideInExtern $SizeTopFrame $SizeBottomFrame
$LogoPage $CheckTime $LinkDir $IconDir $CountDir $UploadDir $UploadUrl
$HiddenPageFile # $HiddenPageFile 추가
);
...
use vars qw(%RevisionTs $FS_lt $FS_gt $StartTime $Sec_Revision $Sec_Ts
$ViewCount $AnchoredFreeLinkPattern %UserInterest %HiddenPage);
...
wiki.pl 이 구동될 때 숨겨진 페이지들의 목록을 읽어 온다.
sub InitRequest {
...
&InitCookie();
my ($status, $data) = &ReadFile($HiddenPageFile);
if ($status) {
%HiddenPage = split(/$FS1/, $data, -1);
}
return 1;
}
숨겨진 페이지는 브라우징을 금지
sub BrowsePage {
my ($fullHtml, $oldId, $allDiff, $showDiff, $openKept);
my ($revision, $goodRevision, $diffRevision, $newText);
if (&PageIsHidden($id)) {
print &GetHeader($id, &QuoteHtml($id), $oldId);
print Ts('%s is a hidden page', $id);
print &GetCommonFooter();
return;
}
&OpenPage($id);
&OpenDefaultText();
...
}
최근 변경 내역에서도 감춤
sub GetRcHtml {
...
foreach $rcline (@outrc) {
($ts, $pagename, $summary, $isEdit, $host, $kind, $extraTemp)
= split(/$FS3/, $rcline);
next if ((!$all) && ($ts < $changetime{$pagename}));
next if (($idOnly ne "") && ($idOnly ne $pagename));
next if (&PageIsHidden($pagename));
...
}
변경내역 열람도 금지
sub DoHistory {
my ($id) = @_;
my ($html, $canEdit, $row, $newText);
if (&PageIsHidden($id)) {
print &GetHeader("",&QuoteHtml(Ts('History of %s', $id)), "");
print Ts('%s is a hidden page', $id);
print &GetCommonFooter();
return;
}
print &GetHeader("",&QuoteHtml(Ts('History of %s', $id)), "") . "<br>";
...
}
편집 가이드에 페이지의 숨겨짐 여부와, 숨김/드러냄 메뉴를 표시
sub GetEditGuide {
my ($id, $rev) = @_;
my $result = "\n<HR class='footer'>\n<DIV class='editguide'>";
if (&UserIsAdmin()) {
...
$result .= " | " . &ScriptLink("action=pagelock&set=0&id=" . $id, T('unlock'));
$result .= " | ";
if (defined($HiddenPage{$id})) {
$result .= T('(hidden)') . " | ";
}
$result .= &ScriptLink("action=pagehide&set=1&id=" . $id, T('hide'));
$result .= " | " . &ScriptLink("action=pagehide&set=0&id=". $id, T('unhide'));
}
...
}
include, includenotoc, includeday 매크로로 내용을 가져오지 못하게 함
sub MacroInclude {
...
my $fname = &GetPageFile($name);
if (!(-f $fname)) {
return "";
}
if (&PageIsHidden($name)) {
return "";
}
my $data = &ReadFileOrDie($fname);
my %SubPage = split(/$FS1/, $data, -1);
...
}
숨겨진 페이지는 당연히 편집도 불가능하다
sub UserCanEdit {
my ($id, $deepCheck) = @_;
if (($id ne "") && (&PageIsHidden($id))) {
return 0;
}
if (($id ne "") && (-f &GetLockedPageFile($id))) {
...
}
전체 페이지 목록을 가져오는 AllPagesList 함수에서 숨겨진 페이지는 반환하지 않게 한다. (따라서 다른 매크로나, 그 외에 전체 페이지 목록을 가져와서 작업을 하는 모든 동작에 영향을 미친다)
sub AllPagesList {
my ($rawIndex, $refresh, $status);
if (!$UseIndex) {
return &GetNotHiddenPages(&GenerateAllPagesList());
}
$refresh = &GetParam("refresh", 0);
if ($IndexInit && !$refresh) {
return &GetNotHiddenPages(@IndexList);
}
if ((!$refresh) && (-f $IndexFile)) {
($status, $rawIndex) = &ReadFile($IndexFile);
if ($status) {
%IndexHash = split(/\s+/, $rawIndex);
@IndexList = sort(keys %IndexHash);
$IndexInit = 1;
return &GetNotHiddenPages(@IndexList);
}
}
}
@IndexList = ();
%IndexHash = ();
@IndexList = &GenerateAllPagesList();
foreach (@IndexList) {
$IndexHash{$_} = 1;
}
$IndexInit = 1;
&RequestIndexLock() or return @IndexList;
&WriteStringToFile($IndexFile, join(" ", %IndexHash));
&ReleaseIndexLock();
return &GetNotHiddenPages(@IndexList);
}
action=pagehide 액션 처리
sub DoOtherRequest {
...
} elsif ($action eq "deleteuploadedfiles") {
&DoDeleteUploadedFiles();
} elsif ($action eq "pagehide") {
&DoPageHide();
} else {
&ReportError(Ts('Invalid action parameter %s', $action));
}
return;
}
...
}
페이지 편집을 금함
sub DoEdit {
...
$editCols = &GetParam("editcols", 65);
print &GetHeader('', &QuoteHtml($header), '');
if (&PageIsHidden($id)) {
print Ts('%s is a hidden page', $id);
print &GetCommonFooter();
return;
}
if (!$canEdit) {
if (&UserIsBanned()) {
...
}
숨겨진 페이지의 역링크를 찾는 액션도 금함
sub DoReverse {
...
&DoIndex();
return;
}
print &GetHeader('', &QuoteHtml(Ts('Links to %s', $string)), '');
if (&PageIsHidden($string)) {
print Ts('%s is a hidden page', $string);
print &GetCommonFooter();
return;
}
print '<br>';
foreach $pagelines (&GetFullLinkList("page=1&inter=1&unique=1&sort=1&exists=2&empty=0&reverse=$string")) {
...
}
페이지 목록을 인자로 받아서 색인을 나누어 출력하는 함수. 여기서 감춰진 페이지는 다시 한 번 걸러낸다. (보통은 이 함수로 들어오기 전에 이미 걸러져 있겠지만) 또한 잠금/해제와 마찬가지로 각 페이지 이름 오른쪽에 숨김/드러냄 메뉴를 표시한다.
sub PrintPageList {
...
$count2 = 0;
foreach $pagename(@_) {
next if (&PageIsHidden($pagename));
until (
$pagename lt @indexSearch[$count]
&& ($count == 0 || $pagename gt @indexSearch[$count-1])
) {
...
if (&UserIsAdmin()) {
if (-f &GetLockedPageFile($pagename)) {
print " " . T('(locked)');
}
print " | " . &ScriptLink("action=pagelock&set=1&id=" . $pagename, T('lock'));
print " | " . &ScriptLink("action=pagelock&set=0&id=" . $pagename, T('unlock'));
if (defined($HiddenPage{$pagename})) {
print " | " . T('(hidden)');
}
print " | " . &ScriptLink("action=pagehide&set=1&id=" . $pagename, T('hide'));
print " | " . &ScriptLink("action=pagehide&set=0&id=" . $pagename, T('unhide'));
}
print $q->br;
print "\n";
}
}
숨겨진 페이지를 삭제할 경우, hidden 화일에서도 엔트리를 제거
sub DeletePage {
$fname = &GetCountFile($page);
unlink ($fname) if (-f $fname);
if (defined($HiddenPage{$page})) {
&EditRecentChanges(1, $page, "");
$fname = $KeepDir . "/" . &GetPageDirectory($page) . "/$page.kp";
unlink($fname) if (-f $fname);
delete $HiddenPage{$page};
&SaveHiddenPageFile();
}
$fname = &GetLinkFile($page);
unlink($fname) if (-f $fname);
...
}
숨겨진 페이지의 이름을 변경할 경우, hidden 화일의 엔트리도 같이 변경
sub RenamePage {
...
&CreatePageDir($CountDir, $new);
rename($oldcnt, $newcnt) || die "error while renaming count file";
}
if (defined($HiddenPage{$old})) {
delete $HiddenPage{$old};
$HiddenPage{$new} = "1";
&SaveHiddenPageFile();
}
my ($oldlink, $newlink);
$oldlink = &GetLinkFile($old);
...
}
페이지 이름을 인자로 받아서, 그 페이지 안에 있는 링크들의 목록을 반환하는 함수. 만일 인자로 받은 페이지 자체가 숨겨진 페이지라면 NULL 을 반환한다. 그렇지 않은 경우, 반환되는 링크 목록 중에서 숨겨진 페이지는 제외하고 반환한다.
sub GetPageLinksFromFile {
...
my ($name, $pagelink, $interlink, $urllink) = @_;
my ($status, $data, %links, @result, $fname);
if (&PageIsHidden($name)) {
return;
}
@result = ();
$fname = &GetLinkFile($name);
...
%links = split($FS1, $data, -1);
push (@result, &GetNotHiddenPages(split($FS2, $links{'pagelinks'}, -1))) if ($pagelink);
push (@result, split($FS2, $links{'interlinks'}, -1)) if ($interlink);
push (@result, split($FS2, $links{'urllinks'}, -1)) if ($urllink);
return @result;
}
action=pagehide 를 처리하는 함수. 통채로 추가
sub DoPageHide {
my ($id);
print &GetHeader('', T('Hide or Unhide page'), '');
return if (!&UserIsAdminOrError());
$id = &GetParam("id", "");
if ($id eq "") {
print '<p>', T('Missing page id to hide/unhide');
return;
}
return if (!&ValidIdOrDie($id));
if (&GetParam("set", 1)) {
$HiddenPage{$id} = "1";
} else {
delete $HiddenPage{$id};
}
if (!(&SaveHiddenPageFile())) {
print T('Hiding/Unhiding page failed');
print &GetCommonFooter();
return;
}
if (defined ($HiddenPage{$id})) {
print '<p>', Ts('%s is hidden.', $id), '<br>';
} else {
print '<p>', Ts('%s is revealed.', $id), '<br>';
}
print &GetCommonFooter();
}
페이지 이름을 인자로 받아서, 숨겨진 페이지인지 아닌지를 반환하는 함수. 통채로 추가
sub PageIsHidden {
my ($id) = @_;
if ((!(defined $HiddenPage{$id})) || (&UserIsAdmin())) {
return 0;
} else {
return 1;
}
}
페이지 이름들의 목록을 배열로 받아서, 그 중에 숨겨진 페이지를 제외하고 나머지를 반환하는 함수. 통채로 추가
sub GetNotHiddenPages {
my (@pages) = @_;
my @notHiddenPages = ();
foreach (@pages) {
push (@notHiddenPages, $_) if (!(&PageIsHidden($_)));
}
return @notHiddenPages;
}
페이지를 숨김 설정하거나 해제할 경우에 hidden 화일을 갱신하는 함수. 통채로 추가
sub SaveHiddenPageFile {
my $data;
$data = join($FS1, %HiddenPage);
&WriteStringToFile($HiddenPageFile, $data);
chmod(0644, $HiddenPageFile);
return 1;
}
가장 최근에 수정된 페이지가 숨겨진 페이지이고, 로그인하지 않은 사용자가 최근변경내역을 볼 경우, "이 날 이후의 변경 리스트 보기" 링크에 숨겨진 페이지의 수정 시각이 노출되는 문제 수정:
sub DoRc {
...
for (my $i = $#fullrc; $i >= 0; $i--) {
my ($ts, $page) = split(/$FS3/, $fullrc[$i]);
$lastTs = $ts, last if not PageIsHidden($page);
}
translations/korean.pl 화일에 추가
(hidden)
(숨겨짐)
hide
숨김
unhide
드러냄
Hide or Unhide page
페이지 숨김 설정 또는 해제
Missing page id to hide/unhide
숨김 또는 해제할 페이지 이름이 누락되었습니다...
%s is hidden.
%s 페이지의 숨김 속성을 설정했습니다.
%s is revealed.
%s 페이지의 숨김 속성을 제거했습니다.
%s is a hidden page
%s 는 관리자가 감춘 페이지입니다.
Hiding/Unhiding page failed
페이지 감춤/드러냄 명령에 실패했습니다
추가 업데이트 내역
- ext2.25a - DoRc 함수의 소소한 문제 수정
Notes
테스트 요청
방문객들에게 테스트를 요청합니다.
현재 이 홈페이지의 연습장 에는 HideTest 라는 페이지가 링크되어 있습니다. 왼쪽의 단어 때문에 이 페이지에도 링크가 되어 버렸으니, 두 군데서 링크하고 있고, HideTest 의 정보는 다음과 같습니다.
- 최종 편집일 : 2003-4-7 2:00 am
- 최종 수정본 : 2 번째
- 페이지의 본문 :
-
이 페이지는 페이지 숨김을 테스트하기 위한 페이지입니다.
동해물과 백두산이 마르고 닳도록
[[연습장]]
GyparkWiki
http://gypark.pe.kr
GyparkWiki:UseModWiki소스수정
이 HideTest 의 내용을 알아낼 방법이 없도록 최대한 감춘다고는 했는데, 정말 모든 경우를 다 봉쇄한 것인지 확신이 서지 않네요. 여기 오시는 분들께서 10분 정도만 시간을 내어서 이런 저런 테스트좀 해보아 주시면 감사하겠습니다.
당연히... 평범하게 링크를 따라가는 길은 다 막혀 있을 테니까... 브라우저의 URL 창에 직접 이런 저런 action 등을 넣어 보거나, 현재 있는 매크로 (매크로 목록은 UseModWiki소스수정에 다 나와 있으니..) 를 사용해 보아야 되겠죠. 텍스트 검색, 역링크, diff 등 수단과 방법을 가리지말고 저 HideTest 페이지 안의 내용을 일부라도 알아낼 수 있는 방법을 발견하시면 제보해 주시면 됩니다. (HideTest 페이지 내에는 연습장으로 가는 링크가 있다.. 내용 중에 "동해물과" 라는 텍스트가 있다.. 등등)
그 외
wantedpages 매크로에서 빈페이지로 표시되지 않고 전체링크로 표시됩니다. 이것은 그다지 상관없을듯... - Bab2
- 역시 Bab2님! 역시 보기 싫은 부분이 생겨나는군요. 이건 매크로 쪽을 손을 볼지 더 근본적인 곳을 손을 볼 지 고민하게 만드는군요..
- 이젠 안 나오죠? :-)
- 예. ^^
그럭저럭 마무리 정리를 하고 일단 공개는 합니다. 처음에는 간단하다고 생각하고 손을 대었는데 신경쓸 곳이 한 두 군데가 아니더군요. 그냥 브라우징, 에디팅, 변경내역 등을 보는 것만 금지시켜 버릴까 했습니다만, (그러면 훨씬 간단하겠죠) 숨기려면 확실히 숨기는 것이 더 낫다고 생각했고, 특히나 역링크나 full link list 등을 통해서 숨겨진 페이지 내에 있는 링크나 URL 을 알 수 있다면 숨기는 의미가 너무 없다고 판단해서 일을 크게 벌렸습니다. 현재 Bab2 님께서 열심히 테스트해주시고 계신 듯 한데, 감사의 말씀을 드립니다. 다른 분들도 한번 도전 좀 해봐주세요~ 헛점을 찾아 감춰진 내용을 알아내기... 재밌지 않나요? ;-)
- 음..한달쯤 전에 이 기능이 올라왔다면..정말 쌍수를 들고 반겼을듯..하나..(개인적인 부분과 회사업무등과 관련된 위키페이지의 필요성이 있어서..) 현재는 그냥 따로 비공개 위키를 깔아서 쓰고있습니다. 아무래도..기능이 복잡해질것같아서..그래도 여기서 테스트는 열심히 해보죠.. :)
- 예, 비공개할 페이지가 많을 경우는 그냥 따로 두는 게 낫죠. 이 기능은 공개된 페이지들을 이것 저것 링크해야 되지만 내용은 감춰야 될 경우 필요한 건데... 제 경우는 제 다이어리나 잡생각을 여기에 같이 끄적일까 해서 만들었지요. 한 달만 빨리 만들 것 그랬군요. ^^
서브페이지는 제가 테스트 할 수 없겠군요. ㅡ.ㅡa
숨겨진 페이지 목록을 저장하는 "hidden" 화일의 퍼미션을 600 에서 644 로 설정하도록 변경했습니다. 데이타 디렉토리를 백업할 때 저 화일을 읽을 수 없는 문제가 있었네요.
Raymundo, this feature inspired me (beside a feature from ancient cwick wiki), a granular visibility on nodes. Something like this (I choose ^ markup, for instance):
sub CommonMarkup )
...
s/\^admn(.*?)\^/&StoreRaw('<div class=visibility>') . $1 . '<\/div>'/gei if (!&UserIsAdmin());
s/\^edit(.*?)\^/&StoreRaw('<div class=visibility>') . $1 . '<\/div>'/gei if (!&UserIsEditor());
and css:
div.visibility {
visibility:hidden;
position: absolute;
top: 0px; left: 0px;
z-index: -1;
}
It can be completed with:
$Text{'text'} =~ s/\^admn(.*?)\^//gei if (not &UserIsAdmin());
$Text{'text'} =~ s/\^edit(.*?)\^//gei if (not &UserIsEditor());
in own actions as: find, diff, raw in in coherence.
Great!
- In fact, there is similar macro "adminonly".
http://gypark.pe.kr/wiki/UseModWiki%EC%86%8C%EC%8A%A4%EC%88%98%EC%A0%95/%EC%82%AC%EC%9A%A9%EC%9E%90%EC%B6%94%EA%B0%80%EB%A7%A4%ED%81%AC%EB%A1%9C#H_5 (Sorry, written in Korean, as always)
Thank you for your code, anyway. :-)
you're right; even in korean, the "adminonly" particle was a clue ...
(Other feature could be granular edition on node ... ).
Thanks.
Hi, Raymundo et col.:
A question if the answer is not hard work for you:
In UseMod, on sub DeletePage:
&EditRecentChanges(1, $page, "") if ($doRC); # Delete page <=========
# Currently don't do anything with page text
is at the bottom, but in UseModKr, is here:
if (defined($HiddenPage{$page})) {
&EditRecentChanges(1, $page, ""); <=====================================
$fname = $KeepDir . "/" . &GetPageDirectory($page) . "/$page.kp";
unlink($fname) if (-f $fname);
delete $HiddenPage{$page};
&SaveHiddenPageFile();
}
Why, please?.
Big thanks, and good holidays.
In UseMod, after a page has been deleted, visitors couldn't see the content of the page.
When I made "bookmark" feature, I decided to improve it, so that visitors can see the content of a deleted page. This was done by removing the line "EditRecentChanges(..)". This subroutine removes the footprint(?) of deleted page in rcfile.
After a while, I made this "page hiding" feature. Then the case became opposite. I had to use that subroutine again, because the footprint of hidden page should be removed in rcfile, so that any other visitors couldn't notice that hidden page.
This is why the line is now in the if-block. I wish my explanation could help you.
Sometimes, I was thinking, when deleting hidden page, don't delete $HiddenPage{$page} (and don't delete rcline (footprint) when delete any page, this last thing for hidden page or not), so doesn't show in Recent Changes because the other filter: "next if PageIsHidden{$page}" on GetRc.
If later, someone wishes edit a "new" page with the same title, then, both assume, (last editor and previous editor), "refresh" the old footprints in Recent Changes about that page, because is now when the old $HiddenPage($page) will be deleted (when the second, third, etc creation). Then, the "new" page inherits the rcline.
I have in mind a conservative information wiki (of any thing) but it's only a design point of view.
Thanks for reading.
Big thanks. Much better now with EditRecentChanges.
위키위키분류