From f0cb6175c1d8ce2756fc0e2a615d763171a6ccd0 Mon Sep 17 00:00:00 2001 From: Bachir Soussi Chiadmi Date: Sun, 27 Sep 2015 18:55:26 +0200 Subject: [PATCH] patched flag list to trigger remove entity from lists when this entity is deleted --- ...ists-remove_deleted_entity-2114731-2.patch | 55 + .../contrib/flag/flag_lists/flag_lists.module | 40 + .../flag/flag_lists/flag_lists.module.orig | 1737 +++++++++++++++++ 3 files changed, 1832 insertions(+) create mode 100644 sites/all/modules/contrib/flag/flag_lists/flag_lists-remove_deleted_entity-2114731-2.patch create mode 100644 sites/all/modules/contrib/flag/flag_lists/flag_lists.module.orig diff --git a/sites/all/modules/contrib/flag/flag_lists/flag_lists-remove_deleted_entity-2114731-2.patch b/sites/all/modules/contrib/flag/flag_lists/flag_lists-remove_deleted_entity-2114731-2.patch new file mode 100644 index 00000000..27f6f074 --- /dev/null +++ b/sites/all/modules/contrib/flag/flag_lists/flag_lists-remove_deleted_entity-2114731-2.patch @@ -0,0 +1,55 @@ +diff --git a/flag_lists.module b/flag_lists.module +index d829113..b3c04f0 100644 +--- a/flag_lists.module ++++ b/flag_lists.module +@@ -1587,6 +1587,32 @@ function flag_lists_fix_link(&$link, $action) { + return $fcid; + } + ++ ++ /** ++ * Remove all entries of entity_id and type ++ * ++ * @param $entity_id ++ * Entity id which has been flagged. ++ * @param $type ++ * The entity type. ++ */ ++ function _flag_lists_remove_entity($entity_id, $type) { ++ $query = db_select('flag_lists_content') ++ ->condition('entity_id', $entity_id) ++ ->condition('entity_type', $type); ++ $query->fields('flag_lists_content', array('fcid', 'fid', 'uid', 'sid')); ++ $items = $query->execute()->fetchAll(); ++ ++ if ($items) { ++ foreach ($items as $key => $value) { ++ db_delete('flag_lists_content') ++ ->condition('fcid', $value->fcid) ++ ->execute(); ++ watchdog('flag_lists', t('Deleted entry @fcid from flat_lists_content', array('@fcid' => $value->fcid))); ++ } ++ } ++ } ++ + /** + * Updates the flag count for this content + */ +@@ -1766,3 +1792,17 @@ function flag_lists_views_form_substitutions() { + $select_all_placeholder => drupal_render($select_all), + ); + } ++ ++ ++/** ++ * Implements hook_entity_delete ++ */ ++function flag_lists_entity_delete($entity, $type) { ++ foreach (flag_get_flags($type) as $flag) { ++ if (isset($entity->vid)) { ++ $items = _flag_lists_remove_entity($entity->vid, $type); ++ } ++ } ++} ++ ++ diff --git a/sites/all/modules/contrib/flag/flag_lists/flag_lists.module b/sites/all/modules/contrib/flag/flag_lists/flag_lists.module index 8b25b241..9f9e0ad4 100644 --- a/sites/all/modules/contrib/flag/flag_lists/flag_lists.module +++ b/sites/all/modules/contrib/flag/flag_lists/flag_lists.module @@ -1575,6 +1575,32 @@ function flag_lists_fix_link(&$link, $action) { return $fcid; } + + /** + * Remove all entries of entity_id and type + * + * @param $entity_id + * Entity id which has been flagged. + * @param $type + * The entity type. + */ + function _flag_lists_remove_entity($entity_id, $type) { + $query = db_select('flag_lists_content') + ->condition('entity_id', $entity_id) + ->condition('entity_type', $type); + $query->fields('flag_lists_content', array('fcid', 'fid', 'uid', 'sid')); + $items = $query->execute()->fetchAll(); + + if ($items) { + foreach ($items as $key => $value) { + db_delete('flag_lists_content') + ->condition('fcid', $value->fcid) + ->execute(); + watchdog('flag_lists', t('Deleted entry @fcid from flat_lists_content', array('@fcid' => $value->fcid))); + } + } + } + /** * Updates the flag count for this content */ @@ -1735,3 +1761,17 @@ function flag_lists_views_form_substitutions() { $select_all_placeholder => drupal_render($select_all), ); } + + +/** + * Implements hook_entity_delete + */ +function flag_lists_entity_delete($entity, $type) { + foreach (flag_get_flags($type) as $flag) { + if (isset($entity->vid)) { + $items = _flag_lists_remove_entity($entity->vid, $type); + } + } +} + + diff --git a/sites/all/modules/contrib/flag/flag_lists/flag_lists.module.orig b/sites/all/modules/contrib/flag/flag_lists/flag_lists.module.orig new file mode 100644 index 00000000..8b25b241 --- /dev/null +++ b/sites/all/modules/contrib/flag/flag_lists/flag_lists.module.orig @@ -0,0 +1,1737 @@ + '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[FLAG_ADMIN_PATH . '/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[FLAG_ADMIN_PATH . '/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[FLAG_ADMIN_PATH . '/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/config/development/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['flag-lists/add/%'] = array( + 'title' => 'Add a list', + 'page callback' => 'flag_lists_add', + 'page arguments' => array(2), + 'access callback' => 'user_access', + 'access arguments' => array('create flag lists'), + 'file' => 'flag_lists.admin.inc', + 'type' => MENU_NORMAL_ITEM, + ); + // Callback for adding a new list through JS + $items['flag-lists/add/%/js'] = array( + 'title' => 'Add a list', + 'page callback' => 'flag_lists_add_js', + 'page arguments' => array(2), + 'access callback' => 'user_access', + 'access arguments' => array('create flag lists'), + 'file' => 'flag_lists.admin.inc', + 'type' => MENU_CALLBACK, + ); + + $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[FLAG_ADMIN_PATH . '/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/%user/flags/lists'] = array( + 'title' => ucfirst(variable_get('flag_lists_name', 'list')), + '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/%user/flags/lists/%'] = array( + 'title' => ucfirst(variable_get('flag_lists_name', 'list')) . ' 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($user) { + // 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($user->uid)); + $output = $view->render(); + drupal_set_title(str_replace(array_keys($view->build_info['substitutions']), $view->build_info['substitutions'], $view->build_info['title'])); + } + return $output; + } + else { + return theme('flag_lists_user_page', array('uid' => $user->uid)); + } +} + +/** + * Theme the output for a user flag administration page. + */ +function theme_flag_lists_user_page($variables) { + $uid = $variables['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', array('flag' => $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', array('items' => $items)); +} + +/** + * List the contents of a user-defined list + */ +function flag_lists_user_list($user, $fid) { + $uid = $user->uid; + // 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(str_replace(array_keys($view->build_info['substitutions']), $view->build_info['substitutions'], $view->build_info['title'])); + } + return $output; + } + + else { + return theme('flag_lists_user_list', array('uid' => $uid, 'fid' => $fid)); + } +} + +/** + * Theme the output of user-defined list page + */ +function theme_flag_lists_user_list($variables) { + $uid = $variables['uid']; + $fid = $variables['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->entity_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', array('items' => $items)); +} + +/** + * Implementation of hook_theme(). + */ +function flag_lists_theme() { + $path = drupal_get_path('module', 'flag') . '/theme'; + + return array( + 'flag_lists_list' => array( + 'variables' => array('node' => NULL, 'create' => NULL, 'ops' => NULL, 'use_flags' => NULL), + ), + 'flag_lists_admin_page' => array( + 'variables' => array('flags' => NULL), + ), + 'flag_lists_user_page' => array( + 'variables' => array('uid' => NULL), + ), + 'flag_lists_user_list' => array( + 'variables' => array('flag_name' => NULL), + ), + 'flag_lists_ops' => array( + 'variables' => array('flag' => NULL), + ) + ); +} + +/** + * Implementation of hook_permission(). + */ +function flag_lists_permission() { + return array( + 'create flag lists' => array( + 'title' => t('Create flag lists'), + 'description' => t(''), + ), + 'edit own flag lists' => array( + 'title' => t('Edit own flag lists'), + 'description' => t(''), + ), + 'delete own flag lists' => array( + 'title' => t('Delete own flag lists'), + 'description' => t(''), + ), + 'view flag lists' => array( + 'title' => t('View flag lists'), + 'description' => t(''), + )); +} + +/** + * 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_select('flag_lists_types', 'f')->fields('f')->execute(); + foreach ($result as $type) { + $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] .= '' . t('(Already has a template.)') . ''; + } + } + $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 the block admin page to place the block.'); + + $form['#validate'][] = 'flag_lists_template_validate'; + $form['#submit'][] = 'flag_lists_template_submit'; + } + break; + + case 'views_exposed_form': + // Force the exposed filters to perform actions on the page itself because + // the views default viwe didn't come with a page display + if ($form['#id'] == 'views-exposed-form-flag-lists-default') { + $form['#action'] = '/' . implode('/', arg()); + } + break; + } + + // Flag lists operations related changes + if (strpos($form_id, 'views_form_') === 0) { + $flo = _flag_lists_ops_get_field($form_state['build_info']['args'][0]); + + // Not a FLO-enabled views form. + if (empty($flo)) { + return; + } + + // Add FLO's custom callbacks. + $form['#validate'][] = 'flag_lists_ops_form_validate'; + $form['#submit'][] = 'flag_lists_ops_form_submit'; + + // Allow FLO to work when embedded using views_embed_view(), or in a block. + if (empty($flo->view->override_path)) { + if (!empty($flo->view->preview) || $flo->view->display_handler instanceof views_plugin_display_block) { + $flo->view->override_path = $_GET['q']; + } + } + + // Quickfix for FLO & exposed filters using ajax. See http://drupal.org/node/1191928. + $query = drupal_get_query_parameters($_GET, array('q')); + $form['#action'] = url($flo->view->get_url(), array('query' => $query)); + + // Add basic FLO functionality. + if ($form_state['step'] == 'views_form_views_form') { + $form = flag_lists_ops_form($form, $form_state, $flo); + } + } +} + +/** + * Add the Flag list select menu widget. + */ +function flag_lists_ops_form($form, &$form_state, $flo) { + $form['#attached']['js'][] = drupal_get_path('module', 'flag_lists') . '/js/flag_lists_ops.js'; + $form['#attached']['css'][] = drupal_get_path('module', 'flag_lists') . '/css/flag_lists_ops.css'; + $form['#prefix'] = '
'; + $form['#suffix'] = '
'; + + $form_state['flo_operation'] = $flo->options['flo']['operation']; + + // Force browser to reload the page if Back is hit. + if (preg_match('/msie/i', $_SERVER['HTTP_USER_AGENT'])) { + drupal_add_http_header('Cache-Control', 'no-cache'); // works for IE6+ + } + else { + drupal_add_http_header('Cache-Control', 'no-store'); // works for Firefox and other browsers + } + + global $user; + global $base_url; + $account = user_load($user->uid); + $items = array(); + if ($flags = flag_lists_get_user_flags(NULL, $account)) { + // Build the list of flag lists for this node. + foreach ($flags as $flag) { + $items[((string)$flag->fid)] = $flag->title; + } + } + + // Group items into a fieldset for easier theming. + $form['flag_lists_' . $form_state['flo_operation']] = array( + '#type' => 'fieldset', + '#title' => t('List operations'), + '#tree' => TRUE, + '#attributes' => array('class' => array('flag-lists-ops-fieldset')), + ); + switch ($flo->options['flo']['operation']) { + case 'unflag': + $null_text = 'Remove from a list'; + $operation = 'unflag'; + $form['flag_lists_' . $form_state['flo_operation']]['list'] = array( + '#type' => 'button', + '#value' => t('Remove from list'), + '#ajax' => array( + 'callback' => 'flag_lists_ops_form_ajax_callback', + 'wrapper' => 'flag-list-ops-container-' . $flo->options['flo']['operation'], + ), + ); + break; + default: + $null_text = 'Add to a list'; + $operation = 'flag'; + drupal_add_library('system', 'ui.dialog'); + $form['flag_lists_' . $form_state['flo_operation']]['list'] = array( + '#type' => 'select', + '#options' => array('0' => t('- ' . $null_text . ' -')) + $items, + '#default_value' => '0', + '#ajax' => array( + 'callback' => 'flag_lists_ops_form_ajax_callback', + 'wrapper' => 'flag-list-ops-container-' . $flo->options['flo']['operation'], + ), + '#attributes' => array( + 'class' => array( + 'flag-lists-ops-dropdown', + ), + ), + ); + + // Add the necessary JS for creating a new list via AJAX + $result = db_select('flag_lists_types') + ->fields('flag_lists_types') + ->execute(); + $list_types = array(); + foreach ($result as $row) { + if (!empty($row->type)) { + $list_types[] = $row->type; + } + } + drupal_add_js(array('flag_lists' => array('types' => $list_types, 'json_path' => $base_url . '/flag-lists/add/%/js')), 'setting'); + break; + } + + // Get the $ops from the originating form. + if (!empty($form_state['values']['flag_lists_' . $form_state['flo_operation']]['list'])) { + $list = $form_state['values']['flag_lists_' . $form_state['flo_operation']]['list']; + } + if (!empty($_REQUEST['flag_lists_ops'])) { + $ops = $_REQUEST['flag_lists_ops']; + } + if (!empty($ops) && !empty($list) && $form_state['values']['flag_lists_' . $form_state['flo_operation']]['operation'] == 'unflag') { + $hidden_deleted_values = ''; + foreach ($ops as $nid) { + $hidden_deleted_values .= ''; + } + } + + $form['flag_lists_' . $form_state['flo_operation']]['operation'] = array( + '#type' => 'hidden', + '#value' => $operation, + ); + $form['flag_lists_' . $form_state['flo_operation']]['go'] = array( + '#type' => 'submit', + '#value' => t('Go'), + '#attributes' => array( + 'class' => array('flag-lists-ops-go'), + ), + ); + unset($form['actions']['submit']); + + // Generate a status message for AJAX submission. + $form['flag_lists_' . $form_state['flo_operation']]['status_message'] = array('#markup' => ''); + $form['flag_lists_' . $form_state['flo_operation']]['#prefix'] = '
'; + $form['flag_lists_' . $form_state['flo_operation']]['#suffix'] = (!empty($hidden_deleted_values)) ? $hidden_deleted_values : '' . '
'; + + 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), + ); +}