You are here

tft.module in Taxonomy File Tree 7.2

Same filename and directory in other branches
  1. 8 tft.module
  2. 7 tft.module
  3. 3.x tft.module

Hook implementations and module logic for TFT.

File

tft.module
View source
<?php

/**
 * @file
 * Hook implementations and module logic for TFT.
 */

/**
 * @defgroup tft_permission List of permissions, defined as constants.
 * @{
 */
define('TFT_PERM__ACCESS_FULL_TREE', 'tft access file tree');
define('TFT_PERM__ADMIN', 'aminister tft');
define('TFT_PERM__REORDER_ITEMS', 'tft reorder terms');
define('TFT_PERM__ADD_FILE', 'tft add a file to any term');
define('TFT_PERM__ADD_TERMS', 'tft add child terms');
define('TFT_PERM__DELETE_TERMS', 'tft delete child terms');

/**
 * @} End of "defgroup tft_permission".
 */

/**
 * @defgroup tft_hook_implementations All hook implementations for TFT.
 * @{
 */

/**
 * Implementation of hook_menu().
 */
function tft_menu() {
  $menu = array(
    'tft/download/file/%' => array(
      'title' => 'Download',
      'page callback' => 'tft_download_file',
      'page arguments' => array(
        3,
      ),
      'access arguments' => array(
        'access content',
      ),
      'file' => 'includes/tft.pages.inc',
      'type' => MENU_CALLBACK,
    ),
    'tft/%' => array(
      'title' => "Taxonomy File Tree",
      'access arguments' => array(
        TFT_PERM__ACCESS_FULL_TREE,
      ),
      'page callback' => 'tft',
      'page arguments' => array(
        1,
      ),
      'file' => 'includes/tft.pages.inc',
      'type' => MENU_CALLBACK,
    ),
    'tft' => array(
      'title' => "Taxonomy File Tree",
      'page callback' => 'tft',
      'access arguments' => array(
        TFT_PERM__ACCESS_FULL_TREE,
      ),
      'file' => 'includes/tft.pages.inc',
      'type' => MENU_CALLBACK,
    ),
    'tft/term/add' => array(
      'title' => "Add a folder",
      'access callback' => 'tft_term_access',
      'access arguments' => array(
        NULL,
        NULL,
        'add-folder',
      ),
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'tft_add_term_form',
      ),
      'file' => 'includes/tft.pages.inc',
      'type' => MENU_CALLBACK,
    ),
    'tft/term/edit/%' => array(
      'title' => "Edit a folder",
      'access callback' => 'tft_term_access',
      'access arguments' => array(
        3,
        NULL,
        'edit',
      ),
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'tft_edit_term_form',
        3,
      ),
      'file' => 'includes/tft.pages.inc',
      'type' => MENU_CALLBACK,
    ),
    'tft/term/delete/%' => array(
      'title' => "Delete a folder",
      'access callback' => 'tft_term_access',
      'access arguments' => array(
        3,
        NULL,
        'delete',
      ),
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'tft_delete_term_form',
        3,
      ),
      'file' => 'includes/tft.pages.inc',
      'type' => MENU_CALLBACK,
    ),
    'tft/terms/reorder/%' => array(
      'title' => t("Reorder items"),
      'access callback' => 'tft_term_access',
      'access arguments' => array(
        3,
        NULL,
        'reorder',
      ),
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'tft_manage_folders_form',
        3,
      ),
      'file' => 'includes/tft.pages.inc',
    ),
    'tft/ajax/get-folder' => array(
      'title' => "Ajax callback",
      'access callback' => 'tft_term_access',
      'access arguments' => array(
        NULL,
        NULL,
        'view',
      ),
      'page callback' => 'tft_ajax_get_folder',
      'file' => 'includes/tft.ajax.inc',
      'type' => MENU_CALLBACK,
    ),
    'admin/config/media/tft' => array(
      'title' => "Taxonomy File Tree Settings",
      'access arguments' => array(
        TFT_PERM__ADMIN,
      ),
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'tft_settings_form',
      ),
      'file' => 'includes/tft.admin.inc',
    ),
  );
  return $menu;
}

/**
 * Implements hook_permission().
 */
function tft_permission() {
  return array(
    TFT_PERM__ACCESS_FULL_TREE => array(
      'title' => t("Access file tree"),
    ),
    TFT_PERM__ADMIN => array(
      'title' => t("Administer Taxonomy file tree"),
    ),
    TFT_PERM__REORDER_ITEMS => array(
      'title' => t("Reorder items in the tree"),
    ),
    TFT_PERM__ADD_FILE => array(
      'title' => t("Add file"),
    ),
    TFT_PERM__ADD_TERMS => array(
      'title' => t("Add new folders"),
    ),
    TFT_PERM__DELETE_TERMS => array(
      'title' => t("Remove folders"),
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function tft_block_info() {
  return array(
    'tft_file_tree' => array(
      'info' => 'TFT file explorer',
    ),
  );
}

/**
 * Implements hook_block_view().
 */
function tft_block_view() {
  return array(
    'subject' => 'File explorer',
    'content' => theme('tft_folder_explorer', array(
      'folders' => tft_output_tree(tft_folder_tree(0)),
      'vid' => variable_get('tft_vocabulary_vid', 0),
    )),
  );
}

/**
 * Implements hook_theme().
 */
function tft_theme() {
  return array(
    'tft' => array(
      'variables' => array(
        'folder_name' => NULL,
        'folder_menu' => NULL,
        'folder_content' => NULL,
        'folder_add_content_links' => NULL,
      ),
      'template' => 'theme/tft',
    ),
    'tft_folder_explorer' => array(
      'variables' => array(
        'folders' => NULL,
        'vid' => NULL,
      ),
      'template' => 'theme/tft-folder-explorer',
    ),
    'tft_folder_menu' => array(
      'variables' => array(
        'name' => NULL,
        'path' => '/',
        'ops_links' => '',
      ),
      'template' => 'theme/tft-folder-menu',
    ),
    'tft_manage_folders_form' => array(
      'render element' => 'form',
    ),
  );
}

/**
 * Implements hook_node_prepare().
 */
function tft_node_prepare($node) {
  $setting = tft_get_file_setting();
  if ($node->type == $setting['type']) {
    if (isset($_GET['tid']) && tft_term_access((int) $_GET['tid'])) {
      $node->tft_folder[LANGUAGE_NONE][0]['tid'] = (int) $_GET['tid'];
    }
  }
}

/**
 * Implements hook_form_alter()
 */
function tft_form_alter(&$form, &$form_state, $form_id) {
  $setting = tft_get_file_setting();
  switch ($form_id) {
    case $setting['type'] . '_node_form':
      $path = drupal_get_path('module', 'tft');
      drupal_add_js("{$path}/js/tft.select-folder.js");
      drupal_add_css("{$path}/css/tft.css");
      module_load_include('inc', 'tft', 'tft.pages');
      $form['tft_folder'][LANGUAGE_NONE]['#prefix'] = '<div class="tft-hide-element element-hidden">' . (!empty($form['tft_folder'][LANGUAGE_NONE]['#prefix']) ? $form['tft_folder'][LANGUAGE_NONE]['#prefix'] : '');
      $form['tft_folder'][LANGUAGE_NONE]['#suffix'] = (!empty($form['tft_folder'][LANGUAGE_NONE]['#suffix']) ? $form['tft_folder'][LANGUAGE_NONE]['#suffix'] : '') . '</div>';
      $form['tft_select_folder'] = array(
        '#type' => 'fieldset',
        '#title' => t("Select folder"),
        '#collapsible' => TRUE,
        '#collapsed' => FALSE,
        '#weight' => $form['tft_folder']['#weight'],
      );
      if (isset($_GET['tid'])) {
        $tid = $_GET['tid'];
      }
      elseif (isset($form['#node']->tft_folder[LANGUAGE_NONE][0]['tid'])) {
        $tid = $form['#node']->tft_folder[LANGUAGE_NONE][0]['tid'];
      }
      $tid = !empty($tid) ? $tid : 0;
      $form['tft_select_folder']['tft_js_folder'] = array(
        '#markup' => '<div id="folder-explorer-container" class="tft-node-form">' . tft_output_tree(tft_folder_tree(0, TRUE)) . '</div>',
      );
      $form['tft_selected_folder'] = array(
        '#type' => 'hidden',
        '#default_value' => $tid,
      );
      break;
  }
}

/**
 * Implements hook_image_default_styles().
 */
function tft_image_default_styles() {
  $styles = array();
  $styles['tft_thumb'] = array(
    'label' => 'TFT Image Preview',
    'effects' => array(
      array(
        'name' => 'image_scale_and_crop',
        'data' => array(
          'width' => 30,
          'height' => 20,
        ),
        'weight' => 0,
      ),
    ),
  );
  return $styles;
}

/**
 * @} End of "defgroup tft_hook_implementations".
 */

/**
 * @defgroup tft_api All API functions for TFT.
 * @{
 */

/**
 * Check if the user has access to the term.
 *
 * Will first check if the term is part of an OG term tree. If so, it will check if the user has access to the OG.
 * If the term is not part of an OG term tree, it will check against the Tac Lite schemes.
 *
 * @param int $tid
 * @param stdClass $account = NULL
 *        The user account to check against. If no account is given, the
 *        current user will be used.
 * @param string $op = 'view'
 *
 * @return boolean
 */
function tft_term_access($tid, $account = NULL, $op = 'view') {
  if (!$account) {
    global $user;
    $account = $user;
  }
  if ($account->uid == 1 || user_access(TFT_PERM__ADMIN, $account)) {
    return TRUE;
  }
  if (!isset($tid)) {
    if (!empty($_GET['tid'])) {
      $tid = $_GET['tid'];
    }
    elseif (!empty($_GET['parent'])) {
      $tid = $_GET['parent'];
    }
    else {
      $tid = 0;
    }
  }

  // Check if any other module has some saying in this matter.
  foreach (module_implements('tft_term_access') as $module) {
    $result = module_invoke($module, 'tft_term_access', $tid, $account, $op);
    if (isset($result)) {
      watchdog('returning access', '<pre>' . print_r($result, TRUE) . '</pre>');
      return $result;
    }
  }
  if ($op == 'view' && user_access(TFT_PERM__ACCESS_FULL_TREE, $account)) {
    return TRUE;
  }
  elseif (($op == 'edit' || $op == 'add-folder') && user_access(TFT_PERM__ADD_TERMS, $account)) {
    return TRUE;
  }
  elseif ($op == 'delete' && user_access(TFT_PERM__DELETE_TERMS, $account)) {
    return TRUE;
  }
  elseif ($op == 'add-file' && user_access(TFT_PERM__ADD_FILE, $account)) {
    return TRUE;
  }
  elseif ($op == 'add-folder' && user_access(TFT_PERM__ADD_TERMS, $account)) {
    return TRUE;
  }
  elseif ($op == 'reorder' && user_access(TFT_PERM__REORDER_ITEMS, $account)) {
    return TRUE;
  }
  else {
    watchdog('returning false at end', '<pre>' . print_r($account, TRUE) . '</pre>');
    return FALSE;
  }
}

/**
 * Get the folder content in HTML table format.
 *
 * @param int $tid
 *
 * @return string
 */
function tft_content_table($tid) {
  $headers = array(
    array(
      'data' => '<span>' . t("Name") . '</span>',
      'id' => 'table-th-name',
    ),
    array(
      'data' => '<span>' . t("Loaded by") . '</span>',
      'id' => 'table-th-loaded-by',
    ),
    array(
      'data' => '<span>' . t("Last modified") . '</span>',
      'id' => 'table-th-date',
    ),
    array(
      'data' => '<span>' . t("Type") . '</span>',
      'id' => 'table-th-type',
    ),
    array(
      'data' => '<span>' . t("Operations") . '</span>',
      'id' => 'table-th-ops',
    ),
  );
  $rows = tft_get_content($tid);
  return theme('table', array(
    'header' => $headers,
    'rows' => $rows,
  ));
}

/**
 * Output the tree as an HTML unordered list.
 * @see tft_output_children().
 *
 * @param array $tree
 *        The folder tree.
 *
 * @return string
 */
function tft_output_tree($tree) {
  return tft_output_children($tree, TRUE);
}

/**
 * Return the sub-tree as an unordered list.
 *
 * @param array $tree
 *        The folder tree.
 * @param boolean $root = FALSE
 *        A flag for setting this <ul> as the root <ul>.
 *
 * @return string
 */
function tft_output_children($tree, $root = FALSE) {
  $html = '<ul class="' . ($root ? 'root-folder' : 'sub-folder') . '">';
  $first = TRUE;
  $odd = TRUE;
  $count = count($tree);
  $i = 1;
  foreach ($tree as $tid => $item) {
    $span_class = '';
    if ($odd) {
      $odd = FALSE;
      $class = ' odd';
    }
    else {
      $odd = TRUE;
      $class = ' even';
    }
    if ($first) {
      $class .= ' first';
      $first = FALSE;
    }
    if ($i == $count) {
      $class .= ' last';
    }
    if (isset($item['children'])) {
      $class .= ' parent-folder closed';
      $span_class = ' closed-icon';
    }
    $html .= tft_li($item['name'], $tid, $class, $span_class);
    if (isset($item['children'])) {
      $html .= tft_output_children($item['children']);
    }
    $html .= '</li>';
    $i++;
  }
  $html .= '</ul>';
  return $html;
}

/**
 * Format an <li> tag for the file explorer.
 *
 * @param string $name
 *        The folder name
 * @param int $tid
 * @param string $li_class
 *        CSS classes for the <li>
 * @param string $span_class
 *        CSS classes for the child <span>
 *
 * @return string
 */
function tft_li($name, $tid, $li_class, $span_class) {
  return '<li id="tid-' . $tid . '" class="folder' . $li_class . '"><span class="icon' . $span_class . '"></span><span class="link-wrapper"><a href="#tft/' . $tid . '" class="folder-link">' . $name . '</a></span>';
}

/**
 * Get the folder content and return it in an array form for the theme_table call.
 *
 * @param int $tid
 *
 * @return array
 */
function tft_get_content($tid) {
  if (!tft_term_access($tid)) {
    drupal_access_denied();
    exit;
  }
  $content = array();
  $elements = tft_folder_content($tid, FALSE);
  $setting = tft_get_file_setting();
  $db_table = 'field_data_' . $setting['field'];
  $db_table = db_escape_field($db_table);
  $db_field = db_escape_field($setting['field'] . '_fid');
  foreach ($elements as $element) {
    if ($element['type'] == 'term') {
      if (!tft_term_access($element['id'])) {
        continue;
      }
      $term = taxonomy_term_load($element['id']);
      $content[] = array(
        tft_l($term->name, $element['id'], 'folder'),
        '',
        '',
        t("Folder"),
        tft_theme_item_operation_links(tft_item_operation_links('folder', $element['id'], $tid)),
      );
    }
    else {
      $node = node_load($element['id']);
      if (node_access('view', $node)) {
        if (db_table_exists($db_table)) {
          $result = db_query("SELECT f.filemime, f.filename, v.title, n.changed, n.uid FROM {node_revision} v\n                                  LEFT JOIN {node} n ON n.vid = v.vid\n                                    LEFT JOIN {" . $db_table . "} c ON c.revision_id = v.vid\n                                      LEFT JOIN {file_managed} f ON c.{$db_field} = f.fid\n                              WHERE n.nid = :nid AND n.status = 1", array(
            ':nid' => $element['id'],
          ));
          if ($row = $result
            ->fetchAssoc()) {
            $parts = explode('.', $row['filename']);
            $content[] = array(
              tft_l($row['title'], $element['id'], $row['filemime']),
              tft_get_username($row['uid']),
              date('d/m/Y H:i', $row['changed']),
              t('!type file', array(
                '!type' => strtoupper(array_pop($parts)),
              )),
              tft_theme_item_operation_links(tft_item_operation_links('file', $element['id'], $tid)),
            );
          }
        }
      }
    }
  }
  return $content;
}

/**
 * Format a folder or file link.
 *
 * @param string $title
 *        The link title
 * @param int $id
 *        Either the taxonomy term tid or the node nid
 * @param string $mime
 *        The mime type of the file (for a folder, use 'folder')
 *
 * @return string
 *        HTML string with the formatted link
 */
function tft_l($title, $id, $mime) {
  if ($mime == 'folder') {
    return '<a href="#tft/' . $id . '" class="folder-folder-link" id="tid-' . $id . '">' . $title . '</a>';
  }
  else {
    $class = 'file';
    $settings = tft_get_file_setting();
    switch ($mime) {
      case 'image/png':
      case 'image/jpeg':
      case 'image/gif':
        if (module_exists('image')) {
          $node = node_load($id);
          $icon = image_style_url('tft_thumb', $node->{$settings['field']}[LANGUAGE_NONE][0]['uri']);
          $class .= ' thumbnail';
          $href = file_create_url($node->{$settings['field']}[LANGUAGE_NONE][0]['uri']);
          if (module_exists('lightbox2')) {
            $class .= ' enable-lightbox lightbox';
          }
          break;
        }

      // Fall through to default if the image module is not available.
      default:

        // Get the filefield icon
        $file = (object) array(
          'filemime' => $mime,
        );
        $icon = base_path() . file_icon_path($file);
        $class .= ' generic';
        $href = "tft/download/file/{$id}";
        break;
    }
    return l($title, $href, array(
      'attributes' => array(
        'class' => $class,
        'style' => "background-image: url({$icon})",
        'target' => '_blank',
      ),
    ));
  }
}

/**
 * Get the username.
 *
 * @param int $uid
 *
 * @return string
 */
function tft_get_username($uid) {
  return db_query("SELECT name FROM {users} WHERE uid = :uid", array(
    ':uid' => $uid,
  ))
    ->fetchField();
}

/**
 * Loads the given folder content.
 *
 * Can optionally load only child folders. By default, will load folders and files.
 *
 * @param  int $tid
 * @param  bool $only_terms = FALSE
 *
 * @return array
 */
function tft_folder_content($tid, $only_terms = FALSE) {
  $content = array();

  // Get all child folders (terms)
  $result = db_query("SELECT td.tid FROM {taxonomy_term_data} td\n                        LEFT JOIN {taxonomy_term_hierarchy} th ON th.tid = td.tid\n                      WHERE th.parent = :ptid AND td.vid = :vid ORDER BY td.name", array(
    ':ptid' => $tid,
    ':vid' => variable_get('tft_vocabulary_vid', 0),
  ));
  while ($term = $result
    ->fetchObject()) {
    if (variable_get('tft_use_weight', 0) && ($res = db_query("SELECT weight FROM {tft_folder_content_weight} WHERE id = :tid AND type = 'term'", array(
      ':tid' => $term->tid,
    )))) {
      $weight = $res
        ->fetchField();
    }
    $content[] = array(
      'id' => $term->tid,
      'type' => 'term',
      'weight' => !empty($weight) ? $weight : 0,
    );
  }
  if ($only_terms) {
    if (variable_get('tft_use_weight', 0)) {
      usort($content, '_tft_array_weight_sort');
    }
    return $content;
  }

  // Get the files
  $result = db_query("SELECT DISTINCT(tn.nid) FROM {node_revision} v\n                        LEFT JOIN {node} n ON n.vid = v.vid\n                          LEFT JOIN {taxonomy_index} tn ON tn.nid = n.nid\n                      WHERE tn.tid = :tid AND n.status = 1 ORDER BY v.title", array(
    ':tid' => $tid,
  ));
  while ($file = $result
    ->fetchObject()) {
    if (variable_get('tft_use_weight', 0) && ($res = db_query("SELECT weight FROM {tft_folder_content_weight} WHERE id = :nid AND type = 'node'", array(
      ':nid' => $file->nid,
    )))) {
      $weight = $res
        ->fetchField();
    }
    $content[] = array(
      'id' => $file->nid,
      'type' => 'node',
      'weight' => !empty($weight) ? $weight : 0,
    );
  }
  if (variable_get('tft_use_weight', 0)) {
    usort($content, '_tft_array_weight_sort');
  }
  return $content;
}

/**
 * Implements hook_tft_item_operation_links_access()
 */
function tft_tft_item_operation_links_access($op, $type, $id, $parent_tid = NULL) {
  if ($type == 'folder') {
    if ($op == 'edit') {
      return user_access(TFT_PERM__ADD_TERMS);
    }
    elseif ($op == 'delete') {
      return user_access(TFT_PERM__DELETE_TERMS);
    }
  }
}

/**
 * Returns a list of operation links for the given item.
 *
 * @param  string $type
 *         The type of operation links. Can be 'folder' or 'file'.
 * @param  int $id
 *         Either the taxonomy term tid or the node nid.
 * @param  int $parent_tid = NULL
 *
 * @return array
 */
function tft_item_operation_links($type, $id, $parent_tid = NULL) {
  $links = array();
  $query = isset($_SESSION['tft']['q']) ? $_SESSION['tft']['q'] : '';
  switch ($type) {
    case 'folder':
      if (tft_term_access($id, NULL, 'edit')) {
        $links['edit'] = array(
          'title' => t("edit"),
          'href' => "tft/term/edit/{$id}",
          'attributes' => array(
            'class' => 'ops-link term-edit-link',
          ),
          'query' => array(
            'destination' => $query . (isset($parent_tid) ? "#tft/{$parent_tid}" : ''),
          ),
        );
      }
      if (tft_term_access($id, NULL, 'delete')) {
        $links['delete'] = array(
          'title' => t("delete"),
          'href' => "tft/term/delete/{$id}",
          'attributes' => array(
            'class' => 'ops-link term-edit-link',
          ),
          'query' => array(
            'destination' => $query . (isset($parent_tid) ? "#tft/{$parent_tid}" : ''),
          ),
        );
      }
      break;
    case 'file':
      $node = node_load($id);
      if (node_access('update', $node)) {
        $links['edit'] = array(
          'title' => t("edit"),
          'href' => "node/{$id}/edit",
          'attributes' => array(
            'class' => 'ops-link node-edit-link',
          ),
          'query' => array(
            'destination' => $query . (isset($parent_tid) ? "#tft/{$parent_tid}" : ''),
          ),
        );
      }
      $links['view'] = array(
        'title' => t("more info"),
        'href' => "node/{$id}",
        'attributes' => array(
          'class' => 'ops-link',
        ),
      );
      break;
  }
  drupal_alter('tft_item_operation_links', $links, $type, $id, $parent_tid);
  return $links;
}

/**
 * Helper function to check operation links access.
 */
function tft_item_operation_links_access($op, $type, $id, $parent_tid = NULL) {
  $results = module_invoke_all('tft_item_operation_links_access', 'edit', $type, $id, $parent_tid);
  foreach ($results as $result) {
    if ($result) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Render the add file and add folder links.
 *
 * @param int $tid = 0
 *        The term tid of the current folder, or 0 for root
 *
 * @return array
 */
function tft_get_add_content_links($tid = 0) {
  $links = array();
  $add_file_query = array(
    'destination' => $_SESSION['tft']['q'] . "#tft/{$tid}",
  );
  $add_term_query = array(
    'destination' => $_SESSION['tft']['q'] . "#tft/{$tid}",
  );
  $setting = tft_get_file_setting();

  // Do we have a tid ?
  if ($tid) {
    $add_file_query['tid'] = $tid;
    $add_term_query['parent'] = $tid;
  }

  // Can the user create files ?
  if (user_access('create ' . $setting['type'] . ' content') && tft_term_access($tid, NULL, 'add-file')) {
    $links['add_file'] = array(
      'title' => t("Add a file"),
      'href' => 'node/add/' . str_replace('_', '-', $setting['type']),
      'attributes' => array(
        'id' => 'add-child-file',
      ),
      'query' => array_reverse($add_file_query),
    );
  }
  if (tft_term_access($tid, NULL, 'add-folder')) {
    $links['add_term'] = array(
      'title' => t("Add a folder"),
      'href' => 'tft/term/add',
      'attributes' => array(
        'id' => 'add-child-folder',
      ),
      'query' => array_reverse($add_term_query),
    );
  }
  drupal_alter('tft_get_add_content_links', $links, $tid);
  return $links;
}

/**
 * Construct the folder tree.
 *
 * @param int $tid = 0
 * @param boolean $inclusive = FALSE
 *        Whether the current term should be included as well
 *
 * @return array
 */
function tft_folder_tree($tid = 0, $inclusive = FALSE) {
  $folders = array();
  $content = tft_folder_content($tid, TRUE);
  foreach ($content as $term) {
    if (tft_term_access($term['id'])) {
      $folders[$term['id']]['tid'] = $term['id'];
      $folders[$term['id']]['name'] = db_query("SELECT name FROM {taxonomy_term_data} WHERE tid = :tid", array(
        ':tid' => $term['id'],
      ))
        ->fetchField();
      $folders[$term['id']]['weight'] = $term['weight'];
      $folders[$term['id']]['parent'] = $tid ? $tid : 0;
      if ($child_terms = tft_folder_tree($term['id'])) {
        $folders[$term['id']]['children'] = $child_terms;
      }
    }
  }
  if ($inclusive) {
    if ($tid == 0) {
      $name = t("Root");
    }
    else {
      $name = db_query("SELECT name FROM {taxonomy_term_data} WHERE tid = :tid", array(
        ':tid' => $tid,
      ))
        ->fetchField();
    }
    $folders = array(
      $tid => array(
        'name' => $name,
        'tid' => $tid,
        'weight' => 0,
        'parent' => 0,
        'children' => $folders,
      ),
    );
  }
  return $folders;
}

/**
 * Get the parent tid based on a tid.
 *
 * @param int $tid
 *
 * @return int
 *        The parent tid or 0 if there's no parent. Will return -1 if the tid is null or 0.
 */
function tft_get_parent_tid($tid) {
  static $cache = array();
  if (!(int) $tid) {
    return -1;
  }
  if (isset($cache[$tid])) {
    return $cache[$tid];
  }
  $result = db_query("SELECT `parent` FROM {taxonomy_term_hierarchy} WHERE tid = :tid", array(
    ':tid' => $tid,
  ))
    ->fetchField();
  $cache[$tid] = is_null($result) ? -1 : $result;
  return (int) $cache[$tid];
}

/**
 * Get the depth of the term
 *
 * @param int $tid
 *        The taxonomy term tid
 *
 * @return int
 *        The depth of the term, or 0 if no valid term tid was given
 */
function tft_get_depth($tid) {
  static $cache = array();
  if (!$tid || !db_query("SELECT COUNT(tid) FROM {taxonomy_term_data} WHERE tid = :tid", array(
    ':tid' => $tid,
  ))
    ->fetchField()) {
    return 0;
  }
  if (isset($cache[$tid])) {
    return $cache[$tid];
  }
  $depth = 0;
  $pid = $tid;
  while ($pid = db_query("SELECT parent FROM {taxonomy_term_hierarchy} WHERE tid = :tid", array(
    ':tid' => $pid,
  ))
    ->fetchField()) {
    $depth++;
  }
  $cache[$tid] = $depth;
  return $depth;
}

/**
 * Helper function to sort item arrays with usort().
 */
function _tft_array_weight_sort($a, $b) {
  if ($a['weight'] != $b['weight']) {
    return $a['weight'] < $b['weight'] ? -1 : 1;
  }
  return 0;
}

/**
 * Construct the folder tree recursively.
 *
 * @param  int $tid
 * @param  bool $inclusive = FALSE
 *         Whether to include the passed term.
 *
 * @return array
 */
function tft_tree($tid = 0, $inclusive = FALSE) {
  $folders = array();
  $content = tft_folder_content($tid);
  foreach ($content as $item) {
    $folders[$item['id']]['weight'] = isset($item['weight']) ? $item['weight'] : 0;
    $folders[$item['id']]['parent'] = $tid ? $tid : 0;
    $folders[$item['id']]['type'] = $item['type'];
    if ($item['type'] == 'term' && tft_term_access($item['id'])) {
      $folders[$item['id']]['tid'] = $item['id'];
      $folders[$item['id']]['name'] = db_query("SELECT name FROM {taxonomy_term_data} WHERE tid = :tid", array(
        ':tid' => $item['id'],
      ))
        ->fetchField();
      if ($child_terms = tft_tree($item['id'])) {
        $folders[$item['id']]['children'] = $child_terms;
      }
    }
    elseif ($item['type'] == 'node' && node_access('view', node_load($item['id']))) {
      $folders[$item['id']]['nid'] = $item['id'];
      $folders[$item['id']]['name'] = db_query("SELECT v.title FROM {node} n LEFT JOIN {node_revision} v ON v.vid = n.vid WHERE n.nid = :nid", array(
        ':nid' => $item['id'],
      ))
        ->fetchField();
    }
  }
  if ($inclusive) {
    if ($tid == 0) {
      $name = t("Root");
    }
    else {
      $name = db_query("SELECT name FROM {taxonomy_term_data} WHERE tid = :tid", array(
        ':tid' => $tid,
      ))
        ->fetchField();
    }
    $folders = array(
      $tid => array(
        'name' => $name,
        'tid' => $tid,
        'weight' => 0,
        'parent' => 0,
        'type' => 'term',
        'children' => $folders,
      ),
    );
  }
  return $folders;
}

/**
 * Get the settings for the node type used as the 'file'.
 *
 * This function is kept for historical reasons. These settings cannot be changed anymore.
 *
 * @return array
 *        An array with a 'type' key for the node type and a 'field' key for the file field.
 */
function tft_get_file_setting() {
  $temp = explode('-', variable_get('tft_content_type', 'tft_file-tft_file'));
  return array(
    'type' => $temp[0],
    'field' => $temp[1],
  );
}

/**
 * Return a list of links for the folder menu.
 *
 * Links include:
 *  - "go to parent"
 *  - "reorder elements"
 *
 * @param int $tid
 *
 * @return array
 */
function tft_get_folder_menu_links($tid) {
  $links = array();
  $parent_tid = tft_get_parent_tid($tid);
  $disabled = FALSE;
  if ($parent_tid >= 0 && $tid != $_SESSION['tft']['root_tid']) {
    if (!tft_term_access($parent_tid)) {
      $disabled = TRUE;
    }
  }
  else {
    $disabled = TRUE;
  }
  $links['parent_folder'] = array(
    'title' => t("parent folder"),
    'href' => 'tft',
    'fragment' => $disabled ? 'tft/0' : "tft/{$parent_tid}",
    'external' => TRUE,
    'attributes' => array(
      'id' => 'tft-back',
      'class' => array(
        'folder-menu-ops-link',
        $disabled ? 'disabled' : 'enabled',
      ),
    ),
  );
  if (user_access(TFT_PERM__REORDER_ITEMS)) {
    $links['reorder_items'] = array(
      'title' => t("reorder elements"),
      'href' => "tft/terms/reorder/{$tid}",
      'attributes' => array(
        'id' => 'manage-folders',
        'class' => array(
          'folder-menu-ops-link',
        ),
      ),
      'query' => array(
        'destination' => (!empty($_SESSION['tft']['q']) ? $_SESSION['tft']['q'] : '') . "#tft/{$tid}",
      ),
    );
  }
  drupal_alter('tft_folder_menu_links', $links, $tid);
  return $links;
}

/**
 * Helper function to render the operation links.
 *
 * @param  array $links
 *
 * @return string
 */
function tft_theme_folder_menu_links($links) {

  // We don't use the theme('links') Drupal function, as we want to set custom classes and IDs on the <li>s.
  // This is not possible (without a lot of overhead) with theme('links').
  $html = '<ul class="tabs primary" id="folder-menu-ops-links">';
  foreach ($links as $link) {
    $html .= '<li class="folder-menu-ops-link" id="' . (isset($link['attributes']['id']) ? $link['attributes']['id'] : '') . '">';

    // Change the ID for the <a>.
    if (isset($link['attributes']['id'])) {
      $link['attributes']['id'] .= '-link';
    }
    $html .= l($link['title'], $link['href'], $link);
    $html .= '</li>';
  }
  $html .= '</ul>';
  return $html;
}

/**
 * Helper function to render the content addition links.
 *
 * @param  array $links
 *
 * @return string
 */
function tft_theme_add_content_links($links) {

  // We don't use the theme('links') Drupal function, as we want to set custom classes and IDs on the <li>s.
  // This is not possible (without a lot of overhead) with theme('links').
  $html = '<ul id="folder-add-content-links">';
  foreach ($links as $link) {
    $html .= '<li class="folder-add-content-link" id="' . (isset($link['attributes']['id']) ? $link['attributes']['id'] : '') . '">';

    // Change the ID for the <a>.
    if (isset($link['attributes']['id'])) {
      $link['attributes']['id'] .= '-link';
    }
    $html .= l($link['title'], $link['href'], $link);
    $html .= '</li>';
  }
  $html .= '</ul>';
  return $html;
}

/**
 * Helper function to render the item operation links.
 *
 * @param  array $links
 *
 * @return string
 */
function tft_theme_item_operation_links($links) {
  return theme('links', array(
    'links' => $links,
    'attributes' => array(
      'class' => array(
        'inline',
      ),
    ),
  ));
}

/**
 * Add a new term and return its ID.
 *
 * @param  string $name
 * @param  int $parent
 *
 * @return int
 */
function tft_add_term($name, $parent) {

  // Add the term data
  $tid = db_insert('taxonomy_term_data')
    ->fields(array(
    'tid' => NULL,
    'vid' => variable_get('tft_vocabulary_vid', 0),
    'name' => $name,
    'description' => '',
    'weight' => 0,
  ))
    ->execute();
  if (empty($parent)) {
    $parent = 0;
  }

  // Add the term hierarchy
  db_insert('taxonomy_term_hierarchy')
    ->fields(array(
    'tid' => $tid,
    'parent' => $parent,
  ))
    ->execute();
  return $tid;
}

/**
 * Update the term name.
 *
 * @param  int $tid
 * @param  string $name
 */
function tft_update_term($tid, $name) {
  db_update('taxonomy_term_data')
    ->fields(array(
    'name' => $name,
  ))
    ->condition('tid', $tid)
    ->execute();
}

/**
 * Check if the term has no files or child terms.
 *
 * @param  int $tid
 *
 * @return bool
 */
function tft_check_term_is_deletable($tid) {
  $count = (int) db_query("SELECT COUNT(tid) FROM {taxonomy_term_hierarchy} WHERE parent = :tid", array(
    ':tid' => $tid,
  ))
    ->fetchField();
  if ($count) {
    return FALSE;
  }
  $count = (int) db_query("SELECT COUNT({taxonomy_index}.nid) FROM {taxonomy_index}\n                              RIGHT JOIN {node} ON {node}.nid = {taxonomy_index}.nid\n                            WHERE {taxonomy_index}.tid = :tid", array(
    ':tid' => $tid,
  ))
    ->fetchField();
  if ($count) {
    return FALSE;
  }
  return TRUE;
}

/**
 * @} End of "defgroup tft_api".
 */

/**
 * @deprecated Archive folders are OG specific
 *
 * Checks if the term is an "Archive" term.
 * These cannot be edited or deleted.
 *
 * @param int $tid
 *
 * @return bool
 */

// @todo OG logic
function tft_is_archive_folder($tid) {

  // Must be a direct child.
  if (tft_get_depth($tid) == 1) {
    $title = db_select('taxonomy_term_data', 't')
      ->fields('t', array(
      'name',
    ))
      ->condition('tid', $tid)
      ->execute()
      ->fetchField();
    if ($title == t('Archives')) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * @deprecated Archive folders are OG specific
 *
 * Finds the archive folder for the current OG term.
 *
 * @param int $og_tid
 *
 * @return int|null
 */

// @todo OG logic
function tft_get_archive_tid($og_tid) {
  return db_query("SELECT td.tid FROM {taxonomy_term_data} td\n                    LEFT JOIN {taxonomy_term_hierarchy} th ON th.tid = td.tid\n                   WHERE th.parent = :tid AND td.name = :name", array(
    ':name' => t('Archives'),
    ':tid' => $og_tid,
  ))
    ->fetchField();
}

/**
 * @deprecated Archive folders are OG specific
 *
 * Checks if the term is already archived.
 *
 * @param int $tid
 *
 * @return bool
 */

// @todo OG logic
function tft_is_term_archived($tid) {
  $og_nid = tft_get_og_nid($tid);
  $og_tid = tft_get_og_tid($og_nid);
  $archive_tid = tft_get_archive_tid($og_tid);
  $root_tid = $tid;
  $depth = tft_get_depth($tid);
  while ($depth > 1 && $root_tid) {
    $root_tid = db_query("SELECT parent FROM {taxonomy_term_hierarchy} WHERE tid = :tid", array(
      ':tid' => $tid,
    ))
      ->fetchField();
    $depth--;
  }
  return $root_tid == $archive_tid;
}

/**
 * @deprecated Archive folders are OG specific
 *
 * Checks if the file is already archived.
 *
 * @param int $nid
 *
 * @return bool
 */

// @todo OG logic
function tft_is_file_archived($nid) {
  $node = node_load($nid);
  $folder_tids = array();

  // Get original folder
  foreach (array_keys($node->taxonomy) as $tid) {
    if (variable_get('tft_vocabulary_vid', 0) == db_query("SELECT vid FROM {taxonomy_term_data} WHERE tid = :tid", array(
      ':tid' => $tid,
    ))
      ->fetchField()) {
      $folder_tids[] = $tid;
    }
  }
  foreach ($folder_tids as $tid) {
    if (tft_is_term_archived($tid)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * @deprecated move this to tft_og
 *
 * Check if the current term is part of a OG term and return the OG nid. If no nid is found, return FALSE.
 *
 * @param int $tid
 *        The tid (and its ancestor tree) to check against
 *
 * @return int|boolean
 *        The OG nid if found, else FALSE
 */
function tft_get_og_nid($tid) {
  static $cache = array();
  if (is_array($tid)) {
    $tid = $tid[0];
  }
  $tid = (int) $tid;
  if (!$tid) {
    return FALSE;
  }
  if (isset($cache[$tid])) {
    return $cache[$tid];
  }
  $param_tid = $tid;
  $depth = tft_get_depth($tid);
  $og_nid = db_query("SELECT og_nid FROM {tft_tid_og_nid} WHERE tid = :tid", array(
    ':tid' => $tid,
  ))
    ->fetchField();
  while ($depth && $tid && !$og_nid) {
    $tid = db_query("SELECT parent FROM {taxonomy_term_hierarchy} WHERE tid = :tid", array(
      ':tid' => $tid,
    ))
      ->fetchField();
    $depth--;
    $og_nid = db_query("SELECT og_nid FROM {tft_tid_og_nid} WHERE tid = :tid", array(
      ':tid' => $tid,
    ))
      ->fetchField();
  }
  if ($og_nid) {
    $cache[$param_tid] = (int) $og_nid;
  }
  else {
    $cache[$param_tid] = FALSE;
  }
  return $cache[$param_tid];
}

/**
 * @deprecated move this to tft_og
 *
 * Get the term tid associated with the OG.
 *
 * @param int $nid
 *        The OG nid
 *
 * @return int|NULL
 *        The term tid
 */
function tft_get_og_tid($nid) {
  return db_query("SELECT tid FROM {tft_tid_og_nid} WHERE og_nid = :nid", array(
    ':nid' => $nid,
  ))
    ->fetchField();
}

/**
 * @deprecated
 * Check that a valid vocabulary vid is set in the settings. Else, redirect to either the settings page (if the user has access)
 * or the home page.
 */
function tft_check_vocabulary_setting() {
  if (!variable_get('tft_vocabulary_vid', 0) || !db_query("SELECT COUNT(vid) FROM {taxonomy_vocabulary} WHERE vid = :vid", array(
    ':vid' => variable_get('tft_vocabulary_vid', 0),
  ))
    ->fetchField()) {
    drupal_set_message(t("You must first enter which vocabulary is used by Taxonomy File Tree."), 'error');
    watchdog('tft', "TFT isn't properly configured. A valid vocabulary must be set as the TFT vocabulary.", array(), WATCHDOG_ERROR);
    if (user_access(TFT_PERM__ADMIN)) {
      drupal_goto('admin/settings/tft');
    }
    else {
      drupal_goto();
    }
  }
}

/**
 * @deprecated Client specific. REmove.
 * Get an array with all the terms to which the user has no access.
 *
 * @param int $tid = 0
 *        The root term tid. 0 by default (looks through the entire tree)
 * @param array() &$forbidden
 *        The forbidden elements array
 * @param stdClass $account = NULL
 *        An optional account to test access. By default, the current user is used.
 */
function tft_get_forbidden_terms($tid = 0, &$forbidden, $account = NULL) {

  // Start by all root terms, and build up from there
  $result = db_query("SELECT tid FROM {taxonomy_term_hierarchy} WHERE parent = :tid AND vid = :vid", array(
    ':tid' => $tid,
    ':vid' => variable_get('tft_vocabulary_vid', 0),
  ));
  while ($tid = $result
    ->fetchObject()) {
    if (!tft_term_access($tid, $account)) {
      $forbidden[] = $tid;
    }
    else {
      tft_get_forbidden_terms($tid, $forbidden, $account);
    }
  }
}

/**
 * @deprecated Archives are OG specific
 *
 * Defines a batch for adding an archive folder to all groups that do not have one.
 */
function tft_archive_folder_batch() {
  $batch = array(
    'title' => t("Adding archive folders"),
    'operations' => array(
      array(
        'tft_archive_folder_batch_process',
        array(),
      ),
    ),
  );
  batch_set($batch);
}

/**
 * @deprecated Archives are OG specific
 *
 * Processes the batch logic.
 */
function tft_archive_folder_batch_process(&$context) {
  module_load_include('inc', 'tft', 'tft.admin');
  if (empty($context['sandbox'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = (int) db_query("SELECT COUNT(*) FROM {node} WHERE type = 'og'")
      ->fetchField();
    $context['finished'] = 0;
  }
  $result = db_query("SELECT * FROM {node} WHERE type = 'og' LIMIT :limit, 10", array(
    ':limit' => $context['sandbox']['progress'],
  ));
  while ($node = $result
    ->fetchObject()) {
    $og_tid = tft_get_og_tid($node->nid);
    $archive_tid = tft_get_archive_tid($og_tid);
    if (empty($archive_tid)) {
      tft_add_term("Archives", $og_tid);
    }
    $context['sandbox']['progress']++;
  }

  // Set the percentage for the loadbar
  if ($context['sandbox']['progress'] < $context['sandbox']['max'] && !$stop) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
  else {
    $context['finished'] = 1;
  }
}

/**
 * @deprecated Archives are OG specific
 *
 */
function tft_log_archive($id, $type, $previous_tid, $og_nid) {
  db_insert('tft_archive_restore')
    ->fields(array(
    'id' => $id,
    'type' => $type,
    'previous_parent_tid' => $previous_tid,
    'og_nid' => $og_nid,
  ))
    ->execute();
}

/**
 * @param $id
 * @param $type
 * @param $og_nid
 * @return bool
 *
 * @deprecated Archives are OG specific
 */
function tft_restore_archived_element($id, $type, $og_nid) {
  $log = db_query("SELECT * FROM {tft_archive_restore} WHERE type = :type AND id = :id", array(
    ':type' => $type,
    ':id' => $id,
  ))
    ->fetchAssoc();
  $og_tid = tft_get_og_tid($og_nid);
  $return = TRUE;
  if (empty($log)) {
    $return = FALSE;
    $log = array(
      'id' => $id,
      'previous_parent_tid' => $og_tid,
    );
  }
  elseif (!db_query("SELECT COUNT(*) FROM {taxonomy_term_data} WHERE tid = :tid", array(
    ':tid' => $log['previous_parent_tid'],
  ))
    ->fetchField()) {
    $return = FALSE;
    $log['previous_parent_tid'] = $og_tid;
  }
  else {
    if (tft_is_term_archived($log['previous_parent_tid'])) {
      $return = FALSE;
      $log['previous_parent_tid'] = $og_tid;
    }
  }
  if ($type == 'node') {
    $archive_tid = tft_get_archive_tid($og_tid);
    $node = node_load($log['id']);

    // Get original folder.
    // Reconstruct list of terms by removing all folder terms.
    $taxonomy = array();
    foreach ($node->taxonomy as $term) {
      if ($term->tid != $archive_tid) {
        $taxonomy[] = $term->tid;
      }
    }
    $taxonomy[] = $log['previous_parent_tid'];
    $node->taxonomy = $taxonomy;
    node_save($node);
  }
  else {
    db_update('taxonomy_term_hierarchy')
      ->fields(array(
      'parent' => $log['previous_parent_tid'],
    ))
      ->condition('tid', $log['id'])
      ->execute();
  }
  db_delete('tft_archive_restore')
    ->condition('id', $id)
    ->condition('type', $type)
    ->execute();
  return $return;
}

/**
 * @deprecated Archives are OG specific
 */
function tft_restore_element($og_nid, $id, $type) {
  if (tft_restore_archived_element($id, $type, $og_nid)) {
    drupal_set_message("L'élément a été restauré à son ancien emplacement.");
  }
  else {
    drupal_set_message("Impossible de restaurer l'élément à son ancien emplacement, car il n'existe plus. Il a été restauré à la racine de l'arborescence.", 'warning');
  }
  if (isset($_GET['destination'])) {
    drupal_goto($_GET['destination']);
  }
  else {
    drupal_goto();
  }
}

Functions

Namesort descending Description
tft_add_term Add a new term and return its ID.
tft_archive_folder_batch Deprecated
tft_archive_folder_batch_process Deprecated
tft_block_info Implements hook_block_info().
tft_block_view Implements hook_block_view().
tft_check_term_is_deletable Check if the term has no files or child terms.
tft_check_vocabulary_setting Deprecated
tft_content_table Get the folder content in HTML table format.
tft_folder_content Loads the given folder content.
tft_folder_tree Construct the folder tree.
tft_form_alter Implements hook_form_alter()
tft_get_add_content_links Render the add file and add folder links.
tft_get_archive_tid
tft_get_content Get the folder content and return it in an array form for the theme_table call.
tft_get_depth Get the depth of the term
tft_get_file_setting Get the settings for the node type used as the 'file'.
tft_get_folder_menu_links Return a list of links for the folder menu.
tft_get_forbidden_terms Deprecated
tft_get_og_nid Deprecated
tft_get_og_tid Deprecated
tft_get_parent_tid Get the parent tid based on a tid.
tft_get_username Get the username.
tft_image_default_styles Implements hook_image_default_styles().
tft_is_archive_folder
tft_is_file_archived
tft_is_term_archived
tft_item_operation_links Returns a list of operation links for the given item.
tft_item_operation_links_access Helper function to check operation links access.
tft_l Format a folder or file link.
tft_li Format an <li> tag for the file explorer.
tft_log_archive Deprecated
tft_menu Implementation of hook_menu().
tft_node_prepare Implements hook_node_prepare().
tft_output_children Return the sub-tree as an unordered list.
tft_output_tree Output the tree as an HTML unordered list.
tft_permission Implements hook_permission().
tft_restore_archived_element Deprecated
tft_restore_element Deprecated
tft_term_access Check if the user has access to the term.
tft_tft_item_operation_links_access Implements hook_tft_item_operation_links_access()
tft_theme Implements hook_theme().
tft_theme_add_content_links Helper function to render the content addition links.
tft_theme_folder_menu_links Helper function to render the operation links.
tft_theme_item_operation_links Helper function to render the item operation links.
tft_tree Construct the folder tree recursively.
tft_update_term Update the term name.
_tft_array_weight_sort Helper function to sort item arrays with usort().

Constants