PubSub Browser
The x60br library, which I mentioned previously, was created
primarily for
this
thing. It is a kind of PubSub browser, showing the nodes and their
relationships.
Working on my Liferea/Akregator-style reader
has come to a stop for the moment, since I need some nodes to actually
test with, so I thought I’d copy the browser idea for managing nodes
(which I can then subscribe to, publish to, read things from, etc.). A
couple of days of hacking have given me this:
Looks pretty basic, but during its creation I’ve given my
library a rather hacky Object Oriented interface. Anyway, the
“browsing” part is working. The Jabber account to use is currently
hard-coded into the application as “test1@localhost”, but this suffices
for my testing purposes.
To use it the address of a
PubSub-capable server is given (in this case “pubsub.localhost”)
and this server is queried for nodes and added to the tree. When the
replies come in they are given to the program as a list of Nodes. These
nodes are checked (rather inefficiently) against the Nodes already
displayed in the tree to see if a) they are the same Node (in which case
they are disgarded) and b) if any listed Node is their parent. Then the
remaining Nodes are added to the tree.
As you can see in the
image I’ve queried the server pubsub.localhost and it contains 2
collection nodes, “/pubsub” and “/home”, which each
contain a leaf node, “/pubsub/nodes” and “/home/localhost” respectively.
UPDATE: Just to clarify a little, PubSub nodes can be anything, they
don’t have to follow a directory-style slash system, these nodes are
simply there by default in ejabberd to give some kind of structure to
the nodes. This is handled by a plugin to the server, which can be
replaced, but since the ejabberd developers know a hell of a lot more
about this than me I’m going with their implementation :D This means
that “/home/localhost” could actually be in “/nodes” rather than
“/home”, or it could be in both or neither. I can make a node “heh” and
put it anywhere. Nodes, as far as I know, need to be unique to the
server, so I can’t have two nodes “blog” on the same server, regardless
of which collections I want them in. However, nodes can also have a
name, so I can make a node “/home/localhost/chris/blog” and give it a
name “blog”, and another node “/home/localhost/joanne/blog” and call
that “blog” too, and another called “/home/localhost/harriet” called
“blog”, etc. these can all be in the same collections if I want, or not.
This flexibility is good, but it does mean that I’m going to see what
other people are doing before working out a structure to use (for
example, should a blog be a collection, with each post being a
collection containing a leaf node with the post and a leaf node with the
comments? Maybe tags should be collections which contain leaf nodes of
applicable posts, etc.)
The main drawback in the current
browser application is that the known nodes are queried one after
another, meaning that leaf nodes in multiple collections won’t work yet
(since it would get as far as the first collection and notice that this
node is already in the list and disgard it before the second collection
is reached).
Next on the agenda is adding icons to see which
rows are leaves, which are collections and which are servers. Then I’ll
stick on some add and remove buttons, and possibly look into drag ‘n’
drop reordering.
UPDATE: Seems adding the icons is a bit of a
bother. Since the correct GTK way of making tree views and lists is VERY
confusing and involved (a TreeView is needed which shows a TreeModel
which contains Columns which contain CellRenderers which draw whatever
GObject type is put in the cell of the column of the model of the view.
Very flexible but also very over the top!) I am using the ObjectTree
from Kiwi, which is
built on PyGTK’s tree system, but is much easier to use. The problem is,
to draw an icon alongside the text I need to give the Column two
CellRenderers. ObjectTree guesses which CellRenderer to use for me based
on what I give it and just gets on with it, however this means I spent a
while trying to reimplement some of these guessing methods in a subclass
of ObjectTree.
Thankfully, however, in the latest version of
Kiwi this functionality has been added by simply making two columns (one
for the icon, one for the text) and giving the first Column to the
second when it is created. This version of Kiwi isn’t in Ubuntu Hardy,
however, so I got the one from Intrepid and installed it (it’s written
in Python, thus shouldn’t care about libc differences and such). I’ve
since upgraded my whole system to Intrepid, but that’s a different
matter.
Anyway, turns out that there are problems with this
way of doing things too, although I’m not sure if it’s due to a bug in
Kiwi as columns-in-columns is so new, or a fault of mine (which I can’t
really check through Google since this functionality hasn’t been around
long enough to let other people make the same mistakes as me). I can get
the icons to appear, but I can’t get them to refresh when I tell the
ObjectTree to refresh. Since the nodes’ type is discovered through a
request to the server and I am using the ObjectTree itself as my data
model, the nodes must be added to the tree before their type is known.
When the reply comes in with the node’s type then I can update the icon
to reflect this type, however the updated icon is never used even after
a refresh. This means I need to know the type when I add it to the
ObjectTree, which would result in more headaches since I’d either get
some nodes unable to find their parent (since the parent hasn’t been
added yet as it is still awaiting its type), or I would have to make a
completely separate storage model and then make sure that the contents
of that storage model are kept in sync with the ObjectTree. I really
hope it’s a bug in Kiwi, since then a) the easy way is the
correct way, just that a bug is stopping it working, and b) I probably
won’t have to fix the bug :P
* The objects I’ve needed to
make, besides the original PubSubClient, are Node and Server. Server
only contains a string called name which stores the address (this is
mainly so it can be added to the tree and for type comparison purposes
at the moment). Node can contain name (it’s node), jid (its JabberID, or
at least the JabberID of the server it lives on), server (the Server it
is on), type which is either “leaf” or “collection” and parent, which is
a Server for top-level nodes or a Node if it is in a collection. Nodes
also have some functions, but these functions must be passed a
PubSubClient through which to send messages. Since the PubSubClient
contains methods for everything defined in XEP-0060 all the Node’s
functions do is call the appropriate method of the PubSubClient handed
to it.
This is a pretty poor level of Object Orientation, but
it can be smarteded up over time and at least applications no longer
need to deal with XML (which wasn’t that different from the XML stanzas
coming in from xmpppy in the first place!)
** Due to having
no domain name and having a router in between it and the Internets, my
local Jabber server (ejabberd) can’t talk to outside servers at the
moment. I’m treating this as a blessing at the moment though, since it
means my tests can only screw up my server (which can easily be
reinstalled since it contains nothing of importance). I can log in to
warbo@jabber.org, chriswarbo@gmail.com or pha06cw@sheffield.ac.uk if I
want to access servers over the real Internets.
*** In PubSub
there are 2 kinds of node. Leaf nodes can have things published to them
(blogs, listened to music tracks, etc.) but they cannot contain any
other nodes. Collection nodes are the opposite, they can contain other
nodes (collection or leaf) but cannot have things published to them.
Leaf nodes can be thought of as files whilst collection nodes can be
thought of as folders, with the main difference being that leaf nodes
can be in any number of collections at once.
UPDATE: PS: I
may add this to Gitorious or something when it works, since there’s no
reason to deny people its use just because my library isn’t finished
yet. The browser can live with a copy of the library which works for the
tasks required and thus doesn’t need updates, whilst all of the breakage
and rewriting and feature development can go on in the main version of
the library.