Completed
Push — milestone/2.0 ( 5944fd...9cc217 )
by
unknown
02:37
created

Association_Field::get_comment_options()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 15

Duplication

Lines 26
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 15
c 0
b 0
f 0
nc 2
nop 1
dl 26
loc 26
rs 8.8571
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\App;
6
use Carbon_Fields\Value_Set\Value_Set;
7
8
/**
9
 * Association field class.
10
 * Allows selecting and manually sorting entries from various types:
11
 *  - Posts
12
 *  - Terms
13
 *  - Users
14
 *  - Comments
15
 */
16
class Association_Field extends Relationship_Field {
17
	
18
	/**
19
	 * WP_Toolset instance
20
	 * @var Carbon_Fields\Toolset\WP_Toolset
21
	 */
22
	protected $wp_toolset;
23
24
	/**
25
	 * Types of entries to associate with.
26
	 * @var array
27
	 */
28
	protected $types = array(
29
		array(
30
			'type' => 'post',
31
			'post_type' => 'post',
32
		),
33
	);
34
35
	/**
36
	 * Create a field from a certain type with the specified label.
37
	 * @param string $name  Field name
38
	 * @param string $label Field label
39
	 */
40
	protected function __construct( $name, $label ) {
41
		$this->wp_toolset = App::resolve( 'wp_toolset' );
42
		$this->value = new Value_Set( Value_Set::TYPE_VALUE_SET, array( 'type' => '', 'subtype' => '', 'object_id' => 0 ) );
43
		Field::__construct( $name, $label );
44
	}
45
46
	/**
47
	 * Alias for $this->value()->set( $value );
48
	 **/
49
	public function set_value( $value ) {
50
		$value = $this->value_string_array_to_value_set( $value );
51
		parent::set_value( $value );
52
	}
53
54
	/**
55
	 * Return a differently formatted value for end-users
56
	 *
57
	 * @return mixed
58
	 **/
59
	public function get_formatted_value() {
60
		return Field::get_formatted_value();
61
	}
62
63
	/**
64
	 * Convert a colo:separated:string into it's expected components
65
	 * Used for backwards compatibility to CF 1.5
66
	 * 
67
	 * @param string $value_string
68
	 * @return array
69
	 */
70
	protected function value_string_to_property_array( $value_string ) {
71
		$value_pieces = explode( ':', $value_string );
72
		$property_array = array(
73
			Value_Set::VALUE_PROPERTY => $value_string,
74
			'type' => $value_pieces[0],
75
			'subtype' => $value_pieces[1],
76
			'object_id' => $value_pieces[2],
77
		);
78
		return $property_array;
79
	}
80
81
	/**
82
	 * Convert a colon:separated:string into it's expected components
83
	 * Used for backwards compatibility to CF 1.5
84
	 * 
85
	 * @param array $value_string_array
86
	 * @return array<array>
87
	 */
88
	protected function value_string_array_to_value_set( $value_string_array ) {
89
		$value_set = array();
90
		foreach ( $value_string_array as $raw_value_entry ) {
91
			$value_string = $raw_value_entry;
92
93
			if ( is_array( $raw_value_entry ) ) {
94
				if ( isset( $raw_value_entry['type'] ) ) {
95
					// array is already in suitable format
96
					$value_set[] = $raw_value_entry;
97
					continue;
98
				}
99
				$value_string = $raw_value_entry[ Value_Set::VALUE_PROPERTY ];
100
			}
101
102
			$property_array = $this->value_string_to_property_array( $value_string );
103
			$value_set[] = $property_array;
104
		}
105
		return $value_set;
106
	}
107
108
	/**
109
	 * Used to get the title of an item.
110
	 *
111
	 * Can be overriden or extended by the `carbon_association_title` filter.
112
	 *
113
	 * @param int $id The database ID of the item.
114
	 * @param string $type Item type (post, term, user, comment, or a custom one).
115
	 * @param string $subtype The subtype - "page", "post", "category", etc.
116
	 * @return string $title The title of the item.
117
	 */
118
	protected function get_title_by_type( $id, $type, $subtype = '' ) {
119
		$title = '';
120
121
		$method = 'get_' . $type . '_title';
122
		$callable = array( $this->wp_toolset, $method );
123
		if ( is_callable( $callable ) ) {
124
			$title = call_user_func( $callable, $id, $subtype );
125
		}
126
127
		if ( $type === 'comment' ) {
128
			$max = apply_filters( 'carbon_association_comment_length', 30, $this->get_name() );
129
			if ( strlen( $title ) > $max ) {
130
				$title = substr( $title, 0, $max ) . '...';
131
			}
132
		}
133
134
		/**
135
		 * Filter the title of the association item.
136
		 *
137
		 * @param string $title   The unfiltered item title.
138
		 * @param string $name    Name of the association field.
139
		 * @param int    $id      The database ID of the item.
140
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
141
		 * @param string $subtype Subtype - "page", "post", "category", etc.
142
		 */
143
		$title = apply_filters( 'carbon_association_title', $title, $this->get_name(), $id, $type, $subtype );
144
145
		if ( ! $title ) {
146
			$title = '(no title) - ID: ' . $id;
147
		}
148
149
		return $title;
150
	}
151
152
	/**
153
	 * Used to get the label of an item.
154
	 *
155
	 * Can be overriden or extended by the `carbon_association_item_label` filter.
156
	 *
157
	 * @param int     $id      The database ID of the item.
158
	 * @param string  $type    Item type (post, term, user, comment, or a custom one).
159
	 * @param string  $subtype Subtype - "page", "post", "category", etc.
160
	 * @return string $label The label of the item.
161
	 */
162
	protected function get_item_label( $id, $type, $subtype = '' ) {
163
		$label = $subtype ? $subtype : $type;
164
165
		if ( $type === 'post' ) {
166
			$post_type_object = get_post_type_object( $subtype );
167
			$label = $post_type_object->labels->singular_name;
168
		} elseif ( $type === 'term' ) {
169
			$taxonomy_object = get_taxonomy( $subtype );
170
			$label = $taxonomy_object->labels->singular_name;
171
		}
172
173
		/**
174
		 * Filter the label of the association item.
175
		 *
176
		 * @param string $label   The unfiltered item label.
177
		 * @param string $name    Name of the association field.
178
		 * @param int    $id      The database ID of the item.
179
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
180
		 * @param string $subtype Subtype - "page", "post", "category", etc.
181
		 */
182
		return apply_filters( 'carbon_association_item_label', $label, $this->get_name(), $id, $type, $subtype );
183
	}
184
185
	/**
186
	 * Get post options
187
	 *
188
	 * @return array $options
189
	 */
190
	protected function get_post_options( $type ) {
191
		/**
192
		 * Filter the default query when fetching posts for a particular field.
193
		 *
194
		 * @param array $args The parameters, passed to get_posts().
195
		 */
196
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'] . '_' . $type['post_type'];
197
		$args = apply_filters( $filter_name, array(
198
			'post_type' => $type['post_type'],
199
			'posts_per_page' => -1,
200
			'fields' => 'ids',
201
			'suppress_filters' => false,
202
		) );
203
204
		// fetch and prepare posts as association items
205
		$posts = get_posts( $args );
206
		foreach ( $posts as &$p ) {
207
			$p = array(
208
				'id' => $p,
209
				'title' => $this->get_title_by_type( $p, $type['type'], $type['post_type'] ),
210
				'type' => $type['type'],
211
				'subtype' => $type['post_type'],
212
				'label' => $this->get_item_label( $p, $type['type'], $type['post_type'] ),
213
				'is_trashed' => ( get_post_status( $p ) == 'trash' ),
214
				'edit_link' => $this->get_object_edit_link( $type, $p ),
215
			);
216
		}
217
		return $posts;
218
	}
219
220
	/**
221
	 * Get term options
222
	 *
223
	 * @return array $options
224
	 */
225
	protected function get_term_options( $type ) {
226
		/**
227
		 * Filter the default parameters when fetching terms for a particular field.
228
		 *
229
		 * @param array $args The parameters, passed to get_terms().
230
		 */
231
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'] . '_' . $type['taxonomy'];
232
		$args = apply_filters( $filter_name, array(
233
			'hide_empty' => 0,
234
			'fields' => 'id=>name',
235
		) );
236
237
		// fetch and prepare terms as association items
238
		$terms = get_terms( $type['taxonomy'], $args );
239
		foreach ( $terms as $term_id => &$term ) {
240
			$term = array(
241
				'id' => $term_id,
242
				'title' => $term,
243
				'type' => $type['type'],
244
				'subtype' => $type['taxonomy'],
245
				'label' => $this->get_item_label( $term_id, $type['type'], $type['taxonomy'] ),
246
				'is_trashed' => false,
247
				'edit_link' => $this->get_object_edit_link( $type, $term_id ),
248
			);
249
		}
250
		return $terms;
251
	}
252
253
	/**
254
	 * Get user options
255
	 *
256
	 * @return array $options
257
	 */
258 View Code Duplication
	protected function get_user_options( $type ) {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
259
		/**
260
		 * Filter the default parameters when fetching users for a particular field.
261
		 *
262
		 * @param array $args The parameters, passed to get_users().
263
		 */
264
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'];
265
		$args = apply_filters( $filter_name, array(
266
			'fields' => 'ID',
267
		) );
268
269
		// fetch and prepare users as association items
270
		$users = get_users( $args );
271
		foreach ( $users as &$u ) {
272
			$u = array(
273
				'id' => $u,
274
				'title' => $this->get_title_by_type( $u, $type['type'] ),
275
				'type' => $type['type'],
276
				'subtype' => 'user',
277
				'label' => $this->get_item_label( $u, $type['type'] ),
278
				'is_trashed' => false,
279
				'edit_link' => $this->get_object_edit_link( $type, $u ),
280
			);
281
		}
282
		return $users;
283
	}
284
285
	/**
286
	 * Get comment options
287
	 *
288
	 * @return array $options
289
	 */
290 View Code Duplication
	protected function get_comment_options( $type ) {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
		/**
292
		 * Filter the default parameters when fetching comments for a particular field.
293
		 *
294
		 * @param array $args The parameters, passed to get_comments().
295
		 */
296
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'];
297
		$args = apply_filters( $filter_name, array(
298
			'fields' => 'ids',
299
		) );
300
301
		// fetch and prepare comments as association items
302
		$comments = get_comments( $args );
303
		foreach ( $comments as &$c ) {
304
			$c = array(
305
				'id' => $c,
306
				'title' => $this->get_title_by_type( $c, $type['type'] ),
307
				'type' => $type['type'],
308
				'subtype' => 'comment',
309
				'label' => $this->get_item_label( $c, $type['type'] ),
310
				'is_trashed' => false,
311
				'edit_link' => $this->get_object_edit_link( $type, $c ),
312
			);
313
		}
314
		return $comments;
315
	}
316
317
	/**
318
	 * Generate the item options.
319
	 *
320
	 * @return array $options The selectable options of the association field.
321
	 */
322
	public function get_options() {
323
		$options = array();
324
325
		foreach ( $this->types as $type ) {
326
			$method = 'get_' . $type['type'] . '_options';
327
			$callable = array( $this, $method );
328
			if ( is_callable( $callable ) ) {
329
				$options = array_merge( $options, call_user_func( $callable, $type ) );
330
			}
331
		}
332
333
		/**
334
		 * Filter the final list of options, available to a certain association field.
335
		 *
336
		 * @param array $options Unfiltered options items.
337
		 * @param string $name Name of the association field.
338
		 */
339
		$options = apply_filters( 'carbon_association_options', $options, $this->get_name() );
340
341
		return $options;
342
	}
343
344
	/**
345
	 * Retrieve the edit link of a particular object.
346
	 *
347
	 * @param  string $type Object type.
348
	 * @param  int $id      ID of the object.
349
	 * @return string       URL of the edit link.
350
	 */
351
	protected function get_object_edit_link( $type, $id ) {
352
		switch ( $type['type'] ) {
353
354
			case 'post':
355
				$edit_link = get_edit_post_link( $id );
356
				break;
357
358
			case 'term':
359
				$edit_link = get_edit_term_link( $id, $type['taxonomy'], $type['type'] );
360
				break;
361
362
			case 'comment':
363
				$edit_link = get_edit_comment_link( $id );
364
				break;
365
366
			case 'user':
367
				$edit_link = get_edit_user_link( $id );
368
				break;
369
370
			default:
371
				$edit_link = false;
372
373
		}
374
375
		return $edit_link;
376
	}
377
378
	/**
379
	 * Modify the types.
380
	 * @param array $types New types
381
	 */
382
	public function set_types( $types ) {
383
		$this->types = $types;
384
		return $this;
385
	}
386
387
	/**
388
	 * Converts the field values into a usable associative array.
389
	 *
390
	 * The association data is saved in the database in the following format:
391
	 * 	array (
392
	 *		0 => 'post:page:4',
393
	 *		1 => 'term:category:2',
394
	 *		2 => 'user:user:1',
395
	 * 	)
396
	 * where the value of each array item contains:
397
	 * 	- Type of data (post, term, user or comment)
398
	 * 	- Subtype of data (the particular post type or taxonomy)
399
	 * 	- ID of the item (the database ID of the item)
400
	 */
401
	protected function value_to_json() {
402
		$value_set = $this->get_value();
403
		$value = array();
404
		foreach ( $value_set as $value_set_entry ) {
405
			$item = array(
406
				'type' => $value_set_entry['type'],
407
				'subtype' => $value_set_entry['subtype'],
408
				'id' => $value_set_entry['object_id'],
409
				'title' => $this->get_title_by_type( $value_set_entry['object_id'], $value_set_entry['type'], $value_set_entry['subtype'] ),
410
				'label' => $this->get_item_label( $value_set_entry['object_id'], $value_set_entry['type'], $value_set_entry['subtype'] ),
411
				'is_trashed' => ( $value_set_entry['type'] == 'post' && get_post_status( $value_set_entry['object_id'] ) === 'trash' ),
412
			);
413
			$value[] = $item;
414
		}
415
		return $value;
416
	}
417
418
	/**
419
	 * Convert the field data into JSON representation.
420
	 * @param  bool $load Whether to load data from the datastore.
421
	 * @return mixed      The JSON field data.
422
	 */
423
	public function to_json( $load ) {
424
		$field_data = Field::to_json( $load );
425
426
		$field_data = array_merge( $field_data, array(
427
			'value' => $this->value_to_json(),
428
			'options' => $this->get_options(),
429
			'max' => $this->max,
430
			'allow_duplicates' => $this->allow_duplicates,
431
		) );
432
433
		$i = 0;
434
		foreach ( $field_data['value'] as $key => $value ) {
435
			$field_data['value'][ $key ]['fieldIndex'] = $i;
436
			$i++;
437
		}
438
		$field_data['nextfieldIndex'] = $i;
439
440
		return $field_data;
441
	}
442
443
	/**
444
	 * Serves as a backbone template for the association items.
445
	 * Used for both the selected and the selectable options.
446
	 *
447
	 * @param bool $display_input Whether to display the selected item input field.
448
	 */
449
	public function item_template( $display_input = true ) {
450
		?>
451
		<li>
452
			<span class="mobile-handle dashicons-before dashicons-menu"></span>
453
			<a href="#" data-item-id="{{{ item.id }}}" data-item-title="{{{ item.title }}}" data-item-type="{{{ item.type }}}" data-item-subtype="{{{ item.subtype }}}" data-item-label="{{{ item.label }}}" data-value="{{{ item.type }}}:{{{ item.subtype }}}:{{{ item.id }}}">
454
				<# if ( item.edit_link ) { #>
455
					<em class="edit-link dashicons-before dashicons-edit" data-href="{{{ item.edit_link }}}"><?php _e( 'Edit', \Carbon_Fields\TEXT_DOMAIN ); ?></em>
456
				<# } #>
457
				<em>{{{ item.label }}}</em>
458
				<span class="dashicons-before dashicons-plus-alt"></span>
459
				{{{ item.title }}}
460
				<# if (item.is_trashed) { #>
461
					<i class="trashed dashicons-before dashicons-trash"></i>
462
				<# } #>
463
			</a>
464
			<?php if ( $display_input ) :  ?>
465
				<input type="hidden" name="{{{ name }}}[{{{ item.fieldIndex }}}]" value="{{{ item.type }}}:{{{ item.subtype }}}:{{{ item.id }}}" />
466
			<?php endif; ?>
467
		</li>
468
		<?php
469
	}
470
}
471