3 Module for handling feed subscription process.
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
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
28 import TreeViewManager
46 class SubscribeView(MVP
.GladeView
):
47 def _initialize(self
):
48 self
._window
= self
._widget
.get_widget('subscribe_dialog')
49 self
._button
1 = self
._widget
.get_widget('button1')
50 self
._button
2 = self
._widget
.get_widget('button2')
51 self
._button
3 = self
._widget
.get_widget('button3')
52 self
._progress
_bar
= self
._widget
.get_widget('progress_bar')
53 self
._notebook
= self
._widget
.get_widget('notebook')
54 self
._location
_entry
= self
._widget
.get_widget('feed_location_entry')
55 self
._username
_entry
= self
._widget
.get_widget('username_entry')
56 self
._password
_entry
= self
._widget
.get_widget('password_entry')
57 self
._category
_cb
= self
._widget
.get_widget('category_cb')
58 self
._error
_text
= self
._widget
.get_widget('error_text')
59 self
._error
_box
= self
._widget
.get_widget('error_box')
60 self
._result
_tree
= self
._widget
.get_widget('result_tree')
62 def _populate_tree(self
, parent_id
, path
= ""):
63 if not self
.nodes
.has_key(parent_id
):
66 for node
in self
.nodes
[parent_id
]:
68 #current_parent = self._create_row(node, parent_iter, path)
70 path
= path
+ " > " + node
.title
74 self
.category_store
.append([path
])
76 if self
.nodes
.has_key(node
.obj_id
):
77 self
._populate
_tree
(node
.obj_id
, path
)
79 def _on_forward(self
):
80 location
= self
._location
_entry
.get_text()
82 self
.display_error(_("Feed Location must not be empty"))
84 #location = "file:///home/ppawel/Desktop/test.rss"
85 username
= self
._username
_entry
.get_text()
86 username
= username
.strip()
87 password
= self
._password
_entry
.get_text()
88 password
= password
.strip()
90 config
= Config
.get_instance()
93 response
= helpers
.report_offline_status(self
._window
)
94 if response
== gtk
.RESPONSE_CANCEL
:
97 config
.offline
= not config
.offline
100 FeedDiscovery
.discover(location
, { "job-done": [ self
._discovery
_finished
] })
102 #self._presenter.subscribe(location, username, password)
105 def progress_pulse(self
, obj
):
106 self
._progress
_bar
.pulse()
109 def progress_destroy(self
):
111 gobject
.source_remove(self
.timer
)
114 def _setup_entries(self
):
115 self
._location
_entry
.set_text("http://")
116 self
._username
_entry
.delete_text(0,-1)
117 self
._password
_entry
.delete_text(0,-1)
119 self
._location
_entry
.grab_focus()
122 self
.state
= STATE_INTRO
123 self
.set_state(gtk
.STOCK_CLOSE
, gtk
.STOCK_GO_FORWARD
, None)
126 self
._setup
_entries
()
127 self
._error
_box
.set_property('visible',False)
128 self
._notebook
.set_current_page(0)
131 renderer
= gtk
.CellRendererToggle()
132 column
= gtk
.TreeViewColumn(_('Keep'), renderer
, active
= 0)
133 self
._result
_tree
.append_column(column
)
134 #renderer.connect('toggled', self._sticky_toggled)
136 renderer
= gtk
.CellRendererText()
137 column
= gtk
.TreeViewColumn(_('_Title'), renderer
, markup
= 1)
138 self
._result
_tree
.append_column(column
)
140 self
.store
= gtk
.ListStore(bool, str)
142 self
._result
_tree
.set_model(self
.store
)
144 self
.setup_combobox()
146 def setup_combobox(self
):
147 self
.category_store
= gtk
.ListStore(str)
148 cell
= gtk
.CellRendererText()
149 self
._category
_cb
.pack_start(cell
, True)
150 self
._category
_cb
.add_attribute(cell
, 'text', 0)
152 self
.categories
, self
.feeds
= FeedManager
.get_model()
153 self
.nodes
= TreeViewManager
.get_nodes()
155 self
._category
_cb
.set_model(self
.category_store
)
157 def display_error(self
, text
):
158 self
._error
_box
.set_property('visible',True)
159 self
._error
_text
.set_text(text
)
161 def set_location_entry(self
, location
):
162 self
._location
_entry
.set_text(location
)
164 def set_parent(self
, parent
):
165 self
._window
.set_transient_for(parent
)
167 def _on_button1_clicked(self
, *args
):
168 self
.progress_destroy()
171 def _on_button2_clicked(self
, *args
):
172 if self
.state
== STATE_INTRO
:
173 if self
._on
_forward
():
174 self
._progress
_bar
.props
.visible
= True
175 self
.timer
= gobject
.timeout_add(100, self
.progress_pulse
, self
)
176 self
._button
2.set_sensitive(False)
177 elif self
.state
== STATE_FINISH
:
178 self
.state
= STATE_INTRO
179 self
.set_state(gtk
.STOCK_CLOSE
, gtk
.STOCK_GO_FORWARD
, None)
180 self
._notebook
.set_current_page(0)
182 def _on_button3_clicked(self
, *args
):
183 if self
.state
== STATE_FINISH
:
186 def _discovery_finished(self
, handler
, feeds
):
187 gtk
.gdk
.threads_enter()
190 label
= xml
.sax
.saxutils
.escape(feed
.title
) + '\n<span size="smaller">' + xml
.sax
.saxutils
.escape(feed
.location
) + '</span>'
191 self
.store
.append([False, label
])
193 self
._progress
_bar
.props
.visible
= False
194 self
._button
2.set_sensitive(True)
195 self
.progress_destroy()
196 self
.state
= STATE_FINISH
197 self
.set_state(gtk
.STOCK_CLOSE
, gtk
.STOCK_GO_BACK
, gtk
.STOCK_APPLY
)
198 self
._notebook
.set_current_page(1)
200 self
._populate
_tree
(1)
201 self
._category
_cb
.set_active(0)
203 gtk
.gdk
.threads_leave()
205 def set_state(self
, b1
, b2
, b3
):
209 self
._button
1.set_label(b1
)
215 self
._button
2.set_label(b2
)
221 self
._button
3.set_label(b3
)
224 def _on_feed_location_entry_key_press_event(self
, widget
, event
):
225 if event
.keyval
== gtk
.keysyms
.Return
:
226 self
._presenter
.subscribe(self
._location
_entry
.get_text())
228 class SubscribePresenter(MVP
.BasicPresenter
):
230 def _normalize(self
, url
):
231 u
= urlparse
.urlsplit(url
.strip())
232 # we check if 'scheme' is not empty here because URIs like
233 # "google.com" IS valid but, in this case, 'scheme' is empty because
234 # urlsplit() expects urls are in the format of scheme://netloc/...
235 if not u
[0] or (u
[0] != "http" and u
[0] != "feed"):
238 u
= urlparse
.urlsplit(u
[2])
239 # .. if that happens then we munge the url by adding a // and assume
240 # that 'http' is the scheme.
242 u
= urlparse
.urlsplit("//" + url
, 'http')
245 def subscribe(self
, location
, username
=None, password
=None):
246 self
._username
= username
247 self
._password
= password
248 self
._location
= location
254 feed
.location
= location
255 feed
.title
= location
256 FeedManager
.save_feed(feed
)
258 def set_parent(self
, parent
):
259 self
.view
.set_parent(parent
)
261 def get_credential(self
):
262 return (self
._location
, self
._username
, self
._password
)
264 def show(self
, url
=None):
266 self
.view
.set_location_entry(url
)
269 # def set_url_dnd(self, url):
270 # self._window.show()
271 # self._feed_location_presenter.view.find_feed(url)
273 def set_location_from_clipboard(self
):
274 def _clipboard_cb(cboard
, url
, data
=None):
276 url
= self
._normalize
(url
)
277 if url
and url
[0] == "http":
278 url
= urlparse
.urlunsplit(url
)
279 self
.view
.set_location_entry(url
)
280 clipboard
= gtk
.clipboard_get(selection
="CLIPBOARD")
281 clipboard
.request_text(_clipboard_cb
, None)
283 class SubscribeConsumer
:
285 def __init__(self
, presenter
):
286 self
._presenter
= presenter
288 def http_results(self
, status
, data
):
291 def process(self
, data
):
292 url
, user
, password
= self
._presenter
.get_credential()
293 feed
= feeds
.Feed
.create_new_feed("", url
, user
, password
)
294 parsed
= SummaryParser
.parse(data
, feed
)
295 if parsed
.title
== "":
296 # store url loc in title in case it's empty
297 parsed
.title
= urlparse
.urlsplit(url
.strip())[1]
298 feed
.title
= helpers
.convert_entities(parsed
.title
)
299 feed
.channel_description
= helpers
.convert_entities(parsed
.description
)
300 feed
.access_info
= (url
, user
, password
)
301 feed
.last_poll
= int(time
.time())
302 fclist
= feeds
.category_list
303 fclist
.append_feed(feed
)
304 feed
.router
.route_all(parsed
)
307 def http_failed(self
, exception
):
308 # XXX signal to the user that subscription failed? incl. erros
309 print "subscribe failed", exception
311 def operation_stopped(self
):
312 # XXX signal that it stopped, why it stopped, and stop all progress
314 print "subscribe operation stopped"
316 def subscribe_show(url
=None, parent
=None):
317 gladefile
= XML(os
.path
.join(straw
.STRAW_DATA_DIR
, "subscribe.glade"))
318 _dialog
= SubscribePresenter(view
=SubscribeView(gladefile
))
319 _dialog
.set_parent(parent
)
320 _dialog
.set_location_from_clipboard()