<?php

/**
 * @file
 * Defines all the hooks this module implements.
 *
 * islandora.module: defines paths (drupal menu items) as entry points and acts
 *  as a hub for dispatching tasks to other modules.
 *
 * This file is part of Islandora.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the program.  If not, see <http ://www.gnu.org/licenses/>.
 */

// Common datastreams.
define('DS_COMP_STREAM', 'DS-COMPOSITE-MODEL');

// Permissions.
define('FEDORA_VIEW_OBJECTS', 'view fedora repository objects');
define('FEDORA_METADATA_EDIT', 'edit fedora metadata');
define('FEDORA_ADD_DS', 'add fedora datastreams');
define('FEDORA_INGEST', 'ingest fedora objects');
define('FEDORA_PURGE', 'delete fedora objects and datastreams');
define('FEDORA_MANAGE_PROPERTIES', 'manage object properties');
define('ISLANDORA_VIEW_DATASTREAM_HISTORY', 'view old datastream versions');

// Hooks.
define('ISLANDORA_VIEW_HOOK', 'islandora_view_object');
define('ISLANDORA_PRINT_HOOK', 'islandora_view_print_object');
define('ISLANDORA_EDIT_HOOK', 'islandora_edit_object');
define('ISLANDORA_OVERVIEW_HOOK', 'islandora_overview_object');
define('ISLANDORA_PRE_INGEST_HOOK', 'islandora_ingest_pre_ingest');
define('ISLANDORA_POST_INGEST_HOOK', 'islandora_ingest_post_ingest');
define('ISLANDORA_PRE_PURGE_OBJECT_HOOK', 'islandora_pre_purge_object');
define('ISLANDORA_POST_PURGE_OBJECT_HOOK', 'islandora_post_purge_object');

// @todo Add Documentation.
define('ISLANDORA_OBJECT_INGESTED_HOOK', 'islandora_object_ingested');
define('ISLANDORA_OBJECT_MODIFIED_HOOK', 'islandora_object_modified');
define('ISLANDORA_OBJECT_PURGED_HOOK', 'islandora_object_purged');
define('ISLANDORA_DATASTREAM_INGESTED_HOOK', 'islandora_datastream_ingested');
define('ISLANDORA_DATASTREAM_MODIFIED_HOOK', 'islandora_datastream_modified');
define('ISLANDORA_DATASTREAM_PURGED_HOOK', 'islandora_datastream_purged');
define('ISLANDORA_INGEST_STEP_HOOK', 'islandora_ingest_steps');

// Autocomplete paths.
define('ISLANDORA_CONTENT_MODELS_AUTOCOMPLETE', 'islandora/autocomplete/content-models');

/**
 * Implements hook_menu().
 *
 * We need some standard entry points so we can have consistent urls for
 * different Object actions
 */
function islandora_menu() {
  $items = array();
  $items['admin/islandora'] = array(
    'title' => 'Islandora',
    'description' => "Configure settings associated with Islandora.",
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/islandora/configure'] = array(
    'title' => 'Configuration',
    'description' => 'Configure settings for Islandora.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('islandora_repository_admin'),
    'file' => 'includes/admin.form.inc',
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
    'weight' => -1,
  );
  $items['admin/islandora/solution_packs'] = array(
    'title' => 'Solution packs',
    'description' => 'Install content models and collections required by installed solution packs.',
    'page callback' => 'islandora_solution_packs_admin',
    'access arguments' => array(FEDORA_ADD_DS),
    'file' => 'includes/solution_packs.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['islandora'] = array(
    'title' => 'Islandora Repository',
    'page callback' => 'islandora_view_default_object',
    'type' => MENU_NORMAL_ITEM,
    'access arguments' => array(FEDORA_VIEW_OBJECTS),
  );
  $items['islandora/object/%islandora_object'] = array(
    'title callback' => 'islandora_drupal_title',
    'title arguments' => array(2),
    'page callback' => 'islandora_view_object',
    'page arguments' => array(2),
    'type' => MENU_NORMAL_ITEM,
    'access callback' => 'islandora_object_access_callback',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
  );
  $items['islandora/object/%islandora_object/print'] = array(
    'page callback' => 'islandora_printer_object',
    'page arguments' => array(2),
    'type' => MENU_NORMAL_ITEM,
    'access callback' => 'islandora_object_access_callback',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
  );
  $items['islandora/object/%islandora_object/view'] = array(
    'title' => 'View',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );
  $items['islandora/object/%islandora_object/view/default'] = array(
    'title' => 'View',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );
  $items['islandora/object/%islandora_object/manage'] = array(
    'title' => 'Manage',
    'page callback' => 'islandora_manage_overview_object',
    'page arguments' => array(2),
    'type' => MENU_LOCAL_TASK,
    'access callback' => 'islandora_object_manage_access_callback',
    'access arguments' => array(
      array(
        FEDORA_MANAGE_PROPERTIES,
        FEDORA_METADATA_EDIT,
        FEDORA_ADD_DS,
        FEDORA_PURGE,
        FEDORA_INGEST,
      ), 2),
  );

  $items['islandora/object/%islandora_object/manage/overview'] = array(
    'title' => 'Overview',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -20,
  );

  $items['islandora/object/%islandora_object/manage/datastreams'] = array(
    'title' => 'Datastreams',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'islandora_edit_object',
    'page arguments' => array(2),
    'access callback' => 'islandora_object_manage_access_callback',
    'access arguments' => array(
      array(
        FEDORA_METADATA_EDIT,
        FEDORA_ADD_DS,
        FEDORA_PURGE,
      ), 2),
    'weight' => -10,
  );

  $items['islandora/object/%islandora_object/manage/properties'] = array(
    'title' => 'Properties',
    'page callback' => 'drupal_get_form',
    'file' => 'includes/object_properties.form.inc',
    'page arguments' => array('islandora_object_properties_form', 2),
    'type' => MENU_LOCAL_TASK,
    'access callback' => 'islandora_object_access_callback',
    'access arguments' => array(FEDORA_MANAGE_PROPERTIES, 2),
    'weight' => -5,
  );
  $items['islandora/object/%islandora_object/delete'] = array(
    'title' => 'Delete object',
    'file' => 'includes/delete_object.form.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('islandora_delete_object_form', 2),
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_object_access_callback',
    'access arguments' => array(FEDORA_PURGE, 2),
  );
  $items['islandora/object/%islandora_object/manage/datastreams/add'] = array(
    'title' => 'Add a datastream',
    'file' => 'includes/add_datastream.form.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('islandora_add_datastream_form', 2),
    'type' => MENU_LOCAL_ACTION,
    'access callback' => 'islandora_object_access_callback',
    'access arguments' => array(FEDORA_ADD_DS, 2),
  );
  $items['islandora/object/%islandora_object/manage/datastreams/add/autocomplete'] = array(
    'file' => 'includes/add_datastream.form.inc',
    'page callback' => 'islandora_add_datastream_form_autocomplete_callback',
    'page arguments' => array(2),
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_object_access_callback',
    'access arguments' => array(FEDORA_ADD_DS, 2),
  );
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream'] = array(
    'title' => 'View datastream',
    'page callback' => 'islandora_view_datastream',
    'page arguments' => array(4, FALSE),
    'type' => MENU_CALLBACK,
    'file' => 'includes/datastream.inc',
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 4),
    'load arguments' => array(2),
  );
  // This menu item uses token authentication in islandora_tokened_object.
  $items['islandora/object/%islandora_tokened_object/datastream/%islandora_tokened_datastream/view'] = array(
    'title' => 'View datastream',
    'load arguments' => array('%map'),
    'access callback' => 'islandora_object_datastream_tokened_access_callback',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 2, 4),
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array(
    'title' => 'Download datastream',
    'page callback' => 'islandora_download_datastream',
    'page arguments' => array(4),
    'type' => MENU_CALLBACK,
    'file' => 'includes/datastream.inc',
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 4),
    'load arguments' => array(2),
  );
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream/edit'] = array(
    'title' => 'Edit datastream',
    'page callback' => 'islandora_edit_datastream',
    'page arguments' => array(4),
    'type' => MENU_CALLBACK,
    'file' => 'includes/datastream.inc',
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(FEDORA_METADATA_EDIT, 4),
    'load arguments' => array(2),
  );
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream/delete'] = array(
    'title' => 'Delete data stream',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('islandora_delete_datastream_form', 4),
    'file' => 'includes/delete_datastream.form.inc',
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(FEDORA_PURGE, 4),
    'load arguments' => array(2),
  );
<<<<<<< HEAD
=======
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream/version'] = array(
    'title' => 'Datastream Versions',
    'page arguments' => array(4),
    'page callback' => 'islandora_datastream_version_table',
    'file' => 'includes/datastream.version.inc',
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(ISLANDORA_VIEW_DATASTREAM_HISTORY, 4),
    'load arguments' => array(2),
  );
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream/version/%/delete'] = array(
    'title' => 'Delete datastream version',
    'page arguments' => array('islandora_delete_datastream_version_form', 4, 6),
    'page callback' => 'drupal_get_form',
    'file' => 'includes/datastream.version.inc',
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(FEDORA_PURGE, 4),
    'load arguments' => array(2),
  );
  $items['islandora/object/%islandora_object/datastream/%islandora_datastream/version/%/view'] = array(
    'title' => 'View datastream version',
    'page callback' => 'islandora_view_datastream',
    'page arguments' => array(4, FALSE, 6),
    'type' => MENU_CALLBACK,
    'file' => 'includes/datastream.inc',
    'access callback' => 'islandora_datastream_access',
    'access arguments' => array(ISLANDORA_VIEW_DATASTREAM_HISTORY, 4),
    'load arguments' => array(2),
  );
  $items['islandora/object/%islandora_object/print'] = array(
    'title' => 'Print Object',
    'page callback' => 'islandora_print_object',
    'page arguments' => array(2),
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_object_access',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
    'load arguments' => array(2),
  );
>>>>>>> 73e09a9d0d9f0f91f2cbca4d6a204a96c9695fa1
  $items['islandora/object/%islandora_object/download_clip'] = array(
    'page callback' => 'islandora_download_clip',
    'page arguments' => array(2),
    'type' => MENU_CALLBACK,
    'access callback' => 'islandora_object_access',
    'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
    'load arguments' => array(2),
  );
  $items[ISLANDORA_CONTENT_MODELS_AUTOCOMPLETE] = array(
    'title' => 'Autocomplete callback',
    'description' => 'Autocomplete a Fedora content model PID.',
    'file' => 'includes/content_model.autocomplete.inc',
    'page callback' => 'islandora_content_model_autocomplete',
    'page arguments' => array(3),
    'access arguments' => array('administer site configuration'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_admin_paths().
 */
function islandora_admin_paths() {
  $paths = array();
  $paths['islandora/object/*/manage*'] = TRUE;
  $paths['islandora/object/*/delete*'] = TRUE;
  $paths['islandora/object/*/datastream/*/edit'] = TRUE;
  $paths['islandora/object/*/datastream/*/versions'] = TRUE;
  return $paths;
}

/**
 * Implements hook_theme().
 */
function islandora_theme() {
  return array(
    // Default object template.
    'islandora_default' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-object',
      'variables' => array('islandora_object' => NULL),
    ),
    // Default edit page.
    'islandora_default_edit' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-object-edit',
      'variables' => array('islandora_object' => NULL),
    ),
      // Default edit page.
    'islandora_default_print' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-object-print',
      'variables' => array('islandora_object' => NULL),
    ),
    // Admin table for solution pack viewer selection.
    'islandora_viewers_table' => array(
      'file' => 'includes/solution_packs.inc',
      'render element' => 'form',
    ),
    // Print object view.
    'islandora_object_print' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-object-print',
      'variables' => array(
        'object' => NULL,
        'content' => NULL,
        'islandora_content' => NULL),
    ),
    // Render a bunch of objects as either a grid or a list.
    'islandora_objects' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-objects',
      'variables' => array(
        'objects' => NULL,
        'display' => NULL,
        'page_size' => 20,
        'limit' => 10,
      ),
    ),
    // Render a bunch of objects as a grid.
    'islandora_objects_grid' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-objects-grid',
      'variables' => array('objects' => NULL),
    ),
    // Render a bunch of objects as a list.
    'islandora_objects_list' => array(
      'file' => 'theme/theme.inc',
      'template' => 'theme/islandora-objects-list',
      'variables' => array('objects' => NULL),
    ),
    'islandora_datastream_edit_link' => array(
      'file' => 'theme/theme.inc',
      'variables' => array('datastream' => NULL),
    ),
    'islandora_datastream_delete_link' => array(
      'file' => 'theme/theme.inc',
      'variables' => array('datastream' => NULL, 'version' => NULL),
    ),
    'islandora_datastream_view_link' => array(
      'file' => 'theme/theme.inc',
      'variables' => array(
        'datastream' => NULL,
        'version' => NULL,
        'label' => NULL,
      ),
    ),
    'islandora_datastream_download_link' => array(
      'file' => 'theme/theme.inc',
      'variables' => array('datastream' => NULL),
    ),
    'islandora_datastream_version_link' => array(
      'file' => 'theme/theme.inc',
      'variables' => array('datastream' => NULL),
    ),
  );
}

/**
 * Implements hook_permission().
 */
function islandora_permission() {
  return array(
    FEDORA_VIEW_OBJECTS => array(
      'title' => t('View repository objects'),
      'description' => t('View objects in the repository. Note: Fedora XACML security policies may override this permission.'),
    ),
    FEDORA_ADD_DS => array(
      'title' => t('Add datastreams to repository objects'),
      'description' => t('Add datastreams to objects in the repository. Note: Fedora XACML security policies may override this position.'),
    ),
    FEDORA_METADATA_EDIT => array(
      'title' => t('Edit metadata'),
      'description' => t('Edit metadata for objects in the repository.'),
    ),
    FEDORA_INGEST => array(
      'title' => t('Create new repository objects'),
      'description' => t('Create new objects in the repository.'),
    ),
    FEDORA_PURGE => array(
      'title' => t('Permanently remove objects from the repository'),
      'description' => t('Permanently remove objects from the repository.'),
    ),
    FEDORA_MANAGE_PROPERTIES => array(
      'title' => t('Manage object properties'),
      'description' => t('Modify object labels, owner IDs, and states.'),
    ),
    ISLANDORA_VIEW_DATASTREAM_HISTORY => array(
      'title' => t('View datastream history'),
      'description' => t('View all previous versions of a datastream.'),
    ),
  );
}

/**
 * Implements hook_forms().
 */
function islandora_forms($form_id) {
  $forms = array();
  if (strpos($form_id, 'islandora_solution_pack_form_') !== FALSE) {
    $forms[$form_id] = array(
      'callback' => 'islandora_solution_pack_form',
    );
  }
  return $forms;
}

/**
 * Checks whether the user can access the given object.
 *
 * Checks for repository access, object/datastream existance, namespace access,
 * user permissions, content models.
 *
 * Will check the given user or the user repersented by the GET token parameter,
 * failing that it will use the global user.
 *
 * @global $user
 *
 * @param mixed $object_or_datastream
 *   The AbstractObject or AbstractDatastream to test for accessibility, if NULL
 *   is given the object is assumed to not exist or be inaccessible.
 * @param array $permissions
 *   The required user permissions.
 * @param array $content_models
 *   The required content models.
 * @param bool $access_any
 *   (optional) TRUE to grant access if any single requirement is met from both
 *   the permissions and content models parameters. FALSE if all requirements
 *   must be met from both the permissions and content model parameters.
 * @param object $user
 *   (optional) The account to check, if not given check the GET parameters for
 *   a token to restore the user. If no GET parameter is present use currently
 *   logged in user.
 *
 * @return bool
 *   TRUE if the user is allowed to access this object/datastream, FALSE
 *   otherwise.
 */
function islandora_user_access($object_or_datastream, array $permissions, $content_models = array(), $access_any = TRUE, $user = NULL) {
  module_load_include('inc', 'islandora', 'includes/utilities');
  $is_repository_accessible = &drupal_static(__FUNCTION__);

  // If the repository is inaccessible then access always fails.
  if (!isset($is_repository_accessible)) {
    $is_repository_accessible = islandora_describe_repository();
    if (!$is_repository_accessible) {
      // Only display the inaccessible message once.
      islandora_display_repository_inaccessible_message();
      return FALSE;
    }
  }
  if (!$is_repository_accessible || !is_object($object_or_datastream) || empty($permissions)) {
    return FALSE;
  }

  // Determine what has been passed as $object.
  if (is_subclass_of($object_or_datastream, 'AbstractObject')) {
    $object = $object_or_datastream;
    $datastream = NULL;
  }
  elseif (is_subclass_of($object_or_datastream, 'AbstractDatastream')) {
    $datastream = $object_or_datastream;
    $object = $datastream->parent;
  }

  // Determine the user account to test against.
  if (!isset($user)) {
    $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
    if ($token) {
      module_load_include('inc', 'islandora', 'includes/authtokens');
      $token_user = islandora_validate_object_token($object->id, $datastream->id, $token);
      if ($user) {
        $user = user_load($token_user->uid);
      }
    }
    else {
      global $user;
    }
  }

  // Check for access.
  if ($access_any) {
    $has_required_content_models = empty($content_models) ? TRUE : count(array_intersect($object->models, $content_models)) > 0;
    if ($has_required_content_models) {
      foreach ($permissions as $p) {
        if ($datastream !== NULL) {
          $check = islandora_datastream_access($p, $datastream, $user);
        }
        else {
          $check = islandora_object_access($p, $object, $user);
        }

        if ($check) {
          return TRUE;
        }
      }
      return FALSE;
    }
  }
  else {
    $has_required_content_models = count(array_diff($content_models, $object->models)) == 0;
    if ($has_required_content_models) {
      foreach ($permissions as $p) {
        if ($datastream !== NULL) {
          $check = islandora_datastream_access($p, $datastream, $user);
        }
        else {
          $check = islandora_object_access($p, $object, $user);
        }

        if (!$check) {
          return FALSE;
        }
      }
      // Should already have failed if there are no $permissions.
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Checks whether the user can access the given object.
 *
 * Checks for object existance, accessiblitly, namespace permissions,
 * and user permissions
 *
 * @param string $perm
 *   User permission to test for.
 * @param AbstractObject $object
 *   The object to test, if NULL given the object doesn't exist or is
 *   inaccessible.
 *
 * @return bool
 *   TRUE if the user is allowed to access this object, FALSE otherwise.
 */
function islandora_object_access_callback($perm, $object = NULL) {
  module_load_include('inc', 'islandora', 'includes/utilities');

  if (!$object && !islandora_describe_repository()) {
    islandora_display_repository_inaccessible_message();
    return FALSE;
  }

  return islandora_object_access($perm, $object);
}

/**
 * Checks whether the user can access the given object and datastream.
 *
 * Checks for object existance, accessiblitly, namespace permissions,
 * and user permissions
 *
 * @param string $perm
 *   The user permission to test for.
 * @param AbstractObject $object
 *   The object to test, if NULL given the object doesn't exist or is
 *   inaccessible.
 * @param AbstractDatastream $datastream
 *   The datastream to test, if NULL given the datastream doesn't exist
 *   or is inaccessible.
 * @param StdObject $account
 *   The account to test permissions as or NULL for current user.
 *
 * @return bool
 *   TRUE if the user is allowed to access this object, FALSE otherwise.
 */
function islandora_object_datastream_access_callback($perm, $object = NULL, $datastream = NULL, $account = NULL) {
  module_load_include('inc', 'islandora', 'includes/utilities');
  $message = islandora_deprecated('7.x-1.2', 'Use islandora_datastream_access().');
  trigger_error(filter_xss($message), E_USER_DEPRECATED);

  return islandora_datastream_access($perm, $datastream, $account);
}

/**
 * Checks whether the user can access the given object and datastream.
 *
 * This function will validate and use a token if present in the GET parameters.
 *
 * Checks for object existance, accessibility, namespace permissions,
 * and user permissions
 */
function islandora_object_datastream_tokened_access_callback($perm, $object = NULL, $datastream = NULL) {
  module_load_include('inc', 'islandora', 'includes/utilities');

  $token_account = NULL;
  $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);

  if ($token) {
    $user = islandora_validate_object_token($object->id, $datastream->id, $token);
    if ($user) {
      $token_account = user_load($user->uid);
    }
  }

  return islandora_datastream_access($perm, $datastream, $token_account);
}

/**
 * Checks whether the user can access the given object's manage tab.
 *
 * Checks for object existance, accessiblitly, namespace permissions,
 * and user permissions
 *
 * @param array $perms
 *   Array of user permission to test for.
 * @param AbstractObject $object
 *   The object to test, if NULL given the object doesn't exist or is
 *   inaccessible.
 *
 * @return bool
 *   TRUE if the user is allowed to access this object, FALSE otherwise.
 */
function islandora_object_manage_access_callback($perms, $object = NULL) {
  module_load_include('inc', 'islandora', 'includes/utilities');

  if (!$object && !islandora_describe_repository()) {
    islandora_display_repository_inaccessible_message();
    return FALSE;
  }

  $has_access = FALSE;
  for ($i = 0; $i < count($perms) && !$has_access; $i++) {
    $has_access = $has_access || islandora_object_access($perms[$i], $object);
  }

  return $has_access;
}

/**
 * Renders the given objects manage overview page.
 *
 * @param AbstractObject $object
 *   The object to manage.
 *
 * @return string
 *   The HTML repersentation of the manage object page.
 */
function islandora_manage_overview_object(AbstractObject $object) {
  module_load_include('inc', 'islandora', 'includes/utilities');
  $output = array();
  foreach (islandora_build_hook_list(ISLANDORA_OVERVIEW_HOOK, $object->models) as $hook) {
    $temp = module_invoke_all($hook, $object);
    islandora_as_renderable_array($temp);
    if (!empty($temp)) {
      $output = array_merge_recursive($output, $temp);
    }
  }
  if (empty($output)) {
    // Add in the default, if we did not get any results.
    $output = islandora_default_islandora_manage_overview_object($object);
  }
  arsort($output);
  drupal_alter(ISLANDORA_OVERVIEW_HOOK, $object, $output);
  islandora_as_renderable_array($output);
  return $output;
}

/**
 * Renders the default manage object page for the given object.
 *
 * @param AbstractObject $object
 *   The object used to render the manage object page.
 *
 * @return array
 *   The default rendering of the object manage page, indexed at
 *   'Default Edit output'.
 */
function islandora_default_islandora_manage_overview_object(AbstractObject $object) {
  $output = theme('islandora_default_overview', array('islandora_object' => $object));
  return array(
    'Default overview output' => array(
      '#markup' => $output,
    ),
  );
}

/**
 * Renders the given objects manage page.
 *
 * Its possible to modify the output of this function if 'ISLANDORA_EDIT_HOOK'
 * is implemented in other modules.
 *
 * If no modules implement 'ISLANDORA_EDIT_HOOK' then this function returns the
 *  default manage view.
 *
 * @param AbstractObject $object
 *   The object to manage.
 *
 * @return string
 *   The HTML repersentation of the manage object page.
 */
function islandora_edit_object(AbstractObject $object) {
  module_load_include('inc', 'islandora', 'includes/breadcrumb');
  module_load_include('inc', 'islandora', 'includes/utilities');
  drupal_set_title($object->label);
  drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
  $output = array();
  foreach (islandora_build_hook_list(ISLANDORA_EDIT_HOOK, $object->models) as $hook) {
    $temp = module_invoke_all($hook, $object);
    islandora_as_renderable_array($temp);
    if (!empty($temp)) {
      $output = array_merge_recursive($output, $temp);
    }
  }
  if (empty($output)) {
    // Add in the default, if we did not get any results.
    $output = islandora_default_islandora_edit_object($object);
  }
  arsort($output);
  drupal_alter(ISLANDORA_EDIT_HOOK, $object, $output);
  islandora_as_renderable_array($output);
  return $output;
}

/**
 * Renders the default manage object page for the given object.
 *
 * @param AbstractObject $object
 *   The object used to render the manage object page.
 *
 * @return array
 *   The default rendering of the object manage page, indexed at
 *   'Default Edit output'.
 */
function islandora_default_islandora_edit_object(AbstractObject $object) {
  $output = theme('islandora_default_edit', array('islandora_object' => $object));
  return array(
    'Default Edit output' => array(
      '#markup' => $output,
    ),
  );
}

/**
 * Page callback for the path "islandora".
 *
 * Redirects to the view of the object indicated by the Drupal variable
 * 'islandora_repository_pid'.
 */
function islandora_view_default_object() {
  $pid = variable_get('islandora_repository_pid', 'islandora:root');
  drupal_goto("islandora/object/$pid");
}

/**
 * Renders the default view object page for the given object.
 *
 * Modules should implement ISLANDORA_VIEW_HOOK for the Fedora Content
 * models that their modules want to provide a view for.
 *
 * If no modules implement the hook then the default view object page
 *  will be rendered.
 *
 * @param AbstractObject $object
 *   The object to view.
 *
 * @return string
 *   The html repersentation of this object.
 */
function islandora_view_object(AbstractObject $object) {
  module_load_include('inc', 'islandora', 'includes/breadcrumb');
  module_load_include('inc', 'islandora', 'includes/utilities');

  // Add the print button via JavaScript.
  $path = drupal_get_path('module', 'islandora');
  drupal_add_js(array(
    'islandora' => array(
      'print_img' => $path . '/images/print-icon.png'),
    ), array(
      'type' => 'setting'));

  drupal_add_js(array(
    'islandora' => array(
      'print_link' => '/islandora/object/' . $object->id . '/print')),
    array('type' => 'setting'));
  drupal_add_js($path . '/js/add_print.js');

  drupal_set_title($object->label);
  drupal_set_breadcrumb(islandora_get_breadcrumbs($object));

  // Optional pager parameters.
  $page_number = (empty($_GET['page'])) ? '1' : $_GET['page'];
  $page_size = (empty($_GET['pagesize'])) ? '10' : $_GET['pagesize'];
  $output = array();
  $hooks = islandora_build_hook_list(ISLANDORA_VIEW_HOOK, $object->models);
  foreach ($hooks as $hook) {
    // @todo Remove page number and size from this hook, implementers of the
    // hook should use drupal page handling directly.
    $temp = module_invoke_all($hook, $object, $page_number, $page_size);
    islandora_as_renderable_array($temp);
    if (!empty($temp)) {
      $output = array_merge_recursive($output, $temp);
    }
  }
  if (empty($output)) {
    // No results, use the default view.
    $output = islandora_default_islandora_view_object($object);
  }

  arsort($output);
  drupal_alter($hooks, $object, $output);
  islandora_as_renderable_array($output);
  return $output;
}

/**
 * This will prepare an object to be printed.
 *
 * By default, all fedora objects can print DC record data,
 * however, Solution packs that wish to modify the form
 * to be printed must implement hook_islandora_view_print_object_alter,
 * create a theme.tpl.php file, and return its markup by calling
 * theme(themename.tpl.php).
 *
 * @param AbstractObject $object
 *   The object to print.
 *
 * @return string
 *   An HTML representation of this object.
 */
function islandora_printer_object(AbstractObject $object) {
  $output = array();
  $temp_arr = array();
  // Dispatch print hook.
  foreach (islandora_build_hook_list(ISLANDORA_PRINT_HOOK, $object->models) as $hook) {
    $temp = module_invoke_all($hook, $object);
    islandora_as_renderable_array($temp);
    if (!empty($temp)) {
      $temp_arr = array_merge_recursive($temp_arr, $temp);
    }
  }
  $output = islandora_default_islandora_printer_object($object, drupal_render($temp_arr));
  arsort($output);
  islandora_as_renderable_array($output);

  // Prompt to print.
  drupal_add_js('jQuery(document).ready(function () { window.print(); });', 'inline');
  return $output;
}

/**
 * Title callback for drupal title.
 *
 * Changes the drupal title to be the objects label.
 * models that their modules want to provide a view for.
 *
 * @param AbstractObject $object
 *   The object to view.
 *
 * @return string
 *   The objects label.
 */
function islandora_drupal_title(AbstractObject $object) {
  module_load_include('inc', 'islandora', 'includes/breadcrumb');
  drupal_set_breadcrumb(islandora_get_breadcrumbs($object));

  return $object->label;
}
/**
 * Renders the default view object page for the given object.
 *
 * @param AbstractObject $object
 *   The object used to render the view object page.
 *
 * @return array
 *   The default rendering of the object view page, indexed at 'Default output'.
 */
function islandora_default_islandora_view_object($object) {
  $output = theme('islandora_default', array('islandora_object' => $object));
  return array(
    'Default output' => array(
      '#markup' => $output,
    ),
  );
}

/**
 * Append the image alter to the printable form.
 *
 * @param AbstractObject $object
 *   The fedora object to print.
 * @param unknown $alter
 *   The string representation of the themed viewable object.
 *
 * @return array
 *   A renderable array
 */
function islandora_default_islandora_printer_object($object, $alter) {
  module_load_include('inc', 'islandora', 'includes/utilities');
  module_load_include('inc', 'islandora', 'includes/datastream');

  $path = drupal_get_path('module', 'islandora');
  drupal_add_css($path . '/css/islandora.print.css');

  $islandora_object = islandora_object_load($object->id);
  $repository = $islandora_object->repository;

  try {
    $dc = $islandora_object['DC']->content;
    $dc_object = DublinCore::importFromXMLString($dc);
  }
  catch (Exception $e) {
    drupal_set_message(t('Error retrieving object %s %t', array('%s' => $islandora_object->id, '%t' => $e->getMessage())), 'error', FALSE);
  }

  $variables = isset($dc_object) ? $dc_object->asArray() : array();
  $output = theme('islandora_object_print', array(
    'object' => $object,
    'dc_array' => $variables,
    'islandora_content' => $alter));

  return array(
    'Default output' => array(
      '#markup' => $output,
    ),
  );
}

/**
 * Just a wrapper around fetchings the IslandoraTuque object.
 *
 * Includes some very basic error logging.
 *
 * @param object $user
 *   The user to connect as.
 * @param string $url
 *   The URL to connect to.
 *
 * @return IslandoraTuque
 *   A IslandoraTuque instance
 */
function islandora_get_tuque_connection($user = NULL, $url = NULL) {
  $tuque = &drupal_static(__FUNCTION__);
  if (!$tuque) {
    if (IslandoraTuque::exists()) {
      try {
        $tuque = new IslandoraTuque($user, $url);
      }
      catch (Exception $e) {
        drupal_set_message(t('Unable to connect to the repository %e', array('%e' => $e)), 'error');
      }
    }
    else {
      return NULL;
    }
  }
  return $tuque;
}

/**
 * Loads the object from the given ID if possible.
 *
 * Often used to get a connection and return an object for the one specified in
 * the menu path as '%islandora_object'.
 *
 * @param string $object_id
 *   The pid of an object in the menu path identified by '%islandora_object'.
 *
 * @return FedoraObject
 *   If the given object id exists in the repository then this returns a
 *   FedoraObject.
 *   If no object was found it returns FALSE which triggers
 *   drupal_page_not_found().
 *   If the object was inaccessible then NULL is returned, and the
 *   access callback is expected to catch that case, triggering
 *   drupal_access_denied().
 */
function islandora_object_load($object_id) {
  $tuque = islandora_get_tuque_connection();
  if ($tuque) {
    try {
      return $tuque->repository->getObject(urldecode($object_id));
    }
    catch (Exception $e) {
      if ($e->getCode() == '404') {
        return FALSE;
      }
      else {
        return NULL;
      }
    }
  }
  else {
    IslandoraTuque::getError();
  }
  // Assuming access denied in all other cases for now.
  return NULL;
}

/**
 * Load the the object using a token if passed as a GET parameter.
 *
 * A helper function to get a connection and return an object using a token
 * for authentication.
 *
 * @param string $object_id
 *   The PID of an object in the menu path identified by
 *   '%islandora_tokened_object'.
 * @param array $map
 *   Used to extract the Fedora object's DSID at $map[4].
 *
 * @return FedoraObject
 *   A token authenticated object. @see islandora_object_load
 */
function islandora_tokened_object_load($object_id, $map) {
  if (array_key_exists('token', $_GET)) {
    $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
    if ($token) {
      module_load_include('inc', 'islandora', 'includes/authtokens');
      $token_user = islandora_validate_object_token($object_id, $map[4], $token);
      $token_user = $token_user ? user_load($token_user->uid) : NULL;
      islandora_get_tuque_connection($token_user);
    }
  }
  return islandora_object_load($object_id);
}

/**
 * Fetches a datastream object.
 *
 * This datastream load must take in arguments in a different
 * order than the usual islandora_datastream_load.  This is because
 * the function islandora_tokened_object_load needs DSID. It uses
 * the path %map to avoid duplicate parameters. The menu system
 * passes 'load arguments' to both islandora_tokened_object_load
 * and this function and the first parameter is positional with the token.
 * An alternative:
 * islandora_tokened_object_load(PID, DSID, PID)
 * islandora_tokened_datastream_load(DSID, DSID, PID)
 *
 * @param mixed $datastream_id
 *   %islandora_tokened_datastream @see islandora_datastream_load
 * @param array $map
 *   Used to extract the Fedora object's PID at $map[2].
 *
 * @return FedoraDatastream
 *   A datastream from Fedora.
 *
 * @see islandora_datastream_load
 */
function islandora_tokened_datastream_load($datastream_id, $map) {
  return islandora_datastream_load($datastream_id, $map[2]);
}

/**
 * Fetches a datastream object.
 *
 * A helper function to get an datastream specified as '%islandora_datastream'
 * for the object specified in the menu path as '%islandora_object'.
 *
 * Its up to the access callbacks and menu callbacks to trigger
 * drupal_access_denied() when appropriate.
 *
 * @param string $datastream_id
 *   The DSID of the datastream specified as '%islandora_datastream' to fetch
 *   from the given object in the menu path identified by '%islandora_object'.
 *
 * @param mixed $object_id
 *   The object to load the datastream from.  This can be a Fedora PID or
 *   an instantiated IslandoraAbstractObject as it implements __toString()
 *   returning the PID.
 *
 * @return FedoraDatastream
 *   If the given datastream ID exists then this returns a FedoraDatastream
 *   object, otherwise it returns NULL which triggers drupal_page_not_found().
 */
function islandora_datastream_load($datastream_id, $object_id) {
  $object = is_object($object_id) ? $object_id : islandora_object_load($object_id);
  if (!$object) {
    return NULL;
  }
  return $object[$datastream_id];
}

/**
 * Fetches the given datastream version from its datastream.
 *
 * Content model, collection view and collection policy datastreams may now
 * optionally define a version number in their top-level XML element as an
 * attribute, as in:
 *   <content_model name="Collection" version="2" ...
 *
 * @param AbstractObject $object
 *   The Object the datastream belongs to.
 * @param string $dsid
 *   The ID of the datastream.
 * @param string $datastream_file
 *   The datastream's content.
 *
 * @return int
 *   The datastreams version if found, NULL otherwise.
 */
function islandora_get_islandora_datastream_version($object = NULL, $dsid = NULL, $datastream_file = NULL) {
  $return = NULL;
  // @TODO, need better check for $object
  if ($object && $object[$dsid]) {
    $doc = simplexml_load_string($object[$dsid]->content);
  }
  elseif (!empty($datastream_file)) {
    $doc = simplexml_load_file($datastream_file);
  }

  if (!empty($doc) && $version = (int) $doc->attributes()->version) {
    $return = $version;
  }

  return $return;
}

/**
 * Implements hook_islandora_required_objects().
 */
function islandora_islandora_required_objects(IslandoraTuque $connection) {
  $module_path = drupal_get_path('module', 'islandora');
  // Root Collection.
  $root_collection = $connection->repository->constructObject('islandora:root');
  $root_collection->owner = 'fedoraAdmin';
  $root_collection->label = 'Top-level Collection';
  $root_collection->models = 'islandora:collectionCModel';
  // Collection Policy Datastream.
  $datastream = $root_collection->constructDatastream('COLLECTION_POLICY', 'X');
  $datastream->label = 'Collection policy';
  $datastream->mimetype = 'text/xml';
  $datastream->setContentFromFile("$module_path/xml/islandora_collection_policy.xml", FALSE);
  $root_collection->ingestDatastream($datastream);
  // TN Datastream.
  $datastream = $root_collection->constructDatastream('TN', 'M');
  $datastream->label = 'Thumbnail';
  $datastream->mimetype = 'image/png';
  $datastream->setContentFromFile("$module_path/images/folder.png", FALSE);
  $root_collection->ingestDatastream($datastream);
  return array(
    'islandora' => array(
      'title' => 'Islandora',
      'objects' => array(
        $root_collection,
      ),
    ),
  );
}

/**
 * Implements islandora_undeleteable_datastreams().
 */
function islandora_islandora_undeletable_datastreams(array $models) {
  return array('DC');
}

/**
 * Ingest the given object.
 *
 * @param AbstractObject $object
 *   An ingestable FedoraObject.
 *
 * @return FedoraObject
 *   The ingested FedoraObject.
 */
function islandora_add_object(AbstractObject &$object) {
  return $object->repository->ingestObject($object);
}

/**
 * Creates a new object with the same properties as the old.
 *
 * @todo Make Tuque objects support cloneing.
 *
 * @param AbstractObject $object
 *   An existing or new Fedora Object.
 *
 * @return AbstractObject
 *   The new Fedora Object with properties identical to the object given. This
 *   returned object is not automatically ingested.
 */
function islandora_copy_object(AbstractObject $object) {
  $clone = $object->repository->constructObject($object->id);
  $object_properties = array(
    'state',
    'createdDate',
    'lastModifiedDate',
    'label',
    'owner',
    'logMessage',
  );
  // Copy Properties.
  foreach ($object_properties as $property) {
    if (isset($object->$property)) {
      $clone->$property = $object->$property;
    }
  }
  // Copy Datastreams.
  foreach ($object as $dsid => $datastream) {
    if (empty($clone[$dsid])) {
      $clone->ingestDatastream($datastream);
    }
    else {
      // Get the content into a file, and add the file.
      $temp_file = drupal_tempnam('temporary://', 'datastream');
      $datastream->getContent($temp_file);
      $clone[$dsid]->setContentFromFile($temp_file);
      drupal_unlink($temp_file);
    }
  }
  return $clone;
}

/**
 * Delete's or purges the given object.
 *
 * @param AbstractObject $object
 *   An object to delete.
 *
 * @return bool
 *   TRUE if successful, FALSE otherwise.
 */
function islandora_delete_object(AbstractObject &$object) {
  try {
    $object->repository->purgeObject($object->id);
    $object = NULL;
    return TRUE;
  }
  catch (Exception $e) {
    // Exception message gets logged in Tuque Wrapper.
    return FALSE;
  }
}

/**
 * Delete's or purges the given datastream.
 *
 * @throws Exception
 *  Which types are undefined, but more than likely because of the hooks
 *  there will be several kinds.
 *
 * @param AbstractDatastream $datastream
 *   The datastream to delete.
 *
 * @return bool
 *   TRUE if successful, FALSE otherwise.
 */
function islandora_delete_datastream(AbstractDatastream &$datastream) {
  $object = $datastream->parent;
  return $object->purgeDatastream($datastream->id);
}

/**
 * Implements hook_cron().
 */
function islandora_cron() {
  module_load_include('inc', 'islandora', 'includes/authtokens');
  islandora_remove_expired_tokens();
}

/**
 * Implements hook_entity_info().
 *
 * Some boiler-plate for Tokens (the module, not our authentication work-
 * around).
 */
function islandora_entity_info() {
  $entities = array();

  $entities['islandora_object'] = array(
    'label' => t('Islandora Object'),
    'controller class' => 'IslandoraObjectEntityController',
    'fieldable' => FALSE,
    'entity keys' => array(
      'id' => 'id',
      'label' => 'label',
    ),
  );

  return $entities;
}

/**
 * Implements hook_entity_property_info().
 *
 * Details the tokens which will be available on the given object.
 */
function islandora_entity_property_info() {
  $info = array();

  $p = &$info['islandora_object']['properties'];

  $p['id'] = array(
    'type' => 'text',
    'label' => t('ID'),
    'description' => t('The identifier of the object.'),
  );
  $p['label'] = array(
    'type' => 'text',
    'label' => t('Object Label'),
    'description' => t('The label of the object.'),
  );
  $p['owner'] = array(
    'type' => 'text',
    'label' => t('Object Owner'),
    'description' => t('The name of the owner of the object.'),
  );
  $p['state'] = array(
    'type' => 'text',
    'label' => t('Object State'),
    'description' => t('An initial representing the state of the object.'),
  );
  $p['models'] = array(
    'type' => 'list<text>',
    'label' => t('Content Models'),
    'description' => t('The list of content models which the object has.'),
  );

  return $info;
}

/**
 * Renders the print page for the given object.
 *
 * Modules can either implement preprocess functions to append content onto the
 * 'content' variable, or override the display by providing a theme suggestion.
 *
 * @param AbstractObject $object
 *   The object.
 *
 * @return array
 *   A renderable array.
 */
function islandora_print_object(AbstractObject $object) {
  drupal_set_title($object->label);
  return theme('islandora_object_print', array('object' => $object));
}

/**
 * Menu callback downloads the given clip.
 */
function islandora_download_clip(AbstractObject $object) {
  if (isset($_GET['clip'])) {
    $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
    $http_protocol = $is_https ? 'https' : 'http';
    $url = $http_protocol . '://' . $_SERVER['HTTP_HOST'] . $_GET['clip'];
    $filename = $object->label;
    header("Content-Disposition: attachment; filename=\"{$filename}.jpg\"");
    header("Content-type: image/jpeg");
    header("Content-Transfer-Encoding: binary");
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_URL, $url);
    $response = curl_exec($ch);
    curl_close($ch);
  }
  exit();
}

/**
 * Implements hook_file_mimetype_mapping_alter().
 *
 * Grab custom islandora mime type list
 * and add any missing ext/mimes to the drupal mapping
 */
function islandora_file_mimetype_mapping_alter(&$mapping) {
  $mime_detect = new MimeDetect();
  $types = $mime_detect->getMimeTypes();
  $diff = array_diff_key($types, $mapping['extensions']);
  foreach ($diff as $ext => $mime) {
    $mapping['mimetypes'][] = $mime;
    end($mapping['mimetypes']);
    $mapping['extensions'][$ext] = key($mapping['mimetypes']);
  }
}

/**
 * Hookable object access callback.
 *
 * @param string $op
 *   String identifying an operation to check. Should correspond to a
 *   permission declared via hook_permission().
 * @param AbstractObject $object
 *   An object to check for permissions.
 * @param object $user
 *   An optional loaded user object. Defaults to the global $user.
 *
 * @return bool
 *   TRUE if at least one implementation of hook_islandora_object_access()
 *   returned TRUE, and no implementation return FALSE; FALSE otherwise.
 */
function islandora_object_access($op, $object, $user = NULL) {
  $cache = &drupal_static(__FUNCTION__);

  if (!is_object($object)) {
    // The object could not be loaded... Presumably, we don't have
    // permission.
    return FALSE;
  }
  if ($user === NULL) {
    global $user;
  }

  // Populate the cache on a miss.
  if (!isset($cache[$op][$object->id][$user->uid])) {
    module_load_include('inc', 'islandora', 'includes/utilities');

    $results = islandora_invoke_hook_list('islandora_object_access', $object->models, array(
      $op,
      $object,
      $user,
    ));

    // Nothing returned FALSE, and something returned TRUE.
    $cache[$op][$object->id][$user->uid] = (!in_array(FALSE, $results, TRUE) && in_array(TRUE, $results, TRUE));
  }

  return $cache[$op][$object->id][$user->uid];
}

/**
 * Implements hook_islandora_object_access().
 *
 * Denies according to PID namespace restrictions, then passes or denies
 * according to core Drupal permissions according to user_access().
 */
function islandora_islandora_object_access($op, $object, $user) {
  module_load_include('inc', 'islandora', 'includes/utilities');

  return islandora_namespace_accessible($object->id) && user_access($op, $user);
}

/**
 * Hookable access callback for datastreams.
 *
 * Requires the equivalent permissions on the object.
 */
function islandora_datastream_access($op, $datastream, $user = NULL) {
  $cache = &drupal_static(__FUNCTION__);

  if (!is_object($datastream)) {
    // The object could not be loaded... Presumably, we don't have
    // permission.
    return NULL;
  }
  if ($user === NULL) {
    global $user;
  }

  // Populate the cache on a miss.
  if (!isset($cache[$op][$datastream->parent->id][$datastream->id][$user->uid])) {
    module_load_include('inc', 'islandora', 'includes/utilities');
    $object_results = islandora_invoke_hook_list('islandora_object_access', $datastream->parent->models, array(
      $op,
      $datastream->parent,
      $user,
    ));

    $datastream_results = islandora_invoke_hook_list('islandora_datastream_access', $datastream->parent->models, array(
      $op,
      $datastream,
      $user,
    ));

    // Neither the object nor the datastream check returned FALSE, and one in
    // the object or datastream checks returned TRUE.
    $cache[$op][$datastream->parent->id][$datastream->id][$user->uid] = (
      !in_array(FALSE, $object_results, TRUE) &&
      !in_array(FALSE, $datastream_results, TRUE) &&
      (in_array(TRUE, $object_results, TRUE) ||  in_array(TRUE, $datastream_results, TRUE))
    );
  }

  return $cache[$op][$datastream->parent->id][$datastream->id][$user->uid];
}

/**
 * Implements hook_islandora_basic_collection_get_query_filters().
 */
function islandora_islandora_basic_collection_get_query_filters() {
  $enforced = variable_get('islandora_namespace_restriction_enforced', FALSE);
  if ($enforced) {
    $namespace_array = islandora_get_allowed_namespaces();
    $namespace_sparql = implode('|', $namespace_array);
    return format_string('regex(str(?object), "info:fedora/(!namespaces):")', array(
      '!namespaces' => $namespace_sparql,
    ));
  }
}