• R/O
  • SSH
  • HTTPS

shibuya-trac: Commit


Commit MetaInfo

Revision694 (tree)
Time2010-10-03 17:13:28
Authortag

Log Message

hirobeさんの許可をいただいて、
リポジトリの全文検索 with HyperEstraier プラグイン Ver 0.1
http://coderepos.org/share/browser/platform/trac/plugins/searchhyperestraier
のtrac0.12対応(+機能追加)版をshibuya-tracにおいた。

Change Summary

Incremental Difference

--- plugins/searchhyperestraier/trunk/MkCommentFile.py (nonexistent)
+++ plugins/searchhyperestraier/trunk/MkCommentFile.py (revision 694)
@@ -0,0 +1,61 @@
1+# -*- coding: utf-8 -*-
2+
3+import os
4+import time
5+import sys, string
6+from svn import core, fs, delta, repos
7+import codecs
8+
9+#argvs[1]:repository path
10+#argvs[2]:changeset folder
11+#argvs[3]:start revision
12+#argvs[4]:end revision
13+argvs = sys.argv #コマンドライン引数リスト
14+argc = len(argvs) #引数の個数
15+
16+if (argc != 5): #5でなければ出る
17+ usage(1)
18+
19+path = argvs[1]
20+path = core.svn_path_canonicalize(path)
21+repos_ptr = repos.open(path)
22+fs_ptr = repos.fs(repos_ptr)
23+
24+changeset_folder = argvs[2]
25+
26+start_rev = int(argvs[3])
27+end_rev = int(argvs[4])
28+
29+if start_rev>end_rev:
30+ sys.exit(exit)
31+
32+rev = fs.youngest_rev(fs_ptr)
33+if start_rev>rev and end_rev>rev:
34+ sys.exit(exit)
35+
36+if start_rev>rev:
37+ start_rev=rev
38+if end_rev>rev:
39+ end_rev=rev
40+
41+for current_rev in range(start_rev,end_rev+1):
42+ #get comment log
43+ log = fs.revision_prop(fs_ptr, current_rev, core.SVN_PROP_REVISION_LOG) or ''
44+
45+ #make dir
46+ folder_num = current_rev/1000
47+ file_folder = changeset_folder+'\\'+str(folder_num)
48+ if not os.path.exists(file_folder):
49+ os.makedirs(file_folder)
50+ #make file
51+ filename = file_folder+'\\'+str(current_rev)+'.txt'
52+ f = open(filename, 'w')
53+ f.write(log)
54+ f.close()
55+ #file update time
56+ date = fs.revision_prop(fs_ptr, current_rev, core.SVN_PROP_REVISION_DATE)
57+ aprtime = core.svn_time_from_cstring(date)
58+ secs = aprtime / 1000000 # aprtime is microseconds; make seconds
59+ timestr=time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(secs))
60+ atime = mtime = time.mktime(time.strptime(timestr, '%Y-%m-%d-%H:%M:%S'))
61+ os.utime(filename, (atime, mtime))
--- plugins/searchhyperestraier/trunk/readme_ja.txt (nonexistent)
+++ plugins/searchhyperestraier/trunk/readme_ja.txt (revision 694)
@@ -0,0 +1,317 @@
1+= !HyperEstraierによる全文検索プラグイン Ver 0.1(勝手にtrac0.12対応版) =
2+ここでは、Hirobeさん作、リポジトリ検索プラグインの[http://weekbuild.sakura.ne.jp/trac/wiki/TracDoc/SearchHyperEstraierPlugin SearchHyperEstraier]を[[BR]]「勝手に」修正し、trac0.12に対応、その他機能追加を行ったものについて記述してます。
3+
4+== 1. 概要 ==
5+全文検索を行うためのプラグインです。[[BR]]
6+trac0.12対応としてマルチリポジトリに対応しています。[[BR]] また、機能追加を行ってますので以下のような検索が行えます。[[BR]]
7+
8+ * リポジトリからチェックアウトしたソースについての検索(従来から機能)
9+ * チェンジセットのコミットログの検索
10+ * 添付ファイル内の文字列検索
11+ * ドキュメントディレクトリの文字列検索
12+
13+仕組み:バッチを使い、リポジトリのソースやコミットログ、添付ファイルやドキュメントディレクトリの内容から、[[BR]]HyperEstraierのインデックスを生成します。[[BR]]そのインデックスを利用し、Trac内でコマンド版cmdestを実行、その結果を表示します。
14+
15+'''制限'''
16+
17+ * ソースの検索は、リポジトリ内の最新のファイルしか検索できません。
18+ * apacheからcmdest.exeを起動できるように権限設定が必要かもしれません。
19+ * Windowsでしか動作確認してません。
20+ * 動作は無保証です。
21+
22+== 2. セットアップ ==
23+インストール方法の後、構成例を挙げ、それに沿って、リポジトリ、チェンジセット、添付ファイル、ドキュメントの[[BR]]
24+それぞれの機能のセットアップを記述してます。[[BR]]
25+それぞれの機能は独立してますので、いくつかだけを有効にすることもできます。[[BR]]
26+必要な部分だけ読んでください。[[BR]]
27+設定例のファイル(makeindex.bat.sample,trac.ini.sample,updateindex.bat.sample)[[BR]]
28+やコミットログを出力するスクリプト(MkCommentFile.py)も添付してますので[[BR]]
29+ご利用下さい。
30+
31+=== 2.0. インストール ===
32+[http://fallabs.com/hyperestraier/ HyperEstraier]のWindows版バイナリパッケージの中の[[BR]]
33+estraier.dll,qdbm.dll,mgwz.dll,libiconv-2.dll, [[BR]]regex.dll,pthreadGC2.dll,estcmd.exe,estxfilt.bat,xdoc2txt.exe,zlib.dll[[BR]]
34+があればHyperEstraierを使えます。pathが通った場所においてください。[[BR]]
35+TracLightningであればインストールフォルダのbinフォルダがよいでしょう。
36+
37+そしてこのプラグインを解凍、setup.pyがあるディレクトリに移動して、
38+{{{
39+python setup.py install
40+}}}
41+を実行してください。
42+=== 2.1.ディレクトリ構成例 ===
43+TracLightningでの使用を意識したディレクトリの構成例を示します。
44+
45+ * c:\TracLight
46+ * projects
47+ * svn
48+ * pj1…default repository
49+ * repos2
50+ * trac
51+ * pj1
52+ * doc
53+ * pj1
54+ * search
55+ * repos
56+ * pj1
57+ * rep
58+ * casket
59+ * repos2
60+ * rep
61+ * casket
62+ * changeset
63+ * pj1
64+ * rep
65+ * casket
66+ * repos2
67+ * rep
68+ * casket
69+ * attach
70+ * pj1
71+ * casket
72+ * doc
73+ * pj1
74+ * casket
75+
76+c:\TracLight\projectsはtracプロジェクトやリポジトリを含むディレクトリを示しています。[[BR]]ここでは、tracプロジェクト1つ(c:\TracLight\projects\trac\pj1)、[[BR]]それに対応するリポジトリ2つ(c:\TracLight\projects\svn\pj1,repos2)と[[BR]]ドキュメントディレクトリ1つ(c:\TracLight\projects\doc\pj1)にしてます。
77+
78+c:\TracLight\searchはHyperEstraierで使用する検索用のindexデータ(casket)と、[[BR]]検索される内容を入れるディレクトリです。[[BR]]検索される内容としては、
79+
80+ * リポジトリからチェックアウトしたソース(c:\TracLight\search\repos\pj1\repとc:\TracLight\search\repos\repos2\rep)
81+ * チェンジセットのコミットログ(c:\TracLight\search\changeset\pj1\repとc:\TracLight\search\changeset\repos2\rep)
82+
83+を配置してます。
84+
85+=== 2.2.リポジトリ ===
86+リポジトリからエクスポートしたソースを検索する設定です。[[BR]]
87+※tracへのリポジトリの登録は事前にしておいてください。
88+==== 2.2.1 エクスポート/インデックス生成バッチファイルの作成 ====
89+リポジトリのエクスポートとインデックス生成を行うバッチを作成します。
90+
91+上図に沿ったバッチファイルの例を示します。
92+
93+{{{
94+set EXPORT_FOLDER=c:\TracLight\search\repos\pj1\rep
95+set INDEX_FOLDER=c:\TracLight\search\repos\pj1\casket
96+set REPOS_URI=file:///C:/TracLight/projects/svn/pj1/trunk
97+
98+rmdir /S /Q %EXPORT_FOLDER%
99+rmdir /S /Q %INDEX_FOLDER%
100+svn export %REPOS_URI% %EXPORT_FOLDER%
101+estcmd gather -cl -fx .pdf,.rtf,.doc,.xls,.ppt T@estxfilt -ic CP932 -pc CP932 -sd %INDEX_FOLDER% %EXPORT_FOLDER%
102+}}}
103+上記バッチファイルでは、冒頭部の環境変数により設定をしています。
104+
105+|| EXPORT_FOLDER || リポジトリのエクスポート先となるディレクトリ。空のディレクトリを指定。 ||
106+|| REPOS_URI || エクスポート元となるリポジトリのURI ||
107+|| INDEX_FOLDER || インデックスの生成先ディレクトリ。空のディレクトリを指定。 ||
108+
109+適切に書き換えてご使用ください。[[BR]]なお、リポジトリのエクスポートの認証は考慮してません。認証が必要であればsvn exportに適切な引数を設定してください。[[BR]] 通常は上記のようにtrunkなどリポジトリの一部を指定します。[[BR]](全体にしてしまうとtagsやbranchesの中身もチェックアウトしてしまいます。)[[BR]]また、上記バッチファイルはフルパス指定であるため、どこにおいてもかまいませんが、[[BR]]tracプロジェクトのconfディレクトリなどにおくのが管理しやすいと思います。[[BR]] また上記では「.pdf,.rtf,.doc,.xls,.ppt」の拡張子のファイルしか検索しません。[[BR]] .txtや.cなどテキストファイルであればestxfilt.batで呼ばれる[[BR]]xdoc2txtはスルーで出しますので[[BR]]「,.txt」のように-fxの後に続く拡張子の列挙の後追記してください。
110+
111+この例ではpj1リポジトリのみ設定してますが、バッチファイル中の「pj1」を[[BR]] 「repos2」に置き換えて下方に同様の処理を書くか、別のバッチファイルを作るかすれば[[BR]] 対応できます。
112+==== 2.2.2 trac.iniの設定 ====
113+テキストファイルでtrac.ini(上図ではc:\TracLight\projects\trac\pj1\conf配下)を開いて [searchhyperestraier]というブロックを追加してください。
114+
115+|| xxx.index_path || インデックス生成パス(バッチファイルのINDEX_FOLDER) ||
116+|| xxx.replace_left || 検索結果のパスの頭で削るべき文字列。 ||
117+|| xxx.url_left || URLを生成する際に頭につける文字列。browse_trac=enabledの場合は/がリポジトリのルートになるようにすること。 ||
118+|| browse_trac || Tracのブラウザへのリンクを作るか否か。enabled=Tracのブラウザへのリンクを作る。リポジトリソース検索共通の機能。デフォルトは'enabled'。 ||
119+|| estcmd_path || 環境変数PATHが設定済みなら設定不要。estcmd.exeの絶対パス。すべての機能に共通の機能。デフォルトは'estcmd' ||
120+|| estcmd_arg || Windowsでは設定不要。estcmd.exeの引数。すべての機能に共通の機能。デフォルトは'search -vx -sf -ic Shift_JIS' ||
121+|| estcmd_encode || Windowsでは設定不要。コマンド実行時のエンコード(Pythonでの形式)。すべての機能に共通の機能。デフォルトは'mbcs' ||
122+
123+ ここではdefaultリポジトリであるかを意識して設定する必要があります。[[BR]] 上表で「xxx」になっている部分はリポジトリ名を記述します。[[BR]] ただし、defaultリポジトリであれば、何も書きません。[[BR]] たとえば、「.index_path」のように記述します[[BR]]
124+(元のSearchHyperEstraierを使っていた人は変更が必要です)。[[BR]]
125+index_path,replace_left,url_leftはリポジトリごとに設定できます。[[BR]]
126+
127+例:
128+
129+{{{
130+[searchhyperestraier]
131+.index_path = C:\TracLight\search\repos\pj1\casket
132+.replace_left = C:\TracLight\search\repos\pj1\rep
133+.url_left = /trunk
134+repos2.index_path = C:\TracLight\search\repos\repos2\casket
135+repos2.replace_left = C:\TracLight\search\repos\repos2\rep
136+repos2.url_left = /trunk
137+}}}
138+browser_tracがenabledになる場合は、登録されるURLはTracのリポジトリブラウザでRoot直下が/となるように replace_left,url_leftを調整する必要があります。[[BR]]たとえば、リポジトリブラウザでRoot/trunk/test3/検索のテスト.docと表示されるファイルは、/trunk/test3/検索のテスト.docとなるように調整してください。[[BR]]難しければ、何も設定せずに、検索結果として表示されたURLを見ながら調整してください。[[BR]]通常、EXPORT_FOLDER=replace_left、INDEX_FOLDER=index_pathになります。[[BR]]
139+
140+また、[components]ブロックでこの機能を有効にしてください。
141+
142+{{{
143+[components]
144+searchhyperestraier.searchhyperestraier.searchhyperestraiermodule = enabled
145+}}}
146+=== 2.3.チェンジセット ===
147+「direct-svnfs」専用です。[[BR]]
148+チェンジセットはtracにも機能がありますが、[[BR]]
149+typeが「direct-svnfs」の場合、DBに取り込まれないので[[BR]]
150+検索できません。そういう場合にお使いください。[[BR]]
151+「direct-svnfs」以外のものは検索しないようにしてますので[[BR]]
152+併用していただいても大丈夫です。[[BR]]
153+
154+※tracへのリポジトリの登録は事前にしておいてください。
155+==== 2.3.1.コミットログファイル/インデックス生成バッチファイルの作成 ====
156+コミットログを一つ一つのファイルとして出力し、インデックス生成を行うバッチを作成します。[[BR]]
157+コミットログをファイルとして出力するために、MkCommentFile.pyというスクリプトを[[BR]]
158+用意しました。[[BR]]
159+
160+上図に沿ったバッチファイルの例を示します。
161+
162+{{{
163+set EXPORT_FOLDER=c:\TracLight\search\changeset\pj1\rep
164+set INDEX_FOLDER=c:\TracLight\search\changeset\pj1\casket
165+set REPOS_FOLDER=C:/TracLight/projects/svn/pj1
166+
167+for /F %%i in ('svnlook youngest %REPOS_FOLDER%') do set LASTREVISION=%%i
168+python MkCommentFile.py %REPOS_FOLDER% %EXPORT_FOLDER% 1 %LASTREVISION%
169+estcmd gather -cl -fx.pdf,.rtf,.doc,.xls,.ppt T@estxfilt -ic CP932 -pc CP932 -sd %INDEX_FOLDER% %EXPORT_FOLDER%
170+
171+}}}
172+2.2.1.と同様、上記バッチファイルでは、冒頭部の環境変数により設定をしています。
173+
174+|| EXPORT_FOLDER || コミットログが入るディレクトリ。空のディレクトリを指定。 ||
175+|| REPOS_FOLDER || 対象リポジトリのディレクトリ。ここではtrunkなどの指定をしません。 ||
176+|| INDEX_FOLDER || インデックスの生成先ディレクトリ。空のディレクトリを指定。 ||
177+
178+適切に書き換えてご使用ください。
179+
180+この例ではpj1リポジトリのみ設定してますが、バッチファイル中の「pj1」を[[BR]] 「repos2」に置き換えて下方に同様の処理を書くか、別のバッチファイルを作るかすれば[[BR]] 対応できます。
181+==== 2.3.2.trac.iniの設定 ====
182+trac.iniを開いて [searchhyperestraier]ブロックに追加してください。
183+
184+|| xxx.cs_index_path || インデックス生成パス(バッチファイルのINDEX_FOLDER) ||
185+
186+2.2.2.と同様、ここではdefaultリポジトリであるかを意識して設定する必要があります。[[BR]] 上表で「xxx」になっている部分はリポジトリ名を記述します。[[BR]] ただし、defaultリポジトリであれば、何も書きません。[[BR]] 「.cs_index_path」のように記述します。[[BR]]
187+xxxの部分を変えて設定することでリポジトリごとに設定できます。[[BR]]
188+
189+例:
190+
191+{{{
192+[searchhyperestraier]
193+.cs_index_path = C:\TracLight\search\changeset\pj1\casket
194+repos2.cs_index_path = C:\TracLight\search\changeset\repos2\casket
195+}}}
196+
197+また、[components]ブロックでこの機能を有効にしてください。
198+
199+{{{
200+[components]
201+searchhyperestraier.searchhyperestraier.searchchangesethyperestraiermodule = enabled
202+}}}
203+=== 2.4.添付ファイル ===
204+tracのチケットやwikiの添付ファイル中の文字列検索を行うための設定です。
205+==== 2.4.1.インデックス生成バッチファイルの作成 ====
206+インデックス生成を行うバッチを作成します。[[BR]]
207+
208+上図に沿ったバッチファイルの例を示します。
209+
210+{{{
211+set INDEX_FOLDER=c:\TracLight\search\attach\pj1\casket
212+set ATT_FOLDER=c:\TracLight\projects\trac\pj1\attachments
213+
214+estcmd gather -cl -fx .pdf,.rtf,.doc,.xls,.ppt,.c,.asm,.py,.txt T@estxfilt -ic CP932 -pc CP932 -sd %INDEX_FOLDER% %ATT_FOLDER%
215+}}}
216+2.2.1.と同様、上記バッチファイルでは、冒頭部の環境変数により設定をしています。
217+
218+|| ATT_FOLDER || tracの添付ファイルディレクトリ。tracプロジェクトのattachmentsディレクトリを指定。 ||
219+|| INDEX_FOLDER || インデックスの生成先ディレクトリ。空のディレクトリを指定。 ||
220+
221+適切に書き換えてご使用ください。
222+
223+==== 2.4.2.trac.iniの設定 ====
224+trac.iniを開いて [searchhyperestraier]ブロックに追加してください。
225+
226+|| att_index_path || インデックス生成パス(バッチファイルのINDEX_FOLDER) ||
227+
228+単純にインデックス生成パスを設定してください。
229+
230+例:
231+
232+{{{
233+[searchhyperestraier]
234+att_index_path = C:\TracLight\search\attach\pj1\casket
235+}}}
236+
237+また、[components]ブロックでこの機能を有効にしてください。
238+
239+{{{
240+[components]
241+searchhyperestraier.searchhyperestraier.searchattachmenthyperestraiermodule = enabled
242+}}}
243+=== 2.5.ドキュメント ===
244+ドキュメントディレクトリにある指定ファイル中の文字列検索を行うための設定です。
245+ドキュメントディレクトリがブラウザで見られるようにhttpd.confを設定しておく必要があります。
246+例
247+{{{
248+Alias /doc "c:\tracpj\doc"
249+<Directory "c:\tracpj\doc">
250+ Options Indexes
251+ IndexOptions +FancyIndexing +NameWidth=* +SuppressDescription +SuppressIcon
252+ Allow from all
253+</Directory>
254+}}}
255+==== 2.5.1.インデックス生成バッチファイルの作成 ====
256+インデックス生成を行うバッチを作成します。[[BR]]
257+
258+上図に沿ったバッチファイルの例を示します。
259+
260+{{{
261+set DOC_FOLDER=c:\TracLight\projects\doc\pj1
262+set INDEX_FOLDER=c:\TracLight\search\doc\pj1\casket
263+
264+estcmd gather -cl -fx .pdf,.rtf,.doc,.xls,.ppt,.c,.asm,.py,.txt T@estxfilt -ic CP932 -pc CP932 -sd %INDEX_FOLDER% %DOC_FOLDER%
265+}}}
266+2.2.1.と同様、上記バッチファイルでは、冒頭部の環境変数により設定をしています。
267+
268+|| DOC_FOLDER || ドキュメントディレクトリ。 ||
269+|| INDEX_FOLDER || インデックスの生成先ディレクトリ。空のディレクトリを指定。 ||
270+
271+適切に書き換えてご使用ください。
272+
273+==== 2.5.2.trac.iniの設定 ====
274+trac.iniを開いて [searchhyperestraier]ブロックに追加してください。
275+
276+|| doc_index_path || インデックス生成パス(バッチファイルのINDEX_FOLDER) ||
277+|| doc_replace_left || 検索結果のパスの頭で削るべき文字列(バッチファイルのDOC_FOLDER)。 ||
278+|| doc_url_left || URLを生成する際に頭につける文字列。 ||
279+
280+単純にインデックス生成パスを設定してください。
281+
282+例:
283+
284+{{{
285+[searchhyperestraier]
286+doc_index_path = C:\TracLight\search\doc\pj1\casket
287+doc_replace_left = c:\TracLight\projects\doc\pj1
288+doc_url_left = /doc/pj1
289+}}}
290+
291+また、[components]ブロックでこの機能を有効にしてください。
292+
293+{{{
294+[components]
295+searchhyperestraier.searchhyperestraier.searchdocumenthyperestraiermodule = enabled
296+}}}
297+== 3. apacheを再起動する ==
298+trac.iniを設定したら、apacheを再起動してください。[[BR]](「サービスのアンインストール」を実行、再度「サービスのインストール」を実行。)
299+
300+== 4. バッチファイルを実行する ==
301+作成したバッチファイルを実行してください。
302+
303+== 5. 検索してみる ==
304+検索タブをクリックして、チェックボックスが表示されることを確認してください。[[BR]]同様の機能と区別するため、頭に「he:」と表記してます。[[BR]]
305+he:リポジトリ,he:添付ファイル,he:チェンジセット,he:ドキュメントのうち[[BR]]
306+機能を有効にしているものが表示されているか確認してください。[[BR]]
307+リポジトリは'BROWSER_VIEW'、チェンジセットは'CHANGESET_VIEW'、[[BR]]
308+添付ファイルatt_index_pathが設定されていること、
309+ドキュメントはdoc_index_path,doc_replace_left,doc_url_leftが[[BR]]
310+設定されていることが表示条件です。[[BR]]
311+表示されていればチェックを入れ、適当なキーワードで検索して、結果を確認してください。[[BR]]リンクをクリックして、画面がリポジトリブラウザに切り替わり、正しくそのファイルを表示していることを確認してください。
312+
313+== 6. バッチファイルをタスク設定する ==
314+動作確認ができたら必要なバッチファイルが1日1回実行できるようにWindowsのタスクを設定してください。[[BR]] 2000またはXPでは、タスクの追加は、以下のように行います。[[BR]] [スタート]メニューから[プログラム]-[アクセサリ]-[システムツール]-[タスク][[BR]] または[[BR]] [コントロール パネル]の[タスク][[BR]] を開き、[スケジュールされたタスクの追加]をクリックします。[[BR]] するとタスクウィザードが起動します。[[BR]] 「実行するプログラムを1つ選択してください。」のところで、[[BR]] 参照ボタンを押し、バッチファイルを指定してください。[[BR]] 「このタスクの実行」で「日単位」を選択してください。[[BR]] その後、開始日時などを設定します。[[BR]] 最後にユーザー名とパスワードの入力を行って終了です。[[BR]] 完了ボタンを押すと登録されます。
315+
316+== 7. 設定時刻を待たずにすぐ更新する ==
317+もし、設定時刻を待たずにすぐ更新したい場合は、[[BR]] バッチファイルを直接実行してください。
\ No newline at end of file
--- plugins/searchhyperestraier/trunk/setup.py (nonexistent)
+++ plugins/searchhyperestraier/trunk/setup.py (revision 694)
@@ -0,0 +1,14 @@
1+from setuptools import find_packages, setup
2+
3+setup(
4+ name='TracHyperestraierPlugin', version='0.1-k-trac0.12',
5+ packages=find_packages(exclude=['*.tests*']),
6+ entry_points = """
7+ [trac.plugins]
8+ searchhyperestraier.searchhyperestraier = searchhyperestraier.searchhyperestraier
9+ searchhyperestraier.searchattachmenthyperestraier = searchhyperestraier.searchattachmenthyperestraier
10+ searchhyperestraier.searchdocumenthyperestraier = searchhyperestraier.searchdocumenthyperestraier
11+ searchhyperestraier.searchchangesethyperestraier = searchhyperestraier.searchchangesethyperestraier
12+ """,
13+)
14+
--- plugins/searchhyperestraier/trunk/searchhyperestraier/searchhyperestraier.py (nonexistent)
+++ plugins/searchhyperestraier/trunk/searchhyperestraier/searchhyperestraier.py (revision 694)
@@ -0,0 +1,413 @@
1+# -*- coding: utf-8 -*-
2+# SearchRepositoryWithHyperestraier plugin
3+
4+from trac.core import *
5+#from trac.Search import ISearchSource
6+from trac.search.api import ISearchSource # for Trac0.11
7+from trac.util import NaivePopen
8+from StringIO import StringIO
9+import urllib
10+import time
11+from xml.dom.minidom import parseString
12+import datetime
13+from trac.util.datefmt import to_datetime, utc # for Trac0.11
14+from trac.util.text import to_unicode # for Trac0.11
15+from trac.versioncontrol import RepositoryManager
16+import os.path
17+
18+class SearchHyperEstraierModule(Component):
19+ implements(ISearchSource)
20+
21+ # ISearchProvider methods
22+ def get_search_filters(self, req):
23+ if req.perm.has_permission('BROWSER_VIEW'):
24+ yield ('repositoryhyperest', u'he:リポジトリ', 0)
25+
26+ def get_search_results(self, req, terms, filters):
27+ if not 'repositoryhyperest' in filters:
28+ return
29+ #estcmd.exeのパス
30+ estcmd_path = self.env.config.get('searchhyperestraier', 'estcmd_path','estcmd')
31+ #estcmd.exeの引数
32+ estcmd_arg = self.env.config.get('searchhyperestraier', 'estcmd_arg','search -vx -sf -ic Shift_JIS')
33+ estcmd_encode = self.env.config.get('searchhyperestraier', 'estcmd_encode','mbcs')
34+ #Tracのブラウザへのリンクを作るか否か。
35+ #enabled:Tracのブラウザへのリンクを作る
36+ #上記以外:replace_left,url_leftで指定したURLへのリンクを作る
37+ browse_trac = self.env.config.get('searchhyperestraier', 'browse_trac','enabled')
38+
39+ #for multi repos
40+ for option in self.config['searchhyperestraier']:
41+ #リポジトリのパス
42+ if not option.endswith('.index_path'):
43+ continue
44+ mrepstr = option[:-len('.index_path')] #'.index_path'の前の文字列がreponame
45+ if RepositoryManager(self.env).get_repository(mrepstr) is None: #mrepstrのrepositoryがない
46+ continue
47+ #インデックスのパス
48+ index_path = self.env.config.get('searchhyperestraier', mrepstr+'.index_path','')
49+ if index_path == '':#mrepstr+'.index_path'がない
50+ continue
51+ #検索結果のパスの頭で削る文字列
52+ replace_left = self.env.config.get('searchhyperestraier', mrepstr+'.replace_left','')
53+ if replace_left == '': #mrepstr+'.replace_left'がない
54+ continue
55+ #URLを生成する際に頭につける文字列
56+ #browse_trac=enabledの場合は/がリポジトリのルートになるように
57+ url_left = self.env.config.get('searchhyperestraier', mrepstr+'.url_left','')
58+ if url_left == '': #mrepstr+'.url_left'がない
59+ continue
60+ if mrepstr != '': #defaultでない
61+ url_left = '/' + mrepstr + url_left
62+
63+ #cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,index_path,unicode(query,'utf-8').encode('CP932'))
64+ qline = ' '.join(terms)
65+ cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,index_path,qline)
66+ self.log.debug('SearchHyperEstraier:%s' % cmdline)
67+ cmdline = unicode(cmdline).encode(estcmd_encode)
68+ np = NaivePopen(cmdline)
69+ #self.log.debug('Result:%s' % unicode(np.out,'utf-8').encode('mbcs'))
70+ #self.log.debug('Result:%s' % np.out)
71+ if np.errorlevel or np.err:
72+ err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel,
73+ np.err)
74+ raise Exception, err
75+ if np.out=='': #何も入ってない
76+ continue
77+ dom = parseString(np.out)
78+ root = dom.documentElement
79+ #estresult_node = root.getElementsByTagName("document")[0]
80+ element_array = root.getElementsByTagName("document")
81+ for element in element_array:
82+ #self.log.debug('Result:%s' % 'hoge')
83+ url = ""
84+ title = ""
85+ date = 0
86+ detail = ""
87+ author = "不明"
88+
89+ #detailを生成
90+ elem_array = element.getElementsByTagName("snippet")
91+ detail = self._get_innerText("",elem_array)
92+
93+ #その他の属性を生成
94+ attrelem_array = element.getElementsByTagName("attribute")
95+ for attrelem in attrelem_array:
96+ attr_name = attrelem.getAttribute("name")
97+ attr_value = unicode(attrelem.getAttribute("value"))
98+ #URLとタイトルを生成
99+ #if attr_name == "_lreal": #"_lreal"ではファイル名に' 'などが入っている場合対応できない
100+ # attr_value=attr_value[len(replace_left):].replace("\\","/")
101+ # if browse_trac == "enabled":
102+ # url = self.env.href.browser(url_left + attr_value)
103+ # title = "source:"+ url_left + attr_value
104+ # else:
105+ # url = url_left + attr_value
106+ # title = url_left + attr_value
107+ if attr_name == "_lpath": #s-jisをquoteしたもの("file:///C|/TracLight/…"の形式)
108+ attr_value = urllib.unquote(attr_value).encode('raw_unicode_escape').decode('CP932')
109+ attr_value = attr_value[(len('file:///')+len(replace_left)):]
110+ if browse_trac == "enabled":
111+ url = self.env.href.browser(url_left + attr_value)
112+ title = "source:"+ urllib.unquote(url).encode('raw_unicode_escape').decode('utf-8')
113+ else:
114+ url = url_left + attr_value
115+ title = urllib.unquote(url).encode('raw_unicode_escape').decode('utf-8')
116+ #更新日時を生成
117+ elif attr_name =="@mdate":
118+ date = time.strptime(attr_value,"%Y-%m-%dT%H:%M:%SZ")
119+ self.log.debug('date:%s' % attr_value)
120+ date = to_datetime(datetime.datetime(date[0],date[1],date[2],date[3],date[4],date[5],0,utc)) # for Trac0.11
121+ yield(url,title,date,to_unicode(author,'utf-8'),to_unicode(detail,'utf-8'))
122+ return
123+
124+ #XMLのElementを再帰的に探してテキストを生成
125+ def _get_innerText(self,text,node_array):
126+ for node in node_array:
127+ if node.nodeType == node.TEXT_NODE:
128+ text = text + unicode(node.data).encode('utf-8')
129+ else:
130+ text = self._get_innerText(text,node.childNodes)
131+ return text
132+
133+class SearchChangesetHyperEstraierModule(Component):
134+ implements(ISearchSource)
135+
136+ # ISearchProvider methods
137+ def get_search_filters(self, req):
138+ if req.perm.has_permission('CHANGESET_VIEW'):
139+ yield ('changesethyperest', u'he:チェンジセット', 0)
140+
141+ def get_search_results(self, req, terms, filters):
142+ if not 'changesethyperest' in filters:
143+ return
144+ #estcmd.exeのパス
145+ estcmd_path = self.env.config.get('searchhyperestraier', 'estcmd_path','estcmd')
146+ #estcmd.exeの引数
147+ estcmd_arg = self.env.config.get('searchhyperestraier', 'estcmd_arg','search -vx -sf -ic Shift_JIS')
148+ estcmd_encode = self.env.config.get('searchhyperestraier', 'estcmd_encode','mbcs')
149+
150+ #for multi repos
151+ for option in self.config['searchhyperestraier']:
152+ #リポジトリのパス
153+ if not option.endswith('.cs_index_path'):
154+ continue
155+ mrepstr = option[:-len('.cs_index_path')] #'.cs_index_path'の前の文字列がreponame
156+ if RepositoryManager(self.env).get_repository(mrepstr) is None: #mrepstrのrepositoryがない
157+ continue
158+ repoinfo = RepositoryManager(self.env).get_all_repositories().get(mrepstr, {})
159+ #self.log.debug('type:%s' % repoinfo.get('type'))
160+ if repoinfo.get('type') != 'direct-svnfs':#'direct-svnfs'のリポジトリでない
161+ continue
162+ #インデックスのパス
163+ cs_index_path = self.env.config.get('searchhyperestraier', mrepstr+'.cs_index_path','')
164+ if cs_index_path == '':#mrepstr+'.cs_index_path'がない
165+ continue
166+ if mrepstr != '': #defaultでない
167+ mrepstr = '/' + mrepstr
168+
169+ #cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,cs_index_path,unicode(query,'utf-8').encode('CP932'))
170+ qline = ' '.join(terms)
171+ cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,cs_index_path,qline)
172+ self.log.debug('SearchChangesetHyperEstraier:%s' % cmdline)
173+ cmdline = unicode(cmdline).encode(estcmd_encode)
174+ np = NaivePopen(cmdline)
175+ #self.log.debug('Result:%s' % unicode(np.out,'utf-8').encode('mbcs'))
176+ #self.log.debug('Result:%s' % np.out)
177+ if np.errorlevel or np.err:
178+ err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel,
179+ np.err)
180+ raise Exception, err
181+ if np.out=='': #何も入ってない
182+ continue
183+ dom = parseString(np.out)
184+ root = dom.documentElement
185+ #estresult_node = root.getElementsByTagName("document")[0]
186+ element_array = root.getElementsByTagName("document")
187+ for element in element_array:
188+ #self.log.debug('Result:%s' % 'hoge')
189+ url = ""
190+ title = ""
191+ date = 0
192+ detail = ""
193+ author = "不明"
194+
195+ #detailを生成
196+ elem_array = element.getElementsByTagName("snippet")
197+ detail = self._get_innerText("",elem_array)
198+
199+ #その他の属性を生成
200+ attrelem_array = element.getElementsByTagName("attribute")
201+ for attrelem in attrelem_array:
202+ attr_name = attrelem.getAttribute("name")
203+ attr_value = unicode(attrelem.getAttribute("value"))
204+ #URLとタイトルを生成
205+ if attr_name == "_lreal":
206+ attr_value=attr_value.replace(".txt","")
207+ end = len(attr_value)
208+ for m in range(1,end):
209+ if not attr_value[(end-m):].isdigit():
210+ break
211+ attr_value = attr_value[(end-m+1):] + mrepstr #数字の文字列 + mrepstr
212+ url = self.env.href('/changeset/' + attr_value )
213+ title = "changeset:" + attr_value
214+ #更新日時を生成
215+ elif attr_name =="@mdate":
216+ date = time.strptime(attr_value,"%Y-%m-%dT%H:%M:%SZ")
217+ self.log.debug('date:%s' % attr_value)
218+ date = to_datetime(datetime.datetime(date[0],date[1],date[2],date[3],date[4],date[5],0,utc)) # for Trac0.11
219+ yield(url,title,date,to_unicode(author,'utf-8'),to_unicode(detail,'utf-8'))
220+ return
221+
222+ #XMLのElementを再帰的に探してテキストを生成
223+ def _get_innerText(self,text,node_array):
224+ for node in node_array:
225+ if node.nodeType == node.TEXT_NODE:
226+ text = text + unicode(node.data).encode('utf-8')
227+ else:
228+ text = self._get_innerText(text,node.childNodes)
229+ return text
230+
231+class SearchAttachmentHyperEstraierModule(Component):
232+ implements(ISearchSource)
233+
234+ # ISearchProvider methods
235+ def get_search_filters(self, req):
236+ if self.env.config.get('searchhyperestraier', 'att_index_path','')!='':
237+ yield ('attachmenthyperest', u'he:添付ファイル', 0)
238+
239+ def get_search_results(self, req, terms, filters):
240+ if not 'attachmenthyperest' in filters:
241+ return
242+ #estcmd.exeのパス
243+ estcmd_path = self.env.config.get('searchhyperestraier', 'estcmd_path','estcmd')
244+ #estcmd.exeの引数
245+ estcmd_arg = self.env.config.get('searchhyperestraier', 'estcmd_arg','search -vx -sf -ic Shift_JIS')
246+ estcmd_encode = self.env.config.get('searchhyperestraier', 'estcmd_encode','mbcs')
247+
248+ #インデックスのパス
249+ att_index_path = self.env.config.get('searchhyperestraier', 'att_index_path','')
250+ #コマンド実行時のエンコード(Pythonでの形式)
251+ #estcmd_argと一致(?)させる必要有り。
252+
253+ #検索結果のパスの頭で削る文字列
254+ #self.log.debug('attpath:%s' % os.path.normpath(self.env.path))
255+ att_replace_left = os.path.join(os.path.normpath(self.env.path),'attachments')
256+ #self.log.debug('attleft:%s' % att_replace_left)
257+
258+ #cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,att_index_path,unicode(query,'utf-8').encode('CP932'))
259+ qline = ' '.join(terms)
260+ cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,att_index_path,qline)
261+ self.log.debug('SearchHyperEstraier:%s' % cmdline)
262+ cmdline = unicode(cmdline).encode(estcmd_encode)
263+ np = NaivePopen(cmdline)
264+ #self.log.debug('Result:%s' % unicode(np.out,'utf-8').encode('mbcs'))
265+ #self.log.debug('Result:%s' % np.out)
266+ if np.errorlevel or np.err:
267+ err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel,
268+ np.err)
269+ raise Exception, err
270+ if np.out == '': #添付ファイルフォルダに何も入ってない
271+ return
272+
273+ dom = parseString(np.out)
274+ root = dom.documentElement
275+ #estresult_node = root.getElementsByTagName("document")[0]
276+ element_array = root.getElementsByTagName("document")
277+ for element in element_array:
278+ #self.log.debug('Result:%s' % 'hoge')
279+ url = ""
280+ title = ""
281+ date = 0
282+ detail = ""
283+ author = "不明"
284+
285+ #detailを生成
286+ elem_array = element.getElementsByTagName("snippet")
287+ detail = self._get_innerText("",elem_array)
288+
289+ #その他の属性を生成
290+ attrelem_array = element.getElementsByTagName("attribute")
291+ for attrelem in attrelem_array:
292+ attr_name = attrelem.getAttribute("name")
293+ attr_value = unicode(attrelem.getAttribute("value")) #添付ファイルはパスがquoteされたものになっている
294+ #URLとタイトルを生成
295+ if attr_name == "_lreal":
296+ attr_value=attr_value[len(att_replace_left):].replace("\\","/")
297+ url = self.env.href.attachment("") #attachmentまでのurl取得
298+ url = url +attr_value[1:] #[1:]は先頭の"/"を除くため
299+ #そのままunquoteすると文字化けするから
300+ title = urllib.unquote(attr_value).encode('raw_unicode_escape').decode('utf-8')
301+ title = "attachment"+ title
302+ title = title.replace("/",":")
303+ #更新日時を生成
304+ elif attr_name =="@mdate":
305+ date = time.strptime(attr_value,"%Y-%m-%dT%H:%M:%SZ")
306+ self.log.debug('date:%s' % attr_value)
307+ date = to_datetime(datetime.datetime(date[0],date[1],date[2],date[3],date[4],date[5],0,utc)) # for Trac0.11
308+ yield(url,title,date,to_unicode(author,'utf-8'),to_unicode(detail,'utf-8'))
309+ return
310+
311+ #XMLのElementを再帰的に探してテキストを生成
312+ def _get_innerText(self,text,node_array):
313+ for node in node_array:
314+ if node.nodeType == node.TEXT_NODE:
315+ text = text + unicode(node.data).encode('utf-8')
316+ else:
317+ text = self._get_innerText(text,node.childNodes)
318+ return text
319+
320+class SearchDocumentHyperEstraierModule(Component):
321+ implements(ISearchSource)
322+
323+ # ISearchProvider methods
324+ def get_search_filters(self, req):
325+ if self.env.config.get('searchhyperestraier', 'doc_index_path','')!='' \
326+ and self.env.config.get('searchhyperestraier', 'doc_replace_left','')!='' \
327+ and self.env.config.get('searchhyperestraier', 'doc_url_left','')!='':
328+ yield ('documenthyperest', u'he:ドキュメント', 0)
329+
330+ def get_search_results(self, req, terms, filters):
331+ if not 'documenthyperest' in filters:
332+ return
333+ #estcmd.exeのパス
334+ estcmd_path = self.env.config.get('searchhyperestraier', 'estcmd_path','estcmd')
335+ #estcmd.exeの引数
336+ estcmd_arg = self.env.config.get('searchhyperestraier', 'estcmd_arg','search -vx -sf -ic Shift_JIS')
337+ estcmd_encode = self.env.config.get('searchhyperestraier', 'estcmd_encode','mbcs')
338+
339+ #インデックスのパス
340+ doc_index_path = self.env.config.get('searchhyperestraier', 'doc_index_path','')
341+ #コマンド実行時のエンコード(Pythonでの形式)
342+ #estcmd_argと一致(?)させる必要有り。
343+
344+ #検索結果のパスの頭で削る文字列
345+ doc_replace_left = self.env.config.get('searchhyperestraier', 'doc_replace_left','')
346+
347+ #tracやsvnと同じならびにくるドキュメントのフォルダ名
348+ doc_url_left = self.env.config.get('searchhyperestraier', 'doc_url_left','doc')
349+
350+ #cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,doc_index_path,unicode(query,'utf-8').encode('CP932'))
351+ qline = ' '.join(terms)
352+ cmdline = "%s %s %s %s" % (estcmd_path,estcmd_arg,doc_index_path,qline)
353+ self.log.debug('SearchHyperEstraier:%s' % cmdline)
354+ cmdline = unicode(cmdline).encode(estcmd_encode)
355+ np = NaivePopen(cmdline)
356+ #self.log.debug('Result:%s' % unicode(np.out,'utf-8').encode('mbcs'))
357+ #self.log.debug('Result:%s' % np.out)
358+ if np.errorlevel or np.err:
359+ err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel,
360+ np.err)
361+ raise Exception, err
362+ if np.out=='': #ドキュメントフォルダに何も入ってない
363+ return
364+ dom = parseString(np.out)
365+ root = dom.documentElement
366+ #estresult_node = root.getElementsByTagName("document")[0]
367+ element_array = root.getElementsByTagName("document")
368+ for element in element_array:
369+ #self.log.debug('Result:%s' % 'hoge')
370+ url = ""
371+ title = ""
372+ date = 0
373+ detail = ""
374+ author = "不明"
375+
376+ #detailを生成
377+ elem_array = element.getElementsByTagName("snippet")
378+ detail = self._get_innerText("",elem_array)
379+
380+ #その他の属性を生成
381+ attrelem_array = element.getElementsByTagName("attribute")
382+ for attrelem in attrelem_array:
383+ attr_name = attrelem.getAttribute("name")
384+ attr_value = unicode(attrelem.getAttribute("value"))
385+ #URLとタイトルを生成
386+ #if attr_name == "_lreal": #"_lreal"ではファイル名に' 'などが入っている場合対応できない
387+ # attr_value=attr_value[len(doc_replace_left):].replace("\\","/")
388+ # title = doc_url_left + attr_value
389+ # url = urllib.quote(title.encode('utf-8'))
390+ # title = '/' + title
391+ if attr_name == "_lpath": #s-jisをquoteしたもの("file:///C|/TracLight/…"の形式)
392+ attr_value = urllib.unquote(attr_value).encode('raw_unicode_escape').decode('CP932')
393+ attr_value = attr_value[(len('file:///')+len(doc_replace_left)):]
394+ #url = doc_url_left + attr_value
395+ #title = '/' + urllib.unquote(url)
396+ title = '/' + doc_url_left + attr_value
397+ url = urllib.quote((doc_url_left + attr_value).encode('utf-8'))
398+ #更新日時を生成
399+ elif attr_name =="@mdate":
400+ date = time.strptime(attr_value,"%Y-%m-%dT%H:%M:%SZ")
401+ self.log.debug('date:%s' % attr_value)
402+ date = to_datetime(datetime.datetime(date[0],date[1],date[2],date[3],date[4],date[5],0,utc)) # for Trac0.11
403+ yield(url,title,date,to_unicode(author,'utf-8'),to_unicode(detail,'utf-8'))
404+ return
405+
406+ #XMLのElementを再帰的に探してテキストを生成
407+ def _get_innerText(self,text,node_array):
408+ for node in node_array:
409+ if node.nodeType == node.TEXT_NODE:
410+ text = text + unicode(node.data).encode('utf-8')
411+ else:
412+ text = self._get_innerText(text,node.childNodes)
413+ return text
--- plugins/searchhyperestraier/trunk/searchhyperestraier/__init__.py (nonexistent)
+++ plugins/searchhyperestraier/trunk/searchhyperestraier/__init__.py (revision 694)
@@ -0,0 +1,2 @@
1+# SearchRepositoryWithHyperEstraier module
2+from searchhyperestraier import *
Show on old repository browser