Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User forms #157

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5bf264c
user forms update
netaustin Mar 10, 2014
45251bf
better form context
netaustin Mar 10, 2014
6a3466d
file uploads
netaustin Mar 30, 2014
adf2ef8
plusher form library
netaustin Apr 1, 2014
0524b02
updating file upload field
netaustin Apr 3, 2014
44865e6
Fixed issue where element doesn't load form values
bcampeau Apr 14, 2014
69beec1
adding thorough tests for this new context
netaustin Jun 23, 2014
04770e3
polish PHPDoc a little
netaustin Jun 23, 2014
20bed08
fix missing fm-element class for textfields
alexisbellido Sep 2, 2014
2a82287
Merge pull request #209 from alleyinteractive/bug/missing_textfield_c…
alexisbellido Sep 2, 2014
814d762
merge from master
netaustin Dec 20, 2014
465a7fa
fix unit test which broke due to markup change. my kingdom for assert…
netaustin Dec 20, 2014
f29e689
init script and style hooks
netaustin Dec 24, 2014
6ed8399
user forms file updates
netaustin Jan 6, 2015
8e6c0b4
merge of master
netaustin Jan 8, 2015
f340dea
merge conflicts
netaustin Jan 8, 2015
871a63a
Merged latest master
bcampeau Jan 26, 2015
64ec597
Merge branch 'master' into user-forms
bcampeau Sep 9, 2015
3780ef9
merge conflict fix
netaustin Feb 19, 2016
7be4149
Merge branch 'master' into user-forms
netaustin Feb 19, 2016
a1d0ab1
merge conflicts
netaustin Feb 19, 2016
895f269
merge conflicts
netaustin Feb 19, 2016
09a315a
comments
netaustin Feb 19, 2016
f8e8de1
another merge conflict
netaustin Feb 19, 2016
6983c3e
test coverage
netaustin Feb 19, 2016
dfe96ef
test coverage; get uploads working properly in all contexts and confi…
netaustin Feb 20, 2016
6c6ae3c
Merge branch 'master' into user-forms
netaustin Feb 20, 2016
06d9718
groups conflict
netaustin Feb 20, 2016
e63a6b1
screw you, old versions of php
netaustin Feb 20, 2016
4d1b311
merge from master
netaustin May 3, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions css/fieldmanager.css
Expand Up @@ -96,6 +96,20 @@ div.fm-group-label-wrapper {
overflow: auto;
}

.fm-wrapper label {
display: block;
margin: 0 0 5px 0;
}

.fm-label-inline {
display: inline;
margin: 0 5px 0 0;
}

.fm-label-inline.fm-label-after {
margin: 0 0 0 5px;
}

.fm-group div.fm-group-label-wrapper h4 {
background-color: transparent;
background-image: none;
Expand Down
80 changes: 72 additions & 8 deletions fieldmanager.php
Expand Up @@ -99,12 +99,39 @@ function fieldmanager_load_file( $file ) {
/**
* Enqueue CSS and JS in the Dashboard.
*/
function fieldmanager_enqueue_scripts() {
function fieldmanager_enqueue_admin_scripts() {
wp_enqueue_script( 'fieldmanager_script', fieldmanager_get_baseurl() . 'js/fieldmanager.js', array( 'jquery' ), '1.0.7' );
wp_enqueue_style( 'fieldmanager_style', fieldmanager_get_baseurl() . 'css/fieldmanager.css', array(), '1.0.4' );
wp_enqueue_script( 'jquery-ui-sortable' );
}
add_action( 'admin_enqueue_scripts', 'fieldmanager_enqueue_scripts' );
add_action( 'admin_enqueue_scripts', 'fieldmanager_enqueue_admin_scripts' );

/**
* Enqueue default scripts for a frontend form
* @param string $uniqid
* @return void
*/
function fm_enqueue_form_scripts( $uniqid ) {
wp_enqueue_script( 'fieldmanager_script', fieldmanager_get_baseurl() . 'js/fieldmanager.js', array( 'jquery' ), '1.0.7' );
wp_enqueue_script( 'jquery-ui-sortable' );
_fieldmanager_registry( 'allow_scripts', true );
// Field constructors enqueue CSS and JS as needed
_fm_form_init_once( $uniqid );
}

/**
* Enqueue default styles for a frontend form
* @param string $uniqid
* @return void
*/
function fm_enqueue_form_styles( $uniqid ) {
wp_enqueue_style( 'fieldmanager_style', fieldmanager_get_baseurl() . 'css/fieldmanager.css', array(), '1.0.3' );
wp_enqueue_style( 'dashicons' );
_fieldmanager_registry( 'allow_styles', true );
// Field constructors enqueue CSS and JS as needed
_fm_form_init_once( $uniqid );
}


/**
* Tell Fieldmanager that it has a base URL somewhere other than the plugins URL.
Expand Down Expand Up @@ -162,19 +189,19 @@ function fieldmanager_get_template( $tpl_slug ) {
* @param string $plugin_dir The base URL to the directory with the script. Default none.
* @param bool $admin Unused.
*/
function fm_add_script( $handle, $path, $deps = array(), $ver = false, $in_footer = false, $data_object = '', $data = array(), $plugin_dir = '', $admin = true ) {
if ( !is_admin() ) {
function fm_add_script( $handle, $path, $deps = array(), $ver = false, $in_footer = false, $data_object = "", $data = array(), $plugin_dir = "", $admin = true ) {
if ( ! is_admin() && ! _fieldmanager_registry( 'allow_scripts' ) ) {
return;
}
if ( !$ver ) {
if ( ! $ver ) {
$ver = FM_GLOBAL_ASSET_VERSION;
}
if ( '' == $plugin_dir ) {
$plugin_dir = fieldmanager_get_baseurl(); // allow overrides for child plugins
}
$add_script = function() use ( $handle, $path, $deps, $ver, $in_footer, $data_object, $data, $plugin_dir ) {
wp_enqueue_script( $handle, $plugin_dir . $path, $deps, $ver, $in_footer );
if ( !empty( $data_object ) && !empty( $data ) ) {
if ( ! empty( $data_object ) && !empty( $data ) ) {
wp_localize_script( $handle, $data_object, $data );
}
};
Expand All @@ -198,7 +225,7 @@ function fm_add_script( $handle, $path, $deps = array(), $ver = false, $in_foote
* @param bool $admin Unused.
*/
function fm_add_style( $handle, $path, $deps = array(), $ver = false, $media = 'all', $admin = true ) {
if( !is_admin() ) {
if ( !is_admin() && !_fieldmanager_registry( 'allow_styles' ) ) {
return;
}
if ( !$ver ) {
Expand Down Expand Up @@ -282,7 +309,9 @@ function fm_get_context( $recalculate = false ) {
*/
function fm_calculate_context() {
// Safe to use at any point in the load process, and better than URL matching.
if ( is_admin() ) {
if ( ! empty( $_POST['fm-form-context'] ) || ! empty( $_GET['fm-form-context'] ) ) {
$calculated_context = array( 'form', sanitize_text_field( !empty( $_POST['fm-form-context'] ) ? $_POST['fm-form-context'] : $_GET['fm-form-context'] ) );
} else if ( is_admin() ) {
$script = substr( $_SERVER['PHP_SELF'], strrpos( $_SERVER['PHP_SELF'], '/' ) + 1 );

/*
Expand Down Expand Up @@ -516,6 +545,41 @@ function _fm_add_submenus() {
}
add_action( 'admin_menu', '_fm_add_submenus', 15 );

/**
* Template tag to output a form by unique ID
* @param string $uniqid
* @return void
*/
function fm_the_form( $uniqid ) {
_fm_form_init_once( $uniqid );
Fieldmanager_Context_Form::get_form( $uniqid )->render_page_form();
}

/**
* Template tag to get a form by unique ID
* @param string $uniqid
* @return void
*/
function fm_get_form( $uniqid ) {
_fm_form_init_once( $uniqid );
return Fieldmanager_Context_Form::get_form( $uniqid );
}
/**
* Load up a form
*/
function _fm_form_init_once( $uniqid ) {
static $loaded_forms;
if ( !$loaded_forms ) $loaded_forms = array();
if ( !empty( $loaded_forms[ $uniqid ] ) ) return;

// mark this form as loaded first in case the action hooks try to reload it
$loaded_forms[ $uniqid ] = true;

// make sure the form wasn't initialized by fm_trigger_context_action()
$ctx = fm_get_context();
if ( $ctx[1] !== $uniqid ) do_action( 'fm_form_' . $uniqid );
}

/**
* Sanitize multi-line text.
*
Expand Down
6 changes: 6 additions & 0 deletions js/validation/fieldmanager-validation.js
@@ -1,3 +1,5 @@
var fm_validation;

( function( $ ) {

fm_validation = {
Expand All @@ -16,6 +18,10 @@ fm_validation = {
submitHandler: function( form_element ) {
$(window).off( 'beforeunload.edit-post' );
form_element.submit();
},
// meant to be overriden by custom plugins.
errorPlacement: function(error, element) {
error.appendTo(element.parent());
}
}

Expand Down
28 changes: 21 additions & 7 deletions php/class-fieldmanager-field.php
Expand Up @@ -110,6 +110,12 @@ abstract class Fieldmanager_Field {
*/
public $sortable = FALSE;

/**
* @var string
* Set submit button for frontend forms
*/
public $submit_button_label = 'Save options';

/**
* @var string
* HTML element to use for label
Expand Down Expand Up @@ -182,6 +188,12 @@ abstract class Fieldmanager_Field {
*/
public $index = False;

/**
* @var boolean
* Should this field be visible?
*/
public $visible = true;

/**
* Save the fields to their own keys (only works in some contexts). Default
* is true.
Expand Down Expand Up @@ -380,6 +392,7 @@ public function set_options( $label, $options ) {
* @return string HTML for all form elements.
*/
public function element_markup( $values = array() ) {
if ( !$this->visible ) return '';
$values = $this->preload_alter_values( $values );
if ( $this->limit != 1 ) {
$max = max( $this->minimum_count, count( $values ) + $this->extra_elements );
Expand All @@ -392,7 +405,7 @@ public function element_markup( $values = array() ) {
$max = 1;
}

$classes = array( 'fm-wrapper', 'fm-' . $this->name . '-wrapper' );
$classes = array( 'fm-wrapper', 'form-group', 'fm-' . $this->name . '-wrapper' );
$fm_wrapper_attrs = array();
if ( $this->sortable ) {
$classes[] = 'fmjs-sortable';
Expand Down Expand Up @@ -683,11 +696,12 @@ public function is_group() {

/**
* Presaves all elements in what could be a set of them, dispatches to $this->presave()
* @input mixed[] $values
* @param mixed[] $values
* @param mixed[] $current_values
* @return mixed[] sanitized values
*/
public function presave_all( $values, $current_values ) {
if ( $this->limit == 1 && empty( $this->multiple ) ) {
if ( 1 === $this->limit && empty( $this->multiple ) ) {
$values = $this->presave_alter_values( array( $values ), array( $current_values ) );
if ( ! empty( $values ) ) {
$value = $this->presave( $values[0], $current_values );
Expand Down Expand Up @@ -834,7 +848,7 @@ protected function presave_alter_values( $values, $current_values = array() ) {
/**
* Presave function, which handles sanitization and validation
* @param mixed $value If a single field expects to manage an array, it must override presave()
* @return sanitized values.
* @return sanitized value.
*/
public function presave( $value, $current_value = array() ) {
// It's possible that some elements (Grid is one) would be arrays at
Expand Down Expand Up @@ -881,12 +895,12 @@ public function get_element_label( $classes = array() ) {
$classes[] = 'fm-label';
$classes[] = 'fm-label-' . $this->name;
if ( $this->inline_label ) {
$this->label_element = 'span';
$classes[] = 'fm-label-inline';
}
if ( $this->label_after_element ) {
$classes[] = 'fm-label-after';
}
// @TODO add inline and block to .label in stylesheet. Test this all.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this todo has been handled right?

return sprintf(
'<%s class="%s"><label for="%s">%s</label></%s>',
sanitize_key( $this->label_element ),
Expand Down Expand Up @@ -967,9 +981,9 @@ public function add_user_form( $title = '' ) {
* @see Fieldmanager_Context_Form
* @param string $uniqid a unique identifier for this form
*/
public function add_page_form( $uniqid ) {
public function add_form( $uniqid ) {
$this->require_base();
return new Fieldmanager_Context_Page( $uniqid, $this );
return Fieldmanager_Context_Form::get_form( $uniqid, $this );
}

/**
Expand Down
134 changes: 134 additions & 0 deletions php/class-fieldmanager-file.php
@@ -0,0 +1,134 @@
<?php
/**
* @package Fieldmanager
*/

/**
* Text field. A good basic implementation guide, too.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📋🍝

* @package Fieldmanager
*/
class Fieldmanager_File extends Fieldmanager_Field {

/**
* @var string
* Override field_class
*/
public $field_class = 'file';

/**
* @var string[]
* List of valid mime types for this upload
*/
public $valid_types = array(
'image/gif',
'image/jpeg',
'image/png',
);

/**
* @var callable
* How to save the file. Defaults to Fieldmanager_File->save_attachment()
* but you could override this to process a file or do something completely novel.
*/
public $save_function = null;

/**
* Override constructor to set default size.
* @param string $label
* @param array $options
*/
public function __construct( $label = '', $options = array() ) {

$this->save_function = array( $this, 'save_attachment' );

parent::__construct( $label, $options );
}

/**
* Presave, validates upload, moves file into place
* @param mixed[] $values
* @param mixed[] $current_values
* @return mixed[] sanitized values
*/
public function presave_all( $values, $current_values ) {
$ancestors = array();
foreach ( $this->get_form_tree() as $p ) {
$ancestors[] = $p->name;
}

$base_name = array_shift( $ancestors );
if ( ! empty( $_FILES[ $base_name ]['name'] ) ) {
$file_keys = array( 'name', 'type', 'tmp_name', 'error', 'size' );
foreach ( $file_keys as $key ) {
$property = $_FILES[ $base_name ][ $key ];
foreach ( $ancestors as $a ) {
if ( ! empty( $property[ $a ] ) ) {
$property = $property[ $a ];
}
}
if ( is_array( $property ) ) {
unset( $property['proto'] );
foreach ( $property as $i => $val ) {
$values[ $i ][ $key ] = $val;
}
} else {
$values[ $key ] = $property;
}
}
}
if ( 1 !== $this->limit ) {
ksort( $values );
}
return parent::presave_all( $values, $current_values );
}

/**
* Override presave to move file into place
* @param mixed $value If a single field expects to manage an array, it must override presave()
* @return sanitized values.
*/
public function presave( $value, $current_value = null ) {

if ( empty( $value['tmp_name'] ) ) {
if ( is_array( $value ) && !empty( $value['saved'] ) ) {
return intval( $value['saved'] );
}
return false; // no upload, stop processing
}

if ( !in_array( $value['type'], $this->valid_types ) ) {
$this->_failed_validation( 'This file is of an invalid type' );
}
return call_user_func_array( $this->save_function, array( $this->name, $value ) );
}

/**
* Return name for hidden form element
*/
public function get_form_saved_name() {
return $this->get_form_name() . '[saved]';
}

/**
* Save the uploaded file to the media library.
* @param string $filename name of the file on disk
* @param mixed[] $file_struct from $_FILES array
* @return int attachment ID
*/
public function save_attachment( $fieldname, $file_struct ) {
global $post;

require_once ABSPATH . 'wp-admin/includes/admin.php';

$post_id = 0;
if ( ! is_object( $post ) && ! empty( $post->ID ) ) {
$post_id = $post->ID;
}

$filename = sanitize_text_field( $file_struct['name'] );
$attach_id = media_handle_sideload( $file_struct, $post_id );

return $attach_id;
}

}