Permalink Manager Lite - Version 0.4

Version Description

  • Rewrite rules are no longer used (SQL queries are optimized). The plugin uses now 'request' filter to detect the page/post that should be loaded instead.
  • Now full URI (including slug) is editable.
  • A few major improvements applied.
  • Partial code optimization.
Download this release

Release Info

Developer mbis
Plugin Icon 128x128 Permalink Manager Lite
Version 0.4
Comparing to
See all releases

Code changes from version 0.3.4 to 0.4

README.txt CHANGED
@@ -1,13 +1,13 @@
1
  === Permalink Manager ===
2
  Contributors: mbis
3
- Tags: urls, permalinks, slugs
4
  Requires at least: 4.0
5
- Tested up to: 4.5.2
6
- Stable tag: 0.3.4
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
- Permalink Manager allows you to control and reset the permalinks (slugs) in all your post types.
11
 
12
  == Description ==
13
 
@@ -15,18 +15,18 @@ It is a really simple plugin that helps managing the permalinks that are used fo
15
 
16
  There are three main functionalities of this plugin:
17
 
18
- 1. You can manually adjust the slugs of selected posts/pages/custom post type items.
19
- 2. You can replace particular (sub)string that is a part of slug with another (sub)string.
20
- 3. You can regenerate/reset the slugs for your posts/pages/custom post types. This might be especially useful if your post titles are updated and slugs needs to be recreated.
21
  4. You can change the default permalink bases (permastructures) for all custom post types & posts and pages (experimental functionality).
22
 
23
  = Example =
24
 
25
  If you want to quickly replace a part of slug (eg. `krakow` with another word `gdansk`):
26
 
27
- `http://example.com/hotels-in-krakow
28
- http://example.com/restaurants-in-krakow
29
- http://example.com/transport-in-krakow
30
  http://example.com/blog/krakow-the-best-city-for-tourists
31
  http://example.com/poland/cities/krakow
32
  http://example.com/poland/cities/stalowa-wola
@@ -35,9 +35,9 @@ http://example.com/poland/cities/poznan`
35
 
36
  If you use the form from `Find and replace` section your URLs will be changed to:
37
 
38
- `http://example.com/hotels-in-gdansk
39
- http://example.com/restaurants-in-gdansk
40
- http://example.com/transport-in-gdansk
41
  http://example.com/blog/gdansk-the-best-city-for-tourists
42
  http://example.com/poland/cities/gdansk
43
  http://example.com/poland/cities/stalowa-wola
@@ -50,6 +50,7 @@ In the next version of plugin more functionalities will be added:
50
 
51
  * Support for taxonomies
52
  * REGEX for `Find and replace` section
 
53
  * Two-step updater, so you can double-check which permalinks will be changed before the change is applied
54
  * AJAX support.
55
 
@@ -71,14 +72,22 @@ After the plugin is installed you can access its dashboard from this page: `Tool
71
  2. Find and replace section.
72
  3. Regenerate section.
73
  4. Custom permastructures.
 
 
74
 
75
  == Frequently Asked Questions ==
76
 
77
- = Q. The permalinks did not work any longer after the plugin was removed. =
78
- A. You need to flush the original rewrite rules. Go to "Settings -> Permalinks" `/wp-admin/options-permalink.php` and click on "Save Changes".
79
 
80
  == Changelog ==
81
 
 
 
 
 
 
 
82
  = 0.3.4 =
83
  * Hotfix for not working custom taxonomies tags.
84
  * Now the rewrite rules for custom post types are stored in different way.
1
  === Permalink Manager ===
2
  Contributors: mbis
3
+ Tags: urls, permalinks, slugs, custom url, custom permalinks, uris, url, slug, permalink
4
  Requires at least: 4.0
5
+ Tested up to: 4.5.3
6
+ Stable tag: 0.4
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
+ Permalink Manager allows you to control how the permalinks for custom post types are created & regenerate them.
11
 
12
  == Description ==
13
 
15
 
16
  There are three main functionalities of this plugin:
17
 
18
+ 1. You can manually adjust the URIs & slugs of all posts/pages/custom post type items.
19
+ 2. You can replace particular (sub)string that is a part of slug and/or URI with another (sub)string.
20
+ 3. You can regenerate/reset the slugs and/or URIs for your posts/pages/custom post types. This might be especially useful if your post titles are updated and slugs needs to be recreated.
21
  4. You can change the default permalink bases (permastructures) for all custom post types & posts and pages (experimental functionality).
22
 
23
  = Example =
24
 
25
  If you want to quickly replace a part of slug (eg. `krakow` with another word `gdansk`):
26
 
27
+ `http://example.com/krakow/hotels-in-krakow
28
+ http://example.com/krakow/restaurants-in-krakow
29
+ http://example.com/krakow/transport-in-krakow
30
  http://example.com/blog/krakow-the-best-city-for-tourists
31
  http://example.com/poland/cities/krakow
32
  http://example.com/poland/cities/stalowa-wola
35
 
36
  If you use the form from `Find and replace` section your URLs will be changed to:
37
 
38
+ `http://example.com/gdansk/hotels-in-gdansk
39
+ http://example.com/gdansk/restaurants-in-gdansk
40
+ http://example.com/gdansk/transport-in-gdansk
41
  http://example.com/blog/gdansk-the-best-city-for-tourists
42
  http://example.com/poland/cities/gdansk
43
  http://example.com/poland/cities/stalowa-wola
50
 
51
  * Support for taxonomies
52
  * REGEX for `Find and replace` section
53
+ * Support for WPML
54
  * Two-step updater, so you can double-check which permalinks will be changed before the change is applied
55
  * AJAX support.
56
 
72
  2. Find and replace section.
73
  3. Regenerate section.
74
  4. Custom permastructures.
75
+ 5. A list of updated posts.
76
+ 6. Editable URI box in Post/Page/CPT edit pagees.
77
 
78
  == Frequently Asked Questions ==
79
 
80
+ = Q. Does the plugin support WPML/qTranslate
81
+ = A. Unfortunately not, the WPML/qTranslate support will be added in next versions.
82
 
83
  == Changelog ==
84
 
85
+ = 0.4 =
86
+ * Rewrite rules are no longer used (SQL queries are optimized). The plugin uses now 'request' filter to detect the page/post that should be loaded instead.
87
+ * Now full URI (including slug) is editable.
88
+ * A few major improvements applied.
89
+ * Partial code optimization.
90
+
91
  = 0.3.4 =
92
  * Hotfix for not working custom taxonomies tags.
93
  * Now the rewrite rules for custom post types are stored in different way.
css/permalink-manager-admin.css CHANGED
@@ -6,8 +6,7 @@
6
  #permalinks-table-wrap #plugin-name-heading a { color: #959595; font-size: 60%; text-decoration: none; }
7
 
8
  #permalinks-table-wrap .tablenav { margin: 6px 0 15px; }
9
- #permalinks-table-wrap .column-post_type { width: 10%; }
10
- #permalinks-table-wrap .column-post_permalink { width: 50%; }
11
  #permalinks-table-wrap .post_name input { max-width: 100%; }
12
  #permalinks-table-wrap .post_permalink a, #permalinks-table-wrap .post_permalink .dashicons { font-size: 13px; color: #aaa; line-height: 1.5em; }
13
 
@@ -23,3 +22,14 @@
23
  #permalinks-table-wrap #permalink-manager-tabs > div.show { display: block; }
24
 
25
  #permalinks-table-wrap #permalink-manager-tabs td label { display: block; }
 
 
 
 
 
 
 
 
 
 
 
6
  #permalinks-table-wrap #plugin-name-heading a { color: #959595; font-size: 60%; text-decoration: none; }
7
 
8
  #permalinks-table-wrap .tablenav { margin: 6px 0 15px; }
9
+ #permalinks-table-wrap .column-post_type, #permalinks-table-wrap .column-post_status { width: 10%; }
 
10
  #permalinks-table-wrap .post_name input { max-width: 100%; }
11
  #permalinks-table-wrap .post_permalink a, #permalinks-table-wrap .post_permalink .dashicons { font-size: 13px; color: #aaa; line-height: 1.5em; }
12
 
22
  #permalinks-table-wrap #permalink-manager-tabs > div.show { display: block; }
23
 
24
  #permalinks-table-wrap #permalink-manager-tabs td label { display: block; }
25
+
26
+ #permalinks-table-wrap small{ display: block; font-weight: normal; color: #888; }
27
+
28
+ #permalink-manager-tabs {}
29
+ #permalink-manager-tabs h4 { margin-bottom: 5px; font-size: 16px; }
30
+ #permalink-manager-tabs .field-container { margin-bottom: 30px; }
31
+ #permalink-manager-tabs .field-container .field-desc { font-style: italic; }
32
+ #permalink-manager-tabs .half { width: 48%; float: left; }
33
+ #permalink-manager-tabs .half2 { margin-left: 4%; }
34
+ #permalink-manager-tabs .half input { width: 100%; }
35
+ #permalink-manager-tabs .clearfix { float: none; overflow: hidden; clear: both; }
inc/permalink-manager-actions.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Additional functions used in classes and another subclasses
5
+ */
6
+
7
+ class Permalink_Manager_Actions extends Permalink_Manager_Class {
8
+
9
+ public function __construct() {}
10
+
11
+ /**
12
+ * Find & replace (bulk action)
13
+ */
14
+ static function find_replace($find_and_replace_fields) {
15
+ global $wpdb;
16
+
17
+ // Reset variables
18
+ $updated_slugs_count = 0;
19
+ $updated_array = array();
20
+ $alert_type = $alert_content = $errors = $main_content = '';
21
+ $old_uris = get_option('permalink-manager-uris');
22
+
23
+ // Prepare default variables from $_POST object
24
+ $old_string = esc_sql($_POST['permalink-manager']['find-replace']['old_string']);
25
+ $new_string = esc_sql($_POST['permalink-manager']['find-replace']['new_string']);
26
+ $mode = isset($_POST['permalink-manager']['find-replace']['variant']) ? $_POST['permalink-manager']['find-replace']['variant'] : array('slugs');
27
+ $post_types_array = ($_POST['permalink-manager']['find-replace']['post_types']);
28
+ $post_statuses_array = ($_POST['permalink-manager']['find-replace']['post_statuses']);
29
+ $post_types = implode("', '", $post_types_array);
30
+ $post_statuses = implode("', '", $post_statuses_array);
31
+
32
+ // Save the rows before they are updated to an array
33
+ //$posts_to_update = $wpdb->get_results("SELECT post_title, post_name, ID FROM {$wpdb->posts} WHERE post_status IN ('{$post_statuses}') AND post_name LIKE '%{$old_string}%' AND post_type IN ('{$post_types}')", ARRAY_A);
34
+ $posts_to_update = $wpdb->get_results("SELECT post_title, post_name, ID FROM {$wpdb->posts} WHERE post_status IN ('{$post_statuses}') AND post_type IN ('{$post_types}')", ARRAY_A);
35
+
36
+ // Now if the array is not empty use IDs from each subarray as a key
37
+ if($posts_to_update && empty($errors)) {
38
+ foreach ($posts_to_update as $row) {
39
+
40
+ // Prepare variables
41
+ $old_post_name = $row['post_name'];
42
+ $old_default_uri = trim(str_replace(home_url("/"), "", get_permalink($row['ID'])), "/");
43
+ $new_default_uri = Permalink_Manager_Helper_Functions::get_uri($row['ID'], true);
44
+ $old_uri = (isset($old_uris[$row['ID']])) ? $old_uris[$row['ID']] : $new_default_uri;
45
+ $old_slug = (strpos($old_uri, '/') !== false) ? substr($old_uri, strrpos($old_uri, '/') + 1) : $old_uri;
46
+ $old_base = (strpos($old_uri, '/') !== false) ? substr($old_uri, 0, strrpos( $old_uri, '/') ) : '';
47
+
48
+ // Process URI & slug
49
+ $new_slug = str_replace($old_string, $new_string, $old_slug);
50
+ $new_base = str_replace($old_string, $new_string, $old_base);
51
+ $new_uri = (in_array('both', $mode)) ? trim("{$new_base}/{$new_slug}", "/") : trim("{$old_base}/{$new_slug}", "/");
52
+ $new_post_name = (in_array('post_names', $mode)) ? str_replace($old_string, $new_string, $old_post_name) : $old_post_name; // Post name is changed only in first mode
53
+
54
+ // Check if native slug should be changed
55
+ if(in_array('post_names', $mode) && ($old_post_name != $new_post_name)) {
56
+ Permalink_Manager_Helper_Functions::update_slug_by_id($new_post_name, $row['ID']);
57
+ }
58
+
59
+ if(($old_uri != $new_uri) || ($old_post_name != $new_post_name)) {
60
+ $old_uris[$row['ID']] = $new_uri;
61
+ $updated_array[] = array('post_title' => $row['post_title'], 'ID' => $row['ID'], 'old_uri' => $old_uri, 'new_uri' => $new_uri, 'old_slug' => $old_post_name, 'new_slug' => $new_post_name);
62
+ $updated_slugs_count++;
63
+ }
64
+ }
65
+
66
+ // Filter array before saving
67
+ $old_uris = array_filter($old_uris);
68
+ update_option('permalink-manager-uris', $old_uris);
69
+
70
+ $output = array('updated' => $updated_array, 'updated_count' => $updated_slugs_count);
71
+ wp_reset_postdata();
72
+ }
73
+
74
+ return ($output) ? $output : "";
75
+ }
76
+
77
+ /**
78
+ * Regenerate slugs & bases (bulk action)
79
+ */
80
+ static function regenerate_all_permalinks() {
81
+ // Setup needed variables
82
+ $updated_slugs_count = 0;
83
+ $updated_array = array();
84
+ $alert_type = $alert_content = $errors = $main_content = '';
85
+
86
+ $post_types_array = ($_POST['permalink-manager']['regenerate_slugs']['post_types']);
87
+ $post_statuses_array = ($_POST['permalink-manager']['regenerate_slugs']['post_statuses']);
88
+ $mode = isset($_POST['permalink-manager']['regenerate_slugs']['variant']) ? $_POST['permalink-manager']['regenerate_slugs']['variant'] : array('slugs');
89
+
90
+ $old_uris = get_option('permalink-manager-uris');
91
+ $all_permastructs = get_option('permalink-manager-permastructs');
92
+
93
+ // Reset query
94
+ $reset_query = new WP_Query( array( 'post_type' => $post_types_array, 'post_status' => $post_statuses_array, 'posts_per_page' => -1 ) );
95
+
96
+ // The Loop
97
+ if ( $reset_query->have_posts() ) {
98
+ while ( $reset_query->have_posts() ) {
99
+ $reset_query->the_post();
100
+ $post_id = get_the_ID();
101
+ $this_post = get_post($post_id);
102
+ $updated = 0;
103
+
104
+ // Prepare permastructs
105
+ $default_permastruct = Permalink_Manager_Helper_Functions::get_default_permastruct($this_post->post_type);
106
+ $custom_permastruct = isset($all_permastructs[$this_post->post_type]) ? $all_permastructs[$this_post->post_type] : $default_permastruct;
107
+
108
+ // Prepare variables
109
+ $old_post_name = $this_post->post_name;
110
+ $old_default_uri = trim(str_replace(home_url("/"), "", get_permalink($post_id)), "/");
111
+ $new_default_uri = Permalink_Manager_Helper_Functions::get_uri($post_id, true);
112
+ $old_uri = isset($old_uris[$post_id]) ? trim($old_uris[$post_id], "/") : $old_default_uri;
113
+ $old_slug = (strpos($old_uri, '/') !== false) ? substr($old_uri, strrpos($old_uri, '/') + 1) : $old_uri;
114
+ $correct_slug = sanitize_title(get_the_title($post_id));
115
+
116
+ // Process URI & slug
117
+ $new_slug = wp_unique_post_slug($correct_slug, $post_id, get_post_status($post_id), get_post_type($post_id), null);
118
+ $new_post_name = (in_array('post_names', $mode)) ? $new_slug : $old_post_name; // Post name is changed only in first mode
119
+ $new_uri = (in_array('both', $mode)) ? $new_default_uri : str_replace($old_slug, $new_slug, $old_uri);
120
+
121
+ // Check if native slug should be changed
122
+ if(in_array('post_names', $mode) && ($old_post_name != $new_post_name)) {
123
+ Permalink_Manager_Helper_Functions::update_slug_by_id($new_post_name, $post_id);
124
+ }
125
+
126
+ if(($old_uri != $new_uri) || ($old_post_name != $new_post_name)) {
127
+ $old_uris[$post_id] = $new_uri;
128
+ $updated_array[] = array('post_title' => get_the_title(), 'ID' => $post_id, 'old_uri' => $old_uri, 'new_uri' => $new_uri, 'old_slug' => $old_post_name, 'new_slug' => $new_post_name);
129
+ $updated_slugs_count++;
130
+ }
131
+
132
+ // Do not store default values
133
+ if($new_uri == $old_default_uri) {
134
+ unset($old_uris[$post_id]);
135
+ }
136
+ }
137
+
138
+ // Filter array before saving
139
+ $old_uris = array_filter($old_uris);
140
+ update_option('permalink-manager-uris', $old_uris);
141
+
142
+ $output = array('updated' => $updated_array, 'updated_count' => $updated_slugs_count);
143
+ wp_reset_postdata();
144
+ }
145
+
146
+ return ($output) ? $output : "";
147
+ }
148
+
149
+ /**
150
+ * Update all slugs & bases (bulk action)
151
+ */
152
+ static function update_all_permalinks() {
153
+ // Setup needed variables
154
+ $updated_slugs_count = 0;
155
+ $updated_array = array();
156
+ $alert_type = $alert_content = $errors = $main_content = '';
157
+
158
+ $old_uris = get_option('permalink-manager-uris');
159
+ $new_uris = isset($_POST['uri']) ? $_POST['uri'] : array();
160
+
161
+ // Double check if the slugs and ids are stored in arrays
162
+ if (!is_array($new_uris)) $new_uris = explode(',', $new_uris);
163
+
164
+ if (!empty($new_uris)) {
165
+ foreach($new_uris as $id => $new_uri) {
166
+ // Prepare variables
167
+ $this_post = get_post($id);
168
+ $updated = '';
169
+
170
+ // Prepare old values
171
+ $old_default_uri = trim(str_replace(home_url("/"), "", get_permalink($id)), "/");
172
+ $old_uri = isset($old_uris[$id]) ? trim($old_uris[$id], "/") : $old_default_uri;
173
+
174
+ // Process new values; Empty entries will be treated as default values
175
+ $new_default_uri = Permalink_Manager_Helper_Functions::get_uri($id, true);
176
+ $new_uri = preg_replace('/\s+/', '', $new_uri);
177
+ $new_uri = ($new_uri) ? trim($new_uri, "/") : $new_default_uri;
178
+ $new_slug = (strpos($new_uri, '/') !== false) ? substr($new_uri, strrpos($new_uri, '/') + 1) : $new_uri;
179
+
180
+ // Neither base nor slug was changed - continue
181
+ if($new_uri == $old_uri) continue;
182
+
183
+ if($new_uri != $old_uri) {
184
+ $old_uris[$id] = $new_uri;
185
+ $updated_array[] = array('post_title' => get_the_title($id), 'ID' => $id, 'old_uri' => $old_uri, 'new_uri' => $new_uri);
186
+ $updated_slugs_count++;
187
+ }
188
+
189
+ // Do not store default values
190
+ if($new_uri == $old_default_uri) {
191
+ unset($old_uris[$id]);
192
+ }
193
+
194
+ }
195
+
196
+ // Filter array before saving
197
+ $old_uris = array_filter($old_uris);
198
+ update_option('permalink-manager-uris', $old_uris);
199
+
200
+ $output = array('updated' => $updated_array, 'updated_count' => $updated_slugs_count);
201
+ }
202
+
203
+ return ($output) ? $output : "";
204
+ }
205
+
206
+ /**
207
+ * Update permastructs
208
+ */
209
+ static function update_permastructs() {
210
+ // Setup needed variables
211
+ $alert_type = $alert_content = $errors = $main_content = '';
212
+ $old_permastructs = get_option('permalink-manager-permastructs');
213
+ $new_permastructs = array_filter($_POST['permalink-manager']['custom-permastructs']);
214
+
215
+ foreach($new_permastructs as $post_type => $new_permstruct) {
216
+ $default_permastruct = Permalink_Manager_Helper_Functions::get_default_permastruct($post_type, true);
217
+ $old_permastruct = $old_permastructs[$post_type];
218
+ $new_permastructs[$post_type] = trim(preg_replace('/\s+/', '', $new_permstruct), "/");
219
+
220
+ // Do not save default permastructs
221
+ if($default_permastruct == $new_permstruct) {
222
+ unset($new_permastructs[$post_type]);
223
+ }
224
+ }
225
+
226
+ update_option('permalink-manager-permastructs', $new_permastructs);
227
+
228
+ return "";
229
+ }
230
+
231
+ }
inc/permalink-manager-base-editor.php CHANGED
@@ -26,10 +26,7 @@ class Permalink_Manager_Base_Editor extends WP_List_Table {
26
  $columns = array(
27
  //'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
28
  'post_type' => __('Post Type', 'permalink-manager'),
29
- 'post_permalink_base' => __('Permalink Base/Permastructure', 'permalink-manager'),
30
- 'post_type_structure_tag' => __('Permalink Base Should End With', 'permalink-manager'),
31
- 'post_type_default_base' => __('Default Permalink Base/Permastructure', 'permalink-manager'),
32
- //'post_sample_permalink' => __('Sample Permalink', 'permalink-manager')
33
  );
34
 
35
  return $columns;
@@ -55,8 +52,9 @@ class Permalink_Manager_Base_Editor extends WP_List_Table {
55
  * Data inside the columns
56
  */
57
  public function column_default( $item, $column_name ) {
58
- $permastruct = Permalink_Manager_Helper_Functions::get_permastruct($item['name'], false);
59
- $default_permastruct = Permalink_Manager_Helper_Functions::get_permastruct($item['name'], false, 'default_permastruct');
 
60
 
61
  switch( $column_name ) {
62
  case 'post_type':
@@ -64,14 +62,8 @@ class Permalink_Manager_Base_Editor extends WP_List_Table {
64
 
65
  case 'post_permalink_base':
66
  $placeholder = $default_permastruct;
67
- $field_args = array('type' => 'text', 'default' => $permastruct, 'without_label' => true, 'input_class' => 'widefat', 'placeholder' => $placeholder);
68
- return Permalink_Manager_Helper_Functions::generate_option_field($item['name'], $field_args, 'base-editor');
69
-
70
- case 'post_type_default_base':
71
- return "<code>{$default_permastruct}</code>";
72
-
73
- case 'post_type_structure_tag':
74
- return "<code>" . Permalink_Manager_Helper_Functions::get_post_tag($item['name']) . "</code>";
75
 
76
  default:
77
  return '';
@@ -94,11 +86,9 @@ class Permalink_Manager_Base_Editor extends WP_List_Table {
94
  */
95
  function extra_tablenav( $which ) {
96
  $save_button = __( 'Save settings', 'permalink-manager' );
97
- $flush_button = __( 'Flush rewrite rules', 'permalink-manager' );
98
 
99
  echo '<div class="alignleft actions">';
100
- submit_button( $save_button, 'primary', "save_permalink_structures[{$which}]", false, array( 'id' => 'doaction', 'value' => 'save_permalink_structures' ) );
101
- submit_button( $flush_button, 'primary', "flush_rewrite_rules[{$which}]", false, array( 'id' => 'doaction', 'value' => 'flush_rewrite_rules' ) );
102
  echo '</div>';
103
  }
104
 
26
  $columns = array(
27
  //'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
28
  'post_type' => __('Post Type', 'permalink-manager'),
29
+ 'post_permalink_base' => __('Custom Permalink Base/Permastruct', 'permalink-manager')
 
 
 
30
  );
31
 
32
  return $columns;
52
  * Data inside the columns
53
  */
54
  public function column_default( $item, $column_name ) {
55
+ $custom_permastructs = get_option('permalink-manager-permastructs');
56
+ $default_permastruct = Permalink_Manager_Helper_Functions::get_default_permastruct($item['name'], true);
57
+ $permastruct = isset($custom_permastructs[$item['name']]) ? $custom_permastructs[$item['name']] : '';
58
 
59
  switch( $column_name ) {
60
  case 'post_type':
62
 
63
  case 'post_permalink_base':
64
  $placeholder = $default_permastruct;
65
+ $field_args = array('type' => 'text', 'placeholder' => $default_permastruct, 'default' => $permastruct, 'without_label' => true, 'input_class' => 'widefat', 'placeholder' => $placeholder);
66
+ return Permalink_Manager_Helper_Functions::generate_option_field($item['name'], $field_args, 'custom-permastructs');
 
 
 
 
 
 
67
 
68
  default:
69
  return '';
86
  */
87
  function extra_tablenav( $which ) {
88
  $save_button = __( 'Save settings', 'permalink-manager' );
 
89
 
90
  echo '<div class="alignleft actions">';
91
+ submit_button( $save_button, 'primary', "save_permastructs[{$which}]", false, array( 'id' => 'doaction', 'value' => 'save_permastructs' ) );
 
92
  echo '</div>';
93
  }
94
 
inc/{permalink-manager-slug-editor.php → permalink-manager-editor.php} RENAMED
@@ -6,7 +6,7 @@ if( ! class_exists( 'WP_List_Table' ) ) {
6
  /**
7
  * Create a new table class that will extend the WP_List_Table
8
  */
9
- class Permalink_Manager_Slug_Editor extends WP_List_Table {
10
  public $screen_options_fields;
11
 
12
  function __construct() {
@@ -26,9 +26,9 @@ class Permalink_Manager_Slug_Editor extends WP_List_Table {
26
  $columns = array(
27
  //'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
28
  'post_title' => __('Title', 'permalink-manager'),
29
- 'post_name' => __('Slug', 'permalink-manager'),
30
- 'post_date_gmt' => __('Date', 'permalink-manager'),
31
- 'post_permalink' => __('Permalink', 'permalink-manager'),
32
  'post_status' => __('Post Status', 'permalink-manager'),
33
  'post_type' => __('Post Type', 'permalink-manager')
34
  );
@@ -59,6 +59,10 @@ class Permalink_Manager_Slug_Editor extends WP_List_Table {
59
  * Data inside the columns
60
  */
61
  public function column_default( $item, $column_name ) {
 
 
 
 
62
  switch( $column_name ) {
63
  case 'post_type':
64
  $post_type_labels = Permalink_Manager_Helper_Functions::get_post_types_array('full', $item['post_type']);
@@ -68,18 +72,18 @@ class Permalink_Manager_Slug_Editor extends WP_List_Table {
68
  $post_statuses_array = get_post_statuses();
69
  return "{$post_statuses_array[$item[ $column_name ]]}<br /><small>({$item[ $column_name ]})</small>";
70
 
71
- case 'post_permalink':
72
- return ($item['post_status'] != 'publish') ? '-' : Permalink_Manager_Helper_Functions::get_correct_permalink($item[ 'ID' ], $item[ 'post_name' ], true) . ' <a href="' . Permalink_Manager_Helper_Functions::get_correct_permalink($item[ 'ID' ], $item[ 'post_name' ]) . '" target="_blank"><span class="dashicons dashicons-admin-links"></span></a>';
73
-
74
  case 'post_name':
75
- $field_name = "slug[{$item['ID']}]";
76
- $field_args = array('type' => 'text', 'default' => $item[ 'post_name' ], 'without_label' => true, 'input_class' => 'widefat');
77
- return Permalink_Manager_Helper_Functions::generate_option_field($field_name, $field_args);
 
78
 
79
  case 'post_title':
80
- $edit_post = $item[ 'post_title' ] . '<div class="row-actions">';
 
 
81
  $edit_post .= '<span class="edit"><a target="_blank" href="' . home_url() . '/wp-admin/post.php?post=' . $item[ 'ID' ] . '&amp;action=edit" title="' . __('Edit', 'permalink-manager') . '">' . __('Edit', 'permalink-manager') . '</a> | </span>';
82
- $edit_post .= '<span class="view"><a target="_blank" href="' . Permalink_Manager_Helper_Functions::get_correct_permalink($item[ 'ID' ], $item[ 'post_name' ]) . '" title="' . __('View', 'permalink-manager') . ' ' . $item[ 'post_title' ] . '" rel="permalink">' . __('View', 'permalink-manager') . '</a> | </span>';
83
  $edit_post .= '<span class="id">#' . $item[ 'ID' ] . '</span>';
84
  $edit_post .= '</div>';
85
  return $edit_post;
6
  /**
7
  * Create a new table class that will extend the WP_List_Table
8
  */
9
+ class Permalink_Manager_Editor extends WP_List_Table {
10
  public $screen_options_fields;
11
 
12
  function __construct() {
26
  $columns = array(
27
  //'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
28
  'post_title' => __('Title', 'permalink-manager'),
29
+ 'post_name' => __('Post Name (Native Slug)', 'permalink-manager'),
30
+ //'post_date_gmt' => __('Date', 'permalink-manager'),
31
+ 'base_with_slug' => __('Base & Slug (URI)', 'permalink-manager'),
32
  'post_status' => __('Post Status', 'permalink-manager'),
33
  'post_type' => __('Post Type', 'permalink-manager')
34
  );
59
  * Data inside the columns
60
  */
61
  public function column_default( $item, $column_name ) {
62
+
63
+ $base = Permalink_Manager_Helper_Functions::get_uri($item[ 'ID' ], false, false);
64
+ $field_args_base = array('type' => 'text', 'default' => $base, 'without_label' => true, 'input_class' => 'small');
65
+
66
  switch( $column_name ) {
67
  case 'post_type':
68
  $post_type_labels = Permalink_Manager_Helper_Functions::get_post_types_array('full', $item['post_type']);
72
  $post_statuses_array = get_post_statuses();
73
  return "{$post_statuses_array[$item[ $column_name ]]}<br /><small>({$item[ $column_name ]})</small>";
74
 
 
 
 
75
  case 'post_name':
76
+ return $item[ 'post_name' ];
77
+
78
+ case 'base_with_slug':
79
+ return ($item['post_status'] != 'publish') ? '-' : Permalink_Manager_Helper_Functions::generate_option_field("uri[{$item['ID']}]", $field_args_base);
80
 
81
  case 'post_title':
82
+ $edit_post = $item[ 'post_title' ];
83
+ $edit_post .= '<small>' . Permalink_Manager_Helper_Functions::get_correct_permalink($item[ 'ID' ]) . '</small>';
84
+ $edit_post .= '<div class="row-actions">';
85
  $edit_post .= '<span class="edit"><a target="_blank" href="' . home_url() . '/wp-admin/post.php?post=' . $item[ 'ID' ] . '&amp;action=edit" title="' . __('Edit', 'permalink-manager') . '">' . __('Edit', 'permalink-manager') . '</a> | </span>';
86
+ $edit_post .= '<span class="view"><a target="_blank" href="' . Permalink_Manager_Helper_Functions::get_correct_permalink($item[ 'ID' ]) . '" title="' . __('View', 'permalink-manager') . ' ' . $item[ 'post_title' ] . '" rel="permalink">' . __('View', 'permalink-manager') . '</a> | </span>';
87
  $edit_post .= '<span class="id">#' . $item[ 'ID' ] . '</span>';
88
  $edit_post .= '</div>';
89
  return $edit_post;
inc/permalink-manager-helper-functions.php CHANGED
@@ -23,21 +23,6 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
23
  }
24
  }
25
 
26
- /**
27
- * Display the permalink in a better way
28
- */
29
- static function get_correct_permalink($id, $correct_slug, $highlight = false) {
30
- $output = get_permalink($id);
31
-
32
- // Get last part of URI
33
- $page_uri = explode('/', get_page_uri($id));
34
- $old_slug = end($page_uri);
35
- $correct_slug = ($highlight) ? "<code>{$correct_slug}</code>" : $correct_slug;
36
- $output = Permalink_Manager_Helper_Functions::str_lreplace($old_slug, $correct_slug, $output);
37
-
38
- return $output;
39
- }
40
-
41
  /**
42
  * Get post_types array
43
  */
@@ -58,14 +43,6 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
58
  return (empty($cpt)) ? $post_types_array : $post_types_array[$cpt];
59
  }
60
 
61
- /**
62
- * Replace last occurence
63
- */
64
- static function str_lreplace($search, $replace, $subject) {
65
- $pos = strrpos($subject, $search);
66
- return ($pos !== false) ? substr_replace($subject, $replace, $pos, strlen($search)) : $subject;
67
- }
68
-
69
  /**
70
  * Generate the fields
71
  */
@@ -81,15 +58,27 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
81
  $label = (isset($args['label'])) ? $args['label'] : '';
82
  $placeholder = (isset($args['placeholder'])) ? "placeholder=\"{$args['placeholder']}\"" : '';
83
  $input_class = (isset($args['input_class'])) ? "class=\"{$args['input_class']}\"" : '';
 
84
  $input_name = ($group) ? "permalink-manager[{$group}][{$name}]" : $name;
 
85
 
86
  switch($args['type']) {
87
  case 'checkbox' :
88
  $fields .= '<div class="checkboxes">';
89
- foreach($args['choices'] as $value => $label) {
90
  $all_checked = (isset($saved_values[$group][$name])) ? $saved_values[$group][$name] : $args['default'];
91
  $checked = in_array($value, $all_checked) ? "checked='checked'" : "";
92
- $fields .= "<label for='{$input_name}[]'><input type='checkbox' {$input_class} value='{$value}' name='{$input_name}[]' {$checked} /> {$label}</label>";
 
 
 
 
 
 
 
 
 
 
93
  }
94
  $fields .= '</div>';
95
  break;
@@ -99,15 +88,21 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
99
  $fields .= "<input type='number' {$input_class} value='{$value}' name='{$input_name}' />";
100
  break;
101
 
 
 
 
102
  default :
103
  $value = (isset($saved_values[$group][$name])) ? $saved_values[$group][$name] : $default_value;
104
  $fields .= "<input type='text' {$input_class} value='{$value}' name='{$input_name}' {$placeholder}/>";
105
  }
106
 
107
  // Get all variables into one final variable
108
- if(isset($group) && !($group === 'screen-options')) {
109
- $output = "<legend>{$label}</legend>";
 
110
  $output .= "<div class='metabox-prefs'><div class='{$name}-container'>{$fields}</div></div>";
 
 
111
  } else if (isset($args['without_label']) && $args['without_label'] == true) {
112
  $output = $fields;
113
  } else {
@@ -118,6 +113,16 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
118
  return $output;
119
  }
120
 
 
 
 
 
 
 
 
 
 
 
121
  /**
122
  * Check if the provided slug is unique and then update it with SQL query.
123
  */
@@ -133,78 +138,120 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
133
  }
134
 
135
  /**
136
- * Save option
137
  */
138
- static function save_option($field = null, $value = null) {
139
- $options = get_option('permalink-manager', array());
140
- if($field) {
141
- $options[$field] = $value;
142
-
143
- // The snippet belows prevent duplicates in permastructures
144
- if($field == 'base-editor') {
145
-
146
- // Algorithm below works like array_count_values(), but we also need to make the permastructs unique
147
- $unique_permastructures = array();
148
- foreach($value as $key => $permastruct) {
149
- // Trim whitespaces & slashes at first
150
- $permastruct = trim(trim($permastruct, "/"));
151
-
152
- // The permastruct is not unique!
153
- if(empty($permastruct)) {
154
- $permastruct = Permalink_Manager_Helper_Functions::get_permastruct($key, false, 'default_permastruct');
155
- } else if(isset($unique_permastructures[$permastruct])) {
156
- $unique_permastructures[$permastruct]++;
157
- // Alter the permastruct
158
- $permastruct = "{$key}/{$permastruct}";
159
- } else {
160
- $unique_permastructures[$permastruct] = 1;
161
- }
162
 
163
- // Trim one more time
164
- $permastruct = trim(trim($permastruct, "/"));
 
 
 
165
 
166
- // Alter the permastruct
167
- $options[$field][$key] = $permastruct;
168
- }
 
169
 
 
 
 
 
 
 
170
  }
171
 
172
- update_option('permalink-manager', $options);
173
- }
174
- }
 
175
 
176
- /**
177
- * Get permastruct
178
- */
179
- static function get_permastruct($post_type = 'page', $title_replace = false, $return = 'permastruct') {
180
- global $wp_rewrite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
- // Load permastruct from options
183
- $options = get_option('permalink-manager', array());
184
- $permastructs = isset($options['base-editor']) ? $options['base-editor'] : array();
 
 
 
185
 
186
- // Get default permastruct
187
- if($post_type == 'page') {
188
- $default_permastruct = $wp_rewrite->get_page_permastruct();
189
- } else if($post_type == 'post') {
190
- $default_permastruct = get_option('permalink_structure');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  } else {
192
- $default_permastruct = $wp_rewrite->get_extra_permastruct($post_type);
193
  }
194
 
195
- // If the permastruct is not saved for post type or empty return default permastruct
196
- $permastruct = (isset($permastructs[$post_type]) && $permastructs[$post_type]) ? $permastructs[$post_type] : $default_permastruct;
 
 
 
197
 
198
- // Remove post name to enable support for hierarchical post types
199
- $permastruct = ($title_replace) ? str_replace(array("%pagename%", "%postname%", "%{$post_type}%"), '', $permastruct) : "";
 
 
200
 
201
- return trim($$return, '/');
202
  }
203
 
204
  /**
205
  * Structure Tags & Rewrite functions
206
  */
207
- static function get_all_structure_tags($code = true, $seperator = ', ') {
208
  global $wp_rewrite;
209
 
210
  $tags = $wp_rewrite->rewritecode;
@@ -212,6 +259,17 @@ class Permalink_Manager_Helper_Functions extends Permalink_Manager_Class {
212
  $last_tag_index = count($tags);
213
  $i = 1;
214
 
 
 
 
 
 
 
 
 
 
 
 
215
  foreach($tags as $tag) {
216
  $sep = ($last_tag_index == $i) ? "" : $seperator;
217
  $output .= ($code) ? "<code>{$tag}</code>{$sep}" : "{$tag}{$sep}";
23
  }
24
  }
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  /**
27
  * Get post_types array
28
  */
43
  return (empty($cpt)) ? $post_types_array : $post_types_array[$cpt];
44
  }
45
 
 
 
 
 
 
 
 
 
46
  /**
47
  * Generate the fields
48
  */
58
  $label = (isset($args['label'])) ? $args['label'] : '';
59
  $placeholder = (isset($args['placeholder'])) ? "placeholder=\"{$args['placeholder']}\"" : '';
60
  $input_class = (isset($args['input_class'])) ? "class=\"{$args['input_class']}\"" : '';
61
+ $container_class = (isset($args['container_class'])) ? " class=\"{$args['container_class']} field-container\"" : " class=\"field-container\"";
62
  $input_name = ($group) ? "permalink-manager[{$group}][{$name}]" : $name;
63
+ $desc = (isset($args['desc'])) ? "<p class=\"field-desc\">{$args['desc']}</p>" : "";
64
 
65
  switch($args['type']) {
66
  case 'checkbox' :
67
  $fields .= '<div class="checkboxes">';
68
+ foreach($args['choices'] as $value => $checkbox_label) {
69
  $all_checked = (isset($saved_values[$group][$name])) ? $saved_values[$group][$name] : $args['default'];
70
  $checked = in_array($value, $all_checked) ? "checked='checked'" : "";
71
+ $fields .= "<label for='{$input_name}[]'><input type='checkbox' {$input_class} value='{$value}' name='{$input_name}[]' {$checked} /> {$checkbox_label}</label>";
72
+ }
73
+ $fields .= '</div>';
74
+ break;
75
+
76
+ case 'radio' :
77
+ $fields .= '<div class="radios">';
78
+ foreach($args['choices'] as $value => $checkbox_label) {
79
+ $all_checked = (isset($saved_values[$group][$name])) ? $saved_values[$group][$name] : $args['default'];
80
+ $checked = in_array($value, $all_checked) ? "checked='checked'" : "";
81
+ $fields .= "<label for='{$input_name}[]'><input type='radio' {$input_class} value='{$value}' name='{$input_name}[]' {$checked} /> {$checkbox_label}</label>";
82
  }
83
  $fields .= '</div>';
84
  break;
88
  $fields .= "<input type='number' {$input_class} value='{$value}' name='{$input_name}' />";
89
  break;
90
 
91
+ case 'clearfix' :
92
+ return "<div class=\"clearfix\"></div>";
93
+
94
  default :
95
  $value = (isset($saved_values[$group][$name])) ? $saved_values[$group][$name] : $default_value;
96
  $fields .= "<input type='text' {$input_class} value='{$value}' name='{$input_name}' {$placeholder}/>";
97
  }
98
 
99
  // Get all variables into one final variable
100
+ if(isset($group) && (in_array($group, array('regenerate_slugs', 'find-replace')))) {
101
+ $output = "<div{$container_class}>";
102
+ $output .= "<h4>{$label}</h4>";
103
  $output .= "<div class='metabox-prefs'><div class='{$name}-container'>{$fields}</div></div>";
104
+ $output .= $desc;
105
+ $output .= "</div>";
106
  } else if (isset($args['without_label']) && $args['without_label'] == true) {
107
  $output = $fields;
108
  } else {
113
  return $output;
114
  }
115
 
116
+ /**
117
+ * Display the permalink in a better way
118
+ */
119
+ static function get_correct_permalink($id) {
120
+ $old_uris = get_option('permalink-manager-uris');
121
+ $permalink = isset($old_uris[$id]) ? home_url('/') . $old_uris[$id] : get_permalink($id);
122
+
123
+ return $permalink;
124
+ }
125
+
126
  /**
127
  * Check if the provided slug is unique and then update it with SQL query.
128
  */
138
  }
139
 
140
  /**
141
+ * Get permastruct
142
  */
143
+ static function get_default_permastruct($post_type = 'page', $remove_post_tag = false) {
144
+ global $wp_rewrite;
145
+
146
+ // Get default permastruct
147
+ if($post_type == 'page') {
148
+ $permastruct = $wp_rewrite->get_page_permastruct();
149
+ } else if($post_type == 'post') {
150
+ $permastruct = get_option('permalink_structure');
151
+ } else {
152
+ $permastruct = $wp_rewrite->get_extra_permastruct($post_type);
153
+ }
154
+
155
+ return ($remove_post_tag) ? trim(str_replace(array("%postname%", "%pagename%", "%{$post_type}%"), "", $permastruct), "/") : $permastruct;
156
+ }
 
 
 
 
 
 
 
 
 
 
157
 
158
+ static function get_uri($post_id, $get_default = false, $remove_slug = true) {
159
+ // Load all bases & post
160
+ $all_uris = get_option('permalink-manager-uris');
161
+ $all_permastructures = get_option('permalink-manager-permastructs');
162
+ $options = get_option('permalink-manager');
163
 
164
+ $post = isset($post_id->post_type) ? $post_id : get_post($post_id);
165
+ $post_id = $post->ID;
166
+ $post_type = $post->post_type;
167
+ $post_name = $post->post_name;
168
 
169
+ if($get_default) {
170
+
171
+ if($all_permastructures) {
172
+ $permastruct = isset($all_permastructures[$post_type]) ? $all_permastructures[$post_type] : Permalink_Manager_Helper_Functions::get_default_permastruct($post_type);
173
+ } else {
174
+ $permastruct = isset($options['base-editor'][$post_type]) ? $options['base-editor'][$post_type] : Permalink_Manager_Helper_Functions::get_default_permastruct($post_type);
175
  }
176
 
177
+ // Get options
178
+ if($permastruct) {
179
+ $default_base = trim($permastruct, '/');
180
+ }
181
 
182
+ // Get the date
183
+ $date = explode(" ",date('Y m d H i s', strtotime($post->post_date)));
184
+
185
+ // Get the category (if needed)
186
+ $category = '';
187
+ if ( strpos($default_base, '%category%') !== false ) {
188
+ $cats = get_the_category($post->ID);
189
+ if ( $cats ) {
190
+ usort($cats, '_usort_terms_by_ID'); // order by ID
191
+ $category_object = apply_filters( 'post_link_category', $cats[0], $cats, $post );
192
+ $category_object = get_term( $category_object, 'category' );
193
+ $category = $category_object->slug;
194
+ if ( $parent = $category_object->parent )
195
+ $category = get_category_parents($parent, false, '/', true) . $category;
196
+ }
197
+ // show default category in permalinks, without having to assign it explicitly
198
+ if ( empty($category) ) {
199
+ $default_category = get_term( get_option( 'default_category' ), 'category' );
200
+ $category = is_wp_error( $default_category ) ? '' : $default_category->slug;
201
+ }
202
+ }
203
 
204
+ // Get the author (if needed)
205
+ $author = '';
206
+ if ( strpos($default_base, '%author%') !== false ) {
207
+ $authordata = get_userdata($post->post_author);
208
+ $author = $authordata->user_nicename;
209
+ }
210
 
211
+ // Fix for hierarchical CPT (start)
212
+ $full_slug = get_page_uri($post);
213
+ $post_type_tag = Permalink_Manager_Helper_Functions::get_post_tag($post_type);
214
+
215
+ // Do the replacement (post tag is removed now to enable support for hierarchical CPT)
216
+ $tags = array('%year%', '%monthnum%', '%day%', '%hour%', '%minute%', '%second%', '%post_id%', '%category%', '%author%', $post_type_tag);
217
+ $replacements = array($date[0], $date[1], $date[2], $date[3], $date[4], $date[5], $post->ID, $category, $author, '');
218
+ $default_uri = str_replace($tags, $replacements, "{$default_base}/{$full_slug}");
219
+
220
+ // Replace custom taxonomies
221
+ $terms = get_taxonomies( array('public' => true, '_builtin' => false), 'names', 'and' );
222
+ $taxonomies = $terms;
223
+ if ( $taxonomies ) {
224
+ foreach($taxonomies as $taxonomy) {
225
+ $tag = "%{$taxonomy}%";
226
+ $terms = wp_get_object_terms($post->ID, $taxonomy);
227
+ if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) {
228
+ $replacement = $terms[0]->slug;
229
+ $default_uri = str_replace($tag, $replacement, $default_uri);
230
+ }
231
+ }
232
+ }
233
  } else {
234
+ $default_uri = str_replace(home_url("/"), "", get_permalink($post_id));
235
  }
236
 
237
+ $uri = isset($all_uris[$post_id]) ? $all_uris[$post_id] : $default_uri;
238
+ // Remove post_name from base (last part of string)
239
+ $uri = ($remove_slug) ? str_replace($post_name, '', $uri) : $uri;
240
+
241
+ $final_uri = ($get_default) ? $default_uri : $uri;
242
 
243
+ // Clean URI
244
+ $final_uri = preg_replace('/\s+/', '', $final_uri);
245
+ $final_uri = str_replace('//', '/', $final_uri);
246
+ $final_uri = trim($final_uri, "/");
247
 
248
+ return $final_uri;
249
  }
250
 
251
  /**
252
  * Structure Tags & Rewrite functions
253
  */
254
+ static function get_all_structure_tags($code = true, $seperator = ', ', $hide_slug_tags = true) {
255
  global $wp_rewrite;
256
 
257
  $tags = $wp_rewrite->rewritecode;
259
  $last_tag_index = count($tags);
260
  $i = 1;
261
 
262
+ // Hide slug tags
263
+ if($hide_slug_tags) {
264
+ $post_types = Permalink_Manager_Helper_Functions::get_post_types_array();
265
+ foreach($post_types as $post_type => $post_type_name) {
266
+ $post_type_tag = Permalink_Manager_Helper_Functions::get_post_tag($post_type);
267
+ // Find key with post type tag from rewritecode
268
+ $key = array_search($post_type_tag, $tags);
269
+ if($key) { unset($tags[$key]); }
270
+ }
271
+ }
272
+
273
  foreach($tags as $tag) {
274
  $sep = ($last_tag_index == $i) ? "" : $seperator;
275
  $output .= ($code) ? "<code>{$tag}</code>{$sep}" : "{$tag}{$sep}";
js/permalink-manager-admin.js CHANGED
@@ -1,35 +1,35 @@
1
  jQuery(document).ready(function() {
2
-
3
  /*
4
  * Tab navigation
5
  */
6
- jQuery('#permalink-manager-tabs-nav a').on('click', function(){
7
-
8
  // Disable current active tab in navigation
9
  jQuery('#permalink-manager-tabs-nav .nav-tab-active').removeClass('nav-tab-active');
10
  jQuery('#permalink-manager-tabs .show').removeClass('show');
11
-
12
  // Get current tab name
13
  var tab_to_open = jQuery(this).data("tab");
14
-
15
  // Add "active" class to the clicked tab
16
  jQuery(this).addClass('nav-tab-active');
17
  jQuery('#permalink-manager-tabs div[data-tab="'+tab_to_open+'"]').addClass('show');
18
-
19
  // Disable native click event
20
  return false;
21
- });
22
-
23
  /*
24
  * "Select all" checkbox
25
  */
26
  jQuery('input[value="all"]').on('change', function() {
27
  // Uncheck "Select all"
28
  jQuery(this).prop('checked', false);
29
-
30
  jQuery(this).parents('.checkboxes').find('input[type="checkbox"]').not(this).each(function() {
31
  jQuery(this).prop('checked', true);
32
  });
33
  });
34
-
35
- });
1
  jQuery(document).ready(function() {
2
+
3
  /*
4
  * Tab navigation
5
  */
6
+ /*jQuery('#permalink-manager-tabs-nav a').on('click', function(){
7
+
8
  // Disable current active tab in navigation
9
  jQuery('#permalink-manager-tabs-nav .nav-tab-active').removeClass('nav-tab-active');
10
  jQuery('#permalink-manager-tabs .show').removeClass('show');
11
+
12
  // Get current tab name
13
  var tab_to_open = jQuery(this).data("tab");
14
+
15
  // Add "active" class to the clicked tab
16
  jQuery(this).addClass('nav-tab-active');
17
  jQuery('#permalink-manager-tabs div[data-tab="'+tab_to_open+'"]').addClass('show');
18
+
19
  // Disable native click event
20
  return false;
21
+ });*/
22
+
23
  /*
24
  * "Select all" checkbox
25
  */
26
  jQuery('input[value="all"]').on('change', function() {
27
  // Uncheck "Select all"
28
  jQuery(this).prop('checked', false);
29
+
30
  jQuery(this).parents('.checkboxes').find('input[type="checkbox"]').not(this).each(function() {
31
  jQuery(this).prop('checked', true);
32
  });
33
  });
34
+
35
+ });
permalink-manager.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin Name: Permalink Manager
5
  * Plugin URI: http://maciejbis.net/
6
  * Description: A simple tool that allows to mass update of slugs that are used to build permalinks for Posts, Pages and Custom Post Types.
7
- * Version: 0.3.4
8
  * Author: Maciej Bis
9
  * Author URI: http://maciejbis.net/
10
  * License: GPL-2.0+
@@ -20,7 +20,7 @@ if ( ! defined( 'WPINC' ) ) {
20
 
21
  // Define the directories used to load plugin files.
22
  define( 'PERMALINK_MANAGER_PLUGIN_NAME', 'permalink-manager' );
23
- define( 'PERMALINK_MANAGER_VERSION', '0.3.4' );
24
  define( 'PERMALINK_MANAGER_DIR', untrailingslashit( dirname( __FILE__ ) ) );
25
  define( 'PERMALINK_MANAGER_URL', untrailingslashit( plugins_url( '', __FILE__ ) ) );
26
  define( 'PERMALINK_MANAGER_WEBSITE', 'http://maciejbis.net' );
@@ -34,25 +34,30 @@ class Permalink_Manager_Class {
34
  public function __construct() {
35
 
36
  $this->permalink_manager_options = get_option('permalink-manager');
 
 
37
 
38
  if( is_admin() ) {
39
  add_action( 'plugins_loaded', array($this, 'localize_me') );
40
- add_action( 'init', array($this, 'flush_rewrite_rules') );
41
- add_action( 'admin_init', array($this, 'bulk_actions') );
42
  add_action( 'admin_menu', array($this, 'add_menu_page') );
 
43
  add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugins_page_links') );
44
-
45
- add_filter( 'page_rewrite_rules', array($this, 'custom_page_rewrite_rules'), 999, 1);
46
- add_filter( 'post_rewrite_rules', array($this, 'custom_post_rewrite_rules'), 999, 1);
47
- add_filter( 'rewrite_rules_array', array($this, 'custom_cpt_rewrite_rules'), 999, 1);
48
  }
49
 
 
 
 
 
 
50
  // Public functions
 
51
  add_filter( '_get_page_link', array($this, 'custom_permalinks'), 999, 2);
52
  add_filter( 'page_link', array($this, 'custom_permalinks'), 999, 2);
53
  add_filter( 'post_link', array($this, 'custom_permalinks'), 999, 2);
54
  add_filter( 'post_type_link', array($this, 'custom_permalinks'), 999, 2);
55
-
56
  }
57
 
58
  /**
@@ -79,15 +84,15 @@ class Permalink_Manager_Class {
79
  function slug_editor_html() {
80
  global $wpdb;
81
 
82
- $Permalink_Manager_Slug_Editor = new Permalink_Manager_Slug_Editor();
83
- $Permalink_Manager_Slug_Editor->set_screen_option_fields($this->fields_arrays('screen_options'));
84
- $Permalink_Manager_Slug_Editor->prepare_items($wpdb->posts);
85
 
86
  ?>
87
 
88
  <form id="permalinks-table" method="post">
89
  <input type="hidden" name="tab" value="slug_editor" />
90
- <?php echo $Permalink_Manager_Slug_Editor->display(); ?>
91
  </form>
92
  <?php
93
  }
@@ -96,7 +101,7 @@ class Permalink_Manager_Class {
96
  * Mass replace options page.
97
  */
98
  function find_and_replace_html() {
99
- $button = get_submit_button( __( 'Find & Replace', 'permalink-manager' ), 'primary', 'find-replace-button', false );
100
 
101
  $return = "<form id=\"permalinks-table-find-replace\" method=\"post\">";
102
  $return .= "<input type=\"hidden\" name=\"tab\" value=\"find_and_replace\" />";
@@ -168,27 +173,25 @@ class Permalink_Manager_Class {
168
  // Tabs array with assigned functions used to display HTML content.
169
  $tabs = array(
170
  'slug_editor' => array(
171
- 'name' => __('Slug Editor', 'permalink-manager'),
172
  'function' => 'slug_editor_html',
173
- 'description' => __('You can disable/enable selected post types from the table below using <strong>"Screen Options"</strong> (click on the upper-right button to show it) section above.', 'permalink-manager')
174
  ),
175
  'find_and_replace' => array(
176
  'name' => __('Find and replace', 'permalink-manager'),
177
- 'function' => 'find_and_replace_html',
178
- 'warning' => (__('<strong>You are doing it at your own risk!</strong>', 'permalink-manager') . '<br />' . __('A backup of MySQL database before using this tool is highly recommended. The search & replace operation might be not revertible!', 'permalink-manager'))
179
  ),
180
  'regenerate_slugs' => array(
181
- 'name' => __('Regenerate slugs', 'permalink-manager'),
182
- 'function' => 'regenerate_slugs_html',
183
- 'warning' => (__('<strong>You are doing it at your own risk!</strong>', 'permalink-manager') . '<br />' . __('A backup of MySQL database before using this tool is highly recommended. The regenerate process of slugs might be not revertible!', 'permalink-manager'))
184
  ),
185
  'base_editor' => array(
186
- 'name' => __('Permalinks Base Editor', 'permalink-manager'),
187
  'function' => 'base_editor_html',
188
  'warning' => array(
189
  sprintf(__('<strong>This is an experimental feature!</strong> Please report all the bugs & issues <a href="%s">here</a>.', 'permalink-manager'), 'https://wordpress.org/support/plugin/permalink-manager'),
190
- __('Custom Post Types should have their own, unique front, eg. <em>products/%product%!</em>', 'permalink-manager'),
191
- __('After you update & save the settings below, you need to flush the rewrite rules!', 'permalink-manager'),
192
  ),
193
  'description' => (sprintf( __('All the <a href="%s" target="_blank">Structure Tags</a> allowed are listed below. Please note that some of them can be used only for particular Post Types.', 'permalink-manager'), "https://codex.wordpress.org/Using_Permalinks#Structure_Tags") . "<br />" . Permalink_Manager_Helper_Functions::get_all_structure_tags())
194
  ),
@@ -306,13 +309,28 @@ class Permalink_Manager_Class {
306
 
307
  // Fields for "Find and replace"
308
  $find_and_replace = array(
 
 
 
309
  'old_string' => array(
310
  'label' => __( 'Find ...', 'permalink-manager' ),
311
  'type' => 'text',
 
312
  ),
313
  'new_string' => array(
314
  'label' => __( 'Replace with ...', 'permalink-manager' ),
315
  'type' => 'text',
 
 
 
 
 
 
 
 
 
 
 
316
  ),
317
  'post_types' => array(
318
  'label' => __( 'Post Types that should be affected', 'permalink-manager' ),
@@ -330,6 +348,13 @@ class Permalink_Manager_Class {
330
 
331
  // Fields for "Regenerate slugs"
332
  $regenerate_slugs = array(
 
 
 
 
 
 
 
333
  'post_types' => array(
334
  'label' => __( 'Post Types that should be affected', 'permalink-manager' ),
335
  'type' => 'checkbox',
@@ -354,119 +379,24 @@ class Permalink_Manager_Class {
354
  function bulk_actions() {
355
  global $wpdb;
356
 
357
- $updated_slugs_count = 0;
358
- $updated_array = array();
359
- $alert_type = $alert_content = $errors = $main_content = '';
360
-
361
  if (isset($_POST['update_all_slugs'])) {
362
-
363
- $slugs = isset($_POST['slug']) ? $_POST['slug'] : array();
364
-
365
- // Double check if the slugs and ids are stored in arrays
366
- if (!is_array($slugs)) $slugs = explode(',', $slugs);
367
-
368
- if (!empty($slugs)) {
369
- foreach($slugs as $id => $new_slug) {
370
- $this_post = get_post($id);
371
-
372
- // Check if slug was changed
373
- if($this_post->post_name != $new_slug) {
374
- // Update slugs
375
- Permalink_Manager_Helper_Functions::update_slug_by_id($new_slug, $id);
376
-
377
- $updated_array[] = array('post_title' => get_the_title($id), 'old_slug' => $this_post->post_name, 'new_slug' => $new_slug);
378
- $updated_slugs_count++;
379
- }
380
-
381
- // Reset slug
382
- $slug = '';
383
- }
384
- }
385
-
386
  } else if (isset($_POST['find-replace-button'])) {
387
-
388
- $var['old_string'] = esc_sql($_POST['permalink-manager']['find-replace']['old_string']);
389
- $var['new_string'] = esc_sql($_POST['permalink-manager']['find-replace']['new_string']);
390
- $post_types_array = ($_POST['permalink-manager']['find-replace']['post_types']);
391
- $post_statuses_array = ($_POST['permalink-manager']['find-replace']['post_statuses']);
392
- $var['post_types'] = implode("', '", $post_types_array);
393
- $var['post_statuses'] = implode("', '", $post_statuses_array);
394
-
395
- // Check if any of variables is not empty
396
- $find_and_replace_fields = $this->fields_arrays('find_and_replace');
397
- foreach($var as $key => $val) {
398
- if(empty($val)) $errors .= '<p>' . sprintf( __( '<strong>"%1s"</strong> field is empty!', 'permalink-manager' ), $find_and_replace_fields[$key]['label'] ) . '</p>';
399
- }
400
-
401
- // Save the rows before they are updated to an array
402
- $posts_to_update = $wpdb->get_results("SELECT post_title, post_name, ID FROM {$wpdb->posts} WHERE post_status IN ('{$var['post_statuses']}') AND post_name LIKE '%{$var['old_string']}%' AND post_type IN ('{$var['post_types']}')", ARRAY_A);
403
-
404
- // Now if the array is not empty use IDs from each subarray as a key
405
- if($posts_to_update && empty($errors)) {
406
- foreach ($posts_to_update as $row) {
407
- // Get new slug
408
- $old_slug = $row['post_name'];
409
- $new_slug = str_replace($var['old_string'], $var['new_string'], $old_slug);
410
-
411
- // Update slugs
412
- Permalink_Manager_Helper_Functions::update_slug_by_id($new_slug, $row['ID']);
413
-
414
- $updated_array[] = array('post_title' => $row['post_title'], 'old_slug' => $old_slug, 'new_slug' => $new_slug);
415
- $updated_slugs_count++;
416
-
417
- // Reset slug
418
- $slug = '';
419
- }
420
- } else {
421
- $alert_type = 'error';
422
- $alert_content = $errors;
423
- }
424
-
425
  } else if (isset($_POST['regenerate-button'])) {
426
-
427
- // Setup needed variables
428
- $post_types_array = ($_POST['permalink-manager']['regenerate_slugs']['post_types']);
429
- $post_statuses_array = ($_POST['permalink-manager']['regenerate_slugs']['post_statuses']);
430
-
431
- // Reset query
432
- $reset_query = new WP_Query( array( 'post_type' => $post_types_array, 'post_status' => $post_statuses_array, 'posts_per_page' => -1 ) );
433
-
434
- // The Loop
435
- if ( $reset_query->have_posts() ) {
436
- while ( $reset_query->have_posts() ) {
437
- $reset_query->the_post();
438
- $this_post = get_post(get_the_ID());
439
-
440
- $correct_slug = sanitize_title(get_the_title());
441
- $old_slug = $this_post->post_name;
442
- $new_slug = wp_unique_post_slug($correct_slug, get_the_ID(), get_post_status(get_the_ID()), get_post_type(get_the_ID()), null);
443
-
444
- if($old_slug != $new_slug) {
445
- $updated_slugs_count++;
446
-
447
- Permalink_Manager_Helper_Functions::update_slug_by_id($new_slug, get_the_ID());
448
- $updated_array[] = array('post_title' => get_the_title(), 'old_slug' => $old_slug, 'new_slug' => $new_slug);
449
- }
450
- }
451
- }
452
-
453
- // Restore original Post Data
454
- wp_reset_postdata();
455
-
456
  // Save Permalink Structures/Permalinks Bases
457
- } else if (isset($_POST['save_permalink_structures'])) {
458
- Permalink_Manager_Helper_Functions::save_option('base-editor', $_POST['permalink-manager']['base-editor']);
459
-
460
- $alert_type = 'updated';
461
- $alert_content = sprintf( __( '<a href="%s">Click here</a> to flush the rewrite rules (it is required to make the new permalinks working).', 'permalink-manager' ), admin_url('admin.php?page=' . PERMALINK_MANAGER_PLUGIN_NAME . '.php&flush_rewrite_rules=true&tab=base_editor'));
462
- Permalink_Manager_Helper_Functions::display_alert($alert_content, $alert_type, true);
463
- return;
464
- // Flush rewrite rules
465
- } else if (isset($_POST['flush_rewrite_rules'])) {
466
- $this->flush_rewrite_rules();
467
- return;
468
  }
469
 
 
 
 
 
 
 
470
  /**
471
  * Display results
472
  */
@@ -474,7 +404,10 @@ class Permalink_Manager_Class {
474
  // Display errors or success message
475
 
476
  // Check how many rows/slugs were affected
477
- if($updated_slugs_count > 0) {
 
 
 
478
  $alert_type = 'updated';
479
  $alert_content = sprintf( _n( '<strong>%d</strong> slug were updated!', '<strong>%d</strong> slugs were updated!', $updated_slugs_count, 'permalink-manager' ), $updated_slugs_count ) . ' ';
480
  $alert_content .= sprintf( __( '<a href="%s">Click here</a> to go to the list of updated slugs', 'permalink-manager' ), '#updated-list');
@@ -487,25 +420,35 @@ class Permalink_Manager_Class {
487
 
488
  // Display summary after update
489
  // Display only if there are any slugs updated
490
- if ( $updated_slugs_count > 0 && $updated_array ) {
491
- add_filter('permalink-manager-after-tabs', function( $arg ) use ( $alert_content, $alert_type, $errors, $updated_array, $main_content ) {
 
 
 
492
 
493
  $header_footer = '<tr>';
494
  $header_footer .= '<th class="column-primary">' . __('Title', 'permalink-manager') . '</th>';
495
- $header_footer .= '<th>' . __('Old slug', 'permalink-manager') . '</th>';
496
- $header_footer .= '<th>' . __('New slug', 'permalink-manager') . '</th>';
 
 
497
  $header_footer .= '</tr>';
498
 
499
  $updated_slugs_count = 0;
 
500
  foreach($updated_array as $row) {
501
  // Odd/even class
502
  $updated_slugs_count++;
503
  $alternate_class = ($updated_slugs_count % 2 == 1) ? ' class="alternate"' : '';
 
 
504
 
505
  $main_content .= "<tr{$alternate_class}>";
506
- $main_content .= '<td class="row-title column-primary" data-colname="' . __('Title', 'permalink-manager') . '">' . $row['post_title'] . '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'permalink-manager') . '</span></button></td>';
507
- $main_content .= '<td data-colname="' . __('Old slug', 'permalink-manager') . '">' . $row['old_slug'] . '</td>';
508
- $main_content .= '<td data-colname="' . __('New slug', 'permalink-manager') . '">' . $row['new_slug'] . '</td>';
 
 
509
  $main_content .= '</tr>';
510
  }
511
 
@@ -526,162 +469,168 @@ class Permalink_Manager_Class {
526
  * Change permalinks for posts, pages & custom post types
527
  */
528
  function custom_permalinks($permalink, $post) {
529
- global $wp_rewrite;
530
 
531
  $post = (is_integer($post)) ? get_post($post) : $post;
532
  $post_type = $post->post_type;
533
- $permastruct = isset($this->permalink_manager_options['base-editor'][$post_type]) ? $this->permalink_manager_options['base-editor'][$post_type] : '';
534
 
535
- // Ignore empty permastructures (do not change them)
536
- if(empty($permastruct) || $post->post_status != 'publish') return $permalink;
537
 
538
- // Get options
539
- if($permastruct) {
540
- $permalink = home_url() . "/" . trim($permastruct, '/');
541
- }
542
 
543
- /**
544
- * Replace Structure Tags
545
- */
546
 
547
- // Get the date
548
- $date = explode(" ",date('Y m d H i s', strtotime($post->post_date)));
549
-
550
- // Get the category (if needed)
551
- $category = '';
552
- if ( strpos($permalink, '%category%') !== false ) {
553
- $cats = get_the_category($post->ID);
554
- if ( $cats ) {
555
- usort($cats, '_usort_terms_by_ID'); // order by ID
556
- $category_object = apply_filters( 'post_link_category', $cats[0], $cats, $post );
557
- $category_object = get_term( $category_object, 'category' );
558
- $category = $category_object->slug;
559
- if ( $parent = $category_object->parent )
560
- $category = get_category_parents($parent, false, '/', true) . $category;
561
- }
562
- // show default category in permalinks, without having to assign it explicitly
563
- if ( empty($category) ) {
564
- $default_category = get_term( get_option( 'default_category' ), 'category' );
565
- $category = is_wp_error( $default_category ) ? '' : $default_category->slug;
566
  }
567
- }
568
 
569
- // Get the author (if needed)
570
- $author = '';
571
- if ( strpos($permalink, '%author%') !== false ) {
572
- $authordata = get_userdata($post->post_author);
573
- $author = $authordata->user_nicename;
574
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
 
576
- // Fix for hierarchical CPT (start)
577
- $full_slug = get_page_uri($post);
578
- $post_type_tag = Permalink_Manager_Helper_Functions::get_post_tag($post_type);
579
-
580
- // Do the replacement (post tag is removed now to enable support for hierarchical CPT)
581
- $tags = array('%year%', '%monthnum%', '%day%', '%hour%', '%minute%', '%second%', '%post_id%', '%category%', '%author%', $post_type_tag);
582
- $replacements = array($date[0], $date[1], $date[2], $date[3], $date[4], $date[5], $post->ID, $category, $author, '');
583
- $permalink = str_replace($tags, $replacements, "{$permalink}{$full_slug}");
584
-
585
- // Replace custom taxonomies
586
- $taxonomies = get_taxonomies( array('public' => true, '_builtin' => false), 'names', 'and' );
587
- if ( $taxonomies ) {
588
- foreach($taxonomies as $taxonomy) {
589
- $tag = "%{$taxonomy}%";
590
- $terms = wp_get_object_terms($post->ID, $taxonomy);
591
- if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) {
592
- $replacement = $terms[0]->slug;
593
- $permalink = str_replace($tag, $replacement, $permalink);
594
  }
595
  }
596
- }
597
 
598
- return $permalink;
 
599
  }
600
 
601
  /**
602
- * Add rewrite rules
603
  */
604
- function custom_cpt_rewrite_rules($rules) {
605
-
606
- global $wp_rewrite;
607
 
608
- $new_rules = array();
609
- $permastructures = $this->permalink_manager_options['base-editor'];
610
 
611
- // Rewrite rules for Posts & Pages are defined in different filters
612
- unset($permastructures['post'], $permastructures['page']);
613
 
614
- foreach($permastructures as $post_type => $permastruct) {
615
- // Ignore empty permastructures (do not add them)
616
- if(empty($permastruct)) continue;
617
 
618
- $new_rules = $wp_rewrite->generate_rewrite_rules($wp_rewrite->root . $permastruct, EP_PERMALINK);
619
-
620
- // Replace old rewrite rules with new ones
621
- $cpt = get_post_type_object( $post_type );
622
- if(isset($cpt->rewrite['slug'])) {
623
- $rules_to_be_replaced = $wp_rewrite->generate_rewrite_rules($wp_rewrite->root . $cpt->rewrite['slug'] . "/%{$post_type}%", EP_NONE);
624
- $rules_to_be_replaced_keys = array_keys($rules_to_be_replaced);
625
 
626
- $split_before = reset($rules_to_be_replaced_keys);
627
- $split_after = end($rules_to_be_replaced_keys);
 
 
628
 
629
- $split_before_pos = array_search($split_before, array_keys($rules));
630
- $split_after_pos = array_search($split_after, array_keys($rules)) + 1;
631
 
632
- $rules_before = array_slice($rules, 0, $split_before_pos, true);
633
- $rules_inside = array_slice($rules, $split_before_pos, ($split_after_pos - $split_before_pos), true);
634
- $rules_after = array_slice($rules, $split_after_pos, null, true);
 
635
 
636
- // $rules = $rules_before + $new_rules + $rules_inside + $rules_after;
637
- /**
638
- * Uncomment above if you would like to keep all original rewrite rules
639
- */
640
- $rules = $rules_before + $new_rules + $rules_after;
641
 
642
- } else {
643
- $rules = array_merge($rules, $new_rules);
644
- }
645
 
 
 
 
 
 
646
  }
647
- return $rules;
648
  }
649
 
650
  /**
651
- * Post Rewrite Rules
652
  */
653
- function custom_post_rewrite_rules($rules) {
654
- global $wp_rewrite;
655
- if(isset($this->permalink_manager_options['base-editor']['post'])) {
656
- $rules = $wp_rewrite->generate_rewrite_rules($wp_rewrite->root . $this->permalink_manager_options['base-editor']['post'], EP_PERMALINK);
657
- }
658
- return $rules;
659
- }
660
 
661
- /**
662
- * Page Rewrite Rules
663
- */
664
- function custom_page_rewrite_rules($rules) {
665
- global $wp_rewrite;
666
- if(isset($this->permalink_manager_options['base-editor']['page'])) {
667
- $rules = $wp_rewrite->generate_rewrite_rules($wp_rewrite->root . $this->permalink_manager_options['base-editor']['page'], EP_PERMALINK);
668
- }
669
- return $rules;
670
- }
671
 
672
- /**
673
- * Flush rewrite rules
674
- */
675
- function flush_rewrite_rules() {
676
- if(isset($_REQUEST['flush_rewrite_rules'])) {
677
- flush_rewrite_rules();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678
 
679
- $alert_type = 'updated';
680
- $alert_content = __( 'The rewrite rules are flushed!', 'permalink-manager' );
681
- return Permalink_Manager_Helper_Functions::display_alert($alert_content, $alert_type, true);
 
 
 
 
 
 
 
 
 
682
  }
683
  }
684
-
685
  }
686
 
687
  /**
@@ -690,10 +639,11 @@ class Permalink_Manager_Class {
690
  function run_permalink_manager() {
691
 
692
  // Load plugin files.
693
- require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-slug-editor.php';
694
  require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-base-editor.php';
695
  require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-screen-options.php';
696
  require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-helper-functions.php';
 
697
 
698
  $Permalink_Manager_Class = new Permalink_Manager_Class();
699
  $Permalink_Manager_Screen_Options = new Permalink_Manager_Screen_Options();
4
  * Plugin Name: Permalink Manager
5
  * Plugin URI: http://maciejbis.net/
6
  * Description: A simple tool that allows to mass update of slugs that are used to build permalinks for Posts, Pages and Custom Post Types.
7
+ * Version: 0.4
8
  * Author: Maciej Bis
9
  * Author URI: http://maciejbis.net/
10
  * License: GPL-2.0+
20
 
21
  // Define the directories used to load plugin files.
22
  define( 'PERMALINK_MANAGER_PLUGIN_NAME', 'permalink-manager' );
23
+ define( 'PERMALINK_MANAGER_VERSION', '0.4' );
24
  define( 'PERMALINK_MANAGER_DIR', untrailingslashit( dirname( __FILE__ ) ) );
25
  define( 'PERMALINK_MANAGER_URL', untrailingslashit( plugins_url( '', __FILE__ ) ) );
26
  define( 'PERMALINK_MANAGER_WEBSITE', 'http://maciejbis.net' );
34
  public function __construct() {
35
 
36
  $this->permalink_manager_options = get_option('permalink-manager');
37
+ $this->permalink_manager_uris = get_option('permalink-manager-uris');
38
+ $this->permalink_manager_permastructs = get_option('permalink-manager-permastructs');
39
 
40
  if( is_admin() ) {
41
  add_action( 'plugins_loaded', array($this, 'localize_me') );
42
+ add_action( 'init', array($this, 'upgrade_plugin'), 99999 );
43
+ add_action( 'wp_loaded', array($this, 'bulk_actions'), 1 );
44
  add_action( 'admin_menu', array($this, 'add_menu_page') );
45
+ add_action( 'save_post', array($this, 'update_single_uri'), 10, 3 );
46
  add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugins_page_links') );
47
+ add_filter( 'get_sample_permalink_html', array($this, 'edit_uri_box'), 10, 4 );
 
 
 
48
  }
49
 
50
+ add_action( 'wp_loaded', array($this, 'permalink_filters'), 9);
51
+
52
+ }
53
+
54
+ function permalink_filters() {
55
  // Public functions
56
+ add_filter( 'request', array($this, 'detect_post') );
57
  add_filter( '_get_page_link', array($this, 'custom_permalinks'), 999, 2);
58
  add_filter( 'page_link', array($this, 'custom_permalinks'), 999, 2);
59
  add_filter( 'post_link', array($this, 'custom_permalinks'), 999, 2);
60
  add_filter( 'post_type_link', array($this, 'custom_permalinks'), 999, 2);
 
61
  }
62
 
63
  /**
84
  function slug_editor_html() {
85
  global $wpdb;
86
 
87
+ $Permalink_Manager_Editor = new Permalink_Manager_Editor();
88
+ $Permalink_Manager_Editor->set_screen_option_fields($this->fields_arrays('screen_options'));
89
+ $Permalink_Manager_Editor->prepare_items($wpdb->posts);
90
 
91
  ?>
92
 
93
  <form id="permalinks-table" method="post">
94
  <input type="hidden" name="tab" value="slug_editor" />
95
+ <?php echo $Permalink_Manager_Editor->display(); ?>
96
  </form>
97
  <?php
98
  }
101
  * Mass replace options page.
102
  */
103
  function find_and_replace_html() {
104
+ $button = get_submit_button( __( 'Find & Replace Slugs', 'permalink-manager' ), 'primary', 'find-replace-button', false );
105
 
106
  $return = "<form id=\"permalinks-table-find-replace\" method=\"post\">";
107
  $return .= "<input type=\"hidden\" name=\"tab\" value=\"find_and_replace\" />";
173
  // Tabs array with assigned functions used to display HTML content.
174
  $tabs = array(
175
  'slug_editor' => array(
176
+ 'name' => __('Permalink editor', 'permalink-manager'),
177
  'function' => 'slug_editor_html',
178
+ 'description' => __('You can disable/enable selected post types from the table below using <strong>"Screen Options"</strong> (click on the upper-right button to show it) section above.', 'permalink-manager'),
179
  ),
180
  'find_and_replace' => array(
181
  'name' => __('Find and replace', 'permalink-manager'),
182
+ 'function' => 'find_and_replace_html'
 
183
  ),
184
  'regenerate_slugs' => array(
185
+ 'name' => __('Regenerate/Reset', 'permalink-manager'),
186
+ 'function' => 'regenerate_slugs_html'
 
187
  ),
188
  'base_editor' => array(
189
+ 'name' => __('Base editor', 'permalink-manager'),
190
  'function' => 'base_editor_html',
191
  'warning' => array(
192
  sprintf(__('<strong>This is an experimental feature!</strong> Please report all the bugs & issues <a href="%s">here</a>.', 'permalink-manager'), 'https://wordpress.org/support/plugin/permalink-manager'),
193
+ __('Each Custom Post Type should have their own, unique front (eg. <em>products</em> for Products)', 'permalink-manager'),
194
+ __('Please note that the following settings will be applied only to new posts.<br />If you want to apply them to exisiting posts, you will need to regenerate the URIs in <strong>"Regnerate/Reset"</strong> section (with <strong>"Slugs & bases"</strong> option selected).', 'permalink-manager'),
195
  ),
196
  'description' => (sprintf( __('All the <a href="%s" target="_blank">Structure Tags</a> allowed are listed below. Please note that some of them can be used only for particular Post Types.', 'permalink-manager'), "https://codex.wordpress.org/Using_Permalinks#Structure_Tags") . "<br />" . Permalink_Manager_Helper_Functions::get_all_structure_tags())
197
  ),
309
 
310
  // Fields for "Find and replace"
311
  $find_and_replace = array(
312
+ 'clearfix1' => array(
313
+ 'type' => 'clearfix'
314
+ ),
315
  'old_string' => array(
316
  'label' => __( 'Find ...', 'permalink-manager' ),
317
  'type' => 'text',
318
+ 'container_class' => 'half'
319
  ),
320
  'new_string' => array(
321
  'label' => __( 'Replace with ...', 'permalink-manager' ),
322
  'type' => 'text',
323
+ 'container_class' => 'half half2'
324
+ ),
325
+ 'clearfix2' => array(
326
+ 'type' => 'clearfix'
327
+ ),
328
+ 'variant' => array(
329
+ 'label' => __( 'Select which elements should be affected', 'permalink-manager' ),
330
+ 'type' => 'radio',
331
+ 'choices' => array('both' => '<strong>' . __('Plugin Slugs & Bases (Full URIs)', 'permalink-manager') . '</strong>', 'slugs' => '<strong>' . __('Only Plugin Slugs', 'permalink-manager') . '</strong>', 'post_names' => '<strong>' . __('Plugin Slugs & Wordpress Native Slugs (Post Names)', 'permalink-manager') . '</strong>'),
332
+ 'default' => array('slugs'),
333
+ 'desc' => __('First two options will affect settings used only by this plugin.<br />A MySQL backup is recommended before using third option - it overwrites the value of <strong>post_name</strong> field (part of <strong>$post</strong> object used by Wordpress core).', 'permalink-manager')
334
  ),
335
  'post_types' => array(
336
  'label' => __( 'Post Types that should be affected', 'permalink-manager' ),
348
 
349
  // Fields for "Regenerate slugs"
350
  $regenerate_slugs = array(
351
+ 'variant' => array(
352
+ 'label' => __( 'Select which elements should be affected', 'permalink-manager' ),
353
+ 'type' => 'radio',
354
+ 'choices' => array('both' => '<strong>' . __('Plugin Slugs & Bases (Full URIs)', 'permalink-manager') . '</strong>', 'slugs' => '<strong>' . __('Only Plugin Slugs', 'permalink-manager') . '</strong>', 'post_names' => '<strong>' . __('Plugin Slugs & Wordpress Native Slugs (Post Names)', 'permalink-manager') . '</strong>'),
355
+ 'default' => array('slugs'),
356
+ 'desc' => __('First two options will affect settings used only by this plugin.<br />A MySQL backup is recommended before using third option - it overwrites the value of <strong>post_name</strong> field (part of <strong>$post</strong> object used by Wordpress core).', 'permalink-manager')
357
+ ),
358
  'post_types' => array(
359
  'label' => __( 'Post Types that should be affected', 'permalink-manager' ),
360
  'type' => 'checkbox',
379
  function bulk_actions() {
380
  global $wpdb;
381
 
382
+ // Trigger a selected function
 
 
 
383
  if (isset($_POST['update_all_slugs'])) {
384
+ $output = Permalink_Manager_Actions::update_all_permalinks();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  } else if (isset($_POST['find-replace-button'])) {
386
+ $output = Permalink_Manager_Actions::find_replace($this->fields_arrays('find_and_replace'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  } else if (isset($_POST['regenerate-button'])) {
388
+ $output = Permalink_Manager_Actions::regenerate_all_permalinks();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  // Save Permalink Structures/Permalinks Bases
390
+ } else if (isset($_POST['save_permastructs'])) {
391
+ $output = Permalink_Manager_Actions::update_permastructs();
 
 
 
 
 
 
 
 
 
392
  }
393
 
394
+ // Load variables
395
+ $updated_array = isset($output['updated']) ? $output['updated'] : array();
396
+ $updated_slugs_count = isset($output['updated_count']) ? $output['updated_count'] : 0;
397
+ $alert_content = isset($output['alert_content']) ? $output['alert_content'] : "";
398
+ $alert_type = isset($output['alert_type']) ? $output['alert_type'] : "";
399
+
400
  /**
401
  * Display results
402
  */
404
  // Display errors or success message
405
 
406
  // Check how many rows/slugs were affected
407
+ if (isset($_POST['save_permastructs'])) {
408
+ $alert_type = 'updated';
409
+ $alert_content = __( 'Permastructures were updated!', 'permalink-manager' ) . ' ';
410
+ } else if($updated_slugs_count > 0) {
411
  $alert_type = 'updated';
412
  $alert_content = sprintf( _n( '<strong>%d</strong> slug were updated!', '<strong>%d</strong> slugs were updated!', $updated_slugs_count, 'permalink-manager' ), $updated_slugs_count ) . ' ';
413
  $alert_content .= sprintf( __( '<a href="%s">Click here</a> to go to the list of updated slugs', 'permalink-manager' ), '#updated-list');
420
 
421
  // Display summary after update
422
  // Display only if there are any slugs updated
423
+ if ( $updated_slugs_count > 0 && is_array($updated_array) ) {
424
+ add_filter('permalink-manager-after-tabs', function( $arg ) use ( $alert_content, $alert_type, $updated_array ) {
425
+
426
+ // Check if slugs should be displayed
427
+ $first_slug = reset($updated_array);
428
 
429
  $header_footer = '<tr>';
430
  $header_footer .= '<th class="column-primary">' . __('Title', 'permalink-manager') . '</th>';
431
+ $header_footer .= '<th>' . __('Old URI', 'permalink-manager') . '</th>';
432
+ $header_footer .= '<th>' . __('New URI', 'permalink-manager') . '</th>';
433
+ $header_footer .= (isset($first_slug['old_slug'])) ? '<th>' . __('Old Slug', 'permalink-manager') . '</th>' : "";
434
+ $header_footer .= (isset($first_slug['new_slug'])) ? '<th>' . __('New Slug', 'permalink-manager') . '</th>' : "";
435
  $header_footer .= '</tr>';
436
 
437
  $updated_slugs_count = 0;
438
+ $main_content = "";
439
  foreach($updated_array as $row) {
440
  // Odd/even class
441
  $updated_slugs_count++;
442
  $alternate_class = ($updated_slugs_count % 2 == 1) ? ' class="alternate"' : '';
443
+ //$permalink = Permalink_Manager_Helper_Functions::get_correct_permalink($row[ 'ID' ]);
444
+ $permalink = home_url("{$row['new_uri']}");
445
 
446
  $main_content .= "<tr{$alternate_class}>";
447
+ $main_content .= '<td class="row-title column-primary" data-colname="' . __('Title', 'permalink-manager') . '">' . $row['post_title'] . "<a target=\"_blank\" href=\"{$permalink}\"><small>{$permalink}</small></a>" . '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'permalink-manager') . '</span></button></td>';
448
+ $main_content .= '<td data-colname="' . __('Old URI', 'permalink-manager') . '">' . $row['old_uri'] . '</td>';
449
+ $main_content .= '<td data-colname="' . __('New URI', 'permalink-manager') . '">' . $row['new_uri'] . '</td>';
450
+ $main_content .= (isset($row['old_slug'])) ? '<td data-colname="' . __('Old Slug', 'permalink-manager') . '">' . $row['old_slug'] . '</td>' : "";
451
+ $main_content .= (isset($row['new_slug'])) ? '<td data-colname="' . __('New Slug', 'permalink-manager') . '">' . $row['new_slug'] . '</td>' : "";
452
  $main_content .= '</tr>';
453
  }
454
 
469
  * Change permalinks for posts, pages & custom post types
470
  */
471
  function custom_permalinks($permalink, $post) {
472
+ global $wp_rewrite, $permalink_manager;
473
 
474
  $post = (is_integer($post)) ? get_post($post) : $post;
475
  $post_type = $post->post_type;
 
476
 
477
+ // Do not change permalink of frontpage
478
+ if(get_option('page_on_front') == $post->ID) { return $permalink; }
479
 
480
+ $uris = $this->permalink_manager_uris;
481
+ if(isset($uris[$post->ID])) $permalink = home_url('/') . $uris[$post->ID];
 
 
482
 
483
+ return $permalink;
484
+ }
 
485
 
486
+ /**
487
+ * Used to optimize SQL queries amount instead of rewrite rules
488
+ */
489
+ function detect_post($query) {
490
+
491
+ // GET URL
492
+ // Fix for Wordpress installed in subdirectories (protocol does not matter here)
493
+ $url = str_replace(home_url(), "http://" . $_SERVER['HTTP_HOST'], "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");
494
+
495
+ // Check if it is correct URL
496
+ if (filter_var($url, FILTER_VALIDATE_URL)) {
497
+
498
+ // Separate endpoints (if set) - support for comment pages will be added later
499
+ preg_match("/(.*)\/(page|feed|embed|attachment|track)\/(.*)/", $url, $url_with_endpoints);
500
+ if(isset($url_with_endpoints[3]) && !(empty($url_with_endpoints[3]))) {
501
+ $url = $url_with_endpoints[1];
502
+ $endpoint = str_replace(array('page', 'trackback'), array('paged', 'tb'), $url_with_endpoints[2]);
503
+ $endpoint_value = $url_with_endpoints[3];
 
504
  }
 
505
 
506
+ // Parse URL
507
+ $url_parts = parse_url($url);
508
+ $uri = trim($url_parts['path'], "/");
509
+ if(empty($uri)) return $query;
510
+
511
+ // Check if current URL is assigned to any post
512
+ $uris = $this->permalink_manager_uris;
513
+ if(!(is_array($uris))) return $query;
514
+ $post_id = array_search($uri, $uris);
515
+
516
+ if(isset($post_id) && is_numeric($post_id)) {
517
+ $post_to_load = get_post($post_id);
518
+ $original_page_uri = get_page_uri($post_to_load->ID);
519
+ unset($query['attachment']);
520
+ unset($query['error']);
521
+
522
+ if($post_to_load->post_type == 'page') {
523
+ $query['pagename'] = $original_page_uri;
524
+ } elseif($post_to_load->post_type == 'post') {
525
+ $query['name'] = $original_page_uri;
526
+ } else {
527
+ $query['post_type'] = $post_to_load->post_type;
528
+ $query['name'] = $original_page_uri;
529
+ $query[$post_to_load->post_type] = $original_page_uri;
530
+ }
531
 
532
+ // Add endpoint
533
+ if(isset($endpoint_value)) {
534
+ $query[$endpoint] = $endpoint_value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  }
536
  }
 
537
 
538
+ }
539
+ return $query;
540
  }
541
 
542
  /**
543
+ * Allow to edit URIs from "Edit Post" admin pages
544
  */
545
+ function edit_uri_box($html, $id, $new_title, $new_slug) {
 
 
546
 
547
+ global $post;
 
548
 
549
+ // Do not change anything if post is not saved yet
550
+ if(empty($post->post_name)) return $html;
551
 
552
+ $uris = $this->permalink_manager_uris;
553
+ $default_uri = trim(str_replace(home_url("/"), "", get_permalink($id)), "/");
554
+ $uri = (isset($uri[$id])) ? $uri[$id] : $default_uri;
555
 
556
+ $html = preg_replace("/(<strong>(.*)<\/strong>)(.*)/is", "$1 ", $html);
557
+ $html .= home_url("/") . " <span id=\"editable-post-name\"><input type='text' value='{$uri}' name='custom_uri'/></span>";
558
+ return $html;
559
+ }
 
 
 
560
 
561
+ /**
562
+ * Update URI from "Edit Post" admin page
563
+ */
564
+ function update_single_uri($post_id, $post, $update) {
565
 
566
+ // Ignore trashed items
567
+ if($post->post_status == 'trash') return;
568
 
569
+ $uris = $this->permalink_manager_uris;
570
+ $old_default_uri = trim(str_replace(home_url("/"), "", get_permalink($post_id)), "/");
571
+ $new_default_uri = Permalink_Manager_Helper_Functions::get_uri($post, true);
572
+ $new_uri = '';
573
 
574
+ // Check if user changed URI (available after post is saved)
575
+ if(isset($_POST['custom_uri'])) {
576
+ $new_uri = trim($_POST['custom_uri'], "/");
577
+ }
 
578
 
579
+ // A little hack
580
+ $new_uri = ($new_uri) ? $new_uri : $new_default_uri;
 
581
 
582
+ // Do not store default values
583
+ if(isset($uris[$post_id]) && ($new_uri == $old_default_uri)) {
584
+ unset($uris[$post_id]);
585
+ } else if ($new_uri != $old_default_uri) {
586
+ $uris[$post_id] = $new_uri;
587
  }
588
+ update_option('permalink-manager-uris', $uris);
589
  }
590
 
591
  /**
592
+ * Convert old plugin structure to the new solution (this function will be removed in 1.0 version)
593
  */
594
+ function upgrade_plugin() {
 
 
 
 
 
 
595
 
596
+ global $wpdb;
 
 
 
 
 
 
 
 
 
597
 
598
+ /*
599
+ * Separate slugs from rest of plugin options
600
+ */
601
+ $options = $this->permalink_manager_options;
602
+ //if !empty($this->permalink_manager_options['base-editor'])
603
+ if (isset($options['base-editor']) && is_array($options['base-editor'])) {
604
+ $old_permastructs = $options['base-editor'];
605
+ $new_permastructs = $uris = array();
606
+
607
+ // At first save permastructs to new separate option field
608
+ foreach($old_permastructs as $post_type => $permastruct) {
609
+ $new_permastructs[$post_type] = trim(str_replace(Permalink_Manager_Helper_Functions::get_post_tag($post_type), '', $permastruct), "/");
610
+ }
611
+ unset($options['base-editor']);
612
+
613
+ // Grab posts from database
614
+ $sql_query = "SELECT * FROM {$wpdb->posts} WHERE post_status IN ('publish') LIMIT 99999";
615
+ $posts = $wpdb->get_results($sql_query);
616
+
617
+ foreach($posts as $post) {
618
+ $uri = Permalink_Manager_Helper_Functions::get_uri($post, true);
619
 
620
+ // Do not save default permastructures
621
+ $default_permastruct = trim( Permalink_Manager_Helper_Functions::get_default_permastruct($post_type), "/" );
622
+ if ($permastruct != $default_permastruct) $uris[$post->ID] = trim($uri, "/");
623
+ }
624
+
625
+ // Save new option fields
626
+ update_option('permalink-manager-uris', $uris);
627
+ update_option('permalink-manager', $options);
628
+ update_option('permalink-manager-permastructs', $new_permastructs);
629
+
630
+ // Reset rewrite rules
631
+ flush_rewrite_rules();
632
  }
633
  }
 
634
  }
635
 
636
  /**
639
  function run_permalink_manager() {
640
 
641
  // Load plugin files.
642
+ require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-editor.php';
643
  require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-base-editor.php';
644
  require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-screen-options.php';
645
  require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-helper-functions.php';
646
+ require_once PERMALINK_MANAGER_DIR . '/inc/permalink-manager-actions.php';
647
 
648
  $Permalink_Manager_Class = new Permalink_Manager_Class();
649
  $Permalink_Manager_Screen_Options = new Permalink_Manager_Screen_Options();