Monday, 11 August 2008

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.

No comments: