download_count.module in Download Count 6.2
Same filename and directory in other branches
Tracks file downloads for files stored in the drupal files table using the private files setting or custom private filefield.
File
download_count.moduleView source
<?php
/**
* @file
* Tracks file downloads for files stored in the drupal files table using the private files setting or custom private filefield.
*/
/**
* Implementation of hook_help().
*/
function download_count_help($path, $arg) {
switch ($path) {
case 'admin/help#download_count':
return '<p>' . t("Tracks file downloads for files stored in the drupal files table. Requires either the 'private' download method setting or the method for combined public and private files described at <a href=@link>http://drupal.org/node/189239</a>. Also logs a message to the watchdog table.", array(
'@link' => url('http://drupal.org/node/189239'),
)) . '</p>';
}
}
/**
* Implementation of hook_perm().
*/
function download_count_perm() {
$perms = array(
'view all download counts',
'view own download counts',
'export download counts',
);
return $perms;
}
/**
* Implementation of hook_menu().
*/
function download_count_menu() {
$items = array();
$items['admin/settings/download_count'] = array(
'title' => 'Download count settings',
'description' => 'Tracks file downloads for files stored in the drupal files table.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'download_count_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'includes/download_count.admin.inc',
);
$items['admin/settings/download_count/general'] = array(
'title' => 'General',
'weight' => 1,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['download_count/%download_count_entry/reset'] = array(
'title' => 'Download Count Reset',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'download_count_reset_form',
1,
),
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_CALLBACK,
'file' => 'includes/download_count.pages.inc',
);
$items['download_count/%download_count_entry/export'] = array(
'title' => 'Download Count Export CSV',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'download_count_export_form',
1,
),
'access arguments' => array(
'export download counts',
),
'type' => MENU_CALLBACK,
'file' => 'includes/download_count.export.inc',
);
$items['download_count'] = array(
'title' => 'Download Count',
'page callback' => 'download_count_view_page',
'page arguments' => array(
'download_count',
),
'access arguments' => array(
'view all download counts',
),
'type' => MENU_NORMAL_ITEM,
'file' => 'includes/download_count.pages.inc',
);
$items['my_download_count'] = array(
'title' => 'My Download Count',
'page callback' => 'download_count_view_page',
'page arguments' => array(
'my_download_count',
),
'access arguments' => array(
'view own download counts',
),
'type' => MENU_NORMAL_ITEM,
'file' => '/includes/download_count.pages.inc',
);
return $items;
}
/**
* Menu wildcard loader.
*/
function download_count_entry_load($dcid) {
return $dcid == 'all' ? $dcid : db_fetch_array(db_query('SELECT dc.dcid, dc.fid, dc.nid, dc.uid, dc.ip_address, dc.referrer, dc.timestamp, f.filename, f.filesize FROM {download_count} dc JOIN {files} f ON dc.fid = f.fid WHERE dcid = %d', $dcid));
}
/**
* Implementation of hook_nodeapi().
*/
function download_count_nodeapi(&$node, $op, $teaser) {
$result = array();
switch ($op) {
case 'view':
if (isset($node->files) && count($node->files) && user_access('view uploaded files') && !$teaser) {
global $user;
if (user_access('view own download counts') && $user->uid != 1) {
$result = db_query("SELECT dc.fid, f.filename, COUNT(dc.dcid) AS count, MAX(dc.timestamp) AS last FROM {download_count} dc JOIN {files} f ON dc.fid = f.fid WHERE dc.nid = %d AND dc.uid = %d GROUP BY dc.fid, f.filename", $node->nid, $user->uid);
}
elseif (user_access('view all download counts')) {
$result = db_query("SELECT dc.fid, f.filename, COUNT(dc.dcid) AS count, MAX(dc.timestamp) AS last FROM {download_count} dc JOIN {files} f ON dc.fid = f.fid WHERE dc.nid = %d GROUP BY dc.fid, f.filename", $node->nid);
}
while ($download = db_fetch_object($result)) {
$downloads[$download->filename]['count'] = $download->count;
$downloads[$download->filename]['last'] = $download->last;
$node->files[$download->fid]->downloads = $download->count;
}
if (isset($downloads)) {
$node->content['files']['#value'] = theme('download_count_upload_attachments', $node->files, $downloads);
}
}
break;
}
}
/**
* Implementation of hook_theme().
*/
function download_count_theme() {
return array(
'download_count_upload_attachments' => array(
'arguments' => array(
'files' => NULL,
'downloads' => NULL,
),
'file' => 'includes/download_count.theme.inc',
),
'download_count_formatter_download_count' => array(
'arguments' => array(
'element' => NULL,
),
'file' => 'includes/download_count.theme.inc',
),
);
}
function download_count_field_formatter_info() {
return array(
'download_count' => array(
'label' => t('Generic files with download count'),
'field types' => array(
'filefield',
),
'multiple values' => CONTENT_HANDLE_CORE,
'description' => t('Displays all kinds of files with an icon and a linked file description with download count information.'),
),
);
}
/**
* Implementation of hook_view_api().
*/
function download_count_views_api() {
return array(
'api' => 2.0,
'path' => drupal_get_path('module', 'download_count') . '/includes',
);
}
/**
* Implementation of hook_file_download().
*/
function download_count_file_download($filename, $checkonly = FALSE) {
// Special use of hook_file_download() - 2nd argument added to indicate that it is not a download, but only an access check.
if ($checkonly) {
return;
}
global $user;
$extensions = explode(' ', drupal_strtolower(trim(variable_get('download_count_excluded_file_extensions', 'jpg jpeg gif png'))));
if (count($extensions)) {
$pathinfo = pathinfo($filename);
if (in_array(drupal_strtolower($pathinfo['extension']), $extensions)) {
return;
}
}
$filepath = file_create_path($filename);
// NULL: not known
// FALSE: not accessible
// TRUE: accessible
$accessible_file = NULL;
// check if the file is known by Upload
$accessible_file = _download_count_is_accessible_by_upload($filepath);
if ($accessible_file === NULL) {
// check if the file is known by CCK FileField
$accessible_file = _download_count_is_accessible_by_filefield($filepath);
}
if ($accessible_file === NULL) {
// not known by any hooks, so we don't care about this file
return;
}
// inaccessible file
if ($accessible_file === FALSE) {
watchdog('download_count', 'Failed to download %file', array(
'%file' => $filename,
), WATCHDOG_ERROR);
return;
}
// accessible file
if ($fileinfo = _download_count_get_file_by_upload($filepath)) {
// core upload file
$fid = $fileinfo->fid;
$nid = $fileinfo->nid;
$vid = $fileinfo->vid;
}
elseif ($fileinfo = _download_count_get_nodes_by_filefield($filepath)) {
//cck filefield
$fid = db_result(db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $filepath));
$node = array_pop($fileinfo);
$nid = $node->nid;
$vid = $node->vid;
}
$ip = ip_address();
$referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : NULL;
$time = time();
db_query("INSERT INTO {download_count} (fid, nid, uid, vid, ip_address, referrer, timestamp) VALUES (%d, %d, %d, %d, '%s', '%s', %d)", $fid, $nid, $user->uid, $vid, $ip, $referrer, $time);
watchdog('download_count', '%file was downloaded', array(
'%file' => $filename,
), WATCHDOG_NOTICE);
if (module_exists('rules')) {
rules_invoke_event('download_count_file_download', $pathinfo['basename'], $user, $nid, $ip, $referrer, $time);
}
}
/**
* Implementation of hook_block().
*
*/
function download_count_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks['files']['info'] = t('Top Downloaded Files');
$blocks['downloaders']['info'] = t('Top Downloaders');
$blocks['users']['info'] = t('Top Downloaded Users');
$blocks['nodes']['info'] = t('Top Downloaded Nodes');
return $blocks;
case 'configure':
$form['download_count_' . $delta . '_block_limit'] = array(
'#type' => 'textfield',
'#title' => t('Number of items to display'),
'#size' => 5,
'#default_value' => variable_get('download_count_' . $delta . '_block_limit', 10),
);
$form['download_count_' . $delta . '_show_size'] = array(
'#type' => 'checkbox',
'#title' => t('Display aggregated filesize'),
'#default_value' => variable_get('download_count_' . $delta . '_show_size', 0),
);
$form['download_count_' . $delta . '_show_last'] = array(
'#type' => 'checkbox',
'#title' => t('Display last download datetime'),
'#default_value' => variable_get('download_count_' . $delta . '_show_last', 0),
);
if ($delta == 'files') {
$form['download_count_files_file_links'] = array(
'#type' => 'checkbox',
'#title' => t('Display files names as links (based on permissions).'),
'#default_value' => variable_get('download_count_files_file_links', 1),
);
}
return $form;
case 'save':
variable_set('download_count_' . $delta . '_block_limit', $edit['download_count_' . $delta . '_block_limit']);
variable_set('download_count_' . $delta . '_show_size', $edit['download_count_' . $delta . '_show_size']);
variable_set('download_count_' . $delta . '_show_last', $edit['download_count_' . $delta . '_show_last']);
$delta == 'files' ? variable_set('download_count_files_file_links', $edit['download_count_files_file_links']) : NULL;
break;
case 'view':
switch ($delta) {
case 'files':
$blocks['subject'] = t('Top Downloaded Files');
$blocks['content'] = _download_count_block_contents('files');
break;
case 'downloaders':
$blocks['subject'] = t('Top Downloaders');
$blocks['content'] = _download_count_block_contents('downloaders');
break;
case 'users':
$blocks['subject'] = t('Top Downloaded Users');
$blocks['content'] = _download_count_block_contents('users');
break;
case 'nodes':
$blocks['subject'] = t('Top Downloaded Nodes');
$blocks['content'] = _download_count_block_contents('nodes');
break;
}
return $blocks;
}
}
/**
* internal functions
*/
function _download_count_get_file_by_upload($filepath) {
// check if Upload is enabled
if (!function_exists('upload_perm')) {
return NULL;
}
// upload not enabled
$result = db_query("SELECT u.nid, u.vid, f.filepath, f.fid FROM {upload} u JOIN {files} f ON f.fid = u.fid WHERE f.filepath = '%s'", $filepath);
return db_fetch_object($result);
}
function _download_count_is_accessible_by_upload($filepath) {
if ($file = _download_count_get_file_by_upload($filepath)) {
if (user_access('view uploaded files') && node_access('view', node_load($file->nid))) {
return TRUE;
// accessible
}
else {
return FALSE;
// inaccessible
}
}
return NULL;
// not known
}
function _download_count_is_accessible_by_filefield($file) {
$nodes = _download_count_get_nodes_by_filefield($file);
if ($nodes === FALSE) {
return FALSE;
}
if ($nodes === NULL) {
return NULL;
}
return TRUE;
}
function _download_count_get_nodes_by_filefield($file) {
// check if FileField is enabled
if (!function_exists('filefield_view_access')) {
return NULL;
}
// not enabled
// The following logic is snipped from filefield.module 1.209 2009/10/20 17:46:22
// which is part of CCK FileField 6.x-3.2 release, see http://drupal.org/project/filefield
$result = db_query("SELECT * FROM {files} WHERE filepath = '%s'", $file);
if (!($file = db_fetch_object($result))) {
// We don't really care about this file.
return NULL;
// not known
}
// Find out if any file field contains this file, and if so, which field
// and node it belongs to. Required for later access checking.
$cck_files = array();
foreach (content_fields() as $field) {
if ($field['type'] == 'filefield' || $field['type'] == 'image') {
$db_info = content_database_info($field);
$table = $db_info['table'];
$fid_column = $db_info['columns']['fid']['column'];
$columns = array(
'vid',
'nid',
);
foreach ($db_info['columns'] as $property_name => $column_info) {
$columns[] = $column_info['column'] . ' AS ' . $property_name;
}
$result = db_query("SELECT " . implode(', ', $columns) . "\n FROM {" . $table . "}\n WHERE " . $fid_column . " = %d", $file->fid);
while ($content = db_fetch_array($result)) {
$content['field'] = $field;
$cck_files[$field['field_name']][$content['vid']] = $content;
}
}
}
// If no file field item is involved with this file, we don't care about it.
if (empty($cck_files)) {
return NULL;
// not known
}
// If any node includes this file but the user may not view this field,
// then deny the download.
foreach ($cck_files as $field_name => $field_files) {
if (!filefield_view_access($field_name)) {
return FALSE;
// inaccessible
}
}
// So the overall field view permissions are not denied, but if access is
// denied for ALL nodes containing the file, deny the download as well.
// Node access checks also include checking for 'access content'.
$nodes = array();
$denied = FALSE;
foreach ($cck_files as $field_name => $field_files) {
foreach ($field_files as $revision_id => $content) {
// Checking separately for each revision is probably not the best idea -
// what if 'view revisions' is disabled? So, let's just check for the
// current revision of that node.
if (isset($nodes[$content['nid']])) {
continue;
// Don't check the same node twice.
}
if ($denied == FALSE && ($node = node_load($content['nid'])) && node_access('view', $node) == FALSE) {
// You don't have permission to view the node this file is attached to.
$denied = TRUE;
}
$nodes[$content['nid']] = $node;
}
if ($denied) {
return FALSE;
// inaccessible
}
}
// Access is granted.
return $nodes;
}
function _download_count_block_contents($block) {
$result = '';
$limit = variable_get('download_count_' . $block . '_block_limit', 10);
$show_size = variable_get('download_count_' . $block . '_show_size', 0);
$show_last = variable_get('download_count_' . $block . '_show_last', 0);
$block == 'files' ? $file_links = variable_get('download_count_files_file_links', 1) : NULL;
switch ($block) {
case 'files':
$rows = array();
$header[] = array(
'data' => t('Filename'),
'class' => 'filename',
);
$header[] = $show_size ? array(
'data' => t('Size'),
'class' => 'size',
) : '';
$header[] = array(
'data' => t('Count'),
'class' => 'count',
);
$header[] = $show_last ? array(
'data' => t('Last Downloaded'),
'class' => 'last',
) : '';
if (user_access('view all download counts')) {
$result = db_query('SELECT COUNT(dc.dcid) AS count, f.filename, f.filepath, f.fid, SUM(f.filesize) AS size, MAX(dc.timestamp) as last FROM {download_count} dc JOIN {files} f ON dc.fid = f.fid GROUP BY f.filename ORDER BY count DESC LIMIT %d', (int) $limit);
}
while ($file = db_fetch_object($result)) {
$row = array();
$row[] = $file_links && (user_access('view uploaded files') || _download_count_is_accessible_by_filefield($file->filepath)) ? l(t('@filename', array(
'@filename' => $file->filename,
)), function_exists('_private_upload_create_url') ? _private_upload_create_url($file) : file_create_url($file->filepath)) : check_plain($file->filename);
$row[] = $show_size ? format_size($file->size) : '';
$row[] = $file->count;
$row[] = $show_last ? t('@time ago', array(
'@time' => format_interval(time() - $file->last),
)) : '';
$rows[] = $row;
}
if (count($rows)) {
return theme('table', $header, $rows, array(
'class' => 'no-sticky',
));
}
return;
case 'downloaders':
$rows = array();
$header[] = array(
'data' => t('User'),
'class' => 'user',
);
$header[] = $show_size ? array(
'data' => t('Size'),
'class' => 'size',
) : '';
$header[] = array(
'data' => t('Count'),
'class' => 'count',
);
$header[] = $show_last ? array(
'data' => t('Last Downloaded'),
'class' => 'last',
) : '';
if (user_access('view all download counts')) {
$result = db_query('SELECT COUNT(dc.dcid) AS count, SUM(f.filesize) AS size, u.name, u.uid, MAX(dc.timestamp) as last FROM {download_count} dc JOIN {users} u ON dc.uid = u.uid JOIN {files} f on dc.fid = f.fid GROUP BY u.name ORDER BY count DESC LIMIT %d', (int) $limit);
}
while ($file = db_fetch_object($result)) {
$row = array();
$row[] = l($file->name, 'user/' . $file->uid);
$row[] = $show_size ? format_size($file->size) : '';
$row[] = $file->count;
$row[] = $show_last ? t('@time ago', array(
'@time' => format_interval(time() - $file->last),
)) : '';
$rows[] = $row;
}
if (count($rows)) {
return theme('table', $header, $rows, array(
'class' => 'no-sticky',
));
}
return;
case 'users':
$rows = array();
$header[] = array(
'data' => t('User'),
'class' => 'user',
);
$header[] = $show_size ? array(
'data' => t('Size'),
'class' => 'size',
) : '';
$header[] = array(
'data' => t('Count'),
'class' => 'count',
);
$header[] = $show_last ? array(
'data' => t('Last Downloaded'),
'class' => 'last',
) : '';
if (user_access('view all download counts')) {
$result = db_query('SELECT COUNT(dc.dcid) AS count, SUM(f.filesize) AS size, u.name, u.uid, MAX(dc.timestamp) as last FROM {download_count} dc JOIN {files} f on dc.fid = f.fid JOIN {users} u ON f.uid = u.uid GROUP BY u.name ORDER BY count DESC LIMIT %d', (int) $limit);
}
while ($file = db_fetch_object($result)) {
$row = array();
$row[] = l($file->name, 'user/' . $file->uid);
$row[] = $show_size ? format_size($file->size) : '';
$row[] = $file->count;
$row[] = $show_last ? t('@time ago', array(
'@time' => format_interval(time() - $file->last),
)) : '';
$rows[] = $row;
}
if (count($rows)) {
return theme('table', $header, $rows, array(
'class' => 'no-sticky',
));
}
return;
case 'nodes':
$rows = array();
$header[] = array(
'data' => t('Page'),
'class' => 'node',
);
$header[] = $show_size ? array(
'data' => t('Size'),
'class' => 'size',
) : '';
$header[] = array(
'data' => t('Count'),
'class' => 'count',
);
$header[] = $show_last ? array(
'data' => t('Last Downloaded'),
'class' => 'last',
) : '';
if (user_access('view all download counts')) {
$result = db_query('SELECT COUNT(dc.dcid) AS count, SUM(f.filesize) AS size, dc.nid, MAX(dc.timestamp) as last FROM {download_count} dc JOIN {files} f on dc.fid = f.fid GROUP BY dc.nid ORDER BY count DESC LIMIT %d', (int) $limit);
}
while ($file = db_fetch_object($result)) {
$row = array();
$node = node_load($file->nid);
$row[] = node_access('view', $node) ? l($node->title, 'node/' . $node->nid) : check_plain($node->title);
$row[] = $show_size ? format_size($file->size) : '';
$row[] = $file->count;
$row[] = $show_last ? t('@time ago', array(
'@time' => format_interval(time() - $file->last),
)) : '';
$rows[] = $row;
}
if (count($rows)) {
return theme('table', $header, $rows, array(
'class' => 'no-sticky',
));
}
return;
default:
return;
}
}
/**
* Implementation of hook_rules_event_info().
* @ingroup rules
*/
function download_count_rules_event_info() {
return array(
'download_count_file_download' => array(
'label' => t('A file has been downloaded'),
'module' => 'Download Count',
'arguments' => array(
'filename' => array(
'type' => 'file',
'label' => t('Filename of the downloaded file.'),
),
'user' => array(
'type' => 'file',
'label' => t('User object of the user downloading the file.'),
),
'nid' => array(
'type' => 'file',
'label' => t('Node from which the file was downloaded.'),
),
'ip' => array(
'type' => 'file',
'label' => t('IP address to which the file was downloaded.'),
),
'referrer' => array(
'type' => 'file',
'label' => t('HTTP Referrer of the download link.'),
),
'time' => array(
'type' => 'file',
'label' => t('Timestamp of the download.'),
),
),
),
);
}
Functions
Name![]() |
Description |
---|---|
download_count_block | Implementation of hook_block(). |
download_count_entry_load | Menu wildcard loader. |
download_count_field_formatter_info | |
download_count_file_download | Implementation of hook_file_download(). |
download_count_help | Implementation of hook_help(). |
download_count_menu | Implementation of hook_menu(). |
download_count_nodeapi | Implementation of hook_nodeapi(). |
download_count_perm | Implementation of hook_perm(). |
download_count_rules_event_info | Implementation of hook_rules_event_info(). |
download_count_theme | Implementation of hook_theme(). |
download_count_views_api | Implementation of hook_view_api(). |
_download_count_block_contents | |
_download_count_get_file_by_upload | internal functions |
_download_count_get_nodes_by_filefield | |
_download_count_is_accessible_by_filefield | |
_download_count_is_accessible_by_upload |