Gists

5 hours ago
<?php
// See: https://facetwp.com/help-center/developers/hooks/output-hooks/facetwp_facet_html/#translate-facet-choices
add_filter( 'facetwp_facet_html', function( $output, $params ) {
	if ( 'my_facet_name' == $params['facet']['name'] ) { // Replace "my_facet_name" with the name of your facet
        
        // For WPML:
	    $current = ( !empty( FWP()->facet->http_params['lang'] ) ) ? FWP()->facet->http_params['lang'] :  apply_filters( 'wpml_current_language', null );  
	    $default = apply_filters('wpml_default_language', NULL );
	    
    	// For Polylang, use these 2 lines instead of 2 above
	    //$current = ( !empty( FWP()->facet->http_params['lang'] ) ) ? FWP()->facet->http_params['lang'] : pll_current_language();
	    //$default = pll_default_language();
        
	    $replace_values = [];
	    $replace_values['nl'] = [
			'Belgium' => 'België',
			'Bulgaria' => 'Bulgarije'
      	];
	    if ( $current != $default && !empty( $replace_values[$current] ) ) {
            $default_values = [];
            $new_values = [];
            foreach ( $replace_values[$current] AS $replace_value => $replacement ) {	
                $default_values[] = $replace_value;
                $new_values[] = $replacement;
            }
            $output = str_replace( $default_values, $new_values, $output );
	    }	    
	}
	
	return $output;
}, 10, 2 );
2 months ago
<?php
/**
 ** disables new template enahncement buffer in WP 6.9 during facet's ajax refresh
 ** to disable WP's buffer completely, you can use 
 ** add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_false' );
 **/
add_filter( 'wp_should_output_buffer_template_for_enhancement', function( $should ) {
  if ( function_exists( 'FWP' ) && FWP()->request && FWP()->request->is_refresh ) {
    return false;
  }
  return $should;
} );
3 months ago
<?php

// Query arguments ready to use in a Listing Builder lising
// Orders 'courses' posts by a future date in the 'offer_date_1' ACF Date Picker field, with the earliest date first (ASC),
// then showing posts without the field or without a date in the field, at the bottom.
// Includes a 'offer_date_query' flag to be used in the 'posts_clauses' function below,
// which is needed to show posts with a date before the posts without one.
// If you're using an ACF Date Time Picker field instead of a Date Picker field, see the second example here:
// https://facetwp.com/help-center/using-facetwp-with/advanced-custom-fields/#order-a-listing-by-a-date-time-picker-field

return [
    'post_type'      => [
        'courses'
    ],
    'post_status'    => [
        'publish'
    ],
    'posts_per_page' => 50,
	
	'offer_date_query' => true, // Needed for identifying in 'posts_clauses' hook

    // Order by date and fallback order	
    'meta_key'       => 'offer_date_1',
    'orderby'        => array(
        'meta_value_num' => 'ASC', // ASC = Smallest number (earliest date) first
        'date'           => 'DESC', // Fallback order if offer_date_1 the same or not exists: post_date (newest first). Adapt as needed.
    ),

    // Only retrieve posts with a date in the future or no date at all
    'meta_query'     => array(
		
        'relation' => 'OR', // Allow multiple conditions
		
        // Get posts with a future date
        array(
            'key'     => 'offer_date_1',
            'value'   => current_time('Ymd'),
            'compare' => '>',
            'type'    => 'NUMERIC',
        ),
				
        // Get posts with no date field
        array(
            'key'     => 'offer_date_1',
            'compare' => 'NOT EXISTS', // Posts without this field
        ),
		
		// Get posts with no date value
        array(
            'key'     => 'offer_date_1',
			'value'   => '',
            'compare' => '=',
        ),
		
    ),

];


// Get the 'offer_date_query' query above to order
// posts with an date first, the rest after 
// Add this to your functions.php
add_filter( 'posts_clauses', function( $clauses, $query ) {
    if ( ! $query->get( 'offer_date_query' ) ) { // the flag in the query arguments above
        return $clauses;
    }

    global $wpdb;

    // Join postmeta explicitly to have a stable alias for ordering
    // If you already have a JOIN for offer_date_1 via meta_query,
    // this will effectively be a no-op because of identical JOIN.
    $clauses['join'] .= $wpdb->prepare(
        " LEFT JOIN {$wpdb->postmeta} AS od1
          ON od1.post_id = {$wpdb->posts}.ID
         AND od1.meta_key = %s",
        'offer_date_1'
    );

    // 1) CASE: posts *with* non-empty offer_date_1 get sort key 0
    //         posts *without* (NULL or '') get sort key 1
    // 2) Then order by the numeric value of offer_date_1 ascending
    // 3) Finally, fallback to post_date DESC

    $clauses['orderby'] = "
        CASE
            WHEN od1.meta_value IS NOT NULL AND od1.meta_value != '' THEN 0
            ELSE 1
        END ASC,
        CAST(od1.meta_value AS UNSIGNED) ASC,
        {$wpdb->posts}.post_date DESC
    ";

    return $clauses;
}, 10, 2 );
3 months ago
<?php
/**
 ** change version of gmaps to load
 ** https://developers.google.com/maps/documentation/javascript/versions
 ** See: https://facetwp.com/help-center/facets/facet-types/map/advanced-map-customizations/#set-the-maps-javascript-api-version-to-load
 **/
add_filter( 'facetwp_gmaps_params', function( $params ) {
    $params['v'] = 'weekly';
    return $params;
});
2 months ago
<?php
/**
 ** NOTE: This issue was FIXED in Create v1.10.2. The snippet below is only needed in older versions.
 ** See: https://wordpress.org/support/topic/vue-errors-in-other-plugins-facetwp/
 
 ** The Create plugin by Mediavine plugin v1.10.1 and earlier was enqueueing its JS on all admin pages.
 ** This snippet blocks its JS from FacetWP's settings page
 ** Create was setting a Vue JS object that conflicted with FacetWP's and causes these Console errors:
 ** 'Vue.config is undefined' in Firefox and
 ** 'Cannot set properties of undefined (setting 'devtools')' in Chrome
 **/

add_action( 'admin_enqueue_scripts', function( $hook ) {
	// block mediavine create script on FacetWP's settings page
    if ( 'settings_page_facetwp' == $hook ) {
        wp_dequeue_script( 'mv_create-script' );
    }
}, 12 );
3 months ago
<?php
// Re-initialize Ays Pro Chartify charts used in a listing template, after a FacetWP refresh
// https://wordpress.org/plugins/chart-builder/
// https://ays-pro.com/wordpress/chart-builder
// Note: this only works in WP archive or custom WP_Query based templates, NOT in FacetWP Listing Builder listings. There is no solution for those.
// This solution works for both JS and Google charts

add_action('facetwp_scripts', function() {
  ?>
  <script>
    (function($) {
      if (typeof FWP !== 'undefined') {

        // Note: this only works in WP archive or custom WP_Query templates, NOT in FacetWP Listing Builder listings,
        // because in those, the respons is not the whole body (only the listing content) and thus it has no access to
        // the updated #chart-builder-chart-js-js-extra script, unfortunately
        FWP.hooks.addFilter('facetwp/template_html', function(resp, params) {

          // Create a temporary div to parse the new HTML response
          let tempContainer = document.createElement('div');
          tempContainer.innerHTML = params.response.template;

          // Find the NEW script element from the AJAX response
          // target #chart-builder-chart-js-js-extra or #chart-builder-charts-google-js-extra but NOT #chart-builder-chart-js-js and #chart-builder-charts-google-js
          const newChartDataElements = tempContainer.querySelectorAll('#chart-builder-chart-js-js-extra, #chart-builder-charts-google-js-extra');

          // also include #chart-builder-inline-css which contains chart specific CSS
          const newChartCssElements = tempContainer.querySelectorAll('#chart-builder-inline-css');

          if (newChartDataElements) {

            newChartDataElements.forEach(function(newScript) {

              const oldChartDataElements = document.getElementById(newScript.id);

              // 1. Remove the old script element from the DOM
              if (oldChartDataElements) {
                oldChartDataElements.remove();
              }

              // 2. Create a brand new script element.
              // Crucial because browsers execute scripts when they are appended
              // as NEWLY created elements, not inserted via innerHTML or when using replaceWith(),
              // which will cause TypeError: _this.dbData is undefined
              const newScriptElement = document.createElement('script');
              newScriptElement.id = newScript.id

              // 4. Copy the JavaScript content (the variable definitions)
              newScriptElement.textContent = newScript.textContent;

              // 5. Append the newly created script element to the document body.
              // This will trigger parsing and executing the script contents,
              // thus defining the new 'aysChartOptions' variables globally.
              document.body.appendChild(newScriptElement);

            });

          }

          if (newChartCssElements) {

            newChartCssElements.forEach(function(newCss) {

              const oldChartCssElements = document.getElementById(newCss.id);

              // 1. Remove the old style element from the DOM
              if (oldChartCssElements) {
                oldChartCssElements.remove();
              }

              // 2. Create a new style element.
              const newInlineCSS = document.createElement('style');
              newInlineCSS.id = newCss.id

              // 4. Copy the CSS content
              newInlineCSS.textContent = newCss.textContent;

              // 5. Append the newly created style element to the document body.
              document.body.appendChild(newInlineCSS);

            });

          }

          return resp;
        }, 1 );

        // Retrigger the charts after a facetwp ajax refresh
        FWP.hooks.addAction('facetwp/loaded', function() {

          if ( FWP.loaded ) { // run only on refresh after first page load

            // Reinitialize JS charts
            if (typeof $.fn.ChartBuilderChartJsMain === 'function') {
              $('.ays-chart-container-chartjs').each(function() {
                try {
                  $(this).ChartBuilderChartJsMain();
                } catch (e) {
                  console.warn('Chart initialization error:', e);
                }
              });
            }

            // Reinitialize Google charts
            if (typeof $.fn.ChartBuilderGoogleChartsMain === 'function') {
              $('.ays-chart-container-google').each(function() {
                try {
                  $(this).ChartBuilderGoogleChartsMain();
                } catch (e) {
                  console.warn('Chart initialization error:', e);
                }
              });
            }

          }

        }, 100);

      }
    })(jQuery);
  </script>
  <?php
}, 100);
3 months ago
<?php
// Allow FacetWP to detect Salient's Visual Composer Post Loop Builder
// Needs the 'facetwp-template' class set on the Post Loop Builder element: https://d.pr/i/x4j75G
// Disable the element's own (Load more) pagination: https://d.pr/i/4ZumQ0
// Use a Pager facet instead (numbered or load more type).

add_filter( 'nectar_post_grid_query', function( $query_args ) {
  
  $post_types = ['resource', 'post']; // Run only for Post Loop Builder grids with these post types

  if ( in_array( $query_args['post_type'], $post_types ) ) {

    // Detect Post Loop Grid query
    $query_args['facetwp'] = true;

    // Fix pagination
    if ( array_key_exists('offset', $query_args ) ) {
      unset( $query_args['offset'] );
    }

  }
  return $query_args;
}, 10 );
4 months ago
<?php
// Scenario: an apply/submit/refresh button is clicked (in this case the Number Range facet's Go button)
// On click, the Proximity facet should use the first item in the dropdown if any text is entered in the input field,
// but not if the user actually made a selection (by clicking an item in the dropdown or using Enter/Return).
// This involves killing the original click event on the button first. Then detecting any selection in the Proximity.
// If there is no selection, trigger a click on the first dropdown item so it is selected, and perform the original refresh.

add_action( 'wp_head', function() {
  ?>
  <script>

    // This needs to run as early as possible, in the head
    document.addEventListener('DOMContentLoaded', function() {

      // First we need to kill the existing click event on the Number Range button
      const selectorToIntercept = '.facetwp-type-number_range .facetwp-submit';

      document.addEventListener('click', function(event) {

        const clickedButton = event.target.closest(selectorToIntercept);

        if (clickedButton) {

          event.preventDefault();
          event.stopImmediatePropagation();

          const clickEvent = new MouseEvent('click', {
            bubbles: true,
            cancelable: true,
            view: window
          });

          // Then, if there is text in the Proximity facet, trigger the first item,
          // if the facet is not already having a selection

          const proximityFacet = document.querySelector('.facetwp-type-proximity');
          const firstLocation= document.querySelector('.location-result:first-child');

          if( firstLocation && proximityFacet.classList.contains('not-active')) {
            firstLocation.dispatchEvent(clickEvent);
          }

          // Then refresh again
          if ('undefined' !== typeof FWP) {
            FWP.refresh();
          }

        }
      }, true);
    });

  </script>
  <?php
}, 100 );
4 months ago
<?php
// To exclude custom fields from FacetWP's get_data_sources() function, used in the admin area, you can use
// the 'facetwp_excluded_custom_fields' filter.
// To do it by partial match, e.g. for fields with a hashed part of the name, 
// you can use the 'facetwp_excluded_custom_fields_like' filter,
// available in FacetWP 4.5+ :

add_filter( 'facetwp_excluded_custom_fields_like', function( $not_like ) {

  $not_like[] = '_crp_cache_'; // exclude cache rows added by Contextual Related Posts plugin

  return $not_like;
});
4 months ago
<?php

// Add this to the Listing Builder listing's Query tab in Dev mode
// Situation: 'enti' post type posts that have an ACF Relationshipship field that relates them with 'progetti' posts
// The 'progetti' posts have a custom field 'disattiva_dol' which is an ACF true/false field.
// The generated query arguments retrieve 'enti' post type posts
// that have a related 'progetti' post for which the 'disattiva_dol' custom field is false

// Step 1: Get the IDs of "project" posts where disattiva_dol is false.
$valid_project_ids_args = array(
  'post_type'      => 'progetti',
  'posts_per_page' => -1, // Retrieve all matching projects
  'fields'         => 'ids', // Return only post IDs for efficiency
  'meta_query'     => array(
    array(
      'key'     => 'disattiva_dol', // The ACF true/false field on 'project' posts
      'value'   => 0,               // ACF stores 'false' as 0 (for true/false fields)
      'compare' => '=',
      'type'    => 'NUMERIC',       // Ensure numeric comparison
    ),
  ),
);

$valid_project_ids = get_posts( $valid_project_ids_args );

// Step 2: Create the WP_Query arguments for "enti" posts based on the valid_project_ids.
$enti_query_args = array(
  'post_type'      => 'enti',
  'posts_per_page' => -1, // Or your desired number of posts
  'post_status'    => 'publish', // Only published 'enti' posts
);

if ( ! empty( $valid_project_ids ) ) {
  $project_relationship_meta_query = array(
    'relation' => 'OR', // An 'enti' post can be related to ANY of the valid projects
  );

  foreach ( $valid_project_ids as $project_id ) {
    // Relationship field stores a single ID (numeric comparison)
    $project_relationship_meta_query[] = array(
      'key'     => 'progetti_oggetto', // Replace with the actual name of your ACF Relationship field on 'enti' posts
      'value'   => $project_id,
      'compare' => '=',
      'type'    => 'NUMERIC',
    );

  }

  $enti_query_args['meta_query'] = $project_relationship_meta_query;

} 

// For checking the resulting args, remove when it works
// echo '<pre>'; var_dump($enti_query_args); echo '</pre>';

return $enti_query_args;