21 번째 수정본
사용하고 말고는 자기 맘입니다만, 동작의 정확성을 보장하지 않습니다. 이 패치를 사용하고 싶다면 아래의 테스트 요청에 협조해 주세요~ :-)
페이지 숨김 기능 추가
- 특정한 페이지를 숨겨서, 관리자 권한을 갖고 있는 사용자만 열람할 수 있도록 한다.
- 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:
"<pre> if (defined($HiddenPage{$page})) {
# ì¨ê¸´ íì¼ì ê²½ì°ë keep íì¼ê³¼ rclog 를 ë¤ ì ê±°íë¤.
&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.
위키위키분류