/[pub]/suikawiki/script/wiki.cgi
Suika

Contents of /suikawiki/script/wiki.cgi

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (hide annotations) (download)
Tue Apr 2 01:32:31 2002 UTC (22 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.13: +4 -3 lines
2002-03-24  wakaba <w@suika.fam.cx>

	* wiki.cgi (inline): 
	- Ignore LinkLikeThis style anchor.
	- Allow [[space including anchor]].
	(make_link): Likewise.  Gets rid of angle branckets
	before make anchor.

1 wakaba 1.9 #!/usr/bin/perl
2     use lib "../lib";
3     use CGI::Carp 'fatalsToBrowser';
4     use Algorithm::Diff qw(traverse_sequences);
5     # use strict;
6     #
7     # yukiwiki.cgi - Yet another WikiWikiWeb clone.
8     #
9     # Copyright (C) 2000,2001 by Hiroshi Yuki.
10     # <hyuki@hyuki.com>
11     # http://www.hyuki.com/yukiwiki/
12     #
13     # This program is free software; you can redistribute it and/or modify
14     # it under the terms of the GNU General Public License as published by
15     # the Free Software Foundation; either version 2 of the License, or
16     # (at your option) any later version.
17     #
18     # This program is distributed in the hope that it will be useful,
19     # but WITHOUT ANY WARRANTY; without even the implied warranty of
20     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21     # GNU General Public License for more details.
22     #
23 wakaba 1.14 # $Id: wiki.cgi,v 1.13 2002/04/02 00:28:14 wakaba Exp $
24 wakaba 1.9 ##############################
25     my $version = "1.6.6";
26     ##############################
27     # 単独テストのときには 1 にする。
28     my $testing = 0;
29     ##############################
30     # 漢字ライブラリ
31     my $jcodelib = 'jcode.pl';
32     ##############################
33     # 保存・表示の漢字コード
34     my $kanjicode = 'euc'; # 'sjis' 'euc'
35     my $charset = 'euc-jisx0213'; # 'Shift_JIS' 'EUC-JP'
36     ##############################
37     # dbmopenが使えるなら1、使えないなら0
38     my $dbmopen = 0;
39     ##############################
40     # データベース名(.pag, .dir, .dbなどは不要)
41     # $dbmopen = 1のときはデータベース名、
42     # $dbmopen = 0のときはディレクトリ名になる。
43     my $dbname = './wikidata';
44     my $diffdbname = './wikidiff';
45     ##############################
46     # 修正者の氏名(自由に変更してください)
47     my $modifier = 'suika';
48     ##############################
49     # 修正者のWebページ(自由に変更してください)
50     my $modifierlink = 'http://suika.fam.cx/';
51     ##############################
52     # このページのURL
53     my $thisurl = 'wiki';
54     ##############################
55     # 開始ページ名
56     my $toppage = 'HomePage';
57     ##############################
58     # 最終更新ページ名
59     my $whatsnew = 'RecentChanges';
60     ##############################
61     # 最終更新に掲載するページ数
62     my $maxnew = 50;
63     ##############################
64     # アイコンファイル名(カラー版)
65     my $iconfile = '';
66     ##############################
67     # アイコンファイル名(モノクロ版)
68     # my $iconfile = '';
69     ##############################
70     # ページを変更したときにtouchするファイル(''なら何もしない)
71     my $touchfile = 'touch.txt';
72     ##############################
73     # プレビュー用の背景色
74     my $preview_color = '#FFCCCC';
75     ##############################
76     # 全ページのスタイル
77     my $style = <<'EOD';
78 wakaba 1.13 @import '/s/simpledoc';
79 wakaba 1.14 pre, dl, ul, ol, p, blockquote { line-height:120%;
80     margin-bottom: 1em}
81 wakaba 1.13 a.wiki .mark {vertical-align: sub, color: GrayText}
82 wakaba 1.9 a { text-decoration: none; }
83     a:link { color: #0000FF; background-color: #FFFFFF; }
84     a:visited { color: #9900CC; background-color: #FFFFFF; }
85     a:hover { text-decoration: underline; }
86     EOD
87     ##############################
88     # テキスト入力部分の大きさ
89     my $cols = 80;
90     my $rows = 20;
91     ##############################
92     my %form = ();
93     my %database = ();
94     my %diffbase = ();
95     my $diff_text = '';
96     my @diff_added = ();
97     my @diff_deleted = ();
98     my $msgrefA;
99     my $msgrefB;
100     ##############################
101     # 編集不可ページ名一覧
102     my @uneditable = ( $whatsnew );
103     ##############################
104     # リンク用の正規表現
105     # YukiWikiのリンクは2種類ある。
106     #
107     # (1) WikiName (RecentChangesとかFrontPageのようなもの)
108     # (2) BracketName ([[結城浩]]とか[[トラブルシュート]]のようなもの)
109     #
110     # ※シフトJISの2バイト目には ']' が来うるので、
111     # 文字']'を1つ多くとるようにしている。
112     #
113     my $WikiName = '([A-Z][a-z]+([A-Z][a-z]+)+)';
114     my $BracketName = '\[\[([^>\x09]+?\]?)\]\]';
115    
116     # アイコン部分のタグ
117     my $IconTag = ''; #<<"EOD";
118     #<a href="http://www.hyuki.com/yukiwiki/"><img src="$iconfile"
119     # border="0" width="80" height="80" alt="[YukiWiki]" /></a>
120     #EOD
121    
122     require "$jcodelib";
123    
124     &init_form($kanjicode);
125    
126     if ($testing) {
127     %form = (
128     # 'mycmd' => 'write',
129     'mycmd' => 'read',
130     #'mycmd' => 'search',
131     #'mycmd' => 'edit',
132     'mymsg' => <<"EOD",
133     はじめまして。
134     これからいろいろ書き込みますね。
135     LinkPageも見てください。
136     TestPageはどうでしょうか。
137     どうぞよろしく。
138     http://www.hyuki.com/
139     [[結城浩]]
140     EOD
141     'mypage' => '<結城浩>',
142     'myword' => '結',
143     # '3C8C8B8FE98D5F3E' => '',
144     # 'TestPage' => '',
145     );
146     }
147     &main;
148     exit(0);
149    
150     # メイン
151     sub main {
152     &normalize_form;
153     if ($dbmopen) {
154     if (!dbmopen(%database, $dbname, 0666)) {
155     &print_error("(dbmopen) $dbname が作れません。");
156     }
157     } else {
158     if (!tie(%database, "YukiWikiDB", $dbname)) {
159     &print_error("(tie error)");
160     }
161     }
162    
163     # myspecial対応
164     foreach (keys %form) {
165     if (/^myspecial_(.*)/) {
166     $form{mycmd} = $1;
167     last;
168     }
169     }
170    
171     if ($form{mycmd} eq 'read') {
172     &do_read;
173     } elsif ($form{mycmd} eq 'preview') {
174     &do_preview;
175     } elsif ($form{mycmd} eq 'write') {
176     &do_write;
177     } elsif ($form{mycmd} eq 'edit') {
178     &do_edit;
179     } elsif ($form{mycmd} eq 'reedit') {
180     &do_reedit;
181     } elsif ($form{mycmd} eq 'search') {
182     &do_search;
183     } elsif ($form{mycmd} eq 'list') {
184     &do_list;
185     } elsif ($form{mycmd} eq 'diff') {
186     &do_diff;
187     } else {
188     $form{mypage} = $toppage;
189     &do_read;
190     }
191     if ($dbmopen) {
192     dbmclose(%database);
193     } else {
194     untie(%database);
195     }
196     }
197    
198     # ページの表示
199     sub do_read {
200     my $page_name = $form{mypage};
201     my $percent_name = &encode_percent($page_name);
202     &print_header($page_name);
203     print qq|<h1>$IconTag<a href="$thisurl?mycmd=search;myword=$percent_name">$page_name</a></h1>\n|;
204     &print_toolbar($page_name);
205     print &convert_html(&get_page($page_name));
206     &print_footer;
207     }
208    
209     # ページの編集
210     sub do_edit {
211     if (not &is_editable($form{mypage})) {
212     # 編集不可ページは表示のみ
213     &do_read;
214     return;
215     }
216     &editpage(&get_page($form{mypage}));
217     }
218    
219     # ページの再編集
220     sub do_reedit {
221     if (not &is_editable($form{mypage})) {
222     # 編集不可ページは表示のみ
223     &do_read;
224     } else {
225     &editpage($form{mymsg});
226     }
227     }
228    
229     sub editpage {
230     my $page_msg = shift;
231     my $page_name = $form{mypage};
232     my $digest = &calc_message_digest($page_msg);
233     &print_header($page_name);
234     print qq|<h1>$IconTag${page_name}の編集</h1>\n|;
235     &print_toolbar($page_name);
236     $page_msg = &escape($page_msg);
237     print <<"EOD";
238     <form action="$thisurl" method="post">
239     <!--<input type="hidden" name="mycmd" value="preview">-->
240     <input type="hidden" name="mypage" value="$page_name">
241     <input type="hidden" name="mydigest" value="$digest">
242     <textarea cols="$cols" rows="$rows" name="mymsg" wrap="virtual">$page_msg</textarea><br>
243     <input type="submit" name="myspecial_preview" value="確認">
244     <input type="submit" name="myspecial_write" value="確認せず変更">
245     </form>
246     <hr>
247     <h3>テキスト整形のルール</h3>
248    
249     <p>通常は入力した文字がそのまま出力されますが、
250     以下のルールに従ってテキスト整形を行うことができます。</p>
251    
252     <ul>
253     <li>
254     空行は段落の区切りとなります。
255    
256     <li>
257     HTMLのタグは書けません。
258    
259     <li>
260     ''強調''のようにシングルクォート二つではさむと、強調になります。
261    
262     <li>
263     '''更に強調'''のようにシングルクォート三つではさむと、更に強調になります。
264    
265     <li>
266     ----のようにマイナス4つがあると、水平線になります。
267    
268     <li>
269     *を行頭に書くと大見出しになります。
270    
271     <li>
272     **を行頭に書くと小見出しになります。
273    
274     <li>
275     -を行頭に書くと箇条書きになります。- -- --- の3レベルがあります。
276    
277     <li>
278     :を行頭に書くと用語と解説文が作れます。
279    
280     <pre>
281     :用語1:いろいろ書いた解説文1
282     :用語2:いろいろ書いた解説文2
283     :用語3:いろいろ書いた解説文3
284     </pre>
285    
286     <li>
287     http://www.hyuki.com/ のようなURLは自動的にリンクになります。
288    
289     <li>
290     YukiWikiのように大文字小文字を混ぜた英文字列を書くと、
291     YukiWikiのページ名になります。
292    
293     <li>
294     [[結城浩]]のように二重の大かっこ[[ ]]でくくった文字列を書くと、
295     YukiWikiのページ名になります。
296     大かっこの中にはスペースを含めてはいけません。
297     日本語も使えます。
298    
299     <li>
300     行頭がスペースで始まっていると、
301     その段落は整形済み扱われます。
302     プログラムを書き込むときに使うと便利です。
303    
304     <li>
305     &gt; を行頭に書くと、
306     引用文が書けます。
307     &gt; の数が多いとインデントが深くなります(3レベルまで)。
308    
309     </ul>
310     EOD
311     &print_footer;
312     }
313    
314     # ページの検索
315     sub do_search {
316     if ($form{myword}) {
317     &print_header('検索結果');
318     print qq|<h1>$IconTag$form{myword}の検索結果</h1>\n|;
319     &print_toolbar();
320     print qq|<ul>\n|;
321     my $count = 0;
322     foreach my $page_name (sort keys %database) { # sortするのは無謀かな
323     if ($database{$page_name} =~ /\Q$form{'myword'}\E/) {
324     my $encoded = &encode_percent($page_name);
325     print qq|<li><a href="$thisurl?mycmd=read;mypage=$encoded">$page_name</a>\n|;
326     $count++;
327     }
328     }
329     print qq|</ul>\n|;
330     if ($count > 0) {
331     print qq|<p><strong>$form{myword}</strong>を含むページは、上に示す<strong>$count</strong> ページです。</p>\n|;
332     } else {
333     print qq|<p><strong>$form{myword}</strong>を含むページは見つかりません。</p>\n|;
334     }
335     } else {
336     &print_header('単語検索');
337     print qq|<h1>$IconTag単語検索</h1>\n|;
338     &print_toolbar();
339     }
340     print <<"EOD";
341     <p>
342     <form action="$thisurl" method="post">
343     <input type="hidden" name="mycmd" value="search">
344     <input type="text" name="myword" size="20" value="$form{myword}">
345     <input type="submit" value="単語検索">
346     </form>
347     </p>
348     EOD
349     &print_footer;
350     }
351    
352     # ページの一覧
353     sub do_list {
354     &print_header('ページ一覧');
355     print qq|<h1>$IconTag ページ一覧</h1>\n|;
356     &print_toolbar();
357     print qq|<ul>\n|;
358     foreach my $page_name (sort keys %database) { # sortするのは無謀かな
359     my $encoded = &encode_percent($page_name);
360     print qq|<li><a href="$thisurl?mycmd=read;mypage=$encoded">$page_name</a>\n|
361     }
362     print qq|</ul>\n|;
363     &print_footer;
364     }
365    
366     # プレビュー
367     sub do_preview {
368     my $page_name = $form{mypage};
369     my $escapedmsg = &escape($form{mymsg});
370     &print_header($page_name);
371     print qq|<h1>$IconTag${page_name}のプレビュー</h1>\n|;
372     &print_toolbar($page_name);
373     # local $percent_name = &encode_percent($page_name);
374     print qq|<p>以下のプレビューを確認して、よければページ下部のボタンで更新してください。</p>\n|;
375     if ($form{mymsg}) {
376     print qq|<table width="100%" bgcolor="$preview_color" ><tr><td>\n|;
377     # print &convert_html($escapedmsg);
378     print &convert_html($form{mymsg});
379     print qq|</td></tr></table>\n|;
380     } else {
381     print qq|<p>(ページの内容は空です。更新するとこのページは<strong>削除</strong>されます。)</p>\n|;
382     }
383     &print_preview_buttons($page_name, $escapedmsg, $form{mydigest});
384     &print_footer;
385     }
386    
387     sub print_preview_buttons {
388     my ($page_name, $escapedmsg, $digest) = @_;
389     print <<"EOD";
390     <form action="$thisurl" method="post">
391     <textarea cols="$cols" rows="$rows" name="mymsg" wrap="virtual">$escapedmsg</textarea>
392     <br />
393     <input type="hidden" name="mypage" value="$page_name">
394     <input type="hidden" name="mydigest" value="$digest">
395     <input type="submit" name="myspecial_preview" value="再度プレビュー">
396     <input type="submit" name="myspecial_write" value="ページの更新">
397     </form>
398     EOD
399     }
400    
401     # 書き込む
402     sub do_write {
403     if (not &is_editable($form{mypage})) {
404     # 編集不可ページは表示のみ
405     &do_read;
406     return;
407     }
408    
409     my $page_name = $form{mypage};
410    
411     # digestを使って、更新の衝突チェック
412     my $original_digest = &calc_message_digest(&get_page($page_name));
413     if ($form{mydigest} ne $original_digest) {
414     &print_header($page_name);
415     print qq|<h1>$IconTag${page_name}で【更新の衝突】が起きました</h1>\n|;
416     print <<"EOD";
417     <p>あなたがこのページを編集している間に、
418     他の人が同じページを更新してしまったようです。
419     </p><p>
420     以下に、あなたの編集したテキストがありますので、
421     あなたの編集内容が失われないように、
422     いますぐ、メモ帳などにコピー&ペーストしてください。
423     </p><p>
424     コピー&ペーストが済んでから、
425     最新の内容を見て再度編集し直してください。
426     最新の内容は
427     <a target="_blank" href="$thisurl?mycmd=read;mypage=$form{mypage}">$form{mypage}</a>
428     で見ることができます。
429     </p>
430     EOD
431     # &print_toolbar($page_name);
432     &print_preview_buttons($page_name, &escape($form{mymsg}), $form{mydigest});
433     &print_footer;
434     return;
435     }
436    
437     # diff生成
438     {
439     &opendiff;
440     my @msg1 = split(/\n/, &get_page($page_name));
441     my @msg2 = split(/\n/, $form{mymsg});
442     $msgrefA = \@msg1;
443     $msgrefB = \@msg2;
444     &diff_check;
445     $diffbase{$form{mypage}} = $diff_text;
446     $diff_text = '';
447     &closediff;
448     }
449    
450     &print_header($page_name);
451     &set_page($page_name, $form{mymsg});
452     if ($form{mymsg}) {
453     print qq|<h1>$IconTag${page_name}を更新しました</h1>\n|;
454     &print_toolbar($page_name);
455     print &convert_html(&get_page($page_name));
456     } else {
457     print qq|<h1>$IconTag${page_name}を削除しました</h1>\n|;
458     &print_toolbar($page_name);
459     print qq|<p>${page_name}を削除しました。</p>\n|;
460     }
461     &print_footer;
462     # 更新されたのでタッチしておく。
463     if ($touchfile) {
464     open(FILE, "> $touchfile");
465     print FILE "\n";
466     close(FILE);
467     }
468     }
469    
470     # ページの変更点
471     sub do_diff {
472     if (not &is_editable($form{mypage})) {
473     # 編集不可ページは表示のみ
474     &do_read;
475     return;
476     }
477     &opendiff;
478     &print_header($form{mypage} . 'の変更点');
479     print qq|<h1>$IconTag <a href="$thisurl?mycmd=read&mypage=$form{mypage}">$form{mypage}</a>の変更点</h1>\n|;
480     &print_toolbar();
481     $_ = &escape($diffbase{$form{mypage}});
482     print <<"EOD";
483     <ul>
484     <li>追加された行は<ins>青色</ins>です。
485     <li>削除された行は<del>赤色</del>です。
486     <li><a href="$thisurl?mycmd=read;mypage=$form{mypage}">$form{mypage}</a>へ行く。
487     </ul>
488     <hr />
489     EOD
490     print qq|<pre>\n|;
491     foreach (split(/\n/, $_)) {
492     if (/^\+(.*)/) {
493     print qq|<ins>$1</ins>\n|;
494     } elsif (/^\-(.*)/) {
495     print qq|<del>$1</del>\n|;
496     } elsif (/^\=(.*)/) {
497     print qq|$1\n|;
498     } else {
499     print qq|??? $_\n|;
500     }
501     }
502     print qq|</pre>\n|;
503     &print_footer;
504     &closediff;
505     }
506    
507     sub opendiff {
508     if ($dbmopen) {
509     if (!dbmopen(%diffbase, $diffdbname, 0666)) {
510     &print_error("(dbmopen) $diffdbname が作れません。");
511     }
512     } else {
513     if (!tie(%diffbase, "YukiWikiDB", $diffdbname)) {
514     &print_error("(tie error)");
515     }
516     }
517     }
518    
519     sub closediff {
520     if ($dbmopen) {
521     dbmclose(%diffbase);
522     } else {
523     untie(%diffbase);
524     }
525     }
526    
527     # フォームからの情報を連想配列 %form に入れる
528     # &init_form('euc');
529     sub init_form {
530     my ($charcode) = @_;
531     my $query;
532     if ($ENV{REQUEST_METHOD} =~ /^post$/i) {
533     read(STDIN, $query, $ENV{CONTENT_LENGTH});
534     } else {
535     $query = $ENV{QUERY_STRING};
536     }
537     my @assocarray = split(/[&;]/, $query);
538     foreach my $assoc (@assocarray) {
539     my ($property, $value) = split(/=/, $assoc);
540     $value =~ tr/+/ /;
541     $value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
542     &jcode::convert(\$value, $charcode);
543     $form{$property} = $value;
544     }
545     }
546    
547     # エラーページを出力する
548     sub print_error {
549     my ($msg) = @_;
550     &print_header('Error');
551     print "<h1>$IconTag$msg</h1></body></html>";
552     exit(0);
553     }
554    
555     sub escape {
556     my ($line) = shift;
557     $line =~ s|<|&lt;|g;
558     $line =~ s|>|&gt;|g;
559     $line =~ s|"|&quot;|g;
560     # $line =~ s|\&|&amp;|g;
561     return $line;
562     }
563    
564     sub inline {
565     my ($line) = shift;
566     $line = &escape($line);
567     $line =~ s|'''([^']+?)'''|<strong>$1</strong>|g;
568     $line =~ s|''([^']+?)''|<em>$1</em>|g;
569     $line =~ s!
570     (
571     (?:&lt;(?:mailto|http|https|ftp|urn):[\x21-\x7E]*)&gt;
572     #| (?:$WikiName) # LocalLinkLikeThis
573     | (?:$BracketName) # [[日本語リンク]]
574     )
575     !
576     &make_link($1)
577     !gex;
578     return $line;
579     }
580    
581     # ページのタイトルからページの内容を得る
582     sub get_page {
583     my $page_name = shift;
584 wakaba 1.10 return $database{$page_name} || $database{'[['.$page_name.']]'};
585 wakaba 1.9 }
586    
587     # ページの内容を与える
588     # &set_page($title, $txt)
589     sub set_page {
590     # ページを更新する
591     my $title = $_[0];
592     $database{$title} = $_[1];
593     # 空ページなら削除する
594     unless ($database{$title}) {
595     delete $database{$title};
596     }
597     # RecentChangesを更新する
598     my $delim = ' - ';
599     my @pages = split(/\n/, $database{$whatsnew});
600     my $datestr = &get_current_datestr;
601     unshift(@pages, qq|-$datestr$delim$title|);
602     # 同一ページの更新は最新のもののみにし、
603     # 存在しないページはスキップする。
604     my %count;
605     my @newpages;
606     foreach my $line (@pages) {
607     my ($prefix, $title) = split(/$delim/, $line);
608     $count{$title}++;
609     if ($count{$title} == 1 and exists($database{$title})) {
610     push(@newpages, qq|$prefix - $title|);
611     }
612     }
613     # ここで本当に更新
614     $database{$whatsnew} = join("\n", splice(@newpages, 0, $maxnew));
615     }
616    
617     # ページのヘッダを出力
618     sub print_header {
619     my $title = shift;
620     print <<"EOD";
621 wakaba 1.10 Content-type: text/html; charset=${charset}
622 wakaba 1.9
623     <html><head>
624     <title>$title</title>
625     <style type="text/css">
626     $style
627     </style>
628     </head>
629     <body>
630     EOD
631     }
632    
633     # ツールバーを出力
634     sub print_toolbar {
635     my $page_name = shift;
636     my $percent_name = &encode_percent($page_name);
637     my $editlink = '';
638     if ($page_name ne '' and &is_editable($page_name)) {
639     $editlink = <<"EOD";
640     <a href="$thisurl?mycmd=edit;mypage=$percent_name">編集</a> |
641     <a href="$thisurl?mycmd=diff;mypage=$percent_name">差分</a> |
642     EOD
643     }
644     print <<"EOD";
645     <p>
646     [
647     <a href="$thisurl?mycmd=read;mypage=$toppage">トップ</a> |
648     <a href="$thisurl?mycmd=list">一覧</a> |
649     $editlink
650     <a href="$thisurl?mycmd=search">単語検索</a> |
651 wakaba 1.10 <a href="$thisurl?mycmd=read;mypage=$whatsnew">最近の更新</a>
652 wakaba 1.9 ]
653     </p>
654     EOD
655     }
656    
657     # ページのフッタを出力
658     sub print_footer {
659     print <<"EOD";
660 wakaba 1.10 <div class="navigation">
661 wakaba 1.9 [<a href="/" title="このサーバーの首頁">/</a>
662     <a href="/map" title="このサーバーの案内">地図</a>
663     <a href="/search/" title="このサーバーの検索">検索</a>]
664 wakaba 1.10 </div>
665 wakaba 1.12 EOD
666     print <<'EOH';
667 wakaba 1.11 <div class="update">
668 wakaba 1.13 <a href="http://www.hyuki.com/yukiwiki/">YukiWiki</a> 1.6.6 Copyright (C) 2000,2001 by <a href="http://www.hyuki.com/">Hiroshi Yuki.</a> ${version}
669     + <a href="$modifierlink">$modifier</a>.
670 wakaba 1.11 <a href="/gate/cvs/wakaba/wiki/" title="CVS Repository">
671 wakaba 1.14 $Revision: 1.13 $ $Date: 2002/04/02 00:28:14 $
672 wakaba 1.11 </a>
673     </div>
674 wakaba 1.9 </body></html>
675 wakaba 1.12 EOH
676 wakaba 1.9 }
677    
678     # URLやページの名前からリンクを作る
679     sub make_link {
680     my $name = shift;
681     $name =~ s/^&lt;(.*)&gt;$/$1/;
682     $name =~ s/^\[\[(.*)\]\]$/$1/;
683     if ($name =~ /^(http|https|ftp).*?(\.png|\.jpeg|\.jpg)?$/) {
684     if ($2) {
685     return qq|<a href="$name"><img border="0" src="$name" /></a>|;
686     } else {
687     return qq|&lt;<a href="$name">$name</a>&gt;|;
688     }
689     } elsif ($name =~ /^mailto:(.*)/) {
690     my $address = $1;
691     return qq|&lt;<a href="$name">$address</a>&gt;|;
692 wakaba 1.10 } elsif ($name =~ m#^urn:[0-9A-Za-z_:;/.-]+#) {
693 wakaba 1.9 return qq|&lt;<a href="/uri-res/N2L?${name}">$name</a>&gt;|;
694     } else {
695     my $name2 = $name; $name2 =~ tr/\x20/-/;
696     if ($database{$name2}) {
697     my $percent_name = &encode_percent($name2);
698     return qq|<a href="$thisurl?mycmd=read;mypage=$percent_name" class="wiki">$name</a>|;
699     } elsif ($database{'[['.$name2.']]'}) {
700     my $percent_name = &encode_percent('[['.$name2.']]');
701     return qq|<a href="$thisurl?mycmd=read;mypage=$percent_name" class="wiki">$name</a>|;
702     } else {
703     my $percent_name = &encode_percent($name2);
704 wakaba 1.10 return qq|<a href="$thisurl?mycmd=edit;mypage=$percent_name" class="wiki newpage">$name<span class="mark">?</span></a>|;
705 wakaba 1.9 }
706     }
707     }
708    
709     # %xx の形式にエンコードする
710     # これは、
711     # http://www.hyuki.com/yukiwiki/yukiwiki.cgi?mycmd=read&mypage=%3C%8C%8B%8F%E9%8D_%3E
712     # という形式のために使われる。
713     # '<結城浩>' → '%3C%8C%8B%8F%E9%8D_%3E'
714     sub encode_percent {
715     my $name = shift;
716     my $encoded = '';
717     foreach my $ch (split(//, $name)) {
718     if ($ch =~ /[A-Za-z0-9_]/) {
719     $encoded .= $ch;
720     } else {
721     $encoded .= '%' . sprintf("%02X", ord($ch));
722     }
723     }
724     return $encoded;
725     }
726    
727     # テキスト本体をHTMLに変換する
728     sub convert_html {
729     my ($txt) = shift;
730     my (@txt) = split(/\n/, $txt);
731     foreach (@txt) {
732     chomp;
733 wakaba 1.13 if (/^\*\*\*\*\*(.*)/) {
734     push(@result, splice(@saved), '<h6>' . &inline($1) . '</h6>');
735     } elsif (/^\*\*\*\*(.*)/) {
736     push(@result, splice(@saved), '<h5>' . &inline($1) . '</h5>');
737     } elsif (/^\*\*\*(.*)/) {
738     push(@result, splice(@saved), '<h4>' . &inline($1) . '</h4>');
739     } elsif (/^\*\*(.*)/) {
740 wakaba 1.9 push(@result, splice(@saved), '<h3>' . &inline($1) . '</h3>');
741     } elsif (/^\*(.*)/) {
742     push(@result, splice(@saved), '<h2>' . &inline($1) . '</h2>');
743     } elsif (/^----/) {
744     push(@result, splice(@saved), '<hr>');
745     } elsif (/^(-{1,3})(.*)/) {
746     &back_push('ul', length($1));
747     push(@result, '<li>' . &inline($2) . '</li>');
748 wakaba 1.13 } elsif (/^(={1,3})(.*)/) {
749     &back_push('ol', length($1));
750     push(@result, '<li>' . &inline($2) . '</li>');
751 wakaba 1.9 } elsif (/^:([^:]+):(.*)/) {
752     &back_push('dl', 1);
753     push(@result, '<dt>' . &inline($1) . '</dt>', '<dd>' . &inline($2) . '</dd>');
754     } elsif (/^(>{1,3})(.*)/) {
755     &back_push('blockquote', length($1));
756     push(@result, &inline($2));
757     } elsif (/^\s*$/) {
758     push(@result, splice(@saved));
759     unshift(@saved, "</p>");
760     push(@result, "<p>");
761     } elsif (/^(\s+.*)$/) {
762     &back_push('pre', 1);
763 wakaba 1.13 #push(@result, &escape($1)); # Not &inline, but &escape
764     push(@result, &inline($1)); # Not &inline, but &escape
765 wakaba 1.9 } else {
766     push(@result, &inline($_));
767     }
768     }
769     push(@result, splice(@saved));
770     return join("\n", @result);
771     }
772    
773     # &back_push($tag, $count)
774     # $tagのタグを$levelレベルまで詰める。
775     sub back_push {
776     my ($tag, $level) = @_;
777     while (@saved > $level) {
778     push(@result, shift(@saved));
779     }
780     if ($saved[0] ne "</$tag>") {
781     push(@result, splice(@saved));
782     }
783     while (@saved < $level) {
784     unshift(@saved, "</$tag>");
785     push(@result, "<$tag>");
786     }
787     }
788    
789     # 編集可能ページか?
790 wakaba 1.10 sub is_editable {return 1;
791     ## TODO: ...
792 wakaba 1.9 my ($pagename) = @_;
793     foreach (@uneditable) {
794     if ($pagename eq $_) {
795     return 0;
796     }
797     }
798     if (&is_valid_name($pagename)) {
799     return 1;
800     }
801     return 0;
802     }
803    
804 wakaba 1.10 # Validな名前か?
805 wakaba 1.9 sub is_valid_name {
806     my ($pagename) = @_;
807     if ($pagename =~ /^$WikiName$/) {
808     return 1;
809     } elsif ($pagename =~ /^$BracketName$/) {
810     return 1;
811     } else {
812     return 0;
813     }
814     }
815    
816     # 現在時刻の文字列を得る
817     sub get_current_datestr {
818     my (@wdays) = ( "日", "月", "火", "水", "木", "金", "土" );
819     my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time);
820     return sprintf("%4d-%02d-%02d (%s) %02d:%02d:%02d",
821     $year + 1900, $mon + 1, $mday, $wdays[$wday], $hour, $min, $sec);
822     }
823    
824     # URL?SomePageや、
825     # URL?[[結城浩]]の形式だった場合、(not yet)
826     # 強制的にmycmdをreadにして$formの内容を設定する。
827     sub normalize_form {
828     foreach my $key (keys %form) {
829     if ($key =~ /^$WikiName$/) {
830     $form{mycmd} = 'read';
831     $form{mypage} = $1;
832     last;
833     } elsif ($key =~ /^$BracketName$/) {
834     $form{mycmd} = 'read';
835     $form{mypage} = $1;
836     last;
837     }
838     }
839     }
840    
841     # 変換テストを行なうときのサンプル
842     sub print_sample {
843     my $txt = &convert_html(<<"EOD");
844     *大見出し1
845     **小見出し1-1
846     -項目1
847     -項目2
848     -項目3
849     段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
850     段落1段落1段落1段落1段落1段落''強調''1段落1段落1段落1段落1段落1
851     段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
852    
853     段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
854     段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
855     段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
856     **小見出し1-2
857     :用語1:いろいろ書いた解説文1と''強調単語''
858     段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
859     段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
860     段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
861     :用語2:いろいろ書いた解説文2
862     :用語3:いろいろ書いた解説文3
863     ----
864     *大見出し2
865     **小見出し2-1
866     &lt;http://suika.fam.cx/&gt;
867     **小見出し2-2
868    
869     [[結城浩]]
870    
871     段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
872     段落1段落1段落1段落'''強調'''1段落1段落1段落1段落1段落1段落1段落1
873     段落1段落1段落1段落'''''強い強調'''''1段落1段落1段落1段落1段落1段落1段落1段落1
874     >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
875     >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
876     >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
877    
878     レベル0レベル0レベル0レベル0レベル0レベル0
879    
880     >レベル1
881     >レベル1
882     >レベル1
883     >>レベル2
884     >>レベル2
885     >>レベル2
886     >>>レベル3
887     -はろ1
888     --はろ2
889     ろろろろ2
890     ---はろ3
891     --はろ2
892     ---はろ3
893     --はろ2
894     ---はろ3
895     >>>レベル3
896     >>>レベル3
897     >>>レベル3
898     EOD
899     print $txt;
900     exit;
901     }
902    
903     sub diff_check {
904     traverse_sequences(
905     $msgrefA, $msgrefB,
906     {
907     MATCH => \&df_match,
908     DISCARD_A => \&df_delete,
909     DISCARD_B => \&df_add,
910     }
911     );
912     &diff_flush;
913     }
914    
915     sub diff_flush {
916     $diff_text .= join('', map { "-$_\n" } splice(@diff_deleted));
917     $diff_text .= join('', map { "+$_\n" } splice(@diff_added));
918     }
919    
920     sub df_match {
921     my ($a, $b) = @_;
922     &diff_flush;
923     $diff_text .= "=$msgrefA->[$a]\n";
924     }
925    
926     sub df_delete {
927     my ($a, $b) = @_;
928     push(@diff_deleted, $msgrefA->[$a]);
929     }
930    
931     sub df_add {
932     my ($a, $b) = @_;
933     push(@diff_added, $msgrefB->[$b]);
934     }
935    
936     sub calc_message_digest { # You have to use MD5...
937     my $text = shift;
938     my @text = split(//, $text);
939     my $len = length($text);
940     my $checksum = 0;
941     foreach (@text) {
942     $checksum += ord($_);
943     $checksum = ($checksum * 2) % 65536 + (($checksum & 32768) ? 1 : 0); # 16bit rotate
944     }
945     return "$len:$checksum";
946     }
947    
948     # Definition of YukiWikiDB
949     package YukiWikiDB;
950    
951     my $debug = 1;
952    
953     # Constructor
954     sub new {
955     return shift->TIEHASH(@_);
956     }
957    
958     # tying
959     sub TIEHASH {
960     my ($class, $dbname) = @_;
961     my $self = {
962     dir => $dbname,
963     keys => [],
964     };
965     if (not -d $self->{dir}) {
966     if (!mkdir($self->{dir}, 0777)) {
967     print "mkdir(" . $self->{dir} . ") fail\n" if ($debug);
968     return undef;
969     }
970     }
971     return bless($self, $class);
972     }
973    
974     # Store
975     sub STORE {
976     my ($self, $key, $val) = @_;
977     my $file = &make_filename($self, $key);
978     if (open(FILE,"> $file")) {
979     binmode(FILE);
980     print FILE $val;
981     close(FILE);
982     return $self->{$key} = $val;
983     } else {
984     print "$file create error.";
985     }
986     }
987    
988     # Fetch
989     sub FETCH {
990     my ($self, $key) = @_;
991     my $file = &make_filename($self, $key);
992     if (open(FILE, $file)) {
993     local $/;
994     $self->{$key} = <FILE>;
995     close(FILE);
996     }
997     return $self->{$key};
998     }
999    
1000     # Exists
1001     sub EXISTS {
1002     my ($self, $key) = @_;
1003     my $file = &make_filename($self, $key);
1004     return -e($file);
1005     }
1006    
1007     # Delete
1008     sub DELETE {
1009     my ($self, $key) = @_;
1010     my $file = &make_filename($self, $key);
1011     unlink $file;
1012     return delete $self->{$key};
1013     }
1014    
1015     sub FIRSTKEY {
1016     my ($self) = @_;
1017     opendir(DIR, $self->{dir}) or die $self->{dir};
1018     @{$self->{keys}} = grep /\.txt$/, readdir(DIR);
1019     foreach my $name (@{$self->{keys}}) {
1020     $name =~ s/\.txt$//;
1021     $name =~ s/[0-9A-F][0-9A-F]/pack("C", hex($&))/eg;
1022     }
1023     return shift @{$self->{keys}};
1024     }
1025    
1026     sub NEXTKEY {
1027     my ($self) = @_;
1028     return shift @{$self->{keys}};
1029     }
1030    
1031     sub make_filename {
1032     my ($self, $key) = @_;
1033     my $enkey = '';
1034     foreach my $ch (split(//, $key)) {
1035     $enkey .= sprintf("%02X", ord($ch));
1036     }
1037     return $self->{dir} . "/$enkey.txt";
1038     }
1039     __END__
1040    
1041     =head1 NAME
1042    
1043     YukiWiki - 自由にページを追加・削除・編集できるWebページ構築CGI
1044    
1045     Copyright (C) 2000,2001 by Hiroshi Yuki.
1046     結城浩 <hyuki@hyuki.com>
1047     http://www.hyuki.com/
1048     http://www.hyuki.com/yukiwiki/
1049    
1050     =head1 SYNOPSIS
1051    
1052     http://www.hyuki.com/yukiwiki/yukiwiki.cgi
1053    
1054     =head1 DESCRIPTION
1055    
1056     YukiWiki(結城ウィキィ)は参加者が自由にページを追加・削除・編集できる
1057     不思議なWebページ群を作るCGIです。
1058     Webで動作する掲示板とちょっと似ていますが、
1059     Web掲示板が単にメッセージを追加するだけなのに対して、
1060     YukiWikiは、Webページ全体を自由に変更することができます。
1061    
1062     YukiWikiは、Cunningham & CunninghamのWikiWikiWebの
1063     仕様を参考にして独自に作られました。
1064    
1065     YukiWikiはPerlで書かれたCGIスクリプトとして実現されていますので、
1066     Perlが動作するWebサーバならば比較的容易に設置できます。
1067    
1068     あとはdbmopenが使える環境ならば設置できます(Version 1.5.0以降ならdbmopenが使えなくても設置できます)。
1069    
1070    
1071     YukiWikiはフリーソフトです。
1072     ご自由にお使いください。
1073    
1074     =head1 設置方法
1075    
1076     =head2 入手
1077    
1078     YukiWikiの最新版は、
1079     http://www.hyuki.com/yukiwiki/
1080     から入手できます。
1081    
1082     =head2 ファイル一覧
1083    
1084     readme.txt ドキュメント
1085     yukiwiki.cgi YukiWiki本体
1086     yukiwiki.gif ロゴ(カラー版)
1087     yukimono.gif ロゴ(モノクロ版)
1088     jcode.pl 漢字コードライブラリ
1089    
1090     =head2 インストール
1091    
1092     =over 4
1093    
1094     =item 1.
1095    
1096     アーカイブを解く。
1097    
1098     =item 2.
1099    
1100     yukiwiki.cgiのはじめの方にある設定を確認します。
1101     通常は何もしなくてよいが、
1102     はじめは$touchfileを''にした方がよいでしょう。
1103    
1104     =item 3.
1105    
1106     yukiwiki.cgiとjcode.plを同じところに設置します。
1107    
1108     =item 4.
1109    
1110     サイズ0のyukiwiki.dbというファイルを設置します。
1111     (Perlシステムによってはyukiwiki.pag, yukiwiki.dir)
1112    
1113     =item 5.
1114    
1115     yukiwiki.cgiにブラウザからアクセスします。
1116    
1117     =back
1118    
1119     =head2 パーミッション
1120    
1121     ファイル パーミッション 転送モード
1122     yukiwiki.cgi 755 ASCII
1123     yukiwiki.gif 644 BINARY
1124     yukimono.gif 644 BINARY
1125     jcode.pl 644 ASCII
1126    
1127     $dbmopen = 1; にした場合:
1128     yukiwiki.db 666 BINARY
1129     (yukiwiki.pag, yukiwiki.dirの場合もあり)
1130    
1131     $dbmopen = 0; にした場合: (カレントディレクトリを777にしておく)
1132     . 777 (転送しない)
1133    
1134     =head1 データのバックアップ方法
1135    
1136     $dbmopen = 1;の場合は、
1137     データはすべてyukiwiki.db(.dir, .pag)に入る。
1138     これをバックアップすればよい。
1139    
1140     $dbmopen = 0;の場合は、
1141     yukiwikiというディレクトリができるので、
1142     これ以下をバックアップすればよい。
1143    
1144     =head1 新しいページの作り方
1145    
1146     =over 4
1147    
1148     =item 1.
1149    
1150     まず、適当なページ(例えばFrontPage)を選び、
1151     ページの下にある「編集」リンクをたどります。
1152    
1153     =item 2.
1154    
1155     するとテキスト入力ができる状態になるので、
1156     そこにNewPageのような単語
1157     (大文字小文字混在している英文字列)
1158     を書いて「保存」します。
1159    
1160     =item 3.
1161    
1162     保存すると、FrontPageのページが書き換わり、
1163     あなたが書いたNewPageという文字列の後ろに ? というリンクが表示されます。
1164     この ? はそのページがまだ存在しないことを示す印です。
1165    
1166     =item 4.
1167    
1168     その ? をクリックすると新しいページNewPageができますので、
1169     あなたの好きな文章をその新しいページに書いて保存します。
1170    
1171     =item 5.
1172    
1173     NewPageページができるとFrontPageの ? は消えて、リンクとなります。
1174    
1175     =back
1176    
1177     =head1 テキスト整形のルール
1178    
1179     =over 4
1180    
1181     =item *
1182    
1183     連続した複数行はフィルされて表示されます。
1184    
1185     =item *
1186    
1187     空行は段落C<< <p> >>の区切りとなります。
1188    
1189     =item *
1190    
1191     HTMLのタグは書けません。
1192    
1193     =item *
1194    
1195     B<''ボールド''>のようにシングルクォート二つではさむと、
1196     ボールドC<< <b> >>になります。
1197    
1198     =item *
1199    
1200     B<'''イタリック'''>のようにシングルクォート三つではさむと、
1201     イタリックC<< <i> >>になります。
1202    
1203     =item *
1204    
1205     B<---->のようにマイナス4つがあると、
1206     水平線C<< <hr> >>になります。
1207    
1208     =item *
1209    
1210     行をB<*>ではじめると、
1211     大見出しC<< <h2> >>になります。
1212    
1213     =item *
1214    
1215     行をB<**>ではじめると、
1216     小見出しC<< <h3> >>になります。
1217    
1218     =item *
1219    
1220     行をマイナス-ではじめると、
1221     箇条書きC<< <ul> >>になります。
1222     マイナスの数が増えるとレベルが下がります(3レベルまで)
1223    
1224     -項目1
1225     --項目1-1
1226     --項目1-2
1227     -項目2
1228     -項目3
1229     --項目3-1
1230     ---項目3-1-1
1231     ---項目3-1-2
1232     --項目3-2
1233    
1234     =item *
1235    
1236     コロンを使うと、
1237     用語と解説文のリストC<< <dl> >>が書けます。
1238    
1239     :用語1:いろいろ書いた解説文1
1240     :用語2:いろいろ書いた解説文2
1241     :用語3:いろいろ書いた解説文3
1242    
1243     =item *
1244    
1245     リンク
1246    
1247     =over 4
1248    
1249     =item *
1250    
1251     LinkToSomePageやFrontPageのように、
1252     英単語の最初の一文字を大文字にしたものが
1253     二つ以上連続したものはYukiWikiのページ名となり、
1254     それが文章中に含まれるとリンクになります。
1255    
1256     =item *
1257    
1258     http://www.hyuki.com/ のようなURLは自動的にリンクになります。
1259    
1260     =item *
1261    
1262     二重の大かっこ[[ ]]でくくった文字列も、
1263     YukiWikiのページ名になります。
1264     大かっこの中にはスペースを含めてはいけません。
1265     日本語も使えます。
1266    
1267     =back
1268    
1269     =item *
1270    
1271     行頭がスペースやタブで始まっていると、
1272     それは整形済みの段落C<< <pre> >>として扱われます。
1273     プログラムの表示などに使うと便利です。
1274    
1275    
1276     =item *
1277    
1278     行を > ではじめると、
1279     引用文C<< <blockquote> >>が書けます。
1280     >の数が多いとインデントが深くなります(3レベルまで)。
1281    
1282     >文章
1283     >文章
1284     >>さらなる引用
1285     >文章
1286    
1287     =back
1288    
1289     =head1 更新履歴
1290    
1291     =over 4
1292    
1293     =item *
1294    
1295     2001年10月20日、Version 1.6.6。
1296    
1297     更新の衝突対策。
1298     元ページの簡単なチェックサムを取っておき、
1299     更新前にチェックサムを比較する。
1300     修正個所はdigestという文字列を検索すれば分かる。
1301     本来はMD5などでちゃんとやった方がいいのだけれど。
1302    
1303     衝突時に表示されるメッセージなどは「極悪」さんのページを参考にした。
1304    
1305     =item *
1306    
1307     2001年10月17日、Version 1.6.5。
1308    
1309     プレビュー画面で、更新ボタンを押したときに送信される
1310     メッセージの内容をinput要素のtype="hidden"を使って埋め込むのをやめる。
1311     代わりに、textarea要素を使う。
1312     再プレビュー用にmyspecial_を導入。でもきれいな対策ではない。
1313    
1314     =item *
1315    
1316     2001年8月30日、Version 1.6.4。
1317    
1318     URLでダイレクトにページ名を指定しても、
1319     $WikiNameと$BracketName以外のページを作れないようにした。
1320     (is_valid_nameとis_editable参照)。
1321    
1322     =item *
1323    
1324     2001年8月30日、Version 1.6.3。
1325    
1326     RecentChangesを編集・再編集不可とした。
1327     編集不可ページは@uneditableにページ名を入れる。
1328    
1329     =item *
1330    
1331     2001年2月25日、Version 1.6.1, 1.6.2。
1332    
1333     差分機能のバグ修正。
1334     do_previewで'>'が扱えないバグを修正
1335     (ユーザからの指摘)。
1336    
1337     =item *
1338    
1339     2001年2月22日、Version 1.6.0。
1340     差分機能を実装した。
1341    
1342     =item *
1343    
1344     2001年2月19日、Version 1.5.4。
1345     画像ファイルへのリンクは画像にしてみた。
1346    
1347     =item *
1348    
1349     2001年2月19日、Version 1.5.3。
1350     RecentChangesの中に削除したページがあるのをやめた。
1351     use strict;で引っかかる部分を少し整理(完全ではない)。
1352    
1353     =item *
1354    
1355     2001年2月16日、Version 1.5.2。
1356     textareaに表示およびプレビューする前に < や > を &lt; や &gt; に変換した
1357     (do_preview, editpage, print_preview_buttons)。
1358    
1359     =item *
1360    
1361     2000年12月27日、Version 1.5.1。
1362     プレビュー画面を整理した。
1363    
1364     =item *
1365    
1366     2000年12月22日、Version 1.5.0。
1367     全体的にずいぶん書き直した。
1368     一覧を別途作成するようにした(do_list)。
1369     書き込む前に確認画面を出すようにした(do_preview)。
1370     テキストの書き方を編集画面に入れた(do_edit, do_reedit)。
1371     WhatsNew→RecentChanges、TopPage→FrontPageに変更した。
1372    
1373     =item *
1374    
1375     2000年12月20日、Version 1.1.0。
1376     tieを利用して、dbmopenが使えない場合でも動作するように修正。
1377     利用者の1人である「極悪」さんから
1378     送っていただいたコードを元にしています。
1379    
1380     =item *
1381    
1382     2000年9月5日、Version 1.0.2。
1383     <body color=...> → <body bgcolor=...>
1384     利用者からの指摘による。感謝。
1385    
1386     =item *
1387    
1388     2000年8月6日、Version 1.0.1を公開。
1389     C MAGAZINE(ソフトバンクパブリッシング)
1390     2000年10月号連載記事向け公開版。
1391     [[ ]] の最後が「望」のようにシフトJISで
1392     0x5Dになる場合の回避を行なった。
1393    
1394     =item *
1395    
1396     2000年8月5日、Version 1.0.0を公開。
1397    
1398     =item *
1399    
1400     2000年7月23日、Version 0.82を公開。
1401     編集時のリンクミス。
1402     <textarea>の属性変更。
1403    
1404     =item *
1405    
1406     2000年7月22日、Version 0.81を公開。
1407     ロゴを組み込む。
1408    
1409     =item *
1410    
1411     2000年7月21日、Version 0.80を公開。
1412     PODをCGI中に書き込む。
1413    
1414     =item *
1415    
1416     2000年7月19日、Version 0.70を公開。
1417     '''イタリック'''や、--、---、>>、>>>などを実装。
1418    
1419     =item *
1420    
1421     2000年7月18日、Version 0.60を公開。
1422     *太字*を''太字''に変更
1423    
1424     =item *
1425    
1426     2000年7月17日、Version 0.50を公開。
1427    
1428     =item *
1429    
1430     2000年7月17日、さらにいろいろ追加する。
1431    
1432     =item *
1433    
1434     2000年7月16日、いろいろ追加。
1435    
1436     =item *
1437    
1438     2000年7月15日、公開。
1439    
1440     =back
1441    
1442     =head1 TODO
1443    
1444     - テキスト表示モード
1445     - Charsetを明示。
1446     - textarea中の閉じタグ対応
1447     - メニューの英語表記付記
1448     - プレビューのボタンで、mymsgをinputのvalueに入れているが、改行をそのままvalueにいれてはいけないのではないか。
1449     - 「再編集」の機能はブラウザの back で充分ではないか。プレビューはもっとシンプルに。
1450     - ページタイトル(Wikiname)が検索にかかるようにする。
1451     - InterWiki風の機能「URLを隠しつつ、リンクを張る」
1452    
1453     =head1 作者
1454    
1455     Copyright (C) 2000 by Hiroshi Yuki.
1456     結城浩 <hyuki@hyuki.com>
1457     http://www.hyuki.com/
1458     http://www.hyuki.com/yukiwiki/
1459    
1460     質問、意見、バグ報告は hyuki@hyuki.com にメールしてください。
1461    
1462     =head1 配布条件
1463    
1464     YukiWikiは、
1465     GNU General Public Licenseにて公開します。
1466    
1467     YukiWikiはフリーソフトです。
1468     ご自由にお使いください。
1469     自分好みのYukiWikiが作れるようにシンプルにしてあります。
1470    
1471     =head1 謝辞
1472    
1473     本家のWikiWikiを作ったCunningham & Cunningham, Inc.に
1474     感謝します。
1475    
1476     YukiWikiを楽しんで使ってくださる
1477     ネット上の方々に感謝します。
1478    
1479     YukiWikiのロゴをデザインしてくださった橋本礼奈さん
1480     http://city.hokkai.or.jp/~reina/
1481     に感謝します。
1482    
1483     tieを使った版の元になるコードを送ってくださった
1484     「極悪」さんに感謝します。
1485    
1486     =head1 参照リンク
1487    
1488     =over 4
1489    
1490     =item *
1491    
1492     YukiWikiホームページ
1493     http://www.hyuki.com/yukiwiki/
1494    
1495     =item *
1496    
1497     本家のWikiWiki
1498     http://c2.com/cgi/wiki?WikiWikiWeb
1499    
1500     =item *
1501    
1502     本家のWikiWikiの作者(Cunningham & Cunningham, Inc.)
1503     http://c2.com/
1504    
1505     =item *
1506    
1507     YukiWikiのロゴデザインをしてくださった橋本礼奈さんのページ
1508     http://city.hokkai.or.jp/~reina/
1509    
1510     =back
1511    
1512     =cut

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24