Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
StagingCLI
0.00% covered (danger)
0.00%
0 / 136
0.00% covered (danger)
0.00%
0 / 13
4160
0.00% covered (danger)
0.00%
0 / 1
 __invoke
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 1
506
 render
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
72
 get_admin_user_id
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 table
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 bold_heading
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 success
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 info
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 warning
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 error
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 colorize_log
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
 new_line
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 log_to_json
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 confirm
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2namespace NewfoldLabs\WP\Module\Staging;
3
4/**
5 * Class for Staging CLI commands
6 */
7class StagingCLI extends \WP_CLI_Command {
8    /**
9     * Used internally to create staging environment.
10     *
11     * @param array $args       Command arguments
12     * @param array $assoc_args Associative command arguments.
13     *
14     * @throws \WP_CLI\ExitException On CLI failure.
15     */
16    public function __invoke( $args, $assoc_args ) {
17
18        if ( ! is_array( $args ) || ! isset( $args[0] ) ) {
19            $this->error( __( 'No sub-command provided', 'wp-module-staging' ) );
20        }
21
22        switch ( $args[0] ) {
23
24            case 'create':
25                $this->render( Staging::getInstance()->createStaging() );
26                break;
27
28            case 'clone':
29                $this->render( Staging::getInstance()->cloneProductionToStaging() );
30                break;
31
32            case 'destroy':
33                $this->render( Staging::getInstance()->destroyStaging() );
34                break;
35
36            case 'sso_staging':
37                $user_id = $this->get_admin_user_id();
38                if ( ! $user_id ) {
39                    $this->error( __( 'Invalid user.', 'wp-module-staging' ) );
40                }
41                $this->render( Staging::getInstance()->switchTo( 'staging', $user_id ) );
42                break;
43
44            case 'sso_production':
45                $user_id = $this->get_admin_user_id();
46                if ( ! $user_id ) {
47                    $this->error( 'Invalid user.' );
48                }
49                $this->render( Staging::getInstance()->switchTo( 'production', $user_id ) );
50                break;
51
52            case 'deploy':
53                $deploy_type = isset( $args[1] ) ? $args[1] : '';
54                switch ( $deploy_type ) {
55                    case 'all':
56                    case 'both':
57                        $this->render( Staging::getInstance()->deployToProduction( 'all' ) );
58                        break;
59                    case 'db':
60                    case 'database':
61                        $this->render( Staging::getInstance()->deployToProduction( 'db' ) );
62                        break;
63                    case 'files':
64                        $this->render( Staging::getInstance()->deployToProduction( 'files' ) );
65                        break;
66                    default:
67                        $this->error( __( 'Invalid deploy type', 'wp-module-staging' ) );
68                }
69                break;
70
71            case 'deploy_files':
72                $this->render( Staging::getInstance()->deployToProduction( 'files' ) );
73                break;
74
75            case 'deploy_db':
76                $this->render( Staging::getInstance()->deployToProduction( 'db' ) );
77                break;
78
79            case 'deploy_files_db':
80                $this->render( Staging::getInstance()->deployToProduction( 'all' ) );
81                break;
82
83            default:
84                $this->error( __( 'Invalid action', 'wp-module-staging' ) );
85        }
86    }
87
88    /**
89     * Render a success or error message based on provided data.
90     *
91     * @param mixed $data The data from which to fetch the message.
92     */
93    protected function render( $data ) {
94        $response = array(
95            'status'  => 'error',
96            'message' => __( 'Invalid JSON response', 'wp-module-staging' ),
97        );
98        switch ( gettype( $data ) ) {
99            case 'string':
100                $decoded = json_decode( $data );
101                if ( $decoded && isset( $decoded['message'] ) ) {
102                    $response = $decoded;
103                }
104                break;
105            case 'array':
106                $response = $data;
107                break;
108            case 'object':
109                if ( is_wp_error( $data ) ) {
110                    $response['message'] = $data->get_error_message();
111                }
112                break;
113        }
114        if ( 'success' === $response['status'] ) {
115            $this->success( $response['message'] );
116        } else {
117            $this->error( $response['message'] );
118        }
119    }
120
121    /**
122     * Get an admin user ID.
123     *
124     * Prefers the currently logged-in administrator when available,
125     * otherwise falls back to the first administrator found.
126     *
127     * @return int
128     */
129    protected function get_admin_user_id() {
130
131        $current_user_id = get_current_user_id();
132        if ( $current_user_id && user_can( $current_user_id, 'manage_options' ) ) {
133            return $current_user_id;
134        }
135
136        $user_id = 0;
137
138        $users = get_users(
139            array(
140                'role'   => 'administrator',
141                'number' => 1,
142            )
143        );
144
145        if ( is_array( $users ) && is_a( $users[0], 'WP_User' ) ) {
146            $user    = $users[0];
147            $user_id = $user->ID;
148        }
149
150        return $user_id;
151    }
152
153
154    /**
155     * Helper to format data into tables.
156     *
157     * By default, the method creates simple $key => $value tables.
158     * Set $type to 'adv' and the table inherits keys from $data. DATA MUST BE UNIFORM & MATCH FIRST ROW.
159     *
160     * 1. Provide $data as an array or object
161     * 2. Provide $keys as two strings -y default 'DETAIL' and 'VALUE' are used.
162     * 3. Prints ASCII Table
163     *
164     * @param array  $data the data
165     * @param array  $keys key values
166     * @param string $type formating
167     */
168    protected function table( $data, $keys = array( 'DETAIL', 'VALUE' ), $type = 'simple' ) {
169        if ( empty( $data ) ) {
170            return;
171        }
172
173        if ( 'adv' === $type ) {
174            $items = $data;
175            $keys  = array_keys( array_shift( $data ) );
176        } else {
177            $items = array();
178            foreach ( $data as $detail => $value ) {
179                $items[] = array(
180                    $keys[0] => $detail,
181                    $keys[1] => $value,
182                );
183            }
184        }
185
186        Utils\format_items( 'table', $items, $keys );
187    }
188
189    /**
190     * Creates Heading with Blue background and Grey text.
191     *
192     * @param string $message the message
193     * @param string $emoji   an emoji to include
194     */
195    protected function bold_heading( $message, $emoji = '' ) {
196        $this->colorize_log( $message, '4', 'W', $emoji );
197    }
198
199    /**
200     * Formatted Success message.
201     *
202     * @param string $message the message
203     * @param bool   $silent  no feedback
204     */
205    protected function success( $message, $silent = false ) {
206        $pre_ = $silent ? '' : 'Success: ';
207        $this->colorize_log( $pre_ . $message, '2', 'k', '✅' );
208    }
209
210    /**
211     * Formatted Info message.
212     *
213     * @param string $message the message
214     */
215    protected function info( $message ) {
216        $this->colorize_log( $message, '4', 'W', 'ℹ️' );
217    }
218
219    /**
220     * Formatted Warning message.
221     *
222     * @param string $message the message
223     */
224    protected function warning( $message ) {
225        $this->colorize_log( $message, '3', 'k', '⚠️' );
226    }
227
228    /**
229     * Formatted Error message. Halts by default.
230     *
231     * @param string $message the message
232     * @param bool   $silent  no feedback
233     * @param bool   $halt    should stop on error
234     * @param int    $code    error code to be supplied
235     *
236     * @throws \WP_CLI\ExitException Throws exit exception.
237     */
238    protected function error( $message, $silent = false, $halt = true, $code = 400 ) {
239        $pre_ = $silent ? '' : __( 'Error: ', 'wp-module-staging' );
240        $this->colorize_log( $pre_ . $message, '1', 'W', '🛑️' );
241        if ( $halt ) {
242            \WP_CLI::halt( $code );
243        }
244    }
245
246    /**
247     * Formatting helper for colorized messages.
248     *
249     * @param string $message      the message
250     * @param string $background   bg color
251     * @param string $text_color   text color
252     * @param string $emoji_prefix an emoji to prefix
253     */
254    protected function colorize_log( $message = '', $background = '', $text_color = '%_', $emoji_prefix = '' ) {
255        if ( ! empty( $background ) ) {
256            $background = '%' . $background;
257        }
258
259        if ( ! empty( $text_color ) && false === strpos( $text_color, '%' ) ) {
260            $text_color = '%' . $text_color;
261        }
262
263        if ( ! empty( $emoji_prefix ) ) {
264            $message = $emoji_prefix . '  ' . $message;
265        }
266
267        \WP_CLI::log( \WP_CLI::colorize( $background . $text_color . $message . '%n' ) );
268    }
269
270    /**
271     * Empty linebreak
272     */
273    protected function new_line() {
274        \WP_CLI::log( __return_empty_string() );
275    }
276
277    /**
278     * Helper function for returning clean JSON response.
279     *
280     * @param array|string $data Provide well-formed array or existing JSON string.
281     */
282    protected function log_to_json( $data ) {
283        if ( is_array( $data ) ) {
284            \WP_CLI::log( wp_json_encode( $data ) );
285        } elseif ( is_array( json_decode( $data, true ) ) ) {
286            \WP_CLI::log( $data );
287        } else {
288            $this->error( __( 'Provided $data wasn\'t valid array or JSON string.', 'wp-module-staging' ) );
289        }
290    }
291
292    /**
293     * Formatted Confirm Dialog. A 'n' response breaks the thread.
294     *
295     * @param string $question the question
296     * @param string $type     type of confirm dialog
297     *
298     * @throws \WP_CLI\ExitException Throws exit exception.
299     */
300    protected function confirm( $question, $type = 'normal' ) {
301        switch ( $type ) {
302            case 'omg':
303                \WP_CLI::confirm( $this->warning( '☢ 🙊 🙈 🙊 ☢️  ' . $question ) );
304                break;
305            case 'red':
306                \WP_CLI::confirm( $this->error( $question, true ) );
307                break;
308            case 'yellow':
309                \WP_CLI::confirm( $this->warning( $question ) );
310                break;
311            case 'green':
312                \WP_CLI::confirm( $this->success( $question ) );
313                break;
314            case 'underline':
315                \WP_CLI::confirm( $this->colorize_log( $question, '', 'U' ) );
316                break;
317            case 'normal':
318            default:
319                \WP_CLI::confirm( $question );
320                break;
321        }
322    }
323}