List Info

Thread: Patch to include epilicious in epiphany-extensions




Patch to include epilicious in epiphany-extensions
user name
2006-07-16 22:19:52
I've been looking into putting epilicious in
epiphany-extensions (from
CVS). I haven't quite gotten so far as to trying it all out
(building
stuff with jhbuild at the moment) but I thought that if I
can get early
feedback on my attempt so far it would probably help a bit.

Anything obvious I've missed?

/M

This is the diff for configure.ac:

--- epiphany-extensions_orig/configure.ac	2006-07-12
22:16:38.000000000 +0100
+++ epiphany-extensions/configure.ac	2006-07-16
22:51:28.000000000 +0100
 -156,7
+156,7 
 USEFUL_EXTENSIONS="actions auto-reload auto-scroller
certificates error-viewer extensions-manager-ui gestures
java-console page-info push-scroller select-stylesheet
sidebar smart-bookmarks tab-groups tab-states"
 DEFAULT_EXTENSIONS="actions auto-scroller
certificates error-viewer extensions-manager-ui gestures
java-console page-info push-scroller select-stylesheet
sidebar smart-bookmarks tab-groups tab-states"
 
-PYTHON_ALL_EXTENSIONS="python-console sample-python
favicon"
+PYTHON_ALL_EXTENSIONS="python-console sample-python
epilicious favicon"
 PYTHON_USEFUL_EXTENSIONS="python-console
favicon"
 PYTHON_DEFAULT_EXTENSIONS="python-console
favicon"
 
 -342,6
+342,7 
 extensions/auto-scroller/Makefile
 extensions/certificates/Makefile
 extensions/dashboard/Makefile
+extensions/epilicious/Makefile
 extensions/gestures/Makefile
 extensions/error-viewer/Makefile
 extensions/error-viewer/mozilla/Makefile


This is the Makefile.am in extensions/epilicious:

epiliciousdir = $(EXTENSIONS_DIR)
epiclicious_PYTHON = \
		     libepilicious/pydelicious.py \
		     libepilicious/progress.py \
		     libepilicious/__init__.py \
		     libepilicious/EpiphanyStore.py \
		     libepilicious/DeliciousStore.py \
		     libepilicious/BaseStore.py \
		     epilicious.py

gladedir = $(pkgdatadir)/glade
glade_DATA = \
	     progress.glade

schemadir = $(GCONF_SCHEMA_FILE_DIR)
schema_DATA = epilicious.schemas

extensioninidir = $(extensiondir)
extensionini_DATA = epilicious.ephy-extension

epilicious.py : epilicious.py.in
	sed -e "s|%EXTENSION_DIR%|$(extensiondir)|"
$< > $

# ??? INTLTOOL_SCHEMAS_RULE
EPIPHANY_EXTENSION_RULE

install-data-local: $(schema_DATA)
if GCONF_SCHEMAS_INSTALL
	if test -z "$(DESTDIR)" ; then \
	for p in $^ ; do \
		GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \
		$(GCONFTOOL) --makefile-install-rule $$p >&1 >
/dev/null; \
	done \
	fi
endif

CLEANFILES  =
DISTCLEANFILES = epilicious.py

EXTRA_DIST = $(glade_DATA) $(schema_DATA)
$(extensionini_DATA)

-- 
Magnus Therning                             (OpenPGP:
0xAB4DFBA4)
magnustherning.org             Jabber: magnus.therninggmail.com
http://therning.org/magnus


Software is not manufactured, it is something you write and
publish.
Keep Europe free from software patents, we do not want
censorship
by patent law on written works.

If our ideas of intellectual property are wrong, we must
change them,
improve them and return them to their original purpose. When
intellectual property rules diminish the supply of new
ideas, they
steal from all of us.
     -- Andrew Brown, November 19, 2005, The Guardian
_______________________________________________
epiphany-list mailing list
epiphany-listgnome.org

http://mail.gnome.org/mailman/listinfo/epiphany-list
Patch to include epilicious in epiphany-extensions
user name
2006-07-17 19:37:08
Le dimanche 16 juillet 2006 à 23:19 +0100, Magnus Therning
a écrit :
> I've been looking into putting epilicious in
epiphany-extensions (from
> CVS). I haven't quite gotten so far as to trying it
all out (building
> stuff with jhbuild at the moment) but I thought that if
I can get early
> feedback on my attempt so far it would probably help a
bit.
> 
> Anything obvious I've missed?
> 

Looks ok to me, except maybe this:

epiliciousdir = $(EXTENSIONS_DIR)
epiclicious_PYTHON = \
                     libepilicious/pydelicious.py \
[...]

Why don't you place those files in the same source
directory, and just
install into the right place with

epiliciousdir = $(EXTENSIONS_DIR)/libepilicious 

?

Regards,
	Christian

_______________________________________________
epiphany-list mailing list
epiphany-listgnome.org

http://mail.gnome.org/mailman/listinfo/epiphany-list
Patch to include epilicious in epiphany-extensions
user name
2006-07-17 20:50:39
On Mon, Jul 17, 2006 at 21:37:08 +0200, Christian Persch
wrote:
>Le dimanche 16 juillet 2006 à 23:19 +0100, Magnus
Therning a écrit :
>> I've been looking into putting epilicious in
epiphany-extensions (from
>> CVS). I haven't quite gotten so far as to trying
it all out (building
>> stuff with jhbuild at the moment) but I thought
that if I can get early
>> feedback on my attempt so far it would probably
help a bit.
>> 
>> Anything obvious I've missed?
>> 
>
>Looks ok to me, except maybe this:
>
>epiliciousdir = $(EXTENSIONS_DIR)
>epiclicious_PYTHON = \
>                     libepilicious/pydelicious.py \
>[...]
>
>Why don't you place those files in the same source
directory, and just
>install into the right place with
>
>epiliciousdir = $(EXTENSIONS_DIR)/libepilicious 
>
>?

No good reason not to do it 

Here's the full patch. Still untested :-(

diff -NuPr epiphany-extensions_orig/configure.ac
epiphany-extensions/configure.ac
--- epiphany-extensions_orig/configure.ac	2006-07-12
22:16:38.000000000 +0100
+++ epiphany-extensions/configure.ac	2006-07-16
22:51:28.000000000 +0100
 -156,7
+156,7 
 USEFUL_EXTENSIONS="actions auto-reload auto-scroller
certificates error-viewer extensions-manager-ui gestures
java-console page-info push-scroller select-stylesheet
sidebar smart-bookmarks tab-groups tab-states"
 DEFAULT_EXTENSIONS="actions auto-scroller
certificates error-viewer extensions-manager-ui gestures
java-console page-info push-scroller select-stylesheet
sidebar smart-bookmarks tab-groups tab-states"
 
-PYTHON_ALL_EXTENSIONS="python-console sample-python
favicon"
+PYTHON_ALL_EXTENSIONS="python-console sample-python
epilicious favicon"
 PYTHON_USEFUL_EXTENSIONS="python-console
favicon"
 PYTHON_DEFAULT_EXTENSIONS="python-console
favicon"
 
 -342,6
+342,7 
 extensions/auto-scroller/Makefile
 extensions/certificates/Makefile
 extensions/dashboard/Makefile
+extensions/epilicious/Makefile
 extensions/gestures/Makefile
 extensions/error-viewer/Makefile
 extensions/error-viewer/mozilla/Makefile
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/BaseStore.py
epiphany-extensions/extensions/epilicious/BaseStore.py
---
epiphany-extensions_orig/extensions/epilicious/BaseStore.py	
1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/BaseStore.py	2006-
07-05 22:39:30.000000000 +0100
 -0,0
+1,49 
+# Copyright (C) 2005 by Magnus Therning
+
+# This program is free software; you can redistribute it
and/or modify
+# it under the terms of the GNU General Public License as
published by
+# the Free Software Foundation; either version 2 of the
License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be
useful,
+# but WITHOUT ANY WARRANTY; without even the implied
warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public
License
+# along with this program; if not, write to the Free
Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
02111-1307  USA
+
+class BaseStore:
+    '''The base of all stores. Never to be
instantiated.'''
+
+    def get_snapshot(self):
+        '''Calculates a snapshot of the store's
bookmarks.
+
+        The format of the snapshot is::
+         { <url> : [ <description>, [tag, tag,
...]],
+           <url> : [ <description>,
[<tags>*]],
+           ... }
+
+        return: the snapshot
+        '''
+        pass
+
+
+    def url_delete(self, url):
+        '''Deletes a URL form the store.
+
+        type url: string
+        param url: URL to delete
+        '''
+        pass
+
+
+    def url_sync(self, url, desc, to_del, to_add):
+        '''Synchronises a URL's tags. The URL is added
if it doesn't exist.
+
+        param url: The URL
+        param to_del: Set of tags to delete
+        param to_add: Set of tags to add
+        '''
+        pass
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/DeliciousStor
e.py
epiphany-extensions/extensions/epilicious/DeliciousStore.py
---
epiphany-extensions_orig/extensions/epilicious/DeliciousStor
e.py	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/DeliciousStore.py	
2006-07-05 22:39:30.000000000 +0100
 -0,0
+1,96 
+# Copyright (C) 2005 by Magnus Therning
+
+# This program is free software; you can redistribute it
and/or modify
+# it under the terms of the GNU General Public License as
published by
+# the Free Software Foundation; either version 2 of the
License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be
useful,
+# but WITHOUT ANY WARRANTY; without even the implied
warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public
License
+# along with this program; if not, write to the Free
Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
02111-1307  USA
+
+from urllib2 import URLError
+from sets import Set
+import pydelicious as delicious
+import libepilicious
+from libepilicious.BaseStore import BaseStore
+
+
+class DeliciousStore(BaseStore):
+    '''The class representing the storage of Delicious
bookmarks.'''
+
+    def __init__(self, user, pwd, space_repl):
+        '''Constructor.
+
+        param user: Delicious username
+        param pwd: Delicious password
+        param space_repl: Character that replaces space
+        '''
+        self.__un = user
+        self.__pwd = pwd
+        self.__sr = space_repl
+        self.__d = delicious.apiNew(user, pwd)
+        self.__snap_utd = 0
+
+    def get_snapshot(self):
+        '''Calculates a snapshot of the del.icio.us
bookmarks.
+
+        note: L{BaseStore.get_snapshot} documents the format
of the return
+        value.
+        '''
+        if self.__snap_utd:
+            return self.__snap
+        all = self.__d.posts_all()
+        res = {}
+        for p in all:
+            # Delicious prepends "dangerous"
links
+            if p['href'][:33] == 'http://del.icio.us/
doc/dangerous#':
+                url = p['href'][33:]
+            else:
+                url = p['href']
+            res[url] = [p['description'], \
+                    [t.replace(self.__sr, ' ') for t in
p['tags'].split(' ')]]
+
+        self.__snap = res
+        self.__snap_utd = 1
+        return res
+
+    def url_delete(self, url):
+        '''Deletes a URL from the storage.
+
+        param url: The URL to delete
+        '''
+        # Delicious prepends "dangerous" links
+        if url[:7] == 'file://':
+            url = 'http://del.icio.us/
doc/dangerous#' + url
+        try:
+            self.__d.posts_delete(url)
+            self.__snap_utd = 0
+        except:
+            libepilicious.get_logger().exception('Failed
to delete URL %s' % url)
+
+    def url_sync(self, url, desc, to_del, to_add):
+        '''Synchronises a URL's tags. The URL is added
if it doesn't exist.
+
+        param url: The URL
+        param to_del: Set of tags to delete
+        param to_add: Set of tags to add
+        '''
+        if to_del or to_add:
+            snap = self.get_snapshot()
+            if snap.has_key(url):
+                tags = (Set(snap[url][1]) | to_add) -
to_del
+            else:
+                tags = to_add
+            tag_str = ' '.join([t.replace(' ',
self.__sr) for t in tags])
+            try:
+                self.__d.posts_add(url, description=desc,
\
+                        tags=tag_str, replace='yes')
+                self.__snap_utd = 0
+            except:
+               
libepilicious.get_logger().exception('Failed to synchronise
URL %s' % url)
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/epilicious.ep
hy-extension
epiphany-extensions/extensions/epilicious/epilicious.ephy-ex
tension
---
epiphany-extensions_orig/extensions/epilicious/epilicious.ep
hy-extension	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/epilicious.ephy-ex
tension	2006-07-06 23:39:56.000000000 +0100
 -0,0
+1,9 
+[Epiphany Extension]
+Version=1
+Name=Epilicious
+Authors=Magnus Therning <magnustherning.org>;
+URL=http://therning
.org/magnus/epilicious
+
+[Loader]
+Type=python
+Module=epilicious
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/epilicious.py
.in
epiphany-extensions/extensions/epilicious/epilicious.py.in
---
epiphany-extensions_orig/extensions/epilicious/epilicious.py
.in	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/epilicious.py.in	2
006-07-16 23:01:10.000000000 +0100
 -0,0
+1,212 
+# Copyright (C) 2005 by Magnus Therning
+
+# This program is free software; you can redistribute it
and/or modify
+# it under the terms of the GNU General Public License as
published by
+# the Free Software Foundation; either version 2 of the
License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be
useful,
+# but WITHOUT ANY WARRANTY; without even the implied
warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public
License
+# along with this program; if not, write to the Free
Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
02111-1307  USA
+
+import gtk
+import gconf
+import gobject
+import sys, os, os.path
+
+sys.path.append('%EXTENSION_DIR%')
+
+import libepilicious
+libepilicious.LOCATION = '%EXTENSION_DIR%'
+
+# localization
+import gettext
+try:
+    t = gettext.translation('epilicious')
+    _ = t.ugettext
+except Exception, e:
+    _ = lambda x : x
+
+## Globals ##
+username = ''
+password = ''
+keyword = ''
+exclude = False
+space_repl = ''
+
+## Configuration ##
+_gconf_dir = '/apps/epiphany/extensions/epilicious'
+_gconf_un =
'/apps/epiphany/extensions/epilicious/username'
+_gconf_pwd =
'/apps/epiphany/extensions/epilicious/password'
+_gconf_kw =
'/apps/epiphany/extensions/epilicious/keyword'
+_gconf_excl =
'/apps/epiphany/extensions/epilicious/exclude'
+
+def _new_un(client, *args, **kwargs):
+    '''Callback to handle the username is modified in
GConf.'''
+    global username
+    username = client.get_string(_gconf_un)
+
+def _new_pwd(client, *args, **kwargs):
+    '''Callback to handle the password is modified in
GConf.'''
+    global password
+    password = client.get_string(_gconf_pwd)
+
+def _new_keyword(client, *args, **kwargs):
+    '''Callback to handle the keyword is modified in
GConf.'''
+    global keyword
+    keyword = client.get_string(_gconf_kw)
+
+def _new_exclude(client, *args, **kwargs):
+    '''Callback to handle the exclude is modified in
GConf.'''
+    global exclude
+    exclude = client.get_bool(_gconf_excl)
+
+def _gconf_register():
+    '''Sets up the GConf callbacks.'''
+    global keyword, exclude, space_repl
+
+    # hard coded for now
+    space_repl = '#'
+
+    client = gconf.client_get_default()
+    client.add_dir(_gconf_dir, gconf.CLIENT_PRELOAD_NONE)
+    client.notify_add(_gconf_un, _new_un)
+    client.notify_add(_gconf_pwd, _new_pwd)
+    client.notify_add(_gconf_kw, _new_keyword)
+    client.notify_add(_gconf_excl, _new_exclude)
+    _new_un(client)
+    _new_pwd(client)
+    _new_keyword(client)
+    _new_exclude(client)
+
+_gconf_register()
+
+## Synchronisation ##
+def _CB_Sync(action, window):
+    # Using gobject.idle_add() together with a generator is
much easier than
+    # getting threading to work properly. It doesn't make
the GUI very
+    # responsive during del.icio.us calls, but it's better
than nothing.
+    libepilicious.get_logger().info('Starting sync')
+    sync_gen = _do_sync()
+    gobject.idle_add(sync_gen.next)
+    libepilicious.get_logger().info('Sync done')
+
+def _do_sync():
+    '''Perform the synchronisation.
+
+    This is the method called from the menu item added in
epiphany. The
+    algorithm is as follows:
+
+    1. Read the base snapshot (L{libepilicious.get_old})
+    2. Create remote storage representation
(L{libepilicious.DeliciousStore})
+    3. Create local storage representation
(L{libepilicious.EpiphanyStore})
+    4. Remove URLs (L{libepilicous.remove_urls})
+    5. Add new URLs and synchronise tags
(L{libepilicous.sync_tags_on_urls})
+    6. Save a local snapshot as a base for the next
synchronisation
+       (L{libepilicious.save_snapshot})
+
+    Any errors that occur are logged.
+    '''
+    try:
+        from libepilicious import *
+        from libepilicious.progress import ProgressBar
+        from libepilicious.DeliciousStore import
DeliciousStore
+        from libepilicious.EpiphanyStore import
EpiphanyStore
+
+        pbar = ProgressBar()
+        pbar.show()
+        stepper = pbar.step()
+
+        remote_store = DeliciousStore(user = username, pwd
= password, \
+                space_repl = space_repl)
+        local_store = EpiphanyStore(keyword = keyword,
exclude = exclude)
+        stepper.next()
+        old = get_old()
+        yield True
+        stepper.next()
+        remote = remote_store.get_snapshot()
+        yield True
+        stepper.next()
+        local = local_store.get_snapshot()
+        yield True
+        stepper.next()
+
+        # Synchronize URLs
+        remove_urls(old = old,
+                remote = remote,
+                local = local,
+                rem_store = remote_store,
+                loc_store = local_store)
+        yield True
+        stepper.next()
+
+        purls = calculate_pertinent_urls(old = old,
+                remote = remote, local = local)
+        sync_tags_on_urls(purls = purls,
+                old = old,
+                remote = remote,
+                local = local,
+                rem_store = remote_store,
+                loc_store = local_store)
+        yield True
+        stepper.next()
+
+        # Save the current state for future sync
+        save_snapshot(local_store.get_snapshot())
+        yield True
+        stepper.next()
+        yield False
+    except StopIteration, e:
+        libepilicious.get_logger().exception('Too many
calls to stepper.next()')
+    except:
+        libepilicious.get_logger().exception('Failed
sync')
+
+
+## Epiphany integration ##
+
+_menu_ui = '''
+<ui>
+  <menubar name="menubar">
+    <menu name="BookmarksMenu"
action="Bookmarks">
+      <separator />
+      <menuitem name="%s"
action="EpiliciousSync" />
+      <separator />
+    </menu>
+  </menubar>
+</ui>
+''' % (_('Epilicious Synchronize'))
+
+_actions = [ \
+        ('EpiliciousSync', None, _('Epilicious
Synchronize'),
+                None, None, _CB_Sync), \
+        ]
+
+# Epiphany extension interface
+def attach_window(window):
+    try:
+        ui_manager = window.get_ui_manager()
+        group = gtk.ActionGroup('My Menu')
+        group.add_actions(_actions, window)
+        ui_manager.insert_action_group(group, 0)
+        ui_id = ui_manager.add_ui_from_string(_menu_ui)
+
+        window._my_menu_data = (group, ui_id)
+    except Exception, e:
+        libepilicious.get_logger().exception('Failed
attach')
+
+def detach_window(window):
+    try:
+        group, ui_id = window._my_menu_data
+        del window._my_menu_data
+
+        ui_manager = window.get_ui_manager()
+        ui_manager.remove_ui(ui_id)
+        ui_manager.remove_action_group(group)
+        ui_manager.ensure_update()
+    except Exception, e:
+        libepilicious.get_logger().exception('Failed
detach')
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/epilicious.sc
hemas.in
epiphany-extensions/extensions/epilicious/epilicious.schemas
.in
---
epiphany-extensions_orig/extensions/epilicious/epilicious.sc
hemas.in	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/epilicious.schemas
.in	2006-07-05 22:39:30.000000000 +0100
 -0,0
+1,60 
+<gconfschemafile>
+  <schemalist>
+
+    <schema>
+     
<key>/schemas/apps/epiphany/extensions/epilicious/user
name</key>
+     
<applyto>/apps/epiphany/extensions/epilicious/username
</applyto>
+      <owner>epiphany</owner>
+      <type>string</type>
+      <default> </default>
+      <locale name="C">
+        <short>del.icio.us username</short>
+        <long>The del.icio.us username that
epilicious will use for
+          synchronizing bookmarks.</long>
+      </locale>
+    </schema>
+
+    <schema>
+     
<key>/schemas/apps/epiphany/extensions/epilicious/pass
word</key>
+     
<applyto>/apps/epiphany/extensions/epilicious/password
</applyto>
+      <owner>epiphany</owner>
+      <type>string</type>
+      <default> </default>
+      <locale name="C">
+        <short>del.icio.us password</short>
+        <long>The del.icio.us password that
epilicious will use for
+          synchronizing bookmarks.</long>
+      </locale>
+    </schema>
+
+    <schema>
+     
<key>/schemas/apps/epiphany/extensions/epilicious/keyw
ord</key>
+     
<applyto>/apps/epiphany/extensions/epilicious/keyword&
lt;/applyto>
+      <owner>epiphany</owner>
+      <type>string</type>
+      <default>EpiliciousShare</default>
+      <locale name="C">
+        <short>Shared topic</short>
+        <long>Bookmarks with this topic will be
synchronized to
+          del.icio.us.</long>
+      </locale>
+    </schema>
+
+    <schema>
+     
<key>/schemas/apps/epiphany/extensions/epilicious/excl
ude</key>
+     
<applyto>/apps/epiphany/extensions/epilicious/exclude&
lt;/applyto>
+      <owner>epiphany</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Exclude topic</short>
+        <long>Controls the function of the topic. If
this is unset then all
+          bookmarks with the topic will be synched with
del.icio.us. If this
+          is set then all bookmarks without the topic will
be synched with
+          del.icio.us.</long>
+      </locale>
+    </schema>
+
+  </schemalist>
+</gconfschemafile>
+<!-- vim: set ft=xml: -->
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/EpiphanyStore
.py
epiphany-extensions/extensions/epilicious/EpiphanyStore.py
---
epiphany-extensions_orig/extensions/epilicious/EpiphanyStore
.py	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/EpiphanyStore.py	2
006-07-05 22:39:30.000000000 +0100
 -0,0
+1,130 
+# Copyright (C) 2005 by Magnus Therning
+
+# This program is free software; you can redistribute it
and/or modify
+# it under the terms of the GNU General Public License as
published by
+# the Free Software Foundation; either version 2 of the
License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be
useful,
+# but WITHOUT ANY WARRANTY; without even the implied
warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public
License
+# along with this program; if not, write to the Free
Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
02111-1307  USA
+
+from sets import Set
+import epiphany
+import libepilicious
+from libepilicious.BaseStore import BaseStore
+
+
+class EpiphanyStore(BaseStore):
+
+    def __init__(self, keyword = None, exclude = False):
+        # TODO: add a check for the priority of the keyword
and set it to 0, at
+        # the moment it's not possible since there's no
method for it
+        # (ephy_node_set_property() isn't in the Python
API)
+        self.__kw = keyword
+        self.__excl = exclude
+
+        self.__bms =
epiphany.ephy_shell_get_default().get_bookmarks()
+        if not self.__bms.find_keyword(self.__kw, False):
+            self.__bms.add_keyword(self.__kw)
+
+    def __get_all_shared_bookmarks(self):
+        # Return all shared bookmarks
+        # Two possibilities:
+        #  1. Take all bookmarks we have and remove the
ones in __kw
+        #  2. All bookmarks are in __kw
+        key = self.__bms.find_keyword(self.__kw, False)
+        if self.__excl:
+            tmp = self.__bms.get_bookmarks().get_children()
+            res = []
+            for b in tmp:
+                if not self.__bms.has_keyword(key, b):
+                    res.append(b)
+            return res
+        else:
+            return key.get_children()
+
+    def __get_all_keywords(self):
+        # Return a list containing all interesting
keywords. All special
+        # keywords (priority==1) and 'All' (id==0) are
uninteresting.
+        kw_node = self.__bms.find_keyword(self.__kw, False)
+        return [kw for kw in
self.__bms.get_keywords().get_children() \
+                if kw.get_id() != 0 and \
+               
kw.get_property_int(epiphany.NODE_KEYWORD_PROP_PRIORITY) !=
1 and \
+                kw != kw_node]
+
+    def get_snapshot(self):
+        '''Calculates a snapshot of the Epiphany
bookmarks.
+
+        note: L{BaseStore.get_snapshot} documents the format
of the return
+        value.
+        '''
+        all_shared_bms = self.__get_all_shared_bookmarks()
+        all_keywords = self.__get_all_keywords()
+
+        res = {}
+        for bm in all_shared_bms:
+           
res[bm.get_property_string(epiphany.NODE_BMK_PROP_LOCATION)]
= \
+                   
[bm.get_property_string(epiphany.NODE_BMK_PROP_TITLE)]
+            keywords = []
+            for key in all_keywords:
+                if self.__bms.has_keyword(key, bm):
+                   
keywords.append(key.get_property_string(epiphany.NODE_KEYWOR
D_PROP_NAME))
+           
res[bm.get_property_string(epiphany.NODE_BMK_PROP_LOCATION)]
.append(keywords)
+
+        return res
+
+    def url_delete(self, url):
+        '''Delete a bookmark.
+
+        :type url: string
+        :param url: The URL of the bookmark
+        '''
+        # This is brute force. It seems the easiest way of
removing a bookmark
+        # is by removing every keyword there is from it.
+        bm = self.__bms.find_bookmark(url)
+        if bm:
+            for kw in
self.__bms.get_keywords().get_children():
+                self.__bms.unset_keyword(kw, bm)
+
+    def url_sync(self, url, desc, to_del, to_add):
+        '''Synchronise a bookmark.
+
+        The bookmark with the given URL is created if
needed and its keywords
+        are adjusted.
+
+        :type url: string
+        :param url: The URL of the bookmark
+        :type desc: string
+        :param desc: One-line description of the bookmark
+        :type to_del: list
+        :param to_del: The keywords (as strings) to delete
from the bookmark
+        :type to_add: list
+        :param to_add: The keywords (as strings) to add to
the bookmark
+        '''
+        bm = self.__bms.find_bookmark(url)
+        sharekw = self.__bms.find_keyword(self.__kw, False)
+        if not bm:
+            bm = self.__bms.add(desc, url)
+
+        if self.__excl:
+            if self.__bms.has_keyword(sharekw, bm):
+                self.__bms.unset_keyword(sharekw, bm)
+        else:
+            if not self.__bms.has_keyword(sharekw, bm):
+                self.__bms.set_keyword(sharekw, bm)
+
+        for k in to_del:
+            kw = self.__bms.find_keyword(k, False)
+            self.__bms.unset_keyword(kw, bm)
+
+        for k in to_add:
+            kw = self.__bms.find_keyword(k, False)
+            if not kw:
+                kw = self.__bms.add_keyword(k)
+            self.__bms.set_keyword(kw, bm)
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/__init__.py
epiphany-extensions/extensions/epilicious/__init__.py
---
epiphany-extensions_orig/extensions/epilicious/__init__.py	1
970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/__init__.py	2006-0
7-06 23:12:07.000000000 +0100
 -0,0
+1,156 
+# Copyright (C) 2005 by Magnus Therning
+
+# This program is free software; you can redistribute it
and/or modify
+# it under the terms of the GNU General Public License as
published by
+# the Free Software Foundation; either version 2 of the
License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be
useful,
+# but WITHOUT ANY WARRANTY; without even the implied
warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public
License
+# along with this program; if not, write to the Free
Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
02111-1307  USA
+
+from sets import Set
+import pickle
+import os, os.path
+import time
+
+# Nice to have for debugging
+logger = None
+def get_logger():
+    '''Create and return a file logger.
+
+    The file used for logging is ~/.epilicious.log.
+
+    return: a file logger
+    '''
+    import logging
+    global logger
+
+    if logger:
+        return logger
+
+    logger = logging.getLogger('epilicious')
+    hdlr =
logging.FileHandler(os.path.join(os.environ['HOME'],
'.epilicious.log'))
+    formatter = logging.Formatter('%(asctime)s
%(levelname)s %(message)s')
+    hdlr.setFormatter(formatter)
+    logger.addHandler(hdlr)
+    logger.setLevel(logging.ERROR)
+    return logger
+
+_file_name = os.path.join(os.environ['HOME'],
'.gnome2', \
+        'epiphany', 'epilicious_old')
+
+def get_old():
+    '''Read the base snapshot.
+
+    The base snapshot is read from
~/.gnome2/epiphany/epilicoius_old.
+
+    return: the base snapshot (an empty dictionary if
none exists)
+    '''
+    res = {}
+    try:
+        f = open(_file_name, 'r')
+        res = pickle.load(f)
+        f.close()
+    except IOError, e:
+        res = {}
+    return res
+
+def save_snapshot(snap):
+    '''Save a snapshot for future synchronisations.
+
+    The base snapshot is written to
~/.gnome2/epiphany/epilicoius_old.
+
+    param snap: The snapshot
+    '''
+    f = open(_file_name, 'w+')
+    pickle.dump(snap, f)
+    f.close()
+
+def remove_urls(old, remote, local, rem_store, loc_store):
+    '''Remove URL from local and remote storage.
+
+    param old: The base snapshot
+    param remote: The remote snapshot
+    param local: The local snapshot
+    param rem_store: The remote storage
(L)
+    param loc_store: The local storage (L)
+    '''
+    o = Set(old.keys())
+    r = Set(remote.keys())
+    l = Set(local.keys())
+    for url in o - l:
+        rem_store.url_delete(url)
+    for url in o - r:
+        loc_store.url_delete(url)
+
+def calculate_pertinent_urls(old, remote, local):
+    '''Calculate the interesting set of URLs.
+
+    The URLs returned are local URLs minus the URLs that
were removed remotely
+    plus the URLs that were added remotely.
+
+    >>> o = {2:'',4:'',6:'',8:'',0:''}
+    >>> r = {2:'',4:'',6:'',8:'',1:''}
+    >>> l = {2:'',3:'',6:'',8:'',0:''}
+    >>> res = calculate_pertinent_urls(o, r, l);
res.sort(); res
+    [1, 2, 3, 6, 8]
+
+    param old: The base snapshot
+    param remote: The remote snapshot
+    param local: The local snapshot
+    return: a list of URLs
+    '''
+    o = Set(old.keys())
+    r = Set(remote.keys())
+    l = Set(local.keys())
+
+    return list((l - (o - r)) | (r - o))
+
+def _get_tags(url, old, rem, loc):
+    '''Get the tags and the description for a URL in all
three locations.
+
+    param url: The URL to get tags for
+    param old: The base snapshot
+    param rem: The remote (Del.icio.us) snapshot
+    param loc: The local (Epiphany) snapshot
+    return: a tuple of description and lists of tags
found in C, C,
+        and C.
+    '''
+    try:
+        otags = Set(old[url][1])
+        desc = old[url][0]
+    except:
+        otags = Set()
+    try:
+        rtags = Set(rem[url][1])
+        desc = rem[url][0]
+    except:
+        rtags = Set()
+    try:
+        ltags = Set(loc[url][1])
+        desc = loc[url][0]
+    except:
+        ltags = Set()
+    return desc, otags, rtags, ltags
+
+def sync_tags_on_urls(purls, old, remote, \
+        local, rem_store, loc_store):
+    '''Synchronise tags on URLs, and add URLs that
don't exist.
+
+    param purls: The list of URLs to synchronise
+    param old: The base snapshot
+    param remote: The remote snapshot
+    param local: The local snapshot
+    param rem_store: The remote storage
(L)
+    param loc_store: The local storage (L)
+    '''
+    for url in purls:
+        desc, otags, rtags, ltags = _get_tags(url, old,
remote, local)
+        rem_store.url_sync(url, desc, otags - ltags, ltags
- otags)
+        loc_store.url_sync(url, desc, otags - rtags, rtags
- otags)
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/Makefile.am
epiphany-extensions/extensions/epilicious/Makefile.am
---
epiphany-extensions_orig/extensions/epilicious/Makefile.am	1
970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/Makefile.am	2006-0
7-17 21:41:32.000000000 +0100
 -0,0
+1,43 
+epiliciousdir = $(EXTENSIONS_DIR)
+epiliciouslibdir = $(EXTENSIONS_DIR)/libepilicious
+epiclicious_PYTHON = \
+		     epilicious.py
+epiliciouslib_PYTHON = \
+		     pydelicious.py \
+		     progress.py \
+		     __init__.py \
+		     EpiphanyStore.py \
+		     DeliciousStore.py \
+		     BaseStore.py
+
+gladedir = $(pkgdatadir)/glade
+glade_DATA = \
+	     progress.glade
+
+schemadir = $(GCONF_SCHEMA_FILE_DIR)
+schema_DATA = epilicious.schemas
+
+extensioninidir = $(extensiondir)
+extensionini_DATA = epilicious.ephy-extension
+
+epilicious.py : epilicious.py.in
+	sed -e "s|%EXTENSION_DIR%|$(extensiondir)|"
$< > $
+
+# ??? INTLTOOL_SCHEMAS_RULE
+EPIPHANY_EXTENSION_RULE
+
+install-data-local: $(schema_DATA)
+if GCONF_SCHEMAS_INSTALL
+	if test -z "$(DESTDIR)" ; then \
+	for p in $^ ; do \
+		GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \
+		$(GCONFTOOL) --makefile-install-rule $$p >&1 >
/dev/null; \
+	done \
+	fi
+endif
+
+CLEANFILES  =
+DISTCLEANFILES = epilicious.py
+
+EXTRA_DIST = $(glade_DATA) $(schema_DATA)
$(extensionini_DATA) \
+	epilicious.py.in epilicious.schemas.in
Binary files
epiphany-extensions_orig/extensions/epilicious/.Makefile.am.
swp and
epiphany-extensions/extensions/epilicious/.Makefile.am.swp
differ
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/progress.glad
e epiphany-extensions/extensions/epilicious/progress.glade
---
epiphany-extensions_orig/extensions/epilicious/progress.glad
e	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/progress.glade	200
6-07-05 22:39:30.000000000 +0100
 -0,0
+1,399 
+<?xml version="1.0"
standalone="no"?> <!--*- mode: xml
-*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gno
me.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog"
id="dlgProgress">
+  <property
name="visible">True</property>
+  <property name="title"
translatable="yes">Synchronizing...</prope
rty>
+  <property
name="type">GTK_WINDOW_TOPLEVEL</property&
gt;
+  <property
name="window_position">GTK_WIN_POS_NONE</p
roperty>
+  <property
name="modal">False</property>
+  <property
name="resizable">False</property>
+  <property
name="destroy_with_parent">False</property
>
+  <property
name="decorated">True</property>
+  <property
name="skip_taskbar_hint">False</property&g
t;
+  <property
name="skip_pager_hint">False</property>
+  <property
name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG&
lt;/property>
+  <property
name="gravity">GDK_GRAVITY_NORTH_WEST</pro
perty>
+  <property
name="focus_on_map">True</property>
+  <property
name="urgency_hint">False</property>
+  <property
name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox"
id="dialog-vbox3">
+      <property
name="visible">True</property>
+      <property
name="homogeneous">False</property>
+      <property
name="spacing">0</property>
+
+      <child
internal-child="action_area">
+	<widget class="GtkHButtonBox"
id="dialog-action_area3">
+	  <property
name="visible">True</property>
+	  <property
name="layout_style">GTK_BUTTONBOX_END</pro
perty>
+
+	  <child>
+	    <widget class="GtkButton"
id="btnClose">
+	      <property
name="visible">True</property>
+	      <property
name="sensitive">False</property>
+	      <property
name="can_default">True</property>
+	      <property
name="can_focus">True</property>
+	      <property
name="label">gtk-close</property>
+	      <property
name="use_stock">True</property>
+	      <property
name="relief">GTK_RELIEF_NORMAL</property&
gt;
+	      <property
name="focus_on_click">True</property>
+	      <property
name="response_id">-7</property>
+	      <signal name="clicked"
handler="on_btnClose_clicked"
last_modification_time="Mon, 03 Jul 2006 22:55:04
GMT"/>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property
name="padding">0</property>
+	  <property
name="expand">False</property>
+	  <property
name="fill">True</property>
+	  <property
name="pack_type">GTK_PACK_END</property>
;
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox"
id="vbox2">
+	  <property
name="visible">True</property>
+	  <property
name="homogeneous">False</property>
+	  <property
name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkLabel"
id="label2">
+	      <property
name="visible">True</property>
+	      <property name="label"
translatable="yes">&lt;b&gt;Synchroni
zing with del.icio.us&lt;/b&gt;</property>
+	      <property
name="use_underline">False</property>
+	      <property
name="use_markup">True</property>
+	      <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+	      <property
name="wrap">False</property>
+	      <property
name="selectable">False</property>
+	      <property
name="xalign">0.5</property>
+	      <property
name="yalign">0.5</property>
+	      <property
name="xpad">3</property>
+	      <property
name="ypad">3</property>
+	      <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+	      <property
name="width_chars">-1</property>
+	      <property
name="single_line_mode">False</property>
;
+	      <property
name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property
name="padding">0</property>
+	      <property
name="expand">False</property>
+	      <property
name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkTable"
id="table1">
+	      <property
name="visible">True</property>
+	      <property
name="n_rows">6</property>
+	      <property
name="n_columns">2</property>
+	      <property
name="homogeneous">False</property>
+	      <property
name="row_spacing">0</property>
+	      <property
name="column_spacing">0</property>
+
+	      <child>
+		<widget class="GtkLabel"
id="label3">
+		  <property
name="visible">True</property>
+		  <property name="label"
translatable="yes">Retrieving previous synch
point</property>
+		  <property
name="use_underline">False</property>
+		  <property
name="use_markup">False</property>
+		  <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+		  <property
name="wrap">False</property>
+		  <property
name="selectable">False</property>
+		  <property
name="xalign">0</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		  <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+		  <property
name="width_chars">-1</property>
+		  <property
name="single_line_mode">False</property>
;
+		  <property
name="angle">0</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">1</property>
+		  <property
name="right_attach">2</property>
+		  <property
name="top_attach">0</property>
+		  <property
name="bottom_attach">1</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel"
id="label4">
+		  <property
name="visible">True</property>
+		  <property name="label"
translatable="yes">Retrieving bookmarks from
del.icio.us</property>
+		  <property
name="use_underline">False</property>
+		  <property
name="use_markup">False</property>
+		  <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+		  <property
name="wrap">False</property>
+		  <property
name="selectable">False</property>
+		  <property
name="xalign">0</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		  <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+		  <property
name="width_chars">-1</property>
+		  <property
name="single_line_mode">False</property>
;
+		  <property
name="angle">0</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">1</property>
+		  <property
name="right_attach">2</property>
+		  <property
name="top_attach">1</property>
+		  <property
name="bottom_attach">2</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel"
id="label5">
+		  <property
name="visible">True</property>
+		  <property name="label"
translatable="yes">Retrieving bookmarks from
epiphany</property>
+		  <property
name="use_underline">False</property>
+		  <property
name="use_markup">False</property>
+		  <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+		  <property
name="wrap">False</property>
+		  <property
name="selectable">False</property>
+		  <property
name="xalign">0</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		  <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+		  <property
name="width_chars">-1</property>
+		  <property
name="single_line_mode">False</property>
;
+		  <property
name="angle">0</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">1</property>
+		  <property
name="right_attach">2</property>
+		  <property
name="top_attach">2</property>
+		  <property
name="bottom_attach">3</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel"
id="label6">
+		  <property
name="visible">True</property>
+		  <property name="label"
translatable="yes">Removing deleted
bookmarks</property>
+		  <property
name="use_underline">False</property>
+		  <property
name="use_markup">False</property>
+		  <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+		  <property
name="wrap">False</property>
+		  <property
name="selectable">False</property>
+		  <property
name="xalign">0</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		  <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+		  <property
name="width_chars">-1</property>
+		  <property
name="single_line_mode">False</property>
;
+		  <property
name="angle">0</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">1</property>
+		  <property
name="right_attach">2</property>
+		  <property
name="top_attach">3</property>
+		  <property
name="bottom_attach">4</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel"
id="label7">
+		  <property
name="visible">True</property>
+		  <property name="label"
translatable="yes">Adding new bookmarks and
synching topics</property>
+		  <property
name="use_underline">False</property>
+		  <property
name="use_markup">False</property>
+		  <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+		  <property
name="wrap">False</property>
+		  <property
name="selectable">False</property>
+		  <property
name="xalign">0</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		  <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+		  <property
name="width_chars">-1</property>
+		  <property
name="single_line_mode">False</property>
;
+		  <property
name="angle">0</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">1</property>
+		  <property
name="right_attach">2</property>
+		  <property
name="top_attach">4</property>
+		  <property
name="bottom_attach">5</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel"
id="label8">
+		  <property
name="visible">True</property>
+		  <property name="label"
translatable="yes">Saving new synch
point</property>
+		  <property
name="use_underline">False</property>
+		  <property
name="use_markup">False</property>
+		  <property
name="justify">GTK_JUSTIFY_LEFT</property&
gt;
+		  <property
name="wrap">False</property>
+		  <property
name="selectable">False</property>
+		  <property
name="xalign">0</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		  <property
name="ellipsize">PANGO_ELLIPSIZE_NONE</pro
perty>
+		  <property
name="width_chars">-1</property>
+		  <property
name="single_line_mode">False</property>
;
+		  <property
name="angle">0</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">1</property>
+		  <property
name="right_attach">2</property>
+		  <property
name="top_attach">5</property>
+		  <property
name="bottom_attach">6</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options"></property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkImage"
id="imgStep1">
+		  <property
name="icon_size">4</property>
+		  <property
name="icon_name">gtk-execute</property>
+		  <property
name="xalign">0.5</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">0</property>
+		  <property
name="right_attach">1</property>
+		  <property
name="top_attach">0</property>
+		  <property
name="bottom_attach">1</property>
+		  <property
name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkImage"
id="imgStep2">
+		  <property
name="icon_size">4</property>
+		  <property
name="icon_name">gtk-execute</property>
+		  <property
name="xalign">0.5</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">0</property>
+		  <property
name="right_attach">1</property>
+		  <property
name="top_attach">1</property>
+		  <property
name="bottom_attach">2</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkImage"
id="imgStep3">
+		  <property
name="icon_size">4</property>
+		  <property
name="icon_name">gtk-execute</property>
+		  <property
name="xalign">0.5</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">0</property>
+		  <property
name="right_attach">1</property>
+		  <property
name="top_attach">2</property>
+		  <property
name="bottom_attach">3</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkImage"
id="imgStep4">
+		  <property
name="icon_size">4</property>
+		  <property
name="icon_name">gtk-execute</property>
+		  <property
name="xalign">0.5</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">0</property>
+		  <property
name="right_attach">1</property>
+		  <property
name="top_attach">3</property>
+		  <property
name="bottom_attach">4</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkImage"
id="imgStep5">
+		  <property
name="icon_size">4</property>
+		  <property
name="icon_name">gtk-execute</property>
+		  <property
name="xalign">0.5</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">0</property>
+		  <property
name="right_attach">1</property>
+		  <property
name="top_attach">4</property>
+		  <property
name="bottom_attach">5</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options">fill</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkImage"
id="imgStep6">
+		  <property
name="icon_size">1</property>
+		  <property
name="icon_name">gtk-execute</property>
+		  <property
name="xalign">0.5</property>
+		  <property
name="yalign">0.5</property>
+		  <property
name="xpad">3</property>
+		  <property
name="ypad">3</property>
+		</widget>
+		<packing>
+		  <property
name="left_attach">0</property>
+		  <property
name="right_attach">1</property>
+		  <property
name="top_attach">5</property>
+		  <property
name="bottom_attach">6</property>
+		  <property
name="x_options">fill</property>
+		  <property
name="y_options">fill</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property
name="padding">0</property>
+	      <property
name="expand">True</property>
+	      <property
name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <placeholder/>
+	  </child>
+	</widget>
+	<packing>
+	  <property
name="padding">0</property>
+	  <property
name="expand">True</property>
+	  <property
name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/progress.py
epiphany-extensions/extensions/epilicious/progress.py
---
epiphany-extensions_orig/extensions/epilicious/progress.py	1
970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/progress.py	2006-0
7-05 22:39:30.000000000 +0100
 -0,0
+1,53 
+# Copyright (C) 2006 by Magnus Therning
+
+# This program is free software; you can redistribute it
and/or modify
+# it under the terms of the GNU General Public License as
published by
+# the Free Software Foundation; either version 2 of the
License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be
useful,
+# but WITHOUT ANY WARRANTY; without even the implied
warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public
License
+# along with this program; if not, write to the Free
Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
02111-1307  USA
+
+import libepilicious
+import gtk, gtk.glade
+import os.path
+
+class ProgressBar:
+
+    def __init__(self):
+        self.gui =
gtk.glade.XML(os.path.join(libepilicious.LOCATION, \
+                'libepilicious', 'progress.glade'))
+        self.gui.signal_autoconnect(self)
+
+        self.dlg = self.gui.get_widget('dlgProgress')
+
+    def show(self):
+        self.dlg.show()
+
+    def hide(self):
+        self.dlg.hide()
+
+    def step(self):
+        images = ['imgStep1', 'imgStep2', 'imgStep3',
'imgStep4', 'imgStep5', 'imgStep6',]
+        for i in images:
+            img = self.gui.get_widget(i)
+            img.show()
+            while gtk.events_pending():
+                gtk.main_iteration()
+            yield True
+            img.set_from_icon_name('gtk-apply',
gtk.ICON_SIZE_MENU)
+       
self.gui.get_widget('btnClose').set_sensitive(True)
+        while gtk.events_pending():
+            gtk.main_iteration()
+        yield True
+
+    ### signal handlers
+    def on_btnClose_clicked(self, widget):
+        self.hide()
+        del(self)
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/pydelicious-l
icense.txt
epiphany-extensions/extensions/epilicious/pydelicious-licens
e.txt
---
epiphany-extensions_orig/extensions/epilicious/pydelicious-l
icense.txt	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/pydelicious-licens
e.txt	2006-07-05 22:39:30.000000000 +0100
 -0,0
+1,10 
+Copyright (c) 2006, Frank Timmermann
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
+
+    * Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
+    * Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
+    * Neither the name Frank Timmermann, developer team of
pydelicious nor the names of its contributors may be used to
endorse or promote products derived from this software
without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/pydelicious.p
y epiphany-extensions/extensions/epilicious/pydelicious.py
---
epiphany-extensions_orig/extensions/epilicious/pydelicious.p
y	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/pydelicious.py	200
6-07-06 23:11:30.000000000 +0100
 -0,0
+1,507 
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+"""Library to access del.icio.us via
python.
+
+This module helps you to ...
+
+get()
+a = apiNew()
+a.tags.get
+a.tags.rename
+a.posts.get
+"""
+
+
+##
+# License: pydelicious is released under the bsd license.
+# See 'license.txt' for more informations.
+#
+
+##
+# TODO fuer pydelicious.py
+#  * die dokumentation aller docs muss noch geschehen
+#  * dokumentation
+#  * welche lizense benutze ich
+#  * lizense einbinden und auch via setup.py verteilen
+#  * readme auch schreiben und via setup.py verteilen
+#  * mehr tests
+#  * auch die funktion von setup.py testen?
+#  * auch auf anderen systemen testen (linux -> uni)
+#  * automatisch releases bauen lassen, richtig benennen
und in das
+#    richtige verzeichnis verschieben.
+#  * was können die anderen librarys denn noch so? (ruby,
java, perl, etc)
+#  * was wollen die, die es benutzen?
+#  * wofür könnte ich es benutzen?
+#  * entschlacken?
+#
+# realy?
+#  * date object von del.icio.us muss ich noch
implementieren
+#
+# done!
+#  * stimmt das so? muss eher noch täg str2utf8
konvertieren
+#    >>> pydelicious.getrss(tag="täg")
+#    url: http://del.icio.us/rss/t
ag/täg
+#  * requester muss eine sekunde warten
+#  * __init__.py gibt die funktionen weiter
+#  * html parser funktioniert noch nicht, gar nicht
+#  * alte funktionen fehlen, get_posts_by_url, etc.
+#  * post funktion erstellen, die auch die fehlenden
attribs addiert.
+#  * die api muss ich noch weiter machen
+#  * requester muss die 503er abfangen
+#  * rss parser muss auf viele möglichkeiten angepasst
werden
+
+
+import re, md5, httplib
+import urllib, urllib2, time
+# import datetime,
+import StringIO
+
+# this is new                                              
            #
+# this relays on an external library, will probably be kept
            #
+import sys
+import os
+
+# !!! zweiter Versuch funzt nur auf linux rechner in der
uni -
+# ersteres auch auf win32, doof.
+# [MT] this is how it works on Debian Sid!
+from elementtree.ElementTree import parse
+import feedparser
+
+# Taken from FeedParser.py
+# timeoutsocket allows feedparser to time out rather than
hang forever on ultra-slow servers.
+# Python 2.3 now has this functionality available in the
standard socket library, so under
+# 2.3 you don't need to install anything.  But you
probably should anyway, because the socket
+# module is buggy and timeoutsocket is better.
+try:
+    import timeoutsocket # http
://www.timo-tasi.org/python/timeoutsocket.py
+    timeoutsocket.setDefaultSocketTimeout(20)
+except ImportError:
+    import socket
+    if hasattr(socket, 'setdefaulttimeout'):
socket.setdefaulttimeout(20)
+
+# some basic settings
+VERSION = '0.3.2'
+AUTHOR = 'Frank Timmermann'
+AUTHOR_EMAIL = 'regenkind_at_gmx_dot_de'
+PROJECT_URL = 'http://del
iciouspython.python-hosting.com/'
+# das folgende ist mit python 2.2.3 nicht erlaubt. :(
+CONTACT = '%(URL)s or %(email)s'%dict(URL = PROJECT_URL,
email = AUTHOR_EMAIL)
+DESCRIPTION = '''pydelicious.py allows you to access the
web service of del.icio.us via it's API through
python.'''
+LONG_DESCRIPTION = '''the goal is to design an easy to
use and fully functional python interface to
+del.icio.us. '''
+DWS_HOSTNAME = 'http://del.icio.us/'
+DWS_HOSTNAME_RSS = 'http://del.icio.us/rss/'
+DWS_REALM = 'del.icio.us API'
+DWS_API = 'http://del.icio.us/api/'
+USER_AGENT = 'pydelicious.py/%(version)s %(contact)s' %
dict(version = VERSION, contact = CONTACT)
+
+LAST_CALL = 0
+DEBUG = 0
+
+delicious_date_pattern  =
re.compile("[1,2][0-9]-[0-2][0-9]-[0-3][0-9]T[0-2][
0-9]:[0-5][0-9]:[0-5][0-9]Z")
+
+
+# Helper Funktion
+def str2uni(s):
+    # type(in) str or unicode
+    # type(out) unicode
+    return ("".join([unichr(ord(i)) for i in
s]))
+
+
+def str2utf8(s):
+    # type(in) str or unicode
+    # type(out) str
+    return
("".join([unichr(ord(i)).encode("utf-8&qu
ot;) for i in s]))
+
+
+def str2quote(s):
+    return
urllib.quote_plus("".join([unichr(ord(i)).encode
("utf-8") for i in s]))
+
+
+def dict0(d):
+    # {'a':'a', 'b':'', 'c': 'c'} => {'a':
'a', 'c': 'c'}
+    dd = dict()
+    for i in d:
+            if d[i] != "": dd[i] = d[i]
+    return dd
+
+
+class Waiter:
+
+    def __init__(self, t = 0, sleeptime = 1):
+        self.t=t
+	self.sleeptime = sleeptime
+	self.waitcommand = 0
+	self.waited = 0
+
+    def wait(self, t=None):
+        self.waitcommand += 1
+        if t == None: t = time.time()
+        if DEBUG: print "Waiter:",t-self.t
+        if t-self.t<self.sleeptime:
+	    time.sleep(self.sleeptime-t+self.t)
+	    self.waited += 1
+	self.t = time.time()
+
+
+Waiter = Waiter()
+
+
+# Fehlerbehandlung
+comment='''Fehlerbehandlungszeug,
+kopiert aus delicious025.py, damit der reqester
funktioniert. brauche ich das alles so?
+'''
+class DeliciousException(Exception):
+    '''Std. Error Function'''
+    pass
+
+
+class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler):
+
+    '''Handles HTTP Error, currently only 503
+    Behandelt die HTTP Fehler, behandelt nur 503
Fehler'''
+    def http_error_503(self, req, fp, code, msg, headers):
+        raise urllib2.HTTPError(req, code,
throttled_message, headers, fp)
+
+
+# ! #
+class post(dict):
+
+    # a post object contains of this:
+    #  href
+    #  description
+    #  hash
+    #  dt
+    #  tags
+    #  extended
+    #  user
+    #  count
+    def __init__(self, href = "", description =
"", hash = "", time =
"", tag = "", extended =
"", user = "", count =
"",
+                 tags = "", url =
"", dt = ""): # tags or tag?
+        self["href"] = href
+        if url != "": self["href"]
= url
+        self["description"] = description
+        self["hash"] = hash
+        self["dt"] = dt
+        if time != "": self["dt"] =
time
+        self["tags"] = tags
+        if tag != "":  self["tags"]
= tag     # tag or tags? # !! tags
+        self["extended"] = extended
+        self["user"] = user
+        self["count"] = count
+
+    def __getattr__(self, name):
+        try: return self[name]
+        except: object.__getattribute__(self, name)
+
+
+class posts(list):
+
+    def __init__(self, *args):
+        for i in args: self.append(i)
+
+    def __getattr__(self, attr):
+        try: return [p[attr] for p in self]
+        except: object.__getattribute__(self, attr)
+
+
+# handle all the RSS stuff
+comment='''rss sollte nun wieder funktionieren, aber
diese try, except scheisse ist so nicht schoen
+
+rss wird unterschiedlich zusammengesetzt. ich kann noch
keinen einheitlichen zusammenhang
+zwischen daten (url, desc, ext, usw) und dem feed erkennen.
warum können die das nicht einheitlich machen?
+'''
+def _readRSS(tag = "", popular = 0, user =
"", url = ''):
+    '''handle a rss request to del.icio.us'''
+    tag = str2quote(tag)
+    user = str2quote(user)
+    if url != '':
+        # http://del.icio.us/rss/url/efbfb246d886393d4806555143
4dab54
+        url = DWS_HOSTNAME_RSS +
'''url/%s'''%md5.new(url).hexdigest()
+    elif user != '' and tag != '':
+        url = DWS_HOSTNAME_RSS +
'''%(user)s/%(tag)s'''%dict(user=user, tag=tag)
+    elif user != '' and tag == '':
+        # http://del.icio.us/rss/d
elpy
+        url = DWS_HOSTNAME_RSS + '''%s'''%user
+    elif popular == 0 and tag == '':
+        url = DWS_HOSTNAME_RSS
+    elif popular == 0 and tag != '':
+        # http://del.icio.us/r
ss/tag/apple
+        # http://del.icio.us/
rss/tag/web2.0
+        url = DWS_HOSTNAME_RSS + "tag/%s"%tag
+    elif popular == 1 and tag == '':
+        url = DWS_HOSTNAME_RSS + '''popular/'''
+    elif popular == 1 and tag != '':
+        url = DWS_HOSTNAME_RSS + '''popular/%s'''%tag
+    rss = _request(url, useUrlAsIs = 1)
+    rss = feedparser.parse(rss)
+    # print rss
+#     for e in rss.entries: print e;print
+    l = posts()
+    for e in rss.entries:
+        if e.has_key("links") and
e["links"]!=[] and
e["links"][0].has_key("href"):
+            url =
e["links"][0]["href"]
+        elif e.has_key("link"):
+            url = e["link"]
+        elif e.has_key("id"):
+            url = e["id"]
+        else:
+            url = ""
+        if e.has_key("title"):
+            description = e['title']
+        elif e.has_key("title_detail") and
e["title_detail"].has_key("title"):
+            description =
e["title_detail"]['value']
+        else:
+            description = ''
+        try: tags = e['categories'][0][1]
+        except:
+            try: tags = e["category"]
+            except: tags = ""
+        if e.has_key("modified"):
+            dt = e['modified']
+        else:
+            dt = ""
+        if e.has_key("summary"):
+            extended = e['summary']
+        elif e.has_key("summary_detail"):
+            e['summary_detail']["value"]
+        else:
+            extended = ""
+        if e.has_key("author"):
+            user = e['author']
+        else:
+            user = ""
+# time = dt ist weist auf ein problem hin
+# die benennung der variablen ist nicht einheitlich
+#  api senden und
+#  xml bekommen sind zwei verschiedene schuhe :(
+        l.append(post(url = url, description = description,
tags = tags, dt = dt, extended = extended, user = user))
+    return l
+
+
+# HTML Parser, deprecated
+comment='''paring html gibt mehr infos als die parsing
von html, aber
+ * eine aenderung des html's macht die funktion kaput
+ * fuer rss gibt es einen guten funktionierenden parser,
muss ich denn trotzdem html wirklich parsen
+ * get_posts_by_url funktioniert nur mit dem parsen von
html ==> stimmt das noch?
+'''
+def _readHTML(tar = "", popular = 0, user =
""):
+    pass
+    # construct url
+    # get data
+    # data 2 posts
+    # return posts
+
+
+# Requester
+comment='''stimmt der requester schon mit den vorgaben
von del.icio.us ueberein, nein ...
+ * sollte ich die daten von del.icio.us auf nur text
untersuchen?
+
+Done
+* eine sekunde pause zwischen jedem request klappt.
+* ich fange noch nicht den 503 code ab, klappt aber in der
alten version
+   muss ich auch nicht, denn das läuft über die Exception
DefaultErrorHandler.
+ '''
+def _request(url, params = '', useUrlAsIs = 0, user =
'', passwd = '', ):
+    if DEBUG: httplib.HTTPConnection.debuglevel = 1
+    # Please wait AT LEAST ONE SECOND between queries, or
you are likely to get automatically throttled.
+    # If you are releasing a library to access the API, you
MUST do this.
+    # Everything is in the Caller, don't use this at home!
+    Waiter.wait()
+    # params come as a dict => dict0 => urlencode
+    params = urllib.urlencode(dict0(params))
+    authinfo = urllib2.HTTPBasicAuthHandler()
+    authinfo.add_password(DWS_REALM, DWS_HOSTNAME, user,
passwd)
+    opener = urllib2.build_opener(authinfo,
DefaultErrorHandler())
+    request = urllib2.Request(DWS_API + url + params)
+    if useUrlAsIs: request = urllib2.Request(url)
+    request.add_header('User-Agent', USER_AGENT)
+    if DEBUG: print "url:",
request.get_full_url()
+    try:
+        o = opener.open(request)
+        return o.read()
+    except DefaultErrorHandler:
+        if DEBUG: return opener.open(request).read()
+        return ""
+
+# XML Parser
+comment='''ist vollständig,
+ * test fehlt nocht
+'''
+def _handleXML(data):
+    if DEBUG: print data
+    x = parse(StringIO.StringIO(data))
+    mode = x.getroot().tag
+    if mode == 'tags':
+        l = [dict(count = t.attrib["count"],
tag = t.attrib["tag"]) for t in
x.findall("tag")]
+    elif mode == "result":
+        if (x.getroot().attrib.has_key("code")
and x.getroot().attrib["code"] == 'done') or
x.getroot().text in ['done', 'ok']:
+            l = True
+        else :
+            l = False
+    elif mode == 'update':
+        l = x.getroot().attrib['time']
+    elif mode == 'dates':
+        l = [dict(count = t.attrib["count"],
date = t.attrib["date"]) for t in
x.findall("date")]
+    elif mode == 'bundles':
+        l = [dict(name = t.attrib["name"], tags
= t.attrib["tags"]) for t in
x.findall("bundle")]
+    elif mode == 'posts':
+        l = posts()
+        for t in x.findall("post"):
+            href, description, hash = '', '', ''
+            tag,time, extended      = '', '', ''
+            count = ''
+            if t.attrib.has_key("href"): href =
t.attrib["href"]
+            if t.attrib.has_key("description"):
description = t.attrib["description"]
+            if t.attrib.has_key("hash"): hash =
t.attrib["hash"]
+            if t.attrib.has_key("tag"): tag =
t.attrib["tag"]
+            if t.attrib.has_key("time"): time =
t.attrib["time"]
+            if t.attrib.has_key("extended"):
extended = t.attrib["extended"]
+            if t.attrib.has_key("count"): count
= t.attrib["count"]
+            p = post(href=href,
description=description,hash=hash,
+                     tag=tag, time=time, extended=extended,
+                     count=count)
+            l.append(p)
+    return l
+
+'''brauche ich das?'''
+def _validatePost(post): pass
+
+
+# del.icio.us api
+comment='''herzstueck
+
+Done
+ * was passiert mit nicht ascii daten in der verschickung
als parameter?
+ * noch sehr unvollstaendig
+ * aufbau der api ist so, glaube ich, nicht mehr sinnvoll,
vielleicht doch lieber nach dem alten schema, also
+        api.tags_get(...) anstatt api.tags.get(...)
+'''
+class _DeliciousAPI:
+# def _request(url, params = '', useUrlAsIs = 0, user =
'', passwd = '', ):
+
+    def __init__(self, user, passwd):
+        self.user = user
+        self.passwd = passwd
+
+    def _main(self, url, params = ''):
+        x = _request(url = url, params = params, user =
self.user, passwd = self.passwd)
+	self.xml = x
+        return _handleXML(x)
+
+    def tags_get(self):
+        return self._main(url = "tags/get?")
+
+    def tags_rename(self, old, new):
+        return self._main("tags/rename?",
(dict(old = str2utf8(old),
+                                                new =
str2utf8(new))))
+
+    def posts_update(self):
+        return self._main("posts/update")
+
+    def posts_dates(self, tag = ""):
+        return self._main("posts/dates?",
(dict(tag = str2utf8(tag))))
+
+    def posts_get(self, tag="",
dt="", url=""):
+        return self._main("posts/get?",
(dict(tag = str2utf8(tag),
+                                              dt =
str2utf8(dt),
+                                              url =
str2utf8(url))))
+
+    def posts_recent(self, tag="",
count=""):
+        return self._main("posts/recent?",
(dict(tag = str2utf8(tag),
+                                                 count =
str2utf8(count))))
+
+    def posts_all(self, tag=""):
+        return self._main("posts/all?",
(dict(tag = str2utf8(tag))))
+
+    def posts_add(self, url, description="",
extended="", tags="",
dt="", replace="no"):
+        '''add an post to del.icio.us
+
+        url - the url of the page you like to add
+        description - a description of the page, often the
title of the page
+        extended (opt) - an extended description, could be
some kind of comment
+        tags - tags to sort your posts
+        dt (opt) - current date in format ...., if no date
is given, the current
+                   date will be used
+        '''
+        return self._main("posts/add?",
(dict(url = str2utf8(url),
+                                              description =
str2utf8(description),
+                                              extended =
str2utf8(extended),
+                                              tags =
str2utf8(tags),
+                                              dt =
str2utf8(dt),
+                                              replace =
str2utf8(replace))))
+
+    def posts_delete(self, url):
+        return self._main("posts/delete?",
(dict(url = str2utf8(url))))
+
+    def bundles_all(self):
+        return self._main(url =
"tags/bundles/all")
+
+    def bundles_set(self, bundle, tags):
+        return self._main(url =
"tags/bundles/set?",
+                          params = (dict(bundle =
str2utf8(bundle),
+                                         tags =
str2utf8(tags))))
+
+    def bundles_delete(self, bundle):
+        return self._main(url =
"tags/bundles/delete?",
+                          params = (dict(bundle =
str2utf8(bundle))))
+
+comment='''kick this, brauche das doch nicht, s.o,'''
+def apiNew(user, passwd):
+    return _DeliciousAPI(user=user, passwd= passwd)
+
+comment=''' holt die Daten via rss, entspricht _readRSS
+Done
+* basiert auch auf rss, html und api
+* braucht deshalb noch, bis es voll funzt. '''
+def getrss(tag = "", popular = 0, url = '',
user = ""):
+    '''get posts from del.icio.us via parsing Rss or
Html
+
+    tag (opt) sort by tag
+    popular (opt) look for the popular stuff
+    user (opt) get the posts by a user, this striks popular
+    url (opt) get the posts by url '''
+    return _readRSS(tag=tag, popular=popular, user=user,
url=url)
+
+
+comment = '''api funktionen, damit die funktionen aus
0.2.5 bestehen bleiben'''
+def add(user, passwd, url, description, tags =
"", extended = "", dt =
"", replace="no"):
+    return apiNew(user=user,
passwd=passwd).posts_add(url=url, description=description,
extended=extended, tags=tags, dt=dt, replace=replace)
+
+
+def get(user, passwd, tag="",
dt="",  count = 0):
+    posts = apiNew(user=user,
passwd=passwd).posts_get(tag=tag,dt=dt)
+    if count != 0: posts = posts[0:count]
+    return posts
+
+
+def get_all(user, passwd, tag = ""):
+    return apiNew(user=user,
passwd=passwd).posts_all(tag=tag)
+
+
+def delete(user, passwd, url):
+    return apiNew(user=user,
passwd=passwd).posts_delete(url=url)
+
+
+def rename_tag(user, passwd, oldtag, newtag):
+    return apiNew(user=user,
passwd=passwd).tags_rename(old=oldtag, new=newtag)
+
+
+def get_tags(user, passwd):
+    return apiNew(user=user, passwd=passwd).tags_get()
+
+
+def get_userposts(user):
+    return getrss(user = user)
+
+
+def get_tagposts(tag):
+    return getrss(tag = tag)
+
+
+def get_urlposts(url):
+    return getrss(url = url)
+
+
+def get_popular(tag = ""):
+    return getrss(tag = tag, popular = 1)
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/THOUGHTS
epiphany-extensions/extensions/epilicious/THOUGHTS
---
epiphany-extensions_orig/extensions/epilicious/THOUGHTS	1970
-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/THOUGHTS	2006-07-0
6 23:12:26.000000000 +0100
 -0,0
+1,63 
+Either I mark the ones that shouldn't be synched (e.g.
with keyword 'Skip')
+and all other bookmarks are synched.
+ Only one del.icio.us account can be used
+
+I mark all the bookmarks that should be synched.
+ More marking needed. Ability to have several del.icio.us
accounts if each
+ account is associated with a specific keyword. When
updating the del.icio.us
+ keywords have to be treated differently than other
keywords.
+
+To update two sets when knowing the origin:
+
+  my_new = (my - (old - your)) | (your - old)
+  your_new = (your - (old - my)) | (my - old)
+
+ After this my_new == your_new.
+
+ Algortihm A:
+
+  URL with keywords are represented as
+   URL,kw1,kw2,...,kwn
+
+  old = Set of URL with keywords from last sync time
+  new = Set of URL with keywords retrieved from remote site
+  mine = Set of URL with keywords locally
+
+  to_add = new - old
+  to_remove = old - new
+
+  new_mine = (mine + to_add) - to_remove
+
+  1. Remove all bookmarks marked for share
+  2. Add bookmarks in new_mine
+  3. (Optional) remove empty tags
+
+  Do step 1-3 for remote site as well.
+
+The problem with descriptions.
+ Currently all bookmarks dealings are based on the URL. A
change in description
+ on either side won't be reflected on the other side after
a synch. Only if
+ there is a change in the tags will a change in description
be propagated as
+ well, but then there is no way of knowing which site's
description to use. At
+ the moment, if there's a change in description epilicious
choses to keep the
+ local description (i.e. if locations A and B are synched
with Delicious, and
+ A's and B's descriptions for a specific site differ then
the name in Delicious
+ will bounce with every synch that changes tags for the
site).
+
+Excluding instead of including bookmarks in synch
+ Two ways to do it:
+
+  1. All bookmarks in _share_kw_ are shared, except if they
are in the
+     _block_kw_. If _share_kw_ isn't set, then all
bookmarks are considered.
+  2. There is only one keyword, then there's a switch
which controls whether
+     it is used to mark shared or non-shared bookmarks.
+
+ The former is the way proposed in the patch by Tom
Coleman. The drawback here
+ is that setting the values must be explained to the user
(at least one must
+ be set).
+
+ The latter is simpler, but it restricts the user somewhat.
I don't think that
+ restriction is relevant though (what becomes impossible is
to have something
+ marked both for inclusion and exclusion).
+
+vim: set ft=text:
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/TODO
epiphany-extensions/extensions/epilicious/TODO
---
epiphany-extensions_orig/extensions/epilicious/TODO	1970-01-
01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/TODO	2006-07-05
22:39:30.000000000 +0100
 -0,0
+1,44 
+- epilicious items
+  (added Fri May 19 17:14:45 2006, incomplete, priority
medium)
+
+  - remove the cache in EpiphanyStore
+    (added Fri May 19 17:16:09 2006, completed on Sun May
21 23:57:55 2006, priority medium)
+
+  - use boolean to determine whether keyword is sharing or
blocking
+    (added Fri May 19 17:19:01 2006, completed on Sun May
21 23:58:24 2006, priority medium)
+
+  - BUG: marking of bookmarks added to epiphany is wrong,
they all get marked
+    with the keyword, that should depend on whether the
keyword is used for
+    inclusion or exclusion
+    (added Mon Jun  5 09:08:16 2006, completed on Mon Jun 
5 22:20:55 2006, priority medium)
+
+  - change priority on keyword so it doesn't show in
bookmark menu
+    (added Fri May 19 17:17:40 2006, incomplete, priority
medium)
+
+  - pop up dialogue if username/password is missing in
gconf
+    (added Fri May 19 17:20:35 2006, incomplete, priority
medium)
+
+  - configuration dialogue
+    (added Fri May 19 17:21:00 2006, incomplete, priority
medium)
+
+  - ability to run in a mode where exceptions are caught
and displayed
+    (added Fri May 19 17:21:32 2006, incomplete, priority
medium)
+
+  - use epiphany's password store for password
+    (added Fri May 19 17:21:50 2006, incomplete, priority
medium)
+
+  - deal better with descriptions of bookmarks
+    (added Fri May 19 17:22:11 2006, incomplete, priority
medium)
+
+  - add support for scuttle
+    (added Fri May 19 17:22:41 2006, incomplete, priority
medium)
+
+    - support for private bookmarks
+      (added Fri May 19 17:22:56 2006, incomplete, priority
medium)
+
+    - changing URL to server
+      (added Fri May 19 17:23:10 2006, incomplete, priority
medium)
+
+  - BUG: non-US characters are not treated right
+    (added Mon Jun  5 09:10:04 2006, incomplete, priority
medium)
+
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/utils/delback
up epiphany-extensions/extensions/epilicious/utils/delbackup
---
epiphany-extensions_orig/extensions/epilicious/utils/delback
up	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/utils/delbackup	20
06-07-06 23:11:49.000000000 +0100
 -0,0
+1,62 
+#! /usr/bin/env python
+
+
+import sys
+from optparse import OptionParser, make_option
+import cPickle as pickle
+from ProgressBar import ProgressBar
+try:
+    import pydelicious as delicious
+except:
+    print >> sys.stderr, "Make sure that
'libepilicious' is in $PYTHONPATH"
+    sys.exit(1)
+
+
+_OPTIONS = [ \
+        make_option('-u', '--username', \
+            help='del.icio.us username [REQUIRED]'), \
+        make_option('-p', '--password', \
+            help='del.icio.us password [REQUIRED]'), \
+        make_option('-o', '--filename', \
+            help='filename to save bookmarks to'), \
+        make_option('-l', '--noclear',
action='store_true', default=False, \
+            help='do not clear del.icio.us after backing
up'), \
+        ]
+
+
+def _clear_delicious(d):
+    dbms = d.posts_all()
+    pb = ProgressBar('Deleting from del.icio.us')
+    num_items = len(dbms)
+    i = 1
+    for b in dbms:
+        pb.update(i, num_items)
+        d.posts_delete(b['href'])
+        i += 1
+    pb.clear()
+
+
+def main():
+    parser = OptionParser(option_list=_OPTIONS)
+    options, args = parser.parse_args()
+    if not options.username or not options.password:
+        print >> sys.stderr, 'Both username and
password are required!\n'
+        sys.exit(1)
+    outfile = sys.stdout
+    if options.filename:
+        try:
+            outfile = file(options.filename, 'w+')
+        except IOError, e:
+            print >> sys.stderr, 'Could not open
file.'
+            sys.exit(1)
+
+    d = delicious.apiNew(options.username,
options.password)
+    dbms = d.posts_all()
+    pickle.dump(dbms, outfile)
+
+    if not options.noclear:
+        _clear_delicious(d)
+
+
+if __name__ == '__main__':
+    main()
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/utils/delrest
ore
epiphany-extensions/extensions/epilicious/utils/delrestore
---
epiphany-extensions_orig/extensions/epilicious/utils/delrest
ore	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/utils/delrestore	2
006-07-05 22:39:30.000000000 +0100
 -0,0
+1,75 
+#! /usr/bin/env python
+
+from optparse import OptionParser, make_option
+import cPickle as pickle
+import sys
+from ProgressBar import ProgressBar
+try:
+    import pydelicious as delicious
+except:
+    print >> sys.stderr, "Make sure that
'libepilicious' is in $PYTHONPATH"
+    sys.exit(1)
+
+
+_OPTIONS = [ \
+        make_option('-u', '--username', \
+            help='del.icio.us username [REQUIRED]'), \
+        make_option('-p', '--password', \
+            help='del.icio.us password [REQUIRED]'), \
+        make_option('-f', '--filename', \
+            help='filename to read bookmarks from'), \
+        make_option('-l', '--noclear',
action='store_true', default=False, \
+            help='do not clear del.icio.us before
restoring'), \
+        ]
+
+
+def _clear_delicious(d):
+    dbms = d.posts_all()
+    pb = ProgressBar('Deleting from del.icio.us')
+    num_items = len(dbms)
+    i = 1
+    for b in dbms:
+        pb.update(i, num_items)
+        d.posts_delete(b['href'])
+        i += 1
+    pb.clear()
+
+
+def _restore_delicious(d, bms):
+    pb = ProgressBar('Restoring to del.icio.us')
+    num_items = len(bms)
+    i = 1
+    for b in bms:
+        pb.update(i, num_items)
+        d.posts_add(b['href'], \
+                description=b['description'], \
+                extended=b['extended'], \
+                tags=b['tags'], \
+                dt=b['dt'])
+        i += 1
+    pb.clear()
+
+
+def main():
+    parser = OptionParser(option_list=_OPTIONS)
+    options, args = parser.parse_args()
+    if not options.username or not options.password:
+        print >> sys.stderr, 'Both username and
password are required!\n'
+        sys.exit(1)
+    infile = sys.stdout
+    if options.filename:
+        try:
+            infile = file(options.filename, 'r')
+        except IOError, e:
+            print >> sys.stderr, 'Could not open
file.'
+            sys.exit(1)
+
+    lbms = pickle.load(infile)
+    d = delicious.apiNew(options.username,
options.password)
+    if not options.noclear:
+        _clear_delicious(d)
+    _restore_delicious(d, lbms)
+
+
+if __name__ == '__main__':
+    main()
diff -NuPr
epiphany-extensions_orig/extensions/epilicious/utils/Progres
sBar.py
epiphany-extensions/extensions/epilicious/utils/ProgressBar.
py
---
epiphany-extensions_orig/extensions/epilicious/utils/Progres
sBar.py	1970-01-01 01:00:00.000000000 +0100
+++
epiphany-extensions/extensions/epilicious/utils/ProgressBar.
py	2006-07-06 23:10:13.000000000 +0100
 -0,0
+1,192 
+# Created by Edward Loper, version 1.2
+# Licensed under GPL (as well as released to the public
domain)
+# Shamelessly copied from
+# http://aspn.activestate.com/ASPN/Cookbook/Python/R
ecipe/475116
+
+import sys, re
+
+class _TerminalController:
+    """
+    A class that can be used to portably generate formatted
output to
+    a terminal.
+
+    `_TerminalController` defines a set of instance
variables whose
+    values are initialized to the control sequence
necessary to
+    perform a given action.  These can be simply included
in normal
+    output to the terminal:
+
+        >>> term = _TerminalController()
+        >>> print 'This is
'+term.GREEN+'green'+term.NORMAL
+
+    Alternatively, the `render()` method can used, which
replaces
+    '$' with the string required to perform
'action':
+
+        >>> term = _TerminalController()
+        >>> print term.render('This is
$green$')
+
+    If the terminal doesn't support a given action, then
the value of
+    the corresponding instance variable will be set to
''.  As a
+    result, the above code will still work on terminals
that do not
+    support color, except that their output will not be
colored.
+    Also, this means that you can test whether the terminal
supports a
+    given action by simply testing the truth value of the
+    corresponding instance variable:
+
+        >>> term = _TerminalController()
+        >>> if term.CLEAR_SCREEN:
+        ...     print 'This terminal supports clearning
the screen.'
+
+    Finally, if the width and height of the terminal are
known, then
+    they will be stored in the `COLS` and `LINES`
attributes.
+    """
+    # Cursor movement:
+    BOL = ''             #: Move the cursor to the
beginning of the line
+    UP = ''              #: Move the cursor up one line
+    DOWN = ''            #: Move the cursor down one line
+    LEFT = ''            #: Move the cursor left one char
+    RIGHT = ''           #: Move the cursor right one
char
+
+    # Deletion:
+    CLEAR_SCREEN = ''    #: Clear the screen and move to
home position
+    CLEAR_EOL = ''       #: Clear to the end of the line.
+    CLEAR_BOL = ''       #: Clear to the beginning of the
line.
+    CLEAR_EOS = ''       #: Clear to the end of the
screen
+
+    # Output modes:
+    BOLD = ''            #: Turn on bold mode
+    BLINK = ''           #: Turn on blink mode
+    DIM = ''             #: Turn on half-bright mode
+    REVERSE = ''         #: Turn on reverse-video mode
+    NORMAL = ''          #: Turn off all modes
+
+    # Cursor display:
+    HIDE_CURSOR = ''     #: Make the cursor invisible
+    SHOW_CURSOR = ''     #: Make the cursor visible
+
+    # Terminal size:
+    COLS = None          #: Width of the terminal (None for
unknown)
+    LINES = None         #: Height of the terminal (None
for unknown)
+
+    # Foreground colors:
+    BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW =
WHITE = ''
+
+    # Background colors:
+    BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
+    BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
+
+    _STRING_CAPABILITIES = """
+    BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
+    CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1
CLEAR_EOS=ed BOLD=bold
+    BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul
NORMAL=sgr0
+    HIDE_CURSOR=cinvis
SHOW_CURSOR=cnorm""".split()
+    _COLORS = """BLACK BLUE GREEN CYAN
RED MAGENTA YELLOW WHITE""".split()
+    _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE
MAGENTA CYAN WHITE".split()
+
+    def __init__(self, term_stream=sys.stdout):
+        """
+        Create a `_TerminalController` and initialize its
attributes
+        with appropriate values for the current terminal.
+        `term_stream` is the stream that will be used for
terminal
+        output; if this stream is not a tty, then the
terminal is
+        assumed to be a dumb terminal (i.e., have no
capabilities).
+        """
+        # Curses isn't available on all platforms
+        try: import curses
+        except: return
+
+        # If the stream isn't a tty, then assume it has no
capabilities.
+        if not term_stream.isatty(): return
+
+        # Check the terminal type.  If we fail, then assume
that the
+        # terminal has no capabilities.
+        try: curses.setupterm()
+        except: return
+
+        # Look up numeric capabilities.
+        self.COLS = curses.tigetnum('cols')
+        self.LINES = curses.tigetnum('lines')
+
+        # Look up string capabilities.
+        for capability in self._STRING_CAPABILITIES:
+            (attrib, cap_name) = capability.split('=')
+            setattr(self, attrib, self._tigetstr(cap_name)
or '')
+
+        # Colors
+        set_fg = self._tigetstr('setf')
+        if set_fg:
+            for i,color in zip(range(len(self._COLORS)),
self._COLORS):
+                setattr(self, color, curses.tparm(set_fg,
i) or '')
+        set_fg_ansi = self._tigetstr('setaf')
+        if set_fg_ansi:
+            for i,color in
zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
+                setattr(self, color,
curses.tparm(set_fg_ansi, i) or '')
+        set_bg = self._tigetstr('setb')
+        if set_bg:
+            for i,color in zip(range(len(self._COLORS)),
self._COLORS):
+                setattr(self, 'BG_'+color,
curses.tparm(set_bg, i) or '')
+        set_bg_ansi = self._tigetstr('setab')
+        if set_bg_ansi:
+            for i,color in
zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
+                setattr(self, 'BG_'+color,
curses.tparm(set_bg_ansi, i) or '')
+
+    def _tigetstr(self, cap_name):
+        # String capabilities can include
"delays" of the form "$<2>".
+        # For any modern terminal, we should be able to
just ignore
+        # these, so strip them out.
+        import curses
+        cap = curses.tigetstr(cap_name) or ''
+        return re.sub(r'\$<\d+>[/*]?', '', cap)
+
+    def render(self, template):
+        """
+        Replace each $-substitutions in the given template
string with
+        the corresponding terminal control string (if it's
defined) or
+        '' (if it's not).
+        """
+        return re.sub(r'\$\$|\${\w+}',
self._render_sub, template)
+
+    def _render_sub(self, match):
+        s = match.group()
+        if s == '$$': return s
+        else: return getattr(self, s[2:-1])
+
+
+class ProgressBar:
+    """
+    A 2-line progress bar, which looks like::
+
+                                Header
+        20% [===========----------------------------------]
+
+    The progress bar adjusts to the width of the terminal.
+    """
+    HEADER = '$%s$\n\n'
+    BAR = '$[%s%s] %d/%d$\n'
+
+    def __init__(self, header, term_stream=sys.stdout):
+        self.term = _TerminalController(term_stream)
+        if not (self.term.CLEAR_EOL and self.term.UP and
self.term.BOL):
+            raise ValueError("Terminal isn't capable
enough -- you "
+                             "should use a simpler
progress dispaly.")
+        self.width = self.term.COLS or 75
+        self.bar = self.term.render(self.BAR)
+        self.header = self.term.render(self.HEADER %
header.center(self.width))
+        self.cleared = 1 #: true if we haven't drawn the
bar yet.
+        self.update(0, 1)
+
+    def update(self, done, total):
+        if self.cleared:
+            sys.stdout.write(self.header)
+            self.cleared = 0
+        percent = float(done) / float(total)
+        n = int((self.width-10)*percent)
+        sys.stdout.write(
+            self.term.BOL + self.term.UP +
self.term.CLEAR_EOL +
+            (self.bar % ('='*n, '-'*(self.width-10-n),
done, total)))
+
+    def clear(self):
+        if not self.cleared:
+            sys.stdout.write(self.term.BOL +
self.term.CLEAR_EOL +
+                             self.term.UP +
self.term.CLEAR_EOL +
+                             self.term.UP +
self.term.CLEAR_EOL)
+            self.cleared = 1
diff -NuPr epiphany-extensions_orig/po/POTFILES.in
epiphany-extensions/po/POTFILES.in
--- epiphany-extensions_orig/po/POTFILES.in	2006-04-24
11:27:02.000000000 +0100
+++ epiphany-extensions/po/POTFILES.in	2006-07-17
21:39:11.000000000 +0100
 -8,6
+8,9 
 extensions/actions/ephy-actions-extension-properties-dialog
.c
 extensions/auto-reload/ephy-auto-reload-extension.c
 extensions/certificates/ephy-certificates-extension.c
+extensions/epilicious/epilicious.py.in
+extensions/epilicious/epilicious.schemas.in
+extensions/epilicious/progress.glade
 extensions/error-viewer/ephy-error-viewer-extension.c
 extensions/error-viewer/error-viewer.glade
 extensions/error-viewer/link-checker.c
 -15,9
+18,9 
 extensions/error-viewer/mozilla/ErrorViewerURICheckerObserv
er.cpp
 extensions/error-viewer/opensp/validate.cpp
 extensions/error-viewer/sgml-validator.c
+extensions/extensions-manager-ui/ephy-extensions-manager-ui
-extension.c
 extensions/extensions-manager-ui/extensions-manager-ui.c
 extensions/extensions-manager-ui/extensions-manager-ui.glad
e
-extensions/extensions-manager-ui/ephy-extensions-manager-ui
-extension.c
 extensions/greasemonkey/ephy-greasemonkey-extension.c
 extensions/java-console/ephy-java-console-extension.c
 extensions/page-info/ephy-page-info-extension.c

-- 
Magnus Therning                             (OpenPGP:
0xAB4DFBA4)
magnustherning.org             Jabber: magnus.therninggmail.com
http://therning.org/magnus


Software is not manufactured, it is something you write and
publish.
Keep Europe free from software patents, we do not want
censorship
by patent law on written works.

Good powers of observation are frequently called
"cynicism" by those
that don't have them.
_______________________________________________
epiphany-list mailing list
epiphany-listgnome.org

http://mail.gnome.org/mailman/listinfo/epiphany-list
[1-3]

about | contact  Other archives ( Real Estate discussion Medical topics )