';
+
+ return $form;
+}
+
+function flag_lists_ops_form_ajax_callback($form, $form_state) {
+ // The form has already been submitted and updated. We can return the replaced
+ // item as it is.
+ if (!flag_lists_ops_form_validate($form, $form_state)) {
+ $message = flag_lists_ops_form_submit($form, $form_state);
+ }
+
+ $form['flag_lists_' . $form_state['flo_operation']]['status_message']['#markup'] = '
' . $message . '
';
+ if ($form_state['flo_operation'] == 'flag') $form['flag_lists_' . $form_state['flo_operation']]['list']['#value'] = '0';
+ $form['flag_lists_' . $form_state['flo_operation']]['status_message']['#attributes']['class'][] = 'alert-success';
+
+ return $form['flag_lists_' . $form_state['flo_operation']];
+}
+
+function flag_lists_ops_form_validate(&$form, &$form_state) {
+ global $user;
+
+ $error_count = 0;
+
+ // Check to see if an items are selected, if not fail right away.
+ if (!isset($_REQUEST['flag_lists_ops']) || empty($_REQUEST['flag_lists_ops'])) {
+ form_set_error('', t('No content selected.'));
+ $error_count++;
+ return $error_count;
+ }
+
+ switch ($form_state['values']['flag_lists_' . $form_state['flo_operation']]['operation']) {
+ case 'unflag':
+ $ops = $_REQUEST['flag_lists_ops'];
+ foreach ($ops as $key => $op) {
+ $ops[$key] = explode('-', $op);
+ if (empty($ops[$key][1]) || !($flag = flag_lists_get_flag($ops[$key][1]))) {
+ form_set_error('flag_lists][remove', t('Invalid options list selected to remove from.'));
+ $error_count++;
+ }
+ else if ($flag->uid != $user->uid) {
+ form_set_error('flag_lists][remove', t('You are only allowed to remove content from your own lists.'));
+ $error_count++;
+ }
+ }
+ break;
+ default:
+ if (empty($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'])) {
+ form_set_error('flag_lists][list', t('No list selected. Please select a list to add to.'));
+ $error_count++;
+ }
+ else if (!($flag = flag_lists_get_flag($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list']))) {
+ form_set_error('flag_lists][list', t('Invalid list selected. Please select a list to add to.'));
+ $error_count++;
+ }
+ else if ($flag->uid != $user->uid) {
+ form_set_error('flag_lists][list', t('Invalid list selected. Please select a list to add to.'));
+ $error_count++;
+ }
+ break;
+ }
+
+ return $error_count;
+}
+
+function flag_lists_ops_form_submit(&$form, &$form_state) {
+ // Get the $ops from the originating form.
+ $ops = $_REQUEST['flag_lists_ops'];
+ $success_count = 0;
+
+ // Get the operation, or set it
+ switch ($form_state['values']['flag_lists_' . $form_state['flo_operation']]['operation']) {
+ case 'unflag':
+ $operation = 'unflag';
+ $message = 'removed from';
+
+ foreach ($ops as $op) {
+ // Process the ID into 2 parts
+ list($nid, $fid) = explode('-', $op);
+ if (empty($nid) || empty($fid)) return;
+
+ if (($flag = flag_lists_get_flag($fid)) && ($node = node_load($nid))) {
+ if (flag_lists_do_flag($flag, $operation, $nid)) {
+ $success_count++;
+ }
+ }
+ }
+ break;
+ default:
+ $operation = 'flag';
+ $message = 'added to';
+
+ if ($flag = flag_lists_get_flag($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'])) {
+ foreach ($ops as $nid) {
+ if (flag_lists_do_flag($flag, $operation, $nid)) {
+ $success_count++;
+ }
+ }
+ }
+ break;
+ }
+
+ $message = t('@count item(s) ' . $message . ' !title', array('@count' => $success_count, '!title' => $flag->title));
+ if ($_GET['q'] != 'system/ajax') {
+ drupal_set_message($message);
+ }
+ else {
+ return $message;
+ }
+}
+
+/**
+ * Gets the FLO field if it exists on the passed-in view.
+ *
+ * @return
+ * The field object if found. Otherwise, FALSE.
+ */
+function _flag_lists_ops_get_field($view) {
+ foreach ($view->field as $field_name => $field) {
+ if ($field instanceof flag_lists_handler_field_ops) {
+ // Add in the view object for convenience.
+ $field->view = $view;
+ return $field;
+ }
+ }
+ return FALSE;
+}
+
+function flag_lists_template_validate($form, &$form_state) {
+ $types = array_filter($form_state['values']['types']);
+ $errors = array();
+ foreach ($types as $type) {
+ $result = db_select('flag_lists_types', 'f')
+ ->fields('f')
+ ->condition('type', $type)
+ ->condition('name', $form_state['values']['name'], '<>')
+ ->execute();
+ foreach ($result as $errors) {
+ $content_types[] = $errors->type;
+ $templates[] = $errors->name;
+ }
+ }
+ if (isset($content_types) && 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.
+ $num_deleted = db_delete('flag_lists_types')
+ ->condition('name', $form_state['values']['name'])
+ ->execute();
+ foreach ($types as $type) {
+ db_insert('flag_lists_types')
+ ->fields(array(
+ 'name' => $form_state['values']['name'],
+ 'type' => $type,
+ ))
+ ->execute();
+ }
+}
+/**
+ * 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_select('node', 'n')
+ ->fields('n', array('type'))
+ ->condition('nid', arg(1))
+ ->execute()
+ ->fetchField();
+
+ // Get current user's flags for this node.
+ $query = db_select('flag_lists', 'fc')
+ ->fields('f', 'fid')
+ ->condition('fc.uid', $user->uid)
+ ->condition('fn.type', $type);
+ $query->leftJoin('flag_types', 'fn', 'fn.fid = fc.fid');
+ $query->leftJoin('flags', 'f', 'fc.fid = f.fid');
+ $fc_result = $query->execute();
+
+ foreach ($fc_result as $row) {
+ $fids[] = $row->fid;
+ }
+ }
+
+ // This is the flag / unflag callback
+ elseif (arg(0) == 'flag' && (arg(1) == 'flag' || arg(1) == 'unflag')) {
+ // Get the flag for this request.
+ $fids[] = db_select('flags', 'f')
+ ->fields('f', array('fid'))
+ ->condition('name', arg(2))
+ ->execute()
+ ->fetchField();
+ }
+
+ // 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.
+ $query = db_select('flags', 'f')
+ ->fields('f', array('fid'))
+ ->condition('fc.fid', NULL);
+ $query->leftJoin('flag_lists', 'fc', 'fc.fid = f.fid');
+ $f_result = $query->execute();
+
+ foreach ($f_result as $obj) {
+ $fids[] = $obj->fid;
+ }
+ if (is_array($fids)) {
+ return array_unique($fids);
+ }
+ else {
+ return array();
+ }
+}
+
+/**
+ * Implements hook_block_info();
+ */
+function flag_lists_block_info() {
+ return array(
+ 'flag_lists' => array(
+ 'info' => t('Lists'),
+ 'cache' => DRUPAL_NO_CACHE,
+ ),
+ 'flag_lists_list' => array(
+ 'info' => t('My lists'),
+ 'cache' => DRUPAL_NO_CACHE,
+ ),
+ );
+}
+
+/**
+ * Implements hook_block_configure().
+ */
+function flag_lists_block_configure($delta = '') {
+ $form = array();
+
+ switch ($delta) {
+ case 'flag_lists':
+ $form = array(
+ '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.'),
+ ),
+ '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.'),
+ ),
+ '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.'),
+ ),
+ );
+ break;
+ }
+ return $form;
+}
+
+/**
+ * Implements hook_block_save().
+ */
+function flag_lists_block_save($delta = '', $edit = array()) {
+ switch ($delta) {
+ case 'flag_lists':
+ 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']);
+ break;
+ }
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function flag_lists_block_view($delta = '') {
+ $block = array();
+
+ switch ($delta) {
+ case 'flag_lists':
+ if (user_access('create flag lists')) {
+ $block = array(
+ 'subject' => t('My lists'),
+ 'content' => theme('flag_lists_list', array('node' => NULL, 'create' => variable_get('flag_lists_create_lists', 0), 'ops' =>variable_get('flag_lists_ops', 0), 'use_flags' => variable_get('flag_lists_include_flags', 0))),
+ );
+ }
+ break;
+ case 'flag_lists_list':
+ if (user_access('create flag lists')) {
+ global $user;
+ $account = user_load($user->uid);
+
+ $block = array(
+ 'subject' => t('My lists'),
+ 'content' => flag_lists_user_page($account),
+ );
+ }
+ break;
+ }
+ return (!empty($block['content'])) ? $block : array();
+}
+
+/**
+ * Implementation of hook_user_delete().
+ */
+function flag_lists_user_delete($account) {
+ // Remove personal flags by this user.
+ $num_deleted = db_delete('flag_lists_flags')
+ ->condition('uid', $account->uid)
+ ->execute();
+}
+
+/**
+ * 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, $entity_id, $action, $account) {
+ if (!empty($flag->module) && $flag->module == 'flag_lists') {
+ switch ($action) {
+ case 'flag':
+ case 'unflag':
+ $fid = db_select('flag_lists_flags', 'f')
+ ->fields('f')
+ ->condition('f.uid', $account->uid)
+ ->execute()
+ ->fetchField();
+ if (!empty($fid)) {
+ 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.
+ $results = db_select('flag_lists_flags', 'f')
+ ->fields('f', array('fid', 'name'))
+ ->condition('pfid', $flag->fid)
+ ->execute();
+ foreach ($results as $fid) {
+ db_delete('flag_lists_counts')
+ ->condition('fid', $flag->fid)
+ ->execute();
+ db_delete('flag_lists_content')
+ ->condition('fid', $flag->fid)
+ ->execute();
+ }
+
+ // flag_lists_types uses the template flag name, not our own fid.
+ db_delete('flag_lists_types')
+ ->condition('name', $flag->name)
+ ->execute();
+
+ // Now delete the sub-flags.
+ $num_deleted = db_delete('flag_lists_flags')
+ ->condition('pfid', $flag->fid)
+ ->execute();
+ if (!empty($num_deleted)) {
+ 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)) {
+ $query = db_select('flag_lists_flags', 'f')->condition('fid', $name);
+ $query->addField('f', 'name');
+ $name = $query->execute()->fetchField();
+ }
+
+ if (!user_access($action . ' own flag lists')) {
+ return FALSE;
+ }
+ if (db_select('flag_lists_flags', 'f')->fields('f')->condition('f.name', $name)->condition('f.uid', $user->uid)->countQuery()->execute()->fetchField()) {
+ 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
+ $query = db_select('flag_lists_flags', 'fl')
+ ->fields('fl')
+ ->condition('fl.uid', $account->uid);
+ $query->leftJoin('flag', 'f', 'fl.pfid = f.fid');
+ $query->leftJoin('flag_lists_types', 'ft', 'ft.name = f.name');
+ $query->addField('ft', 'type');
+ if ($content_type) {
+ $query->condition('ft.type', $content_type);
+ }
+
+ $result = $query->execute();
+ foreach ($result as $row) {
+ 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($variables) {
+ $flag = $variables['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', array('links' => $links, 'attributes' => 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
+ */
+
+// @todo Separate out the code from the theming better.
+function theme_flag_lists_list($variables) {
+ $node = $variables['node'];
+ $create = $variables['create'];
+ $ops = $variables['ops'];
+ $use_flags = $variables['use_flags'];
+
+ $items = array();
+
+ // Make sure we have a node.
+ if (is_object($node) && user_access('create flag lists')) {
+ $content_type = $node->type;
+ $entity_id = $node->nid;
+ }
+ // Or at least confirm we are on a node page and use has access.
+ elseif (arg(0) == 'node' && is_numeric(arg(1)) && user_access('create flag lists')) {
+ $entity_id = arg(1);
+ $query = db_select('node')->condition('nid', $entity_id);
+ $query->addField('node', 'type');
+ $content_type = $query->execute()->fetchField();
+ }
+ 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, $entity_id, $user->uid, 0) ? 'unflag' : 'flag';
+ }
+ else {
+ $action = $flag->is_flagged($entity_id) ? 'unflag' : 'flag';;
+ }
+
+ // Do we need the ops?
+ if ($ops && $flag->module == 'flag_lists') {
+ $ops_links = theme('flag_lists_ops', array('flag' => $flag));
+ $link = $flag->theme($action, $entity_id) . $ops_links;
+ }
+ else {
+ $link = $flag->theme($action, $entity_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')))), 'flag-lists/add/' . $content_type, array('query' => drupal_get_destination()));
+ }
+
+ // Return if nothing to display.
+ if (empty($items) || !count($items)) {
+ return;
+ }
+
+ drupal_add_css(drupal_get_path('module', 'flag_lists') . '/theme/flag_lists.css');
+ return theme('item_list', array('items' => $items, 'type' => 'ul', 'attributes' => 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) {
+ $query = db_select('flag_lists_flags', 'fl')
+ ->fields('fl')
+ ->groupBy('fl.fid')
+ ->extend('PagerDefault')
+ ->limit($limit);
+ $query->leftJoin('flag_types', 'ft', 'ft.fid = fl.pfid');
+ $query->addExpression('GROUP_CONCAT(ft.type)', 'types');
+ $result = $query->execute();
+ }
+ else {
+ $query = db_select('flag_lists_flags', 'fl')
+ ->fields('fl')
+ ->groupBy('fl.fid');
+ $query->leftJoin('flag_types', 'ft', 'ft.fid = fl.pfid');
+ $query->addExpression('GROUP_CONCAT(ft.type)', 'types');
+ $result = $query->execute();
+ }
+ foreach ($result as $row) {
+ $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)) {
+ $query = db_select('flag_lists_flags')
+ ->condition('name', $fid);
+ $query->addField('flag_lists_flags', 'fid');
+ $fid = $query->execute()->fetchField();
+ }
+
+ $query = db_select('flag_lists_flags', 'fl')
+ ->fields('fl')
+ ->condition('fl.fid', $fid);
+ $query->leftJoin('flag_types', 'ft', 'ft.fid = fl.pfid');
+ $query->addField('ft', 'type');
+ $result = $query->execute();
+
+ foreach ($result as $row) {
+ 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_select('flag_lists_content', 'f')
+ ->fields('f')
+ ->condition('f.fid', $flag->fid)
+ ->condition('f.uid', $uid)
+ ->execute();
+
+ foreach ($result as $row) {
+ $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 $entity_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, $entity_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',
+ // ),
+ // );
+
+$flags = array();
+// Exported flag: "Flag lists template".
+$flags['fl_template'] = array(
+ 'entity_type' => 'node',
+ 'title' => 'Flag lists template',
+ 'global' => 0,
+ 'types' => array(),
+ '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]',
+ 'unflag_denied_text' => '',
+ 'link_type' => 'toggle',
+ 'weight' => 0,
+ 'api_version' => 3,
+ 'module' => 'flag_lists',
+ 'show_on_page' => 0,
+ 'show_on_teaser' => 0,
+ 'show_on_form' => 0,
+ 'status' => FALSE,
+ 'import_roles' => array(
+ 'flag' => array(),
+ 'unflag' => array(),
+ ),
+);
+return $flags;
+
+
+}
+
+/**
+ * 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) {
+ $num_updated = db_update('flag_lists_flags')
+ ->fields(array(
+ 'title' => $flag->title,
+ 'name' => $flag->name,
+ 'options' => $flag->get_serialized_options($flag),
+ ))
+ ->condition('fid', $flag->fid)
+ ->execute();
+}
+
+/**
+ * Saves a new flag to the database. Better use save($flag).
+ */
+function flag_lists_insert($flag) {
+ $flag->fid = db_insert('flag_lists_flags')
+ ->fields(array(
+ 'pfid' => $flag->pfid,
+ 'uid' => $flag->uid,
+ 'entity_type' => $flag->entity_type,
+ 'name' => $flag->name,
+ 'title' => $flag->title,
+ 'options' => $flag->get_serialized_options($flag),
+ ))
+ ->execute();
+ $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_delete('flag_lists_counts')->condition('fid', $flag->fid)->execute();
+ db_delete('flag_lists_content')->condition('fid', $flag->fid)->execute();
+ db_delete('flag_lists_flags')->condition('fid', $flag->fid)->execute();
+
+ $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, $entity_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, $entity_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 (empty($flag_name) || !($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, $entity_id);
+ }
+
+ // If an error was received, set a message and exit.
+ if (isset($error)) {
+ if ($js) {
+ drupal_add_http_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_add_http_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, $entity_id, $user->uid, $sid) ? 'unflag' : 'flag';
+ $new_link = $flag->theme($new_action, $entity_id, array("after_flagging" => TRUE));
+ flag_lists_fix_link($new_link, $new_action);
+ drupal_json_output(array(
+ 'status' => TRUE,
+ 'newLink' => $new_link,
+ // Further information for the benefit of custom JavaScript event handlers:
+ 'contentId' => $entity_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', $entity_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 $entity_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, $entity_id, $account = NULL, $skip_permission_check = FALSE) {
+ if (!isset($account)) {
+ $account = $GLOBALS['user'];
+ }
+ if (!$account) {
+ return FALSE;
+ }
+ if (!$skip_permission_check) {
+ if (!$flag->access($entity_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($entity_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, $entity_id, $uid, $sid);
+ if ($action == 'unflag') {
+ if ($flagged) {
+ $fcid = _flag_lists_unflag($flag, $entity_id, $uid, $sid);
+ module_invoke_all('flag', 'unflag', $flag, $entity_id, $account, $fcid);
+ }
+ }
+ elseif ($action == 'flag') {
+ if (!$flagged) {
+ $fcid = _flag_lists_flag($flag, $entity_id, $uid, $sid);
+ module_invoke_all('flag', 'flag', $flag, $entity_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, $entity_id, $uid, $sid) {
+ $query = db_select('flag_lists_content')
+ ->condition('fid', $flag->fid)
+ ->condition('uid', $uid)
+ ->condition('sid', $sid)
+ ->condition('entity_id', $entity_id);
+ $query->addField('flag_lists_content', 'fid');
+ return $query->execute()->fetchField();
+ }
+
+ /**
+ * 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, $entity_id, $uid, $sid) {
+ $fcid = db_insert('flag_lists_content')
+ ->fields(array(
+ 'fid' => $flag->fid,
+ 'entity_type' => $flag->entity_type,
+ 'entity_id' => $entity_id,
+ 'uid' => $uid,
+ 'sid' => $sid,
+ 'timestamp' => REQUEST_TIME,
+ ))
+ ->execute();
+
+ _flag_lists_update_count($flag, $entity_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, $entity_id, $uid, $sid) {
+ $query = db_select('flag_lists_content')
+ ->condition('fid', $flag->fid)
+ ->condition('entity_id', $entity_id)
+ ->condition('uid', $uid)
+ ->condition('sid', $sid);
+ $query->addField('flag_lists_content', 'fcid');
+ $fcid = $query->execute()->fetchField();
+ if ($fcid) {
+ db_delete('flag_lists_content')
+ ->condition('fcid', $fcid)
+ ->execute();
+ _flag_lists_update_count($flag, $entity_id);
+ }
+ return $fcid;
+ }
+
+ /**
+ * Updates the flag count for this content
+ */
+ function _flag_lists_update_count($flag, $entity_id) {
+ $count = db_select('flag_lists_content', 'f')
+ ->fields('f')
+ ->condition('fid', $flag->fid)
+ ->condition('entity_id', $entity_id)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+
+ if (empty($count)) {
+ $num_deleted = db_delete('flag_lists_counts')
+ ->condition('fid', $flag->fid)
+ ->condition('entity_id', $entity_id)
+ ->execute();
+ }
+ else {
+ $num_updated = db_update('flag_lists_counts')
+ ->fields(array(
+ 'count' => $count,
+ ))
+ ->condition('fid', $flag->fid)
+ ->condition('entity_id', $entity_id)
+ ->execute();
+ if (empty($num_updated)) {
+ db_insert('flag_lists_counts')
+ ->fields(array(
+ 'fid' => $flag->fid,
+ 'entity_type' => $flag->entity_type,
+ 'entity_id' => $entity_id,
+ 'count' => $count,
+ ))
+ ->execute();
+ }
+ }
+ }
+
+/**
+ * Checks for a list template for a content type.
+ */
+function flag_lists_template_exists($type) {
+ $query = db_select('flag_lists_types')
+ ->condition('type', $type);
+ $query->addField('flag_lists_types', 'type');
+ $exists = $query->execute()->fetchField();
+ if (!empty($exists)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Checks for a list title by node type.
+ */
+function flag_lists_title_exists($title, $type) {
+ return db_query("SELECT COUNT(flf.fid) FROM {flag_lists_flags} flf LEFT JOIN {flag_types} ft ON flf.pfid=ft.fid WHERE flf.title=:title AND ft.type=:type AND flf.uid=:uid", array(':title' => $title, ':type' => $type, ':uid' => $GLOBALS['user']->uid))->fetchField();
+}
+
+/**
+ * Get a list of template flag names.
+ */
+function flag_lists_get_templates() {
+ $templates = array();
+ $result = db_select('flag_lists_types', 'f')
+ ->fields('f', array(
+ 'name'
+ ))
+ ->distinct()
+ ->execute();
+ foreach ($result as $obj) {
+ $templates[] = flag_get_flag($obj->name);
+ }
+ return $templates;
+}
+
+
+/**
+ * Implements hook_token_info().
+ */
+function flag_lists_token_info() {
+ $type = array(
+ 'name' => t('Flag lists'),
+ 'description' => t('Tokens related to flag lists.'),
+ 'needs-data' => 'flag_lists',
+ );
+
+ $flag_lists['term'] = array(
+ 'name' => t("Term"),
+ 'description' => t("The terminology used to name the lists, such as list, wishlist, favorites, etc."),
+ );
+ $flag_lists['title'] = array(
+ 'name' => t("Title"),
+ 'description' => t("The title of the list."),
+ );
+
+ return array(
+ 'types' => array('flag_lists' => $type),
+ 'tokens' => array('flag_lists' => $flag_lists),
+ );
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function flag_lists_tokens($type, $tokens, array $data = array(), array $options = array()) {
+ $replacements = array();
+
+ if ($type == 'flag_lists' && !empty($data['flag_lists'])) {
+ $flag_list = $data['flag_lists'];
+ foreach ($tokens as $name => $original) {
+ switch ($name) {
+ case 'title':
+ $replacements[$original] = $flag_list->title;
+ break;
+ case 'term':
+ $replacements[$original] = variable_get('flag_lists_name', t('list'));
+ break;
+ }
+ }
+ }
+ return $replacements;
+}
+
+/**
+ * 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') && !empty($variables['flag']->module) && $variables['flag']->module == 'flag_lists') {
+ if (!empty($variables['link_text'])) {
+ $variables['link_text'] = token_replace($variables['link_text'], array('flag_lists' => $variables['flag']));
+ }
+ if (!empty($variables['link_title'])) {
+ $variables['link_title'] = token_replace($variables['link_title'], array('flag_lists' => $variables['flag']));
+ }
+ if (!empty($variables['message_text'])) {
+ $variables['message_text'] = token_replace($variables['message_text'], array('flag_lists' => $variables['flag']));
+ }
+ }
+}
+
+/**
+ * Implements hook_views_form_substitutions().
+ */
+function flag_lists_views_form_substitutions() {
+ // Views check_plains the column label, so Flag lists needs to do the same
+ // in order for the replace operation to succeed.
+ $select_all_placeholder = check_plain('');
+ $select_all = array(
+ '#type' => 'checkbox',
+ '#default_value' => FALSE,
+ '#attributes' => array('class' => array('flo-table-select-all')),
+ );
+ return array(
+ $select_all_placeholder => drupal_render($select_all),
+ );
+}