Skip to content

Commit b35f792

Browse files
committed
Reorganise dompdf for it to work
1 parent 5a970c4 commit b35f792

File tree

9 files changed

+496
-18
lines changed

9 files changed

+496
-18
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: added
3+
4+
Forms: allow export response as PDF

projects/packages/forms/src/contact-form/class-contact-form-endpoint.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,24 @@ public function register_routes() {
336336
),
337337
)
338338
);
339+
340+
// Generate PDF for a form response.
341+
register_rest_route(
342+
$this->namespace,
343+
$this->rest_base . '/(?P<id>\d+)/pdf',
344+
array(
345+
'methods' => \WP_REST_Server::READABLE,
346+
'permission_callback' => array( $this, 'get_items_permissions_check' ),
347+
'callback' => array( $this, 'get_response_pdf' ),
348+
'args' => array(
349+
'id' => array(
350+
'type' => 'integer',
351+
'required' => true,
352+
'sanitize_callback' => 'absint',
353+
),
354+
),
355+
)
356+
);
339357
}
340358

341359
/**
@@ -447,6 +465,52 @@ public function get_status_counts( $request ) {
447465
return rest_ensure_response( $result );
448466
}
449467

468+
/**
469+
* Generates a PDF for a form response.
470+
*
471+
* @param WP_REST_Request $request Full data about the request.
472+
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
473+
*/
474+
public function get_response_pdf( $request ) {
475+
476+
// Load the Response_PDF class.
477+
require_once __DIR__ . '/class-response-pdf.php';
478+
479+
$post_id = $request->get_param( 'id' );
480+
481+
if ( ! $post_id ) {
482+
return new WP_Error(
483+
'rest_missing_param',
484+
__( 'Missing required parameter: id', 'jetpack-forms' ),
485+
array( 'status' => 400 )
486+
);
487+
}
488+
489+
// Verify the post exists and is a feedback post
490+
$post = get_post( $post_id );
491+
if ( ! $post || $post->post_type !== 'feedback' ) {
492+
return new WP_Error(
493+
'rest_invalid_post',
494+
__( 'Invalid post ID or post is not a feedback entry', 'jetpack-forms' ),
495+
array( 'status' => 404 )
496+
);
497+
}
498+
499+
// Generate and stream the PDF
500+
$pdf_service = Response_PDF::init();
501+
$pdf = $pdf_service->stream_pdf( $post_id );
502+
503+
if ( ! $pdf ) {
504+
return new WP_Error(
505+
'rest_cannot_download_pdf',
506+
__( 'Could not download the PDF.', 'jetpack-forms' ),
507+
array( 'status' => 500 )
508+
);
509+
}
510+
511+
return $pdf;
512+
}
513+
450514
/**
451515
* Adds the additional fields to the item's schema.
452516
*

projects/packages/forms/src/contact-form/class-response-pdf.php

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
* @package automattic/jetpack
66
*/
77

8-
namespace Automattic\Jetpack\Forms\Service;
8+
namespace Automattic\Jetpack\Forms\ContactForm;
99

1010
use Dompdf\Dompdf;
11+
use Dompdf\Options;
1112

1213
/**
1314
* Class Response_PDF
@@ -46,23 +47,25 @@ private function __construct() {
4647
* This is a helper function to get the setup for the post to URL.
4748
* It will return false if the setup is not valid.
4849
*
50+
* See documentation https://github.com/dompdf/dompdf
51+
*
52+
* @param int $feedback_id the feedback id.
53+
*
4954
* @return string
5055
*/
51-
private function get_pdf() {
52-
if ( ! class_exists( 'Dompdf\Dompdf' ) ) {
56+
public function stream_pdf( $feedback_id ) {
57+
if ( ! class_exists( Dompdf::class ) ) {
5358
return;
5459
}
55-
$options = new Dompdf\Options();
60+
61+
$options = new Options();
5662

5763
// Allow remote images (http/s), but they're only allowed from same-site urls
5864
$options->set( 'isRemoteEnabled', true );
65+
$options->set( 'isHtml5ParserEnabled', true );
5966

60-
$dompdf = new Dompdf\Dompdf( $options );
61-
62-
$html = $this->get_form_html();
63-
$dompdf->loadHtml( $html );
67+
$dompdf = new Dompdf( $options );
6468

65-
// (Optional) Setup the paper size and orientation
6669
$dompdf->setPaper( 'A4', 'landscape' );
6770
$dompdf->setHttpContext(
6871
stream_context_create(
@@ -76,17 +79,78 @@ private function get_pdf() {
7679
)
7780
);
7881

82+
$html = $this->get_form_html( $feedback_id );
83+
84+
$dompdf->loadHtml( $html );
85+
7986
// Render the HTML as PDF
8087
$dompdf->render();
8188

89+
$filename = 'jetpack-forms-response-' . $feedback_id . '.pdf';
90+
91+
header( 'Content-type: application/pdf', true, 200 );
92+
header( 'Content-Disposition: attachment; filename=' . $filename );
93+
header( 'Cache-Control: private' );
94+
header( 'Expires: 0' );
95+
8296
// Output the generated PDF to Browser
83-
return $dompdf->stream();
97+
return $dompdf->stream( $filename, array( 'Attachment' => false ) );
8498
}
8599

86100
/**
87101
* Return Form HTML
102+
*
103+
* @param int $feedback_id the feedback id.
88104
*/
89-
private function get_form_html() {
90-
return '<em>Hello world</em>';
105+
private function get_form_html( $feedback_id ) {
106+
107+
$footer = '';
108+
$tracking_pixel = '';
109+
$actions = '';
110+
$style = '';
111+
$template = '';
112+
$title = '';
113+
114+
/**
115+
* Filter the title used in the response pdf.
116+
*
117+
* @module contact-form
118+
*
119+
* @since $$next-version$$
120+
*
121+
* @param string the title of the pdf
122+
*/
123+
$title = (string) apply_filters( 'jetpack_forms_response_pdf_title', '' );
124+
$body = Contact_Form::get_compiled_form_for_email( $feedback_id, null );
125+
126+
/**
127+
* Filter the filename of the template HTML surrounding the response email. The PHP file will return the template in a variable called $template.
128+
*
129+
* @module contact-form
130+
*
131+
* @since $$next-version$$
132+
*
133+
* @param string the filename of the HTML template used for response pdf
134+
*/
135+
require apply_filters( 'jetpack_forms_response_pdf_template', __DIR__ . '/templates/email-response.php' );
136+
$html_message = sprintf(
137+
// The tabs are just here so that the raw code is correctly formatted for developers
138+
// They're removed so that they don't affect the final message sent to users
139+
str_replace(
140+
"\t",
141+
'',
142+
$template
143+
),
144+
( $title !== '' ? '<h1>' . $title . '</h1>' : '' ),
145+
$body,
146+
'',
147+
'',
148+
$footer,
149+
$style,
150+
$tracking_pixel,
151+
$actions
152+
);
153+
154+
return $html_message;
91155
}
92156
}

projects/packages/forms/src/contact-form/templates/email-response.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
.actions .button_block .pad a span {
176176
mso-text-raise: 15pt;
177177
}
178-
178+
179179
.actions .button_block .pad i {
180180
letter-spacing: 25px;
181181
mso-font-width: -100%;
@@ -279,4 +279,10 @@
279279
line-height: inherit;
280280
}
281281
}
282+
283+
@media print {
284+
.actions {
285+
display: none;
286+
}
287+
}
282288
</style>';

projects/packages/forms/src/dashboard/inbox/stage/actions.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import apiFetch from '@wordpress/api-fetch';
66
import { Icon } from '@wordpress/components';
77
import { store as coreStore } from '@wordpress/core-data';
88
import { __, _n, sprintf } from '@wordpress/i18n';
9-
import { seen, unseen, trash, backup, commentContent } from '@wordpress/icons';
9+
import { seen, unseen, trash, backup, commentContent, download } from '@wordpress/icons';
1010
import { store as noticesStore } from '@wordpress/notices';
1111
/**
1212
* Internal dependencies
@@ -127,6 +127,17 @@ const withTimeout = (
127127
] );
128128
};
129129

130+
const triggerFileDownload = ( blob: Blob, filename: string ): void => {
131+
const url = window.URL.createObjectURL( blob );
132+
const link = document.createElement( 'a' );
133+
link.href = url;
134+
link.download = filename;
135+
document.body.appendChild( link );
136+
link.click();
137+
document.body.removeChild( link );
138+
window.URL.revokeObjectURL( url );
139+
};
140+
130141
/**
131142
* Type for the result of processStatusChange.
132143
*/
@@ -903,3 +914,37 @@ export const markAsUnreadAction: Action = {
903914
createErrorNotice( errorMessage, { type: 'snackbar' } );
904915
},
905916
};
917+
918+
export const downloadPdfAction: Action = {
919+
id: 'pdf',
920+
isPrimary: false,
921+
icon: <Icon icon={ download } />,
922+
label: __( 'Download PDF', 'jetpack-forms' ),
923+
supportsBulk: false,
924+
async callback( items, { registry } ) {
925+
jetpackAnalytics.tracks.recordEvent( 'jetpack_forms_inbox_action_click', {
926+
action: 'pdf',
927+
multiple: false,
928+
} );
929+
930+
const { createErrorNotice } = registry.dispatch( noticesStore );
931+
932+
const downloadPromises = items.map( async ( { id } ) => {
933+
try {
934+
const response = ( await apiFetch( {
935+
path: `/wp/v2/feedback/${ id }/pdf`,
936+
parse: false,
937+
} ) ) as Response;
938+
939+
const blob = await response.blob();
940+
triggerFileDownload( blob, `jetpack-forms-response-${ id }.pdf` );
941+
} catch {
942+
createErrorNotice( __( 'Unable to download the PDF. Please try again.', 'jetpack-forms' ), {
943+
type: 'snackbar',
944+
} );
945+
}
946+
} );
947+
948+
await Promise.all( downloadPromises );
949+
},
950+
};

projects/packages/forms/src/dashboard/inbox/stage/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
markAsReadAction,
4343
markAsUnreadAction,
4444
editFormAction,
45+
downloadPdfAction,
4546
} from './actions.tsx';
4647
import { useView, defaultLayouts } from './views.js';
4748

@@ -434,7 +435,7 @@ export default function InboxView() {
434435
const viewResponseAction = isMobileViewport ? mobileViewAction : desktopViewAction;
435436

436437
const primaryActions = [ viewResponseAction ];
437-
const secondaryActions = [ markAsUnreadAction, editFormAction ];
438+
const secondaryActions = [ markAsUnreadAction, editFormAction, downloadPdfAction ];
438439

439440
switch ( statusFilter ) {
440441
case 'trash':
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: enhancement
3+
4+
Forms: allow export response as PDF

projects/plugins/jetpack/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
"automattic/jetpack-sync": "@dev",
5555
"automattic/jetpack-videopress": "@dev",
5656
"automattic/jetpack-waf": "@dev",
57-
"automattic/woocommerce-analytics": "@dev"
57+
"automattic/woocommerce-analytics": "@dev",
58+
"dompdf/dompdf": "^3.1"
5859
},
5960
"require-dev": {
6061
"antecedent/patchwork": "^2.2",

0 commit comments

Comments
 (0)