Feature #43104

Add Confirm- Radio- CheckBoxDialog to CommandController(CLI)

Added by Rafael Kähm over 2 years ago. Updated over 2 years ago.

Status:Rejected Start date:2012-11-19
Priority:Could have Due date:
Assigned To:Bastian Waidelich % Done:

30%

Category:Cli
Target version:-
PHP Version:5.3 Complexity:medium
Has patch:No

Description

For interactive confirmation or multiple choice by Commands (CLI).

for example: the same code is on https://gist.github.com/4111197
sorry for doccomments

Settings as assoc-array is not comfortably for developers, please comment or speak your ideas about settings!

  1<?php
  2namespace ........... ;
  3
  4/*                                                                        *
  5 * This script belongs to the TYPO3 Flow package '............'.        *
  6 *                                                                        *
  7 *                                                                        */
  8
  9use TYPO3\Flow\Annotations as Flow;
 10
 11/**
 12 * ConfirmationDialog command controller for the Train2Web.Core package
 13 * @todo ANSI FOREGROUNDS and BLINK & co.  
 14 * @Flow\Scope("singleton")
 15 */
 16class ConfirmationDialogCommandController extends \TYPO3\Flow\Cli\CommandController {
 17
 18        /**
 19         * Constants for anwer pattern
 20         */ 
 21        const PATTERN_USER_INPUT_YES = '/^(y|yes|j|ja|si)$/i';
 22        const PATTERN_USER_INPUT_NO = '/^(n|no|nein)$/i';
 23        const PATTERN_USER_INPUT_CANCEL = '/^(c|cancel|abort|abbrechen|abortire|abortar)$/i';
 24
 25        /**
 26     * A simpliest example for confirm dialog
 27         * 
 28         * if (user puts yes) {do something} else { do something else } 
 29         * also if user puts n, no, c, cancel then is it else
 30         * 
 31     * @return void
 32     */
 33    public function exampleCommand() {
 34            if ($this->confirmDialog()) {
 35                echo "I will continue.".PHP_EOL;
 36            } else {
 37                echo "I will not continue.".PHP_EOL;
 38            }
 39    }
 40
 41        /**
 42     * A simple example confirm dialog with cancel
 43         * 
 44         * Please note that switch case provide == comparison and not === <br>
 45         * also if you use == instead of === you can't differentiate between no and cancel!!!
 46         * 
 47     * @return void
 48     */
 49    public function example2Command() {
 50            $usersChoice = $this->confirmDialog();
 51            if ($usersChoice) {
 52                echo 'You typed "yes"!'.PHP_EOL;     
 53            } else if ($usersChoice === NULL) {
 54                echo 'You typed "cancel"!'.PHP_EOL;
 55            } else if ($usersChoice === FALSE) {
 56                echo 'You typed "no"!'.PHP_EOL;
 57            }
 58    }
 59
 60        /**
 61     * An example confirm dialog with $optionalSettings[], answer depends messages and callbacks  
 62         * 
 63         * Please note that $optionalSettings['*'] - array has priority 
 64         * 
 65     * @return void
 66     */
 67    public function demoCommand() {
 68            $settings = array(
 69                'confirmationMessage'       => 'Please put "y" or "n" or "c" or nothing to use "%1$s" and press enter: ',
 70                'standardAnswer'            => 'yes',
 71                'messageByYes'              => 'You typed "%2$s", i will run one callback for "AGREEMENT" but only if it is declared!',
 72                'messageByNo'               => 'You typed "%2$s", i will run one callback for "REJECTION" but only if it is declared!',
 73                'messageByCancel'           => 'You typed "%2$s", i will run one callback for "RESET" but only if it is declared!',
 74                'messageByWrongInput'       => 'Wrong input; "%2$s"!!!'.PHP_EOL.'You will be asked for so long, until correct entry is detected.',
 75                'callBackByYes'             => array(array($this, 'callBackForYes'), array()),
 76                'callBackByNo'              => array(array($this, 'callBackForNo'), array()),
 77                'callBackByCancel'          => array(array($this, 'callBackForCancel'), array()),
 78                'callBackByWrongInput'      => array(array($this, 'callBackForWrongInput'), array())
 79            );
 80            $this->confirmDialog('This message will be overwritten if $optionalSettings[\'confirmationMessage\'] is set.', 'NO', $settings);
 81    }
 82
 83        /**
 84     * An example <b>DEMO for callbacks returnvalue</b> based on <b>confirmationdialog:demo</b> 
 85         * 
 86         * 
 87         * Please note that $optionalSettings['*'] - array has priority 
 88         * 
 89     * @return void
 90     */
 91    public function demo2Command() {
 92            $settings = array(
 93                'confirmationMessage'       => 'Please put "y" or "n" or "c" or nothing to use "%1$s" and press enter: ',
 94                'standardAnswer'            => 'yes',
 95                'messageByYes'              => 'You typed "%2$s", i will run one callback for "AGREEMENT" but only if it is declared!',
 96                'messageByNo'               => 'You typed "%2$s", i will run one callback for "REJECTION" but only if it is declared!',
 97                'messageByCancel'           => 'You typed "%2$s", i will run one callback for "RESET" but only if it is declared!',
 98
 99                'callBackByYes'             => array(array($this, 'callBackForYes'), array()),
100                'callBackByNo'              => array(array($this, 'callBackForNo'), array()),
101                'callBackByCancel'          => array(array($this, 'callBackForCancel'), array()),
102                'callBackByWrongInput'      => array(array($this, 'callBackForWrongInput2'), array())
103            );
104            $this->confirmDialog('This message will be overwritten if $optionalSettings[\'confirmationMessage\'] is set.', 'NO', $settings);
105    }
106
107    /**
108     * Shows confirmation message and reads users answer from command line. 
109     *  
110     * <b>WARNING: don't set anything if you do not want to use it, it will cause one error. <br>
111     * ALL PARAMETERS ARE OPTIONAL. <br>
112     * ATTENTION: all settings in array $optionalSettings have priority.</b><br>
113     * 
114     * <b>$optionalSettings</b> = array (<br>
115     * <b>'confirmationMessage'</b>     => 'You can use %1$s as placeholder for your "standardAnswer".',<br>
116     * <b>'standardAnswer'</b>          => $standardAnswer,<br>
117     * <b>'messageByYes'</b>            => 'You can use %2$s as placeholder for users input string.',<br>
118     * <b>'messageByNo'</b>             => 'Please see "messageByYes".',<br>
119     * <b>'messageByCancel'</b>         => 'Please see "messageByYes".',<br>
120     * <b>'messageByWrongInput'</b>     => 'Please see "messageByYes".',<br>
121     * <b>'callBackByYes'</b>           => array (array($object, 'functionName'), array($param1, $param2, 'otherParam')), // if your callback return one value then confirmDialog return this instead of standard return of confirmDialog<br>
122     * <b>'callBackByNo'</b>            => '', // can also be emppty, confirmDialog will print messageBy{yes|no|cancel} and then returns standard value for this answer.<br>
123     * <b>'callBackByCancel'</b>        => 'please see "callBackByYes" and "callBackByNo"',<br>
124     * <b>'callBackByWrongInput'</b>    => 'please see "callBackByYes" and "callBackByNo"', // if return is void then : will ask for so long, until correct entry is detected.<br>
125     * <b>'infiniteLoopByWrongInput'</b> => TRUE, // if FALSE and callBackByWrongInput is void then throws \InvalidArgumentException with code 1352747275<br>
126     * <b>'vsprintfVars'</b>            => array($your, $own, $vars) <br>
127     * );
128     * 
129     * @param string $confirmationMessage = <b>'Would you like to perform this command? [yes|no|cancel] [%1$s] : '</b>
130     * @param string $standardAnswer = <b>'no'</b>
131     * @param array $optionalSettings <b>have priority for $confirmationMessage and $standardAnswer</b>
132     * @return boolean|NULL|yourOwnType this function will return your own type if your callback returns one value and if not then callbackBy{<br>Yes returns <b>TRUE</b><br>No returns <b>FALSE</b><br>Cancel returns <b>NULL</b><br>WrongInput throws <b>\InvalidArgumentException</b><br>}.
133     * @throws \InvalidArgumentException (code: 1353073679) if this function will be called with wrong settings 
134     */
135    protected function confirmDialog($confirmationMessage = 'Would you like to perform this command? [yes|no|cancel] [%1$s] : ', $standardAnswer = 'no', array $optionalSettings = NULL){
136        // search patterns
137
138        //| prepare and set $optionalSettings
139        //| 
140        //|// set needed DEFAULTSETTINGS -> can be overwritten with array $optionalSettings
141        $defaultSettings = array(
142            'confirmationMessage'           => $confirmationMessage,
143            'standardAnswer'                => $standardAnswer,
144            // by infiniteLoopByWrongInput == TRUE prints messageByWrongInput
145            'messageByWrongInput'           => 'Wrong input: %2$s',
146            'callBackByWrongInput'          => '',
147            // set to TRUE to prevent \InvalidArgumentException by wrong input -> will cause infinite loop to the same confirm dialog. Prints "Wrong input: %3$s" and confirmationMessage again
148            'infiniteLoopByWrongInput'      => TRUE,
149            'vsprintfVars'                  => array (
150                                                $standardAnswer,
151                                                '' // placeholder for current users input, can be used in 'wrongInputMessage'
152                                               ) 
153        );
154
155        //|//| overwrite DEFAULTSETTINGS with users SETTINGS if nacessary ($optionalSettings) 
156        if ($optionalSettings === NULL) {
157            $settings = $defaultSettings;
158        } else {
159            $settings = array_merge($defaultSettings, $optionalSettings);
160        //|//|//| set 'vsprintfVars' properly -> this will force priority for entire settings array
161            if (!empty($optionalSettings['standardAnswer'])) {
162                $settings['vsprintfVars'][0] = $optionalSettings['standardAnswer'];
163            }
164        }
165        // analyse developers set of $optionalSettings['standardAnswer']
166        $developersStandardAnswerIsYes = preg_match(self::PATTERN_USER_INPUT_YES, $settings['standardAnswer']);
167        $developersStandardAnswerIsNo = preg_match(self::PATTERN_USER_INPUT_NO, $settings['standardAnswer']);
168        $developersStandardAnswerIsCancel = preg_match(self::PATTERN_USER_INPUT_CANCEL, $settings['standardAnswer']);
169
170        // throw an exception, if this function will be called with wrong parameter
171        //
172        // developers standardAnswer must be set correctly and can not be empty
173        $developersStandardAnswerIsCorrect = $developersStandardAnswerIsYes || $developersStandardAnswerIsNo || $developersStandardAnswerIsCancel;
174        if (!$developersStandardAnswerIsCorrect) {
175            throw new \TYPO3\Flow\Exception('Developer has set $settings[\'standardAnswer\'] as '.$settings['standardAnswer']. ' please set this in your code properly', 1352736688);
176        }
177        $messages = array();
178        foreach ($settings as $key => $value) {
179            // Texts validation: will trying to render defined mesage by fail developer can see it immediately
180            if (preg_match('/^message/', $key) && !empty($value)) { 
181                $messages[$key] = \vsprintf($settings[$key], $settings['vsprintfVars']);
182            }
183            // CallBack: throw an Exceptions immediately if something is wrong with callBacks 
184            if (preg_match('/^callBack/', $key) && !empty($value) 
185                && !method_exists($value[0][0], $value[0][1])) {
186
187                throw new \InvalidArgumentException(
188                        'function "'.$value[0][1].'" for "'.$key.'" not exists or can not be called.'.PHP_EOL.
189                        'See: call_user_func_array()'.PHP_EOL.
190                        'http://php.net/manual/en/function.call-user-func-array.php', 
191                        1353073679
192                );
193            }
194        }
195
196        // render output for question
197        $output = vsprintf($settings['confirmationMessage'], $settings['vsprintfVars']);
198        echo $output.' ';
199
200        $usersInput = rtrim(fgets(STDIN));
201        // set 'vsprintfVars' properly
202        $settings['vsprintfVars'][1] = $usersInput;
203        if (empty($usersInput)) {
204            $settings['vsprintfVars'][1] = $settings['standardAnswer'];
205        } 
206        // prepare users answer
207        $userAnsweredWithYes = preg_match(self::PATTERN_USER_INPUT_YES, $usersInput) 
208                || empty($usersInput) && $developersStandardAnswerIsYes;
209        $userAnsweredWithNo = preg_match(self::PATTERN_USER_INPUT_NO, $usersInput) 
210                || empty($usersInput) && $developersStandardAnswerIsNo;
211        $userAnsweredWithCancel = preg_match(self::PATTERN_USER_INPUT_CANCEL, $usersInput)
212                || empty($usersInput) && $developersStandardAnswerIsCancel;
213
214        $usersInputIsCorrect = $userAnsweredWithYes || $userAnsweredWithNo || $userAnsweredWithCancel || empty($usersInput);
215
216        if (empty($usersInput)) {
217            $usersInput = $settings['standardAnswer'];
218        }
219        // evaluate
220        if ($usersInputIsCorrect) {
221            // for YES
222            if ($userAnsweredWithYes) {
223                if (!empty($settings['messageByYes'])) { 
224                    echo vsprintf($settings['messageByYes'], $settings['vsprintfVars']).PHP_EOL;
225                }
226                if (!empty($settings['callBackByYes'])) {
227                    $returnValueByYes = call_user_func_array($settings['callBackByYes'][0], $settings['callBackByYes'][1]);
228                }
229                if (isset($returnValueByYes)) {
230                    return $returnValueByYes;
231                }
232                return true;
233            }
234            // for NO
235            if ($userAnsweredWithNo) {
236                if (!empty($settings['messageByNo'])) { 
237                    echo vsprintf($settings['messageByNo'], $settings['vsprintfVars']).PHP_EOL;
238                }
239                if (!empty($settings['callBackByNo'])) {
240                    $returnValueByNo = call_user_func_array($settings['callBackByNo'][0], $settings['callBackByNo'][1]);
241                }
242                if (isset($returnValueByNo)) {
243                    return $returnValueByNo;
244                }
245                return false;
246            }
247            // for CANCEL
248            if ($userAnsweredWithCancel) {
249                if (!empty($settings['messageByCancel'])) { 
250                    echo vsprintf($settings['messageByCancel'], $settings['vsprintfVars']).PHP_EOL;
251                } 
252                if (!empty($settings['callBackByCancel'])) {
253                    $returnValueByCancel = call_user_func_array($settings['callBackByCancel'][0], $settings['callBackByCancel'][1]);
254                }
255                if (isset($returnValueByCancel)) {
256                    return $returnValueByCancel;
257                }
258                return NULL;
259            }
260        } else { 
261            // for wrong input
262            if (!empty($settings['messageByWrongInput'])) { 
263                echo vsprintf($settings['messageByWrongInput'], $settings['vsprintfVars']).PHP_EOL;
264            }
265            if (!empty($settings['callBackByWrongInput'])) {
266                $returnValueByWrongInput = call_user_func_array($settings['callBackByWrongInput'][0], $settings['callBackByCancel'][1]);
267            }
268            if (isset($returnValueByWrongInput)) {
269                return $returnValueByWrongInput;
270            } 
271            // throws  Exception, if developer will no infinite loops
272            if (!$settings['infiniteLoopByWrongInput']) {
273                throw new \InvalidArgumentException('User has made incorrect input and you have not caught this exception in your confirmation dialog.  TIPP: You can set $optionalSettings[\'infiniteLoopByWrongInput\']', 1352747275);
274            }
275        }
276        // will give rise to infinite looping by incorrect user's input if $optionalSettings['infiniteLoopByWrongInput'] == TRUE
277        return $this->confirmDialog($confirmationMessage, $standardAnswer, $settings);
278    }
279
280    private function callBackForYes() {
281        echo 'You called '.__FUNCTION__.'().'.PHP_EOL;
282    }
283    private function callBackForNo() {
284        echo 'You called '.__FUNCTION__.'().'.PHP_EOL;
285    }
286    private function callBackForCancel() {
287        echo 'You called '.__FUNCTION__.'().'.PHP_EOL;
288    }
289    private function callBackForWrongInput() {
290        echo 'You called '.__FUNCTION__.'().'.PHP_EOL;
291    }
292    /**
293     * as you can see this function has return value
294     * 
295     * confirmDialog() will return calbacks return value
296     */
297    private function callBackForWrongInput2() {
298        echo 'You called '.__FUNCTION__.'().'.PHP_EOL;
299        echo 'Wrong input is not allowed, I will cancel this command.'.PHP_EOL;
300        return 'blockish';
301    }       
302
303}
304
305?>

History

#1 Updated by Adrian Föder over 2 years ago

  • Assigned To deleted (Adrian Föder)
  • Has patch changed from Yes to No

Rafael, thanks for your submission.
The functionality generally is nice (i.e. the idea); I would say it could be an AbstractInteractiveCommandController which users/developers will be allowed to use.
Please update your code to this, maintaining the Coding Guidelines (http://flow.typo3.org/documentation/codingguidelines.html), and push such a patch to the review system (http://wiki.typo3.org/Contribution_Walkthrough_with_CommandLine).

However, feel free to work on the gist until the code would be ready for use.

#2 Updated by Adrian Föder over 2 years ago

  • Priority changed from Should have to Could have

#3 Updated by Karsten Dambekalns over 2 years ago

  • Target version deleted (2.0 beta 1)

#4 Updated by Bastian Waidelich over 2 years ago

  • Category changed from Command to Cli
  • Status changed from New to Needs Feedback
  • Assigned To set to Bastian Waidelich

Thanks for this!
IMO this is quite specific and a bit too much magic to be implemented in the core.
Does anything speak against adding this to a separate package which someone could add a dependency on when relying on this kind of feature?

If you agree I'd close this issue.

#5 Updated by Bastian Waidelich over 2 years ago

  • Status changed from Needs Feedback to Rejected

Closing due to lack of feedback.
I hope this can be useful for people looking for a similar feature (google should still point them here) but – as written above – I'd consider this too specific to be added to the core.
Feel free to reopen and/or comment if you don't agree

Also available in: Atom PDF