WP Manifestindependent plugin directory
manifest / forms / wenprise-forms

Wenprise Forms

Extend Nette Forms for WordPress use.

by WordPress 智库 · github.com/iwillhappy1314/wenprise-forms · website

6stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/iwillhappy1314/wenprise-forms/archive/refs/heads/master.zip

Readme

wenprise-forms

Extend Nette Forms for WordPress use.

Basic usage: Nette Form

Features

  • Supports both Composer library mode and WordPress plugin mode
  • Extends Nette Forms with WordPress-ready fields and UI widgets
  • Includes form validation, conditional display, datastore integration, and AJAX-friendly inputs

Requirements

  • PHP >=8.3
  • ext-json
  • nette/forms ^3.2
  • nette/utils ^4.1
  • WordPress >=6.0 (recommended)

Installation

Option A: Install as a Composer library

composer require wenprise/forms

Option B: Install as a WordPress plugin

  1. Clone or download this repository into wp-content/plugins/wenprise-forms.
  2. Run composer install inside the plugin directory.
  3. Activate Wenprise Forms in WordPress admin.

Runtime Modes

Composer library mode

  • Your project is responsible for loading Composer autoload.
  • Your code decides when and where to bootstrap the form objects.

WordPress plugin mode

  • Plugin entry file wenprise-forms.php loads vendor/autoload.php automatically.
  • Plugin bootstraps with new \Wenprise\Forms\Init();.

Quick Start (Minimal Working Example)

Forms has its own HTTP request handling flow. Instantiate it before head_sent, usually on init.

<?php
use Wenprise\Forms\Form;
use Wenprise\Forms\Helpers;
use Wenprise\Forms\Renders\DefaultFormRender;

add_action('init', function () {
    global $demo_form;

    $demo_form = Helpers::get_form();
    $demo_form->setRenderer(new DefaultFormRender('horizontal'));
    $demo_form->setMethod('POST');
    $demo_form->setAction(home_url('/'));
    $demo_form->addText('first_name', 'First Name')->setRequired();
    $demo_form->addSubmit('send', 'Save');
});

add_shortcode('wenprise_form_demo', function () {
    global $demo_form;

    if ($demo_form->isSuccess()) {
        $values = $demo_form->getValues();
        $first_name = $values->first_name;
        // Save or process your value here.
    }

    return (string) $demo_form;
});

Usage

Quick start

Forms includes its own HTTP request handling flow, so instantiate it before head_sent, usually in the init action.

add_action('init', function ()
{
    global $form;

    // Get forms object
    $form = Helpers::get_form();
});

Create a Form instance

use Wenprise\Forms\Form;

$form = new Form;

$form->setRenderer(new \Wenprise\Forms\Renders\DefaultFormRender('horizontal'));

// Set form method
$form->setMethod( 'POST' );
$form->setAction('https://www.example.com');

// Set form field
$form->addText('first_name', 'First Name');

// Set submit button
$form->addSubmit( 'send', 'Save' );

// Validate form and get form data
if ( $form->isSuccess() ) {

    $values = $form->getValues();

    $first_name = $values->first_name;

}   

Frontend Assets

Some fields depend on JavaScript/CSS widgets (for example: Slider, Chosen, Date Picker, Signature, Star Rating, Autocomplete, Chained Select).

  • Ensure assets are properly enqueued in your theme/plugin runtime.
  • Prefer testing one advanced field at a time when integrating into existing themes.

Security Notes

  • Always include CSRF protection for public forms ($form->addCsrf(...)).
  • For AJAX endpoints, validate nonce and capability before processing input.
  • Sanitize incoming data and escape output in templates.
  • Do not trust client-side validation alone; keep validation rules on the server side.

Set Required

$form->addText('first_name', 'First Name')
     ->setRequired();

Add rule

$form->addPassword('re_password', 'Password again:')
     ->addRule($form::EQUAL, 'Password mismatch', $form['password']);

Rule Documentation

Add field description

$form->addText('first_name', 'First Name')
     ->setOption('description', 'This is your first name.');

With Html

$form->addTextArea('description', Html::el('p')
    ->setHtml('This number remains hidden. <a href="...">Terms of service.</a>')
    );

Set condition display

$form->addColorPicker('first_name3', 'First Name')
     ->setHtmlAttribute('data-cond', '[name=first_name2] == 2');

Control width

setWidth() supports both Bootstrap-based renderers and TailwindGridFormRender.

$form->addText('first_name', 'First Name')
     ->setWidth(4);

You can also define responsive widths with breakpoint names:

$form->addText('first_name', 'First Name')
     ->setWidth(6, 'base')
     ->setWidth(3, 'md')
     ->setWidth(4, 'lg');

Or use the shorthand form:

$form->addText('first_name', 'First Name')
     ->setWidth(6, 3, 4);

The shorthand above means:

  • base = 6
  • md = 3
  • lg = 4

With DefaultFormRender('vertical') and InlineFormRender(), setWidth() is mapped to Bootstrap-style classes such as rs-col-md-4.

With TailwindGridFormRender('vertical'), setWidth() is mapped to grid span classes such as rs-grid-form__span-md-4.

DefaultFormRender('horizontal') keeps each field full width by design, so width settings are ignored there.

Repeater

Use addRepeater() to build repeatable field groups.

use Wenprise\Forms\Renders\GridFormRender;

$form = new \Wenprise\Forms\Form('contacts_form');
$form->setRenderer(new GridFormRender('vertical'));

$form->addRepeater('contacts', 'Contacts', function (\Wenprise\Forms\Container $row): void {
    $row->addText('name', 'Name')->setWidth(6, 3, 4);
    $row->addText('phone', 'Phone')->setWidth(6, 3, 4);
    $row->addText('email', 'Email')->setWidth(12, 6, 4);
}, 1, 5);

$form->addSubmit('send', 'Save');

addRepeater() arguments:

  • $name: repeater field name
  • $label: repeater label
  • $factory: callback used to define each row container
  • $copy_count: initial row count
  • $max_copies: optional maximum row count

Submitted values are returned as nested arrays and can be stored by the built-in datastore layer.

Multiple Submit Button

$form->addSubmit('save', 'Save');
$form->addSubmit('delete', 'Delete');

if ($form->isSuccess()) {
    if ($form['save']->isSubmittedBy()) {
        ....
    }

    if ($form['delete']->isSubmittedBy()) {
        ....
    }
}

Use html in label or description

$confirm = Html::el('span')->setHtml('I agree the <a href="#">Terms of service.</a>');

$form->addCheckbox('confirm', $confirm)->setOption('description', $confirm);

Set Datastore

$form->setDatastore(new \Wenprise\Forms\Datastores\PostMetaDatastore(1));

$form->save();

Custom Settings Page Example

Use OptionsDatastore to build a WordPress admin settings page quickly.

<?php
use Wenprise\Forms\Form;
use Wenprise\Forms\Datastores\OptionsDatastore;
use Wenprise\Forms\Renders\AdminFormRender;

add_action('admin_menu', function () {
    add_menu_page(
        'Wenprise Form Settings',
        'Form Settings',
        'manage_options',
        'wenprise-form-settings',
        'render_wenprise_form_settings_page'
    );
});

function render_wenprise_form_settings_page() {
    $form = new Form('wenprise_form_settings');
    $form->setRenderer(new AdminFormRender('vertical'));
    $form->setMethod('POST');

    $form->addText('wprs_company_name', 'Company Name')
        ->setDefaultValue(get_option('wprs_company_name', ''));

    $form->addText('wprs_support_email', 'Support Email')
        ->setRequired()
        ->addRule($form::EMAIL, 'Please enter a valid email address.')
        ->setDefaultValue(get_option('wprs_support_email', ''));

    $form->addCheckbox('wprs_enable_notifications', 'Enable Email Notifications')
        ->setDefaultValue((bool) get_option('wprs_enable_notifications', false));

    $form->addSubmit('save', 'Save Settings');

    $form->setDatastore(new OptionsDatastore());
    $form->save();

    echo '<div class="wrap"><h1>Wenprise Form Settings</h1>';
    $form->render();
    echo '</div>';
}

Tab Groups

Use tab groups to split long forms into multiple panels.

$form = new \Wenprise\Forms\Form();

$form->addTab('basic', 'Basic', true);
$form->addText('first_name', 'First Name');
$form->addText('email', 'Email');

$form->addTab('advanced', 'Advanced');
$form->addTextArea('notes', 'Notes');
$form->endTab();

// This submit button is outside tab groups.
$form->addSubmit('send', 'Save');

// Template context:
echo $form;

Tabs are rendered automatically through the existing render flow ($form->render() / echo $form), so no extra render method is required.

Submit buttons created inside tab groups are rendered outside tab panes automatically, so the save action stays visible.

You can also use underscore naming method:

$form->add_tab('basic', 'Basic', true);
echo $form;

Stepper Groups

Use stepper groups to render a progress bar with previous and next step actions.

$form = new \Wenprise\Forms\Form();

$form->addStep('campaign', 'Select master blaster campaign settings', true);
$form->addText('campaign_name', 'Campaign Name');
$form->endStep();

$form->addStep('ad_group', 'Create an ad group');
$form->addText('group_name', 'Group Name');
$form->endStep();

$form->addStep('ad', 'Create an ad');
$form->addTextArea('ad_content', 'Ad Content');
$form->endStep();

$form->addSubmit('send', 'Save');

echo $form;

You can also use underscore naming method:

$form->add_step('campaign', 'Campaign', true);

If you render inside a shortcode callback, return the form HTML instead of echoing:

add_shortcode('wenprise_form_demo', function () use ($form) {
    return (string) $form;
});

Fields

nonce field

$form->addCsrf('postform', 'Nonce invalid');

WordPress Tinymce editor

Settings

$form->addEditor('post_extra', 'Extra content', []);

Ajax uploader

$form->addAjaxUpload('photos', 'Photos', true, )
             ->setUrl(  admin_url( 'admin-ajax.php?action=upload' ) );

Uploader backend sample.

add_action('wp_ajax_upload', 'ajax_uploader');
add_action('wp_ajax_nopriv_upload', 'ajax_uploader');

function ajax_uploader()
{

    require_once(ABSPATH . 'wp-admin/includes/image.php');
    require_once(ABSPATH . 'wp-admin/includes/file.php');
    require_once(ABSPATH . 'wp-admin/includes/media.php');

    $attachment_id = media_handle_upload('file', 0);

    $thumb_url  = wp_get_attachment_thumb_url($attachment_id);
    $origin_url = wp_get_attachment_url($attachment_id);

    $thumb = get_post($attachment_id);

    $file_data = [
        'id'       => $attachment_id,
        'original' => $thumb->post_title,
        'size'     => $thumb->size,
        'state'    => 'SUCCESS',
        'title'    => $thumb->post_title,
        'type'     => $thumb->post_mime_type,
        'thumb'    => $thumb_url,
        'url'      => $origin_url,
    ];

    wp_send_json($file_data, 200);

    return false;
}

Slider input

Setting

$form->addSlider('price', 'Price', []);

Data Picker

Setting

$form->AddBirthdaypicker('_birthday', 'Date of Birth', [
    'format' => 'YYYY-MM-DD', 
    'template' => 'YYYY-MM-DD',
    'minYear' => '1900',
    'maxYear' => date("Y")
]);

Color Picker

Setting

$form->addColorPicker('color', 'Color', []);

Chosen

$choices = [
    'php'         => 'PHP',
    'javascript'  => 'JavaScript',
    'css'         => 'CSS',
    'java'        => 'Java',
];

$form->addChosen('category', 'Category', $choices);

Multi Chosen

$form->addMultiChosen('post_tags', 'Tags', $choices);

Signature Field

$options = [
   'width'      => '500',
   'height'     => '250',
   'border'     => '#999',
   'background' => '#f3f3f3',
];

$form->addSignature('first_name9', 'First Name', $options);

Star rating input

$options = [
    'displayOnly' => false,
    'showClear'   => false,
    'theme'       => 'krajee-svg',
    'step'        => 1,
    'min'         => 1,
    'max'         => 5,
];

$form->addStarRating('rating', 'Rating', $options);

Image Select

$options = [
    'light' => 'https://via.placeholder.com/64/EEEEEE/000000/?text=Light',
    'dark'  => 'https://via.placeholder.com/64/000000/FFFFFF/?text=Dark',
];

$form->addImagePicker('theme', 'Theme', $options);

Autocomplete Input

Source is an array or a url returns an array.

$form->addAutocomplete('name', 'Name')->setSource([
    [
        'value' => 'aaa',
        'data'  => 'AAA',
    ],
    [
        'value' => 'bbb',
        'data'  => 'BBB',
    ],
    [
        'value' => 'ccc',
        'data'  => 'CCC',
    ],
]);

If Source is an ajax url, the backend need return data as bellow.

suggestions: [
  { "value": "United Arab Emirates", "data": "AE" },
  { "value": "United Kingdom",       "data": "UK" },
  { "value": "United States",        "data": "US" }
]

Table input

$fields = [
    [
        'name'    => 'name',
        'display' => 'Product Name',
        'type'    => 'text',
    ],
    [
        'name'    => 'quantity',
        'display' => 'Quantity',
        'type'    => 'text',
    ],
    [
        'name'    => 'price',
        'display' => 'Unit Price',
        'type'    => 'text',
    ],
];

$values = [
    [
        'name'     => 'Macbook Pro',
        'quantity' => '1',
        'price'    => '8500',
    ],
    [
        'name'     => 'Pixel XL',
        'quantity' => '2',
        'price'    => '8500',
    ],
];


$form->addTableInput('table', 'Table', [], $fields)
     ->setDefaultValue($values);

Read the full README on GitHub →

Releases

TagPublished
v3.1.3 May 19, 2026
v3.1.2 May 19, 2026
v3.1.1 May 19, 2026
v3.1.0 May 18, 2026
3.0.4 May 14, 2026
3.0.3 May 14, 2026
3.0.2 May 14, 2026
3.0.1 May 14, 2026
3.0.0 May 14, 2026
v2.3.5 Aug 27, 2025
v2.3.4 Aug 27, 2025
v2.3.3 Dec 24, 2024
v2.3.2 Dec 16, 2024
v2.3.1 Dec 16, 2024
2.3.0 Sep 19, 2024
2.2.1 Aug 15, 2023
2.2.0 Aug 15, 2023
2.1.3 Aug 11, 2023
2.1.2 Aug 6, 2023
2.1.1 Aug 5, 2023
2.1.0 May 27, 2023
2.0.2 Feb 15, 2023
2.0.1 Feb 14, 2023
2.0.0 Feb 14, 2023
1.8.9 Dec 16, 2022

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.