GFPDFHelperHelper_Misc

The Gravity PDF GFPDF Helper Misc class.

Defined (1)

The class is defined in the following location(s).

/src/helper/Helper_Misc.php  
  1. class Helper_Misc { 
  2.  
  3. /** 
  4. * Holds the abstracted Gravity Forms API specific to Gravity PDF 
  5. * @var \GFPDF\Helper\Helper_Form 
  6. * @since 4.0 
  7. */ 
  8. protected $gform; 
  9.  
  10. /** 
  11. * Holds our log class 
  12. * @var \Monolog\Logger|LoggerInterface 
  13. * @since 4.0 
  14. */ 
  15. protected $log; 
  16.  
  17. /** 
  18. * Holds our Helper_Data object 
  19. * which we can autoload with any data needed 
  20. * @var \GFPDF\Helper\Helper_Data 
  21. * @since 4.0 
  22. */ 
  23. protected $data; 
  24.  
  25. /** 
  26. * Store required classes locally 
  27. * @param \Monolog\Logger|LoggerInterface $log 
  28. * @param \GFPDF\Helper\Helper_Abstract_Form $gform 
  29. * @param \GFPDF\Helper\Helper_Data $data 
  30. * @since 4.0 
  31. */ 
  32. public function __construct( LoggerInterface $log, Helper_Abstract_Form $gform, Helper_Data $data ) { 
  33.  
  34. /** Assign our internal variables */ 
  35. $this->log = $log; 
  36. $this->gform = $gform; 
  37. $this->data = $data; 
  38.  
  39. /** 
  40. * Check if the current admin page is a Gravity PDF page 
  41. * @since 4.0 
  42. * @return boolean 
  43. */ 
  44. public function is_gfpdf_page() { 
  45. if ( is_admin() ) { 
  46. if ( isset( $_GET['page'] ) && 'gfpdf-' === ( substr( $_GET['page'], 0, 6 ) ) || 
  47. ( isset( $_GET['subview'] ) && 'PDF' === strtoupper( $_GET['subview'] ) ) 
  48. ) { 
  49. return true; 
  50.  
  51. return false; 
  52.  
  53. /** 
  54. * Check if we are on the current global settings page / tab 
  55. * @since 4.0 
  56. * @param string $name The current page ID to check 
  57. * @return boolean 
  58. */ 
  59. public function is_gfpdf_settings_tab( $name ) { 
  60. if ( is_admin() ) { 
  61. if ( $this->is_gfpdf_page() ) { 
  62. $tab = ( isset( $_GET['tab'] ) ) ? $_GET['tab'] : 'general'; 
  63.  
  64. if ( $name === $tab ) { 
  65. return true; 
  66.  
  67. return false; 
  68.  
  69. /** 
  70. * Gravity Forms has a 'type' for each field. 
  71. * Based on that type, attempt to match it to Gravity PDFs field classes 
  72. * @param string $type The field type we are looking up 
  73. * @return string|boolean The Fully Qualified Namespaced Class we matched, or false 
  74. * @since 4.0 
  75. */ 
  76. public function get_field_class( $type ) { 
  77.  
  78. /** change our product field types to use a single master product class */ 
  79. $convert_product_type = [ 'quantity', 'option', 'shipping', 'total' ]; 
  80.  
  81. if ( in_array( strtolower( $type ), $convert_product_type ) ) { 
  82. $type = 'product'; 
  83.  
  84. /** Format the type name correctly */ 
  85. $typeArray = explode( '_', $type ); 
  86. $typeArray = array_map( 'ucwords', $typeArray ); 
  87. $type = implode( '_', $typeArray ); 
  88.  
  89. /** See if we have a class that matches */ 
  90. $fqns = 'GFPDF\Helper\Fields\Field_'; 
  91.  
  92. if ( class_exists( $fqns . $type ) ) { 
  93. return $fqns . $type; 
  94.  
  95. return false; 
  96.  
  97. /** 
  98. * Manipulate header and footer for more consistent display in PDF 
  99. * Changes made include: 
  100. * 1. Apply wpautop to content 
  101. * 2. Apply wp_kses_post to content 
  102. * 3. mPDF currently has no cascading CSS ability to target 'inline' elements. Fix image display issues in header / footer 
  103. * by adding a specific class name we can target 
  104. * 4. Convert any image URLs to local path where applicable 
  105. * @param string $html The HTML to parse 
  106. * @return string 
  107. */ 
  108. public function fix_header_footer( $html ) { 
  109.  
  110. $html = wp_kses_post( $html ); 
  111. $html = trim( wpautop( $html ) ); 
  112. $html = $this->fix_header_footer_images( $html ); 
  113.  
  114. return $html; 
  115.  
  116. /** 
  117. * Convert image URLs to local path (where able) and add specific class names to images for better 
  118. * targetting in Mpdf 
  119. * @param string $html 
  120. * @return string 
  121. * @since 7.0.4 
  122. */ 
  123. public function fix_header_footer_images( $html ) { 
  124. try { 
  125. /** Get the <img> from the DOM and extract required details */ 
  126. $qp = new Helper_QueryPath(); 
  127. $wrapper = $qp->html5( $html ); 
  128.  
  129. $images = $wrapper->find( 'img' ); 
  130.  
  131. if ( sizeof( $images ) > 0 ) { 
  132. /** Loop through each matching element */ 
  133. foreach ( $images as $image ) { 
  134.  
  135. /** Get current image src */ 
  136. $image_src = trim( $image->attr( 'src' ) ); 
  137. $image_src_path = $this->convert_url_to_path( $image_src ); 
  138.  
  139. if ( false !== $image_src_path ) { 
  140. $image->attr( 'src', $image_src_path ); 
  141.  
  142. /** Get the current image classes */ 
  143. $image_classes = $image->attr( 'class' ); 
  144.  
  145. /** Remove width/height and add a override class */ 
  146. $image->removeAttr( 'width' )->removeAttr( 'height' )->addClass( 'header-footer-img' ); 
  147.  
  148. if ( strlen( $image_classes ) > 0 ) { 
  149. /** Wrap in a new div that includes the image classes */ 
  150. $image->wrap( '<div class="' . $image_classes . '"></div>' ); 
  151.  
  152. return $wrapper->top( 'html' )->innerHTML5(); 
  153.  
  154. return $html; 
  155.  
  156. } catch ( Exception $e ) { 
  157. /** if there was any issues we'll just return the $html */ 
  158. return $html; 
  159.  
  160. /** 
  161. * Processes a hex colour and returns an appopriately contrasting black or white 
  162. * @param string $hexcolor The Hex to be inverted 
  163. * @return string 
  164. * @since 4.0 
  165. */ 
  166. public function get_contrast( $hexcolor ) { 
  167. $hexcolor = str_replace( '#', '', $hexcolor ); 
  168.  
  169. if ( 6 !== strlen( $hexcolor ) ) { 
  170. $hexcolor = str_repeat( substr( $hexcolor, 0, 1 ), 2 ) . str_repeat( substr( $hexcolor, 1, 1 ), 2 ) . str_repeat( substr( $hexcolor, 2, 1 ), 2 ); 
  171.  
  172. $r = hexdec( substr( $hexcolor, 0, 2 ) ); 
  173. $g = hexdec( substr( $hexcolor, 2, 2 ) ); 
  174. $b = hexdec( substr( $hexcolor, 4, 2 ) ); 
  175. $yiq = ( ( $r * 299 ) + ( $g * 587 ) + ( $b * 114 ) ) / 1000; 
  176.  
  177. return ( $yiq >= 150 ) ? '#000' : '#FFF'; 
  178.  
  179. /** 
  180. * Change the brightness of the passed in colour 
  181. * $diff should be negative to go darker, positive to go lighter and 
  182. * is subtracted from the decimal (0-255) value of the colour 
  183. * @param $hexcolor Hex colour to be modified 
  184. * @param integer $diff amount to change the color 
  185. * @return string hex colour 
  186. * @since 4.0 
  187. */ 
  188. public function change_brightness( $hexcolor, $diff ) { 
  189.  
  190. $hexcolor = trim( str_replace( '#', '', $hexcolor ) ); 
  191.  
  192. if ( 6 !== strlen( $hexcolor ) ) { 
  193. $hexcolor = str_repeat( substr( $hexcolor, 0, 1 ), 2 ) . str_repeat( substr( $hexcolor, 1, 1 ), 2 ) . str_repeat( substr( $hexcolor, 2, 1 ), 2 ); 
  194.  
  195. $rgb = str_split( $hexcolor, 2 ); 
  196.  
  197. foreach ( $rgb as &$hex ) { 
  198. $dec = hexdec( $hex ); 
  199. $dec += $diff; 
  200. $dec = max( 0, min( 255, $dec ) ); 
  201. $hex = str_pad( dechex( $dec ), 2, '0', STR_PAD_LEFT ); 
  202.  
  203. return '#' . implode( $rgb ); 
  204.  
  205. /** 
  206. * Pass in a background colour and get the appropriate contrasting background and border colour 
  207. * @param string $background_hex Hex colour to get contrast of 
  208. * @return array 
  209. * @since 4.0 
  210. */ 
  211. public function get_background_and_border_contrast( $background_hex ) { 
  212.  
  213. /** Get contrasting background colour */ 
  214. $background_color_contrast = $this->get_contrast( $background_hex ); 
  215.  
  216. /** If the background isn't white we'll go down 20, otherwise go up 20 */ 
  217. $contrast_value = ( $background_color_contrast === '#FFF' ) ? 20 : -20; 
  218.  
  219. /** Get the new contrasting background colour */ 
  220. $contrast_background_color = $this->change_brightness( $background_hex, $contrast_value ); 
  221.  
  222. /** Get the new border contrast based on the background contrast colour */ 
  223. $border_contrast = ( $background_color_contrast === '#FFF' ) ? 60 : -60; 
  224.  
  225. /** Finally get a contrasting border colour */ 
  226. $contrast_border_color = $this->change_brightness( $background_hex, $border_contrast ); 
  227.  
  228. return [ 
  229. 'background' => $contrast_background_color,  
  230. 'border' => $contrast_border_color,  
  231. ]; 
  232.  
  233. /** 
  234. * Push an associative array onto the beginning of an existing array 
  235. * @param array $array The array to push onto 
  236. * @param string $key The key to use for the newly-pushed array 
  237. * @param mixed $val The value being pushed 
  238. * @return array The modified array 
  239. * @since 4.0 
  240. */ 
  241. public function array_unshift_assoc( $array, $key, $val ) { 
  242. $array = array_reverse( $array, true ); 
  243. $array[ $key ] = $val; 
  244.  
  245. return array_reverse( $array, true ); 
  246.  
  247. /** 
  248. * This function recursively deletes all files and folders under the given directory, and then the directory itself 
  249. * equivalent to Bash: rm -r $dir 
  250. * @param string $dir The path to be deleted 
  251. * @return bool|WP_Error 
  252. * @since 4.0 
  253. */ 
  254. public function rmdir( $dir ) { 
  255.  
  256. try { 
  257. $files = new RecursiveIteratorIterator( 
  258. new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ),  
  259. RecursiveIteratorIterator::CHILD_FIRST 
  260. ); 
  261.  
  262. foreach ( $files as $fileinfo ) { 
  263. $function = ( $fileinfo->isDir() ) ? 'rmdir' : 'unlink'; 
  264. if ( ! @$function( $fileinfo->getRealPath() ) ) { 
  265. throw new Exception( 'Could not run ' . $function . ' on ' . $fileinfo->getRealPath() ); 
  266. } catch ( Exception $e ) { 
  267. $this->log->addError( 'Filesystem Delete Error', [ 
  268. 'dir' => $dir,  
  269. 'exception' => $e->getMessage(),  
  270. ] ); 
  271.  
  272. return new WP_Error( 'recursion_delete_problem', $e ); 
  273.  
  274. return rmdir( $dir ); 
  275.  
  276. /** 
  277. * Wrapper function for rmdir() which ensures the directory gets automatically recreated after being deleted 
  278. * @param string $path 
  279. * @since 4.0 
  280. */ 
  281. public function cleanup_dir( $path ) { 
  282. $this->rmdir( $path ); 
  283. wp_mkdir_p( $path ); 
  284.  
  285. /** 
  286. * This function recursively copies all files and folders under a given directory 
  287. * equivalent to Bash: cp -R $dir 
  288. * @param string $source The path to be copied 
  289. * @param string $destination The path to copy to 
  290. * @return boolean 
  291. * @since 4.0 
  292. */ 
  293. public function copyr( $source, $destination ) { 
  294.  
  295. try { 
  296. if ( ! is_dir( $destination ) ) { 
  297. if ( ! wp_mkdir_p( $destination ) ) { 
  298. $this->log->addError( 'Failed Creating Folder Structure', [ 
  299. 'dir' => $destination,  
  300. ] ); 
  301.  
  302. throw new Exception( 'Could not create folder structure at ' . $destination ); 
  303.  
  304. $files = new RecursiveIteratorIterator( 
  305. new RecursiveDirectoryIterator( $source, RecursiveDirectoryIterator::SKIP_DOTS ),  
  306. RecursiveIteratorIterator::SELF_FIRST 
  307. ); 
  308.  
  309. foreach ( $files as $fileinfo ) { 
  310. if ( $fileinfo->isDir() && ! file_exists( $destination . $files->getSubPathName() ) ) { 
  311. if ( ! @mkdir( $destination . $files->getSubPathName() ) ) { 
  312. $this->log->addError( 'Failed Creating Folder', [ 
  313. 'dir' => $destination . $files->getSubPathName(),  
  314. ] ); 
  315.  
  316. throw new Exception( 'Could not create folder at ' . $destination . $files->getSubPathName() ); 
  317. } elseif ( ! $fileinfo->isDir() ) { 
  318. if ( ! @copy( $fileinfo, $destination . $files->getSubPathName() ) ) { 
  319. $this->log->addError( 'Failed Creating File', [ 
  320. 'file' => $destination . $files->getSubPathName(),  
  321. ] ); 
  322. throw new Exception( 'Could not create file at ' . $destination . $files->getSubPathName() ); 
  323. } catch ( Exception $e ) { 
  324.  
  325. $this->log->addError( 'Filesystem Copy Error', [ 
  326. 'source' => $source,  
  327. 'destination' => $destination,  
  328. 'exception' => $e->getMessage(),  
  329. ] ); 
  330.  
  331. return new WP_Error( 'recursion_copy_problem', $e ); 
  332.  
  333. return true; 
  334.  
  335. /** 
  336. * Get a path relative to the root WP directory, provided a user hasn't moved the wp-content directory outside the ABSPATH 
  337. * @param string $path The relative path 
  338. * @param string $replace What ABSPATH should be replaced with 
  339. * @return string 
  340. * @since 4.0 
  341. */ 
  342. public function relative_path( $path, $replace = '' ) { 
  343. return str_replace( ABSPATH, $replace, $path ); 
  344.  
  345. /** 
  346. * Modified version of get_upload_path() which just focuses on the base directory 
  347. * no matter if single or multisite installation 
  348. * We also only needed the basedir and baseurl so stripped out all the extras 
  349. * @return array Base dir and url for the upload directory 
  350. * @since 4.0 
  351. */ 
  352. public function get_upload_details() { 
  353.  
  354. $siteurl = get_option( 'siteurl' ); 
  355.  
  356. /** 
  357. * Older versions of multisite installations didn't use a standardised upload path 
  358. * which messes with our folder structure. We'll switch to the initial primary blog (which did use the 
  359. * /wp-content/uploads/ directory) and then restore the current blog at the end. 
  360. * Note: because BLOG_ID_CURRENT_SITE can be changed and the 'upload_path' option doesn't change with it we 
  361. * opted to hardcode the ID instead. 
  362. */ 
  363. if ( is_multisite() ) { 
  364. switch_to_blog( 1 ); 
  365.  
  366. $upload_path = trim( get_option( 'upload_path' ) ); 
  367. $dir = $upload_path; 
  368.  
  369. if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path ) { 
  370. $dir = WP_CONTENT_DIR . '/uploads'; 
  371. } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) { 
  372. /** $dir is absolute, $upload_path is (maybe) relative to ABSPATH */ 
  373. $dir = path_join( ABSPATH, $upload_path ); 
  374.  
  375. if ( ! $url = get_option( 'upload_url_path' ) ) { 
  376. if ( empty( $upload_path ) || ( 'wp-content/uploads' == $upload_path ) || ( $upload_path == $dir ) ) { 
  377. $url = WP_CONTENT_URL . '/uploads'; 
  378. } else { 
  379. $url = trailingslashit( $siteurl ) . $upload_path; 
  380.  
  381. /** Resort the current multisite blog */ 
  382. if ( is_multisite() ) { 
  383. restore_current_blog(); 
  384.  
  385. return [ 
  386. 'path' => $dir,  
  387. 'url' => $url,  
  388. ]; 
  389.  
  390. /** 
  391. * Attempt to convert the current URL to an internal path 
  392. * @param string $url The Url to convert 
  393. * @return string|boolean Path on success or false on failure 
  394. * @since 4.0 
  395. */ 
  396. public function convert_url_to_path( $url ) { 
  397.  
  398. /** If $url is empty we'll return early */ 
  399. if ( trim( $url ) == false ) { 
  400. return $url; 
  401.  
  402. /** Mostly we'll be accessing files in the upload directory, so attempt that first */ 
  403. $upload = wp_upload_dir(); 
  404.  
  405. $try_path = str_replace( $upload['baseurl'], $upload['basedir'], $url ); 
  406.  
  407. if ( is_file( $try_path ) ) { 
  408. return $try_path; 
  409.  
  410. /** If WP_CONTENT_DIR and WP_CONTENT_URL are set we'll try them */ 
  411. if ( defined( 'WP_CONTENT_DIR' ) && defined( 'WP_CONTENT_URL' ) ) { 
  412. $try_path = str_replace( WP_CONTENT_URL, WP_CONTENT_DIR, $url ); 
  413.  
  414. if ( is_file( $try_path ) ) { 
  415. return $try_path; 
  416.  
  417. /** Include our get_home_path functionality */ 
  418. if ( ! function_exists( 'get_home_path' ) ) { 
  419. require_once ABSPATH . 'wp-admin/includes/file.php'; 
  420.  
  421. /** If that didn't work let's try use home_url() and get_home_path() */ 
  422. $try_path = str_replace( home_url(), get_home_path(), $url ); 
  423.  
  424. if ( is_file( $try_path ) ) { 
  425. return $try_path; 
  426.  
  427. /** If that didn't work let's try use site_url() and ABSPATH */ 
  428. $try_path = str_replace( site_url(), ABSPATH, $url ); 
  429.  
  430. if ( is_file( $try_path ) ) { 
  431. return $try_path; 
  432.  
  433. /** If we are here we couldn't locate the file */ 
  434.  
  435. return false; 
  436.  
  437. /** 
  438. * Attempt to convert the current path to a URL 
  439. * @param string $path The path to convert 
  440. * @return string|boolean Url on success or false 
  441. * @since 4.0 
  442. */ 
  443. public function convert_path_to_url( $path ) { 
  444.  
  445. /** If $url is empty we'll return early */ 
  446. if ( trim( $path ) == false ) { 
  447. return $path; 
  448.  
  449. /** Mostly we'll be accessing files in the upload directory, so attempt that first */ 
  450. $upload = wp_upload_dir(); 
  451.  
  452. $try_url = str_replace( $upload['basedir'], $upload['baseurl'], $path ); 
  453.  
  454. if ( $try_url !== $path ) { 
  455. return $try_url; 
  456.  
  457. /** If WP_CONTENT_DIR and WP_CONTENT_URL are set we'll try them */ 
  458. if ( defined( 'WP_CONTENT_DIR' ) && defined( 'WP_CONTENT_URL' ) ) { 
  459. $try_url = str_replace( WP_CONTENT_DIR, WP_CONTENT_URL, $path ); 
  460.  
  461. if ( $try_url !== $path ) { 
  462. return $try_url; 
  463.  
  464. /** Include our get_home_path functionality */ 
  465. if ( ! function_exists( 'get_home_path' ) ) { 
  466. require_once ABSPATH . 'wp-admin/includes/file.php'; 
  467.  
  468. /** If that didn't work let's try use home_url() and get_home_path() */ 
  469. $try_url = str_replace( get_home_path(), home_url(), $path ); 
  470.  
  471. if ( $try_url !== $path ) { 
  472. return $try_url; 
  473.  
  474. /** If that didn't work let's try use site_url() and ABSPATH */ 
  475. $try_url = str_replace( ABSPATH, site_url(), $path ); 
  476.  
  477. if ( $try_url !== $path ) { 
  478. return $try_url; 
  479.  
  480. /** If we are here we couldn't locate the file */ 
  481.  
  482. return false; 
  483.  
  484.  
  485.  
  486. /** 
  487. * Remove any characters that are invalid in filenames (mostly on Windows systems) 
  488. * @param string $name The string / name to process 
  489. * @return string 
  490. * @since 4.0 
  491. */ 
  492. public function strip_invalid_characters( $name ) { 
  493. $characters = [ '/', '\\', '"', '*', '?', '|', ':', '<', '>' ]; 
  494.  
  495. return str_replace( $characters, '_', $name ); 
  496.  
  497. /** 
  498. * Backwards compatibility that allows multiple IDs to be passed to the renderer 
  499. * @param integer $entry_id The fallback ID if none present 
  500. * @param array $settings The current PDF settings 
  501. * @return array 
  502. * @since 4.0 
  503. */ 
  504. public function get_legacy_ids( $entry_id, $settings ) { 
  505.  
  506. $leads = rgget( 'lid' ); 
  507. $override = ( isset( $settings['public_access'] ) && $settings['public_access'] == 'Yes' ) ? true : false; 
  508.  
  509. if ( $leads && ( $override === true || $this->gform->has_capability( 'gravityforms_view_entries' ) ) ) { 
  510. $ids = explode( ', ', $leads ); 
  511.  
  512. /** ensure all passed ids are integers */ 
  513. array_walk( $ids, function ( &$id ) { 
  514. $id = (int) $id; 
  515. } ); 
  516.  
  517. /** filter our any zero-value ids */ 
  518. $ids = array_filter( $ids ); 
  519.  
  520. if ( sizeof( $ids ) > 0 ) { 
  521. return $ids; 
  522.  
  523. /** if not processing legacy endpoint, or if invalid IDs were passed we'll return the original entry ID */ 
  524. return [ $entry_id ]; 
  525.  
  526. /** 
  527. * Add support for the third-party plugin GF Multi Currency 
  528. * https://github.com/ilanco/gravity-forms-multi-currency 
  529. * @return void 
  530. * @since 4.0 
  531. */ 
  532. public function maybe_add_multicurrency_support() { 
  533. if ( class_exists( 'GFMultiCurrency' ) && method_exists( 'GFMultiCurrency', 'admin_pre_render' ) ) { 
  534. $currency = GFMultiCurrency::init(); 
  535. add_filter( 'gform_form_post_get_meta', [ $currency, 'admin_pre_render' ] ); 
  536.  
  537. /** 
  538. * Remove an extension from the end of a string 
  539. * @param string $string 
  540. * @param string $type The extension to remove from the end of the string 
  541. * @return string 
  542. * @since 4.0 
  543. */ 
  544. public function remove_extension_from_string( $string, $type = '.pdf' ) { 
  545. $type_length = mb_strlen( $type ); 
  546.  
  547. if ( mb_strtolower( mb_substr( $string, -$type_length ) ) === mb_strtolower( $type ) ) { 
  548. $string = mb_substr( $string, 0, -$type_length ); 
  549.  
  550. return $string; 
  551.  
  552. /** 
  553. * Convert our v3 boolean values into 'Yes' or 'No' responses 
  554. * @param mixed $value 
  555. * @return mixed 
  556. * @since 4.0 
  557. */ 
  558. public function update_deprecated_config( $value ) { 
  559.  
  560. if ( is_bool( $value ) ) { 
  561. $value = ( $value ) ? 'Yes' : 'No'; 
  562.  
  563. return $value; 
  564.  
  565. /** 
  566. * Determine if the logic should show or hide the item 
  567. * @param array $logic 
  568. * @param array $entry The Gravity Forms entry object 
  569. * @return boolean Will always return true if item should be shown, or false if should be hidden 
  570. * @since 4.0 
  571. */ 
  572. public function evaluate_conditional_logic( $logic, $entry ) { 
  573.  
  574. /** exit early if type not found */ 
  575. if ( ! isset( $logic['actionType'] ) ) { 
  576. return true; 
  577.  
  578. $form = $this->gform->get_form( $entry['form_id'] ); 
  579.  
  580. /** Do the evaluation */ 
  581. $evaluation = GFCommon::evaluate_conditional_logic( $logic, $form, $entry ); 
  582.  
  583. /** If the logic is to hide the item we'll invert the evaluation */ 
  584. if ( $logic['actionType'] !== 'show' ) { 
  585. return ! $evaluation; 
  586.  
  587. return $evaluation; 
  588.  
  589. /** 
  590. * Takes a Gravity Form ID and returns the list of fields which can be accessed using their ID 
  591. * @param integer $form_id The Gravity Form ID 
  592. * @return array The field array ordered by the field ID 
  593. * @since 4.0 
  594. */ 
  595. public function get_fields_sorted_by_id( $form_id ) { 
  596. $form = $this->gform->get_form( $form_id ); 
  597. $fields = []; 
  598.  
  599. if ( isset( $form['fields'] ) && is_array( $form['fields'] ) ) { 
  600. foreach ( $form['fields'] as $field ) { 
  601. $fields[ $field->id ] = $field; 
  602.  
  603. return $fields; 
  604.  
  605. /** 
  606. * Converts the 4.x settings array into a compatible 3.x settings array 
  607. * @param array $settings The 4.x settings to be converted 
  608. * @param array $form (since 4.0.6) The Gravity Forms array 
  609. * @param array $entry (since 4.0.6) The Gravity Forms entry 
  610. * @return array The 3.x compatible settings 
  611. * @since 4.0 
  612. */ 
  613. public function backwards_compat_conversion( $settings, $form, $entry ) { 
  614.  
  615. $compat = []; 
  616. $compat['premium'] = ( isset( $settings['advanced_template'] ) && $settings['advanced_template'] == 'Yes' ) ? true : false; 
  617. $compat['rtl'] = ( isset( $settings['rtl'] ) && $settings['rtl'] == 'Yes' ) ? true : false; 
  618. $compat['dpi'] = ( isset( $settings['image_dpi'] ) ) ? (int) $settings['image_dpi'] : 96; 
  619. $compat['security'] = ( isset( $settings['security'] ) && $settings['security'] == 'Yes' ) ? true : false; 
  620. $compat['pdf_password'] = ( isset( $settings['password'] ) ) ? $this->gform->process_tags( $settings['password'], $form, $entry ) : ''; 
  621. $compat['pdf_privileges'] = ( isset( $settings['privileges'] ) ) ? $settings['privileges'] : ''; 
  622. $compat['pdfa1b'] = ( isset( $settings['format'] ) && $settings['format'] == 'PDFA1B' ) ? true : false; 
  623. $compat['pdfx1a'] = ( isset( $settings['format'] ) && $settings['format'] == 'PDFX1A' ) ? true : false; 
  624.  
  625. return $compat; 
  626.  
  627. /** 
  628. * Converts the 4.x output to into a compatible 3.x type 
  629. * @param string $type 
  630. * @return string 
  631. * @since 4.0 
  632. */ 
  633. public function backwards_compat_output( $type = '' ) { 
  634. switch ( strtolower( $type ) ) { 
  635. case 'display': 
  636. return 'view'; 
  637. break; 
  638.  
  639. case 'download': 
  640. return 'download'; 
  641. break; 
  642.  
  643. default: 
  644. return 'save'; 
  645. break; 
  646.  
  647. /** 
  648. * Check if the Gravity Forms GFEntryDetail class exists, otherwise load it 
  649. * @since 4.0 
  650. */ 
  651. public function maybe_load_gf_entry_detail_class() { 
  652. /** Ensure Gravity Forms GFEntryDetail class is loaded */ 
  653. if ( ! class_exists( 'GFEntryDetail' ) ) { 
  654. $entry_details_file = GFCommon::get_base_path() . '/entry_detail.php'; 
  655.  
  656. if ( is_file( $entry_details_file ) ) { 
  657. require_once( $entry_details_file ); 
  658.  
  659. /** 
  660. * A recursive function that will search a multidimensional array for the value 
  661. * @param mixed $needle The value to search for 
  662. * @param array $haystack The multidimensional array to search in 
  663. * @param bool $strict Pass `true` to match for the value and type, false for just the type. 
  664. * @return bool True when found, false otherwise 
  665. */ 
  666. public function in_array( $needle, $haystack, $strict = true ) { 
  667. foreach ( $haystack as $item ) { 
  668. if ( ( $strict ? $item === $needle : $item == $needle ) || 
  669. ( is_array( $item ) && $this->in_array( $needle, $item, $strict ) ) 
  670. ) { 
  671. return true; 
  672.  
  673. return false; 
  674.  
  675. /** 
  676. * Ensure an extension is added to the end of the name 
  677. * @param string $name The PHP template 
  678. * @param string $extension The extension that should be added to the filename 
  679. * @return string 
  680. * @since 4.1 
  681. */ 
  682. public function get_file_with_extension( $name, $extension = '.php' ) { 
  683. if ( substr( $name, -strlen( $extension ) ) !== $extension ) { 
  684. $name = $name . $extension; 
  685.  
  686. return $name; 
  687.  
  688. /** 
  689. * Check the Nonce and any user capabilities required before all AJAX requests 
  690. * If once of these fails the appropriate response code will be sent 
  691. * @param string $endpoint_desc The name of the endpoint currently being processed (for the log) 
  692. * @param string|bool $capability The capability name the logged in user is required to run this endpoint, or false if no authentation needed 
  693. * @param string $nonce_name The name of the Nonce our $_POST['nonce'] should be checked against 
  694. * @todo move this to an abstract class when we refractor the AJAX code into classes 
  695. * @since 4.1 
  696. */ 
  697. public function handle_ajax_authentication( $endpoint_desc, $capability = 'gravityforms_edit_settings', $nonce_name = 'gfpdf_ajax_nonce' ) { 
  698.  
  699. $this->log->addNotice( 'Running AJAX Endpoint', [ 
  700. 'type' => $endpoint_desc,  
  701. 'post' => $_POST,  
  702. ] ); 
  703.  
  704. /** 
  705. * Validate Endpoint 
  706. */ 
  707. $nonce = ( isset( $_POST['nonce'] ) ) ? $_POST['nonce'] : ''; 
  708. if ( ! wp_verify_nonce( $nonce, $nonce_name ) ) { 
  709.  
  710. $this->log->addWarning( 'Nonce Verification Failed' ); 
  711.  
  712. /** Unauthorized response */ 
  713. wp_die( '401', 401 ); 
  714.  
  715. /** prevent unauthorized access */ 
  716. if ( $capability !== false && ! $this->gform->has_capability( $capability ) ) { 
  717.  
  718. $this->log->addCritical( 'Lack of User Capabilities', [ 
  719. 'user' => wp_get_current_user(),  
  720. 'user_meta' => get_user_meta( get_current_user_id() ),  
  721. 'capability_needed' => $capability,  
  722. ] ); 
  723.  
  724. /** Unauthorized response */ 
  725. wp_die( '401', 401 );