Traversing the user/group repositories to build a directory view

Previous: Main program ] [ Top:  ] [ Next: Presenting a directory view in HTML ]

So. The first step is to build a directory view. I'm thinking of the directory view as something along these lines:
<dirview root="" cur_user="michael" state="folder:+">
<folder name="folder1" label="My Folder">
<object class="document" object="ddriou2" label="A document in My Folder"/>
</folder>
<object class="document" object="3324ffq" label="My Document for Stuff"/>
<object class="document" object="333333s" label="Another Document"/>
</dirview>
Let's talk a little about what I expect all that to mean. First, the elements "dirview", "folder", and "object" are pretty obvious. An "object" is a leaf in the directory tree, a document. (Of any type; classes in the wftk permission system are simply strings with no semantics at all, actually.) Folders and objects have names and labels; the name is the machine-readable identifier, while the label appears in the actual display.

The "root" of the directory view is used to tell the constructor where to start the view; by default it is the current user, so that the directory displayed will be those objects owned/accessible by the current user. However, there are plenty of reasons to start directory views elsewhere, so that's what the root is for.

The "state" of the directory view determines which of the folders is shown in an open state and which in a closed state. Open folders must of course be traversed. The convention I'm setting (tentatively) is pretty simple; if the state is blank, then nothing's open. If it's a "+", then everything's open. If a folder is named, that folder is open, and if two folders are open they're separated by commas. Finally, to open subfolders, I use semicolons, so if the folder named "f1" is open in main folder "f2", then my state is "f2:f1". If all subfolders of f2 where to be open, the state would be "f2:+". We'll see if that's adequate, but it feels right.

All these incoming attributes of the dirview element are basically parameters to this construction function.

So. All that explained, let's write some code.
 
See Scanning a user or group
void dirview_create (XML * dv, XML * cur_user)
{
   XML * elem;
   XML * direlem;

   dirview_scan (dv, cur_user, xml_attrval (dv, "state"));
}


Scanning a user or group
We start the process by scanning the root user or group; the dirview_scan function will call itself recursively if the state indicates that it should.
 
int  folder_append (XML * view, XML * elem);
void dirview_scan (XML * dv, XML * entity, const char *state)
{
   XML * elem;
   XML * direlem;
   XML * group;

   elem = xml_firstelem (entity);
   while (elem) {
      if (!strcmp (elem->name, "object")) {
         direlem = xml_create ("object");
         if (*xml_attrval (elem, "label")) {
            xml_set (direlem, "label", xml_attrval (elem, "label"));
         } else {
            xml_set (direlem, "label", xml_attrval (elem, "object"));
         }
         xml_append (dv, direlem);
      } else if (!strcmp (elem->name, "group") || !strcmp (elem->name, "group-include")) {
         direlem = xml_create ("folder");
         xml_set (direlem, "name", xml_attrval (elem, "name"));
         if (*xml_attrval (elem, "label")) {
            xml_set (direlem, "label", xml_attrval (elem, "label"));
         } else {
            xml_set (direlem, "label", xml_attrval (elem, "name"));
         }
         if (folder_append (dv, direlem)) {
            group = group_get (xml_attrval (elem, "name"));
            dirview_scan (direlem, group, state);
            xml_free (group);
         }
      }
      elem = xml_nextelem (elem);
   }
}
So now let's look at the folder_append function, which simply wraps xml_append with some code to avoid duplicates and cycles in the directory tree (our "directory" structure is significantly more dangerous than a real file structure in that regard.)

If the folder is indeed appended, the function returns 1; otherwise, the folder is freed and the function returns 0.
 
int folder_append (XML * view, XML * elem)
{
   XML * check;
   const char * name = xml_attrval (elem, "name");

   check = view;
   while (check) {
      if (!strcmp (xml_attrval (check, "name"), name)) {
         xml_free (elem);
         return (0);
      }
      check = check->parent;
   }

   check = xml_firstelem (view);
   while (check) {
      if (!strcmp (check->name, "folder")) {
         if (!strcmp (xml_attrval (check, "name"), name)) {
            xml_free (elem);
            return (0);
         }
      }
      check = xml_nextelem (check);
   }

   xml_append (view, elem);
   return (1);
}
Previous: Main program ] [ Top:  ] [ Next: Presenting a directory view in HTML ]


This code and documentation are released under the terms of the GNU license. They are copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license.