Newer
Older
* @file
* Drupal Module: RecommenderGhost
* Adds the required Javascript to the bottom of all your Drupal pages
* to allow tracking by the RecommenderGhost recommendation engine.
* Adds 2 blocks to display recommendations on a node level
* - list view
* - picture view
* JS Tracking part of this module is based on the Google Analytics module code.
// Remove tracking from all administrative pages,
// see http://drupal.org/node/34970.
define('RECOMMENDERGHOST_PAGES', "admin\nadmin/*\nbatch\nnode/add*\nnode/*/*\nuser/*/*");
// Define global API URL.
define('RECOMMENDERGHOST_API_URL', "api.recommenderghost.com");
/**
* Implements hook_help().
*/
function recommenderghost_help($path, $arg) {
switch ($path) {
case 'admin/config/system/recommenderghost':
return t('<a href="@rg_url">RecommenderGhost</a> is a free (registration required) service that allows you to add a recommender system to your website.',
array('@rg_url' => 'http://www.recommenderghost.com'));
}
}
/**
* Implements hook_permission().
*/
function recommenderghost_permission() {
return array(
'administer recommenderghost' => array(
'title' => t('Administer RecommenderGhost'),
'description' => t('Perform maintenance tasks for RecommenderGhost.'),
'restrict access' => TRUE,
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
),
'use PHP for recommenderghost' => array(
'title' => t('Use PHP for tracking visibility'),
'description' => t('Enter PHP code in the field for tracking visibility settings.'),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_menu().
*/
function recommenderghost_menu() {
$items['admin/config/system/recommenderghost'] = array(
'title' => 'RecommenderGhost',
'description' => 'Configure tracking and recommendation engine for RecommenderGhost.',
'page callback' => 'drupal_get_form',
'page arguments' => array('recommenderghost_admin_settings_form'),
'access arguments' => array('administer recommenderghost'),
'type' => MENU_NORMAL_ITEM,
'file' => 'recommenderghost.admin.inc',
);
return $items;
}
/**
* Implements hook_field_extra_fields().
*/
function recommenderghost_field_extra_fields() {
$extra['user']['user']['form']['recommenderghost'] = array(
'label' => t('RecommenderGhost configuration'),
'description' => t('RecommenderGhost module form element.'),
'weight' => 3,
);
return $extra;
}
/**
* Implements hook_page_alter().
*
* Inserts JavaScript to the appropriate scope/region of the page.
*/
function recommenderghost_page_alter(&$page) {
global $user;
hhhc
committed
// Following privacy policy of RecommenderGhost we need to
// encode user ids into non-revertable formatOutput.
$uid = sha1($user->uid);
$apikey = variable_get('recommenderghost_apikey', '');
$websiteid = variable_get('recommenderghost_websiteid', '');
$is_node = FALSE;
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
$is_node = TRUE;
}
// 1. Check that api key and website id have a value.
// 2. Track page views based on visibility value.
// 3. Check if we should track the currently active user's role.
if ($is_node && !empty($apikey) && !empty($websiteid) && _recommenderghost_visibility_pages() && _recommenderghost_visibility_user($user) && _recommenderghost_visibility_content_types($node)) {
// If node view then define the node variables we want to track.
if ($is_node) {
// Get node id and site URL.
$item_id = $node->nid;
$item_url = $_SERVER["REQUEST_URI"];
$title = $node->title;
if (!$title) {
$title = "";
$img_field = variable_get('recommenderghost_image_token_field', '');
$path = token_replace($img_field, array('node' => $node));
// Filter unchanged tokens.
$clean = preg_replace('/\[(.*)\]/is', "", $path);
$clean_array = explode(",", $clean);
// Only use relative paths as recommended.
$url = parse_url(trim($clean_array[0]));
$item_image_url = $url['path'];
// We allow different methods. Default to 'JS' but
// allow users to override if they really need to.
$method = variable_get('recommenderghost_tracking_method', 'JS');
$mapping = variable_get('recommenderghost_mapping_' . $node->type, 'ITEM');
// Check which method (REST or JS) to use for tracking.
// REST will only work if no caching (eg Varnish, Boost) will be used.
if ($method == "REST" && $is_node) {
$recommendations = recommenderghost_call_api("view", array(
"itemid" => $item_id,
"itemdescription" => $title,
"itemurl" => $item_url,
"itemimageurl" => $item_image_url,
"sessionid" => session_id(),
"itemtype" => $mapping,
}
else {
// We allow different scopes for JS. Default to 'header' but allow user
// to override if they really need to.
$scope = variable_get('recommenderghost_js_scope', 'header');
// Add any custom code snippets if specified.
$codesnippet_before = variable_get('recommenderghost_codesnippet_before', '');
$custom_callback = variable_get('recommenderghost_drawingcallback', '');
// Build tracker code.
$script_tracker = 'var apiKey = ' . drupal_json_encode($apikey) . ';';
$script_tracker .= 'var tenantId = ' . drupal_json_encode($websiteid) . ';';
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
$script_tracker .= 'var easyrecApiUrl="https://' . RECOMMENDERGHOST_API_URL . '/api/1.0/json/";';
}
$script = "";
if (!empty($custom_callback)) {
$script .= $custom_callback;
}
if ($is_node) {
$script .= "
easyrec_sendAction('view',{
itemId:'" . $item_id . "',
itemUrl:'" . check_url($item_url) . "',
itemDescription: '" . $title . "',";
if ($item_image_url) {
$script .= "
itemImageUrl:'" . check_url($item_image_url) . "',";
}
if ($uid) {
$script .= "
hhhc
committed
userId:'" . $uid . "',";
}
$script .= "
itemType:'" . $mapping . "',
});
";
}
if (!empty($codesnippet_after)) {
$script .= $codesnippet_after;
}
// Add SSL protocol.
$protocol = "http://";
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
// When using ISAPI with IIS, the value will be off if the
// request was not made through the HTTPS protocol.
$protocol = "https://";
}
// JS file and tracker vars need to be in header so that blocks work.
// Code for actual tracking can be put to footer.
drupal_add_js($protocol . RECOMMENDERGHOST_API_URL . "/api-js/ghost.js",
array('scope' => 'header'));
drupal_add_js($script_tracker,
array('scope' => 'header', 'type' => 'inline'));
if ($script && $is_node) {
drupal_add_js($script, array('scope' => $scope, 'type' => 'inline'));
}
}
}
}
/**
* Return visibility setting based on current page.
*/
function _recommenderghost_visibility_pages() {
static $page_match;
// Cache visibility result if function is called more than once.
if (!isset($page_match)) {
$visibility = variable_get('recommenderghost_visibility_pages', 0);
$setting_pages = variable_get('recommenderghost_pages', RECOMMENDERGHOST_PAGES);
// Match path if necessary.
if (!empty($setting_pages)) {
// Convert path to lowercase. This allows comparison of the same path
// with different case. Ex: /Page, /page, /PAGE.
$pages = drupal_strtolower($setting_pages);
if ($visibility < 2) {
// Convert the Drupal path to lowercase.
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
$path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
// Compare the lowercase internal and lowercase path alias (if any).
$page_match = drupal_match_path($path, $pages);
if ($path != $_GET['q']) {
$page_match = $page_match || drupal_match_path($_GET['q'], $pages);
}
// When $visibility has a value of 0, the tracking code is displayed on
// all pages except those listed in $pages. When set to 1, it
// is displayed only on those pages listed in $pages.
$page_match = !($visibility xor $page_match);
}
elseif (module_exists('php')) {
$page_match = php_eval($setting_pages);
}
else {
$page_match = FALSE;
}
}
else {
$page_match = TRUE;
}
}
return $page_match;
}
/**
* Return visibility settings for current role.
*/
function _recommenderghost_visibility_roles($account) {
$visibility = variable_get('recommenderghost_visibility_roles', 0);
$enabled = $visibility;
$roles = variable_get('recommenderghost_roles', array());
if (array_sum($roles) > 0) {
// One or more roles are selected.
foreach (array_keys($account->roles) as $rid) {
// Is the current user a member of one of these roles?
if (isset($roles[$rid]) && $rid == $roles[$rid]) {
// Current user is a member of a role that should be tracked/
// excluded from tracking.
$enabled = !$visibility;
break;
}
}
}
else {
// No role is selected for tracking, therefore all roles should be tracked.
$enabled = TRUE;
}
return $enabled;
}
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/**
* Return visibility settings for current content type.
*/
function _recommenderghost_visibility_content_types($node) {
$visibility = variable_get('recommenderghost_visibility_content_types', 0);
$enabled = $visibility;
$content_types = variable_get('recommenderghost_content_types', array());
if (count($content_types) > 0) {
// One or more content_types are selected.
$type = $node->type;
if (isset($content_types[$type]) && $type === $content_types[$type]) {
// Current node is a member of content type that should be tracked/
// excluded from tracking.
$enabled = !$visibility;
}
else {
$enabled = $visibility;
}
}
else {
// No content_type is selected for tracking,
// therefore all content_types should be tracked.
$enabled = TRUE;
}
return $enabled;
}
/**
* Tracking visibility check for an user object.
*
* @param object $account
* A user object containing an array of roles to check.
*
* @return bool
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
* A decision on if the current user is being tracked by RecommenderGhost.
*/
function _recommenderghost_visibility_user($account) {
$enabled = FALSE;
// Is current user a member of a role that should be tracked?
if (_recommenderghost_visibility_roles($account)) {
// Use the user's block visibility setting, if necessary.
if (($custom = variable_get('recommenderghost_custom', 0)) != 0) {
if ($account->uid && isset($account->data['recommenderghost']['custom'])) {
$enabled = $account->data['recommenderghost']['custom'];
}
else {
$enabled = ($custom == 1);
}
}
else {
$enabled = TRUE;
}
}
return $enabled;
}
/**
* Implements a central REST query. Returns json of the result.
function recommenderghost_call_api($function, $parameters) {
$apikey = variable_get('recommenderghost_apikey', '');
$websiteid = variable_get('recommenderghost_websiteid', '');
$url = RECOMMENDERGHOST_API_URL . "/api/1.0/json/" . $function;
$url .= "?apikey=$apikey&tenantid=$websiteid&";
$url .= http_build_query($parameters);
$response = drupal_http_request(($url), array(
"timeout" => 1,
));
return json_decode($response->data);
/**
* Implements hook_block_info().
*/
function recommenderghost_block_info() {
$blocks['rg_also_viewed_list'] = array(
'info' => t('RecommenderGhost: Recommendations List View'),
'cache' => DRUPAL_CACHE_PER_PAGE,
);
$blocks['rg_also_viewed_pictures'] = array(
'info' => t('RecommenderGhost: Recommendations Picture View'),
'cache' => DRUPAL_CACHE_PER_PAGE,
);
return $blocks;
}
/**
* Implements hook_block_view().
*
* The block defines various blocks for showing recommendations.
function recommenderghost_block_view($delta = '') {
global $user;
// Define available recommendations.
$recommendation_types = array(
"otherusersalsoviewed" => t("Other users also viewed"),
"otherusersalsobought" => t("Other users also bought"),
"recommendationsforuser" => t("Recommendations for user"),
"related items" => t("Related items"),
);
// Following privacy policy of RecommenderGhost we need to
// encode user ids into non-revertable formatOutput.
$uid = sha1($user->uid);
$nid = NULL;
$tracking_method = variable_get('recommenderghost_tracking_method', 'JS');
// Only show the recommendations for node views.
if (arg(0) == 'node' && is_numeric(arg(1)) && !arg(2)) {
$node = node_load(arg(1));
$nid = arg(1);
}
else {
// No node view.
return;
}
$item_type = variable_get('recommenderghost_mapping_' . $node->type, 'ITEM');
$use_custom_callback = variable_get('recommenderghost_use_custom_drawingcallback_' . $delta, FALSE);
switch ($delta) {
case 'rg_also_viewed_list':
if (user_access('access content')) {
$div = "recommenderDivAlsoViewedList";
($tracking_method == "JS" && $use_custom_callback) ? $drawing_callback = "drawCustomCallbackToDiv" : $drawing_callback = "drawRecommendationList";
$recommendationtype = variable_get('recommenderghost_block_recommendationtype_rg_also_viewed_list', 'otherusersalsoviewed');
$nb_of_recommendations = variable_get('recommenderghost_block_number_of_recommendations_rg_also_viewed_list', '5');
$limit_same_type = variable_get('recommenderghost_block_same_itemtype_rg_also_viewed_list', FALSE);
$with_uid = variable_get('recommenderghost_block_with_uid_rg_also_viewed_list', FALSE);
$block['subject'] = $recommendation_types[$recommendationtype];
}
break;
case 'rg_also_viewed_pictures':
if (user_access('access content')) {
$div = "recommenderDivAlsoViewedPictures";
($tracking_method == "JS" && $use_custom_callback) ? $drawing_callback = "drawCustomCallbackToDiv" : $drawing_callback = "drawRecommendationListWithPictures";
$recommendationtype = variable_get('recommenderghost_block_recommendationtype_rg_also_viewed_pictures', 'otherusersalsoviewed');
$nb_of_recommendations = variable_get('recommenderghost_block_number_of_recommendations_rg_also_viewed_pictures', '5');
$limit_same_type = variable_get('recommenderghost_block_same_itemtype_rg_also_viewed_pictures', FALSE);
$with_uid = variable_get('recommenderghost_block_with_uid_rg_also_viewed_pictures', FALSE);
$block['subject'] = $recommendation_types[$recommendationtype];
}
break;
}
$options = array(
"itemtype" => $item_type,
"itemid" => $nid,
($limit_same_type) ? $options["requesteditemtype"] = $item_type : "";
($recommendationtype == "recommendationsforuser" || $with_uid) ? $options["userid"] = $uid : "";
$recommendations = recommenderghost_call_api($recommendationtype, $options);
// No recommendations for this item.
if (!isset($recommendations->recommendeditems->item)) {
return;
}
// If only one result then change array structure.
if (isset($recommendations->recommendeditems->item->creationDate)) {
$items[0] = $recommendations->recommendeditems->item;
}
else {
$items = $recommendations->recommendeditems->item;
}
// Correct some data.
$new_items = array();
$item->imageUrl = isset($item->imageUrl) ? $item->imageUrl : "http://www.recommenderghost.com/sites/all/themes/bootstrap/img/no_image.gif";
$new_items[] = $item;
$block['content'] = theme('recommenderghost_list_view', array('items' => $new_items));
else {
$block['content'] = theme('recommenderghost_picture_view', array('items' => $new_items));
}
$delta_block = str_replace("_", "-", $delta);
$js = "<script>";
($drawing_callback == "drawRecommendationListWithPictures" || $drawing_callback == "drawRecommendationList") ? $drawing_callback .= "ToDiv" : NULL;
$js .= "
function recommenderghost_drawingCallback_forwarding_$delta(json) {
$drawing_callback(json,'recommenderDiv$delta');
} else {
document.getElementById('block-recommenderghost-$delta_block').style.display='none';
}
easyrec_getRecommendations('$recommendationtype', {
itemId:'$nid',
numberOfResults: $nb_of_recommendations,
drawingCallback:'recommenderghost_drawingCallback_forwarding_$delta',
itemType:'$item_type',";
($limit_same_type) ? $js .= "
requestedItemType:'$item_type'" : "";
($recommendationtype == "recommendationsforuser" || $with_uid) ? $js .= "
userId:'$uid'" : "";
$js .= "
/**
* Implements hook_theme().
*/
function recommenderghost_theme() {
return array(
'recommenderghost_list_view' => array(
'template' => 'recommenderghost_list_view',
'variables' => array('items' => NULL),
),
'recommenderghost_picture_view' => array(
'template' => 'recommenderghost_picture_view',
'variables' => array('items' => NULL),
),
);
}
/**
* Implements template_preprocess_HOOK.
*/
function template_preprocess_recommenderghost_list_view(&$variables) {
$variables['theme_hook_suggestions'][] = 'recommenderghost_list_view__block';
foreach ($variables['items'] as $key => $one) {
$new = array();
$new["creationDate"] = $one->creationDate;
$new["description"] = check_plain($one->description);
$new["imageUrl"] = check_url($one->imageUrl);
$new["id"] = $one->id;
$new["itemType"] = $one->itemType;
$new["url"] = check_url($one->url);
$new["value"] = $one->value;
$variables["items"][$key] = $new;
}
}
/**
* Implements template_preprocess_HOOK.
*/
function template_preprocess_recommenderghost_picture_view(&$variables) {
template_preprocess_recommenderghost_list_view($variables);
$variables['theme_hook_suggestions'][] = 'recommenderghost_list_pictures__block';
}
/**
* Implements hook_block_configure().
*/
function recommenderghost_block_configure($delta="") {
$form['blockconfig'] = array(
'#type' => 'fieldset',
'#title' => t('RecommenderGhost block settings'),
);
// Define available recommendations.
$recommendation_types = array(
"otherusersalsoviewed" => t("Other users also viewed"),
"otherusersalsobought" => t("Other users also bought"),
"recommendationsforuser" => t("Recommendations for user"),
"related items" => t("Related items"),
);
$form['blockconfig']['recommenderghost_block_recommendationtype_' . $delta] = array(
'#type' => 'select',
'#title' => t('Which recommendation type would you like to show?'),
'#description' => t('This parameter determines which analysis method is used for retrieving recommendations from RecommenderGhost.'),
'#default_value' => variable_get('recommenderghost_block_recommendationtype_' . $delta, 'otherusersalsoviewed'),
'#options' => $recommendation_types,
);
// Define nb of recommendations.
$nb = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
$form['blockconfig']['recommenderghost_block_number_of_recommendations_' . $delta] = array(
'#type' => 'select',
'#title' => t('How many recommendations would you like to show?'),
'#description' => t('This parameter determines the number of recommendations retrieved from RecommenderGhost.'),
'#default_value' => variable_get('recommenderghost_block_number_of_recommendations_' . $delta, '5'),
'#options' => $nb,
);
// Show drawing callback option for JS method only.
if (variable_get('recommenderghost_tracking_method', 'JS') == "JS") {
$form['blockconfig']['recommenderghost_use_custom_drawingcallback_' . $delta] = array(
'#type' => 'checkbox',
'#title' => t('Use custom drawing callback function?'),
'#description' => t('You can define your own Javascript drawing callback function in the advanced configuration section of RecommenderGhost. Tick this box to use the custom one.'),
'#default_value' => variable_get('recommenderghost_use_custom_drawingcallback_' . $delta, FALSE),
);
}
// Define to limit item type.
$form['blockconfig']['recommenderghost_block_same_itemtype_' . $delta] = array(
'#type' => 'checkbox',
'#title' => t('Limit recommendations to same item type?'),
'#description' => t('If you have several item types (eg CD, DVD) configured in RecommenderGhost you can filter recommendations to only show the ones from the same item type as current one. This applies only if you have configured more than the standard item type ("ITEM").'),
'#default_value' => variable_get('recommenderghost_block_same_itemtype_' . $delta, FALSE),
);
// Define to personlize to current user (if logged in).
$form['blockconfig']['recommenderghost_block_with_uid_' . $delta] = array(
'#type' => 'checkbox',
'#title' => t('Filter items user already viewed?'),
'#description' => t('If set any provided items viewed by this user are suppressed.'),
'#default_value' => variable_get('recommenderghost_block_with_uid_' . $delta, FALSE),
);
return $form;
}
/**
* Implements hook_block_save().
*/
function recommenderghost_block_save($delta='', $edit=array()) {
variable_set('recommenderghost_block_recommendationtype_' . $delta, $edit['recommenderghost_block_recommendationtype_' . $delta]);
variable_set('recommenderghost_block_number_of_recommendations_' . $delta, $edit['recommenderghost_block_number_of_recommendations_' . $delta]);
variable_set('recommenderghost_use_custom_drawingcallback_' . $delta, $edit['recommenderghost_use_custom_drawingcallback_' . $delta]);
variable_set('recommenderghost_block_same_itemtype_' . $delta, $edit['recommenderghost_block_same_itemtype_' . $delta]);
variable_set('recommenderghost_block_with_uid_' . $delta, $edit['recommenderghost_block_with_uid_' . $delta]);