Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.42% |
61 / 66 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
Plugins_List_Table | |
92.42% |
61 / 66 |
|
75.00% |
3 / 4 |
25.27 | |
0.00% |
0 / 1 |
plugin_specific_action_links | |
100.00% |
34 / 34 |
|
100.00% |
1 / 1 |
10 | |||
row_meta | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
8 | |||
merge_arrays | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
edit_plugins_array | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * The main class in the plugin. |
4 | * Hooked to actions and filters defined in WP_Plugins_List_Table. |
5 | * |
6 | * Actions links runs first, but needs to pull in links from meta links, so fires that filter during its run, which |
7 | * is then fired again later during the normal meta links run. |
8 | * |
9 | * @link https://github.com/brianhenryie/bh-wp-plugins-page |
10 | * @since 1.0.0 |
11 | * |
12 | * @package brianhenryie/bh-wp-plugins-page |
13 | */ |
14 | |
15 | namespace BrianHenryIE\WP_Plugins_Page\Admin; |
16 | |
17 | use BrianHenryIE\WP_Plugins_Page\API\API; |
18 | use BrianHenryIE\WP_Plugins_Page\API\Parsed_Link; |
19 | |
20 | /** |
21 | * Parses each link, then decides to move or discard, then removes formatting. |
22 | * |
23 | * @see \WP_List_Table |
24 | * @see \WP_Plugins_List_Table |
25 | */ |
26 | class Plugins_List_Table { |
27 | |
28 | /** |
29 | * Links found in the first column that should be moved to the middle column. |
30 | * indexed by plugin basename. |
31 | * |
32 | * @var array<string, array<Parsed_Link>> |
33 | */ |
34 | protected array $external_parsed_action_links = array(); |
35 | |
36 | /** |
37 | * Links from the middle column that should be moved to the first column. |
38 | * indexed by plugin basename. |
39 | * |
40 | * @var array<string, array<Parsed_Link>> |
41 | */ |
42 | protected array $internal_parsed_meta_links = array(); |
43 | |
44 | /** |
45 | * Hooked to plugin-specific action links filters (by looping over 'active_plugins' option). |
46 | * |
47 | * @hooked plugin_action_links_{$basename} |
48 | * |
49 | * @param array<int|string, string> $action_links The existing plugin links (usually "Deactivate"). |
50 | * @param string $plugin_basename The plugin's directory/filename.php. |
51 | * @param null|array<int|string, mixed> $plugin_data An array of plugin data. See `get_plugin_data()`. |
52 | * @param string $context The plugin context. 'all'|'active'|'inactive'|'recently_activated' |
53 | * |'upgrade'|'mustuse'|'dropins'|'search'. |
54 | * |
55 | * @return array<int|string, string> The links to display below the plugin name on plugins.php. |
56 | */ |
57 | public function plugin_specific_action_links( array $action_links, string $plugin_basename, ?array $plugin_data, string $context ): array { |
58 | |
59 | // This is probably the case where JetPack (or maybe another plugin, like this does) is running `apply_filters`, so this isn't the case we want to work on. |
60 | if ( empty( $plugin_data ) ) { |
61 | return $action_links; |
62 | } |
63 | |
64 | $parsed_action_links = array(); |
65 | foreach ( $action_links as $key => $html_string ) { |
66 | $parsed_action_links[ $key ] = new Parsed_Link( $key, $html_string ); |
67 | } |
68 | |
69 | $internal_parsed_action_links = array(); |
70 | $external_parsed_action_links = array(); |
71 | |
72 | // Save external links to move them to the middle column. |
73 | foreach ( $parsed_action_links as $key => $parsed_link ) { |
74 | |
75 | if ( $parsed_link->has_external_url() ) { |
76 | $external_parsed_action_links[ $key ] = $parsed_link; |
77 | } else { |
78 | $internal_parsed_action_links[ $key ] = $parsed_link; |
79 | } |
80 | } |
81 | |
82 | $this->external_parsed_action_links[ $plugin_basename ] = $external_parsed_action_links; |
83 | |
84 | /** |
85 | * Get internal links from second column. |
86 | * |
87 | * We're already hooked on this filter. We need to invoke it to pull in data from other plugins. |
88 | * |
89 | * @see self::row_meta() |
90 | */ |
91 | apply_filters( 'plugin_row_meta', array(), $plugin_basename, $plugin_data, $context ); |
92 | $internal_parsed_meta_links = $this->internal_parsed_meta_links[ $plugin_basename ] ?? array(); |
93 | |
94 | /** |
95 | * All links we want in this column. |
96 | * |
97 | * @var Parsed_Link[] $parsed_action_links |
98 | */ |
99 | $parsed_action_links = $this->merge_arrays( array( $internal_parsed_action_links, $internal_parsed_meta_links ) ); |
100 | |
101 | // Reorder: |
102 | // Move settings to the beginning. |
103 | // Move Logs second to end. |
104 | // Move Deactivate at the end. |
105 | $ordered_links_arrays = array( |
106 | 'settings' => array(), |
107 | 'links' => array(), |
108 | 'log' => array(), |
109 | 'deactivate' => array(), |
110 | ); |
111 | |
112 | // If there is no anchor, e.g. it is just text, we do not want it in the action links. |
113 | // Filter unwanted links. |
114 | // Remove upsells. |
115 | foreach ( $parsed_action_links as $key => $parsed_link ) { |
116 | |
117 | if ( $parsed_link->is_empty() |
118 | || $parsed_link->is_contains_unwanted_terms() ) { |
119 | continue; |
120 | } |
121 | |
122 | $type = $parsed_link->get_type() ?? 'links'; |
123 | |
124 | if ( is_int( $key ) ) { |
125 | $ordered_links_arrays[ $type ][] = $parsed_link; |
126 | } else { |
127 | $ordered_links_arrays[ $type ][ $key ] = $parsed_link; |
128 | } |
129 | } |
130 | |
131 | /** |
132 | * Merge the sorted links into one array. |
133 | * |
134 | * @var Parsed_Link[] $ordered_links |
135 | */ |
136 | $ordered_links = $this->merge_arrays( $ordered_links_arrays ); |
137 | |
138 | $cleaned_action_links = array(); |
139 | foreach ( $ordered_links as $key => $parsed_link ) { |
140 | $cleaned_action_links[ $key ] = $parsed_link->get_cleaned_link(); |
141 | } |
142 | return $cleaned_action_links; |
143 | } |
144 | |
145 | /** |
146 | * Row meta is the middle column. |
147 | * |
148 | * Thankfully, plugin_row_meta runs after plugin_action_links, allowing us to move links from the more important |
149 | * column to the description column. |
150 | * |
151 | * @hooked plugin_row_meta |
152 | * Hooked at 9999 so all links have been added first. |
153 | * |
154 | * @see https://rudrastyh.com/wordpress/plugin_action_links-plugin_row_meta.html |
155 | * |
156 | * @param string[] $meta_links The meta information/links displayed by the plugin description. |
157 | * @param string $plugin_file_name The plugin filename to match when filtering. |
158 | * @param string[] $plugin_data Associative array including PluginURI, slug, Author, Version. |
159 | * @param string $status The plugin status, e.g. 'Inactive'. |
160 | * |
161 | * @return string[] The filtered $plugin_meta. |
162 | */ |
163 | public function row_meta( array $meta_links, $plugin_file_name, ?array $plugin_data, ?string $status ): array { |
164 | |
165 | $parsed_meta_links = array(); |
166 | foreach ( $meta_links as $key => $html_string ) { |
167 | $parsed_meta_links[ $key ] = new Parsed_Link( $key, $html_string ); |
168 | } |
169 | |
170 | $internal_parsed_meta_links = array(); |
171 | $external_parsed_meta_links = array(); |
172 | |
173 | // Save external links to move them to the middle column. |
174 | foreach ( $parsed_meta_links as $key => $parsed_link ) { |
175 | |
176 | // Check all URLs in the text for external links. |
177 | if ( $parsed_link->has_internal_url() && 'view-details' !== $parsed_link->get_type() ) { |
178 | $internal_parsed_meta_links[ $key ] = $parsed_link; |
179 | } else { |
180 | $external_parsed_meta_links[ $key ] = $parsed_link; |
181 | } |
182 | } |
183 | |
184 | // Save internal links for use in the first column. |
185 | $this->internal_parsed_meta_links[ $plugin_file_name ] = $internal_parsed_meta_links; |
186 | |
187 | // Get external links from first column. |
188 | $external_parsed_action_links = $this->external_parsed_action_links[ $plugin_file_name ] ?? array(); |
189 | |
190 | /** |
191 | * All the links we want to display in this column. |
192 | * |
193 | * @var Parsed_Link[] $external_parsed_meta_links |
194 | */ |
195 | $external_parsed_meta_links = $this->merge_arrays( array( $external_parsed_meta_links, $external_parsed_action_links ) ); |
196 | |
197 | // Filter unwanted links. |
198 | // Remove upsells. |
199 | // Remove external license links. |
200 | $cleaned_links = array(); |
201 | foreach ( $external_parsed_meta_links as $key => $parsed_link ) { |
202 | |
203 | if ( $parsed_link->is_empty() |
204 | || $parsed_link->is_contains_unwanted_terms() ) { |
205 | continue; |
206 | } |
207 | |
208 | $parsed_link->replace_text_with_icons(); |
209 | $cleaned_links[ $key ] = $parsed_link->get_cleaned_link(); |
210 | } |
211 | |
212 | return $cleaned_links; |
213 | } |
214 | |
215 | |
216 | /** |
217 | * Merge associative arrays, preserve string keys. |
218 | * |
219 | * @param array<mixed> $all_arrays Array of arrays. |
220 | * |
221 | * @return array<mixed> |
222 | */ |
223 | protected function merge_arrays( array $all_arrays ): array { |
224 | $merged_array = array(); |
225 | |
226 | foreach ( $all_arrays as $sub_array ) { |
227 | foreach ( $sub_array as $key => $value ) { |
228 | if ( is_int( $key ) ) { |
229 | $merged_array[] = $value; |
230 | } else { |
231 | $merged_array[ $key ] = $value; |
232 | } |
233 | } |
234 | } |
235 | return $merged_array; |
236 | } |
237 | |
238 | /** |
239 | * Merge our saved changes into the get_plugins() array when the page is rendering. |
240 | * |
241 | * @hooked all_plugins |
242 | * @see \WP_Plugins_List_Table::prepare_items() |
243 | * |
244 | * @param array<string,array<string,string>> $all_plugins The WordPress `get_plugins()` array. |
245 | * |
246 | * @return array<string,array<string,string>> |
247 | */ |
248 | public function edit_plugins_array( array $all_plugins ): array { |
249 | |
250 | $changes = get_option( API::PLUGINS_PAGE_CHANGES_OPTION_NAME, array() ); |
251 | |
252 | foreach ( $changes as $plugin_basename => $plugin_changes ) { |
253 | if ( isset( $all_plugins[ $plugin_basename ] ) ) { |
254 | $all_plugins[ $plugin_basename ] = array_merge( $all_plugins[ $plugin_basename ], $plugin_changes ); |
255 | } |
256 | } |
257 | |
258 | return $all_plugins; |
259 | } |
260 | } |