Skip to content

Instantly share code, notes, and snippets.

@michaeluno
Last active November 28, 2016 08:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michaeluno/8f2ea5514925d60b7134f71783f25fb2 to your computer and use it in GitHub Desktop.
Save michaeluno/8f2ea5514925d60b7134f71783f25fb2 to your computer and use it in GitHub Desktop.
Demonstrates front-end forms using Admin Page Framework.
<?php
/**
* Plugin Name: Admin Page Framework - Front-end Form (beta)
* Plugin URI: http://en.michaeluno.jp/admin-page-framework
* Description: Demonstrates front-end forms using Admin Page Framework.
* Author: Michael Uno
* Author URI: http://michaeluno.jp
* Version: 1.0.0
*
*/
final class AdminPageFramework_FrontEndFormBeta_Bootstrap {
public function __construct() {
add_action( 'plugins_loaded', array( $this, 'replyToLoadPlugin' ) );
}
public function replyToLoadPlugin() {
$_sAPFPath = defined( 'WP_DEBUG' ) && WP_DEBUG
? dirname( dirname( __FILE__ ) ) . '/admin-page-framework/development/admin-page-framework.php'
: dirname( dirname( __FILE__ ) ) . '/admin-page-framework/library/apf/admin-page-framework.php';
include( $_sAPFPath );
if ( ! class_exists( 'AdminPageFramework' ) ) {
return;
}
include( dirname( __FILE__ ) . '/AdminPageFramework_FormBeta_frontend.php' );
include( dirname( __FILE__ ) . '/AdminPageFramework_FrontendFormBeta_Base.php' );
include( dirname( __FILE__ ) . '/AdminPageFramework_FrontendFormBeta.php' );
}
}
new AdminPageFramework_FrontEndFormBeta_Bootstrap;
<?php
class AdminPageFramework_FormBeta_frontend extends AdminPageFramework_Form {
/**
* Stores sub-object class names.
*
*
* @since 3.8.11
*/
public $aSubClasses = array(
'submit_notice' => '', // disable it
'field_error' => '', // disable it
'last_input' => '', // disable it
'message' => 'AdminPageFramework_Message',
);
public function setFieldErrors( $aErrors ) {}
public function getFieldErrors() {
return $this->aArguments[ 'caller_object' ]->aFieldErrors;
}
}
<?php
/**
* Displays a form in the front-end.
*
*/
class AdminPageFramework_FrontendFormBeta extends AdminPageFramework_FrontendFormBeta_Base {
/**
* The action/filter hook that the form output will be inserted.
*/
public $sHookName = 'the_content';
/**
* Set the callback priority passed to the `add_action()` or `add_filter()` function.
*/
public $iPriority = 11;
/**
* Set the hook type either `filter` or `action`.
*/
public $sHookType = 'filter'; // or action
/**
* Gets called when the page loads. Do some necessary set-ups here.
*/
public function load() {
// Register field types
if ( class_exists( 'DateCustomFieldType' ) ) {
new DateCustomFieldType( get_class( $this ) );
}
}
/**
* Determines whether the form should be loaded in the current page.
* @return boolean
*/
public function isInThePage() {
if ( ! is_singular() ) {
return false;
}
if ( ! is_main_query() ) {
return false;
}
return true;
}
/**
* @return array
*/
public function getStyles() {
return array(
'.form-table {
border: none;
}
.form-table th {
width: 30%;
}
.form-table tr > * {
vertical-align: top;
border: none;
}
.form-table tr label,
.form-table tr fieldset {
vertical-align: middle;
}
.form-table .admin-page-framework-input-label-container label {
vertical-align: top;
}
.form-table .admin-page-framework-field-radio input {
margin-right: 0.4em;
}
.form-table .admin-page-framework-field-submit {
text-align: right;
width: 100%;
}
.form-table .admin-page-framework-field-submit .admin-page-framework-input-button-container {
padding-right: 0;
}
.form-table .select_image.button,
.form-table .remove_image.button,
.form-table .remove_value.button {
text-decoration: none;
border: none;
}
.form-table .admin-page-framework-field {
margin: 0;
}
.form-table .admin-page-framework-checkbox-label {
margin-top: 0;
}
.form-table .admin-page-framework-field .admin-page-framework-input-label-container {
margin-bottom: 0;
}
@media only screen and (max-width: 600px) {
.form-table > tbody > tr > td,
.form-table > tbody > tr > th {
display: inline-block;
width: 100%;
}
.form-table > tbody > tr > td {
margin-bottom: 0.6em;
}
.form-table > tbody > tr {
}
}
/* Notification Containers */
.submit-notice > p {
border-radius: 6px;
padding: 1em 2em;
}
.submit-notice.error > p {
border: solid 1px #8a2323;
background-color: #ffe0e0;
}
.submit-notice.update > p {
border: solid 1px #418a23;
background-color: #e9ffe0;
}
',
// or set a CSS file path
// dirname( __FILE__ ) . '/css/style.css',
);
}
/**
* Retrieves form section definition arrays.
* @return array
*/
public function getSections() {
return array();
}
/**
* Retrieves form field definition arrays.
*/
public function getFields() {
return array(
array(
'title' => __( 'Text Input', 'admin-page-framework' ),
'show_debug_info' => false,
'field_id' => 'text',
'type' => 'text',
),
array(
'title' => __( 'Text Area', 'admin-page-framework' ),
'field_id' => 'textarea',
'type' => 'textarea',
'show_debug_info' => false,
),
array(
'title' => __( 'Radio Buttons', 'admin-page-framework' ),
'field_id' => 'radio',
'type' => 'radio',
'show_debug_info' => false,
'label' => array(
'on' => __( 'On', 'admin-page-framework' ),
'off' => __( 'Off', 'admin-page-framework' ),
),
'default' => 'on',
),
array(
'title' => __( 'Drop-dorn', 'admin-page-framework' ),
'field_id' => 'select',
'type' => 'select',
'show_debug_info' => false,
'label' => array(
0 => __( 'Sunday', 'admin-page-framework' ),
1 => __( 'Monday', 'admin-page-framework' ),
2 => __( 'Tuesday', 'admin-page-framework' ),
3 => __( 'Wednesday', 'admin-page-framework' ),
4 => __( 'Thursday', 'admin-page-framework' ),
5 => __( 'Friday', 'admin-page-framework' ),
6 => __( 'Saturday', 'admin-page-framework' ),
),
'default' => 4,
),
array(
'title' => __( 'Check Box', 'admin-page-framework' ),
'field_id' => 'checkbox',
'type' => 'checkbox',
'show_debug_info' => false,
'label' => __( 'This is a check box.', 'admin-page-framework' ),
),
array(
'title' => __( 'Image', 'admin-page-framework' ),
'field_id' => 'image',
'type' => 'image',
'show_debug_info' => false,
),
array(
'title' => __( 'Color', 'admin-page-framework' ),
'field_id' => 'color',
'type' => 'color',
'show_debug_info' => false,
),
array(
'field_id' => 'date',
'type' => 'date',
'title' => __( 'Date', 'admin-page-framework' ),
'date_format' => 'mm/dd',
'show_debug_info' => false,
),
array(
'field_id' => '_submit',
'type' => 'submit',
'value' => __( 'Submit', 'admin-page-framework' ),
'show_debug_info' => false,
),
);
}
/**
* Get the output.
* Here you can modify the output.
* @return string
*/
public function content( $sFormOutput ) {
return "<h3>" . __( 'Form', 'admin-page-framework' ) . "</h3>"
. $sFormOutput;
}
/**
* Do form submission handling.
* @return void
*/
public function validate( $aInputs ) {
$_bHasError = false;
$_aErrors = array();
if ( ! is_numeric( $aInputs[ 'text' ] ) ) {
$_aErrors[ 'text' ] = 'Type a numeric value.';
$_bHasError = true;
}
if ( empty( $aInputs[ 'textarea' ] ) ) {
$_aErrors[ 'textarea' ] = 'Please type something.';
$_bHasError = true;
}
$this->setFieldErrors( $_aErrors );
if ( $_bHasError ) {
$this->setSubmitNotice( 'Please correct some information you entered.', 'error' );
$this->aFormData = $aInputs;
} else {
$this->setSubmitNotice( 'Form has been submitted', 'update' );
}
}
}
new AdminPageFramework_FrontendFormBeta;
<?php
/**
* Provides an abstract base class for front-end forms.
*
* Usage
*
* 1. In your desired location, print an output of your custom WordPress filter.
* ```
* echo apply_filters( 'your_custom_filter', '' );
* ```
*
* Or, do an action.
* ```
* do_action( 'your_custom_action' );
* ```
*
* 2. Extend this class.
* ```
* class YourFrontendFormClass extends AdminPageFramework_FrontendFormBeta_Base {
*
* }
* ```
*
* 3. Set your filter or action in the `$sHookName` property.
* ```
* public $sHookName = 'your_custom_filter';
* public $sHookType = 'filter';
* ```
* Or
* ```
* public $sHookName = 'your_custom_action';
* public $sHookType = 'action';
* ```
*
* 4. In the `getFields()` method, define field definition arrays to return.
* ```
* public function getFields() {
* return array(
* array(
* 'title' => __( 'Text Input', 'admin-page-framework' ),
* 'field_id' => 'text',
* 'type' => 'text',
* ),
* array(
* 'field_id' => '_submit',
* 'type' => 'submit',
* 'value' => __( 'Submit', 'admin-page-framework' ),
* ),
* );
* }
* ```
* Similarly, define section in the `getSections()` method.
*
* 5. In the `getStyles()` method, define your CSS rules or set paths for stylesheets.
* ```
* public function getStyles() {
* return array(
* dirname( __FILE__ ) . '/css/style.css',
* );
* }
* Similarly, set script paths in the `getScripts()` method.
*
* ```
* 6. Instantiate the class.
* ```
* new YourFrontendFormClass;
* ```
*
* When you include this class with your generated framework files,
* make sure the used class names here with AdminPageFramework are all prefixed with the one you set in Generator.
*
* @remark Required Admin Page Framework 3.8.11 or above.
*/
abstract class AdminPageFramework_FrontendFormBeta_Base {
/* Override these properties from here */
public $sHookName = ''; // e.g `the_content`
public $iPriority = 11;
public $sHookType = 'filter'; // or action
/* to here */
/**
* Stores the form field values.
*/
public $aFormData = array();
public $aFieldErrors = array();
public $aSubmitNotices = array();
public $oForm;
private $___sNonceActionName = 'admin-page-framework-frontend_form';
/**
* Sets up hooks.
*/
public function __construct() {
add_action( 'wp', array( $this, '_replyToDetermineWhetherToLoad' ) );
$this->construct();
}
/**
* Determines whether to load the form.
* @callback action wp
* @return void
*/
public function _replyToDetermineWhetherToLoad() {
if ( ! class_exists( 'AdminPageFramework_Message' ) ) {
return;
}
if ( ! $this->isInThePage() ) {
return;
}
$this->___setHooks();
$this->___setProperties();
/**
* Let the user do necessary routines before setting up the form such as showing a notification message based on the form submission etc.
*/
$this->load();
$this->___setUp();
if ( $this->___isFormSubmitted() ) {
$this->___handleFormSubmission();
}
}
/**
* @return boolean
*/
private function ___isFormSubmitted() {
if ( ! isset( $_REQUEST[ $this->___sNonceActionName ] ) ) {
return false;
}
if ( ! wp_verify_nonce( $_REQUEST[ $this->___sNonceActionName ], $this->___sNonceActionName ) ) {
return false;
}
return true;
}
private function ___handleFormSubmission() {
$aInputs = $_REQUEST;
unset(
$aInputs[ '_admin-page-framework_frontend_form' ],
$aInputs[ '_wp_http_referer' ],
$aInputs[ '__submit' ],
$aInputs[ '__repeatable_elements_' . $this->oForm->aArguments[ 'structure_type' ] ]
);
$this->validate( $aInputs );
}
private function ___setProperties() {
$this->oForm = new AdminPageFramework_FormBeta_frontend(
array( // form object arguments
'caller_id' => get_class( $this ),
'structure_type' => 'frontend',
'action_hook_form_registration' => 'wp_enqueue_scripts',
// custom arguments
'caller_object' => $this,
),
array( // callbacks
'saved_data' => array( $this, 'replyToSetFormData' ),
)
);
}
private function ___setHooks() {
if ( ! $this->sHookName ) {
return;
}
$_aFunctionNames = array(
'filter' => 'add_filter',
'action' => 'add_action',
);
$_sFunctionName = isset( $_aFunctionNames[ $this->sHookType ] )
? $_aFunctionNames[ $this->sHookType ]
: $_aFunctionNames[ 'filter' ];
call_user_func_array(
$_sFunctionName,
array(
$this->sHookName,
array( $this, '_replyToGetForm' ),
$this->iPriority
)
);
}
/**
* @callback filter|action
*/
public function _replyToGetForm( $sContent ) {
if ( 'filter' === $this->sHookType ) {
return $sContent . $this->___getFormWrapped();
}
echo $this->___getFormWrapped();
}
/**
* @return string
*/
private function ___getFormWrapped() {
return $this->___getSubmitNotices()
. "<form class='admin-page-framework-frontend-form' method='post'>"
. $this->___getHiddenFields()
. $this->___getFormOutput()
. "</form>";
}
/**
* @return string
*/
private function ___getSubmitNotices() {
$_sOutput = '';
if ( empty( $this->aSubmitNotices ) ) {
return $_sOutput;
}
foreach( $this->aSubmitNotices as $_aNotice ) {
$_sOutput .= "<div class='submit-notice " . esc_attr( $_aNotice[ 1 ] ) . "'>"
. "<p>" . $_aNotice[ 0 ] . "</p>"
. "</div>";
}
return $_sOutput;
}
private function ___getFormOutput() {
return $this->content( $this->oForm->get() );
}
private function ___getHiddenFields() {
return wp_nonce_field(
$this->___sNonceActionName, // nonce action name
$this->___sNonceActionName, // input name
true, // embed referrer
false // echo
);
}
/**
* Sets up form elements.
*/
private function ___setUp() {
$this->___setResourcesByType( ( array ) $this->getStyles(), 'style' );
$this->___setResourcesByType( ( array ) $this->getScripts(), 'script' );
$_aSections = $this->getSections();
$_aSections = is_array( $_aSections ) ? $_aSections : array();
foreach( $_aSections as $_asSection ) {
$this->oForm->addSection( $_asSection );
}
$_aFields = $this->getFields();
$_aFields = is_array( $_aFields ) ? $_aFields : array();
foreach( $_aFields as $_asField ) {
$this->oForm->addField( $_asField );
}
}
private function ___setResourcesByType( array $aItems, $sType ) {
foreach( $aItems as $_iItem ) {
if ( ! $_iItem ) {
continue;
}
if ( $this->___isURL( $_iItem ) || file_exists( $_iItem ) ) {
$_sKey = 'style' === $sType ? 'src_styles' : 'src_scripts';
$this->oForm->addResource( $_sKey, $_iItem );
}
$_sKey = 'style' === $sType ? 'internal_styles' : 'internal_scripts';
$this->oForm->addResource( $_sKey, $_iItem );
}
}
/**
* @return boolean
*/
private function ___isURL( $sItem ) {
return ( ! filter_var( $sItem, FILTER_VALIDATE_URL ) === false );
}
/**
* @callback form_filter saved_data
* @return array
*/
public function replyToSetFormData( $aData ) {
return $this->aFormData + $aData;
}
/**
* Sets a given field errors.
* @return void
*/
public function setFieldErrors( $aErrors ) {
$this->aFieldErrors = $aErrors;
}
/**
* Sets the given message to be displayed in the next page load.
*
* This is used to inform users about the submitted input data, such as "Updated successfully." or "Problem occurred." etc.
* and normally used in validation callback methods.
*
* <h4>Example</h4>
* `
* if ( ! $bVerified ) {
* $this->setFieldErrors( $aErrors );
* $this->setSubmitNotice( 'There was an error in your input.' );
* return $aOldPageOptions;
* }
* `
* @access public
* @param string $sMessage the text message to be displayed.
* @param string $sType (optional) the type of the message, either "error" or "updated" is used.
* @param array $asAttributes (optional) the tag attribute array applied to the message container HTML element. If a string is given, it is used as the ID attribute value.
* @param boolean $bOverride (optional) If true, only one message will be shown in the next page load. false: do not override when there is a message of the same id. true: override the previous one.
* @return void
*/
public function setSubmitNotice( $sMessage, $sType='error', $asAttributes=array(), $bOverride=true ) {
$_aNotice = func_get_args() + array( '', 'error', array(), false );
// Has a message?
if ( ! $_aNotice[ 0 ] ) {
return;
}
// Override?
if ( $_aNotice[ 3 ] ) {
$this->aSubmitNotices = $_aNotice;
return;
}
$this->aSubmitNotices[] = $_aNotice;
}
/** -----------------------------------------------------------------------------------
* Override the methods below.
*/
/**
* User constructor.
* @remark Override this method to do necessary set-ups.
*/
public function construct() {}
/**
* Gets called when the page loads. Do some necessary set-ups here.
*/
public function load() {}
/**
* @remark Override this method in an extended class.
* @return boolean
*/
public function isInThePage() {
return true;
}
/**
* Return the form output.
* @return string
*/
public function content( $sFormOutput ) {
return $sFormOutput;
}
/**
* Retrieves form section definition arrays.
* @return array
*/
public function getSections() {
return array();
}
/**
* Retrieves form field definition arrays.
* @return array
*/
public function getFields() {
return array();
}
/**
* @return array
*/
public function validate( $aInputs ) {
return array();
}
/**
* Return internal CSS rules.
* @return array
* ```
* return array(
* dirname( __FILE__ ) . '/css/style.css',
* dirname( __FILE__ ) . '/css/style2.css', // path
* plugins_url( '/asset/css/style.css' ), // url
* '.form-field { max-width: 100; }', // text rules
* );
* ```
*/
public function getStyles() {
return array();
}
/**
* Return internal JavaSript script.
* @return array
* ```
* return array(
* dirname( __FILE__ ) . '/js/script.js',
* dirname( __FILE__ ) . '/js/script2.js', // path
* plugins_url( '/asset/js/script.js' ), // url
* 'jQuery( '.admin-page-framework-section-tabs-contents' ).createTabs(); ', // text script
* );
* ```
*/
public function getScripts() {
return array();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment