1 <?php
2 /**
3 * Admin Page Framework
4 *
5 * http://en.michaeluno.jp/admin-page-framework/
6 * Copyright (c) 2013-2014 Michael Uno; Licensed MIT
7 *
8 */
9 if ( ! class_exists( 'AdminPageFramework_TaxonomyField' ) ) :
10 /**
11 * Provides methods for creating fields in the taxonomy page (edit-tags.php).
12 *
13 * <h2>Hooks</h2>
14 * <p>The class automatically creates WordPress action and filter hooks associated with the class methods.
15 * The class methods corresponding to the name of the below actions and filters can be extended to modify the page output. Those methods are the callbacks of the filters and actions.</p>
16 * <h3>Methods and Action Hooks</h3>
17 * <ul>
18 * <li><strong>start_{instantiated class name}</strong> – triggered at the end of the class constructor.</li>
19 * <li><strong>do_{instantiated class name}</strong> – triggered when the meta box gets rendered. The first parameter: the class object [3.1.3+]. </li>
20 * </ul>
21 * <h3>Methods and Filter Hooks</h3>
22 * <ul>
23 * <li><strong>field_types_{instantiated class name}</strong> – receives the field type definition array. The first parameter: the field type definition array.</li>
24 * <li><strong>field_{instantiated class name}_{field ID}</strong> – receives the form input field output of the given input field ID. The first parameter: output string. The second parameter: the array of option.</li>
25 * <li><strong>content_{instantiated class name}</strong> – receives the entire output of the meta box. The first parameter: the output HTML string.</li>
26 * <li><strong>style_common_{instantiated class name}</strong> – receives the output of the base CSS rules applied to the pages of the associated post types with the meta box.</li>
27 * <li><strong>style_ie_common_{instantiated class name}</strong> – receives the output of the base CSS rules for Internet Explorer applied to the pages of the associated post types with the meta box.</li>
28 * <li><strong>style_{instantiated class name}</strong> – receives the output of the CSS rules applied to the pages of the associated post types with the meta box.</li>
29 * <li><strong>style_ie_{instantiated class name}</strong> – receives the output of the CSS rules for Internet Explorer applied to the pages of the associated post types with the meta box.</li>
30 * <li><strong>script_common_{instantiated class name}</strong> – receives the output of the base JavaScript scripts applied to the pages of the associated post types with the meta box.</li>
31 * <li><strong>script_{instantiated class name}</strong> – receives the output of the JavaScript scripts applied to the pages of the associated post types with the meta box.</li>
32 * <li><strong>validation_{instantiated class name}</strong> – receives the form submission values as array. The first parameter: submitted input array. The second parameter: the original array stored in the database.</li>
33 * <li><strong>columns_{taxonomy slug}</strong> – receives the header columns array. The first parameter: the header columns array.</li>
34 * <li><strong>columns_{instantiated class name}</strong> – receives the header sortable columns array. The first parameter: the header columns array.</li>
35 * <li><strong>sortable_columns_{taxonomy slug}</strong> – receives the header sortable columns array. The first parameter: the header columns array.</li>
36 * <li><strong>sortable_columns_{instantiated class name}</strong> – receives the header columns array. The first parameter: the header columns array.</li>
37 * <li><strong>cell_{taxonomy slug}</strong> – receives the cell output of the term listing table. The first parameter: the output string. The second parameter: the column slug. The third parameter: the term ID.</li>
38 * <li><strong>cell_{instantiated class name}</strong> – receives the cell output of the term listing table. The first parameter: the output string. The second parameter: the column slug. The third parameter: the term ID.</li>
39 * <li><strong>cell_{instantiated class name}_{column slug}</strong> – receives the cell output of the term listing table. The first parameter: the output string. The second parameter: the term ID.</li>
40 * </ul>
41 * @abstract
42 * @since 3.0.0
43 * @use AdminPageFramework_Utility
44 * @use AdminPageFramework_Message
45 * @use AdminPageFramework_Debug
46 * @use AdminPageFramework_Property_MetaBox
47 * @package AdminPageFramework
48 * @subpackage TaxonomyField
49 * @extends AdminPageFramework_Factory
50
51 * @todo Fix issues caused by extending the factory class and provide missing elements which were present in the meta box base class.
52 */
53 abstract class AdminPageFramework_TaxonomyField extends AdminPageFramework_Factory {
54
55 /**
56 * Defines the fields type.
57 * @since 3.0.0
58 * @internal
59 */
60 static protected $_sFieldsType = 'taxonomy';
61
62 /**
63 * Constructs the class object instance of AdminPageFramework_TaxonomyField.
64 *
65 * Handles setting up properties and hooks.
66 *
67 * <h4>Examples</h4>
68 * <code>
69 * new APF_TaxonomyField( 'apf_sample_taxonomy' ); // taxonomy slug
70 * </code>
71 *
72 * @since 3.0.0
73 * @param array|string The taxonomy slug(s). If multiple slugs need to be passed, enclose them in an array and pass the array.
74 * @param string The option key used for the options table to save the data. By default, the instantiated class name will be applied.
75 * @param string The access rights. Default: <em>manage_options</em>.
76 * @param string The text domain. Default: <em>admin-page-framework</em>.
77 * @return void
78 */
79 function __construct( $asTaxonomySlug, $sOptionKey='', $sCapability='manage_options', $sTextDomain='admin-page-framework' ) {
80
81 if ( empty( $asTaxonomySlug ) ) return;
82
83 /* Properties */
84 $this->oProp = new AdminPageFramework_Property_TaxonomyField( $this, get_class( $this ), $sCapability, $sTextDomain, self::$_sFieldsType );
85 $this->oProp->aTaxonomySlugs = ( array ) $asTaxonomySlug;
86 $this->oProp->sOptionKey = $sOptionKey ? $sOptionKey : $this->oProp->sClassName;
87
88 parent::__construct( $this->oProp );
89
90 if ( $this->oProp->bIsAdmin ) {
91
92 add_action( 'wp_loaded', array( $this, '_replyToDetermineToLoad' ) );
93
94 }
95
96 $this->oUtil->addAndDoAction( $this, "start_{$this->oProp->sClassName}" );
97
98 }
99
100 /**
101 * Determines whether the meta box belongs to the loading page.
102 *
103 * @since 3.0.3
104 * @internal
105 */
106 protected function _isInThePage() {
107
108 return ( in_array( $this->oProp->sPageNow, array( 'edit-tags.php', 'admin-ajax.php' ) ) );
109
110 }
111
112 /**
113 * Determines whether the meta box should be loaded in the currently loading page.
114 *
115 * @since 3.0.3
116 * @internal
117 */
118 public function _replyToDetermineToLoad() {
119
120 if ( ! $this->_isInThePage() ) return;
121
122 $this->_setUp();
123 $this->oUtil->addAndDoAction( $this, "set_up_{$this->oProp->sClassName}", $this );
124 $this->oProp->_bSetupLoaded = true;
125 add_action( 'current_screen', array( $this, '_replyToRegisterFormElements' ), 20 ); // the screen object should be established to detect the loaded page.
126
127 foreach( $this->oProp->aTaxonomySlugs as $__sTaxonomySlug ) {
128
129 /* Validation callbacks need to be set regardless of whether the current page is edit-tags.php or not */
130 add_action( "created_{$__sTaxonomySlug}", array( $this, '_replyToValidateOptions' ), 10, 2 );
131 add_action( "edited_{$__sTaxonomySlug}", array( $this, '_replyToValidateOptions' ), 10, 2 );
132
133 // if ( $GLOBALS['pagenow'] != 'admin-ajax.php' && $GLOBALS['pagenow'] != 'edit-tags.php' ) continue;
134 add_action( "{$__sTaxonomySlug}_add_form_fields", array( $this, '_replyToAddFieldsWOTableRows' ) );
135 add_action( "{$__sTaxonomySlug}_edit_form_fields", array( $this, '_replyToAddFieldsWithTableRows' ) );
136
137 add_filter( "manage_edit-{$__sTaxonomySlug}_columns", array( $this, '_replyToManageColumns' ), 10, 1 );
138 add_filter( "manage_edit-{$__sTaxonomySlug}_sortable_columns", array( $this, '_replyToSetSortableColumns' ) );
139 add_action( "manage_{$__sTaxonomySlug}_custom_column", array( $this, '_replyToSetColumnCell' ), 10, 3 );
140
141 }
142
143 }
144
145 /**
146 * The set up method.
147 *
148 * @remark should be overridden by the user definition class.
149 * @since 3.0.0
150 */
151 public function setUp() {}
152
153 /**
154 * Sets the <var>$aOptions<var> property array in the property object.
155 *
156 * This array will be referred later in the getFieldOutput() method.
157 *
158 * @since unknown
159 * @since 3.0.0 the scope is changed to protected as the taxonomy field class redefines it.
160 * #internal
161 */
162 protected function _setOptionArray( $iTermID=null, $sOptionKey ) {
163
164 $aOptions = get_option( $sOptionKey, array() );
165 $this->oProp->aOptions = isset( $iTermID, $aOptions[ $iTermID ] ) ? $aOptions[ $iTermID ] : array();
166
167 }
168
169 /**
170 * Adds input fields
171 *
172 * @internal
173 * @since 3.0.0
174 */
175 public function _replyToAddFieldsWOTableRows( $oTerm ) {
176 echo $this->_getFieldsOutput( isset( $oTerm->term_id ) ? $oTerm->term_id : null, false );
177 }
178
179 /**
180 * Adds input fields with table rows.
181 *
182 * @remark Used for the Edit Category(taxonomy) page.
183 * @internal
184 * @since 3.0.0
185 */
186 public function _replyToAddFieldsWithTableRows( $oTerm ) {
187 echo $this->_getFieldsOutput( isset( $oTerm->term_id ) ? $oTerm->term_id : null, true );
188 }
189
190 /**
191 * Modifies the columns of the term listing table in the edit-tags.php page.
192 *
193 * @internal
194 * @since 3.0.0
195 */
196 public function _replyToManageColumns( $aColumns ) {
197
198 /* By default something like this is passed.
199 Array (
200 [cb] => <input type="checkbox" />
201 [name] => Name
202 [description] => Description
203 [slug] => Slug
204 [posts] => Admin Page Framework
205 )
206 */
207 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] )
208 $aColumns = $this->oUtil->addAndApplyFilter( $this, "columns_{$_GET['taxonomy']}", $aColumns );
209 $aColumns = $this->oUtil->addAndApplyFilter( $this, "columns_{$this->oProp->sClassName}", $aColumns );
210 return $aColumns;
211
212 }
213
214 /**
215 *
216 * @internal
217 * @since 3.0.0
218 */
219 public function _replyToSetSortableColumns( $aSortableColumns ) {
220
221 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] )
222 $aSortableColumns = $this->oUtil->addAndApplyFilter( $this, "sortable_columns_{$_GET['taxonomy']}", $aSortableColumns );
223 $aSortableColumns = $this->oUtil->addAndApplyFilter( $this, "sortable_columns_{$this->oProp->sClassName}", $aSortableColumns );
224 return $aSortableColumns;
225
226 }
227 /**
228 *
229 * @internal
230 * @since 3.0.0
231 */
232 public function _replyToSetColumnCell( $vValue, $sColumnSlug, $sTermID ) {
233
234 $sCellHTML = '';
235 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] )
236 $sCellHTML = $this->oUtil->addAndApplyFilter( $this, "cell_{$_GET['taxonomy']}", $vValue, $sColumnSlug, $sTermID );
237 $sCellHTML = $this->oUtil->addAndApplyFilter( $this, "cell_{$this->oProp->sClassName}", $sCellHTML, $sColumnSlug, $sTermID );
238 $sCellHTML = $this->oUtil->addAndApplyFilter( $this, "cell_{$this->oProp->sClassName}_{$sColumnSlug}", $sCellHTML, $sTermID ); // 3.0.2+
239 echo $sCellHTML;
240
241 }
242
243 /**
244 * Retrieves the fields output.
245 *
246 * @since 3.0.0
247 * @internal
248 */
249 private function _getFieldsOutput( $iTermID, $bRenderTableRow ) {
250
251 $_aOutput = array();
252
253 /* Set nonce. */
254 $_aOutput[] = wp_nonce_field( $this->oProp->sClassHash, $this->oProp->sClassHash, true, false );
255
256 /* Set the option property array */
257 $this->_setOptionArray( $iTermID, $this->oProp->sOptionKey );
258
259 /* Format the fields arrays - taxonomy fields do not support sections */
260 $this->oForm->format();
261
262 /* Get the field outputs */
263 $_oFieldsTable = new AdminPageFramework_FormTable( $this->oProp->aFieldTypeDefinitions, $this->_getFieldErrors(), $this->oMsg );
264 $_aOutput[] = $bRenderTableRow
265 ? $_oFieldsTable->getFieldRows( $this->oForm->aFields['_default'], array( $this, '_replyToGetFieldOutput' ) )
266 : $_oFieldsTable->getFields( $this->oForm->aFields['_default'], array( $this, '_replyToGetFieldOutput' ) );
267
268 /* Filter the output */
269 $_sOutput = $this->oUtil->addAndApplyFilters( $this, 'content_' . $this->oProp->sClassName, implode( PHP_EOL, $_aOutput ) );
270
271 /* Do action */
272 $this->oUtil->addAndDoActions( $this, 'do_' . $this->oProp->sClassName, $this );
273
274 return $_sOutput;
275
276 }
277
278 /**
279 * Validates the given option array.
280 *
281 * @since 3.0.0
282 * @internal
283 */
284 public function _replyToValidateOptions( $iTermID ) {
285
286 if ( ! isset( $_POST[ $this->oProp->sClassHash ] ) ) { return; }
287 if ( ! wp_verify_nonce( $_POST[ $this->oProp->sClassHash ], $this->oProp->sClassHash ) ) { return; }
288
289 $aTaxonomyFieldOptions = get_option( $this->oProp->sOptionKey, array() );
290 $aOldOptions = isset( $aTaxonomyFieldOptions[ $iTermID ] ) ? $aTaxonomyFieldOptions[ $iTermID ] : array();
291 $aSubmittedOptions = array();
292 foreach( $this->oForm->aFields as $_sSectionID => $_aFields )
293 foreach( $_aFields as $_sFieldID => $_aField )
294 if ( isset( $_POST[ $_sFieldID ] ) )
295 $aSubmittedOptions[ $_sFieldID ] = $_POST[ $_sFieldID ];
296
297 /* Apply validation filters to the submitted option array. */
298 $aSubmittedOptions = $this->oUtil->addAndApplyFilters( $this, 'validation_' . $this->oProp->sClassName, $aSubmittedOptions, $aOldOptions, $this );
299
300 $aTaxonomyFieldOptions[ $iTermID ] = $this->oUtil->uniteArrays( $aSubmittedOptions, $aOldOptions );
301 update_option( $this->oProp->sOptionKey, $aTaxonomyFieldOptions );
302
303 }
304
305 /**
306 * Registers form fields and sections.
307 *
308 * @since 3.0.0
309 * @internal
310 */
311 public function _replyToRegisterFormElements( $oScreen ) {
312
313 // Schedule to add head tag elements and help pane contents.
314 if ( 'edit-tags.php' != $this->oProp->sPageNow ) return;
315
316 $this->_loadDefaultFieldTypeDefinitions();
317
318 // Format the fields array.
319 $this->oForm->format();
320 $this->oForm->applyConditions();
321
322 // not sure if setDynamicElements() should be performed or not...
323
324 $this->_registerFields( $this->oForm->aConditionedFields );
325
326 }
327
328 /**
329 * Redirects undefined callback methods.
330 * @internal
331 * @since 3.0.0
332 * @deprecated
333 */
334 function ___call( $sMethodName, $aArgs=null ) {
335
336 /* if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] ) :
337 if ( substr( $sMethodName, 0, strlen( 'columns_' . $_GET['taxonomy'] ) ) == 'columns_' . $_GET['taxonomy'] ) return $aArgs[ 0 ];
338 if ( substr( $sMethodName, 0, strlen( 'sortable_columns_' . $_GET['taxonomy'] ) ) == 'sortable_columns_' . $_GET['taxonomy'] ) return $aArgs[ 0 ];
339 if ( substr( $sMethodName, 0, strlen( 'cell_' . $_GET['taxonomy'] ) ) == 'cell_' . $_GET['taxonomy'] ) return $aArgs[ 0 ];
340 endif;
341
342 if ( substr( $sMethodName, 0, strlen( 'columns_' . $this->oProp->sClassName ) ) == 'columns_' . $this->oProp->sClassName ) return $aArgs[ 0 ];
343 if ( substr( $sMethodName, 0, strlen( 'sortable_columns_' . $this->oProp->sClassName ) ) == 'sortable_columns_' . $this->oProp->sClassName ) return $aArgs[ 0 ];
344 if ( substr( $sMethodName, 0, strlen( 'cell_' . $this->oProp->sClassName ) ) == 'cell_' . $this->oProp->sClassName ) return $aArgs[ 0 ];
345 */
346
347 if ( has_filter( $sMethodName ) ) {
348 return isset( $aArgs[ 0 ] ) ? $aArgs[ 0 ] : null;
349 }
350
351 return parent::__call( $sMethodName, $aArgs );
352
353 }
354 }
355 endif;