ttributes_to_string(array $attributes) { $reserved_attributes = array( 'id', 'name', 'value', 'method', 'action', 'type', 'for', 'multiline', 'default', 'textfield', 'valuefield', 'includenull', 'yearrange', 'fields', 'inlineerrors', 'description'); $result = array(); // Build string from array if(is_array($attributes)) { foreach($attributes as $attribute => $value) { // Ignore reserved attributes if(!in_array(strtolower($attribute), $reserved_attributes)) { $result[] = $attribute . '="' . $value . '"'; } } } return implode(' ', $result); } /** * Extracts the Field ID and Field Name for an input element from the arguments * originally passed to a rendering function. * * @param array field_args The arguments passed to the rendering function which * is going to render the input field. * @param string field_id Output argument. It will contain the ID of the field. * @param string field_name Output argument. It will contain the name of the field. */ protected function get_field_ids(array $field_args, &$field_id, &$field_name) { // Determine field ID and Name $field_id = $field_args['id']; if(empty($field_id)) { throw new InvalidArgumentException(__('Field ID must be specified.', $this->_textdomain)); } $field_name = $field_args['name'] ?? $field_args['attributes']['name'] ?? $field_id; // If a Settings Key has been passed, modify field ID and Name to make them // part of the Settings Key array $settings_key = $field_args['settings_key'] ?? null; $field_id = $this->group_field($field_id, $settings_key); $field_name = $this->group_field($field_name, $settings_key); } /** * Takes a field ID and transforms it so that it becomes part of a field group. * Example * - Field ID: MyField * - Group: SomeGroup * * Result: SomeGroup[MyField] * This allows to group fields together and access them as an array. * * @param string id The Field ID. * @param string group The group to which the field should be added. * @return string The new field name. */ protected function group_field($id, $group) { return empty($group) ? $id : $group . '[' . $id . ']'; } /*** Rendering methods ***/ /** * Renders a select box. * * @param array args An array of arguments passed by add_settings_field(). * @param bool display Indicates if the HTML for the field should be printed * out directly (true) or just returned (false). * @see add_settings_field(). */ public function render_dropdown($args, $display = true) { $this->get_field_ids($args, $field_id, $field_name); // Retrieve the options that will populate the dropdown $dropdown_options = $args['options']; if(!is_array($dropdown_options)) { throw new InvalidArgumentException(__('Argument "options" must be an array.', $this->_textdomain)); } // Retrieve the selected Option elements $selected_options = get_value('selected', $args, array()); if(!is_array($selected_options)) { $selected_options = array($selected_options); } // Retrieve the HTML attributes $attributes = get_value('attributes', $args, array()); // If we are about to render a multi-select dropdown, add two square brackets // so that all selected values will be returned as an array // TODO Make search in array case-insensitive if(in_array('multiple', $attributes)) { $field_name .= '[]'; } $html = ''; if(!empty($attributes['description'])) { $html .= '

' . $attributes['description'] . '

'; } if($display) { echo $html; } return $html; } /** * Build the HTML to represent an element. * * @param string type The type of input. It can be text, password, hidden, * checkbox or radio. * @param string field_id The ID of the field. * @param string value The field value. * @param array attribues Additional field attributes. * @param string field_name The name of the field. If unspecified, the field * ID will be taken. * @return string The HTML representation of the field. */ protected function get_input_html($type, $field_id, $value, $attributes, $field_name = null) { $field_name = !empty($field_name) ? $field_name : $field_id; $html = 'attributes_to_string($attributes) . ' />'; if(!empty($attributes['description'])) { if($type == 'checkbox') { $element = 'span'; } else { $element = 'p'; } $html .= '<' . $element . ' class="description">' . $attributes['description'] . ''; } return $html; } /** * Build the HTML to represent a '; if(!empty($attributes['description'])) { $html .= '

' . $attributes['description'] . '

'; } return $html; } /** * Renders a hidden field. * * @param array args An array of arguments passed by add_settings_field(). * @param bool display Indicates if the HTML for the field should be printed * out directly (true) or just returned (false). * @see add_settings_field(). */ public function render_hidden($args, $display = true) { $this->get_field_ids($args, $field_id, $field_name); // Retrieve the HTML attributes $attributes = get_value('attributes', $args, array()); $value = get_value('value', $args, ''); $html = $this->get_input_html('hidden', $field_id, $value, $attributes, $field_name); if($display) { echo $html; } return $html; } /** * Renders a text box (input or textarea). To render a textarea, pass an * attribute named "multiline" set to true. * * @param array args An array of arguments passed by add_settings_field(). * @param bool display Indicates if the HTML for the field should be printed * out directly (true) or just returned (false). * @see add_settings_field(). */ public function render_textbox($args, $display = true) { $this->get_field_ids($args, $field_id, $field_name); // Retrieve the HTML attributes $attributes = get_value('attributes', $args, array()); $value = get_value('value', $args, ''); $multiline = get_value('multiline', $attributes); if($multiline) { $html = $this->get_textarea_html($field_id, $value, $attributes, $field_name); } else { $field_type = get_value('type', $args, 'text'); $html = $this->get_input_html($field_type, $field_id, $value, $attributes, $field_name); } if($display) { echo $html; } return $html; } /** * Renders a checkbox. * * @param array args An array of arguments passed by add_settings_field(). * @param bool display Indicates if the HTML for the field should be printed * out directly (true) or just returned (false). * @see add_settings_field(). */ public function render_checkbox($args, $display = true) { $this->get_field_ids($args, $field_id, $field_name); // Retrieve the HTML attributes $attributes = get_value('attributes', $args, array()); if(get_value('checked', $attributes, false)) { $attributes['checked'] = 'checked'; } else { unset($attributes['checked']); } $value = get_value('value', $args, 1); /* The hidden input is a "trick" to get a value also when a checkbox is not * ticked. Unticked checkboxes are not POSTed with a form, therefore the only * way to get a "non selected" value would be to check if each checkbox field * was posted. By adding a hidden field just before it, with a value of zero, * we can ensure that something is always POSTed, whether the checkbox field * is ticked or not: * - When it's ticked, its value is sent with the form, overriding the hidden field. * - When it's not ticked, the value of the hidden field is sent instead. * * This allows to skip a lot of checks, and to just take the field value. */ $html = $this->get_input_html('hidden', $field_id . '_unticked', 0, array(), $field_name); $html .= $this->get_input_html('checkbox', $field_id, $value, $attributes, $field_name); if($display) { echo $html; } return $html; } /** * Event handler, fired when setting page is loaded. */ public function options_page_load() { if(get_value('settings-updated', $_GET)) { //plugin settings have been saved. Display a message, or do anything you like. } } /** * Initialises the settings page. */ public function init_settings_page() { $this->add_settings_tabs(); $this->add_settings_sections(); $this->add_settings_fields(); } /** * Renders a simple text field. * * @param string section The section where the field will be rendered. * @param string field_id The field ID. * @param string label The field label. * @param string description The field description. * @param string css_class The CSS class to give to the rendered input field. * @param array attributes Additional HTML attributes. * @param string type The field type. */ protected function render_text_field($section, $field_id, $label, $description = '', $css_class = '', $attributes = array(), $type = 'text') { // Fetch the value from the attributes passed with the field definition, if present. If absent, fetch // the value from current settings. This allows 3rd parties to inject custom fields whose value has // to be determined using some specific logic // @since 2.1.1.201208 $value = $attributes['value'] ?? $this->current_settings($field_id, $this->default_settings($field_id, '')); add_settings_field( $field_id, $label, array($this, 'render_textbox'), $this->_settings_key, $section, array( 'settings_key' => $attributes['settings_key'] ?? $this->_settings_key, 'id' => $field_id, 'label_for' => $field_id, 'value' => $value, // Field type // @since 2.0.9.191108 'type' => $type, // Input field attributes 'attributes' => array_merge(array( 'class' => $css_class . ' ' . $field_id, 'description' => $description, ), $attributes), ) ); } /** * Renders a simple checkbox field. * * @param string section The section where the field will be rendered. * @param string field_id The field ID. * @param string label The field label. * @param string description The field description. * @param string css_class The CSS class to give to the rendered input field. * @param array attributes Additional HTML attributes. */ protected function render_checkbox_field($section, $field_id, $label, $description = '', $css_class = '', $attributes = array()) { // Fetch the value from the attributes passed with the field definition, if present. If absent, fetch // the value from current settings. This allows 3rd parties to inject custom fields whose value has // to be determined using some specific logic // @since 2.1.1.201208 $value = $attributes['value'] ?? $this->current_settings($field_id, $this->default_settings($field_id, '')); add_settings_field( $field_id, $label, array($this, 'render_checkbox'), $this->_settings_key, $section, array( 'settings_key' => $attributes['settings_key'] ?? $this->_settings_key, 'id' => $field_id, 'label_for' => $field_id, // Input field attributes 'attributes' => array_merge(array( 'class' => $css_class . ' ' . $field_id, 'description' => $description, 'checked' => $value, ), $attributes), ) ); } /** * Renders a dropdown field. * * @param string section The section where the field will be rendered. * @param string field_id The field ID. * @param string label The field label. * @param array options An associative array of value => label entries. It * will be used to populate the options available for the dropdown field. * @param string description The field description. * @param string css_class The CSS class to give to the rendered input field. * @param array attributes Additional HTML attributes (e.g. "multiple"). */ protected function render_dropdown_field($section, $field_id, $label, $options, $description = '', $css_class = '', $attributes = array()) { // Fetch the value from the attributes passed with the field definition, if present. If absent, fetch // the value from current settings. This allows 3rd parties to inject custom fields whose value has // to be determined using some specific logic // @since 2.1.1.201208 $value = $attributes['value'] ?? $this->current_settings($field_id, $this->default_settings($field_id, '')); add_settings_field( $field_id, $label, array($this, 'render_dropdown'), $this->_settings_key, $section, array( 'settings_key' => $attributes['settings_key'] ?? $this->_settings_key, 'id' => $field_id, 'label_for' => $field_id, 'options' => $options, 'selected' => $value, // Input field attributes 'attributes' => array_merge(array( 'class' => $css_class . ' ' . $field_id, 'description' => $description, ), $attributes), ) ); } /** * Handlesa custom field, which will be rendered using a dedicated callback function. * * @param string $section * @param string $field_id * @param string $field_label * @param array $attributes * @param callable $render_callback * @since 2.1.0.201112 */ protected function render_custom_field($section, $field_id, $field_label, array $attributes, $render_callback) { if(!is_callable($render_callback)) { // Invalid callback, report issue return; } // Add "Exchange Rates" table add_settings_field( $field_id, $field_label, $render_callback, $this->_settings_key, $section, $attributes ); } }