/[drupal]/contributions/modules/filefield_paths/filefield_paths.module
ViewVC logotype

Contents of /contributions/modules/filefield_paths/filefield_paths.module

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


Revision 1.23 - (show annotations) (download) (as text)
Fri Nov 6 02:22:38 2009 UTC (2 weeks, 5 days ago) by deciphered
Branch: MAIN
CVS Tags: HEAD
Changes since 1.22: +48 -73 lines
File MIME type: text/x-php
Drupal 7 port
1 <?php
2 // $Id: filefield_paths.module,v 1.22 2009/11/03 21:41:39 deciphered Exp $
3
4 /**
5 * @file
6 * Adds extra functionality to FileFields Path settings.
7 */
8
9 /**
10 * Implements hook_filefield_paths_field_settings().
11 */
12 function filefield_paths_filefield_paths_field_settings() {
13 return array(
14 'file_path' => array(
15 'title' => 'File path',
16 'sql' => 'filepath',
17
18 'form' => array(
19 'file_path' => array(
20 '#maxlength' => 512,
21 '#size' => 128,
22 ),
23 ),
24 ),
25
26 'file_name' => array(
27 'title' => 'File name',
28 'sql' => 'filename',
29
30 'form' => array(
31 'file_name' => array(
32 '#type' => 'textfield',
33 '#title' => t('File name'),
34 '#default_value' => '[file:ffp:onlyname:original].[file:ffp:extension:original]',
35 ),
36 ),
37 )
38 );
39 }
40
41 /**
42 * Implements hook_theme().
43 */
44 function filefield_paths_theme() {
45 return array(
46 'filefield_paths_token_help' => array(
47 'arguments' => array($types = array())
48 ),
49 );
50 }
51
52 function theme_filefield_paths_token_help($types) {
53 $types = count($types) == 0 ? array('file', 'node', 'site', 'user') : $types;
54 $full_list = token_info();
55 $table = array(
56 //'header' => array(t('Token'), t('Replacement value')),
57 'rows' => array(),
58 'attributes' => array('class' => 'description'),
59 );
60
61 foreach ($types as $type) {
62 $table['rows'][] = array(array('data' => drupal_ucfirst($type) . ' ' . t('tokens'), 'class' => 'region', 'colspan' => 2));
63 foreach ($full_list['tokens'][$type] as $token => $data) {
64 $table['rows'][] = array(
65 '[' . $type . ':' . $token . ']',
66 $data['description'],
67 );
68 }
69 }
70
71 return theme('table', $table);
72 }
73
74 /**
75 * Implements hook_init().
76 */
77 function filefield_paths_init() {
78 foreach (module_list() as $module) {
79 if (file_exists($file = drupal_get_path('module', 'filefield_paths') . '/modules/' . $module . '.inc')) {
80 require_once $file;
81 }
82 }
83 }
84
85 /**
86 * Implements hook_form_alter().
87 */
88 function filefield_paths_form_alter(&$form, $form_state, $form_id) {
89 $ffp = array();
90
91 // Invoke hook_filefield_paths_form_alter().
92 foreach (module_implements('filefield_paths_form_alter') as $module) {
93 $function = $module . '_filefield_paths_form_alter';
94 $function($form, $ffp);
95 }
96
97 // If supporting module enabled, show FileField Paths settings form.
98 if (count($ffp) > 0) {
99 $fields = module_invoke_all('filefield_paths_field_settings');
100 foreach ($ffp as $field_name => $field_data) {
101
102 $results = db_query("SELECT * FROM {filefield_paths} WHERE type = :type AND field = :field", array(
103 ':type' => $field_data['type'],
104 ':field' => $field_name
105 ));
106 foreach ($results as $result) { break; }
107 if (!empty($result)) {
108 foreach ($fields as &$field) {
109 $field['settings'] = unserialize($result->$field['sql']);
110 }
111 unset($field);
112 }
113
114 $count = 0;
115 foreach ($fields as $name => $field) {
116 $count++;
117
118 if (isset($field['form']) && is_array($field['form'])) {
119 $keys = array_keys($field['form']);
120 for ($i = 1; $i < count($field['form']); $i++) {
121 $field['form'][$keys[$i]]['#weight'] = ($count - 1) * 3 + 2 + $i;
122 }
123 unset($keys);
124
125 $field_data['form_path'] = array_merge_recursive($field_data['form_path'], $field['form']);
126 }
127
128 $field_data['form_path']['#tree'] = TRUE;
129 $field_data['form_path'][$name]['#weight'] = ($count - 1) * 3;
130
131 // Set defualt value for patterns.
132 if (isset($field['settings']['value'])) {
133 $field_data['form_path'][$name]['#default_value'] = $field['settings']['value'];
134
135 if (isset($field['data'])) {
136 foreach ($field['data'] as $key => $value) {
137 $field_data['form_path'][$value]['#default_value'] = $field['settings'][$key];
138 }
139 }
140 }
141
142 $field_data['form_path'][$name . '_cleanup'] = array(
143 '#type' => 'fieldset',
144 '#title' => t('!title cleanup settings', array('!title' => $field['title'])),
145 '#collapsible' => TRUE,
146 '#collapsed' => TRUE,
147 '#weight' => ($count - 1) * 3 + 1,
148 '#attributes' => array(
149 'class' => array($name . ' cleanup')
150 )
151 );
152
153 // Cleanup field with Pathauto module.
154 $field_data['form_path'][$name . '_cleanup'][$name . '_pathauto'] = array(
155 '#type' => 'checkbox',
156 '#title' => t('Cleanup using Pathauto') . '.',
157 '#default_value' => isset($field['settings']['pathauto'])
158 ? $field['settings']['pathauto']
159 : 0
160 ,
161 '#description' => t('Cleanup !title using !url', array('!title' => $field['title'], '!url' => l(t('Pathauto settings'), 'admin/build/path/pathauto'))),
162 );
163 if (!module_exists('pathauto')) {
164 $field_data['form_path'][$name . '_cleanup'][$name . '_pathauto']['#disabled'] = TRUE;
165 $field_data['form_path'][$name . '_cleanup'][$name . '_pathauto']['#default_value'] = 0;
166 }
167
168 // Convert field to lower case.
169 $field_data['form_path'][$name . '_cleanup'][$name . '_tolower'] = array(
170 '#type' => 'checkbox',
171 '#title' => t('Convert to lower case') . '.',
172 '#default_value' => isset($field['settings']['tolower'])
173 ? $field['settings']['tolower']
174 : 0
175 ,
176 '#description' => t('Convert !title to lower case', array('!title' => $field['title'])) . '.'
177 );
178
179 // Transliterate field with Transliteration module.
180 $field_data['form_path'][$name . '_cleanup'][$name . '_transliterate'] = array(
181 '#type' => 'checkbox',
182 '#title' => t('Transliterate') . '.',
183 '#default_value' => isset($field['settings']['transliterate'])
184 ? $field['settings']['transliterate']
185 : 0
186 ,
187 '#description' => t('Transliterate !title', array('!title' => $field['title'])) . '.'
188 );
189 if (!module_exists('transliteration')) {
190 $field_data['form_path'][$name . '_cleanup'][$name . '_transliterate']['#disabled'] = TRUE;
191 $field_data['form_path'][$name . '_cleanup'][$name . '_transliterate']['#default_value'] = 0;
192 }
193
194 // Replacement patterns for field.
195 $field_data['form_path'][$name . '_tokens'] = array(
196 '#type' => 'fieldset',
197 '#title' => t('!title replacement patterns', array('!title' => $field['title'])),
198 '#collapsible' => TRUE,
199 '#collapsed' => TRUE,
200 '#description' => theme('filefield_paths_token_help'),
201 '#weight' => ($count - 1) * 3 + 2,
202 '#attributes' => array(
203 'class' => array($name . ' tokens')
204 )
205 );
206 }
207
208 // Retroactive updates.
209 $field_data['form_path']['retroactive_update'] = array(
210 '#type' => 'checkbox',
211 '#title' => t('Retroactive update'),
212 '#description' => t('Move and rename previously uploaded files') . '.' .
213 '<br /> <strong style="color: #FF0000;">' . t('Warning') . ':</strong> ' .
214 t('This feature should only be used on developmental servers or with extreme caution') . '.',
215 '#weight' => 10
216 );
217
218 // Active updating.
219 $field_data['form_path']['active_updating'] = array(
220 '#type' => 'checkbox',
221 '#title' => t('Active updating'),
222 '#default_value' => variable_get("ffp_{$field_data['type']}_{$field_name}", 0),
223 '#description' => t('Actively move and rename previously uploaded files as required') . '.' .
224 '<br /> <strong style="color: #FF0000;">' . t('Warning') . ':</strong> ' .
225 t('This feature should only be used on developmental servers or with extreme caution') . '.',
226 '#weight' => 11
227 );
228
229 if (!in_array('filefield_paths_form_submit', $form['#submit'])) {
230 $form['#submit'][] = 'filefield_paths_form_submit';
231 }
232 }
233 }
234 }
235
236 /**
237 * Implements hook_form_submit().
238 */
239 function filefield_paths_form_submit($form, &$form_state) {
240 $ffp = array();
241
242 // Invoke hook_filefield_paths_form_submit().
243 foreach (module_implements('filefield_paths_form_submit') as $module) {
244 $function = $module . '_filefield_paths_form_submit';
245 $function($form_state, $ffp);
246 }
247
248 if (count($ffp) > 0) {
249 $retroactive_update = FALSE;
250 $fields = module_invoke_all('filefield_paths_field_settings');
251 foreach ($ffp as $field_name => $field_data) {
252 $cols = array();
253
254 foreach ($fields as $name => &$field) {
255 $field['settings'] = array(
256 'value' => $form_state['values']['ffp_' . $field_name][$name],
257 'tolower' => $form_state['values']['ffp_' . $field_name][$name . '_cleanup'][$name . '_tolower'],
258 'pathauto' => $form_state['values']['ffp_' . $field_name][$name . '_cleanup'][$name . '_pathauto'],
259 'transliterate' => $form_state['values']['ffp_' . $field_name][$name . '_cleanup'][$name . '_transliterate']
260 );
261
262 // Store additional settings from addon modules.
263 if (isset($field['data'])) {
264 foreach ($field['data'] as $key => $value) {
265 $field['settings'][$key] = $form_state['values']['ffp_' . $field_name][$value];
266 }
267 }
268
269 $cols[$field['sql']] = serialize($field['settings']);
270 }
271
272 $results = db_query(
273 "SELECT * FROM {filefield_paths} WHERE type = :type AND field = :field", array(
274 ':type' => $field_data['type'],
275 ':field' => $field_name
276 ));
277 foreach ($results as $result) { break; }
278
279 // Update existing entry.
280 if (!empty($result)) {
281 db_update('filefield_paths')
282 ->fields($cols)
283 ->condition('type', $field_data['type'])
284 ->condition('field', $field_name)
285 ->execute();
286 }
287
288 // Create new entry.
289 else {
290 db_insert('filefield_paths')
291 ->fields(array_merge(array(
292 'type' => $field_data['type'],
293 'field' => $field_name,
294 ), $cols))
295 ->execute();
296 }
297
298 if ($form_state['values']['ffp_' . $field_name]['retroactive_update']) {
299 $retroactive_update = TRUE;
300 $module = isset($form['#field']) ? $form['#field']['module'] : $field_name;
301 filefield_paths_batch_update($module, $field_name, arg(4));
302 }
303
304 variable_set("ffp_{$field_data['type']}_{$field_name}", $form_state['values']['ffp_' . $field_name]['active_updating']);
305 }
306
307 if ($retroactive_update) {
308 // Run batch.
309 batch_process($form_state['redirect']);
310 }
311 }
312 }
313
314 function filefield_paths_batch_update($module, $field, $type) {
315 $objects = array();
316
317 // Invoke hook_filefield_paths_batch_update().
318 if (function_exists($function = $module . '_filefield_paths_batch_update')) {
319 $function($field, str_replace('-', '_', $type), $objects);
320 }
321
322 // Create batch.
323 $batch = array(
324 'title' => t('Updating FileField Paths'),
325 'operations' => array(
326 array('_filefield_paths_batch_update_process', array($objects, $module, $field))
327 ),
328 );
329 batch_set($batch);
330 }
331
332 function _filefield_paths_batch_update_process($objects, $module, $field, &$context) {
333 if (!isset($context['sandbox']['progress'])) {
334 $context['sandbox']['progress'] = 0;
335 $context['sandbox']['max'] = count($objects);
336 $context['sandbox']['objects'] = $objects;
337 }
338
339 // Process nodes by groups of 5.
340 $count = min(5, count($context['sandbox']['objects']));
341 for ($i = 1; $i <= $count; $i++) {
342 // For each oid, load the object, update the files and save it.
343 $oid = array_shift($context['sandbox']['objects']);
344
345 // Invoke hook_filefield_paths_update().
346 if (function_exists($function = $module . '_filefield_paths_update')) {
347 $function($oid, $field);
348 }
349
350 // Update our progress information.
351 $context['sandbox']['progress']++;
352 }
353
354 // Inform the batch engine that we are not finished,
355 // and provide an estimation of the completion level we reached.
356 if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
357 $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
358 }
359 }
360
361 /**
362 * Implements hook_node_insert().
363 */
364 function filefield_paths_node_insert($node) {
365 filefield_paths_node_update($node);
366 }
367
368 /**
369 * Implements hook_node_update().
370 */
371 function filefield_paths_node_update($node) {
372 if (($ffp = filefield_paths_get_fields($node)) !== FALSE) {
373 $update = new stdClass;
374 $update->node = FALSE;
375
376 // Process files.
377 foreach ($ffp['#files'] as &$file) {
378 // Invoke hook_filefield_paths_process_file().
379 foreach (module_implements('filefield_paths_process_file') as $module) {
380 $function = $module . '_filefield_paths_process_file';
381 $function(($file['new'] || variable_get("ffp_{$node->type}_{$file['name']}", 0)), $file, $ffp['#settings'][$file['name']], $node, $update);
382 }
383 }
384
385 field_attach_update('node', $node);
386
387 // // Cleanup temporary paths.
388 // if ($ffp['#settings']) {
389 // foreach ($ffp['#settings'] as $name => $field) {
390 // $paths = explode('/', $field['filepath']['value']);
391 // filefield_paths_cleanup_temp($paths);
392 //
393 // // Invoke hook_filefield_paths_cleanup().
394 // foreach (module_implements('filefield_paths_cleanup') as $module) {
395 // $function = $module . '_filefield_paths_cleanup';
396 // $function($ffp, $paths, $name);
397 // }
398 // }
399 // }
400 }
401 }
402
403 /**
404 * Implementation of hook_content_fieldapi().
405 */
406 function filefield_paths_field_delete_field($field) {
407 db_delete('filefield_paths')
408 ->condition('type', $field['bundles']['node'][0])
409 ->condition('field', $field['field_name'])
410 ->execute();
411 }
412
413 //function filefield_paths_cleanup_temp($paths) {
414 // while ($paths) {
415 // if (@rmdir(file_directory_path() . '/' . implode('/', $paths)) === TRUE) {
416 // array_pop($paths);
417 // continue;
418 // }
419 // break;
420 // }
421 //}
422
423 function filefield_paths_get_fields(&$node, $op = NULL) {
424 $ffp = array();
425
426 // Invoke hook_filefield_paths_get_fields().
427 foreach (module_implements('filefield_paths_get_fields') as $module) {
428 $function = $module . '_filefield_paths_get_fields';
429 $function($node, $ffp);
430 }
431
432 if (count($ffp) == 0 || (isset($ffp['#types']) && !is_array($ffp['#types']))) {
433 return FALSE;
434 }
435
436 $fields = module_invoke_all('filefield_paths_field_settings');
437
438 // Load fields settings
439 foreach ($ffp['#types'] as $name => $temp) {
440 $results = db_query("SELECT * FROM {filefield_paths} WHERE type = :type AND field = :field", array(
441 ":type" => $node->type,
442 ":field" => $name
443 ));
444 foreach ($results as $result) { break; }
445
446 if (!empty($result)) {
447 foreach ($fields as $field) {
448 $ffp['#settings'][$name][$field['sql']] = unserialize($result->$field['sql']);
449 }
450 }
451 }
452
453 return $ffp;
454 }
455
456 /**
457 * Implements hook_filefield_paths_process_file().
458 */
459 function filefield_paths_filefield_paths_process_file($new, &$file, $settings, &$node, &$update) {
460 if ($new) {
461 global $user;
462 $token_data = array('file' => file_load($file['field']['fid']), 'node' => $node, 'user' => $user);
463
464 // Process filename.
465 $file['filename']['old'] = $file['field']['filename'];
466 $file['filename']['new'] = (($settings['filename']['value']) != '')
467 ? filefield_paths_process_string($settings['filename']['value'], $token_data, $settings['filename'])
468 : $file['field']['filename'];
469
470 // Process filepath.
471 $file['filepath']['old'] = $file['field']['uri'];
472 $file['filepath']['new'] = file_uri_scheme($file['field']['uri']) . '://' . filefield_paths_process_string($settings['filepath']['value'] . '/' . $file['filename']['new'], $token_data, $settings['filepath']);
473
474 // Finalize files if necessary.
475 if (dirname($file['filepath']['new']) != dirname($file['field']['uri']) || $file['filename']['new'] != $file['field']['uri']) {
476 if (file_prepare_directory(dirname($file['filepath']['new']), FILE_CREATE_DIRECTORY) && file_move((object) $file['field'], $file['filepath']['new'])) {
477
478 // Fix reference to old paths.
479 $file_directory_path = file_uri_scheme($file['field']['uri']) !== 'private' ? file_directory_path(file_uri_scheme($file['field']['uri'])) : 'system/files';
480
481 $pattern = array(
482 'regex' => str_replace('/', '\/', $file_directory_path) . '([^"]*?)' . str_replace('/', '\/', str_replace(file_directory_path(), '', file_uri_target($file['filepath']['old']))),
483 'regex_enc' => str_replace('/', '\/', drupal_encode_path($file_directory_path)) . '([^"]*?)' . str_replace('/', '\/', str_replace(drupal_encode_path(file_directory_path()), '', drupal_encode_path(file_uri_target($file['filepath']['old'])))),
484 'replace' => $file_directory_path . '$1' . str_replace(file_directory_path(), '', file_uri_target($file['filepath']['new'])),
485 );
486
487 // TODO: Replace with field_info_fields() when http://drupal.org/node/613754 is fixed.
488 $instances = field_info_instances('node', $node->type);
489 $fields = array();
490 foreach ($instances as $field_name => $instance) {
491 $fields[$field_name] = field_info_field($field_name);
492 }
493
494 foreach ($fields as $name => $field) {
495 if ($field['module'] == 'text' && isset($node->{$field['field_name']}) && is_array($node->{$field['field_name']})) {
496 foreach ($node->{$field['field_name']}['zxx'] as &$item) {
497 $original = $item['value'];
498 $item['value'] = (preg_match('/' . $pattern['regex'] . '/s', $item['value']))
499 ? preg_replace('/' . $pattern['regex'] . '/s', $pattern['replace'], $item['value'])
500 : preg_replace('/' . $pattern['regex_enc'] . '/s', $pattern['replace'], $item['value']);
501
502 if ($item['value'] !== $original) {
503 $update->node = TRUE;
504 }
505 }
506 }
507 }
508
509 // Store new filename in file Array
510 $file['field']['filename'] = $file['filename']['new'];
511 $file['field']['uri'] = $file['filepath']['new'];
512 }
513 }
514
515 // Replace description.
516 //if ($file['field']['description'] == $file['filename']['old']) {
517 // $file['field']['description'] = $file['filename']['new'];
518 //}
519 }
520 }
521
522 /**
523 * Implements hook_token_info().
524 */
525 function filefield_paths_token_info() {
526 $file['ffp:onlyname'] = array(
527 'name' => t("File name"),
528 'description' => t("File name without extension."),
529 );
530 $file['ffp:onlyname:original'] = array(
531 'onlyname' => t("File name - original"),
532 'description' => t("File name without extension - original."),
533 );
534 $file['ffp:extension'] = array(
535 'name' => t("File extension"),
536 'description' => t("File extension."),
537 );
538 $file['ffp:extension:original'] = array(
539 'name' => t("File extension - original"),
540 'description' => t("File extension - original."),
541 );
542
543 return array(
544 'tokens' => array('file' => $file),
545 );
546 }
547
548 /**
549 * Implements hook_tokens().
550 */
551 function filefield_paths_tokens($type, $tokens, array $data = array(), array $options = array()) {
552 $url_options = array('absolute' => TRUE);
553 if (isset($language)) {
554 $url_options['language'] = $language;
555 }
556 $sanitize = !empty($options['sanitize']);
557
558 $replacements = array();
559
560 if ($type == 'file' && !empty($data['file'])) {
561 $file = $data['file'];
562
563 foreach ($tokens as $name => $original) {
564 switch ($name) {
565 case 'ffp:onlyname':
566 $info = pathinfo($file->filename);
567 // PHP < 5.2: pathinfo() doesn't return 'filename' variable.
568 $replacements[$original] = isset($orig['filename']) ? $info['filename'] : basename($file->filename, '.' . $info['extension']);
569 break;
570
571 case 'ffp:onlyname:original':
572 $info = pathinfo($file->origname);
573 // PHP < 5.2: pathinfo() doesn't return 'filename' variable.
574 $replacements[$original] = isset($orig['filename']) ? $info['filename'] : basename($file->origname, '.' . $info['extension']);
575 break;
576
577 case 'ffp:extension':
578 $info = pathinfo($file->filename);
579 $replacements[$original] = $info['extension'];
580 break;
581
582 case 'ffp:extension:original':
583 $info = pathinfo($file->origname);
584 $replacements[$original] = $info['extension'];
585 break;
586 }
587 }
588 }
589
590 return $replacements;
591 }
592
593 /**
594 * Process and cleanup strings.
595 */
596 function filefield_paths_process_string($value, $data, $settings = array()) {
597 // Process string tokens.
598 $value = token_replace($value, $data);
599
600 // Transliterate string.
601 if (module_exists('transliteration') && isset($settings['transliterate']) && $settings['transliterate']) {
602 $value = transliteration_get($value);
603 if ($type == 'field') {
604 $paths = explode('/', $value);
605 foreach ($paths as &$path) {
606 $path = transliteration_clean_filename($path);
607 }
608
609 $value = implode('/', $paths);
610 }
611 }
612
613 // Convert string to lower case.
614 if ((isset($settings['tolower']) && $settings['tolower']) || (isset($settings['pathauto']) && $settings['pathauto'] && variable_get('pathauto_case', 0))) {
615 // Convert string to lower case
616 $value = drupal_strtolower($value);
617 }
618
619 // Ensure that there are no double-slash sequences due to empty token values.
620 $value = preg_replace('/\/+/', '/', $value);
621
622 return $value;
623 }
624
625 /**
626 * Implements hook_file_insert().
627 */
628 function filefield_paths_file_insert(&$file) {
629 // Store original filename in the database.
630 if (empty($file->origname)) {
631 $file->origname = $file->filename;
632 db_update('file')
633 ->fields(array('origname' => $file->filename))
634 ->condition('fid', $file->fid)
635 ->execute();
636 }
637 }

  ViewVC Help
Powered by ViewVC 1.1.2