Index: includes/flag_handler_relationships.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/flag/includes/Attic/flag_handler_relationships.inc,v retrieving revision 1.1.2.9 diff -u -r1.1.2.9 flag_handler_relationships.inc --- includes/flag_handler_relationships.inc 26 Apr 2009 21:35:12 -0000 1.1.2.9 +++ includes/flag_handler_relationships.inc 28 Sep 2009 05:38:18 -0000 @@ -43,11 +43,16 @@ if (!$form['flag']['#options']) { $form = array( 'error' => array( - '#value' => '
' . t('No %type flags exist. You must first create a %type flag before being able to use this relationship type.', array('%type' => $content_type, '@create-url' => url('admin/build/flags'))) . '
', + '#value' => '' . t('No %type flags exist. You must first create a %type flag before being able to use this relationship type.', array('%type' => $content_type, '@create-url' => url('admin/build/flags'))) . '
', ), ); $form_state['no flags exist'] = TRUE; } + if (module_exists('session_api')) { + $form['session_warning'] = array( + '#value' => '' . t('Warning: Adding this relationship for any flag that contains anonymous flagging access will disable page caching for anonymous users when this view is executed. It is recommended to create a dedicated page for views containing anonymous user data.', array('%type' => $content_type, '@create-url' => url('admin/build/flags'))) . '
', + ); + } } function options_validate($form, &$form_state) { @@ -81,6 +86,17 @@ 'value' => '***CURRENT_USER***', 'numeric' => TRUE, ); + if (array_search(DRUPAL_ANONYMOUS_RID, $flag->roles['flag']) !== FALSE) { + // Disable page caching for anonymous users. + $GLOBALS['conf']['cache'] = CACHE_DISABLED; + + // Add in the SID from Session API for anonymous users. + $this->definition['extra'][] = array( + 'field' => 'sid', + 'value' => flag_get_sid(), + 'numeric' => TRUE, + ); + } } parent::query(); } Index: includes/flag.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/flag/includes/Attic/flag.admin.inc,v retrieving revision 1.1.4.2.2.6 diff -u -r1.1.4.2.2.6 flag.admin.inc --- includes/flag.admin.inc 28 Sep 2009 02:05:06 -0000 1.1.4.2.2.6 +++ includes/flag.admin.inc 28 Sep 2009 05:38:18 -0000 @@ -324,19 +324,26 @@ $form['access']['roles'] = array( '#title' => t('Roles that may use this flag'), '#access' => empty($flag->locked['roles']), - '#description' => t('Users may only unflag content if they have access to flag the content initially. Checking authenticated user will allow access for all logged-in users. Anonymous users may not flag content.'), + '#description' => t('Users may only unflag content if they have access to flag the content initially. Checking authenticated user will allow access for all logged-in users.'), '#theme' => 'flag_form_roles', '#weight' => -2, ); + if (module_exists('session_api')) { + $form['access']['roles']['#description'] .= ' ' . t('Support for anonymous users is being provided by Session API.'); + } + else { + $form['access']['roles']['#description'] .= ' ' . t('Anonymous users may flag content if the Session API module is installed.'); + } + $form['access']['roles']['flag'] = array( '#type' => 'checkboxes', - '#options' => user_roles(TRUE), + '#options' => user_roles(!module_exists('session_api')), '#default_value' => $flag->roles['flag'], '#parents' => array('roles', 'flag'), ); $form['access']['roles']['unflag'] = array( '#type' => 'checkboxes', - '#options' => user_roles(TRUE), + '#options' => user_roles(!module_exists('session_api')), '#default_value' => $flag->roles['unflag'], '#parents' => array('roles', 'unflag'), ); Index: includes/flag_handler_field_ops.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/flag/includes/Attic/flag_handler_field_ops.inc,v retrieving revision 1.1.2.5.2.1 diff -u -r1.1.2.5.2.1 flag_handler_field_ops.inc --- includes/flag_handler_field_ops.inc 14 Sep 2009 11:46:40 -0000 1.1.2.5.2.1 +++ includes/flag_handler_field_ops.inc 28 Sep 2009 05:38:18 -0000 @@ -81,6 +81,11 @@ 'value' => '***CURRENT_USER***', 'numeric' => TRUE, ); + $join->extra[] = array( + 'field' => 'sid', + 'value' => flag_get_sid(), + 'numeric' => TRUE, + ); } $flag_table = $this->query->add_table('flag_content', $parent, $join); $this->aliases['is_flagged'] = $this->query->add_field($flag_table, 'content_id'); Index: flag.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.install,v retrieving revision 1.2.2.32.2.2 diff -u -r1.2.2.32.2.2 flag.install --- flag.install 28 Sep 2009 02:05:06 -0000 1.2.2.32.2.2 +++ flag.install 28 Sep 2009 05:38:17 -0000 @@ -196,6 +196,12 @@ 'not null' => TRUE, 'default' => 0, ), + 'sid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), 'timestamp' => array( 'type' => 'int', 'unsigned' => TRUE, @@ -206,11 +212,11 @@ ), 'primary key' => array('fcid'), 'unique keys' => array( - 'fid_content_type_content_id_uid' => array('fid', 'content_type', 'content_id', 'uid'), + 'fid_content_type_content_id_uid_sid' => array('fid', 'content_type', 'content_id', 'uid', 'sid'), ), 'indexes' => array( 'content_type_content_id' => array('content_type', 'content_id'), - 'content_type_uid' => array('content_type', 'uid'), + 'content_type_uid_sid' => array('content_type', 'uid', 'sid'), ), ); @@ -476,6 +482,26 @@ return $ret; } +/** + * Add the sid column and unique index on the flag_content table. + */ +function flag_update_6201() { + $ret = array(); + + // Drop the keys affected by the addition of the SID column. + db_drop_unique_key($ret, 'flag_content', 'fid_content_type_content_id_uid'); + db_drop_index($ret, 'flag_content', 'content_type_uid'); + + // Add the column. + db_add_field($ret, 'flag_content', 'sid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0)); + + // Re-add the removed keys. + db_add_unique_key($ret, 'flag_content', 'fid_content_type_content_id_uid_sid', array('fid', 'content_type', 'content_id', 'uid', 'sid')); + db_add_index($ret, 'flag_content', 'content_type_uid_sid', array('content_type', 'uid', 'sid')); + + return $ret; +} + // This is a replacement for update_sql(). The latter doesn't support placeholders. function _flag_update_sql($sql) { $args = func_get_args(); Index: flag.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.module,v retrieving revision 1.11.2.72.2.13 diff -u -r1.11.2.72.2.13 flag.module --- flag.module 28 Sep 2009 02:05:06 -0000 1.11.2.72.2.13 +++ flag.module 28 Sep 2009 05:38:17 -0000 @@ -88,6 +88,10 @@ if (module_exists('token')) { include_once $path .'/includes/flag.token.inc'; } + if (module_exists('session_api')) { + // Set the anonymous user SID immediately, in case the user logs in. + flag_set_sid(); + } } /** @@ -280,14 +284,8 @@ ); } } - elseif (isset($form['type']) && isset($form['#node']) - && ($form_id == $form['type']['#value'] .'_node_form')) { - if (!$user->uid) { - return; - } - + elseif (isset($form['type']) && isset($form['#node']) && ($form_id == $form['type']['#value'] .'_node_form')) { $nid = !empty($form['nid']['#value']) ? $form['nid']['#value'] : NULL; - $flags = flag_get_flags('node', $form['type']['#value'], $user); // Filter out flags which need to be included on the node form. @@ -407,6 +405,16 @@ */ function flag_user($op, &$edit, &$account, $category = NULL) { switch ($op) { + case 'login': + // Migrate anonymous flags to this user's account. + if (module_exists('session_api')) { + // The @ symbol suppresses errors if the user flags a piece of content + // they have already flagged as a logged-in user. + @db_query("UPDATE {flag_content} SET uid = %d, sid = 0 WHERE uid = 0 AND sid = %d", $account->uid, flag_get_sid(0)); + // Delete any remaining flags this user had as an anonymous user. + db_query("DELETE FROM {flag_content} WHERE uid = 0 AND sid = %d", flag_get_sid()); + } + break; case 'delete': // Remove flags by this user. $result = db_query("SELECT fc.fid, fc.content_id, c.count FROM {flag_content} fc LEFT JOIN {flag_counts} c ON fc.content_id = c.content_id AND fc.content_type = c.content_type WHERE fc.uid = %d", $account->uid); @@ -448,6 +456,22 @@ } /** + * Implementation of hook_session_api_cleanup(). + * + * Clear out anonymous user flaggings during Session API cleanup. + */ +function flag_session_api_cleanup($op = 'run') { + if ($op == 'run') { + // Fetch list of outdated sids. + $result = db_query("SELECT fc.sid FROM {flag_content} fc LEFT JOIN {session_api} s ON (fc.sid = s.sid) WHERE fc.sid <> 0 AND s.sid IS NULL"); + while ($row = db_fetch_object($result)) { + // Remove the flag content rows. + db_query("DELETE FROM {flag_content} WHERE sid = %d", $row->sid); + } + } +} + +/** * Implementation of hook_node_type(). */ function flag_node_type($op, $info) { @@ -635,10 +659,10 @@ $result = db_query("SELECT nid, uid FROM {node} WHERE nid IN ($nids) AND type in ($placeholders)", $flag->types); while ($row = db_fetch_object($result)) { if ($flag->access_author == 'own') { - $passed[$row->nid] = $row->uid != $account->uid ? FALSE : $passed[$row->nid]; + $passed[$row->nid] = $row->uid != $account->uid ? FALSE : NULL; } elseif ($flag->access_author == 'others') { - $passed[$row->nid] = $row->uid == $account->uid ? FALSE : $passed[$row->nid]; + $passed[$row->nid] = $row->uid == $account->uid ? FALSE : NULL; } } return $passed; @@ -1159,6 +1183,9 @@ * @param $uid * Optional. The user ID whose flags we're checking. If none given, the * current user will be used. + * @param $sid + * Optional. The user SID (proved by Session API) whose flags we're checking. + * If none given, the current user will be used. SID is 0 for logged in users. * @param $reset * Reset the internal cache and execute the SQL query another time. * @@ -1170,7 +1197,7 @@ * [nid] => [name] => Object from above. * */ -function flag_get_user_flags($content_type, $content_id = NULL, $uid = NULL, $reset = FALSE) { +function flag_get_user_flags($content_type, $content_id = NULL, $uid = NULL, $sid = NULL, $reset = FALSE) { static $flagged_content; if ($reset) { @@ -1181,29 +1208,30 @@ } $uid = !isset($uid) ? $GLOBALS['user']->uid : $uid; + $sid = !isset($sid) ? flag_get_sid($uid) : $sid; if (isset($content_id)) { if (!isset($flagged_content[$uid][$content_type][$content_id])) { $flag_names = _flag_get_flag_names(); - $flagged_content[$uid][$content_type][$content_id] = array(); - $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND content_id = %d AND (uid = %d OR uid = 0)", $content_type, $content_id, $uid); + $flagged_content[$uid][$sid][$content_type][$content_id] = array(); + $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND content_id = %d AND (uid = %d OR uid = 0) AND sid = %s", $content_type, $content_id, $uid, $sid); while ($flag_content = db_fetch_object($result)) { - $flagged_content[$uid][$content_type][$content_id][$flag_names[$flag_content->fid]] = $flag_content; + $flagged_content[$uid][$sid][$content_type][$content_id][$flag_names[$flag_content->fid]] = $flag_content; } } - return $flagged_content[$uid][$content_type][$content_id]; + return $flagged_content[$uid][$sid][$content_type][$content_id]; } else { - if (!isset($flagged_content[$uid]['all'][$content_type])) { + if (!isset($flagged_content[$uid][$sid]['all'][$content_type])) { $flag_names = _flag_get_flag_names(); - $flagged_content[$uid]['all'][$content_type] = TRUE; - $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND (uid = %d OR uid = 0)", $content_type, $uid); + $flagged_content[$uid][$sid]['all'][$content_type] = TRUE; + $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND (uid = %d OR uid = 0) AND sid = %s", $content_type, $uid, $sid); while ($flag_content = db_fetch_object($result)) { - $flagged_content[$uid][$content_type]['all'][$flag_names[$flag_content->fid]][$flag_content->content_id] = $flag_content; + $flagged_content[$uid][$sid][$content_type]['all'][$flag_names[$flag_content->fid]][$flag_content->content_id] = $flag_content; } } - return $flagged_content[$uid][$content_type]['all']; + return $flagged_content[$uid][$sid][$content_type]['all']; } } @@ -1298,13 +1326,47 @@ /** * Get a private token used to protect links from spoofing - CSRF. */ -function flag_get_token($nid) { - return drupal_get_token($nid); +function flag_get_token($content_id) { + // Anonymous users get a less secure token, since it must be the same for all + // anonymous users on the entire site to work with page caching. + return ($GLOBALS['user']->uid) ? drupal_get_token($content_id) : md5(drupal_get_private_key() . $content_id); } /** * Check to see if a token value matches the specified node. */ -function flag_check_token($token, $seed) { - return drupal_get_token($seed) == $token; +function flag_check_token($token, $content_id) { + return flag_get_token($content_id) == $token; +} + +/** + * Set the Session ID for a user. Utilizes the Session API module. + * + * This function is only called in flag_init(), to set the current user's + * SID in case the user logs in during this request. + */ +function flag_set_sid($uid = NULL) { + static $sids = array(); + + if (!isset($uid)) { + $uid = $GLOBALS['user']->uid; + } + + if (!isset($sids[$uid])) { + if (module_exists('session_api') && session_api_available() && $uid == 0) { + $sids[$uid] = session_api_get_sid(); + } + else { + $sids[$uid] = 0; + } + } + + return $sids[$uid]; +} + +/** + * Get the Session ID for a user. Utilizes the Session API module. + */ +function flag_get_sid($uid = NULL) { + return flag_set_sid($uid); } Index: flag.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.inc,v retrieving revision 1.1.2.30.2.10 diff -u -r1.1.2.30.2.10 flag.inc --- flag.inc 28 Sep 2009 03:39:13 -0000 1.1.2.30.2.10 +++ flag.inc 28 Sep 2009 05:38:17 -0000 @@ -399,13 +399,13 @@ $account = $GLOBALS['user']; } - // Anonymous user can't use this system. For now. - if ($account->uid == 0) { + // Anonymous user can't use this system unless Session API is installed. + if ($account->uid == 0 && !module_exists('session_api')) { return FALSE; } $matched_roles = array_intersect($this->roles[$action], array_keys($account->roles)); - return !empty($matched_roles) || !empty($this->roles[$action]) || $account->uid == 1; + return (bool) $matched_roles || $account->uid == 1; } /** @@ -431,7 +431,9 @@ } if (!isset($action)) { - $action = $this->is_flagged($content_id, $account->uid) ? 'unflag' : 'flag'; + $uid = $account->uid; + $sid = flag_get_sid($uid); + $action = $this->is_flagged($content_id, $uid, $sid) ? 'unflag' : 'flag'; } // Base initial access on the user's basic permission to use this flag. @@ -546,18 +548,19 @@ // 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, TRUE); + flag_get_user_flags(NULL, NULL, NULL, NULL, TRUE); // Perform the flagging or unflagging of this flag. $uid = $this->global ? 0 : $account->uid; - $flagged = $this->_is_flagged($content_id, $uid); + $sid = $this->global ? 0 : flag_get_sid($uid); + $flagged = $this->_is_flagged($content_id, $uid, $sid); if ($action == 'unflag' && $flagged) { - $fcid = $this->_unflag($content_id, $uid); + $fcid = $this->_unflag($content_id, $uid, $sid); // Let other modules perform actions. module_invoke_all('flag', 'unflag', $this, $content_id, $account, $fcid); } elseif ($action == 'flag' && !$flagged) { - $fcid = $this->_flag($content_id, $uid); + $fcid = $this->_flag($content_id, $uid, $sid); // Let other modules perform actions. module_invoke_all('flag', 'flag', $this, $content_id, $account, $fcid); } @@ -575,10 +578,12 @@ * Optional. The user ID whose flags we're checking. If none given, the * current user will be used. */ - function is_flagged($content_id, $uid = NULL) { - $uid = !isset($uid) ? $GLOBALS['user']->uid : $uid; + function is_flagged($content_id, $uid = NULL, $sid = NULL) { + $uid = $this->global ? 0 : (!isset($uid) ? $GLOBALS['user']->uid : $uid); + $sid = $this->global ? 0 : (!isset($sid) ? flag_get_sid($uid) : $sid); + // flag_get_user_flags() does caching. - $user_flags = flag_get_user_flags($this->content_type, $content_id, $uid); + $user_flags = flag_get_user_flags($this->content_type, $content_id, $uid, $sid); return isset($user_flags[$this->name]); } @@ -594,8 +599,8 @@ * * @private */ - function _is_flagged($content_id, $uid) { - return db_result(db_query("SELECT fid FROM {flag_content} WHERE fid = %d AND uid = %d AND content_id = %d", $this->fid, $uid, $content_id)); + function _is_flagged($content_id, $uid, $sid) { + return db_result(db_query("SELECT fid FROM {flag_content} WHERE fid = %d AND uid = %d AND sid = %d AND content_id = %d", $this->fid, $uid, $sid, $content_id)); } /** @@ -606,8 +611,8 @@ * * @private */ - function _flag($content_id, $uid) { - db_query("INSERT INTO {flag_content} (fid, content_type, content_id, uid, timestamp) VALUES (%d, '%s', %d, %d, %d)", $this->fid, $this->content_type, $content_id, $uid, time()); + function _flag($content_id, $uid, $sid) { + db_query("INSERT INTO {flag_content} (fid, content_type, content_id, uid, sid, timestamp) VALUES (%d, '%s', %d, %d, %d, %d)", $this->fid, $this->content_type, $content_id, $uid, $sid, time()); $fcid = db_last_insert_id('flag_content', 'fcid'); $this->_update_count($content_id); return $fcid; @@ -621,8 +626,8 @@ * * @private */ - function _unflag($content_id, $uid) { - $fcid = db_result(db_query("SELECT fcid FROM {flag_content} WHERE fid = %d AND uid = %d AND content_id = %d", $this->fid, $uid, $content_id)); + function _unflag($content_id, $uid, $sid) { + $fcid = db_result(db_query("SELECT fcid FROM {flag_content} WHERE fid = %d AND content_id = %d AND uid = %d AND sid = %d", $this->fid, $content_id, $uid, $sid)); if ($fcid) { db_query("DELETE FROM {flag_content} WHERE fcid = %d", $fcid); $this->_update_count($content_id); @@ -657,10 +662,10 @@ /** * Returns the number of items a user has flagged. * - * For global flags, pass '0' as the user ID. + * For global flags, pass '0' as the user ID and session ID. */ - function get_user_count($uid) { - return db_result(db_query('SELECT COUNT(*) FROM {flag_content} WHERE fid = %d AND uid = %d', $this->fid, $uid)); + function get_user_count($uid, $sid) { + return db_result(db_query('SELECT COUNT(*) FROM {flag_content} WHERE fid = %d AND uid = %d AND sid = %d', $this->fid, $uid, $sid)); } /** @@ -1070,10 +1075,9 @@ return parent::flag($action, $content_id, $account, $skip_permission_check); } - - function is_flagged($content_id, $uid = NULL) { + function is_flagged($content_id, $uid = NULL, $sid = NULL) { $content_id = $this->get_translation_id($content_id); - return parent::is_flagged($content_id, $uid); + return parent::is_flagged($content_id, $uid, $sid); } function get_labels_token_types() { @@ -1423,7 +1427,6 @@ function _flag_is_drupal_5() { return !function_exists('theme_get_registry'); } - // That's ugly, but duplicating this logic is uglier. function _flag_url($path, $fragment = NULL, $absolute = TRUE) { return _flag_is_drupal_5()