Skip to content

Commit

Permalink
see changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
Hube2 committed Jun 8, 2019
1 parent e2ca9d0 commit aaf43c9
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 219 deletions.
47 changes: 6 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
# ACF User Role Field Setting

***This plugin requires Advanced Custom Fields (ACF) Version >= 5.***
This plugin will not provide any functionality if ACF 5 is not installed and active. This plugin does
not support ACF4 or before as the filters used were not available before ACF5.
***This plugin requires Advanced Custom Fields (ACF) Version >= 5.5.0
This plugin will not provide any functionality if ACF 5 is not installed and active.

This plugin adds a field setting to all field types that allows for the selection of WP User Roles
that should be allowed to manage the value of the field. Fields that the current user does not have
permission to edit are removed from the field group when it's fields are loaded.

## Additional Security for All ACF Fields

***Please note that this plugin does not hide the fields, it completely removes the fields!*** However,
this removal does not effect any values that are already saved in the field. This plugin does not effect the operation of any of the other ACF function like get_field(), the_field(), get_fields(), get_field_ojects(), etc. If you find any of the template functions are being effected by this plugin please
[open a new issue](https://github.com/Hube2/acf-user-role-field-setting/issues).

***Why remove fields?*** Using an ACF load_field filter it is possible disable a field or to make a field
read only. It is also possible to add CSS in the admin head to hide a field base on the user. However,
this is not a secure way to keep people that should not be allowed to edit fields from editing them.
Anyone with limited HTML knowledge can easily inspect and alter the HTML and CSS for a page and make
the fields visible and editable. If someone is not supposed to be able to modify a value then that value
should not be present on the page in the first place. This is the only secure way to ensure that it
cannot be edited.

***$_POST['acf'] Input Checking*** All fields submitted are checked agains the user role field
setting. Any submitted value that a user should not be able to edit is removed from the submitted
values. This is done before acf saves any values. Why? This prevents the possibility of someone
modifying a forms HTML to add input fields that they are not supposed to be able to edit. A field
that a user should not be able to edit could only be submitted by aomeone attempting to hack the
form.

***Caution:*** It is possible to set a field so that it can never be edited by anyone. This can be done
in several ways. The first example is easy, simply do not select any user role that can edit the field.
The second example is less obvious. If you have a field that appears on "Posts" and you set this field
Expand All @@ -38,16 +16,16 @@ a field could be useful on a front end form where you want extra field for subsc
available to visitors that are not logged in.

### Exclude Field Types
Added in version 1.1.0

The Tab and Clone fields have been excluded from having the user role setting added to them.

Most of the time it would not make sense for a tab field, unless all of the fields in the tab were set
the same, in other words, removing a tab should remove all the fields in that tab. That's not something
that I can do at this point.
that I can do. You would need to enable the tab field and set the user role for the tab and all fields that should appear in the tab.

I'm not sure about the clone field, I haven't worked with it much. You can test it out if you want.
I'm not sure about the clone field, I haven't worked with it much. You can test it out if you want and let me know what happens.

I also added a filter so that you can adjust the types of fields that are excluded
There is a filter so that you can adjust the types of fields that are excluded
```
add_filter('acf/user_role_setting/exclude_field_types', 'user_role_setting_excluded_field_types');
function user_role_setting_excluded_field_types($exclude) {
Expand All @@ -67,17 +45,4 @@ function user_role_setting_excluded_field_types($exclude) {
return $exclude;
}
```

#### Automatic Updates
Github updater support has been removed. This plugin has been published to WordPress.Org here
https://wordpress.org/plugins/user-role-field-setting-for-acf/. If you are having problems updating please
try installing from there.

#### Remove Nag
You may notice that I've started adding a little nag to my plugins. It's just a box on some pages that lists my
plugins that you're using with a request do consider making a donation for using them. If you want to disable them
add the following filter to your functions.php file.
```
add_filter('remove_hube2_nag', '__return_true');
```
175 changes: 19 additions & 156 deletions acf-user-role-field-setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Plugin Name: ACF User Role Field Setting
Plugin URI: https://wordpress.org/plugins/user-role-field-setting-for-acf/
Description: Set user types that should be allowed to edit fields
Version: 2.1.15
Version: 3.0.0
Author: John A. Huebner II
Author URI: https://github.com/Hube2/
License: GPL
Expand All @@ -21,16 +21,16 @@ class acf_user_role_field_setting {
private $current_user = array();
private $exclude_field_types = array(
'tab' => 'tab',
'clone' => 'clone'
'clone' => 'clone',
'repeater' => 'repeater',
'group' => 'group',
'flexible_content' => 'flexible_content'
);
private $removed = array();

public function __construct() {
add_action('init', array($this, 'init'), 1);
add_action('acf/init', array($this, 'add_actions'));
add_action('acf/save_post', array($this, 'save_post'), -1);
add_action('after_setup_theme', array($this, 'after_setup_theme'));
//add_filter('acf/get_field_types', array($this, 'add_actions'), 20, 1);
} // end public function __construct

public function after_setup_theme() {
Expand All @@ -43,13 +43,15 @@ public function after_setup_theme() {
$acf_version = acf_get_setting('version');
if (version_compare($acf_version, '5.5.0', '>=')) {
add_filter('acf/prepare_field', array($this, 'prepare_field'), 99);
} else {
// if < 5.5.0 user the acf/get_fields hook to remove fields
add_filter('acf/get_fields', array($this, 'get_fields'), 20, 2);
}
} // end public function after_setup_theme

public function prepare_field($field) {
global $post;
$post_type = get_post_type($post->ID);
if ($post_type == 'acf-field' || $post_type == 'acf-field-group') {
return $field;
}
$return_field = false;
$exclude = apply_filters('acf/user_role_setting/exclude_field_types', $this->exclude_field_types);
if (in_array($field['type'], $exclude)) {
Expand All @@ -72,107 +74,33 @@ public function prepare_field($field) {
// or user roles is otherwise disabled for this field
$return_field = true;
}
//echo '<pre>'; print_r($field); echo '</pre>';
if ($return_field) {
return $field;
}
// [
preg_match('/(\[[^\]]+\])$/', $field['name'], $matches);
$name = $matches[1];
if (!in_array($name, $this->removed)) {
$this->removed[] = $name;
?><input type="hidden" name="acf_removed<?php echo $name; ?>" value="<?php
echo $field['name']; ?>" /><?php
if (!in_array($field['type'], array('tab', 'clone', 'repeater', 'group', 'flexible_content'))) {
// output a hidden field, this preserves repeater sub fields
?><input type="hidden" name="<?php echo $field['name']; ?>" value="<?php
echo $field['value']; ?>" /><?php
}
return false;
} // end public function prepare_field

public function save_post($post_id=false, $values=array()) {
if (!isset($_POST['acf'])) {
return;
}
$this->exclude_field_types = apply_filters('acf/user_role_setting/exclude_field_types', $this->exclude_field_types);
if (is_array($_POST['acf'])) {
$_POST['acf'] = $this->filter_post_values($_POST['acf']);
}
if (isset($_POST['acf_removed'])) {
$this->get_removed($post_id);
$_POST['acf'] = $this->array_merge_recursive_distinct($_POST['acf'], $_POST['acf_removed']);
}
} // end public function save_post

private function get_removed($post_id) {
foreach ($_POST['acf_removed'] as $field_key => $value) {
$_POST['acf_removed'][$field_key] = get_field($field_key, $post_id, false);
}
} // end private function get_removed

private function array_merge_recursive_distinct(array &$array1, array &$array2) {
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = $this->array_merge_recursive_distinct($merged[$key], $value);
} else {
// do not overwrite value in first array
if (!isset($merged[$key])) {
$merged[$key] = $value;
}
}
}
return $merged;
} // end private function array_merge_recursive_distinct

private function filter_post_values($input) {
// this is a recursive function the examinse all posted fields
// and removes any fields the a user is not supposed to have access to
$output = array();
foreach ($input as $index => $value) {
$keep = true;
if (substr($index, 0, 6) === 'field_') {
// check to see if this field can be edited
$field = get_field_object($index);
if (in_array($field['type'], $this->exclude_field_types)) {
$keep = true;
} else {
if (isset($field['user_roles'])) {
$keep = false;
if (!empty($field['user_roles']) && is_array($field['user_roles'])) {
foreach ($field['user_roles'] as $role) {
if ($role == 'all' || in_array($role, $this->current_user)) {
$keep = true;
// keepiing, no point in continuing to other rolese
break;
}
} // end foreach
} // end if settings is array
} // end if setting exists
} // end if excluded field type else
} // end if field_
if ($keep) {
if (is_array($value)) {
// recurse nested array
$output[$index] = $this->filter_post_values($value);
} else {
$output[$index] = $value;
}
} // end if keep
} // end foreach input
return $output;
} // end private function filter_post_values

public function init() {
$this->get_roles();
$this->current_user_roles();
} // end public function init

public function add_actions() {
$exclude = apply_filters('acf/user_role_setting/exclude_field_types', $this->exclude_field_types);
if (!function_exists('acf_get_setting')) {
return;
}
$acf_version = acf_get_setting('version');
if (version_compare($acf_version, '5.5.0', '<')) {
return;
}
$exclude = apply_filters('acf/user_role_setting/exclude_field_types', $this->exclude_field_types);
$sections = acf_get_field_types();
if ((version_compare($acf_version, '5.5.0', '<') || version_compare($acf_version, '5.6.0', '>=')) && version_compare($acf_version, '5.7.0', '<')) {
if (version_compare($acf_version, '5.6.0', '>=') && version_compare($acf_version, '5.7.0', '<')) {
foreach ($sections as $section) {
foreach ($section as $type => $label) {
if (!isset($exclude[$type])) {
Expand Down Expand Up @@ -215,70 +143,6 @@ private function get_roles() {
$this->choices = $choices;
} // end private function get_roles

public function get_fields($fields, $parent) {
global $post;
if (is_object($post) && isset($post->ID) &&
(get_post_type($post->ID) == 'acf-field-group') ||
(get_post_type($post->ID) == 'acf-field')) {
// do not alter when editing field or field group
return $fields;
}
$this->exclude_field_types = apply_filters('acf/user_role_setting/exclude_field_types', $this->exclude_field_types);
$fields = $this->check_fields($fields);
return $fields;
} // end public function get_fields

private function check_fields($fields) {
// recursive function
// see if field should be kept
$keep_fields = array();
if (is_array($fields) && count($fields)) {
foreach ($fields as $field) {
$keep = false;
if (in_array($field['type'], $this->exclude_field_types)) {
$keep = true;
} else {
if (isset($field['user_roles'])) {
if (!empty($field['user_roles']) && is_array($field['user_roles'])) {
foreach ($field['user_roles'] as $role) {
if ($role == 'all' || in_array($role, $this->current_user)) {
$keep = true;
// already keeping, no point in continuing to check
break;
}
}
}
} else {
// field setting is not set
// this field was created before this plugin was in use
// or this field is not effected, it could be a "layout"
// there is currently no way to add field settings to
// layouts in ACF
// assume 'all'
$keep = true;
}
} // end if excluded type else
if ($keep) {
$sub_fields = false;
if (isset($field['layouts'])) {
$sub_fields = 'layouts';
}
if (isset($field['sub_fields'])) {
$sub_fields = 'sub_fields';
}
if ($sub_fields) {
// rucurse sub fields
$field[$sub_fields] = $this->check_fields($field[$sub_fields]);
}
$keep_fields[] = $field;
}
} // end foreach field
} else {
return $fields;
}
return $keep_fields;
} // end private function check_fields

public function render_field_settings($field) {
$args = array(
'type' => 'checkbox',
Expand All @@ -294,7 +158,6 @@ public function render_field_settings($field) {
'layout' => 'horizontal'
);
acf_render_field_setting($field, $args, false);

} // end public function render_field_settings

} // end class acf_user_type_field_settings
Expand Down
33 changes: 11 additions & 22 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Contributors: Hube2
Tags: acf, advanced custom fields, user role, setting, security, multisite
Requires at least: 4.0
Tested up to: 5.1
Stable tag: 2.1.15
Tested up to: 5.2
Stable tag: 3.0.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Expand All @@ -19,18 +19,6 @@ This is an add on plugin for Advanced Custom Fields (ACF) Version 5.
This plugin adds a field setting to all field types so that user roles allowed to edit the field can
be selected. Only those roles selected for the field will be able to edit the field.

This adds additional security to fields. This plugin does not simply hide field, it removes them
completely from the field group. Using standard ACF filters is is possible to set many field types to
readonly or disabled. It is even possible by adding custom CSS to hide fields based on the current
user's role. However, this is not a secure way to prevent those that should not be allowed to edit
a field from editing them if they really want to. Anyone with limited HTML knowledge can easily instpect
the HTML of a page and alter the html and css to make the fields visible and editable. The only secure
way to prevent the fields from being edited is to not have them present in the form to begin with.

***$_POST Filtering:*** In addition to removing the fields from field groups so that they can not be
edited this plugin also checks submitted values to see if the current user is allowed to manage the
fields submitted before allowing ACF to save any values to the database.


== Installation ==

Expand Down Expand Up @@ -58,7 +46,9 @@ Most of the time it would not make sense for a tab field, unless all of the fiel

I'm not sure about the clone field, I haven't worked with it much. You can test it out if you want.

I also added a filter so that you can adjust the types of fields that are excluded. Here is an example
I have also removed support for repeater, group and flexible content fields because I cannot preserver the values of these fields if they are not editable in the ACF interface. Please note that I do not know what the effect of allowing user role settings on these fields will be.

There is a filter so that you can adjust the types of fields that are excluded. Here is an example
`
<?php

Expand All @@ -84,15 +74,14 @@ I also added a filter so that you can adjust the types of fields that are exclud
?>
`

== Remove Nag ==

If you would like to remove my little nag that appears on some admin pages add the following to your functions.php file
`
add_filter('remove_hube2_nag', '__return_true');
`

== Changelog ==

= 3.0.0 =
* removed support for ACF < Version 5.5.0
* removed user role setting from repeater, group and flexibe content fields (these can be re-enabled using available filter, however doing so may not be safe)
* corrected issue with repeater sub field reordering
* no longer filtering $_POST to corect repeater reordering issue

= 2.1.15 =
* added composer support
* removed donation nag
Expand Down

0 comments on commit aaf43c9

Please sign in to comment.