/[drupal]/contributions/modules/ubercart/uc_file/uc_file.module
ViewVC logotype

Contents of /contributions/modules/ubercart/uc_file/uc_file.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download) (as text)
Thu Jul 10 12:41:01 2008 UTC (16 months, 2 weeks ago) by islandusurper
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--2
Changes since 1.1: +1164 -1132 lines
File MIME type: text/x-php
Begin the Ubercart 6.x-2.x branch.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Allows products to be associated with downloadable files.
7 *
8 * uc_file allows ubercart products to have associated downloadable files.
9 * Optionally, after a customer purchases such a product they will be sent a
10 * download link via email. Additionally, after logging on a customer can
11 * download files via their account page. Optionally, an admininstrator can set
12 * restrictions on how and when files are downloaded.
13 *
14 * Development sponsored by the Ubercart project. http://www.ubercart.org
15 */
16
17 define('UC_FILE_PAGER_SIZE', 50);
18 define('UC_FILE_REQUEST_LIMIT', 50);
19 define('UC_FILE_BYTE_SIZE', 1024);
20
21 /* **************************************************************************** *
22 * Hook Functions (Drupal) *
23 * **************************************************************************** */
24
25 /**
26 * Implementation of hook_form_alter().
27 */
28 function uc_file_form_alter(&$form, &$form_state, $form_id) {
29 if ($form_id == "uc_product_feature_settings_form") {
30 $form['#submit'][] = 'uc_file_feature_settings_submit';
31 $form['#validate'][] = 'uc_file_feature_settings_validate';
32 }
33 }
34
35 /**
36 * Implementation of hook_menu().
37 */
38 function uc_file_menu() {
39 $items = array();
40
41 $items['_autocomplete_file'] = array(
42 'page callback' => '_autocomplete_filename',
43 'access arguments' => array('administer products'),
44 'type' => MENU_CALLBACK,
45 );
46 $items['admin/store/products/files'] = array(
47 'title' => 'View file downloads',
48 'description' => 'View all file download features on products.',
49 'page callback' => 'uc_file_files_admin',
50 'access arguments' => array('administer products'),
51 'type' => MENU_NORMAL_ITEM,
52 );
53 if (module_exists('uc_notify')) {
54 $items['admin/store/settings/notify/edit/file'] = array(
55 'title' => 'File download',
56 'description' => 'Edit the notification settings for purchased file downloads.',
57 'page callback' => 'drupal_get_form',
58 'page arguments' => array('uc_file_notify_settings'),
59 'access arguments' => array('administer store'),
60 'type' => MENU_LOCAL_TASK,
61 );
62 }
63 $items['user/%user/files'] = array(
64 'title' => 'Files',
65 'description' => 'View your purchased files.',
66 'page callback' => 'uc_file_user_downloads',
67 'page arguments' => array(1),
68 'access callback' => 'uc_file_user_access',
69 'access arguments' => array(1),
70 'type' => MENU_LOCAL_TASK,
71 );
72 $items['download/%/%'] = array(
73 'page callback' => '_file_download',
74 'page arguments' => array(1, 2),
75 'access arguments' => array('download file'),
76 'type' => MENU_CALLBACK,
77 );
78
79 return $items;
80 }
81
82 function uc_file_user_access($account) {
83 global $user;
84 return $user->uid && (user_access('view all downloads') || $user->uid == $account->uid);
85 }
86
87 function uc_file_init() {
88 drupal_add_css(drupal_get_path('module', 'uc_file') .'/uc_file.css');
89 }
90
91 /**
92 * Implementation of hook_perm().
93 */
94 function uc_file_perm() {
95 return array('download file', 'view all downloads');
96 }
97
98 function uc_file_theme() {
99 return array(
100 'uc_file_downloads_token' => array(
101 'arguments' => array('file_downloads' => NULL),
102 ),
103 'uc_file_files_form' => array(
104 'arguments' => array('form' => NULL),
105 ),
106 );
107 }
108
109 /**
110 * Implementation of hook_user().
111 */
112 function uc_file_user($op, &$edit, &$account, $category = NULL) {
113 global $user;
114 switch ($op) {
115 case 'delete':
116 _user_table_action('remove', NULL, $account->uid);
117 break;
118 case 'form':
119 if (user_access('administer users') && (is_null($category) || $category == 'account')) {
120 $file_downloads = db_query("SELECT * FROM {uc_file_users} as u INNER JOIN {uc_files} as f ON u.fid = f.fid WHERE uid = %d", $account->uid);
121 $files = db_query("SELECT * FROM {uc_files} ORDER BY filename ASC");
122 $available_downloads = array();
123 $available_files = array();
124 while ($file_download = db_fetch_object($file_downloads)) {
125 $available_downloads[$file_download->key] = $file_download->filename;
126 }
127 while ($file = db_fetch_object($files)) {
128 if (substr($file->filename, -1) != '/' && substr($file->filename, -1) != '\\') {
129 $available_files[$file->fid] = $file->filename;
130 }
131 }
132 $form['file'] = array(
133 '#type' => 'fieldset',
134 '#title' => t('File downloads'),
135 '#collapsible' => TRUE,
136 '#collapsed' => TRUE,
137 '#weight' => 10
138 );
139 $form['file']['remove_file'] = array(
140 '#type' => 'select',
141 '#title' => t('Remove file'),
142 '#multiple' => TRUE,
143 '#description' => t('Select a file to remove as a download. Hold Ctrl to select or unselect multiple files.'),
144 '#options' => $available_downloads,
145 );
146 $form['file']['add_file'] = array(
147 '#type' => 'select',
148 '#title' => t('Add file'),
149 '#multiple' => TRUE,
150 '#description' => t('Select a file to add as a download. Hold Ctrl to select or unselect multiple files.'),
151 '#options' => $available_files,
152 );
153 }
154 return $form;
155 break;
156 case 'submit':
157 if (!empty($edit['remove_file'])) {
158 foreach ($edit['remove_file'] as $hash_key) {
159 if (!is_null($hash_key)) {
160 _user_table_action('remove', NULL, $account->uid, $hash_key);
161 }
162 }
163 }
164 if (!empty($edit['add_file'])) {
165 foreach ($edit['add_file'] as $fid) {
166 $pfid = db_result(db_query("SELECT pfid FROM {uc_file_products} WHERE fid = %d", $fid));
167 _user_table_action('allow', $fid, $account->uid, $pfid);
168 }
169 }
170 break;
171 case 'view':
172 $existing_download = db_result(db_query("SELECT fid FROM {uc_file_users} WHERE uid = %d", $account->uid));
173 if ((user_access('view all downloads') || $user->uid == $account->uid) && $user->uid && $existing_download) {
174 $items['uc_file_download'] = array(
175 'value' => l(t('Click here to view your file downloads.'), 'user/'. $account->uid .'/files'),
176 'class' => 'member',
177 );
178 return array(t('File downloads') => $items);
179 }
180 break;
181 default:
182 break;
183 }
184 }
185
186 /* **************************************************************************** *
187 * Übercart Hooks *
188 * **************************************************************************** */
189
190 /**
191 * Implementation of hook_cart_item().
192 */
193 function uc_file_cart_item($op, &$item) {
194 switch ($op) {
195 case 'can_ship':
196 $files = db_query("SELECT shippable, model FROM {uc_file_products} as fp INNER JOIN {uc_product_features} as pf ON pf.pfid = fp.pfid WHERE nid = %d", $item->nid);
197 while ($file = db_fetch_object($files)) {
198 $sku = (empty($item->data['model'])) ? $item->model : $item->data['model'];
199 if ($sku == $file->model || empty($file->model)) {
200 return ($file->shippable) ? TRUE : FALSE;
201 }
202 }
203 break;
204 }
205 }
206
207 /**
208 * Implementation of hook_order().
209 */
210 function uc_file_order($op, $order, $status) {
211 global $user;
212
213 switch ($op) {
214 case 'update':
215 if (($order->uid > 0) && ($order_user = user_load(array('uid' => $order->uid))) !== FALSE) {
216 foreach ($order->products as $product) {
217 $files = db_query("SELECT fp.fid, fp.pfid, model, f.filename FROM {uc_file_products} AS fp INNER JOIN {uc_product_features} AS pf ON pf.pfid = fp.pfid INNER JOIN {uc_files} as f ON f.fid = fp.fid WHERE nid = %d", $product->nid);
218 while ($file = db_fetch_object($files)) {
219 if (($file->model == $product->model || empty($file->model)) && $status == variable_get('uc_file_default_order_status', 'completed')) {
220 $downloads = _user_table_action('allow', $file->fid, $order_user->uid, $file->pfid);
221 $user_downloads = (!empty($user_downloads)) ? array_merge($user_downloads, $downloads) : $downloads ;
222 if (_get_dir_file_ids($file->fid)) {
223 $comment = t('User can now download files in the directory %dir.', array('%dir' => $file->filename));
224 }
225 else {
226 $comment = t('User can now download the file %file.', array('%file' => basename($file->filename)));
227 }
228 uc_order_comment_save($order->order_id, $user->uid, $comment);
229 }
230 }
231 }
232 if (!is_null($user_downloads)) {
233 _email_file_download($order_user, $order, $user_downloads);
234 }
235 }
236 break;
237 default:
238 break;
239 }
240 }
241
242 /**
243 * Implementation of hook_product_feature().
244 */
245 function uc_file_product_feature() {
246 $features[] = array(
247 'id' => 'file',
248 'title' => t('File download'),
249 'callback' => 'uc_file_feature_form',
250 'delete' => 'uc_file_feature_delete',
251 'settings' => 'uc_file_feature_settings',
252 );
253
254 return $features;
255 }
256
257 /**
258 * Implementation of hook_store_status().
259 */
260 function uc_file_store_status() {
261 $message = array();
262 if (!is_dir(variable_get('uc_file_base_dir', NULL))) {
263 $message[] = array(
264 'status' => 'warning',
265 'title' => t('File Downloads'),
266 'desc' => t('The file downloads directory is not valid or set. Set a valid directory in the <a href="!url">product feature settings</a> under the file download settings fieldset.', array('!url' => url('admin/store/settings/products/edit/features'))),
267 );
268 }
269 else {
270 $message[] = array(
271 'status' => 'ok',
272 'title' => t('File Downloads'),
273 'desc' => t('The file downloads directory has been set and is working.'),
274 );
275 }
276 return $message;
277 }
278
279 /**
280 * Implementation of hook_token_list().
281 */
282 function uc_file_token_list($type = 'all') {
283 if ($type == 'uc_file' || $type == 'ubercart' || $type == 'all') {
284 $tokens['uc_file']['file-downloads'] = t('The list of file download links (if any) associated with an order');
285 }
286
287 return $tokens;
288 }
289
290 /**
291 * Implementation of hook_token_values().
292 */
293 function uc_file_token_values($type, $object = NULL) {
294 switch ($type) {
295 case 'uc_file':
296 if (!empty($object)) {
297 $values['file-downloads'] = theme('uc_file_downloads_token', $object);
298 }
299 break;
300 }
301
302 return $values;
303 }
304
305 /**
306 * Implementation of hook_uc_message().
307 */
308 function uc_file_uc_message() {
309 $messages['uc_file_download_subject'] = t("File Downloads for Order# [order-id]");
310 $messages['uc_file_download_message'] = t("Your order (order# [order-link]) at [store-name] included file download(s). You may access them with the following link(s):\n\n[file-downloads]\n\nAfter downloading these files these links will have expired. If you need to download the files again, you can login at [site-login] and visit the \"My Account\" section of the site.\n\nThanks again, \n\n[store-name]\n[site-slogan]");
311
312 return $messages;
313 }
314
315 /* **************************************************************************** *
316 * Callback Functions, Forms, and Tables *
317 * **************************************************************************** */
318
319 /**
320 * Theme file download links token
321 */
322 function theme_uc_file_downloads_token($file_downloads) {
323 $output = '';
324 foreach ($file_downloads as $file_download) {
325 $filename = basename(db_result(db_query("SELECT filename FROM {uc_files} WHERE fid = %d", $file_download->fid)));
326 $download_url = url('download/'. $file_download->fid .'/'. $file_download->key, NULL, NULL, TRUE);
327 $output .= '<a href="'. $download_url .'">'. $download_url ."</a>\n";
328 }
329 return $output;
330 }
331
332 /**
333 * product_feature delete function
334 */
335 function uc_file_feature_delete($feature) {
336 db_query("DELETE FROM {uc_file_products} WHERE pfid = %d", $feature['pfid']);
337 }
338
339 /**
340 * Form builder for hook_product_feature
341 */
342 function uc_file_feature_form($form_state, $node, $feature) {
343 if (!is_dir(variable_get('uc_file_base_dir', NULL))) {
344 drupal_set_message(t('A file directory needs to be configured in <a href="!url">product feature settings</a> before a file can be selected.', array('!url' => url('admin/store/settings/products/edit/features'))), 'error');
345 }
346 _file_table_action('insert');
347 $models = (!_get_adjustment_models($node->nid)) ? array(NULL => t('Any'), $node->model => $node->model) : array(NULL => t('Any'), $node->model => $node->model) + _get_adjustment_models($node->nid);
348
349 if (!empty($feature)) {
350 $file_product = db_fetch_object(db_query("SELECT * FROM {uc_file_products} as p LEFT JOIN {uc_files} as f ON p.fid = f.fid WHERE pfid = %d", $feature['pfid']));
351 $default_feature = $feature['pfid'];
352 $default_model = $file_product->model;
353 $default_filename = $file_product->filename;
354 $default_description = $file_product->description;
355 $default_shippable = $file_product->shippable;
356 }
357 else {
358 $default_shippable = $node->shippable;
359 }
360
361 $form['nid'] = array(
362 '#type' => 'value',
363 '#value' => $node->nid,
364 );
365 $form['pfid'] = array(
366 '#type' => 'value',
367 '#value' => $default_feature,
368 );
369 $form['uc_file_model'] = array(
370 '#type' => 'select',
371 '#title' => t('Model/SKU'),
372 '#default_value' => $default_model,
373 '#description' => t('This is the model/SKU that will need to be purchased to obtain the file download.'),
374 '#options' => $models,
375 );
376 $form['uc_file_filename'] = array(
377 '#type' => 'textfield',
378 '#title' => t('File download'),
379 '#default_value' => $default_filename,
380 '#autocomplete_path' => '_autocomplete_file',
381 '#description' => t('The file that can be downloaded when product is purchased (enter a path relative to the %dir directory).', array('%dir' => variable_get('uc_file_base_dir', NULL))),
382 );
383 $form['uc_file_description'] = array(
384 '#type' => 'textfield',
385 '#title' => t('Description'),
386 '#default_value' => $default_description,
387 '#maxlength' => 255,
388 '#description' => t('A description of the download associated with the product.'),
389 );
390 $form['uc_file_shippable'] = array(
391 '#type' => 'checkbox',
392 '#title' => t('Shippable product'),
393 '#default_value' => $default_shippable,
394 '#description' => t('Check if this product model/SKU file download is also associated with a shippable product.'),
395 );
396
397 return uc_product_feature_form($form);
398 }
399
400 function uc_file_feature_form_validate($form, &$form_state) {
401 if (!db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_state['values']['uc_file_filename']))) {
402 form_set_error('uc_file_filename', t('%file is not a valid file or directory inside file download directory.', array('%file' => $form_state['values']['uc_file_filename'])));
403 }
404 }
405
406 function uc_file_feature_form_submit($form, &$form_state) {
407 global $user;
408 $fid = db_result(db_query("SELECT fid FROM {uc_files} WHERE filename = '%s'", $form_state['values']['uc_file_filename']));
409 //Build product feature descriptions
410 $description = (empty($form_state['values']['uc_file_model'])) ? t('<strong>SKU:</strong> Any<br/>') : t('<strong>SKU:</strong> !sku<br/>', array('!sku' => $form_state['values']['uc_file_model']));
411 if (is_dir(variable_get('uc_file_base_dir', NULL) ."/". $form_state['values']['uc_file_filename'])) {
412 $description .= t('<strong>Directory:</strong> !dir<br/>', array('!dir' => $form_state['values']['uc_file_filename']));
413 }
414 else {
415 $description .= t('<strong>File:</strong> !file<br/>', array('!file' => basename($form_state['values']['uc_file_filename'])));;
416 }
417 $shippable = ($form_state['values']['uc_file_shippable']) ? 1 : 0;
418 $description .= ($shippable) ? t('<strong>Shippable:</strong> Yes') : t('<strong>Shippable:</strong> No');
419
420 //Insert or update uc_file_product table
421 if (empty($form_state['values']['pfid'])) {
422 db_query("INSERT INTO {uc_file_products} (fid, model, description, shippable) VALUES (%d, '%s', '%s', %d)", $fid, $form_state['values']['uc_file_model'], $form_state['values']['uc_file_description'], $shippable);
423 $pfid = db_last_insert_id('uc_product_features', 'pfid');
424 }
425 else {
426 $pfid = $form_state['values']['pfid'];
427 db_query("UPDATE {uc_file_products} SET fid = %d, model = '%s', description = '%s', shippable = %d WHERE pfid = %d", $fid, $form_state['values']['uc_file_model'], $form_state['values']['uc_file_description'], $shippable, $pfid);
428 }
429
430 $data = array(
431 'pfid' => $pfid,
432 'nid' => $form_state['values']['nid'],
433 'fid' => 'file',
434 'description' => $description,
435 );
436
437 $form_state['redirect'] = uc_product_feature_save($data);
438 }
439
440 /**
441 * Form builder for file settings
442 */
443 function uc_file_feature_settings() {
444 uc_add_js('$(document).ready(function() { if ($("#edit-uc-file-download-limit-duration-granularity").val() == "never") {$("#edit-uc-file-download-limit-duration-qty").attr("disabled", "disabled").val("");} });', 'inline');
445 $statuses = array();
446 foreach (uc_order_status_list('general') as $status) {
447 $statuses[$status['id']] = $status['title'];
448 }
449
450 $form['uc_file_default_order_status'] = array(
451 '#type' => 'select',
452 '#title' => t('Order status'),
453 '#default_value' => variable_get('uc_file_default_order_status', 'completed'),
454 '#description' => t('Where in the order status the user will be given the file download. Be aware that if payments are processed automatically, this happens before anonymous customers have an account created. This order status should not be reached before the user account exists.'),
455 '#options' => $statuses,
456 );
457 $form['uc_file_base_dir'] = array(
458 '#type' => 'textfield',
459 '#title' => t('Files path'),
460 '#description' => t('The absolute path (or relative to Drupal root) where files used for file downloads are located. For security reasons, it is reccommended to choose a path outside the web root.'),
461 '#default_value' => variable_get('uc_file_base_dir', NULL),
462 );
463 $form['uc_file_file_mask'] = array(
464 '#type' => 'textfield',
465 '#title' => t('Files mask'),
466 '#description' => t("The regular expression used for masking files in files directory. Don't change unless you know what you're doing."),
467 '#default_value' => variable_get('uc_file_file_mask', ".*"),
468 );
469 $form['uc_file_download_limit'] = array(
470 '#type' => 'fieldset',
471 '#title' => t('Download limits'),
472 '#collapsible' => TRUE,
473 '#collapsed' => FALSE,
474 );
475 $form['uc_file_download_limit']['uc_file_download_limit_number'] = array(
476 '#type' => 'textfield',
477 '#title' => t('Downloads'),
478 '#description' => t("The number of times a file can be downloaded. Leave empty to set no limit."),
479 '#default_value' => variable_get('uc_file_download_limit_number', NULL),
480 '#maxlength' => 4,
481 '#size' => 4,
482 );
483 $form['uc_file_download_limit']['uc_file_download_limit_addresses'] = array(
484 '#type' => 'textfield',
485 '#title' => t('IP addresses'),
486 '#description' => t("The number of unique IPs that a file can be downloaded from. Leave empty to set no limit."),
487 '#default_value' => variable_get('uc_file_download_limit_addresses', NULL),
488 '#maxlength' => 4,
489 '#size' => 4,
490 );
491 $form['uc_file_download_limit']['uc_file_download_limit_duration_qty'] = array(
492 '#type' => 'textfield',
493 '#title' => t('Time'),
494 '#default_value' => (variable_get('uc_file_download_limit_duration_granularity', 'never') == 'never') ? NULL : variable_get('uc_file_download_limit_duration_qty', NULL),
495 '#size' => 4,
496 '#maxlength' => 4,
497 '#prefix' => '<div class="duration">',
498 '#suffix' => '</div>',
499 );
500 $form['uc_file_download_limit']['uc_file_download_limit_duration_granularity'] = array(
501 '#type' => 'select',
502 '#options' => array(
503 'never' => t('never'),
504 'day' => t('day(s)'),
505 'week' => t('week(s)'),
506 'month' => t('month(s)'),
507 'year' => t('year(s)')
508 ),
509 '#default_value' => variable_get('uc_file_download_limit_duration_granularity', 'never'),
510 '#attributes' => array(
511 'onchange' => 'if (this.value == "never") {$("#edit-uc-file-download-limit-duration-qty").attr("disabled", "disabled").val("");} else {$("#edit-uc-file-download-limit-duration-qty").removeAttr("disabled");}'
512 ),
513 '#description' => t('How long after a product has been purchased until its file download expires.'),
514 '#prefix' => '<div class="duration">',
515 '#suffix' => '</div>',
516 );
517 return $form;
518 }
519
520 function uc_file_feature_settings_validate($form, &$form_state) {
521 if (!empty($form_state['values']['uc_file_base_dir']) && $form_state['values']['op'] == t('Save configuration') && !is_dir($form_state['values']['uc_file_base_dir'])) {
522 form_set_error('uc_file_base_dir', t('%dir is not a valid file or directory', array('%dir' => $form_state['values']['uc_file_base_dir'])));
523 }
524 }
525
526 function uc_file_feature_settings_submit($form, &$form_state) {
527 $action = (empty($form_state['values']['uc_file_base_dir'])) ? 'empty' : 'insert';
528 _file_table_action($action);
529 _file_table_action('refresh');
530 }
531
532 /**
533 * Page builder for file products admin
534 */
535 function uc_file_files_admin() {
536 _file_table_action('insert');
537 return drupal_get_form('uc_file_files_form');
538 }
539
540 /**
541 * Implementation of theme_form($form)
542 */
543 function theme_uc_file_files_form($form) {
544 $output = '';
545
546 //Only display files on 1st form step
547 if ($form['step']['#value'] == 1) {
548 $files = array();
549 $args = array('form' => $form);
550 $header = tablesort_sql(tapir_get_header('uc_file_files_table', array()));
551 $order = (empty($header)) ? "ORDER BY f.filename ASC" : $header .", f.filename ASC";
552 $count_query = "SELECT COUNT(*) FROM {uc_files}";
553 $query = pager_query("SELECT n.nid, f.filename, n.title, fp.model, f.fid, pf.pfid FROM {uc_files} as f LEFT JOIN {uc_file_products} as fp ON (f.fid = fp.fid) LEFT JOIN {uc_product_features} as pf ON (fp.pfid = pf.pfid) LEFT JOIN {node} as n ON (pf.nid = n.nid) ". $order, UC_FILE_PAGER_SIZE, 0, $count_query);
554 while ($file = db_fetch_object($query)) {
555 $files[] = $file;
556 }
557 $args['files'] = $files;
558
559 $output .= '<p>'. t('File downloads can be attached to any Ubercart product as a product feature. For security reasons the <a href="!download_url">file downloads directory</a> is separated from the Drupal <a href="!file_url">file system</a>. Here are the list of files (and their associated Ubercart products) that can be used for file downloads.', array('!download_url' => url('admin/store/settings/products/edit/features'), '!file_url' => url('admin/settings/file-system'))) .'</p>';
560 $output .= drupal_render($form['uc_file_action']);
561 $output .= tapir_get_table('uc_file_files_table', $args);
562 $output .= theme('pager', NULL, UC_FILE_PAGER_SIZE, 0);
563 }
564
565 //Checkboxes already rendered in uc_file_files_table
566 foreach ($form as $form_element => $form_data) {
567 if (strpos($form_element, 'file_select_') !== FALSE) {
568 unset($form[$form_element]);
569 }
570 }
571 $output .= drupal_render($form);
572
573 return $output;
574 }
575
576 /**
577 * Form builder for file products admin
578 */
579 function uc_file_files_form($form_state) {
580 if (!isset($form_state['step']) || $form_state['step'] < 2) {
581 //Display File Options and File checkboxes
582 $files = db_query("SELECT * FROM {uc_files}");
583 $file_actions = array(
584 'uc_file_delete' => t('Delete file(s)'),
585 'uc_file_upload' => t('Upload file')
586 );
587
588 //Check any if any hook_file_action('info', $args) are implemented
589 foreach (module_implements('file_action') as $module) {
590 $name = $module .'_file_action';
591 $result = $name('info', NULL);
592 if (is_array($result)) {
593 foreach ($result as $key => $action) {
594 if ($key != 'uc_file_delete' && $key != 'uc_file_upload') {
595 $file_actions[$key] = $action;
596 }
597 }
598 }
599 }
600
601 while ($file = db_fetch_object($files)) {
602 $form['file_select_'. $file->fid] = array('#type' => 'checkbox');
603 }
604 $form['uc_file_action'] = array(
605 '#type' => 'fieldset',
606 '#title' => t('File options'),
607 '#collapsible' => FALSE,
608 '#collapsed' => FALSE,
609 );
610 $form['uc_file_action']['action'] = array(
611 '#type' => 'select',
612 '#title' => t('Action'),
613 '#options' => $file_actions,
614 '#prefix' => '<div class="duration">',
615 '#suffix' => '</div>',
616 );
617 $form['uc_file_action']['submit'] = array(
618 '#type' => 'submit',
619 '#value' => t('Perform action'),
620 '#prefix' => '<div class="duration">',
621 '#suffix' => '</div>',
622 );
623 }
624 else {
625 //Perform File Action (Upload, Delete, hooked in actions)
626 $file_ids = array();
627 foreach ($form_state['values'] as $name => $form_value) {
628 if (strpos($name, 'file_select_') !== FALSE) {
629 $file_ids[] = intval(str_replace('file_select_', '', $name));
630 }
631 }
632 $form['file_ids'] = array('#type' => 'value', '#value' => $file_ids);
633 $form['action'] = array('#type' => 'value', '#value' => $form_state['values']['action']);
634
635 //Switch to an action to perform
636 switch ($form_state['values']['action']) {
637 case 'uc_file_delete': //Delete selected files
638 foreach ($file_ids as $file_id) {
639 $filename = db_result(db_query("SELECT filename FROM {uc_files} WHERE fid = %d", $file_id));
640 $filename = (substr($filename, -1) == "/") ? $filename .' ('. t('directory') .')' : $filename;
641 $file_list[] = $filename;
642 }
643 $form['files'] = array(
644 '#type' => 'markup',
645 '#value' => theme_item_list($file_list, NULL, 'ul', array('class' => 'file-name')),
646 );
647 $form['recurse_directories'] = array(
648 '#type' => 'checkbox',
649 '#title' => t('Delete selected directories and their sub directories'),
650 );
651 $form = confirm_form($form, t('Delete the following file(s)?'), 'admin/store/products/files', t('Deleting a file will remove all its associated file downloads and product features. Removing a directory will remove any files it contains and their associated file downloads and product features.'), t('Yes'), t('No'));
652 break;
653 case 'uc_file_upload': //Upload file
654 drupal_set_title(t('Upload File'));
655 $max_bytes = trim(ini_get('post_max_size'));
656 $directories = array('' => '/');
657
658 switch (strtolower($max_bytes{strlen($max_bytes)-1})) {
659 case 'g':
660 $max_bytes *= 1024;
661 case 'm':
662 $max_bytes *= 1024;
663 case 'k':
664 $max_bytes *= 1024;
665 }
666
667 $files = db_query("SELECT * FROM {uc_files}");
668 while ($file = db_fetch_object($files)) {
669 if (is_dir(variable_get('uc_file_base_dir', NULL) ."/". $file->filename)) {
670 $directories[$file->filename] = $file->filename;
671 }
672 }
673
674 $form['#attributes']['enctype'] = 'multipart/form-data';
675 $form['upload_dir'] = array(
676 '#type' => 'select',
677 '#title' => t('Directory'),
678 '#description' => t('The directory to upload the file to. The default directory is the root of the file downloads directory.'),
679 '#options' => $directories,
680 );
681 $form['upload'] = array(
682 '#type' => 'file',
683 '#title' => t('File'),
684 '#description' => t('The maximum file size that can be uploaded is %size bytes. You will need to use a different method to upload the file to the directory (e.g. FTP, SSH) if your file exceeds this size.', array('%size' => number_format($max_bytes))),
685 );
686 $form['submit'] = array(
687 '#type' => 'submit',
688 '#value' => t('Upload'),
689 );
690 break;
691 default:
692 //Check any if any hook_file_action('form', $args) are implemented
693 foreach (module_implements('file_action') as $module) {
694 $name = $module .'_file_action';
695 $result = $name('form', array('action' => $form_state['values']['action'], 'file_ids' => $file_ids));
696 $form = (is_array($result)) ? array_merge($form, $result) : $form;
697 }
698 break;
699 }
700 }
701
702 return $form;
703 }
704
705 function uc_file_files_form_validate($form, &$form_state) {
706 switch ($form_state['step']) {
707 case 2:
708 switch ($form_state['values']['action']) {
709 case 'uc_file_delete': //Nothing to validate for file delete
710 break;
711 case 'uc_file_upload':
712 //Check any if any hook_file_action('validate', $args) are implemented
713 if ($temp_file = file_save_upload('upload', array())) {
714 foreach (module_implements('file_action') as $module) {
715 $name = $module .'_file_action';
716 $result = $name('upload_validate', array('file_object' => $temp_file, 'form_id' => $form_id, 'form_state' => $form_state));
717 }
718 $form_state['storage']['temp_file'] = $temp_file;
719 }
720 else {
721 form_set_error('', t('An error occurred while uploading the file'));
722 }
723 break;
724 default:
725 //Check any if any hook_file_action('validate', $args) are implemented
726 foreach (module_implements('file_action') as $module) {
727 $name = $module .'_file_action';
728 $result = $name('validate', array('form_id' => $form_id, 'form_state' => $form_state));
729 }
730 break;
731 }
732 break;
733 default:
734 break;
735 }
736 }
737
738 function uc_file_files_form_submit($form, &$form_state) {
739 switch ($form_state['step']) {
740 case 2:
741 switch ($form_state['values']['action']) {
742 case 'uc_file_delete':
743 foreach ($form_state['values']['file_ids'] as $file_id) {
744 _file_table_action('remove', $file_id, $form_state['values']['recurse_directories'], TRUE);
745 }
746 drupal_set_message(t('The select file(s) have been deleted.'));
747 break;
748 case 'uc_file_upload':
749 $dir = variable_get('uc_file_base_dir', NULL) .'/';
750 $dir = (is_null($form_state['values']['upload_dir'])) ? $dir : $dir . $form_state['values']['upload_dir'];
751 if (is_dir($dir)) {
752 $file_object = $form_state['storage']['temp_file'];
753 if (file_copy($file_object, $dir, FILE_EXISTS_REPLACE)) {
754 //Check any if any hook_file_action('upload', $args) are implemented
755 foreach (module_implements('file_action') as $module) {
756 $name = $module .'_file_action';
757 $result = $name('upload', array('file_object' => $file_object, 'form_id' => $form_id, 'form_state' => $form_state));
758 }
759
760 _file_table_action('insert');
761 drupal_set_message(t('The %file has been uploaded to %dir', array('%file' => basename($file_object->filepath), '%dir' => $dir)));
762 }
763 else {
764 drupal_set_message(t('An error occurred while copying the file to %dir', array('%dir' => $dir)));
765 }
766 }
767 else {
768 drupal_set_message(t('Can not move file to %dir', array('%dir' => $dir)));
769 }
770 break;
771 default:
772 //Check any if any hook_file_action('validate', $args) are implemented
773 foreach (module_implements('file_action') as $module) {
774 $name = $module .'_file_action';
775 $result = $name('submit', array('form_id' => $form_id, 'form_state' => $form_state));
776 }
777 break;
778 }
779 $form_state['rebuild'] = FALSE;
780 drupal_goto('admin/store/products/files');
781 break;
782 default:
783 $form_state['step'] = 2;
784 $form_state['rebuild'] = TRUE;
785 break;
786 }
787 }
788
789 /**
790 * Form builder for file download notification settings.
791 */
792 function uc_file_notify_settings() {
793 $form['uc_file_download_notification'] = array(
794 '#type' => 'checkbox',
795 '#title' => t('Send email to customer with file download link(s).'),
796 '#default_value' => variable_get('uc_file_download_notification', FALSE),
797 );
798 $form['uc_file_download_notification_subject'] = array(
799 '#type' => 'textfield',
800 '#title' => t('Message subject'),
801 '#default_value' => variable_get('uc_file_download_notification_subject', uc_get_message('uc_file_download_subject')),
802 );
803 $form['uc_file_download_notification_message'] = array(
804 '#type' => 'textarea',
805 '#title' => t('Message text'),
806 '#default_value' => variable_get('uc_file_download_notification_message', uc_get_message('uc_file_download_message')),
807 '#description' => t('The message the user receives after purchasing products with file downloads (<a href="!token_url">uses order, uc_file, and global tokens</a>)', array('!token_url' => url('admin/store/help/tokens'))),
808 '#rows' => 10,
809 );
810 $form['uc_file_download_notification_format'] = filter_form(variable_get('uc_file_download_notification_format', FILTER_FORMAT_DEFAULT), NULL, array('uc_file_download_notification_format'));
811
812 return system_settings_form($form);
813 }
814
815 /**
816 * Table builder for file products admin
817 */
818 function uc_file_files_table($form_state, $args = array()) {
819 $table = array();
820
821 $fields = array();
822 $fields[] = array(
823 'id' => 'select',
824 'cell' => '',
825 'weight' => 0,
826 );
827 $fields[] = array(
828 'id' => 'filename',
829 'cell' => array('data' => t('File'), 'field' => 'f.filename'),
830 'weight' => 1,
831 );
832 $fields[] = array(
833 'id' => 'product',
834 'cell' => array('data' => t('Product'), 'field' => 'n.title'),
835 'weight' => 2,
836 );
837 $fields[] = array(
838 'id' => 'model',
839 'cell' => array('data' => t('Model/SKU'), 'field' => 'fp.model'),
840 'weight' => 3,
841 'attributes' => array()
842 );
843 $table['columns'] = $fields;
844
845 $files = _group_filenames($args['files']);
846 foreach ($files as $file) {
847 $data = array();
848 $data['select'] = array('value' => $args['form']['file_select_'. $file->fid], 'cell' => drupal_render($args['form']['file_select_'. $file->fid]));
849 $filename = (is_dir(variable_get('uc_file_base_dir', NULL) .'/'. $file->filename)) ? '<strong>'. $file->filename .'</strong>' : $file->filename;
850 $data['filename'] = array('cell' => $filename == $last_filename ? '' : $filename);
851 if ($filename == $last_filename && !empty($data['#attributes'])) {
852 $table['rows'][count($table['rows']) - 1]['attributes'] = array('class' => 'group');
853 }
854 $last_filename = (empty($last_filename) || $filename != $last_filename) ? $filename : $last_filename;
855 $data['product'] = array('cell' => !empty($file->title) ? l($file->title, 'node/'. $file->nid) : '');
856 $data['model'] = array('cell' => !empty($file->model) ? $file->model : '');
857 $data['#attributes'][] = array();
858 $table['rows'][] = array('data' => $data, 'attributes' => array());
859 }
860
861 $form['table'] = array(
862 '#value' => $table,
863 '#theme' => 'tapir_table',
864 '#attributes' => array(
865 'class' => 'file-table',
866 ),
867 );
868 }
869
870 /**
871 * Table builder for user downloads
872 */
873 function uc_file_user_downloads($account) {
874 drupal_set_title(t('File downloads'));
875 uc_add_js(drupal_get_path('module', 'uc_file') .'/uc_file.js');
876
877 $header = array(
878 array('data' => t('Purchased'), 'field' => 'u.granted', 'sort' => 'desc'),
879 array('data' => t('Filename'), 'field' => 'f.filename'),
880 array('data' => t('Description'), 'field' => 'p.description'),
881 array('data' => t('Downloads'), 'field' => 'u.accessed'),
882 );
883
884 $sql = "SELECT granted, filename, accessed, description, `key`, f.fid FROM {uc_file_users} as u LEFT JOIN {uc_files} as f ON u.fid = f.fid LEFT JOIN {uc_file_products} as p ON p.pfid = u.pfid WHERE uid = %d";
885 $count_query = "SELECT COUNT(*) FROM {uc_file_users} WHERE uid = %d";
886 $download_limit = variable_get('uc_file_download_limit_number', NULL);
887 $file_ids = array();
888 $rows = array();
889 $files = pager_query($sql . tablesort_sql($header), UC_FILE_PAGER_SIZE, 0, $count_query, $account->uid);
890
891 while ($file = db_fetch_object($files)) {
892 $row = count($rows);
893 $expiration = _file_expiration_date($file->granted);
894 $onclick = array('onclick' => 'uc_file_update_download('. $row .', '. $file->accessed .', '. ((empty($download_limit)) ? -1 : $download_limit) .');', 'id' => 'link-'. $row);
895
896 if (!$expiration) {
897 $file_link = l(basename($file->filename), 'download/'. $file->fid .'/'. $file->key, $onclick);
898 }
899 else {
900 if (time() > $expiration) {
901 $file_link = basename($file->filename);
902 }
903 else {
904 $file_link = l(basename($file->filename), 'download/'. $file->fid .'/'. $file->key, $onclick) .' ('. t('expires on @date', array('@date' => format_date($expiration, 'custom', variable_get('uc_date_format_default', 'm/d/Y')))) .')';
905 }
906 }
907
908 $rows[] = array(
909 array('data' => format_date($file->granted, 'custom', variable_get('uc_date_format_default', 'm/d/Y')), 'class' => 'date-row', 'id' => 'date-'. $row),
910 array('data' => $file_link, 'class' => 'filename-row', 'id' => 'filename-'. $row),
911 array('data' => $file->description, 'class' => 'description-row', 'id' => 'description-'. $row),
912 array('data' => $file->accessed, 'class' => 'download-row', 'id' => 'download-'. $row),
913 );
914 }
915 if (empty($rows)) {
916 $rows[] = array(array('data' => t('No downloads found'), 'colspan' => 4));
917 }
918
919 $output = theme('table', $header, $rows) . theme('pager', NULL, UC_FILE_PAGER_SIZE, 0);
920 return $output;
921 }
922
923 /* **************************************************************************** *
924 * Module and Helper Functions *
925 * **************************************************************************** */
926
927 /**
928 * Implement Drupal autocomplete textfield
929 *
930 * @return:
931 * Sends string containing javascript array of matched files
932 */
933 function _autocomplete_filename() {
934 // Catch "/" characters that drupal autocomplete doesn't escape
935 $url = explode('_autocomplete_file/', request_uri());
936 $string = $url[1];
937 $matches = array();
938 $files = db_query("SELECT filename FROM {uc_files} WHERE filename LIKE LOWER('%s')", '%'. $string .'%');
939
940 while ($file = db_fetch_object($files)) {
941 $matches[$file->filename] = $file->filename;
942 }
943 print drupal_to_js($matches);
944 exit();
945 }
946
947 /**
948 * Email a user with download links for a product file download
949 *
950 * @param $user
951 * The Drupal user object
952 * @param $order
953 * The order object associated with message
954 * @param $file_user
955 * An array for user file downloads (uc_file_user row) associated with message
956 * @return:
957 * Sends result of drupal_mail
958 */
959 function _email_file_download($user, $order, $file_users) {
960 if (!variable_get('uc_file_download_notification', FALSE)) {
961 return;
962 }
963 $key = 'uc_file_download_notify';
964 $to = $order->primary_email;
965 $from = uc_store_email_from();
966 $params = array();
967
968 //drupal_set_message("Mail Sent<br/>key: $key, <br/>to: $to, <br/>subject: $subject, <br/>body: $body, <br/>from: $from, <br/>");
969 return drupal_mail('uc_file', $key, $to, user_preferred_language($user), $params, $from);
970 }
971
972 function uc_file_mail($key, &$message, $params) {
973 $token_filters = array('global' => NULL, 'user' => $user, 'order' => $order, 'uc_file' => $file_users);
974 $message['subject'] = token_replace_multiple(variable_get('uc_file_download_notification_subject', uc_get_message('uc_file_download_subject')), $token_filters);
975 $body = token_replace_multiple(variable_get('uc_file_download_notification_message', uc_get_message('uc_file_download_message')), $token_filters);
976 $body = check_markup($body, variable_get('uc_file_download_notification_format', 3), FALSE);
977 $message['body'][] = $body;
978 $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed';
979 }
980
981 /**
982 * Perform first-pass authorization. Call authorization hooks afterwards.
983 *
984 * Called when a user requests a file download, function checks download
985 * limits then checks for any implementation of hook_download_authorize.
986 * Passing that, the function _file_download_transfer is called.
987 *
988 * @param $fid
989 * The fid of the file specified to download.
990 * @param $key
991 * The hash key of a user's download
992 */
993 function _file_download($fid, $key) {
994 global $user;
995 $ip = ip_address();
996 $message_admin = t('Please contact the site administrator if this message has been received in error.');
997
998 $message_user = ($user->uid) ? t('The user %username ', array('%username' => $user->name)) : t('The IP address %ip ', array('%ip' => $ip));
999 $file_download = db_fetch_object(db_query("SELECT * FROM {uc_file_users} WHERE fid = %d AND `key` = '%s'", $fid, $key));
1000 $request_cache = cache_get('uc_file_'. $ip);
1001 $requests = ($request_cache) ? $request_cache->data + 1 : 1;
1002
1003 if ($requests > UC_FILE_REQUEST_LIMIT) {
1004 _file_download_deny($user->uid, t('You have attempted to download an incorrect file URL too many times. ') . $message_admin);
1005 }
1006
1007 if (!$file_download) {
1008 cache_set('uc_file_'. $ip, $requests, 'cache', time() + 86400);
1009 if ($requests == UC_FILE_REQUEST_LIMIT) {
1010 watchdog('uc_file', '%username has been temporarily banned from file downloads.', array('%username' => $message_user), WATCHDOG_WARNING);
1011 }
1012 _file_download_deny($user->uid, t("The following URL is not a valid download link. ") . $message_admin);
1013 }
1014 else {
1015 $ip_limit = variable_get