#!/usr/bin/perl
use lib "../lib";
use CGI::Carp 'fatalsToBrowser';
use Algorithm::Diff qw(traverse_sequences);
# use strict;
#
# yukiwiki.cgi - Yet another WikiWikiWeb clone.
#
# Copyright (C) 2000,2001 by Hiroshi Yuki.
#
# http://www.hyuki.com/yukiwiki/
#
# 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 2 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.
#
# $Id: wiki.cgi,v 1.5 2002/02/04 15:22:00 wakaba Exp $
##############################
my $version = "1.6.6";
##############################
# 単独テストのときには 1 にする。
my $testing = 0;
##############################
# 漢字ライブラリ
my $jcodelib = 'jcode.pl';
##############################
# 保存・表示の漢字コード
my $kanjicode = 'euc'; # 'sjis' 'euc'
my $charset = 'euc-jisx0213'; # 'Shift_JIS' 'EUC-JP'
##############################
# dbmopenが使えるなら1、使えないなら0
my $dbmopen = 0;
##############################
# データベース名(.pag, .dir, .dbなどは不要)
# $dbmopen = 1のときはデータベース名、
# $dbmopen = 0のときはディレクトリ名になる。
my $dbname = './wikidata';
my $diffdbname = './wikidiff';
##############################
# 修正者の氏名(自由に変更してください)
my $modifier = 'suika';
##############################
# 修正者のWebページ(自由に変更してください)
my $modifierlink = 'http://suika.fam.cx/';
##############################
# このページのURL
my $thisurl = 'wiki';
##############################
# 開始ページ名
my $toppage = 'HomePage';
##############################
# 最終更新ページ名
my $whatsnew = 'RecentChanges';
##############################
# 最終更新に掲載するページ数
my $maxnew = 50;
##############################
# アイコンファイル名(カラー版)
my $iconfile = '';
##############################
# アイコンファイル名(モノクロ版)
# my $iconfile = '';
##############################
# ページを変更したときにtouchするファイル(''なら何もしない)
my $touchfile = 'touch.txt';
##############################
# プレビュー用の背景色
my $preview_color = '#FFCCCC';
##############################
# 全ページのスタイル
my $style = <<'EOD';
pre, dl, ul, ol, p, blockquote { line-height:120%; }
a { text-decoration: none; }
a:link { color: #0000FF; background-color: #FFFFFF; }
a:visited { color: #9900CC; background-color: #FFFFFF; }
a:hover { text-decoration: underline; }
EOD
##############################
# テキスト入力部分の大きさ
my $cols = 80;
my $rows = 20;
##############################
my %form = ();
my %database = ();
my %diffbase = ();
my $diff_text = '';
my @diff_added = ();
my @diff_deleted = ();
my $msgrefA;
my $msgrefB;
##############################
# 編集不可ページ名一覧
my @uneditable = ( $whatsnew );
##############################
# リンク用の正規表現
# YukiWikiのリンクは2種類ある。
#
# (1) WikiName (RecentChangesとかFrontPageのようなもの)
# (2) BracketName ([[結城浩]]とか[[トラブルシュート]]のようなもの)
#
# ※シフトJISの2バイト目には ']' が来うるので、
# 文字']'を1つ多くとるようにしている。
#
my $WikiName = '([A-Z][a-z]+([A-Z][a-z]+)+)';
my $BracketName = '\[\[([^>\s]+?\]?)\]\]';
# アイコン部分のタグ
my $IconTag = ''; #<<"EOD";
#
#EOD
require "$jcodelib";
&init_form($kanjicode);
if ($testing) {
%form = (
# 'mycmd' => 'write',
'mycmd' => 'read',
#'mycmd' => 'search',
#'mycmd' => 'edit',
'mymsg' => <<"EOD",
はじめまして。
これからいろいろ書き込みますね。
LinkPageも見てください。
TestPageはどうでしょうか。
どうぞよろしく。
http://www.hyuki.com/
[[結城浩]]
EOD
'mypage' => '<結城浩>',
'myword' => '結',
# '3C8C8B8FE98D5F3E' => '',
# 'TestPage' => '',
);
}
&main;
exit(0);
# メイン
sub main {
&normalize_form;
if ($dbmopen) {
if (!dbmopen(%database, $dbname, 0666)) {
&print_error("(dbmopen) $dbname が作れません。");
}
} else {
if (!tie(%database, "YukiWikiDB", $dbname)) {
&print_error("(tie error)");
}
}
# myspecial対応
foreach (keys %form) {
if (/^myspecial_(.*)/) {
$form{mycmd} = $1;
last;
}
}
if ($form{mycmd} eq 'read') {
&do_read;
} elsif ($form{mycmd} eq 'preview') {
&do_preview;
} elsif ($form{mycmd} eq 'write') {
&do_write;
} elsif ($form{mycmd} eq 'edit') {
&do_edit;
} elsif ($form{mycmd} eq 'reedit') {
&do_reedit;
} elsif ($form{mycmd} eq 'search') {
&do_search;
} elsif ($form{mycmd} eq 'list') {
&do_list;
} elsif ($form{mycmd} eq 'diff') {
&do_diff;
} else {
$form{mypage} = $toppage;
&do_read;
}
if ($dbmopen) {
dbmclose(%database);
} else {
untie(%database);
}
}
# ページの表示
sub do_read {
my $page_name = $form{mypage};
my $percent_name = &encode_percent($page_name);
&print_header($page_name);
print qq|\n|;
&print_toolbar($page_name);
print &convert_html(&get_page($page_name));
&print_footer;
}
# ページの編集
sub do_edit {
if (not &is_editable($form{mypage})) {
# 編集不可ページは表示のみ
&do_read;
return;
}
&editpage(&get_page($form{mypage}));
}
# ページの再編集
sub do_reedit {
if (not &is_editable($form{mypage})) {
# 編集不可ページは表示のみ
&do_read;
} else {
&editpage($form{mymsg});
}
}
sub editpage {
my $page_msg = shift;
my $page_name = $form{mypage};
my $digest = &calc_message_digest($page_msg);
&print_header($page_name);
print qq|$IconTag${page_name}の編集
\n|;
&print_toolbar($page_name);
$page_msg = &escape($page_msg);
print <<"EOD";
テキスト整形のルール
通常は入力した文字がそのまま出力されますが、
以下のルールに従ってテキスト整形を行うことができます。
EOD
&print_footer;
}
# ページの検索
sub do_search {
if ($form{myword}) {
&print_header('検索結果');
print qq|$IconTag$form{myword}の検索結果
\n|;
&print_toolbar();
print qq|\n|;
my $count = 0;
foreach my $page_name (sort keys %database) { # sortするのは無謀かな
if ($database{$page_name} =~ /\Q$form{'myword'}\E/) {
my $encoded = &encode_percent($page_name);
print qq|- $page_name\n|;
$count++;
}
}
print qq|
\n|;
if ($count > 0) {
print qq|$form{myword}を含むページは、上に示す$count ページです。
\n|;
} else {
print qq|$form{myword}を含むページは見つかりません。
\n|;
}
} else {
&print_header('単語検索');
print qq|$IconTag単語検索
\n|;
&print_toolbar();
}
print <<"EOD";
EOD
&print_footer;
}
# ページの一覧
sub do_list {
&print_header('ページ一覧');
print qq|$IconTag ページ一覧
\n|;
&print_toolbar();
print qq|\n|;
foreach my $page_name (sort keys %database) { # sortするのは無謀かな
my $encoded = &encode_percent($page_name);
print qq|- $page_name\n|
}
print qq|
\n|;
&print_footer;
}
# プレビュー
sub do_preview {
my $page_name = $form{mypage};
my $escapedmsg = &escape($form{mymsg});
&print_header($page_name);
print qq|$IconTag${page_name}のプレビュー
\n|;
&print_toolbar($page_name);
# local $percent_name = &encode_percent($page_name);
print qq|以下のプレビューを確認して、よければページ下部のボタンで更新してください。
\n|;
if ($form{mymsg}) {
print qq|\n|;
# print &convert_html($escapedmsg);
print &convert_html($form{mymsg});
print qq| |
\n|;
} else {
print qq|(ページの内容は空です。更新するとこのページは削除されます。)
\n|;
}
&print_preview_buttons($page_name, $escapedmsg, $form{mydigest});
&print_footer;
}
sub print_preview_buttons {
my ($page_name, $escapedmsg, $digest) = @_;
print <<"EOD";
EOD
}
# 書き込む
sub do_write {
if (not &is_editable($form{mypage})) {
# 編集不可ページは表示のみ
&do_read;
return;
}
my $page_name = $form{mypage};
# digestを使って、更新の衝突チェック
my $original_digest = &calc_message_digest(&get_page($page_name));
if ($form{mydigest} ne $original_digest) {
&print_header($page_name);
print qq|$IconTag${page_name}で【更新の衝突】が起きました
\n|;
print <<"EOD";
あなたがこのページを編集している間に、
他の人が同じページを更新してしまったようです。
以下に、あなたの編集したテキストがありますので、
あなたの編集内容が失われないように、
いますぐ、メモ帳などにコピー&ペーストしてください。
コピー&ペーストが済んでから、
最新の内容を見て再度編集し直してください。
最新の内容は
$form{mypage}
で見ることができます。
EOD
# &print_toolbar($page_name);
&print_preview_buttons($page_name, &escape($form{mymsg}), $form{mydigest});
&print_footer;
return;
}
# diff生成
{
&opendiff;
my @msg1 = split(/\n/, &get_page($page_name));
my @msg2 = split(/\n/, $form{mymsg});
$msgrefA = \@msg1;
$msgrefB = \@msg2;
&diff_check;
$diffbase{$form{mypage}} = $diff_text;
$diff_text = '';
&closediff;
}
&print_header($page_name);
&set_page($page_name, $form{mymsg});
if ($form{mymsg}) {
print qq|$IconTag${page_name}を更新しました
\n|;
&print_toolbar($page_name);
print &convert_html(&get_page($page_name));
} else {
print qq|$IconTag${page_name}を削除しました
\n|;
&print_toolbar($page_name);
print qq|${page_name}を削除しました。
\n|;
}
&print_footer;
# 更新されたのでタッチしておく。
if ($touchfile) {
open(FILE, "> $touchfile");
print FILE "\n";
close(FILE);
}
}
# ページの変更点
sub do_diff {
if (not &is_editable($form{mypage})) {
# 編集不可ページは表示のみ
&do_read;
return;
}
&opendiff;
&print_header($form{mypage} . 'の変更点');
print qq|\n|;
&print_toolbar();
$_ = &escape($diffbase{$form{mypage}});
print <<"EOD";
EOD
print qq|\n|;
foreach (split(/\n/, $_)) {
if (/^\+(.*)/) {
print qq|$1\n|;
} elsif (/^\-(.*)/) {
print qq|$1\n|;
} elsif (/^\=(.*)/) {
print qq|$1\n|;
} else {
print qq|??? $_\n|;
}
}
print qq|
\n|;
&print_footer;
&closediff;
}
sub opendiff {
if ($dbmopen) {
if (!dbmopen(%diffbase, $diffdbname, 0666)) {
&print_error("(dbmopen) $diffdbname が作れません。");
}
} else {
if (!tie(%diffbase, "YukiWikiDB", $diffdbname)) {
&print_error("(tie error)");
}
}
}
sub closediff {
if ($dbmopen) {
dbmclose(%diffbase);
} else {
untie(%diffbase);
}
}
# フォームからの情報を連想配列 %form に入れる
# &init_form('euc');
sub init_form {
my ($charcode) = @_;
my $query;
if ($ENV{REQUEST_METHOD} =~ /^post$/i) {
read(STDIN, $query, $ENV{CONTENT_LENGTH});
} else {
$query = $ENV{QUERY_STRING};
}
my @assocarray = split(/[&;]/, $query);
foreach my $assoc (@assocarray) {
my ($property, $value) = split(/=/, $assoc);
$value =~ tr/+/ /;
$value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
&jcode::convert(\$value, $charcode);
$form{$property} = $value;
}
}
# エラーページを出力する
sub print_error {
my ($msg) = @_;
&print_header('Error');
print "$IconTag$msg