API Guide

Table of Contents

Introduction

The Snap2 API is the programmer visible interface to the Snap2 system. It provides all of the functionality of the web-based tool, and then some. In fact, the web-based tool uses the API to do most of its work. It simply displays results and processes user input. The current API is a clean rewrite of the old Snap2 API, which had a number of inconsistencies and strange behavior. The new API is more robust, more orthogonal and easier to use than the older one. As an example of that, switching to the new API resulted in hundreds of lines of code being deleted from the web-based tool.

The Snap2 API is integrated with PageRender and other core systems so that it takes very little to use it and most things Just Work. Of course, it still probably has bugs, and I'd like to be made aware of them, so feel free to let me know if something isn't working as documented.

You will want to make use of the API documentation on this site.

There is also internal documentation for Snap2, available in the Common internal documentation site. There are some public functions that are listed there that are not listed in the above documentation. They will be indicated as "For internal use only". Please respect that. I reserve the right to remove or change any function that is not in the public documentation at any time. And in any case, it's probably hard to use the "For internal use only" functions correctly.

Getting Started

The first and only required step to enable Snap2 in your project is to add the line:

SAutoLoad::addModule('snap2');

to your project's standardIncludes.php5. There's no need to create a DBI or connect, or any of that. It's all handled for you automagically. Now, you may want to connect to the database as the admin user, or connect to the dev database instead of the prod database. In that case, you will want to add one or both of the following lines somewhere in your standardIncludes.php5. The only rule is that they be mentioned before you do anything with the Snap2 API. As soon as you start using the API, Snap2 will connect with the values it already has. There's no way to disconnect and reconnect right now (as it is unnecessarily wasteful and, well, unnecessary).

# this line will connect you as the admin user
SConfig::setDefault('snap2.user', 'admin');
# this line will connect to the dev server instead of the prod one
SConfig::setDefault('snap2.host', 'dev');

You will need to connect as the admin user if you plan to modify any information in Snap2. You will want to connect to the dev server mostly if you are doing development on Snap2 itself and don't want to break the prod database. Otherwise, you probably want to always connect to the prod database.

Snap2 makes use of an internal cache to avoid unnecessary database queries. If you are only using Snap2 read-only (not making any changes to content or metadata in Snap2), then you can disable the cache for a slight performance boost. To do so, add the following line to your standardIncludes.php5, probably near the lines mentioned above (or where they would be):

SConfig::setDefault('snap2.useCache', false);

Now you are ready to start using the API...

First Steps

The first things we will look at are retrieving objects and retrieving lists. I will not go over all of the details since those are covered in the above-mentioned API documentation (or will be eventually ;)).

You can retrieve objects by path or by ID. If you have a path, you have several options. If you already know the type of object (directory, resource or version), then you can call the static lookup() method on that class. If you don't, you can use the generic one provided by the Snap2 class, or the one provided by SnapFile if you know that it is a directory or resource, but not a version. Here are some examples:

# look up a directory by path (note the trailing slash: all directory paths end in a slash)
$dir = SnapDirectory::lookup('//test/foo/');

# look up a resource (note the LACK of trailing slash)
$res = SnapResource::lookup('//test/myresource');

# look up a version (note the '@' followed by version number)
$v = SnapVersion::lookup('//test/myresource\@5');

# we don't know what this path points to, so we'll use the generic lookup() method
$obj = Snap2::lookup($myPath);
# this will print out 'Directory', 'Resource' or 'Version'
print "My type is: " . $obj->getType() . "<br />";

# we know we have a directory or resource, so we can use this less generic lookup() method
$file = SnapFile::lookup($myPath);
# this will print out 'Directory' or 'Resource'
print "My type is: " . $file->getType() . "<br />";

Note that if the path is invalid (that is, not well-formed), or it indicates an object that does not exist, or there is an internal error (unlikely!), the lookup() methods will return null. Make sure to check for this condition so that you don't get a fatal PHP error when you try to call a function on the object.

If you know the ID of an object, you can use a different set of methods, all called retrieve(). Since an ID, in itself, does not indicate what kind of object it refers to like paths do, there is no generic Snap2::retrieve() method. Here are some examples of using retrieve:

# retrieves directory with ID 37
$dir = SnapDirectory::retrieve(37);

# retrieves resource with ID 37 (same ID, but different object!)
$res = SnapResource::retrieve(37);

# retrieves version with some ID passed in to us from somewhere
$v = SnapVersion::retrieve($versionId);

Like the lookup() methods, these methods return null if the ID is invalid (i.e., not a positive integer), or the ID does not refer to an object that exists, or there is an internal error. So always check for null. Also take note that this replaces the traditional SModel style populate system, where you would do something like the following:

$obj = new MyModelClass(42);

While PHP does not forbid you doing that with directories, resources and versions, you still shouldn't do it. If caching is enabled, it can lead to inconsistency and perhaps data corruptions. Use the retrieve() and lookup() methods. Other methods will also return objects, such as the listing methods.

Now we want to get information from these objects. Directories and resources share mostly the same set of fields, with resources have a few extra. Below is a list of fields used by both directories and resources:

id
unique identifier of directory or resource
name
descriptive name of directory or resource
description
longer description of directory or resource (usually blank)
created
time at which directory or resource was created
modified
last time at which metadata was altered (any of these attributes are metadata, but nothing else)
canonParentId
the directory ID of the canonical parent of this directory or resource
refCount
number of links associated with this directory or resource (i.e., the number of parents)
parents
list of directory objects, one for each parent of this directory or resource
ordinals
list of ordinals, one for each parent of this directory or resource
shortNames
list of short names held by this directory or resource; each parent has its own short name associated with it
canonicalPath
the primary path to this directory or resource
canonicalParent
the primary parent (object) of this directory or resource
permission
SnapPermission object that provides an interface to manipulate and view permissions on this directory or resource
The following are only for directories:
maxDirOrdinal
highest ordinal of all subdirectories of this directory
maxResOrdinal
highest ordinal of all subresources of this directory
The following are only for resources:
contentType
resource type (e.g., HTML, XML, etc. -- there are constants in SnapResource for these)
approvalDate
time at which most recent live version was approved for live
validDate
date at which this resource is considered "valid" (what that means is user-defined)
liveVersionId
version ID of the version which is currently live for this resource, or null if there is no live version
devVersionId
version ID of the version which is currently dev for this resource, or null if there is no dev version
liveVersion
SnapVersion object for live version, if there is one
devVersion
SnapVersion object for dev version, if there is one
activeVersion
SnapVersion object for whichever of dev or live is considered active on the current server (live on prod, dev on newdev)
maxOrdinal
highest ordinal of all the versions belonging to this resource

To retrieve any of these fields, you use a getter method. The name of the getter method for any field is formed by taking 'get', capitalizing the field and concatenating the two. For example, to get the name, you call getName(), to get the liveVersionId, you call getLiveVersionId(). There are generally no corresponding setters for these fields, but there are API methods that change them. They require permissions and also that you are connected to the database as admin. We'll talk about those methods later.

The most likely use for the Snap2 API outside of the admin tool is to retrieve and display content. To do this, you must grab the resource whose content you want, and then you can do one of several things. You can load the resource directly into your PageRender module ($prm) and PageRender will take care of the details. Or you can grab the active version from the resource and then grab the content module from the version, and then grab the HTML from the content module. You can also stop after grabbing the version and pass that to PageRender, but you might as well just give it the resource and save yourself some trouble. Below are some examples of both ways of loading content:

# load our resource from %Snap2
$res = SnapResource::lookup('//test/mycontent');

# load the resource into our PRM and let PageRender generate
#  the HTML automatically the content will show up in the same
#  place on the page as if we had just added plain HTML here
$prm->load($res, array());

# ...OR... we can be complicated
$v = $res->getActiveVersion();
$cm = $v->getContentModule();
$html = $cm->getHTML();
$prm->load($html, array('inFormat' => 'html'));

You might prefer the latter course if you plan on retrieving other information about the content from the content module. This would be useful if the content type of the resource were, say, an image. Perhaps you want to find out the dimensions of the image. The only way to do that is to ask the content module. But first, what is a content module? It's an object associated with each version that manages the content contained by that version. Since some content might refer to external files, such as image files, the content module concept was invented to handle the details of each different content type.

Below is an example of gathering extra information from the content module:

# retrieve our image resource
$res = SnapResource::lookup('//test/myimage');

# get the version and content module
$v = $res->getActiveVersion();
$cm = $v->getContentModule();

# grab the width and height from the content module
$width = $cm->getWidth();
$height = $cm->getHeight();

# load in some HTML to the PageRender module that describes the image
$prm->load("Image width: $width and height: $height<br />", array('inFormat' => 'html'));

# ...and still load the resource into the PRM, which will automatically generate the HTML and display it
$prm->load($res, array());

That's enough to get you started with using the Snap2 API. The following sections will discuss listing, modification and permissions.

Listing Content

SnapDirectory and SnapResource have a few methods to retrieve a list of objects contained in the directory or resource. For SnapDirectory, you use the SnapDirectory::listContents() method, or its counterpart SnapDirectory::countContents() to simply get a count of how many items listContents() would return. For SnapResource, there are two methods: SnapResource::listVersions() and SnapResource::listRelevantVersions(). The former simply lists all versions, but the latter will only list versions that are defunct and some defunct versions that happen to be listed as previous versions for non-defunct versions. The Admin Tool uses this method to produce the version listing when you view a resource. You will note that usually you don't see a defunct version in that listing, but you will if that defunct version was the previous version for one of the non-defunct versions. As for SnapDirectory, there are also countXxx() versions for both of the listing functions.

The listing functions have three optional arguments. The first is a special array that can be used to trim down the result set. It is used to generate the WHERE clause in the database query that retrieves the results. The second argument allows you to limit the number of results returned and is used to build the LIMIT clause of the query. The third argument lets you sort the results and is used to build the ORDER BY clause of the query. Before going into detail about these arguments, let's look at some basic examples of listing first:

$dir = SnapDirectory::lookup('//test/');

# list all directories and resources in //test/
$contents = $dir->listContents();

# list only directories in //test/
$contents = $dir->listContents(array('fileType' => 'Directory'));

# list the first 25 files in //test/
$contents = $dir->listContents("", 25);

# list everything, but sort by short name
$contents = $dir->listContents("", "", array('shortName'));

# list resources 25 to 50 sorting by short name
$contents = $dir->listContents(array('fileType' => 'Resource'), array(25, 50), array('shortName'));

$res = SnapDirectory::lookup('//test/myres');

# list all versions in //test/myres
$versions = $res->listVersions();

# list all Pending versions in //test/myres
$versions = $res->listVersions(array('status' => SnapVersion::STATUS_PENDING));

# list all versions created by user 1312, sorting by status
$versions = $res->listVersions(array('author' => 1312), "", array('status'));

# list versions 10 to 20 containing the word "the" in their content, sorting by status
$versions = $res->listVersions(array('LIKE BINARY', 'content' => '%the%'), array(10, 20), array('status'));

So now let's explain how the three parameters work. I will start with the last parameter and move towards the left. The last parameter specifies how to sort the results. There are two ways you can use this parameter. In the first way, you simply provide a list of columns to sort by. When you sort by multiple columns, MySQL seems to have the sort priority from right to left and only sorts by additional columns if the first sort left areas of results unsorted (because their values were all the same for that column). A second way to use the parameter is to make the array associative. The key will be the name of the column to sort and the value will be either 'ASC' or 'DESC', to get ascending or descending sort, respectively.

The second parameter has three ways to use it. The first is to simply provide a number, which is the number of results you want returned. The second way is to put that number as the sole element of an array. It functions the same as the first way. The third way is to provide an array with two numbers. The first number is the offset into the resultset that you want to start retrieving results at and the second number is how many results you want to retrieve. For example, if there were 300 results in total, you could ask to retrieve 25 of those results, starting at result 100: array(100, 25).

And finally, the first parameter. The first parameter is very complex, but I will provide a very distilled explanation which should be satisfactory for most use. You simply provide an array in which each key is the name of a column and each value is the value you want to compare that column to. So if you want to only retrieve results where the file type is a directory and the canonical parent ID is 37, then you would use: array('fileType' => 'Directory', 'canonParentId' => 37). If you want to check multiple values for an item, you put the values in an array: array('fileType' => array('Directory', 'Resource')).

Adding Content to Snap2

Before we discuss the details of adding content to Snap2, it is import to describe briefly the life-cycle of content in the Snap2 system. The first step is to, of course, create a resource to contain the content. Once you have a resource, you will create one or more versions. Each version holds a revision of the content. The content in each version is independent of the content in other versions. You can continue to edit/modify content in a version as long as it is in the initial state (called 'In Progress'). When you are finished editing content, you submit the version and the approve it for dev or for live. Once it has been approved for dev or live, it will be active on one of those two sites. If it has only been approved for dev, it will only show up on the dev site. If it has been approved for live, and there is no dev version, then it will show up on both dev and live. If it has been approved live, but there is an existing dev version, then it will only show up on the live site. There can only be at most one dev and one live version per resource.

Alright, so now we need to recreate that with the API. Fortunately, it really is as simple as translating each step into a single API call (for the most part). The following code segment walks you through creating an HTML resource, adding the content and then submitting and approving it.

# get directory in which we will create our resource
$dir = SnapDirectory::lookup('//test/');

# create the resource
#  first argument is the short name
#  second argument is the resource type (a constant from the SnapResource class)
#  third argument is the descriptive name (optional; defaults to be the same as the short name)
#  fourth argument is the full description (optional)
$res = $dir->createResource('myhtml', SnapResource::TYPE_HTML, "My HTML Resource");

# create a version for the resource
$v = $res->createVersion();

# create a content module to manage the content
#  there is a separate class for each type of content; in this case we are using
#  the class for HTML content
# also note that we must pass the version to the content module
# do NOT pass a second argument or things won't work...
$cm = new SnapContentHtml($v);

# create some content
$html = <<<END_HTML
&lt;p&gt;This is some HTML&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List Item One&lt;/li&gt;
&lt;li&gt;List Item Two&lt;/li&gt;
&lt;/ul&gt;
END_HTML;

# load our content into the content module
$cm->setHTML($html);

# save the content in the version
$v->update($cm);

# submit the version
$v->submit();

# approve the version for dev
$v->approveForDev();

# and now we're done!

One big big thing to remember is to check for errors. There is no error checking above because it would clutter the example, but in the real world, all of the modification functions could fail. In general, all of the functions on the version return false on failure, true on success. Same with functions on the content module. If resource creation fails, the directory object would return null. And if the directory lookup failed, it would return null as well. If the update() method should fail, you need to call $cm->delete() to free up any external resources the content module might be using. This is important for media types, but not really necessary for HTML or XML content.

Modifying and Manipulating Files and Versions

To be written...

Permissions

The permission system is now somewhat complex. Read the Conceptual Overview for more information on how permissions work in an abstract sense. This section will simply describe how to use the API to find out about permissions or make changes to them.

Querying Permissions

Every file (directory or resource) has a permission object associated with it. You can retrieve this object by calling getPermission() on the file. You now have an instance of a SnapPermission object. Now, there are several options for querying permissions. You can query directly for permissions, or simply ask whether some operation can be done, regardless of what permissions it requires. For the former, you will want to use one of the following methods:

If you only want to find out whether a certain operation is allowed to be performed, you can call one of the "may" methods in SnapPermission. There is one of these for each "can" method in one of the Snap2 file or version classes, and there is a "can" method for each method that results in a change of state for that, or another, object. The difference between the "may" and "can" methods is that the "may" methods only check whether the user has permission, they do not check whether the operation is feasible for other reasons (such as bad parameters, or some other condition). The "may" methods are called by the "can" methods, so you usually can rely on the "can" methods to determine if some operation is allowed.

Let us now take a look at the five methods listed above for querying permissions. The first method, has(), is the simplest. You pass it a capability name (such as "CreateDirectory") and optionally a user ID and it will tell you with a boolean whether the user (or you) is granted or denied that permission. Example:

# grab the file and its associated permission object
$dir = SnapDirectory::lookup('//test/');
$perm = $dir->getPermission();

# user defaults to currently logged in user
$access = $perm->has('CreateDirectory');

# we could also do, with a specified user ID:
# $access = $perm->has('CreateDirectory', 1234);

if($access == true)
    print "User has CreateDirectory<br />";
else
    print "User does not have CreateDirectory<br />";

The method hasEx() takes the same arguments as has(), but instead of returning true or false, it returns extended information about the derivation of the permission. It returns an array with two elements: access derivation and a group name (or false, if a group name is not relevant). It can also return an error value of false for both elements. The following table summarizes the possible return values and what they mean:

Return Value Meaning
false, false Error
ACCESS_DEFAULT_DENY, false Default deny: no explicit user or group permissions were set for this user and the groups he/she belongs to
ACCESS_GROUP_ALLOW, $groupName At least one group has granted the permission and no other groups deny it, nor are there explicit user permissions; name of first group that had an explicit ALLOW is given in $groupName
ACCESS_GROUP_DENY, $groupName At least one group has explicitly denied the permission and there are no explicit user permissions to override that; name of first group that had an explicit DENY is given in $groupName
ACCESS_USER_ALLOW, false User is explicitly granted the permission; group permissions are ignored
ACCESS_USER_DENY, false User is explicitly denied the permission; group permissions are ignored
ACCESS_ADMIN, false User is granted the permission by having the ADMIN privilege; all other permissions are ignored

Changing Permissions


Generated on Wed Nov 24 02:01:29 2010 for Common by  doxygen 1.5.6