PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / class-plugin-deactivation.php
wp-2fa Last commit date
css 10 hours ago dist 10 hours ago includes 10 hours ago js 10 hours ago languages 10 hours ago templates 10 hours ago vendor 10 hours ago class-plugin-deactivation.php 10 hours ago index.php 10 hours ago license.txt 10 hours ago readme.txt 10 hours ago wp-2fa.php 10 hours ago
class-plugin-deactivation.php
357 lines
1 <?php
2 /**
3 * Plugin deactivation class.
4 *
5 * Adds a form to request the reason for deactivation.
6 *
7 * @package Progress_Planner
8 */
9
10 namespace WP2FA_Deactivation_Feedback_Server;
11
12 if ( ! class_exists( '\WP2FA_Deactivation_Feedback_Server\Plugin_Deactivation' ) ) {
13
14 /**
15 * Plugin deactivation class.
16 */
17 class Plugin_Deactivation {
18
19 /**
20 * The plugin slug.
21 *
22 * This is used to identify and catch the deactivation trigger.
23 *
24 * @var string
25 */
26 const PLUGIN_SLUG = 'wp-2fa';
27
28 /**
29 * The remote API URL to send the deactivation reason to.
30 *
31 * @var string
32 */
33 const REMOTE_URL = 'https://proxytron.melapress.com';
34
35 /**
36 * Get the plugin slug suffix for premium version.
37 *
38 * @return string
39 */
40 public static function plugin_slug() {
41 $premium_version_slug = 'wp-2fa-premium/wp-2fa.php';
42 if ( \is_plugin_active( $premium_version_slug ) ) {
43 if ( ! function_exists( 'get_plugin_data' ) ) {
44 require_once ABSPATH . 'wp-admin/includes/plugin.php';
45 }
46 $plugin_file = WP_PLUGIN_DIR . '/' . $premium_version_slug;
47 $plugin_data = array();
48 if ( file_exists( $plugin_file ) ) {
49 $plugin_data = \get_plugin_data( $plugin_file, false, false );
50 }
51 $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_data['Name'] );
52
53 return $plugin_slug;
54 }
55
56 return self::PLUGIN_SLUG;
57 }
58
59 /**
60 * Constructor.
61 */
62 public function __construct() {
63 \add_action( 'admin_footer', array( $this, 'maybe_add_script' ) );
64 }
65
66 /**
67 * Maybe add the script to the admin footer.
68 *
69 * @return void
70 */
71 public function maybe_add_script() {
72 // Check if we're in the plugins page.
73 if ( ! \function_exists( 'get_current_screen' ) || ! \get_current_screen() || ( 'plugins' !== \get_current_screen()->id && 'plugins-network' !== \get_current_screen()->id ) ) {
74 return;
75 }
76 $this->the_popover();
77 $this->the_inline_script();
78 $this->the_inline_style();
79 }
80
81 /**
82 * The popover.
83 *
84 * @return void
85 */
86 protected function the_popover() {
87 $reasons = array(
88 array(
89 'id' => 'unexpected-behavior',
90 'label' => \__( 'The plugin isn\'t working, caused issues, or has a bug', 'wp-2fa' ),
91 'feedback_placeholder' => \__( 'Can you briefly describe the issue?', 'wp-2fa' ),
92 'feedback_type' => 'textarea',
93 ),
94 // [
95 // 'id' => 'wrong-feature',
96 // 'label' => \__( "It's not what I was looking for", 'wp-2fa' ),
97 // 'feedback_placeholder' => \__( 'What were you looking for?', 'wp-2fa' ),
98 // 'feedback_type' => 'textarea',
99 // ],
100 // [
101 // 'id' => 'not-working',
102 // 'label' => \__( 'The plugin is not working', 'wp-2fa' ),
103 // 'feedback_placeholder' => \__( 'Kindly share what didn\'t work so we can fix it for future users...', 'wp-2fa' ),
104 // 'feedback_type' => 'textarea',
105 // ],
106 array(
107 'id' => 'found-better-plugin',
108 'label' => \__( 'I found a better alternative', 'wp-2fa' ),
109 'feedback_placeholder' => \__( 'Which plugin did you switch to?', 'wp-2fa' ),
110 'feedback_type' => 'text',
111 ),
112 array(
113 'id' => 'missing-feature',
114 'label' => \__( 'The plugin is missing a specific feature', 'wp-2fa' ),
115 'feedback_placeholder' => \__( 'What feature were you looking for?', 'wp-2fa' ),
116 'feedback_type' => 'textarea',
117 ),
118 array(
119 'id' => 'hard-to-understand',
120 'label' => \__( 'The plugin is too hard to set up or understand', 'wp-2fa' ),
121 'feedback_placeholder' => \__( 'Can you tell us a bit more about this?', 'wp-2fa' ),
122 'feedback_type' => 'text',
123 ),
124 array(
125 'id' => 'temporary-deactivation',
126 'label' => \__( 'This is a temporary deactivation', 'wp-2fa' ),
127 'feedback_type' => false,
128 'feedback_placeholder' => false,
129 ),
130 );
131
132 // Randomize the order of the reasons.
133 \shuffle( $reasons );
134
135 // Add the "other" reason at the end.
136 $reasons[] = array(
137 'id' => 'other',
138 'label' => \__( 'Other', 'wp-2fa' ),
139 'feedback_placeholder' => false,
140 'feedback_type' => false,
141 );
142
143 ?>
144 <div id="<?php echo \esc_attr( self::plugin_slug() ); ?>-popover" popover>
145 <div style="text-align: center; margin-bottom: 1.5rem;">
146 <img src="<?php echo \esc_url( WP_2FA_URL . 'dist/images/wizard-logo.png' ); ?>" alt="<?php echo \esc_attr( 'Plugin Logo', 'wp-2fa' ); ?>" style="width: 50px; margin-bottom: 1rem;"> </div>
147 <h1><?php \esc_html_e( "We're sorry to see you go", 'wp-2fa' ); ?></h1>
148 <p><?php \esc_html_e( 'If you have a moment, please let us know why you are deactivating this plugin:', 'wp-2fa' ); ?></p>
149 <form>
150 <?php foreach ( $reasons as $reason ) : ?>
151 <div class="reason-wrapper" data-reason="<?php echo \esc_attr( $reason['id'] ); ?>">
152 <span class="radio-wrapper">
153 <input
154 id="deactivate-plugin-reason-<?php echo \esc_attr( $reason['id'] ); ?>"
155 type="radio"
156 name="reason"
157 value="<?php echo \esc_attr( $reason['id'] ); ?>"
158 >
159 <label for="deactivate-plugin-reason-<?php echo \esc_attr( $reason['id'] ); ?>">
160 <?php echo \esc_html( $reason['label'] ); ?>
161 </label>
162 </span>
163 <?php if ( $reason['feedback_type'] ) : ?>
164 <div class="feedback-wrapper">
165 <?php if ( 'textarea' === $reason['feedback_type'] ) : ?>
166 <textarea
167 id="deactivate-plugin-reason-<?php echo \esc_attr( $reason['id'] ); ?>-feedback"
168 name="feedback"
169 placeholder="<?php echo \esc_attr( $reason['feedback_placeholder'] ); ?>"
170 ></textarea>
171 <?php else : ?>
172 <input
173 id="deactivate-plugin-reason-<?php echo \esc_attr( $reason['id'] ); ?>-feedback"
174 type="text"
175 name="feedback"
176 placeholder="<?php echo \esc_attr( $reason['feedback_placeholder'] ); ?>"
177 >
178 <?php endif; ?>
179 </div>
180 <?php endif; ?>
181 </div>
182 <?php endforeach; ?>
183
184 <div class="actions">
185 <button type="button" class="submit"><?php \esc_html_e( 'Submit & Deactivate', 'wp-2fa' ); ?></button>
186 <button type="button" class="dismiss"><?php \esc_html_e( 'Skip & Deactivate', 'wp-2fa' ); ?></button>
187 </div>
188 </form>
189 </div>
190 <?php
191 }
192
193 /**
194 * The inline script.
195 *
196 * @return void
197 */
198 protected function the_inline_script() {
199 ?>
200 <script>
201 (function() {
202 // A helper function to make AJAX requests.
203 const deactivatePluginFeedbackAjaxRequest = ( { url, data, action } ) => {
204 const http = new XMLHttpRequest();
205 http.open( 'POST', url, true );
206 http.onreadystatechange = () => {
207 let response;
208 try {
209 response = JSON.parse( http.response );
210 } catch ( e ) {}
211 return action( response );
212 };
213 const dataForm = new FormData();
214 for ( let [ key, value ] of Object.entries( data ) ) {
215 dataForm.append( key, value );
216 }
217 http.send( dataForm );
218 }
219
220 // Add an event listener to the deactivate button.
221 const deactivateButton = document.getElementById( 'deactivate-<?php echo \esc_attr( self::plugin_slug() ); ?>' );
222
223 const deactivationPopover = document.getElementById( '<?php echo \esc_attr( self::plugin_slug() ); ?>-popover' );
224 if ( deactivateButton && deactivationPopover ) {
225 deactivateButton.addEventListener( 'click', function( e ) {
226 e.preventDefault();
227 deactivationPopover.showPopover();
228 } );
229
230 // Show/hide the feedback fields based on the selected reason.
231 deactivationPopover.querySelectorAll( '.reason-wrapper' ).forEach( function( reasonWrapper ) {
232 reasonWrapper.addEventListener( 'click', function( changeEvent ) {
233 const radio = reasonWrapper.querySelector( 'input[type="radio"]' );
234 if ( radio ) {
235 radio.checked = true;
236 }
237 const feedbackWrapper = reasonWrapper.querySelector( '.feedback-wrapper' );
238 // Hide any existing feedback fields.
239 deactivationPopover.querySelectorAll( '.feedback-wrapper' ).forEach( function( feedbackWrapper ) {
240 feedbackWrapper.style.display = 'none';
241 } );
242 if ( feedbackWrapper ) {
243 reasonWrapper.querySelector( '.feedback-wrapper' ).style.display = 'block';
244 }
245 } );
246 } );
247
248 // Handle clicking on the dismiss button.
249 deactivationPopover.querySelector( 'button.dismiss' ).addEventListener( 'click', function( dismissEvent ) {
250 dismissEvent.preventDefault();
251 window.location.href = deactivateButton.href;
252 } );
253
254 // Handle clicking on the submit button.
255 deactivationPopover.querySelector( 'button.submit' ).addEventListener( 'click', function( submitEvent ) {
256 submitEvent.preventDefault();
257 const requestData = {
258 action: 'plugin_deactivation',
259 plugin: '<?php echo \esc_attr( self::plugin_slug() ); ?>',
260 site: '<?php echo \esc_attr( get_site_url() ); ?>',
261 };
262 deactivatePluginFeedbackAjaxRequest( {
263 // Get a nonce from the remote server.
264 url: '<?php echo \esc_url( self::REMOTE_URL ); ?>/?rest_route=/deactivation-feedback-server/v1/get-nonce',
265 data: requestData,
266 action: ( response ) => {
267 response = response || {};
268 // Add the nonce to the request data, and build the data object for the feedback.
269 requestData.nonce = response.nonce;
270 const formData = new FormData( deactivationPopover.querySelector( 'form' ) );
271 requestData.reason = formData.get( 'reason' );
272 const feedbackEl = document.getElementById( `deactivate-plugin-reason-${requestData.reason}-feedback` );
273 requestData.feedback = feedbackEl ? feedbackEl.value : '';
274
275 // Make the request to the remote server to submit the feedback.
276 deactivatePluginFeedbackAjaxRequest( {
277 url: '<?php echo \esc_url( self::REMOTE_URL ); ?>/?rest_route=/deactivation-feedback-server/v1/submit-feedback',
278 data: requestData,
279 action: ( response ) => {
280 window.location.href = deactivateButton.href;
281 },
282 } );
283 },
284 } );
285
286 // Submit the form.
287 deactivationPopover.hidePopover();
288 } );
289 }
290 })();
291 </script>
292 <?php
293 }
294
295 /**
296 * The inline style.
297 *
298 * @return void
299 */
300 protected function the_inline_style() {
301 ?>
302 <style>
303 #<?php echo \esc_attr( self::plugin_slug() ); ?>-popover {
304 border: 1px solid #ccc;
305 padding: 2rem;
306 border-radius: 8px;
307
308 form {
309 display: flex;
310 flex-direction: column;
311 gap: 1rem;
312
313 .reason-wrapper {
314 display: flex;
315 gap: 1rem;
316 flex-direction: column;
317
318 .feedback-wrapper {
319 display: none;
320
321 textarea, input {
322 width: 100%;
323 border: 1px solid #ccc;
324 border-radius: 4px;
325 padding: 0.5rem;
326 }
327 }
328 }
329
330 .actions {
331 display: flex;
332 gap: 1rem;
333 }
334
335 button {
336 padding: 0.5rem 1rem;
337 border-radius: 4px;
338 border: 1px solid #ccc;
339 background-color: #40D3F0;
340 cursor: pointer;
341 color: #fff;
342 margin: 0;
343
344 &.dismiss {
345 background-color: #fff;
346 cursor: pointer;
347 color: #000;
348 }
349 }
350 }
351 }
352 </style>
353 <?php
354 }
355 }
356 }
357