GFPDFHelperHelper_Migration

Class to assist with migrations.

Defined (1)

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

/src/helper/Helper_Migration.php  
  1. class Helper_Migration { 
  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. * Holds our Helper_Abstract_Options / Helper_Options_Fields object 
  27. * Makes it easy to access global PDF settings and individual form PDF settings 
  28. * @var \GFPDF\Helper\Helper_Options_Fields 
  29. * @since 4.0 
  30. */ 
  31. protected $options; 
  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. * Load our model and view and required actions 
  59. * @param \GFPDF\Helper\Helper_Abstract_Form $form 
  60. * @param \Monolog\Logger|LoggerInterface $log 
  61. * @param \GFPDF\Helper\Helper_Data $data 
  62. * @param \GFPDF\Helper\Helper_Abstract_Options $options 
  63. * @param \GFPDF\Helper\Helper_Misc $misc 
  64. * @param \GFPDF\Helper\Helper_Notices $notices 
  65. * @param \GFPDF\Helper\Helper_Templates $templates 
  66. * @since 4.0 
  67. */ 
  68. public function __construct( Helper_Abstract_Form $gform, LoggerInterface $log, Helper_Data $data, Helper_Abstract_Options $options, 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->data = $data; 
  74. $this->options = $options; 
  75. $this->misc = $misc; 
  76. $this->notices = $notices; 
  77. $this->templates = $templates; 
  78.  
  79. /** 
  80. * Process our v3 to v4 migration 
  81. * @return boolean 
  82. * @since 4.0 
  83. */ 
  84. public function begin_migration() { 
  85.  
  86. /** Load our configuration file */ 
  87. try { 
  88. $raw_config = $this->load_old_configuration(); 
  89. } catch ( Exception $e ) { 
  90.  
  91. $this->log->addError( 'Migration Error', [ 
  92. 'exception' => $e->getMessage(),  
  93. ] ); 
  94.  
  95. $this->notices->add_error( esc_html__( 'There was a problem processing the action. Please try again.', 'gravity-forms-pdf-extended' ) ); 
  96.  
  97. return false; 
  98.  
  99. /** Convert our v3 config into our v4 format and merge in the defaults */ 
  100. $v4_config = $this->convert_v3_to_v4( $raw_config ); 
  101. $v4_config = $this->process_default_configuration( $v4_config ); 
  102.  
  103. /** Index configuration by form ID */ 
  104. $config = $this->process_v3_configuration( $v4_config ); 
  105.  
  106. /** Import the configuration into the database */ 
  107. $this->import_v3_config( $config ); 
  108.  
  109. /** Migrate fonts for multisite */ 
  110. $this->migrate_multisite_fonts(); 
  111.  
  112. /** Clean-up the old 'output' directory as we use 'tmp' now */ 
  113. $this->cleanup_output_directory(); 
  114.  
  115. /** Remove the old font config.php file */ 
  116. $this->cleanup_font_config(); 
  117.  
  118. return true; 
  119.  
  120. /** 
  121. * Load our v3 configuration 
  122. * @return array 
  123. * @throws Exception 
  124. * @since 4.0 
  125. */ 
  126. private function load_old_configuration() { 
  127.  
  128. $path = $this->templates->get_template_path(); 
  129.  
  130. /** Import our configuration files */ 
  131. if ( is_file( $path . 'configuration.php' ) ) { 
  132. require_once( $path . 'configuration.php' ); 
  133. } else { 
  134. throw new Exception( 'Could not locate v3 configuration file.' ); 
  135.  
  136. return [ 
  137. 'default' => ( isset( $gf_pdf_default_configuration ) && is_array( $gf_pdf_default_configuration ) ) ? $gf_pdf_default_configuration : [],  
  138. 'config' => ( isset( $gf_pdf_config ) && is_array( $gf_pdf_config ) ) ? $gf_pdf_config : [],  
  139. ]; 
  140.  
  141. /** 
  142. * Process v3 config into our v4 
  143. * @param array $raw_config The config data loaded from our v3 configuration file 
  144. * @return array 
  145. * @since 4.0 
  146. */ 
  147. private function convert_v3_to_v4( $raw_config ) { 
  148.  
  149. $migration_key = [ 
  150. 'notifications' => 'notification',  
  151. 'premium' => 'advanced_template',  
  152. 'access' => 'public_access',  
  153. 'dpi' => 'image_dpi',  
  154. 'pdf_password' => 'password',  
  155. 'pdf_privileges' => 'privileges',  
  156. 'pdf_master_password' => 'master_password',  
  157. 'default-show-html' => 'show_html',  
  158. 'default-show-empty' => 'show_empty',  
  159. 'default-show-page-names' => 'show_page_names',  
  160. 'default-show-section-content' => 'show_section_content',  
  161. ]; 
  162.  
  163. foreach ( $raw_config['config'] as &$node ) { 
  164. $node = $this->process_individual_v3_nodes( $node, $migration_key ); 
  165.  
  166. $raw_config['default'] = $this->process_individual_v3_nodes( $raw_config['default'], $migration_key ); 
  167.  
  168. return $raw_config; 
  169.  
  170. /** 
  171. * Pass in an individual v3 configuration node and conver to our v4 format 
  172. * @param array $node The configuration to be converted 
  173. * @param array $migration_key A migration mapping key to convert the previous config keys 
  174. * @return array 
  175. * @since 4.0 
  176. */ 
  177. private function process_individual_v3_nodes( $node, $migration_key = [] ) { 
  178.  
  179. /** Handle PDFA1B and PDFX1A separately */ 
  180. if ( isset( $node['pdfa1b'] ) && $node['pdfa1b'] === true ) { 
  181. unset( $node['pdfa1b'] ); 
  182. $node['format'] = 'PDFA1B'; 
  183.  
  184. if ( isset( $node['pdfx1a'] ) && $node['pdfx1a'] === true ) { 
  185. unset( $node['pdfx1a'] ); 
  186. $node['format'] = 'PDFX1A'; 
  187.  
  188. if ( ! isset( $node['format'] ) ) { 
  189. $node['format'] = 'Standard'; 
  190.  
  191. /** Fix the public access key */ 
  192. if ( isset( $node['access'] ) ) { 
  193. $node['access'] = ( $node['access'] == 'all' ) ? 'Yes' : 'No'; 
  194.  
  195. /** Remove .php from the template file */ 
  196. if ( isset( $node['template'] ) ) { 
  197. $node['template'] = $this->misc->remove_extension_from_string( $node['template'], '.php' ); 
  198.  
  199. /** Remove .pdf from the filename */ 
  200. if ( isset( $node['filename'] ) ) { 
  201. $node['filename'] = $this->misc->remove_extension_from_string( $node['filename'] ); 
  202.  
  203. /** Fix up our custom PDF size */ 
  204. if ( isset( $node['pdf_size'] ) && is_array( $node['pdf_size'] ) ) { 
  205.  
  206. /** Ensure it's in the correct format */ 
  207. if ( sizeof( $node['pdf_size'] ) == 2 ) { 
  208. $node['pdf_size'][0] = (int) $node['pdf_size'][0]; 
  209. $node['pdf_size'][1] = (int) $node['pdf_size'][1]; 
  210. $node['pdf_size'][2] = 'millimeters'; 
  211.  
  212. $node['custom_pdf_size'] = $node['pdf_size']; 
  213. $node['pdf_size'] = 'CUSTOM'; 
  214. } else { 
  215. unset( $node['pdf_size'] ); 
  216. } else if ( isset( $node['pdf_size'] ) && ! is_array( $node['pdf_size'] ) ) { 
  217. $node['pdf_size'] = mb_strtoupper( $node['pdf_size'] ); 
  218.  
  219. /** Loop through each array key */ 
  220. foreach ( $node as $id => &$val ) { 
  221.  
  222. /** Convert our boolean values into 'Yes' or 'No' responses, with the exception of notification */ 
  223. $skip_nodes = [ 'notifications', 'notification' ]; 
  224. if ( ! in_array( $id, $skip_nodes ) ) { 
  225. $val = $this->misc->update_deprecated_config( $val ); 
  226.  
  227. /** Convert to our v4 configuration names */ 
  228. if ( isset( $migration_key[ $id ] ) ) { 
  229. unset( $node[ $id ] ); 
  230. $node[ $migration_key[ $id ] ] = $val; 
  231.  
  232. return $node; 
  233.  
  234. /** 
  235. * Process v3 config into an acceptable format 
  236. * @param array $raw_config The config data loaded from our v3 configuration file 
  237. * @return array 
  238. * @since 4.0 
  239. */ 
  240. private function process_v3_configuration( $raw_config ) { 
  241.  
  242. if ( ! is_array( $raw_config['config'] ) || sizeof( $raw_config['config'] ) == 0 ) { 
  243. return []; 
  244.  
  245. /** Store configuration by form ID */ 
  246. $config_by_fid = []; 
  247.  
  248. foreach ( $raw_config['config'] as $node ) { 
  249.  
  250. /** If set, merge in our defaults first */ 
  251. if ( ! defined( 'GFPDF_SET_DEFAULT_TEMPLATE' ) || GFPDF_SET_DEFAULT_TEMPLATE === true ) { 
  252. $node = $this->merge_defaults( $raw_config['default'], $node ); 
  253.  
  254. if ( is_array( $node['form_id'] ) ) { 
  255. foreach ( $node['form_id'] as $id ) { 
  256. $id = (int) $id; 
  257.  
  258. if ( $id ) { 
  259. $new_node = $node; 
  260. unset( $new_node['form_id'] ); 
  261. $config_by_fid[ $id ][] = $new_node; 
  262. } else { 
  263. $id = (int) $node['form_id']; 
  264.  
  265. if ( $id ) { 
  266. unset( $node['form_id'] ); 
  267. $config_by_fid[ $id ][] = $node; 
  268.  
  269. return $config_by_fid; 
  270.  
  271. /** 
  272. * Add the default configuration to any missing forms 
  273. * @param array $raw_config The semi-processed configuration 
  274. * @return array 
  275. * @since 4.0 
  276. */ 
  277. private function process_default_configuration( $raw_config ) { 
  278.  
  279. /** Only handle when enabled */ 
  280. if ( ( ! defined( 'GFPDF_SET_DEFAULT_TEMPLATE' ) || GFPDF_SET_DEFAULT_TEMPLATE === true ) && sizeof( $raw_config['default'] ) > 0 ) { 
  281.  
  282. /** Get all forms */ 
  283. $forms = $this->gform->get_forms(); 
  284.  
  285. /** Create an index of current form IDs */ 
  286. $form_ids = []; 
  287. foreach ( $raw_config['config'] as $config ) { 
  288.  
  289. if ( is_array( $config['form_id'] ) ) { 
  290. foreach ( $config['form_id'] as $fid ) { 
  291. $form_ids[ $fid ] = 1; 
  292. } else { 
  293. $form_ids[ $config['form_id'] ] = 1; 
  294.  
  295. /** Loop through all forms and merge in defaults */ 
  296. foreach ( $forms as $form ) { 
  297.  
  298. /** If nothing exists we'll merge in our default parameters */ 
  299. if ( ! isset( $form_ids[ $form['id'] ] ) ) { 
  300.  
  301. $new_config = array_merge( $raw_config['default'], [ 'form_id' => $form['id'] ] ); 
  302. $raw_config['config'][] = $new_config; 
  303.  
  304. return $raw_config; 
  305.  
  306. /** 
  307. * Merge the configuration node with the default options, ensuring the config node takes precendent 
  308. * @param array $defaults The default data loaded from our v3 configuration file 
  309. * @param array $node The individual PDF node 
  310. * @return array 
  311. * @since 4.0 
  312. */ 
  313. private function merge_defaults( $defaults, $node ) { 
  314.  
  315. /** If the default settings are set we'll merge them into the configuration index */ 
  316. if ( is_array( $defaults ) && is_array( $node ) ) { 
  317. $node = array_replace_recursive( $defaults, $node ); 
  318.  
  319. return $node; 
  320.  
  321. /** 
  322. * Import the v3 configuration into the database 
  323. * @param array $config The config data loaded from our v3 configuration file 
  324. * @return array 
  325. * @since 4.0 
  326. */ 
  327. private function import_v3_config( $config ) { 
  328.  
  329. $errors = []; 
  330.  
  331. /** Loop through forms and attempt to get the form data */ 
  332. foreach ( $config as $form_id => $nodes ) { 
  333. $form = $this->gform->get_form( $form_id ); 
  334.  
  335. if ( ! is_wp_error( $form ) ) { 
  336.  
  337. /** Get an array of all the form notification for later use */ 
  338. $notifications = []; 
  339.  
  340. /** Filter out the save and continue notifications */ 
  341. $omit = [ 'form_saved', 'form_save_email_requested' ]; 
  342.  
  343. foreach ( $form['notifications'] as $notification ) { 
  344. $event = ( isset( $notification['event'] ) ) ? $notification['event'] : ''; 
  345.  
  346. if ( ! in_array( $event, $omit ) ) { 
  347. $notifications[ $notification['id'] ] = $notification['name']; 
  348.  
  349. /** Hold name in array so we can prevent duplicates */ 
  350. $name = []; 
  351.  
  352. /** Loop through the nodes and add to our form array */ 
  353. foreach ( $nodes as $node ) { 
  354.  
  355. /** Skip any nodes which don't have a template */ 
  356. if ( empty( $node['template'] ) ) { 
  357. continue; 
  358.  
  359. /** Set our default fields */ 
  360. $node['id'] = uniqid(); 
  361. $node['active'] = true; 
  362. $node['name'] = $this->templates->human_readable_template_name( $node['template'] ); 
  363. $node['conditionalLogic'] = ''; 
  364.  
  365.  
  366. /** Include a filename if none given */ 
  367. if ( empty( $node['filename'] ) ) { 
  368. $node['filename'] = 'form-{form_id}-entry-{entry_id}'; 
  369.  
  370. /** Prevent duplicate names by adding a number to the end of the name */ 
  371. if ( isset( $name[ $node['name'] ] ) ) { 
  372. $original_name = $node['name']; 
  373. $node['name'] .= ' #' . $name[ $node['name'] ]; 
  374. $name[ $original_name ]++; 
  375. } else { 
  376. $name[ $node['name'] ] = 1; 
  377.  
  378. /** Update all notification and pull correct IDs into new array */ 
  379. if ( isset( $node['notification'] ) ) { 
  380.  
  381. /** If assigned to all we'll consume all notification IDs, otherwise we'll sniff out the correct IDs */ 
  382. if ( $node['notification'] === true ) { 
  383. $node['notification'] = array_keys( $notifications ); 
  384. } else { 
  385.  
  386. /** Turn into array if not already */ 
  387. if ( ! is_array( $node['notification'] ) ) { 
  388. $node['notification'] = [ $node['notification'] ]; 
  389.  
  390. $new_notification = []; 
  391. foreach ( $node['notification'] as $email ) { 
  392. $match = array_search( $email, $notifications ); 
  393.  
  394. if ( $match !== false ) { 
  395. $new_notification[] = $match; 
  396.  
  397. $node['notification'] = $new_notification; 
  398.  
  399. if ( sizeof( $node['notification'] ) === 0 ) { 
  400. unset( $node['notification'] ); 
  401.  
  402. /** Insert into database */ 
  403. $results = $this->options->update_pdf( $form_id, $node['id'], $node, true, false ); 
  404.  
  405. if ( $results ) { 
  406. /** return the ID if successful */ 
  407. $this->log->addNotice( 'Successfully Added.', [ 
  408. 'pdf' => $node,  
  409. ] ); 
  410. } else { 
  411. /** Log errors */ 
  412. $this->log->addError( 'Error Saving.', [ 
  413. 'error' => $results,  
  414. 'pdf' => $node,  
  415. ] ); 
  416.  
  417. $node['form_id'] = $form_id; 
  418. $errors[] = $node; 
  419.  
  420. /** Check for any errors */ 
  421. if ( sizeof( $errors ) > 0 ) { 
  422.  
  423. $error_msg = esc_html__( 'There was a problem migrating the following configuration nodes. You will need to manually setup those PDFs.', 'gravity-forms-pdf-extended' ); 
  424. $error_msg .= '<ul>'; 
  425.  
  426. foreach ( $errors as $error ) { 
  427. $error_msg .= "<li>Form #{$error['form_id']}: {$error['template']}</li>"; 
  428.  
  429. $error_msg .= '</ul>'; 
  430. $this->notices->add_error( $error_msg ); 
  431. } else { 
  432. $this->notices->add_notice( esc_html__( 'Migration Successful.', 'gravity-forms-pdf-extended' ) ); 
  433.  
  434. /** Attempt to rename the configuration file */ 
  435. $this->archive_v3_configuration(); 
  436.  
  437. return true; 
  438.  
  439.  
  440. /** 
  441. * Archive our configuration file 
  442. * @return void 
  443. * @since 4.0 
  444. */ 
  445. private function archive_v3_configuration() { 
  446. $path = $this->templates->get_template_path(); 
  447.  
  448. if ( is_file( $path . 'configuration.php' ) ) { 
  449. @rename( $path . 'configuration.php', $path . 'configuration.archive.php' ); 
  450.  
  451. /** 
  452. * Search through all multisite font directories and move them to our top level font folder before cleaning up individual font directories 
  453. * @return void 
  454. * @since 4.0 
  455. */ 
  456. private function migrate_multisite_fonts() { 
  457. if ( is_multisite() ) { 
  458. $path = $this->templates->get_template_path(); 
  459.  
  460. /** Check if there is a fonts directory to migrate from and to */ 
  461. if ( is_dir( $path . 'fonts' ) && is_dir( $this->data->template_font_location ) ) { 
  462. $fonts = glob( $path . 'fonts/' . '*.[tT][tT][fF]' ); 
  463. $fonts = ( is_array( $fonts ) ) ? $fonts : []; 
  464.  
  465. foreach ( $fonts as $font ) { 
  466. $font_name = basename( $font ); 
  467. @copy( $font, $this->data->template_font_location . $font_name ); 
  468.  
  469. /** Delete the existing font directory */ 
  470. $this->misc->rmdir( $path . 'fonts' ); 
  471.  
  472. /** 
  473. * Try and clean-up the old output directory during the migration 
  474. * @return boolean 
  475. * @since 4.0 
  476. */ 
  477. private function cleanup_output_directory() { 
  478. $output_dir = $this->templates->get_template_path() . 'output'; 
  479.  
  480. if ( is_dir( $output_dir ) ) { 
  481. return $this->misc->rmdir( $output_dir ); 
  482.  
  483. return false; 
  484.  
  485. /** 
  486. * Try remove the font/config.php file during the migration 
  487. * @return boolean 
  488. * @since 4.0 
  489. */ 
  490. private function cleanup_font_config() { 
  491. $config = $this->data->template_font_location . 'config.php'; 
  492.  
  493. if ( is_file( $config ) && unlink( $config ) ) { 
  494. return true; 
  495.  
  496. return false;