Feed URL and PNG icon can be set.
[0publish-gui.git] / main.py
blob09c418de164c17957ec5a0fc24fd3fc4fa52c3d5
1 from xml.dom import Node, minidom
3 import rox, os, pango, sys, textwrap
4 from rox import g
5 import gtk.glade
7 import signing
8 from xmltools import *
10 RESPONSE_SAVE = 1
11 RESPONSE_SAVE_AND_TEST = 0
13 gladefile = os.path.join(rox.app_dir, '0publish-gui.glade')
15 def choose_feed():
16 tree = gtk.glade.XML(gladefile, 'no_file_specified')
17 box = tree.get_widget('no_file_specified')
18 tree.get_widget('new_button').grab_focus()
19 resp = box.run()
20 box.destroy()
21 if resp == 0:
22 chooser = g.FileChooserDialog('Choose a location for the new feed',
23 None, g.FILE_CHOOSER_ACTION_SAVE)
24 chooser.set_current_name('MyProg.xml')
25 chooser.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL)
26 chooser.add_button(g.STOCK_NEW, g.RESPONSE_OK)
27 elif resp == 1:
28 chooser = g.FileChooserDialog('Choose the feed to edit',
29 None, g.FILE_CHOOSER_ACTION_OPEN)
30 chooser.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL)
31 chooser.add_button(g.STOCK_OPEN, g.RESPONSE_OK)
32 else:
33 sys.exit(1)
34 chooser.set_default_response(g.RESPONSE_OK)
35 if chooser.run() != g.RESPONSE_OK:
36 sys.exit(1)
37 path = chooser.get_filename()
38 chooser.destroy()
39 return FeedEditor(path)
41 emptyFeed = """<?xml version='1.0'?>
42 <interface xmlns="%s">
43 <name>Name</name>
44 </interface>
45 """ % (XMLNS_INTERFACE)
47 class FeedEditor:
48 def __init__(self, pathname):
49 self.pathname = pathname
51 self.wTree = gtk.glade.XML(gladefile, 'main')
52 self.window = self.wTree.get_widget('main')
53 self.window.connect('destroy', rox.toplevel_unref)
55 help = gtk.glade.XML(gladefile, 'main_help')
56 help_box = help.get_widget('main_help')
57 help_box.set_default_size(g.gdk.screen_width() / 4,
58 g.gdk.screen_height() / 4)
59 help_box.connect('delete-event', lambda box, ev: True)
60 help_box.connect('response', lambda box, resp: box.hide())
62 def resp(box, resp):
63 if resp == g.RESPONSE_HELP:
64 help_box.present()
65 elif resp == RESPONSE_SAVE_AND_TEST:
66 self.save()
67 elif resp == RESPONSE_SAVE:
68 self.save()
69 else:
70 box.destroy()
71 self.window.connect('response', resp)
72 rox.toplevel_ref()
74 keys = signing.get_secret_keys()
75 key_menu = self.wTree.get_widget('feed_key')
76 key_model = g.ListStore(str, str)
77 key_menu.set_model(key_model)
78 cell = g.CellRendererText()
79 cell.set_property('ellipsize', pango.ELLIPSIZE_MIDDLE)
80 key_menu.pack_start(cell)
81 key_menu.add_attribute(cell, 'text', 1)
83 key_model.append((None, '(unsigned)'))
84 for k in keys:
85 key_model.append(k)
87 if os.path.exists(self.pathname):
88 data, _, self.key = signing.check_signature(self.pathname)
89 self.doc = minidom.parseString(data)
90 self.update_fields()
91 else:
92 self.doc = minidom.parseString(emptyFeed)
93 self.key = None
94 key_menu.set_active(0)
96 def update_fields(self):
97 root = self.doc.documentElement
99 def set(name):
100 value = singleton_text(root, name)
101 if value:
102 self.wTree.get_widget('feed_' + name).set_text(value)
103 set('name')
104 set('summary')
105 set('homepage')
107 for icon in children(root, 'icon'):
108 if icon.getAttribute('type') == 'image/png':
109 href = icon.getAttribute('href')
110 self.wTree.get_widget('feed_icon').set_text(href)
111 break
113 description = singleton_text(root, 'description') or ''
114 paragraphs = [format_para(p) for p in description.split('\n\n')]
115 buffer = self.wTree.get_widget('feed_description').get_buffer()
116 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
117 buffer.insert_at_cursor('\n'.join(paragraphs))
119 key_menu = self.wTree.get_widget('feed_key')
120 model = key_menu.get_model()
121 if self.key:
122 i = 0
123 for line in model:
124 if line[0] == self.key:
125 break
126 i += 1
127 else:
128 model.append((self.key, 'Missing key (%s)' % self.key))
129 key_menu.set_active(i)
130 else:
131 key_menu.set_active(0)
133 def update_doc(self):
134 root = self.doc.documentElement
135 def update(name, required = False, attrs = {}, value_attr = None):
136 widget = self.wTree.get_widget('feed_' + name)
137 if isinstance(widget, g.TextView):
138 buffer = widget.get_buffer()
139 text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
140 paras = ['\n'.join(textwrap.wrap(para, 80)) for para in text.split('\n') if para.strip()]
141 value = '\n' + '\n\n'.join(paras)
142 else:
143 value = widget.get_text()
144 elems = list(children(root, name, attrs = attrs))
145 if value:
146 if elems:
147 elem = elems[0]
148 else:
149 elem = create_element(root, name,
150 before = ['group', 'implementation', 'requires'])
151 for x in attrs:
152 elem.setAttribute(x, attrs[x])
153 if value_attr:
154 elem.setAttribute(value_attr, value)
155 set_data(elem, None)
156 else:
157 set_data(elem, value)
158 else:
159 if required:
160 raise Exception('Missing required field "%s"' % name)
161 for e in elems:
162 remove_element(e)
164 update('name', True)
165 update('summary', True)
166 update('description', True)
167 update('homepage')
168 update('icon', attrs = {'type': 'image/png'}, value_attr = 'href')
170 uri = self.wTree.get_widget('feed_url').get_text()
171 if uri:
172 root.setAttribute('uri', uri)
173 elif root.hasAttribute('uri'):
174 root.removeAttribute('uri')
176 def save(self):
177 self.update_doc()
178 if self.key:
179 sign = signing.sign_xml
180 else:
181 sign = signing.sign_unsigned
182 data = self.doc.toxml()
183 sign(self.pathname, data, self.key)