config_cache = null;
// Also clear any WordPress object cache
wp_cache_delete('easy_invoice_settings_config', 'easy_invoice');
// Force clear any other caches
if (function_exists('wp_cache_flush')) {
wp_cache_flush();
}
}
/**
* Initialize the controller
*
* @return void
*/
public function init() {
// Add AJAX handlers
add_action('wp_ajax_easy_invoice_save_settings', [$this, 'saveSettings']);
add_action('wp_ajax_easy_invoice_test_email', [$this, 'testEmail']);
add_action('wp_ajax_easy_invoice_test_template_email', [$this, 'testTemplateEmail']);
add_action('wp_ajax_easy_invoice_test_payment_reminder_email', [$this, 'testPaymentReminderEmail']);
add_action('wp_ajax_regenerate_invoice_numbers', [$this, 'ajaxRegenerateInvoiceNumbers']);
add_action('wp_ajax_regenerate_quote_numbers', [$this, 'ajaxRegenerateQuoteNumbers']);
// Clear cache on admin init to ensure new fields are recognized
add_action('admin_init', [$this, 'clearCache']);
// Enqueue admin scripts
add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']);
// Initialize settings
$this->initializeSettings();
}
/**
* Enqueue admin scripts and styles for the settings page.
*
* @param string $hook_suffix The current admin page.
* @return void
*/
public function enqueueAdminScripts($hook_suffix) {
// Check if we are on the Easy Invoice settings page.
if (empty($hook_suffix) || strpos($hook_suffix, PagesSlugs::SETTINGS) === false) {
return;
}
wp_enqueue_script('jquery-ui-sortable');
wp_enqueue_media(); // For the logo uploader
// Enqueue Select2 for enhancing multiselect fields
wp_enqueue_style(
'select2',
EASY_INVOICE_URL . 'assets/lib/select2/select2.min.css',
[],
'4.0.13'
);
wp_enqueue_script(
'select2',
EASY_INVOICE_URL . 'assets/lib/select2/select2.min.js',
['jquery'],
'4.0.13',
true
);
// Enqueue confirmation modal for premium features
wp_enqueue_script(
'easy-invoice-confirmation-modal',
EASY_INVOICE_URL . 'assets/js/confirmation-modal.js',
['jquery'],
EASY_INVOICE_VERSION,
true
);
// Enqueue toast notification system
wp_enqueue_script(
'easy-invoice-toast',
EASY_INVOICE_URL . 'assets/js/easy-invoice-toast.js',
['jquery'],
EASY_INVOICE_VERSION,
true
);
$script_handle = 'easy-invoice-settings';
wp_enqueue_script(
$script_handle,
EASY_INVOICE_URL . 'assets/js/settings.js',
['jquery', 'jquery-ui-sortable', 'wp-util', 'select2', 'easy-invoice-confirmation-modal', 'media-views'],
EASY_INVOICE_VERSION,
true
);
// Enqueue the new settings.css only on the settings page
wp_enqueue_style(
'easy-invoice-settings',
EASY_INVOICE_URL . 'assets/css/settings.css',
[],
EASY_INVOICE_VERSION
);
$localize_data = apply_filters('easy_invoice_settings_js_data', [
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('easy_invoice_settings'),
'saveSuccess' => __('Settings saved successfully', 'easy-invoice'),
'saveError' => __('Error saving settings', 'easy-invoice'),
]);
wp_localize_script($script_handle, 'easyInvoiceSettings', $localize_data);
do_action('easy_invoice_after_admin_scripts', $hook_suffix);
}
/**
* Check if Easy Invoice Pro is active
*
* @return bool
*/
private function isProActive(): bool {
return easy_invoice_has_pro();
}
/**
* Get settings fields configuration
*
* @return array
*/
public function get_settings_fields_config() {
// Use cached config if available
if (is_array($this->config_cache)) {
return $this->config_cache;
}
$config = [
'company' => [
'title' => __('Company Information', 'easy-invoice'),
'description' => __('Set up your company details', 'easy-invoice'),
'icon' => 'fas fa-building',
'fields' => [
'easy_invoice_company_name' => ['label' => __('Company Name', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_company_email' => ['label' => __('Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_company_phone' => ['label' => __('Phone Number', 'easy-invoice'), 'type' => 'tel', 'default' => '', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_company_website' => ['label' => __('Website', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6' ],
// Both are required by EN 16931 and are what makes an intra-EU
// reverse charge determinable. See Services\TaxTreatment.
'easy_invoice_company_vat_number' => ['label' => __('VAT number', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3', 'description' => __('Your VAT identifier, including the country prefix — for example DE123456789.', 'easy-invoice') ],
'easy_invoice_company_country' => ['label' => __('Country', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3', 'description' => __('Two-letter country code, for example DE, FR or GB.', 'easy-invoice') ],
'easy_invoice_company_address' => ['label' => __('Address', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_tax_number' => ['label' => __('Tax ID / VAT Number', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_company_logo' => ['label' => __('Company Logo', 'easy-invoice'), 'type' => 'image', 'default' => '', 'col_span' => 'sm:col-span-6' ],
]
],
'invoice' => [
'title' => __('Invoice Settings', 'easy-invoice'),
'description' => __('Configure invoice-specific settings and preferences', 'easy-invoice'),
'icon' => 'fas fa-file-invoice',
'fields' => [
'easy_invoice_invoice_prefix' => [
'label' => __('Invoice Prefix', 'easy-invoice'),
'type' => 'text',
'default' => 'EIIN_',
'description' => __('Prefix for invoice numbers (e.g., EIIN_, INV-)', 'easy-invoice'),
'col_span' => 'sm:col-span-2'
],
'easy_invoice_next_invoice_number' => [
'label' => __('Next Invoice Number', 'easy-invoice'),
'type' => 'number',
'default' => '1',
'description' => __('Set the next invoice number to be generated. This will be the number of the next invoice created.', 'easy-invoice'),
'col_span' => 'sm:col-span-2',
],
'easy_invoice_regenerate_invoice_numbers' => [
'label' => __('Regenerate Invoice Numbers', 'easy-invoice'),
'type' => 'button',
'button_text' => __('Regenerate All Invoice Numbers', 'easy-invoice'),
'button_class' => 'button button-secondary',
'description' => __('Click to regenerate all invoice numbers starting from the Last Invoice Number. This will update all existing invoices with new sequential numbers.', 'easy-invoice'),
'col_span' => 'sm:col-span-2',
'ajax_action' => 'regenerate_invoice_numbers'
],
'easy_invoice_invoice_show_adjust_field' => [
'label' => __('Show/Hide Adjust Field', 'easy-invoice'),
'type' => 'checkbox',
'default' => 'yes',
'description' => __('Enable/Disable Adjust field. Tick this to show adjust field', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_invoice_terms_conditions' => [
'label' => __('Terms & Conditions', 'easy-invoice'),
'type' => 'wp_editor',
'default' => __('Payment is due within 30 days from date of invoice', 'easy-invoice'),
'description' => __('Terms and conditions that will be displayed on your invoice', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_invoice_footer_text' => [
'label' => __('Footer Text', 'easy-invoice'),
'type' => 'wp_editor',
'default' => '',
'description' => __('You can modify your invoice footer text from here. HTML tags supports: a, br, em, strong, hr, p, h1 to h4', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
],
],
'quote' => [
'title' => __('Quote Settings', 'easy-invoice'),
'description' => __('Configure quote-specific settings and preferences', 'easy-invoice'),
'icon' => 'fas fa-file-contract',
'fields' => [
'easy_invoice_quote_prefix' => [
'label' => __('Quote Prefix', 'easy-invoice'),
'type' => 'text',
'default' => 'EIQN_',
'description' => __('Prefix for quote numbers (e.g., EIQN_, QT-)', 'easy-invoice'),
'col_span' => 'sm:col-span-2'
],
'easy_invoice_next_quote_number' => [
'label' => __('Next Quote Number', 'easy-invoice'),
'type' => 'number',
'default' => '1',
'description' => __('Set the next quote number to be generated. This will be the number of the next quote created.', 'easy-invoice'),
'col_span' => 'sm:col-span-2',
],
'easy_invoice_regenerate_quote_numbers' => [
'label' => __('Regenerate Quote Numbers', 'easy-invoice'),
'type' => 'button',
'button_text' => __('Regenerate All Quote Numbers', 'easy-invoice'),
'button_class' => 'button button-secondary',
'description' => __('Click to regenerate all quote numbers starting from the Next Quote Number. This will update all existing quotes with new sequential numbers.', 'easy-invoice'),
'col_span' => 'sm:col-span-2',
'ajax_action' => 'regenerate_quote_numbers'
],
'easy_invoice_quote_show_adjust_field' => [
'label' => __('Show/Hide Adjust Field', 'easy-invoice'),
'type' => 'checkbox',
'default' => 'yes',
'description' => __('Enable/Disable Adjust field. Tick this to show adjust field', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_terms_conditions' => [
'label' => __('Terms & Conditions', 'easy-invoice'),
'type' => 'wp_editor',
'default' => __('This quote has a fixed price. Upon acceptance, we kindly ask for a 25% deposit prior to initiating the work.', 'easy-invoice'),
'description' => __('Terms and conditions that will be displayed on your quote!', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_footer_text' => [
'label' => __('Footer Text', 'easy-invoice'),
'type' => 'wp_editor',
'default' => __('Thank you for your business.', 'easy-invoice'),
'description' => __('You can modify your quote footer text from here. HTML tags supports: a, br, em, strong, hr, p, h1 to h4', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_accept_button' => [
'label' => __('Accept quote button', 'easy-invoice'),
'type' => 'checkbox',
'default' => 'yes',
'description' => __('Show/hide accept quote button on quotes.', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_accept_action' => [
'label' => __('Accept quote button action', 'easy-invoice'),
'type' => 'select',
'default' => 'convert',
'options' => [
'convert' => __('Convert quote to invoice - Draft', 'easy-invoice'),
'convert_available' => __('Convert quote to invoice - Available', 'easy-invoice'),
'convert_send' => __('Convert quote to invoice and send to client - Available', 'easy-invoice'),
'duplicate' => __('Create new invoice, keep quote as-is - Draft', 'easy-invoice'),
'duplicate_send' => __('Create new invoice and send to client, keep quote as-is - Available', 'easy-invoice'),
'do_nothing' => __('Do nothing', 'easy-invoice'),
],
'description' => __('Upon the client clicking the "accept quote" button, the subsequent action will be activated.', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_accept_text' => [
'label' => __('Accept Quote Text', 'easy-invoice'),
'type' => 'wp_editor',
'default' => __('Important: When you accept this Quote, an Invoice will be created automatically. This will form a legally binding contract.', 'easy-invoice'),
'description' => __('This information tells your client what happens once they accept the Quote', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_accepted_message' => [
'label' => __('Accepted Quote Message', 'easy-invoice'),
'type' => 'wp_editor',
'default' => __('You\'ve confirmed the Quote.
We\'ll get in touch with you shortly.', 'easy-invoice'),
'description' => __('If the client accepts the Quote, display this message.', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_decline_reason_required' => [
'label' => __('Decline Reason Required', 'easy-invoice'),
'type' => 'checkbox',
'default' => 'no',
'description' => __('Make the \'Reason for declining\' field mandatory when rejecting.', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_quote_declined_message' => [
'label' => __('Declined Quote Message', 'easy-invoice'),
'type' => 'wp_editor',
'default' => '',
'description' => __('Message to display if client declines the Quote', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
],
],
'currency' => [
'title' => __('Currency Settings', 'easy-invoice'),
'description' => __('Configure currency display preferences', 'easy-invoice'),
'icon' => 'fas fa-dollar-sign',
'fields' => [
'easy_invoice_currency_code' => [
'label' => __('Currency & Symbol', 'easy-invoice'),
'type' => 'select',
'default' => 'USD',
'options' => CurrencyHelper::getCurrencyOptions(),
'col_span' => 'sm:col-span-6',
'description' => __('Select your preferred currency. The symbol will be automatically set.', 'easy-invoice')
],
'easy_invoice_currency_position' => [
'label' => __('Symbol Position', 'easy-invoice'),
'type' => 'select',
'default' => 'left',
'options' => [
'left' => __('Left', 'easy-invoice'),
'right' => __('Right', 'easy-invoice'),
'left_space' => __('Left with space', 'easy-invoice'),
'right_space' => __('Right with space', 'easy-invoice'),
],
'col_span' => 'sm:col-span-3'
],
'easy_invoice_currency_symbol_type' => [
'label' => __('Currency Symbol Type', 'easy-invoice'),
'type' => 'select',
'default' => 'symbol',
'options' => [
'code' => __('Currency Code', 'easy-invoice'),
'symbol' => __('Currency Symbol', 'easy-invoice'),
],
'description' => __('Choose whether to display currency code (USD) or currency symbol ($)', 'easy-invoice'),
'col_span' => 'sm:col-span-3'
],
'easy_invoice_thousands_separator' => ['label' => __('Thousands Separator', 'easy-invoice'), 'type' => 'text', 'default' => ',', 'col_span' => 'sm:col-span-3' ],
'easy_invoice_decimal_separator' => ['label' => __('Decimal Separator', 'easy-invoice'), 'type' => 'text', 'default' => '.', 'col_span' => 'sm:col-span-3' ],
'easy_invoice_decimal_precision' => ['label' => __('Decimal Places', 'easy-invoice'), 'type' => 'number', 'default' => '2', 'min' => 0, 'max' => 4, 'col_span' => 'sm:col-span-3' ],
]
],
'tax' => [
'title' => __('Tax Settings', 'easy-invoice'),
'description' => __('Configure tax rates and preferences', 'easy-invoice'),
'icon' => 'fas fa-percent',
'fields' => [
'easy_invoice_tax_enabled' => ['label' => __('Enable Tax', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_tax_entry_method' => [
'label' => __('How do you enter tax?', 'easy-invoice'),
'type' => 'select',
'default' => 'exclusive',
'options' => [
'inclusive' => __('I will enter price inclusive of tax', 'easy-invoice'),
'exclusive' => __('I will enter price exclusive of tax', 'easy-invoice'),
],
'description' => __('Choose how you want to enter prices - with or without tax included', 'easy-invoice'),
'col_span' => 'sm:col-span-6'
],
'easy_invoice_tax_rate' => ['label' => __('Default Tax Rate (%)', 'easy-invoice'), 'type' => 'number', 'default' => '0', 'step' => '0.01', 'min' => '0', 'max' => '100', 'col_span' => 'sm:col-span-3' ],
'easy_invoice_tax_name' => ['label' => __('Tax Name', 'easy-invoice'), 'type' => 'text', 'default' => __('Tax', 'easy-invoice'), 'col_span' => 'sm:col-span-3' ],
]
],
'payment' => [
'title' => __('Payment Methods', 'easy-invoice'),
'description' => __('Configure and order available payment methods.', 'easy-invoice'),
'icon' => 'fas fa-credit-card',
'is_special_section' => true,
'gateways' => $this->getGatewaySettingsConfigs(),
],
'email' => [
'title' => __('Email Settings', 'easy-invoice'),
'description' => __('Configure email sending options and templates', 'easy-invoice'),
'icon' => 'fas fa-envelope',
'subsections' => [
'general' => [
'title' => __('General Email Settings', 'easy-invoice'),
'description' => __('Configure general email sending options', 'easy-invoice'),
'fields' => [
'easy_invoice_email_from_name' => ['label' => __('From Name', 'easy-invoice'), 'type' => 'text', 'default' => get_bloginfo('name') ?: 'Easy Invoice', 'col_span' => 'sm:col-span-3'],
'easy_invoice_email_from_address' => ['label' => __('From Email Address', 'easy-invoice'), 'type' => 'email', 'default' => get_bloginfo('admin_email') ?: get_option('admin_email', ''), 'col_span' => 'sm:col-span-3'],
'easy_invoice_email_reply_to' => ['label' => __('Reply-To Email', 'easy-invoice'), 'type' => 'email', 'default' => '', 'col_span' => 'sm:col-span-3'],
'easy_invoice_email_reply_to_name' => ['label' => __('Reply-To Name', 'easy-invoice'), 'type' => 'text', 'default' => '', 'col_span' => 'sm:col-span-3'],
'easy_invoice_enable_email_styling' => ['label' => __('Enable HTML Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
'easy_invoice_bcc_admin' => ['label' => __('BCC Admin on All Emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6'],
// Off by default: attaching a document changes what every
// customer receives and makes messages much larger, which
// some SMTP relays reject. See EmailManager::shouldAttachInvoicePdf().
'easy_invoice_attach_pdf_to_email' => ['label' => __('Attach a PDF copy to invoice and quote emails', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6', 'description' => __('Generates the document on the server and attaches it. The PDF contains real, selectable text rather than an image.', 'easy-invoice')],
'easy_invoice_pdf_paper_size' => ['label' => __('PDF paper size', 'easy-invoice'), 'type' => 'select', 'default' => 'a4', 'options' => ['a4' => 'A4', 'letter' => 'Letter', 'legal' => 'Legal'], 'col_span' => 'sm:col-span-3'],
'easy_invoice_pdf_download_method' => ['label' => __('"Download as PDF" button', 'easy-invoice'), 'type' => 'select', 'default' => 'browser', 'options' => ['browser' => __('Exactly as shown on screen (rendered in the browser)', 'easy-invoice'), 'server' => __('Generated on the server (real text, smaller file)', 'easy-invoice')], 'col_span' => 'sm:col-span-3', 'description' => __('The browser capture reproduces the page pixel for pixel — every design, custom template and watermark. The server file is selectable text and a fraction of the size. Emails, the API and e-invoicing always use the server.', 'easy-invoice')],
'easy_invoice_pdf_layout' => ['label' => __('Server-generated PDF layout', 'easy-invoice'), 'type' => 'select', 'default' => 'design', 'options' => ['design' => __('Match the selected invoice / quote design', 'easy-invoice'), 'plain' => __('Plain print layout', 'easy-invoice')], 'col_span' => 'sm:col-span-3', 'description' => __('Applies to PDFs the server produces: email attachments, the API, e-invoicing, and the download button when set to "server". The design is the template chosen on the document; the plain layout is a neutral table-based page.', 'easy-invoice')],
'easy_invoice_admin_email' => ['label' => __('Admin Email for BCC', 'easy-invoice'), 'type' => 'email', 'default' => get_option('admin_email', ''), 'col_span' => 'sm:col-span-6'],
'easy_invoice_email_logo' => ['label' => __('Email Logo URL', 'easy-invoice'), 'type' => 'url', 'default' => '', 'col_span' => 'sm:col-span-6'],
'easy_invoice_email_footer_text' => ['label' => __('Email Footer Text', 'easy-invoice'), 'type' => 'textarea', 'default' => '', 'col_span' => 'sm:col-span-6'],
'easy_invoice_test_email' => [
'label' => __('Test Email', 'easy-invoice'),
'type' => 'test_email',
'default' => '',
'col_span' => 'sm:col-span-6',
'description' => __('Send a test email to verify your email configuration is working correctly.', 'easy-invoice')
],
]
],
'invoice_available' => [
'title' => __('Invoice Available Email', 'easy-invoice'),
'description' => __('Configure email sent when an invoice is available for the client', 'easy-invoice'),
'fields' => [
'easy_invoice_invoice_email_enabled' => ['label' => __('Enable Invoice Available Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
'easy_invoice_invoice_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Your Invoice #{{invoice_number}} from {{company_name}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
'easy_invoice_invoice_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('invoice'), 'col_span' => 'sm:col-span-6'],
'easy_invoice_invoice_test_template' => [
'label' => __('Test Invoice Email', 'easy-invoice'),
'type' => 'test_template_email',
'default' => '',
'col_span' => 'sm:col-span-6',
'description' => __('Send a test invoice email to verify the template and configuration.', 'easy-invoice'),
'template_type' => 'invoice_available'
],
]
],
'quote_available' => [
'title' => __('Quote Available Email', 'easy-invoice'),
'description' => __('Configure email sent when a quote is available for the client', 'easy-invoice'),
'fields' => [
'easy_invoice_quote_email_enabled' => ['label' => __('Enable Quote Available Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
'easy_invoice_quote_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Your Quote #{{quote_number}} from {{company_name}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
'easy_invoice_quote_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('quote'), 'col_span' => 'sm:col-span-6'],
'easy_invoice_quote_test_template' => [
'label' => __('Test Quote Email', 'easy-invoice'),
'type' => 'test_template_email',
'default' => '',
'col_span' => 'sm:col-span-6',
'description' => __('Send a test quote email to verify the template and configuration.', 'easy-invoice'),
'template_type' => 'quote_available'
],
]
],
'payment_received' => [
'title' => __('Payment Received Email', 'easy-invoice'),
'description' => __('Configure email sent when a payment is received', 'easy-invoice'),
'fields' => [
'easy_invoice_payment_email_enabled' => ['label' => __('Enable Payment Received Email', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'yes', 'col_span' => 'sm:col-span-6'],
'easy_invoice_payment_email_subject' => ['label' => __('Subject', 'easy-invoice'), 'type' => 'text', 'default' => __('Payment Received - Invoice #{{invoice_number}}', 'easy-invoice'), 'col_span' => 'sm:col-span-6'],
'easy_invoice_payment_email_body' => ['label' => __('Email Body', 'easy-invoice'), 'type' => 'wp_editor', 'default' => \EasyInvoice\Services\EmailManager::defaultTemplate('payment'), 'col_span' => 'sm:col-span-6'],
'easy_invoice_payment_test_template' => [
'label' => __('Test Payment Email', 'easy-invoice'),
'type' => 'test_template_email',
'default' => '',
'col_span' => 'sm:col-span-6',
'description' => __('Send a test payment email to verify the template and configuration.', 'easy-invoice'),
'template_type' => 'payment_received'
],
]
],
]
],
'text_settings' => [
'title' => __('Text Settings', 'easy-invoice'),
'description' => __('Customize text labels for invoices and quotes. Perfect for multi-language support.', 'easy-invoice'),
'icon' => '',
'fields' => [
// Invoice Text Settings
'easy_invoice_text_invoice' => ['label' => __('Invoice', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoice', 'easy-invoice'), 'col_span' => '', 'description' => __('Label for single invoice', 'easy-invoice')],
'easy_invoice_text_invoices' => ['label' => __('Invoices', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoices', 'easy-invoice'), 'col_span' => '', 'description' => __('Label for multiple invoices', 'easy-invoice')],
'easy_invoice_text_to' => ['label' => __('To', 'easy-invoice'), 'type' => 'text', 'default' => __('To', 'easy-invoice'), 'col_span' => '', 'description' => __('To label on invoice/quote', 'easy-invoice')],
'easy_invoice_text_invoice_number' => ['label' => __('Invoice Number', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoice Number', 'easy-invoice'), 'col_span' => '', 'description' => __('Invoice number label', 'easy-invoice')],
'easy_invoice_text_invoice_date' => ['label' => __('Invoice Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Invoice Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Invoice date label', 'easy-invoice')],
'easy_invoice_text_due_date' => ['label' => __('Due Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Due Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Due date label', 'easy-invoice')],
'easy_invoice_text_total_due' => ['label' => __('Total Due', 'easy-invoice'), 'type' => 'text', 'default' => __('Total Due', 'easy-invoice'), 'col_span' => '', 'description' => __('Total due label', 'easy-invoice')],
'easy_invoice_text_qty' => ['label' => __('Qty', 'easy-invoice'), 'type' => 'text', 'default' => __('Qty', 'easy-invoice'), 'col_span' => '', 'description' => __('Quantity label', 'easy-invoice')],
'easy_invoice_text_service' => ['label' => __('Service', 'easy-invoice'), 'type' => 'text', 'default' => __('Service', 'easy-invoice'), 'col_span' => '', 'description' => __('Service label', 'easy-invoice')],
'easy_invoice_text_rate_price' => ['label' => __('Rate', 'easy-invoice'), 'type' => 'text', 'default' => __('Rate', 'easy-invoice'), 'col_span' => '', 'description' => __('Rate or price label', 'easy-invoice')],
'easy_invoice_text_adjust' => ['label' => __('Adjust', 'easy-invoice'), 'type' => 'text', 'default' => __('Adjust', 'easy-invoice'), 'col_span' => '', 'description' => __('Adjust label', 'easy-invoice')],
'easy_invoice_text_sub_total' => ['label' => __('Sub Total', 'easy-invoice'), 'type' => 'text', 'default' => __('Sub Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Sub total label', 'easy-invoice')],
'easy_invoice_text_total' => ['label' => __('Total', 'easy-invoice'), 'type' => 'text', 'default' => __('Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Total label', 'easy-invoice')],
'easy_invoice_text_line_total' => ['label' => __('Line total', 'easy-invoice'), 'type' => 'text', 'default' => __('Total', 'easy-invoice'), 'col_span' => '', 'description' => __('Heading of the line-item amount column', 'easy-invoice')],
'easy_invoice_text_tax' => ['label' => __('Tax', 'easy-invoice'), 'type' => 'text', 'default' => __('Tax', 'easy-invoice'), 'col_span' => '', 'description' => __('Tax label', 'easy-invoice')],
'easy_invoice_text_discount' => ['label' => __('Discount', 'easy-invoice'), 'type' => 'text', 'default' => __('Discount', 'easy-invoice'), 'col_span' => '', 'description' => __('Discount label', 'easy-invoice')],
'easy_invoice_text_print' => ['label' => __('Print', 'easy-invoice'), 'type' => 'text', 'default' => __('Print', 'easy-invoice'), 'col_span' => '', 'description' => __('Print button text', 'easy-invoice')],
'easy_invoice_text_download_pdf' => ['label' => __('Download as PDF', 'easy-invoice'), 'type' => 'text', 'default' => __('Download as PDF', 'easy-invoice'), 'col_span' => '', 'description' => __('Download PDF button text', 'easy-invoice')],
'easy_invoice_text_send_email' => ['label' => __('Send Email', 'easy-invoice'), 'type' => 'text', 'default' => __('Send Email', 'easy-invoice'), 'col_span' => '', 'description' => __('Send email button text', 'easy-invoice')],
'easy_invoice_text_pay_now' => ['label' => __('Pay Now', 'easy-invoice'), 'type' => 'text', 'default' => __('Pay Now', 'easy-invoice'), 'col_span' => '', 'description' => __('Pay now button text', 'easy-invoice')],
// Quote Text Settings
'easy_invoice_text_quote' => ['label' => __('Quote', 'easy-invoice'), 'type' => 'text', 'default' => __('Quote', 'easy-invoice'), 'col_span' => '', 'description' => __('Label for single quote', 'easy-invoice')],
'easy_invoice_text_quote_number' => ['label' => __('Quote Number', 'easy-invoice'), 'type' => 'text', 'default' => __('Quote Number', 'easy-invoice'), 'col_span' => '', 'description' => __('Quote number label', 'easy-invoice')],
'easy_invoice_text_accept_quote' => ['label' => __('Accept Quote', 'easy-invoice'), 'type' => 'text', 'default' => __('Accept Quote', 'easy-invoice'), 'col_span' => '', 'description' => __('Accept quote button text', 'easy-invoice')],
'easy_invoice_text_decline_quote' => ['label' => __('Decline Quote', 'easy-invoice'), 'type' => 'text', 'default' => __('Decline Quote', 'easy-invoice'), 'col_span' => '', 'description' => __('Decline quote button text', 'easy-invoice')],
'easy_invoice_text_decline_reason' => ['label' => __('Reason for declining', 'easy-invoice'), 'type' => 'text', 'default' => __('Reason for declining', 'easy-invoice'), 'col_span' => '', 'description' => __('Decline reason label', 'easy-invoice')],
'easy_invoice_text_valid_until' => ['label' => __('Valid Until Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Valid Until Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Valid until date label', 'easy-invoice')],
'easy_invoice_text_quote_date' => ['label' => __('Quote Date', 'easy-invoice'), 'type' => 'text', 'default' => __('Quote Date', 'easy-invoice'), 'col_span' => '', 'description' => __('Quote date label', 'easy-invoice')],
]
],
'advanced' => [
'title' => __('Advanced Settings', 'easy-invoice'),
'description' => __('Configure system preferences', 'easy-invoice'),
'icon' => 'fas fa-cog',
'fields' => [
'easy_invoice_date_format' => [
'label' => __('Date Format', 'easy-invoice'),
'type' => 'select',
'default' => 'us',
'options' => [
'us' => __('MM/DD/YYYY (US) - 01/15/2024', 'easy-invoice'),
'uk' => __('DD/MM/YYYY (UK) - 15/01/2024', 'easy-invoice'),
'iso' => __('YYYY-MM-DD (ISO) - 2024-01-15', 'easy-invoice'),
],
'col_span' => 'sm:col-span-3'
],
'easy_invoice_invoice_numbering' => ['label' => __('Auto-increment invoice numbers', 'easy-invoice'), 'type' => 'checkbox', 'description' => __('Automatically increment invoice numbers for new invoices', 'easy-invoice'), 'default' => 'yes', 'col_span' => 'sm:col-span-6' ],
'easy_invoice_payment_reminder_days' => ['label' => __('Payment Reminder Days', 'easy-invoice'), 'type' => 'number', 'default' => 3, 'col_span' => 'sm:col-span-3'],
'easy_invoice_remove_data_on_uninstall' => ['label' => __('Remove all data when the plugin is deleted', 'easy-invoice'), 'type' => 'checkbox', 'default' => 'no', 'col_span' => 'sm:col-span-6', 'description' => __('Off, deleting the plugin from the Plugins screen keeps every invoice, quote, payment, client record and setting, so reinstalling picks up where you left off. On, all of it is removed — invoices are tax records, so leave this off unless you are certain.', 'easy-invoice')],
]
],
];
// Cache and filter the config
$this->config_cache = apply_filters('easy_invoice_settings_fields_config', $config);
return $this->config_cache;
}
/**
* Get all gateway settings configurations
*
* @return array Gateway settings configs
*/
private function getGatewaySettingsConfigs(): array {
$gateway_configs = [];
try {
// Get gateway manager from the main plugin instance
$gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
$gateways = $gateway_manager->getGateways();
// Collect settings from each gateway
foreach ($gateways as $gateway_id => $gateway) {
try {
$gateway_configs[$gateway_id] = $gateway->getSettingsConfig();
} catch (\Exception $e) {
// Log the error but continue with other gateways
$gateway_configs[$gateway_id] = [];
}
}
} catch (\Exception $e) {
}
return $gateway_configs;
}
/**
* Prepare settings for display with proper filtering
*
* @param array $settings The settings array
* @return array The filtered settings
*/
public function prepareSettingsForDisplay($settings) {
return apply_filters('easy_invoice_settings_for_display', $settings);
}
/**
* Display method implementation
*
* @param array $args Display arguments
* @return void
*/
public function display(array $args = []) {
$page = isset($args['page']) ? $args['page'] : '';
$subsection = isset($args['subsection']) ? $args['subsection'] : '';
// Clear cache to ensure new fields are recognized
$this->clearCache();
switch ($page) {
case PagesSlugs::SETTINGS:
if (!empty($subsection)) {
// Handle email settings subsections
$this->displayEmailSettingsPage($subsection);
} else {
$this->displaySettingsPage();
}
break;
default:
$this->displaySettingsPage();
break;
}
}
/**
* Display email settings page for specific subsection
*
* @param string $subsection The subsection to display
* @return void
*/
protected function displayEmailSettingsPage($subsection) {
// Get settings configuration
$settings_config = $this->get_settings_fields_config();
$settings = $this->getSettings();
// Filter to show only email settings
$email_config = $settings_config['email'] ?? [];
// Map subsection to the appropriate subsection in email config
$subsection_mapping = [
PagesSlugs::EMAIL_SETTINGS_GENERAL => 'general',
PagesSlugs::EMAIL_SETTINGS_INVOICE => 'invoice_available',
PagesSlugs::EMAIL_SETTINGS_QUOTE => 'quote_available',
PagesSlugs::EMAIL_SETTINGS_PAYMENT => 'payment_received',
PagesSlugs::EMAIL_SETTINGS_PAYMENT_REMINDER => 'payment_reminder',
];
$target_subsection = $subsection_mapping[$subsection] ?? 'general';
// Create a modified config with only the target subsection
$filtered_config = [
'email' => [
'title' => __('Email Settings', 'easy-invoice'),
'description' => __('Configure email sending options and templates', 'easy-invoice'),
'icon' => 'fas fa-envelope',
'subsections' => [
$target_subsection => $email_config['subsections'][$target_subsection] ?? []
]
]
];
// Include the settings page template with filtered config
include EASY_INVOICE_PLUGIN_DIR . 'templates/settings-page.php';
}
/**
* Display settings page
*
* @return void
*/
protected function displaySettingsPage() {
$settings = $this->getSettings();
// Allow developers to modify settings before display
$settings = apply_filters('easy_invoice_before_display_settings', $settings);
// Ensure payment methods are set (this is critical for gateway checkboxes)
if (!isset($settings['easy_invoice_payment_methods']) || !is_array($settings['easy_invoice_payment_methods'])) {
$settings['easy_invoice_payment_methods'] = get_option('easy_invoice_payment_methods', []);
}
// Ensure gateway order is set
if (!isset($settings['easy_invoice_payment_gateway_order']) || !is_array($settings['easy_invoice_payment_gateway_order'])) {
$settings['easy_invoice_payment_gateway_order'] = get_option('easy_invoice_payment_gateway_order', []);
}
$gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
$all_gateways_unsorted = $gateway_manager->getGateways();
$gateway_order_option = $settings['easy_invoice_payment_gateway_order'];
$payment_methods_enabled = $settings['easy_invoice_payment_methods'];
$all_gateways_sorted = [];
if (!empty($gateway_order_option)) {
foreach ($gateway_order_option as $gateway_id) {
if (isset($all_gateways_unsorted[$gateway_id])) {
$all_gateways_sorted[$gateway_id] = $all_gateways_unsorted[$gateway_id];
unset($all_gateways_unsorted[$gateway_id]);
}
}
}
$all_gateways_sorted = array_merge($all_gateways_sorted, $all_gateways_unsorted);
$settings_config = $this->get_settings_fields_config();
// Prepare template variables
$template_args = apply_filters('easy_invoice_settings_template_args', [
'settings' => $settings,
'settings_config' => $settings_config,
'all_gateways_sorted' => $all_gateways_sorted,
'payment_methods_enabled' => $payment_methods_enabled
]);
// Display the template with template args
$this->displayTemplate(
apply_filters('easy_invoice_settings_template_path', EASY_INVOICE_PLUGIN_DIR . 'templates/settings-page.php'),
$template_args
);
// Action for plugins to add their own content after the settings page
do_action('easy_invoice_after_settings_page');
}
/**
* Save payment settings
*
* @param array $posted_settings Posted settings
* @param array $response Response array
* @return void
*/
private function savePaymentSettings($posted_settings, &$response) {
// Handle payment methods
if (isset($posted_settings['easy_invoice_payment_methods']) && is_array($posted_settings['easy_invoice_payment_methods'])) {
$sanitized_methods = array_map('sanitize_key', $posted_settings['easy_invoice_payment_methods']);
update_option('easy_invoice_payment_methods', $sanitized_methods);
$response['saved_options']['easy_invoice_payment_methods'] = $sanitized_methods;
} else {
update_option('easy_invoice_payment_methods', []);
$response['saved_options']['easy_invoice_payment_methods'] = [];
}
// Handle gateway order
if (isset($posted_settings['easy_invoice_gateway_order']) && is_array($posted_settings['easy_invoice_gateway_order'])) {
$gateway_order = array_map('sanitize_key', $posted_settings['easy_invoice_gateway_order']);
update_option('easy_invoice_payment_gateway_order', $gateway_order);
$response['saved_options']['easy_invoice_gateway_order'] = $gateway_order;
} else {
update_option('easy_invoice_payment_gateway_order', []);
$response['saved_options']['easy_invoice_gateway_order'] = [];
}
// Handle gateway display names
$gateway_manager = \EasyInvoice\EasyInvoice::getInstance()->getGatewayManager();
$all_gateways = $gateway_manager->getGateways();
foreach ($all_gateways as $gateway_id => $gateway) {
$display_name_key = 'easy_invoice_gateway_display_name_' . $gateway_id;
if (isset($posted_settings[$display_name_key])) {
$display_name = wp_strip_all_tags(wp_unslash($posted_settings[$display_name_key]));
update_option($display_name_key, $display_name);
$response['saved_options'][$display_name_key] = $display_name;
}
}
// Handle gateway-specific settings
try {
$gateway_configs = $this->getGatewaySettingsConfigs();
foreach ($gateway_configs as $gateway_id => $gateway_config) {
if (isset($gateway_config['fields']) && is_array($gateway_config['fields'])) {
foreach ($gateway_config['fields'] as $option_key => $field_config) {
if (isset($posted_settings[$option_key])) {
$this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
} elseif ($field_config['type'] === 'checkbox') {
// Checkboxes not in POST are considered unchecked
update_option($option_key, 'no');
$response['saved_options'][$option_key] = 'no';
}
}
}
}
} catch (\Exception $e) {
throw $e; // Re-throw to be caught by the main try-catch
}
}
/**
* Save settings
*
* @return void
*/
public function saveSettings() {
// Verify nonce. $_POST['easy_invoice_settings_nonce'] was read unguarded, so a
// request without it raised "Undefined array key" before the check could run.
$nonce = isset($_POST['easy_invoice_settings_nonce'])
? sanitize_text_field(wp_unslash($_POST['easy_invoice_settings_nonce']))
: '';
if (!wp_verify_nonce($nonce, 'easy_invoice_settings')) {
wp_die(esc_html__('Security check failed', 'easy-invoice'));
}
// Check permissions
if (!current_user_can('manage_options')) {
wp_die(esc_html__('You do not have permission to perform this action', 'easy-invoice'));
}
$posted_settings = $_POST['settings'] ?? [];
$settings_config = $this->get_settings_fields_config();
$response = [
'success' => true,
'message' => __('Settings saved successfully', 'easy-invoice'),
'saved_options' => []
];
try {
// Process each section
foreach ($settings_config as $section_id => $section_data) {
if (isset($section_data['fields']) && is_array($section_data['fields'])) {
// Handle regular fields
foreach ($section_data['fields'] as $option_key => $field_config) {
if (isset($posted_settings[$option_key])) {
$this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
} elseif ($field_config['type'] === 'checkbox') {
// Checkboxes not in POST are considered unchecked
update_option($option_key, 'no');
$response['saved_options'][$option_key] = 'no';
}
}
} else if (isset($section_data['subsections']) && is_array($section_data['subsections'])) {
// Handle sections with subsections (like invoice, quote, email, etc.)
foreach ($section_data['subsections'] as $subsection_id => $subsection_data) {
if (isset($subsection_data['fields']) && is_array($subsection_data['fields'])) {
foreach ($subsection_data['fields'] as $option_key => $field_config) {
if (isset($posted_settings[$option_key])) {
$this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
} elseif ($field_config['type'] === 'checkbox') {
// Checkboxes not in POST are considered unchecked
update_option($option_key, 'no');
$response['saved_options'][$option_key] = 'no';
}
}
}
}
}
}
// Fields other code adds to the Tax section for display only
// (`easy_invoice_tax_section_additional_fields`) are saved with the
// same rules — the reverse-charge switch lived only there and could
// never be turned on from the screen.
$extra_tax_fields = apply_filters('easy_invoice_tax_section_additional_fields', [], []);
$known = [];
foreach ($settings_config as $section_data) {
foreach ((array) ($section_data['fields'] ?? []) as $k => $unused) { $known[$k] = true; }
foreach ((array) ($section_data['subsections'] ?? []) as $sub) { foreach ((array) ($sub['fields'] ?? []) as $k => $unused) { $known[$k] = true; } }
}
foreach ((array) $extra_tax_fields as $option_key => $field_config) {
if (!is_string($option_key) || isset($known[$option_key]) || !is_array($field_config)) {
continue;
}
if (isset($posted_settings[$option_key])) {
$this->sanitizeAndSaveSetting($option_key, $field_config, $posted_settings[$option_key], $response);
} elseif (($field_config['type'] ?? '') === 'checkbox') {
update_option($option_key, 'no');
$response['saved_options'][$option_key] = 'no';
}
}
// Handle special sections like payment methods
if (isset($settings_config['payment']) && isset($settings_config['payment']['is_special_section'])) {
$this->savePaymentSettings($posted_settings, $response);
}
// Clear any caches
wp_cache_flush();
// Clear settings cache
$this->settings_cache = null;
self::$shared_settings_cache = null;
// Log the save action
$this->log('Settings saved successfully by user: ' . get_current_user_id());
} catch (\Exception $e) {
$response['success'] = false;
$response['message'] = __('Error saving settings: ', 'easy-invoice') . $e->getMessage();
$this->log('Error saving settings: ' . $e->getMessage());
}
// Clear settings cache to ensure new fields are recognized
$this->clearCache();
// Send JSON response
wp_send_json($response);
}
/**
* Sanitize and save a single setting
*
* @param string $option_key The option key
* @param array $field_config The field configuration
* @param mixed $value The value to sanitize and save
* @param array &$response The response array to update
* @return void
*/
private function sanitizeAndSaveSetting($option_key, $field_config, $value, &$response) {
if (in_array($option_key, [
'easy_invoice_quote_accept_text',
'easy_invoice_quote_accepted_message',
'easy_invoice_quote_declined_message'
])) {
}
// Unslash the value to prevent double-escaping
$value = wp_unslash($value);
// Allow pre-sanitization filters
$value = apply_filters('easy_invoice_pre_sanitize_option', $value, $option_key, $field_config);
// Sanitize based on field type
switch ($field_config['type']) {
case 'email':
$value = sanitize_email($value);
break;
case 'url':
$value = esc_url_raw($value);
break;
case 'textarea':
// Use wp_kses_post for textarea to allow safe HTML while preventing double-escaping
$value = wp_kses_post($value);
break;
case 'wp_editor':
// For wp_editor content, use WordPress's built-in sanitization
if (is_string($value)) {
// Use wp_kses_post which expects unslashed data
$value = wp_kses_post($value);
}
break;
case 'number':
if (isset($field_config['step']) && strpos((string)$field_config['step'], '.') !== false) {
$value = floatval(wp_strip_all_tags(easy_invoice_str_replace(',', '.', $value)));
} elseif (isset($field_config['min']) && intval($field_config['min']) < 0) {
$value = intval($value);
} else {
$value = absint($value);
}
break;
case 'checkbox':
// For checkboxes, we expect 'yes' or 'no' values
$value = ($value === 'yes' || $value === '1' || $value === true) ? 'yes' : 'no';
break;
case 'select':
$value = sanitize_key($value);
// Ensure currency codes are always uppercase
if ($option_key === 'easy_invoice_currency_code') {
$value = strtoupper($value);
}
break;
case 'multiselect':
// For multiselect, ensure it's an array and sanitize each value
if (!is_array($value)) {
$value = [];
} else {
$value = array_map('sanitize_key', $value);
}
break;
default:
// For regular text fields, use wp_strip_all_tags to prevent double-escaping
$value = wp_strip_all_tags($value);
break;
}
// Allow post-sanitization value modification
$value = apply_filters("easy_invoice_sanitize_option_{$option_key}", $value, $field_config);
$value = apply_filters('easy_invoice_sanitize_option', $value, $option_key, $field_config);
// Save the setting normally
update_option($option_key, $value);
$response['saved_options'][$option_key] = $value;
}
/**
* Get all settings for the plugin
*
* @param bool $use_cache Whether to use cached settings
* @return array The settings array
*/
public function getSettings($use_cache = true): array {
// Return cached settings if available
if ($use_cache && is_array($this->settings_cache)) {
return $this->settings_cache;
}
if ($use_cache && is_array(self::$shared_settings_cache)) {
return $this->settings_cache = self::$shared_settings_cache;
}
$settings = [];
$config = $this->get_settings_fields_config();
foreach ($config as $section_id => $section_data) {
if ($section_id === 'payment' && isset($section_data['gateways'])) {
// Handle gateway fields specifically
foreach ($section_data['gateways'] as $gateway_id => $gateway_config) {
// The per-gateway display name is saved by savePaymentSettings()
// but was never loaded back, so the field always showed the default.
$display_key = 'easy_invoice_gateway_display_name_' . $gateway_id;
$display_val = get_option($display_key, '');
if ('' !== $display_val) {
$settings[$display_key] = $display_val;
}
if (isset($gateway_config['fields']) && is_array($gateway_config['fields'])) {
foreach ($gateway_config['fields'] as $option_key => $field_data) {
$default_value = $field_data['default'] ?? '';
$settings[$option_key] = get_option($option_key, $default_value);
}
}
}
}
else if (isset($section_data['subsections']) && is_array($section_data['subsections'])) {
// Handle sections with subsections (like email, invoice, quote)
foreach ($section_data['subsections'] as $subsection_id => $subsection_data) {
if (isset($subsection_data['fields']) && is_array($subsection_data['fields'])) {
foreach ($subsection_data['fields'] as $option_key => $field_data) {
$default_value = $field_data['default'] ?? '';
// Handle special defaults
if ($option_key === 'easy_invoice_email_from_name' && $default_value === get_bloginfo('name')) {
$default_value = get_bloginfo('name');
}
if ($option_key === 'easy_invoice_email_from_address' && $default_value === get_bloginfo('admin_email')) {
$default_value = get_bloginfo('admin_email');
}
// Check if field has a value_callback function
if (isset($field_data['value_callback']) && is_callable($field_data['value_callback'])) {
$settings[$option_key] = $field_data['value_callback']();
} else {
// Get option value or default
$settings[$option_key] = get_option($option_key, $default_value);
}
}
}
}
}
else if (isset($section_data['fields']) && is_array($section_data['fields'])) {
foreach ($section_data['fields'] as $option_key => $field_data) {
$default_value = $field_data['default'] ?? '';
// Handle special defaults
if ($option_key === 'easy_invoice_email_from_name' && $default_value === get_bloginfo('name')) {
$default_value = get_bloginfo('name');
}
if ($option_key === 'easy_invoice_email_from_address' && $default_value === get_bloginfo('admin_email')) {
$default_value = get_bloginfo('admin_email');
}
// Check if field has a value_callback function
if (isset($field_data['value_callback']) && is_callable($field_data['value_callback'])) {
$settings[$option_key] = $field_data['value_callback']();
} else {
// Get option value or default
$settings[$option_key] = get_option($option_key, $default_value);
}
}
}
}
// Add special array settings
$settings['easy_invoice_payment_methods'] = get_option('easy_invoice_payment_methods', []);
$settings['easy_invoice_payment_gateway_order'] = get_option('easy_invoice_payment_gateway_order', []);
// Cache settings
$this->settings_cache = $settings;
self::$shared_settings_cache = $settings;
// Allow third party to add/modify settings
return apply_filters('easy_invoice_settings', $settings);
}
/**
* Log debug information
*
* @param string $message The message to log
* @return void
*/
protected function log(string $message): void {
}
/**
* Initialize default settings
*/
public function initializeSettings() {
// One-time migration (2.1.22): strip the leftover
// "Shortcode: [easy_(invoice|quote)_url …]"
// reference line from saved email bodies. Originally intended as admin-side
// documentation, but it lived inside the email template and got emailed to
// clients verbatim. Guarded by a flag option so each install runs at most once.
//
// The regex tolerates the optional surrounding
...
because WP's // wp_kses_post may normalise the wrapping when the option is saved. if (!get_option('easy_invoice_email_shortcode_hint_removed_v2')) { foreach (['easy_invoice_invoice_email_body', 'easy_invoice_quote_email_body'] as $opt_key) { $body = get_option($opt_key, ''); if (!is_string($body) || $body === '') { continue; } $cleaned = preg_replace( '#\s*(?:\s*)?\s*Shortcode\s*:\s*\s*\s*\[easy_(?:invoice|quote)_url[^\]]*\]\s*(?:\s*
echo \EasyInvoice\Controllers\SettingsController::getTextFrom(); : echo $company_name;
//echo \EasyInvoice\Controllers\SettingsController::getTextTo(); : echo $client_name;
// /** * Get custom text setting with fallback to default * * @param string $key Text setting key * @param string $default Default text * @return string Custom text or default */ /** * Every document label the site can override, with its saved value. * * The getText*() methods below are what templates call; this list exists * for anything that needs the whole set at once -- the Pro Translations * page offers each saved label for translation. * * @return array