On Thu, 2006-03-16 at 04:52 -0500, Francisco Jesús Jordano Jiménez wrote: Hi Francisco ! > I've developed a handler for personal del.icio.us bookmarks. > It searchs your bookmarks looking for the tags you type in the deskbar. > > The attach contains the handler and a png for displaying results :). If you wanna test it you should set in the handler your delicious user and password. > > If you have any question of suggestion about this handler please tell me :) I have modified and updated your handler to be a bit more consistent with the way deskbar works now. I attach the handler to this mail so everyone can enjoy it. Now we should decide wether we want this in the deskbar package or if it should be distributed outside (by word of mouth, currently..) My guess is, let's include it for now until we find a way to properly install external (from-the-web) handlers.. Opinions ? Thanks again for your work ! Raf
import os, cgi, os.path, deskbar, deskbar.Match, deskbar.Handler import gnomevfs, gtk from gettext import gettext as _ import urllib2,base64,libxml2,sys GCONF_DELICIOUS_USER = deskbar.GCONF_DIR+"/desklicious/user" GCONF_DELICIOUS_PASS = deskbar.GCONF_DIR+"/desklicious/pass" DEFAULT_QUERY_TAG = "http://del.icio.us/api/posts/all?tag" MAX_QUERIES = 10 QUERY_DELAY = 1 def _check_requirements(): #We need user and password if not deskbar.GCONF_CLIENT.get_string(GCONF_DELICIOUS_USER) or not deskbar.GCONF_CLIENT.get_string(GCONF_DELICIOUS_PASS): return (deskbar.Handler.HANDLER_HAS_REQUIREMENTS, _("You need to configure your deli.icio.us account."), _on_config_account) else: return (deskbar.Handler.HANDLER_IS_CONFIGURABLE, _("You can modify your deli.icio.us account."), _on_config_account) HANDLERS = { "DeliciousHandler" : { "name": _("del.icio.us Bookmarks"), "description": _("Search your del.icio.us bookmarks by tag name"), "requirements" : _check_requirements } } def _on_config_account(dialog): dialog = gtk.Dialog(_("del.icio.us Account"), dialog, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) table = gtk.Table(rows=3, columns=2) table.attach(gtk.Label(_("Enter your deli.icio.us username and password below")), 0, 2, 0, 1) user_entry = gtk.Entry() user_entry.set_text(deskbar.GCONF_CLIENT.get_string(GCONF_DELICIOUS_USER)) table.attach(gtk.Label(_("Username: ")), 0, 1, 1, 2) table.attach(user_entry, 1, 2, 1, 2) pass_entry = gtk.Entry() pass_entry.set_text(deskbar.GCONF_CLIENT.get_string(GCONF_DELICIOUS_PASS)) pass_entry.set_visibility(False) table.attach(gtk.Label(_("Password: ")), 0, 1, 2, 3) table.attach(pass_entry, 1, 2, 2, 3) table.show_all() dialog.vbox.add(table) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_ACCEPT and user_entry.get_text() != "" and pass_entry.get_text() != "": deskbar.GCONF_CLIENT.set_string(GCONF_DELICIOUS_USER, user_entry.get_text()) deskbar.GCONF_CLIENT.set_string(GCONF_DELICIOUS_PASS, pass_entry.get_text()) class DeliciousMatch(deskbar.Match.Match): def __init__(self, handler, url=None, tags=None, extended=None, **args): deskbar.Match.Match.__init__ (self, handler, **args) self.url = url self.tags = tags self.extended = extended def get_verb(self): return "<b>%(name)s</b>\n<span size='small' foreground='grey'>%(tags)s</span>" def get_name(self, text=None): return { "name": cgi.escape(self.name), "tags": cgi.escape(' '.join(self.tags)), } def action(self, text=None): gnomevfs.url_show(self.url) def get_category(self): return "web" def get_hash(self, text=None): return self.url class DeliciousHandler(deskbar.Handler.AsyncHandler): def __init__(self): deskbar.Handler.AsyncHandler.__init__ (self, os.path.join(os.path.dirname(__file__), "delicious.png")) self._delicious = DeliciousTagQueryEngine(self) def query(self, tag): #Hey man, calm down and query once a time :P self.check_query_changed (timeout=QUERY_DELAY) qmax = min (deskbar.DEFAULT_RESULTS_PER_HANDLER, MAX_QUERIES) # Yes, the google and yahoo search might take a long time # and of course deliciuos too !!! ... better check if we're still valid self.check_query_changed () #The queryyyyYyyYy :) print "Asking del.icio.us tags for %s" % tag posts = self._delicious.get_posts_by_tag(tag) self.check_query_changed () return posts[:qmax] class DeliciousTagQueryEngine: def __init__(self, handler): """We need use the globals DELICIOUS_USER and DELICIOUS_PASS""" self.handler = handler self._user = deskbar.GCONF_CLIENT.get_string(GCONF_DELICIOUS_USER) self._pass = deskbar.GCONF_CLIENT.get_string(GCONF_DELICIOUS_PASS) deskbar.GCONF_CLIENT.notify_add(GCONF_DELICIOUS_USER, lambda x, y, z, a: self.on_username_change(z.value)) deskbar.GCONF_CLIENT.notify_add(GCONF_DELICIOUS_PASS, lambda x, y, z, a: self.on_pass_change(z.value)) def on_username_change(self, value): if value != None and value.type == gconf.VALUE_STRING: self._user = value.get_string() def on_pass_change(self, value): if value != None and value.type == gconf.VALUE_STRING: self._pass = value.get_string() def get_posts_by_tag(self, tag): url = "%s=%s" % (DEFAULT_QUERY_TAG, tag) #Get the info from del.icio.us and parse xml = libxml2.parseDoc(self._get_delicious_url(url)) postsXML = xml.xpathEval("/posts/post") #And return the results posts=[] for post in postsXML: posts.append( DeliciousMatch(self.handler, name=post.prop('description'), url=post.prop('href'), tags=post.prop('tag').split(" "), extended=post.prop('extended'))) return posts def _get_delicious_url(self, url): req = urllib2.Request(url) try: handle = urllib2.urlopen(req) except IOError, e: #We need to authenticate base64string = base64.encodestring('%s:%s' % (self._user, self._pass))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) try: handle = urllib2.urlopen(req) except IOError, e: #User or Pass invalid!!! print "Desklicious info ::: user or pass invalid, please check your params." return None return handle.read() else: #We do not need authentication return handle.read() class DeliciousPost: def __init__(self,data): self.description=data.prop('description') self.href=data.prop('href') self.tags=data.prop('tag').split(" ") self.extended=data.prop('extended')
Attachment:
signature.asc
Description: This is a digitally signed message part