Issue #1820556: Don't show achievement popups if the overlay is active.
[project/achievements.git] / achievements.api.php
index 76efe75..76796c1 100644 (file)
  *
  * This hook describes your achievements to the base module so that it can
  * create the pages and caches necessary for site-wide display. The base
- * module doesn't know how to unlock any of your defined achievements...
- * instead, you use Drupal's existing hooks, the achievement storage tables,
- * and a few helper functions to complete the workflow. See the remaining
- * documentation in this file for further code samples.
+ * module doesn't know how to unlock any of your achievements... instead, you
+ * use Drupal's existing hooks, the achievement storage tables, and a few
+ * helper functions to complete the workflow. See the remaining documentation
+ * in this file for further code samples.
+ *
+ * There are many different kinds of achievements, and it's accurate enough to
+ * say that if you can measure or respond to an action, it can be made into a
+ * matching achievement. Be creative. Look at what others are doing. Have fun.
+ * Your gamification efforts will fail or be un-fun if you don't have a gamer
+ * helping you, if you make everything a mindless grind, or if you simply
+ * copy achievements from another site or install.
  *
  * @return
- *   An array whose keys are internal achievement IDs and whose values
- *   identify properties of the achievement. These properties are:
- *   - id: (required) Internal ID of the achievement (32 character max.)
+ *   An array whose keys are internal achievement IDs (32 chars max) and whose
+ *   values identify properties of the achievement. These properties are:
  *   - title: (required) The title of the achievement.
  *   - description: (required) A description of the achievement.
  *   - points: (required) How many points the user will earn when unlocked.
- *   - hidden: (optional) The achievement is a sekrit until it is unlocked.
+ *   - images: (optional) An array of (optional) keys 'locked', 'unlocked',
+ *     and 'secret' whose values are image file paths. Achievements exist in
+ *     three separate display states: unlocked (the user has it), locked (the
+ *     user doesn't have it), and secret (the user doesn't have it, and the
+ *     achievement is a secret). Each state can have its own default image
+ *     associated with it (which administrators can configure), or achievements
+ *     can specify their own images for one, some, or all states.
+ *   - storage: (optional) If you store statistics for your achievement, the
+ *     core module assumes you've used the achievement ID for the storage
+ *     location. If you haven't, specify the storage location here. This lets
+ *     the core module know what to delete when an administrator manually
+ *     removes an achievement unlock from a user. If your achievement
+ *     tracks statistics that are NOT set with achievements_storage_get()
+ *     or _set, you don't have to define the 'storage' key.
+ *   - secret: (optional) The achievement displays on a user's Achievements
+ *     tab but does not reveal its title, description, or points until the
+ *     user has unlocked it. Compatible with 'invisible'.
+ *   - invisible: (optional) The achievement does NOT display on a user's
+ *     Achievements tab, but does show up on the leaderboards when necessary.
+ *     Compatible with 'secret' (i.e., if another user has unlocked an
+ *     invisible achievement, a user who has not unlocked it will see the
+ *     placeholder secret text instead of the actual achievement itself).
+ *
+ *   Achievements can also be categorized into groups. Groups are simply
+ *   arrays whose keys are internal group IDs and whose values identify
+ *   the 'title' of the group as well as the array of 'achievements' that
+ *   correspond to that group. If some achievements are within a group and
+ *   some achievements are without a group, the groupless achievements will
+ *   automatically be placed into a "Miscellany" category.
  */
 function hook_achievements_info() {
-  return array(
+  $achievements = array(
     'comment-count-50' => array(
-      'id'          => 'comment-count-50',
       'title'       => t('Posted 50 comments!'),
       'description' => t("We no longer think you're a spam bot. Maybe."),
+      'storage'     => 'comment-count',
       'points'      => 50,
     ),
     'comment-count-100' => array(
-      'id'          => 'comment-count-100',
       'title'       => t('Posted 100 comments!'),
       'description' => t('But what about the children?!'),
+      'storage'     => 'comment-count',
       'points'      => 100,
+      'images' => array(
+        'unlocked'  => 'sites/default/files/example1.png',
+        // 'secret' and 'locked' will use the defaults.
+      ),
     ),
-    'node-mondays-2' => array(
-      'id'          => 'node-mondays-2',
-      'title'       => t('Created a node on two separate Mondays.'),
-      'description' => t('Garfield hates you. Hates youuUUuUuu!'),
-      'points'      => 10,
+
+    // An example of achievement groups: 'article-creation' is the group ID,
+    // "Article creation" is the group title, and all relevant achievements are
+    // placed in an 'achievements' array. The ungrouped comment achievements
+    // above will be automatically pushed into a "Miscellany" group.
+    'article-creation' => array(
+      'title' => t('Article creation'),
+      'achievements' => array(
+        'node-mondays' => array(
+          'title'       => t('Published some content on a Monday'),
+          'description' => t("Go back to bed: it's still the weekend!"),
+          'points'      => 5,
+          'images' => array(
+            'unlocked'  => 'sites/default/files/example1.png',
+            'locked'    => 'sites/default/files/example2.png',
+            'secret'    => 'sites/default/files/example3.png',
+            // all default images have been replaced.
+          ),
+        ),
+      ),
     ),
   );
+
+  return $achievements;
 }
 
 /**
@@ -55,16 +110,18 @@ function example_comment_insert($comment) {
   // aggregated over time. To ease the storage of this data, the achievement
   // module ships with achievement_storage_get() and _set(), which allow you
   // to store custom data on a per-user basis. In most cases, the storage
-  // key is the same as your achievement ID but in situations where you have
-  // progressive achievements (10, 20, 50, 100 comments etc.), it's better
-  // to use a single key shared amongst them.
-
+  // location is the same as your achievement ID but in situations where you
+  // have progressive achievements (1, 2, 50 comments etc.), it's better to
+  // share a single place like we do below. If you don't use the achievement
+  // ID for the storage location, you must specify the new location in the
+  // 'storage' key of hook_achievements_info().
+  //
   // Here we're grabbing the number of comments that the current commenter has
   // left in the past (which might be 0), adding 1 (for the current insert),
   // and then saving the count back to the database. The saved data is
-  // serialized so can be as simple or as complex as you need it.
-  $current_count = achievements_storage_get('comment-count') + 1;
-  achievements_storage_set('comment-count', $current_count);
+  // serialized so can be as simple or as complex as you need it to be.
+  $current_count = achievements_storage_get('comment-count', $comment->uid) + 1;
+  achievements_storage_set('comment-count', $current_count, $comment->uid);
 
   // Note that we're not checking if the user has previously earned any of the
   // commenting achievements yet. There are two reasons: first, we might want
@@ -76,17 +133,16 @@ function example_comment_insert($comment) {
   // after the achievement has been unlocked - you never know if you'll want
   // to add a future milestone that will unlock on higher increments.
   //
-  // Secondly, the achievements_unlocked() function below will automatically
-  // check if the user has unlocked the achievement already, and will not
-  // unlock it again if they have. This saves you a small bit of repetitive
-  // coding but you're welcome to use achievements_unlocked_already() should
-  // the need every arise.
+  // Secondly, the achievements_unlocked() function below automatically checks
+  // if the user has unlocked the achievement already, and will not reward it
+  // again if they have. This saves you a small bit of repetitive coding but
+  // you're welcome to use achievements_unlocked_already() as needed.
   //
   // Knowing that we currently have 50 and 100 comment achievements, we simply
   // loop through each milestone and check if the current count value matches.
   foreach (array(50, 100) as $count) {
     if ($current_count == $count) {
-      achievements_unlocked('comment-count-' . $count);
+      achievements_unlocked('comment-count-' . $count, $comment->uid);
     }
   }
 }
@@ -95,16 +151,127 @@ function example_comment_insert($comment) {
  * Implements hook_node_insert().
  */
 function example_node_insert($node) {
-  // This is very similar to the comment counting achievement, but shows
-  // storage of an array and a slightly different unlocking check. Yawn.
+  // Sometimes, we don't need any storage at all.
   if (format_date(REQUEST_TIME, 'custom', 'D') == 'Mon') {
-    $current_mondays = achievements_storage_get('node-mondays');
-    $current_mondays = is_array($current_mondays) ? $current_mondays : array();
-    $current_mondays[format_date(REQUEST_TIME, 'custom' 'Y-m-d')] = 1;
-    achievements_storage_set('node-mondays', $current_mondays);
+    achievements_unlocked('node-mondays', $node->uid);
+  }
+}
 
-    if (count($current_mondays) == 2) {
-      achievements_unlocked('node-mondays-2');
-    }
+/**
+ * Implements hook_achievements_info_alter().
+ *
+ * Modify achievements that have been defined in hook_achievements_info().
+ * Note that achievement info is cached so if you add or modify this hook,
+ * also clear said achievement cache in admin/config/people/achievements.
+ *
+ * @param &$achievements
+ *   An array of defined achievements returned by hook_achievements_info().
+ */
+function example_achievements_info_alter(&$achievements) {
+  $achievements['comment-count-100']['points'] = 200;
+}
+
+/**
+ * Implements hook_achievements_unlocked().
+ *
+ * This hook is invoked after an achievement has been unlocked and all
+ * the relevant information has been stored or updated in the database.
+ *
+ * @param $achievement
+ *  An array of information about the achievement.
+ * @param $uid
+ *  The user ID who has unlocked the achievement.
+ */
+function example_achievements_unlocked($achievement, $uid) {
+  // post to twitter or facebook, unlock an additional reward, etc., etc.
+}
+
+/**
+ * Implements hook_achievements_locked().
+ *
+ * This hook is invoked after an achievement has been removed from a user and
+ * all relevant information has been stored or updated in the database. This
+ * is currently only possible from the UI at admin/config/people/achievements.
+ *
+ * @param $achievement
+ *  An array of information about the achievement.
+ * @param $uid
+ *  The user ID who is having the achievement taken away.
+ */
+function example_achievements_locked($achievement, $uid) {
+  // react to achievement removal. bad user, BaAaDdd UUserrRR!
+}
+
+/**
+ * Implements hook_achievements_leaderboard_alter().
+ *
+ * Allows you to tweak or even recreate the leaderboard as required. The
+ * default implementation creates leaderboards as HTML tables and this hook
+ * allows you to modify that table (new columns, tweaked values, etc.) or
+ * replace it entirely with a new render element.
+ *
+ * @param &$leaderboard
+ *   An array of information about the leaderboard. Available keys are:
+ *   - achievers: The database results from the leaderboard queries.
+ *     Results are keyed by leaderboard type (top, relative, first, and
+ *     recent) and then by user ID, sorted in proper ranking order.
+ *   - block: A boolean indicating whether this is a block-based leaderboard.
+ *   - type: The type of leaderboard being displayed. One of: top (the overall
+ *     leaderboard displayed on achievements/leaderboard), relative (the
+ *     current-user-centric version with nearby ranks), first (the first users
+ *     who unlocked a particular achievement), and recent (the most recent
+ *     users who unlocked a particular achievement).
+ *   - render: A render array for use with drupal_render(). Default rendering
+ *     is with #theme => table, and you'll receive all the keys necessary
+ *     for that implementation. You're welcome to insert your own unique
+ *     render, bypassing the default entirely.
+ */
+function example_achievements_leaderboard_alter(&$leaderboard) {
+  if ($leaderboard['type'] == 'first') {
+    $leaderboard['render']['#caption'] = t('Congratulations to our first 10!');
+  }
+}
+
+/**
+ * Implements hook_query_alter().
+ *
+ * The following database tags have been created for hook_query_alter() and
+ * the matching hook_query_TAG_alter(). If you need more than this, don't
+ * hesitate to create an issue asking for them.
+ *
+ * achievement_totals:
+ *   Find the totals of all users in ranking order.
+ *
+ * achievement_totals_user:
+ *   Find the totals of the passed user.
+ *
+ * achievement_totals_user_nearby:
+ *   Find users nearby the ranking of the passed user.
+ */
+function example_query_alter(QueryAlterableInterface $query) {
+  // futz with morbus' logic. insert explosions and singularities.
+}
+
+/**
+ * Implements hook_achievements_access_earn().
+ *
+ * Allows you to programmatically determine if a user has access to earn
+ * achievements. We do already have an "earn achievements" permission, but
+ * this allows more complex methods of determining that privilege. For an
+ * example, see the achievements_optout.module, which allows a user to opt-out
+ * of earning achievements, even if you've already granted them permission to.
+ *
+ * @param $uid
+ *   The user ID whose access is being questioned.
+ *
+ * @return
+ *   TRUE if the $uid can earn achievements, FALSE if they can't,
+ *   or NULL if there's no change to the user's default access.
+ */
+function example_achievements_access_earn($uid) {
+  $account = user_load($uid);
+  if ($account->name == 'Morbus Iff') {
+    // always, mastah, alllwayyYAYsss.
+    return TRUE;
   }
-}
\ No newline at end of file
+}