mostpopular.module in Drupal Most Popular 7
Same filename and directory in other branches
The main file for the Most Popular module.
File
mostpopular.moduleView source
<?php
// $Id$
/*
* Drupal Most Popular - Showcase the most popular content across your Drupal website and engage your audience.
* Copyright © 2009-2012 New Signature
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
* You can contact New Signature by electronic mail at labs@newsignature.com -or- by U.S. Postal Service at 1100 H St. NW, Suite 940, Washington, DC 20005.
*/
/**
* @file
* The main file for the Most Popular module.
*/
// Define some constants to determine how much to style the mostpopular block.
define('MOSTPOPULAR_STYLE_NONE', 0);
define('MOSTPOPULAR_STYLE_BASIC', 1);
define('MOSTPOPULAR_STYLE_FULL', 2);
define('MOSTPOPULAR_SERVICE_STATUS_ERROR', -2);
define('MOSTPOPULAR_SERVICE_STATUS_DISABLED', -1);
define('MOSTPOPULAR_SERVICE_STATUS_PENDING', 0);
define('MOSTPOPULAR_SERVICE_STATUS_CONFIGURED', 1);
define('MOSTPOPULAR_SERVICE_STATUS_OK', 2);
define('MOSTPOPULAR_DEFAULT_EXCLUDES', "admin/*\nuser/*\nnode/*/*\n*?*\n");
/**
* Implements hook_permission().
*/
function mostpopular_permission() {
return array(
'administer mostpopular' => array(
'title' => t('Administer Most Popular'),
),
);
}
/**
* Implement hook_help().
*/
function mostpopular_help($path, $arg) {
if ($path == 'admin/help#mostpopular') {
return t('The Most Popular module retrieves a list of the most popular content on your site through Google Analytics or other services, and provides a block for displaying the most popular content to users.');
}
if ($path == 'admin/config/mostpopular/intervals') {
return '<p>' . t("The interval field for each row must contain a string that can be understood by\n<a href='@strtotime' target='php'>strtotime()</a>. You must specify each as a\nnegative interval relative to today.", array(
'@strtotime' => 'http://php.net/manual/en/function.strtotime.php',
)) . '</p>' . '<p>' . t('To remove an interval, clear both the title and interval values.') . '</p>' . '<p>' . t("If you make changes to the intervals, any custom service throttles you may\nhave set up will be reset to their default values.") . '</p>';
}
}
/**
* Implements hook_menu().
*/
function mostpopular_menu() {
$items = array();
$items['admin/config/mostpopular'] = array(
'title' => 'Most Popular',
'description' => 'Configure the Most Popular block functionality.',
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array(
'administer mostpopular',
),
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
'position' => 'right',
'type' => MENU_NORMAL_ITEM,
);
$items['admin/config/mostpopular/config'] = array(
'title' => 'Settings',
'description' => 'Basic settings for all Most Popular blocks.',
'page callback' => 'drupal_get_form',
'file' => 'mostpopular.admin.inc',
'page arguments' => array(
'mostpopular_settings_form',
),
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_LOCAL_TASK | MENU_VISIBLE_IN_TREE,
'weight' => 0,
);
$items['admin/config/mostpopular/blocks'] = array(
'title' => 'Blocks',
'description' => 'Configure the available Most Popular blocks.',
'file' => 'mostpopular.blocks.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'mostpopular_blocks_admin_form',
),
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_LOCAL_TASK | MENU_VISIBLE_IN_TREE,
'weight' => 5,
);
$items['admin/config/mostpopular/services'] = array(
'title' => 'Services',
'description' => 'Configure the services available in each Most Popular block.',
'file' => 'mostpopular.services.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'mostpopular_services_admin_form',
),
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_LOCAL_TASK | MENU_VISIBLE_IN_TREE,
'weight' => 10,
);
$items['admin/config/mostpopular/services/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -50,
);
$items['admin/config/mostpopular/services/%/edit'] = array(
'title callback' => 'mostpopular_service_title',
'title arguments' => array(
4,
),
'file' => 'mostpopular.services.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'mostpopular_service_config_form',
4,
),
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_NORMAL_ITEM,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
);
$items['admin/config/mostpopular/services/%/delete'] = array(
'title' => 'Delete',
'file' => 'mostpopular.services.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'mostpopular_service_delete_form',
4,
),
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_NORMAL_ITEM,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
);
$items['admin/config/mostpopular/intervals'] = array(
'title' => 'Intervals',
'description' => 'Configure the intervals of time used for each Most Popular block.',
'file' => 'mostpopular.intervals.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'mostpopular_intervals_admin_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_LOCAL_TASK | MENU_VISIBLE_IN_TREE,
'weight' => 20,
);
$items['admin/config/mostpopular/refresh'] = array(
'title' => 'Refresh Stats',
'description' => 'Refresh the Most Popular stats.',
'page callback' => 'mostpopular_refresh',
'access callback' => 'user_access',
'access arguments' => array(
'administer mostpopular',
),
'type' => MENU_LOCAL_TASK | MENU_VISIBLE_IN_TREE,
'weight' => 30,
);
$items['mostpopular/ajax/%/%/%'] = array(
'title' => 'Get the most popular stats via AJAX',
'page callback' => 'mostpopular_items_ajax',
'page arguments' => array(
2,
3,
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_theme().
*/
function mostpopular_theme($existing, $type, $theme, $path) {
$themes = array(
'mostpopular_admin_blocks_table' => array(
'render element' => 'element',
'file' => 'mostpopular.blocks.inc',
),
'mostpopular_admin_services_table' => array(
'render element' => 'element',
'file' => 'mostpopular.services.inc',
),
'mostpopular_service_status' => array(
'variables' => array(
'status' => 0,
),
'file' => 'mostpopular.services.inc',
),
'mostpopular_admin_intervals_table' => array(
'render element' => 'element',
'file' => 'mostpopular.intervals.inc',
),
// Theme for the block
'mostpopular_block' => array(
'variables' => array(
'services' => array(),
'intervals' => array(),
'bid' => 0,
),
'file' => 'mostpopular.theme.inc',
'template' => 'templates/mostpopular-block',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_block',
),
),
// Themes for the service tabs
'mostpopular_services' => array(
'variables' => array(
'services' => array(),
),
'file' => 'mostpopular.theme.inc',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_services',
),
),
'mostpopular_service' => array(
'variables' => array(
'service' => array(),
),
'file' => 'mostpopular.theme.inc',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_service',
),
),
// Themes for the intervals tabs
'mostpopular_intervals' => array(
'variables' => array(
'intervals' => array(),
),
'file' => 'mostpopular.theme.inc',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_intervals',
),
),
'mostpopular_interval' => array(
'variables' => array(
'interval' => array(),
),
'file' => 'mostpopular.theme.inc',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_interval',
),
),
// Themes for the most popular result items
'mostpopular_items' => array(
'variables' => array(
'items' => array(),
),
'file' => 'mostpopular.theme.inc',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_items',
),
),
'mostpopular_item' => array(
'variables' => array(
'item' => null,
),
'file' => 'mostpopular.theme.inc',
'template' => 'templates/mostpopular-item',
'preprocess functions' => array(
'template_preprocess',
'template_preprocess_mostpopular_item',
),
),
'mostpopular_items__none' => array(
'variables' => array(),
'file' => 'mostpopular.theme.inc',
),
);
return $themes;
}
function _mostpopular_save($type, &$object) {
if (!is_object($object)) {
$object = (object) $object;
}
// Get information about the table
$table = "mostpopular_{$type}";
$schema = drupal_get_schema($table);
// Find the object's ID
foreach ($schema['fields'] as $key => $field) {
if ($field['type'] == 'serial') {
$id = $key;
}
elseif (!empty($field['serialize'])) {
$data = $key;
}
}
// Create a data element if necessary
if (isset($data) && !isset($object->{$data})) {
$object->{$data} = array();
}
// Now, invoke the callbacks and save the object
module_invoke_all("{$table}_presave", $object);
if (!empty($object->{$id})) {
drupal_write_record($table, $object, array(
$id,
));
$status = 'update';
}
else {
drupal_write_record($table, $object);
$status = 'insert';
}
module_invoke_all("{$table}_{$status}", $object);
}
function _mostpopular_callback($callback, $info) {
if (is_array($info)) {
$info = (object) $info;
}
if (isset($info->file)) {
include $info->file;
}
$module = $info->module;
$delta = $info->delta;
$function = FALSE;
if (isset($info->callbacks[$callback]) && function_exists($info->callbacks[$callback])) {
$function = $info->callbacks[$callback];
}
elseif (function_exists("{$module}_{$callback}_{$delta}")) {
$function = "{$module}_{$callback}_{$delta}";
}
elseif (function_exists("{$module}_{$callback}")) {
$function = "{$module}_{$callback}";
}
elseif (function_exists("mostpopular_default_{$callback}")) {
$function = "mostpopular_default_{$callback}";
}
return $function;
}
function _mostpopular_invoke($callback, $info, &$arg1 = NULL, &$arg2 = NULL, &$arg3 = NULL) {
if (is_array($info)) {
$info = (object) $info;
}
if (isset($info->file)) {
include $info->file;
}
$function = _mostpopular_callback($callback, $info);
if ($function) {
return $function($info, $arg1, $arg2, $arg3);
}
return FALSE;
}
/**
* Gets the list of available services.
*
* @see hook_mostpopular_service_info()
*/
function mostpopular_service_info($module = NULL, $delta = NULL) {
$services =& drupal_static(__FUNCTION__);
if (!isset($services)) {
$services = array();
foreach (module_implements('mostpopular_service_info') as $m) {
$info = module_invoke($m, 'mostpopular_service_info');
foreach ($info as $d => $data) {
$data += array(
'module' => $m,
'delta' => $d,
'title' => '',
);
$services[$m][$d] = $data;
}
}
}
if (!empty($module)) {
if (!empty($delta)) {
if (isset($services[$module][$delta])) {
return $services[$module][$delta];
}
return FALSE;
}
if (isset($services[$module])) {
return $services[$module];
}
return FALSE;
}
return $services;
}
/**
* Implements hook_load().
*
* Loads a configured most popular service based on its ID.
*
* @param integer $sid
* The ID of the configured service.
* @param boolean $reset
* True if the cache should be reset.
* @return object
* A most popular service configuration from the database.
*/
function mostpopular_service_load($sid, $reset = FALSE) {
$services =& drupal_static(__FUNCTION__, array());
if (!isset($services[$sid]) || $reset) {
// Set variable so that it can be passed by reference
$most = mostpopular_service_load_multiple(array(
$sid,
));
$services[$sid] = reset($most);
}
return $services[$sid];
}
/**
* Loads configured most popular services within the given block.
*
* @param integer $bid
* The block ID.
* @param boolean $enabled
* If true (default), only enabled services will be retrieved.
* If false, all services will be retrieved.
* @return array
* An array of most popular service configurations.
*/
function mostpopular_service_load_by_block($bid, $enabled = TRUE, $reset = FALSE) {
$services =& drupal_static(__FUNCTION__, array());
if (!isset($services[$bid]) || $reset) {
$params = array(
'bid' => $bid,
);
if ($enabled) {
$params['enabled'] = TRUE;
}
$services[$bid] = mostpopular_service_load_multiple(array(), $params);
}
return $services[$bid];
}
/**
* Implements hook_load_multiple().
*
* Loads configured most popular services based on their IDs.
*
* @param array $sids
* The IDs of the desired services.
* @param array $conditions
* Additional conditions to use.
* @param boolean $reset
* True if the cache should be reset.
* @return array
* An array of most popular service configurations.
*
* @see entity_load()
*/
function mostpopular_service_load_multiple($sids = array(), $conditions = array()) {
// Unless conditions are specified explicitly, only get the enabled services.
if (empty($sids) && empty($conditions)) {
$conditions['enabled'] = 1;
}
$q = db_select('mostpopular_service', 's')
->fields('s');
if (!empty($sids)) {
$q
->condition('sid', $sids);
}
foreach ($conditions as $field => $value) {
$q
->condition($field, $value);
}
$q
->orderBy('weight', 'ASC');
$services = $q
->execute()
->fetchAllAssoc('sid', PDO::FETCH_ASSOC);
// Augment each service with data from the hooks
foreach ($services as $sid => $service) {
$info = mostpopular_service_info($service['module'], $service['delta']);
if ($info) {
$service += $info;
}
// Unserialize the extra data
if (!empty($service['data'])) {
$service['data'] = unserialize($service['data']);
}
$services[$sid] = (object) $service;
}
return $services;
}
function mostpopular_service_save($service) {
_mostpopular_save('service', $service);
}
/**
* Before a mostpopular service is saved, update its status.
*
* @param object $service The service being saved.
*/
function mostpopular_mostpopular_service_presave($service) {
if ($service->enabled == 0) {
$service->status = MOSTPOPULAR_SERVICE_STATUS_DISABLED;
}
elseif ($service->status == MOSTPOPULAR_SERVICE_STATUS_DISABLED) {
$service->status = MOSTPOPULAR_SERVICE_STATUS_PENDING;
}
}
/**
* Returns the name of a service, for the menu hooks.
*
* @param integer $sid The service ID.
*/
function mostpopular_service_title($sid) {
$service = mostpopular_service_load($sid);
if ($service) {
return t('@name: %title', array(
'@name' => $service->name,
'%title' => $service->title,
));
}
}
/**
* Deletes the service configuration with the given ID.
*
* @param integer $sid The service ID.
*/
function mostpopular_service_delete($sid) {
if (!empty($sid) && is_numeric($sid)) {
db_delete('mostpopular_service')
->condition('sid', $sid)
->execute();
}
}
/**
* Gets the configured Most Popular intervals.
*
* @param integer $iid Optionally the ID of a single interval to retrieve.
*/
function mostpopular_intervals($iid = NULL) {
$intervals =& drupal_static(__FUNCTION__);
if (!isset($intervals)) {
$intervals = db_select('mostpopular_interval', 'i')
->fields('i')
->execute()
->fetchAllAssoc('iid');
}
if (!empty($iid)) {
if (isset($intervals[$iid])) {
return $intervals[$iid];
}
return FALSE;
}
return $intervals;
}
/**
* Gets the configured intervals for the given block.
*
* @param integer $bid The ID of the block.
*/
function mostpopular_interval_load_by_block($bid) {
$intervals =& drupal_static(__FUNCTION__, array());
if (!isset($intervals[$bid])) {
$intervals[$bid] = db_select('mostpopular_interval', 'i')
->fields('i')
->condition('bid', $bid)
->orderBy('weight', 'ASC')
->execute()
->fetchAllAssoc('iid');
}
return $intervals[$bid];
}
/**
* Returns the timestamp, relative to the current time, that marks
* the start of this interval.
*
* @param object $interval
* The interval configuration.
* @return integer
* A timestamp in the past, or 0 if there is no interval string specified.
*/
function mostpopular_interval_timestamp($interval) {
if (is_int($interval)) {
$interval = mostpopular_intervals($interval);
}
return !empty($interval->string) ? strtotime($interval->string) : 0;
}
/**
* Returns the full title, which is the interval's title prepended with
* 'Past '. So, for instance, 'Day' becomes 'Past Day'.
*
* @return string
* The full title of the interval.
*/
function mostpopular_interval_title($interval) {
if (is_int($interval)) {
$interval = mostpopular_intervals($interval);
}
if (!$interval) {
return '';
}
return 'Past ' . $interval->title;
}
function mostpopular_interval_save($interval) {
_mostpopular_save('interval', $interval);
}
function mostpopular_interval_delete($interval) {
// Delete the interval itself
db_delete('mostpopular_interval')
->condition('iid', $interval->iid)
->execute();
// Delete any cached items for this interval
db_delete('mostpopular_item')
->condition('iid', $interval->iid)
->execute();
}
/**
* Returns an array of default intervals to use for a new block.
*
* @param integer $bid The ID of the block.
*/
function mostpopular_interval_defaults($bid) {
$defaults = array(
(object) array(
'bid' => $bid,
'title' => '1 day',
'string' => '-1 day',
'weight' => 0,
),
(object) array(
'bid' => $bid,
'title' => '1 week',
'string' => '-1 week',
'weight' => 1,
),
(object) array(
'bid' => $bid,
'title' => '1 month',
'string' => '-1 month',
'weight' => 2,
),
(object) array(
'bid' => $bid,
'title' => '1 year',
'string' => '-1 year',
'weight' => 3,
),
);
// Allow other modules to change this list.
drupal_alter('mostpopular_interval_defaults', $defaults, $bid);
return $defaults;
}
/**
* Gets the configured Most Popular blocks.
*
* @param integer $bid Optionally, the block ID of an individual block to get.
* @return mixed If a $bid was provided, returns just the matching block. Otherwise,
* returns a list of all the configured blocks.
*/
function mostpopular_blocks($bid = NULL) {
$blocks =& drupal_static(__FUNCTION__);
if (!isset($blocks)) {
$blocks = db_select('mostpopular_block', 'b')
->fields('b')
->execute()
->fetchAllAssoc('bid');
foreach ($blocks as $b => $block) {
$blocks[$b]->data = unserialize($block->data);
if (!$blocks[$b]->data) {
$blocks[$b]->data = array();
}
}
}
if (!empty($bid)) {
if (isset($blocks[$bid])) {
return $blocks[$bid];
}
return FALSE;
}
return $blocks;
}
function mostpopular_blocks_local() {
$blocks =& drupal_static(__FUNCTION__);
if (!isset($blocks)) {
$blocks = db_select('mostpopular_block', 'b')
->fields('b')
->condition('remote_bid', NULL)
->execute()
->fetchAllAssoc('bid');
foreach ($blocks as $b => $block) {
$blocks[$b]->data = unserialize($block->data);
if (!$blocks[$b]->data) {
$blocks[$b]->data = array();
}
}
}
return $blocks;
}
function mostpopular_blocks_remote() {
$blocks =& drupal_static(__FUNCTION__);
if (!isset($blocks)) {
$blocks = db_select('mostpopular_block', 'b')
->fields('b')
->condition('remote_bid', 0, '>')
->execute()
->fetchAllAssoc('bid');
foreach ($blocks as $b => $block) {
$blocks[$b]->data = unserialize($block->data);
if (!$blocks[$b]->data) {
$blocks[$b]->data = array();
}
}
}
return $blocks;
}
/**
* Implements hook_block_save().
*
* @param unknown_type $delta
* @param unknown_type $values
*/
function mostpopular_block_save($block, $values = array()) {
if (!is_object($block)) {
$block = mostpopular_blocks($block);
}
if ($block) {
if (isset($values['mostpopular'])) {
$values = $values['mostpopular'];
}
foreach ($values as $key => $value) {
if (is_array($value)) {
$data =& $block->{$key};
foreach ($value as $k => $v) {
$data[$k] = $v;
}
}
else {
$block->{$key} = $value;
}
}
_mostpopular_save('block', $block);
}
}
function mostpopular_block_delete($bid) {
if (empty($bid)) {
return;
}
$services = mostpopular_service_load_by_block($bid, FALSE);
$intervals = mostpopular_interval_load_by_block($bid);
db_delete('mostpopular_block')
->condition('bid', $bid)
->execute();
db_delete('mostpopular_service')
->condition('bid', $bid)
->execute();
db_delete('mostpopular_interval')
->condition('bid', $bid)
->execute();
if (!empty($services) && !empty($intervals)) {
db_delete('mostpopular_item')
->condition('sid', array_keys($services))
->condition('iid', array_keys($intervals))
->execute();
db_delete('mostpopular_last_run')
->condition('sid', array_keys($services))
->condition('iid', array_keys($intervals))
->execute();
}
drupal_set_message(t('Deleted block @bid', array(
'@bid' => $bid,
)));
}
/**
* Implements hook_block_info().
*
* Defines each of the most popular blocks.
*
* Each block will render its contents based on user cookies. This allows us to
* cache the block itself.
*/
function mostpopular_block_info() {
$out = array();
$blocks = mostpopular_blocks();
foreach ($blocks as $bid => $block) {
$out[$block->bid] = array(
'info' => t('Most Popular: @name', array(
'@name' => !empty($block->name) ? $block->name : $block->bid,
)),
'cache' => DRUPAL_CACHE_GLOBAL,
);
}
return $out;
}
/**
* Implements hook_block_view().
*
* Creates the most popular block.
*
* The service and interval to show by default are loaded from the user's cookie.
* If they don't have a cookie set, the first service and first interval will be
* selected by default.
*
* @param integer $bid The ID of the block.
*/
function mostpopular_block_view($bid = '') {
$block = mostpopular_blocks($bid);
if ($block) {
if (!empty($block->data['remote_database'])) {
if (!db_set_active($block->data['remote_database'])) {
watchdog('mostpopular', 'Missing remote database @database', array(
'@database' => $block->data['remote_database'],
), WATCHDOG_ERROR);
db_set_active('default');
return NULL;
}
$services = mostpopular_service_load_by_block($block->remote_bid);
$intervals = mostpopular_interval_load_by_block($block->remote_bid);
db_set_active('default');
}
else {
$services = mostpopular_service_load_by_block($bid);
$intervals = mostpopular_interval_load_by_block($bid);
}
$out = array(
'subject' => !empty($block->title) ? $block->title : t('Most Popular'),
'content' => array(
'#theme' => 'mostpopular_block',
'#services' => $services,
'#intervals' => $intervals,
'#bid' => $bid,
),
);
// Attach the stylesheets and javascript
$path = drupal_get_path('module', 'mostpopular');
switch (variable_get('mostpopular_styling', MOSTPOPULAR_STYLE_FULL)) {
case MOSTPOPULAR_STYLE_BASIC:
$out['content']['#attached']['css'][] = "{$path}/css/mostpopular-basic.css";
break;
case MOSTPOPULAR_STYLE_FULL:
$out['content']['#attached']['css'][] = "{$path}/css/mostpopular-basic.css";
$out['content']['#attached']['css'][] = "{$path}/css/mostpopular-full.css";
break;
}
$out['content']['#attached']['js'][] = 'misc/jquery.cookie.js';
$out['content']['#attached']['js'][] = "{$path}/js/fade.js";
$out['content']['#attached']['js'][] = "{$path}/js/mostpopular.js";
$out['content']['#attached']['js'][] = array(
'data' => array(
'mostpopular' => array(
'url' => url('mostpopular/ajax'),
),
),
'type' => 'setting',
);
return $out;
}
return NULL;
}
function mostpopular_items_ajax($bid, $sid, $iid) {
$rendered =& drupal_static(__FUNCTION__);
$block = mostpopular_blocks($bid);
$cid = "mostpopular_items:{$bid}:{$sid}:{$iid}";
if (!isset($rendered[$bid][$sid][$iid])) {
$cached = cache_get($cid, 'cache_block');
if ($cached && $cached->expire > time()) {
$rendered[$bid][$sid][$iid] = $cached->data;
}
}
if (!isset($rendered[$bid][$sid][$iid])) {
if (!empty($block->data['remote_database'])) {
if (!db_set_active($block->data['remote_database'])) {
watchdog('mostpopular', 'Missing remote database @database', array(
'@database' => $block->data['remote_database'],
), WATCHDOG_ERROR);
db_set_active('default');
drupal_json_output('');
return;
}
}
try {
$items = db_select('mostpopular_item', 'i')
->fields('i')
->condition('sid', $sid)
->condition('iid', $iid)
->orderBy('count', 'DESC')
->execute()
->fetchAll();
$out = array(
'#theme' => 'mostpopular_items',
'#service' => mostpopular_service_load($sid),
'#interval' => mostpopular_intervals($iid),
'#items' => $items,
);
// Cache the rendered items until the next update
$next_run = db_select('mostpopular_last_run', 'm')
->fields('m', array(
'next_run',
))
->condition('sid', $sid)
->condition('iid', $iid)
->execute()
->fetchColumn();
} catch (Exception $ex) {
watchdog('mostpopular', 'Failed to load @source mostpopular items: %message', array(
'@source' => !empty($block->data['remote_database']) ? t('remote') : t('local'),
'%message' => $ex
->getMessage(),
), WATCHDOG_WARNING);
}
db_set_active('default');
$rendered[$bid][$sid][$iid] = drupal_render($out);
cache_set($cid, $rendered[$bid][$sid][$iid], 'cache_block', $next_run > 0 ? $next_run : CACHE_TEMPORARY);
}
if (isset($rendered[$bid][$sid][$iid])) {
drupal_json_output($rendered[$bid][$sid][$iid]);
}
else {
drupal_json_output('');
}
}
/**
* Implements the default 'next_run' callback, for services that don't provide their own.
*
* Returns the timestamp at which to next refresh the data for this interval.
*
* @param object $service The service definition
* @param integer $span The number of seconds representing the current interval.
* @param integer $last_run The timestamp at which this service was last run for this interval.
*/
function mostpopular_default_next_run($service, $span, $last_run) {
// If the interval is 2 days or less, refresh once per hour
if ($span <= 60 * 60 * 24 * 2) {
return strtotime('1 hour', $last_run);
}
elseif ($span >= 60 * 60 * 24 * 365) {
return strtotime('1 week', $last_run);
}
// Otherwise, refresh once per day.
return strtotime('1 day', $last_run);
}
/* ----------------------------------------------------------------------------
* Cron jobs to fetch stats
* --------------------------------------------------------------------------*/
/**
* Implements hook_cron().
*
* Refreshes data from the services periodically.
*/
function mostpopular_cron() {
$t = mostpopular_refresh(TRUE);
watchdog('mostpopular_cron', $t);
}
/**
* Refreshes data from each service by invoking the refresh callback for each service.
*/
function mostpopular_refresh($cron = FALSE) {
$t = '';
// Loop through all the local blocks
$blocks = mostpopular_blocks_local();
foreach ($blocks as $bid => $block) {
$services = mostpopular_service_load_by_block($bid);
$intervals = mostpopular_interval_load_by_block($bid);
foreach ($services as $sid => $service) {
$count = 0;
$t .= '<div>';
$t .= t("Refreshing %title", array(
'%title' => $service->title,
));
$status = array();
foreach ($intervals as $iid => $interval) {
// Get the number of seconds that this interval spans.
$now = time();
$span = $now - strtotime($interval->string, $now);
// Get the next time this service should run.
$row = db_select('mostpopular_last_run', 'r')
->fields('r', array(
'last_run',
'next_run',
))
->condition('sid', $sid)
->condition('iid', $iid)
->range(0, 1)
->execute()
->fetch();
if ($row) {
$last_run = $row->last_run;
$next_run = $row->next_run;
}
else {
$last_run = 0;
$next_run = $now;
}
// When running as a cron job, ask the service how often it should refresh.
// When running as a page request, refresh the service immediately.
if (!$cron || $next_run <= $now) {
// Invoke the module
$out = _mostpopular_invoke('refresh', $service, $block, $span, $last_run);
// If the module returned any results, save them to the database.
if ($out !== FALSE) {
if (count($out) > 0) {
// Remove the previous results, if there are any
db_delete('mostpopular_item')
->condition('sid', $sid)
->condition('iid', $iid)
->execute();
// Write the new results to the cache table
foreach ($out as $value) {
$value->sid = $sid;
$value->iid = $iid;
// Fill in the entity properties if they are not already set.
if (!empty($value->entity_type) && !empty($value->entity_id)) {
if (empty($value->path) || empty($value->title)) {
$entity = reset(entity_load($value->entity_type, array(
$value->entity_id,
)));
if ($entity && empty($value->path)) {
$uri = entity_uri($value->entity_type, $entity);
if (isset($uri['path'])) {
$value->path = $uri['path'];
}
}
if ($entity && empty($value->title)) {
$value->title = entity_label($value->entity_type, $entity);
}
}
}
// Save the URL as an absolute path, so we can reuse it on other sites.
$value->url = url($value->path, array(
'absolute' => TRUE,
));
drupal_write_record('mostpopular_item', $value);
}
// Since there were items returned, the service is OK.
if ($service->status != MOSTPOPULAR_SERVICE_STATUS_OK) {
$service->status = MOSTPOPULAR_SERVICE_STATUS_OK;
mostpopular_service_save($service);
}
}
// Ask the service when it should next run on this interval.
$last_run = $now;
$next_run = _mostpopular_invoke('next_run', $service, $span, $last_run);
db_merge('mostpopular_last_run')
->fields(array(
'last_run' => $last_run,
'next_run' => $next_run,
))
->key(array(
'sid' => $sid,
'iid' => $iid,
))
->execute();
// Clear the item cache
cache_clear_all("mostpopular_items:{$bid}:{$sid}:{$iid}", 'cache_block');
$status[] = t("%interval: Found %count items", array(
'%interval' => $interval->title,
'%count' => count($out),
));
}
else {
$status[] = t("%interval: Error retrieving results", array(
'%interval' => $interval->title,
));
$service->status = MOSTPOPULAR_SERVICE_STATUS_ERROR;
mostpopular_service_save($service);
}
}
else {
$status[] = t('%interval: No need to refresh yet', array(
'%interval' => $interval->title,
));
}
}
$t .= theme('item_list', array(
'items' => $status,
));
$t .= '</div><br/>';
}
}
if (empty($t)) {
$t .= t("You must first enable services. Go to !link", array(
'!link' => l(t('the services tab'), 'admin/settings/mostpopular/services'),
));
}
return $t;
}
/**
* Matches the given URL to a Drupal node, resolving aliases appropriately.
* The homepage will never be included in this list.
*
* The URL can be an internal URL or it can start with one of the configured
* Drupal base paths, which will be stripped from the URL before the alias is
* resolved.
*
* If the URL corresponds to a node, an array will be returned with properties
* of that node from the most popular service.
*
* @param string $url
* A URL to match. This can be either an internal Drupal URL or it can start
* with one of the configured site basepaths.
* @param integer $count
* The number of times this node appears.
*
* @return array
* If the url corresponds to an entity, returns an array containing:
* - entity_type: the type of entity.
* - entity_id: the ID of the entity.
* - title: the title of the entity, fetched from the entity itself.
* - url: the external URL of the entity.
* - path: the internal Drupal path of the entity.
* - count: the number of times the entity was referenced.
* Otherwise, returns NULL.
*/
function mostpopular_match_result_nodes($url, $count, $options = array()) {
$options += array(
'entities_only' => FALSE,
'entity_types' => array(),
);
$url = trim($url);
// Strip out the base path from the URL.
$basepaths = variable_get('mostpopular_basepaths', array());
foreach ($basepaths as $base) {
if (stripos($url, $base) === 0) {
$url = drupal_substr($url, drupal_strlen($base));
break;
}
}
// Strip off any leading slashes
if (stripos($url, '/') === 0) {
$url = drupal_substr($url, 1);
}
// If the URL points to an excluded path, ignore it.
$excludepaths = variable_get('mostpopular_exclude_paths', '');
if (empty($url) || drupal_match_path($url, $excludepaths)) {
return NULL;
}
// Get the internal path for the URL alias.
$path = drupal_get_normal_path($url);
// If the URL points to an excluded path, ignore it.
if (drupal_match_path($path, $excludepaths)) {
return NULL;
}
$out = (object) array(
'path' => $path,
'count' => $count,
);
// Attempt to lookup the entity
$item = menu_get_item($path);
$entity = NULL;
if (!empty($item['load_functions'])) {
foreach ($item['load_functions'] as $i => $func) {
if ($func == 'menu_tail_load') {
break;
}
// Extract the entity type from the name of the load function
$entity_type = substr($func, 0, -5);
// Compare this to the list of valid entity types
if (empty($options['entity_types']) || isset($options['entity_types'][$entity_type])) {
// Load the entity
$parts = explode('/', $path);
if (isset($parts[$i]) && function_exists($func)) {
$entity = $func($parts[$i]);
// Check that the bundle matches
if (isset($entity) && is_object($entity)) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
if (!empty($options['entity_types']) && !isset($options['entity_types'][$entity_type][$bundle])) {
$entity = NULL;
}
}
else {
$entity = NULL;
}
}
}
break;
}
}
if (isset($entity)) {
// Check that anonymous users have access to view this entity
$access = entity_access('view', $entity_type, $entity, user_load(0));
if (isset($access) && $access === FALSE) {
return NULL;
}
$out->entity_type = $entity_type;
$out->entity_id = entity_id($entity_type, $entity);
$out->title = entity_label($entity_type, $entity);
$uri = entity_uri($entity_type, $entity);
if (isset($uri['path'])) {
$out->path = $uri['path'];
}
}
if ($entity || !$options['entities_only']) {
return $out;
}
return NULL;
}
/**
* Implements hook_hook_info().
*/
function mostpopular_hook_info() {
$hooks = array();
$hooks['form_block_admin_configure_alter']['group'] = 'blocks';
return $hooks;
}
Functions
Name | Description |
---|---|
mostpopular_blocks | Gets the configured Most Popular blocks. |
mostpopular_blocks_local | |
mostpopular_blocks_remote | |
mostpopular_block_delete | |
mostpopular_block_info | Implements hook_block_info(). |
mostpopular_block_save | Implements hook_block_save(). |
mostpopular_block_view | Implements hook_block_view(). |
mostpopular_cron | Implements hook_cron(). |
mostpopular_default_next_run | Implements the default 'next_run' callback, for services that don't provide their own. |
mostpopular_help | Implement hook_help(). |
mostpopular_hook_info | Implements hook_hook_info(). |
mostpopular_intervals | Gets the configured Most Popular intervals. |
mostpopular_interval_defaults | Returns an array of default intervals to use for a new block. |
mostpopular_interval_delete | |
mostpopular_interval_load_by_block | Gets the configured intervals for the given block. |
mostpopular_interval_save | |
mostpopular_interval_timestamp | Returns the timestamp, relative to the current time, that marks the start of this interval. |
mostpopular_interval_title | Returns the full title, which is the interval's title prepended with 'Past '. So, for instance, 'Day' becomes 'Past Day'. |
mostpopular_items_ajax | |
mostpopular_match_result_nodes | Matches the given URL to a Drupal node, resolving aliases appropriately. The homepage will never be included in this list. |
mostpopular_menu | Implements hook_menu(). |
mostpopular_mostpopular_service_presave | Before a mostpopular service is saved, update its status. |
mostpopular_permission | Implements hook_permission(). |
mostpopular_refresh | Refreshes data from each service by invoking the refresh callback for each service. |
mostpopular_service_delete | Deletes the service configuration with the given ID. |
mostpopular_service_info | Gets the list of available services. |
mostpopular_service_load | Implements hook_load(). |
mostpopular_service_load_by_block | Loads configured most popular services within the given block. |
mostpopular_service_load_multiple | Implements hook_load_multiple(). |
mostpopular_service_save | |
mostpopular_service_title | Returns the name of a service, for the menu hooks. |
mostpopular_theme | Implements hook_theme(). |
_mostpopular_callback | |
_mostpopular_invoke | |
_mostpopular_save |