WP Htaccess Editor - Version 1.65

Version Description

Download this release

Release Info

Developer WebFactory
Plugin Icon 128x128 WP Htaccess Editor
Version 1.65
Comparing to
See all releases

Code changes from version 1.60 to 1.65

css/wp-htaccess-editor.css CHANGED
@@ -3,9 +3,14 @@
3
  * (c) WebFactory Ltd, 2018-2019
4
  */
5
 
 
 
 
 
6
  .settings_page_wp-htaccess-editor .card {
7
  padding: 1em 2em 1em 2em;
8
- width: 520px;
 
9
  }
10
 
11
  .settings_page_wp-htaccess-editor h1 img {
@@ -22,6 +27,7 @@
22
 
23
  .settings_page_wp-htaccess-editor .card.collapsed p,
24
  .settings_page_wp-htaccess-editor .card.collapsed b,
 
25
  .settings_page_wp-htaccess-editor .card.collapsed ul {
26
  display: none;
27
  }
@@ -61,9 +67,9 @@
61
 
62
  #enable-editor-notice {
63
  background-color: #dd3036e0;
64
- width: 574px;
65
  min-height: 300px;
66
- height: calc(100vh - 400px);
67
  position: absolute;
68
  z-index: 10;
69
  padding: 30px;
@@ -81,7 +87,7 @@
81
  }
82
 
83
  #wphe-buttons .button {
84
- margin-right: 15px;
85
  }
86
 
87
  .settings_page_wp-htaccess-editor .red {
@@ -133,11 +139,11 @@
133
 
134
  #htaccess-editor-wrap .CodeMirror,
135
  #htaccess-editor-wrap textarea {
136
- width: 574px;
137
  min-width: 300px;
138
  max-width: 100%;
139
- min-height: 300px;
140
- height: calc(100vh - 400px);
141
  border: 1px solid #ddd;
142
  box-sizing: border-box;
143
  }
3
  * (c) WebFactory Ltd, 2018-2019
4
  */
5
 
6
+ body, p {
7
+ font-size: 14px;
8
+ }
9
+
10
  .settings_page_wp-htaccess-editor .card {
11
  padding: 1em 2em 1em 2em;
12
+ max-width: 620px;
13
+ box-sizing: content-box;
14
  }
15
 
16
  .settings_page_wp-htaccess-editor h1 img {
27
 
28
  .settings_page_wp-htaccess-editor .card.collapsed p,
29
  .settings_page_wp-htaccess-editor .card.collapsed b,
30
+ .settings_page_wp-htaccess-editor .card.collapsed ol,
31
  .settings_page_wp-htaccess-editor .card.collapsed ul {
32
  display: none;
33
  }
67
 
68
  #enable-editor-notice {
69
  background-color: #dd3036e0;
70
+ width: 620px;
71
  min-height: 300px;
72
+ height: 400px;
73
  position: absolute;
74
  z-index: 10;
75
  padding: 30px;
87
  }
88
 
89
  #wphe-buttons .button {
90
+ margin-right: 20px;
91
  }
92
 
93
  .settings_page_wp-htaccess-editor .red {
139
 
140
  #htaccess-editor-wrap .CodeMirror,
141
  #htaccess-editor-wrap textarea {
142
+ width: 620px;
143
  min-width: 300px;
144
  max-width: 100%;
145
+ min-height: 250px;
146
+ height: 400px;
147
  border: 1px solid #ddd;
148
  box-sizing: border-box;
149
  }
js/wp-htaccess-editor.js CHANGED
@@ -31,17 +31,23 @@ jQuery(document).ready(function($) {
31
  return true;
32
  }
33
  });
34
-
35
  let wphe_width = parseInt(localStorage.getItem('wphe-editor-width'), 10);
36
  let wphe_height = parseInt(localStorage.getItem('wphe-editor-height'), 10);
37
-
 
 
 
 
 
38
  if (wphe_width > 0 && wphe_height > 0) {
39
  $('.CodeMirror')
40
  .width(wphe_width)
41
  .height(wphe_height);
 
42
  $('#enable-editor-notice')
43
- .width(wphe_width)
44
- .height(wphe_height);
45
  }
46
  }); // CodeMirror setup
47
 
@@ -189,6 +195,7 @@ jQuery(document).ready(function($) {
189
  showConfirmButton: false,
190
  timer: 1000
191
  });
 
192
  }
193
  });
194
  } else if (typeof data.success != 'undefined' && !data.success) {
@@ -211,6 +218,57 @@ jQuery(document).ready(function($) {
211
  return false;
212
  }); // save htaccess
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  // restore htaccess backup from DB
215
  $('#wphe_restore_htaccess').click(function(e) {
216
  message = wp_htaccess_editor.restore_message;
31
  return true;
32
  }
33
  });
34
+
35
  let wphe_width = parseInt(localStorage.getItem('wphe-editor-width'), 10);
36
  let wphe_height = parseInt(localStorage.getItem('wphe-editor-height'), 10);
37
+
38
+ if(!(wphe_width > 0 && wphe_height > 0)){
39
+ wphe_width = 676;
40
+ wphe_height = 400;
41
+ }
42
+
43
  if (wphe_width > 0 && wphe_height > 0) {
44
  $('.CodeMirror')
45
  .width(wphe_width)
46
  .height(wphe_height);
47
+
48
  $('#enable-editor-notice')
49
+ .innerHeight(wphe_height)
50
+ .innerWidth(wphe_width);
51
  }
52
  }); // CodeMirror setup
53
 
195
  showConfirmButton: false,
196
  timer: 1000
197
  });
198
+ $('#wphe-rating-notice').show();
199
  }
200
  });
201
  } else if (typeof data.success != 'undefined' && !data.success) {
218
  return false;
219
  }); // save htaccess
220
 
221
+
222
+ // test htaccess file
223
+ $('#wphe_test_htaccess').click(function(e) {
224
+ e.preventDefault();
225
+
226
+ block_ui(wp_htaccess_editor.testing);
227
+ $.post({
228
+ url: ajaxurl,
229
+ data: {
230
+ action: 'wp_htaccess_editor_do_action',
231
+ _ajax_nonce: wp_htaccess_editor.nonce_do_action,
232
+ subaction: 'test_htaccess',
233
+ new_content: wp.themePluginEditor.instance.codemirror.getValue()
234
+ }
235
+ })
236
+ .always(function(data) {
237
+ swal.close();
238
+ })
239
+ .done(function(data) {
240
+ if (typeof data.success != 'undefined' && data.success) {
241
+ swal({
242
+ type: 'success',
243
+ title: wp_htaccess_editor.test_success,
244
+ html: data.data
245
+ });
246
+ } else if (typeof data.success != 'undefined' && !data.success) {
247
+ swal({
248
+ type: 'error',
249
+ title: wp_htaccess_editor.test_failed,
250
+ html: data.data });
251
+ } else {
252
+ swal({
253
+ type: 'error',
254
+ title: wp_htaccess_editor.undocumented_error });
255
+ }
256
+ })
257
+ .fail(function(data) {
258
+ if (data.data) {
259
+ swal({
260
+ type: 'error',
261
+ title: wp_htaccess_editor.test_failed
262
+ });
263
+ } else {
264
+ swal({ type: 'error', title: wp_htaccess_editor.undocumented_error });
265
+ }
266
+ });
267
+
268
+ return false;
269
+ }); // test htaccess
270
+
271
+
272
  // restore htaccess backup from DB
273
  $('#wphe_restore_htaccess').click(function(e) {
274
  message = wp_htaccess_editor.restore_message;
readme.txt CHANGED
@@ -4,23 +4,28 @@ Contributors: WebFactory, UnderConstructionPage, googlemapswidget, securityninja
4
  Requires at least: 4.0
5
  Requires PHP: 5.2
6
  Tested up to: 5.2
7
- Stable tag: 1.60
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- A safe & simple htaccess file editor. Automatically creates htaccess file backups.
12
 
13
  == Description ==
14
 
15
- <a href="https://wphtaccess.com/?utm_source=wordpressorg&utm_medium=content&utm_campaign=wp-htaccess-editor&utm_term=readme-top">WP Htaccess Editor</a> provides a **simple, safe & fast way** to edit the site's .htaccess file from WP admin. It automatically creates a backup every time you make a change to the htaccess file. Backups can be restored directly from the plugin, or via FTP if the errors in .htaccess file prevent WP from running normally. For all questions, including support please use the official <a href="https://wordpress.org/support/plugin/wp-htaccess-editor">forum</a>.
16
 
17
  Access WP Htaccess Editor via WP Admin - Settings menu.
18
 
 
 
 
 
19
  #### Automatic Backups
20
 
21
  Htaccess Editor makes automatic backups of htaccess file every time you make a change to it. Backups are located in `/wp-content/htaccess-editor-backups/` and timestamped so you can easily find the latest htaccess backup and restore it.
22
 
23
  #### WordPress Network (WPMU) Support
 
24
  WP Htaccess Editor is fully compatible and tested with WP Network (WPMU). It shows up under the Settings menu in network admin. It's not available in individual sites as there is only one .htaccess file per network.
25
 
26
 
@@ -50,6 +55,11 @@ Or if needed, upload manually;
50
 
51
 
52
  == Changelog ==
 
 
 
 
 
53
 
54
  = v1.60 =
55
  * 2019/03/12
4
  Requires at least: 4.0
5
  Requires PHP: 5.2
6
  Tested up to: 5.2
7
+ Stable tag: 1.65
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ A safe & simple htaccess file editor. Test htaccess syntax before saving & automatically create htaccess file backups.
12
 
13
  == Description ==
14
 
15
+ <a href="https://wphtaccess.com/?utm_source=wordpressorg&utm_medium=content&utm_campaign=wp-htaccess-editor&utm_term=readme-top">WP Htaccess Editor</a> provides a **simple, safe & fast way** to edit & test the site's .htaccess file from WP admin. Before saving htaccess can be tested for syntax errors. It also automatically creates a backup every time you make a change to the htaccess file. Backups can be restored directly from the plugin, or via FTP if the errors in .htaccess file prevent WP from running normally. For all questions, including support please use the official <a href="https://wordpress.org/support/plugin/wp-htaccess-editor">forum</a>.
16
 
17
  Access WP Htaccess Editor via WP Admin - Settings menu.
18
 
19
+ #### Testing Htaccess Syntax
20
+
21
+ Use the "test before saving" button to test htaccess file syntax before saving. Please note that this test does not check the logic of your htaccess file, ie if the redirects work as intended. It only checks for syntax errors.
22
+
23
  #### Automatic Backups
24
 
25
  Htaccess Editor makes automatic backups of htaccess file every time you make a change to it. Backups are located in `/wp-content/htaccess-editor-backups/` and timestamped so you can easily find the latest htaccess backup and restore it.
26
 
27
  #### WordPress Network (WPMU) Support
28
+
29
  WP Htaccess Editor is fully compatible and tested with WP Network (WPMU). It shows up under the Settings menu in network admin. It's not available in individual sites as there is only one .htaccess file per network.
30
 
31
 
55
 
56
 
57
  == Changelog ==
58
+ = v1.65 =
59
+ * 2019/08/16
60
+ * fixed a few bugs
61
+ * new feature: test htaccess file for syntax errors before saving
62
+ * 50,000 installations hit on 2019-08-10
63
 
64
  = v1.60 =
65
  * 2019/03/12
wp-htaccess-editor.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
- /*
3
  Plugin Name: WP Htaccess Editor
4
  Plugin URI: https://wphtaccess.com/
5
  Description: Safe and easy way to edit the .htaccess file directly from WP admin without using FTP.
6
- Version: 1.60
7
  Author: WebFactory Ltd
8
  Author URI: https://www.webfactoryltd.com/
9
  Text Domain: wp-htaccess-editor
@@ -81,6 +81,7 @@ class WP_Htaccess_Editor
81
  add_filter('plugin_action_links_' . $this->plugin_basename, array($this, 'plugin_action_links'));
82
  add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
83
  add_filter('admin_footer_text', array($this, 'admin_footer_text'));
 
84
  } // __construct
85
 
86
 
@@ -556,6 +557,9 @@ class WP_Htaccess_Editor
556
  'restore_title' => __('Restore last backup?', 'wp-htaccess-editor'),
557
  'restore_button' => __('Restore Last Saved Backup of .htaccess', 'wp-htaccess-editor'),
558
  'restore_success' => __('Backup has been successfully restored. Click OK to reload the page.', 'wp-htaccess-editor'),
 
 
 
559
  'site_error' => __('There is an error in the .htaccess file and your site is probably no longer accessible.<br><br>DO NOT panic or reload this page. Close this message. First, try the "Restore Last Backup" button. If it doesn\'t work read instruction on this very page on how to restore the site.', 'wp-htaccess-editor'),
560
  'nonce_dismiss_notice' => wp_create_nonce('wp-htaccess-editor_dismiss_notice'),
561
  'nonce_do_action' => wp_create_nonce('wp-htaccess-editor_do_action'),
@@ -622,6 +626,43 @@ class WP_Htaccess_Editor
622
  }
623
 
624
  wp_send_json_success();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  } elseif ($subaction == 'restore_htaccess_from_db') {
626
  $res = $this->restore_db_backup();
627
  if (!is_wp_error($res)) {
@@ -808,16 +849,6 @@ class WP_Htaccess_Editor
808
  $notice_shown = true;
809
  }
810
 
811
- // ask for rating after first save
812
- if ($meta['edits_count'] > 0 && false === $notice_shown && false == $this->get_dismissed_notices('rate')) {
813
- echo '<div class="card notice-wrapper">';
814
- echo '<h2>' . __('Please help us keep the plugin free &amp; up-to-date', 'wp-htaccess-editor') . '</h2>';
815
- echo '<p>' . __('If you use &amp; enjoy WP Htaccess Editor, <b>please rate it on WordPress.org</b>. It only takes a second and helps us keep the plugin free and maintained. Thank you!', 'wp-htaccess-editor') . '</p>';
816
- echo '<p><a class="button-primary button" title="' . __('Rate WP Htaccess Editor', 'wp-htaccess-editor') . '" target="_blank" href="https://wordpress.org/support/plugin/wp-htaccess-editor/reviews/#new-post">' . __('Help keep the plugin free - rate it!', 'wp-htaccess-editor') . '</a> <a href="#" class="wphe-dismiss-notice dismiss-notice-rate" data-notice="rate">' . __('I\'ve already rated it', 'wp-htaccess-editor') . '</a></p>';
817
- echo '</div>';
818
- $notice_shown = true;
819
- }
820
-
821
  // TODO: properly mark for translation
822
  echo '<div class="card" id="card-description">';
823
  echo '<a class="toggle-card" href="#" title="' . __('Collapse / expand box', 'wp-htaccess-editor') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
@@ -827,7 +858,7 @@ class WP_Htaccess_Editor
827
  echo 'For more details about .htaccess syntax and examples, please visit the <a href="http://httpd.apache.org/docs/current/howto/htaccess.html" target="_blank">official Apache Tutorial</a>.</p>';
828
 
829
  echo '<b>How to restore the site in case of error 500 or white screen caused by .htaccess</b>';
830
- echo '<p>Do not panic. No data is lost, and your site will be up again in minutes. FTP to your site or open the server\'s control panel such as cPanel to locate the .htaccess file in <code>' . $this->get_htaccess_path() . '</code>. Once you find the file there are several options to restore the site.<br><br>The first option is to edit the file and fix the error(s) you made.<br><br>The second one is to delete the file. Obviously, any custom rules in it will be gone, and in order for permalinks to work again you have to visit <a href="' . admin_url('options-permalink.php') . '">WP Admin - Options - Permalinks</a> and click "Save Changes". This will rebuild the default .htaccess file.<br><br>Third (and preferred) way of fixing is to restore the file from the backup which you\'ll find in the <code>' . $this->get_backup_folder() . '</code> folder. The folder will probably contain multiple backup files. Locate the latest one by looking at the timestamp in the filename. Once located copy the file to <code>' . $this->get_htaccess_path(true) . '</code> and rename it to .htaccess.</p>';
831
 
832
  echo '<b>How to restore .htaccess in case of a non-white-screen error</b>';
833
  echo '<p>Click the "Restore Last Saved Backup" button below the editor and .htaccess will be restored to the version before the last save. Please note that this method only works if the error in the file is logical, not syntactical. For instance, if you banned the wrong IP you can undo. But if you misspelled "RewriteCond" you have to use the method above as the only way to recover is via FTP or cPanel.</p>';
@@ -836,6 +867,25 @@ class WP_Htaccess_Editor
836
  echo '<p>For additional support and questions, please visit the <a href="https://wordpress.org/support/plugin/wp-htaccess-editor" target="_blank">official support forum</a> or <a href="mailto:support@webfactoryltd.com?subject=WP Htaccess Editor problems">email us</a> if the issue can\'t be discussed publically.</p>';
837
  echo '</div>';
838
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
  echo '<div id="htaccess-editor-wrap">';
840
  if (false == $this->get_dismissed_notices('editor-warning')) {
841
  echo '<div id="enable-editor-notice" class="notice-wrapper"><h3><strong>Please be careful when editing the .htaccess file!</strong><br>This plugin makes automatic backups every time you make a change. Detailed instructions on how to restore backups are available in the box above.</h3><br><a href="#" data-notice="editor-warning" class="wphe-dismiss-notice button button-secondary">I understand. Enable the editor.</a></div>';
@@ -843,13 +893,94 @@ class WP_Htaccess_Editor
843
  echo '<textarea cols="70" rows="20" name="newcontent" id="newcontent" aria-describedby="editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4">' . esc_textarea($htaccess_content) . '</textarea>';
844
  echo '</div>';
845
 
846
- echo '<p id="wphe-buttons" style="' . ($this->get_dismissed_notices('editor-warning') ? '' : 'display: none;') . '"><a id="wphe_save_htaccess" href="#" class="button button-primary">Save Changes</a> <a id="wphe_restore_htaccess" href="#" class="button button-secondary">Restore Last Backup</a></p>';
 
 
 
 
847
 
848
  echo '</form>';
849
  echo '</div>'; // wrap
850
  } // plugin_page
851
 
852
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
853
  /**
854
  * Clean up on uninstall; no action on deactive at the moment.
855
  *
1
  <?php
2
+ /*
3
  Plugin Name: WP Htaccess Editor
4
  Plugin URI: https://wphtaccess.com/
5
  Description: Safe and easy way to edit the .htaccess file directly from WP admin without using FTP.
6
+ Version: 1.65
7
  Author: WebFactory Ltd
8
  Author URI: https://www.webfactoryltd.com/
9
  Text Domain: wp-htaccess-editor
81
  add_filter('plugin_action_links_' . $this->plugin_basename, array($this, 'plugin_action_links'));
82
  add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
83
  add_filter('admin_footer_text', array($this, 'admin_footer_text'));
84
+ add_filter('install_plugins_table_api_args_featured', array($this, 'featured_plugins_tab'));
85
  } // __construct
86
 
87
 
557
  'restore_title' => __('Restore last backup?', 'wp-htaccess-editor'),
558
  'restore_button' => __('Restore Last Saved Backup of .htaccess', 'wp-htaccess-editor'),
559
  'restore_success' => __('Backup has been successfully restored. Click OK to reload the page.', 'wp-htaccess-editor'),
560
+ 'test_success' => __('Test Completed Successfully', 'wp-htaccess-editor'),
561
+ 'test_failed' => __('Test Failed', 'wp-htaccess-editor'),
562
+ 'testing' => __('Testing .htaccess syntax. Please wait.', 'wp-htaccess-editor'),
563
  'site_error' => __('There is an error in the .htaccess file and your site is probably no longer accessible.<br><br>DO NOT panic or reload this page. Close this message. First, try the "Restore Last Backup" button. If it doesn\'t work read instruction on this very page on how to restore the site.', 'wp-htaccess-editor'),
564
  'nonce_dismiss_notice' => wp_create_nonce('wp-htaccess-editor_dismiss_notice'),
565
  'nonce_do_action' => wp_create_nonce('wp-htaccess-editor_do_action'),
626
  }
627
 
628
  wp_send_json_success();
629
+ } elseif ($subaction == 'test_htaccess') {
630
+ $new_content = wp_unslash(trim($_POST['new_content']));
631
+ $uploads_directory = wp_upload_dir();
632
+ $test_id = rand(1000, 9999);
633
+ $htaccess_test_folder = $uploads_directory['basedir'] . '/htaccess-test-' . $test_id . '/';
634
+ $htaccess_test_url = $uploads_directory['baseurl'] . '/htaccess-test-' . $test_id . '/';
635
+
636
+ // Create test directory and files
637
+ if (!$this->wp_filesystem->is_dir($htaccess_test_folder)) {
638
+ if (true !== $this->wp_filesystem->mkdir($htaccess_test_folder, 0777)) {
639
+ wp_send_json_error(__('Failed to create test directory. Please check that your uploads folder is writable.', 'wp-htaccess-editor'));
640
+ }
641
+ }
642
+
643
+ if (true !== $this->wp_filesystem->put_contents($htaccess_test_folder . 'index.html', 'htaccess-test-' . $test_id)) {
644
+ wp_send_json_error(__('Failed to create test files. Please check that your uploads folder is writable.', 'wp-htaccess-editor'));
645
+ }
646
+
647
+ if (true !== $this->wp_filesystem->put_contents($htaccess_test_folder . '.htaccess', $new_content)) {
648
+ wp_send_json_error(__('Failed to create test directory and files. Please check that your uploads folder is writeable.', 'wp-htaccess-editor'));
649
+ }
650
+
651
+ // Retrieve test file over http
652
+ $response = wp_remote_get($htaccess_test_url . 'index.html', array('sslverify' => false, 'redirection' => 0));
653
+ $response_code = wp_remote_retrieve_response_code($response);
654
+
655
+ // Remove Test Directory
656
+ $this->wp_filesystem->delete($htaccess_test_folder . '.htaccess');
657
+ $this->wp_filesystem->delete($htaccess_test_folder . 'index.html');
658
+ $this->wp_filesystem->rmdir($htaccess_test_folder);
659
+
660
+ // Check if test file content is what we expect
661
+ if ((in_array($response_code, range(200, 299)) && !is_wp_error($response) && wp_remote_retrieve_body($response) == 'htaccess-test-' . $test_id) || (in_array($response_code, range(300, 399)) && !is_wp_error($response))) {
662
+ wp_send_json_success(__('This test only makes sure there are no syntax errors that could result in 500 errors for your entire site. It does not check the logic of the <i>.htaccess</i> file, ie if redirects work as intended.', 'wp-htaccess-editor'));
663
+ } else {
664
+ wp_send_json_error(__('There are syntax errors in your unsaved <i>.htaccess</i> content. Saving it will cause your entire site, including the admin, to become inaccessible. Fix the errors before saving.', 'wp-htaccess-editor'));
665
+ }
666
  } elseif ($subaction == 'restore_htaccess_from_db') {
667
  $res = $this->restore_db_backup();
668
  if (!is_wp_error($res)) {
849
  $notice_shown = true;
850
  }
851
 
 
 
 
 
 
 
 
 
 
 
852
  // TODO: properly mark for translation
853
  echo '<div class="card" id="card-description">';
854
  echo '<a class="toggle-card" href="#" title="' . __('Collapse / expand box', 'wp-htaccess-editor') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
858
  echo 'For more details about .htaccess syntax and examples, please visit the <a href="http://httpd.apache.org/docs/current/howto/htaccess.html" target="_blank">official Apache Tutorial</a>.</p>';
859
 
860
  echo '<b>How to restore the site in case of error 500 or white screen caused by .htaccess</b>';
861
+ echo '<p>Do not panic. No data is lost, and your site will be up again in minutes. FTP to your site or open the server\'s control panel such as cPanel to locate the .htaccess file in <code>' . $this->get_htaccess_path() . '</code>. Once you find the file there are several options to restore the site;<ol><li>Edit the file and fix the error(s) you made, or</li><li>Delete the file. Obviously, any custom rules in it will be gone, and in order for permalinks to work again you have to visit <a href="' . admin_url('options-permalink.php') . '">WP Admin - Options - Permalinks</a> and click "Save Changes". This will rebuild the default .htaccess file, or</li><li>Third (and preferred) way of fixing is to restore the file from the backup which you\'ll find in the <code>' . $this->get_backup_folder() . '</code> folder. The folder will probably contain multiple backup files. Locate the latest one by looking at the timestamp in the filename. Once located copy the file to <code>' . $this->get_htaccess_path(true) . '</code> and rename it to .htaccess.</li></ol>';
862
 
863
  echo '<b>How to restore .htaccess in case of a non-white-screen error</b>';
864
  echo '<p>Click the "Restore Last Saved Backup" button below the editor and .htaccess will be restored to the version before the last save. Please note that this method only works if the error in the file is logical, not syntactical. For instance, if you banned the wrong IP you can undo. But if you misspelled "RewriteCond" you have to use the method above as the only way to recover is via FTP or cPanel.</p>';
867
  echo '<p>For additional support and questions, please visit the <a href="https://wordpress.org/support/plugin/wp-htaccess-editor" target="_blank">official support forum</a> or <a href="mailto:support@webfactoryltd.com?subject=WP Htaccess Editor problems">email us</a> if the issue can\'t be discussed publically.</p>';
868
  echo '</div>';
869
 
870
+
871
+ // ask for rating after first save
872
+ if (true == $this->get_dismissed_notices('rate')) {
873
+ // notice dismissed, never show again
874
+ } else {
875
+ if ($meta['edits_count'] > 0 && false === $notice_shown) {
876
+ $notice_shown = true;
877
+ $show_rate_notice = '';
878
+ } else {
879
+ $show_rate_notice = 'display: none;';
880
+ }
881
+
882
+ echo '<div id="wphe-rating-notice" class="card notice-wrapper" style="' . $show_rate_notice . '">';
883
+ echo '<h2>' . __('Please help us keep the plugin free &amp; up-to-date', 'wp-htaccess-editor') . '</h2>';
884
+ echo '<p>' . __('If you use &amp; enjoy WP Htaccess Editor, <b>please rate it on WordPress.org</b>. It only takes a second and helps us keep the plugin free and maintained. Thank you!', 'wp-htaccess-editor') . '</p>';
885
+ echo '<p><a class="button-primary button" title="' . __('Rate WP Htaccess Editor', 'wp-htaccess-editor') . '" target="_blank" href="https://wordpress.org/support/plugin/wp-htaccess-editor/reviews/#new-post">' . __('Help keep the plugin free - rate it!', 'wp-htaccess-editor') . '</a> <a href="#" class="wphe-dismiss-notice dismiss-notice-rate" data-notice="rate">' . __('I\'ve already rated it', 'wp-htaccess-editor') . '</a></p>';
886
+ echo '</div>';
887
+ }
888
+
889
  echo '<div id="htaccess-editor-wrap">';
890
  if (false == $this->get_dismissed_notices('editor-warning')) {
891
  echo '<div id="enable-editor-notice" class="notice-wrapper"><h3><strong>Please be careful when editing the .htaccess file!</strong><br>This plugin makes automatic backups every time you make a change. Detailed instructions on how to restore backups are available in the box above.</h3><br><a href="#" data-notice="editor-warning" class="wphe-dismiss-notice button button-secondary">I understand. Enable the editor.</a></div>';
893
  echo '<textarea cols="70" rows="20" name="newcontent" id="newcontent" aria-describedby="editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4">' . esc_textarea($htaccess_content) . '</textarea>';
894
  echo '</div>';
895
 
896
+ echo '<p id="wphe-buttons" style="' . ($this->get_dismissed_notices('editor-warning') ? '' : 'display: none;') . '">';
897
+ echo '<a id="wphe_save_htaccess" href="#" class="button button-primary"> Save Changes</a>';
898
+ echo '<a id="wphe_test_htaccess" href="#" class="button button-secondary">Test Before Saving</a>';
899
+ echo '<a id="wphe_restore_htaccess" href="#" class="button button-secondary">Restore Last Backup</a>';
900
+ echo '</p>';
901
 
902
  echo '</form>';
903
  echo '</div>'; // wrap
904
  } // plugin_page
905
 
906
 
907
+ /**
908
+ * Adds plugins to recommended list
909
+ *
910
+ * @param object $res List of plugins for recommended list
911
+ *
912
+ * @return object
913
+ */
914
+ function plugins_api_result($res)
915
+ {
916
+ remove_filter('plugins_api_result', array($this, 'plugins_api_result'), 10);
917
+
918
+ $res = $this->add_plugin_favs('wp-reset', $res);
919
+ $res = $this->add_plugin_favs('eps-301-redirects', $res);
920
+
921
+ return $res;
922
+ } // plugins_api_result
923
+
924
+
925
+ /**
926
+ * Helper function for adding plugins to recommended list
927
+ *
928
+ * @param $args
929
+ *
930
+ * @return $args
931
+ */
932
+ function featured_plugins_tab($args)
933
+ {
934
+ add_filter('plugins_api_result', array($this, 'plugins_api_result'), 10);
935
+
936
+ return $args;
937
+ } // featured_plugins_tab
938
+
939
+
940
+ /**
941
+ * Fetches plugin info from cache or wp.org
942
+ *
943
+ * @param string $plugin_slug Plugin slug
944
+ * @param object $res List of plugins
945
+ *
946
+ * @return object $res List of plugins
947
+ */
948
+ function add_plugin_favs($plugin_slug, $res)
949
+ {
950
+ if (!empty($res->plugins) && is_array($res->plugins)) {
951
+ foreach ($res->plugins as $plugin) {
952
+ if (is_object($plugin) && !empty($plugin->slug) && $plugin->slug == $plugin_slug) {
953
+ return $res;
954
+ }
955
+ } // foreach
956
+ }
957
+
958
+ $plugin_info = get_transient('wf-plugin-info-' . $plugin_slug);
959
+ if ($plugin_info && is_object($plugin_info)) {
960
+ array_unshift($res->plugins, $plugin_info);
961
+ } else {
962
+ $plugin_info = plugins_api('plugin_information', array(
963
+ 'slug' => $plugin_slug,
964
+ 'is_ssl' => is_ssl(),
965
+ 'fields' => array(
966
+ 'banners' => true,
967
+ 'reviews' => true,
968
+ 'downloaded' => true,
969
+ 'active_installs' => true,
970
+ 'icons' => true,
971
+ 'short_description' => true,
972
+ )
973
+ ));
974
+ if (!is_wp_error($plugin_info)) {
975
+ array_unshift($res->plugins, $plugin_info);
976
+ set_transient('wf-plugin-info-' . $plugin_slug, $plugin_info, DAY_IN_SECONDS * 7);
977
+ }
978
+ }
979
+
980
+ return $res;
981
+ } // add_plugin_favs
982
+
983
+
984
  /**
985
  * Clean up on uninstall; no action on deactive at the moment.
986
  *