General

Getting Started #

GraffitiBundle can be added to existing Web projects — to expand functionality as a desktop companion app — or new Web projects.

To add GraffitiBundle to your project, follow these simple steps:

  • Extract the downloaded archive
  • Open the bundle-web Xojo project
  • Select the “GraffitiBundle” module in the IDE’s project navigator (left-hand pane)
  • Right-click and select “Copy”
  • Open the destination project where you wish to implement GraffitiBundle
  • Select Edit > Paste from the menubar

To implement GraffitiBundle, the easiest way is to added the following property to your Session object:

Public Property controller As GraffitiBundle.Controller

Then, in the Session Opening event, add the following code replacing <YourBundleID> with a unique string identifying your application pair (these IDs must be identical for the Web and Desktop applications), something like com.mycompany.myapplication:

controller = new GraffitiBundle.Controller( self.Identifier )
controller.BundleID = "<YourBundleID>"

Now, in your App object’s Opening event, you can add the following code if you wish for your app to serve requests other than those from a GraffitiBundle client application:

GraffitiBundle.Controller.BundleRequestsOnly = False

Next, in your App.HandleURL event, add the following code to direct requests to GraffitiBundle and cancel further processing if those requests have been handled:

if GraffitiBundle.Controller.HandleURL( request, response ) then Return True

Finally, add a Receiver to your page(s) by dragging a GraffitiBundle.Receiver from the project navigator to your destination page(s). Receivers will intercept all events emitted by the GraffitiBundle controller, but need to be added to the controller in order to function. Remember that any added receiver will have all events raised, so you may need to devise a method of determining which events are meant for which receivers if you plan to implement a multi-page application.

Once you’ve added a receiver to a page, add it to the controller by using the following code:

Session.controller.AddReceiver( Receiver1 )

When closing a page, be sure to remove that page’s receiver:

Session.controller.RemoveReceiver( Receiver1 )

Now you’re ready to implement the events of the receiver and use the methods of the Session.controller object to communicate with a GraffitiBundle client application.

File Path Tokens #

While GraffitiBundle child Web applications distributed with a client Desktop application will have, depending on system security, unfettered access to the file system on their own, GraffitiBundle Desktop does incorporate some helpers. Path tokens are replaced in real-time when a path operation is passed from the Web application to the Desktop application. This allows you to build URI paths such as the following to reference a file named “test.txt” on the user’s desktop:

{desktop}/test.txt

Various tokens have been provided:

TokenEquivalent
{app}Path to the client Desktop application.
{appuser}Path to the user’s local storage directory (IE: /Users/TomSelleck/Library/Application Support/com.graffitisuite.bundledemo).
{appchild}Path to the child application directory in the user’s local storage directory (IE: {appuser}/application).
{appdata}SpecialFolder.ApplicationData
{desktop}SpecialFolder.Desktop
{documents}SpecialFolder.Documents
{home}SpecialFolder.UserHome
{library}SpecialFolder.UserLibrary
{preferences}SpecialFolder.Preferences
{fonts}SpecialFolder.Fonts
{resources}SpecialFolder.Resources
{temporary}SpecialFolder.Temporary
{sharedappdata}SpecialFolder.SharedApplicationData
{shareddesktop}SpecialFolder.SharedDesktop
{shareddocuments}SpecialFolder.SharedDocuments
{sharedpreferences}SpecialFolder.SharedPreferences

Additionally, as a basic security measure, directory traversal is not allowed using “..” or “.” path parts, even though full URI paths may be passed to functions.

DirectoryListingReceived JSONItem Contents #

The DirectoryListingReceived event has a data as JSONItem parameter. This JSONItem will be an object containing the original path and an array of data representing the files with the following child item properties that match those of the FolderItems:

KeyValue
pathFolderItem.ShellPath
nameFolderItem.Name
isFolderFolderItem.IsFolder
lengthFolderItem.Length
creationDateTimeFolderItem.CreationDateTime
modificationDateTimeFolderItem.ModificationDateTime
isReadableFolderItem.IsReadable
isWriteableFolderItem.IsWriteable
nativePathFolderItem.NativePath
ownerFolderItem.Owner
permissionsFolderItem.Permissions
typeFolderItem.Type

Example:

var path as String = data.Lookup( "path", "" ).StringValue
labelPath.Text = path

var children as JSONItem = data.Lookup( "children", nil )
if children = nil then Return

var max as Integer = children.LastRowIndex
var child as JSONItem
for index as Integer = 0 to max
  child = children.ValueAt(index)
  listChildren.AddRow( child.Value( "name" ).StringValue, child.Value( "length" ).StringValue )
  listChildren.RowTagAt( listChildren.LastAddedRowIndex ) = child.Value( "path" )
next