Completed
Push — milestone/2.0 ( 9cc217...20a31b )
by
unknown
02:47
created

Relationship_Field::parse_serialized_value()   D

Complexity

Conditions 9
Paths 2

Size

Total Lines 34
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 23
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 34
rs 4.909
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Value_Set\Value_Set;
6
7
/**
8
 * Relationship field class.
9
 * Allows selecting and manually sorting entries from any custom post type.
10
 */
11
class Relationship_Field extends Field {
12
	protected $post_type = array( 'post' );
13
	
14
	protected $max = -1;
15
16
	protected $allow_duplicates = false;
17
18
	/**
19
	 * Default field value
20
	 *
21
	 * @var array
22
	 */
23
	protected $default_value = array();
24
25
	/**
26
	 * Create a field from a certain type with the specified label.
27
	 * @param string $name  Field name
28
	 * @param string $label Field label
29
	 */
30
	protected function __construct( $name, $label ) {
31
		$this->value = new Value_Set( Value_Set::TYPE_MULTIPLE_VALUES );
32
		parent::__construct( $name, $label );
33
	}
34
35
	/**
36
	 * Admin initialization actions
37
	 */
38
	public function admin_init() {
39
		$this->add_template( $this->get_type() . '_item', array( $this, 'item_template' ) );
40
41
		parent::admin_init();
42
	}
43
44
	/**
45
	 * Load the field value from an input array based on it's name
46
	 *
47
	 * @param array $input (optional) Array of field names and values. Defaults to $_POST
48
	 **/
49
	public function set_value_from_input( $input = null ) {
50
		if ( is_null( $input ) ) {
51
			$input = $_POST;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
52
		}
53
54
		$value = array();
55
		if ( isset( $input[ $this->get_name() ] ) ) {
56
			$value = stripslashes_deep( $input[ $this->get_name() ] );
57
			if ( is_array( $value ) ) {
58
				$value = array_values( $value );
59
			}
60
		}
61
		$this->set_value( $value );
62
	}
63
64
	/**
65
	 * Set the post type of the entries.
66
	 *
67
	 * @param string|array $post_type Post type
68
	 */
69
	public function set_post_type( $post_type ) {
70
		if ( ! is_array( $post_type ) ) {
71
			$post_type = array( $post_type );
72
		}
73
74
		$this->post_type = $post_type;
75
		return $this;
76
	}
77
78
	/**
79
	 * Set the maximum allowed number of selected entries.
80
	 *
81
	 * @param int $max
82
	 */
83
	public function set_max( $max ) {
84
		$this->max = intval( $max );
85
		return $this;
86
	}
87
88
	/**
89
	 * Specify whether to allow each entry to be selected multiple times.
90
	 *
91
	 * @param  boolean $allow
92
	 */
93
	public function allow_duplicates( $allow = true ) {
94
		$this->allow_duplicates = (bool) $allow;
95
		return $this;
96
	}
97
98
	/**
99
	 * Used to get the title of an item.
100
	 *
101
	 * Can be overriden or extended by the `carbon_relationship_title` filter.
102
	 *
103
	 * @param int     $id      The database ID of the item.
104
	 * @param string  $type    Item type (post, term, user, comment, or a custom one).
105
	 * @param string  $subtype The subtype - "page", "post", "category", etc.
106
	 * @return string $title The title of the item.
107
	 */
108
	protected function get_title_by_type( $id, $type, $subtype = '' ) {
109
		$title = get_the_title( $id );
110
		if ( ! $title ) {
111
			$title = '(no title) - ID: ' . $id;
112
		}
113
114
		/**
115
		 * Filter the title of the relationship item.
116
		 *
117
		 * @param string $title   The unfiltered item title.
118
		 * @param string $name    Name of the relationship field.
119
		 * @param int    $id      The database ID of the item.
120
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
121
		 * @param string $subtype Subtype - "page", "post", "category", etc.
122
		 */
123
		return apply_filters( 'carbon_relationship_title', $title, $this->get_name(), $id, $type, $subtype );
124
	}
125
126
	/**
127
	 * Used to get the label of an item.
128
	 *
129
	 * Can be overriden or extended by the `carbon_relationship_item_label` filter.
130
	 *
131
	 * @param int     $id      The database ID of the item.
132
	 * @param string  $type    Item type (post, term, user, comment, or a custom one).
133
	 * @param string  $subtype Subtype - "page", "post", "category", etc.
134
	 * @return string $label The label of the item.
135
	 */
136
	protected function get_item_label( $id, $type, $subtype = '' ) {
137
		$object = get_post_type_object( $subtype );
138
		$label = $object ? $object->labels->singular_name : null;
139
140
		/**
141
		 * Filter the label of the relationship item.
142
		 *
143
		 * @param string $label   The unfiltered item label.
144
		 * @param string $name    Name of the relationship field.
145
		 * @param int    $id      The database ID of the item.
146
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
147
		 * @param string $subtype Subtype - "page", "post", "category", etc.
148
		 */
149
		return apply_filters( 'carbon_relationship_item_label', $label, $this->get_name(), $id, $type, $subtype );
150
	}
151
152
	/**
153
	 * Generate the item options for the relationship field.
154
	 *
155
	 * @return array $options The selectable options of the relationship field.
156
	 */
157
	public function get_options() {
158
		$options = array();
159
		/**
160
		 * Filter the default query when fetching posts for a particular field.
161
		 *
162
		 * @param array $args The parameters, passed to get_posts().
163
		 */
164
		foreach ( $this->post_type as $post_type ) {
165
			$filter_name = 'carbon_relationship_options_' . $this->get_name() . '_post_' . $post_type;
166
			$args = apply_filters( $filter_name, array(
167
				'post_type' => $post_type,
168
				'posts_per_page' => -1,
0 ignored issues
show
introduced by
Disabling pagination is prohibited in VIP context, do not set posts_per_page to -1 ever.
Loading history...
169
				'fields' => 'ids',
170
				'suppress_filters' => false,
171
			) );
172
173
			// fetch and prepare posts as relationship items
174
			$new_options = get_posts( $args );
175
			foreach ( $new_options as &$p ) {
176
				$p = array(
177
					'id' => $p,
178
					'title' => $this->get_title_by_type( $p, 'post', $post_type ),
179
					'type' => 'post',
180
					'subtype' => $post_type,
181
					'label' => $this->get_item_label( $p, 'post', $post_type ),
182
					'is_trashed' => ( get_post_status( $p ) == 'trash' ),
183
					'edit_link'  => get_edit_post_link( $p ),
184
				);
185
			}
186
187
			$options = array_merge( $options, $new_options );
188
		}
189
190
		/**
191
		 * Filter the final list of options, available to a certain relationship field.
192
		 *
193
		 * @param array $options Unfiltered options items.
194
		 * @param string $name Name of the relationship field.
195
		 */
196
		$options = apply_filters( 'carbon_relationship_options', $options, $this->get_name() );
197
198
		return $options;
199
	}
200
201
	/**
202
	 * Returns an array that holds the field data, suitable for JSON representation.
203
	 * This data will be available in the Underscore template and the Backbone Model.
204
	 *
205
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
206
	 * @return array
207
	 */
208
	public function to_json( $load ) {
209
		$field_data = parent::to_json( $load );
210
211
		$field_data['nextfieldIndex'] = 0;
212
		if ( ! empty( $field_data['value'] ) ) {
213
			$value = array();
214
215
			$field_data['value'] = maybe_unserialize( $field_data['value'] );
216
			$i = 0;
217
			foreach ( $field_data['value'] as $single_value ) {
218
				$post_type = get_post_type( $single_value );
219
				$value[] = array(
220
					'id' => $single_value,
221
					'title' => $this->get_title_by_type( $single_value, 'post', $post_type ),
222
					'type' => 'post',
223
					'subtype' => $post_type,
224
					'label' => $this->get_item_label( $single_value, 'post', $post_type ),
225
					'is_trashed' => ( get_post_status( $single_value ) == 'trash' ),
226
					'fieldIndex' => $i,
227
				);
228
				$i++;
229
			}
230
			$field_data['nextfieldIndex'] = $i;
231
			$field_data['value'] = $value;
232
		}
233
234
		$field_data = array_merge( $field_data, array(
235
			'options' => $this->get_options(),
236
			'max' => $this->max,
237
			'allow_duplicates' => $this->allow_duplicates,
238
		) );
239
240
		return $field_data;
241
	}
242
243
	/**
244
	 * The main Underscore template of this field.
245
	 */
246
	public function template() {
247
		?>
248
		<div class="carbon-relationship-container">
249
			<div class="selected-items-container">
250
				<strong>
251
					<#
252
					var selected_items_length = 0;
253
					if ( value ) {
254
						selected_items_length = value.length;
255
					} #>
256
					<span class="selected-counter">{{{ selected_items_length }}}</span>
257
					<span class="selected-label" data-single-label="<?php _e( 'selected item', \Carbon_Fields\TEXT_DOMAIN ); ?>" data-plural-label="<?php _e( 'selected items', \Carbon_Fields\TEXT_DOMAIN ); ?>">
258
						<?php _e( 'selected items', \Carbon_Fields\TEXT_DOMAIN ); ?>
259
					</span>
260
261
					<?php
262
					/* If set_max() has been set, show the allowed maximum items number */
263
					?>
264
					<# if ( max !== -1 ) { #>
265
						<span class="remaining"><?php _e( 'out of', \Carbon_Fields\TEXT_DOMAIN ); ?> {{{ max }}}</span>
266
					<# } #>
267
				</strong>
268
			</div>
269
270
			<div class="search-field carbon-relationship-search dashicons-before dashicons-search">
271
				<input type="text" class="search-field" placeholder="<?php esc_attr_e( 'Search...', \Carbon_Fields\TEXT_DOMAIN ); ?>" />
272
			</div>
273
274
			<div class="carbon-relationship-body">
275
				<div class="carbon-relationship-left">
276
					<ul class="carbon-relationship-list">
277
						<# if (options) { #>
278
							<# _.each(options, function(item) { #>
279
								<?php echo $this->item_template( false ); ?>
280
							<# }); #>
281
						<# } #>
282
					</ul>
283
				</div>
284
285
				<div class="carbon-relationship-right">
286
					<label><?php _e( 'Associated:', \Carbon_Fields\TEXT_DOMAIN ); ?></label>
287
288
					<ul class="carbon-relationship-list">
289
						<# if (value) { #>
290
							<# _.each(value, function(item) { #>
291
								<?php echo $this->item_template(); ?>
292
							<# }); #>
293
						<# } #>
294
					</ul>
295
				</div>
296
			</div>
297
		</div>
298
		<?php
299
	}
300
301
	/**
302
	 * Serves as a Underscore template for the relationship items.
303
	 * Used for both the selected and the selectable options.
304
	 *
305
	 * @param bool $display_input Whether to display the selected item input field.
306
	 */
307
	public function item_template( $display_input = true ) {
308
		?>
309
		<li>
310
			<span class="mobile-handle dashicons-before dashicons-menu"></span>
311
			<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.id }}}">
312
				<# if ( item.edit_link ) { #>
313
					<em class="edit-link dashicons-before dashicons-edit" data-href="{{{ item.edit_link }}}"><?php _e( 'Edit', \Carbon_Fields\TEXT_DOMAIN ); ?></em>
314
				<# } #>
315
				<em>{{{ item.label }}}</em>
316
				<span class="dashicons-before dashicons-plus-alt"></span>
317
318
				{{{ item.title }}}
319
			</a>
320
			<?php if ( $display_input ) :  ?>
321
				<input type="hidden" name="{{{ name }}}[{{{ item.fieldIndex }}}]" value="{{{ item.id }}}" />
322
			<?php endif; ?>
323
		</li>
324
		<?php
325
	}
326
}
327