PluginProbe
Filter Everything — WordPress & WooCommerce Filters / trunk
Filter Everything — WordPress & WooCommerce Filters vtrunk
1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 All 51 releases
filter-everything / src / Settings / Tabs / PermalinksTab.php

PermalinksTab.php in Filter Everything — WordPress & WooCommerce Filters trunk, at src/Settings/Tabs/PermalinksTab.php

450 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace FilterEverything\Filter;
5
6 if ( ! defined('ABSPATH') ) {
7 exit;
8 }
9
10 class PermalinksTab extends BaseSettings
11 {
12 use SeoTabTrait;
13 private $em;
14
15 private $fs;
16
17 protected $page = 'wpc-filter-admin-permalinks';
18
19 protected $group = 'wpc_seo_settings_permalinks';
20
21 public $optionName = 'wpc_filter_permalinks';
22
23 public function init()
24 {
25 add_action( 'admin_init', array( $this, 'initSettings') );
26 add_filter( 'wpc_pre_save_filter', array( $this, 'maybeAddGlobalPrefix' ) );
27 add_filter( 'wpc_after_get_filter', array( $this, 'prependGlobalPrefix' ) );
28
29 add_action( 'after_delete_post', array( $this, 'maybeRemoveGlobalPrefix' ), 10, 2 );
30
31 add_filter( 'pre_update_option', [$this, 'preUpdatePermalinks'], 10, 3 );
32
33 $this->em = Container::instance()->getEntityManager();
34 $this->fs = Container::instance()->getFilterService();
35
36 }
37
38 public function initSettings()
39 {
40 $saved_options = get_option( $this->optionName );
41 $flat_entities = $this->em->getFlatEntities();
42
43 register_setting( $this->group, $this->optionName );
44
45 $settings = array(
46 'wpc_slugs' => array(
47 'label' => esc_html__( 'Global URL Var Names', 'filter-everything' ),
48 )
49 );
50
51 if ( defined('FLRT_FILTERS_PRO') ) {
52 $settings = array(
53 'wpc_slugs' => array(
54 'label' => esc_html__( 'Global filter prefixes and order', 'filter-everything' ),
55 )
56 );
57 }
58
59 if ( ! $saved_options ) {
60
61 add_action('wpc_after_sections_settings_fields_wpc_slugs', array( $this, 'noFiltersMessage' ) );
62
63 } else {
64 /**
65 * @bug When you draggin field its size is changing.
66 */
67
68 // Move post Meta Num values into end of the array
69 $sorted_saved_options = $this->movePostMetaNumInTheEnd( $saved_options );
70
71 foreach( $sorted_saved_options as $entity => $slug ) {
72
73 $entity_name = $this->fs->getEntityCanonicalName( $entity );
74
75 $classes = array( 'regular-text' );
76
77 if( defined('FLRT_FILTERS_PRO') ){
78 $classes[] = 'pro-version';
79 }else{
80 $classes[] = 'free-version';
81 }
82
83 if ( ! in_array( $entity_name, [ 'post_meta_num', 'tax_numeric', 'post_date', 'post_meta_date' ] ) ) {
84 $classes[] = 'wpc-sortable-row';
85 }
86
87 $class = implode( ' ', $classes );
88
89 if( isset( $flat_entities[ $entity_name ] ) ) {
90
91 $translatable_title = defined('FLRT_FILTERS_PRO') ? sprintf( esc_html__( '%s prefix', 'filter-everything' ), $flat_entities[ $entity_name ] ) : sprintf( '%s', $flat_entities[ $entity_name ] );
92
93 $settings['wpc_slugs']['fields'][$entity] = array(
94 'type' => 'text',
95 'title' => $translatable_title,
96 'class' => $class,
97 'sortable' => true
98 );
99 }
100 }
101 }
102 if (!empty($settings)){
103 $first_key = array_key_first($settings);
104 $settings = $this->addSectionSettingsWrapper($settings, true);
105 add_action('wpc_after_seo_setting_section_title_' . $first_key, array( $this, 'explanationMessage' ));
106 }
107
108 $this->registerSettings( $settings, $this->page, $this->optionName );
109 }
110
111 public function movePostMetaNumInTheEnd( $saved_options )
112 {
113 $num_saved_options = [];
114 $other_saved_options = [];
115
116 foreach( $saved_options as $entity => $slug ) {
117 if ( ( mb_strpos( $entity, 'post_meta_num' ) !== false ) || ( mb_strpos( $entity, 'tax_numeric' ) !== false ) || ( mb_strpos( $entity, 'post_date' ) !== false ) || ( mb_strpos( $entity, 'post_meta_date' ) !== false ) ) {
118 $num_saved_options[ $entity ] = $slug;
119 } else {
120 $other_saved_options[ $entity ] = $slug;
121 }
122 }
123
124 return array_merge( $other_saved_options, $num_saved_options );
125 }
126
127 private function updatePostsPrefixes( $prefixChangeFrom, $prefixChangeTo )
128 {
129 $result = true;
130
131 if( ! $prefixChangeFrom || ! $prefixChangeTo ){
132 return false;
133 }
134
135 if( $prefixChangeFrom === $prefixChangeTo ){
136 return false;
137 }
138
139 $args = array(
140 'name' => $prefixChangeFrom,
141 'post_type' => FLRT_FILTERS_POST_TYPE,
142 'fields' => 'ids',
143 'posts_per_page' => -1
144 );
145
146 $filterPosts = new \WP_Query( $args );
147 $postsToChange = $filterPosts->get_posts();
148
149 if( ! empty( $postsToChange ) ){
150 foreach ( $postsToChange as $postId ){
151
152 $toSave = array(
153 'ID' => $postId,
154 'post_name' => $prefixChangeTo
155 );
156
157 $toSave = wp_slash( $toSave );
158
159 if( ! wp_update_post( $toSave ) && $result ){
160 $result = false;
161 }
162 }
163 }
164
165 return $result;
166 }
167
168 public function preUpdatePermalinks( $prefixesList, $option, $oldPrefixesList )
169 {
170 if( $option === $this->optionName ){
171 if( is_array( $prefixesList ) && ! empty( $prefixesList ) ){
172 $validator = new Validator();
173 $sanitizedPrefixesList = [];
174
175 foreach( $prefixesList as $entityKey => $prefix ){
176 $prefix = preg_replace( '/[^a-z0-9\-\_]+/', '', mb_strtolower($prefix) );
177 $prefix = trim($prefix, '-');
178
179 // Ensure, that prefix contains at least one alphabetic character
180 // Also this prevents from empty prefix
181 if( ! $validator->validateAlphabCharsExists( $prefix ) ){
182 add_settings_error( 'general', 'settings_updated', esc_html__( 'The Prefix must contain at least one alphabetical character.', 'filter-everything' ), 'error' );
183 return $oldPrefixesList;
184 }
185
186 $sanitizedPrefixesList[ $entityKey ] = $prefix;
187 }
188
189 foreach( $sanitizedPrefixesList as $entityKey => $prefix ){
190 // Check hyphens problem when cat and cat-x exists
191 if( ! $validator->validatePrefixHyphens( $prefix, $sanitizedPrefixesList ) ){
192 add_settings_error( 'general', 'settings_updated', esc_html__( 'Prefix part before the "-" character can not be equal to another existing prefix.', 'filter-everything' ), 'error' );
193 return $oldPrefixesList;
194 }
195
196 if( flrt_get_term_by_slug( $prefix ) ){
197 add_settings_error( 'general', 'settings_updated', sprintf( esc_html__( 'Prefix «%s» is not allowed because such a term already exists. Please, use another prefix.', 'filter-everything' ), $prefix ), 'error' );
198 return $oldPrefixesList;
199 }
200 }
201
202 if( ! $validator->validateDuplicates( $sanitizedPrefixesList ) ){
203 add_settings_error( 'general', 'settings_updated', esc_html__( 'Duplicate prefixes. Prefixes should be unique.', 'filter-everything' ), 'error' );
204 return $oldPrefixesList;
205 }
206
207 if( ! is_array( $oldPrefixesList ) ){
208 return $sanitizedPrefixesList;
209 }
210
211 // Update all filters with new prefix
212 $prefixesChanged = array_diff( $sanitizedPrefixesList, $oldPrefixesList );
213
214 foreach( $prefixesChanged as $entityKey => $prefix ){
215 if( isset( $oldPrefixesList[$entityKey] ) ){
216 $prefixChangeFrom = $oldPrefixesList[$entityKey];
217 $prefixChangeTo = $sanitizedPrefixesList[$entityKey];
218 $this->updatePostsPrefixes( $prefixChangeFrom, $prefixChangeTo );
219 $this->updatePostsSeoRulesPrefixes( $oldPrefixesList[$entityKey], $prefixChangeTo, $entityKey);
220 }
221 }
222
223 return $sanitizedPrefixesList;
224 }
225 }
226
227 return $prefixesList;
228 }
229
230 /**
231 * @param string $slugChangeFrom
232 * @param string $slugChangeTo
233 * @param string $entityKey
234 */
235 private function updatePostsSeoRulesPrefixes( $slugChangeFrom, $slugChangeTo, $entityKey)
236 {
237 $result = true;
238
239 if( ! $slugChangeFrom || ! $slugChangeTo ){
240 return false;
241 }
242
243 if( $slugChangeFrom === $slugChangeTo ){
244 return false;
245 }
246
247 if(empty($entityKey)){
248 return false;
249 }
250
251
252 if (strpos($entityKey, '#') === false) {
253 return false;
254 }
255
256 $entity = explode('#', $entityKey)[1];
257
258 if(empty($entity)){
259 return false;
260 }
261
262 if ( ! defined('FLRT_SEO_RULES_POST_TYPE') ) {
263 return false;
264 }
265
266 $args = array(
267 'post_type' => FLRT_SEO_RULES_POST_TYPE,
268 'posts_per_page' => -1,
269 'post_status' => array('any')
270 );
271
272 $filterPosts = new \WP_Query( $args );
273 $postsToChange = $filterPosts->get_posts();
274
275
276 if( ! empty( $postsToChange ) ){
277 foreach ( $postsToChange as $post){
278 $post->post_excerpt = maybe_unserialize($post->post_excerpt);
279 if (is_array($post->post_excerpt) && isset($post->post_excerpt[$entity])) {
280 if (mb_strpos($post->post_title, $slugChangeFrom) !== false) {
281 $slugChangeFrom = preg_quote($slugChangeFrom, '/');
282 $new_post_title = preg_replace("#$slugChangeFrom#u", $slugChangeTo, $post->post_title);
283 if ($new_post_title !== $post->post_title) {
284 $toSave = array(
285 'ID' => $post->ID,
286 'post_title' => $new_post_title
287 );
288
289 $toSave = wp_slash( $toSave );
290
291 if( ! wp_update_post( $toSave ) && $result ){
292 $result = false;
293 }
294 }
295 }
296 }
297 }
298 }
299
300 return $result;
301 }
302
303 // Add previously configured prefix to filter fields before show them
304 public function prependGlobalPrefix( $filter )
305 {
306 $savedPrefixes = get_option( $this->optionName, [] );
307 $entityKey = $this->fs->getEntityKey( $filter['entity'], $filter['e_name'] );
308
309 if( isset( $savedPrefixes[$entityKey] ) && $savedPrefixes[ $entityKey ] ){
310 $filter['slug'] = $savedPrefixes[$entityKey];
311 }
312
313 return $filter;
314 }
315
316 public function maybeRemoveGlobalPrefix( $postId, $post )
317 {
318 if( $post->post_type !== FLRT_FILTERS_POST_TYPE ){
319 return $postId;
320 }
321 $thePrefix = $post->post_name;
322
323 $args = array(
324 'post_type' => FLRT_FILTERS_POST_TYPE,
325 'posts_per_page' => 1,
326 'post_name__in' => array( $thePrefix )
327 );
328
329 $exists = get_posts( $args );
330
331 if( empty( $exists ) ){
332 $savedPrefixes = get_option( $this->optionName, [] );
333
334 foreach ( $savedPrefixes as $entityName => $prefix ){
335 if( $thePrefix === $prefix ){
336 unset( $savedPrefixes[$entityName] );
337 }
338 }
339
340 $this->updatePrefixes( $savedPrefixes );
341 }
342
343 return $postId;
344 }
345
346 public function maybeAddGlobalPrefix( $filter )
347 {
348 // Non-path segments we can store in separate settings
349 // We need these slugs to have collection of them
350 // But these entities shouldn't be available to change segments order.
351
352 $savedPrefixes = get_option( $this->optionName, [] );
353 $entityKey = $this->fs->getEntityKey( $filter['entity'], $filter['e_name'] );
354
355 // First Global prefix;
356 if ( ! $savedPrefixes || empty( $savedPrefixes ) ) {
357 $savedPrefixes = [];
358 $savedPrefixes[ $entityKey ] = $filter['slug'];
359 $this->updatePrefixes( $savedPrefixes );
360 return $filter;
361 }
362
363 if( isset( $savedPrefixes[ $entityKey ] ) && $savedPrefixes[ $entityKey ] ){
364 // Force entity prefix to global before saving it.
365 $filter['slug'] = $savedPrefixes[ $entityKey ];
366 }else{
367 $savedPrefixes[ $entityKey ] = $filter['slug'];
368 $this->updatePrefixes( $savedPrefixes );
369 }
370
371 return $filter;
372 }
373
374 private function updatePrefixes( $prefixes )
375 {
376 /**
377 * @feature Prefix may be deleted if form has old prefixes list and submitted.
378 */
379 return update_option( $this->optionName, $prefixes );
380 }
381
382 public function explanationMessage( $page )
383 {
384 if( $page === $this->page){
385 if( defined('FLRT_FILTERS_PRO') ){
386 echo '<p>'.wp_kses( __( 'Edit, drag filter prefixes, and arrange them in the order you need.<br />This order determines the order of the filters in the URL.', 'filter-everything' ), array( 'br' => array() ) ).'</p>'."\r\n";
387 }else{
388 echo '<p>'.esc_html__( 'Edit var names.', 'filter-everything' ).'</p>'."\r\n";
389 }
390 }
391 }
392
393 function noFiltersMessage($page){
394 if( $page === $this->page ){
395 echo wp_kses(
396 sprintf( __('No filters have been created on this site yet. <a href="%s" target="_blank">Create your first one!</a>', 'filter-everything'), admin_url('post-new.php?post_type=' . FLRT_FILTERS_SET_POST_TYPE) ),
397 array( 'a' => array('href' => true, 'target' => true) )
398 );
399 }
400 }
401
402 public function getLabel()
403 {
404 return esc_html__('SEO', 'filter-everything');
405 }
406
407 public function sectionName()
408 {
409 return defined('FLRT_FILTERS_PRO') ? esc_html__('URL Prefixes', 'filter-everything') : esc_html__('URL Var Names', 'filter-everything');
410 }
411
412 public function getName()
413 {
414 return 'prefixes';
415 }
416
417 public function valid()
418 {
419 return true;
420 }
421
422 public function proSettingsLink(): array
423 {
424 if( ! defined('FLRT_FILTERS_PRO') ) {
425 $links = array(
426 array(
427 'link' => flrt_vailable_in_pro_attr_link(),
428 'text' => esc_html__('Indexed Filters', 'filter-everything') . flrt_pro_promo_label()
429 ),
430 array(
431 'link' => flrt_vailable_in_pro_attr_link(),
432 'text' => esc_html__('Indexing Depth', 'filter-everything') . flrt_pro_promo_label()
433 )
434 );
435 return $links;
436 }
437 return [];
438 }
439
440 public function getFreeLink() : array
441 {
442 if( ! defined('FLRT_FILTERS_PRO') ) {
443 return array(
444 'link' => flrt_vailable_in_pro_attr_link(),
445 'text' => esc_html__('Import/Export', 'filter-everything') . flrt_pro_promo_label()
446 );
447 }
448 return [];
449 }
450 }