now supporting page tab similar to canvas front page.
[project/fb.git] / fb_tab.module
1 <?php
2 /**
3 * @file
4 *
5 * This module provides support for "Profile Tabs" that can be added to
6 * facebook pages (no longer allowed for user profiles).
7 *
8 * http://developers.facebook.com/docs/guides/canvas/#tabs
9 *
10 */
11 // hook_fb
12 define('FB_TAB_HOOK', 'fb_tab');
13
14 //// Hook_fb_tab operations
15 define('FB_TAB_OP_VIEW', 'fb_tab_view');
16 define('FB_TAB_OP_FORM', 'fb_tab_form');
17
18 define('FB_TAB_PATH_VIEW', 'fb_tab/view');
19 define('FB_TAB_PATH_FORM', 'fb_tab/config');
20 define('FB_TAB_PATH_FORM_ARGS', 2);
21
22 define('FB_TAB_VAR_PROCESS_FBML', 'fb_tab_process_fbml');
23 define('FB_TAB_VAR_PROCESS_IFRAME', 'fb_tab_process_iframe');
24 define('FB_TAB_VAR_PROCESS_ABSOLUTE', 'fb_tab_process_absolute_links');
25 define('FB_TAB_VAR_PROCESS_TO_CANVAS', 'fb_tab_process_to_canvas');
26
27 function fb_tab_menu() {
28 $items = array();
29
30 $items[FB_TAB_PATH_VIEW] = array(
31 'page callback' => 'fb_tab_view',
32 'access arguments' => array('access content'),
33 'type' => MENU_CALLBACK,
34 );
35
36 $items[FB_TAB_PATH_FORM] = array(
37 'title' => 'Configure Tabs',
38 'page callback' => 'fb_tab_pages',
39 'access arguments' => array('access content'),
40 'type' => MENU_CALLBACK,
41 );
42
43 $items[FB_TAB_PATH_FORM . '/%'] = array(
44 'page callback' => 'drupal_get_form',
45 'page arguments' => array('fb_tab_config_form', FB_TAB_PATH_FORM_ARGS),
46 'access arguments' => array('access content'),
47 'type' => MENU_CALLBACK,
48 );
49
50 // Admin pages
51 $items[FB_PATH_ADMIN .'/fb_tab'] = array(
52 'title' => 'Page Tabs',
53 'description' => 'Configure Tabs',
54 'page callback' => 'drupal_get_form',
55 'page arguments' => array('fb_tab_admin_settings'),
56 'access arguments' => array(FB_PERM_ADMINISTER),
57 'file' => 'fb_tab.admin.inc',
58 'type' => MENU_LOCAL_TASK,
59 );
60
61 return $items;
62 }
63
64 /**
65 * Implementation of hook_fb
66 *
67 * @param $op
68 * @param $data -- data payload
69 * @param $return
70 */
71 function fb_tab_fb($op, $data, &$return) {
72 $fb = isset($data['fb']) ? $data['fb'] : NULL;
73 $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL;
74
75 if ($op == FB_OP_POST_INIT) {
76 // Include our admin hooks.
77 if (fb_is_fb_admin_page()) {
78 require drupal_get_path('module', 'fb_tab') . '/fb_tab.admin.inc';
79 }
80
81 if (fb_is_tab()) {
82 // Include our javascript.
83 drupal_add_js(array(
84 'fb_tab' => array(
85 'fbu' => fb_facebook_user(),
86 'uid' => $GLOBALS['user']->uid,
87 'canvas' => $fb_app->canvas,
88 ),
89 ), 'setting');
90 drupal_add_js(drupal_get_path('module', 'fb_tab') . '/fb_tab.js');
91 }
92 }
93 elseif ($op == FB_OP_CURRENT_APP && fb_is_tab()) {
94 if ($id = fb_settings(FB_SETTINGS_CB)) {
95 // Using fb_url_rewrite.
96 $fb_app = fb_get_app(array('id' => $id));
97
98 if (!$fb_app) {
99 // DEPRECATED. For backward compatibility, accept apikey in FB_SETTINGS_CB
100 $fb_app = fb_get_app(array('apikey' => $id));
101 }
102
103 }
104 elseif ($id = fb_settings(FB_SETTINGS_ID)) {
105 // New SDK includes ID when session is present.
106 $fb_app = fb_get_app(array('id' => $id));
107 }
108
109 // Old, deprecated, FBML tabs.
110 elseif (isset($_REQUEST['fb_sig_api_key'])) {
111 $return = fb_get_app(array('apikey' => $_REQUEST['fb_sig_api_key']));
112 }
113
114 if ($fb_app) {
115 $return = $fb_app;
116 }
117 }
118 elseif ($op == FB_OP_INITIALIZE) {
119 if (fb_is_tab()) {
120 $config = _fb_tab_get_config($fb_app);
121 if (!isset($GLOBALS['custom_theme'])) {
122 $GLOBALS['custom_theme'] = $config['custom_theme'];
123 }
124 if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB &&
125 variable_get(FB_TAB_VAR_PROCESS_IFRAME, TRUE)) {
126 // Process iframe
127 $use_ob = TRUE;
128 }
129 elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE &&
130 variable_get(FB_TAB_VAR_PROCESS_FBML, TRUE)) {
131 // Process FBML
132 $use_ob = TRUE;
133 }
134 else {
135 $use_ob = FALSE;
136 }
137
138 // Hack to init the theme before _drupal_maintenance_theme initializes the wrong one.
139 if (variable_get('site_offline', FALSE)) {
140 $dummy = theme('dummy');
141 }
142
143 // Store entire page in output buffer. Will post-process on exit.
144 if ($use_ob) {
145 ob_start();
146 $GLOBALS['fb_tab_post_process'] = TRUE;
147 }
148
149 // Override what Drupal renders.
150 if ($_REQUEST['q'] == FB_TAB_PATH_VIEW) { // Do not override fb_tab/config.
151 $sr = $fb->getSignedRequest();
152 if ($sr['page'] && $sr['page']['liked'] && $config['profile_tab_url_liked']) {
153 $path = $config['profile_tab_url_liked'];
154 }
155 else {
156 $path = $config['profile_tab_url'];
157 }
158 if ($path && $path != FB_TAB_PATH_VIEW) {
159 // Tell Drupal what to really render.
160 menu_set_active_item(drupal_get_normal_path($path));
161 }
162 }
163
164 }
165 }
166 elseif ($op == FB_OP_EXIT && fb_is_tab()) {
167 if (isset($GLOBALS['fb_tab_post_process']) && $GLOBALS['fb_tab_post_process']) {
168 $output = ob_get_contents();
169 ob_end_clean();
170 include_once(drupal_get_path('module', 'fb') . '/fb.process.inc');
171
172 if (variable_get(FB_TAB_VAR_PROCESS_TO_CANVAS, TRUE)) {
173 $to_canvas = $fb_app->canvas;
174 }
175 else {
176 $to_canvas = FALSE;
177 }
178
179 if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) {
180 // Process iframe
181 $output = fb_process($output, array(
182 'add_target' => '_top',
183 'absolute_links' => TRUE,
184 'to_canvas' => $to_canvas,
185 ));
186 }
187 elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) {
188 // Process FBML (deprecated)
189 $output = fb_process($output, array(
190 'add_target' => FALSE,
191 'absolute_links' => TRUE,
192 'to_canvas' => $to_canvas,
193 ));
194 }
195 if (isset($output)) {
196 print($output);
197 }
198 }
199 }
200 }
201
202 /**
203 * Implements fb_tab_form_alter.
204 */
205 function fb_tab_form_alter(&$form, &$form_state, $form_id) {
206 if (isset($form['fb_app_data']) && is_array($form['fb_app_data'])) {
207 // Add our settings to the fb_app edit form.
208 //require 'fb_canvas.admin.inc';
209 fb_tab_admin_form_alter($form, $form_state, $form_id);
210 }
211 }
212
213 /**
214 * Helper returns configuration for this module, on a per-app basis.
215 */
216 function _fb_tab_get_config($fb_app) {
217 $fb_app_data = fb_get_app_data($fb_app);
218 $config = isset($fb_app_data['fb_tab']) ? $fb_app_data['fb_tab'] : array();
219
220 // Merge in defaults
221 $config += array(
222 'custom_theme' => NULL,
223 'tab_default_name' => isset($fb_app->title) ? $fb_app->title : NULL,
224 'profile_tab_url' => NULL,
225 'profile_tab_url_liked' => NULL,
226 'edit_url' => FB_TAB_PATH_FORM,
227 );
228 return $config;
229 }
230
231 /**
232 * Another module provides the contents of the tab depending
233 * on the context that we give it (who is calling it, etc.)
234 */
235 function fb_tab_view() {
236 $fb_app = $GLOBALS['_fb_app'];
237 $fb = $GLOBALS['_fb'];
238
239 $profile_id = fb_settings(FB_SETTINGS_CB_PAGE);
240
241 $config = fb_tab_fetch_config($fb_app, $profile_id);
242
243 $sr = $fb->getSignedRequest();
244
245 $data = array(
246 'fb_app' => $fb_app,
247 'fb' => $fb,
248 'profile_id' => $profile_id,
249 'config' => isset($config['data']) ? $config['data'] : NULL,
250 'page' => $sr['page'],
251 );
252
253 $content = fb_invoke(FB_TAB_OP_VIEW, $data, array(), FB_TAB_HOOK);
254
255 return drupal_render($content);
256 }
257
258 /**
259 * Build the tab config form. Invokes hook_fb_tab() to get the custom settings.
260 */
261 function fb_tab_config_form($form_state, $profile_id) {
262 $fb_app = $GLOBALS['_fb_app'];
263 $fb = $GLOBALS['_fb'];
264
265 $config = fb_tab_fetch_config($fb_app, $profile_id);
266 $sr = $fb->getSignedRequest();
267
268 $data = array(
269 'fb_app' => $fb_app,
270 'fb' => $fb,
271 'profile_id' => $profile_id,
272 'config' => isset($config['data']) ? $config['data'] : NULL,
273 'page' => $sr['page'],
274 );
275
276 try {
277 //$fbu = fb_facebook_user($fb);
278 $fbu = fb_require_authorization($fb);
279 $page_info = $fb->api($profile_id, array(
280 'access_token' => fb_get_token($fb),
281 ));
282 $admin_info = fb_fql_query($fb, "SELECT uid, type FROM page_admin WHERE uid=$fbu AND page_id=$profile_id"); // FQL not SQL, no {curly_brackets}
283 //dpm($admin_info, __FUNCTION__);
284
285 // @TODO: if not page admin, prompt user to login or deny access.
286
287 $form = array(
288 'info' => array(
289 '#type' => 'markup',
290 '#value' => t('Tab settings for <a href="!href">%page</a>', array(
291 '!href' => $page_info['link'],
292 '%page' => $page_info['name'],
293 )),
294 '#prefix' => '<h3>',
295 '#suffix' => '</h3>',
296 '#weight' => -99,
297 ),
298 'label' => array(
299 '#type' => 'value',
300 '#value' => $fb_app->label,
301 ),
302 'profile_id' => array(
303 '#type' => 'value',
304 '#value' => $profile_id,
305 ),
306 'data' => array(
307 // Modules will add their fields here.
308 '#tree' => TRUE,
309 ),
310 'submit' => array(
311 '#type' => 'submit',
312 '#value' => t('Submit'),
313 '#weight' => 90,
314 ),
315 );
316
317 if (isset($config['fb_tab_id'])) {
318 $form['fb_tab_id'] = array(
319 '#type' => 'value',
320 '#value' => $config['fb_tab_id'],
321 );
322 }
323
324 $form['data'] = fb_invoke(FB_TAB_OP_FORM, $data, $form['data'], FB_TAB_HOOK);
325
326 //print('<pre>' . print_r($data, 1) . '</pre>'); flush();
327 //print(print_r($content, 1)); flush();
328
329 return $form;
330 }
331 catch (Exception $e) {
332 fb_log_exception($e, t('Failed to get details for facebook page %profile_id', array(
333 '%profile_id' => $profile_id,
334 )));
335 return array(
336 'error' => array(
337 '#value' => t('Unable to set properties.'),
338 ),
339 );
340 }
341 }
342
343 /**
344 * Submit handler for tab config form.
345 */
346 function fb_tab_config_form_submit($form, &$form_state) {
347 if ($profile_id = $form_state['values']['profile_id']) {
348 fb_tab_write_config($form_state['values']);
349 $data = fb_fql_query($GLOBALS['_fb'], "SELECT page_id, name, pic_small, page_url, type FROM page WHERE page_id=$profile_id"); // FQL, no {curly_brackets}
350 drupal_set_message(t('The tab settings for <a href="!url">%page</a> have been updated.', array(
351 '!url' => $data[0]['page_url'],
352 '%page' => $data[0]['name'],
353 )));
354 }
355 }
356
357 /*
358 * Store configuration data for a tab by application.
359 */
360 function fb_tab_write_config(&$row) {
361 if (!isset($row['created']))
362 $row['created'] = time();
363 $row['data'] = serialize($row['data']);
364 return drupal_write_record('fb_tab', $row,
365 isset($row['fb_tab_id']) ? array('fb_tab_id') : NULL);
366 }
367
368 /*
369 * Return configuration of a tab by application (access via 'label') and page ID.
370 * Page ID exists because an app can present different views on different pages.
371 */
372 function fb_tab_fetch_config($fb_app, $profile_id) {
373 $result = db_query("SELECT * FROM {fb_tab} WHERE label = '%s' AND profile_id = %d" , $fb_app->label, $profile_id);
374
375 $row = db_fetch_array($result);
376 if ($row) {
377 $row['data'] = unserialize($row['data']);
378 return $row;
379 }
380 else {
381 return array();
382 }
383 }
384
385 /**
386 * This page callback will show the user a list of pages they have authority
387 * to configure.
388 */
389 function fb_tab_pages() {
390 if ($fbu = fb_facebook_user()) {
391
392 // @TODO: broken on facebook's end??? this query used to work.
393 $result = fb_fql_query($GLOBALS['_fb'], "SELECT page_id, name, pic_small, page_url, type FROM page WHERE has_added_app=1 AND page_id IN (SELECT page_id FROM page_admin WHERE uid=$fbu)"); // FQL, no {curly_brackets}
394
395 if (count($result)) {
396 $output['pages'] = array(
397 '#prefix' => '<ul>',
398 '#suffix' => '</ul>',
399 );
400 foreach ($result as $data) {
401 $output['pages'][$data['page_id']] = array(
402 '#value' => l($data['name'], fb_tab_config_url($data['page_id'], array('fb_canvas' => fb_is_canvas()))),
403 '#prefix' => '<li>',
404 '#suffix' => '</li>',
405 );
406 }
407 $output['intro'] = array(
408 '#value' => t('Select one of your pages to configure:'),
409 '#prefix' => '<p>',
410 '#suffix' => '</p>',
411 );
412 }
413 else {
414 $output['intro'] = array(
415 '#value' => t('Found no pages to configure.'),
416 '#prefix' => '<p>',
417 '#suffix' => '</p>',
418 );
419 }
420 return drupal_render($output);
421 }
422 else {
423 fb_access_denied();
424 }
425 }
426
427
428 /**
429 * Provides the URL of the settings page for a given facebook page.
430 */
431 function fb_tab_config_url($profile_id = NULL, $options = array()) {
432 if (!$profile_id) {
433 $profile_id = fb_settings(FB_SETTINGS_CB_PAGE);
434 }
435 return url(FB_TAB_PATH_FORM . '/' . $profile_id, $options);
436 }
437