Work on feed discovery assistant.
[straw/fork.git] / straw / subscribe.py
blob4874bb19691392c1cab43f037046652c0c213f1f
1 """ subscribe.py
3 Module for handling feed subscription process.
4 """
5 __copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__ = """ GNU General Public License
8 This program is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
11 version.
13 This program is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA. """
22 from gtk.glade import XML
23 from straw import helpers
24 import Config
25 import MVP
26 import SummaryParser
27 import error
28 import gettext
29 import gobject
30 import gtk
31 import os, os.path
32 import pygtk
33 import straw
34 import time
35 import urllib
36 import urlparse
37 pygtk.require('2.0')
39 STATE_INTRO = 1
40 STATE_FINISH = 2
42 class SubscribeView(MVP.GladeView):
43 def _initialize(self):
44 self._window = self._widget.get_widget('subscribe_dialog')
45 self._button1 = self._widget.get_widget('button1')
46 self._button2 = self._widget.get_widget('button2')
47 self._button3 = self._widget.get_widget('button3')
48 self._progress_bar = self._widget.get_widget('progress_bar')
49 self._notebook = self._widget.get_widget('notebook')
50 self._location_entry = self._widget.get_widget('feed_location_entry')
51 self._username_entry = self._widget.get_widget('username_entry')
52 self._password_entry = self._widget.get_widget('password_entry')
53 self._error_text = self._widget.get_widget('error_text')
54 self._error_box = self._widget.get_widget('error_box')
56 self._result_tree = self._widget.get_widget('result_tree')
58 def _on_forward(self):
59 location = self._location_entry.get_text()
60 if not location:
61 self.display_error(_("Feed Location must not be empty"))
62 return False
63 #location = "file:///home/ppawel/Desktop/test.rss"
64 username = self._username_entry.get_text()
65 username = username.strip()
66 password = self._password_entry.get_text()
67 password = password.strip()
69 config = Config.get_instance()
70 if config.offline:
71 self._window.hide()
72 response = helpers.report_offline_status(self._window)
73 if response == gtk.RESPONSE_CANCEL:
74 self.show()
75 return False
76 config.offline = not config.offline
78 import FeedDiscovery
79 FeedDiscovery.discover(location, { "job-done": [ self._discovery_finished ] })
81 #self._presenter.subscribe(location, username, password)
82 return True
84 def progress_pulse(self, obj):
85 self._progress_bar.pulse()
86 return True
88 def progress_destroy(self):
89 if self.timer != 0:
90 gobject.source_remove(self.timer)
91 self.timer = 0
93 def _setup_entries(self):
94 self._location_entry.set_text("http://")
95 self._username_entry.delete_text(0,-1)
96 self._password_entry.delete_text(0,-1)
98 self._location_entry.grab_focus()
100 def show(self):
101 self.state = STATE_INTRO
102 self.set_state(gtk.STOCK_CLOSE, gtk.STOCK_GO_FORWARD, None)
103 self.timer = 0
105 self._setup_entries()
106 self._error_box.set_property('visible',False)
107 self._notebook.set_current_page(0)
108 self._window.show()
110 renderer = gtk.CellRendererToggle()
111 column = gtk.TreeViewColumn(_('Keep'), renderer, active = 0)
112 self._result_tree.append_column(column)
113 #renderer.connect('toggled', self._sticky_toggled)
115 renderer = gtk.CellRendererText()
116 column = gtk.TreeViewColumn(_('_Title'), renderer, text = 1)
117 self._result_tree.append_column(column)
119 self.store = gtk.ListStore(bool, str)
121 self._result_tree.set_model(self.store)
123 def display_error(self, text):
124 self._error_box.set_property('visible',True)
125 self._error_text.set_text(text)
127 def set_location_entry(self, location):
128 self._location_entry.set_text(location)
130 def set_parent(self, parent):
131 self._window.set_transient_for(parent)
133 def _on_button1_clicked(self, *args):
134 self.progress_destroy()
135 self._window.hide()
137 def _on_button2_clicked(self, *args):
138 if self.state == STATE_INTRO:
139 if self._on_forward():
140 self._progress_bar.props.visible = True
141 self.timer = gobject.timeout_add(100, self.progress_pulse, self)
142 self._button2.set_sensitive(False)
143 elif self.state == STATE_FINISH:
144 self.state = STATE_INTRO
145 self.set_state(gtk.STOCK_CLOSE, gtk.STOCK_GO_FORWARD, None)
146 self._notebook.set_current_page(0)
148 def _on_button3_clicked(self, *args):
149 if self.state == STATE_FINISH:
150 pass
152 def _discovery_finished(self, handler, feeds):
153 gtk.gdk.threads_enter()
155 for feed in feeds:
156 self.store.append([False, feed.title])
158 self._progress_bar.props.visible = False
159 self._button2.set_sensitive(True)
160 self.progress_destroy()
161 self.state = STATE_FINISH
162 self.set_state(gtk.STOCK_CLOSE, gtk.STOCK_GO_BACK, gtk.STOCK_APPLY)
163 self._notebook.set_current_page(1)
165 gtk.gdk.threads_leave()
167 def set_state(self, b1, b2, b3):
168 if b1 == None:
169 self._button1.hide()
170 else:
171 self._button1.set_label(b1)
172 self._button1.show()
174 if b2 == None:
175 self._button2.hide()
176 else:
177 self._button2.set_label(b2)
178 self._button2.show()
180 if b3 == None:
181 self._button3.hide()
182 else:
183 self._button3.set_label(b3)
184 self._button3.show()
186 def _on_feed_location_entry_key_press_event(self, widget, event):
187 if event.keyval == gtk.keysyms.Return:
188 self._presenter.subscribe(self._location_entry.get_text())
190 class SubscribePresenter(MVP.BasicPresenter):
192 def _normalize(self, url):
193 u = urlparse.urlsplit(url.strip())
194 # we check if 'scheme' is not empty here because URIs like
195 # "google.com" IS valid but, in this case, 'scheme' is empty because
196 # urlsplit() expects urls are in the format of scheme://netloc/...
197 if not u[0] or (u[0] != "http" and u[0] != "feed"):
198 return None
199 if u[0] == 'feed':
200 u = urlparse.urlsplit(u[2])
201 # .. if that happens then we munge the url by adding a // and assume
202 # that 'http' is the scheme.
203 if u[1] == '':
204 u = urlparse.urlsplit("//" + url, 'http')
205 return u
207 def subscribe(self, location, username=None, password=None):
208 self._username = username
209 self._password = password
210 self._location = location
212 import FeedManager
213 import model
215 feed = model.Feed()
216 feed.location = location
217 feed.title = location
218 FeedManager.save_feed(feed)
220 def set_parent(self, parent):
221 self.view.set_parent(parent)
223 def get_credential(self):
224 return (self._location, self._username, self._password)
226 def show(self, url=None):
227 if url:
228 self.view.set_location_entry(url)
229 self.view.show()
231 # def set_url_dnd(self, url):
232 # self._window.show()
233 # self._feed_location_presenter.view.find_feed(url)
235 def set_location_from_clipboard(self):
236 def _clipboard_cb(cboard, url, data=None):
237 if url:
238 url = self._normalize(url)
239 if url and url[0] == "http":
240 url = urlparse.urlunsplit(url)
241 self.view.set_location_entry(url)
242 clipboard = gtk.clipboard_get(selection="CLIPBOARD")
243 clipboard.request_text(_clipboard_cb, None)
245 class SubscribeConsumer:
247 def __init__(self, presenter):
248 self._presenter = presenter
250 def http_results(self, status, data):
251 self.process(data)
253 def process(self, data):
254 url, user, password = self._presenter.get_credential()
255 feed = feeds.Feed.create_new_feed("", url, user, password)
256 parsed = SummaryParser.parse(data, feed)
257 if parsed.title == "":
258 # store url loc in title in case it's empty
259 parsed.title = urlparse.urlsplit(url.strip())[1]
260 feed.title = helpers.convert_entities(parsed.title)
261 feed.channel_description = helpers.convert_entities(parsed.description)
262 feed.access_info = (url, user, password)
263 feed.last_poll = int(time.time())
264 fclist = feeds.category_list
265 fclist.append_feed(feed)
266 feed.router.route_all(parsed)
267 feed.poll_done()
269 def http_failed(self, exception):
270 # XXX signal to the user that subscription failed? incl. erros
271 print "subscribe failed", exception
273 def operation_stopped(self):
274 # XXX signal that it stopped, why it stopped, and stop all progress
275 # bars, etc...
276 print "subscribe operation stopped"
278 def subscribe_show(url=None, parent=None):
279 gladefile = XML(os.path.join(straw.STRAW_DATA_DIR, "subscribe.glade"))
280 _dialog = SubscribePresenter(view=SubscribeView(gladefile))
281 _dialog.set_parent(parent)
282 _dialog.set_location_from_clipboard()
283 _dialog.show()