GFPDFModelModel_PDF

Model_PDF.

Defined (1)

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

/src/model/Model_PDF.php  
  1. class Model_PDF extends Helper_Abstract_Model { 
  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_Abstract_Options / Helper_Options_Fields object 
  19. * Makes it easy to access global PDF settings and individual form PDF settings 
  20. * @var \GFPDF\Helper\Helper_Options_Fields 
  21. * @since 4.0 
  22. */ 
  23. protected $options; 
  24.  
  25. /** 
  26. * Holds our Helper_Data object 
  27. * which we can autoload with any data needed 
  28. * @var \GFPDF\Helper\Helper_Data 
  29. * @since 4.0 
  30. */ 
  31. protected $data; 
  32.  
  33. /** 
  34. * Holds our Helper_Misc object 
  35. * Makes it easy to access common methods throughout the plugin 
  36. * @var \GFPDF\Helper\Helper_Misc 
  37. * @since 4.0 
  38. */ 
  39. protected $misc; 
  40.  
  41. /** 
  42. * Holds our Helper_Notices object 
  43. * which we can use to queue up admin messages for the user 
  44. * @var \GFPDF\Helper\Helper_Notices 
  45. * @since 4.0 
  46. */ 
  47. protected $notices; 
  48.  
  49. /** 
  50. * Holds our Helper_Templates object 
  51. * used to ease access to our PDF templates 
  52. * @var \GFPDF\Helper\Helper_Templates 
  53. * @since 4.0 
  54. */ 
  55. protected $templates; 
  56.  
  57. /** 
  58. * Setup our view with the needed data and classes 
  59. * @param \GFPDF\Helper\Helper_Abstract_Form $gform Our abstracted Gravity Forms helper functions 
  60. * @param \Monolog\Logger|LoggerInterface $log Our logger class 
  61. * @param \GFPDF\Helper\Helper_Abstract_Options $options Our options class which allows us to access any settings 
  62. * @param \GFPDF\Helper\Helper_Data $data Our plugin data store 
  63. * @param \GFPDF\Helper\Helper_Misc $misc Our miscellaneous class 
  64. * @param \GFPDF\Helper\Helper_Notices $notices Our notice class used to queue admin messages and errors 
  65. * @param \GFPDF\Helper\Helper_Templates $templates 
  66. * @since 4.0 
  67. */ 
  68. public function __construct( Helper_Abstract_Form $gform, LoggerInterface $log, Helper_Abstract_Options $options, Helper_Data $data, Helper_Misc $misc, Helper_Notices $notices, Helper_Templates $templates ) { 
  69.  
  70. /** Assign our internal variables */ 
  71. $this->gform = $gform; 
  72. $this->log = $log; 
  73. $this->options = $options; 
  74. $this->data = $data; 
  75. $this->misc = $misc; 
  76. $this->notices = $notices; 
  77. $this->templates = $templates; 
  78.  
  79. /** 
  80. * Our Middleware used to handle the authentication process 
  81. * @param string $pid The Gravity Form PDF Settings ID 
  82. * @param integer $lid The Gravity Form Entry ID 
  83. * @param string $action Whether the PDF should be viewed or downloaded 
  84. * @since 4.0 
  85. * @return WP_Error 
  86. */ 
  87. public function process_pdf( $pid, $lid, $action = 'view' ) { 
  88.  
  89. /** 
  90. * Check if we have a valid Gravity Form Entry and PDF Settings ID 
  91. */ 
  92. $entry = $this->gform->get_entry( $lid ); 
  93.  
  94. /** not a valid entry */ 
  95. if ( is_wp_error( $entry ) ) { 
  96. $this->log->addError( 'Invalid Entry.', [ 
  97. 'entry' => $entry,  
  98. ] ); 
  99.  
  100. return $entry; /** return error */ 
  101.  
  102. $settings = $this->options->get_pdf( $entry['form_id'], $pid ); 
  103.  
  104. /** Not valid settings */ 
  105. if ( is_wp_error( $settings ) ) { 
  106.  
  107. $this->log->addError( 'Invalid PDF Settings.', [ 
  108. 'entry' => $entry,  
  109. 'WP_Error_Message' => $settings->get_error_message(),  
  110. 'WP_Error_Code' => $settings->get_error_code(),  
  111. ] ); 
  112.  
  113. return $settings; /** return error */ 
  114.  
  115. /** Add our download setting */ 
  116. $settings['pdf_action'] = $action; 
  117.  
  118. /** 
  119. * Our middleware authenticator 
  120. * Allow users to tap into our middleware and add or remove additional authentication layers 
  121. * Default middleware includes 'middle_public_access', 'middle_active', 'middle_conditional', 'middle_owner_restriction', 'middle_logged_out_timeout', 'middle_auth_logged_out_user', 'middle_user_capability' 
  122. * If WP_Error is returned the PDF won't be parsed 
  123. * See https://gravitypdf.com/documentation/v4/gfpdf_pdf_middleware/ for more details about this filter 
  124. */ 
  125. $middleware = apply_filters( 'gfpdf_pdf_middleware', false, $entry, $settings ); 
  126.  
  127. /** Throw error */ 
  128. if ( is_wp_error( $middleware ) ) { 
  129.  
  130. $this->log->addError( 'PDF Authentication Failure.', [ 
  131. 'entry' => $entry,  
  132. 'settings' => $settings,  
  133. 'WP_Error_Message' => $middleware->get_error_message(),  
  134. 'WP_Error_Code' => $middleware->get_error_code(),  
  135. ] ); 
  136.  
  137. return $middleware; 
  138.  
  139. /** Add backwards compatibility support for certain settings */ 
  140. $settings = $this->apply_backwards_compatibility_filters( $settings, $entry ); 
  141.  
  142. /** Ensure Gravity Forms depedancy loaded */ 
  143. $this->misc->maybe_load_gf_entry_detail_class(); 
  144.  
  145. /** If we are here we can generate our PDF */ 
  146. $controller = $this->getController(); 
  147. $controller->view->generate_pdf( $entry, $settings ); 
  148.  
  149. return null; 
  150.  
  151. /** 
  152. * Apply filters to particular settings to maintain backwards compatibility 
  153. * Note: If you want to modify the $settings array you should use the new "gfpdf_pdf_config" filter instead 
  154. * @param array $settings The PDF settings array 
  155. * @param array $entry 
  156. * @return array The $settings array 
  157. * @since 4.0 
  158. */ 
  159. public function apply_backwards_compatibility_filters( $settings, $entry ) { 
  160.  
  161. $form = $this->gform->get_form( $entry['form_id'] ); 
  162.  
  163. $settings['filename'] = $this->misc->remove_extension_from_string( apply_filters( 'gfpdfe_pdf_name', $settings['filename'], $form, $entry ) ); 
  164. $settings['template'] = $this->misc->remove_extension_from_string( apply_filters( 'gfpdfe_template', $settings['template'], $form, $entry ), '.php' ); 
  165.  
  166. if ( isset( $settings['orientation'] ) ) { 
  167. $settings['orientation'] = apply_filters( 'gfpdf_orientation', $settings['orientation'], $form, $entry ); 
  168.  
  169. if ( isset( $settings['security'] ) ) { 
  170. $settings['security'] = $this->misc->update_deprecated_config( apply_filters( 'gfpdf_security', $settings['security'], $form, $entry ) ); 
  171.  
  172. if ( isset( $settings['privileges'] ) ) { 
  173. $settings['privileges'] = apply_filters( 'gfpdf_privilages', $settings['privileges'], $form, $entry ); 
  174.  
  175. if ( isset( $settings['password'] ) ) { 
  176. $settings['password'] = apply_filters( 'gfpdf_password', $settings['password'], $form, $entry ); 
  177.  
  178. if ( isset( $settings['master_password'] ) ) { 
  179. $settings['master_password'] = apply_filters( 'gfpdf_master_password', $settings['master_password'], $form, $entry ); 
  180.  
  181. if ( isset( $settings['rtl'] ) ) { 
  182. $settings['rtl'] = $this->misc->update_deprecated_config( apply_filters( 'gfpdf_rtl', $settings['rtl'], $form, $entry ) ); 
  183.  
  184. return $settings; 
  185.  
  186. /** 
  187. * Check if the current PDF trying to be viewed has public access enabled 
  188. * If it does, we'll remove some of our middleware filters to allow this feature 
  189. * @param boolean|object $action 
  190. * @param array $entry The Gravity Forms Entry 
  191. * @param array $settings The Gravity Form PDF Settings 
  192. * @return boolean|object 
  193. * @since 4.0 
  194. */ 
  195. public function middle_public_access( $action, $entry, $settings ) { 
  196.  
  197. if ( isset( $settings['public_access'] ) && 'Yes' === $settings['public_access'] ) { 
  198. remove_filter( 'gfpdf_pdf_middleware', [ $this, 'middle_owner_restriction' ], 40 ); 
  199. remove_filter( 'gfpdf_pdf_middleware', [ $this, 'middle_logged_out_timeout' ], 50 ); 
  200. remove_filter( 'gfpdf_pdf_middleware', [ $this, 'middle_auth_logged_out_user' ], 60 ); 
  201. remove_filter( 'gfpdf_pdf_middleware', [ $this, 'middle_user_capability' ], 70 ); 
  202.  
  203. return $action; 
  204.  
  205. /** 
  206. * Check if the current PDF trying to be viewed is active 
  207. * @param boolean|object $action 
  208. * @param array $entry The Gravity Forms Entry 
  209. * @param array $settings The Gravity Form PDF Settings 
  210. * @return boolean|object 
  211. * @since 4.0 
  212. */ 
  213. public function middle_active( $action, $entry, $settings ) { 
  214.  
  215. if ( ! is_wp_error( $action ) ) { 
  216. if ( $settings['active'] !== true ) { 
  217. return new WP_Error( 'inactive', esc_html__( 'The PDF configuration is not currently active.', 'gravity-forms-pdf-extended' ) ); 
  218.  
  219. return $action; 
  220.  
  221. /** 
  222. * Check if the current PDF trying to be viewed has conditional logic which passes 
  223. * @param boolean|object $action 
  224. * @param array $entry The Gravity Forms Entry 
  225. * @param array $settings The Gravity Form PDF Settings 
  226. * @return boolean|object 
  227. * @since 4.0 
  228. */ 
  229. public function middle_conditional( $action, $entry, $settings ) { 
  230.  
  231. if ( ! is_wp_error( $action ) ) { 
  232. if ( isset( $settings['conditionalLogic'] ) && ! $this->misc->evaluate_conditional_logic( $settings['conditionalLogic'], $entry ) ) { 
  233. return new WP_Error( 'conditional_logic', esc_html__( 'PDF conditional logic requirements have not been met.', 'gravity-forms-pdf-extended' ) ); 
  234.  
  235. return $action; 
  236.  
  237. /** 
  238. * Check if the current user attempting to access is the PDF owner 
  239. * @param array $entry The Gravity Forms Entry 
  240. * @param string $type The authentication type we should use 
  241. * @return boolean 
  242. * @since 4.0 
  243. */ 
  244. public function is_current_pdf_owner( $entry, $type = 'all' ) { 
  245. $owner = false; 
  246. /** check if the user is logged in and the entry is assigned to them */ 
  247. if ( $type === 'all' || $type === 'logged_in' ) { 
  248. if ( is_user_logged_in() && (int) $entry['created_by'] === get_current_user_id() ) { 
  249. $owner = true; 
  250.  
  251. if ( $type === 'all' || $type === 'logged_out' ) { 
  252. $user_ip = trim( GFFormsModel::get_ip() ); 
  253. if ( $entry['ip'] == $user_ip && $entry['ip'] !== '127.0.0.1' && strlen( $user_ip ) !== 0 ) { /** check if the user IP matches the entry IP */ 
  254. $owner = true; 
  255.  
  256. return $owner; 
  257.  
  258. /** 
  259. * Check the "Restrict Logged Out User" global setting and validate it against the current user 
  260. * @param boolean|object $action 
  261. * @param array $entry The Gravity Forms Entry 
  262. * @param array $settings The Gravity Form PDF Settings 
  263. * @return boolean|object 
  264. * @since 4.0 
  265. */ 
  266. public function middle_owner_restriction( $action, $entry, $settings ) { 
  267.  
  268. /** ensure another middleware filter hasn't already done validation */ 
  269. if ( ! is_wp_error( $action ) ) { 
  270. /** get the setting */ 
  271. $owner_restriction = ( isset( $settings['restrict_owner'] ) ) ? $settings['restrict_owner'] : 'No'; 
  272.  
  273. if ( $owner_restriction === 'Yes' && ! is_user_logged_in() ) { 
  274.  
  275. $this->log->addNotice( 'Redirecting to Login.', [ 
  276. 'entry' => $entry,  
  277. 'settings' => $settings,  
  278. ] ); 
  279.  
  280. /** prompt user to login */ 
  281. auth_redirect(); 
  282.  
  283. return $action; 
  284.  
  285. /** 
  286. * Check the "Logged Out Timeout" global setting and validate it against the current user 
  287. * @param boolean|object $action 
  288. * @param array $entry The Gravity Forms Entry 
  289. * @param array $settings The Gravity Form PDF Settings 
  290. * @return boolean|object 
  291. * @since 4.0 
  292. */ 
  293. public function middle_logged_out_timeout( $action, $entry, $settings ) { 
  294.  
  295. /** ensure another middleware filter hasn't already done validation */ 
  296. if ( ! is_wp_error( $action ) ) { 
  297.  
  298. /** only check if PDF timed out if our logged out restriction is not 'Yes' and the user is not logged in */ 
  299. if ( ! is_user_logged_in() && $this->is_current_pdf_owner( $entry, 'logged_out' ) === true ) { 
  300. /** get the global PDF settings */ 
  301. $timeout = (int) $this->options->get_option( 'logged_out_timeout', '30' ); 
  302.  
  303. /** if '0' there is no timeout, or if the logged out restrictions are enabled we'll ignore this */ 
  304. if ( $timeout !== 0 ) { 
  305.  
  306. $timeout_stamp = 60 * $timeout; /** 60 seconds multiplied by number of minutes */ 
  307. $entry_created = strtotime( $entry['date_created'] ); /** get entry timestamp */ 
  308. $timeout_expires = $entry_created + $timeout_stamp; /** get the timeout expiry based on the entry created time */ 
  309.  
  310. /** compare our two timestamps and throw error if outside the timeout */ 
  311. if ( time() > $timeout_expires ) { 
  312.  
  313. /** if there is no user account assigned to this entry throw error */ 
  314. if ( empty( $entry['created_by'] ) ) { 
  315. return new WP_Error( 'timeout_expired', esc_html__( 'Your PDF is no longer accessible.', 'gravity-forms-pdf-extended' ) ); 
  316. } else { 
  317.  
  318. $this->log->addNotice( 'Redirecting to Login.', [ 
  319. 'entry' => $entry,  
  320. 'settings' => $settings,  
  321. ] ); 
  322.  
  323. /** prompt to login */ 
  324. auth_redirect(); 
  325.  
  326. return $action; 
  327.  
  328. /** 
  329. * Check if the user is logged out and authenticate as needed 
  330. * @param boolean|object $action 
  331. * @param array $entry The Gravity Forms Entry 
  332. * @param array $settings The Gravity Form PDF Settings 
  333. * @return boolean|object 
  334. * @since 4.0 
  335. */ 
  336. public function middle_auth_logged_out_user( $action, $entry, $settings ) { 
  337.  
  338. if ( ! is_wp_error( $action ) ) { 
  339.  
  340. /** check if the user is not the current entry owner */ 
  341. if ( ! is_user_logged_in() && $this->is_current_pdf_owner( $entry, 'logged_out' ) === false ) { 
  342. /** check if there is actually a user who owns entry */ 
  343. if ( ! empty( $entry['created_by'] ) ) { 
  344.  
  345. $this->log->addNotice( 'Redirecting to Login.', [ 
  346. 'entry' => $entry,  
  347. 'settings' => $settings,  
  348. ] ); 
  349.  
  350. /** prompt user to login to get access */ 
  351. auth_redirect(); 
  352. } else { 
  353. /** there's no returning, throw generic error */ 
  354. return new WP_Error( 'error' ); 
  355.  
  356. return $action; 
  357.  
  358. /** 
  359. * Check the "User Restriction" global setting and validate it against the current user 
  360. * @param boolean|object $action 
  361. * @param array $entry The Gravity Forms Entry 
  362. * @param array $settings The Gravity Form PDF Settings 
  363. * @return boolean|object 
  364. * @since 4.0 
  365. */ 
  366. public function middle_user_capability( $action, $entry, $settings ) { 
  367.  
  368. if ( ! is_wp_error( $action ) ) { 
  369. /** check if the user is logged in but is not the current owner */ 
  370. if ( is_user_logged_in() && 
  371. ( ( $this->options->get_option( 'limit_to_admin', 'No' ) == 'Yes' ) || ( $this->is_current_pdf_owner( $entry, 'logged_in' ) === false ) ) 
  372. ) { 
  373.  
  374. /** Handle permissions checks */ 
  375. $admin_permissions = $this->options->get_option( 'admin_capabilities', [ 'gravityforms_view_entries' ] ); 
  376.  
  377. /** loop through permissions and check if the current user has any of those capabilities */ 
  378. $access = false; 
  379. foreach ( $admin_permissions as $permission ) { 
  380. if ( $this->gform->has_capability( $permission ) ) { 
  381. $access = true; 
  382.  
  383. /** throw error if no access granted */ 
  384. if ( ! $access ) { 
  385. return new WP_Error( 'access_denied', esc_html__( 'You do not have access to view this PDF.', 'gravity-forms-pdf-extended' ) ); 
  386.  
  387. return $action; 
  388.  
  389. /** 
  390. * Display PDF on Gravity Form entry list page 
  391. * @param integer $form_id Gravity Form ID 
  392. * @param integer $field_id Current field ID 
  393. * @param mixed $value Current value of field 
  394. * @param array $entry Entry Information 
  395. * @return void 
  396. * @since 4.0 
  397. */ 
  398. public function view_pdf_entry_list( $form_id, $field_id, $value, $entry ) { 
  399.  
  400. $controller = $this->getController(); 
  401. $pdf_list = $this->get_pdf_display_list( $entry ); 
  402.  
  403. $this->log->addNotice( 'Display PDF Entry List.', [ 
  404. 'pdfs' => $pdf_list,  
  405. 'entry' => $entry,  
  406. ] ); 
  407.  
  408. if ( ! empty( $pdf_list ) ) { 
  409.  
  410. if ( sizeof( $pdf_list ) > 1 ) { 
  411. $args = [ 
  412. 'pdfs' => $pdf_list,  
  413. 'view' => strtolower( $this->options->get_option( 'default_action' ) ),  
  414. ]; 
  415.  
  416. $controller->view->entry_list_pdf_multiple( $args ); 
  417. } else { 
  418. /** Only one PDF for this form so display a simple 'View PDF' link */ 
  419. $args = [ 
  420. 'pdf' => array_shift( $pdf_list ),  
  421. 'view' => strtolower( $this->options->get_option( 'default_action' ) ),  
  422. ]; 
  423.  
  424. $controller->view->entry_list_pdf_single( $args ); 
  425.  
  426. /** 
  427. * Display the PDF links on the entry detailed section of the admin area 
  428. * @param integer $form_id Gravity Form ID 
  429. * @param array $entry The entry information 
  430. * @return void 
  431. * @since 4.0 
  432. */ 
  433. public function view_pdf_entry_detail( $form_id, $entry ) { 
  434.  
  435. $controller = $this->getController(); 
  436. $pdf_list = $this->get_pdf_display_list( $entry ); 
  437.  
  438. $this->log->addNotice( 'Display PDF Entry Detail List.', [ 
  439. 'pdfs' => $pdf_list,  
  440. 'entry' => $entry,  
  441. ] ); 
  442.  
  443. if ( ! empty( $pdf_list ) ) { 
  444. $args = [ 
  445. 'pdfs' => $pdf_list,  
  446. ]; 
  447. $controller->view->entry_detailed_pdf( $args ); 
  448.  
  449. /** 
  450. * Get a preformatted list of active PDFs with name and URL 
  451. * @param array $entry 
  452. * @return array 
  453. * @since 4.0 
  454. */ 
  455. public function get_pdf_display_list( $entry ) { 
  456.  
  457. /** Stores our formatted PDFs */ 
  458. $args = []; 
  459.  
  460. /** Check if we have any PDFs */ 
  461. $form = $this->gform->get_form( $entry['form_id'] ); 
  462. $pdfs = ( isset( $form['gfpdf_form_settings'] ) ) ? $this->get_active_pdfs( $form['gfpdf_form_settings'], $entry ) : []; 
  463.  
  464. if ( ! empty( $pdfs ) ) { 
  465.  
  466. foreach ( $pdfs as $settings ) { 
  467.  
  468. $args[] = [ 
  469. 'name' => $this->get_pdf_name( $settings, $entry ),  
  470. 'view' => $this->get_pdf_url( $settings['id'], $entry['id'], false ),  
  471. 'download' => $this->get_pdf_url( $settings['id'], $entry['id'], true ),  
  472. ]; 
  473.  
  474. return $args; 
  475.  
  476. /** 
  477. * Generate the PDF Name 
  478. * @param array $settings The PDF Form Settings 
  479. * @param array $entry The Gravity Form entry details 
  480. * @return string The PDF Name 
  481. * @since 4.0 
  482. */ 
  483. public function get_pdf_name( $settings, $entry ) { 
  484.  
  485. $form = $this->gform->get_form( $entry['form_id'] ); 
  486. $name = $this->gform->process_tags( $settings['filename'], $form, $entry ); 
  487.  
  488. /** Decode HTML entities */ 
  489. $name = wp_specialchars_decode( $name, ENT_QUOTES ); 
  490.  
  491. /** 
  492. * Add filter to modify PDF name 
  493. * See https://gravitypdf.com/documentation/v4/gfpdf_pdf_filename/ for more details about this filter 
  494. */ 
  495. $name = apply_filters( 'gfpdf_pdf_filename', $name, $form, $entry, $settings ); 
  496.  
  497. /** Backwards compatible filter */ 
  498. $name = apply_filters( 'gfpdfe_pdf_filename', $name, $form, $entry, $settings ); 
  499.  
  500. /** Remove any characters that cannot be present in a filename */ 
  501. $name = $this->misc->strip_invalid_characters( $name ); 
  502.  
  503. return $name; 
  504.  
  505. /** 
  506. * Create a PDF Link based on the current PDF settings and entry 
  507. * @param integer $pid The PDF Form Settings ID 
  508. * @param integer $id The Gravity Form entry ID 
  509. * @param boolean $download Whether the PDF should be downloaded or not 
  510. * @param boolean $print Whether we should mark the PDF to be printed 
  511. * @param boolean $esc Whether to escape the URL or not 
  512. * @return string Direct link to the PDF 
  513. * @since 4.0 
  514. */ 
  515. public function get_pdf_url( $pid, $id, $download = false, $print = false, $esc = true ) { 
  516. global $wp_rewrite; 
  517.  
  518. /** Check if permalinks are enabled, otherwise fall back to our ugly link structure for 4.0 (not the same as our v3 links) */ 
  519. if ( $wp_rewrite->using_permalinks() ) { 
  520. $url = home_url() . '/' . $wp_rewrite->root; /** Handle "almost pretty" permalinks - fix for IIS servers without modrewrite */ 
  521. $url .= 'pdf/' . $pid . '/' . $id . '/'; 
  522.  
  523. if ( $download ) { 
  524. $url .= 'download/'; 
  525.  
  526. if ( $print ) { 
  527. $url .= '?print=1'; 
  528. } else { 
  529. $url = home_url() . '/?gpdf=1&pid=' . $pid . '&lid=' . $id; 
  530.  
  531. if ( $download ) { 
  532. $url .= '&action=download'; 
  533.  
  534. if ( $print ) { 
  535. $url .= '&print=1'; 
  536.  
  537. if ( $esc ) { 
  538. $url = esc_url( $url ); 
  539.  
  540. return $url; 
  541.  
  542. /** 
  543. * Filter out inactive PDFs and those who don't meet the conditional logic 
  544. * @param array $pdfs The PDF settings array 
  545. * @param array $entry The current entry information 
  546. * @return array The filtered PDFs 
  547. * @since 4.0 
  548. */ 
  549. public function get_active_pdfs( $pdfs, $entry ) { 
  550.  
  551. $filtered = []; 
  552. $form = $this->gform->get_form( $entry['form_id'] ); 
  553.  
  554. foreach ( $pdfs as $pdf ) { 
  555. if ( $pdf['active'] && ( empty( $pdf['conditionalLogic'] ) || $this->misc->evaluate_conditional_logic( $pdf['conditionalLogic'], $entry ) ) ) { 
  556. $filtered[ $pdf['id'] ] = $pdf; 
  557.  
  558. return $filtered; 
  559.  
  560. /** 
  561. * Generate and save PDF to disk 
  562. * @param \GFPDF\Helper\Helper_PDF $pdf The Helper_PDF object 
  563. * @return boolean 
  564. * @since 4.0 
  565. */ 
  566. public function process_and_save_pdf( Helper_PDF $pdf ) { 
  567.  
  568. /** Check that the PDF hasn't already been created this session */ 
  569. if ( ! $this->does_pdf_exist( $pdf ) ) { 
  570.  
  571. /** Ensure Gravity Forms depedancy loaded */ 
  572. $this->misc->maybe_load_gf_entry_detail_class(); 
  573.  
  574. /** Enable Multicurrency support */ 
  575. $this->misc->maybe_add_multicurrency_support(); 
  576.  
  577. /** Get required parameters */ 
  578. $entry = $pdf->get_entry(); 
  579. $settings = $pdf->get_settings(); 
  580. $form = $this->gform->get_form( $entry['form_id'] ); 
  581.  
  582. $args = $this->templates->get_template_arguments( 
  583. $form,  
  584. $this->misc->get_fields_sorted_by_id( $form['id'] ),  
  585. $entry,  
  586. $this->get_form_data( $entry ),  
  587. $settings,  
  588. $this->templates->get_config_class( $settings['template'] ),  
  589. $this->misc->get_legacy_ids( $entry['id'], $settings ) 
  590. ); 
  591.  
  592. /** Add backwards compatibility support */ 
  593. $GLOBALS['wp']->query_vars['pid'] = $settings['id']; 
  594. $GLOBALS['wp']->query_vars['lid'] = $entry['id']; 
  595.  
  596. try { 
  597.  
  598. /** Initialise our PDF helper class */ 
  599. $pdf->init(); 
  600. $pdf->set_template(); 
  601. $pdf->set_output_type( 'save' ); 
  602.  
  603. /** Increment our rudimentary PDF counter */ 
  604. $this->options->increment_pdf_count(); 
  605.  
  606. /** Add Backwards compatibility support for our v3 Tier 2 Add-on */ 
  607. if ( isset( $settings['advanced_template'] ) && strtolower( $settings['advanced_template'] ) == 'yes' ) { 
  608.  
  609. /** Check if we should process this document using our legacy system */ 
  610. if ( $this->handle_legacy_tier_2_processing( $pdf, $entry, $settings, $args ) ) { 
  611. return true; 
  612.  
  613. /** Render the PDF template HTML */ 
  614. $pdf->render_html( $args ); 
  615.  
  616. /** Generate and save the PDF */ 
  617. $pdf->save_pdf( $pdf->generate() ); 
  618.  
  619. return true; 
  620. } catch ( Exception $e ) { 
  621.  
  622. $this->log->addError( 'PDF Generation Error', [ 
  623. 'pdf' => $pdf,  
  624. 'exception' => $e->getMessage(),  
  625. ] ); 
  626.  
  627. return false; 
  628.  
  629. return true; 
  630.  
  631. /** 
  632. * Handles the loading and running of our legacy Tier 2 PDF templates 
  633. * @param \GFPDF\Helper\Helper_PDF $pdf The Helper_PDF object 
  634. * @param array $entry The Gravity Forms raw entry data 
  635. * @param array $settings The Gravity PDF settings 
  636. * @param array $args The data that should be passed directly to a PDF template 
  637. * @return bool 
  638. * @since 4.0 
  639. */ 
  640. public function handle_legacy_tier_2_processing( Helper_PDF $pdf, $entry, $settings, $args ) { 
  641.  
  642. $form = $this->gform->get_form( $entry['form_id'] ); 
  643.  
  644. $prevent_main_pdf_loader = apply_filters( 'gfpdfe_pre_load_template',  
  645. $form['id'],  
  646. $entry['id'],  
  647. basename( $pdf->get_template_path() ),  
  648. $form['id'] . $entry['id'],  
  649. $this->misc->backwards_compat_output( $pdf->get_output_type() ),  
  650. $pdf->get_filename(),  
  651. $this->misc->backwards_compat_conversion( $settings, $form, $entry ),  
  652. $args 
  653. ); /** Backwards Compatibility */ 
  654.  
  655. return ( $prevent_main_pdf_loader === true ) ? true : false; 
  656.  
  657. /** 
  658. * Generate and save the PDF to disk 
  659. * @param array $entry The Gravity Form entry array (usually passed in as a filter or pulled using GFAPI::get_entry( $id ) ) 
  660. * @param array $settings The PDF configuration settings for the particular entry / form being processed 
  661. * @return string|WP_Error Return the full path to the PDF, or a WP_Error on failure 
  662. * @since 4.0 
  663. */ 
  664. public function generate_and_save_pdf( $entry, $settings ) { 
  665.  
  666. $pdf_generator = new Helper_PDF( $entry, $settings, $this->gform, $this->data, $this->misc, $this->templates ); 
  667. $pdf_generator->set_filename( $this->get_pdf_name( $settings, $entry ) ); 
  668.  
  669. if ( $this->process_and_save_pdf( $pdf_generator ) ) { 
  670. $pdf_path = $pdf_generator->get_full_pdf_path(); 
  671.  
  672. if ( is_file( $pdf_path ) ) { 
  673.  
  674. /** Add appropriate filters so developers can access the PDF when it is generated */ 
  675. $form = $this->gform->get_form( $entry['form_id'] ); 
  676. $filename = basename( $pdf_path ); 
  677.  
  678. do_action( 'gfpdf_post_pdf_save', $form['id'], $entry['id'], $settings, $pdf_path ); /** Backwards compatibility */ 
  679.  
  680. /** See https://gravitypdf.com/documentation/v4/gfpdf_post_save_pdf/ for more details about these actions */ 
  681. do_action( 'gfpdf_post_save_pdf', $pdf_path, $filename, $settings, $entry, $form ); 
  682. do_action( 'gfpdf_post_save_pdf_' . $form['id'], $pdf_path, $filename, $settings, $entry, $form ); 
  683.  
  684. return $pdf_path; 
  685.  
  686. return new WP_Error( 'pdf_generation_failure', esc_html__( 'The PDF could not be saved.', 'gravity-forms-pdf-extended' ) ); 
  687.  
  688.  
  689. /** 
  690. * Check if the form has any PDFs, generate them and attach to the notification 
  691. * @param array $notifications Gravity Forms Notification Array 
  692. * @param array $form 
  693. * @param array $entry 
  694. * @return array 
  695. * @since 4.0 
  696. */ 
  697. public function notifications( $notifications, $form, $entry ) { 
  698.  
  699. /** 
  700. * Ensure our entry is stored in the database by checking it has an ID 
  701. * This resolves any issues with the "Save and Continue" feature 
  702. * See https://github.com/GravityPDF/gravity-pdf/issues/360 
  703. */ 
  704. if ( null === $entry['id'] ) { 
  705. return $notifications; 
  706.  
  707. $pdfs = ( isset( $form['gfpdf_form_settings'] ) ) ? $this->get_active_pdfs( $form['gfpdf_form_settings'], $entry ) : []; 
  708.  
  709. if ( sizeof( $pdfs ) > 0 ) { 
  710.  
  711. /** Ensure our notification has an array setup for the attachments key */ 
  712. $notifications['attachments'] = ( isset( $notifications['attachments'] ) ) ? $notifications['attachments'] : []; 
  713.  
  714. /** Loop through each PDF config and generate */ 
  715. foreach ( $pdfs as $settings ) { 
  716.  
  717. /** Reset the variables each loop */ 
  718. $filename = $tier_2_filename = ''; 
  719.  
  720. if ( $this->maybe_attach_to_notification( $notifications, $settings ) ) { 
  721.  
  722. /** Generate our PDF */ 
  723. $filename = $this->generate_and_save_pdf( $entry, $settings ); 
  724.  
  725. if ( ! is_wp_error( $filename ) ) { 
  726. $notifications['attachments'][] = $filename; 
  727.  
  728. $this->log->addNotice( 'Gravity Forms Attachments', [ 
  729. 'attachments' => $notifications['attachments'],  
  730. 'notification' => $notifications,  
  731. ] ); 
  732.  
  733.  
  734. return $notifications; 
  735.  
  736. /** 
  737. * Determine if the PDF should be attached to the current notification 
  738. * @param array $notification The Gravity Form Notification currently being processed 
  739. * @param array $settings The current Gravity PDF Settings 
  740. * @return boolean 
  741. * @since 4.0 
  742. */ 
  743. public function maybe_attach_to_notification( $notification, $settings ) { 
  744.  
  745. if ( isset( $settings['notification'] ) && is_array( $settings['notification'] ) ) { 
  746. if ( in_array( $notification['id'], $settings['notification'] ) ) { 
  747. return true; 
  748.  
  749. return false; 
  750.  
  751. /** 
  752. * Determine if the PDF should be saved to disk 
  753. * @param array $settings The current Gravity PDF Settings 
  754. * @return boolean 
  755. * @since 4.0 
  756. */ 
  757. public function maybe_always_save_pdf( $settings ) { 
  758. if ( isset( $settings['save'] ) && strtolower( $settings['save'] ) == 'yes' ) { 
  759. return true; 
  760.  
  761. return false; 
  762.  
  763. /** 
  764. * Creates a PDF on every submission, except when the PDF is already created during the notification hook 
  765. * @param array $entry The GF Entry Details 
  766. * @param array $form The Gravity Form 
  767. * @return void 
  768. * @since 4.0 
  769. */ 
  770. public function maybe_save_pdf( $entry, $form ) { 
  771. $pdfs = ( isset( $form['gfpdf_form_settings'] ) ) ? $this->get_active_pdfs( $form['gfpdf_form_settings'], $entry ) : []; 
  772.  
  773. if ( sizeof( $pdfs ) > 0 ) { 
  774.  
  775. /** Loop through each PDF config */ 
  776. foreach ( $pdfs as $pdf ) { 
  777. $settings = $this->options->get_pdf( $entry['form_id'], $pdf['id'] ); 
  778.  
  779. /** Only generate if the PDF wasn't created during the notification process */ 
  780. if ( ! is_wp_error( $settings ) && $this->maybe_always_save_pdf( $settings ) ) { 
  781. $this->generate_and_save_pdf( $entry, $settings ); 
  782.  
  783. /** 
  784. * Check if the current PDF to be processed already exists on disk 
  785. * @param \GFPDF\Helper\Helper_PDF $pdf The Helper_PDF Object 
  786. * @return boolean 
  787. * @since 4.0 
  788. */ 
  789. public function does_pdf_exist( Helper_PDF $pdf ) { 
  790.  
  791. if ( is_file( $pdf->get_full_pdf_path() ) ) { 
  792. return true; 
  793.  
  794. return false; 
  795.  
  796. /** 
  797. * To prevent ourt tmp directory getting huge we will clean it up every 24 hours 
  798. * @return void 
  799. * @since 4.0 
  800. */ 
  801. public function cleanup_tmp_dir() { 
  802.  
  803. $max_file_age = 24 * 3600; /** Max age is 24 hours old */ 
  804. $tmp_directory = $this->data->template_tmp_location; 
  805.  
  806. if ( is_dir( $tmp_directory ) ) { 
  807. /** Scan the tmp directory and get a list of files / folders */ 
  808. $directory_list = array_diff( scandir( $tmp_directory ), [ '..', '.', '.htaccess' ] ); 
  809.  
  810. foreach ( $directory_list as $item ) { 
  811. $file = $tmp_directory . $item; 
  812. $directory = false; 
  813.  
  814. /** Fix to allow filemtime to work on directories too */ 
  815. if ( is_dir( $file ) ) { 
  816. $file .= '.'; 
  817. $directory = true; 
  818.  
  819. /** Check if the file is too old and delete file / directory */ 
  820. if ( file_exists( $file ) && filemtime( $file ) < time() - $max_file_age ) { 
  821.  
  822. if ( $directory ) { 
  823. $this->misc->rmdir( substr( $file, 0, -1 ) ); 
  824. } else { 
  825. if ( ! unlink( $file ) ) { 
  826. $this->log->addError( 'Filesystem Delete Error', [ 
  827. 'file' => $file,  
  828. ] ); 
  829.  
  830. /** 
  831. * Remove the generated PDF from the server to save disk space 
  832. * @internal In future we may give the option to cache PDFs to save on processing power 
  833. * @param array $entry The GF Entry Data 
  834. * @param array $form The Gravity Form 
  835. * @return void 
  836. * @since 4.0 
  837. * @todo Add PDF caching support to make software more performant. Need to review correct triggers for a cleanup (API-based, UI actions, 3rd-party add-on compatibility) 
  838. */ 
  839. public function cleanup_pdf( $entry, $form ) { 
  840.  
  841. $pdfs = ( isset( $form['gfpdf_form_settings'] ) ) ? $this->get_active_pdfs( $form['gfpdf_form_settings'], $entry ) : []; 
  842.  
  843. if ( sizeof( $pdfs ) > 0 ) { 
  844.  
  845. /** loop through each PDF config */ 
  846. foreach ( $pdfs as $pdf ) { 
  847. $settings = $this->options->get_pdf( $entry['form_id'], $pdf['id'] ); 
  848.  
  849. /** Only generate if the PDF wasn't during the notification process */ 
  850. if ( ! is_wp_error( $settings ) ) { 
  851.  
  852. $pdf_generator = new Helper_PDF( $entry, $settings, $this->gform, $this->data, $this->misc, $this->templates ); 
  853. $pdf_generator->set_filename( $this->get_pdf_name( $settings, $entry ) ); 
  854.  
  855. if ( $this->does_pdf_exist( $pdf_generator ) ) { 
  856. try { 
  857. $this->misc->rmdir( $pdf_generator->get_path() ); 
  858. } catch ( Exception $e ) { 
  859.  
  860. $this->log->addError( 'Cleanup PDF Error', [ 
  861. 'pdf' => $pdf,  
  862. 'exception' => $e->getMessage(),  
  863. ] ); 
  864.  
  865. /** 
  866. * Triggered after the Gravity Form entry is updated 
  867. * @param array $form 
  868. * @param int $entry_id 
  869. */ 
  870. public function cleanup_pdf_after_submission( $form, $entry_id ) { 
  871. $entry = $this->gform->get_entry( $entry_id ); 
  872. $this->cleanup_pdf( $entry, $form ); 
  873.  
  874. /** 
  875. * Clean-up any PDFs stored on disk before we resend any notifications 
  876. * @param array $form The Gravity Forms object 
  877. * @param array $leads An array of Gravity Form entry IDs 
  878. * @since 4.0 
  879. * @return array We tapped into a filter so we need to return the form object 
  880. */ 
  881. public function resend_notification_pdf_cleanup( $form, $leads ) { 
  882. foreach ( $leads as $entry_id ) { 
  883. $entry = $this->gform->get_entry( $entry_id ); 
  884. $this->cleanup_pdf( $entry, $form ); 
  885.  
  886. return $form; 
  887.  
  888. /** 
  889. * Changes mPDF's tmp folder 
  890. * @param string $path The current path 
  891. * @return string The new path 
  892. */ 
  893. public function mpdf_tmp_path( $path ) { 
  894. return $this->data->template_tmp_location; 
  895.  
  896. /** 
  897. * Changes mPDF's fontdata folders 
  898. * @param string $path The current path 
  899. * @return string The new path 
  900. */ 
  901. public function mpdf_tmp_font_path( $path ) { 
  902. return $this->data->template_fontdata_location; 
  903.  
  904. /** 
  905. * An mPDF filter that checks if mPDF has the font currently installed, otherwise 
  906. * will look in the Gravity PDF font folder for an alternative. 
  907. * @param string $path The current path to the font mPDF is trying to load 
  908. * @param string $font The current font name trying to be loaded 
  909. * @since 4.0 
  910. * @return string 
  911. */ 
  912. public function set_current_pdf_font( $path, $font ) { 
  913.  
  914. /** If the current font doesn't exist in mPDF core we'll look in our font folder */ 
  915. if ( ! is_file( $path ) ) { 
  916.  
  917. if ( is_file( $this->data->template_font_location . $font ) ) { 
  918. $path = $this->data->template_font_location . $font; 
  919.  
  920. return $path; 
  921.  
  922. /** 
  923. * An mPDF filter which will register our custom font data with mPDF 
  924. * @param array $fonts The registered fonts 
  925. * @since 4.0 
  926. * @return array 
  927. */ 
  928. public function register_custom_font_data_with_mPDF( $fonts ) { 
  929.  
  930. $custom_fonts = $this->options->get_custom_fonts(); 
  931.  
  932. foreach ( $custom_fonts as $font ) { 
  933.  
  934. $fonts[ $font['shortname'] ] = array_filter( [ 
  935. 'R' => basename( $font['regular'] ),  
  936. 'B' => basename( $font['bold'] ),  
  937. 'I' => basename( $font['italics'] ),  
  938. 'BI' => basename( $font['bolditalics'] ),  
  939. ] ); 
  940.  
  941. return $fonts; 
  942.  
  943. /** 
  944. * Read all fonts from our fonts directory and auto-load them into mPDF if they are not found 
  945. * @param array $fonts The registered fonts 
  946. * @since 4.0 
  947. * @return array 
  948. */ 
  949. public function add_unregistered_fonts_to_mPDF( $fonts ) { 
  950.  
  951. $user_fonts = glob( $this->data->template_font_location . '*.[tT][tT][fF]' ); 
  952. $user_fonts = ( is_array( $user_fonts ) ) ? $user_fonts : []; 
  953.  
  954. foreach ( $user_fonts as $font ) { 
  955.  
  956. /** Get font shortname */ 
  957. $font_name = basename( $font ); 
  958. $short_name = $this->options->get_font_short_name( substr( $font_name, 0, -4 ) ); 
  959.  
  960. /** Check if it exists already, otherwise add it */ 
  961. if ( ! isset( $fonts[ $short_name ] ) && ! $this->misc->in_array( $font_name, $fonts ) ) { 
  962. $fonts[ $short_name ] = [ 
  963. 'R' => $font_name,  
  964. ]; 
  965.  
  966. return $fonts; 
  967.  
  968. /** 
  969. * Generates our $data array 
  970. * @param array $entry The Gravity Form Entry 
  971. * @return array The $data array 
  972. * @since 4.0 
  973. */ 
  974. public function get_form_data( $entry ) { 
  975.  
  976. if ( ! isset( $entry['form_id']) ) { 
  977. return []; 
  978.  
  979. $form = $this->gform->get_form( $entry['form_id'] ); 
  980.  
  981. if( ! is_array( $form ) ) { 
  982. return []; 
  983.  
  984. /** Setup our basic structure */ 
  985. $data = [ 
  986. 'misc' => [],  
  987. 'field' => [],  
  988. 'field_descriptions' => [],  
  989. ]; 
  990.  
  991. /** 
  992. * Create a product class for use 
  993. * @var Field_Products 
  994. */ 
  995. $products = new Field_Products( new GF_Field(), $entry, $this->gform, $this->misc ); 
  996.  
  997. /** Get the form details */ 
  998. $form_meta = $this->get_form_data_meta( $form, $entry ); 
  999.  
  1000. /** Get the survey, quiz and poll data if applicable */ 
  1001. $quiz = $this->get_quiz_results( $form, $entry ); 
  1002. $survey = $this->get_survey_results( $form, $entry ); 
  1003. $poll = $this->get_poll_results( $form, $entry ); 
  1004.  
  1005. /** Merge in the meta data and survey, quiz and poll data */ 
  1006. $data = array_replace_recursive( $data, $form_meta, $quiz, $survey, $poll ); 
  1007.  
  1008. /** 
  1009. * Loop through the form data, call the correct field object and 
  1010. * save the data to our $data array 
  1011. */ 
  1012. if ( isset( $form['fields'] ) ) { 
  1013. foreach ( $form['fields'] as $field ) { 
  1014.  
  1015. /** Skip over captcha, password and page fields */ 
  1016. $fields_to_skip = apply_filters( 'gfpdf_form_data_skip_fields', [ 
  1017. 'captcha',  
  1018. 'password',  
  1019. 'page',  
  1020. ] ); 
  1021.  
  1022. if ( in_array( $field->type, $fields_to_skip ) ) { 
  1023. continue; 
  1024.  
  1025. /** Include any field descriptions */ 
  1026. $data['field_descriptions'][ $field->id ] = ( ! empty( $field->description ) ) ? $field->description : ''; 
  1027.  
  1028. /** Get our field object */ 
  1029. $class = $this->get_field_class( $field, $form, $entry, $products ); 
  1030.  
  1031. /** Merge in the field object form_data() results */ 
  1032. $data = array_replace_recursive( $data, $class->form_data() ); 
  1033.  
  1034. /** Load our product array if products exist */ 
  1035. if ( ! $products->is_empty() ) { 
  1036. $data = array_replace_recursive( $data, $products->form_data() ); 
  1037.  
  1038. /** Re-order the array keys to make it more readable */ 
  1039. $order = [ 
  1040. 'misc',  
  1041. 'field',  
  1042. 'list',  
  1043. 'signature_details_id',  
  1044. 'products',  
  1045. 'products_totals',  
  1046. 'poll',  
  1047. 'survey',  
  1048. 'quiz',  
  1049. 'pages',  
  1050. 'html_id',  
  1051. 'section_break',  
  1052. 'field_descriptions',  
  1053. 'signature',  
  1054. 'signature_details',  
  1055. 'html',  
  1056. ]; 
  1057.  
  1058. foreach ( $order as $key ) { 
  1059.  
  1060. /** If item exists pop it onto the end of the array */ 
  1061. if ( isset( $data[ $key ] ) ) { 
  1062. $item = $data[ $key ]; 
  1063. unset( $data[ $key ] ); 
  1064. $data[ $key ] = $item; 
  1065.  
  1066. $this->log->addNotice( 'Form Data Array Created', [ 
  1067. 'data' => $data,  
  1068. ] ); 
  1069.  
  1070. return $data; 
  1071.  
  1072. /** 
  1073. * Return our general $data information 
  1074. * @param array $form The Gravity Form 
  1075. * @param array $entry The Gravity Form Entry 
  1076. * @return array The $data array 
  1077. * @since 4.0 
  1078. */ 
  1079. public function get_form_data_meta( $form, $entry ) { 
  1080. $data = []; 
  1081.  
  1082. /** Add form_id and entry_id for convinience */ 
  1083. $data['form_id'] = $entry['form_id']; 
  1084. $data['entry_id'] = $entry['id']; 
  1085.  
  1086. /** Set title and dates (both US and international) */ 
  1087. $data['form_title'] = ( isset( $form['title'] ) ) ? $form['title'] : ''; 
  1088.  
  1089. $data['form_description'] = ( isset( $form['description'] ) ) ? $form['description'] : ''; 
  1090. $data['date_created'] = GFCommon::format_date( $entry['date_created'], false, 'j/n/Y', false ); 
  1091. $data['date_created_usa'] = GFCommon::format_date( $entry['date_created'], false, 'n/j/Y', false ); 
  1092.  
  1093. /** Include page names */ 
  1094. $data['pages'] = ( isset( $form['pagination']['pages'] ) ? $form['pagination']['pages'] : [] ); 
  1095.  
  1096. /** Add misc fields */ 
  1097. $data['misc']['date_time'] = GFCommon::format_date( $entry['date_created'], false, 'Y-m-d H:i:s', false ); 
  1098. $data['misc']['time_24hr'] = GFCommon::format_date( $entry['date_created'], false, 'H:i', false ); 
  1099. $data['misc']['time_12hr'] = GFCommon::format_date( $entry['date_created'], false, 'g:ia', false ); 
  1100.  
  1101. $include = [ 
  1102. 'is_starred',  
  1103. 'is_read',  
  1104. 'ip',  
  1105. 'source_url',  
  1106. 'post_id',  
  1107. 'currency',  
  1108. 'payment_status',  
  1109. 'payment_date',  
  1110. 'transaction_id',  
  1111. 'payment_amount',  
  1112. 'is_fulfilled',  
  1113. 'created_by',  
  1114. 'transaction_type',  
  1115. 'user_agent',  
  1116. 'status',  
  1117. ]; 
  1118.  
  1119. foreach ( $include as $item ) { 
  1120. $data['misc'][ $item ] = ( isset( $entry[ $item ] ) ) ? $entry[ $item ] : ''; 
  1121.  
  1122. return $data; 
  1123.  
  1124. /** 
  1125. * Pull the Survey Results into the $form_data array 
  1126. * @param array $form The Gravity Form 
  1127. * @param array $entry The Gravity Form Entry 
  1128. * @return array The results 
  1129. * @since 4.0 
  1130. */ 
  1131. public function get_survey_results( $form, $entry ) { 
  1132.  
  1133. $data = []; 
  1134.  
  1135. if ( class_exists( 'GFSurvey' ) && $this->check_field_exists( 'survey', $form ) ) { 
  1136.  
  1137. /** Get survey fields */ 
  1138. $fields = GFCommon::get_fields_by_type( $form, [ 'survey' ] ); 
  1139.  
  1140. /** Include the survey score, if any */ 
  1141. if ( isset( $lead['gsurvey_score'] ) ) { 
  1142. $data['survey']['score'] = $lead['gsurvey_score']; 
  1143.  
  1144. $results = $this->get_addon_global_data( $form, [], $fields ); 
  1145.  
  1146. /** Loop through the global survey data and convert information correctly */ 
  1147. foreach ( $fields as $field ) { 
  1148.  
  1149. /** Check if we have a multifield likert and replace the row key */ 
  1150. if ( isset( $field['gsurveyLikertEnableMultipleRows'] ) && $field['gsurveyLikertEnableMultipleRows'] == 1 ) { 
  1151.  
  1152. foreach ( $field['gsurveyLikertRows'] as $row ) { 
  1153.  
  1154. $results['field_data'][ $field->id ] = $this->replace_key( $results['field_data'][ $field->id ], $row['value'], $row['text'] ); 
  1155.  
  1156. if ( isset( $field->choices ) && is_array( $field->choices ) ) { 
  1157. foreach ( $field->choices as $choice ) { 
  1158. $results['field_data'][ $field->id ][ $row['text'] ] = $this->replace_key( $results['field_data'][ $field->id ][ $row['text'] ], $choice['value'], $choice['text'] ); 
  1159.  
  1160. /** Replace the standard row data */ 
  1161. if ( isset( $field->choices ) && is_array( $field->choices ) ) { 
  1162. foreach ( $field->choices as $choice ) { 
  1163. $results['field_data'][ $field->id ] = $this->replace_key( $results['field_data'][ $field->id ], $choice['value'], $choice['text'] ); 
  1164.  
  1165. $data['survey']['global'] = $results; 
  1166.  
  1167. return $data; 
  1168.  
  1169. /** 
  1170. * Pull the Quiz Results into the $form_data array 
  1171. * @param array $form The Gravity Form 
  1172. * @param array $entry The Gravity Form Entry 
  1173. * @return array The results 
  1174. * @since 4.0 
  1175. */ 
  1176. public function get_quiz_results( $form, $entry ) { 
  1177.  
  1178. $data = []; 
  1179.  
  1180. if ( class_exists( 'GFQuiz' ) && $this->check_field_exists( 'quiz', $form ) ) { 
  1181.  
  1182. /** Get quiz fields */ 
  1183. $fields = GFCommon::get_fields_by_type( $form, [ 'quiz' ] ); 
  1184.  
  1185. /** Store the quiz pass configuration */ 
  1186. $data['quiz']['config']['grading'] = ( isset( $form['gravityformsquiz']['grading'] ) ) ? $form['gravityformsquiz']['grading'] : ''; 
  1187. $data['quiz']['config']['passPercent'] = ( isset( $form['gravityformsquiz']['passPercent'] ) ) ? $form['gravityformsquiz']['passPercent'] : ''; 
  1188. $data['quiz']['config']['grades'] = ( isset( $form['gravityformsquiz']['grades'] ) ) ? $form['gravityformsquiz']['grades'] : ''; 
  1189.  
  1190. /** Store the user's quiz results */ 
  1191. $data['quiz']['results']['score'] = rgar( $entry, 'gquiz_score' ); 
  1192. $data['quiz']['results']['percent'] = rgar( $entry, 'gquiz_percent' ); 
  1193. $data['quiz']['results']['is_pass'] = rgar( $entry, 'gquiz_is_pass' ); 
  1194. $data['quiz']['results']['grade'] = rgar( $entry, 'gquiz_grade' ); 
  1195.  
  1196. /** Poll for the global quiz overall results */ 
  1197. $data['quiz']['global'] = $this->get_quiz_overall_data( $form, $fields ); 
  1198.  
  1199.  
  1200. return $data; 
  1201.  
  1202. /** 
  1203. * Pull the Poll Results into the $form_data array 
  1204. * @param array $form The Gravity Form 
  1205. * @param array $entry The Gravity Form Entry 
  1206. * @return array The results 
  1207. * @since 4.0 
  1208. */ 
  1209. public function get_poll_results( $form, $entry ) { 
  1210.  
  1211. $data = []; 
  1212.  
  1213. if ( class_exists( 'GFPolls' ) && $this->check_field_exists( 'poll', $form ) ) { 
  1214.  
  1215. /** Get poll fields and the overall results */ 
  1216. $fields = GFCommon::get_fields_by_type( $form, [ 'poll' ] ); 
  1217. $results = $this->get_addon_global_data( $form, [], $fields ); 
  1218.  
  1219. /** Loop through our fields and update the results as needed */ 
  1220. foreach ( $fields as $field ) { 
  1221.  
  1222. /** Add the field name to a new 'misc' array key */ 
  1223. $results['field_data'][ $field->id ]['misc']['label'] = $field->label; 
  1224.  
  1225. /** Loop through the field choices */ 
  1226. foreach ( $field->choices as $choice ) { 
  1227. $results['field_data'][ $field->id ] = $this->replace_key( $results['field_data'][ $field->id ], $choice['value'], $choice['text'] ); 
  1228.  
  1229. $data['poll']['global'] = $results; 
  1230.  
  1231. return $data; 
  1232.  
  1233. /** 
  1234. * Parse the Quiz Overall Results 
  1235. * @param array $form The Gravity Form 
  1236. * @param array $fields The quiz fields 
  1237. * @return array The parsed results 
  1238. * @since 4.0 
  1239. */ 
  1240. public function get_quiz_overall_data( $form, $fields ) { 
  1241.  
  1242. if ( ! class_exists( 'GFQuiz' ) ) { 
  1243. return []; 
  1244.  
  1245. /** GFQuiz is a singleton. Get the instance */ 
  1246. $quiz = GFQuiz::get_instance(); 
  1247.  
  1248. /** Create our callback to add additional data to the array specific to the quiz plugin */ 
  1249. $options['callbacks']['calculation'] = [ 
  1250. $quiz,  
  1251. 'results_calculation',  
  1252. ]; 
  1253.  
  1254. $results = $this->get_addon_global_data( $form, $options, $fields ); 
  1255.  
  1256. /** Loop through our fields and update our global results */ 
  1257. foreach ( $fields as $field ) { 
  1258.  
  1259. /** Replace ['totals'] key with ['misc'] key */ 
  1260. $results['field_data'][ $field->id ] = $this->replace_key( $results['field_data'][ $field->id ], 'totals', 'misc' ); 
  1261.  
  1262. /** Add the field name to the ['misc'] key */ 
  1263. $results['field_data'][ $field->id ]['misc']['label'] = $field->label; 
  1264.  
  1265. /** Loop through the field choices */ 
  1266. if ( is_array( $field->choices ) ) { 
  1267. foreach ( $field->choices as $choice ) { 
  1268. $results['field_data'][ $field->id ] = $this->replace_key( $results['field_data'][ $field->id ], $choice['value'], $choice['text'] ); 
  1269.  
  1270. /** Check if this is the correct field */ 
  1271. if ( isset( $choice['gquizIsCorrect'] ) && $choice['gquizIsCorrect'] == 1 ) { 
  1272. $results['field_data'][ $field->id ]['misc']['correct_option_name'][] = esc_html( $choice['text'] ); 
  1273.  
  1274. return $results; 
  1275.  
  1276. /** 
  1277. * Pull Gravity Forms global results Data 
  1278. * @param array $form The Gravity Form array 
  1279. * @param array $options The global query options 
  1280. * @param array $fields The field array to use in our query 
  1281. * @return array The results 
  1282. * @since 4.0 
  1283. */ 
  1284. private function get_addon_global_data( $form, $options, $fields ) { 
  1285. /** If the results class isn't loaded, load it */ 
  1286. if ( ! class_exists( 'GFResults' ) ) { 
  1287. require_once( GFCommon::get_base_path() . '/includes/addon/class-gf-results.php' ); 
  1288.  
  1289. $form_id = $form['id']; 
  1290.  
  1291. /** Add form filter to keep in line with GF standard */ 
  1292. $form = apply_filters( 'gform_form_pre_results', $form ); 
  1293. $form = apply_filters( 'gform_form_pre_results_' . $form_id, $form ); 
  1294.  
  1295. /** Initiate the results class */ 
  1296. $gf_results = new GFResults( '', $options ); 
  1297.  
  1298. /** Ensure that only active leads are queried */ 
  1299. $search = [ 
  1300. 'field_filters' => [ 'mode' => '' ],  
  1301. 'status' => 'active',  
  1302. ]; 
  1303.  
  1304. /** Get the results */ 
  1305. $data = $gf_results->get_results_data( $form, $fields, $search ); 
  1306.  
  1307. /** Unset some array keys we don't need */ 
  1308. unset( $data['status'] ); 
  1309. unset( $data['timestamp'] ); 
  1310.  
  1311. return $data; 
  1312.  
  1313. /** 
  1314. * Sniff the form fields and determine if there are any of the $type available 
  1315. * @param string $type the field type we are looking for 
  1316. * @param array $form the form array 
  1317. * @return boolean Whether there is a match or not 
  1318. * @since 4.0 
  1319. */ 
  1320. public function check_field_exists( $type, $form ) { 
  1321.  
  1322. if ( isset( $form['fields'] ) ) { 
  1323. foreach ( $form['fields'] as $field ) { 
  1324. if ( $field['type'] == $type ) { 
  1325. return true; 
  1326.  
  1327. return false; 
  1328.  
  1329. /** 
  1330. * Swap out the array key 
  1331. * @param array $array The array to be modified 
  1332. * @param string $key The key to remove 
  1333. * @param string $replacement_key The new array key 
  1334. * @return array The modified array 
  1335. * @since 4.0 
  1336. */ 
  1337. public function replace_key( $array, $key, $replacement_key ) { 
  1338. if ( $key !== $replacement_key && isset( $array[ $key ] ) ) { 
  1339.  
  1340. /** Replace the array key with the actual field name */ 
  1341. $array[ $replacement_key ] = $array[ $key ]; 
  1342. unset( $array[ $key ] ); 
  1343.  
  1344. return $array; 
  1345.  
  1346. /** 
  1347. * Pass in a Gravity Form Field Object and get back a Gravity PDF Field Object 
  1348. * @param object $field Gravity Form Field Object 
  1349. * @param array $form The Gravity Form Array 
  1350. * @param array $entry The Gravity Form Entry 
  1351. * @param \GFPDF\Helper\Fields\Field_Products $products A Field_Products Object 
  1352. * @return object Gravity PDF Field Object 
  1353. * @since 4.0 
  1354. */ 
  1355. public function get_field_class( $field, $form, $entry, Field_Products $products ) { 
  1356.  
  1357. $class_name = $this->misc->get_field_class( $field->type ); 
  1358.  
  1359. try { 
  1360. /** if we have a valid class name... */ 
  1361. if ( class_exists( $class_name ) ) { 
  1362.  
  1363. /** 
  1364. * Developer Note 
  1365. * We've purposefully not added any filters to the Field_* child classes directly. 
  1366. * Instead, if you want to change how one of the fields are displayed or output (without effecting Gravity Forms itself) you should tap 
  1367. * into one of the filters below and override or extend the entire class. 
  1368. * Your class MUST extend the \GFPDF\Helper\Helper_Abstract_Fields abstract class - either directly or by extending an existing \GFPDF\Helper\Fields class. 
  1369. * eg. class Fields_New_Text extends \GFPDF\Helper\Helper_Abstract_Fields or Fields_New_Text extends \GFPDF\Helper\Fields\Field_Text 
  1370. * To make your life more simple you should either use the same namespace as the field classes (\GFPDF\Helper\Fields) or import the class directly (use \GFPDF\Helper\Fields\Field_Text) 
  1371. * We've tried to make the fields as modular as possible. If you have any feedback about this approach please submit a ticket on GitHub (https://github.com/GravityPDF/gravity-pdf/issues) 
  1372. */ 
  1373. if ( GFCommon::is_product_field( $field->type ) ) { 
  1374.  
  1375. /** Product fields are handled through a single function */ 
  1376. $product = new Field_Product( $field, $entry, $this->gform, $this->misc ); 
  1377. $product->set_products( $products ); 
  1378.  
  1379. $class = apply_filters( 'gfpdf_field_product_class', $product, $field, $entry, $form ); 
  1380. } else { 
  1381. /** 
  1382. * Load the selected class 
  1383. * See https://gravitypdf.com/documentation/v4/gfpdf_field_class/ for more details about these filters 
  1384. */ 
  1385. $class = apply_filters( 'gfpdf_field_class', new $class_name( $field, $entry, $this->gform, $this->misc ), $field, $entry, $form ); 
  1386. $class = apply_filters( 'gfpdf_field_class_' . $field->type, $class, $field, $entry, $form ); 
  1387.  
  1388. if ( empty( $class ) || ! ( $class instanceof Helper_Abstract_Fields ) ) { 
  1389. throw new Exception( 'Class not found' ); 
  1390. } catch ( Exception $e ) { 
  1391.  
  1392. $this->log->addError( 'Invalid Field Class.', [ 
  1393. 'exception' => $e->getMessage(),  
  1394. 'field' => $field,  
  1395. 'form_id' => $form['id'],  
  1396. 'entry' => $entry,  
  1397. ] ); 
  1398.  
  1399. /** Exception thrown. Load generic field loader */ 
  1400. $class = apply_filters( 'gfpdf_field_default_class', new Field_Default( $field, $entry, $this->gform, $this->misc ), $field, $entry, $form ); 
  1401.  
  1402. return $class; 
  1403.  
  1404. /** 
  1405. * Attempts to find a configuration which matches the legacy routing method 
  1406. * @param array $config 
  1407. * @return mixed 
  1408. * @since 4.0 
  1409. */ 
  1410. public function get_legacy_config( $config ) { 
  1411.  
  1412. /** Get the form settings */ 
  1413. $pdfs = $this->options->get_form_pdfs( $config['fid'] ); 
  1414.  
  1415. if ( is_wp_error( $pdfs ) ) { 
  1416. return $pdfs; 
  1417.  
  1418. /** Reindex the $pdfs keys */ 
  1419. $pdfs = array_values( $pdfs ); 
  1420.  
  1421. /** Use the legacy aid to determine which PDF to load */ 
  1422. if ( $config['aid'] !== false ) { 
  1423. $selector = $config['aid'] - 1; 
  1424.  
  1425. if ( isset( $pdfs[ $selector ] ) && $pdfs[ $selector ]['template'] == $config['template'] ) { 
  1426. return $pdfs[ $selector ]['id']; 
  1427.  
  1428. /** The aid method failed so lets load the first matching configuration */ 
  1429. foreach ( $pdfs as $pdf ) { 
  1430. if ( $pdf['active'] === true && $pdf['template'] == $config['template'] ) { 
  1431. return $pdf['id']; 
  1432.  
  1433. return new WP_Error( 'pdf_configuration_error', esc_html__( 'Could not find PDF configuration requested', 'gravity-forms-pdf-extended' ) ); 
  1434.  
  1435. /** 
  1436. * Do any preprocessing to our arguments before they are sent to the template 
  1437. * @param array $args 
  1438. * @return array 
  1439. * @since 4.0 
  1440. */ 
  1441. public function preprocess_template_arguments( $args ) { 
  1442.  
  1443. if ( isset( $args['settings']['header'] ) ) { 
  1444. $args['settings']['header'] = $this->gform->process_tags( $args['settings']['header'], $args['form'], $args['entry'] ); 
  1445. $args['settings']['header'] = $this->misc->fix_header_footer( $args['settings']['header'] ); 
  1446.  
  1447. if ( isset( $args['settings']['first_header'] ) ) { 
  1448. $args['settings']['first_header'] = $this->gform->process_tags( $args['settings']['first_header'], $args['form'], $args['entry'] ); 
  1449. $args['settings']['first_header'] = $this->misc->fix_header_footer( $args['settings']['first_header'] ); 
  1450.  
  1451. if ( isset( $args['settings']['footer'] ) ) { 
  1452. $args['settings']['footer'] = $this->gform->process_tags( $args['settings']['footer'], $args['form'], $args['entry'] ); 
  1453. $args['settings']['footer'] = $this->misc->fix_header_footer( $args['settings']['footer'] ); 
  1454.  
  1455. if ( isset( $args['settings']['first_footer'] ) ) { 
  1456. $args['settings']['first_footer'] = $this->gform->process_tags( $args['settings']['first_footer'], $args['form'], $args['entry'] ); 
  1457. $args['settings']['first_footer'] = $this->misc->fix_header_footer( $args['settings']['first_footer'] ); 
  1458.  
  1459. return $args;