Skip to content
This repository has been archived by the owner on Feb 1, 2019. It is now read-only.

Commit

Permalink
Merge pull request #15 from gboone/fieldset-fix
Browse files Browse the repository at this point in the history
Fieldset fix
  • Loading branch information
Scotchester committed Aug 27, 2014
2 parents 4c57025 + 2384373 commit 650dfac
Show file tree
Hide file tree
Showing 10 changed files with 739 additions and 561 deletions.
560 changes: 101 additions & 459 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion cms-toolkit.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
available throughout the application and make building complex functionality
in WordPress a little easier.
Version: 1.1.1
Version: 1.2
Author: Greg Boone, Aman Kaur, Matthew Duran
Author URI: http://github.com/cfpb/
License: Public Domain work of the Federal Government
Expand Down
32 changes: 32 additions & 0 deletions faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## FAQ

1. Why do I have to make a child plugin for this to do anything?

The short answer is we wanted to build this plugin in as robust a way as
possible to give you the tools to do what you want with it. There are other
plugins that give you a GUI for post type and taxonomy registration but this
plugin takes a different approach to abstracting those repetitivie processes
away. Want to build an options screen on top of this plugin to do post type
registration? Go for it! However you end up using it we hope you have fun.

2. Why all the namespacing?

We wanted to make the undelying code as readable as possible and thought there
might be a world where a WordPress install has two classes called `MetaBox`, for
example. Since the tools here are all generic methods, the namespaces add a
little more insurance from naming collisions. This way when you're writing a
child plugin you know you're always getting `CFPB`'s `MetaBox` and not some
other vendor's.

3. Why a plugin and not a composer library?

Honestly, it could probably be both. We released it as a plugin because the
project started as a plugin first and it was just easier to continue developing
it that way.

4. I'm getting this error: `The cms-toolkit plugin requires PHP version 5.3 or
higher...`, what's up with that?

This plugin requires several features found only in PHP version 5.3 or higher.
If you really want to use this plugin either upgrade your PHP to at least 5.3.0
or contact your system administrator and request it be updated.
338 changes: 338 additions & 0 deletions inc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
# Taxonomies, Post Types, Meta Boxes, and Capabilities

## Table Of Contents

1. [Simplified taxonomy registration](#simplified-taxonomy-registration)
1. [Simplified post type registration](#simplified-post-type-registration)
1. [MetaBox\Models: Register a self-validating meta box](#metaboxmodels-register-a-self-validating-meta-box)
1. [Capabilities](#capabilities)

## Simplified taxonomy registration

_Namespace `\CFPB\Utils\` Class: Taxonomy Filename: inc/taxonmies.php_

The function `build_taxonomies` is a helper function that reduces the amount of
repeating yourself required to register multiple taxonomies. It takes several
parameters and calls `register_taxonomy`. To actually register taxonomies, you
should hook your `build_taxonomies` call into `init` and flush rewrite rules if
necessary. Example:

```php
<?php

$T = new \CFPB\Utils\Taxonomy();
function register_my_taxonomy() {
$T->build_taxonomy(
'Sub Category',
'Sub Categories',
'sub_category',
'custom_post_type'
);
}
?>
```

In this example we create a taxonomy for the 'custom_post_type's called Sub
Category. You still need to hook `register_my_taxonomy` into `init`.

Also contained here is `remove_post_term` which can be used to remove a term by
ID or slug from a post.

## Simplified post type registration

_Namespace: `\CFPB\Utils\` Class: `PostType` Filename: inc/post-types.php_

Post-types.php contains a single class for post type registration with two
methods: `build_post_type` and `maybe_flush_rewrite_rules`. To create a post
type, simply instantiate the class with a singlular ($name) and plural
($plural) version of the name, a slug, an optional prefix string, and
arguments array then pass these to `build_post_type`. Inline documentation
elaborates this more clearly.

Finally, this class includes a method for flushing the rewrite rules only if
needed. Rewrite rules are stored in a single database table and are cached by
WordPress to help map URLs to the proper resource. Flushing them, especially on
a website with many custom rules, is expensive, but must be done in order for
custom post type archives and permalinks to begin working. This method flushes
checks the cached rewrite object and only flushes if the custom post type's url
string is absent. It is currently written only to support custom post types.

## MetaBox\Models: Register a self-validating meta box

_Namespace: `\CFPB\Utils\MetaBox` Class: Models Filename: inc/meta-box-models.php_

WordPress supports the adding of custom meta boxes on post editing screens, and
for now is limited only to those screens. The meta-box-models.php file contains
a class called `Models` which can be extended to create new MetaBoxes and have
them register automatically. This shortcuts the traditional route to meta box
construction an reduces the amount of repeating yourself required to make
multiple boxes on the same site. Creating a new meta box is as simple as:

```php
<?php
// metabox.php
namespace testNamespace;
class TestMetaBox extends \CFPB\Utils\MetaBox\Models {
public $title = 'Meta Box';
public $slug = 'meta_box';
public $post_type = 'post';
public $context = 'side';
public $fields = array(
'field_one' => array(
'title' => 'This is a field',
'slug' => 'field_one',
'type' =>'text_area',
'params' => array(
'cols' => 27,
),
'placeholder' => 'Enter text',
'howto' => 'Type some text',
'meta_key' => 'field_one',
),
'field_two' => array(
'slug' => 'field_two',
'title' => 'This is another field',
'type' => 'number',
'params' => array(),
'placeholder' => '0-100',
'howto' => 'Type a number',
'meta_key' => 'category',
),
);

function __construct() { parent::__construct(); } }
?>
```

Then hook your functions into your plugin activation. We recommend using three
functions within a class to do this: one function to call `generate` out of your
meta box class, another to call `validate_and_save`, and a third to add those
functions to their appropriate actions. Finally, hook the third function into
plugins_loaded.

```php
<?php
// plugin.php
namespace testNamespace;
class Base {
function hook_the_things() {
require_once( 'metabox.php');

add_action( 'save_post', array( '\testNamespace\TestMetaBox', 'do_the_saves' ) );
add_action( 'add_meta_boxes', array('\testNamespace', 'add_the_box' ) );
}

function add_the_box() {
$TestMetaBox = new \testNamespace\TestMetaBox();
$TestMetaBox->generate();
}

function do_the_saves( $post_id ) {
$post = get_post( $post_id );
$TestMetaBox = new \testNamespace\TestMetaBox();
if ( in_array( $post->post_type, $TestMetaBox->post_type ) ) {
$TestMetaBox->validate_and_save($post_id);
}
}
}
add_action( 'plugins_loaded', array( '\testNamespace\Base', 'hook_the_things' ) );
?>
```

The class has a few key parts, the public variables $title, $slug,
$post_type, $context and $fields. The last is an array containing arrays for
each html element of the box you want to generate. In the example
above we make one `<textarea>` field 27 columns wide targeted at the
'`field_one`' meta key and one `<input type="number">` field. Both of these will
go into a meta box on the side of 'post' editing screens with the title Meta Box.

Once you have the class, you need to hook it's `generate` method into
`add_meta_boxes` in order for it to show in WordPress. See the example above for
how to do this.

This class also contains methods for validating and saving this form, too. Lines
106-148 handle form data. To use these validators, just hook `validate_and_save`
into `save_post` as illustrated above.

Because all of these functions are contained in classes you are extending, you
can overwrite them if needed. Just declare a function with the same name as the
one in the parent class and WordPress will use yours instead of ours. If you
want to still run the parent's version, you can always call `parent::
overwritten_function_name()`. In certain cases you can also fully replace a
class from the cms-toolkit by injecting a new dependency. See the unit tests for
an example of how to do this.

### Fields

A meta box class can accept many different field types that correspond to valid
HTML elements. Each field array should contain the following keys: 'slug',
'title', 'type', 'params', 'placeholder', 'howto', and 'meta_key'. It can also
contain 'class', which assigns a class to a div wrapping the header and fields.
With the exception of 'params' these are all strings. A field array like the
following:

```php
<?php
'checkbox' => array(
'title' => 'Checkbox',
'slug' => 'checkbox',
'label' => 'A Checkbox',
'class' => 'some-class',
'type' => 'boolean',
'params' => array(),
'placeholder' => '',
'howto' => 'Check the box',
'meta_key' => 'boolean_one',
),
?>
```

Will generate the following HTML in a meta box:

```HTML
<div class="some-class">
<h4>Checkbox</h4>
<p>
<p>
<label for="checkbox">A Checkbox</label>
<input id="checkbox" name="boolean_one" value="" type="checkbox">Checkbox</input>
</p>
<p class="howto">Check the box</p>
</p>
</div>
```

The IDs and classes
correspond to IDs and classes used in the WordPress admin. Changing the value of
'type' will modify the type of form field generated. Possible values are listed
below. Check the unit tests for examples of how to use each type.

* `text_area` generates a text area meta box.
* `number` generates an input field with the number type, optionally add a
'max_num' key to the params array to limit the length of input. For example:
`'param' => array( 'max_length' => 2),`
* `text` generates a standard input field * `boolean` an input field with
the 'checkbox' type * `radio` two input fields with values 'true' and 'false'
(this may change in the future)
* `email` an input with the 'email' type
* `url` an input with the 'url' type
* `link` two inputs, one with the `url` type and another with `text`, validates
as an array like `array(0 => 'url', 1 => 'text')`;
* `date` generates a trio of fields: a dropdown for months and two
input fields for day and year
* `select` generates a `<select>` field with options specified in the 'params'
array. For example `'param' => array( 'one', 'two', 'three',),`
* `mutliselect` is identical to `select` except that it
passes the 'multiple' attribute, generating a multiselect box styled with
[multiselect.js](http://loudev.com)
* `taxonomyselect` generates a `<select>`
field with options pulled from the terms attached to the taxonomy specified in
`meta_key`
* `nonce` generates a WordPress Nonce field using 'slug' for the ID
* `hidden` generates a hidden field with a value you can pass in 'params'
* `post_select` generates a drop down menu of all posts. The array passed to
'params' will be passed to `get_posts` and [you can use all the
keys](http://codex.wordpress.org/Template_Tags/get_posts).
* `fieldset` to make a set of fields that affect the same meta key ([see below](#fieldsets))

__Note:__ invalid 'type' values will generate nothing and cause validation errors
and invalid values for `$post_type` or `$context` will generate `WP_Error`s

### Fieldsets

Fieldsets are groups of fields that display together and save with similar (
though not identical) meta keys. As an example, say you are making an address
book and want a way to save a phone number and a description to the `phone` key
. You'll need two fields, one text field limited to 10 characters and another
text_area field limited to 40 characters. The example below will save the
number and description to meta keys `phone_num` and `phone_desc`.

```php
<?php
$this->fields = array(
'phone' => array(
'title' => 'Phone number',
'slug' => 'phone',
'type' => 'fieldset',
'fields' => array(
array(
'type' => 'text',
'max_length' => 11,
'label' => 'Number',
'meta_key' => 'number',
),
array(
'type' => 'text',
'max_length' => 40,
'label' => 'Description',
'meta_key' => 'desc',
),
),
'meta_key' => 'phone',
'howto' => '',
),
);
?>
```

That form data will be saved to the `phone_number` and `phone_desc` custom-field keys like this:
```php
<?php
$phone_number = array( '5555555', );
$phone_desc = array( 'Description of the phone number', );
?>
```

### Formsets

Formsets is a feature that allows you to repeat a set of fields (listed above)
up to a fixed maximum value. This is slightly different from how Django thinks
about formsets.

As an example of how to use them, think about this user story: As a user, I
want to be able to add at least one but no more than 10 related links to any given
post so that I can show readers a predictable amount of relative content.

To do this, we could either create 10 link fields within a single meta box's
'fields' array ( with meta keys like `field_1`, `field_2`, `field_3`, and so on, or
we could make a formset with an initial number of forms set to 1 and the maximum
set to 10. That code looks like this:

```php
<?php
$fields = array(
'related_link' => array(
'slug' => 'related_link',
'type' => link,
'params' => array(
'init_num_forms' => 1,
'max_num_forms' => 10,
),
'meta_key' => 'related_link',
'howto' => 'Tell the people how to use this',
),
);
?>
```

Formsets are currently supported by the following field types:

- `link` (as of 1.1)

## Capabilities

The least developed feature of this plugin is the capability management
functions defined in capabilities.php. By 'least developed' we mean that it
could be more useful. This class removes the ability to edit the administrator
role and the ability for editors to promote users beyond their current level.
That is, _if_ an editor can modify user permissions and promote users (which
would need to be done separately), they can only do so for non-administrators
and cannot promote anyone to administrator.

If the [Review And Move to Production (RAMP)
plugin](http://crowdfavorite.com/ramp/) is active, this class will also make
that plugin available to editors but not authors. By default RAMP is made
available to any user with the ability to edit posts.

In the future we may see a world where this class can be used by feature plugins
to create meta capabilities for individual post types but we are not there yet.
Loading

0 comments on commit 650dfac

Please sign in to comment.