Jump to content

Use delimited texarea, table, or YAML for settings


Macrura
 Share

Recommended Posts

Needed to show someone how to quickly setup some settings for various things in a simple text area:

could be used for slider settings, site settings, etc;

What it does: gives you a matching variable for each key of each line...

1.) setup a textarea field for the settings ; i'm calling it settings_ta

2.) add delimited settings, 1 per line; i use a pipe (|) delimiter;

example:

address|some info here
facebook|https://www.facebook.com
twitter|https://twitter.com
phone|(999) 999-9999

3.) in your _init.php, get the settings - replace the page number and the name of your settings field:

    $settings_ta = $pages->get(1644)->settings_ta;	
    $settings_lines = explode(PHP_EOL, $settings_ta);	
   
    foreach($settings_lines as $settings_row) {
        $settings_pair = explode('|', $settings_row);
        ${trim($settings_pair[0])} = trim($settings_pair[1]);
    }

more condensed version, for those of you who like brevity...

foreach(explode(PHP_EOL, $pages->get(1644)->settings_ta) as $settings_row) {
	$settings_pair = explode('|', $settings_row);
	${trim($settings_pair[0])} = trim($settings_pair[1]);	
}

now you can do this in your templates:

echo $address;
echo $facebook;
echo $twitter;
echo $phone;

Edit: made the code simpler....; 2nd edit - added trim to support using ace editor with tabs

Addendum: as an added convenience, you could use the Ace text editor module which would give you a monospaced text field and the ability to use tabs for your settings, which could be more readable and easier to edit. The code has been updated to support this by trimming the exploded strings prior to generating the variable/value.

address   |  some info here
facebook  |  http://www.facebook.com
twitter   |  http://twitter.com
phone     |  (999) 999-9999
Edited by Macrura
  • Like 14
Link to comment
Share on other sites

@adrian - yes, i love that YAML thing; and as I see it there are maybe 4-5 good ways to do settings, depending on the site and the scenario;

I usually use a profields table, and have 2 columns, and do a similar code as above to get the $vars, and i'm using this for a lot of things like slider settings, site settings, settings for javascript plugins etc.. where i need to have a ton of settings without much effort...

For some things that are mission critical I make fields, especially if the setting can use a color picker, rangeslider, or select.

the delimited textarea is good for beginners who maybe don't have profields, and who need a quick easy way to have some editable settings in the b/e..

and there should be an easy way to use the YAML field to do this; i guess i should try it and do another tutorial for using that for settings...

  • Like 3
Link to comment
Share on other sites

Part II: Using a Profields Table [Note: this part requires you to own ProFields]

1.) Setup a field of type table for settings, I call it settings_table. It will have 2 columns, setting and value

2.) add it to your template (example would be if you had a settings template).

3.) Add some settings..

| site_title | My Great Site           |
|------------|-------------------------|
| phone      | (666) 777-8888          |
|------------|-------------------------|
| slogan     | Tulips Rule             |
|------------|-------------------------|
| facebook   | http://www.facebook.com |

3.) Place this code in your _init.php or wherever you have global stuff.. replace the page number with the appropriate page number:

$settings_table = $pages->get(1020)->settings_table;

foreach($settings_table as $row) {
    ${$row->setting} = $row->value;
}

now you can do this:

echo $site_title
echo $phone
echo $slogan
echo $facebook

- - -

...and one might wonder, what is to prevent a client from inadvertently changing the setting name or even deleting a critical setting and consequently breaking their site?

with some jQuery, and the help of admin custom files (courtesy of martijn-geerts) you can disable any existing setting name from being edited. or deleted;

add this to your AdminCustomFiles/ProcessPageEdit.js file:

$(function(){
    $('li.Inputfield_settings_table tr').each(function(){
            
        setting = $(this).find('input[name*="_setting"]');    
        value = $(this).find('input[name*="_value"]').val();
        icon = $(this).find('i.InputfieldTableRowDeleteLink');
            if(value) {
                setting.addClass("disabled").attr('readonly', true);
                icon.removeClass("InputfieldTableRowDeleteLink fa-trash-o").addClass("fa-lock");
            }
    });
});   

this does assume that the table is named settings_table, the setting column is named "setting" and the value column is named "value".

you can also add this to your AdminCustomFiles/ProcessPageEdit.css

li.Inputfield_settings_table tr input.disabled {
	background-color: #e8e5e5 !important;
	color: #949494!important;
}

Here is a screenshot of this in action:

post-136-0-61518800-1417044448_thumb.png

Edit: added instructions to protect the settings names, prevent deletion, and change the color to differentiate the field status.

Edited by Macrura
  • Like 6
Link to comment
Share on other sites

Part III: Using YAML (part of structured data module).

1.) Install the module (make sure to install the new version, FieldtypeDataStructure)

2.) create the field for it (ex. settings_ds)

3.) Add some settings - these can be more sophisticated/nested than the settings above, because of the YAML structure options:

- name: address
  street: 27 Hawthorne Lane
  city: New York
  state: NY
  zip: 10982
  phones:
    main: (999) 888 9874
    fax: (555) 548-5647
- name: social_media
  facebook: https://www.facebook.com
  twitter: https://twitter.com
- name: global
  site_title: My Awesome Site
  tagline: Tulips Rule

4.) Get the field into your template:

$settings_ds = $pages->get(1644)->settings_ds;

5a.) Option 1: Auto populate the $vars

foreach($settings_ds as $setting) ${$setting->name} = $setting;

5b.) Option 2: Query the field with PW selectors (this is awesome):

$address = $settings_ds->get("name=address");
$social_media = $settings_ds->get("name=social_media");
$global = $settings_ds->get("name=global");

this is cool because you can keep all the parts of a setting together as a group, and have multiple parameters like this:

// address
echo $address->street;
echo $address->city;
echo $address->state;
echo $address->zip;
echo $address->phones->main;
echo $address->phones->fax;

// social media
echo $social_media->facebook;
echo $social_media->twitter;

// global
echo $global->site_title;
echo $global->tagline;
Edited by Macrura
  • Like 5
Link to comment
Share on other sites

  • 8 months later...

haven't tested this but i'm thinking of adding the settings to the $config class so they are available inside functions and wireInclude, wireRender..

would just need to change the loop to this i think:

foreach($settings_lines as $settings_row) {
    $settings_pair = explode('|', $settings_row);
    $config->{trim($settings_pair[0])} = trim($settings_pair[1]);
}

or

foreach($settings_table as $row) {
    $config->{$row->setting} = $row->value;
}
Link to comment
Share on other sites

  • 4 months later...

A settings field based on a hooked Profields Table.

When running the code (in the spoiler), superusers are able to edit, drag & delete all,

while other users are limited to edit the setting only.

(Thanks MacRura for the idea)
 
Superuser sees:

post-577-0-94216200-1449317098_thumb.png

 
Everybody else sees:

post-577-0-58529100-1449317137_thumb.png

Code lives in the spoiler.

/**
 * How to ?
 * 
 * - Create /site/init.php
 * - Place this code in the init.php
 * - Change $wanted_field to the fieldname of the TableField
 * - Change $wanted_page_id to the id of the page where the TableField lives
 *
 */
 
wire()->addHookAfter('InputfieldTable::render', function($event) {
    
    if (wire('user')->isSuperuser()) return;
    
    // What field
    $wanted_field = 'table_settings';
    // On what page ?
    $wanted_page_id = 1;
    $inputfield = $event->object;
 
    if ($inputfield->name !== $wanted_field) return;
    if (wire('input')->get->id != $wanted_page_id) return;
 
    $html = new DOMDocument;
    $html->loadHTML($event->return);
 
    $table = $html->getElementsByTagName('table')->item(0);
    $trs = $table->getElementsByTagName('tr');
 
    foreach ($trs as $index => $tr) {
 
        // td & th's
        $columns = $tr->childNodes;
 
        $columnMove = $columns->item(0);
        $columName  =  $columns->item(1);
        $columLabel = $columns->item(2);
        $columValue = $columns->item(3);
        $columTrash = $columns->item($columns->length - 1);
 
        // Skip table header
        if ($tr->getElementsByTagName('td')) {
            // Name column
            foreach ($columName->childNodes as $key => $child) {
                if ($child instanceof DOMElement) {
                    $columLabel->appendChild($child);
                }
            }
 
            // Trash column (append to label)
            foreach ($columTrash->getElementsByTagName('input') as $i => $child) {
                $inputfield = $html->createElement('input', $child->getAttribute('value'));
                $inputfield->setAttribute('name', $child->getAttribute('name'));
                $inputfield->setAttribute('value', $child->getAttribute('value'));
                $columLabel->appendChild($inputfield);
            }
 
            // Label column
            foreach ($columLabel->childNodes as $i => $child) {
                if ($child instanceof DOMElement) {
                    // Set all inputfields to hidden hidden
                    $child->setAttribute('type', 'hidden');
                    if ($i === 0) {
                        $label = $html->createTextNode($child->getAttribute('value'));
                        $columLabel->appendChild($label);
                    }
                }
            }
        }
 
        // Put width of name column to value column
        $widthName = (int) filter_var($columName->getAttribute('style'), FILTER_SANITIZE_NUMBER_INT);
        $widthValue = (int) filter_var($columValue->getAttribute('style'), FILTER_SANITIZE_NUMBER_INT);
        $columValue->setAttribute('style', 'width: ' . ($widthName + $widthValue) . '%;');
 
        // Remove from table column
        $tr->removeChild($columnMove);
        $tr->removeChild($columName);
        $tr->removeChild($columTrash);
    }
 
    $result = new DOMDocument;
    $result->preserveWhiteSpace = false;
    $result->appendChild($result->importNode($table, true));
    $table = $result->saveHTML();
    $markup = preg_replace('/<table(.*?)<\/table>/', $table, $event->return);
    $markup = str_replace("<p><a class='InputfieldTableAddRow' href='#'><i class='fa fa-plus-circle'></i> Add Row</a></p>", '', $markup);
 
    $event->return = $markup;
});

Maybe some time I build a Module for this one.

  • Like 7
Link to comment
Share on other sites

@Martijn - thanks - that's very cool.  I like the idea of the label - so i'm going to see about integrating your version into my current project's settings table... It is definitely more user friendly to have the label - some clients put wrong stuff into value because they only see the variable name and sort of don't get it.

Also in newer versions of my settings i have an options column, which inside i can place pipe-delimited options, such as enabled|disabled, or true|false, or on|off; then in the value row, when you click into the cell, it brings up a select; this helps to control the spelling and to also remind people of what sort of values are acceptable for the field.

To achieve this i'm using a commercial jQuery plugin called Pickle, which lets you create a select out of any text input;(if i could find a non-commercial plugin that did the same thing i would post the instructions here)

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

update - if you need to be able to access your variables inside functions or the admin you can use $config..

in config.php, setup an empty array for siteSettings like this:

$config->siteSettings = array();

then in ready.php file, array merge values from a settings table (profields table) or any other type of setting (MultiValue Textformatter, YAML settings ...), in the admin like this:

// Get your settings from wherever
$st = $pages->get('/settings/')->settings_table;

// temporary array to merge with global
$settings = array();

foreach($st as $row) {
    if(!$row->value) continue;
    if($row->disable == 1) continue;
    $settings[$row->setting] = $row->value;
}
// merge
$config->siteSettings = array_merge($config->siteSettings, $settings);

i'm curious if anyone sees any possible issues with this; it works now and i can access my siteSettings everywhere doing this;

you can access the settings like this from templates:

echo $config->siteSettings['yourKey'];

or in function..

echo wire('config')>siteSettings['yourKey'];
  • Like 1
Link to comment
Share on other sites

  • 1 year later...

I'm using a highly modded version of ProcessGeneralSettings; got it working with most fieldtypes, so i can setup settings dashboard now and have fields for most things i need on the frontend, social media urls, metas, company info as well as visual settings; it all stores as module config, so i can have a lot of fields (50-60) without having to add those fields to PW...

  • Like 4
Link to comment
Share on other sites

@Macrura Thanks for the reply. It's good to know that you are having success with the module approach - I did take a brief look at it as I like the idea, but I ended up passing it over when I realised there was no support for images out of the box. Could you perhaps issue a pull-request to the module author with your changes?  They sound like a worthwhile addition.

  • Like 1
Link to comment
Share on other sites

it might really need to be released as a whole new module as it is massively changed;

for example, my version has 2 modules, one process module for setting up the fields and one that actually handles the object for providing the settings to the front end; has to be done like this if you want to give access to that settings panel to any other role besides superuser;

Also, i think i tried to ask some questions to the original module author but they seem to be no longer around (mostly in reference to the major conflict the original module has with the $settings global variable which is also used by ListerPro)..

The way i handle images is to have a page tree select where you select the page holding the image; this works well for me because most sites i'm building have some type of media library, so this allows users to select media pages containing stuff like logos, open graph fallback images, schema profile images etc;

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...