# ACS 4 Photo Album Package Design Documentation

by Tom Baginski, bags@arsdigita.com

## Essentials

- User accessible directory depends on where package is mounted in site.

- No separate administration directories

- Tcl procedures: packages/photo-album/tcl/photo-album-procs.tcl (link to proc depends on install)

- Data model: packages/photo-album/sql/photo-album-create.sql (link to data model depends on install)

## Introduction

The basic content element of the photo album system is a photo. When a user uploads a photo, the system stores attribute data such as caption, story, and title as a single content element. Each photo associated with several (three to start) image elements that store the actual binary files. The image elements, which are created by the server, are standard sized versions of the original photo used for display. Photos and images can have descriptive attributes associated with them. The attributes and binary files can be revised and the system will retain past versions.

Photos are grouped together into albums which can contain 0 or more photos. The albums can have descriptive attribute information that can be revised with history tracking. The albums can be displayed as a unit that allows user to browse through the photos in the album.

Albums can be grouped together into folders that can contain 0 or more albums or other folders.

An instance of the package include pages to display the folders, albums, and photos along with admin pages. Instances can be mounted to different subsite and managed independently. The grouping is included within the instance so that the albums maintain a consistent url even if they are re-sorted to different folders within the instance (as long as the subsite url isn't changed).

The display, grouping, and administration functionality of the photo album package will be included in the initial release of the package. This is intended to be one part of a larger system that will allow bulk uploading and purchasing of photos. These two feature have already been implemented on aD customer sites. ACS 4 versions of these features will be either incorporated into a future version of the photo album package or added as individual packages that depend on the photo album.

The basic tasks of the photo album revolve around storing and displaying content and associated attributes. As such, this package will take advantage of the exiting features of the content repository service package. The content repository can store multiple revisions of content items such as photos and images and their associated attributes. The content repository also provides grouping functions. The acs permission service will be used for access control so view, edit, and administration privileges will be highly customizable. Finally individual photo album instances can be added to subsites to support multiple independent photo albums on the same site.

## Historical Considerations

The the development of photo album package was base largely on the experience of building a custom photo album application for the Iluvcamp client site. The Iluvcamp photo album was built using an early version of the content repository. The photo album package uses the current version of the ACS content repository and takes advantage of several key features of the ACS4 structure such as permissions, templating, the content repository. The photo album package avoid several of the limitation of the Iluvcamp Photo Album that were made clear after supporting a summer of use and uploading over 70GB of original photos.

A photodb application was developed for ACS 3.x. The option of porting the photodb application to ACS4 was not followed due to the customized nature of the photodb application. It was primarily designed for storing and displaying information about photos for photographers. In contrast, the photo album application provides a general system for storing and displaying user uploaded photos, which can be customized to any particular client application.

## Competitive Analysis

Not done. A google search for "photo album web application" returns links for several shareware photo album applications. I did not install or test any of these.

## Design Tradeoffs

The package uses/requires the convert and identify ImageMagick binaries to be installed on the host computer at the location specified in the ImageMagickPath parameter. ImageMagick is readily available and easy to use from within server pages. ImageMagick is copyrighted by ImageMagick Studio, a nonprofit organization. ImageMagick is available for free, may be used to support both open and proprietary applications, and may be redistributed without fee. The previous ImageMagick Homepage has some mention of LZW patent issues and redirects to a new location. If these patent issues become a problem, the package will need to be re-coded to use different image manipulation software.

By default the package stores the image binary files in the file system. This decision was made for ease of use and development. This was also based on the assumption that file system storage is more appropriate for large systems with many giga-bytes of photos. The content repository also provides support for database storage of binaries in blobs. Future version of the photo album will also support database storage of photos. Load testing will be used to determine the most appropriate storage method for small and large volumes of photos.

The default photo storage directory is outside of the page root and images are served using a .vuh file. This was done to prevent potential execution of user uploaded files. Although the package checks that the user only uploads image files, in theory a rouge user could upload a code file. If that file was directly accessible from the page root, the rouge user could execute arbitrary code with all the authority of the web server user. Serving the image through a .vuh file also allows for checking access permissions to a file before it is served to the user. The benefits of serving image files with a .vuh file comes at a cost of some additional server resources. Again, load testing will be used to determine the true cost of using a .vuh file.

The photo attribute information and binary files are stored in two different content types (pa_photos and pa_images). The linkage between the binaries and the attribute info is maintained as a relationship between the two content_items. This decision was made so the the number of image sizes per photo could be changed with minor code revision and no datamodel changes. This flexibility comes at a cost, the queries to retreive and serve the appropriate version of a photo need to join in several tables to do so. There also is not an easy way to store the relation between different versions of the photo-attribute info such as caption and the versions of the binaries.

## API

### PL/SQL API

The PL/SQL database api is incorporated into four packages: pa_image, pa_photo, pa_album, and photo_album. The first three correspond to the 3 new object types created for the photo album application. The last contains general database functions for the application.

- pa_image package

  - `pa_image.new` creates a new pa_image object type and returns the cr_item.item_id of the newly created object. It also create the first revision of that object. It accepts an item_id and revision_id as arguments but both will be created if not provided. Although relation tag defaults to null it should be in (thumb, viewer, base). Path is the path to the image binary relative to the upload base (a parameterized directory).

          function new (
            name		in cr_items.name%TYPE,
            parent_id		in cr_items.parent_id%TYPE default null,
            item_id		in acs_objects.object_id%TYPE default null,
            revision_id		in acs_objects.object_id%TYPE default null,
            content_type	in acs_object_types.object_type%TYPE default 'pa_image',
            creation_date	in acs_objects.creation_date%TYPE default sysdate,
            creation_user	in acs_objects.creation_user%TYPE default null,
            creation_ip		in acs_objects.creation_ip%TYPE default null,
            locale		in cr_items.locale%TYPE default null,
            context_id		in acs_objects.context_id%TYPE default null,
            title		in cr_revisions.title%TYPE default null,
            description		in cr_revisions.description%TYPE default null,
            mime_type		in cr_revisions.mime_type%TYPE default null,
            nls_language	in cr_revisions.nls_language%TYPE default null,
            relation_tag	in cr_child_rels.relation_tag%TYPE default null,
            is_live		in char default 'f',
            publish_date	in cr_revisions.publish_date%TYPE default sysdate,
            path		in pa_images.path%TYPE,
            height		in pa_images.height%TYPE default null,
            width		in pa_images.width%TYPE default null,
            file_size		in pa_images.file_size%TYPE default null
          ) return cr_items.item_id%TYPE;

  - `pa_image.delte_revision` deletes a specified revision of a pa_image and schedules the binary for deleation from the file-system by a sweep procedure

          procedure delete_revision (
            revision_id		in cr_revisions.revision_id%TYPE
          );

  - `pa_image.delete` deletes a pa_image and all revisions Also schedules the binary files for deletion from the file-sytem.

          procedure delete (
            item_id		in cr_items.item_id%TYPE
          );

- pa_photo package

  - `pa_photo.new` create a new pa_photo object and returns the cr_item.item_id of the new object. Also creates first revision of object

          function new (
            name		in cr_items.name%TYPE,
            parent_id		in cr_items.parent_id%TYPE default null,
            item_id		in acs_objects.object_id%TYPE default null,
            revision_id		in acs_objects.object_id%TYPE default null,
            content_type	in acs_object_types.object_type%TYPE default 'pa_photo',
            creation_date	in acs_objects.creation_date%TYPE default sysdate,
            creation_user	in acs_objects.creation_user%TYPE default null,
            creation_ip		in acs_objects.creation_ip%TYPE default null,
            locale		in cr_items.locale%TYPE default null,
            context_id		in acs_objects.context_id%TYPE default null,
            title		in cr_revisions.title%TYPE default null,
            description		in cr_revisions.description%TYPE default null,
            relation_tag	in cr_child_rels.relation_tag%TYPE default null,
            is_live		in char default 'f',
            publish_date	in cr_revisions.publish_date%TYPE default sysdate,
            mime_type		in cr_revisions.mime_type%TYPE default null,
            nls_language	in cr_revisions.nls_language%TYPE default null,
            caption		in pa_photos.caption%TYPE default null,
            story		in pa_photos.story%TYPE default null,
            user_filename	in pa_photos.user_filename%TYPE default null
          ) return cr_items.item_id%TYPE;

  - `pa_photo.delete_revision` deletes a specified revision of a pa_photo

          procedure delete_revision (
            revision_id		in acs_objects.object_id%TYPE
          );

  - `pa_photo.delete` deletes a pa_photo object, all revision of a pa_photo, and all associated pa_images (including binaries). Basically nukes a photo, so be careful using this one, because it can't be undone.

          procedure delete (
            item_id             in acs_objects.object_id%TYPE
          );

- pa_album package

  - `pa_album.new` creates a new pa_album object type and returns the cr_item.item_id of the newly created object. It also create the first revision of that object. It accepts an item_id and revision_id as arguments but both will be created if not provided.

          function new (
             name           in cr_items.name%TYPE,
             album_id       in cr_items.item_id%TYPE default null,
             parent_id	    in cr_items.parent_id%TYPE default null,
             revision_id    in cr_revisions.revision_id%TYPE default null,
             content_type   in acs_object_types.object_type%TYPE default 'pa_album',
             is_live	    in char default 'f',
             creation_date  in acs_objects.creation_date%TYPE default sysdate,
             creation_user  in acs_objects.creation_user%TYPE default null,
             creation_ip    in acs_objects.creation_ip%TYPE default null,
             locale	    in cr_items.locale%TYPE default null,
             context_id	    in acs_objects.context_id%TYPE default null,
             relation_tag   in cr_child_rels.relation_tag%TYPE default null,
             publish_date   in cr_revisions.publish_date%TYPE default sysdate,
             mime_type      in cr_revisions.mime_type%TYPE default null,
             nls_language   in cr_revisions.nls_language%TYPE default null,
             title	    in cr_revisions.title%TYPE default null,
             description    in cr_revisions.description%TYPE default null,
             story	    in pa_albums.story%TYPE default null
          ) return cr_items.item_id%TYPE;

  - `pa_album.delte_revision` deletes a specified revision of a pa_album

          procedure delete_revision (
            revision_id		in cr_revisions.revision_id%TYPE
          );

  - `pa_album.delete` deletes a pa_album and all revisions of that album. The album must not have any child pa_phots or the delete throws an error. So there is nothing equilent to `rm -r *`. You separately delete the photos in an album (or move them to a different album) then delete the album.

          procedure delete (
            album_id		in cr_items.item_id%TYPE
          );

- photo_album package

  - `photo_album.get_root_folder` returns the root folder for a given package_id (package instance). The root folder for a package is actually cached by the web server. So it is probably faster to call the server proc pa_get_root_folder and pass the value in as a bind variable any place you need the root_folder in a query.

          function get_root_folder (
              package_id in apm_packages.package_id%TYPE
          ) return pa_package_root_folder_map.folder_id%TYPE;

### Procedural API

The public procedural API available from the webserver is currently implemented as tcl procedures.

- `pa_get_root_folder {package_id ""}` Returns the folder_id of the root folder for an instance of the photo album system. If no root folder exists, as when a new package instance is accessed for the first time, a new root folder is created automatically with appropriate permissions If value has be previously requested, value pulled from cache. If pakage_id is not specified, procedure uses value from `[ad_conn package_id]`

- `pa_context_bar_list {-final ""} item_id` Constructs the list to be fed to ad_context_bar appropriate for item_id. If -final is specified, that string will be the last item in the context bar. Otherwise, the name corresponding to item_id will be used.

- `pa_make_file_name {-assert:boolean} {-ext ""} id` Constructs a filename for an image based on id and extention. Files are created into a 3 tier directory structure: year/xx/zz/ for a photo_id 1234xxzz.jpg Same file would return year/xx/zz/1234xxzz.jpg. If -assert specified, proc creates directory and any parent directories if necessary

- `pa_is_folder_p folder_id {package_id ""}` Returns "t" if folder_id is a folder that is a child of the root folder for the package, else "f". If package_id is not given procedure uses value from `[ad_conn package_id]`.

- `pa_is_album_p album_id {package_id ""}` Returns "t" if album_id is a album that is a child of the root folder for the package, else "f" If package_id is not given procedure uses value from `[ad_conn package_id]`.

- `pa_is_photo_p photo_id {package_id ""}` Returns "t" if photo_id is a photo that is a child of the root folder for the package, else "f" If package_id is not given procedure uses value from `[ad_conn package_id]`.

- `pa_grant_privilege_to_creator object_id {user_id ""}` Grants a set of default privileges stored in parameter PrivilegeForCreator on object id to user_id. If user_id is not specified, uses current user.

- `pa_image_width_height filename width_var height_var` Uses ImageMagick program to get the width and height in pixels of filename. Sets height to the variable named in height_var in the calling level. Sets width_var to the variable named in width_var in the calling level.

  I Use ImageMagick instead of aolsever funcition because it can handle more than just gifs and jpegs.

- `pa_make_new_image base_image new_image max_width {max_height ""}` Uses ImageMagick program to create a file named new_image from base_image that fits within a box defined by max_width by max_height (in pixels). ImageMagick will retain the aspect ratio of the base_image when creating the new_image If max_height is not specified, max_width is used for max_height.

- `pa_all_photos_in_album album_id` Returns a list of all the photo_ids in an album sorted in ascending order. Pulls value from cache if already there, caches result and returns result if not

- `pa_count_photos_in_album album_id` Returns count of number of photos in album_id

- `pa_all_photos_on_page album_id page` Returns a list of the photo_ids on page page of album_id. List is in ascending order

- `pa_count_pages_in_album album_id` Returns the number of pages in album_id

- `pa_page_of_photo_in_album photo_id album_id` Returns the page number of a photo in an album. If photo is not in the album returns -1

- `pa_flush_photo_in_album_cache album_id` Clears the cacheed value set by pa_all_photos_in_album for a single album Call proc on any page that alters the number or order of photos in an album.

- `pa_pagination_page_number_links page total_pages` Returns html table fragament for navigating pages of an album.

- `pa_photo_number_links cur_id all_ids` Given a current photo_id and and an ordered list of all the photo_id in an album, creates an html fragment that allows user to navigate to any photo by number, or next/previous. Pulls url from connection so should work on page showing a single photo based on photo_id (such as viewer size display page or base size display page).

## Data Model Discussion

The data model defines 3 new content types within the content repository. These are pa_album, pa_photo, and pa_image. It uses the exiting content_folder for grouping pa_albums. Pa_albums store attribute information about a group of photos and serves as a container for 0 or more photos. Pa_photos store attributes of user uploaded photo. Pa_images are used as a helper type to store the base (original), thumbnail and viewer display images associated with a pa_photo. Each pa_photo has one child pa_image for its base photo, one for its thumbnail image, and one child pa_image for its viewer image. The pa_images are created by the server whenever a pa_photo is added or edited.

The root folder for each package instance is stored in pa_root_folder_package_map. All folders, albums, photos, and images within a package instance are decendants of the root folder for that package.

Image files scheduled for deletion are stored in pa_files_to_delete. A nightly web-server process deletes the files in this table and then removes them from the table. This table allows images to be deleted transactionally from both the database and the file-system. All photo/image delete operations should add the binary file-name to pa_files_to_delete and then delete the attribute information in the database within a transaction. This way the file system delete will not happen unless the database delete succeeds.

## User Interface

The user accessible pages contain a set of pages for displaying, adding, and editing folders, albums, and photos. The folder page which serves as the index page allows a user to navigate to sub-folders and albums within the folder. The album page displays the photos (or a sub-set of photos) within an album. The photo page displays a single photo.

## Configuration/Parameters

Parameters are set by default through the apm and can be edited by a user with admin privileges for a sub-site through the current sub-site admin pages. Depending on which version of the apm is installed, the server may need to be restarted for the parameters of a new package instance are available.

The defined parameters are as follows:

`ImageMagickPath`

  Description: Absolute path from computer root to directory containing ImageMagick executables convert and identify.

  Default: /usr/local/bin

`PhotoDir`

  Description: Relative path from acs_root_dir to directory for storing uploaded images. (do not include leading or trailing forward slash)

  Default: uploaded-photos

`FullTempDir`

  Description: Absolute path from computer root to path for temporary photos.

  Default: /tmp/photo-album

`PhotoStorageMethod`

  Description: Default method for storing photos, file-system or database. (Package currently only supports file-system)

  Default: file-system

`ThumbnailSize`

  Description: Max Dimension for thumbnail image

  Default: 100

`ViewerSize`

  Description: Max Dimension for viewer images

  Default: 400

`ThumbnailsPerPage`

  Description: Number of thumbnail images to be displayed per album page

  Default: 12

`PrivilegeForCreator`

  Description: CSV of privileges to grant to creators of new objects within photo album

  Default: admin

`AcceptableUploadMIMETypes`

  Description: CSV of acceptable MIME Types for Photo Upload

  Default: image/jpeg,image/gif

`DefaultRootFolderPrivileges`

  Description: List of grantee privilege pairs for new root folders. Grantee is an integer or function that returns a party_id. Format as a space separated list

  Default: acs.magic_object_id('the_public') read acs.magic_object_id('registered_users') pa_create_album acs.magic_object_id('registered_users') pa_create_folder

`AllowBasePhotoAccessP`

  Description: If set to "t", users are presented a link to display the full sized base image from photo page.

  Default: t

`CheckPermissionOnImageServeP`

  If set to "t", the images/index.vuh file runs a query to check if user has read access to photo prior to serving it. Note, setting to "t" can impact performance on high volume sites. The urls for images also change depending on this parameter. If "t" url contains the pa_image_id, else contains the path to the pa_image in file-system.

  Default: f

## Future Improvements/Areas of Likely Change

- Server backed image manipulation

- Purchase and printing of photo through ecommerce package and photo printing vendor.

- Bulk upload tool

- User specified attributes

- Upload quotas

- Admin notification of file space limitations.

- Search and retrieval of photos and albums based on attributes or key words.

- Admin specified attributes

## Authors

- System creator, System owner, and Documentation author: Tom Baginski, bags@arsdigita.com

## Revision History

Document Revision # | Action Taken, Notes | When? | By Whom?
--- | --- | --- | ---
0.1 | Creation | 12/17/2000 | Tom Baginski
0.2 | Editing | 1/9/2001 | Tom Baginski
0.3 | Editing and additions to API and Data Model sections. | 2/5/2001 | Tom Baginski

bags@arsdigita.com