Issue #1820556: Don't show achievement popups if the overlay is active.
[project/achievements.git] / achievements.api.php
1 <?php
2
3 /**
4 * @file
5 * Hooks provided by the Achievements module and how to implement them.
6 */
7
8 /**
9 * Define an achievement.
10 *
11 * This hook describes your achievements to the base module so that it can
12 * create the pages and caches necessary for site-wide display. The base
13 * module doesn't know how to unlock any of your achievements... instead, you
14 * use Drupal's existing hooks, the achievement storage tables, and a few
15 * helper functions to complete the workflow. See the remaining documentation
16 * in this file for further code samples.
17 *
18 * There are many different kinds of achievements, and it's accurate enough to
19 * say that if you can measure or respond to an action, it can be made into a
20 * matching achievement. Be creative. Look at what others are doing. Have fun.
21 * Your gamification efforts will fail or be un-fun if you don't have a gamer
22 * helping you, if you make everything a mindless grind, or if you simply
23 * copy achievements from another site or install.
24 *
25 * @return
26 * An array whose keys are internal achievement IDs (32 chars max) and whose
27 * values identify properties of the achievement. These properties are:
28 * - title: (required) The title of the achievement.
29 * - description: (required) A description of the achievement.
30 * - points: (required) How many points the user will earn when unlocked.
31 * - images: (optional) An array of (optional) keys 'locked', 'unlocked',
32 * and 'secret' whose values are image file paths. Achievements exist in
33 * three separate display states: unlocked (the user has it), locked (the
34 * user doesn't have it), and secret (the user doesn't have it, and the
35 * achievement is a secret). Each state can have its own default image
36 * associated with it (which administrators can configure), or achievements
37 * can specify their own images for one, some, or all states.
38 * - storage: (optional) If you store statistics for your achievement, the
39 * core module assumes you've used the achievement ID for the storage
40 * location. If you haven't, specify the storage location here. This lets
41 * the core module know what to delete when an administrator manually
42 * removes an achievement unlock from a user. If your achievement
43 * tracks statistics that are NOT set with achievements_storage_get()
44 * or _set, you don't have to define the 'storage' key.
45 * - secret: (optional) The achievement displays on a user's Achievements
46 * tab but does not reveal its title, description, or points until the
47 * user has unlocked it. Compatible with 'invisible'.
48 * - invisible: (optional) The achievement does NOT display on a user's
49 * Achievements tab, but does show up on the leaderboards when necessary.
50 * Compatible with 'secret' (i.e., if another user has unlocked an
51 * invisible achievement, a user who has not unlocked it will see the
52 * placeholder secret text instead of the actual achievement itself).
53 *
54 * Achievements can also be categorized into groups. Groups are simply
55 * arrays whose keys are internal group IDs and whose values identify
56 * the 'title' of the group as well as the array of 'achievements' that
57 * correspond to that group. If some achievements are within a group and
58 * some achievements are without a group, the groupless achievements will
59 * automatically be placed into a "Miscellany" category.
60 */
61 function hook_achievements_info() {
62 $achievements = array(
63 'comment-count-50' => array(
64 'title' => t('Posted 50 comments!'),
65 'description' => t("We no longer think you're a spam bot. Maybe."),
66 'storage' => 'comment-count',
67 'points' => 50,
68 ),
69 'comment-count-100' => array(
70 'title' => t('Posted 100 comments!'),
71 'description' => t('But what about the children?!'),
72 'storage' => 'comment-count',
73 'points' => 100,
74 'images' => array(
75 'unlocked' => 'sites/default/files/example1.png',
76 // 'secret' and 'locked' will use the defaults.
77 ),
78 ),
79
80 // An example of achievement groups: 'article-creation' is the group ID,
81 // "Article creation" is the group title, and all relevant achievements are
82 // placed in an 'achievements' array. The ungrouped comment achievements
83 // above will be automatically pushed into a "Miscellany" group.
84 'article-creation' => array(
85 'title' => t('Article creation'),
86 'achievements' => array(
87 'node-mondays' => array(
88 'title' => t('Published some content on a Monday'),
89 'description' => t("Go back to bed: it's still the weekend!"),
90 'points' => 5,
91 'images' => array(
92 'unlocked' => 'sites/default/files/example1.png',
93 'locked' => 'sites/default/files/example2.png',
94 'secret' => 'sites/default/files/example3.png',
95 // all default images have been replaced.
96 ),
97 ),
98 ),
99 ),
100 );
101
102 return $achievements;
103 }
104
105 /**
106 * Implements hook_comment_insert().
107 */
108 function example_comment_insert($comment) {
109 // Most achievements measure some kind of statistical data that must be
110 // aggregated over time. To ease the storage of this data, the achievement
111 // module ships with achievement_storage_get() and _set(), which allow you
112 // to store custom data on a per-user basis. In most cases, the storage
113 // location is the same as your achievement ID but in situations where you
114 // have progressive achievements (1, 2, 50 comments etc.), it's better to
115 // share a single place like we do below. If you don't use the achievement
116 // ID for the storage location, you must specify the new location in the
117 // 'storage' key of hook_achievements_info().
118 //
119 // Here we're grabbing the number of comments that the current commenter has
120 // left in the past (which might be 0), adding 1 (for the current insert),
121 // and then saving the count back to the database. The saved data is
122 // serialized so can be as simple or as complex as you need it to be.
123 $current_count = achievements_storage_get('comment-count', $comment->uid) + 1;
124 achievements_storage_set('comment-count', $current_count, $comment->uid);
125
126 // Note that we're not checking if the user has previously earned any of the
127 // commenting achievements yet. There are two reasons: first, we might want
128 // to add another commenting achievement for, say, 250 comments, and if we
129 // had stopped the storage counter above at 100, someone who currently has
130 // 300 comments wouldn't unlock the achievement until they added another 150
131 // nuggets of wisdom to the site. Generally speaking, if you need to store
132 // incremental data for an achievement, you should continue to store it even
133 // after the achievement has been unlocked - you never know if you'll want
134 // to add a future milestone that will unlock on higher increments.
135 //
136 // Secondly, the achievements_unlocked() function below automatically checks
137 // if the user has unlocked the achievement already, and will not reward it
138 // again if they have. This saves you a small bit of repetitive coding but
139 // you're welcome to use achievements_unlocked_already() as needed.
140 //
141 // Knowing that we currently have 50 and 100 comment achievements, we simply
142 // loop through each milestone and check if the current count value matches.
143 foreach (array(50, 100) as $count) {
144 if ($current_count == $count) {
145 achievements_unlocked('comment-count-' . $count, $comment->uid);
146 }
147 }
148 }
149
150 /**
151 * Implements hook_node_insert().
152 */
153 function example_node_insert($node) {
154 // Sometimes, we don't need any storage at all.
155 if (format_date(REQUEST_TIME, 'custom', 'D') == 'Mon') {
156 achievements_unlocked('node-mondays', $node->uid);
157 }
158 }
159
160 /**
161 * Implements hook_achievements_info_alter().
162 *
163 * Modify achievements that have been defined in hook_achievements_info().
164 * Note that achievement info is cached so if you add or modify this hook,
165 * also clear said achievement cache in admin/config/people/achievements.
166 *
167 * @param &$achievements
168 * An array of defined achievements returned by hook_achievements_info().
169 */
170 function example_achievements_info_alter(&$achievements) {
171 $achievements['comment-count-100']['points'] = 200;
172 }
173
174 /**
175 * Implements hook_achievements_unlocked().
176 *
177 * This hook is invoked after an achievement has been unlocked and all
178 * the relevant information has been stored or updated in the database.
179 *
180 * @param $achievement
181 * An array of information about the achievement.
182 * @param $uid
183 * The user ID who has unlocked the achievement.
184 */
185 function example_achievements_unlocked($achievement, $uid) {
186 // post to twitter or facebook, unlock an additional reward, etc., etc.
187 }
188
189 /**
190 * Implements hook_achievements_locked().
191 *
192 * This hook is invoked after an achievement has been removed from a user and
193 * all relevant information has been stored or updated in the database. This
194 * is currently only possible from the UI at admin/config/people/achievements.
195 *
196 * @param $achievement
197 * An array of information about the achievement.
198 * @param $uid
199 * The user ID who is having the achievement taken away.
200 */
201 function example_achievements_locked($achievement, $uid) {
202 // react to achievement removal. bad user, BaAaDdd UUserrRR!
203 }
204
205 /**
206 * Implements hook_achievements_leaderboard_alter().
207 *
208 * Allows you to tweak or even recreate the leaderboard as required. The
209 * default implementation creates leaderboards as HTML tables and this hook
210 * allows you to modify that table (new columns, tweaked values, etc.) or
211 * replace it entirely with a new render element.
212 *
213 * @param &$leaderboard
214 * An array of information about the leaderboard. Available keys are:
215 * - achievers: The database results from the leaderboard queries.
216 * Results are keyed by leaderboard type (top, relative, first, and
217 * recent) and then by user ID, sorted in proper ranking order.
218 * - block: A boolean indicating whether this is a block-based leaderboard.
219 * - type: The type of leaderboard being displayed. One of: top (the overall
220 * leaderboard displayed on achievements/leaderboard), relative (the
221 * current-user-centric version with nearby ranks), first (the first users
222 * who unlocked a particular achievement), and recent (the most recent
223 * users who unlocked a particular achievement).
224 * - render: A render array for use with drupal_render(). Default rendering
225 * is with #theme => table, and you'll receive all the keys necessary
226 * for that implementation. You're welcome to insert your own unique
227 * render, bypassing the default entirely.
228 */
229 function example_achievements_leaderboard_alter(&$leaderboard) {
230 if ($leaderboard['type'] == 'first') {
231 $leaderboard['render']['#caption'] = t('Congratulations to our first 10!');
232 }
233 }
234
235 /**
236 * Implements hook_query_alter().
237 *
238 * The following database tags have been created for hook_query_alter() and
239 * the matching hook_query_TAG_alter(). If you need more than this, don't
240 * hesitate to create an issue asking for them.
241 *
242 * achievement_totals:
243 * Find the totals of all users in ranking order.
244 *
245 * achievement_totals_user:
246 * Find the totals of the passed user.
247 *
248 * achievement_totals_user_nearby:
249 * Find users nearby the ranking of the passed user.
250 */
251 function example_query_alter(QueryAlterableInterface $query) {
252 // futz with morbus' logic. insert explosions and singularities.
253 }
254
255 /**
256 * Implements hook_achievements_access_earn().
257 *
258 * Allows you to programmatically determine if a user has access to earn
259 * achievements. We do already have an "earn achievements" permission, but
260 * this allows more complex methods of determining that privilege. For an
261 * example, see the achievements_optout.module, which allows a user to opt-out
262 * of earning achievements, even if you've already granted them permission to.
263 *
264 * @param $uid
265 * The user ID whose access is being questioned.
266 *
267 * @return
268 * TRUE if the $uid can earn achievements, FALSE if they can't,
269 * or NULL if there's no change to the user's default access.
270 */
271 function example_achievements_access_earn($uid) {
272 $account = user_load($uid);
273 if ($account->name == 'Morbus Iff') {
274 // always, mastah, alllwayyYAYsss.
275 return TRUE;
276 }
277 }