You are here

flag_lists.module in Flag Lists 6

The Flag Lists module.

Extends flag to allow individual users to create personal flags.

File

flag_lists.module
View source
<?php

module_load_include('inc', 'flag', 'includes/flag.admin');
module_load_include('inc', 'flag', 'flag');

/**
 * @file
 * The Flag Lists module.
 *
 * Extends flag to allow individual users to create personal flags.
 */

/**
 * Implementation of hook_menu().
 */
function flag_lists_menu() {
  $items = array();
  $items['admin/build/flags/lists'] = array(
    'title' => 'Lists',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'flag_lists_settings_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer flags',
    ),
    'description' => 'Configure default settings allowing users to mark content with personal flags.',
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );
  $items['admin/build/flags/lists/settings'] = array(
    'title' => 'Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'flag_lists_settings_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer flags',
    ),
    'description' => 'Configure default settings allowing users to mark content with personal flags.',
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 1,
  );
  $items['admin/build/flags/lists/list'] = array(
    'title' => 'List',
    'page callback' => 'flag_lists_admin_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer flags',
    ),
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
  );
  $items['admin/build/flags/lists/template'] = array(
    'title' => 'New template',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'flag_lists_create_template_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer flags',
    ),
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 3,
  );
  if (module_exists('devel_generate')) {
    $items['admin/generate/flag_lists'] = array(
      'title' => 'Generate lists',
      'description' => 'Generate a given number of lists and listings on site content. Optionally delete existing lists and listings.',
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'flag_lists_generate_lists_form',
      ),
      'access callback' => 'user_access',
      'access arguments' => array(
        'administer flags',
      ),
      'file' => 'flag_lists.admin.inc',
    );
  }
  $items['flags/lists/add/%'] = array(
    'title' => 'Add a list',
    'page callback' => 'flag_lists_add',
    'page arguments' => array(
      3,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'create flag lists',
    ),
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['flags/lists/edit/%'] = array(
    'title' => 'Edit a list',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'flag_lists_form',
      3,
    ),
    'access callback' => 'flag_lists_is_owner',
    'access arguments' => array(
      2,
      3,
    ),
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['flags/lists/delete/%'] = array(
    'title' => 'Delete a list',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'flag_lists_delete_confirm',
      3,
    ),
    'access callback' => 'flag_lists_is_owner',
    'access arguments' => array(
      2,
      3,
    ),
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/build/flag_lists/rebuild'] = array(
    'title' => 'Rebuild all flag lists',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'flag_lists_rebuild_confirm',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer flag lists',
    ),
    'file' => 'flag_lists.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['flag-lists'] = array(
    'title' => 'Flag',
    'page callback' => 'flag_lists_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['user/%/flags/lists'] = array(
    'title' => 'Lists',
    'page callback' => 'flag_lists_user_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'view flag lists',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['user/%/flags/lists/%'] = array(
    'title' => 'Listed content',
    'page callback' => 'flag_lists_user_list',
    'page arguments' => array(
      1,
      4,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'view flag lists',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * User flag page. Display a list of user-created flag lists.
 */
function flag_lists_user_page($uid) {

  // Can we use our default view?
  if (module_exists('views')) {
    $view = views_get_view('flag_lists_user_lists', FALSE);
    if (!empty($view)) {
      $view
        ->set_display('default');
      $view
        ->set_arguments(array(
        $uid,
      ));
      $output = $view
        ->render();
      drupal_set_title($view->build_info['title']);
    }
    return $output;
  }
  else {
    return theme('flag_lists_user_page', $uid);
  }
}

/**
 * Theme the output for a user flag administration page.
 */
function theme_flag_lists_user_page($uid) {
  $account = user_load($uid);
  drupal_set_title(t('Lists'));
  if ($flags = flag_lists_get_user_flags(NULL, $account)) {

    // Build the list of flag lists for this node.
    foreach ($flags as $flag) {
      $ops = theme('flag_lists_ops', $flag);
      $items[] = l($flag->title, "user/{$uid}/flags/lists/" . $flag->fid) . $ops;
    }
  }
  drupal_add_css(drupal_get_path('module', 'flag_lists') . '/theme/flag_lists.css');
  return theme('item_list', $items);
}

/**
 * List the contents of a user-defined list
 */
function flag_lists_user_list($uid, $fid) {

  // Can we use our default view?
  if (module_exists('views')) {
    $view = views_get_view('flag_lists_user_list', FALSE);
    if (!empty($view)) {
      $view
        ->set_display('default');
      $view
        ->set_arguments(array(
        $fid,
      ));
      $output = $view
        ->render();
      drupal_set_title($view->build_info['title']);
    }
    return $output;
  }
  else {
    return theme('flag_lists_user_list', $uid, $fid);
  }
}

/**
 * Theme the output of user-defined list page
 */
function theme_flag_lists_user_list($uid, $fid) {
  $flag = flag_lists_get_flag($fid);
  drupal_set_title($flag->title);
  $content = flag_lists_get_flagged_content($fid, $uid);
  foreach ($content as $item) {
    if ($item->content_type == 'node') {
      $node = node_load($item->content_id);
      $items[] = l($node->title, 'node/' . $node->nid);
    }
  }
  $breadcrumb = menu_get_active_breadcrumb();
  $breadcrumb[] = l(t('@name lists', array(
    '@name' => drupal_ucfirst(variable_get('flag_lists_name', t('lists'))),
  )), 'user/' . arg(1) . '/flags/lists');
  drupal_set_breadcrumb($breadcrumb);
  return theme('item_list', $items);
}

/**
 * Implementation of hook_theme().
 */
function flag_lists_theme() {
  $path = drupal_get_path('module', 'flag') . '/theme';
  return array(
    'flag_lists_list' => array(
      'arguments' => array(
        'content_type' => NULL,
        'content_id' => NULL,
      ),
    ),
    'flag_lists_admin_page' => array(
      'arguments' => array(
        'flags' => NULL,
      ),
    ),
    'flag_lists_user_page' => array(
      'arguments' => array(
        'uid' => NULL,
      ),
    ),
    'flag_lists_user_list' => array(
      'arguments' => array(
        'flag_name' => NULL,
      ),
    ),
    'flag_lists_ops' => array(
      'arguments' => array(
        'flag' => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_perm().
 */
function flag_lists_perm() {
  return array(
    'create flag lists',
    'edit own flag lists',
    'delete own flag lists',
    'view flag lists',
  );
}

/**
 * Implementation of hook_form_alter().
 */
function flag_lists_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {
    case 'flag_form':

      // A template flag should always have a record in the flag_lists_types table.
      $result = db_query("SELECT * FROM {flag_lists_types}");
      while ($type = db_fetch_array($result)) {
        $types[$type['name']] = $type['type'];
      }
      if (isset($types[$form['name']['#default_value']])) {
        $form['name']['#type'] = 'value';
        $form['global']['#type'] = 'value';
        $form['title']['#description'] = t('A short, descriptive title for this template. It will be used in administrative interfaces to refer to this template.');

        // Warn about types that already have a template.
        foreach ($form['access']['types']['#options'] as $option => $value) {
          if (in_array($option, $types) && $form['access']['types']['#default_value'] != $option) {
            $form['access']['types']['#options'][$option] .= '<span class="description">' . t(' (Already has a template.) ') . '</span>';
          }
        }
        $form['access']['types']['#description'] .= t(' A type may only be selected in one list template.');

        // Unset anon permissions for now. @todo allow anon listing.
        unset($form['access']['roles']['flag']['#options'][1]);
        unset($form['access']['roles']['unflag']['#options'][1]);
        foreach (element_children($form['display']) as $display) {
          $form['display'][$display]['#type'] = 'value';
        }
        $form['display']['link_type']['#default_value'] = 'fl_template';
        $form['display']['#description'] = t('Unlike normal flags, lists are only displayed in a block provided by this module or in views blocks. See <a href="/admin/build/block/list">the block admin page</a> to place the block.');
        $form['#validate'][] = 'flag_lists_template_validate';
        $form['#submit'][] = 'flag_lists_template_submit';
      }
      break;
  }
}
function flag_lists_template_validate($form, &$form_state) {
  $types = array_filter($form_state['values']['types']);
  $errors = array();
  foreach ($types as $type) {
    if ($result = db_query("SELECT * FROM {flag_lists_types} WHERE type = '%s' AND name <> '%s'", $type, $form_state['values']['name'])) {
      while ($errors = db_fetch_array($result)) {
        $content_types[] = $errors['type'];
        $templates[] = $errors['name'];
      }
    }
  }
  if (count($content_types)) {
    $content_types = implode(', ', $content_types);
    $templates = implode(', ', array_unique($templates));
    form_set_error('types', t('The flaggable content type(s) "@type" is(are) already assigned to the template(s) "@template." A content type may be assigned to only one template. To reassign a content type you must first remove its other assignment.', array(
      '@type' => $content_types,
      '@template' => $templates,
    )));
  }
}
function flag_lists_template_submit($form, &$form_state) {
  $types = array_filter($form_state['values']['types']);

  // Clean out the old types, then add the new.
  db_query("DELETE FROM {flag_lists_types} WHERE name = '%s'", $form_state['values']['name']);
  foreach ($types as $type) {
    db_query("INSERT INTO {flag_lists_types} (name, type) VALUES ('%s', '%s')", $form_state['values']['name'], $type);
  }
}

/**
 * Helper function to build an array of all lists available to or owned by the
 * current user and that are available on the current content type.
 */
function flag_lists_get_content_fids() {
  global $user;

  // This is a node view. We only care about nodes for now.
  if (arg(0) == 'node' && is_numeric(arg(1)) && is_null(arg(2))) {
    $type = db_result(db_query("SELECT type from {node} WHERE nid = %d", arg(1)));

    // Get current user's flags for this node.
    $fc_result = db_query("SELECT f.fid\n        FROM {flag_lists} fc\n        LEFT JOIN {flag_types} fn ON fn.fid = fc.fid\n        LEFT JOIN {flags} f ON fc.fid = f.fid\n        WHERE fc.uid = %d\n        AND fn.type = '%s'", $user->uid, $type);
    while ($row = db_fetch_array($fc_result)) {
      $fids[] = $row['fid'];
    }
  }
  elseif (arg(0) == 'flag' && (arg(1) == 'flag' || arg(1) == 'unflag')) {

    // Get the flag for this request.
    $fids[] = db_result(db_query("SELECT f.fid\n        FROM {flags} f\n        WHERE f.name = '%s'", arg(2)));
  }

  // Get the regular flags for this node. The flag module will narrow by role,
  // etc. when flag_get_flags() is called. These flag ids are always returned.
  $f_result = db_query("SELECT f.fid\n        FROM {flags} f\n        LEFT JOIN {flag_lists} fc ON fc.fid = f.fid\n        WHERE fc.fid IS NULL");
  while ($row = db_fetch_array($f_result)) {
    $fids[] = $row['fid'];
  }
  if (is_array($fids)) {
    return array_unique($fids);
  }
  else {
    return array();
  }
}

/**
 * Implementation of hook_block().
 *
 * A block of personal flags for the current user on the current node.
 */
function flag_lists_block($op = 'list', $delta = 0, $edit = array()) {
  if ($op == 'list') {
    $blocks[0]['info'] = t('Lists');
    $blocks[0]['cache'] = BLOCK_NO_CACHE;
    return $blocks;
  }
  else {
    if ($op == 'configure' && $delta == 0) {
      $form['create_lists'] = array(
        '#type' => 'checkbox',
        '#title' => t('Show link to add new list'),
        '#default_value' => variable_get('flag_lists_create_lists', 0),
        '#description' => t('Checking this adds a link to the create new list form.'),
      );
      $form['ops'] = array(
        '#type' => 'checkbox',
        '#title' => t('Show edit and delete links'),
        '#default_value' => variable_get('flag_lists_ops', 0),
        '#description' => t('Checking this appends edit and delete links to each list name for users with access.'),
      );
      $form['include_flags'] = array(
        '#type' => 'checkbox',
        '#title' => t('Include flag module flags'),
        '#default_value' => variable_get('flag_lists_include_flags', 0),
        '#description' => t('Checking this will append flag module flags to the list of lists.'),
      );
      return $form;
    }
    elseif ($op == 'save' && $delta == 0) {
      variable_set('flag_lists_create_lists', $edit['create_lists']);
      variable_set('flag_lists_ops', $edit['ops']);
      variable_set('flag_lists_include_flags', $edit['include_flags']);
    }
    elseif ($op == 'view' && user_access('create flag lists')) {
      $block['subject'] = t('My lists');
      $block['content'] = theme('flag_lists_list', NULL, variable_get('flag_lists_create_lists', 0), variable_get('flag_lists_ops', 0), variable_get('flag_lists_include_flags', 0));
      if (!is_null($block['content'])) {
        return $block;
      }
    }
  }
}

/**
 * Implementation of hook_user().
 */
function flag_lists_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'delete':

      // Remove personal flags by this user.
      $result = db_query("DELETE FROM {flag_lists_flags} WHERE uid = %d", $account->uid);
      break;
  }
}

/**
 * Build a flag's messages.
 */
function flag_lists_set_messages(&$flag) {

  // Get the parent flag. These are cached by the flag module.
  $pflag = flag_get_flag(NULL, $flag->pfid);
  $title = $flag->title;
  $lists_name = variable_get('flag_lists_name', t('list'));
  $flag->flag_short = $pflag->flag_short;
  $flag->flag_long = $pflag->flag_long;
  $flag->flag_message = $pflag->flag_message;
  $flag->unflag_short = $pflag->unflag_short;
  $flag->unflag_long = $pflag->unflag_long;
  $flag->unflag_message = $pflag->unflag_message;
}

/**
 * Implementation of hook_flag_access().
 *
 * Make sure a user can only see his/her own personal flags.
 */
function flag_lists_flag_access($flag, $content_id, $action, $account) {
  if ($flag->module == 'flag_lists') {
    switch ($action) {
      case 'flag':
      case 'unflag':
        if (db_result(db_query("SELECT * from {flag_lists_flags} f WHERE f.uid = %d", $account->uid))) {
          return array(
            'flag_lists' => TRUE,
          );
        }
        else {
          return array(
            'flag_lists' => FALSE,
          );
        }
    }
  }
}

/**
 * Implementation of hook_link().
 */

// There may be a better way to keep flag lists out of the links, but this
// works for now. @todo Find a better way to keep flags lists out of links.
function flag_lists_link_alter(&$links, $node) {
  if (!variable_get('flag_lists_use_links', 1)) {
    foreach ($links as $name => $link) {
      if (stristr($name, 'flag-fl_')) {
        unset($links[$name]);
      }
    }
  }
}

/**
 * Implementation of hook_flag_alter().
 */
function flag_lists_flag_alter(&$flag) {
}

/**
 * Implementation of hook_flag_delete().
 *
 * This is not in flag yet.
 */
function flag_lists_flag_delete(&$flag) {

  // Template flag is being deleted. Clean up our tables.
  // Collect the sub-flag fids so we can delete counts and content records.
  if ($fids = db_fetch_array(db_query("SELECT fid, name from {flag_lists_flags} WHERE pfid = %d", $flag->fid))) {
    foreach ($fids as $fid) {
      db_query("DELETE FROM {flag_lists_counts} WHERE fid = %d", $fid['fid']);
      db_query("DELETE FROM {flag_lists_content} WHERE fid = %d", $fid['fid']);
    }
  }

  // flag_lists_types uses the template flag name, not our own fid.
  db_query("DELETE FROM {flag_lists_types} WHERE name = '%s'", $flag->name);

  // Now delete the sub-flags.
  db_query("DELETE FROM {flag_lists_flags} WHERE pfid = %d", $flag->fid);
  drupal_set_message(t('The template flag "@title" and all its sub-flags have been deleted.', array(
    '@title' => $flag->title,
  )));
}

/**
 * Implementation of hook_views_api().
 */
function flag_lists_views_api() {
  return array(
    'api' => 2.0,
    'path' => drupal_get_path('module', 'flag_lists') . '/includes',
  );
}

/**
 * Helper function to test if a flag is owned by the current user, or current
 * user can administer flags.
 */
function flag_lists_is_owner($action, $name) {
  global $user;
  if (user_access('administer flags')) {
    return TRUE;
  }

  // If we don't have an fid, then we have the flag name.
  if (is_numeric($name)) {
    $name = db_result(db_query("SELECT name from {flag_lists_flags} WHERE fid = %d", $name));
  }
  if (!user_access($action . ' own flag lists')) {
    return FALSE;
  }
  if (db_result(db_query("SELECT * from {flag_lists_flags} f WHERE f.name = '%s' AND f.uid = %d", $name, $user->uid))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Get a single user's lists, and merge in flag module flags
 */
function flag_lists_get_user_flags($content_type = NULL, $account = NULL, $use_flags = FALSE) {
  $flags = array();
  $lists = array();
  if (!isset($account)) {
    $account = $GLOBALS['user'];
  }

  // Get flag lists flags
  $sql = "SELECT fl.*, ft.type from {flag_lists_flags} fl\n    LEFT JOIN {flags} f on fl.pfid = f.fid\n    LEFT JOIN {flag_lists_types} ft on ft.name = f.name\n          WHERE fl.uid = %d";
  if ($content_type) {
    $sql .= " AND ft.type = '%s'";
  }
  $result = db_query($sql, $account->uid, $content_type);
  while ($row = db_fetch_object($result)) {
    if (!isset($lists[$row->name])) {
      $lists[$row->name] = flag_flag::factory_by_row($row);
      $lists[$row->name]->module = 'flag_lists';
    }
    else {
      $lists[$row->name]->types[] = $row->type;
    }
  }

  // Get regular flags.
  if ($use_flags) {
    $flags = flag_get_flags('node', $content_type, $account);

    // Strip out any list templates
    foreach ($flags as $key => $flag) {
      if (stristr($flag->name, 'fl_template') !== FALSE) {
        unset($flags[$key]);
      }
    }
  }
  $flags = array_merge($lists, $flags);
  return $flags;
}

/**
 * Theme function to return edit, delete links.
 *
 * @param $flag
 *   The flag whose links are being built.
 */
function theme_flag_lists_ops($flag) {
  $links = array(
    'flags_edit' => array(
      'title' => t('edit'),
      'href' => 'flags/lists/edit/' . $flag->name,
      'query' => drupal_get_destination(),
    ),
    'flags_delete' => array(
      'title' => t('delete'),
      'href' => 'flags/lists/delete/' . $flag->name,
      'query' => drupal_get_destination(),
    ),
  );
  return theme('links', $links, NULL, 'ul', array(
    'class' => 'flag_lists_ops',
  ));
}

/**
 * Theme a list of lists
 *
 * @param $node
 *   The listable node
 * @param boolean $create
 *   Show the create list form.
 * @param boolean $ops
 *   Show the edit / delete links for lists
 * @param boolean $use_flags
 *   Show flags from the flag module
 * @return <type>
 */

// @todo Separate out the code from the theming better.
function theme_flag_lists_list($node, $create = TRUE, $ops = TRUE, $use_flags = FALSE) {

  // Make sure we have a node.
  if (is_object($node) && user_access('create flag lists')) {
    $content_type = $node->type;
    $content_id = $node->nid;
  }
  elseif (arg(0) == 'node' && is_numeric(arg(1)) && is_null(arg(2)) && user_access('create flag lists')) {
    $content_id = arg(1);
    $content_type = db_result(db_query("SELECT type from {node} WHERE nid = %d", $content_id));
  }
  else {
    return;
  }

  // Do we have a list template for this node type, or are we s
  if (!flag_lists_template_exists($content_type) && !$use_flags) {
    return;
  }
  global $user;
  if ($flags = flag_lists_get_user_flags($content_type, $user, $use_flags)) {

    // Build the list of lists for this node.
    foreach ($flags as $flag) {
      if ($flag->module == 'flag_lists') {
        $action = _flag_lists_is_flagged($flag, $content_id, $user->uid, 0) ? 'unflag' : 'flag';
      }
      else {
        $action = $flag
          ->is_flagged($content_id) ? 'unflag' : 'flag';
      }

      // Do we need the ops?
      if ($ops && $flag->module == 'flag_lists') {
        $ops_links = theme('flag_lists_ops', $flag);
        $link = $flag
          ->theme($action, $content_id) . $ops_links;
      }
      else {
        $link = $flag
          ->theme($action, $content_id);
      }

      // If it's a list, fix the link.
      if ($flag->module == 'flag_lists') {
        flag_lists_fix_link($link, $action);
      }
      $items[] = $link;
    }
  }
  if ($create && flag_lists_template_exists($content_type)) {
    $items[] = l(t('Make a new @name', array(
      '@name' => variable_get('flag_lists_name', t('list')),
    )), 'flags/lists/add/' . $content_type, array(
      'query' => drupal_get_destination(),
    ));
  }

  // Return if nothing to display.
  if (!$items) {
    return;
  }
  drupal_add_css(drupal_get_path('module', 'flag_lists') . '/theme/flag_lists.css');
  return theme('item_list', $items, NULL, 'ul', array(
    'class' => 'flag-lists-links',
  ));
}

// Do we still need this, and/or do we need our own cache?

/**
 * Clear the flag cache.
 *
 * This is a less severe cache clear than provided by flag. All flag lists
 * users must be authorized, so we don't need to flush the page cache. For now,
 * flag lists titles won't be in the menu, so no need to clear that.
 */
function _flag_lists_clear_cache() {

  // We're not using _flag_clear_cache because we probably don't need the menu
  // rebuild and don't need to clear the page cache.
  if (module_exists('views')) {
    views_invalidate_cache();
  }
}

/**
 * Update ALL flag lists with settings form values.
 */
function flag_lists_rebuild() {
  $flags = flag_lists_get_flags();
  foreach ($flags as $flag) {
    flag_lists_set_messages($flag);
    $flag->link_type = 'toggle';
    flag_lists_save($flag);
  }
}

/**
 * Build array of all flag lists.
 *
 * @return If limit and header arguments are provided, the paged flags, otherwise
 * an array of all flags.
 */
function flag_lists_get_flags($limit = NULL, $header = NULL) {
  $flags = array();
  if ($limit) {
    $sql = "SELECT fl.*, GROUP_CONCAT(ft.type) AS types FROM {flag_lists_flags} fl LEFT JOIN {flag_types} ft on ft.fid = fl.pfid GROUP BY fl.fid " . tablesort_sql($header) . "";
    $count_sql = "SELECT COUNT(fid) FROM {flag_lists_flags}";
    $result = pager_query($sql, $limit, 0, $count_sql);
  }
  else {
    $result = db_query("SELECT fl.*, GROUP_CONCAT(ft.type) AS types FROM {flag_lists_flags} fl LEFT JOIN {flag_types} ft on ft.fid = fl.pfid GROUP BY fl.fid");
  }
  while ($row = db_fetch_object($result)) {
    $flags[$row->name] = flag_flag::factory_by_row($row);
    $flags[$row->name]->types = explode(',', $row->types);
    $flags[$row->name]->uid = $row->uid;
  }
  return $flags;
}

/**
 * Get a specific flag.
 *
 * Using this instead of flag_get_flag() for performance.
 */
function flag_lists_get_flag($fid) {

  // If we don't have an fid, then we have the flag name.
  if (!is_numeric($fid)) {
    $fid = db_result(db_query("SELECT fid from {flag_lists_flags} WHERE name = '%s'", $fid));
  }
  $result = db_query("SELECT fl.*, ft.type FROM {flag_lists_flags} fl LEFT JOIN {flag_types} ft on ft.fid = fl.pfid WHERE fl.fid = %d", $fid);
  while ($row = db_fetch_object($result)) {
    if (!isset($flag->name)) {
      $flag = flag_flag::factory_by_row($row);
    }
    else {
      $flag->types[] = $row->type;
    }
  }
  return $flag;
}

/**
 * Get all flagged content in a flag.
 *
 * Using this instead of flag_get_flagged_content() because we need to make sure that we use flag_lists_get_flags()
 *
 * @param
 *   The flag name for which to retrieve flagged content.
 */
function flag_lists_get_flagged_content($fid, $uid) {
  $return = array();
  $flag = flag_lists_get_flag($fid);
  $result = db_query("SELECT * FROM {flag_lists_content} WHERE fid = %d AND uid = %d", $flag->fid, $uid);
  while ($row = db_fetch_object($result)) {
    $return[] = $row;
  }
  return $return;
}

/**
 * Implementation of hook_flag_link().
 *
 * When Flag uses a link type provided by this module, it will call this
 * implementation of hook_flag_link(). It returns a single link's attributes,
 * using the same structure as hook_link(). Note that "title" is provided by
 * the Flag configuration if not specified here.
 *
 * @param $flag
 *   The full flag object of for the flag link being generated.
 * @param $action
 *   The action this link will perform. Either 'flag' or 'unflag'.
 * @param $content_id
 *   The ID of the node, comment, user, or other object being flagged.
 * @return
 *   An array defining properties of the link.
 */
function flag_lists_flag_link($flag, $action, $content_id) {
  return array();
}

/**
 * Implementation of hook_flag_link_types().
 */
function flag_lists_flag_link_types() {
  return array(
    'fl_template' => array(
      'title' => t('Flag Lists toggle'),
      'description' => t('If you are creating a Flag lists template flag, you must select this link type. '),
    ),
  );
}
function flag_lists_flag_default_flags($name = 'fl_template') {
  return array(
    array(
      'api_version' => 2,
      'name' => $name,
      'module' => 'flag_lists',
      'content_type' => 'node',
      'global' => 0,
      'show_on_page' => 0,
      'show_on_teaser' => 0,
      'show_on_form' => 0,
      // The following UI labels aren't wrapped in t() because they are written
      // to the DB in English. They are passed to t() later, thus allowing for
      // multilingual sites.
      'title' => 'Flag lists template',
      'flag_short' => 'Add to your [flag-lists-title] [flag-lists-term]',
      'flag_long' => 'Add this post to your [flag-lists-title] [flag-lists-term]',
      'flag_message' => 'This post has been added to your [flag-lists-title] [flag-lists-term]',
      'unflag_short' => 'Remove this from your [flag-lists-title] [flag-lists-term]',
      'unflag_long' => 'Remove this post from your [flag-lists-title] [flag-lists-term]',
      'unflag_message' => 'This post has been removed from your [flag-lists-title] [flag-lists-term]',
      'types' => array(),
      'link_type' => 'toggle',
    ),
  );
}

/**
 * Saves a flag to the database. It is a wrapper around update($flag) and insert($flag).
 */
function flag_lists_save(&$flag, $account = NULL) {
  if (!isset($account)) {
    $account = $GLOBALS['user'];
  }
  if (isset($flag->fid)) {
    flag_lists_update($flag);
    $flag->is_new = FALSE;
    module_invoke_all('flag_lists', $flag, $account);
  }
  else {
    flag_lists_insert($flag);
    $flag->is_new = TRUE;
    module_invoke_all('flag_lists', $flag, $account);
  }

  // Clear the page cache for anonymous users.
  //    cache_clear_all('*', 'cache_page', TRUE);
}

/**
 * Saves an existing flag to the database. Better use save($flag).
 */
function flag_lists_update($flag) {
  db_query("UPDATE {flag_lists_flags} SET title = '%s', name = '%s', options = '%s' WHERE fid = %d", $flag->title, $flag->name, $flag
    ->get_serialized_options($flag), $flag->fid);
}

/**
 * Saves a new flag to the database. Better use save($flag).
 */
function flag_lists_insert($flag) {
  db_query("INSERT INTO {flag_lists_flags} (pfid, uid, content_type, name, title, options) VALUES (%d, %d, '%s', '%s', '%s', '%s')", $flag->pfid, $flag->uid, $flag->content_type, $flag->name, $flag->title, $flag
    ->get_serialized_options($flag));
  $flag->fid = db_last_insert_id('flag_lists_flags', 'fid');
  $flag->name = 'flag_lists_' . $flag->uid . '_' . $flag->fid;
  flag_lists_update($flag);
}

/**
 * Delete a flag_lists flag.
 *
 */
function flag_lists_fl_delete($flag, $account = NULL) {
  if (!isset($account)) {
    $account = $GLOBALS['user'];
  }
  db_query("DELETE FROM {flag_lists_counts} WHERE fid = %d", $flag->fid);
  db_query("DELETE FROM {flag_lists_content} WHERE fid = %d", $flag->fid);
  db_query("DELETE FROM {flag_lists_flags} WHERE fid = %d", $flag->fid);
  $flag->is_deleted = TRUE;
  module_invoke_all('flag_lists', $flag, $account);
  _flag_lists_clear_cache();
  drupal_set_message(t('The @name @title has been deleted.', array(
    '@name' => variable_get('flag_lists_name', t('list')),
    '@title' => $flag->title,
  )));
}

/**
 * Menu callback for (un)flagging a node.
 *
 * Used both for the regular callback as well as the JS version. We use this
 * instead of the flag module's because our flags are not in the flags table.
 */
function flag_lists_page($action = NULL, $flag_name = NULL, $content_id = NULL) {
  global $user;

  // Shorten up the variables that affect the behavior of this page.
  $js = isset($_REQUEST['js']);
  $token = $_REQUEST['token'];

  // Specifically $_GET to avoid getting the $_COOKIE variable by the same key.
  $has_js = isset($_GET['has_js']);

  // Check the flag token, then perform the flagging.
  if (!flag_check_token($token, $content_id)) {
    $error = t('Bad token. You seem to have followed an invalid link.');
  }
  elseif ($user->uid == 0 && !$has_js) {
    $error = t('You must have JavaScript and cookies enabled in your browser to flag content.');
  }
  else {
    if (!($flag = flag_lists_get_flag($flag_name))) {

      // Flag does not exist.
      $error = t('You are not allowed to flag, or unflag, this content.');
    }

    // Identify it as ours.
    $flag->module = 'flag_lists';
    flag_lists_do_flag($flag, $action, $content_id);
  }

  // If an error was received, set a message and exit.
  if (isset($error)) {
    if ($js) {
      drupal_set_header('Content-Type: text/javascript; charset=utf-8');
      print drupal_to_js(array(
        'status' => FALSE,
        'errorMessage' => $error,
      ));
      exit;
    }
    else {
      drupal_set_message($error);
      drupal_access_denied();
      return;
    }
  }

  // If successful, return data according to the request type.
  if ($js) {
    drupal_set_header('Content-Type: text/javascript; charset=utf-8');

    //    $flag = flag_lists_get_flag($flag_name);
    //   $flag->link_type = 'toggle';
    $sid = flag_get_sid($user->uid);
    $new_action = _flag_lists_is_flagged($flag, $content_id, $user->uid, $sid) ? 'unflag' : 'flag';
    $new_link = $flag
      ->theme($new_action, $content_id, TRUE);
    flag_lists_fix_link($new_link, $new_action);
    print drupal_to_js(array(
      'status' => TRUE,
      'newLink' => $new_link,
      // Further information for the benefit of custom JavaScript event handlers:
      'contentId' => $content_id,
      'contentType' => $flag->content_type,
      'flagName' => $flag->name,
      'flagStatus' => $action,
    ));
    exit;
  }
  else {
    $flag = flag_lists_get_flag($flag->fid);
    drupal_set_message($flag
      ->get_label($action . '_message', $content_id));
    drupal_goto();
  }
}
function flag_lists_fix_link(&$link, $action) {

  // This is a hack to let us use our own flag/unflag callbacks without having
  // to override $flag->theme and creating our own flag_link type.
  $link = str_replace('/flag/' . $action . '/', '/flag-lists/' . $action . '/', $link);
}

/**
 * Flags, or unflags, an item.
 *
 * @param $action
 *   Either 'flag' or 'unflag'.
 * @param $content_id
 *   The ID of the item to flag or unflag.
 * @param $account
 *   The user on whose behalf to flag. Leave empty for the current user.
 * @param $skip_permission_check
 *   Flag the item even if the $account user doesn't have permission to do so.
 * @return
 *   FALSE if some error occured (e.g., user has no permission, flag isn't
 *   applicable to the item, etc.), TRUE otherwise.
 */
function flag_lists_do_flag($flag, $action, $content_id, $account = NULL, $skip_permission_check = FALSE) {
  if (!isset($account)) {
    $account = $GLOBALS['user'];
  }
  if (!$account) {
    return FALSE;
  }
  if (!$skip_permission_check) {
    if (!$flag
      ->access($content_id, $action, $account)) {

      // User has no permission to flag/unflag this object.
      return FALSE;
    }
  }
  else {

    // We are skipping permission checks. However, at a minimum we must make
    // sure the flag applies to this content type:
    if (!$flag
      ->applies_to_content_id($content_id)) {
      return FALSE;
    }
  }

  // Clear various caches; We don't want code running after us to report
  // wrong counts or false flaggings.
  //    flag_get_counts(NULL, NULL, TRUE);
  //    flag_get_user_flags(NULL, NULL, NULL, NULL, TRUE);
  // Find out which user id to use.
  $uid = $flag->global ? 0 : $account->uid;
  $sid = flag_get_sid($uid);

  // Anonymous users must always have a session id.
  if ($sid == 0 && $account->uid == 0) {
    return FALSE;
  }

  // Perform the flagging or unflagging of this flag. We invoke hook_flag here
  // because we do our own flagging.
  $flagged = _flag_lists_is_flagged($flag, $content_id, $uid, $sid);
  if ($action == 'unflag') {
    if ($flagged) {
      $fcid = _flag_lists_unflag($flag, $content_id, $uid, $sid);
      module_invoke_all('flag', 'unflag', $flag, $content_id, $account, $fcid);
    }
  }
  elseif ($action == 'flag') {
    if (!$flagged) {
      $fcid = _flag_lists_flag($flag, $content_id, $uid, $sid);
      module_invoke_all('flag', 'flag', $flag, $content_id, $account, $fcid);
    }
  }
  return TRUE;
}

/**
 * Returns TRUE if a certain user has flagged this content.
 *
 *
 * This method is similar to is_flagged() except that it does direct SQL and
 * doesn't do caching. Use it when you want to not affect the cache, or to
 * bypass it.
 *
 */
function _flag_lists_is_flagged($flag, $content_id, $uid, $sid) {
  return db_result(db_query("SELECT fid FROM {flag_lists_content} WHERE fid = %d AND uid = %d AND sid = %d AND content_id = %d", $flag->fid, $uid, $sid, $content_id));
}

/**
 * A low-level method to flag content.
 *
 * You probably shouldn't call this raw private method: call the
 * flag_lists_do_flag() function instead.
 *
 */
function _flag_lists_flag($flag, $content_id, $uid, $sid) {
  db_query("INSERT INTO {flag_lists_content} (fid, content_type, content_id, uid, sid, timestamp) VALUES (%d, '%s', %d, %d, %d, %d)", $flag->fid, $flag->content_type, $content_id, $uid, $sid, time());
  $fcid = db_last_insert_id('flag_lists_content', 'fcid');
  _flag_lists_update_count($flag, $content_id);
  return $fcid;
}

/**
 * A low-level method to unflag content.
 *
 * You probably shouldn't call this raw private method: call the
 * flag_lists_do_flag() function instead.
 *
 */
function _flag_lists_unflag($flag, $content_id, $uid, $sid) {
  $fcid = db_result(db_query("SELECT fcid FROM {flag_lists_content} WHERE fid = %d AND content_id = %d AND uid = %d AND sid = %d", $flag->fid, $content_id, $uid, $sid));
  if ($fcid) {
    db_query("DELETE FROM {flag_lists_content} WHERE fcid = %d", $fcid);
    _flag_lists_update_count($flag, $content_id);
  }
  return $fcid;
}

/**
 * Updates the flag count for this content
 */
function _flag_lists_update_count($flag, $content_id) {
  $count = db_result(db_query("SELECT COUNT(*) FROM {flag_lists_content} WHERE fid = %d AND content_id = %d", $flag->fid, $content_id));
  if ($count == 0) {
    db_query("DELETE FROM {flag_lists_counts} WHERE fid = %d AND content_id = %d", $flag->fid, $content_id);
  }
  else {
    db_query("UPDATE {flag_lists_counts} SET count = %d WHERE fid = %d AND content_id = %d", $count, $flag->fid, $content_id);
    if (!db_affected_rows()) {
      db_query("INSERT INTO {flag_lists_counts} (fid, content_type, content_id, count) VALUES (%d, '%s', %d, %d)", $flag->fid, $flag->content_type, $content_id, $count);
    }
  }
}

/**
 * Checks for a list template for a content type.
 */
function flag_lists_template_exists($type) {
  if ($exists = db_result(db_query("SELECT type from {flag_lists_types} WHERE type = '%s'", $type))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Checks for a list title by node type.
 */
function flag_lists_title_exists($title, $type) {
  global $user;
  return db_result(db_query("SELECT COUNT(*) FROM {flag_lists_flags} fl LEFT JOIN {flag_types} ft ON fl.pfid = ft.fid WHERE fl.title = '%s' AND ft.type = '%s' AND fl.uid = %d", $title, $type, $user->uid));
}

/**
 * Get a list of template flag names.
 */
function flag_lists_get_templates() {
  $result = db_query("SELECT DISTINCT name FROM {flag_lists_types}");
  while ($row = db_fetch_array($result)) {
    $templates[] = flag_get_flag($row['name']);
  }
  return $templates;
}

/**
 * Implementation of hook_token_list().
 */
function flag_lists_token_list($type = 'all') {
  $tokens = array();
  if ($type == 'node' || $type == 'all') {
    $tokens['flag_lists']['flag-lists-term'] = t('The terminology used to name the lists, such as list, wishlist, favorites, etc.');
    $tokens['flag_lists']['flag-lists-title'] = t('The title of the lis.');
  }
  return $tokens;
}

/**
 * Implementation of hook_token_values().
 */
function flag_lists_token_values($type, $object = NULL) {
  $values = array();
  if ($type == 'flag_lists') {
    $values['flag-lists-term'] = check_plain(variable_get('flag_lists_name', t('list')));
    $values['flag-lists-title'] = check_plain($object->title);
  }
  return $values;
}

/**
 * Preprocess link title and text for the flag.tpl.php
 *
 * This seems to be the only place to do this
 */
function flag_lists_preprocess_flag(&$variables) {
  if (module_exists('token') && $variables['flag']->module == 'flag_lists') {
    $variables['link_text'] = token_replace($variables['link_text'], 'flag_lists', $variables['flag']);
    $variables['link_title'] = token_replace($variables['link_title'], 'flag_lists', $variables['flag']);
    $variables['message_text'] = token_replace($variables['message_text'], 'flag_lists', $variables['flag']);
  }
}

Functions

Namesort descending Description
flag_lists_block Implementation of hook_block().
flag_lists_do_flag Flags, or unflags, an item.
flag_lists_fix_link
flag_lists_flag_access Implementation of hook_flag_access().
flag_lists_flag_alter Implementation of hook_flag_alter().
flag_lists_flag_default_flags
flag_lists_flag_delete Implementation of hook_flag_delete().
flag_lists_flag_link Implementation of hook_flag_link().
flag_lists_flag_link_types Implementation of hook_flag_link_types().
flag_lists_fl_delete Delete a flag_lists flag.
flag_lists_form_alter Implementation of hook_form_alter().
flag_lists_get_content_fids Helper function to build an array of all lists available to or owned by the current user and that are available on the current content type.
flag_lists_get_flag Get a specific flag.
flag_lists_get_flagged_content Get all flagged content in a flag.
flag_lists_get_flags Build array of all flag lists.
flag_lists_get_templates Get a list of template flag names.
flag_lists_get_user_flags Get a single user's lists, and merge in flag module flags
flag_lists_insert Saves a new flag to the database. Better use save($flag).
flag_lists_is_owner Helper function to test if a flag is owned by the current user, or current user can administer flags.
flag_lists_link_alter
flag_lists_menu Implementation of hook_menu().
flag_lists_page Menu callback for (un)flagging a node.
flag_lists_perm Implementation of hook_perm().
flag_lists_preprocess_flag Preprocess link title and text for the flag.tpl.php
flag_lists_rebuild Update ALL flag lists with settings form values.
flag_lists_save Saves a flag to the database. It is a wrapper around update($flag) and insert($flag).
flag_lists_set_messages Build a flag's messages.
flag_lists_template_exists Checks for a list template for a content type.
flag_lists_template_submit
flag_lists_template_validate
flag_lists_theme Implementation of hook_theme().
flag_lists_title_exists Checks for a list title by node type.
flag_lists_token_list Implementation of hook_token_list().
flag_lists_token_values Implementation of hook_token_values().
flag_lists_update Saves an existing flag to the database. Better use save($flag).
flag_lists_user Implementation of hook_user().
flag_lists_user_list List the contents of a user-defined list
flag_lists_user_page User flag page. Display a list of user-created flag lists.
flag_lists_views_api Implementation of hook_views_api().
theme_flag_lists_list
theme_flag_lists_ops Theme function to return edit, delete links.
theme_flag_lists_user_list Theme the output of user-defined list page
theme_flag_lists_user_page Theme the output for a user flag administration page.
_flag_lists_clear_cache Clear the flag cache.
_flag_lists_flag A low-level method to flag content.
_flag_lists_is_flagged Returns TRUE if a certain user has flagged this content.
_flag_lists_unflag A low-level method to unflag content.
_flag_lists_update_count Updates the flag count for this content