PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/plugins/attachments/classes/class.attachments.php

https://github.com/zackseuberling/k-r
PHP | 1787 lines | 976 code | 380 blank | 431 comment | 149 complexity | 8a4f00db1fae836f6102f9a8d858a8fe MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Attachments
  4. *
  5. * Attachments allows you to simply append any number of items from your WordPress
  6. * Media Library to Posts, Pages, and Custom Post Types
  7. *
  8. * @package Attachments
  9. * @subpackage Main
  10. */
  11. // Exit if accessed directly
  12. if( !defined( 'ABSPATH' ) ) exit;
  13. // Declare our class
  14. if ( !class_exists( 'Attachments' ) ) :
  15. /**
  16. * Main Attachments Class
  17. *
  18. * @since 3.0
  19. */
  20. class Attachments {
  21. private $version; // stores Attachments' version number
  22. private $url; // stores Attachments' URL
  23. private $dir; // stores Attachments' directory
  24. private $instances; // all registered Attachments instances
  25. private $instances_for_post_type; // instance names that apply to the current post type
  26. private $fields; // stores all registered field types
  27. private $attachments; // stores all of the Attachments for the given instance
  28. private $legacy = false; // whether or not there is legacy Attachments data
  29. private $legacy_pro = false; // whether or not there is legacy Attachment Pro data
  30. private $image_sizes = array( 'full' ); // store all registered image sizes
  31. private $default_instance = true; // use the default instance?
  32. private $attachments_ref = -1; // flags where a get() loop last did it's thing
  33. private $meta_key = 'attachments'; // our meta key
  34. private $valid_filetypes = array( // what WordPress considers to be valid file types
  35. 'image',
  36. 'video',
  37. 'text',
  38. 'audio',
  39. 'application'
  40. );
  41. /**
  42. * Constructor
  43. *
  44. * @since 3.0
  45. */
  46. function __construct( $instance = null, $post_id = null )
  47. {
  48. global $_wp_additional_image_sizes;
  49. // establish our environment variables
  50. $this->version = '3.4';
  51. $this->url = ATTACHMENTS_URL;
  52. $this->dir = ATTACHMENTS_DIR;
  53. // includes
  54. include_once( ATTACHMENTS_DIR . 'upgrade.php' );
  55. include_once( ATTACHMENTS_DIR . '/classes/class.field.php' );
  56. // include our fields
  57. $this->fields = $this->get_field_types();
  58. // deal with our legacy issues if the user hasn't dismissed or migrated already
  59. $this->check_for_legacy_data();
  60. // set our image sizes
  61. $this->image_sizes = array_merge( $this->image_sizes, get_intermediate_image_sizes() );
  62. // hook into WP
  63. add_action( 'admin_enqueue_scripts', array( $this, 'assets' ), 999, 1 );
  64. add_action( 'admin_enqueue_scripts', array( $this, 'admin_pointer' ), 999 );
  65. // register our user-defined instances
  66. add_action( 'init', array( $this, 'setup_instances' ) );
  67. // determine which instances apply to the current post type
  68. add_action( 'init', array( $this, 'set_instances_for_current_post_type' ) );
  69. add_action( 'add_meta_boxes', array( $this, 'meta_box_init' ) );
  70. add_action( 'admin_footer', array( $this, 'admin_footer' ) );
  71. add_action( 'save_post', array( $this, 'save' ) );
  72. // only show the Settings screen if it hasn't been explicitly disabled
  73. if( !( defined( 'ATTACHMENTS_SETTINGS_SCREEN' ) && ATTACHMENTS_SETTINGS_SCREEN === false ) )
  74. add_action( 'admin_menu', array( $this, 'admin_page' ) );
  75. // with version 3 we'll be giving at least one admin notice
  76. add_action( 'admin_notices', array( $this, 'admin_notice' ) );
  77. add_action( 'admin_head', array( $this, 'field_inits' ) );
  78. add_action( 'admin_print_footer_scripts', array( $this, 'field_assets' ) );
  79. // execution of actions varies depending on whether we're in the admin or not and an instance was passed
  80. if( is_admin() )
  81. {
  82. add_action( 'after_setup_theme', array( $this, 'apply_init_filters' ) );
  83. $this->attachments = $this->get_attachments( $instance, $post_id );
  84. }
  85. elseif( !is_null( $instance ) )
  86. {
  87. $this->apply_init_filters();
  88. $this->attachments = $this->get_attachments( $instance, $post_id );
  89. }
  90. }
  91. /**
  92. * Various initialization filter triggers
  93. *
  94. * @since 3.4
  95. */
  96. function apply_init_filters()
  97. {
  98. // allows a different meta_key to be used
  99. $this->meta_key = apply_filters( 'attachments_meta_key', $this->meta_key );
  100. }
  101. /**
  102. * Stores whether or not this environment has active legacy Attachments/Pro data
  103. *
  104. * @since 3.1.3
  105. */
  106. function check_for_legacy_data()
  107. {
  108. // we'll get a warning issued if fired when Network Activated
  109. // since it's supremely unlikely we'd have legacy data at this point, we're going to short circuit
  110. if( is_multisite() )
  111. {
  112. $plugins = get_site_option( 'active_sitewide_plugins' );
  113. if ( isset($plugins['attachments/index.php']) )
  114. return;
  115. }
  116. // deal with our legacy issues if the user hasn't dismissed or migrated already
  117. if( false == get_option( 'attachments_migrated' ) && false == get_option( 'attachments_ignore_migration' ) )
  118. {
  119. $legacy_attachments_settings = get_option( 'attachments_settings' );
  120. if( $legacy_attachments_settings && is_array( $legacy_attachments_settings['post_types'] ) && count( $legacy_attachments_settings['post_types'] ) )
  121. {
  122. // we have legacy settings, so we're going to use the post types
  123. // that Attachments is currently utilizing
  124. // the keys are the actual CPT names, so we need those
  125. foreach( $legacy_attachments_settings['post_types'] as $post_type => $value )
  126. if( $value )
  127. $post_types[] = $post_type;
  128. // set up our WP_Query args to grab anything with legacy data
  129. $args = array(
  130. 'post_type' => isset( $post_types ) ? $post_types : array( 'post', 'page' ),
  131. 'post_status' => 'any',
  132. 'posts_per_page' => 1,
  133. 'meta_key' => '_attachments',
  134. 'suppress_filters' => true,
  135. );
  136. $legacy = new WP_Query( $args );
  137. $this->legacy = empty( $legacy->found_posts ) ? false : true;
  138. }
  139. }
  140. // deal with our legacy Pro issues if the user hasn't dismissed or migrated already
  141. if( false == get_option( 'attachments_pro_migrated' ) && false == get_option( 'attachments_pro_ignore_migration' ) )
  142. {
  143. $post_types = get_post_types();
  144. // set up our WP_Query args to grab anything (really anything) with legacy data
  145. $args = array(
  146. 'post_type' => !empty( $post_types ) ? $post_types : array( 'post', 'page' ),
  147. 'post_status' => 'any',
  148. 'posts_per_page' => 1,
  149. 'meta_key' => '_attachments_pro',
  150. 'suppress_filters' => true,
  151. );
  152. $legacy_pro = new WP_Query( $args );
  153. $this->legacy_pro = empty( $legacy_pro->found_posts ) ? false : true;
  154. }
  155. }
  156. /**
  157. * Facilitates searching for Attachments
  158. *
  159. * @since 3.3
  160. */
  161. function search( $query = null, $params = array() )
  162. {
  163. $defaults = array(
  164. 'attachment_id' => null, // not searching for a single attachment ID
  165. 'instance' => 'attachments', // default instance
  166. 'post_type' => null, // search 'any' post type
  167. 'post_id' => null, // searching all posts
  168. 'post_status' => 'publish', // search only published posts
  169. 'fields' => null, // search all fields
  170. );
  171. $query = is_null( $query ) ? null : sanitize_text_field( $query );
  172. $params = array_merge( $defaults, $params );
  173. // sanitize parameters
  174. $params['attachment_id'] = is_null( $params['attachment_id'] ) ? null : intval( $params['attachment_id'] );
  175. $params['instance'] = !is_string( $params['instance'] ) ? 'attachments' : sanitize_text_field( $params['instance'] );
  176. $params['post_type'] = is_null( $params['post_type'] ) ? 'any' : sanitize_text_field( $params['post_type'] );
  177. $params['post_id'] = is_null( $params['post_id'] ) ? null : intval( $params['post_id'] );
  178. $params['post_status'] = sanitize_text_field( $params['post_status'] );
  179. if( is_string( $params['fields'] ) )
  180. $params['fields'] = array( $params['fields'] ); // we always want an array
  181. // since we have an array for our fields, we need to loop through and sanitize
  182. for( $i = 0; $i < count( $params['fields'] ); $i++ )
  183. $params['fields'][$i] = sanitize_text_field( $params['fields'][$i] );
  184. // prepare our search args
  185. $args = array(
  186. 'nopaging' => true,
  187. 'post_status' => $params['post_status'],
  188. 'meta_query' => array(
  189. array(
  190. 'key' => 'attachments',
  191. 'value' => $query,
  192. 'compare' => 'LIKE'
  193. )
  194. ),
  195. );
  196. // append any applicable parameters that got passed to the original method call
  197. if( $params['post_type'] )
  198. $args['post_type'] = $params['post_type'];
  199. if( $params['post_id'] )
  200. $args['post__in'] = array( $params['post_id'] ); // avoid using 'p' or 'page_id'
  201. // we haven't utilized all parameters yet because they're meta-value based so we need to
  202. // do some parsing on our end to validate the returned results
  203. $possible_posts = new WP_Query( $args );
  204. $potential_attachments = false; // stores valid attachments as restrictions are added
  205. if( $possible_posts->found_posts )
  206. {
  207. // we have results from the reliminary search, we need to quantify them
  208. while( $possible_posts->have_posts() )
  209. {
  210. $possible_posts->next_post();
  211. $possible_post_ids[] = $possible_posts->post->ID;
  212. }
  213. // now that we have our possible post IDs we can grab all Attachments for all of those posts
  214. foreach( $possible_post_ids as $possible_post_id )
  215. {
  216. $possible_attachments = $this->get_attachments( $params['instance'], $possible_post_id );
  217. foreach( $possible_attachments as $possible_attachment )
  218. $potential_attachments[] = $possible_attachment;
  219. }
  220. }
  221. // if there aren't even any potential attachments, we'll just short circuit
  222. if( !$potential_attachments )
  223. return;
  224. // first we need to make sure that our query matches each attachment
  225. // we need to do this because the LIKE query returned the entire meta record,
  226. // not necessarily tied to any specific Attachment
  227. $total_potentials = count( $potential_attachments );
  228. for( $i = 0; $i < $total_potentials; $i++ )
  229. {
  230. $valid = false;
  231. // if we need to limit our search to specific fields, we'll do that here
  232. if( $params['fields'] )
  233. {
  234. // we only want to check certain fields
  235. foreach( $params['fields'] as $field )
  236. if( isset( $potential_attachments[$i]->fields->$field ) ) // does the field exist?
  237. if( strpos( strtolower( $potential_attachments[$i]->fields->$field ),
  238. strtolower( $query ) ) !== false ) // does the value match?
  239. $valid = true;
  240. }
  241. else
  242. {
  243. // we want to check all fields
  244. foreach( $potential_attachments[$i]->fields as $field_name => $field_value )
  245. if( strpos( strtolower( $field_value) , strtolower( $query ) ) !== false )
  246. $valid = true;
  247. }
  248. if( !$valid )
  249. unset( $potential_attachments[$i] );
  250. // now our potentials have been limited to each match the query based on any field
  251. }
  252. // limit to attachment ID if applicable
  253. if( $params['attachment_id'] )
  254. {
  255. $total_potentials = count( $potential_attachments );
  256. for( $i = 0; $i < $total_potentials; $i++ )
  257. if( $potential_attachments[$i]->id != $params['attachment_id'] )
  258. unset( $potential_attachments[$i] );
  259. }
  260. $this->attachments = array_values( $potential_attachments );
  261. }
  262. /**
  263. * Returns whether or not the current object has any Attachments
  264. *
  265. * @since 3.0
  266. */
  267. function exist()
  268. {
  269. return !empty( $this->attachments );
  270. }
  271. /**
  272. * Returns the number of Attachments
  273. *
  274. * @since 3.0.6
  275. */
  276. function total()
  277. {
  278. return count( $this->attachments );
  279. }
  280. /**
  281. * Returns the next Attachment for the current object and increments the index
  282. *
  283. * @since 3.0
  284. */
  285. function get()
  286. {
  287. $this->attachments_ref++;
  288. if( !count( $this->attachments ) || $this->attachments_ref >= count( $this->attachments ) )
  289. return false;
  290. return $this->attachments[$this->attachments_ref];
  291. }
  292. /**
  293. * Returns a specific Attachment
  294. *
  295. * @since 3.2
  296. */
  297. function get_single( $index )
  298. {
  299. return isset( $this->attachments[$index] ) ? $this->attachments[$index] : false;
  300. }
  301. /**
  302. * Returns the asset (array) for the current Attachment
  303. *
  304. * @since 3.0.6
  305. */
  306. function asset( $size = 'thumbnail', $index = null )
  307. {
  308. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  309. // do we have our meta yet?
  310. if( !isset( $this->attachments[$index]->meta ) )
  311. $this->attachments[$index]->meta = wp_get_attachment_metadata( $this->attachments[$index]->id );
  312. // is it an image?
  313. if(
  314. isset( $this->attachments[$index]->meta['sizes'] ) && // is it an image?
  315. in_array( $size, $this->image_sizes ) ) // do we have the right size?
  316. {
  317. $asset = wp_get_attachment_image_src( $this->attachments[$index]->id, $size );
  318. }
  319. else
  320. {
  321. // either it's not an image or we don't have the proper size, so we'll use the icon
  322. $asset = $this->icon( $index );
  323. }
  324. return $asset;
  325. }
  326. /**
  327. * Returns the icon (array) for the current Attachment
  328. *
  329. * @since 3.0.6
  330. */
  331. function icon( $index = null )
  332. {
  333. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  334. $asset = wp_get_attachment_image_src( $this->attachments[$index]->id, null, true );
  335. return $asset;
  336. }
  337. /**
  338. * Returns an appropriate <img /> for the current Attachment if it's an image
  339. *
  340. * @since 3.0
  341. */
  342. function image( $size = 'thumbnail', $index = null )
  343. {
  344. $asset = $this->asset( $size, $index );
  345. $image_src = $asset[0];
  346. $image_width = $asset[1];
  347. $image_height = $asset[2];
  348. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  349. $image_alt = get_post_meta( $this->attachments[$index]->id, '_wp_attachment_image_alt', true );
  350. $image = '<img src="' . $image_src . '" width="' . $image_width . '" height="' . $image_height . '" alt="' . $image_alt . '" />';
  351. return $image;
  352. }
  353. /**
  354. * Returns the URL for the current Attachment if it's an image
  355. *
  356. * @since 3.0
  357. */
  358. function src( $size = 'thumbnail', $index = null )
  359. {
  360. $asset = $this->asset( $size, $index );
  361. return $asset[0];
  362. }
  363. /**
  364. * Returns the formatted filesize of the current Attachment
  365. *
  366. * @since 3.0
  367. */
  368. function filesize( $index = null )
  369. {
  370. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  371. if( !isset( $this->attachments[$index]->id ) )
  372. return false;
  373. $url = wp_get_attachment_url( $this->attachments[$index]->id );
  374. $uploads = wp_upload_dir();
  375. $file_path = str_replace( $uploads['baseurl'], $uploads['basedir'], $url );
  376. $formatted = '0 bytes';
  377. if( file_exists( $file_path ) )
  378. {
  379. $formatted = size_format( @filesize( $file_path ) );
  380. }
  381. return $formatted;
  382. }
  383. /**
  384. * Returns the type of the current Attachment
  385. *
  386. * @since 3.0
  387. */
  388. function type( $index = null )
  389. {
  390. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  391. if( !isset( $this->attachments[$index]->id ) )
  392. return false;
  393. $attachment_mime = explode( '/', get_post_mime_type( $this->attachments[$index]->id ) );
  394. return isset( $attachment_mime[0] ) ? $attachment_mime[0] : false;
  395. }
  396. /**
  397. * Returns the subtype of the current Attachment
  398. *
  399. * @since 3.0
  400. */
  401. function subtype( $index = null )
  402. {
  403. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  404. if( !isset( $this->attachments[$index]->id ) )
  405. return false;
  406. $attachment_mime = explode( '/', get_post_mime_type( $this->attachments[$index]->id ) );
  407. return isset( $attachment_mime[1] ) ? $attachment_mime[1] : false;
  408. }
  409. /**
  410. * Returns the id of the current Attachment
  411. *
  412. * @since 3.0
  413. */
  414. function id( $index = null )
  415. {
  416. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  417. return isset( $this->attachments[$index]->id ) ? $this->attachments[$index]->id : false;
  418. }
  419. /**
  420. * Returns the $post->ID of the current Attachment
  421. *
  422. * @since 3.3
  423. */
  424. function post_id( $index = null )
  425. {
  426. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  427. return isset( $this->attachments[$index]->post_id ) ? $this->attachments[$index]->post_id : false;
  428. }
  429. /**
  430. * Returns the URL for the current Attachment
  431. *
  432. * @since 3.0
  433. */
  434. function url( $index = null )
  435. {
  436. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  437. return isset( $this->attachments[$index]->id ) ? wp_get_attachment_url( $this->attachments[$index]->id ) : false;
  438. }
  439. /**
  440. * Returns the field value for the submitted field name
  441. *
  442. * @since 3.0
  443. */
  444. function field( $name = 'title', $index = null )
  445. {
  446. $index = is_null( $index ) ? $this->attachments_ref : intval( $index );
  447. return isset( $this->attachments[$index]->fields->$name ) ? $this->attachments[$index]->fields->$name : false;
  448. }
  449. /**
  450. * Fires all of our actions
  451. *
  452. * @since 3.0
  453. */
  454. function setup_instances()
  455. {
  456. // implement our default instance if appropriate
  457. if( !defined( 'ATTACHMENTS_DEFAULT_INSTANCE' ) )
  458. $this->register();
  459. // facilitate user-defined instance registration
  460. do_action( 'attachments_register', $this );
  461. }
  462. /**
  463. * Enqueues our necessary assets
  464. *
  465. * @since 3.0
  466. */
  467. function assets( $hook )
  468. {
  469. global $post;
  470. // we only want our assets on edit screens
  471. if( !empty( $this->instances_for_post_type ) && 'edit.php' != $hook && 'post.php' != $hook && 'post-new.php' != $hook )
  472. return;
  473. // we only want to enqueue if appropriate
  474. if( empty( $this->instances_for_post_type ) )
  475. return;
  476. $post_id = isset( $post->ID ) ? $post->ID : null;
  477. wp_enqueue_media( array( 'post' => $post_id ) );
  478. wp_enqueue_style( 'attachments', trailingslashit( $this->url ) . 'css/attachments.css', null, $this->version, 'screen' );
  479. wp_enqueue_script( 'attachments', trailingslashit( $this->url ) . 'js/attachments.js', array( 'jquery', 'jquery-ui-sortable' ), $this->version, true );
  480. }
  481. /**
  482. * Registers meta box(es) for the current edit screen
  483. *
  484. * @since 3.0
  485. */
  486. function meta_box_init()
  487. {
  488. $nonce_sent = false;
  489. if( !empty( $this->instances_for_post_type ) )
  490. {
  491. foreach( $this->instances_for_post_type as $instance )
  492. {
  493. $instance_name = $instance;
  494. $instance = (object) $this->instances[$instance];
  495. $instance->name = $instance_name;
  496. $position = isset($instance->position) ? $instance->position : 'normal';
  497. $priority = isset($instance->priority) ? $instance->priority : 'high';
  498. add_meta_box( 'attachments-' . $instance_name, __( esc_attr( $instance->label ) ), array( $this, 'meta_box_markup' ), $this->get_post_type(), $position, $priority, array( 'instance' => $instance, 'setup_nonce' => !$nonce_sent ) );
  499. $nonce_sent = true;
  500. }
  501. }
  502. }
  503. /**
  504. * Callback that outputs the meta box markup
  505. *
  506. * @since 3.0
  507. */
  508. function meta_box_markup( $post, $metabox )
  509. {
  510. // single out our $instance
  511. $instance = (object) $metabox['args']['instance'];
  512. if( $metabox['args']['setup_nonce'] )
  513. wp_nonce_field( 'attachments_save', 'attachments_nonce' );
  514. ?>
  515. <div id="attachments-<?php echo $instance->name; ?>" class="attachments-parent-container<?php if( $instance->append == false ) : ?> attachments-prepend<?php endif; ?>">
  516. <?php if( !empty( $instance->note ) ) : ?>
  517. <div class="attachments-note"><?php echo apply_filters( 'the_content', $instance->note ); ?></div>
  518. <?php endif; ?>
  519. <?php if( $instance->append == false ) : ?>
  520. <div class="attachments-invoke-wrapper">
  521. <a class="button attachments-invoke"><?php _e( esc_attr( $instance->button_text ), 'attachments' ); ?></a>
  522. </div>
  523. <?php endif; ?>
  524. <div class="attachments-container attachments-<?php echo $instance->name; ?>"><?php
  525. if( isset( $instance->attachments ) && !empty( $instance->attachments ) )
  526. {
  527. foreach( $instance->attachments as $attachment )
  528. {
  529. // we need to give our Attachment a uid to carry through to all the fields
  530. $attachment->uid = uniqid();
  531. // we'll create the attachment
  532. $this->create_attachment( $instance->name, $attachment );
  533. }
  534. }
  535. ?></div>
  536. <?php if( $instance->append == true ) : ?>
  537. <div class="attachments-invoke-wrapper">
  538. <a class="button attachments-invoke"><?php _e( esc_attr( $instance->button_text ), 'attachments' ); ?></a>
  539. </div>
  540. <?php endif; ?>
  541. </div>
  542. <script type="text/javascript">
  543. jQuery(document).ready(function($){
  544. var $element = $('#attachments-<?php echo esc_attr( $instance->name ); ?>'),
  545. title = '<?php echo __( esc_attr( $instance->label ) ); ?>',
  546. button = '<?php echo __( esc_attr( $instance->modal_text ) ); ?>',
  547. router = '<?php echo __( esc_attr( $instance->router ) ); ?>',
  548. attachmentsframe;
  549. $element.on( 'click', '.attachments-invoke', function( event ) {
  550. var options, attachment;
  551. event.preventDefault();
  552. // if the frame already exists, open it
  553. if ( attachmentsframe ) {
  554. attachmentsframe.open();
  555. attachmentsframe.content.mode(router);
  556. return;
  557. }
  558. // set our seetings
  559. attachmentsframe = wp.media({
  560. title: title,
  561. <?php if( $instance->limit < 0 || $instance->limit > 1 ) : ?>
  562. multiple: true,
  563. <?php endif; ?>
  564. library: {
  565. type: '<?php echo esc_attr( implode( ",", $instance->filetype ) ); ?>'
  566. },
  567. // Customize the submit button.
  568. button: {
  569. // Set the text of the button.
  570. text: button
  571. }
  572. });
  573. // set up our select handler
  574. attachmentsframe.on( 'select', function() {
  575. selection = attachmentsframe.state().get('selection');
  576. if ( ! selection )
  577. return;
  578. // compile our Underscore template using Mustache syntax
  579. _.templateSettings = {
  580. variable : 'attachments',
  581. interpolate : /\{\{(.+?)\}\}/g
  582. }
  583. var template = _.template($('script#tmpl-attachments-<?php echo $instance->name; ?>').html());
  584. // loop through the selected files
  585. selection.each( function( attachment ) {
  586. // set our attributes to the template
  587. attachment.attributes.attachment_uid = attachments_uniqid( 'attachmentsjs' );
  588. // by default use the generic icon
  589. attachment.attributes.attachment_thumb = attachment.attributes.icon;
  590. // only thumbnails have sizes which is what we're on the hunt for
  591. // TODO: this is a mess
  592. if(attachments_isset(attachment.attributes)){
  593. if(attachments_isset(attachment.attributes.sizes)){
  594. if(attachments_isset(attachment.attributes.sizes.thumbnail)){
  595. if(attachments_isset(attachment.attributes.sizes.thumbnail.url)){
  596. // use the thumbnail
  597. attachment.attributes.attachment_thumb = attachment.attributes.sizes.thumbnail.url;
  598. }
  599. }
  600. }
  601. }
  602. var templateData = attachment.attributes;
  603. // append the template
  604. $element.find('.attachments-container').<?php if( $instance->append ) : ?>append<?php else : ?>prepend<?php endif; ?>(template(templateData));
  605. // if we're in a sidebar we DO want to show the fields which are normally hidden on load via CSS
  606. if($element.parents('#side-sortables')){
  607. $element.find('.attachments-attachment:last .attachments-fields').show();
  608. }
  609. // see if we need to set a default
  610. // TODO: can we tie this into other field types (select, radio, checkbox)?
  611. $element.find('.attachments-attachment:last .attachments-fields input, .attachments-attachment:last .attachments-fields textarea').each(function(){
  612. if($(this).data('default')){
  613. var meta_for_default = $(this).data('default');
  614. if(attachments_isset(attachment.attributes)){
  615. if(attachments_isset(attachment.attributes[meta_for_default])){
  616. $(this).val(attachment.attributes[meta_for_default]);
  617. }
  618. }
  619. }
  620. });
  621. $('body').trigger('attachments/new');
  622. // if it wasn't an image we need to ditch the dimensions
  623. if(!attachments_isset(attachment.attributes.width)||!attachments_isset(attachment.attributes.height)){
  624. $element.find('.attachments-attachment:last .dimensions').hide();
  625. }
  626. });
  627. });
  628. // open the frame
  629. attachmentsframe.open();
  630. attachmentsframe.content.mode(router);
  631. });
  632. $element.on( 'click', '.delete-attachment a', function( event ) {
  633. var targetAttachment;
  634. event.preventDefault();
  635. targetAttachment = $(this).parents('.attachments-attachment');
  636. targetAttachment.find('.attachment-meta').fadeOut(125);
  637. targetAttachment.css('min-height',0).animate(
  638. {
  639. padding: 0,
  640. margin: 0,
  641. height: 0
  642. },
  643. 600,
  644. function(){
  645. targetAttachment.remove();
  646. }
  647. );
  648. } );
  649. });
  650. </script>
  651. <?php }
  652. /**
  653. * Support the inclusion of custom, user-defined field types
  654. * Borrowed implementation from Custom Field Suite by Matt Gibbs
  655. * https://uproot.us/docs/creating-custom-field-types/
  656. *
  657. * @since 3.0
  658. **/
  659. function get_field_types()
  660. {
  661. $field_types = array(
  662. 'text' => ATTACHMENTS_DIR . 'classes/fields/class.field.text.php',
  663. 'textarea' => ATTACHMENTS_DIR . 'classes/fields/class.field.textarea.php',
  664. 'select' => ATTACHMENTS_DIR . 'classes/fields/class.field.select.php',
  665. 'wysiwyg' => ATTACHMENTS_DIR . 'classes/fields/class.field.wysiwyg.php',
  666. );
  667. // support custom field types
  668. // $field_types = apply_filters( 'attachments_fields', $field_types );
  669. $field_index = 0;
  670. foreach( $field_types as $type => $path )
  671. {
  672. // proceed with inclusion
  673. if( file_exists( $path ) )
  674. {
  675. // include the file
  676. include_once( $path );
  677. // store the registered classes so we can single out what gets added
  678. $existing_classes = get_declared_classes();
  679. // we're going to use our Attachments class as a reference because
  680. // during subsequent instantiations of Attachments (e.g. within template files)
  681. // these field classes WILL NOT be added to the array again because
  682. // we're using include_once() so that strategy is no longer useful
  683. // determine it's class
  684. $flag = array_search( 'Attachments_Field', $existing_classes );
  685. // the field's class is next
  686. $field_class = $existing_classes[$flag + $field_index + 1];
  687. // create our link using our new field class
  688. $field_types[$type] = $field_class;
  689. $field_index++;
  690. }
  691. }
  692. // send it back
  693. return $field_types;
  694. }
  695. /**
  696. * Registers a field type for use within an instance
  697. *
  698. * @since 3.0
  699. */
  700. function register_field( $params = array() )
  701. {
  702. $defaults = array(
  703. 'name' => 'title',
  704. 'type' => 'text',
  705. 'label' => __( 'Title', 'attachments' ),
  706. 'meta' => array(),
  707. );
  708. $params = array_merge( $defaults, $params );
  709. // ensure it's a valid type
  710. if( !isset( $this->fields[$params['type']] ) )
  711. return false;
  712. // sanitize
  713. if( isset( $params['name'] ) )
  714. $params['name'] = str_replace( '-', '_', sanitize_title( $params['name'] ) );
  715. if( isset( $params['label'] ) )
  716. $params['label'] = __( esc_html( $params['label'] ) );
  717. if( !isset( $params['meta'] ) || !is_array( $params['meta'] ) )
  718. $params['meta'] = array();
  719. // instantiate the class for this field and send it back
  720. return new $this->fields[ $params['type'] ]( $params['name'], $params['label'], $params['meta'] );
  721. }
  722. /**
  723. * Registers an Attachments instance
  724. *
  725. * @since 3.0
  726. */
  727. function register( $name = 'attachments', $params = array() )
  728. {
  729. $defaults = array(
  730. // title of the meta box (string)
  731. 'label' => __( 'Attachments', 'attachments' ),
  732. // all post types to utilize (string|array)
  733. 'post_type' => array( 'post', 'page' ),
  734. // meta box position (string) (normal, side or advanced)
  735. 'position' => 'normal',
  736. // meta box priority (string) (high, default, low, core)
  737. 'priority' => 'high',
  738. // maximum number of Attachments (int) (-1 is unlimited)
  739. 'limit' => -1,
  740. // allowed file type(s) (array) (image|video|text|audio|application)
  741. 'filetype' => null, // no filetype limit
  742. // include a note within the meta box (string)
  743. 'note' => null, // no note
  744. // by default new Attachments will be appended to the list
  745. // but you can have then prepend if you set this to false
  746. 'append' => true,
  747. // text for 'Attach' button (string)
  748. 'button_text' => __( 'Attach', 'attachments' ),
  749. // text for modal 'Attach' button (string)
  750. 'modal_text' => __( 'Attach', 'attachments' ),
  751. // which tab should be the default in the modal (string) (browse|upload)
  752. 'router' => 'browse',
  753. // fields for this instance (array)
  754. 'fields' => array(
  755. array(
  756. 'name' => 'title', // unique field name
  757. 'type' => 'text', // registered field type
  758. 'label' => __( 'Title', 'attachments' ), // label to display
  759. 'default' => 'title', // default value upon selection
  760. ),
  761. array(
  762. 'name' => 'caption', // unique field name
  763. 'type' => 'wysiwyg', // registered field type
  764. 'label' => __( 'Caption', 'attachments' ), // label to display
  765. 'default' => 'caption', // default value upon selection
  766. ),
  767. ),
  768. );
  769. $params = array_merge( $defaults, $params );
  770. // sanitize
  771. if( !is_array( $params['post_type'] ) )
  772. $params['post_type'] = array( $params['post_type'] ); // we always want an array
  773. if( !is_array( $params['filetype'] ) )
  774. $params['filetype'] = array( $params['filetype'] ); // we always want an array
  775. $params['label'] = esc_html( $params['label'] );
  776. $params['limit'] = intval( $params['limit'] );
  777. $params['note'] = esc_sql( $params['note'] );
  778. $params['button_text'] = esc_attr( $params['button_text'] );
  779. $params['modal_text'] = esc_attr( $params['modal_text'] );
  780. // make sure we've got valid filetypes
  781. if( is_array( $params['filetype'] ) )
  782. {
  783. foreach( $params['filetype'] as $key => $filetype )
  784. {
  785. if( !in_array( $filetype, $this->valid_filetypes ) )
  786. {
  787. unset( $params['filetype'][$key] );
  788. }
  789. }
  790. }
  791. // WordPress sanitizes post type names when registering, so we will too
  792. foreach( $params['post_type'] as $key => $post_type )
  793. $params['post_type'][$key] = sanitize_key( $post_type );
  794. // make sure the instance name is proper
  795. $instance = str_replace( '-', '_', sanitize_title( $name ) );
  796. // register the fields
  797. if( isset( $params['fields'] ) && is_array( $params['fields'] ) && count( $params['fields'] ) )
  798. {
  799. foreach( $params['fields'] as $field )
  800. {
  801. // register the field
  802. $this->register_field( $field );
  803. }
  804. }
  805. // set the instance
  806. $this->instances[$instance] = $params;
  807. // set the Attachments for this instance
  808. $this->instances[$instance]['attachments'] = $this->get_attachments( $instance );
  809. }
  810. /**
  811. * Gets the applicable Attachments instances for the current post type
  812. *
  813. * @since 3.0
  814. */
  815. function get_instances_for_post_type( $post_type = null )
  816. {
  817. $post_type = ( !is_null( $post_type ) && post_type_exists( $post_type ) ) ? $post_type : $this->get_post_type();
  818. $instances = array();
  819. if( !empty( $this->instances ) )
  820. {
  821. foreach( $this->instances as $name => $params )
  822. {
  823. if( in_array( $post_type, $params['post_type'] ) )
  824. {
  825. $instances[] = $name;
  826. }
  827. }
  828. }
  829. return $instances;
  830. }
  831. /**
  832. * Our own implementation of WordPress' get_post_type() as it's not
  833. * functional when we need it
  834. *
  835. * @since 3.0
  836. */
  837. function get_post_type()
  838. {
  839. global $post;
  840. // TODO: Retrieving the post_type at this point is ugly to say the least. This needs major cleanup.
  841. if( empty( $post->ID ) && isset( $_GET['post_type'] ) )
  842. {
  843. $post_type = str_replace( '-', '_', sanitize_title( $_GET['post_type'] ) ); // TODO: Better sanitization
  844. }
  845. elseif( !empty( $post->ID ) )
  846. {
  847. $post_type = get_post_type( $post->ID );
  848. }
  849. elseif( isset( $_GET['post'] ) )
  850. {
  851. $post_type = get_post_type( intval( $_GET['post'] ) );
  852. }
  853. else
  854. {
  855. $post_type = 'post';
  856. }
  857. return $post_type;
  858. }
  859. /**
  860. * Sets the applicable Attachments instances for the current post type
  861. *
  862. * @since 3.0
  863. */
  864. function set_instances_for_current_post_type()
  865. {
  866. // store the applicable instances for this post type
  867. $this->instances_for_post_type = $this->get_instances_for_post_type( $this->get_post_type() );
  868. }
  869. /**
  870. * Outputs HTML for a single Attachment within an instance
  871. *
  872. * @since 3.0
  873. */
  874. function create_attachment_field( $instance, $field, $attachment = null )
  875. {
  876. // the $field at this point is just the user-declared array
  877. // we need to make it a field object
  878. $type = $field['type'];
  879. if( isset( $this->fields[$type] ) )
  880. {
  881. $name = sanitize_title( $field['name'] );
  882. $label = esc_html( $field['label'] );
  883. $default = isset( $field['default'] ) ? $field['default'] : false; // validated in the class
  884. $meta = isset( $field['meta'] ) ? $field['meta'] : array();
  885. $value = isset( $attachment->fields->$name ) ? $attachment->fields->$name : null;
  886. $field = new $this->fields[$type]( $name, $label, $value, $meta );
  887. $field->value = $field->format_value_for_input( $field->value );
  888. // does this field already have a unique ID?
  889. $uid = ( isset( $attachment->uid ) ) ? $attachment->uid : null;
  890. // TODO: make sure we've got a registered instance
  891. $field->set_field_instance( $instance, $field );
  892. $field->set_field_identifiers( $field, $uid );
  893. $field->set_field_type( $type );
  894. $field->set_field_default( $default );
  895. ?>
  896. <div class="attachments-attachment-field attachments-attachment-field-<?php echo $instance; ?> attachments-attachment-field-<?php echo $field->type; ?> attachment-field-<?php echo $field->name; ?>">
  897. <div class="attachment-label attachment-label-<?php echo $instance; ?>">
  898. <label for="<?php echo $field->field_id; ?>"><?php echo $field->label; ?></label>
  899. </div>
  900. <div class="attachment-field attachment-field-<?php echo $instance; ?>">
  901. <?php echo $this->create_field( $instance, $field ); ?>
  902. </div>
  903. </div>
  904. <?php
  905. }
  906. else
  907. {
  908. $field = false;
  909. }
  910. return $field;
  911. }
  912. /**
  913. * Outputs HTML for submitted field
  914. *
  915. * @since 3.0
  916. */
  917. function create_field( $instance, $field )
  918. {
  919. $field = (object) $field;
  920. // with all of our attributes properly set, we can output
  921. $field->html( $field );
  922. }
  923. /**
  924. * Outputs all the necessary markup for an Attachment
  925. *
  926. * @since 3.0
  927. */
  928. function create_attachment( $instance, $attachment = null )
  929. {
  930. ?>
  931. <div class="attachments-attachment attachments-attachment-<?php echo $instance; ?>">
  932. <?php $array_flag = ( isset( $attachment->uid ) ) ? $attachment->uid : '{{ attachments.attachment_uid }}'; ?>
  933. <input type="hidden" name="attachments[<?php echo $instance; ?>][<?php echo $array_flag; ?>][id]" value="<?php echo isset( $attachment->id ) ? $attachment->id : '{{ attachments.id }}' ; ?>" />
  934. <?php
  935. // since attributes can change over time (image gets replaced, cropped, etc.) we'll pull that info
  936. if( isset( $attachment->id ) )
  937. {
  938. // we'll just use the full size since that's what Media in 3.5 uses
  939. $attachment_meta = wp_get_attachment_metadata( $attachment->id );
  940. // only images return the 'file' key
  941. if( !isset( $attachment_meta['file'] ))
  942. $attachment_meta['file'] = get_attached_file( $attachment->id );
  943. $attachment->width = isset( $attachment_meta['width'] ) ? $attachment_meta['width'] : null;
  944. $attachment->height = isset( $attachment_meta['height'] ) ? $attachment_meta['height'] : null;
  945. $attachment->filename = end( explode( "/", $attachment_meta['file'] ) );
  946. $attachment_mime = explode( '/', get_post_mime_type( $attachment->id ) );
  947. $attachment->type = isset( $attachment_mime[0] ) ? $attachment_mime[0] : null;
  948. $attachment->subtype = isset( $attachment_mime[1] ) ? $attachment_mime[1] : null;
  949. }
  950. ?>
  951. <div class="attachment-meta media-sidebar">
  952. <?php
  953. $thumbnail = isset( $attachment->id ) ? wp_get_attachment_image_src( $attachment->id, 'thumbnail', true ) : false;
  954. $image = $thumbnail ? $thumbnail[0] : '{{ attachments.attachment_thumb }}';
  955. ?>
  956. <div class="attachment-thumbnail">
  957. <img src="<?php echo $image; ?>" alt="Thumbnail" />
  958. </div>
  959. <div class="attachment-details attachment-info details">
  960. <div class="filename"><?php echo isset( $attachment->filename ) ? $attachment->filename : '{{ attachments.filename }}' ; ?></div>
  961. <?php if( ( isset( $attachment->id ) && isset( $attachment->width ) ) || !isset( $attachment->id ) ) : ?>
  962. <div class="dimensions"><?php echo isset( $attachment->width ) ? $attachment->width : '{{ attachments.width }}' ; ?> &times; <?php echo isset( $attachment->height ) ? $attachment->height : '{{ attachments.height }}' ; ?></div>
  963. <?php endif; ?>
  964. <div class="delete-attachment"><a href="#"><?php _e( 'Remove', 'attachments' ); ?></a></div>
  965. <div class="attachments-attachment-fields-toggle"><a href="#"><?php _e( 'Toggle Fields', 'attachments' ); ?></a></div>
  966. </div>
  967. </div>
  968. <div class="attachments-handle"><img src="<?php echo trailingslashit( $this->url ) . 'images/handle.gif'; ?>" alt="Handle" width="20" height="20" /></div>
  969. <div class="attachments-fields">

Large files files are truncated, but you can click here to view the full file