Plugin Directory

Changeset 3400600


Ignore:
Timestamp:
11/21/2025 04:46:28 PM (7 weeks ago)
Author:
arothman
Message:

Refined canonical tag removal code.

Location:
pcrecruiter-extensions/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • pcrecruiter-extensions/trunk/PCRecruiter-Extensions.php

    r3226490 r3400600  
    44Plugin URI: https://www.pcrecruiter.net
    55Description: Embeds PCRecruiter forms and iframe content via shortcodes, and facilitiates RSS/XML Job Feeds.
    6 Version: 1.4.33
     6Version: 1.4.37
    77Author: Main Sequence Technology, Inc.
    88Author URI: https://www.pcrecruiter.net
     
    1818    wp_register_script( 'pcr-iframe', 'https://www2.pcrecruiter.net/pcrimg/inc/pcrframehost.js', false, false, false);
    1919    wp_enqueue_script( 'pcr-iframe' );
     20    wp_enqueue_style( 'pcr-styles', 'https://www2.pcrecruiter.net/pcrimg/inc/pcrframehost.css', array(), null );
    2021}
    2122add_action( 'wp_enqueue_scripts', 'pcr_assets' );
     
    4445    $initialheight = intval($a['initialheight']);
    4546    $background = preg_match('/^#[a-fA-F0-9]{3,6}$|^transparent$/', $a['background']) ? $a['background'] : 'transparent';
    46     $pcrframecss = '<link rel="stylesheet" href="https://www2.pcrecruiter.net/pcrimg/inc/pcrframehost.css">';
    47 
    4847
    4948    // Modify the URL when needed
     
    9594    $doc->appendChild($iframe);
    9695
    97     return "<!-- Start PCRecruiter WP 1.4.33-->"
    98         . $pcrframecss
     96    return "<!-- Start PCRecruiter WP 1.4.36-->"
    9997        . $doc->saveHTML()
    10098        . "<!-- End PCRecruiter WP -->";
     
    104102
    105103
    106 /* Remove Canonical Link */
    107 remove_action('wp_head', 'rel_canonical');
    108 add_filter( 'wpseo_canonical', '__return_false' );
     104/**
     105 * Remove canonical links for iframe job board pages only
     106 * Prevents search engines from consolidating ?recordid= variations into one page
     107 */
     108function pcrecruiter_remove_canonical_for_iframe() {
     109    global $post;
     110   
     111    if (!is_a($post, 'WP_Post')) {
     112        return;
     113    }
     114   
     115    // Only proceed if this page has the PCRecruiter shortcode
     116    if (!has_shortcode($post->post_content, 'PCRecruiter')) {
     117        return;
     118    }
     119   
     120    // Check if it's an iframe installation (not jobmanager)
     121    $pattern = get_shortcode_regex(['PCRecruiter']);
     122    if (preg_match_all('/' . $pattern . '/s', $post->post_content, $matches)) {
     123        foreach ($matches[3] as $attrs) {
     124            // If this is jobmanager (Full Sync), keep canonicals
     125            if (stripos($attrs, 'jobmanager') !== false || stripos($attrs, 'internaljobmanager') !== false) {
     126                return;
     127            }
     128        }
     129    }
     130   
     131    // This is an iframe page - remove canonical to allow search engines
     132    // to index individual ?recordid= URLs as separate pages
     133    remove_action('wp_head', 'rel_canonical');
     134    add_filter('wpseo_canonical', '__return_false');
     135}
     136add_action('wp', 'pcrecruiter_remove_canonical_for_iframe');
    109137
    110138/*
     
    139167add_action('pcr_feed', 'pcr_feed_activation');
    140168
    141 function pcr_feed_func(){
     169// Register hook for admin notices
     170add_action('admin_notices', 'pcr_admin_notices');
     171
     172/**
     173 * Display admin notices for PCRecruiter feed status
     174 */
     175function pcr_admin_notices() {
     176    // Only show on the PCRecruiter settings page
     177    $screen = get_current_screen();
     178    if ($screen->id !== 'settings_page_pcr-ext-setting-admin') {
     179        return;
     180    }
     181   
     182    // Check if we have a feed status to display
     183    $feed_status = get_option('pcr_feed_status');
     184    if (!$feed_status) {
     185        return;
     186    }
     187   
     188    // Format the time for display
     189    $status_time = !empty($feed_status['time']) ?
     190        ' (' . human_time_diff(strtotime($feed_status['time']), current_time('timestamp')) . ' ago)' : '';
     191       
     192    // Display success or error message
     193    if ($feed_status['success']) {
     194        echo '<div class="notice notice-success is-dismissible">';
     195        echo '<p><strong>Feed:</strong> ' . esc_html($feed_status['message']) . esc_html($status_time) . '</p>';
     196        echo '</div>';
     197    } else {
     198        echo '<div class="notice notice-error is-dismissible">';
     199        echo '<p><strong>Feed Error:</strong> ' . esc_html($feed_status['message']) . esc_html($status_time) . '</p>';
     200        echo '</div>';
     201    }
     202}
     203
     204
     205function pcr_feed_func() {
    142206    // do this via cron
    143207    $pcr_feed_options = get_option('pcr_feed_options', array());
    144     $pcr_customfields = str_replace(' ', '%20', $pcr_feed_options['custom_fields']  ?? '');
    145     $pcr_standardfields = str_replace(' ', '%20', $pcr_feed_options['standard_fields']  ?? '');
    146     $pcr_id_number = $pcr_feed_options['id_number'] ?? '';
    147     $pcr_activation = $pcr_feed_options['activation'] ?? '';
     208    $pcr_customfields = str_replace(' ', '%20', $pcr_feed_options['custom_fields'] ?? '');
     209    $pcr_standardfields = str_replace(' ', '%20', $pcr_feed_options['standard_fields'] ?? '');
     210    $pcr_id_number = $pcr_feed_options['id_number'] ?? '';
     211    $pcr_activation = $pcr_feed_options['activation'] ?? '';
    148212    $url = "https://host.pcrecruiter.net/pcrbin/feeds.aspx?action=customfeed&query=". $pcr_feed_options['query'] ."&xtransform=RSS2&SessionId=" . $pcr_id_number . "&FieldsPlus=" . $pcr_standardfields . "&custom=" . $pcr_customfields . "&mode=" . $pcr_feed_options['mode'];
    149213
    150 
    151     if($pcr_activation){
    152 
    153         // JSON
    154         //$xml = simplexml_load_string($url, null, LIBXML_NOCDATA, "http://www.pcrecruiter.net");
    155         $buffer = file_get_contents($url);
     214    if($pcr_activation) {
     215        // First, try to safely get the content from the URL
     216        $buffer = @file_get_contents($url);
     217       
     218        // Check if we actually got content
     219        if ($buffer === false) {
     220            // Log the error and store the status message
     221            $error_message = 'Failed to fetch XML feed. Please check your PCRecruiter SessionID and internet connection.';
     222            error_log('PCRecruiter Extensions: ' . $error_message);
     223            update_option('pcr_feed_status', array(
     224                'success' => false,
     225                'message' => $error_message,
     226                'time' => current_time('mysql')
     227            ));
     228            return false;
     229        }
     230       
     231        // Clean the buffer
    156232        $buffer = str_replace(array("\n", "\r", "\t"), '', $buffer);
    157         $xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA);
    158         $namespaces = $xml->getDocNamespaces(true);
    159 
    160        //var_dump($namespaces);
    161 
    162         $array = [];
    163         foreach ($namespaces as $namespace) {
    164             if (is_object($namespace) && property_exists($namespace, 'key')) {
    165                 $xml = simplexml_load_string($buffer, null, LIBXML_NOCDATA, $namespace->key);
    166                 $array = array_merge_recursive($array, (array) $xml);
    167             } else {
    168                 // Handle the case where $namespace is not a valid object with a 'key' property
    169                 // For example: log an error or skip this iteration
    170             }
    171         }
    172 
    173         $json = json_encode($array, JSON_PRETTY_PRINT);
    174 
    175         //$xml = file_get_contents($url);
    176         //$xml = str_replace('<pcr:', '<', $xml);
    177         //$xml = simplexml_load_string($xml, null, LIBXML_NOCDATA);
    178         //
    179         //$xml->channel->item->children('pcr', true);
    180         //$xml->registerXPathNamespace('pcr', 'http://www.pcrecruiter.net');
    181         // use XPath to get all pcr:* nodes
    182         /*
    183         $items = $xml->xpath('//pcr:*');
    184         foreach($items as $item) {
    185             $pcrItems = $pcrItems . $item;
    186         }
    187         */
    188         //
    189         //$xml->createElementNS('//pcr:*', '//*'); // no prefix
    190         //$json = json_encode($xml, JSON_PRETTY_PRINT);
    191         //$array = json_decode($json,TRUE);
    192         //$fileContents = str_replace(array("\n", "\r", "\t"), '', $fileContents);
    193         //$fileContents = trim(str_replace('"', "'", $fileContents));
    194         //$simpleXml = simplexml_load_string($fileContents);
    195         //$json_data = json_encode($fileContents);
    196         file_put_contents(WP_CONTENT_DIR . '/uploads/pcrjobfeed.json', $json);
    197 
    198         if (!copy($url, WP_CONTENT_DIR . "/uploads/pcrjobfeed.xml")) {
    199 
    200         } else {
    201 
    202         }
    203     }
     233       
     234        // Try to load the XML with error suppression
     235        libxml_use_internal_errors(true); // Suppress XML errors
     236        $xml = @simplexml_load_string($buffer, null, LIBXML_NOCDATA);
     237       
     238        // Check if XML loading was successful
     239        if ($xml === false) {
     240            $errors = libxml_get_errors();
     241            libxml_clear_errors();
     242           
     243            $error_message = 'XML load from '.$xml.' failed. Please verify your PCR SessionID and that the feed data is valid.';
     244            if (count($errors) > 0) {
     245                $error_message .= ' Error: ' . $errors[0]->message;
     246            }
     247           
     248            // Log the error and store the status message
     249            error_log('PCRecruiter Extensions: ' . $error_message);
     250            update_option('pcr_feed_status', array(
     251                'success' => false,
     252                'message' => $error_message,
     253                'time' => current_time('mysql')
     254            ));
     255            return false;
     256        }
     257       
     258        // Try to get namespaces safely
     259        try {
     260            $namespaces = $xml->getDocNamespaces(true);
     261           
     262            $array = [];
     263            if (!empty($namespaces)) {
     264                foreach ($namespaces as $prefix => $namespace) {
     265                    if (is_string($namespace)) {
     266                        $nsXml = @simplexml_load_string($buffer, null, LIBXML_NOCDATA, $prefix);
     267                        if ($nsXml !== false) {
     268                            $array = array_merge_recursive($array, (array) $nsXml);
     269                        }
     270                    }
     271                }
     272            } else {
     273                // If no namespaces, just use the base XML
     274                $array = (array) $xml;
     275            }
     276           
     277            // Encode to JSON
     278            $json = json_encode($array, JSON_PRETTY_PRINT);
     279            if ($json === false) {
     280                $error_message = 'JSON encoding failed: ' . json_last_error_msg();
     281                error_log('PCRecruiter Extensions: ' . $error_message);
     282                update_option('pcr_feed_status', array(
     283                    'success' => false,
     284                    'message' => $error_message,
     285                    'time' => current_time('mysql')
     286                ));
     287                return false;
     288            }
     289           
     290            // Save the files
     291            $json_result = @file_put_contents(WP_CONTENT_DIR . '/uploads/pcrjobfeed.json', $json);
     292            $xml_result = @copy($url, WP_CONTENT_DIR . "/uploads/pcrjobfeed.xml");
     293           
     294            if ($json_result === false || $xml_result === false) {
     295                $error_message = 'Failed to save feed file. Please check folder permissions.';
     296                error_log('PCRecruiter Extensions: ' . $error_message);
     297                update_option('pcr_feed_status', array(
     298                    'success' => false,
     299                    'message' => $error_message,
     300                    'time' => current_time('mysql')
     301                ));
     302                return false;
     303            }
     304           
     305            // Update status with success message
     306            update_option('pcr_feed_status', array(
     307                'success' => true,
     308                'message' => 'Feed successfully updated.',
     309                'time' => current_time('mysql')
     310            ));
     311           
     312            return true;
     313           
     314        } catch (Exception $e) {
     315            $error_message = 'Exception occurred: ' . $e->getMessage();
     316            error_log('PCRecruiter Extensions: ' . $error_message);
     317            update_option('pcr_feed_status', array(
     318                'success' => false,
     319                'message' => $error_message,
     320                'time' => current_time('mysql')
     321            ));
     322            return false;
     323        }
     324    }
     325   
     326    return true;
    204327}
    205328
     
    281404        if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
    282405            /* translators: 1: The name of the PHP constant that is set. */
    283             return new WP_Error( 'cron_info', sprintf( __( 'The %s constant is set to true. WP-Cron spawning is disabled.', 'wp-cron' ), 'DISABLE_WP_CRON' ) );
     406            return new WP_Error( 'cron_info', sprintf( __( 'The %s constant is set to true. WP-Cron spawning is disabled.', 'pcrecruiter-extensions' ), 'DISABLE_WP_CRON' ) );
    284407        }
    285408
    286409        if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
    287410            /* translators: 1: The name of the PHP constant that is set. */
    288             return new WP_Error( 'cron_info', sprintf( __( 'The %s constant is set to true.', 'wp-cron' ), 'ALTERNATE_WP_CRON' ) );
     411            return new WP_Error( 'cron_info', sprintf( __( 'The %s constant is set to true.', 'pcrecruiter-extensions' ), 'ALTERNATE_WP_CRON' ) );
    289412        }
    290413
     
    317440            return new WP_Error( 'unexpected_http_response_code', sprintf(
    318441                /* translators: 1: The HTTP response code. */
    319                 __( 'Unexpected HTTP response code: %s', 'wp-cron' ),
     442                __( 'Unexpected HTTP response code: %s', 'pcrecruiter-extensions' ),
    320443                intval( wp_remote_retrieve_response_code( $result ) )
    321444            ) );
     
    347470                        printf(
    348471                            /* translators: 1: Error message text. */
    349                             esc_html__( 'There was a problem with your cron configuration. The cron events on your site may not work. The problem was: %s', 'wp-cron' ),
     472                            esc_html__( 'There was a problem with your cron configuration. The cron events on your site may not work. The problem was: %s', 'pcrecruiter-extensions' ),
    350473                            '<br><strong>' . esc_html( $status->get_error_message() ) . '</strong>'
    351474                        );
     
    361484     * Register and add settings
    362485     */
     486
     487
     488    /**
     489     * Sanitize each setting field as needed
     490     *
     491     * @param array $input Contains all settings fields as array keys
     492     */
     493    public function sanitize($input)
     494{
     495    $new_input = array();
     496
     497    // Activation should be a boolean
     498    if (isset($input['activation'])) {
     499        $new_input['activation'] = (bool)($input['activation']);
     500    }
     501
     502    // Frequency should be one of the predefined options
     503    if (isset($input['frequency'])) {
     504        $allowed_frequencies = array('daily', 'hourly', 'twicedaily');
     505        $new_input['frequency'] = in_array($input['frequency'], $allowed_frequencies)
     506            ? $input['frequency']
     507            : 'daily'; // Default to daily if invalid
     508    }
     509
     510    // ID number should be sanitized as text field
     511    if (isset($input['id_number'])) {
     512        $new_input['id_number'] = sanitize_text_field($input['id_number']);
     513    }
     514
     515    // Standard fields should be sanitized as text
     516    if (isset($input['standard_fields'])) {
     517        $new_input['standard_fields'] = sanitize_text_field($input['standard_fields']);
     518    }
     519
     520    // Custom fields should be sanitized as text
     521    if (isset($input['custom_fields'])) {
     522        $new_input['custom_fields'] = sanitize_text_field($input['custom_fields']);
     523    }
     524
     525    if (isset($input['query'])) {
     526        // Expanded character set to handle all PCRecruiter query parameters safely
     527        $new_input['query'] = preg_replace('/[^\w\s%\(\)\=\-\'\"\.\_\+\!\*\,\;\:\[\]\{\}\<\>\?\/\&\|]+/', '', $input['query']);
     528    }
     529
     530    // Mode should be either 'job' or 'apply'
     531    if (isset($input['mode'])) {
     532        $new_input['mode'] = in_array($input['mode'], array('job', 'apply'))
     533            ? $input['mode']
     534            : 'job'; // Default to job if invalid
     535    }
     536   
     537    // If activation is enabled, check the feed immediately
     538    if (isset($new_input['activation']) && $new_input['activation']) {
     539        // Schedule a one-time event to check the feed after settings are saved
     540        wp_schedule_single_event(time() + 2, 'pcr_check_feed_once');
     541    }
     542
     543    return $new_input;
     544}
     545
    363546    public function page_init()
    364547    {
     
    434617    }
    435618
    436     /**
    437      * Sanitize each setting field as needed
    438      *
    439      * @param array $input Contains all settings fields as array keys
    440      */
    441     public function sanitize( $input )
    442     {
    443         $new_input = array();
    444 
    445 
    446         if( isset( $input['activation'] ) )
    447             $new_input['activation'] = sanitize_text_field( $input['activation'] );
    448 
    449         if( isset( $input['frequency'] ) )
    450             $new_input['frequency'] = sanitize_text_field( $input['frequency'] );
    451 
    452         if( isset( $input['id_number'] ) )
    453             $new_input['id_number'] = $input['id_number'];
    454 
    455         if( isset( $input['standard_fields'] ) )
    456             $new_input['standard_fields'] = sanitize_text_field( $input['standard_fields'] );
    457 
    458         if( isset( $input['custom_fields'] ) )
    459             $new_input['custom_fields'] = sanitize_text_field( $input['custom_fields'] );
    460 
    461         if( isset( $input['query'] ) )
    462             $new_input['query'] = $input['query'];
    463 
    464         if( isset( $input['mode'] ) )
    465             $new_input['mode'] = $input['mode'];
    466 
    467         return $new_input;
    468     }
    469619
    470620    /**
     
    473623    public function print_section_info()
    474624    {
    475         echo '<p>When enabled, this feature will duplicate PCRecruiter\'s dynamic RSS feed as a static file at <a target="_blank" href="'. esc_url( site_url() ) .'/wp-content/uploads/pcrjobfeed.xml">'. esc_url( site_url() ) .'/wp-content/uploads/pcrjobfeed.xml</a>. You may use this data as a source for plugins and other third-party feed utilities.</p>';
     625        echo '<p style="font-size:1.25em;"><strong style="text-transform:uppercase">NOTE: These settings are NOT involved in typical PCR Job Board implementations.</strong></p><p style="font-size:1em;">When this optional function is enabled, the plugin will duplicate PCR\'s dynamic RSS feed as a static file at <a target="_blank" href="'. esc_url( site_url() ) .'/wp-content/uploads/pcrjobfeed.xml">'. esc_url( site_url() ) .'/wp-content/uploads/pcrjobfeed.xml</a>. You may use this data as a source for plugins and other third-party feed utilities.</p>';
    476626         // Check to see if "Store Local Feed" is active. If it is, show the manual save button
    477627                if($this->options['activation'] ?? false){
     
    479629                    $fname = WP_CONTENT_DIR . "/uploads/".$filename;
    480630                    if (file_exists($fname)) {
    481                         $d = date ("F d Y H:i:s", filectime($fname));
     631                        $d = gmdate("F d Y H:i:s", filectime($fname));
    482632                        echo "<em style=\"font-weight:bold\">" . esc_html( $filename ) . " last updated: " . esc_html( $d ) . " (UTC).</em>";
    483633                    } else {
     
    515665    {
    516666        printf(
    517             '<input type="text" id="id_number" name="pcr_feed_options[id_number]" value="%s" size="60" />',
     667            '<input type="text" id="id_number" name="pcr_feed_options[id_number]" value="%s" size="60" placeholder="Please contact PCR support for your unique session-id value." />',
    518668            isset( $this->options['id_number'] ) ? esc_attr( $this->options['id_number']) : ''
    519669        );
     
    573723
    574724/* Begin update cron schedule if frequency changes */
    575 function update_cron() {
    576      register_setting('pcr_ext_option_group', 'pcr_feed_options');
    577 }
    578 add_action('admin_init', 'update_cron');
    579725
    580726function do_after_update($old, $new) {
  • pcrecruiter-extensions/trunk/readme.txt

    r3360765 r3400600  
    33Tags: Recruiting, Staffing, Applicant Tracking
    44Requires at least: 3.0
    5 Tested up to: 6.7.1
    6 Stable tag: trunk
     5Tested up to: 6.8
     6Stable tag: 1.4.37
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     9Text Domain: pcrecruiter-extensions
    910
    1011PCR Job Board iframe embed, PCR Custom Form scripts, and PCR RSS download handler. Note: This plugin does not interact with the WordPress database.
Note: See TracChangeset for help on using the changeset viewer.