? contrib/ec_media Index: file/file.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/file/file.module,v retrieving revision 1.37 diff -u -F^f -r1.37 file.module --- file/file.module 31 May 2006 04:53:03 -0000 1.37 +++ file/file.module 31 May 2006 18:16:24 -0000 @@ -142,6 +142,15 @@ function file_productapi(&$node, $op, $a '#type' => 'fieldset', '#title' => t('File settings'), ); + if (_ec_file_is_managed($node->nid)) { + $description = t('Do not edit. File is managed by %nodetype node.', + array('%nodetype' => $node->type)); + $attributes = array('disabled' => 'disabled'); + } + else { + $description = t('Enter the filesystem path to this file (not the URL). This path will be prefixed with %file_path/ Here is a list of files in this directory. You may need to FTP your file to this directory before you can create the file product.', array('%file_quicklist' => url('admin/store/products/files'), '%file_path' => variable_get('file_directory_path', 'files'))); + $attribues = NULL; + } $form['f_settings']['fpath'] = array( '#type' => 'textfield', '#title' => t('File path'), @@ -149,7 +158,8 @@ function file_productapi(&$node, $op, $a '#size' => 50, '#maxlength' => 200, '#autocomplete_path' => 'ec_file/autocomplete', - '#description' => t('Enter the filesystem path to this file (not the URL). This path will be prefixed with %file_path/ Here is a list of files in this directory. You may need to FTP your file to this directory before you can create the file product.', array('%file_quicklist' => url('admin/store/products/files'), '%file_path' => variable_get('file_directory_path', 'files'))), + '#description' => $description, + '#attributes' => $attributes, ); $output = $form; @@ -164,15 +174,18 @@ function file_productapi(&$node, $op, $a /* Node has been saved, write to product tables. */ case 'insert': - return db_query("INSERT INTO {ec_product_file} (nid, fpath, size) VALUES (%d, '%s', '%s')", $node->nid, $node->fpath, $node->size); + if (!_ec_file_is_managed($node->nid)) + return db_query("INSERT INTO {ec_product_file} (nid, fpath, size) VALUES (%d, '%s', '%s')", $node->nid, $node->fpath, $node->size); break; case 'update': - return db_query("UPDATE {ec_product_file} SET fpath = '%s', size = '%s' WHERE nid = %d", $node->fpath, $node->size, $node->nid); + if (!_ec_file_is_managed($node->nid)) + return db_query("UPDATE {ec_product_file} SET fpath = '%s', size = '%s' WHERE nid = %d", $node->fpath, $node->size, $node->nid); break; case 'delete': - return db_query("DELETE FROM {ec_product_file} WHERE nid = %d", $node->nid); + if (!_ec_file_is_managed($node->nid)) + return db_query("DELETE FROM {ec_product_file} WHERE nid = %d", $node->nid); break; } } @@ -396,10 +409,6 @@ function ec_file_create_url($path) { if (user_access('administer store')) { $uid = arg(2); } - - if (strpos($path, variable_get('ec_file_directory_path', 'files')) !== false) { - $path = trim(substr($path, strlen(variable_get('ec_file_directory_path', 'files'))), '\\/'); - } return url("store/myfiles/$uid/download", 'file='. urlencode($path)); } @@ -409,7 +418,7 @@ function ec_file_create_url($path) { * file system directory. * * @param $dest Path to verify - * @return Path to file with file system directory appended if necessary. + * @return Path to file with file system directory prepended if necessary. * Returns FALSE if the path is invalid (i.e. outside the configured 'files'-directory). */ function ec_file_create_path($dest = 0) { @@ -437,24 +446,44 @@ function ec_file_create_path($dest = 0) * Call modules to find out if a file is accessible for a given user. */ function ec_product_download() { - $file = $_GET['file']; + $file = urldecode($_GET['file']); if (file_exists(ec_file_create_path($file))) { - $list = module_list(); - foreach ($list as $module) { - $headers = ec_file_build_download($file); - if (!$headers) { - drupal_access_denied(); - } - elseif (is_array($headers)) { - ec_file_transfer($file, $headers); - } + $headers = ec_file_build_download($file); + if (!$headers) { + drupal_access_denied(); + return; + } + elseif (is_array($headers)) { + ec_file_transfer($file, $headers); } } drupal_not_found(); } +function ec_file_may_download($file, $uid) { + // TODO: cache results to avoid repeated queries + + /* Check invoice and expiration dates... */ + + $expired_length = variable_get('file_expired', '7') * 86400; // Convert expiration to seconds + $max_expired_date = time() + $expired_length; // Absolute longest expiration date. + + $data = db_fetch_object(db_query("SELECT st.created, st.expires FROM {ec_transaction} AS st, {users} AS u, {ec_product} AS p, {ec_product_file} AS pf, {ec_transaction_product} AS stp WHERE u.uid = st.uid AND st.uid = %d AND p.nid = stp.nid AND st.txnid = stp.txnid AND pf.nid = p.nid AND st.payment_status = '2' AND pf.fpath = '%s'", $uid, $file)); + + /* Expiration date is found via the sitewide file expiration date OR + the special case where a store admin has extended the expiration date for + a given transaction. The 'special case' always take precedence, so you can + disable downloads before the sitewide expiration date is reached. */ + if ($data && + (($data->created + $expired_length < $max_expired_date && !$data->expires) || + ($data->expires && $data->expires > time()))) { + return TRUE; + } +} + /** - * Implementation of the file_download_hook() to find out if a file is accessible for a given user. + * Generate headers necessary for a file download. First check that user is + * able to download the file. */ function ec_file_build_download($file) { @@ -465,33 +494,20 @@ function ec_file_build_download($file) { $uid = arg(2); } - /* Check invoice and expiration dates... */ - - $expired_length = variable_get('file_expired', '7') * 86400; // Convert expiration to seconds - $max_expired_date = time() + $expired_length; // Absolute longest expiration date. - - $data = db_fetch_object(db_query("SELECT st.created, st.expires FROM {ec_transaction} AS st, {users} AS u, {ec_product} AS p, {ec_product_file} AS pf, {ec_transaction_product} AS stp WHERE u.uid = st.uid AND st.uid = %d AND p.nid = stp.nid AND st.txnid = stp.txnid AND pf.nid = p.nid AND st.payment_status = '2' AND pf.fpath = '%s'", $uid, $file)); - - /* Expiration date is found via the sitewide file expiration date OR - the special case where a store admin has extended the expiration date for - a given transaction. The 'special case' always take precedence, so you can - disable downloads before the sitewide expiration date is reached. */ - if (($data->created + $expired_length < $max_expired_date && !$data->expires) || - ($data->expires && $data->expires > time())) { - + if (ec_file_may_download($file, $uid)) { $filename = basename($file); if (file_iemac_hack()) { if (strlen($filename) > 30) { $filename = substr($filename, strlen($filename) - 30); } } - + $file = ec_file_create_path($file); $header[] = 'Content-type: application/x-download'; $header[] = 'Content-Disposition: attachment; filename="'. $filename .'";'; $header[] = 'Accept-Ranges: bytes'; $header[] = 'Content-Length: '. filesize($file); - + return $header; } else { @@ -537,3 +553,117 @@ function ec_file_autocomplete($string) { print drupal_implode_autocomplete($matches); exit(); } + +/** + * Node-managed file functions + * + * The following hooks allow third-party modules which contain files to treat + * their files as products. This module allows downloads only when the + * products have been purchased. + * + * See also the ec_media module (ecommerce/contrib/ec_media). + */ + +/** + * Determine which node types are eligible to be treated as products. + * + * Invokes hook_ec_file_nodetypes. Third-party modules must implement that + * hook to be treated as products. + */ +function _ec_file_get_managed_types() { + static $cache; + if (!$cache) { + $cache = module_invoke_all('ec_file_nodetypes'); + } + return $cache; +} + +/** + * Determine whether a node is an instance of a node-managed product. + */ +function _ec_file_is_managed($nid) { + $node = node_load($nid); + return in_array($node->type, array_keys(_ec_file_get_managed_types())); +} + +/** + * Returns settings specific to nodes of the given type. + */ +function _ec_file_managed_settings($node) { + $settings = _ec_file_get_managed_types(); + return $settings[$node->type]; +} + +/** + * hook_file_ec_file_event + * + * Notification of a file-related event. + * + * @param $fid + * Numerical file ID. + * @param $realm + * Textual file-type identifier. The $fid and $realm combine to form a unique ID for the file. + * @param $node + * Node with which the file is associated. + * @param $op + * Type of event which has occurred. (I.e. 'insert', 'update', 'delete') + * @param $info + * Associative array of information about the file. Should contain at a minimum 'filepath' and 'filesize'. + * + * @return + * none + */ +function file_ec_file_event($fid, $realm, $node, $op, $info) { + if (!_ec_file_is_managed($node->nid)) + return; + + if (($settings = _ec_file_managed_settings($node)) && + ($settings['realm'] == $realm)) { + // The file managed by the node type has changed. Keep our data in sync. + if ($op == 'insert') { + db_query("INSERT INTO {ec_product_file} (nid, fpath, size) VALUES (%d, '%s', '%s')", $node->nid, $info->filepath, $info->filesize); + } + else if ($op == 'update') { + db_query("UPDATE {ec_product_file} SET fpath = '%s', size = '%s' WHERE nid = %d", $info->filepath, $info->filesize, $node->nid); + } + else if ($op == 'delete') { + db_query("DELETE FROM {ec_product_file} WHERE nid = %d", $node->nid); + } + } +} + +/** + * hook_file_ec_file_access + * + * Grant permission to access files. This implementation allows us to grant + * downloads only to users who have purchased files. + * + * @param $fid + * Numerical file ID. + * @param $realm + * Textual file-type identifier. The $fid and $realm combine to form a unique ID for the file. + * @param $node + * Node with which the file is associated. + * @param $op + * Type of access requested. (I.e. 'view', 'download') + * @param $info + * Associative array of information about the file. Should contain at a minimum 'filepath'. + * @param $account + * The user data structure for the account requesting permission. + * + * @return + * TRUE, only if user is allowed access to the file. + */ +function file_ec_file_access($fid, $realm, $node, $op, $info, $account) { + if ($node->ptype != 'file') + return; + + if (($settings = _ec_file_managed_settings($node)) && + ($settings['realm'] == $realm)) { + // The file is one we oversee. + if ($op == 'view' || $op == 'download') { + //Check that user may download. + return ec_file_may_download($info->filepath, $account->uid); + } + } +}