1 <?php
2
3 /**
4 * Use fieldmanager to create meta boxes on the new/edit term screens and save
5 * data primarily to term meta.
6 *
7 * @package Fieldmanager_Context
8 */
9 class Fieldmanager_Context_Term extends Fieldmanager_Context_Storable {
10 /**
11 * @var string
12 * Title of field to display in standard term form field location
13 */
14 public $title = '';
15
16 /**
17 * @var string[]
18 * What taxonomies to render these fields
19 */
20 public $taxonomies = array();
21
22 /**
23 * @var boolean
24 * Whether or not to show the fields on the term add form
25 */
26 public $show_on_add = true;
27
28 /**
29 * @var boolean
30 * Whether or not to show the fields on the term edit form
31 */
32 public $show_on_edit = true;
33
34 /**
35 * @var int
36 * Only show this field on child terms of this parent
37 */
38 public $parent = '';
39
40 /**
41 * @var array
42 * Field names reserved for WordPress on the term add/edit forms
43 */
44 public $reserved_fields = array( 'name', 'slug', 'description' );
45
46 /**
47 * Use FM term meta or WordPres core term meta. The default is a bit
48 * confusing: technically, it's to use core's term meta, but if the class is
49 * instantiated using the now-deprecated separated arguments, this gets set
50 * to true for backwards-compatibility purposes.
51 *
52 * This should be false whenever possible to instead use core's built-in
53 * term meta (introduced in WordPress 4.4).
54 *
55 * @var boolean
56 */
57 public $use_fm_meta = false;
58
59 /**
60 * Base field
61 *
62 * @var Fieldmanager_Field
63 */
64 public $fm = '';
65
66 /**
67 * The current taxonomy. Used when saving data.
68 * @var string
69 */
70 private $current_taxonomy;
71
72
73 /**
74 * Instantiate this context. You can either pass an array of all args
75 * (preferred), or pass them individually (deprecated).
76 *
77 * @param array|string $args {
78 * Array of arguments.
79 *
80 * If a string (deprecated), this will be used as the $title.
81 *
82 * @type string $title The context/meta box title.
83 * @type string|array $taxonomies The taxonomy/taxonomies to which to
84 * add this field.
85 * @type bool $show_on_add Optional. Should this field show on the "Add
86 * Term" screen? Defaults to yes (true).
87 * @type bool $show_on_edit Optional. Should this field show on the
88 * "Edit Term" screen? Defaults to yes (true).
89 * @type int $parent Optional. Should this field only show if its parent
90 * matches this term ID?
91 * @type bool $use_fm_meta Optional. Should this context store its data
92 * using FM term meta (true, deprecated) or
93 * WordPress core term meta (false). Defaults to
94 * false.
95 * @type Fieldmanager_Field $field Optional. The field to which to
96 * attach this context.
97 * }
98 * @param string|array $taxonomies Optional. Deprecated. Required if $args
99 * is a string.
100 * @param boolean $show_on_add Optional. Deprecated.
101 * @param boolean $show_on_edit Optional. Deprecated.
102 * @param string $parent Optional. Deprecated.
103 * @param Fieldmanager_Field $fm Optional. Deprecated.
104 */
105 public function __construct( $args, $taxonomies = array(), $show_on_add = true, $show_on_edit = true, $parent = '', $fm = null ) {
106 if ( is_array( $args ) ) {
107 $args = wp_parse_args( $args, array(
108 'show_on_add' => true,
109 'show_on_edit' => true,
110 'parent' => '',
111 'use_fm_meta' => false,
112 'field' => null,
113 ) );
114 if ( ! isset( $args['title'], $args['taxonomies'] ) ) {
115 throw new FM_Developer_Exception( esc_html__( '"title" and "taxonomies" are required values for Fieldmanager_Context_Term', 'fieldmanager' ) );
116 }
117
118 $this->title = $args['title'];
119 $this->taxonomies = (array) $args['taxonomies'];
120 $this->show_on_add = $args['show_on_add'];
121 $this->show_on_edit = $args['show_on_edit'];
122 $this->parent = $args['parent'];
123 $this->use_fm_meta = $args['use_fm_meta'];
124 $this->fm = $args['field'];
125 } elseif ( empty( $taxonomies ) ) {
126 throw new FM_Developer_Exception( esc_html__( '"title" and "taxonomies" are required values for Fieldmanager_Context_Term', 'fieldmanager' ) );
127 } else {
128 // Instantiating Fieldmanager_Context_Term using individual
129 // arguments is deprecated as of Fieldmanager-1.0.0-beta.3; you
130 // should pass an array of arguments instead.
131
132 // Set the class variables
133 $this->title = $args;
134 $this->taxonomies = (array) $taxonomies;
135 $this->show_on_add = $show_on_add;
136 $this->show_on_edit = $show_on_edit;
137 $this->parent = $parent;
138 $this->use_fm_meta = true;
139 $this->fm = $fm;
140 }
141
142 // Iterate through the taxonomies and add the fields to the requested forms
143 // Also add handlers for saving the fields and which forms to validate (if enabled)
144 foreach ( $this->taxonomies as $taxonomy ) {
145 if ( $this->show_on_add ) {
146 add_action( $taxonomy . '_add_form_fields', array( $this, 'add_term_fields' ), 10, 1 );
147 add_action( 'created_term', array( $this, 'save_term_fields'), 10, 3 );
148 }
149
150 if ( $this->show_on_edit ) {
151 add_action( $taxonomy . '_edit_form_fields', array( $this, 'edit_term_fields' ), 10, 2 );
152 add_action( 'edited_term', array( $this, 'save_term_fields'), 10, 3 );
153 }
154
155 if ( $this->use_fm_meta ) {
156 // Handle removing FM term meta when a term is deleted
157 add_action( 'delete_term', array( $this, 'delete_term_fields'), 10, 4 );
158 }
159 }
160 }
161
162 /**
163 * Creates the HTML template for wrapping fields on the add term form and prints the field
164 * @access public
165 * @param string $taxonomy
166 * @return void
167 */
168 public function add_term_fields( $taxonomy ) {
169 // If the parent is set, do nothing because we don't know what the parent term is yet
170 if ( ! empty( $this->parent ) ) {
171 return;
172 }
173
174 // Create the HTML template for output
175 $html_template = '<div class="form-field">%s%s</div>';
176
177 // Check if any validation is required
178 $fm_validation = Fieldmanager_Util_Validation( 'addtag', 'term' );
179 $fm_validation->add_field( $this->fm );
180
181 // Display the field
182 echo $this->term_fields( $html_template, $taxonomy );
183 }
184
185 /**
186 * Creates the HTML template for wrapping fields on the edit term form and prints the field
187 * @access public
188 * @param WP_Term $tag
189 * @param string $taxonomy
190 * @return void
191 */
192 public function edit_term_fields( $term, $taxonomy ) {
193 // Check if this term's parent matches the specified term if it is set
194 if ( ! empty( $this->parent ) && $this->parent != $term->parent ) {
195 return;
196 }
197
198 // Create the HTML template for output
199 $html_template = '<tr class="form-field"><th scope="row" valign="top">%s</th><td>%s</td></tr>';
200
201 // Check if any validation is required
202 $fm_validation = Fieldmanager_Util_Validation( 'edittag', 'term' );
203 $fm_validation->add_field( $this->fm );
204
205 // Display the field
206 echo $this->term_fields( $html_template, $taxonomy, $term );
207 }
208
209 /**
210 * Generates HTML for the term field
211 * @access public
212 * @param string $html_template THe HTML used to wrap the field
213 * @param string $taxonomy
214 * @param WP_Term $term
215 * @return string The element markup
216 */
217 public function term_fields( $html_template, $taxonomy, $term = null ) {
218 // Make sure the user hasn't specified a field name we can't use
219 if ( in_array( $this->fm->name, $this->reserved_fields ) ) {
220 $this->fm->_invalid_definition( sprintf( __( 'The field name "%s" is reserved for WordPress on the term form.', 'fieldmanager' ), $this->fm->name ) );
221 }
222
223 // Set the data type and ID
224 $this->fm->data_type = 'term';
225 $this->fm->data_id = is_object( $term ) ? $term->term_id : null;
226 $this->current_taxonomy = $taxonomy;
227
228 // Create the display label if one is set
229 if ( ! empty( $this->title ) ) {
230 $label = sprintf(
231 '<label for="%s">%s</label>',
232 esc_attr( $this->fm->name ),
233 esc_html( $this->title )
234 );
235 } else {
236 $label = '';
237 }
238
239 $field = $this->render_field( array( 'echo' => false ) );
240
241 // Create the markup and return it
242 return sprintf(
243 $html_template,
244 $label,
245 $field
246 );
247 }
248
249 /**
250 * Saves custom term fields
251 * @access public
252 * @param int $term_id
253 * @param int $tt_id
254 * @param string $taxonomy
255 * @return void
256 */
257 public function save_term_fields( $term_id, $tt_id, $taxonomy ) {
258 // Make sure this field is attached to the taxonomy being saved and this is the appropriate action
259 if ( ! in_array( $taxonomy, $this->taxonomies ) ) {
260 return;
261 }
262
263 // Make sure that our nonce field arrived intact
264 if ( ! $this->is_valid_nonce() ) {
265 return;
266 }
267
268 // Make sure the current user can save this post
269 $tax_obj = get_taxonomy( $taxonomy );
270 if ( ! current_user_can( $tax_obj->cap->manage_terms ) ) {
271 $this->fm->_unauthorized_access( __( 'User cannot edit this term', 'fieldmanager' ) );
272 return;
273 }
274
275 // Save the data
276 $this->save_to_term_meta( $term_id, $taxonomy );
277 }
278
279 /**
280 * Helper to save an array of data to term meta
281 * @param int $term_id
282 * @param array $data
283 * @return void
284 */
285 public function save_to_term_meta( $term_id, $taxonomy, $data = null ) {
286 // Set the data ID and type
287 $this->fm->data_id = $term_id;
288 $this->fm->data_type = 'term';
289 $this->current_taxonomy = $taxonomy;
290
291 $this->save( $data );
292 }
293
294 /**
295 * Saves custom fields for the sport taxonomy.
296 *
297 * @deprecated Fieldmanager-1.0.0-beta.3 This is not necessary if you're
298 * using core's term meta.
299 *
300 * @access public
301 *
302 * @param int $term_id
303 * @param int $tt_id
304 * @param string $taxonomy
305 * @param WP_term $deleted_term
306 */
307 public function delete_term_fields( $term_id, $tt_id, $taxonomy, $deleted_term ) {
308 // Get an instance of the term meta class
309 $term_meta = Fieldmanager_Util_Term_Meta();
310
311 // Delete any instance of this field for the term that was deleted
312 $term_meta->delete_term_meta( $term_id, $taxonomy, $this->fm->name );
313 }
314
315 /**
316 * Callback to get term meta for the given term ID and current taxonomy.
317 *
318 * @see get_term_meta().
319 * @see Fieldmanager_Util_Term_Meta::get_term_meta() (Deprecated).
320 */
321 protected function get_data( $term_id, $meta_key, $single = false ) {
322 if ( $this->use_fm_meta ) {
323 return fm_get_term_meta( $term_id, $this->current_taxonomy, $meta_key, $single );
324 } else {
325 return get_term_meta( $term_id, $meta_key, $single );
326 }
327 }
328
329 /**
330 * Callback to add term meta for the given term ID and current taxonomy.
331 *
332 * @see add_term_meta().
333 * @see Fieldmanager_Util_Term_Meta::add_term_meta() (Deprecated).
334 */
335 protected function add_data( $term_id, $meta_key, $meta_value, $unique = false ) {
336 if ( $this->use_fm_meta ) {
337 return fm_add_term_meta( $term_id, $this->current_taxonomy, $meta_key, $meta_value, $unique );
338 } else {
339 return add_term_meta( $term_id, $meta_key, $meta_value, $unique );
340 }
341 }
342
343 /**
344 * Callback to update term meta for the given term ID and current taxonomy.
345 *
346 * @see update_term_meta().
347 * @see Fieldmanager_Util_Term_Meta::update_term_meta() (Deprecated).
348 */
349 protected function update_data( $term_id, $meta_key, $meta_value, $meta_prev_value = '' ) {
350 if ( $this->use_fm_meta ) {
351 return fm_update_term_meta( $term_id, $this->current_taxonomy, $meta_key, $meta_value, $meta_prev_value );
352 } else {
353 return update_term_meta( $term_id, $meta_key, $meta_value, $meta_prev_value );
354 }
355 }
356
357 /**
358 * Callback to delete term meta for the given term ID and current taxonomy.
359 *
360 * @see delete_term_meta().
361 * @see Fieldmanager_Util_Term_Meta::delete_term_meta() (Deprecated).
362 */
363 protected function delete_data( $term_id, $meta_key, $meta_value = '' ) {
364 if ( $this->use_fm_meta ) {
365 return fm_delete_term_meta( $term_id, $this->current_taxonomy, $meta_key, $meta_value );
366 } else {
367 return delete_term_meta( $term_id, $meta_key, $meta_value );
368 }
369 }
370
371 }
372