root/lang/python/misc/blosxom-client.py @ 15600

Revision 1275, 12.5 kB (checked in by mattn, 6 years ago)

lang/python/misc/blosxom-client.py: added blosxom client.

  • Property svn:executable set to *
Line 
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3"""blosxom-client v0.05 : A simple blosxom client."""
4import pygtk
5pygtk.require("2.0")
6import gtk
7import pango
8import ftplib
9import time
10import os
11import re
12
13class BlosxomClient:
14        def __init__(self):
15                """
16                初期化処理
17                """
18                self.settings = {}
19                self.links = {}
20                self.load_settings()
21
22        def is_wysiwyg(self):
23                if self.settings.has_key("wysiwyg"):
24                        return self.settings["wysiwyg"] == "true"
25                return False
26
27        def get_html(self):
28                """
29                TextViewからのHTML取得処理
30                TextTagからHTMLタグに変換
31                """
32                iter = self.buffer.get_start_iter()
33                if not self.is_wysiwyg():
34                        end_iter = self.buffer.get_end_iter()
35                        return self.buffer.get_text(iter, end_iter)
36                html = ''
37                bold_prev = False
38                italic_prev = False
39                link_prev = False
40                while not iter.is_end():
41                        bold = iter.has_tag(self.bold_tag)
42                        italic = iter.has_tag(self.italic_tag)
43                        link = iter.has_tag(self.link_tag)
44                        image = iter.has_tag(self.image_tag)
45                        url = ''
46                        for tag in iter.get_tags():
47                                for mark in iter.get_marks():
48                                        url = self.links.get(mark.get_name(), '')
49
50                        if link == True and link != link_prev:
51                                html += "<a href=\"%s\">" % url;
52                        if bold == True and bold != bold_prev:
53                                html += "<b>";
54                        if italic == True and italic != italic_prev:
55                                html += "<i>";
56                        if image == True:
57                                html += "<img src=\"%s\">" % url;
58                        if italic == False and italic != italic_prev:
59                                html += "</i>";
60                        if bold == False and bold != bold_prev:
61                                html += "</b>";
62                        if link == False and link != link_prev:
63                                html += "</a>";
64                        bold_prev = bold
65                        italic_prev = italic
66                        link_prev = link
67
68                        iter_orig = iter.copy()
69                        tag_found = iter.forward_to_tag_toggle(None)
70                        if not tag_found:
71                                iter = self.buffer.get_end_iter()
72                        text = self.buffer.get_text(iter_orig, iter)
73                        text = text.replace("&", "&amp;")
74                        text = text.replace("\"", "&quot;")
75                        text = text.replace("<", "&gt;")
76                        text = text.replace(">", "&lt;")
77                        text = text.replace("\n", "<br>\n")
78                        html += text
79                return html
80
81        def insert_link(self, is_image = False):
82                """
83                リンク挿入処理
84                ダイアログから入力されたリンク(もしくは画像パス)を挿入
85                """
86                dialog = gtk.Dialog("追加",
87                                buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
88                                "追加(_A)", gtk.RESPONSE_ACCEPT))
89       
90                dialog.set_default_response(gtk.RESPONSE_ACCEPT)
91                dialog.set_has_separator(False)
92                dialog.set_resizable(False)
93                dialog.vbox.set_spacing(2)
94
95                table = gtk.Table(2, 2)
96                dialog.vbox.pack_start(table)
97
98                label = gtk.Label("タイトル:")
99                label.set_alignment(0.0, 0.5)
100                table.attach(label, 0, 1, 0, 1, gtk.FILL)
101
102                title = gtk.Entry()           
103                table.attach(title, 1, 2, 0, 1)
104
105                label = gtk.Label("URL:")
106                label.set_alignment(0.0, 0.5)
107                table.attach(label, 0, 1, 1, 2, gtk.FILL)
108
109                url = gtk.Entry()
110                url.set_activates_default(True)
111                table.attach(url, 1, 2, 1, 2)
112
113                selection = self.buffer.get_selection_bounds()
114                if selection:
115                        title.set_text(self.buffer.get_text(selection[0], selection[1]))
116
117                table.show_all()
118
119                if selection:
120                        url.grab_focus()
121                else:
122                        title.grab_focus()
123
124                response = dialog.run()
125
126                link_title = None
127                link_url = None
128                if response == gtk.RESPONSE_ACCEPT:
129                        link_title = title.get_text()
130                        link_url = url.get_text()
131                        ins_mark = self.buffer.get_insert()
132                        if not is_image:
133                                tag = self.link_tag
134                        else:
135                                tag = self.image_tag
136                        mark_iter = self.buffer.get_iter_at_mark(ins_mark)
137                        if self.is_wysiwyg():
138                                if not selection:
139                                        self.buffer.insert_with_tags(mark_iter, link_title, tag)
140                                else:
141                                        self.buffer.apply_tag(tag, selection[0], selection[1])
142                        else:
143                                if is_image:
144                                        link_title = "<img src=\"%s\" title=\"%s\">" % (link_url, link_title)
145                                else:
146                                        link_title = "<a href=\"%s\" target=\"_blank\">" % link_url
147                                if not selection:
148                                        self.buffer.insert(mark_iter, link_title)
149                                else:
150                                        self.buffer.insert(selection[0], link_title)
151                                        self.buffer.insert(selection[1], tag.closing_tag)
152                        mark = self.buffer.create_mark("%03d" % len(self.links), mark_iter)
153                        self.links[mark.get_name()] = link_url
154
155                dialog.hide()
156
157        def on_menu_bold(self, widget):
158                """
159                メニュー「太字」のハンドラ
160                太字タグを適応する
161                """
162                selection = self.buffer.get_selection_bounds()
163                if not selection:
164                        return
165                if self.is_wysiwyg():
166                        if selection[0].has_tag(self.bold_tag):
167                                self.buffer.remove_tag(self.bold_tag, selection[0], selection[1])
168                        else:
169                                self.buffer.apply_tag(self.bold_tag, selection[0], selection[1])
170                else:
171                        text = self.bold_tag.opening_tag + self.buffer.get_text(selection[0], selection[1]) + self.bold_tag.closing_tag
172                        self.buffer.delete(selection[0], selection[1])
173                        self.buffer.insert_at_cursor(text)
174
175        def on_menu_italic(self, widget):
176                """
177                メニュー「斜体」のハンドラ
178                斜体タグを適応する
179                """
180                selection = self.buffer.get_selection_bounds()
181                if not selection:
182                        return
183                if self.is_wysiwyg():
184                        if selection[0].has_tag(self.italic_tag):
185                                self.buffer.remove_tag(self.italic_tag, selection[0], selection[1])
186                        else:
187                                self.buffer.apply_tag(self.italic_tag, selection[0], selection[1])
188                else:
189                        text = self.italic_tag.opening_tag + self.buffer.get_text(selection[0], selection[1]) + self.italic_tag.closing_tag
190                        self.buffer.delete(selection[0], selection[1])
191                        self.buffer.insert_at_cursor(text)
192
193        def on_menu_link(self, widget):
194                """
195                メニュー「リンク」のハンドラ
196                リンク挿入処理を呼び出す
197                """
198                self.insert_link(False)
199
200        def on_menu_image(self, widget):
201                """
202                メニュー「画像」のハンドラ
203                リンク挿入処理(画像)を呼び出す
204                """
205                self.insert_link(True)
206
207        def on_menu_help(self, widget):
208                """
209                メニュー「ヘルプ」のハンドラ
210                ヘルプダイアログを表示する
211                """
212                dialog = gtk.MessageDialog(self.window,
213                                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
214                                gtk.BUTTONS_CLOSE,
215                                "blosxom-client v0.05 : A simple blosxom client.\n\tby mattn")
216                dialog.run()
217                dialog.destroy()
218
219        def on_button_clicked(self, button):
220                """
221                ボタン「公開」のハンドラ
222                FTPにアクセスしてフォルダを生成し、記事をポストする
223                """
224                buffer = self.body.get_buffer()
225                start_iter = buffer.get_start_iter()
226                end_iter = buffer.get_end_iter()
227                txt = buffer.get_text(start_iter, end_iter, 0)
228                filename = "%i.%s" % (int(time.time()), "txt")
229                message = "サーバに接続中..."
230                try:
231                        ftp = ftplib.FTP(self.settings["server"])
232                        message = "サーバにログイン中..."
233                        ftp.login(self.settings["userid"], self.settings["password"])
234                        message = "公開ディレクトリに移動中..."
235                        ftp.cwd(self.settings["publish_root"])
236                        categories = self.category.get_text().split("/")
237                        for c in categories:
238                                if not c:
239                                        continue
240                                try:
241                                        ftp.cwd(c)
242                                except:
243                                        message = "公開ディレクトリにディレクトリを作成中..."
244                                        ftp.mkd(c);
245                                        ftp.cwd(c)
246                        message = "ローカルにテンポラリを作成中..."
247                        file = open(filename, "w")
248                        file.write(self.title.get_text())
249                        file.write("\n")
250                        file.write(txt)
251                        file.close()
252                        message = "ローカルのテンポラリを読み込み中..."
253                        file = open(filename, "rb")
254                        message = "エントリファイルのアップロード中..."
255                        ftp.storbinary("STOR %s" % filename, file)
256                        ftp.quit()
257                        file.close()
258                        message = "ローカルのテンポラリを削除中..."
259                        os.remove(filename)
260                except Exception, e:
261                        try:
262                                ftp.close()
263                        except:
264                                pass
265                        try:
266                                file.close()
267                        except:
268                                pass
269                        try:
270                                os.remove(filename)
271                        except:
272                                pass
273                        dialog = gtk.MessageDialog(self.window,
274                                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR,
275                                        gtk.BUTTONS_CLOSE,
276                                        message + "\n" + str(e))
277                        dialog.run()
278                        dialog.destroy()
279
280        def load_settings(self):
281                """
282                設定ファイルを読み込む
283                """
284                fname = os.path.expanduser("~/.blosxom-client")
285                try:
286                        file = open(fname, "r")
287                        reg = re.compile("^([^=]+)=(\S*)$")
288                        while 1:
289                                line = file.readline()
290                                if not line:
291                                        break
292                                m = reg.search(line)
293                                self.settings[m.group(1)] = m.group(2)
294                        file.close
295                except:
296                        pass
297
298        def main(self):
299                """
300                メイン処理
301                """
302                self.window = gtk.Window()
303                self.window.set_title("Blosxom Client")
304                self.window.connect("delete-event", gtk.main_quit)
305
306                accel_group = gtk.AccelGroup()
307                self.window.add_accel_group(accel_group)
308
309                vbox = gtk.VBox()
310                vbox.set_spacing(2)
311                self.window.add(vbox)
312
313                menubar = gtk.MenuBar()
314                vbox.pack_start(menubar, expand=False)
315
316                # ファイルメニュー
317                menuitem = gtk.MenuItem('ファイル(_F)')
318
319                menu = gtk.Menu()
320                submenuitem = gtk.MenuItem('終了(_X)')
321                submenuitem.connect("activate", gtk.main_quit, "file.quit")
322                submenuitem.add_accelerator('activate', accel_group, gtk.gdk.keyval_from_name("q"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
323                menu.add(submenuitem)
324                menuitem.set_submenu(menu)
325
326                menubar.add(menuitem)
327
328                # 編集メニュー
329                menuitem = gtk.MenuItem('編集(_E)')
330
331                menu = gtk.Menu()
332                submenuitem = gtk.MenuItem('太字(_B)')
333                submenuitem.connect("activate", self.on_menu_bold)
334                submenuitem.add_accelerator('activate', accel_group, gtk.gdk.keyval_from_name("b"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
335                menu.add(submenuitem)
336                submenuitem = gtk.MenuItem('斜体(_I)')
337                submenuitem.connect("activate", self.on_menu_italic)
338                submenuitem.add_accelerator('activate', accel_group, gtk.gdk.keyval_from_name("i"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
339                menu.add(submenuitem)
340                menuitem.set_submenu(menu)
341                submenuitem = gtk.MenuItem('リンク(_L)')
342                submenuitem.connect("activate", self.on_menu_link)
343                submenuitem.add_accelerator('activate', accel_group, gtk.gdk.keyval_from_name("r"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
344                menu.add(submenuitem)
345                menuitem.set_submenu(menu)
346                submenuitem = gtk.MenuItem('画像(_L)')
347                submenuitem.connect("activate", self.on_menu_image)
348                submenuitem.add_accelerator('activate', accel_group, gtk.gdk.keyval_from_name("t"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
349                menu.add(submenuitem)
350                menuitem.set_submenu(menu)
351
352                menubar.add(menuitem)
353
354                # ヘルプメニュー
355                menuitem = gtk.MenuItem('ヘルプ(_H)')
356                menuitem.set_right_justified(True)
357
358                menu = gtk.Menu()
359                submenuitem = gtk.MenuItem('blosxom-clientについて(_A)')
360                submenuitem.connect("activate", self.on_menu_help)
361                menu.add(submenuitem)
362                menuitem.set_submenu(menu)
363
364                menubar.add(menuitem)
365
366                table = gtk.Table(2, 3, False)
367                table.set_border_width(10)
368                table.show()
369                vbox.add(table)
370
371                label = gtk.Label("タイトル: ")
372                table.attach(label,
373                                0, 1,                   0, 1,
374                                gtk.FILL,  gtk.EXPAND | gtk.FILL,
375                                0,                      0)
376                self.title = gtk.Entry()
377                table.attach(self.title,
378                                1, 2,                   0, 1,
379                                gtk.EXPAND | gtk.FILL,  gtk.EXPAND | gtk.FILL,
380                                0,                      0)
381
382                label = gtk.Label("カテゴリ: ")
383                table.attach(label,
384                                0, 1,                   1, 2,
385                                gtk.FILL,  gtk.EXPAND | gtk.FILL,
386                                0,                      0)
387                self.category = gtk.Entry()
388                table.attach(self.category,
389                                1, 2,                   1, 2,
390                                gtk.EXPAND | gtk.FILL,  gtk.EXPAND | gtk.FILL,
391                                0,                      0)
392
393                sw = gtk.ScrolledWindow()
394                sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
395                table.attach(sw,
396                                0, 2,                   2, 3,
397                                gtk.EXPAND | gtk.FILL,  gtk.EXPAND | gtk.FILL,
398                                0,                      0)
399
400                self.body = gtk.TextView()
401                self.body.set_size_request(500, 200)
402                self.body.queue_resize()
403                font = pango.FontDescription('Sans 12')
404                self.body.modify_font(font)
405                sw.add(self.body)
406
407                self.buffer = self.body.get_buffer()
408                self.bold_tag = self.buffer.create_tag("b")
409                self.bold_tag.opening_tag = '<b>'
410                self.bold_tag.closing_tag = '</b>'
411                self.bold_tag.set_property("weight", pango.WEIGHT_BOLD)
412                self.italic_tag = self.buffer.create_tag("i")
413                self.italic_tag.opening_tag = '<i>'
414                self.italic_tag.closing_tag = '</i>'
415                self.italic_tag.set_property("style", pango.STYLE_ITALIC)
416                self.link_tag = self.buffer.create_tag("a")
417                self.link_tag.opening_tag = '<a>'
418                self.link_tag.closing_tag = '</a>'
419                self.link_tag.set_property("underline", pango.UNDERLINE_SINGLE)
420                self.link_tag.set_property("foreground", "#0000FF")
421                self.image_tag = self.buffer.create_tag("img")
422                self.image_tag.opening_tag = '<img>'
423                self.image_tag.closing_tag = None
424
425                button = gtk.Button("公開")
426                button.connect("clicked", self.on_button_clicked)
427                vbox.add(button)
428
429                self.window.show_all()
430                gtk.main()
431
432if __name__=='__main__':
433        try:
434                blosxom_client = BlosxomClient()
435                blosxom_client.main()
436        except Exception, e:
437                print(e)
438                pass
Note: See TracBrowser for help on using the browser.