downloads counted per user with filemanager/attachment modules

jwade - October 25, 2007 - 22:56
Project:download_count
Version:5.x-1.x-dev
Component:Code
Category:feature request
Priority:normal
Assigned:jwade
Status:closed
Description

I am using the attachment/filemanager module combination to enable file titles and descriptions for downloadable files. What this combo does not support is counting downloads (similar to the download_count module: http://drupal.org/project/download_count). In particular, I want to be able to count downloads per user as described in: http://drupal.org/node/124925

I'm not sure whether it would be easier to make download_count work with attachment/filemanager, or whether to modify either the filemanager or attachment modules to support this. If anyone has advice on the shortest distance to this goal, please reply.

Regards,
~ j.Wade

#1

cscsteve - October 27, 2007 - 20:02

As neither Attachment nor Filemanager use the user information (attachment calls a function to check permissions, but never directly looks at the user data), neither are probably the place to add this information.

I think the best place for it would be in the module that already is supposed to do this function, download_count. I'm sure the maintainer of that module would appreciate it if you made the module work transparently with both the standard Drupal uploads module as well as Filemanager.

If you need any hooks for notification added into Filemanager, please let me know, or provide the patch with the new hook.

Good luck,
- Steve

#2

jwade - October 30, 2007 - 21:01

I think the best place for it would be in the module that already is supposed to do this function, download_count. …
If you need any hooks for notification added into Filemanager, please let me know, or provide the patch with the new hook.

It appears that the download_count module relies on the file_download() hook. [http://api.drupal.org/api/function/file_download/5].

I have two remaining issues:

1.) How to modify download_count (and or filemanager) to use a similar hook to log downloads in the database?

2.) How to list either/or requirements in download_count.info?

It does not appear too difficult to modify download_count to work with filemanager INSTEAD of the core Upload module, but I do not know if it is even possible to get it to work with EITHER of the two modules.

Most of my PHP experience involves procedural programming. Groking object-oriented PHP and the Drupal API is a bit of a stretch for me. Any advice or pointers to the relevant docs is greatly appreciated.

Regards,
~ j.Wade

#3

cscsteve - November 1, 2007 - 22:06
Project:Attachment» download_count
Version:5.x-1.x-dev» 5.x-1.x-dev

filemanager has a hook that is similar: hook_filemanager_download().

I don't know download_count's code, but likely:

1. Move the code currently in download_count_file_download to a new function like _download_count_fd(). Make download_count_file_download call this new function.
2. Create a new fn for the filemanager_download() hook and have it call the new common function.

The above assumes the same code will work for both uploads and filemanager. This might not be the case. I see a DB query in there. Assuming they need to be different, then keep the code in file_download, copy that code and create the filemanager_download() and modify the code to make it work. Other functions might need modifications also, I don't know.

You can test for filemanager or upload modules using the module_exists() function.

I don't know if this can easily be added to download_count, or if you need to fork the project to create a filemanager version.

the .info file doesn't support or clauses on the required modules AFAIK. Not sure what to do here, so the above suggestion might need to happen.

I'm moving this over to the download_count project as this seems more related there than to Attachment. Also, I'm guessing the maintainers there should be able to help you with that code better.

- Steve

#4

jwade - November 8, 2007 - 06:11
Title:need downloads to be counted per user» downloads counted per user with filemanager/attachment modules
Status:active» patch (code needs work)

Here is my first crack at the forked version of the download_count module. The goal is to get it to count downloads of files attached via the filemanager/attachment combination of modules.

There is still quite a but of work to do. I know that some of the descriptions still refer to the Upload module. The good news is that things look okay for the most part … nothing is broken. The bad news is that it isn't logging downloads to the database, neither via watchdog, nor direct sql insert/update queries (which is the whole goal of this project!).

The download_count module consists of 3 files: .info, .install, and .module.

download_count.info:

; $Id$
name = download_count
description = "Increments a download counter and logs a descriptive message each time an attached file is downloaded."
dependencies = filemanager attachment

download_count.install

<?php
// $Id$

function download_count_install() {
  switch (
$GLOBALS['db_type']) {
    case
'mysql':
    case
'mysqli':
    
db_query("CREATE TABLE if not exists {file_downloads} (
          fid int(10) NOT NULL,
          count int(11) NOT NULL default '0',
          `timestamp` int(11) NOT NULL default '0',
          uid int(11) NOT NULL default '0',
          hostname varchar(15) NOT NULL default '',
          UNIQUE KEY fid (fid,uid)
        ) /*!40100 DEFAULT CHARACTER SET utf8 */"
);
      break;
    case
'pgsql':
     
db_query("CREATE TABLE if not exists {file_downloads} (
        did serial NOT NULL,
        fid integer DEFAULT 0 NOT NULL,
        count integer DEFAULT 0 NOT NULL,
         \"timestamp\" integer DEFAULT 0 NOT NULL,
        uid integer DEFAULT 0 NOT NULL,
        hostname character varying(15) NOT NULL
        )"
);
      
db_query("ALTER TABLE ONLY {file_downloads} ADD CONSTRAINT {file_downloads}_pkey PRIMARY KEY (did)");
      break;
  }
}

function
download_count_uninstall() {
  
db_query("DROP TABLE {file_downloads}");
}
?>

download_count.module:

<?php /* $Id jwade $ */
/**
* @file Download counter
*/
/**
* Implementation of hook_help()
*/
function download_count_help($section) {
  switch (
$section) {
    case
'admin/modules#description':
     
// This description is shown in the listing at admin/modules.
     
return t('Increments a download counter and logs a descriptive message each time an attached file is downloaded.');
  }
}
/**
* Implementation of hook_perm()
*/
function download_count_perm() {
 
$perms = array();
 
$perms[] = 'view all downloads count';
 
$perms[] = 'view own nodes downloads count';
 
$perms[] = 'view all downloads count in nodes';
 
$perms[] = 'view own nodes downloads count in nodes';
  return
$perms;
}
/**
* Implementation of hook_menu()
*/
function download_count_menu($may_cache) {
 
$items = array();
 
$access = user_access('view all downloads count') || user_access('view own nodes downloads count');

  if (
$may_cache) { 
   
$items[] = array('path' => 'admin/settings/download_count',
     
'description' => t('Increments a download counter and logs a descriptive message each time an attached file is downloaded.'),
     
'title' => t('Download count'),
     
'callback' => 'drupal_get_form',
     
'callback arguments' => 'download_count_admin_settings',
     
'type' => MENU_NORMAL_ITEM);
     
   
$items[] = array('path' => 'download_counter',
     
'title' => t('Download counter'),
     
'callback' => 'download_count_view_page',
     
'access' => $access,
     
'type' => MENU_NORMAL_ITEM);
  }
  return
$items;
}

function
download_count_admin_settings() {

   switch (
variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
    case
FILE_DOWNLOADS_PUBLIC:
     
$output = '<p>' . t('You are using public download. Set your download method to private on the ') . l('admin/settings', 'admin/settings') . t(' page (go in "File system"), if you wish for Drupal to keep a record of downloaded files.') . '</p>';
      break;
    case
FILE_DOWNLOADS_PRIVATE:
     
$output = '<p>' . t('You are using the private download method, hence you have the correct setting to allow this module to keep a record of downloaded files.') . '</p>';
      break;
  }
 
 
$form['info on download method'] = array(
   
'#value' => $output,
  );

 
$form['ignoring a set of file extensions'] = array(
   
'#type' => 'fieldset',
   
'#title' => t('Ignoring certain file extensions'),
   
'#collapsible' => TRUE,
   
'#collapsed' => FALSE,
  );
 
 
$form['ignoring a set of file extensions']['download_count_excluded_file_extensions'] = array(
     
'#type' => 'textfield',
     
'#title' => t('Excluded file extensions'),
     
'#default_value' => variable_get('download_count_excluded_file_extensions', 'jpg jpeg gif png'),
     
'#maxlength' => 255,
     
'#description' => t('This module only considers files that have been uploaded with the upload module and that become file attachments.  However, if you are using a contributed module to upload images and display them in the body of nodes, these files may get flagged as downloaded whenever a visitor or robot views the node page, because strictly speaking they are file attachments. This will happen with the module img_assist. Note that this won\'t happen with the module imce, because imce treats inline images as nodes. If you do not want to set a download counter for image files, list their extension here. Separate extensions with a space and do not include the leading dot. For example, you could list these extensions&nbsp: jpg jpeg gif png. If you do not want to exclude any file, leave that field blank. ')
   );
   
 
$form['dounload count in nodes'] = array(
   
'#type' => 'fieldset',
   
'#title' => t('Download count in nodes'),
   
'#collapsible' => TRUE,
   
'#collapsed' => FALSE,
  );
 
 
$form['dounload count in nodes']['do_not_show_download_count_in_node_for_admin'] = array(
   
'#type' => 'checkbox',
   
'#title' => t('Do not show download count in the node view for the administrator'),
   
'#default_value' => variable_get('do_not_show_download_count_in_node_for_admin', FALSE),
   
'#description' => t('Check this if you do not want to see the download count in the node view.'),
  );
 
 
$form['download counter page'] = array(
   
'#type' => 'fieldset',
   
'#title' => t('Download counter page'),
   
'#collapsible' => TRUE,
   
'#collapsed' => FALSE,
  );
 
 
$form['download counter page']['download_counter_view_page_title'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Title'),
   
'#default_value' => variable_get('download_counter_view_page_title', t('Download counter')),
   
'#description' => t('Title of this ') . l('page', 'download_counter') . '.',
  );
 
 
$form['download counter page']['download_counter_view_page_header'] = array(
   
'#type' => 'textarea',
   
'#title' => t('Header'),
   
'#cols' => 60,
   
'#rows' => 6,
   
'#default_value' => variable_get('download_counter_view_page_header', t('')),
   
'#description' => t('Text to appear between the title of the page and the download counter table.'),
  );

 
$form['download counter page']['download_counter_view_page_format'] = filter_form(variable_get('download_counter_view_page_format', 2), 0, array('download_counter_view_page_format'));
 
 
$form['download counter page']['download_counter_view_page_footer'] = array(
   
'#type' => 'textarea',
   
'#title' => t('Footer'),
   
'#cols' => 60,
   
'#rows' => 6,
   
'#default_value' => variable_get('download_counter_view_page_footer', t('')),
   
'#description' => t('Text to appear underneath the download counter table.'),
  );
 
 
$form['info_on_access control'] = array(
   
'#value' => '<p>' . t('Visit the  ') . l('control access page', 'admin/user/access') . ' to allow roles to view download count in nodes and on the download counter page.</p>',
  );
 
  return
system_settings_form($form);
}

function
download_count_view_page() {

  global
$user;

 
drupal_set_title(variable_get('download_counter_view_page_title', t('Download counter')));

 
$header[] = array('data' => t('filename'), 'field' => 'filename');
 
$header[] = array('data' => t('user'), 'field' => 'uid');
 
$header[] = array('data' => t('hits'), 'field' => 'count', 'sort' => 'desc');
 
$header[] = array('data' => t('last download'), 'field' => 'timestamp');
 
 
$rows = array();

  if(
user_access('view all downloads count')) {
     
$result = db_query("SELECT f.filename, fd.count, fd.timestamp, fd.uid, fd.hostname FROM {file_downloads} fd JOIN {file} f ON f.fid = fd.fid" . tablesort_sql($header));
    }
  
  while (
$file = db_fetch_object($result)) {
   
$row = array();
   
$row[] = $file->filename;
     
$user = user_load(array(uid=>$file->uid));
   
$row[] = l($user->name, 'user/'.$user->uid);
   
$row[] = $file->count;  
   
$row[] = format_interval(time() - $file->timestamp) . ' ago';  
   
$rows[] = $row;   
  }

  if (empty(
$rows)) {
   
$rows[] = array(array('data' => t('No file attachment has been downloaded.'), 'colspan' => '4'));
  }

 
$output = check_markup(variable_get('download_counter_view_page_header', ''),
                  
variable_get('download_counter_view_page_format', 0),
                  
false);

 
$output .= theme('table', $header, $rows, array('class' => 'download_count'));
 
 
$output .= check_markup(variable_get('download_counter_view_page_footer', ''),
                  
variable_get('download_counter_view_page_format', 0),
                  
false);
 
  return
$output;
}
/**
* Implementation of hook_filemanager_download()
*/
function download_count_filemanager_download($file) {

  global
$user;

 
$extensions = explode(' ', trim(variable_get('download_count_excluded_file_extensions', '')));
  if (
count($extensions)) {
    if (
in_array(substr($filename, strpos($filename, '.') + 1), $extensions)) {
      return;
    }
  }

  if (
$file = filemanager_get_file_info($file)) {
   
$message = t('%file was downloaded', array('%file' => $file->filename));
   
watchdog('download', $message, WATCHDOG_NOTICE);       
   
// If the file is already added, just increment the count,
    // otherwise add the file with count 1
   
if(db_result(db_query("SELECT fid FROM {file_downloads} WHERE fid = '%d' AND uid = '%d'", $file->fid, $user->uid))) {
     
db_query("UPDATE {file_downloads} SET count = count+1, timestamp = %d  WHERE fid = '%d' AND uid = '%d'", time(), $file->fid, $user->uid);
    }
    else {
     
db_query("INSERT INTO {file_downloads} (fid, count, timestamp, uid, hostname) VALUES ('%d', 1, %d, %d, '%s')", $file->fid, time(), $user->uid, $user->hostname);
    }
  }
  else {
   
$msg = t('%file was NOT downloaded', array('%file' => $file->filename));
   
watchdog('download', $msg, WATCHDOG_NOTICE);
  }
}
/**
* Implementation of hook_nodeapi()
*/
function download_count_nodeapi(&$node, $op) {
  if (
$op == 'alter') {
    global
$user;
    if (
$user->uid == 1 && variable_get('do_not_show_download_count_in_node_for_admin', FALSE)) {
          return;
      }
    if (!
$node->teaser && count($node->attachments)) {
      if (
user_access('view all downloads count in nodes') || (user_access('view own nodes downloads count in nodes') && ($node->uid == $user->uid)) ) {
         
$node->body = theme('download_count_body', $node);
      }
    }
  }
}

function
theme_download_count_body($node) {

 
$header[] = array('data' => t('Attachment'));
 
$header[] = array('data' => t('User'));
 
$header[] = array('data' => t('Hits'));
 
$header[] = array('data' => t('Last download'));
 
$rows = array();
 
  foreach ((array)
$node->attachments as $attachment) {
   
$href = filemanager_url($attachment['fid']);
   
$text = $attachment['title'] ? $attachment['title'] : $attachment['filename'];
   
$pick = db_query("SELECT f.filename, u.name, fd.count, fd.timestamp FROM {file_downloads} fd LEFT JOIN {file} f ON fd.fid = f.fid JOIN {users} u ON fd.uid = u.uid WHERE fd.fid = '%d' GROUP BY u.name", $attachment['fid']);
    while (
$attach = db_fetch_object($pick)){
     
$count = $attach->count;
     
$last = format_interval(time() - $attach->timestamp) . ' ago';   
     
$rows[] = array(l($text, $href), $attach->name, $count, $last);
    }
    if (
is_null($attach)) {
       
$count = 0;
       
$last = t('Not yet downloaded');
       
$rows[] = array(l($text, $href), "--", $count, $last);
    }
  }
  if (
count($rows)) {
   
$attachments = theme('table', $header, $rows, array('id' => 'attachments'));
  }
  else {
   
$attachments = '';
  }

  if (
strstr($node->body, '<table id="attachments"')) {
   
$start = strpos ($node->body, '<table id="attachments"');
   
$end = strpos ($node->body, '</table>', $start);
      
// return substr_replace($node->body, '', $start, $end - $start + 8) . $attachments;
   
return substr_replace($node->body, $attachments, $start, $end - $start + 8);  
  }
  else {
    return
$node->body . $attachments;  
  }

}
?>

#5

jwade - November 20, 2007 - 21:15
Assigned to:Anonymous» jwade
Status:patch (code needs work)» fixed

I now have a working version of this module, and I have decided to call it "download_count_fm". (download_count, of course, comes from the original module; the appended fm is for filemanager.)

I will apply for drupal CVS access so I can add this to the available modules, but for now, I have attached a gzipped tar-ball of the module files.

All comments are welcome. Enjoy!

~ j.Wade

AttachmentSize
download_count_fm.tar.gz9.2 KB

#6

Anonymous - December 4, 2007 - 22:45
Status:fixed» closed

Automatically closed -- issue fixed for two weeks with no activity.

#7

soundsational - January 10, 2008 - 22:59

I get an error msg after installing this module when i view the download count page. Error msg:

user warning: Unknown column 'fd.uid' in 'field list' query: SELECT f.filename, fd.uid, fd.count, fd.timestamp FROM file_downloads fd JOIN file f ON f.fid = fd.fid ORDER BY count DESC in /Applications/MAMP/htdocs/drupal5/includes/database.mysqli.inc on line 151.

I'm running Drupal 5.5 using PHP 5.2 and mySQL 5

filemanager-5.x-1.x-dev

dowload_count-5.x-1.x-dev

attachment-5.x-1.x-dev

thanks,

#8

ezunkwelle - June 30, 2008 - 16:15

Subsribing

#9

kulvik - September 3, 2008 - 11:59

Good work. I'm going to test this later today. Exactly what I was looking for :)

 
 

Drupal is a registered trademark of Dries Buytaert.