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.</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_MetaBox_Base
50 */
51 abstract class AdminPageFramework_TaxonomyField extends AdminPageFramework_MetaBox_Base {
52
53 /**
54 * Stores the property object.
55 * @since 3.0.0
56 */
57 protected $oProp;
58
59 /**
60 * Stores the head tag object.
61 * @since 3.0.0
62 * @internal
63 */
64 protected $oHeadTag;
65
66 /**
67 * Stores the contextual help pane object.
68 * @since 3.0.0
69 * @internal
70 */
71 protected $oHelpPane;
72
73 /**
74 * Defines the fields type.
75 * @since 3.0.0
76 * @internal
77 */
78 static protected $_sFieldsType = 'taxonomy';
79
80 /**
81 * Constructs the class object instance of AdminPageFramework_TaxonomyField.
82 *
83 * Handles setting up properties and hooks.
84 *
85 * <h4>Examples</h4>
86 * <code>
87 * new APF_TaxonomyField( 'apf_sample_taxonomy' ); // taxonomy slug
88 * </code>
89 *
90 * @since 3.0.0
91 * @param array|string The taxonomy slug(s). If multiple slugs need to be passed, enclose them in an array and pass the array.
92 * @param string The option key used for the options table to save the data. By default, the instantiated class name will be applied.
93 * @param string The access rights. Default: <em>manage_options</em>.
94 * @param string The text domain. Default: <em>admin-page-framework</em>.
95 * @return void
96 */
97 function __construct( $asTaxonomySlug, $sOptionKey='', $sCapability='manage_options', $sTextDomain='admin-page-framework' ) {
98
99 if ( empty( $asTaxonomySlug ) ) return;
100
101 /* Objects */
102 $this->oProp = new AdminPageFramework_Property_TaxonomyField( $this, get_class( $this ), $sCapability );
103 $this->oUtil = new AdminPageFramework_WPUtility;
104 $this->oMsg = AdminPageFramework_Message::instantiate( $sTextDomain );
105 $this->oDebug = new AdminPageFramework_Debug;
106
107 $this->oHeadTag = new AdminPageFramework_HeadTag_TaxonomyField( $this->oProp );
108 $this->oHelpPane = new AdminPageFramework_HelpPane_TaxonomyField( $this->oProp );
109
110 /* Properties */
111 $this->oProp->aTaxonomySlugs = ( array ) $asTaxonomySlug;
112 $this->oProp->sOptionKey = $sOptionKey ? $sOptionKey : $this->oProp->sClassName;
113 $this->oProp->sFieldsType = self::$_sFieldsType;
114 $this->oForm = new AdminPageFramework_FormElement( $this->oProp->sFieldsType, $sCapability );
115
116 if ( $this->oProp->bIsAdmin ) {
117
118 add_action( 'wp_loaded', array( $this, '_replyToDetermineToLoad' ) );
119
120 }
121
122 $this->oUtil->addAndDoAction( $this, "start_{$this->oProp->sClassName}" );
123
124 }
125
126 /**
127 * Determines whether the meta box belongs to the loading page.
128 *
129 * @since 3.0.3
130 * @internal
131 */
132 protected function _isInThePage() {
133
134 return ( in_array( $GLOBALS['pagenow'], array( 'edit-tags.php', 'admin-ajax.php' ) ) );
135
136 }
137
138 /**
139 * Determines whether the meta box should be loaded in the currently loading page.
140 *
141 * @since 3.0.3
142 * @internal
143 */
144 public function _replyToDetermineToLoad() {
145
146 if ( ! $this->_isInThePage() ) return;
147
148 $this->_loadDefaultFieldTypeDefinitions();
149 $this->setUp();
150 add_action( 'current_screen', array( $this, '_replyToRegisterFormElements' ) ); // the screen object should be established to detect the loaded page.
151
152 foreach( $this->oProp->aTaxonomySlugs as $__sTaxonomySlug ) {
153
154 /* Validation callbacks need to be set regardless of whether the current page is edit-tags.php or not */
155 add_action( "created_{$__sTaxonomySlug}", array( $this, '_replyToValidateOptions' ), 10, 2 );
156 add_action( "edited_{$__sTaxonomySlug}", array( $this, '_replyToValidateOptions' ), 10, 2 );
157
158 // if ( $GLOBALS['pagenow'] != 'admin-ajax.php' && $GLOBALS['pagenow'] != 'edit-tags.php' ) continue;
159 add_action( "{$__sTaxonomySlug}_add_form_fields", array( $this, '_replyToAddFieldsWOTableRows' ) );
160 add_action( "{$__sTaxonomySlug}_edit_form_fields", array( $this, '_replyToAddFieldsWithTableRows' ) );
161
162 add_filter( "manage_edit-{$__sTaxonomySlug}_columns", array( $this, '_replyToManageColumns' ), 10, 1 );
163 add_filter( "manage_edit-{$__sTaxonomySlug}_sortable_columns", array( $this, '_replyToSetSortableColumns' ) );
164 add_action( "manage_{$__sTaxonomySlug}_custom_column", array( $this, '_replyToSetColumnCell' ), 10, 3 );
165
166 }
167
168 }
169
170 /**
171 * The set up method.
172 *
173 * @remark should be overridden by the user definition class.
174 * @since 3.0.0
175 */
176 public function setUp() {}
177
178 /**
179 * Sets the <var>$aOptions<var> property array in the property object.
180 *
181 * This array will be referred later in the getFieldOutput() method.
182 *
183 * @since unknown
184 * @since 3.0.0 the scope is changed to protected as the taxonomy field class redefines it.
185 * #internal
186 */
187 protected function setOptionArray( $iTermID=null, $sOptionKey ) {
188
189 $aOptions = get_option( $sOptionKey, array() );
190 $this->oProp->aOptions = isset( $iTermID, $aOptions[ $iTermID ] ) ? $aOptions[ $iTermID ] : array();
191
192 }
193
194 /**
195 * Adds input fields
196 *
197 * @internal
198 * @since 3.0.0
199 */
200 public function _replyToAddFieldsWOTableRows( $oTerm ) {
201 echo $this->_getFieldsOutput( isset( $oTerm->term_id ) ? $oTerm->term_id : null, false );
202 }
203
204 /**
205 * Adds input fields with table rows.
206 *
207 * @remark Used for the Edit Category(taxonomy) page.
208 * @internal
209 * @since 3.0.0
210 */
211 public function _replyToAddFieldsWithTableRows( $oTerm ) {
212 echo $this->_getFieldsOutput( isset( $oTerm->term_id ) ? $oTerm->term_id : null, true );
213 }
214
215 /**
216 * Modifies the columns of the term listing table in the edit-tags.php page.
217 *
218 * @internal
219 * @since 3.0.0
220 */
221 public function _replyToManageColumns( $aColumns ) {
222
223 /* By default something like this is passed.
224 Array (
225 [cb] => <input type="checkbox" />
226 [name] => Name
227 [description] => Description
228 [slug] => Slug
229 [posts] => Admin Page Framework
230 )
231 */
232 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] )
233 $aColumns = $this->oUtil->addAndApplyFilter( $this, "columns_{$_GET['taxonomy']}", $aColumns );
234 $aColumns = $this->oUtil->addAndApplyFilter( $this, "columns_{$this->oProp->sClassName}", $aColumns );
235 return $aColumns;
236
237 }
238
239 /**
240 *
241 * @internal
242 * @since 3.0.0
243 */
244 public function _replyToSetSortableColumns( $aSortableColumns ) {
245
246 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] )
247 $aSortableColumns = $this->oUtil->addAndApplyFilter( $this, "sortable_columns_{$_GET['taxonomy']}", $aSortableColumns );
248 $aSortableColumns = $this->oUtil->addAndApplyFilter( $this, "sortable_columns_{$this->oProp->sClassName}", $aSortableColumns );
249 return $aSortableColumns;
250
251 }
252 /**
253 *
254 * @internal
255 * @since 3.0.0
256 */
257 public function _replyToSetColumnCell( $vValue, $sColumnSlug, $sTermID ) {
258
259 $sCellHTML = '';
260 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] )
261 $sCellHTML = $this->oUtil->addAndApplyFilter( $this, "cell_{$_GET['taxonomy']}", $vValue, $sColumnSlug, $sTermID );
262 $sCellHTML = $this->oUtil->addAndApplyFilter( $this, "cell_{$this->oProp->sClassName}", $sCellHTML, $sColumnSlug, $sTermID );
263 $sCellHTML = $this->oUtil->addAndApplyFilter( $this, "cell_{$this->oProp->sClassName}_{$sColumnSlug}", $sCellHTML, $sTermID ); // 3.0.2+
264 echo $sCellHTML;
265
266 }
267
268 /**
269 * Retrieves the fields output.
270 *
271 * @since 3.0.0
272 * @internal
273 */
274 private function _getFieldsOutput( $iTermID, $bRenderTableRow ) {
275
276 $aOutput = array();
277
278 /* Set nonce. */
279 $aOutput[] = wp_nonce_field( $this->oProp->sClassHash, $this->oProp->sClassHash, true, false );
280
281 /* Set the option property array */
282 $this->setOptionArray( $iTermID, $this->oProp->sOptionKey );
283
284 /* Format the fields arrays - taxonomy fields do not support sections */
285 $this->oForm->format();
286
287 /* Get the field outputs */
288 $oFieldsTable = new AdminPageFramework_FormTable( $this->oProp->aFieldTypeDefinitions, $this->oMsg );
289 $aOutput[] = $bRenderTableRow
290 ? $oFieldsTable->getFieldRows( $this->oForm->aFields['_default'], array( $this, '_replyToGetFieldOutput' ) )
291 : $oFieldsTable->getFields( $this->oForm->aFields['_default'], array( $this, '_replyToGetFieldOutput' ) );
292
293 /* Filter the output */
294 $sOutput = $this->oUtil->addAndApplyFilters( $this, 'content_' . $this->oProp->sClassName, implode( PHP_EOL, $aOutput ) );
295
296 /* Do action */
297 $this->oUtil->addAndDoActions( $this, 'do_' . $this->oProp->sClassName );
298
299 return $sOutput;
300
301 }
302
303 /**
304 * Validates the given option array.
305 *
306 * @since 3.0.0
307 * @internal
308 */
309 public function _replyToValidateOptions( $iTermID ) {
310
311 if ( ! wp_verify_nonce( $_POST[ $this->oProp->sClassHash ], $this->oProp->sClassHash ) ) return;
312
313 $aTaxonomyFieldOptions = get_option( $this->oProp->sOptionKey, array() );
314 $aOldOptions = isset( $aTaxonomyFieldOptions[ $iTermID ] ) ? $aTaxonomyFieldOptions[ $iTermID ] : array();
315 $aSubmittedOptions = array();
316 foreach( $this->oForm->aFields as $_sSectionID => $_aFields )
317 foreach( $_aFields as $_sFieldID => $_aField )
318 if ( isset( $_POST[ $_sFieldID ] ) )
319 $aSubmittedOptions[ $_sFieldID ] = $_POST[ $_sFieldID ];
320
321 /* Apply validation filters to the submitted option array. */
322 $aSubmittedOptions = $this->oUtil->addAndApplyFilters( $this, 'validation_' . $this->oProp->sClassName, $aSubmittedOptions, $aOldOptions );
323
324 $aTaxonomyFieldOptions[ $iTermID ] = $this->oUtil->uniteArrays( $aSubmittedOptions, $aOldOptions );
325 update_option( $this->oProp->sOptionKey, $aTaxonomyFieldOptions );
326
327 }
328
329 /**
330 * Registers form fields and sections.
331 *
332 * @since 3.0.0
333 * @internal
334 */
335 public function _replyToRegisterFormElements() {
336
337 // Schedule to add head tag elements and help pane contents.
338 if ( $GLOBALS['pagenow'] != 'edit-tags.php' ) return;
339
340 // Format the fields array.
341 $this->oForm->format();
342 $this->oForm->applyConditions();
343 // $this->oForm->applyFiltersToFields( $this, $this->oProp->sClassName );
344
345 // not sure if setDynamicElements() should be performed or not...
346
347 $this->_registerFields( $this->oForm->aConditionedFields );
348
349 }
350
351 /**
352 * Redirects undefined callback methods.
353 * @internal
354 * @since 3.0.0
355 */
356
357 function __call( $sMethodName, $aArgs=null ) {
358
359 if ( isset( $_GET['taxonomy'] ) && $_GET['taxonomy'] ) :
360 if ( substr( $sMethodName, 0, strlen( 'columns_' . $_GET['taxonomy'] ) ) == 'columns_' . $_GET['taxonomy'] ) return $aArgs[ 0 ];
361 if ( substr( $sMethodName, 0, strlen( 'sortable_columns_' . $_GET['taxonomy'] ) ) == 'sortable_columns_' . $_GET['taxonomy'] ) return $aArgs[ 0 ];
362 if ( substr( $sMethodName, 0, strlen( 'cell_' . $_GET['taxonomy'] ) ) == 'cell_' . $_GET['taxonomy'] ) return $aArgs[ 0 ];
363 endif;
364
365 if ( substr( $sMethodName, 0, strlen( 'columns_' . $this->oProp->sClassName ) ) == 'columns_' . $this->oProp->sClassName ) return $aArgs[ 0 ];
366 if ( substr( $sMethodName, 0, strlen( 'sortable_columns_' . $this->oProp->sClassName ) ) == 'sortable_columns_' . $this->oProp->sClassName ) return $aArgs[ 0 ];
367 if ( substr( $sMethodName, 0, strlen( 'cell_' . $this->oProp->sClassName ) ) == 'cell_' . $this->oProp->sClassName ) return $aArgs[ 0 ];
368
369 return parent::__call( $sMethodName, $aArgs );
370
371 }
372 }
373 endif;