Changeset 867

Show
Ignore:
Timestamp:
03/03/10 20:57:52 (5 months ago)
Author:
frans
Message:

update: flag to 6.x-2.x-dev version

Location:
drupal-6/sites/all/modules/flag
Files:
35 added
17 modified

Legend:

Unmodified
Added
Removed
  • drupal-6/sites/all/modules/flag/README.txt

    r535 r867  
    1 // $Id: README.txt,v 1.1.4.6 2008/10/07 16:41:26 mooffie Exp $ 
     1// $Id: README.txt,v 1.1.4.6.2.1 2009/10/28 00:01:31 quicksketch Exp $ 
    22 
    33You may want to visit the handbook of this module, at: 
     
    4646displays possible relating to the number of times an item has been flagged. 
    4747 
    48 This module was formerly known as Views Bookmark. 
     48This module was formerly known as Views Bookmark, which was originally was 
     49written by Earl Miles. Flag was written by Nathan Haug and mystery man Mooffie. 
    4950 
    50 Ongoing development by Nathan Haug and mystery man Mooffie. 
    51  
    52 Originally written by Earl Miles. 
     51This module built by robots: http://www.lullabot.com 
    5352 
    5453Recommended Modules 
    5554------------------- 
    5655- Views 
     56- Session API 
    5757 
    5858Installation 
     
    6161 
    62622) Enable the module using Administer -> Modules (/admin/build/modules) 
     63 
     64Optional Installation 
     65--------------------- 
     661) The ability for anonymous users to flag content is provided by the Session 
     67   API module, available at http://drupal.org/project/session_api. 
    6368 
    6469Configuration 
  • drupal-6/sites/all/modules/flag/flag.inc

    r865 r867  
    11<?php 
    2 // $Id: flag.inc,v 1.1.2.32 2010/01/08 05:00:45 quicksketch Exp $ 
     2// $Id: flag.inc,v 1.1.2.30.2.20 2010/01/08 05:14:24 quicksketch Exp $ 
    33 
    44/** 
     
    110110  // database columns. 
    111111  var $title = ''; 
    112   var $roles = array(DRUPAL_AUTHENTICATED_RID); 
    113112  var $global = FALSE; 
    114113  // The sub-types, e.g. node types, this flag applies to. 
     
    137136    unset($flag->options, $flag->type); 
    138137 
    139     $options = (array)unserialize($row->options); 
     138    // Populate the options with the defaults. 
     139    $options = (array) unserialize($row->options); 
     140    $options += $flag->options(); 
     141 
    140142    // Make the unserialized options accessible as normal properties. 
    141143    foreach ($options as $option => $value) { 
     
    148150    } 
    149151 
    150     $flag->roles = empty($row->roles) ? array() : explode(',', $row->roles); 
    151  
    152152    return $flag; 
    153153  } 
     
    185185   * Derived classes should want to override this. 
    186186   */ 
    187   function default_options() { 
    188     return array( 
     187  function options() { 
     188    $options = array( 
    189189      'flag_short' => '', 
    190190      'flag_long' => '', 
    191191      'flag_message' => '', 
    192       'flag_confirmation' => '', 
    193192      'unflag_short' => '', 
    194193      'unflag_long' => '', 
    195194      'unflag_message' => '', 
    196       'unflag_confirmation' => '', 
     195      'unflag_denied_text' => '', 
    197196      'link_type' => 'toggle', 
    198     ); 
     197      'roles' => array( 
     198        'flag' => array(DRUPAL_AUTHENTICATED_RID), 
     199        'unflag' => array(DRUPAL_AUTHENTICATED_RID), 
     200      ), 
     201    ); 
     202 
     203    // Merge in options from the current link type. 
     204    $link_type = $this->get_link_type(); 
     205    $options = array_merge($options, $link_type['options']); 
     206 
     207    // Allow other modules to change the flag options. 
     208    drupal_alter('flag_options', $options, $this); 
     209    return $options; 
    199210  } 
    200211 
     
    211222   */ 
    212223  function construct() { 
    213     $options = $this->default_options(); 
     224    $options = $this->options(); 
    214225    foreach ($options as $option => $value) { 
    215226      $this->$option = $value; 
     
    227238    } 
    228239    // But checkboxes need some massaging: 
    229     $this->roles = array_values(array_filter($this->roles)); 
     240    $this->roles['flag'] = array_values(array_filter($this->roles['flag'])); 
     241    $this->roles['unflag'] = array_values(array_filter($this->roles['unflag'])); 
    230242    $this->types = array_values(array_filter($this->types)); 
    231243    // Clear internal titles cache: 
     
    234246 
    235247  /** 
    236    * Validates a flag settings 
     248   * Validates this flag's options. 
     249   * 
     250   * @return 
     251   *   A list of errors encountered while validating this flag's options. 
    237252   */ 
    238253  function validate() { 
    239     $this->validate_name(); 
    240   } 
    241  
     254    // TODO: It might be nice if this used automatic method discovery rather 
     255    // than hard-coding the list of validate functions. 
     256    return array_merge_recursive( 
     257      $this->validate_name(), 
     258      $this->validate_access() 
     259    ); 
     260  } 
     261 
     262  /** 
     263   * Validates that the current flag's name is valid. 
     264   */ 
    242265  function validate_name() { 
     266    $errors = array(); 
     267 
    243268    // Ensure a safe machine name. 
    244269    if (!preg_match('/^[a-z_][a-z0-9_]*$/', $this->name)) { 
    245       form_set_error('name', t('The flag name may only contain lowercase letters, underscores, and numbers.')); 
     270      $errors['name'][] = array( 
     271        'error' => 'flag_name_characters', 
     272        'message' => t('The flag name may only contain lowercase letters, underscores, and numbers.'), 
     273      ); 
    246274    } 
    247275    // Ensure the machine name is unique. 
    248     if (!isset($this->fid)) { 
    249       $flag = flag_get_flag($this->name); 
    250       if (!empty($flag)) { 
    251         form_set_error('name', t('Flag names must be unique. This flag name is already in use.')); 
    252       } 
    253     } 
     276    $flag = flag_get_flag($this->name); 
     277    if (!empty($flag) && (!isset($this->fid) || $flag->fid != $this->fid)) { 
     278      $errors['name'][] = array( 
     279        'error' => 'flag_name_unique', 
     280        'message' => t('Flag names must be unique. This flag name is already in use.'), 
     281      ); 
     282    } 
     283 
     284    return $errors; 
     285  } 
     286 
     287  /** 
     288   * Validates that the current flag's access settings are valid. 
     289   */ 
     290  function validate_access() { 
     291    $errors = array(); 
     292 
     293    // Require an unflag access denied message a role is not allowed to unflag. 
     294    if (empty($this->unflag_denied_text)) { 
     295      foreach ($this->roles['flag'] as $key => $rid) { 
     296        if ($rid && empty($this->roles['unflag'][$key])) { 
     297          $errors['unflag_denied_text'][] = array( 
     298            'error' => 'flag_denied_text_required', 
     299            'message' => t('The "Unflag not allowed text" is required if any user roles are not allowed to unflag.'), 
     300          ); 
     301          break; 
     302        } 
     303      } 
     304    } 
     305 
     306    // Do not allow unflag access without flag access. 
     307    foreach ($this->roles['unflag'] as $key => $rid) { 
     308      if ($rid && empty($this->roles['flag'][$key])) { 
     309        $errors['roles'][] = array( 
     310          'error' => 'flag_roles_unflag', 
     311          'message' => t('Any user role that has the ability to unflag must also have the ability to flag.'), 
     312        ); 
     313        break; 
     314      } 
     315    } 
     316 
     317    return $errors; 
    254318  } 
    255319 
     
    294358 
    295359  /** 
     360   * @defgroup access Access control 
     361   * @{ 
     362   */ 
     363 
     364  /** 
    296365   * Returns TRUE if the flag applies to the given content. 
    297366   * Derived classes must implement this. 
     
    315384 
    316385  /** 
     386   * Returns TRUE if user has access to use this flag. 
     387   * 
     388   * @param $action 
     389   *   Optional. The action to test, either "flag" or "unflag". If none given, 
     390   *   "flag" will be tested, which is the minimum permission to use a flag. 
     391   * @param $account 
     392   *   Optional. The user object. If none given, the current user will be used. 
     393   * 
     394   * @return 
     395   *   Boolean TRUE if the user is allowed to flag/unflag. FALSE otherwise. 
     396   */ 
     397  function user_access($action = 'flag', $account = NULL) { 
     398    if (!isset($account)) { 
     399      $account = $GLOBALS['user']; 
     400    } 
     401 
     402    // Anonymous user can't use this system unless Session API is installed. 
     403    if ($account->uid == 0 && !module_exists('session_api')) { 
     404      return FALSE; 
     405    } 
     406 
     407    $matched_roles = array_intersect($this->roles[$action], array_keys($account->roles)); 
     408    return !empty($matched_roles) || $account->uid == 1; 
     409  } 
     410 
     411  /** 
     412   * Returns TRUE if the user can flag, or unflag, the given content. 
     413   * 
     414   * @param $content_id 
     415   *   The content ID to flag/unflag. 
     416   * @param $account 
     417   *   The user on whose behalf to test the flagging action. Leave NULL for the 
     418   *   current user. 
     419   * @param $action 
     420   *   The action to test. Either 'flag' or 'unflag'. Leave NULL to determine 
     421   *   by flag status. 
     422   */ 
     423  function access($content_id, $action = NULL, $account = NULL) { 
     424    if (!isset($account)) { 
     425      $account = $GLOBALS['user']; 
     426    } 
     427 
     428    if (isset($content_id) && !$this->applies_to_content_id($content_id) ){ 
     429      // Flag does not apply to this content. 
     430      return FALSE; 
     431    } 
     432 
     433    if (!isset($action)) { 
     434      $uid = $account->uid; 
     435      $sid = flag_get_sid($uid); 
     436      $action = $this->is_flagged($content_id, $uid, $sid) ? 'unflag' : 'flag'; 
     437    } 
     438 
     439    // Base initial access on the user's basic permission to use this flag. 
     440    $access = $this->user_access($action, $account); 
     441 
     442    // Allow modules to disallow (or allow) access to flagging. 
     443    $access_array = module_invoke_all('flag_access', $this, $content_id, $action, $account); 
     444 
     445    foreach ($access_array as $set_access) { 
     446      if (isset($set_access)) { 
     447        $access = $set_access; 
     448      } 
     449    } 
     450 
     451    return $access; 
     452  } 
     453 
     454  /** 
     455   * Similar to access() but works on multiple IDs at once. It is called in the 
     456   * pre_render() stage of the 'Flag links' field within Views to find out where 
     457   * that link applies. The reason we do a separate DB query, and not lump this 
     458   * test in the Views query, is to make 'many to one' tests possible without 
     459   * interfering with the rows, and also to reduce the complexity of the code. 
     460   * 
     461   * @param $content_ids 
     462   *   The array of content IDs to check. The keys are the content IDs, the 
     463   *   values are the actions to test: either 'flag' or 'unflag'. 
     464   * @param $account 
     465   *   Optional. The account for which the actions will be compared against. 
     466   *   If left empty, the current user will be used. 
     467   */ 
     468  function access_multiple($content_ids, $account = NULL) { 
     469    $account = isset($account) ? $account : $GLOBALS['user']; 
     470    $access = array(); 
     471 
     472    // First check basic user access for this action. 
     473    foreach ($content_ids as $content_id => $action) { 
     474      $access[$content_id] = $this->user_access($content_ids[$content_id], $account); 
     475    } 
     476 
     477    // Merge in module-defined access. 
     478    foreach (module_implements('flag_access_multiple') as $module) { 
     479      $module_access = module_invoke($module, 'flag_access_multiple', $this, $content_ids, $account); 
     480      foreach ($module_access as $content_id => $content_access) { 
     481        if (isset($content_access)) { 
     482          $access[$content_id] = $content_access; 
     483        } 
     484      } 
     485    } 
     486 
     487    return $access; 
     488  } 
     489 
     490  /** 
     491   * @} End of "defgroup access". 
     492   */ 
     493 
     494  /** 
    317495   * Given a content object, returns its ID. 
    318496   * Derived classes must implement this. 
     
    333511 
    334512  /** 
    335    * Returns TRUE if user has access to use this flag. 
    336    * 
    337    * @param $account 
    338    *   Optional. The user object. If none given, the current user will be used. 
    339    * 
    340    * @return 
    341    *   Boolean TRUE if the user is allowed to flag/unflag. FALSE otherwise. 
    342    */ 
    343   function user_access($account = NULL) { 
    344     if (!isset($account)) { 
    345       $account = $GLOBALS['user']; 
    346     } 
    347     $matched_roles = array_intersect($this->roles, array_keys($account->roles)); 
    348     return !empty($matched_roles) || empty($this->roles) || $account->uid == 1; 
    349   } 
    350  
    351   /** 
    352    * Flags, on unflags, an item. 
     513   * Returns TRUE if this flag requires anonymous user cookies. 
     514   */ 
     515  function uses_anonymous_cookies() { 
     516    global $user; 
     517    return $user->uid == 0 && variable_get('cache', CACHE_DISABLED) > 0; 
     518  } 
     519 
     520  /** 
     521   * Flags, or unflags, an item. 
    353522   * 
    354523   * @param $action 
     
    371540      return FALSE; 
    372541    } 
    373  
    374     if (!$account->uid) { 
    375       // Anonymous users can't flag with this system. For now. 
    376       // 
    377       // @todo This is legacy code. $flag->user_access() should handle this. 
    378       // This will also make it posible to have flags that do support anonymous 
    379       // users. 
    380       return FALSE; 
    381     } 
    382  
    383     if (!$skip_permission_check && !$this->user_access($account)) { 
    384       // User has no permission to use this flag. 
    385       return FALSE; 
    386     } 
    387     if (!$this->applies_to_content_id($content_id)) { 
    388       // Flag does not apply to this content. 
    389       return FALSE; 
     542    if (!$skip_permission_check) { 
     543      if (!$this->access($content_id, $action, $account)) { 
     544        // User has no permission to flag/unflag this object. 
     545        return FALSE; 
     546      } 
     547    } 
     548    else { 
     549      // We are skipping permission checks. However, at a minimum we must make 
     550      // sure the flag applies to this content type: 
     551      if (!$this->applies_to_content_id($content_id)) { 
     552        return FALSE; 
     553      } 
    390554    } 
    391555 
     
    393557    // wrong counts or false flaggings. 
    394558    flag_get_counts(NULL, NULL, TRUE); 
    395     flag_get_user_flags(NULL, NULL, NULL, TRUE); 
     559    flag_get_user_flags(NULL, NULL, NULL, NULL, TRUE); 
    396560 
    397561    // Perform the flagging or unflagging of this flag. 
    398562    $uid = $this->global ? 0 : $account->uid; 
    399     $flagged = $this->_is_flagged($content_id, $uid); 
    400     if ($action == 'unflag' && $flagged) { 
    401       $this->_unflag($content_id, $uid); 
    402       // Let other modules perform actions. 
    403       module_invoke_all('flag', 'unflag', $this, $content_id, $account); 
    404     } 
    405     elseif ($action == 'flag' && !$flagged) { 
    406       $this->_flag($content_id, $uid); 
    407       // Let other modules perform actions. 
    408       module_invoke_all('flag', 'flag', $this, $content_id, $account); 
     563    $sid = $this->global ? 0 : flag_get_sid($uid); 
     564    $flagged = $this->_is_flagged($content_id, $uid, $sid); 
     565    if ($action == 'unflag') { 
     566      if ($this->uses_anonymous_cookies()) { 
     567        $this->_unflag_anonymous($content_id); 
     568      } 
     569      if ($flagged) { 
     570        $fcid = $this->_unflag($content_id, $uid, $sid); 
     571        module_invoke_all('flag', 'unflag', $this, $content_id, $account, $fcid); 
     572      } 
     573    } 
     574    elseif ($action == 'flag') { 
     575      if ($this->uses_anonymous_cookies()) { 
     576        $this->_flag_anonymous($content_id); 
     577      } 
     578      if (!$flagged) { 
     579        $fcid = $this->_flag($content_id, $uid, $sid); 
     580        module_invoke_all('flag', 'flag', $this, $content_id, $account, $fcid); 
     581      } 
    409582    } 
    410583 
     
    422595   *   current user will be used. 
    423596   */ 
    424   function is_flagged($content_id, $uid = NULL) { 
    425     $uid = !isset($uid) ? $GLOBALS['user']->uid : $uid; 
     597  function is_flagged($content_id, $uid = NULL, $sid = NULL) { 
     598    $uid = $this->global ? 0 : (!isset($uid) ? $GLOBALS['user']->uid : $uid); 
     599    $sid = $this->global ? 0 : (!isset($sid) ? flag_get_sid($uid) : $sid); 
     600 
    426601    // flag_get_user_flags() does caching. 
    427     $user_flags = flag_get_user_flags($this->content_type, $content_id, $uid); 
     602    $user_flags = flag_get_user_flags($this->content_type, $content_id, $uid, $sid); 
    428603    return isset($user_flags[$this->name]); 
    429604  } 
     
    441616   * @private 
    442617   */ 
    443   function _is_flagged($content_id, $uid) { 
    444     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)); 
     618  function _is_flagged($content_id, $uid, $sid) {  
     619    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)); 
    445620  } 
    446621 
     
    453628   * @private 
    454629   */ 
    455   function _flag($content_id, $uid) { 
    456     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()); 
     630  function _flag($content_id, $uid, $sid) { 
     631    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()); 
     632    $fcid = db_last_insert_id('flag_content', 'fcid'); 
    457633    $this->_update_count($content_id); 
     634    return $fcid; 
    458635  } 
    459636 
     
    466643   * @private 
    467644   */ 
    468   function _unflag($content_id, $uid) { 
    469     db_query("DELETE FROM {flag_content} WHERE fid = %d AND uid = %d AND content_id = %d", $this->fid, $uid, $content_id); 
    470     $this->_update_count($content_id); 
     645  function _unflag($content_id, $uid, $sid) { 
     646    $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)); 
     647    if ($fcid) { 
     648      db_query("DELETE FROM {flag_content} WHERE fcid = %d", $fcid); 
     649      $this->_update_count($content_id); 
     650    } 
     651    // Remove anonymous cookies (if any). 
     652    if (isset($_COOKIE['flag'][$this->name . '_' . $content_id])) { 
     653      unset($_COOKIE['flags'][$this->name . '_' . $content_id]); 
     654    } 
     655    return $fcid; 
    471656  } 
    472657 
     
    490675 
    491676  /** 
     677   * Set a cookie for anonymous users to record their flagging. 
     678   * 
     679   * @private 
     680   */ 
     681  function _flag_anonymous($content_id) { 
     682    // Global flags persist for the length of the minimum cache lifetime. 
     683    if ($this->global) { 
     684      $cookie_key = 'flag_global_' . str_replace('_', '-', $this->name) . '_' . $content_id; 
     685      $cookie_lifetime = (variable_get('cache', 0) > CACHE_DISABLED) ? variable_get('cache_lifetime', 0) : -1; 
     686      // Do not let the cookie lifetime be 0 (which is the no cache limit on 
     687      // anonymous page caching), since it would expire immediately. Usually 
     688      // the no cache limit means caches are cleared on cron, which usually runs 
     689      // at least once an hour. 
     690      if ($cookie_lifetime == 0) { 
     691        $cookie_lifetime = 3600; 
     692      } 
     693      setcookie($cookie_key, 1, time() + $cookie_lifetime, base_path()); 
     694      $_COOKIE[$cookie_key] = 1; 
     695    } 
     696    // The anonymous per-user flags are stored in a single cookie, so that all 
     697    // of them persist as long as the Drupal cookie lifetime. 
     698    else { 
     699      $cookie_flags = isset($_COOKIE['flags']) ? $_COOKIE['flags'] : ''; 
     700      $cookie_flags = explode(' ', $cookie_flags); 
     701      $cookie_key = str_replace('_', '-', $this->name) . '_' . $content_id; 
     702      $cookie_lifetime = min((int) ini_get('session.cookie_lifetime'), (int) ini_get('session.gc_maxlifetime')); 
     703      if (array_search($cookie_key, $cookie_flags) === FALSE) { 
     704        $cookie_flags[] = $cookie_key; 
     705        $cookie_flags = implode(' ', array_filter($cookie_flags)); 
     706        setcookie('flags', $cookie_flags, time() + $cookie_lifetime, base_path()); 
     707        $_COOKIE['flags'] = $cookie_flags; 
     708      } 
     709    } 
     710  } 
     711 
     712  /** 
     713   * Remove the cookie for anonymous users to record their unflagging. 
     714   * 
     715   * @private 
     716   */ 
     717  function _unflag_anonymous($content_id) { 
     718    // Global flags are easy, just delete the global cookie. 
     719    if ($this->global) { 
     720      $cookie_key = 'flag_global_' . str_replace('_', '-', $this->name) . '_' . $content_id; 
     721      $cookie_lifetime = (variable_get('cache', 0) > CACHE_DISABLED) ? variable_get('cache_lifetime', 0) : -1; 
     722      // Do not let the cookie lifetime be 0 (which is the no cache limit on 
     723      // anonymous page caching), since it would expire immediately. Usually 
     724      // the no cache limit means caches are cleared on cron, which usually runs 
     725      // at least once an hour. 
     726      if ($cookie_lifetime == 0) { 
     727        $cookie_lifetime = 3600; 
     728      } 
     729      setcookie($cookie_key, 0, time() + $cookie_lifetime, base_path()); 
     730      $_COOKIE[$cookie_key] = 0; 
     731    } 
     732    // User flags, update the single cookie for all user-data. 
     733    else { 
     734      $cookie_flags = isset($_COOKIE['flags']) ? $_COOKIE['flags'] : ''; 
     735      $cookie_flags = explode(' ', $cookie_flags); 
     736      $cookie_key = str_replace('_', '-', $this->name) . '_' . $content_id; 
     737      $cookie_lifetime = min((int) ini_get('session.cookie_lifetime'), (int) ini_get('session.gc_maxlifetime')); 
     738      $cookie_index = array_search($cookie_key, $cookie_flags); 
     739      if ($cookie_index !== FALSE) { 
     740        unset($cookie_flags[$cookie_index]); 
     741        $cookie_flags = implode(' ', $cookie_flags); 
     742        setcookie('flags', $cookie_flags, $cookie_lifetime, base_path()); 
     743        $_COOKIE['flags'] = $cookie_flags; 
     744      } 
     745    } 
     746  } 
     747 
     748  /** 
    492749   * Returns the number of times an item is flagged. 
    493750   * 
     
    503760   * Returns the number of items a user has flagged. 
    504761   * 
    505    * For global flags, pass '0' as the user ID. 
    506    */ 
    507   function get_user_count($uid) { 
    508     return db_result(db_query('SELECT COUNT(*) FROM {flag_content} WHERE fid = %d AND uid = %d', $this->fid, $uid)); 
     762   * For global flags, pass '0' as the user ID and session ID. 
     763   */ 
     764  function get_user_count($uid, $sid) { 
     765    return db_result(db_query('SELECT COUNT(*) FROM {flag_content} WHERE fid = %d AND uid = %d AND sid = %d', $this->fid, $uid, $sid)); 
    509766  } 
    510767 
     
    537794 
    538795  /** 
     796   * Get the link type for this flag. 
     797   */ 
     798  function get_link_type() { 
     799    $link_types = flag_get_link_types(); 
     800    return (isset($this->link_type) && isset($link_types[$this->link_type])) ? $link_types[$this->link_type] : $link_types['normal']; 
     801  } 
     802 
     803  /** 
    539804   * Replaces tokens in a label. Only the 'global' token context is regognized 
    540805   * by default, so derived classes should override this method to add all 
     
    641906    return array(); 
    642907  } 
    643    
     908 
    644909  /** 
    645910   * Defines the Rules argument for flag actions or conditions 
     
    648913    return array(); 
    649914  } 
    650    
     915 
    651916  /** 
    652917   * @} End of "addtogroup rules". 
     
    671936 
    672937  /** 
    673    * Similar to applies_to_content_id() but works on a bunch of IDs. It is 
    674    * called in the pre_render() stage of the 'Flag links' field to find out where 
    675    * that link applies. The reason we do a separate DB query, and not lump this 
    676    * test in the Views query, is to make 'many to one' tests possible without 
    677    * interfering with the rows, and also to reduce the complexity of the code. 
    678    */ 
    679   function applies_to_content_id_array($content_ids) { 
    680     return array(); 
    681   } 
    682  
    683   /** 
    684938   * @} End of "addtogroup views". 
    685939   */ 
     
    691945    if (isset($this->fid)) { 
    692946      $this->update(); 
     947      $this->is_new = FALSE; 
    693948    } 
    694949    else { 
    695950      $this->insert(); 
    696     } 
     951      $this->is_new = TRUE; 
     952    } 
     953    // Clear the page cache for anonymous users. 
     954    cache_clear_all('*', 'cache_page', TRUE); 
    697955  } 
    698956 
     
    701959   */ 
    702960  function update() { 
    703     db_query("UPDATE {flags} SET name = '%s', title = '%s', roles = '%s', global = %d, options = '%s' WHERE fid = %d", $this->name, $this->title, implode(',', $this->roles), $this->global, $this->get_serialized_options(), $this->fid); 
     961    db_query("UPDATE {flags} SET name = '%s', title = '%s', global = %d, options = '%s' WHERE fid = %d", $this->name, $this->title, $this->global, $this->get_serialized_options(), $this->fid); 
    704962    db_query("DELETE FROM {flag_types} WHERE fid = %d", $this->fid); 
    705963    foreach ($this->types as $type) { 
     
    712970   */ 
    713971  function insert() { 
    714     if (function_exists('db_last_insert_id')) { 
    715       // Drupal 6. We have a 'serial' primary key. 
    716       db_query("INSERT INTO {flags} (content_type, name, title, roles, global, options) VALUES ('%s', '%s', '%s', '%s', %d, '%s')", $this->content_type, $this->name, $this->title, implode(',', $this->roles), $this->global, $this->get_serialized_options()); 
    717       $this->fid = db_last_insert_id('flags', 'fid'); 
    718     } 
    719     else { 
    720       // Drupal 5. We have an 'integer' primary key. 
    721       $this->fid = db_next_id('{flags}_fid'); 
    722       db_query("INSERT INTO {flags} (fid, content_type, name, title, roles, global, options) VALUES (%d, '%s', '%s', '%s', '%s', %d, '%s')", $this->fid, $this->content_type, $this->name, $this->title, implode(',', $this->roles), $this->global, $this->get_serialized_options()); 
    723     } 
     972    db_query("INSERT INTO {flags} (content_type, name, title, global, options) VALUES ('%s', '%s', '%s', %d, '%s')", $this->content_type, $this->name, $this->title, $this->global, $this->get_serialized_options()); 
     973    $this->fid = db_last_insert_id('flags', 'fid'); 
    724974    foreach ($this->types as $type) { 
    725975      db_query("INSERT INTO {flag_types} (fid, type) VALUES (%d, '%s')", $this->fid, $type); 
     
    731981   */ 
    732982  function get_serialized_options() { 
    733     $option_names = array_keys($this->default_options()); 
     983    $option_names = array_keys($this->options()); 
    734984    $options = array(); 
    735985    foreach ($option_names as $option) { 
     
    7781028   */ 
    7791029  function theme($action, $content_id, $after_flagging = FALSE) { 
    780     if (!_flag_is_drupal_5()) { 
    781       // We're running Drupal 6. 
    782       return theme($this->theme_suggestions(), $this, $action, $content_id, $after_flagging); 
    783     } 
    784     else { 
    785       // We're running Drupal 5. Noting to do: The theme_suggestions[] are 
    786       // handed to phptemplate in phptemplate_flag(), if the user bothered to 
    787       // copy that function into her 'template.php'. 
    788       return theme('flag', $this, $action, $content_id, $after_flagging); 
    789     } 
     1030    static $js_added = array(); 
     1031 
     1032    // If the flagging user is anonymous and the page cache is enabled, always 
     1033    // show the "flag" link, and then set the unflag link through JavaScript. 
     1034    if ($this->uses_anonymous_cookies() && !$after_flagging) { 
     1035      $js_action = $this->global ? ($action == 'flag' ? 'unflag' : 'flag') : 'flag'; 
     1036      if (!isset($js_added[$this->name . '_' . $content_id])) { 
     1037        $js_added[$this->name . '_' . $content_id] = TRUE; 
     1038        $js_template = theme($this->theme_suggestions(), $this, 'unflag', $content_id, $after_flagging); 
     1039        drupal_add_js(array('flag' => array('templates' => array($this->name . '_' . $content_id => $js_template))), 'setting'); 
     1040      } 
     1041    } 
     1042 
     1043    return theme($this->theme_suggestions(), $this, $action, $content_id, $after_flagging); 
    7901044  } 
    7911045 
     
    8051059 */ 
    8061060class flag_node extends flag_flag { 
    807   function default_options() { 
    808     $options = parent::default_options(); 
     1061  function options() { 
     1062    $options = parent::options(); 
    8091063    $options += array( 
    8101064      'show_on_page' => TRUE, 
    8111065      'show_on_teaser' => TRUE, 
    8121066      'show_on_form' => FALSE, 
     1067      'access_author' => '', 
    8131068      'i18n' => 0, 
    8141069    ); 
     
    8181073  function options_form(&$form) { 
    8191074    parent::options_form($form); 
    820     // Note there isn't a translation helpers module in Drupal 5, 
    821     // this feature is essentially Drupal 6 only. 
     1075    // Support for i18n flagging requires Translation helpers module. 
    8221076    $form['i18n'] = array( 
    8231077      '#type' => 'radios', 
     
    8321086      '#weight' => 5, 
    8331087    ); 
     1088 
     1089    $form['access']['access_author'] = array( 
     1090      '#type' => 'radios', 
     1091      '#title' => t('Flag access by content authorship'), 
     1092      '#options' => array( 
     1093        '' => t('No additional restrictions'), 
     1094        'own' => t('Users may only flag content they own'), 
     1095        'others' => t('Users may only flag content of others'), 
     1096      ), 
     1097      '#default_value' => $this->access_author, 
     1098      '#description' => t("Restrict access to this flag based on the user's ownership of the content. Users must also have access to the flag through the role settings."), 
     1099    ); 
     1100 
    8341101    $form['display']['show_on_teaser'] = array( 
    8351102      '#type' => 'checkbox', 
     
    8641131  } 
    8651132 
     1133  function access_multiple($content_ids, $account = NULL) { 
     1134    $access = parent::access_multiple($content_ids, $account); 
     1135 
     1136    // Ensure that only flaggable node types are granted access. This avoids a 
     1137    // node_load() on every type, usually done by applies_to_content_id(). 
     1138    $nids = implode(',', array_map('intval', array_keys($content_ids))); 
     1139    $placeholders = implode(',', array_fill(0, sizeof($this->types), "'%s'")); 
     1140    $result = db_query("SELECT nid as content_id FROM {node} WHERE nid IN ($nids) AND type NOT IN ($placeholders)", $this->types); 
     1141    while ($row = db_fetch_object($result)) { 
     1142      $access[$row->content_id] = FALSE; 
     1143    } 
     1144 
     1145    return $access; 
     1146  } 
     1147 
    8661148  function get_content_id($node) { 
    8671149    return $node->nid; 
     
    8981180  } 
    8991181 
    900  
    901   function is_flagged($content_id, $uid = NULL) { 
     1182  function is_flagged($content_id, $uid = NULL, $sid = NULL) { 
    9021183    $content_id = $this->get_translation_id($content_id); 
    903     return parent::is_flagged($content_id, $uid); 
     1184    return parent::is_flagged($content_id, $uid, $sid); 
    9041185  } 
    9051186 
     
    9091190 
    9101191  function replace_tokens($label, $contexts, $content_id) { 
    911     if ($content_id && ($node = $this->fetch_content($content_id))) { 
     1192    if (is_numeric($content_id) && ($node = $this->fetch_content($content_id))) { 
    9121193      $contexts['node'] = $node; 
     1194    } 
     1195    // Nodes accept the node-type as a $content_id in the case that a new node 
     1196    // is being created and a full node object does not yet exist. 
     1197    elseif (!empty($content_id) && ($type = node_get_types('type', $content_id))) { 
     1198      $content_id = NULL; 
     1199      $contexts['node'] = (object) array( 
     1200        'nid' => NULL, 
     1201        'type' => $type->type, 
     1202        'title' => '', 
     1203      ); 
    9131204    } 
    9141205    return parent::replace_tokens($label, $contexts, $content_id); 
     
    9481239    ); 
    9491240  } 
    950    
     1241 
    9511242  function rules_get_element_argument_definition() { 
    9521243    return array('type' => 'node', 'label' => t('Flagged content')); 
     
    9641255    ); 
    9651256  } 
    966  
    967   function applies_to_content_id_array($content_ids) { 
    968     $passed = array(); 
    969     $content_ids  = implode(',', array_map('intval', $content_ids)); 
    970     $placeholders = implode(',', array_fill(0, sizeof($this->types), "'%s'")); 
    971     $result = db_query("SELECT nid FROM {node} WHERE nid IN ($content_ids) AND type in ($placeholders)", $this->types); 
    972     while ($row = db_fetch_object($result)) { 
    973       $passed[$row->nid] = TRUE; 
    974     } 
    975     return $passed; 
    976   } 
    977  
    9781257} 
    9791258 
     
    9821261 */ 
    9831262class flag_comment extends flag_flag  { 
    984   function default_options() { 
    985     $options = parent::default_options(); 
     1263  function options() { 
     1264    $options = parent::options(); 
    9861265    $options += array( 
     1266      'access_author' => '', 
    9871267      'show_on_comment' => TRUE, 
    9881268    ); 
     
    9921272  function options_form(&$form) { 
    9931273    parent::options_form($form); 
     1274 
     1275    $form['access']['access_author'] = array( 
     1276      '#type' => 'radios', 
     1277      '#title' => t('Flag access by content authorship'), 
     1278      '#options' => array( 
     1279        '' => t('No additional restrictions'), 
     1280        'comment_own' => t('Users may only flag own comments'), 
     1281        'comment_others' => t('Users may only flag comments by others'), 
     1282        'node_own' => t('Users may only flag comments of nodes they own'), 
     1283        'node_others' => t('Users may only flag comments of nodes by others'), 
     1284      ), 
     1285      '#default_value' => $this->access_author, 
     1286      '#description' => t("Restrict access to this flag based on the user's ownership of the content. Users must also have access to the flag through the role settings."), 
     1287    ); 
     1288 
    9941289    $form['display']['show_on_comment'] = array( 
    9951290      '#type' => 'checkbox', 
     
    10091304    } 
    10101305    return FALSE; 
     1306  } 
     1307 
     1308  function access_multiple($content_ids, $account = NULL) { 
     1309    $account = isset($account) ? $account : $GLOBALS['user']; 
     1310    $access = parent::access_multiple($content_ids, $account); 
     1311 
     1312    // Ensure node types are granted access. This avoids a 
     1313    // node_load() on every type, usually done by applies_to_content_id(). 
     1314    $cids = implode(',', array_map('intval', array_keys($content_ids))); 
     1315    $placeholders = implode(',', array_fill(0, sizeof($this->types), "'%s'")); 
     1316    $result = db_query("SELECT cid as content_id FROM {comments} c INNER JOIN {node} n ON c.nid = n.nid WHERE cid IN ($cids) and n.type NOT IN ($placeholders)", $this->types); 
     1317    while ($row = db_fetch_object($result)) { 
     1318      $access[$row->content_id] = FALSE; 
     1319    } 
     1320 
     1321    return $access; 
    10111322  } 
    10121323 
     
    10631374    ); 
    10641375  } 
    1065    
     1376 
    10661377  function rules_get_element_argument_definition() { 
    10671378    return array('type' => 'comment', 'label' => t('Flagged comment')); 
     
    10791390    ); 
    10801391  } 
    1081  
    1082   function applies_to_content_id_array($content_ids) { 
    1083     $passed = array(); 
    1084     $content_ids  = implode(',', array_map('intval', $content_ids)); 
    1085     $placeholders = implode(',', array_fill(0, sizeof($this->types), "'%s'")); 
    1086     $result = db_query("SELECT cid FROM {comments} c INNER JOIN {node} n ON c.nid = n.nid WHERE cid IN ($content_ids) and n.type IN ($placeholders)", $this->types); 
    1087     while ($row = db_fetch_object($result)) { 
    1088       $passed[$row->cid] = TRUE; 
    1089     } 
    1090     return $passed; 
    1091   } 
    10921392} 
    10931393 
     
    10961396 */ 
    10971397class flag_user extends flag_flag { 
    1098   function default_options() { 
    1099     $options = parent::default_options(); 
     1398  function options() { 
     1399    $options = parent::options(); 
    11001400    $options += array( 
    11011401      'show_on_profile' => TRUE, 
     1402      'access_uid' => '', 
    11021403    ); 
    11031404    return $options; 
     
    11061407  function options_form(&$form) { 
    11071408    parent::options_form($form); 
    1108     $form['types'] = array( 
    1109       // A user flag doesn't support node types. (Maybe will support roles instead, in the future.) 
     1409    $form['access']['types'] = array( 
     1410      // A user flag doesn't support node types. 
     1411      // TODO: Maybe support roles instead of node types. 
    11101412      '#type' => 'value', 
    11111413      '#value' => array(0 => 0), 
     1414    ); 
     1415    $form['access']['access_uid'] = array( 
     1416      '#type' => 'checkbox', 
     1417      '#title' => t('Users may flag themselves'), 
     1418      '#description' => t('Disabling this option may be useful when setting up a "friend" flag, when a user flagging themself does not make sense.'), 
     1419      '#default_value' => $this->access_uid ? 0 : 1, 
    11121420    ); 
    11131421    $form['display']['show_on_profile'] = array( 
     
    11191427  } 
    11201428 
     1429  function form_input($form_values) { 
     1430    parent::form_input($form_values); 
     1431    // The access_uid value is intentionally backwards from the UI, to avoid 
     1432    // confusion caused by checking a box to disable a feature. 
     1433    $this->access_uid = empty($form_values['access_uid']) ? 'others' : ''; 
     1434  } 
     1435 
    11211436  function _load_content($content_id) { 
    11221437    return user_load(array('uid' => $content_id)); 
     
    11301445    } 
    11311446    return FALSE; 
     1447  } 
     1448 
     1449  function access($content_id, $action = NULL, $account = NULL) { 
     1450    $access = parent::access($content_id, $action, $account); 
     1451    $account = isset($account) ? $account : $GLOBALS['user']; 
     1452 
     1453    // Prevent users from flagging themselves. 
     1454    if ($this->access_uid == 'others' && $content_id == $account->uid) { 
     1455      $access = FALSE; 
     1456    } 
     1457 
     1458    return $access; 
     1459  } 
     1460 
     1461  function access_multiple($content_ids, $account = NULL) { 
     1462    $account = isset($account) ? $account : $GLOBALS['user']; 
     1463    $access = parent::access_multiple($content_ids, $account); 
     1464 
     1465    // Exclude anonymous. 
     1466    if (array_key_exists(0, $access)) { 
     1467      $access[0] = FALSE; 
     1468    } 
     1469 
     1470    // Prevent users from flagging themselves. 
     1471    if ($this->access_uid == 'others' && array_key_exists($account->uid, $access)) { 
     1472      $access[$account->uid] = FALSE; 
     1473    } 
     1474 
     1475    return $access; 
    11321476  } 
    11331477 
     
    11771521    ); 
    11781522  } 
    1179    
     1523 
    11801524  function rules_get_element_argument_definition() { 
    11811525    return array('type' => 'user', 'label' => t('Flagged user')); 
     
    11931537    ); 
    11941538  } 
    1195  
    1196   function applies_to_content_id_array($content_ids) { 
    1197     // This user flag doesn't currently support subtypes so all users are 
    1198     // applicable for flagging. 
    1199     $passed = array(); 
    1200     foreach ($content_ids as $uid) { 
    1201       if ($uid) { // Exclude anonymous. 
    1202         $passed[$uid] = TRUE; 
    1203       } 
    1204     } 
    1205     return $passed; 
    1206   } 
    1207  
    12081539} 
    12091540 
     
    12201551} 
    12211552 
    1222 // Returns TRUE if we're running under Drupal 5. 
    1223 // 
    1224 // I use this function because I don't want to maintain two versions of a file 
    1225 // just because a handful of lines of code. 
    1226 function _flag_is_drupal_5() { 
    1227   return !function_exists('theme_get_registry'); 
     1553/** 
     1554 * A shortcut function to output the link URL. 
     1555 */ 
     1556function _flag_url($path, $fragment = NULL, $absolute = TRUE) { 
     1557  return url($path, array('fragment' => $fragment, 'absolute' => $absolute)); 
    12281558} 
    1229  
    1230 // That's ugly, but duplicating this logic is uglier. 
    1231 function _flag_url($path, $fragment = NULL, $absolute = TRUE) { 
    1232   return _flag_is_drupal_5() 
    1233     ? url($path, NULL, $fragment, $absolute) 
    1234     : url($path, array('absolute' => TRUE, 'fragment' => $fragment)); 
    1235 } 
    1236  
  • drupal-6/sites/all/modules/flag/flag.info

    r865 r867  
    44core = 6.x 
    55package = Flags 
    6  
    7 ; Information added by drupal.org packaging script on 2010-01-08 
    8 version = "6.x-1.2" 
    9 core = "6.x" 
    10 project = "flag" 
    11 datestamp = "1262929205" 
    12  
  • drupal-6/sites/all/modules/flag/flag.install

    r865 r867  
    11<?php 
    2 // $Id: flag.install,v 1.2.2.34 2010/01/08 05:00:45 quicksketch Exp $ 
     2// $Id: flag.install,v 1.2.2.32.2.8 2010/01/08 04:42:32 quicksketch Exp $ 
    33 
    44/** 
     
    2323  } 
    2424 
    25   $success = drupal_install_schema('flag'); 
    26  
    27   if ($success) { 
    28     // Install a demonstration flag. 
     25  drupal_install_schema('flag'); 
     26} 
     27 
     28/** 
     29 * Implementation of hook_uninstall(). 
     30 */ 
     31function flag_uninstall() { 
     32  drupal_uninstall_schema('flag'); 
     33  $result = db_query("SELECT name FROM {variable} WHERE name LIKE 'flag_%'"); 
     34  while ($row = db_fetch_object($result)) { 
     35    variable_del($row->name); 
     36  } 
     37 
     38  drupal_set_message(t('Flag has been uninstalled.')); 
     39} 
     40 
     41/** 
     42 * Implementation of hook_enable(). 
     43 * 
     44 * We create the demonstration flag on enable, so hook implementations in flag 
     45 * module will fire correctly, as the APIs are not available on install. 
     46 */ 
     47function flag_enable() { 
     48  // Load the flag API in case we want to use it when enabling. 
     49  include_once(drupal_get_path('module', 'flag') .'/flag.module'); 
     50 
     51  if (!flag_get_flags()) { 
     52    // Install a demonstration flag only if no flag exists. This is to prevent 
     53    // a case where a disables and enables the module, and the demonstration 
     54    // flag is overwritten or re-created. 
    2955    $flag = flag_flag::factory_by_content_type('node'); 
    3056    $configuration = array( 
     
    4975    $flag->save(); 
    5076  } 
    51  
    52   if ($success) { 
    53     drupal_set_message(st('Flag module installed tables successfully.')); 
    54   } 
    55   else { 
    56     drupal_set_message(st('The installation of Flag module failed.'), 'error'); 
    57   } 
    58 } 
    59  
    60 /** 
    61  * Implementation of hook_uninstall(). 
    62  */ 
    63 function flag_uninstall() { 
    64   drupal_uninstall_schema('flag'); 
    65   $result = db_query("SELECT name FROM {variable} WHERE name LIKE 'flag_%'"); 
    66   while ($row = db_fetch_object($result)) { 
    67     variable_del($row->name); 
    68   } 
    69  
    70   drupal_set_message(t('Flag has been uninstalled.')); 
    7177} 
    7278 
     
    8187  if ($phase == 'install') { 
    8288    if (!defined('MAINTENANCE_MODE') && _flag_flag_content_installed()) { 
    83       $requirements['flag_content_clash']['title'] = $t('Flag'); 
    84       $requirements['flag_content_clash']['severity'] = REQUIREMENT_ERROR; 
    85       $requirements['flag_content_clash']['description'] = _flag_flag_content_message(); 
    86     } 
    87   } 
    88  
    89   if ($phase == 'runtime' && module_exists('translation') && !module_exists('translation_helpers')) { 
    90     $requirements['flag_translation']['title'] = $t('Flag'); 
    91     $requirements['flag_translation']['severity'] = REQUIREMENT_ERROR; 
    92     $requirements['flag_translation']['description'] = $t('To have the flag module work with translations, you need to install and enable the <a href="http://drupal.org/project/translation_helpers">Translation helpers</a> module.'); 
    93     $requirements['flag_translation']['value'] = $t('Translation helpers module not found.'); 
     89      $requirements['flag_content_clash'] = array( 
     90        'title' => $t('Flag'), 
     91        'severity' => REQUIREMENT_ERROR, 
     92        'description' => _flag_flag_content_message(), 
     93      ); 
     94    } 
     95  } 
     96 
     97  if ($phase == 'runtime') { 
     98    if (module_exists('translation') && !module_exists('translation_helpers')) { 
     99      $requirements['flag_translation'] = array( 
     100        'title' => $t('Flag'), 
     101        'severity' => REQUIREMENT_ERROR, 
     102        'description' => $t('To have the flag module work with translations, you need to install and enable the <a href="http://drupal.org/project/translation_helpers">Translation helpers</a> module.'), 
     103        'value' => $t('Translation helpers module not found.'), 
     104      ); 
     105    } 
     106    if (module_exists('session_api')) { 
     107      if (file_exists('./robots.txt')) { 
     108        $flag_path = url('flag') . '/'; 
     109        $robots_string = 'Disallow: ' . $flag_path; 
     110        $contents = file_get_contents('./robots.txt'); 
     111        if (strpos($contents, $robots_string) === FALSE) { 
     112          $requirements['flag_robots'] = array( 
     113            'title' => $t('Flag robots.txt problem'), 
     114            'severity' => REQUIREMENT_WARNING, 
     115            'description' => $t('Flag module may currently be used with anonymous users, however the robots.txt file does not exlude the "@flag-path" path, which may cause search engines to randomly flag and unflag content when they index the site. It is highly recommended to add "@robots-string" to your robots.txt file (located in the root of your Drupal installation).', array('@flag-path' => $flag_path, '@robots-string' => $robots_string)), 
     116            'value' => $t('Search engines flagging content'), 
     117          ); 
     118        } 
     119      } 
     120    } 
    94121  } 
    95122  return $requirements; 
     
    197224        'default' => 0, 
    198225      ), 
     226      'sid' => array( 
     227        'type' => 'int', 
     228        'unsigned' => TRUE, 
     229        'not null' => TRUE, 
     230        'default' => 0, 
     231      ), 
    199232      'timestamp' => array( 
    200233        'type' => 'int', 
     
    207240    'primary key' => array('fcid'), 
    208241    'unique keys' => array( 
    209       'fid_content_type_content_id_uid' => array('fid', 'content_type', 'content_id', 'uid'), 
     242      'fid_content_id_uid_sid' => array('fid', 'content_id', 'uid', 'sid'), 
    210243    ), 
    211244    'indexes' => array( 
    212245      'content_type_content_id' => array('content_type', 'content_id'), 
    213       'content_type_uid' => array('content_type', 'uid'), 
     246      'content_type_uid_sid' => array('content_type', 'uid', 'sid'), 
    214247    ), 
    215248  ); 
     
    269302      'fid_content_type' => array('fid', 'content_type'), 
    270303      'content_type_content_id' => array('content_type', 'content_id'), 
     304      'count' => array('count'), 
    271305    ), 
    272306  ); 
     
    375409  $ret = array(); 
    376410 
    377   if (_flag_column_exists('flags', 'flag_short')) { 
     411  if (db_column_exists('flags', 'flag_short')) { 
    378412    $result = db_query("SELECT * FROM {flags}"); 
    379413    while ($flag = db_fetch_object($result)) { 
     
    404438  $ret = array(); 
    405439 
    406   if (!_flag_column_exists('flag_content', 'fcid')) { 
     440  if (db_column_exists('flag_content', 'fcid')) { 
    407441    db_drop_primary_key($ret, 'flag_content'); 
    408442    db_add_field($ret, 'flag_content', 'fcid', array( 
     
    447481 
    448482/** 
    449  * Add auto-increment to the flags.fid column for users upgrading from Drupal 5. 
     483 * Remove count = 0 rows from the count tables. 
    450484 */ 
    451485function flag_update_6004() { 
    452   // Update removed and included in flag_update_6000() 
     486  // Same update as flag_update_6203() 
    453487  return array(); 
    454488} 
    455489 
    456490/** 
    457  * Remove count = 0 rows from the flag_counts table for consistency. 
    458  */ 
    459 function flag_update_6005() { 
     491 * Convert role access to have separate "flag" and "unflag" permissions. 
     492 */ 
     493function flag_update_6200() { 
     494  $ret = array(); 
     495 
     496  if (db_column_exists('flags', 'roles')) { 
     497    $result = db_query('SELECT * FROM {flags}'); 
     498    while ($flag = db_fetch_object($result)) { 
     499      $roles = array_filter(explode(',', $flag->roles)); 
     500      $options = unserialize($flag->options); 
     501      $options['roles'] = array( 
     502        'flag' => $roles, 
     503        'unflag' => $roles, 
     504      ); 
     505      db_query("UPDATE {flags} SET options = '%s' WHERE fid = %d", serialize($options), $flag->fid); 
     506    } 
     507    db_drop_field($ret, 'flags', 'roles'); 
     508  } 
     509 
     510  return $ret; 
     511} 
     512 
     513/** 
     514 * Refine the indexes. 
     515 * 
     516 * The content type inclusion actually slowed down on unique key. And a count 
     517 * index would be helpful for sorting by counts. 
     518 */ 
     519function flag_update_6201() { 
     520  $ret = array(); 
     521 
     522  // Remove "content type" from one key, see http://drupal.org/node/612602. 
     523  db_drop_unique_key($ret, 'flag_content', 'fid_content_type_content_id_uid'); 
     524  db_add_unique_key($ret, 'flag_content', 'fid_content_id_uid', array('fid', 'content_id', 'uid')); 
     525 
     526  // Add a count index, see http://drupal.org/node/489610. 
     527  db_add_index($ret, 'flag_counts', 'count', array('count')); 
     528 
     529  return $ret; 
     530} 
     531 
     532/** 
     533 * Add the sid column and unique index on the flag_content table. 
     534 */ 
     535function flag_update_6202() { 
     536  $ret = array(); 
     537 
     538  // Drop the keys affected by the addition of the SID column. 
     539  db_drop_unique_key($ret, 'flag_content', 'fid_content_id_uid'); 
     540  db_drop_index($ret, 'flag_content', 'content_type_uid'); 
     541 
     542  // Add the column. 
     543  db_add_field($ret, 'flag_content', 'sid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0)); 
     544 
     545  // Re-add the removed keys. 
     546  db_add_unique_key($ret, 'flag_content', 'fid_content_id_uid_sid', array('fid', 'content_id', 'uid', 'sid')); 
     547  db_add_index($ret, 'flag_content', 'content_type_uid_sid', array('content_type', 'uid', 'sid')); 
     548 
     549  return $ret; 
     550} 
     551 
     552/** 
     553 * Remove count = 0 rows from the count tables. 
     554 */ 
     555function flag_update_6203() { 
    460556  $ret = array(); 
    461557  $ret[] = update_sql("DELETE FROM {flag_counts} WHERE count = 0"); 
     
    474570  return array('success' => $result !== FALSE, 'query' => check_plain($sql)); 
    475571} 
    476  
    477 // D5/6 compatible version of db_column_exists(). 
    478 function _flag_column_exists($table, $column) { 
    479   if (function_exists('db_column_exists')) { 
    480     return db_column_exists($table, $column); 
    481   } 
    482  
    483   switch ($GLOBALS['db_type']) { 
    484     case 'mysql': 
    485     case 'mysqli': 
    486       return (bool) db_fetch_object(db_query("SHOW COLUMNS FROM {flags} LIKE 'flag_short'")); 
    487     case 'pgsql': 
    488       return (bool) db_result(db_query("SELECT COUNT(pg_attribute.attname) FROM pg_class, pg_attribute WHERE pg_attribute.attrelid = pg_class.oid AND pg_class.relname = '{". db_escape_table($table) ."}' AND attname = '". db_escape_table($column) ."'")); 
    489   } 
    490 } 
  • drupal-6/sites/all/modules/flag/flag.module

    r865 r867  
    11<?php 
    2 // $Id: flag.module,v 1.11.2.78 2010/01/08 04:47:21 quicksketch Exp $ 
     2// $Id: flag.module,v 1.11.2.72.2.31 2010/01/08 05:17:57 quicksketch Exp $ 
    33 
    44/** 
     
    66 * The Flag module. 
    77 */ 
     8 
     9define('FLAG_API_VERSION', 2); 
    810 
    911include_once dirname(__FILE__) .'/flag.inc'; 
     
    5860    'file' => 'includes/flag.admin.inc', 
    5961    'type' => MENU_LOCAL_TASK, 
     62    'weight' => 1, 
     63  ); 
     64  $items['admin/build/flags/import'] = array( 
     65    'title' => 'Import', 
     66    'page callback' => 'drupal_get_form', 
     67    'page arguments' => array('flag_import_form'), 
     68    'access arguments' => array('administer flags'), 
     69    'type' => MENU_LOCAL_TASK, 
     70    'file' => 'includes/flag.export.inc', 
     71    'weight' => 2, 
     72  ); 
     73  $items['admin/build/flags/export'] = array( 
     74    'title' => 'Export', 
     75    'page callback' => 'drupal_get_form', 
     76    'page arguments' => array('flag_export_form'), 
     77    'access arguments' => array('administer flags'), 
     78    'type' => MENU_LOCAL_TASK, 
     79    'file' => 'includes/flag.export.inc', 
     80    'weight' => 3, 
     81  ); 
     82  $items['admin/build/flags/update/%'] = array( 
     83    'title' => 'Update', 
     84    'page callback' => 'flag_update_page', 
     85    'page arguments' => array(4), 
     86    'access arguments' => array('administer flags'), 
     87    'type' => MENU_CALLBACK, 
     88    'file' => 'includes/flag.export.inc', 
    6089  ); 
    6190  $items['flag'] = array( 
     
    89118    include_once $path .'/includes/flag.token.inc'; 
    90119  } 
     120  if (module_exists('session_api')) { 
     121    // Set the anonymous user SID immediately, in case the user logs in. 
     122    flag_set_sid(); 
     123  } 
    91124} 
    92125 
     
    102135 
    103136/** 
     137 * Implementation of hook_features_api(). 
     138 */ 
     139function flag_features_api() { 
     140  return array( 
     141    'flag' => array( 
     142      'feature_source' => TRUE, 
     143      'default_hook' => 'flag_default_flags', 
     144      'file' => drupal_get_path('module', 'flag') . '/includes/flag.features.inc', 
     145    ), 
     146  ); 
     147} 
     148 
     149/** 
    104150 * Implementation of hook_perm(). 
    105151 */ 
     
    114160 */ 
    115161function flag_access($flag, $account = NULL) { 
    116   return $flag->user_access($account); 
     162  return $flag->user_access('flag', $account); 
    117163} 
    118164 
     
    145191  global $user; 
    146192 
    147   // Anonymous users can't create flags with this system. 
    148   if (!$user->uid) { 
    149     return; 
    150   } 
    151  
    152193  // Get all possible flags for this content-type. 
    153194  $flags = flag_get_flags($type); 
    154195 
    155196  foreach ($flags as $flag) { 
    156     if (!$flag->user_access($user)) { 
    157       // User has no permission to use this flag. 
    158       continue; 
    159     } 
     197    $content_id = $flag->get_content_id($object); 
     198 
    160199    if (!$flag->uses_hook_link($teaser)) { 
    161200      // Flag is not configured to show its link here. 
    162201      continue; 
    163202    } 
    164     if (!$flag->applies_to_content_object($object)) { 
    165       // Flag does not apply to this content. 
     203    if (!$flag->access($content_id) && (!$flag->is_flagged($content_id) || !$flag->access($content_id, 'flag'))) { 
     204      // User has no permission to use this flag or flag does not apply to this 
     205      // content. The link is not skipped if the user has "flag" access but 
     206      // not "unflag" access (this way the unflag denied message is shown). 
    166207      continue; 
    167208    } 
    168209 
    169     $content_id = $flag->get_content_id($object); 
    170210    // The flag links are actually fully rendered theme functions. 
    171211    // The HTML attribute is set to TRUE to allow whatever the themer desires. 
     
    201241  $token = flag_get_token($content_id); 
    202242  return array( 
    203     'href' => "flag/". ($flag->link_type == 'confirm' ? 'confirm/' : '') ."$action/$flag->name/$content_id", 
     243    'href' => 'flag/'. ($flag->link_type == 'confirm' ? 'confirm/' : '') ."$action/$flag->name/$content_id", 
    204244    'query' => drupal_get_destination() . ($flag->link_type == 'confirm' ? '' : '&token='. $token), 
    205245  ); 
     
    211251function flag_flag_link_types() { 
    212252  return array( 
    213     'toggle' => t('JavaScript toggle'), 
    214     'normal' => t('Normal link'), 
    215     'confirm' => t('Confirmation form'), 
     253    'toggle' => array( 
     254      'title' => t('JavaScript toggle'), 
     255      'description' => t('An AJAX request will be made and degrades to type "Normal link" if JavaScript is not available.'), 
     256    ), 
     257    'normal' => array( 
     258      'title' => t('Normal link'), 
     259      'description' => t('A normal non-JavaScript request will be made and the current page will be reloaded.'), 
     260    ), 
     261    'confirm' => array( 
     262      'title' => t('Confirmation form'), 
     263      'description' => t('The user will be taken to a confirmation form on a separate page to confirm the flag.'), 
     264      'options' => array( 
     265        'flag_confirmation' => '', 
     266        'unflag_confirmation' => '', 
     267      ), 
     268    ), 
    216269  ); 
    217270} 
     
    256309        $form['workflow']['flag'][$var] = array( 
    257310          '#type' => 'checkbox', 
    258           '#title' => $flag->get_label('flag_short'), 
     311          '#title' => $flag->get_label('flag_short', $form['#node_type']->type), 
    259312          '#default_value' => variable_get($var .'_'. $form['#node_type']->type, 0), 
    260313          '#return_value' => 1, 
     
    274327    } 
    275328  } 
    276   elseif (isset($form['type']) && isset($form['#node']) 
    277       && ($form_id == $form['type']['#value'] .'_node_form')) { 
    278     if (!$user->uid) { 
    279       return; 
    280     } 
    281  
     329  elseif (isset($form['type']) && isset($form['#node']) && ($form_id == $form['type']['#value'] .'_node_form')) { 
    282330    $nid = !empty($form['nid']['#value']) ? $form['nid']['#value'] : NULL; 
    283  
    284331    $flags = flag_get_flags('node', $form['type']['#value'], $user); 
    285332 
    286333    // Filter out flags which need to be included on the node form. 
     334    $flags_in_form = 0; 
     335    $flags_visible = 0; 
    287336    foreach ($flags as $name => $flag) { 
    288337      if (!$flag->show_on_form) { 
    289         unset($flags[$name]); 
    290       } 
    291     } 
    292  
    293     if (count($flags)) { 
    294       $form['flag'] = array( 
    295         '#type' => 'fieldset', 
     338        continue; 
     339      } 
     340 
     341      if (isset($form['#node']->flag[$flag->name])) { 
     342        $flag_status = $form['#node']->flag[$flag->name]; 
     343      } 
     344      else { 
     345        $flag_status_default = variable_get('flag_' . $flag->name . '_default_' . $form['type']['#value'], 0); 
     346        $flag_status = $nid ? $flag->is_flagged($nid) : $flag_status_default; 
     347      } 
     348 
     349      // If the flag is not global and the user doesn't have access, skip it. 
     350      // Global flags have their value set even if the user doesn't have access 
     351      // to it, similar to the way "published" and "promote" keep the default 
     352      // values even if the user doesn't have "administer nodes" permission. 
     353      $access = $flag->access($nid, $flag_status ? 'unflag' : 'flag', $user); 
     354      if (!$access && !$flag->global) { 
     355        continue; 
     356      } 
     357 
     358      // Add the flag checkbox if displaying on the form. 
     359      $form['flag'][$flag->name] = array( 
     360        '#type' => 'checkbox', 
     361        '#title' => $flag->get_label('flag_short', $nid ? $nid : $form['type']['#value']), 
     362        '#description' => $flag->get_label('flag_long', $nid ? $nid : $form['type']['#value']), 
     363        '#default_value' => $flag_status, 
     364        '#return_value' => 1, 
     365      ); 
     366      // If the user does not have access to the flag, set as a value. 
     367      if (!$access) { 
     368        $form['flag'][$flag->name]['#type'] = 'value'; 
     369        $form['flag'][$flag->name]['#value'] = $flag_status; 
     370      } 
     371      else { 
     372        $flags_visible++; 
     373      } 
     374      $flags_in_form++; 
     375    } 
     376    if ($flags_in_form) { 
     377      $form['flag'] += array( 
    296378        '#weight' => module_exists('content') ? content_extra_field_weight($form['#node']->type, 'flags') : 1, 
    297379        '#tree' => TRUE, 
     380      ); 
     381    } 
     382    if ($flags_visible) { 
     383      $form['flag'] += array( 
     384        '#type' => 'fieldset', 
    298385        '#title' => t('Flags'), 
    299386        '#collapsible' => TRUE, 
     
    301388    } 
    302389 
    303     foreach ($flags as $flag) { 
    304       if (isset($form['#node']->flag[$flag->name])) { 
    305         $flag_status = $form['#node']->flag[$flag->name]; 
    306       } 
    307       else { 
    308         $flag_status_default = variable_get('flag_' . $flag->name . '_default_' . $form['type']['#value'], 0); 
    309         $flag_status = $nid ? $flag->is_flagged($nid) : $flag_status_default; 
    310       } 
    311       $form['flag'][$flag->name] = array( 
    312         '#type' => 'checkbox', 
    313         '#title' => $flag->get_label('flag_short', $nid), 
    314         '#description' => $flag->get_label('flag_long', $nid), 
    315         '#default_value' => $flag_status, 
    316         '#return_value' => 1, 
    317       ); 
    318     } 
    319390  } 
    320391} 
     
    341412            $remembered = TRUE; 
    342413          } 
    343           flag($state ? 'flag' : 'unflag', $name, $node->nid); 
     414          flag($state ? 'flag' : 'unflag', $name, $node->nid, $user, TRUE); 
    344415        } 
    345416      } 
     
    377448function flag_user($op, &$edit, &$account, $category = NULL) { 
    378449  switch ($op) { 
     450    case 'login': 
     451      // Migrate anonymous flags to this user's account. 
     452      if (module_exists('session_api')) { 
     453        // The @ symbol suppresses errors if the user flags a piece of content 
     454        // they have already flagged as a logged-in user. 
     455        @db_query("UPDATE {flag_content} SET uid = %d, sid = 0 WHERE uid = 0 AND sid = %d", $account->uid, flag_get_sid(0)); 
     456        // Delete any remaining flags this user had as an anonymous user. 
     457        db_query("DELETE FROM {flag_content} WHERE uid = 0 AND sid = %d", flag_get_sid(0)); 
     458        // Clean up anonymous cookies. 
     459        if (isset($_COOKIE['flags'])) { 
     460          setcookie('flags', FALSE, 0, base_path()); 
     461          unset($_COOKIE['flags']); 
     462        } 
     463        foreach ($_COOKIE as $key => $value) { 
     464          if (strpos($key, 'flag_global_') === 0) { 
     465            setcookie($key, FALSE, 0, base_path()); 
     466            unset($_COOKIE[$key]); 
     467          } 
     468        } 
     469      } 
     470      break; 
    379471    case 'delete': 
    380472      // Remove flags by this user. 
     
    390482      $flag_items = array(); 
    391483      foreach ($flags as $flag) { 
    392         if (!$flag->user_access()) { 
     484        if (!$flag->access($account->uid)) { 
    393485          // User has no permission to use this flag. 
    394           continue; 
    395         } 
    396         if (!$flag->applies_to_content_object($account)) { 
    397           // Flag does not apply to this content. 
    398486          continue; 
    399487        } 
     
    422510 
    423511/** 
     512 * Implementation of hook_session_api_cleanup(). 
     513 * 
     514 * Clear out anonymous user flaggings during Session API cleanup. 
     515 */ 
     516function flag_session_api_cleanup($arg = 'run') { 
     517  // Session API 1.1 version: 
     518  if ($arg == 'run') { 
     519    $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"); 
     520    while ($row = db_fetch_object($result)) { 
     521      db_query("DELETE FROM {flag_content} WHERE sid = %d", $row->sid); 
     522    } 
     523  } 
     524  // Session API 1.2+ version. 
     525  elseif (is_array($arg)) { 
     526    $outdated_sids = $arg; 
     527    db_query('DELETE FROM {flag_content} WHERE sid IN (' . implode(',', $outdated_sids) . ')'); 
     528  } 
     529} 
     530 
     531/** 
    424532 * Implementation of hook_node_type(). 
    425533 */ 
     
    431539      break; 
    432540  } 
     541} 
     542 
     543/** 
     544 * Implementation of hook_activity_info(). 
     545 */ 
     546function flag_activity_info() { 
     547  $info = new stdClass(); 
     548  $info->api = 2; 
     549  $info->name = 'flag'; 
     550  // The order of objects here matters. If a user is both the author and the 
     551  // flagging user, the flagging user message takes precedence. 
     552  $info->objects = array('node author' => 'node', 'flagging user' => 'account', 'comment author' => 'comment'); 
     553  $info->hooks = array('flag' => array('flag', 'unflag')); 
     554 
     555  // Figure out the activity access control realms and the types. 
     556  $flags = flag_get_flags(); 
     557  foreach ($flags as $fid => $flag) { 
     558    $info->realms["flag_" . $flag->fid] = $flag->title; 
     559    // Currently, flagging a user cannot be recorded. 
     560    if ($flag->content_type != 'user') { 
     561      $info->type_options[$flag->fid] = $flag->title; 
     562    } 
     563  } 
     564  $info->object_type = 'flag'; 
     565  $info->path = drupal_get_path('module', 'flag') . '/includes'; 
     566  return $info; 
    433567} 
    434568 
     
    557691function flag_flag($action, $flag, $content_id, $account) { 
    558692  if (module_exists('trigger')) { 
    559  
    560     $flag_action = $flag->get_flag_action($content_id); 
    561     $flag_action->action = $action; 
    562     $context = (array)$flag_action; 
    563     $aids = _trigger_get_hook_aids($action, $action); 
    564     foreach ($aids as $aid => $action_info) { 
     693    $context['hook'] = 'flag'; 
     694    $context['account'] = $account; 
     695    $context['flag'] = $flag; 
     696    $context['op'] = $action; 
     697 
     698    // We add to the $context all the objects we know about: 
     699    $context = array_merge($flag->get_relevant_action_objects($content_id), $context); 
     700 
     701    // Generic "all flags" actions. 
     702    foreach (_trigger_get_hook_aids($action, $action) as $aid => $action_info) { 
    565703      // The 'if ($aid)' is a safeguard against http://drupal.org/node/271460#comment-886564 
    566704      if ($aid) { 
     
    568706      } 
    569707    } 
    570  
     708    // Actions specifically for this flag. 
     709    foreach (_trigger_get_hook_aids($action . '_' . $flag->name, $action) as $aid => $action_info) { 
     710      if ($aid) { 
     711        actions_do($aid, $flag, $context); 
     712      } 
     713    } 
    571714  } 
    572715 
     
    579722 
    580723/** 
     724 * Implementation of hook_flag_access(). 
     725 */ 
     726function flag_flag_access($flag, $content_id, $action, $account) { 
     727  // Restrict access by authorship. It's important that TRUE is never returned 
     728  // here, otherwise we'd grant permission even if other modules denied access. 
     729  if ($flag->content_type == 'node') { 
     730    $node = node_load($content_id); 
     731    if ($flag->access_author == 'own' && $node->uid != $account->uid) { 
     732      return FALSE; 
     733    } 
     734    elseif ($flag->access_author == 'others' && $node->uid == $account->uid) { 
     735      return FALSE; 
     736    } 
     737  } 
     738 
     739  // Restrict access by comment authorship. 
     740  if ($flag->content_type == 'comment') { 
     741    $comment = _comment_load($content_id); 
     742    $node = node_load($comment->nid); 
     743    if ($flag->access_author == 'node_own' && $node->uid != $account->uid) { 
     744      return FALSE; 
     745    } 
     746    elseif ($flag->access_author == 'node_others' && $node->uid == $account->uid) { 
     747      return FALSE; 
     748    } 
     749    elseif ($flag->access_author == 'comment_own' && $comment->uid != $account->uid) { 
     750      return FALSE; 
     751    } 
     752    elseif ($flag->access_author == 'comment_others' && $comment->uid == $account->uid) { 
     753      return FALSE; 
     754    } 
     755  } 
     756} 
     757 
     758/** 
     759 * Implementation of hook_flag_access_multiple(). 
     760 */ 
     761function flag_flag_access_multiple($flag, $content_ids, $account) { 
     762  $access = array(); 
     763 
     764  if ($flag->content_type == 'node') { 
     765    // Restrict access by authorship. This is similar to flag_flag_access() 
     766    // above, but returns an array of 'nid' => $access values. Similarly, we 
     767    // should never return TRUE in any of these access values, only FALSE if we 
     768    // want to deny access, or use the current access value provided by Flag. 
     769    $nids = implode(',', array_map('intval', array_keys($content_ids))); 
     770    $placeholders = implode(',', array_fill(0, sizeof($flag->types), "'%s'")); 
     771    $result = db_query("SELECT nid, uid FROM {node} WHERE nid IN ($nids) AND type in ($placeholders)", $flag->types); 
     772    while ($row = db_fetch_object($result)) { 
     773      if ($flag->access_author == 'own') { 
     774        $access[$row->nid] = $row->uid != $account->uid ? FALSE : NULL; 
     775      } 
     776      elseif ($flag->access_author == 'others') { 
     777        $access[$row->nid] = $row->uid == $account->uid ? FALSE : NULL; 
     778      } 
     779    } 
     780  } 
     781 
     782  if ($flag->content_type == 'comment') { 
     783    // Restrict access by comment ownership. 
     784    $cids = implode(',', array_map('intval', array_keys($content_ids))); 
     785    $result = db_query("SELECT c.cid, c.nid, c.uid as comment_uid, n.nid as node_uid FROM {comments} c LEFT JOIN {node} n ON c.nid = n.nid WHERE cid IN ($cids)"); 
     786    while ($row = db_fetch_object($result)) { 
     787      if ($flag->access_author == 'node_own') { 
     788        $access[$row->cid] = $row->node_uid != $account->uid ? FALSE : NULL; 
     789      } 
     790      elseif ($flag->access_author == 'node_others') { 
     791        $access[$row->cid] = $row->node_uid == $account->uid ? FALSE : NULL; 
     792      } 
     793      elseif ($flag->access_author == 'comment_own') { 
     794        $access[$row->cid] = $row->comment_uid != $account->uid ? FALSE : NULL; 
     795      } 
     796      elseif ($flag->access_author == 'comment_others') { 
     797        $access[$row->cid] = $row->comment_uid == $account->uid ? FALSE : NULL; 
     798      } 
     799    } 
     800  } 
     801 
     802  // Always return an array (even if empty) of accesses. 
     803  return $access; 
     804} 
     805/** 
     806 * Trim a flag to a certain size. 
     807 * 
     808 * @param $fid 
     809 *   The flag object. 
     810 * @param $account 
     811 *   The user object on behalf the trimming will occur. 
     812 * @param $cutoff_size 
     813 *   The number of flaggings allowed. Any flaggings beyond that will be trimmed. 
     814 */ 
     815function flag_trim_flag($flag, $account, $cutoff_size) { 
     816  $result = db_query("SELECT * FROM {flag_content} WHERE fid = %d AND (uid = %d OR uid = 0) ORDER BY timestamp DESC", $flag->fid, $account->uid); 
     817  $i = 1; 
     818  while ($row = db_fetch_object($result)) { 
     819    if ($i++ > $cutoff_size) { 
     820      flag('unflag', $flag->name, $row->content_id, $account); 
     821    } 
     822  } 
     823} 
     824 
     825/** 
    581826 * Implementation of hook_node_operations(). 
    582827 * 
     
    590835 
    591836  foreach ($flags as $flag) { 
    592     $operations['flag_'. $flag->name] = array( 
     837    $operations['flag_' . $flag->name] = array( 
    593838      'label' => $flag->get_label('flag_short'), 
    594839      'callback' => 'flag_nodes', 
     
    596841      'behavior' => array(), 
    597842    ); 
    598     $operations['unflag_'. $flag->name] = array( 
     843    $operations['unflag_' . $flag->name] = array( 
    599844      'label' => $flag->get_label('unflag_short'), 
    600845      'callback' => 'flag_nodes', 
     
    614859    $performed |= flag($action, $flag_name, $nid); 
    615860  } 
    616   // Drupal 6 doesn't display a confirmation message itself, so it's our responsibility. 
    617861  if ($performed) { 
    618862    drupal_set_message(t('The update has been performed.')); 
     863  } 
     864} 
     865 
     866/** 
     867 * Implementation of hook_user_operations(). 
     868 */ 
     869function flag_user_operations() { 
     870  global $user; 
     871 
     872  $flags = flag_get_flags('user', NULL, $user); 
     873  $operations = array(); 
     874 
     875  foreach ($flags as $flag) { 
     876    $operations['flag_' . $flag->name] = array( 
     877      'label' => $flag->get_label('flag_short'), 
     878      'callback' => 'flag_users', 
     879      'callback arguments' => array('flag', $flag->name), 
     880    ); 
     881    $operations['unflag_' . $flag->name] = array( 
     882      'label' => $flag->get_label('unflag_short'), 
     883      'callback' => 'flag_users', 
     884      'callback arguments' => array('unflag', $flag->name), 
     885    ); 
     886  } 
     887  return $operations; 
     888} 
     889/** 
     890* Callback function for hook_user_operations(). 
     891*/ 
     892function flag_users($users, $action, $flag_name) { 
     893  foreach ($users as $uid) { 
     894    flag($action, $flag_name, $uid); 
    619895  } 
    620896} 
     
    630906      break; 
    631907  } 
     908} 
     909 
     910/** 
     911 * Implementation of hook_service(). 
     912 */ 
     913function flag_service() { 
     914  $items = array(); 
     915 
     916  $items[] = array( 
     917    '#method' => 'flag.flag', 
     918    '#callback' => 'flag_service_flag', 
     919    '#access callback' => 'flag_service_flag_access', 
     920    '#file' => array( 
     921      'file' => 'inc', 
     922      'module' => 'flag', 
     923      'file name' => 'includes/flag.services', 
     924    ), 
     925    '#args' => array( 
     926      array( 
     927        '#name' => 'flag_name', 
     928        '#type' => 'string', 
     929        '#description' => t('The name of the flag.'), 
     930      ), 
     931      array( 
     932        '#name' => 'content_id', 
     933        '#type' => 'int', 
     934        '#description' => t('The content ID.'), 
     935      ), 
     936      array( 
     937        '#name' => 'uid', 
     938        '#type' => 'int', 
     939        '#description' => t('The user ID for which to flag.'), 
     940        '#optional' => TRUE, 
     941      ), 
     942      array( 
     943        '#name' => 'action', 
     944        '#type' => 'string', 
     945        '#description' => t('Optional; The action to perform, default is "flag". Should be "flag" or "unflag".'), 
     946        '#optional' => TRUE, 
     947      ), 
     948      array( 
     949        '#name' => 'skip_permission_check', 
     950        '#type' => 'boolean', 
     951        '#description' => t('Optional; Flag the content even if the user does not have permission to do so. FALSE by default'), 
     952        '#optional' => TRUE, 
     953      ), 
     954    ), 
     955    '#return' => 'boolean', 
     956    '#help' => t('Flags (or unflags) a content.') 
     957  ); 
     958 
     959  $items[] = array( 
     960    '#method' => 'flag.is_flagged', 
     961    '#callback' => 'flag_service_is_flagged', 
     962    '#access callback' => 'flag_service_flag_access', 
     963    '#file' => array( 
     964      'file' => 'inc', 
     965      'module' => 'flag', 
     966      'file name' => 'includes/flag.services', 
     967    ), 
     968    '#args' => array( 
     969      array( 
     970        '#name' => 'flag_name', 
     971        '#type' => 'string', 
     972        '#description' => t('The name of the flag.'), 
     973      ), 
     974      array( 
     975        '#name' => 'content_id', 
     976        '#type' => 'int', 
     977        '#description' => t('The content ID.'), 
     978      ), 
     979      array( 
     980        '#name' => 'uid', 
     981        '#type' => 'int', 
     982        '#description' => t('The user ID that might have flagged the content.'), 
     983        '#optional' => TRUE, 
     984      ), 
     985    ), 
     986    '#return' => 'boolean', 
     987    '#help' => t('Check if a content was flagged by a user.') 
     988  ); 
     989 
     990  return $items; 
    632991} 
    633992 
     
    6511010      'arguments' => array('flags' => NULL, 'default_flags' => NULL), 
    6521011    ), 
     1012    'flag_form_roles' => array( 
     1013      'arguments' => array('element' => NULL), 
     1014    ), 
    6531015    'flag_rules_radios' => array( 
    6541016      'arguments' => array(), 
     
    6681030 * 
    6691031 * See 'flag.tpl.php' for their documentation. 
    670  * 
    671  * Note: The Drupal 5 version of this module calls this function directly. 
    6721032 */ 
    6731033function template_preprocess_flag(&$variables) { 
     1034  global $user; 
    6741035  static $first_time = TRUE; 
    6751036 
     
    6781039  $action = $variables['action']; 
    6791040  $content_id = $variables['content_id']; 
     1041  $flag_css_name = str_replace('_', '-', $flag->name); 
    6801042 
    6811043  // Generate the link URL. 
    682   $link_types = flag_get_link_types(); 
    683   $link_type_module = $link_types[$flag->link_type]['module']; 
    684   $link = module_invoke($link_type_module, 'flag_link', $flag, $action, $content_id); 
     1044  $link_type = $flag->get_link_type(); 
     1045  $link = module_invoke($link_type['module'], 'flag_link', $flag, $action, $content_id); 
    6851046  if (isset($link['title']) && empty($link['html'])) { 
    6861047    $link['title'] = check_plain($link['title']); 
    6871048  } 
    6881049 
    689   if ($flag->link_type == 'toggle' && $first_time) { 
     1050  // Replace the link with the access denied text if unable to flag. 
     1051  if ($action == 'unflag' && !$flag->access($content_id, 'unflag')) { 
     1052    $link['title'] = $flag->get_label('unflag_denied_text', $content_id); 
     1053    unset($link['href']); 
     1054  } 
     1055 
     1056  // Tell the template file to add JavaScript/CSS if using the toggle link type. 
     1057  // Anonymous users always need the JavaScript to maintain their flag state. 
     1058  if (($flag->link_type == 'toggle' || $user->uid == 0) && $first_time) { 
    6901059    $variables['setup'] = $first_time; 
    6911060    $first_time = FALSE; 
     
    6951064  } 
    6961065 
    697   $variables['link_href'] = check_url(url($link['href'], $link)); 
     1066  $variables['link_href'] = isset($link['href']) ? check_url(url($link['href'], $link)) : FALSE; 
    6981067  $variables['link_text'] = isset($link['title']) ? $link['title'] : $flag->get_label($action . '_short', $content_id); 
    6991068  $variables['link_title'] = isset($link['attributes']['title']) ? check_plain($link['attributes']['title']) : check_plain(strip_tags($flag->get_label($action . '_long', $content_id))); 
    700   $variables['flag_name_css'] = str_replace('_', '-', $flag->name); 
    7011069  $variables['last_action'] = ($action == 'flag' ? 'unflagged' : 'flagged'); 
     1070 
     1071  $variables['flag_wrapper_classes_array'] = array(); 
     1072  $variables['flag_wrapper_classes_array'][] = 'flag-wrapper'; 
     1073  $variables['flag_wrapper_classes_array'][] = 'flag-' . $flag_css_name; 
     1074  $variables['flag_wrapper_classes_array'][] = 'flag-' . $flag_css_name . '-' . $content_id; 
     1075  $variables['flag_wrapper_classes'] = implode(' ', $variables['flag_wrapper_classes_array']); 
     1076 
    7021077  $variables['flag_classes_array'] = array(); 
    7031078  $variables['flag_classes_array'][] = 'flag'; 
    7041079  $variables['flag_classes_array'][] = $variables['action'] .'-action'; 
    7051080  $variables['flag_classes_array'][] = 'flag-link-'. $flag->link_type; 
    706  
    7071081  if (isset($link['attributes']['class'])) { 
    7081082    $variables['flag_classes_array'][] = $link['attributes']['class']; 
    7091083  } 
    710  
    7111084  if ($variables['after_flagging']) { 
    7121085    $inverse_action = ($action == 'flag' ? 'unflag' : 'flag'); 
     
    7141087    $variables['flag_classes_array'][] = $variables['last_action']; 
    7151088  } 
    716  
    7171089  $variables['flag_classes'] = implode(' ', $variables['flag_classes_array']); 
    7181090} 
     
    7971169} 
    7981170 
     1171/** 
     1172 * Return an array of flag link type descriptions. 
     1173 */ 
     1174function _flag_link_type_descriptions() { 
     1175  $options = array(); 
     1176  $types = flag_get_link_types(); 
     1177  foreach ($types as $type_name => $type) { 
     1178    $options[$type_name] = $type['description']; 
     1179  } 
     1180  return $options; 
     1181} 
     1182 
     1183/** 
     1184 * Return an array of flag link fields that are dependent on a link type. 
     1185 */ 
     1186function _flag_link_type_fields() { 
     1187  $options = array(); 
     1188  $types = flag_get_link_types(); 
     1189  foreach ($types as $type_name => $type) { 
     1190    $options[$type_name] = array_keys($type['options']); 
     1191  } 
     1192  return $options; 
     1193} 
     1194 
    7991195// --------------------------------------------------------------------------- 
    8001196// Non-Views public API 
     
    8351231 
    8361232/** 
     1233 * Get the total count of items flagged within a flag. 
     1234 * 
     1235 * @param $flag_name 
     1236 *   The flag name for which to retrieve a flag count. 
     1237 * @param $reset 
     1238 *   Reset the internal cache and execute the SQL query another time. 
     1239 */ 
     1240function flag_get_flag_counts($flag_name, $reset = FALSE) { 
     1241  static $counts; 
     1242 
     1243  if ($reset) { 
     1244    $counts = array(); 
     1245  } 
     1246  if (!isset($counts[$flag_name])) { 
     1247    $flag = flag_get_flag($flag_name); 
     1248    $counts[$flag_name] = db_result(db_query("SELECT COUNT(*) FROM {flag_content} WHERE fid = %d", $flag->fid)); 
     1249  } 
     1250 
     1251  return $counts[$flag_name]; 
     1252} 
     1253 
     1254/** 
    8371255 * Load a single flag either by name or by flag ID. 
    8381256 * 
     
    8561274    } 
    8571275  } 
     1276  return FALSE; 
    8581277} 
    8591278 
     
    9171336      } 
    9181337    } 
     1338 
     1339    // Allow modules implementing hook_flag_alter(&$flag) to modify each flag. 
     1340    foreach ($flags as $flag) { 
     1341      drupal_alter('flag', $flag); 
     1342    } 
    9191343  } 
    9201344 
     
    9611385      $flag = flag_flag::factory_by_array($config); 
    9621386      $flag->module = $module; 
     1387 
     1388      // Disable flags that are not at the current API version. 
     1389      if (!isset($flag->api_version) || $flag->api_version < FLAG_API_VERSION) { 
     1390        $flag->status = FALSE; 
     1391        $flag->api_version = isset($flag->api_version) ? $flag->api_version : 1; 
     1392      } 
     1393 
    9631394      // Add flags that have been enabled. 
    9641395      if ((!isset($flag_status[$flag->name]) && (!isset($flag->status) || $flag->status)) || !empty($flag_status[$flag->name])) { 
     
    9661397        $default_flags[$flag->name] = $flag; 
    9671398      } 
     1399      // Add flags that have been disabled. 
    9681400      elseif ($include_disabled) { 
    9691401        $flag->status = FALSE; 
     
    9741406 
    9751407  return $default_flags; 
     1408} 
     1409 
     1410/** 
     1411 * Get all flagged content in a flag. 
     1412 * 
     1413 * @param 
     1414 *   The flag name for which to retrieve flagged content. 
     1415 */ 
     1416function flag_get_flagged_content($flag_name) { 
     1417  $return = array(); 
     1418  $flag = flag_get_flag($flag_name); 
     1419  $result = db_query("SELECT * FROM {flag_content} WHERE fid = %d", $flag->fid); 
     1420  while ($row = db_fetch_object($result)) { 
     1421    $return[] = $row; 
     1422  } 
     1423  return $return; 
     1424} 
     1425 
     1426/** 
     1427 * Get content ID from a flag content ID. 
     1428 * 
     1429 * @param $fcid 
     1430 *   The flag content ID for which to look up the content ID. 
     1431 */ 
     1432function flag_get_content_id($fcid) { 
     1433  return db_result(db_query("SELECT content_id FROM {flag_content} WHERE fcid = %d", $fcid)); 
    9761434} 
    9771435 
     
    9871445 *   Optional. The user ID whose flags we're checking. If none given, the 
    9881446 *   current user will be used. 
     1447 * @param $sid 
     1448 *   Optional. The user SID (provided by Session API) whose flags we're 
     1449 *   checking. If none given, the current user will be used. The SID is 0 for 
     1450 *   logged in users. 
    9891451 * @param $reset 
    9901452 *   Reset the internal cache and execute the SQL query another time. 
     
    9981460 * 
    9991461 */ 
    1000 function flag_get_user_flags($content_type, $content_id = NULL, $uid = NULL, $reset = FALSE) { 
     1462function flag_get_user_flags($content_type, $content_id = NULL, $uid = NULL, $sid = NULL, $reset = FALSE) { 
    10011463  static $flagged_content; 
    10021464 
     
    10091471 
    10101472  $uid = !isset($uid) ? $GLOBALS['user']->uid : $uid; 
     1473  $sid = !isset($sid) ? flag_get_sid($uid) : $sid; 
    10111474 
    10121475  if (isset($content_id)) { 
    10131476    if (!isset($flagged_content[$uid][$content_type][$content_id])) { 
    10141477      $flag_names = _flag_get_flag_names(); 
    1015       $flagged_content[$uid][$content_type][$content_id] = array(); 
    1016       $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); 
     1478      $flagged_content[$uid][$sid][$content_type][$content_id] = array(); 
     1479      $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); 
    10171480      while ($flag_content = db_fetch_object($result)) { 
    1018         $flagged_content[$uid][$content_type][$content_id][$flag_names[$flag_content->fid]] = $flag_content; 
    1019       } 
    1020     } 
    1021     return $flagged_content[$uid][$content_type][$content_id]; 
     1481        $flagged_content[$uid][$sid][$content_type][$content_id][$flag_names[$flag_content->fid]] = $flag_content; 
     1482      } 
     1483    } 
     1484    return $flagged_content[$uid][$sid][$content_type][$content_id]; 
    10221485  } 
    10231486 
    10241487  else { 
    1025     if (!isset($flagged_content[$uid]['all'][$content_type])) { 
     1488    if (!isset($flagged_content[$uid][$sid]['all'][$content_type])) { 
    10261489      $flag_names = _flag_get_flag_names(); 
    1027       $flagged_content[$uid]['all'][$content_type] = TRUE; 
    1028       $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND (uid = %d OR uid = 0)", $content_type, $uid); 
     1490      $flagged_content[$uid][$sid]['all'][$content_type] = TRUE; 
     1491      $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND (uid = %d OR uid = 0) AND sid = %s", $content_type, $uid, $sid); 
    10291492      while ($flag_content = db_fetch_object($result)) { 
    1030         $flagged_content[$uid][$content_type]['all'][$flag_names[$flag_content->fid]][$flag_content->content_id] = $flag_content; 
    1031       } 
    1032     } 
    1033     return $flagged_content[$uid][$content_type]['all']; 
     1493        $flagged_content[$uid][$sid][$content_type]['all'][$flag_names[$flag_content->fid]][$flag_content->content_id] = $flag_content; 
     1494      } 
     1495    } 
     1496    return $flagged_content[$uid][$sid][$content_type]['all']; 
    10341497  } 
    10351498 
     
    10381501/** 
    10391502 * Return a list of users who have flagged a piece of content. 
    1040  */ 
    1041 function flag_get_content_flags($content_type, $content_id, $reset = FALSE) { 
     1503 * 
     1504 * @param $content_type 
     1505 *   The type of content that will be retrieved. Usually 'node'. 
     1506 * @param $content_id 
     1507 *   The content ID to check for flagging. 
     1508 * @param $flag_name 
     1509 *   Optional. The name of a flag if wanting a list specific to a single flag. 
     1510 * @param $reset 
     1511 *   Reset the internal cache of flagged content. 
     1512 * @return 
     1513 *   If no flag name is given, an array of flagged content, keyed by the user 
     1514 *   ID that flagged the content. Each flagged content array is structured as 
     1515 *   an array of flag information for each flag, keyed by the flag name. If 
     1516 *   a flag name is specified, only the information for that flag is returned. 
     1517 */ 
     1518function flag_get_content_flags($content_type, $content_id, $flag_name = NULL, $reset = FALSE) { 
    10421519  static $content_flags; 
    10431520 
     
    10461523    $result = db_query("SELECT * FROM {flag_content} WHERE content_type = '%s' AND content_id = %d ORDER BY timestamp DESC", $content_type, $content_id); 
    10471524    while ($flag_content = db_fetch_object($result)) { 
     1525      // Build a list of flaggings for all flags by user. 
    10481526      $content_flags[$content_type][$content_id]['users'][$flag_content->uid][$flag_names[$flag_content->fid]] = $flag_content; 
    1049     } 
    1050   } 
    1051  
    1052   return $content_flags[$content_type][$content_id]['users']; 
     1527      // Build a list of flaggings for each individual flag. 
     1528      $content_flags[$content_type][$content_id]['flags'][$flag_names[$flag_content->fid]][$flag_content->uid] = $flag_content; 
     1529    } 
     1530  } 
     1531 
     1532  return isset($flag_name) ? $content_flags[$content_type][$content_id]['flags'][$flag_name] : $content_flags[$content_type][$content_id]['users']; 
    10531533} 
    10541534 
     
    10731553    return; 
    10741554  } 
    1075   if (!$flag->user_access()) { 
     1555  if (!$flag->access($content_id)) { 
    10761556    // User has no permission to use this flag. 
    1077     return; 
    1078   } 
    1079   if (!$flag->applies_to_content_id($content_id)) { 
    1080     // Flag does not apply to this content. 
    10811557    return; 
    10821558  } 
     
    10941570    foreach (module_implements('flag_link_types') as $module) { 
    10951571      $module_types = module_invoke($module, 'flag_link_types'); 
    1096       foreach ($module_types as $type_name => $type_title) { 
    1097         $link_types[$type_name] = array( 
    1098           'module' => $module, 
    1099           'title' => $type_title, 
     1572      foreach ($module_types as $type_name => $info) { 
     1573        $link_types[$type_name] = $info; 
     1574        $link_types[$type_name]['module'] = $module; 
     1575        $link_types[$type_name] += array( 
     1576          'title' => '', 
     1577          'description' => '', 
     1578          'options' => array(), 
    11001579        ); 
    11011580      } 
    11021581    } 
     1582    drupal_alter('flag_link_types', $link_types); 
    11031583  } 
    11041584 
     
    11091589 * Get a private token used to protect links from spoofing - CSRF. 
    11101590 */ 
    1111 function flag_get_token($nid) { 
    1112   return drupal_get_token($nid); 
     1591function flag_get_token($content_id) { 
     1592  // Anonymous users get a less secure token, since it must be the same for all 
     1593  // anonymous users on the entire site to work with page caching. 
     1594  return ($GLOBALS['user']->uid) ? drupal_get_token($content_id) : md5(drupal_get_private_key() . $content_id); 
    11131595} 
    11141596 
     
    11161598 * Check to see if a token value matches the specified node. 
    11171599 */ 
    1118 function flag_check_token($token, $seed) { 
    1119   return drupal_get_token($seed) == $token; 
    1120 } 
     1600function flag_check_token($token, $content_id) { 
     1601  return flag_get_token($content_id) == $token; 
     1602} 
     1603 
     1604/** 
     1605 * Set the Session ID for a user. Utilizes the Session API module. 
     1606 * 
     1607 * This function is only called in flag_init(), to set the current user's 
     1608 * SID in case the user logs in during this request. 
     1609 */ 
     1610function flag_set_sid($uid = NULL) { 
     1611  static $sids = array(); 
     1612 
     1613  if (!isset($uid)) { 
     1614    $uid = $GLOBALS['user']->uid; 
     1615  } 
     1616 
     1617  if (!isset($sids[$uid])) { 
     1618    if (module_exists('session_api') && session_api_available() && $uid == 0) { 
     1619      $sids[$uid] = session_api_get_sid(); 
     1620    } 
     1621    else { 
     1622      $sids[$uid] = 0; 
     1623    } 
     1624  } 
     1625 
     1626  return $sids[$uid]; 
     1627} 
     1628 
     1629/** 
     1630 * Get the Session ID for a user. Utilizes the Session API module. 
     1631 */ 
     1632function flag_get_sid($uid = NULL) { 
     1633  return flag_set_sid($uid); 
     1634} 
  • drupal-6/sites/all/modules/flag/flag_actions.info

    r865 r867  
    55dependencies[] = flag 
    66package = Flags 
    7 ; Information added by drupal.org packaging script on 2010-01-08 
    8 version = "6.x-1.2" 
    9 core = "6.x" 
    10 project = "flag" 
    11 datestamp = "1262929205" 
    12  
  • drupal-6/sites/all/modules/flag/flag_actions.install

    r535 r867  
    11<?php 
    2 // $Id: flag_actions.install,v 1.1.2.2 2008/09/30 03:08:49 quicksketch Exp $ 
     2// $Id: flag_actions.install,v 1.1.2.2.2.1 2009/09/19 01:30:46 quicksketch Exp $ 
    33 
    44/** 
     
    4848        'type' => 'int', 
    4949        'size' => 'small', 
    50         'not null' => FALSE, 
     50        'not null' => TRUE, 
     51        'default' => 0, 
     52        'disp-width' => '5', 
     53      ), 
     54      'repeat_threshold' => array( 
     55        'type' => 'int', 
     56        'size' => 'small', 
     57        'not null' => TRUE, 
     58        'default' => 0, 
    5159        'disp-width' => '5', 
    5260      ), 
     
    6876  return $schema; 
    6977} 
     78 
     79/** 
     80 * Add a "repeat_threshold" value to all existing Flag actions. 
     81 */ 
     82function flag_actions_update_6200() { 
     83  $ret = array(); 
     84 
     85  // Add the new repeat_threshold column. 
     86  if (!db_column_exists('flag_actions', 'repeat_threshold')) { 
     87    $column = array( 
     88      'type' => 'int', 
     89      'size' => 'small', 
     90      'not null' => TRUE, 
     91      'default' => 0, 
     92      'disp-width' => '5', 
     93    ); 
     94    db_add_field($ret, 'flag_actions', 'repeat_threshold', $column); 
     95  } 
     96 
     97  // Update the normal threshold column to default to 0. 
     98  $column = array( 
     99    'type' => 'int', 
     100    'size' => 'small', 
     101    'not null' => TRUE, 
     102    'default' => 0, 
     103    'disp-width' => '5', 
     104  ); 
     105  db_change_field($ret, 'flag_actions', 'threshold', 'threshold', $column); 
     106 
     107  return $ret; 
     108} 
  • drupal-6/sites/all/modules/flag/flag_actions.module

    r865 r867  
    11<?php 
    2 // $Id: flag_actions.module,v 1.1.2.13 2009/09/14 11:58:32 quicksketch Exp $ 
     2// $Id: flag_actions.module,v 1.1.2.12.2.5 2009/09/29 02:27:37 quicksketch Exp $ 
    33 
    44/** 
     
    7979function flag_actions_get_actions($flag_name = NULL, $reset = FALSE) { 
    8080  static $flag_actions; 
     81  module_load_include('inc', 'flag', 'includes/flag.actions'); 
    8182 
    8283  // Get a list of all possible actions defined by modules. 
     
    126127 * @param $fid 
    127128 *   The flag object ID. 
     129 * @param $event 
     130 *   The flag event, such as "flag" or "unflag". 
    128131 * @param $threshold 
    129132 *   The flagging threshold at which this action will be executed. 
     133 * @param $repeat_threshold 
     134 *   The number of additional flaggings after which the action will be repeated. 
    130135 * @param $callback 
    131136 *   The action callback to be executed. 
     
    133138 *   The action parameters. 
    134139 */ 
    135 function flag_actions_insert_action($fid, $event, $threshold, $callback, $parameters) { 
    136   db_query("INSERT INTO {flag_actions} (fid, event, threshold, callback, parameters) VALUES (%d, '%s', %d, '%s', '%s')", $fid, $event, $threshold, $callback, serialize($parameters)); 
     140function flag_actions_insert_action($fid, $event, $threshold, $repeat_threshold, $callback, $parameters) { 
     141  db_query("INSERT INTO {flag_actions} (fid, event, threshold, repeat_threshold, callback, parameters) VALUES (%d, '%s', %d, %d, '%s', '%s')", $fid, $event, $threshold, $repeat_threshold, $callback, serialize($parameters)); 
    137142  return db_last_insert_id('flag_actions', 'aid'); 
    138143} 
     
    143148 * @param $aid 
    144149 *   The flag action ID to update. 
     150 * @param $event 
     151 *   The flag event, such as "flag" or "unflag". 
    145152 * @param $threshold 
    146153 *   The flagging threshold at which this action will be executed. 
     154 * @param $repeat_threshold 
     155 *   The number of additional flaggings after which the action will be repeated. 
    147156 * @param $parameters 
    148157 *   The action parameters. 
    149158 */ 
    150 function flag_actions_update_action($aid, $event, $threshold, $parameters) { 
    151   return db_query("UPDATE {flag_actions} SET event = '%s', threshold = %d, parameters = '%s' WHERE aid = %d", $event, $threshold, serialize($parameters), $aid); 
     159function flag_actions_update_action($aid, $event, $threshold, $repeat_threshold, $parameters) { 
     160  return db_query("UPDATE {flag_actions} SET event = '%s', threshold = %d, repeat_threshold = %d, parameters = '%s' WHERE aid = %d", $event, $threshold, $repeat_threshold, serialize($parameters), $aid); 
    152161} 
    153162 
     
    177186  $node_changed = FALSE; 
    178187  foreach ($actions as $aid => $action) { 
    179     $threshold = ($action->event == $event) && 
    180                 (($action->event == 'flag'   && $count == $action->threshold) || 
    181                  ($action->event == 'unflag' && $count == $action->threshold - 1)); 
    182     if ($threshold && !$action->missing) { 
     188    if ($action->event == 'flag') { 
     189      $at_threshold = ($count == $action->threshold); 
     190      $repeat = $action->repeat_threshold ? (($count > $action->threshold) && (($count - $action->threshold) % $action->repeat_threshold == 0)) : FALSE; 
     191    } 
     192    elseif ($action->event == 'unflag') { 
     193      $at_threshold = ($count == $action->threshold - 1); 
     194      $repeat = $action->repeat_threshold ? (($count < $action->threshold - 1) && (($count - $action->threshold - 1) % $action->repeat_threshold == 0)) : FALSE; 
     195    } 
     196    if (($at_threshold || $repeat) && $action->event == $event && !$action->missing) { 
    183197      $context = $action->parameters; 
    184198      $context['callback'] = $action->callback; 
     
    240254    $flag = flag_get_flag($action->flag); 
    241255 
     256    // Build a sample string representing repeating actions. 
     257    if ($action->repeat_threshold) { 
     258      $repeat_count = 3; 
     259      $repeat_subtract = ($action->event == 'flag') ? 1 : -1; 
     260      $repeat_samples = array(); 
     261      for ($n = 1; $n < $repeat_count + 2; $n++) { 
     262        $sample = $action->threshold + (($n * $action->repeat_threshold) * $repeat_subtract); 
     263        if ($sample > 0) { 
     264          $repeat_samples[] = $sample; 
     265        } 
     266      } 
     267      if (count($repeat_samples) > $repeat_count) { 
     268        $repeat_samples[$repeat_count] = '&hellip;'; 
     269      } 
     270      $repeat_string = implode(', ', $repeat_samples); 
     271    } 
     272    else { 
     273      $repeat_string = '-'; 
     274    } 
     275 
    242276    $row = array(); 
    243277    $row[] = $flag->get_title(); 
    244     $row[] = ($action->event == 'flag' ? '≥ ' : '< ') . $action->threshold; 
     278    $row[] = ($action->event == 'flag' ? '&ge; ' : '&lt; ') . $action->threshold; 
     279    $row[] = $repeat_string; 
    245280    $row[] = empty($action->missing) ? $action->description : '<div class="error">' . $action->description . '</div>'; 
    246281    $row[] = l(t('edit'), 'admin/build/flags/actions/configure/'. $action->aid); 
     
    256291    t('Flag'), 
    257292    t('Threshold'), 
     293    t('Repeats'), 
    258294    t('Action'), 
    259295    array('data' => t('Operations'), 'colspan' => 2), 
     
    380416    $action->event = 'flag'; 
    381417    $action->threshold = 10; 
     418    $action->repeat_threshold = 0; 
    382419    drupal_set_title(t('Add "@action" action to the @title flag', array('@action' => $action->description, '@title' => $flag->get_title()))); 
    383420  } 
     
    388425    '#theme' => 'flag_actions_flag_form', 
    389426    '#action' => $action, 
     427    '#flag' => $flag, 
    390428  ); 
    391429 
     
    422460  ); 
    423461 
     462  $form['flag']['repeat_threshold'] = array( 
     463    '#type' => 'textfield', 
     464    '#size' => 6, 
     465    '#maxlength' => 6, 
     466    '#default_value' => $action->repeat_threshold, 
     467  ); 
     468 
     469  if ($flag->global) { 
     470    $form['flag']['threshold']['#disabled'] = 1; 
     471    $form['flag']['threshold']['#value'] = 1; 
     472    $form['flag']['repeat_threshold']['#access'] = FALSE; 
     473    $form['flag']['repeat_threshold']['#value'] = 0; 
     474  } 
     475 
     476  // Merge in the standard flag action form. 
    424477  $action_form = $callback .'_form'; 
     478  $edit = array(); 
    425479  if (function_exists($action_form)) { 
    426     $edit = $action->parameters; 
     480    $edit += $action->parameters; 
    427481    $edit['actions_description'] = $action->description; 
    428482    $edit['actions_type'] = $action->type; 
    429     $form = array_merge($form, $action_form($edit)); 
    430   } 
    431  
     483    $edit['actions_flag'] = $flag->name; 
     484    $additions = flag_actions_form_additions($action_form, $edit); 
     485    $form = array_merge($form, $additions); 
     486  } 
     487 
     488  // Add a few customizations to existing flag actions. 
    432489  $flag_actions_form = 'flag_actions_'. $callback .'_form'; 
    433490  if (function_exists($flag_actions_form)) { 
    434     $flag_actions_form($form, $flag, isset($edit) ? $edit : array()); 
     491    $flag_actions_form($form, $flag, $edit); 
    435492  } 
    436493 
     
    441498 
    442499  return $form; 
     500} 
     501 
     502/** 
     503 * Execute an action form callback to retrieve form additions. 
     504 * 
     505 * This function prevents the form callback from modifying local variables. 
     506 */ 
     507function flag_actions_form_additions($callback, $edit) { 
     508  return $callback($edit); 
    443509} 
    444510 
     
    455521  } 
    456522 
    457   $aid        = $form_state['values']['flag']['aid']; 
    458   $flag       = $form_state['values']['flag']['flag']; 
    459   $event      = $form_state['values']['flag']['event']; 
    460   $threshold  = $form_state['values']['flag']['threshold']; 
    461   $callback   = $form_state['values']['flag']['callback']; 
     523  $aid              = $form_state['values']['flag']['aid']; 
     524  $flag             = $form_state['values']['flag']['flag']; 
     525  $event            = $form_state['values']['flag']['event']; 
     526  $threshold        = $form_state['values']['flag']['threshold']; 
     527  $repeat_threshold = $form_state['values']['flag']['repeat_threshold']; 
     528  $callback         = $form_state['values']['flag']['callback']; 
    462529 
    463530  $parameters = $form_state['values']; 
     
    465532 
    466533  if (empty($aid)) { 
    467     $aid = flag_actions_insert_action($flag->fid, $event, $threshold, $callback, $parameters); 
     534    $aid = flag_actions_insert_action($flag->fid, $event, $threshold, $repeat_threshold, $callback, $parameters); 
    468535    $form_state['values']['flag']['aid'] = $aid; 
    469536    $form_state['values']['flag']['is_new'] = TRUE; 
    470537  } 
    471538  else { 
    472     flag_actions_update_action($aid, $event, $threshold, $parameters); 
     539    flag_actions_update_action($aid, $event, $threshold, $repeat_threshold, $parameters); 
    473540  } 
    474541 
     
    482549  $event = drupal_render($form['event']); 
    483550  $threshold = drupal_render($form['threshold']); 
     551  $repeat_threshold = drupal_render($form['repeat_threshold']); 
    484552  $action = $form['#action']->description; 
    485553 
     
    487555  $output .= '<div class="container-inline">'; 
    488556  $output .= t('Perform action when content !event !threshold flags', array('!event' => $event, '!threshold' => $threshold)); 
     557  if ($form['#flag']->global) { 
     558    $output .= ' ' . t('(global flags always have a threshold of 1)'); 
     559  } 
     560  $output .= '</div>'; 
     561  $output .= '<div class="container-inline">'; 
     562  if (!$form['#flag']->global) { 
     563    $output .= t('Repeat this action every !repeat_threshold additional flags after the threshold is reached', array('!repeat_threshold' => $repeat_threshold)); 
     564  } 
    489565  $output .= '</div>'; 
    490566 
    491567  $element = array( 
    492568    '#title' => t('Flagging threshold'), 
    493     '#description' => t('Set the event for which this action should be executed.'), 
    494569    '#required' => TRUE, 
    495570  ); 
     
    527602} 
    528603 
     604/** 
     605 * Make modifications to the "Send e-mail" action form. 
     606 */ 
    529607function flag_actions_system_send_email_action_form(&$form, &$flag, $context) { 
    530608  $form['flag_tip'] = array( 
     
    536614    '#weight' => -1, 
    537615  ); 
    538   return $form; 
    539 } 
    540  
     616} 
     617 
     618/** 
     619 * Make modifications to the "Send tokenized e-mail" action form. 
     620 */ 
    541621function flag_actions_token_actions_send_email_action_form(&$form, &$flag, $context) { 
    542622  if (!isset($context['recipient'])) { 
  • drupal-6/sites/all/modules/flag/includes/flag.rules.inc

    r865 r867  
    11<?php 
    2 // $Id: flag.rules.inc,v 1.1.2.2 2009/05/06 23:37:47 quicksketch Exp $ 
     2// $Id: flag.rules.inc,v 1.1.2.2.2.2 2009/11/03 01:53:36 quicksketch Exp $ 
    33 
    44/** 
     
    4646 
    4747  /** 
    48    * Returns radios for selecting a flag of the type given in  
     48   * Returns radios for selecting a flag of the type given in 
    4949   * $info['flag_type']. 
    5050   */ 
     
    8383  $flags = flag_get_flags(); 
    8484  foreach ($flags as $flag) { 
    85      
     85 
    8686    $arguments = array( 
    8787      // First, define ubiquitous arguments. 
     
    166166    if ($flag->rules_get_element_argument_definition()) { 
    167167      $args += array('object' => $flag->rules_get_element_argument_definition()); 
    168        
     168 
    169169      $items += array( 
    170170        'flag_rules_action_flag_'. $type => array( 
     
    215215 
    216216/** 
    217  * Base action implementation. 
     217 * Base action implementation: Trim flag. 
    218218 */ 
    219219function flag_rules_action_trim($flag, $flagging_user, $cutoff_size, $settings) { 
    220   // We can't use db_query_range(), because we can't specify 'infinity'. 
    221   $result = db_query("SELECT * FROM {flag_content} WHERE fid = %d AND (uid = %d OR uid = 0) ORDER BY timestamp DESC", $flag->fid, $flagging_user->uid); 
    222   $i = 1; 
    223   while ($row = db_fetch_object($result)) { 
    224     if ($i++ > $cutoff_size) { 
    225       flag('unflag', $flag->name, $row->content_id, $flagging_user); 
    226     } 
    227   } 
     220  flag_trim_flag($flag, $flagging_user, $cutoff_size); 
    228221} 
    229222 
     
    244237    if ($flag->rules_get_element_argument_definition()) { 
    245238      $args += array('object' => $flag->rules_get_element_argument_definition()); 
    246    
     239 
    247240      $items += array( 
    248241        'flag_rules_condition_threshold_'. $type => array( 
  • drupal-6/sites/all/modules/flag/includes/flag.views_default.inc

    r535 r867  
    11<?php 
    2 // $Id: flag.views_default.inc,v 1.1.2.5 2009/02/22 22:20:39 quicksketch Exp $ 
     2// $Id: flag.views_default.inc,v 1.1.2.5.2.1 2009/09/28 02:05:06 quicksketch Exp $ 
    33 
    44/** 
     
    104104  $access = array( 
    105105    'type' => 'role', 
    106     'role' => drupal_map_assoc($flag->roles), 
     106    'role' => drupal_map_assoc($flag->roles['flag']), 
    107107    'perm' => '', 
    108108  ); 
  • drupal-6/sites/all/modules/flag/includes/flag_handler_field_ops.inc

    r865 r867  
    11<?php 
    2 // $Id: flag_handler_field_ops.inc,v 1.1.2.6 2010/01/08 04:48:43 quicksketch Exp $ 
     2// $Id: flag_handler_field_ops.inc,v 1.1.2.5.2.3 2010/01/08 04:43:17 quicksketch Exp $ 
    33 
    44/** 
     
    8383        'numeric' => TRUE, 
    8484      ); 
     85      $join->extra[] = array( 
     86        'field' => 'sid', 
     87        'value' => flag_get_sid(), 
     88        'numeric' => TRUE, 
     89      ); 
    8590    } 
    8691    $flag_table = $this->query->add_table('flag_content', $parent, $join); 
     
    103108    $flag = $this->get_flag(); 
    104109 
    105     if (!$flag->user_access()) { 
    106       // User has no permission to use this flag. 
    107       return; 
    108     } 
    109  
    110110    $ids = array(); 
    111111    foreach ($values as $row) { 
    112112      $content_id = $row->{$this->aliases['content_id']}; 
     113      $is_flagged = $row-&