5

I'm working on creating a settings system for a Wordpress installation, where super admins can disable some ACF Flexible Content layouts if they are not needed for the current website, without modifying the structure of the plugin.

I'm looking for a way to list all of the available ACF Flexible Content Layouts inside a new admin page.

After looking through the plugin's folders I have found the following code inside class-acf-field-flexible-content.php:

<script type="text-html" class="tmpl-popup"><?php 
    ?><div class="acf-fc-popup"><ul><?php foreach( $layouts as $layout ): 

        $atts = array(
            'href'            => '#',
            'data-layout'    => $layout['name'],
            'data-min'         => $layout['min'],
            'data-max'         => $layout['max'],
        );

        ?><li><a <?php acf_esc_attr_e( $atts ); ?>><?php echo $layout['label']; ?></a></li><?php 

    endforeach; ?></ul></div>
</script>

Which builds a list of available layouts for the client to use when creating new posts/pages.

Does anyone know of a simple way that I can build an array of ACF layouts in PHP that I can just display on a different page inside the WP Admin?

Sorry I don't really have any code to show, it's a bit of a unique request

Andy Holmes
  • 7,817
  • 10
  • 50
  • 83

2 Answers2

2

So what it seems like you are looking for is the below:

  • Customers website has a custom field that lists multiple options for them to select in order to alter the page layout (sort of like a template).
  • You want to be able to disable certain layouts for certain customers website without editing the custom field and/or the plugin code.

My answer for this would be to use the ACF User Role Plugin and limit the accounts that can see certain fields i.e. limit the fields to just your user role so only you have access to this (may have to create a new user role if they have the same role as you).

You would then want to add to your current custom fields another section for each layout that is a radio button that displays something like "Enable" and "Disable". And then you want to edit your layout fields to have conditional formatting where it checks the value of this field and if it is not equal to enable then it doesn't display.

  1. Install The Plugin
  2. Create a Radio Button field with the options enable/disable
  3. Limit the user role that can see the radio button field
  4. Add conditioning to the layouts to only display when the matching radio button is equal to enable
  5. Enable / Disable the layouts you don't want to appear

Additional Steps - You may need to create another user role for this method if they share the same role.

There is multiple plugins out there that you can use, just search create a new user role in wordpress. All you have to do is give it the exact same permissions as your role but it allows you to select a role that only applies to you so they cant see the field.

I hope this helps, if not I can clarify further.

OPTION 2:

  1. Create options page, limit its view to super admin
  2. Add checkboxes to options page for disable/enable
  3. Connect to the flexible fields dropdown list
  4. Run if statement to run the remove from array php on checkbox value = disable
  5. Update the flexible fields dropdown
  6. Finished

Since you are happy with the other answered I won't go through the code for this, it'd be similar just minus the json part and the success message.

Daniel Vickers
  • 1,054
  • 1
  • 12
  • 32
  • This is a smart approach, when I'm back in the office on Monday we'll give this a shot and i'll let you know the outcome. Cheers dude – Andy Holmes Jan 13 '18 at 00:06
  • Thanks @Daniel. I've created a layout where all fields are conditional, based on a radio button of Enabled / Disabled. I've created a Message field for if the radio button is set to disabled so if the layout is selected, an editor is only shown a message saying layout has been disabled.Problem is when you hide the radio button from the editor, it looks like the conditional work breaks as I cannot see the field that should be shown if it set to disabled (i've set the default value to disabled for this test). Thanks. – M. Ferguson Jan 15 '18 at 11:07
  • Hi, please can you provide me an image so I can inspect your fields just to make sure they are all configured correctly (sometimes we miss little things). – Daniel Vickers Jan 15 '18 at 13:13
  • You can download a zip of my images here: [link](https://wetransfer.com/downloads/599440f446528a5135a9dd8b9e1a2ecb20180115142002/7edf0a). You'll see that I have shown you the enable/disable radio button, my message that should appear if the radio button is disabled and all of the other fields that appear (this have all been set to if the Enabled radio button is set to Enabled – M. Ferguson Jan 15 '18 at 14:23
  • Hmm, it seems all of your fields are correctly configured. Is the value definetly being set for the enable/disable (it may be that you select it and update the page and it doesn't save. If this is the case increase the php max input vars). – Daniel Vickers Jan 15 '18 at 14:44
  • I upped the memory limit but it doesn't change anything. The fact that the user role completely removes the radio button would suggest that there is no way a field would be able conditionally match to the radio button as it doesn't exist. With that happening, the page would see that the conditional statement had not been met and then not show the field. – M. Ferguson Jan 15 '18 at 16:18
  • It doesn't really remove the field as such, it effectively looks up the user and hides it from others. It still stores the value in the page meta so it should still work. Without taking a look at the whole setup I am not sure how much further I can aid this. Would this at all be possible? My email is daniel@splendidapple.co.uk. – Daniel Vickers Jan 16 '18 at 10:17
  • Hey @DanielVickers, how soon is option two coming? – Andy Holmes Jan 17 '18 at 11:46
  • I will crank it out tonight, busy busy day today! – Daniel Vickers Jan 17 '18 at 11:48
  • No worries dude, Mark's option is pretty spot on with what's needed but I'm interested in seeing what your other option is :) – Andy Holmes Jan 17 '18 at 11:49
  • Option 2 done, pretty much the same minus the need to use JSON. Mark the other chaps as correct so he can get that sweet bounty! – Daniel Vickers Jan 17 '18 at 18:14
2

I have come up with a solution that on a settings page, will display all of the flexible content layouts names alongside a checkbox where you can un-select which layouts are removed from the list when you press the 'Add' button when you try to add a new layout.

As a brief introduction, I used acf-json to get all of the flexible layout names and then created another json file which holds the names of the layouts I wish to disable. I then run a function that looks at each layout name, checks if it isn't in the disabled list, and if it is, it will be removed.

To start with, I initialised the local JSON within ACF. To do this, follow the steps on https://www.advancedcustomfields.com/resources/local-json/.

I then created a new settings page within functions.php:

<?php
function add_theme_menu_item()
{
    add_options_page("Flexible Layouts", "Flexible Layouts", "manage_options", "flexible-layouts", "theme_settings_page", null, 99);
}

add_action("admin_menu", "add_theme_menu_item");
?>

Within the theme_settings_page function, you would need to decode the JSON for the flexible content group:

<?php
$jsonURL = get_template_directory_uri(). "/acf-json/flexible_content_group.json";
$contents = file_get_contents("{$jsonURL}");
$data =  json_decode($contents);
?>

I then did the same with for a JSON file that would hold all of the disabled fields (I'll explain how this file is created shortly):

<?php
$jsonDisabledFieldsURL = get_template_directory_uri(). "/acf-json/disabledFields.json";
$disabledFieldsContents = file_get_contents("{$jsonDisabledFieldsURL}");
$disabledFieldsData =  json_decode($disabledFieldsContents);
?>

I then pushed all of the layout names to an array $availableOptions:

<?php
$availableOptions = [];
foreach($data->fields as $field) {
    foreach($field->layouts as $layout) {
        array_push($availableOptions, $layout->name);
    }
}
?>

So we need a list of all fields with checkboxes and a submit button. With the submit button, I used ajax to post the disabled fields that I placed within an array:

<script type="text/javascript" src="<?php echo get_template_directory_uri(); ?>/js/lib/sweetalert2.js"></script>
<script>
    jQuery('.submit_json_handler').click(function(e){
        e.preventDefault();
        var self = jQuery(this);
        var array = [];

        jQuery('input:checkbox:not(:checked)').each(function() {
            array.push(jQuery(this).val());
        });

        jQuery.ajax({
            type:'POST',
            url:'<?php echo get_template_directory_uri(); ?>/acf-json/custom_json_handler.php',
            data: {
                'disabled_fields' : array
            },
            success:function(data){
                console.log(data);
                swal(
                  'Success!',
                  'The active layouts have now been updated.',
                  'success'
                )
            }
        });
    });
</script>

I used the SweetAlert2 plugin (https://sweetalert2.github.io/) in order to process a success message when the fields have modified.

My code for the custom_json_handler.php gets the posted disabled fields and pushes them into an array. The array is then encoded into the JSON file spoken about earlier (disabledFields.json):

<?php
    $disabledFields = $_POST['disabled_fields'];
    $disabledFieldsArray = [];

    try {   

        foreach($disabledFields as $field) {
            array_push($disabledFieldsArray, $field); 
        }

        $fp = fopen($_SERVER['DOCUMENT_ROOT']."/wp-content/themes/pblite-theme/acf-json/disabledFields.json","wb");
        fwrite($fp,json_encode($disabledFieldsArray));
        fclose($fp);

    }
    catch(Exception $e) {
        return $e;
    }

    echo "Success!";
?>

The final piece of the puzzle within the theme_settings_page function, is to create the form which will show all layouts, with checkboxes next to them which are able to be checked/unchecked based on the selected disabled fields:

<?php
$activeLayouts = array_diff($availableOptions, $disabledFieldsData);

echo "<form action=\"post\">";
echo "<table>";
foreach($data->fields as $field) {

    foreach($field->layouts as $layout) {

        if(in_array($layout->name, $activeLayouts)) {
            $checked = "checked";
        } else {
            $checked = "";
        }

        echo "<tr><td><input type=\"checkbox\" {$checked} name=\"disabled_flexible_layouts[]\" value=\"{$layout->name}\" /></td><td>{$layout->label}</td></tr>";

    }

}

echo "</table>";
echo "<p class=\"submit\"><input type=\"button\" value=\"Submit\" name=\"submit\" class=\"button button-primary submit_json_handler\" /></p>";
echo "</form>";
?>

This will then give you your list of layouts where disabled fields will be unchecked.

Lastly, I needed to use the disabled fields JSON in order to remove the list items within the 'Add' button in a page where you select a layout. To do this, I created another function within functions.php that hooks into the acf/input/admin_head (so the function will fun when ACF is present):

<?php

function acf_admin_head_layout( $field ) {

?>

<script type="text/javascript">

    (function($) {

        $(document).ready(function(){

            $.get('<?php echo get_template_directory_uri(); ?>/acf-json/disabledFields.json', function(data) {

            // alert(data);
                $.each(data, function(i, item) {

                    tmpl = $('.tmpl-popup').html();

                    //Create jQuery object
                    tmplDiv = $('<div>', {html : tmpl});

                    //Target element and remove it
                    tmplDiv.find('a[data-layout="'+item+'"]').closest('li').remove();

                    tmpl = tmplDiv.html();

                    $('.tmpl-popup').replaceWith('<script type="text-html" class="tmpl-popup">'+tmpl+'</sc'+'ript>');

                });

            });                   

        });

    })(jQuery);  

</script>  

<?php

}

add_action('acf/input/admin_head', 'acf_admin_head_layout', 10, 1);

?>

This will find the class within the page where the string is that holds the layout options, converts this to HTML so we are able to manipulate the content and store this within a temporary variable. We then loop through each of the disabled fields that were present in the JSON file and try to find a match within the data-layout attribute and if there is a match, then the closet list item (it's parent), will be removed.

We then have to add back in the <script> tags and put back into the page so that the newly cleaned list is used rather than the old one.

Once this has all be done, you should have an area within your settings menu where you have all of your layouts that you can enable/disable. You should then be able to add/edit a page and select the Add button and you should only see the layouts that you have selected in the settings page.

M. Ferguson
  • 1,331
  • 1
  • 10
  • 15
  • This seems like a lot more work than is needed for this to simply process it. You could simply add checkboxes to layouts on a settings page. Run a script to see what the value is and then add css hiding and displaying fields in the admin area. You could also use the checkboxes and initiate the acf update_field(); to update all options for that layout. A lot of this seems unnecessary. – Daniel Vickers Jan 17 '18 at 10:50
  • 2
    Whilst you may feel that this is complex for something that should be a 'simple' process, the theory and execution work for exactly the question asked. By using the acf-json, when a layout is created/removed, the flexible layouts on the settings page is automatically updated as the JSON file would be automatically updated and there is no need to hide anything with CSS. Using CSS to hide elements to remove something from the user can be exploited. In order to modify the 'Add' button's list with your solution, you would still have to go through most of the functions that I created anyway. – M. Ferguson Jan 17 '18 at 11:07
  • When I checked his screenshots I didn't see him using flexible content of which is the only thing that uses the add button to display a list of blocks to choose from. It's from my understanding that the layouts will just appear on that page as normal blocks which means simply destroying or hiding the block is suffice without needing to connect to an add button list. Which already makes a lot of your code unnecessary. – Daniel Vickers Jan 17 '18 at 11:22
  • 2
    "'m working on creating a settings system for a Wordpress installation, where super admins can disable some ACF Flexible Content layouts if they are not needed for the current website, without modifying the structure of the plugin." It is in the question that says he wants a list of all layouts that appear within the Flexible Content group. I'm sorry that you feel that a lot of my code is unnecessary and take offence to it, however the code I have written works for the question asked. – M. Ferguson Jan 17 '18 at 11:38
  • 2
    Either way I am not trying to cause offence nor taking offence, but it seems that sections are not needed, if it works it works. – Daniel Vickers Jan 17 '18 at 11:43