Please upgrade here. These earlier versions are no longer being updated and have security issues.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.
Options

How to add validation to settings activated via the Configuration model

rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one"NY ✭✭✭

I've been playing with the configuration model as a vehicle that manages the settings for a plugin I'm writing. Works like a charm and now I want to add validation to the entered settings. I saw plenty of examples how to do that with forms, but using the configuration model does not use a plugin-defined form, it's all handled internally.

In my trials and errors I was able to add validation, but I haven't figured out how to display the settings screen again when the validation fails (not having used a form, I am not able to render a named view name even though I bet Vanilla is using a view when it renders the setting screen).

Clearly I prefer a solution without coding my own form - I like how configuration model elegantly manages everything (well, almost) for me.

What am I missing? (I haven't posted the code because the plugin is large, but I think my explanation is clear. If necessary to help me I'll code another short version that shows what I did).

Comments

  • Options

    Since you are commenting under this plugin, I guess you are referring to the configuration module.

    There is no way to add validation to the configuration module currently, other than handling the form submission yourself by calling Schema instead of Initialize. This way it will only built the form but not handle the posting. You can then use the configuration model to validate the inputs.

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Thanks @Bleistivt - I will try that!

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    You can try to do something like that: https://github.com/R-J/EventCalendar/blob/master/class.eventcalendar.plugin.php#L95-L140 and see if you could add validation results after if ($sender->Form->authenticatedPostBack()) {

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭
    edited September 2015

    R_J, when I posted the problem I indicated that I was already able to perform the validation. I was doing it exactly as you suggested. My problem was in telling Vanilla to display the setup screen again with the error messages.

    I may have found a way -- will post later my progress (if any).
    Thanks for your help!

  • Options

    You could also assign the ConfigurationModule to the controller (like $sender->ConfigurationModule = $confand create a simple view where you render the form:

    echo $this->ConfigurationModule->ToString();
    

    This would let you add the validation errors above.

    To be honest, I don't see the need for validation on a simple settings screen, though. Users with dashboard access should know what they are doing. I'd rather put more work in writing a clear description for the field.

    If you really, technically rely on a config value being an integer for example, you can always cast in your code to prevent errors.

    (int)c('HasToBeInt',  0);
    
  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    So after few more adjustments I am able to leverage the configuration module as well as perform the validation and get the red validation messages displayed on the screen. What I don't like is that even if there is a validation error I get a "Your changes have been saved" message.

    So now the issue is reduced to how to tell Vanilla not to save the values (which include at least one invalid entry).

  • Options
    peregrineperegrine MVP
    edited September 2015

    even if there is a validation error I get a "Your changes have been saved" message.

    edited...

    The code you have written is not showing in my crystal ball. :grin:

    I have a crossword puzzle but can't figure out what 10 across is, can you help me? that is the only clue I am giving you

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Peregrine - are you referring to the "Your changes have been saved" issue -- my question on how to tell vanilla not to save the form values?

    Because the rest is solved (and works like a charm and the code is simple and clean and easy to change)

  • Options

    Yes, I believe he is. Because we can only guess where the error is, without a single line of code.

    There is no way to add validation to the configuration module currently, other than handling the form submission yourself by calling Schema instead of Initialize.

    Are you doing that?

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    @Bleistivt said:
    To be honest, I don't see the need for validation on a simple settings screen, though. Users with dashboard access should know what they are doing. I'd rather put more work in writing a clear description for the field.

    +1 for that one.

    But just for the sake of "what is possible":

    public function settingsController_aah_create($sender) {
        $sender->permission('Garden.Settings.Manage');
        $sender->title(t('Aah Settings'));
        $sender->addSideMenu('dashboard/settings/plugins');
        $sender->description('Aah Description');
    
        $sender->Form = new Gdn_Form();
        $validation = new Gdn_Validation();
    
        if ($sender->Form->authenticatedPostBack()) {
            $formPostValues = $sender->Form->formValues();
            // Do validation here
            if (count($formPostValues['Aah.MarxBrothers']) > 1) {
                $validation->addValidationResult('Aah.MarxBrothers', 'I\'ve said: CHOOSE ONLY ONE!!!!!');
                $sender->Form->setValidationResults($validation->results());
                // Reset form values to current config or default
                $sender->Form->setFormValue('Aah.MarxBrothers', c('Aah.MarxBrothers', 'Groucho'));
                // Set translation of "Saved" to "" to suppress it
                Gdn::locale()->setTranslation('Saved', '');
            }
        }
    
        $configurationModule = new ConfigurationModule($sender);
        $configurationModule->initialize(
            array(
                'Aah.MarxBrothers' => array(
                    'Control' => 'CheckBoxList',
                    'LabelCode' => 'Marx Brothers',
                    'Items' => array('Groucho', 'Chico', 'Harpo'),
                    'Description' => 'Choose only one...',
                ),
            )
        );
        $configurationModule->renderAll();
    }
    

    The 3 commented lines do 1) validation 2) fake a "not save" and 3) suppress the saved message.
    If you have more than one setting in there you would have to reset the form values for all of them.

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭
    edited September 2015

    R_J - thanks for putting the effort to put the above. First, I want to say that the form I use is simple, so using the Configuration Model makes sense for me. My code indeed reflects your sample above.

    When I detect a form input error, in addition to setting the error message I also clear out the relevant config settings (which makes them assuming the defaults). I do that via "removeFromConfig('MyPlugin');" which is convenient to accomplish a "not saved" state with the defaults brought up.

    So now the remaining issue is the Saved message. The Gdn::locale()->setTranslation('Saved', ''); trick to change the "Saved" message didn't work for me. Adding 'HasLocale' => TRUE to the plugin definitions didn't help (so not the admin gets a "saved" message even though nothing is saved).

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    @rbrahmson said:
    So now the remaining issue is the Saved message. The Gdn::locale()->setTranslation('Saved', ''); trick to change the "Saved" message didn't work for me.

    It works for me... Are you sure you have put it at the right place?

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    That's both good and embarrassing news. I will check again that I haven't missed something.

    BTW, in the meanwhile I turned on Eventi to see if there are any hooks for the saving of the configuration. Eventi revealed the following:

    Gdn_ConfigurationSource_BeforeSave_Handler
    Gdn_ConfigurationSource::FireEvent ('BeforeSave')
    /Vanilla/library/core/class.configuration.php:1114
    Arguments:
    ConfigDirty: b1
    ConfigNoSave: b
    ConfigType: 'file'
    ConfigSource: '/XXXhost/Vanilla/conf/config.php'
    ConfigData: array{20}

    so I thought I could create a function that hooks into that and avert the saving. I am not that versed in Vanilla hooks to know how to name the function. I tried:

    public function Gdn_ConfigurationSource_BeforeSave_Handle_create($Sender)

    But it never got control. Either I am not naming it correctly or this is impossible...

    Of course if your trick would work the above is theoretical, but I'm still curious;-)

  • Options
    R_JR_J Ex-Fanboy Munich Admin

    Look at the function initialize() of the ConfigurationModule: there is the line saveToConfig($Data, array('RemoveEmpty' => true)); which does the saving but there is no fireEvent anywhere. So whatever Eventi shows, it will not influence the config setting saving.

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Got it, thanks! But if it did influence the save, how would I have had to name the function for that event?

  • Options

    In my opinion the cleanest way to do this is by not calling initialize, but calling schema. It will not automatically save this way and you can use the configuration model to validate and save the inputs - or not.

  • Options
    rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭
    edited September 2015

    Bleistivt - schema works (in not saving...), but then I need to save all the form variables myself (obviously). I can do that for each form value, but I wonder if there is a function that saves them all like "SavetoConfig" (same way there is a "removeFromConfig").

  • Options

    This works and does everything you want. It shows the saved message, validation results and no view is required:

    public function settingscontroller_test_create($sender) {
        $sender->permission('Garden.Settings.Manage');
        $sender->addSideMenu();
    
        $schema = [
            'TestConfig1' => [
                'Control' => 'CheckBox',
                'LabelCode' => 'Test 1'
            ],
            'TestConfig2' => [
                'Control' => 'textbox',
                'LabelCode' => 'Test 2'
            ]
        ];
    
        $conf = new ConfigurationModule($sender);
        $conf->schema($schema);
    
        $validation = new Gdn_Validation();
        $model = new Gdn_ConfigurationModel($validation);
        $model->setField(array_keys($schema));
        $sender->Form->setModel($model);
    
        $model->Validation->applyRule('TestConfig2', 'Required');
    
        if ($sender->Form->authenticatedPostBack() && $sender->Form->save() !== false) {
            $sender->informMessage(t('Your changes have been saved.'));
        }
    
        $sender->title('Test');
        $conf->renderAll();
    }
    
  • Options
    peregrineperegrine MVP
    edited September 2015

    I was wondering when someone was going to realize hints weren't going to work and break down and just provide the code :grin:

    but will this be the end of the questions regarding validation and the configuration model. stay tuned....
    .

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

Sign In or Register to comment.