Changeset 3400600
- Timestamp:
- 11/21/2025 04:46:28 PM (7 weeks ago)
- Location:
- pcrecruiter-extensions/trunk
- Files:
-
- 2 edited
-
PCRecruiter-Extensions.php (modified) (15 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
pcrecruiter-extensions/trunk/PCRecruiter-Extensions.php
r3226490 r3400600 4 4 Plugin URI: https://www.pcrecruiter.net 5 5 Description: Embeds PCRecruiter forms and iframe content via shortcodes, and facilitiates RSS/XML Job Feeds. 6 Version: 1.4.3 36 Version: 1.4.37 7 7 Author: Main Sequence Technology, Inc. 8 8 Author URI: https://www.pcrecruiter.net … … 18 18 wp_register_script( 'pcr-iframe', 'https://www2.pcrecruiter.net/pcrimg/inc/pcrframehost.js', false, false, false); 19 19 wp_enqueue_script( 'pcr-iframe' ); 20 wp_enqueue_style( 'pcr-styles', 'https://www2.pcrecruiter.net/pcrimg/inc/pcrframehost.css', array(), null ); 20 21 } 21 22 add_action( 'wp_enqueue_scripts', 'pcr_assets' ); … … 44 45 $initialheight = intval($a['initialheight']); 45 46 $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 48 47 49 48 // Modify the URL when needed … … 95 94 $doc->appendChild($iframe); 96 95 97 return "<!-- Start PCRecruiter WP 1.4.33-->" 98 . $pcrframecss 96 return "<!-- Start PCRecruiter WP 1.4.36-->" 99 97 . $doc->saveHTML() 100 98 . "<!-- End PCRecruiter WP -->"; … … 104 102 105 103 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 */ 108 function 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 } 136 add_action('wp', 'pcrecruiter_remove_canonical_for_iframe'); 109 137 110 138 /* … … 139 167 add_action('pcr_feed', 'pcr_feed_activation'); 140 168 141 function pcr_feed_func(){ 169 // Register hook for admin notices 170 add_action('admin_notices', 'pcr_admin_notices'); 171 172 /** 173 * Display admin notices for PCRecruiter feed status 174 */ 175 function 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 205 function pcr_feed_func() { 142 206 // do this via cron 143 207 $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'] ?? ''; 148 212 $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']; 149 213 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 156 232 $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; 204 327 } 205 328 … … 281 404 if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) { 282 405 /* 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' ) ); 284 407 } 285 408 286 409 if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) { 287 410 /* 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' ) ); 289 412 } 290 413 … … 317 440 return new WP_Error( 'unexpected_http_response_code', sprintf( 318 441 /* translators: 1: The HTTP response code. */ 319 __( 'Unexpected HTTP response code: %s', ' wp-cron' ),442 __( 'Unexpected HTTP response code: %s', 'pcrecruiter-extensions' ), 320 443 intval( wp_remote_retrieve_response_code( $result ) ) 321 444 ) ); … … 347 470 printf( 348 471 /* 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' ), 350 473 '<br><strong>' . esc_html( $status->get_error_message() ) . '</strong>' 351 474 ); … … 361 484 * Register and add settings 362 485 */ 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 363 546 public function page_init() 364 547 { … … 434 617 } 435 618 436 /**437 * Sanitize each setting field as needed438 *439 * @param array $input Contains all settings fields as array keys440 */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 }469 619 470 620 /** … … 473 623 public function print_section_info() 474 624 { 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>'; 476 626 // Check to see if "Store Local Feed" is active. If it is, show the manual save button 477 627 if($this->options['activation'] ?? false){ … … 479 629 $fname = WP_CONTENT_DIR . "/uploads/".$filename; 480 630 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)); 482 632 echo "<em style=\"font-weight:bold\">" . esc_html( $filename ) . " last updated: " . esc_html( $d ) . " (UTC).</em>"; 483 633 } else { … … 515 665 { 516 666 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." />', 518 668 isset( $this->options['id_number'] ) ? esc_attr( $this->options['id_number']) : '' 519 669 ); … … 573 723 574 724 /* 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');579 725 580 726 function do_after_update($old, $new) { -
pcrecruiter-extensions/trunk/readme.txt
r3360765 r3400600 3 3 Tags: Recruiting, Staffing, Applicant Tracking 4 4 Requires at least: 3.0 5 Tested up to: 6. 7.16 Stable tag: trunk5 Tested up to: 6.8 6 Stable tag: 1.4.37 7 7 License: GPLv2 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-2.0.html 9 Text Domain: pcrecruiter-extensions 9 10 10 11 PCR 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.