filefield_paths.module

Tracking 7.x-1.x branch
  1. drupal
    1. 7 contributions/filefield_paths/filefield_paths.module

Contains core functions for the File (Field) Paths module.

Functions & methods

NameDescription
filefield_paths_batch_updateSet batch process to update File (Field) Paths.
filefield_paths_entity_insertImplements hook_entity_insert().
filefield_paths_entity_updateImplements hook_entity_update().
filefield_paths_file_presaveImplements hook_file_presave().
filefield_paths_form_alterImplements hook_form_alter().
filefield_paths_form_submitSubmit callback for File (Field) Paths settings form.
filefield_paths_process_stringProcess and cleanup strings.
_filefield_paths_batch_update_process
_filefield_paths_get_field_types
_filefield_paths_replace_pathRun regular expression over all available text-based fields.
_filefield_paths_replace_path_uri_schemeHelper function for File (Field) Paths URI updater regular expression.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Contains core functions for the File (Field) Paths module.
  5. */
  6. /**
  7. * Include additional files.
  8. */
  9. foreach (module_list() as $module) {
  10. if (file_exists($file = dirname(__FILE__) . "/modules/{$module}.inc")) {
  11. require_once $file;
  12. }
  13. }
  14. /**
  15. * Implements hook_form_alter().
  16. */
  17. function filefield_paths_form_alter(&$form, $form_state, $form_id) {
  18. $field_types = _filefield_paths_get_field_types();
  19. if (isset($form['#field']) && in_array($form['#field']['type'], array_keys($field_types))) {
  20. $entity_info = entity_get_info($form['#instance']['entity_type']);
  21. $settings = isset($form['#instance']['settings']['filefield_paths']) ? $form['#instance']['settings']['filefield_paths'] : array();
  22. // Hide standard File directory field.
  23. $form['instance']['settings']['file_directory']['#access'] = FALSE;
  24. // File (Field) Paths fieldset element.
  25. $form['instance']['settings']['filefield_paths'] = array(
  26. '#type' => 'fieldset',
  27. '#title' => t('File (Field) Path settings'),
  28. '#collapsible' => TRUE,
  29. '#collapsed' => TRUE,
  30. '#weight' => 1,
  31. '#tree' => TRUE,
  32. );
  33. // Additional File (Field) Paths widget fields.
  34. $fields = module_invoke_all('filefield_paths_field_settings');
  35. foreach ($fields as $name => $field) {
  36. // Attach widget fields.
  37. $form['instance']['settings']['filefield_paths'][$name] = array(
  38. '#type' => 'container',
  39. );
  40. // Attach widget field form elements.
  41. if (isset($field['form']) && is_array($field['form'])) {
  42. foreach (array_keys($field['form']) as $delta => $key) {
  43. $form['instance']['settings']['filefield_paths'][$name][$key] = array_merge(
  44. $field['form'][$key],
  45. array(
  46. '#element_validate' => array('token_element_validate'),
  47. '#token_types' => array('file', $entity_info['token type']),
  48. )
  49. );
  50. // Fetch stored value from instance.
  51. if (isset($settings[$name][$key])) {
  52. $form['instance']['settings']['filefield_paths'][$name][$key]['#default_value'] = $settings[$name][$key];
  53. }
  54. }
  55. // Field options.
  56. $form['instance']['settings']['filefield_paths'][$name]['options'] = array(
  57. '#type' => 'fieldset',
  58. '#title' => t('@title options', array('@title' => $field['title'])),
  59. '#collapsible' => TRUE,
  60. '#collapsed' => TRUE,
  61. '#weight' => 1,
  62. '#attributes' => array(
  63. 'class' => array("{$name} cleanup")
  64. ),
  65. );
  66. // @TODO - Make this more modular.
  67. // Cleanup field with Pathauto module.
  68. $form['instance']['settings']['filefield_paths'][$name]['options']['pathauto'] = array(
  69. '#type' => 'checkbox',
  70. '#title' => t('Cleanup using Pathauto') . '.',
  71. '#default_value' => isset($settings[$name]['options']['pathauto']) && module_exists('pathauto')
  72. ? $settings[$name]['options']['pathauto']
  73. : FALSE,
  74. '#description' => t('Cleanup @title using', array('@title' => $field['title'])) . ' ' . l(t('Pathauto settings'), 'admin/config/search/path/settings'),
  75. '#disabled' => !module_exists('pathauto'),
  76. );
  77. // Transliterate field with Transliteration module.
  78. $form['instance']['settings']['filefield_paths'][$name]['options']['transliterate'] = array(
  79. '#type' => 'checkbox',
  80. '#title' => t('Transliterate') . '.',
  81. '#default_value' => isset($settings[$name]['options']['transliterate']) && module_exists('transliteration')
  82. ? $settings[$name]['options']['transliterate']
  83. : 0,
  84. '#description' => t('Transliterate @title', array('@title' => $field['title'])) . '.',
  85. '#disabled' => !module_exists('transliteration'),
  86. );
  87. // Replacement patterns for field.
  88. $form['instance']['settings']['filefield_paths']['token_tree'] = array(
  89. '#type' => 'fieldset',
  90. '#title' => t('Replacement patterns'),
  91. '#collapsible' => TRUE,
  92. '#collapsed' => TRUE,
  93. '#description' => theme('token_tree', array('token_types' => array('file', $entity_info['token type']))),
  94. '#weight' => 10,
  95. );
  96. // Retroactive updates.
  97. $form['instance']['settings']['filefield_paths']['retroactive_update'] = array(
  98. '#type' => 'checkbox',
  99. '#title' => t('Retroactive update'),
  100. '#description' => t('Move and rename previously uploaded files') . '.' .
  101. '<br /> <strong style="color: #FF0000;">' . t('Warning') . ':</strong> ' .
  102. t('This feature should only be used on developmental servers or with extreme caution') . '.',
  103. '#weight' => 11,
  104. );
  105. // Active updating.
  106. $form['instance']['settings']['filefield_paths']['active_updating'] = array(
  107. '#type' => 'checkbox',
  108. '#title' => t('Active updating'),
  109. '#default_value' => isset($settings['active_updating']) ? $settings['active_updating'] : FALSE,
  110. '#description' => t('Actively move and rename previously uploaded files as required') . '.' .
  111. '<br /> <strong style="color: #FF0000;">' . t('Warning') . ':</strong> ' .
  112. t('This feature should only be used on developmental servers or with extreme caution') . '.',
  113. '#weight' => 12
  114. );
  115. }
  116. }
  117. $form['#submit'][] = 'filefield_paths_form_submit';
  118. }
  119. }
  120. /**
  121. * Submit callback for File (Field) Paths settings form.
  122. */
  123. function filefield_paths_form_submit($form, &$form_state) {
  124. // Retroactive updates.
  125. if ($form_state['values']['instance']['settings']['filefield_paths']['retroactive_update']) {
  126. filefield_paths_batch_update($form_state['values']['instance']);
  127. batch_process($form_state['redirect']);
  128. }
  129. }
  130. /**
  131. * Set batch process to update File (Field) Paths.
  132. *
  133. * @param $instance
  134. */
  135. function filefield_paths_batch_update($instance) {
  136. $query = new EntityFieldQuery();
  137. $result = $query->entityCondition('entity_type', $instance['entity_type'])
  138. ->entityCondition('bundle', array($instance['bundle']))
  139. ->fieldCondition($instance['field_name'])
  140. ->execute();
  141. $objects = array_keys($result[$instance['entity_type']]);
  142. // Create batch.
  143. $batch = array(
  144. 'title' => t('Updating File (Field) Paths'),
  145. 'operations' => array(
  146. array('_filefield_paths_batch_update_process', array($objects, $instance))
  147. ),
  148. );
  149. batch_set($batch);
  150. }
  151. /**
  152. *
  153. */
  154. function _filefield_paths_batch_update_process($objects, $instance, &$context) {
  155. if (!isset($context['sandbox']['progress'])) {
  156. $context['sandbox']['progress'] = 0;
  157. $context['sandbox']['max'] = count($objects);
  158. $context['sandbox']['objects'] = $objects;
  159. }
  160. // Process nodes by groups of 5.
  161. $count = min(5, count($context['sandbox']['objects']));
  162. for ($i = 1; $i <= $count; $i++) {
  163. // For each oid, load the object, update the files and save it.
  164. $oid = array_shift($context['sandbox']['objects']);
  165. $entity = current(entity_load($instance['entity_type'], array($oid)));
  166. // Enable active updating if it isn't already enabled.
  167. $active_updating = $instance['settings']['filefield_paths']['active_updating'];
  168. if (!$active_updating) {
  169. $instance['settings']['filefield_paths']['active_updating'] = TRUE;
  170. field_update_instance($instance);
  171. }
  172. // Invoke File (Field) Paths implementation of hook_entity_update().
  173. filefield_paths_entity_update($entity, $instance['entity_type']);
  174. // Restore active updating to it's previous state if necessary.
  175. if (!$active_updating) {
  176. $instance['settings']['filefield_paths']['active_updating'] = $active_updating;
  177. field_update_instance($instance);
  178. }
  179. // Update our progress information.
  180. $context['sandbox']['progress']++;
  181. }
  182. // Inform the batch engine that we are not finished,
  183. // and provide an estimation of the completion level we reached.
  184. if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
  185. $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  186. }
  187. }
  188. /**
  189. * Implements hook_entity_insert().
  190. */
  191. function filefield_paths_entity_insert($entity, $type) {
  192. filefield_paths_entity_update($entity, $type);
  193. }
  194. /**
  195. * Implements hook_entity_update().
  196. */
  197. function filefield_paths_entity_update($entity, $type) {
  198. $field_types = _filefield_paths_get_field_types();
  199. $entity_info = entity_get_info($type);
  200. $bundle_name = !empty($entity_info['entity keys']['bundle']) ? $entity->{$entity_info['entity keys']['bundle']} : $type;
  201. if ($entity_info['fieldable']) {
  202. foreach (field_info_fields($type, $bundle_name) as $field) {
  203. if (in_array($field['type'], array_keys($field_types))) {
  204. $files = array();
  205. $instance = field_info_instance($type, $field['field_name'], $bundle_name);
  206. if (isset($entity->{$field['field_name']})) {
  207. foreach ($entity->{$field['field_name']} as $langcode => &$deltas) {
  208. foreach ($deltas as $delta => &$file) {
  209. // Prepare file.
  210. if (function_exists($function = "{$field['module']}_field_load")) {
  211. $items = array(array(&$file));
  212. $function($type, array($entity), $field, array($instance), $langcode, $items, FIELD_LOAD_CURRENT);
  213. }
  214. $files[] = &$file;
  215. }
  216. // Invoke hook_filefield_paths_process_file().
  217. foreach (module_implements('filefield_paths_process_file') as $module) {
  218. if (function_exists($function = "{$module}_filefield_paths_process_file")) {
  219. $function($type, $entity, $field, $instance, $langcode, $files);
  220. }
  221. }
  222. }
  223. }
  224. }
  225. }
  226. if (isset($entity->revision)) {
  227. // Remember revision flag.
  228. $revision = $entity->revision;
  229. // Remove revision flag as long as fields already processed it, and no need
  230. // to create new revision for moved files.
  231. $entity->revision = FALSE;
  232. }
  233. // Save any changes back to the database.
  234. $entity->original = isset($entity->original) ? $entity->original : NULL;
  235. field_attach_update($type, $entity);
  236. if (isset($entity->revision)) {
  237. // Restore revision flag so that other modules can process it if needed.
  238. $entity->revision = $revision;
  239. }
  240. }
  241. }
  242. /**
  243. * Implements hook_file_presave().
  244. */
  245. function filefield_paths_file_presave($file) {
  246. // Store original filename in the database.
  247. if (empty($file->origname) && isset($file->filename)) {
  248. $file->origname = $file->filename;
  249. }
  250. }
  251. /**
  252. * Run regular expression over all available text-based fields.
  253. */
  254. function _filefield_paths_replace_path($old, $new, $entity) {
  255. // Build regular expression.
  256. $info = parse_url(file_stream_wrapper_uri_normalize($old));
  257. $info['path'] = !empty($info['path']) ? $info['path'] : '';
  258. $absolute = str_replace("{$info['host']}{$info['path']}", '', file_create_url($old));
  259. $relative = parse_url($absolute, PHP_URL_PATH);
  260. $regex = str_replace('/', '\/', "({$absolute}|{$relative}|{$info['scheme']}://)(styles/.*?/{$info['scheme']}/|)({$info['host']}{$info['path']})");
  261. // Build replacement.
  262. $info = parse_url(file_stream_wrapper_uri_normalize($new));
  263. $info['path'] = !empty($info['path']) ? $info['path'] : '';
  264. $replacement = "_filefield_paths_replace_path_uri_scheme('\\1', '{$old}', '{$new}') . '\\2{$info['host']}{$info['path']}'";
  265. $fields = field_info_fields();
  266. foreach ($fields as $name => $field) {
  267. if ($field['module'] == 'text' && isset($entity->{$field['field_name']}) && is_array($entity->{$field['field_name']})) {
  268. foreach ($entity->{$field['field_name']} as &$language) {
  269. foreach ($language as &$item) {
  270. $item['value'] = preg_replace("/$regex/e", $replacement, $item['value']);
  271. }
  272. }
  273. }
  274. }
  275. }
  276. /**
  277. * Helper function for File (Field) Paths URI updater regular expression.
  278. *
  279. * Determines what format the old URI prefix was and returns the new URI prefix
  280. * in the same format.
  281. */
  282. function _filefield_paths_replace_path_uri_scheme($prefix, $old, $new) {
  283. switch (TRUE) {
  284. case $prefix == file_uri_scheme($old) . '://':
  285. return file_uri_scheme($new) . '://';
  286. case $prefix == file_create_url(file_uri_scheme($old) . '://'):
  287. return file_create_url(file_uri_scheme($new) . '://');
  288. case $prefix == parse_url(file_create_url(file_uri_scheme($old) . '://'), PHP_URL_PATH):
  289. return parse_url(file_create_url(file_uri_scheme($new) . '://'), PHP_URL_PATH);
  290. }
  291. return $prefix;
  292. }
  293. /**
  294. * Process and cleanup strings.
  295. */
  296. function filefield_paths_process_string($value, $data, $settings = array()) {
  297. $transliterate = module_exists('transliteration') && isset($settings['transliterate']) && $settings['transliterate'];
  298. $pathauto = module_exists('pathauto') && isset($settings['pathauto']) && $settings['pathauto'] == TRUE;
  299. if ($pathauto == TRUE) {
  300. module_load_include('inc', 'pathauto');
  301. }
  302. $paths = explode('/', $value);
  303. foreach ($paths as &$path) {
  304. // Process string tokens.
  305. $path = token_replace($path, $data, array('clear' => TRUE));
  306. // Cleanup with pathauto.
  307. if ($pathauto == TRUE) {
  308. $path_parts = explode('.', $path);
  309. foreach ($path_parts as &$path_part) {
  310. $path_part = pathauto_cleanstring($path_part);
  311. }
  312. $path = implode('.', $path_parts);
  313. }
  314. // Transliterate string.
  315. if ($transliterate == TRUE) {
  316. $path = transliteration_clean_filename($path);
  317. }
  318. }
  319. $value = implode('/', $paths);
  320. // Ensure that there are no double-slash sequences due to empty token values.
  321. $value = preg_replace('/\/+/', '/', $value);
  322. return $value;
  323. }
  324. /**
  325. *
  326. */
  327. function _filefield_paths_get_field_types($reset = FALSE) {
  328. $field_types = &drupal_static(__FUNCTION__);
  329. if (empty($field_types) || $reset) {
  330. $field_types = module_invoke_all('filefield_paths_field_type_info');
  331. $field_types = array_flip($field_types);
  332. foreach (array_keys($field_types) as $type) {
  333. $info = field_info_field_types($type);
  334. $field_types[$type] = array(
  335. 'label' => $info['label']
  336. );
  337. }
  338. }
  339. return $field_types;
  340. }