filefield_paths.module 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. <?php
  2. /**
  3. * @file
  4. * Contains core functions for the File (Field) Paths module.
  5. */
  6. /**
  7. * Include additional files.
  8. */
  9. $dirname = dirname(__FILE__) . "/modules";
  10. $includes = file_scan_directory($dirname, '/.inc$/');
  11. foreach (module_list() as $module) {
  12. if (isset($includes[$file = "{$dirname}/{$module}.inc"])) {
  13. require_once $file;
  14. }
  15. }
  16. /**
  17. * Implements hook_menu().
  18. */
  19. function filefield_paths_menu() {
  20. $items['admin/config/media/file-system/filefield-paths'] = array(
  21. 'title' => t('File (Field) Paths settings'),
  22. 'page callback' => 'drupal_get_form',
  23. 'page arguments' => array('filefield_paths_settings_form'),
  24. 'access arguments' => array('administer site configuration'),
  25. 'type' => MENU_NORMAL_ITEM,
  26. 'file' => 'filefield_paths.admin.inc',
  27. );
  28. return $items;
  29. }
  30. /**
  31. * Implements hook_form_alter().
  32. *
  33. * @param $form
  34. */
  35. function filefield_paths_form_alter(&$form) {
  36. // Force all File (Field) Paths uploads to go to the temporary file system
  37. // prior to being processed.
  38. if (isset($form['#entity']) && isset($form['#entity_type']) && isset($form['#bundle']) && $form['#type'] == 'form') {
  39. filefield_paths_temporary_upload_location($form);
  40. }
  41. $field_types = _filefield_paths_get_field_types();
  42. if (isset($form['#field']) && in_array($form['#field']['type'], array_keys($field_types))) {
  43. $entity_info = entity_get_info($form['#instance']['entity_type']);
  44. $settings = isset($form['#instance']['settings']['filefield_paths']) ? $form['#instance']['settings']['filefield_paths'] : array();
  45. $form['instance']['settings']['filefield_paths_enabled'] = array(
  46. '#type' => 'checkbox',
  47. '#title' => t('Enable File (Field) Paths?'),
  48. '#default_value' => isset($form['#instance']['settings']['filefield_paths_enabled']) ? $form['#instance']['settings']['filefield_paths_enabled'] : TRUE,
  49. '#weight' => 2,
  50. );
  51. // Hide standard File directory field.
  52. $form['instance']['settings']['file_directory']['#states'] = array(
  53. 'visible' => array(
  54. ':input[name="instance[settings][filefield_paths_enabled]"]' => array('checked' => FALSE),
  55. ),
  56. );
  57. // File (Field) Paths fieldset element.
  58. $form['instance']['settings']['filefield_paths'] = array(
  59. '#type' => 'fieldset',
  60. '#title' => t('File (Field) Path settings'),
  61. '#collapsible' => TRUE,
  62. '#collapsed' => TRUE,
  63. '#weight' => 3,
  64. '#tree' => TRUE,
  65. '#states' => array(
  66. 'visible' => array(
  67. ':input[name="instance[settings][filefield_paths_enabled]"]' => array('checked' => TRUE),
  68. ),
  69. ),
  70. );
  71. // Additional File (Field) Paths widget fields.
  72. $fields = module_invoke_all('filefield_paths_field_settings', $form['#field'], $form['#instance']);
  73. foreach ($fields as $name => $field) {
  74. // Attach widget fields.
  75. $form['instance']['settings']['filefield_paths'][$name] = array(
  76. '#type' => 'container',
  77. );
  78. // Attach widget field form elements.
  79. if (isset($field['form']) && is_array($field['form'])) {
  80. foreach (array_keys($field['form']) as $delta => $key) {
  81. $form['instance']['settings']['filefield_paths'][$name][$key] = $field['form'][$key];
  82. if (module_exists('token')) {
  83. $form['instance']['settings']['filefield_paths'][$name][$key]['#element_validate'][] = 'token_element_validate';
  84. $form['instance']['settings']['filefield_paths'][$name][$key]['#token_types'] = array(
  85. 'file',
  86. $entity_info['token type']
  87. );
  88. }
  89. // Fetch stored value from instance.
  90. if (isset($settings[$name][$key])) {
  91. $form['instance']['settings']['filefield_paths'][$name][$key]['#default_value'] = $settings[$name][$key];
  92. }
  93. }
  94. // Field options.
  95. $form['instance']['settings']['filefield_paths'][$name]['options'] = array(
  96. '#type' => 'fieldset',
  97. '#title' => t('@title options', array('@title' => t($field['title']))),
  98. '#collapsible' => TRUE,
  99. '#collapsed' => TRUE,
  100. '#weight' => 1,
  101. '#attributes' => array(
  102. 'class' => array("{$name} cleanup")
  103. ),
  104. );
  105. // Cleanup slashes (/).
  106. $form['instance']['settings']['filefield_paths'][$name]['options']['slashes'] = array(
  107. '#type' => 'checkbox',
  108. '#title' => t('Remove slashes (/) from tokens'),
  109. '#default_value' => isset($settings[$name]['options']['slashes']) ? $settings[$name]['options']['slashes'] : FALSE,
  110. '#description' => t('If checked, any slashes (/) in tokens will be removed from %title.', array('%title' => t($field['title']))),
  111. );
  112. // Cleanup field with Pathauto module.
  113. $form['instance']['settings']['filefield_paths'][$name]['options']['pathauto'] = array(
  114. '#type' => 'checkbox',
  115. '#title' => t('Cleanup using Pathauto'),
  116. '#default_value' => isset($settings[$name]['options']['pathauto']) && module_exists('pathauto') ? $settings[$name]['options']['pathauto'] : FALSE,
  117. '#description' => t('Cleanup %title using <a href="@pathauto">Pathauto settings</a>.', array(
  118. '%title' => t($field['title']),
  119. '@pathauto' => url('admin/config/search/path/settings')
  120. )),
  121. '#disabled' => !module_exists('pathauto'),
  122. );
  123. // Transliterate field with Transliteration module.
  124. $form['instance']['settings']['filefield_paths'][$name]['options']['transliterate'] = array(
  125. '#type' => 'checkbox',
  126. '#title' => t('Transliterate'),
  127. '#default_value' => isset($settings[$name]['options']['transliterate']) && module_exists('transliteration') ? $settings[$name]['options']['transliterate'] : 0,
  128. '#description' => t('Provides one-way string transliteration (romanization) and cleans the %title during upload by replacing unwanted characters.', array('%title' => t($field['title']))),
  129. '#disabled' => !module_exists('transliteration'),
  130. );
  131. // Replacement patterns for field.
  132. if (module_exists('token')) {
  133. $form['instance']['settings']['filefield_paths']['token_tree'] = array(
  134. '#theme' => 'token_tree',
  135. '#token_types' => array('file', $entity_info['token type']),
  136. '#dialog' => TRUE,
  137. '#weight' => 10,
  138. );
  139. }
  140. // Redirect
  141. $form['instance']['settings']['filefield_paths']['redirect'] = array(
  142. '#type' => 'checkbox',
  143. '#title' => t('Create Redirect'),
  144. '#description' => t('Create a redirect to the new location when a previously uploaded file is moved.'),
  145. '#default_value' => isset($settings['redirect']) ? $settings['redirect'] : FALSE,
  146. '#weight' => 11,
  147. );
  148. if (!module_exists('redirect')) {
  149. $form['instance']['settings']['filefield_paths']['redirect']['#disabled'] = TRUE;
  150. $form['instance']['settings']['filefield_paths']['redirect']['#description'] .= '<br />' . t('Requires the <a href="https://drupal.org/project/redirect" target="_blank">Redirect</a> module.');
  151. }
  152. // Retroactive updates.
  153. $form['instance']['settings']['filefield_paths']['retroactive_update'] = array(
  154. '#type' => 'checkbox',
  155. '#title' => t('Retroactive update'),
  156. '#description' => t('Move and rename previously uploaded files.') . '<div>' . t('<strong class="warning">Warning:</strong> This feature should only be used on developmental servers or with extreme caution.') . '</div>',
  157. '#weight' => 12,
  158. );
  159. // Active updating.
  160. $form['instance']['settings']['filefield_paths']['active_updating'] = array(
  161. '#type' => 'checkbox',
  162. '#title' => t('Active updating'),
  163. '#default_value' => isset($settings['active_updating']) ? $settings['active_updating'] : FALSE,
  164. '#description' => t('Actively move and rename previously uploaded files as required.') . '<div>' . t('<strong class="warning">Warning:</strong> This feature should only be used on developmental servers or with extreme caution.') . '</div>',
  165. '#weight' => 13
  166. );
  167. }
  168. }
  169. $form['#submit'][] = 'filefield_paths_form_submit';
  170. }
  171. }
  172. /**
  173. * Recursively set temporary upload location of all File (Field) Paths enabled
  174. * managed file fields.
  175. *
  176. * @param $element
  177. */
  178. function filefield_paths_temporary_upload_location(&$element) {
  179. if (isset($element['#type']) && $element['#type'] == 'managed_file' && isset($element['#entity_type']) && isset($element['#field_name']) && isset($element['#bundle'])) {
  180. $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
  181. if (isset($instance['settings']['filefield_paths_enabled']) && $instance['settings']['filefield_paths_enabled']) {
  182. $element['#upload_location'] = variable_get('filefield_paths_temp_location', 'public://filefield_paths');
  183. }
  184. return;
  185. }
  186. foreach (element_children($element) as $child) {
  187. filefield_paths_temporary_upload_location($element[$child]);
  188. }
  189. }
  190. /**
  191. * Submit callback for File (Field) Paths settings form.
  192. *
  193. * @param $form
  194. * @param $form_state
  195. */
  196. function filefield_paths_form_submit($form, &$form_state) {
  197. // Retroactive updates.
  198. if ($form_state['values']['instance']['settings']['filefield_paths_enabled'] && $form_state['values']['instance']['settings']['filefield_paths']['retroactive_update']) {
  199. if (filefield_paths_batch_update($form_state['values']['instance'])) {
  200. batch_process($form_state['redirect']);
  201. }
  202. }
  203. }
  204. /**
  205. * Set batch process to update File (Field) Paths.
  206. *
  207. * @param $instance
  208. *
  209. * @return bool
  210. */
  211. function filefield_paths_batch_update($instance) {
  212. $query = new EntityFieldQuery();
  213. $result = $query->entityCondition('entity_type', $instance['entity_type'])
  214. ->entityCondition('bundle', array($instance['bundle']))
  215. ->fieldCondition($instance['field_name'])
  216. ->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT')
  217. ->execute();
  218. // If there are no results, do not set a batch as there is nothing to process.
  219. if (empty($result[$instance['entity_type']])) {
  220. return FALSE;
  221. }
  222. $objects = array_keys($result[$instance['entity_type']]);
  223. $instance = field_info_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
  224. // Create batch.
  225. $batch = array(
  226. 'title' => t('Updating File (Field) Paths'),
  227. 'operations' => array(
  228. array('_filefield_paths_batch_update_process', array($objects, $instance))
  229. ),
  230. );
  231. batch_set($batch);
  232. return TRUE;
  233. }
  234. /**
  235. * Batch callback for File (Field) Paths retroactive updates.
  236. *
  237. * @param $objects
  238. * @param $instance
  239. * @param $context
  240. *
  241. * @throws FieldException
  242. */
  243. function _filefield_paths_batch_update_process($objects, $instance, &$context) {
  244. if (!isset($context['sandbox']['progress'])) {
  245. $context['sandbox']['progress'] = 0;
  246. $context['sandbox']['max'] = count($objects);
  247. $context['sandbox']['objects'] = $objects;
  248. }
  249. // Process nodes by groups of 5.
  250. $count = min(5, count($context['sandbox']['objects']));
  251. for ($i = 1; $i <= $count; $i++) {
  252. // For each oid, load the object, update the files and save it.
  253. $oid = array_shift($context['sandbox']['objects']);
  254. $entity = current(entity_load($instance['entity_type'], array($oid)));
  255. // Enable active updating if it isn't already enabled.
  256. $active_updating = $instance['settings']['filefield_paths']['active_updating'];
  257. if (!$active_updating) {
  258. $instance['settings']['filefield_paths']['active_updating'] = TRUE;
  259. field_update_instance($instance);
  260. }
  261. // Invoke field_attach_update().
  262. field_attach_update($instance['entity_type'], $entity);
  263. // Restore active updating to it's previous state if necessary.
  264. if (!$active_updating) {
  265. $instance['settings']['filefield_paths']['active_updating'] = $active_updating;
  266. field_update_instance($instance);
  267. }
  268. // Update our progress information.
  269. $context['sandbox']['progress']++;
  270. }
  271. // Inform the batch engine that we are not finished,
  272. // and provide an estimation of the completion level we reached.
  273. if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
  274. $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  275. }
  276. }
  277. /**
  278. * Implements hook_field_storage_pre_insert().
  279. *
  280. * @param $entity_type
  281. * @param $entity
  282. */
  283. function filefield_paths_field_storage_pre_insert($entity_type, $entity) {
  284. filefield_paths_field_storage_pre_update($entity_type, $entity);
  285. }
  286. /**
  287. * Implements hook_field_storage_pre_update().
  288. *
  289. * @param $entity_type
  290. * @param $entity
  291. */
  292. function filefield_paths_field_storage_pre_update($entity_type, $entity) {
  293. $field_types = _filefield_paths_get_field_types();
  294. $entity_info = entity_get_info($entity_type);
  295. list(, , $bundle) = entity_extract_ids($entity_type, $entity);
  296. if ($entity_info['fieldable']) {
  297. foreach (field_info_fields() as $field) {
  298. if (in_array($field['type'], array_keys($field_types))) {
  299. $files = array();
  300. $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
  301. $enabled = (isset($instance['settings']['filefield_paths_enabled']) && $instance['settings']['filefield_paths_enabled']) || !isset($instance['settings']['filefield_paths_enabled']);
  302. if ($enabled && isset($entity->{$field['field_name']}) && is_array($entity->{$field['field_name']})) {
  303. foreach ($entity->{$field['field_name']} as $langcode => &$deltas) {
  304. foreach ($deltas as $delta => &$file) {
  305. // Prepare file.
  306. if (function_exists($function = "{$field['module']}_field_load")) {
  307. $items = array(array(&$file));
  308. $function($entity_type, array($entity), $field, array($instance), $langcode, $items, FIELD_LOAD_CURRENT);
  309. }
  310. $files[] = &$file;
  311. }
  312. // Invoke hook_filefield_paths_process_file().
  313. foreach (module_implements('filefield_paths_process_file') as $module) {
  314. if (function_exists($function = "{$module}_filefield_paths_process_file")) {
  315. $function($entity_type, $entity, $field, $instance, $langcode, $files);
  316. }
  317. }
  318. }
  319. }
  320. }
  321. }
  322. }
  323. }
  324. /**
  325. * Implements hook_file_presave().
  326. *
  327. * @param $file
  328. */
  329. function filefield_paths_file_presave($file) {
  330. // Store original filename in the database.
  331. if (empty($file->origname) && isset($file->filename)) {
  332. $file->origname = $file->filename;
  333. }
  334. }
  335. /**
  336. * Creates a redirect for a moved File field.
  337. *
  338. * @param $source
  339. * @param $path
  340. *
  341. * @throws Exception
  342. */
  343. function _filefield_paths_create_redirect($source, $path) {
  344. global $base_path;
  345. watchdog('filefield_paths', 'Creating redirect from @source to @path', array(
  346. '@source' => $source,
  347. '@path' => $path
  348. ), WATCHDOG_DEBUG);
  349. $redirect = new stdClass();
  350. redirect_object_prepare($redirect);
  351. $parsed_source = parse_url(file_create_url($source), PHP_URL_PATH);
  352. $parsed_path = parse_url(file_create_url($path), PHP_URL_PATH);
  353. $redirect->source = drupal_substr(urldecode($parsed_source), drupal_strlen($base_path));
  354. $redirect->redirect = drupal_substr(urldecode($parsed_path), drupal_strlen($base_path));
  355. // Check if the redirect exists before saving.
  356. $hash = redirect_hash($redirect);
  357. if (!redirect_load_by_hash($hash)) {
  358. redirect_save($redirect);
  359. }
  360. }
  361. /**
  362. * Run regular expression over all available text-based fields.
  363. *
  364. * @param $old
  365. * @param $new
  366. * @param $entity
  367. */
  368. function _filefield_paths_replace_path($old, $new, $entity) {
  369. $info = parse_url($old);
  370. if (isset($info['path'])) {
  371. $info['host'] .= $info['path'];
  372. }
  373. // Generate all path prefix variations.
  374. $prefixes = _filefield_paths_replace_path_get_prefixes($info['scheme'], TRUE);
  375. $prefixes = implode('|', $prefixes);
  376. // Generate all image style path variations.
  377. $styles['raw'] = "styles/REGEX/{$info['scheme']}/";
  378. $styles['urlencode'] = urlencode($styles['raw']);
  379. foreach ($styles as &$style) {
  380. $style = str_replace(array('/', 'REGEX'), array('\/', '(.*?)'), $style);
  381. }
  382. $styles = implode('|', $styles);
  383. // General all path variations.
  384. $paths['raw'] = preg_quote($info['host'], '/');
  385. $paths['urlencode'] = preg_quote(urlencode($info['host']), '/');
  386. $paths['drupal_encode_path'] = preg_quote(drupal_encode_path($info['host']), '/');
  387. $paths = implode('|', $paths);
  388. // Newer versions of the Image module add an 8 character token which is
  389. // required if the image style hasn't been generated yet.
  390. $itok = '';
  391. if (defined('IMAGE_DERIVATIVE_TOKEN')) {
  392. $itok = '((?:[\?|&](?:\S+?&)*|(?:%3F|%26)(?:\S+?%26)*)' . IMAGE_DERIVATIVE_TOKEN . '(?:=|%3D)(\S{8}))*';
  393. }
  394. // Build regular expression pattern.
  395. $pattern = "/({$prefixes})({$styles})*({$paths}){$itok}/";
  396. // Create an anonymous function for the replacement via preg_replace_callback.
  397. $callback = function ($matches) use ($new, $old) {
  398. return filefield_paths_replace_path_callback($matches, $new, $old);
  399. };
  400. if (!$callback) {
  401. watchdog('filefield_paths', 'Unable to create an anonymous function to find references of %old and replace with %new.', array(
  402. '%old' => $old,
  403. '%new' => $new,
  404. ));
  405. return;
  406. }
  407. $fields = field_info_fields();
  408. foreach ($fields as $name => $field) {
  409. if ($field['module'] == 'text' && isset($entity->{$field['field_name']}) && is_array($entity->{$field['field_name']})) {
  410. foreach ($entity->{$field['field_name']} as &$language) {
  411. foreach ($language as &$item) {
  412. foreach (array('value', 'summary') as $column) {
  413. if (isset($item[$column])) {
  414. $item[$column] = preg_replace_callback($pattern, $callback, $item[$column]);
  415. }
  416. }
  417. }
  418. }
  419. }
  420. }
  421. }
  422. /**
  423. * Helper function; Returns all variations of the file path prefix.
  424. *
  425. * @param $scheme
  426. * @param bool|FALSE $preg_quote
  427. * @param bool|FALSE $reset
  428. *
  429. * @return mixed
  430. */
  431. function _filefield_paths_replace_path_get_prefixes($scheme, $preg_quote = FALSE, $reset = FALSE) {
  432. $prefixes =& drupal_static(__FUNCTION__, array());
  433. // Force clean urls on.
  434. $clean_url = $GLOBALS['conf']['clean_url'];
  435. $GLOBALS['conf']['clean_url'] = TRUE;
  436. $id = $scheme . '::' . (string) $preg_quote;
  437. if (!isset($prefixes[$id]) || $reset) {
  438. $prefixes[$id]['uri'] = "{$scheme}://";
  439. $prefixes[$id]['absolute'] = file_create_url($prefixes[$id]['uri']);
  440. $prefixes[$id]['relative'] = parse_url($prefixes[$id]['absolute'], PHP_URL_PATH);
  441. $prefixes[$id]['unclean'] = '?q=' . drupal_substr($prefixes[$id]['relative'], drupal_strlen(base_path()));
  442. foreach ($prefixes[$id] as $key => $prefix) {
  443. $prefixes[$id]["{$key}-urlencode"] = urlencode($prefix);
  444. $prefixes[$id]["{$key}-drupal_encode_path"] = drupal_encode_path($prefix);
  445. }
  446. if ($preg_quote) {
  447. foreach ($prefixes[$id] as $key => $prefix) {
  448. $prefixes[$id][$key] = preg_quote($prefixes[$id][$key], '/');
  449. }
  450. }
  451. }
  452. // Restore clean url settings.
  453. $GLOBALS['conf']['clean_url'] = $clean_url;
  454. return $prefixes[$id];
  455. }
  456. /**
  457. * Callback for regex string replacement functionality.
  458. *
  459. * @param $matches
  460. * @param $new
  461. * @param $old
  462. *
  463. * @return string
  464. */
  465. function filefield_paths_replace_path_callback($matches, $new, $old) {
  466. $prefix = $matches[1];
  467. $styles = $matches[2];
  468. $query = isset($matches[6]) ? $matches[6] : '';
  469. // Get file path info for old file.
  470. $old_info = parse_url($old);
  471. if (isset($old_info['path'])) {
  472. $old_info['host'] .= $old_info['path'];
  473. }
  474. // Determine the file path variation type/modifier.
  475. $old_prefixes = _filefield_paths_replace_path_get_prefixes($old_info['scheme']);
  476. $modifier = NULL;
  477. foreach ($old_prefixes as $key => $old_prefix) {
  478. if ($prefix == $old_prefix) {
  479. $parts = explode('-', $key);
  480. $modifier = isset($parts[1]) ? $parts[1] : NULL;
  481. break;
  482. }
  483. }
  484. // Get file path info for new file.
  485. $new_info = parse_url($new);
  486. if (isset($new_info['path'])) {
  487. $new_info['host'] .= $new_info['path'];
  488. }
  489. // Replace prefix.
  490. $prefixes = _filefield_paths_replace_path_get_prefixes($new_info['scheme']);
  491. if (isset($key) && isset($prefixes[$key])) {
  492. $prefix = $prefixes[$key];
  493. }
  494. // Replace styles directory.
  495. if (!empty($styles)) {
  496. $styles = str_replace($old_info['scheme'], $new_info['scheme'], $styles);
  497. // Newer versions of the Image module add an 8 character token which is
  498. // required if the image style hasn't been generated yet.
  499. if (defined('IMAGE_DERIVATIVE_TOKEN') && isset($matches[7])) {
  500. $image_style = !empty($matches[3]) ? $matches[3] : $matches[4];
  501. // Only replace the token if the old one was valid.
  502. if ($matches[7] == image_style_path_token($image_style, $old)) {
  503. $query = substr_replace($query, image_style_path_token($image_style, $new), -strlen($matches[7]));
  504. }
  505. }
  506. }
  507. // Replace path.
  508. $path = $new_info['host'];
  509. if (!is_null($modifier) && function_exists($modifier)) {
  510. $path = call_user_func($modifier, $path);
  511. }
  512. return $prefix . $styles . $path . $query;
  513. }
  514. /**
  515. * Process and cleanup strings.
  516. *
  517. * @param $value
  518. * @param $data
  519. * @param array $settings
  520. *
  521. * @return mixed|string
  522. */
  523. function filefield_paths_process_string($value, $data, $settings = array()) {
  524. $transliterate = module_exists('transliteration') && isset($settings['transliterate']) && $settings['transliterate'];
  525. $pathauto = module_exists('pathauto') && isset($settings['pathauto']) && $settings['pathauto'] == TRUE;
  526. $remove_slashes = !empty($settings['slashes']);
  527. if ($pathauto == TRUE) {
  528. module_load_include('inc', 'pathauto');
  529. }
  530. // If '/' is to be removed from tokens, token replacement need to happen after
  531. // splitting the paths to subdirs, otherwise tokens containing '/' will be
  532. // part of the final path.
  533. if (!$remove_slashes) {
  534. $value = token_replace($value, $data, array('clear' => TRUE));
  535. }
  536. $paths = explode('/', $value);
  537. foreach ($paths as $i => &$path) {
  538. if ($remove_slashes) {
  539. $path = token_replace($path, $data, array('clear' => TRUE));
  540. }
  541. if ($pathauto == TRUE) {
  542. if ('file_name' == $settings['context'] && count($paths) == $i + 1) {
  543. $pathinfo = pathinfo($path);
  544. $basename = drupal_basename($path);
  545. $extension = preg_match('/\.[^.]+$/', $basename, $matches) ? $matches[0] : NULL;
  546. $pathinfo['filename'] = !is_null($extension) ? drupal_substr($basename, 0, drupal_strlen($basename) - drupal_strlen($extension)) : $basename;
  547. if ($remove_slashes) {
  548. $path = '';
  549. if (!empty($pathinfo['dirname']) && $pathinfo['dirname'] !== '.') {
  550. $path .= $pathinfo['dirname'] . '/';
  551. }
  552. $path .= $pathinfo['filename'];
  553. $path = pathauto_cleanstring($path);
  554. if (!empty($pathinfo['extension'])) {
  555. $path .= '.' . pathauto_cleanstring($pathinfo['extension']);
  556. }
  557. $path = str_replace('/', '', $path);
  558. }
  559. else {
  560. $path = str_replace($pathinfo['filename'], pathauto_cleanstring($pathinfo['filename']), $path);
  561. }
  562. }
  563. else {
  564. $path = pathauto_cleanstring($path);
  565. }
  566. }
  567. elseif ($remove_slashes) {
  568. $path = str_replace('/', '', $path);
  569. }
  570. // Transliterate string.
  571. if ($transliterate == TRUE) {
  572. $path = transliteration_clean_filename($path);
  573. }
  574. }
  575. $value = implode('/', $paths);
  576. // Ensure that there are no double-slash sequences due to empty token values.
  577. $value = preg_replace('/\/+/', '/', $value);
  578. return $value;
  579. }
  580. /**
  581. * Provides a list of all available field types for use with File (Field) Paths.
  582. *
  583. * @param bool|FALSE $reset
  584. *
  585. * @return array
  586. */
  587. function _filefield_paths_get_field_types($reset = FALSE) {
  588. $field_types = &drupal_static(__FUNCTION__);
  589. if (empty($field_types) || $reset) {
  590. $field_types = module_invoke_all('filefield_paths_field_type_info');
  591. $field_types = array_flip($field_types);
  592. foreach (array_keys($field_types) as $type) {
  593. $info = field_info_field_types($type);
  594. $field_types[$type] = array(
  595. 'label' => $info['label']
  596. );
  597. }
  598. }
  599. return $field_types;
  600. }