Jump to content
Macrura

Use delimited texarea, table, or YAML for settings

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

Share this post


Link to post
Share on other sites

I wonder if owzim's new YAML fieldtype would be useful for this scenario also?

Your approach might be simpler for non-tech users to understand and format though!

  • Like 1

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

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;
}

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Sure, but that's true for every field in the admin :)

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

I guess when you add an extra column to that table you can can solve it with PHP. The script loops over over each row, and you could modify it as you wish.

  • Like 1

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Curious to know what other people are doing for this now - especially in light of the new FunctionalFields feature.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Atlasfreeman
      Hi!
      I want to make a small site, a one page site.
      And i have this idea about doing 2 to 3 diffrent template that i can load into the index / home page.

      I want to do this with an array so that i can keep creating more topics (with the template) id needed.
      <?php include('./head.inc'); // include header markup ?> <?php $children = $page->get('template=onecolmn|twocolumn, sort=sort'); foreach($children as $child) { include($child); } ?> <?php include('./foot.inc'); // include footer markup ?>  
      As you can see i have to template wish i want to control from the backend.
      I know include does not work this way, put what is my other option to make an array that loads the whole page on an other pages.
       
       
    • By Kevin C. McCarthy
      I am desperate to learn how to you ProcessWire to it's fullest potential, and while the documentation is great and always appreciated, I simply can't follow along because it gets way too technical without really showing how applicable and versatile it can be. Then again, I'm almost a moderate understanding of PHP and no experience with APIs or programming JavaScript—so it's probably leagues ahead of where I am at the moment. That said, I learn best by watching and the doing. Think Codecademy or FreeCodeCamp. I was wonder if there are any video tutorials or walk-through lessons to give me a greater understanding of ProcessWire and how to utilize it effectively.
      For some background, I'm great with WordPress and I'm great with writing websites by hand with Notepad only. The biggest hurdles I have with PW is the phrasing is so far left of WP at times that it's a massive hurdle for me to get over. Like in WP, themes, templates, etc are totally different things. And as someone who builds WP sites for a living, it gets hard to kill those old preconceived meanings.
      I want to start building out PW sites for numerous reasons. For one, most of my clients they would benefit from it vs the Bloated Beast. Two, it would allow me to differentiate me in a market saturated by WP devs. I know I have a long ways to go until I reach that point of considering myself a "PW dev", but I am desperate for resources to help me wrap my head around it.
      I've built my own website in PW but TBH it only handles some of the data while most of the text has been hard-coded into the PHP template files because I couldn't get my my head around the "best practice" of structuring the data.
      Anyway, enough rambling, I'm just hoping those of you in the community can point me to easily-digestible sources out there that can help move me along so I can actually benefit from using the platform. Thank you!
    • By Fran
      Hi, I'm using this piece of code to retrieve the names of two arrays of images and it works really well. But my question is how can I use only one foreach so i can use both arrays inside of it? Thanks very much.
      $precioschico = $page->get("planos"); $preciosgrande = $page->get("mapas"); foreach( $preciosgrande as $preciogrande ) { echo $preciogrande . '<br/>'; } foreach( $precioschico as $preciochico ) { echo $preciochico . '<br/>'; }  
    • By Orkun
      $stats = array( "total" => array( "main" => array( "selector" => "template=50, parent=$formSuperSelector", "count" => $this->pages->find($stats['total']['main']['selector'])->count ), "yes" => array( "selector" => $stats['total']['main']['selector'].", teilnahme=yes", "count" => $this->pages->find($stats['total']['yes']['selector'])->count ), "no" => array( "selector" => $stats['total']['main']['selector'].", teilnahme=no", "count" => $this->pages->find($stats['total']['no']['selector'])->count ) ), "intern" => array( "main" => array( "selector" => "template=50, formtype=intern, parent=$formSuperSelector", "count" => $this->pages->find($stats['intern']['main']['selector'])->count ), "yes" => array( "selector" => $stats['intern']['main']['selector'].", teilnahme=yes", "count" => $this->pages->find($stats['intern']['yes']['selector'])->count ), "no" => array( "selector" => $stats['intern']['main']['selector'].", teilnahme=no", "count" => $this->pages->find($stats['intern']['no']['selector'])->count ) ), "extern" => array( "main" => array( "selector" => "template=50, formtype=extern, parent=$formSuperSelector", "count" => $this->pages->find($stats['extern']['main']['selector'])->count ), "yes" => array( "selector" => $stats['extern']['main']['selector'].", teilnahme=yes", "count" => $this->pages->find($stats['extern']['yes']['selector'])->count ), "no" => array( "selector" => $stats['extern']['main']['selector'].", teilnahme=no", "count" => $this->pages->find($stats['extern']['no']['selector'])->count ) ), ); var_dump($stats['total']['main']['count']); How can I bring this to work? I always get a "Notice: Undefined variable: stats in..." when trying to access:
      "count" => $this->pages->find($stats['intern']['yes']['selector'])->count or
      "selector" => $stats['intern']['main']['selector'].", teilnahme=yes",
    • By benbyf
      Hi!
      I'm writing some tutorials for Tuts+ and I'll put some information here when I have it. Love to hear your suggestions, and I think a couple of other people are wirting tutorials there and of course many other places.
×
×
  • Create New...