最終更新: 2013-11-08T01:03+0900
表意文字で文章を書いていて使わない手はないでしょう(ローマ字がないと交差点の名前とか読めませんから)。こんなかんじで書くと、
ルビを付けたいテキスト ルビを付けたいテキスト
こういう HTMLになる(※インデントとコメントは後から追加した)。
<ruby> <rb>ルビを付けたいテキスト</rb> <rp>(</rp> <rt>ルビ</rt> <rp>)</rp> </ruby> <ruby> <rbc><rb>ルビを付けたいテキスト</rb></rbc> <rp>(</rp><!--本当ならここに rpタグは使えません--> <rtc><rt>ルビ1</rt></rtc> <rp> / </rp><!--本当ならここに rpタグは使えません--> <rtc><rt>ルビ2</rt></rtc> <rp>)</rp><!--本当ならここに rpタグは使えません--> </ruby>
2番目はうまく表示されないね。
シグナルイエローには《危険信号》っていう字があるから、本名と二つ名をルビで並記しようとするとルビのルビが必要になる。が、rb,rt要素は rubyを除いたインライン要素しか包含できない。
2番目の複雑形式は非サポートブラウザ向けのフォールバックがない(www.w3.org)らしいから rpタグを挿入するのは間違いだった。1.2.3 Complex ruby markupの最後に The rp element is not availableってしっかり書いてある。Firefoxも Operaもルビをサポートしないなりに期待通りの表示をしてくれたので良しとしておくが、よくよく考えてみれば、対応ブラウザには表示しないことを求め、非対応ブラウザにはタグを無視して中身をそのまま表示してもらうことを期待したタグなんだから、細かい出現規則(単純形式だけとか 0回か 2回(※rt要素の前後に限る)しか出現できないとか)を決めるのも従うのも意味がないのではないか。HTML5(dev.w3.org)は1番目の形式だけ、それも rbタグを取り除いたものがサポートされるらしい(ただしテキスト-rt要素の並びが1つの ruby要素の中に複数出現可能。だがベーステキストとルビがインターリーブされることで読み上げ、スタイルの適用、スクリプト操作、コピペなどに不都合があるとも)。ドラフトを読むと rubyタグのネストとか連続する rt要素で
細かい補足。パッチは別のパッチをあてた hikidoc.rbがベースだから素直にはあたらない。変更点の参考程度に。二重かっこでマークすることにしたけどかっこの対応は気にしてない(このいい加減さが rubyタグのネストを防いでたりする)。rtcタグが一度に1回か2回しか出現できないと知らなくて、ルビをいくつでも振れるかのようなコードになってるのが良くない。URLはリンクになるし強調とか打ち消しとかの装飾も、ベースになるテキストとルビ双方で有効みたい(hikidoc的にも XHTML的にも)。ルビに装飾って有用性がわからんけど、実は単なるインラインのテーブルだからできちゃいました(※後で苦労するフラグ)ってことなんか?
rubyforge.orgは Subversionサポートをやめたのかな。http://hikidoc.rubyforge.org/svn/ にリソースが見つからない。hikidocの最新版はどこから?
octocatいいよね(最後にこれはちゃぶ台返し?)。
自分でそういう書き方をするつもりがない(見た目が嫌いだ)から配慮しなかったけど、熟語単位でルビを付けつつ、ルビを個々の文字の上に配置したいかもしれない。熟語の上にじゅくごではなく、熟の上にじゅく、語の上にごを置きたいが、熟(じゅく)語(ご)みたいには書くのも表示されるのも嫌だという場合。セパレータを何か決めて、
熟,語 火,継,ぎの,祭,祀,場
こうかな?2番目はひらがな部分のルビを省略してるからフォールバックがうまくいかないだろうな。かといってルビで単語を分断したくないからこういう書き方を考案してるわけで、ひらがなにもルビをつけるしかないのか@2013-11-08。
hikidocで表のセルを横方向に連結する文字が > だから、 > や >> で区切ったときはその直前のセルが2マス3マス分に伸びるとか?
蟷 螂 大 人 気
入力のしやすさやノイズとしてあまり目立たないことからいえばスペースで区切りたいけど、ルビの部分に英語がきたり、空白で区切った姓名にルビを振るときに困る。とりあえず、使わないのに無用な複雑さは導入しない(さっさと決めないとアップデートで既存の文章を壊す危険性が増すとも考えられるが)。TODOにしとく。
1998年のルビの文書「ルビ(www.doraneko.org)」(翻訳されたW3Cワーキングドラフト)を読んでたら ruby-devで見かける名前を見つけた。バナナの下の力持ち。えらい人だなあ。
「ルビのために(hp.vector.co.jp)」という文書を読んでいたら最後にぼんやりと見覚えのある名前(筆者)を発見。そうだこの名前はここで見た。「JintrickのマイクロWeb日記, 2012 秋」
狭いなあというよりは。怖いなあ。
という経過を辿った。1分も考えなかった。ルビを振りたいテキストを囲むかっこはなくても困らないんだろうけど、1番目の理由によって存在している。
@2013-11-08 [[2.4. Autohiding Annotations(www.w3.org)|http://www.w3.org/TR/2013/WD-css3-ruby-20130919/#autohide]]
というよりは [[大規模ネットワークの性質と先端グラフアルゴリズム|http://www.slideshare.net/iwiwi/ss-15517666/7]]
最終更新: 2012-01-17T01:44+0900
自分の稼働中の tdiary.rbを見てみると該当部分はこうなっていた。
def load_plugins calendar @lazy_diaries.update( @diaries ) if @plugin @plugin.diaries = @lazy_diaries @plugin.date = @date @plugin.last_modified = last_modified @plugin.comment = @comment return @plugin end @plugin = Plugin::new( 'conf' => @conf, 'mode' => mode, 'diaries' => @lazy_diaries, 'cgi' => @cgi, 'years' => @years, 'cache_path' => cache_path, 'date' => @date, 'comment' => @comment, 'last_modified' => last_modified, 'logger' => @logger ) end
if @plugin ... end部分はたしか kazuhikoさんのコードだったような……。
そうそうこれこれ*。>>脳log[2009-08-06-p01] tdiary.rbと plugin/navi_user.rbにパッチあて(plugin/recent_list.rbの分は使用してないのでスルー) + セクションごとの最終更新日時に一票 >>[tDiary-devel] all_filtersとかload_pluginsが呼ばれ過ぎで遅い件
@plugin.diariesと @plugin.dateと @plugin.comment、それとたぶん @plugin.last_modifiedに更新が必要なのはすでにわかってた話なんじゃないの、ってことなんだけど、このコードは tDiary-3.0.1になったときに失われてる。svn logは「Merge branch 'master' into svn」なので経緯は不明。
>>Re: [tDiary-devel] #194 blog-category.rbが動作しない
* 「@plugin」で引っかからなかったので「捨ておけぬ」でサイト検索して見つけた。
最終更新: 2011-10-28T14:10+0900
^http://www\.google\.(?:[^/]+)/url\?.*?[&;]cd=(1\d)\b.*?[&;]url=[^&;]*?(\d{6}[\dpct]*).*?[&;]q=([^&;]+).*$ google検索 \1th of「\3」→ \2 ^http://www\.google\.(?:[^/]+)/url\?.*?[&;]cd=(\d*1)\b.*?[&;]url=[^&;]*?(\d{6}[\dpct]*).*?[&;]q=([^&;]+).*$ google検索 \1st of「\3」→ \2 ^http://www\.google\.(?:[^/]+)/url\?.*?[&;]cd=(\d*2)\b.*?[&;]url=[^&;]*?(\d{6}[\dpct]*).*?[&;]q=([^&;]+).*$ google検索 \1nd of「\3」→ \2 ^http://www\.google\.(?:[^/]+)/url\?.*?[&;]cd=(\d*3)\b.*?[&;]url=[^&;]*?(\d{6}[\dpct]*).*?[&;]q=([^&;]+).*$ google検索 \1rd of「\3」→ \2 ^http://www\.google\.(?:[^/]+)/url\?.*?[&;]cd=(\d+).*?[&;]url=[^&;]*?(\d{6}[\dpct]*).*?[&;]q=([^&;]+).*$ google検索 \1th of「\3」→ \2
パラメータの順番を仮定しないために、結局こうなった。名前付きキャプチャは関係なくて、繰り返し付きキャプチャに最終的に入るものを把握することが大事。
^http://www\.google\.(?:[^/]+)/url(?:[?&;](?:url=[^&;]*?(\d{6}[\dpct]*)[^&;]*|q=([^&;]+)|cd=(1\d)|(?!cd=)[^&;]*))*$ google検索 \3th of「\2」→ \1 ^http://www\.google\.(?:[^/]+)/url(?:[?&;](?:url=[^&;]*?(\d{6}[\dpct]*)[^&;]*|q=([^&;]+)|cd=(\d*1)|(?!cd=)[^&;]*))*$ google検索 \3st of「\2」→ \1 ^http://www\.google\.(?:[^/]+)/url(?:[?&;](?:url=[^&;]*?(\d{6}[\dpct]*)[^&;]*|q=([^&;]+)|cd=(\d*2)|(?!cd=)[^&;]*))*$ google検索 \3nd of「\2」→ \1 ^http://www\.google\.(?:[^/]+)/url(?:[?&;](?:url=[^&;]*?(\d{6}[\dpct]*)[^&;]*|q=([^&;]+)|cd=(\d*3)|(?!cd=)[^&;]*))*$ google検索 \3rd of「\2」→ \1 ^http://www\.google\.(?:[^/]+)/url(?:[?&;](?:url=[^&;]*?(\d{6}[\dpct]*)[^&;]*|q=([^&;]+)|cd=(\d+)|(?!cd=)[^&;]*))*$ google検索 \3th of「\2」→ \1
リファラ周りを覗いたついでに、セルフリンクの記録を解禁した。my-sequel.rbプラグインの機能を代替できるかな、と。
tdiary.confのベースとした tdiary.conf.sampleに最初からセルフリンクよけがあるのでこれを削除。dayモードからのリンクでないものを除外するように(=dayモードからのリンクだけを記録するように)ブラウザで設定した。(index.rbの有無とか日付を ?date=形式で渡してるかどうかとかサイトに合わせた調整が必要)
リンク元記録除外リスト ^http://vvvvvv\.sakura\.ne\.jp(?!/ds14050/diary/\d{8})
最新の日記にだけ表示される「以前の日記へのリンク元」からのリンクは自己言及とは言えずノイズでしかないので 05referer.rbの referer_save_currentに次のコードを挿入した。
# reject self-reference from (maybe) volatile list return if referer.start_with?(@conf.base_url) and (date = referer.scan(/\b(\d{4})(\d\d)(\d\d)\b/).last) and latest_day?(Struct.new(:date).new(Time.local(*date)))
期待通り動いてるか動いてないかはまだ観察できてない。
「以前の日記へのリンク元」へセルフリンクを記録しない方法をとらなかったのは、そこで過去の日記の動きを観察したかったから。でもリストに記録された日からどこへ移動したかはわからないね。リファラを集約するときに「どこへ(のリンクか)」という情報を蔑ろにしたせいだ。余計なお世話だと思ってた googleの新しい形式(といっても2009年)のリファラがその情報を与えてくれてたりする。
tdiary.conf.beginnerには FeedReaderからのリンクを記録しない設定が予め用意されている。俺も除外したいと思った。でも tdiary.conf.sampleには用意されていない。beginnerの定義、.beginnerと .sampleを分ける法が知りたい。
どうやら volatile referer listからのリンクの除外に失敗してる。原理的にリファラを表示したときとリンクを踏んだときの間に日記の投稿があった場合には除外できないんだけど、それにしては数が多い。volatile referer listは特別に内部リンクを表示しないことにする。
@referer_volatile.each_referer( limit ) do |count,ref| next if ref.start_with?(@conf.base_url) # この一行。 result << %Q[<li>#{count} <a rel="nofollow" href="#{h ref}">#{h disp_referer( @referer_table, ref )}</a></li>\n] end
最終更新: 2011-08-04T03:29+0900
日と日の境が行き過ぎてしまわないようにスクロール量を制限する。
うっとうしいかな? スムーズスクロールも効かないしな。でも AutoPagerizeの機能の一部としてあってもいいような気もする。
動作確認済み> Firefox 5.0, Internet Explorer 9, Safari 5.0.2, Opera 11.50
* この日記(tDiary-2.3.3.20091124)にはまだ js_urlメソッドとか enable_jsメソッドとか無いのです。
最終更新: 2017-09-15T10:23+0900
なんというか……ばっちい(だから非公開(書き直すつもりがないので観念した))。だらだら書き下すとこうなるのね、っていう。申し訳のように抜き出したカスタマイズポイントもうまく分離できなかった記憶がある。たんに切り取って別の場所に貼り付けただけだろう、と。svn logを見てみたら 2009年3月に書いたものらしい。最初はセクションごとに表示する仕様で、それだと日記を分断して邪魔なので一日の最後に表示するオプションを付けたとか。……logによれば。とりあえずこの日記(tDiary-2.3.3.20091124, Ruby-1.8.7-p248)では動いてるみたい。
「カテゴリ[……]の他の日記」リンクの URLはこの日記に特有の、最新表示とカテゴリ表示をくっつけたもののだから他の日記では使えないね(それも非公開の理由)。
2007年や 2009年の日記のタイトルが 2011年のこの日記から参照できてるのは「プラグインが自由に日記データを取得できる手段を提供した」恩恵を my-exプラグインが受けているからじゃないかと推測してる。
最終更新: 2012-12-12T02:17+0900
適当にカスタマイズして設置した。
XHTML化キットの存在は投稿されたアナウンスを読んで知ってたんだけど、XHTML好きにもかかわらず今まで導入してなかったのは、日記に数式を書く機会がなかったのと、出力段階での文字列置換による XHTML化が乱暴に思えたから。結局、他に方法がないので目をつぶったが。
閲覧不可能になった日記がないか月別表示で全日記を表示してみたら 10か所くらい見つかった。パターンは以下の通り。
<script>タグの中であっても & は & と書かなければいけない。不等号も。でも if(a && b) {}
とか置換してしまったらアホだ。CDATAセクションを使う。
scriptタグが解釈できないブラウザなんて PCを手に入れた当時から持ってなかったので
<script><!-- --></script>
なんて最初から書かなかったし、XHTMLを知ってからは下のように書くことにしてる。
<script>/*<![CDATA[*/ /*]]>*/</script>
コメントで囲うのは JavaScriptとして正しくあるため。より字数の少ない行コメントにしないのは、なにかでエラーになったから。MSがリリースしてた JavaScriptを暗号化(難読化)するツールに <script>タグを含んだ HTMLファイルを通したときだったか……。
但し書きを付けても無駄。HTMLと日本語は混ぜるな危険。中途半端な HTMLの隠蔽は悪。tDiaryスタイルと etDiaryスタイルより Wikiスタイル。
自業自得。さすがに、タグに挟まれた部分に & や <, > を放置してたわけではない。
<a href="ttp://hoge/script.cgi?a=b&c=d"> <img src="ttp://hoge/script.cgi?a=b&c=d">
すべてこの形の & がエラーになっていた。HTMLを出力するスクリプトでは、属性値は機械的に必ず HTMLエスケープするようにしてるんだけど、手書きだと上のような (X)HTMLは正しく見えてしまうのか、忘れられてた。こんなことがあるとやっぱりセミコロンで区切りたいねえ。
それにしても、ちらちら目に入る古い日記が恥ずかしくて死ねる。読む人間も、そもそも読める内容もないのであえて気づかぬふりで放置するけども。
最終更新: 2010-05-01T17:13+0900
リストをネストしたときだけ生のテキストに<p>タグが付くのがオリジナルのはてなと違うんだそう。ついでに空の<p>タグが生成されるのも気になるけどそれは別。
最初に。<li>はブロック要素もインライン要素も包含可能らしいけど両方を並べて含んでいいのかはわからなかった。読めない(block or inlineの 0以上の繰り返し、だという気はする)。Another HTML-lintが文句を言わなかったのでいいものとしよう。
次に、CodeReposの hatena_style.rbの中を見てみる。Hatena::Blockがなにやら特別な位置を占めてるけど、一応 Hatena::Inlineと同じように振る舞うみたい。ところで、Hatena::Blockはブロック要素の連なり、Hatena::Inlineはインライン要素の連なりを表してるけど、その両方の連なり(<li>の中身とか)を表すクラスがない。
てっとりばやく済ませるために、Hatena::BlockAndorInlineというクラスを用意して Hatena::Blockが行っていた仕事をごっそり移動するそれだけでなく。Hatena::BlockAndorInlineにはインライン要素を含んでいいのかを示すフラグを initializeを通して与え、それにより何が何でも<p>タグで囲むのかそのままにするのかという動作を変えることにした。現状、求められてるのがその違いだけだから。
Hatenaスタイルは使ってないからあからさまなバグがあっても気付かない可能性が大きいです。
suppress_ptag_in_nested_list.patch (7.1KiB, 2010-04-16, r37217との差分)古くなりました。
順序付きリストや定義リストの対応を忘れてる。
<dt>の中はインライン要素の 0以上の繰り返し、<dd>の中はインライン要素とブロック要素の 0以上の繰り返しっぽい。Hatenaスタイルでは定義リストにはインライン要素しか認めていないので対応は必要なし。順序付きリストだけ。
suppress_ptag_in_nested_list.rev2.patch (8.2KiB, 2010-04-16)
svn diffで -x -p (-x <ARG>: diffに渡すオプション, -p: Cの関数名を表示)を初めて付けてみたけど、Rubyスクリプトであってもクラス名は拾ってくれるみたい。
次々空いてくる穴をふさぐのはどちらかといえば好きだけど……。
それはそれとして、
Hatena::Section#bodyは期待通りうごかないかも。(目的がデバッグ出力だったなら OKだけど)
def body @tree.body.to_s end
@treeは以前は Hatena::Blockか Hatena::Inlineだった、俺の変更で Hatena::BlockAndorInlineか Hatena::Inlineになった。カスタマイズされた to_sメソッドを持っているのは Hatena::Blockだけ。
肝心の深すぎる再帰は再現できない。自分自身を子要素に持ってるなんてことはないはずだから convertメソッドが自分自身を呼び出すしかない。その可能性があったのは Hatena::Block#convertのみ。そのときは if @body == selfでチェックして @body(Hatena::Block).convertを回避していたし、今は @bodyに Hatena::Inlineか Hatena::BlockAndorInlineしか入っていないから自分自身を呼ぶことは考えられない。わからん。キャッシュのせいだったら拍子抜けだなあ。<<<以前は @bodyに Hatena::Blockが入っていたんだから、古いデータと新しいコードの組み合わせで無限再帰は十分ありうる話。.parserキャッシュにはクラス名やインスタンス変数も入ってるし。
fix_c37239.patch (623B, c37239の修正)
サブタイトルの有無の判断が間違って反対になっていた。
サブタイトルのみのときにエラーになっていた。
fix_c37240.patch (955B, c37240の修正)
順序付き、順序なしリストの階層が勝手に深くなっていた。
セクションの分割に失敗していた。
たぶん HTMLソースレベルの空行なんだと思う。Hatena::BlockAndorInline#convertでインライン要素の後だけは改行を付加しないようにすればなくせるが、<p>の有無と違って(white-space:preとかしない限り)見た目に関与しないのでこだわらない。(<p>の後ろと</p>の前にも改行が挿入される、だとか考え始めるときりがない)
スレ主は hatena_style.rbを使うことにこだわってはいないという。こちらは楽しんでやってるんだけど、いつまでもデバッグに付き合ってもらうわけにもいかないだろう。はてなはウクレレ記法ができてこそのはてなだと思ってるので、それができない hatena_style.rbから乗り換えるのも悪くないと思う。
「ウクレレ記法」で検索したら主犯が見つかった。最初は Hikiに実装したって書いてるよ。(始めて一か月ほどだったとも書かれていて)つくづくプログラミングは何に使うかが大事だと思う。(英語なんかと一緒でツール)
http://coderepos.org/share/ に書かれている通りにメールを送ってみたが音沙汰がない。スパム判定されたか。comitterってそのページに書かれてるので全てだよね。行動力のある人間なら(というか人並みの人でも) Twitterなり IRCなり blogなりでアクションを起こすんだろうが……。(だから手続きが自動化されてなさそうなのが嫌だったんだ。漏れるし遅れるし。それにサービス自体個人の奉仕に依存してる)
気になる。自分のバグも他人のバグも気になる。
Hatena::Section#initializeの部分の修正は、既に行頭の *で分割された文字列が与えられるから問題ないとして先のファイル(fix_c37240.patch)に含めてなかったけど、まあ subが gsubになったって減るもんじゃなし。
それだけでなく 実は一行だけ buffer = '' を buffer = lines.shiftに書き換えた。これがないと無限ループに陥るケース(「>>>」という本文。3文字目は他の文字でもいい)があるみたいなので。
最終更新: 2010-05-07T12:01+0900
設定画面に jQueryが仕込まれたのと、JavaScriptの置き場所が用意されたので、ファイルのアップロードを非同期に行う準備が整ったのではないか。独自のサブミットボタンを編集画面に貼り付けるプラグインが全部非同期になれば、プレビュー画面にアップロードフォームを表示しても(編集中の本文が消えたと)怒られないだろう。
ジャンクだけど Firefox3.6と Internet Explorer8と Google Chrome4.1で動くものをこの日記(最新じゃないので jQuery対応ではない)に仕込んでみた。
これで本文を書きながらプレビューもできるしファイルのアップロードもできる。
クリップボードアクセスのために Flashを使うよりはましだけど、<iframe>に頼らなければいけないというのもわりと屈辱的。<frameset><frame>以上に、これまで存在を認めてこなかったのに。
何となくできないものと思っていたけど <iframe>の中から外のドキュメントにアクセスすることができるみたい。それができたら編集画面が無制限に入れ子になる(編集画面の<iframe>の中の編集画面の<iframe>の中の……)ことを今より簡単に防止できて、スクリプトももうちょっと使いやすく書き換えられる。
「Karetta|[JavaScript] Firefoxでtextareaのカーソル位置に文字列を挿入した後にスクロールが先頭に戻ってしまう問題(karetta.jp)」で紹介されている通りで直った。といってもできるだけ余計なことをしないように条件を付けたが。
var scrollTop = textarea.scrollTop; /* ここで、テキストエリアに文字列を挿入する。 */ if(0 == textarea.scrollTop && 0 < scrollTop) { // スクロール位置がリセットされていたので復元する。 textarea.scrollTop = scrollTop; }
ところで、Internet Explorer 8だけはテキストエリアのキャレット位置を保存しようなんてことを考えなかったみたいで、いったんテキストエリアがフォーカスを失うとキャレットが必ず末尾に移動する。スクロール位置がどうこうどころではないわな。
「何となくできないものと思っていたけど <iframe>の中から外のドキュメントにアクセスすること」 < HTML5 sanbbox属性
最終更新: 2014-12-05T17:23+0900
送信元が限られてるのかワンパターンだったので、これまでは </a> を NGワードにしてほとんどの spamを防いでいたんだけど、今日は手ひどくやられた。
フィルタはインストール方法がよくわからない。Comment-key Filterを tdiary/filter/ に設置したがエラーが出るので、とりあえず最新の 2.3.3.20091124にアップデートした。そうすると plugin/60sf.rbが有効になっているはずなので misc/filter/ にフィルタスクリプトを、misc/filter/plugin/ にフィルタに関連する設定などを表示するプラグインスクリプトを、misc/filter/plugin/ja/ などにプラグインの言語リソースをインストールし、設定画面でフィルタを有効にするといずれのスクリプトも読み込まれるようになる。
コメントフォームにキー文字列を埋め込み、コメントが HTMLフォームを通して投稿されたことを確認する。キーは日記設置者の決めた文字列と日記の日付によって一意に決められるので、ある日のキー文字列を一度取得すればその日には何件でも機械的に投稿することができる。GETして HTMLを切り刻む手間をかければ最初から機械的に投稿することもできる。そんなのは防げない。
tDiary-2.3.3.20091124にインストールするのなら key.rbを misc/filter/key.rbへ、comment_key.rbを misc/filter/plugin/key.rbへ、ja/comment_key.rbを misc/filter/plugin/ja/key.rbへコピーすれば良い。key.rbというファイル名と KeyFilterというクラス名は対応しているので、key.rbから comment_key.rbへの改名はやめたほうがいい。コピー後にフィルタを有効にし、キー文字列を設定する。
連投対策。同じ IPアドレスからの連続する投稿をはじく。30分につき 3件までしか許可しない、など。
# coding: utf-8 # # limit_freq.rb: # # a spam-filtering plugin of tDiary # rejects frequent comments posted from an IP address. module TDiary::Filter class LimitFreqFilter < Filter def comment_filter( diary, comment ) now = Time.now.to_i comment_i = Comment_t.new( now, @cgi.remote_addr ) # 数値形式の日時と文字列形式の IPアドレスの二要素 # 配列の配列を、日時をもとに昇順にソートしたもの。 # log = [[123456, "1.2.3.4"], [234567, "5.6.7.8"],...] log = [] require 'pstore' ps = PStore.new( cache_path ) ps.transaction( false ) { |db| log = db.fetch( DBRoot, log ) # 新しいコメントを logに追加する。 log.insert( ArrayExtension.lower_bound(log){|pair| pair.first - comment_i.time }, [comment_i.time, comment_i.ipaddr] ) # 古い logを捨てる。 oldest = now - time_span log.slice!( 0 ... ArrayExtension.lower_bound(log){|pair| pair.first - oldest } ) db[DBRoot] = log db.commit } # 残った logに投稿者の IPアドレスがいくつ含まれるか数え、 # その数が閾値を超えていないか確かめる。 count = 0 if log.all?{|pair| count += 1 if pair.last == comment_i.ipaddr count < threshold } then return this_is_not_a_spam( comment ) else debug("limit_freq.rb: spam: the # of comments posted from #{comment_i.ipaddr} exceeds threshold:#{threshold} within the last #{time_span.to_i}seconds.") return this_is_a_spam( comment ) end rescue debug("limit_freq.rb: BUG: #{$!}") return this_is_not_a_spam( comment ) end private DBRoot = 'LimitFreqLog' Comment_t = Struct.new(:time, :ipaddr) def cache_path return File.join( (@conf.cache_path || "#{@conf.data_path}cache"), 'limit_freq.data' ) end # どれだけの期間(秒単位)、コメントの投稿頻度の判定に # 利用するログ(日時とIPアドレスのペア)を保存するか。 def time_span 30 * 60 end # time_span当たり、何件目の投稿からを拒否するか。 def threshold 4 end module ArrayExtension # ソート済みだという前提を活かしていない! def lower_bound( arr, &block ) index = 0 arr.length.times{ return index if 0 <= yield( arr[index] ) index += 1 } return index end module_function :lower_bound end end end
やめればいいのに本体もちょろちょろと変更。
Index: core/tdiary.rb =================================================================== --- core/tdiary.rb (リビジョン 44436) +++ core/tdiary.rb (作業コピー) @@ -342,6 +342,22 @@ @logger.info("#{@cgi.remote_addr}->#{(@cgi.params['date'][0] || 'no date').dump}: #{msg}") end + + private + # config: hide or drop spam comment + def hide_spam? + return @conf.options.include?('spamfilter.filter_mode') && @conf.options['spamfilter.filter_mode'] + end + + def this_is_a_spam( comment ) + comment.show = false + return hide_spam? + rescue + return false # comment could be a String(@cgi.referer). + end + def this_is_not_a_spam( comment ) + return true + end end end @@ -1303,7 +1319,7 @@ filter_path = @conf.filter_path || "#{PATH}/tdiary/filter" Dir::glob( "#{filter_path}/*.rb" ).sort.each do |file| require file.untaint - @filters << TDiary::Filter::const_get( "#{File::basename( file, '.rb' ).capitalize}Filter" )::new( @cgi, @conf, @logger ) + @filters << TDiary::Filter::const_get( "#{File::basename( file, '.rb' ).split(/[-_]+/).map{|x|x.capitalize}.join('')}Filter" )::new( @cgi, @conf, @logger ) end end Index: core/plugin/60sf.rb =================================================================== --- core/plugin/60sf.rb (リビジョン 44436) +++ core/plugin/60sf.rb (作業コピー) @@ -4,10 +4,9 @@ # Modified by KURODA Hiraku. SF_PREFIX = 'sf' -@sf_path = ( @conf["#{SF_PREFIX}.path"] || "#{::TDiary::PATH}/misc/filter" ).to_a -@sf_path = @sf_path.collect do |path| - /\/$/ =~ path ? path.chop : path -end +@sf_path = ( @conf["#{SF_PREFIX}.path"] || "#{::TDiary::PATH}/misc/filter" ).to_a.map{|path| + path.chomp('/') +} # get plugin option def sf_option( key ) @@ -128,7 +127,7 @@ if File.readable?( path ) then begin require path - @sf_filters << TDiary::Filter::const_get("#{File::basename(filename, ".rb").capitalize}Filter")::new(@cgi, @conf, @logger) + @sf_filters << ::TDiary::Filter::const_get("#{File::basename(filename, ".rb").split(/[-_]+/).map{|x|x.capitalize}.join('')}Filter")::new(@cgi, @conf, @logger) plugin_path = "#{dir}/plugin/#{filename}" load_plugin(plugin_path) if File.readable?(plugin_path) rescue Exception
一件二件すり抜けるぐらいはいいかと思っていたがここ数日は毎日なのでいいかげん腹が立つ。なによりアクセスログを見たらヒット数、転送量トップが spammerだというのが決定的に許せない。節度を知れ。iptablesはいじれないので .htaccessで Apacheに拒否してもらう。
spamコメントがくる月っていうのは、いつもの決まった 2、3か国からのアクセスではなく 10近い国から少数ずつアクセスがあるもんだけど、日単位では固定の IPアドレスから数十件の spamがくるのが常だった。でも今日は一件一件みごとに IPアドレスが異なっている。IPアドレスブロックで次のステージに進んでしまったのか。
spamコメントの目的が特定の URLへの誘導であるかぎりは、コメントに含まれる URLのドメインが白か黒かを判定する方法が有効でしょうね。外部に問い合わせるのは避けたかったんだけど、spamコメントの内容が URLも含めてワンパターンだから実際の問い合わせはごくごく限られた回数にできそうだし、悪くないかな。
ここで上記の comment-keyフィルタも含めてスパムフィルタの最新版が管理されていた。 >http://coderepos.org/share/browser/platform/tdiary/filter
plugin/00default.rbに含まれるメソッド navi_itemを自分でも使っていたのだけど、tDiaryをアップデートしたら三番目の引数が真偽値からリンクの rel属性文字列へと変更されているせいで rel="true" なるリンクができていた。こうする。
def navi_item( link, label, rel = nil ) rel = "nofollow" if rel == true # backward compatibility
スパムコメント一掃のために YYYYMM.tdcを削除したけど日記の編集画面からコメントが消えない。YYYYMM.parserを開くとコメントが含まれていたし、これを削除すると編集画面からもコメントが消えた。原因はデータファイルを直接削除するというイレギュラーな操作だけど、日記の編集はマスターデータに対して行いたい気もする。
最終更新: 2013-05-21T00:39+0900
<h4>と <h5>にもアンカー(<a name="..."></a>)を与える。href属性や中のテキスト( )は URLをコピーしやすくするためのおまけ。そもそも <h4>や <h5>に id属性を付加するのでなく <a>を利用しているところからが利便性目的以外のなにものでもない。<h4>と <h5>の中に <a>を置くことには、highlight.rbを修正しなくてもハイライトが機能するというメリットもある。デメリットは見出し語の先頭に余分な空白が挿入されている、ということ。セクションタイトル右端の編集用リンク「✍」は、<h3>に含まれないように気をつかっているのですよ。
def do_html4( date, idx, opt ) strdump = lambda{|s| s.dump.gsub( /%/, '\\\\045' ) } r = @html.lstrip r.sub!( %r!<h3>(.+?)</h3>!m ) do "<h3><%= subtitle_proc( Time::at( #{date.to_i} ), #{strdump.call $1} ) %></h3>" end or r.sub!( %r!^<p>(.+?)</p>$!m ) do "<p><%= subtitle_proc( Time::at( #{date.to_i} ), #{strdump.call $1} ) %></p>" end serial = [nil, nil, nil, '%02d'%idx, '00', '00'] dumped_param = strdump.call "#{date.strftime( '%Y%m%d' )}#p#{'%02d' % idx}" header_t = Struct.new(:opentag, :level) bqlevel = 0 r.gsub!( %r!(<blockquote\b)|(</blockquote>)|<h(4|5)[^>]*>!i ) do bqlevel += 1 if $1 bqlevel -= 1 if $2 next $& if bqlevel != 0 or $3.nil? h = header_t.new($&, $3.to_i) serial[h.level, serial.length-h.level] = Array.new(serial.length-h.level){|i| i==0 ? serial[h.level].succ : '00' } frag = 'p' + serial[3 .. h.level].join('.') %!#{h.tag}<a name="#{frag}" href="<%=anchor(#{dumped_param}).gsub(/#p#{'%02d'%idx}/, #{strdump.call('#'+frag)}) %>"> </a>! end r.gsub( /<(\/)?tdiary-section>/, '<\\1p>' ) end
2段階に評価されるせいで、すんごく読みにくい。
ハッシュテーブルのキーがシンボルか文字列か気にしたくないのと、括弧や引用符やコロンがじゃまくさいので、. ひとつで済む Structに登場してもらった。
lambdaの .call() も嫌いだなあ。[] で () を代用するおぞましさよりはマシだが。
試してないけど、「end or r.sub!(...」を複数の行に分けるとシンタックスエラーになりそうなのも嫌。
破壊的メソッドが返す nilを利用するコードを初めて書いた!
H4のシリアルナンバーが増加したときに H5のシリアルをリセットするように変更した。以前はこういう連番だった。
最後が p01.02.01になるように。
URLフォーマットが変わると影響範囲が広くて困る。自分の日記へのリンクを検出する正規表現をあちこちで書き直した。
Index: core/tdiary/wiki_style.rb =================================================================== --- core/tdiary/wiki_style.rb (リビジョン 43193) +++ core/tdiary/wiki_style.rb (作業コピー) @@ -149,7 +149,7 @@ hikihtml = HikiDoc::HTMLOutput.new html.gsub!( %r!<a href="(.*?)">(.*?)</a>! ) do k, u = hikihtml.unescape_html($2), hikihtml.unescape_html($1) - if /^(\d{4}|\d{6}|\d{8}|\d{8}-\d+)[^\d]*?#?([pct]\d+)?$/ =~ u then + if /^(\d{4}|\d{6}|\d{8}|\d{8}-\d+)\D*([pct]\d+(?:\.\d+)*)?$/ =~ u then %Q[<%=my '#{$1}#{$2}', '#{escape_quote CGI.escapeHTML k}' %>] elsif /:/ =~ u scheme, path = u.split( /:/, 2 ) Index: core/plugin/00default.rb =================================================================== --- core/plugin/00default.rb (リビジョン 43193) +++ core/plugin/00default.rb (作業コピー) @@ -480,7 +480,7 @@ # make anchor string # def anchor( s ) - if /^([\-\d]+)#?([pct]\d*)?$/ =~ s then + if /^([\-\d]+)#?([pct][\d\.])?$/ =~ s then if $2 then "?date=#$1##$2" else @@ -540,7 +540,7 @@ # make anchor tag in my diary # def my( a, str, title = nil ) - date, frag = a.scan( /^(\d{8}-\d+|\d{8}|\d{6}|\d{4}|)\D*([pct]\d+)?$/ )[0] + date, frag = a.scan( /^(\d{8}-\d+|\d{8}|\d{6}|\d{4})\D*([pct]\d+(?:\.\d+)*)?$/ )[0] anc = frag ? "#{date}#{frag}" : date index = /^https?:/ =~ @index ? '' : @conf.base_url index += @index.sub(%r|^\./|, '') Index: plugin/html_anchor.rb =================================================================== --- plugin/html_anchor.rb (リビジョン 43193) +++ plugin/html_anchor.rb (作業コピー) @@ -14,7 +14,7 @@ if @conf.index.empty? or /\/$/ =~ @conf.index def anchor( s ) - if /^([\-\d]+)#?([pct]\d*)?$/ =~ s then + if /^([\-\d]+)#?([pct][\d\.]*)?$/ =~ s then if $2 then "#$1.html##$2" else Index: plugin/my-sequel.rb =================================================================== --- plugin/my-sequel.rb (リビジョン 43193) +++ plugin/my-sequel.rb (作業コピー) @@ -487,7 +487,7 @@ alias :my_sequel_orig_my :my unless defined?(my_sequel_orig_my) def my(*args) if @my_sequel_active and @my_sequel_date and @my_sequel_anchor and @mode != 'preview' then - dst_date, frag = args[0].scan(/^(\d{8})\D*(?:p(\d+))?$/)[0] + dst_date, frag = args[0].scan(/^(\d{8})\D*(?:p(\d+)(?:\.\d+)*)?$/)[0] if dst_date and dst_date < @my_sequel_date then dst_anchor = "#{dst_date}#{frag ? "#p%02d" % frag.to_i : ''}" @my_sequel.add(@my_sequel_anchor, dst_anchor) Index: plugin/my-ex.rb =================================================================== --- plugin/my-ex.rb (リビジョン 43193) +++ plugin/my-ex.rb (作業コピー) @@ -13,9 +13,10 @@ unless @conf.mobile_agent? +alias :my_overwritten_by_my_ex :my def my( a, str, title = nil ) m = /^(\d{8})\D*(?:([pct])(\d+))?$/.match( a ) - return '' unless m + return my_overwritten_by_my_ex( a, str, title ) unless m _, date, place, frag = *m if title.nil? and date and frag and @diaries[date] then
misc/plugin/html_anchor.rbと plugin/00default.rbプラグインを再び修正。
元々 | if /^([\-\d]+)#?([pct]\d*)?$/ =~ s then |
修正ミス | if /^([\-\d]+)#?([pct]\d+(?:\.\d+)*)?$/ =~ s then |
OK | if /^([\-\d]+)#?([pct][\d\.]*)?$/ =~ s then |
「ツッコミを入れる」リンクは YYYYMMDD#c へのリンクなので、cの後ろに数字が続いていない。これをうっかり除外してしまっていた。
最終更新: 2009-11-05T05:03+0900
いやらしいプラグインだなあ(笑)
しかしこの変哲のないただリンクに見えるもののどこに着目して、googleは小見出しを作成してるんだ。
最終更新: 2013-09-14T00:17+0900
使えないのを知っていて以前から HikiDocフォーマットでタイトルを書いていた。>>20090823 >>20090403
これを HTML化するのは意外と簡単。プラグインでできる。
add_title_proc {|date, title| if title.index('<') title.sub(/<span class="title">([^<>]+)<\/span>/){ %/<div class="title">#{WikiSection.new(CGI.unescapeHTML $1).body_to_html}<\/div>/ } else WikiSection.new(CGI.unescapeHTML title).body_to_html end }
今日のタイトルに含まれる「==日記==ブログ」という部分があまりにわかりにくかったので、HTML化してみた次第。URL自動リンクも有効になって、うまうま。
勘違い発覚。Headingがブロック要素を包含できる気がしていたが、Heading自身がブロック要素だということの記憶違い。<div>を含めちゃだめだ。
修正。
add_title_proc {|date, title| inline_or_nil = lambda{|src| lines = src.split(/\r?\n/) return nil if 1 < lines.length html = WikiSection.new(lines.first).body_to_html return nil if html[0,3] != '<p>' or html[-4,4] != '</p>' return html } if title.index('<') title.sub(/<span class="title">([^<>]+)<\/span>/){|_0| html = inline_or_nil.call(CGI.unescapeHTML $1) html ? %/<span class="title">#{html}<\/span>/ : _0 } else inline_or_nil.call(CGI.unescapeHTML title) or title end rescue title }
それなりにチェックはしてるけど、ブロック要素を返すプラグインを呼んだりしたら(HTMLの文法的に)即アウト。
Before...
♭ ななしデバッグモードでコンパイルすると、4883行で、 error C2440: 'int' から 'CLayoutInt..
♭ ななしrev8です。 あいうえお 1かきくけこ 0123456789 「い」の右側から、3行全部を0幅選択してスペース..
♭ ななしrev8.1でOKな気がします。 いい感じ (^^)
♭ anonymouse「1文字のTAB/SPACE入力が必ずインデント扱いになってしまっていて変」 (2009年11月10日 (火) 10..
♭ ななし念のため。 上記の、 ・タブキーとスペースキーに割り付けられた機能を削除すると、 複数行選択したときのタブやスペ..
♭ ds14050ええと、具体的にどういう動作が不満で、どういう結果になるのがよいのでしょうか。rev.9版のバイナリは http:/..
♭ ななし行末を超えた位置にも通常文字は入る。 SPACEキーにインデントを割り付けて*いない*ときは、 行末を超えた位置には..
♭ ds14050この部分ですね。 ---- if( nDataLen == 1 && IsIndentChar( pData[0] ..
♭ ななしnDataLen == 1 && IsIndentChar( pData[0] ) なんていう条件だと、 クリッ..
♭ ななし> 選択範囲が一行だけだったとき、Command_INDENT_TAB()は Command_INDENTの代わりに..
♭ ds14050>1字のSPACE/TABという条件は、コードをはじめて見た >瞬間におかしいと思ったし、誰でも同じことが想像できる..
♭ ds14050どうしようもないな。「元の挙動を残し」た。<<<大嘘
♭ ds14050なんかもうぐだぐだだけど気づいてしまってので訂正。 >知らないのは事実です。最初のコメントに書いたとおり。 最初のコ..