I suddenly realized that though file system settings say the downloads are handled through Drupal, I can't find the download count for each attachment anywhere. Am I missing something, or doesn't Drupal really save the download count anywhere?

If Drupal doesn't have this feature (how could it be true), is there a module that could just give this feature?
It's not desired to upload every attachment as a node and then add the node as an attachment through some huge and clever file management module, since I've already have around 400 nodes with audio attachments which I wouldn't wanna go through - just wanna see how many times each attachment has been downloaded.

Comments

tormu’s picture

Woot? So simple thing and it's not there, how can it be? :(

tormu’s picture

Sounds insane that this wouldn't already be on the core, to show the amount of times the attachment was downloaded for example on the node/edit page :O

Well, anyway here's how I did it:
We have a custom module, so I just added an file_download implementation to the module, because I saw that whenever a file is downloaded through Drupal, it will call file_download-function, and that calls all modules for a function with the same name:
module_invoke_all('file_download', $filepath); (http://api.drupal.org/api/4.7/function/file_download)

So, just made a modulename_file_download-function to my module and added a database table that will contain the information. It's not hard to modify it to also get the related nodeId and the fileId and save those too, but this will do it for now :)

function modulename_file_download($filepath)
{
  if(substr($filepath, -4) == '.mp3')
  {
    //If the file is already added, just increment the count, otherwise add the file with count 1
    if(db_result(db_query("SELECT filename FROM file_downloads WHERE filename = '$filepath'")))
      db_query("UPDATE {file_downloads} SET count = count+1 WHERE filename = '$filepath'");
    else
      db_query("INSERT INTO {file_downloads} (filename, count) VALUES ('$filepath', 1)");
  }
}
Chill35’s picture

I though that by using Private Downloads, there would at least be a log each time someone downloads an attached file ? There isn't. Am I missing something ?

Where do I need to hack the code so that Drupal logs a message each time a file attachement is downloaded ? I need the following info in each log :

- what file was downloaded
- by whom (if anonymous, I need the IP address)
- when

ANYONE who can help, please reply !!!!

Caroline

Chill35’s picture

I guess I have to use a hook function and another one, watchdog-something...?

Caroline

Chill35’s picture

Can someone provide information on how to show number of dowloads next to the download file in the node for someone who has "perm" as in permission to see it ? :)

So far, I created a module : download_count

download_count.install (can't figure out the pgsql case) :

function download_count_install() {
  switch ($GLOBALS['db_type']) {
    case 'mysql':
    case 'mysqli':
      db_query("CREATE TABLE IF NOT EXISTS {file_downloads} (
        filename varchar(255) character set utf8 NOT NULL,
        count int(11) NOT NULL default '0') 
      ");
      break;
    case 'pgsql':
      break;
  }
}

download_count.module :

/**
 * Implementation of file_download()
 */
function download_count_file_download($filepath) {
      watchdog('download', t('%file was downloaded', array('%file' => theme('placeholder', t($filepath)))), WATCHDOG_NOTICE);
    // If the file is already in the table, increment its count, otherwise add the filename to the table with count=1
    if(db_result(db_query("SELECT filename FROM {file_downloads} WHERE filename = '$filepath'")))
      db_query("UPDATE {file_downloads} SET count = count+1 WHERE filename = '$filepath'");
    else
      db_query("INSERT INTO {file_downloads} (filename, count) VALUES ('$filepath', 1)");
}<

The table is created with the install file and it gets properly updated. So does the "log". I now need to use
some hook function (which one ?) in nodes with attachments to show "x downloads" next to the download file name,
for visitors with special perm.

Caroline

tormu’s picture

I think it's hook_link, going somehow like this.. Haven't really tried, just copy-pasted the implementation of the hook_link from the Spam-module, which makes custom links too.

function spam_link($type, $node = 0, $main = 0) {
  $links = array();
  if ($type == 'comment') {
    // allow comment spam links to be themed
    if ($spam_link = theme('spam_link', $node, 'comment')) {
      $links[] = $spam_link;
    }
  }
  if ($type == 'node') {
    // allow node spam links to be themed
    if ($spam_link = theme('spam_link', $node, 'node')) {
      $links[] = $spam_link;
    }
  }

  return ($links);
}
Chill35’s picture

I think it's theme_upload_attachments($files) in upload.module...

Except I don't know how to modify this in a separate module... I would only know how to hack upload.module...

Anyone has an idea ?

Caroline
11 heavens

Chill35’s picture

To create the table :

CREATE TABLE `file_downloads` (
`filename` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`count` INT( 11 ) NOT NULL DEFAULT '0'
) ENGINE = MYISAM ;

Caroline

Chill35’s picture

I modified download_count.module :


 /**
 * 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 filename counter each time an attached file is downloaded. Also logs a descriptive message.');
  }
}

/**
 * Implementation of file_download()
 */
function download_count_file_download($filepath) {

  // check if the user has access to this particular node
  if (user_access('view uploaded files') && node_access('view', node_load($filepath->nid))) {     
     watchdog('download', t('%file was downloaded', array('%file' => theme('placeholder', t($filepath)))), 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 filename FROM {file_downloads} WHERE filename = '$filepath'"))) {
      	db_query("UPDATE {file_downloads} SET count = count+1 WHERE filename = '$filepath'");
    	} else {
      	    db_query("INSERT INTO {file_downloads} (filename, count) VALUES ('$filepath', 1)");
		}
			
   } else {	
	watchdog('download', t('Failed to download %file', array('%file' => theme('placeholder', t($filepath)))), WATCHDOG_WARNING);
          }	
} 

The thing is the hook function file_download is called when an attempt is made to download the file. In the above code, when the attempt fails, a warning is logged, and the counter is NOT incremented.

ps : an access denied message is logged whenever there is a failed attempt, AS WELL.

Caroline
11 heavens

Chill35’s picture

Is there a more direct way to know when an attached file is successfully downloaded ?

More direct, and hence more accurate, than this :

 function download_count_file_download($filepath) {
...
if (user_access('view uploaded files') && node_access('view', node_load($filepath->nid))) { 

Thanks in advance ?

Caroline
11 heavens

Chill35’s picture

The above code did not work.

The module hook function file_download is receiving a string, that is, the name of the file which someone is attempting to access for download. I had to modify the code to get to the node to which the file is possibly attached (not all files uploaded are attached to nodes...). The problem with the above code is that I was behaving with $filename as if it was an object. $filename->nid is undefined, and calling node_load() with an undefined argument causes all sorts of problems : http://drupal.org/node/117630

The new code, hence, is :


/**
 * Implementation of file_download()
 * This function is called whenever there is an attempt to download a file that was uploaded
 * through the core upload module. 
 */
function download_count_file_download($filename) {

  $file = file_create_path($filename);
  $result = db_query("SELECT f.* FROM {files} f WHERE filepath = '%s'", $file);
  if ($file = db_fetch_object($result)) {
    if (user_access('view uploaded files') && node_access('view', node_load($file->nid))) { 
      $message = t('%file was downloaded', array('%file' => theme('placeholder', $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 filename FROM {file_downloads} WHERE filename = '$filename'"))) {
        db_query("UPDATE {file_downloads} SET count = count+1 WHERE filename = '$filename'");
      } 
      else {
        db_query("INSERT INTO {file_downloads} (filename, count) VALUES ('$filename', 1)");
      }			
    } 
    else {
      $message = t('Failed to download %file', array('%file' => theme('placeholder', $filename))); 	
      watchdog('download', $message, WATCHDOG_WARNING);
    }	
  }
}

Caroline
11 heavens

Chill35’s picture

I improved upon the above code and put the module in CVS : http://drupal.org/project/download_count

Caroline
Who am I | Where are we
11 heavens

tormu’s picture

Weeh :)
Great to see that this got wind under its wings and produced a module :)

Chill35’s picture

Wheeeeeeeeeeeeeeeeeeeeeeeeeeee LOL

And I thanked you on my project page ! :)

Caroline
Who am I | Where are we
11 heavens