Merge branch 'master' of https://git.maemo.org/projects/feedingit
[feedingit] / src / opml.py
1 #!/usr/bin/env python2.5
2
3
4 # Copyright (c) 2007-2008 INdT.
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 #  This program is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public License
16 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 # ============================================================================
20 # Name        : FeedingIt.py
21 # Author      : Yves Marcoz
22 # Version     : 0.2.2
23 # Description : Simple RSS Reader
24 # ============================================================================
25
26 from xml.dom.minidom import parse, parseString
27 import urllib2
28 import gtk
29 import hildon
30 import gobject
31 import time
32 from os.path import isfile, dirname
33 import gobject
34
35 class ExportOpmlData():
36     def __init__(self, parent, listing):
37         fs = hildon.FileSystemModel()
38         dialog = hildon.FileChooserDialog(parent, gtk.FILE_CHOOSER_ACTION_SAVE, fs)
39                                #(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
40                                 #gtk.STOCK_SAVE, gtk.RESPONSE_OK))
41                                #)
42         #dialog = gobject.new(hildon.FileChooserDialog, \
43         #            action=gtk.FILE_CHOOSER_ACTION_SAVE)
44         #dialog.set_default_response(gtk.RESPONSE_OK)
45         #dialog.set_property('autonaming',False)
46         #dialog.set_property('show-files',True)
47         dialog.set_current_folder('/home/user/MyDocs/')
48         dialog.set_current_name('feedingit-export')
49         dialog.set_extension('opml')
50         response = dialog.run()
51         dialog.hide()
52         if response == gtk.RESPONSE_OK:
53                 filename = dialog.get_filename()
54                 print filename
55             #try:
56
57                 cont = True
58                 if isfile(filename):
59                     note = "File already exists. Aborted"
60                     confirm = hildon.Note ("confirmation", parent, "File already exists. Are you sure you want to overwrite it?", gtk.STOCK_DIALOG_WARNING )
61                     confirm.set_button_texts ("Yes", "Cancel")
62                     response = confirm.run()
63                     confirm.destroy()
64                     if response == gtk.RESPONSE_OK:
65                         cont = True
66                     else:
67                         note = "Operation cancelled."
68                         cont = False
69                 if cont:
70                     file = open(filename, "w")
71                     file.write(self.getOpmlText(listing))
72                     file.close()
73                     note = "Feeds exported to %s" %filename
74             #except:
75                 note = "Failed to export feeds"
76             
77             #dialog.destroy()
78             #dialog = hildon.Note ("information", parent, note , gtk.STOCK_DIALOG_INFO )
79             #dialog.run()
80             #dialog.destroy()
81         elif response == gtk.RESPONSE_CANCEL:
82             dialog.destroy()  
83
84     def getOpmlText(self, listing):
85         time_now = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
86         opml_text = """<?xml version="1.0" encoding="UTF-8"?>
87 <opml version="1.0">
88 <head>
89     <title>Feeding It Export</title>
90 </head>
91 <body>
92 """
93         for key in listing.getListOfFeeds():
94             title = listing.getFeedTitle(key)
95             url = listing.getFeedUrl(key)
96             if not title == "Archived Articles": 
97                 opml_text += """\n\t\t<outline  type="rss" text="%s" title="%s" xmlUrl="%s"/>""" % (self.sanitize(title), self.sanitize(title), self.sanitize(url))
98         opml_text += """\n</body>\n</opml>\n"""
99         return opml_text
100     
101     def sanitize(self, text):
102         from cgi import escape
103         return escape(text).encode('ascii', 'xmlcharrefreplace')
104         
105         
106
107 class GetOpmlData():
108     def __init__(self, parent):
109         self.parent = parent
110         dialog = hildon.Note ("confirmation", parent, "What type of OPML?", gtk.STOCK_DIALOG_WARNING )
111         dialog.set_button_texts ("File", "URL")
112         response = dialog.run()
113         dialog.destroy()
114     
115         if response == gtk.RESPONSE_OK:
116             # Choose a file
117             self.data = self.askForFile()
118         else:
119             # Download a URL
120             self.data = self.downloadFile()
121             
122     def getData(self):
123         if not self.data == None:
124                dialog = OpmlDialog(self.parent, self.data)
125                response = dialog.run()
126                if response == gtk.RESPONSE_ACCEPT:
127                    items = dialog.getItems()
128                else:
129                    items = []
130                dialog.destroy()
131                return items
132         return []
133
134     def downloadFile(self):
135         dlg = gtk.Dialog("Import OPML from web", self.parent, gtk.DIALOG_DESTROY_WITH_PARENT,
136                      ('Import', gtk.RESPONSE_OK,
137                       gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
138         hb = gtk.HBox(False, 5)
139         hb.pack_start(gtk.Label('URL:'), expand=False)
140         entry = hildon.Entry(0)
141         entry.set_text("http://")
142         entry.select_region(-1, -1)
143         hb.pack_start(entry, expand=True)
144         hb.show_all()
145         dlg.vbox.pack_start(hb, False)
146
147         resp = dlg.run()
148         url = entry.get_text()
149         dlg.destroy()
150         if resp == gtk.RESPONSE_CANCEL:
151             return None
152         try:
153             f = urllib2.urlopen(url)
154             data = f.read()
155             f.close()
156         except:
157             #Show error note
158             return None
159         return data
160
161     def askForFile(self):
162         #dialog = hildon.FileChooserDialog(self.parent,
163         #                       gtk.FILE_CHOOSER_ACTION_OPEN)
164         #dialog = gobject.new(hildon.FileChooserDialog, \
165         #            action=gtk.FILE_CHOOSER_ACTION_OPEN)
166         #dialog.set_default_response(gtk.RESPONSE_OK)
167         fs = hildon.FileSystemModel()
168         dialog = hildon.FileChooserDialog(self.parent, gtk.FILE_CHOOSER_ACTION_OPEN, fs)
169         
170         filter = gtk.FileFilter()
171         filter.set_name("All files")
172         filter.add_pattern("*")
173         dialog.add_filter(filter)
174
175         filter = gtk.FileFilter()
176         filter.set_name("OPML")
177         filter.add_pattern("*.xml")
178         filter.add_pattern("*.opml")
179         dialog.add_filter(filter)
180
181         response = dialog.run()
182         if response == gtk.RESPONSE_OK:
183             file = open(dialog.get_filename())
184             data = file.read()
185             file.close()
186             dialog.destroy()
187             return data
188         elif response == gtk.RESPONSE_CANCEL:
189             dialog.destroy()
190             return None
191
192
193 class OpmlDialog(gtk.Dialog):
194     def parse(self, opmlData):
195         self.feeds = []
196         dom1 = parseString(opmlData)
197         
198         outlines = dom1.getElementsByTagName('outline')
199         for outline in outlines:
200             title = outline.getAttribute('text')
201             url = outline.getAttribute('xmlUrl')
202             if url == "":
203                 url = outline.getAttribute('htmlUrl')
204             if not url == "":
205                 self.feeds.append( (title, url) )
206         
207     def getFeedLinks(self):
208         return self.feeds
209         
210     def __init__(self, parent, opmlData):
211         self.parse(opmlData)
212         gtk.Dialog.__init__(self, "Select OPML Feeds",  parent, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
213         
214         self.pannableArea = hildon.PannableArea()
215         self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
216         self.treeview = gtk.TreeView(self.treestore)
217
218         self.displayFeeds()
219
220         self.set_default_size(-1, 600)
221         self.vbox.pack_start(self.pannableArea)
222         
223         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
224         button.set_label("Select All")
225         button.connect("clicked", self.button_select_all_clicked)
226         self.action_area.pack_end(button)
227         
228         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
229         button.set_label("Unselect All")
230         button.connect("clicked", self.button_select_none_clicked)
231         self.action_area.pack_end(button)
232         
233         self.show_all()
234         
235     def button_select_all_clicked(self, button):
236         self.treeview.get_selection().select_all()
237         
238     def button_select_none_clicked(self, button):
239         self.treeview.get_selection().unselect_all()
240         
241     def displayFeeds(self):
242         self.treeview.destroy()
243         self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
244         self.treeview = gtk.TreeView()
245         
246         self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
247         hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_EDIT)
248         self.refreshList()
249         self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
250
251         self.pannableArea.add(self.treeview)
252         self.pannableArea.show_all()
253         self.treeview.get_selection().select_all()
254
255     def refreshList(self, selected=None, offset=0):
256         rect = self.treeview.get_visible_rect()
257         y = rect.y+rect.height
258         self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
259         self.treeview.set_model(self.treestore)
260         for (title, url) in self.feeds:
261             item = self.treestore.append([title, url])
262             self.treeview.get_selection().select_iter(item)
263         #self.treeview.get_selection().select_all()
264         self.pannableArea.show_all()
265
266     def getItems(self):
267         list = []
268         treeselection = self.treeview.get_selection()
269         (model, pathlist) = treeselection.get_selected_rows()
270         for path in pathlist:
271             list.append( (model.get_value(model.get_iter(path),0), model.get_value(model.get_iter(path),1)) )
272         return list
273
274 def showOpmlData(widget, parent, button):
275     dialog = GetOpmlData(parent)
276     print dialog.getData()
277     #dialog.destroy()
278
279 if __name__ == "__main__":
280     window = hildon.Window()
281     window.set_title("Test App")
282
283     
284     button = gtk.Button("Click to confirm.")
285     window.add(button)
286     button.connect("clicked", showOpmlData, window, button)
287     window.connect("destroy", gtk.main_quit)
288     window.show_all()
289
290     gtk.main()
291     window.destroy()