PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.1
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.1
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / templates / document / single.php

single.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.1, at templates/document/single.php

274 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Public invoice / quote page.
4 *
5 * One page for both documents. The design (what the client sees inside the
6 * frame) comes from templates/invoice-templates or templates/quote-templates;
7 * this file is the frame around it: the action bar, the payment panel, the
8 * admin's CSS editor.
9 *
10 * Override by copying to {theme}/easy-invoice/document/single.php.
11 *
12 * Variables available to overrides:
13 * $document_type 'invoice' | 'quote'
14 * $document The model
15 * $invoice Same object when it is an invoice, else null
16 * $quote Same object when it is a quote, else null
17 * $formatter Currency formatter for this document
18 * $text_settings Button labels from Settings → Text
19 * $template_file Absolute path of the resolved design template
20 *
21 * @package Easy_Invoice
22 * @subpackage Templates
23 */
24
25 if ( ! defined( 'ABSPATH' ) ) {
26 exit;
27 }
28
29 global $post;
30
31 $document_type = ( $post && \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE === $post->post_type ) ? 'quote' : 'invoice';
32 $is_quote = ( 'quote' === $document_type );
33
34 $document = null;
35 if ( $post ) {
36 if ( $is_quote && \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE === $post->post_type ) {
37 $document = \EasyInvoice\Providers\QuoteServiceProvider::getQuoteRepository()->find( $post->ID );
38 if ( $document && is_callable( [ $document, 'ensureProperSlug' ] ) ) {
39 $document->ensureProperSlug();
40 }
41 } elseif ( ! $is_quote && \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE === $post->post_type ) {
42 $document = \EasyInvoice\Providers\InvoiceServiceProvider::getInvoiceRepository()->find( $post->ID );
43 }
44 }
45
46 if ( ! $document ) {
47 wp_die( $is_quote ? esc_html__( 'Quote not found.', 'easy-invoice' ) : esc_html__( 'Invoice not found.', 'easy-invoice' ) );
48 }
49
50 $invoice = $is_quote ? null : $document;
51 $quote = $is_quote ? $document : null;
52 $document_id = (int) $document->getId();
53 $formatter = $is_quote ? new \EasyInvoice\Helpers\QuoteFormatter( $quote ) : new \EasyInvoice\Helpers\InvoiceFormatter( $invoice );
54 $text_settings = $is_quote
55 ? \EasyInvoice\Helpers\TemplateTextHelper::getQuoteTextSettings()
56 : \EasyInvoice\Helpers\TemplateTextHelper::getInvoiceTextSettings();
57 $template_file = easy_invoice_design_template( $document_type, (string) $document->getTemplate() );
58 $status = (string) $document->getStatus();
59 $is_admin_user = is_user_logged_in() && current_user_can( 'manage_options' );
60
61 // The emailed link carries an access token; every URL we build keeps it so
62 // the recipient stays authorised across print, PDF and email actions.
63 $token_param = $is_quote ? 'qk' : 'ik';
64 $token = isset( $_GET[ $token_param ] ) ? sanitize_text_field( wp_unslash( $_GET[ $token_param ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
65 /** This filter is documented in includes/Controllers/InvoiceController.php */
66 $token = (string) apply_filters( 'easy_invoice_presented_access_token', $token, $document_type );
67 $pdf_url = add_query_arg( array_filter( [ 'auto_download_pdf' => '1', $token_param => $token ] ), get_permalink( $document_id ) );
68
69 // Invoice: may the viewer pay? Quote: may the viewer accept or decline?
70 // Payable while something is owed: unpaid, part-paid or overdue — a draft is
71 // not issued yet, a paid or cancelled invoice has nothing to collect.
72 $can_pay = ! $is_quote
73 && in_array( $status, [ 'unpaid', 'available', 'partial', 'overdue' ], true )
74 && \EasyInvoice\Services\InvoiceBalance::due( $invoice ) > 0;
75 // Open for a decision: authorised viewer, and the quote neither decided nor
76 // past its expiry date. When it is closed the reason is shown instead.
77 $quote_closed_reason = '';
78 $quote_closed_tone = 'expired';
79 $can_act = false;
80 if ( $is_quote && \EasyInvoice\Controllers\QuoteController::canActOnQuote( $document_id, $quote ) ) {
81 $ei_open = \EasyInvoice\Controllers\QuoteController::openForDecision( $quote );
82 if ( true === $ei_open ) {
83 $can_act = true;
84 } elseif ( is_wp_error( $ei_open ) ) {
85 // The reader came here to decide; tell them what already happened
86 // instead of showing a quote with no buttons and no explanation.
87 $ei_qstatus = strtolower( (string) $quote->getStatus() );
88 if ( 'accepted' === $ei_qstatus ) {
89 $ei_when = (string) get_post_meta( $document_id, '_easy_invoice_quote_accepted_date', true );
90 $quote_closed_tone = 'accepted';
91 $quote_closed_reason = '' !== $ei_when && strtotime( $ei_when )
92 /* translators: %s: date. */
93 ? sprintf( __( 'You accepted this quote on %s. Thank you — nothing more to do here.', 'easy-invoice' ), date_i18n( get_option( 'date_format' ), strtotime( $ei_when ) ) )
94 : __( 'This quote has been accepted. Thank you — nothing more to do here.', 'easy-invoice' );
95 } elseif ( 'declined' === $ei_qstatus ) {
96 $quote_closed_tone = 'declined';
97 $quote_closed_reason = __( 'This quote was declined. If that was a mistake, reply to the email it came with and we will send a fresh one.', 'easy-invoice' );
98 } else {
99 $quote_closed_reason = $ei_open->get_error_message();
100 }
101 }
102 }
103
104 $accept_description = '';
105 if ( $can_act ) {
106 switch ( \EasyInvoice\Controllers\SettingsController::getQuoteAcceptAction() ) {
107 case 'convert':
108 $accept_description = __( 'This will convert the quote to an invoice.', 'easy-invoice' );
109 break;
110 case 'convert_send':
111 $accept_description = __( 'This will convert the quote to an invoice and send it to the client.', 'easy-invoice' );
112 break;
113 case 'duplicate':
114 $accept_description = __( 'This will create a new invoice while keeping this quote unchanged.', 'easy-invoice' );
115 break;
116 case 'duplicate_send':
117 $accept_description = __( 'This will create a new invoice and send it to the client, while keeping this quote unchanged.', 'easy-invoice' );
118 break;
119 default:
120 $accept_description = __( 'This will mark the quote as accepted.', 'easy-invoice' );
121 }
122 }
123
124 $additional_css = (string) get_post_meta( $document_id, '_easy_invoice_additional_css', true );
125 ?><!DOCTYPE html>
126 <html <?php language_attributes(); ?>>
127 <head>
128 <meta charset="<?php bloginfo( 'charset' ); ?>">
129 <meta name="viewport" content="width=device-width, initial-scale=1.0">
130 <meta name="robots" content="noindex, nofollow">
131 <title><?php echo esc_html( $document->getTitle() ?: ( $is_quote ? __( 'Quote', 'easy-invoice' ) : __( 'Invoice', 'easy-invoice' ) ) ); ?></title>
132 <?php wp_head(); ?>
133 <?php
134 /**
135 * Fires in the <head> of the public document page.
136 *
137 * Kept for the gateways and addons that print their assets here
138 * directly; new code should enqueue on `wp_enqueue_scripts` and test
139 * `DocumentPage::currentType()` instead.
140 */
141 do_action( 'easy_invoice_head' );
142 ?>
143 <?php if ( '' !== $additional_css ) : ?>
144 <?php // Same treatment as core's wp_custom_css_cb(): entities are not decoded inside <style>, so tags are stripped instead of escaped. ?>
145 <style id="custom-<?php echo esc_attr( $document_type ); ?>-css"><?php echo wp_strip_all_tags( $additional_css ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></style>
146 <?php endif; ?>
147 </head>
148 <body <?php body_class(); ?>>
149
150 <div class="ei-document easy-invoice-<?php echo esc_attr( $document_type ); ?>-container">
151
152 <?php if ( $can_act && '' !== $accept_description ) : ?>
153 <div class="ei-document__notice accept-action-info">
154 <strong><?php echo esc_html( $text_settings['accept_quote'] ); ?>:</strong>
155 <?php echo esc_html( $accept_description ); ?>
156 </div>
157 <?php elseif ( '' !== $quote_closed_reason ) : ?>
158 <div class="ei-document__notice ei-document__notice--<?php echo esc_attr( $quote_closed_tone ); ?>">
159 <?php echo esc_html( $quote_closed_reason ); ?>
160 </div>
161 <?php endif; ?>
162
163 <div class="ei-document__actions <?php echo esc_attr( $document_type ); ?>-actions">
164 <?php if ( $can_act ) : ?>
165 <button type="button" class="ei-btn ei-btn--accept accept" data-ei-action="accept"
166 data-description="<?php echo esc_attr( $accept_description ); ?>">
167 <?php echo esc_html( $text_settings['accept_quote'] ); ?>
168 </button>
169 <button type="button" class="ei-btn ei-btn--decline decline" data-ei-action="decline"
170 data-message="<?php echo esc_attr( \EasyInvoice\Controllers\SettingsController::getDeclinedQuoteMessage() ); ?>"
171 data-reason-required="<?php echo \EasyInvoice\Controllers\SettingsController::isDeclineReasonRequired() ? '1' : '0'; ?>">
172 <?php echo esc_html( $text_settings['decline_quote'] ); ?>
173 </button>
174 <?php endif; ?>
175
176 <button type="button" class="ei-btn ei-btn--print" data-ei-action="print">
177 <?php echo esc_html( $text_settings['print'] ); ?>
178 </button>
179
180 <a class="ei-btn ei-btn--pdf" href="<?php echo esc_url( $pdf_url ); ?>" data-ei-action="pdf">
181 <?php echo esc_html( $text_settings['download_pdf'] ); ?>
182 </a>
183
184 <button type="button" class="ei-btn ei-btn--email send-email-button" data-ei-action="email">
185 <?php echo esc_html( $text_settings['send_email'] ); ?>
186 </button>
187
188 <?php if ( $can_pay ) : ?>
189 <button type="button" id="toggle-payment-panel" class="ei-btn ei-btn--pay pay" data-ei-action="pay" data-label="<?php echo esc_attr( $text_settings['pay_now'] ); ?>">
190 <?php echo esc_html( $text_settings['pay_now'] ); ?>
191 </button>
192 <?php endif; ?>
193
194 <?php
195 /**
196 * Fires after the built-in buttons in the action bar.
197 *
198 * @param object $document The invoice or quote.
199 * @param string $document_type 'invoice' or 'quote'.
200 */
201 do_action( 'easy_invoice_document_actions', $document, $document_type );
202 ?>
203 </div>
204
205 <?php if ( ! $is_quote ) : ?>
206 <?php do_action( 'easy_invoice_before_invoice_content_top', $invoice ); ?>
207 <?php endif; ?>
208
209 <div class="ei-document__layout <?php echo esc_attr( $document_type ); ?>-layout">
210 <div class="ei-document__content <?php echo esc_attr( $document_type ); ?>-content" id="<?php echo esc_attr( $document_type ); ?>-content">
211
212 <?php if ( $is_admin_user ) : ?>
213 <button type="button" id="additional-css-btn" class="ei-css-toggle <?php echo '' !== $additional_css ? 'has-css' : ''; ?>" title="<?php esc_attr_e( 'Additional CSS for this document', 'easy-invoice' ); ?>">
214 <span aria-hidden="true">{ }</span>
215 </button>
216 <?php endif; ?>
217
218 <?php
219 if ( $is_quote ) {
220 do_action( 'easy_invoice_quote_view_content_top', $quote );
221 } else {
222 do_action( 'easy_invoice_invoice_view_content_top', $invoice );
223 }
224
225 if ( '' !== $template_file ) {
226 include $template_file;
227 } else {
228 easy_invoice_get_template( 'document/fallback.php', [
229 'document' => $document,
230 'document_type' => $document_type,
231 'formatter' => $formatter,
232 'invoice' => $invoice,
233 'quote' => $quote,
234 ] );
235 }
236
237 if ( $is_quote ) {
238 do_action( 'easy_invoice_quote_content_after', $quote );
239 }
240 ?>
241 </div>
242
243 <?php if ( $can_pay ) : ?>
244 <div id="payment-panel" class="ei-payment-panel payment-panel" hidden>
245 <button type="button" class="ei-payment-panel__close" data-ei-action="close-payment" aria-label="<?php esc_attr_e( 'Close', 'easy-invoice' ); ?>">&times;</button>
246 <?php easy_invoice_get_template( 'payment-section.php' ); ?>
247 </div>
248 <?php endif; ?>
249 </div>
250 </div>
251
252 <?php if ( $is_admin_user ) : ?>
253 <?php
254 easy_invoice_get_template( 'document/css-panel.php', [
255 'document_type' => $document_type,
256 'additional_css' => $additional_css,
257 ] );
258 ?>
259 <?php endif; ?>
260
261 <?php
262 /**
263 * Fires before wp_footer() on the public document page.
264 *
265 * @param object $document The invoice or quote.
266 * @param string $document_type 'invoice' or 'quote'.
267 */
268 do_action( 'easy_invoice_document_footer', $document, $document_type );
269
270 wp_footer();
271 ?>
272 </body>
273 </html>
274