class-acf-field-file.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. <?php
  2. if ( ! class_exists( 'acf_field_file' ) ) :
  3. class acf_field_file extends acf_field {
  4. /*
  5. * __construct
  6. *
  7. * This function will setup the field type data
  8. *
  9. * @type function
  10. * @date 5/03/2014
  11. * @since 5.0.0
  12. *
  13. * @param n/a
  14. * @return n/a
  15. */
  16. function initialize() {
  17. // vars
  18. $this->name = 'file';
  19. $this->label = __( 'File', 'acf' );
  20. $this->category = 'content';
  21. $this->defaults = array(
  22. 'return_format' => 'array',
  23. 'library' => 'all',
  24. 'min_size' => 0,
  25. 'max_size' => 0,
  26. 'mime_types' => '',
  27. );
  28. // filters
  29. add_filter( 'get_media_item_args', array( $this, 'get_media_item_args' ) );
  30. }
  31. /*
  32. * input_admin_enqueue_scripts
  33. *
  34. * description
  35. *
  36. * @type function
  37. * @date 16/12/2015
  38. * @since 5.3.2
  39. *
  40. * @param $post_id (int)
  41. * @return $post_id (int)
  42. */
  43. function input_admin_enqueue_scripts() {
  44. // localize
  45. acf_localize_text(
  46. array(
  47. 'Select File' => __( 'Select File', 'acf' ),
  48. 'Edit File' => __( 'Edit File', 'acf' ),
  49. 'Update File' => __( 'Update File', 'acf' ),
  50. )
  51. );
  52. }
  53. /*
  54. * render_field()
  55. *
  56. * Create the HTML interface for your field
  57. *
  58. * @param $field - an array holding all the field's data
  59. *
  60. * @type action
  61. * @since 3.6
  62. * @date 23/01/13
  63. */
  64. function render_field( $field ) {
  65. // vars
  66. $uploader = acf_get_setting( 'uploader' );
  67. // allow custom uploader
  68. $uploader = acf_maybe_get( $field, 'uploader', $uploader );
  69. // enqueue
  70. if ( $uploader == 'wp' ) {
  71. acf_enqueue_uploader();
  72. }
  73. // vars
  74. $o = array(
  75. 'icon' => '',
  76. 'title' => '',
  77. 'url' => '',
  78. 'filename' => '',
  79. 'filesize' => '',
  80. );
  81. $div = array(
  82. 'class' => 'acf-file-uploader',
  83. 'data-library' => $field['library'],
  84. 'data-mime_types' => $field['mime_types'],
  85. 'data-uploader' => $uploader,
  86. );
  87. // has value?
  88. if ( $field['value'] ) {
  89. $attachment = acf_get_attachment( $field['value'] );
  90. if ( $attachment ) {
  91. // has value
  92. $div['class'] .= ' has-value';
  93. // update
  94. $o['icon'] = $attachment['icon'];
  95. $o['title'] = $attachment['title'];
  96. $o['url'] = $attachment['url'];
  97. $o['filename'] = $attachment['filename'];
  98. if ( $attachment['filesize'] ) {
  99. $o['filesize'] = size_format( $attachment['filesize'] );
  100. }
  101. }
  102. }
  103. ?>
  104. <div <?php echo acf_esc_attrs( $div ); ?>>
  105. <?php
  106. acf_hidden_input(
  107. array(
  108. 'name' => $field['name'],
  109. 'value' => $field['value'],
  110. 'data-name' => 'id',
  111. )
  112. );
  113. ?>
  114. <div class="show-if-value file-wrap">
  115. <div class="file-icon">
  116. <img data-name="icon" src="<?php echo esc_url( $o['icon'] ); ?>" alt=""/>
  117. </div>
  118. <div class="file-info">
  119. <p>
  120. <strong data-name="title"><?php echo esc_html( $o['title'] ); ?></strong>
  121. </p>
  122. <p>
  123. <strong><?php _e( 'File name', 'acf' ); ?>:</strong>
  124. <a data-name="filename" href="<?php echo esc_url( $o['url'] ); ?>" target="_blank"><?php echo esc_html( $o['filename'] ); ?></a>
  125. </p>
  126. <p>
  127. <strong><?php _e( 'File size', 'acf' ); ?>:</strong>
  128. <span data-name="filesize"><?php echo esc_html( $o['filesize'] ); ?></span>
  129. </p>
  130. </div>
  131. <div class="acf-actions -hover">
  132. <?php if ( $uploader != 'basic' ) : ?>
  133. <a class="acf-icon -pencil dark" data-name="edit" href="#" title="<?php _e( 'Edit', 'acf' ); ?>"></a>
  134. <?php endif; ?>
  135. <a class="acf-icon -cancel dark" data-name="remove" href="#" title="<?php _e( 'Remove', 'acf' ); ?>"></a>
  136. </div>
  137. </div>
  138. <div class="hide-if-value">
  139. <?php if ( $uploader == 'basic' ) : ?>
  140. <?php if ( $field['value'] && ! is_numeric( $field['value'] ) ) : ?>
  141. <div class="acf-error-message"><p><?php echo acf_esc_html( $field['value'] ); ?></p></div>
  142. <?php endif; ?>
  143. <label class="acf-basic-uploader">
  144. <?php
  145. acf_file_input(
  146. array(
  147. 'name' => $field['name'],
  148. 'id' => $field['id'],
  149. 'key' => $field['key'],
  150. )
  151. );
  152. ?>
  153. </label>
  154. <?php else : ?>
  155. <p><?php _e( 'No file selected', 'acf' ); ?> <a data-name="add" class="acf-button button" href="#"><?php _e( 'Add File', 'acf' ); ?></a></p>
  156. <?php endif; ?>
  157. </div>
  158. </div>
  159. <?php
  160. }
  161. /*
  162. * render_field_settings()
  163. *
  164. * Create extra options for your field. This is rendered when editing a field.
  165. * The value of $field['name'] can be used (like bellow) to save extra data to the $field
  166. *
  167. * @type action
  168. * @since 3.6
  169. * @date 23/01/13
  170. *
  171. * @param $field - an array holding all the field's data
  172. */
  173. function render_field_settings( $field ) {
  174. acf_render_field_setting(
  175. $field,
  176. array(
  177. 'label' => __( 'Return Value', 'acf' ),
  178. 'instructions' => __( 'Specify the returned value on front end', 'acf' ),
  179. 'type' => 'radio',
  180. 'name' => 'return_format',
  181. 'layout' => 'horizontal',
  182. 'choices' => array(
  183. 'array' => __( 'File Array', 'acf' ),
  184. 'url' => __( 'File URL', 'acf' ),
  185. 'id' => __( 'File ID', 'acf' ),
  186. ),
  187. )
  188. );
  189. acf_render_field_setting(
  190. $field,
  191. array(
  192. 'label' => __( 'Library', 'acf' ),
  193. 'instructions' => __( 'Limit the media library choice', 'acf' ),
  194. 'type' => 'radio',
  195. 'name' => 'library',
  196. 'layout' => 'horizontal',
  197. 'choices' => array(
  198. 'all' => __( 'All', 'acf' ),
  199. 'uploadedTo' => __( 'Uploaded to post', 'acf' ),
  200. ),
  201. )
  202. );
  203. }
  204. /**
  205. * Renders the field settings used in the "Validation" tab.
  206. *
  207. * @since 6.0
  208. *
  209. * @param array $field The field settings array.
  210. * @return void
  211. */
  212. function render_field_validation_settings( $field ) {
  213. // Clear numeric settings.
  214. $clear = array(
  215. 'min_size',
  216. 'max_size',
  217. );
  218. foreach ( $clear as $k ) {
  219. if ( empty( $field[ $k ] ) ) {
  220. $field[ $k ] = '';
  221. }
  222. }
  223. acf_render_field_setting(
  224. $field,
  225. array(
  226. 'label' => __( 'Minimum', 'acf' ),
  227. 'instructions' => __( 'Restrict which files can be uploaded', 'acf' ),
  228. 'type' => 'text',
  229. 'name' => 'min_size',
  230. 'prepend' => __( 'File size', 'acf' ),
  231. 'append' => 'MB',
  232. )
  233. );
  234. acf_render_field_setting(
  235. $field,
  236. array(
  237. 'label' => __( 'Maximum', 'acf' ),
  238. 'instructions' => __( 'Restrict which files can be uploaded', 'acf' ),
  239. 'type' => 'text',
  240. 'name' => 'max_size',
  241. 'prepend' => __( 'File size', 'acf' ),
  242. 'append' => 'MB',
  243. )
  244. );
  245. acf_render_field_setting(
  246. $field,
  247. array(
  248. 'label' => __( 'Allowed file types', 'acf' ),
  249. 'hint' => __( 'Comma separated list. Leave blank for all types', 'acf' ),
  250. 'type' => 'text',
  251. 'name' => 'mime_types',
  252. )
  253. );
  254. }
  255. /*
  256. * format_value()
  257. *
  258. * This filter is appied to the $value after it is loaded from the db and before it is returned to the template
  259. *
  260. * @type filter
  261. * @since 3.6
  262. * @date 23/01/13
  263. *
  264. * @param $value (mixed) the value which was loaded from the database
  265. * @param $post_id (mixed) the $post_id from which the value was loaded
  266. * @param $field (array) the field array holding all the field options
  267. *
  268. * @return $value (mixed) the modified value
  269. */
  270. function format_value( $value, $post_id, $field ) {
  271. // bail early if no value
  272. if ( empty( $value ) ) {
  273. return false;
  274. }
  275. // bail early if not numeric (error message)
  276. if ( ! is_numeric( $value ) ) {
  277. return false;
  278. }
  279. // convert to int
  280. $value = intval( $value );
  281. // format
  282. if ( $field['return_format'] == 'url' ) {
  283. return wp_get_attachment_url( $value );
  284. } elseif ( $field['return_format'] == 'array' ) {
  285. return acf_get_attachment( $value );
  286. }
  287. // return
  288. return $value;
  289. }
  290. /*
  291. * get_media_item_args
  292. *
  293. * description
  294. *
  295. * @type function
  296. * @date 27/01/13
  297. * @since 3.6.0
  298. *
  299. * @param $vars (array)
  300. * @return $vars
  301. */
  302. function get_media_item_args( $vars ) {
  303. $vars['send'] = true;
  304. return( $vars );
  305. }
  306. /*
  307. * update_value()
  308. *
  309. * This filter is appied to the $value before it is updated in the db
  310. *
  311. * @type filter
  312. * @since 3.6
  313. * @date 23/01/13
  314. *
  315. * @param $value - the value which will be saved in the database
  316. * @param $post_id - the $post_id of which the value will be saved
  317. * @param $field - the field array holding all the field options
  318. *
  319. * @return $value - the modified value
  320. */
  321. function update_value( $value, $post_id, $field ) {
  322. // Bail early if no value.
  323. if ( empty( $value ) ) {
  324. return $value;
  325. }
  326. // Parse value for id.
  327. $attachment_id = acf_idval( $value );
  328. // Connect attacment to post.
  329. acf_connect_attachment_to_post( $attachment_id, $post_id );
  330. // Return id.
  331. return $attachment_id;
  332. }
  333. /**
  334. * validate_value
  335. *
  336. * This function will validate a basic file input
  337. *
  338. * @type function
  339. * @date 11/02/2014
  340. * @since 5.0.0
  341. *
  342. * @param $post_id (int)
  343. * @return $post_id (int)
  344. */
  345. function validate_value( $valid, $value, $field, $input ) {
  346. // bail early if empty
  347. if ( empty( $value ) ) {
  348. return $valid;
  349. }
  350. // bail early if is numeric
  351. if ( is_numeric( $value ) ) {
  352. return $valid;
  353. }
  354. // bail early if not basic string
  355. if ( ! is_string( $value ) ) {
  356. return $valid;
  357. }
  358. // decode value
  359. $file = null;
  360. parse_str( $value, $file );
  361. // bail early if no attachment
  362. if ( empty( $file ) ) {
  363. return $valid;
  364. }
  365. // get errors
  366. $errors = acf_validate_attachment( $file, $field, 'basic_upload' );
  367. // append error
  368. if ( ! empty( $errors ) ) {
  369. $valid = implode( "\n", $errors );
  370. }
  371. // return
  372. return $valid;
  373. }
  374. /**
  375. * Validates file fields updated via the REST API.
  376. *
  377. * @param bool $valid
  378. * @param int $value
  379. * @param array $field
  380. *
  381. * @return bool|WP_Error
  382. */
  383. public function validate_rest_value( $valid, $value, $field ) {
  384. if ( is_null( $value ) && empty( $field['required'] ) ) {
  385. return $valid;
  386. }
  387. /**
  388. * A bit of a hack, but we use `wp_prepare_attachment_for_js()` here
  389. * since it returns all the data we need to validate the file, and we use this anyways
  390. * to validate fields updated via UI.
  391. */
  392. $attachment = wp_prepare_attachment_for_js( $value );
  393. $param = sprintf( '%s[%s]', $field['prefix'], $field['name'] );
  394. $data = array(
  395. 'param' => $param,
  396. 'value' => (int) $value,
  397. );
  398. if ( ! $attachment ) {
  399. $error = sprintf( __( '%s requires a valid attachment ID.', 'acf' ), $param );
  400. return new WP_Error( 'rest_invalid_param', $error, $data );
  401. }
  402. $errors = acf_validate_attachment( $attachment, $field, 'prepare' );
  403. if ( ! empty( $errors ) ) {
  404. $error = $param . ' - ' . implode( ' ', $errors );
  405. return new WP_Error( 'rest_invalid_param', $error, $data );
  406. }
  407. return $valid;
  408. }
  409. /**
  410. * Return the schema array for the REST API.
  411. *
  412. * @param array $field
  413. * @return array
  414. */
  415. public function get_rest_schema( array $field ) {
  416. $schema = array(
  417. 'type' => array( 'integer', 'null' ),
  418. 'required' => isset( $field['required'] ) && $field['required'],
  419. );
  420. if ( ! empty( $field['min_width'] ) ) {
  421. $schema['minWidth'] = (int) $field['min_width'];
  422. }
  423. if ( ! empty( $field['min_height'] ) ) {
  424. $schema['minHeight'] = (int) $field['min_height'];
  425. }
  426. if ( ! empty( $field['min_size'] ) ) {
  427. $schema['minSize'] = $field['min_size'];
  428. }
  429. if ( ! empty( $field['max_width'] ) ) {
  430. $schema['maxWidth'] = (int) $field['max_width'];
  431. }
  432. if ( ! empty( $field['max_height'] ) ) {
  433. $schema['maxHeight'] = (int) $field['max_height'];
  434. }
  435. if ( ! empty( $field['max_size'] ) ) {
  436. $schema['maxSize'] = $field['max_size'];
  437. }
  438. if ( ! empty( $field['mime_types'] ) ) {
  439. $schema['mimeTypes'] = $field['mime_types'];
  440. }
  441. return $schema;
  442. }
  443. /**
  444. * Apply basic formatting to prepare the value for default REST output.
  445. *
  446. * @param mixed $value
  447. * @param string|int $post_id
  448. * @param array $field
  449. * @return mixed
  450. */
  451. public function format_value_for_rest( $value, $post_id, array $field ) {
  452. return acf_format_numerics( $value );
  453. }
  454. }
  455. // initialize
  456. acf_register_field_type( 'acf_field_file' );
  457. endif; // class_exists check
  458. ?>