{"id":318495,"date":"2026-07-19T04:54:48","date_gmt":"2026-07-19T04:54:48","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/estimation-tool-for-woocommerce\/"},"modified":"2026-07-19T16:04:42","modified_gmt":"2026-07-19T16:04:42","slug":"quotely-estimates-for-woocommerce","status":"publish","type":"plugin","link":"https:\/\/sv.wordpress.org\/plugins\/quotely-estimates-for-woocommerce\/","author":21114905,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"3.19.2","stable_tag":"3.19.2","tested":"7.0.2","requires":"6.4","requires_php":"7.4","requires_plugins":null,"header_name":"Quotely Estimates for WooCommerce","header_author":"ruyhan","header_description":"Adds a WooCommerce product estimation interface with PDF downloads and admin submission management.","assets_banners_color":"919cd5","last_updated":"2026-07-19 16:04:42","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/ruyhan.com\/","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/quotely-estimates-for-woocommerce\/","header_author_uri":"https:\/\/ruyhan.com\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":105,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"3.19.0":{"tag":"3.19.0","author":"ruyhan","date":"2026-07-19 06:26:53"},"3.19.1":{"tag":"3.19.1","author":"ruyhan","date":"2026-07-19 12:05:45"},"3.19.2":{"tag":"3.19.2","author":"ruyhan","date":"2026-07-19 16:04:42"}},"upgrade_notice":{"2.1.0":"<p>Major rename, security, and i18n update. Configure your branding under Estimations \u2192 Settings after upgrading.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3613246,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3613246,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3613246,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3613246,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["3.19.0","3.19.1","3.19.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3613246,"resolution":"1","location":"assets","locale":"","width":1762,"height":889},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3613246,"resolution":"2","location":"assets","locale":"","width":1762,"height":889},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3613246,"resolution":"3","location":"assets","locale":"","width":1762,"height":889},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3613246,"resolution":"4","location":"assets","locale":"","width":1762,"height":889},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3613246,"resolution":"5","location":"assets","locale":"","width":1762,"height":889},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3613246,"resolution":"6","location":"assets","locale":"","width":1762,"height":889}},"screenshots":[]},"plugin_section":[262246],"plugin_tags":[62339,1764,169251,5055,286],"plugin_category":[45,50],"plugin_contributors":[272258],"plugin_business_model":[],"class_list":["post-318495","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-estimation","plugin_tags-pdf","plugin_tags-product-quote","plugin_tags-quote","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_category-media","plugin_contributors-ruyhan","plugin_committers-ruyhan"],"banners":{"banner":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/banner-772x250.png?rev=3613246","banner_2x":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/banner-1544x500.png?rev=3613246","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/icon-128x128.png?rev=3613246","icon_2x":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/icon-256x256.png?rev=3613246","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/screenshot-1.png?rev=3613246","caption":""},{"src":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/screenshot-2.png?rev=3613246","caption":""},{"src":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/screenshot-3.png?rev=3613246","caption":""},{"src":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/screenshot-4.png?rev=3613246","caption":""},{"src":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/screenshot-5.png?rev=3613246","caption":""},{"src":"https:\/\/ps.w.org\/quotely-estimates-for-woocommerce\/assets\/screenshot-6.png?rev=3613246","caption":""}],"raw_content":"<!--section=description-->\n<p>Quotely Estimates for WooCommerce adds a frontend interface where customers can search your catalog, add products and quantities to a worksheet, and download a PDF estimate. Submissions are stored in WordPress so you can review, restore, or permanently delete them later, and you can re-download the PDF for any submission from the admin.<\/p>\n\n<p><strong>Features<\/strong><\/p>\n\n<ul>\n<li>Frontend product search bound to your WooCommerce catalog (simple, variable, grouped and external products)<\/li>\n<li>Quantity controls, suggested products, and a live running total in the active currency<\/li>\n<li>Lead capture form with international phone validation (intl-tel-input \u2014 70+ countries)<\/li>\n<li>Save &amp; resume \u2014 customers email themselves a link to come back later<\/li>\n<li>Fully branded PDF export (TCPDF): company logo, name, tagline, footer note, store locations, PDF metadata \u2014 all editable from the admin<\/li>\n<li>Admin notifications: customer confirmation emails, HTML email body with token replacement, multiple admin recipients, From name\/email, optional PDF attachment<\/li>\n<li>Admin submissions list with search, sort, workflow status (new \/ contacted \/ quoted \/ won \/ lost), trash, restore, bulk actions, CSV export, PDF re-download<\/li>\n<li>Admin dashboard widget with 30-day stats and top products<\/li>\n<li><code>[estitofo_form]<\/code> shortcode and a matching Elementor widget<\/li>\n<li>WooCommerce HPOS and Cart\/Checkout Blocks compatible<\/li>\n<\/ul>\n\n<p><strong>How to use<\/strong><\/p>\n\n<ol>\n<li>Configure your branding under <em>Estimations \u2192 Settings<\/em>.<\/li>\n<li>Drop the <code>[estitofo_form]<\/code> shortcode onto any page, or use the Elementor widget <em>Estimation Tool<\/em>.<\/li>\n<li>Customers fill the form and receive a PDF; you receive the submission in <em>Estimations \u2192 Submissions<\/em>.<\/li>\n<\/ol>\n\n<h3>Premium add-on (optional)<\/h3>\n\n<p>The free plugin is a complete estimation \/ quotation tool \u2014 every feature listed above is fully usable without paying anything.<\/p>\n\n<p>If your business needs more, <strong>Quotely Estimates for WooCommerce \u2014 Pro<\/strong> is a separately-sold add-on that extends the free plugin with:<\/p>\n\n<ul>\n<li><strong>Convert estimations to WooCommerce orders<\/strong> \u2014 manually from the admin list, or automatically on submission, with your chosen initial order status.<\/li>\n<li><strong>File uploads on the customer form<\/strong> \u2014 let customers attach drawings, spec sheets or photos. Configurable max size and allowed extensions.<\/li>\n<li><strong>Google reCAPTCHA v3<\/strong> \u2014 score-based spam filtering on form submissions.<\/li>\n<li><strong>Discount \/ tax \/ shipping fields<\/strong> \u2014 add custom pricing logic directly on the customer form.<\/li>\n<li><strong>Multiple PDF templates<\/strong> \u2014 Classic \/ Modern \/ Minimal styles.<\/li>\n<li><strong>30-day analytics dashboard<\/strong> \u2014 submission funnel, won\/lost values, top products, time-series chart.<\/li>\n<li><strong>White-label mode<\/strong> \u2014 strip the plugin author credit from PDFs.<\/li>\n<\/ul>\n\n<p>The Pro add-on is sold separately on CodeCanyon. The free plugin works fully on its own and never expires or limits functionality.<\/p>\n\n<h3>Third-Party Libraries<\/h3>\n\n<ul>\n<li><strong>TCPDF<\/strong> \u2014 LGPL-3.0 \u2014 https:\/\/tcpdf.org<\/li>\n<li><strong>intl-tel-input<\/strong> \u2014 MIT \u2014 https:\/\/github.com\/jackocnr\/intl-tel-input<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/<\/code>, or install it through the WordPress Plugins screen.<\/li>\n<li>Make sure WooCommerce is installed and active.<\/li>\n<li>Activate the plugin.<\/li>\n<li>Visit <em>Estimations \u2192 Settings<\/em> to set your company branding for the PDF.<\/li>\n<li>Add <code>[estitofo_form]<\/code> to any page, or insert the Elementor widget.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20require%20woocommerce%3F\"><h3>Does this plugin require WooCommerce?<\/h3><\/dt>\n<dd><p>Yes. Product data, pricing, and currency all come from WooCommerce.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20elementor%3F\"><h3>Does it work with Elementor?<\/h3><\/dt>\n<dd><p>Yes. An <em>Estimation Tool<\/em> widget appears in the Elementor editor (Elementor 3.5 or higher).<\/p><\/dd>\n<dt id=\"is%20it%20compatible%20with%20woocommerce%20high-performance%20order%20storage%20%28hpos%29%3F\"><h3>Is it compatible with WooCommerce High-Performance Order Storage (HPOS)?<\/h3><\/dt>\n<dd><p>Yes. The plugin declares compatibility with <code>custom_order_tables<\/code> and <code>cart_checkout_blocks<\/code>.<\/p><\/dd>\n<dt id=\"how%20are%20submissions%20stored%3F\"><h3>How are submissions stored?<\/h3><\/dt>\n<dd><p>In a dedicated database table (<code>wp_estimation_submissions<\/code>). Soft-deleted rows go to a trash view and can be restored or permanently deleted.<\/p><\/dd>\n<dt id=\"what%20happens%20on%20uninstall%3F\"><h3>What happens on uninstall?<\/h3><\/dt>\n<dd><p>The submissions table and the plugin's options are removed. Active submissions are deleted.<\/p><\/dd>\n<dt id=\"how%20is%20the%20pdf%20generated%3F\"><h3>How is the PDF generated?<\/h3><\/dt>\n<dd><p>The plugin ships TCPDF (LGPL-3.0) for PDF generation. No external service is contacted.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>3.19.2<\/h4>\n\n<ul>\n<li>Fix: the View, Notes, status dropdown and Download PDF controls on the admin Submissions page did nothing, caused by a JavaScript settings-object name mismatch (estitofoAdmin vs estitofo_admin) that stopped the admin script from initialising.<\/li>\n<\/ul>\n\n<h4>3.19.1<\/h4>\n\n<ul>\n<li>Fix: PDF download showed a critical error because two required TCPDF library files (<code>tcpdf_autoconfig.php<\/code> and the core font metric files) were missing from the bundled <code>lib\/<\/code> directory. PDF generation now works again on all sites.<\/li>\n<\/ul>\n\n<h4>3.19.0<\/h4>\n\n<ul>\n<li>WordPress.org review: renamed the plugin from the generic \"Estimation Tool for WooCommerce\" to the distinctive <strong>Quotely Estimates for WooCommerce<\/strong>, with a matching slug\/text domain of <code>quotely-estimates-for-woocommerce<\/code>. The internal <code>estitofo_<\/code> code prefix, the <code>[estitofo_form]<\/code> shortcode and stored option keys are unchanged, so existing installs upgrade without data loss.<\/li>\n<\/ul>\n\n<h4>3.18.0<\/h4>\n\n<ul>\n<li>WordPress.org review: replaced the bundled <code>tcpdf\/<\/code> directory with a Composer + Strauss-managed copy under <code>lib\/<\/code> where the main class is prefixed to <code>Estitofo_TCPDF<\/code> (and all sub-classes likewise). This eliminates the naming conflict that would occur if another plugin bundles a different TCPDF version \u2014 the classes are now completely isolated under the plugin's own namespace.<\/li>\n<li>All plugin code updated to reference <code>Estitofo_TCPDF<\/code> instead of <code>TCPDF<\/code>.<\/li>\n<li>All files under <code>lib\/<\/code> include a <code>defined('ABSPATH') || exit;<\/code> direct-access guard as required by WordPress.org.<\/li>\n<\/ul>\n\n<h4>3.17.0<\/h4>\n\n<ul>\n<li>WordPress.org review: moved TCPDF's <code>K_TCPDF_EXTERNAL_CONFIG<\/code> and <code>K_TCPDF_THROW_EXCEPTION_ERROR<\/code> constants into <code>tcpdf\/tcpdf.php<\/code> (where they belong as library configuration) so they no longer appear in the plugin's own namespace scan.<\/li>\n<li>WordPress.org review: added a <code>class_exists('TCPDF')<\/code> early-return guard at the top of <code>tcpdf\/tcpdf.php<\/code> so the library is self-protecting against double-load regardless of load order.<\/li>\n<li>Code quality: resolved all remaining WordPress Coding Standards violations \u2014 Yoda conditions, short ternaries, mixed class\/function declaration, camelCase variable names, reserved keyword parameter names, deprecated <code>current_time('timestamp')<\/code>, translator comment placement, and inline comment punctuation.<\/li>\n<\/ul>\n\n<h4>3.16.9<\/h4>\n\n<ul>\n<li>Raised the minimum supported WordPress version (<code>Requires at least<\/code>) from <code>5.6<\/code> to <code>6.4<\/code> in both the plugin header and <code>readme.txt<\/code>.<\/li>\n<li>Confirmed <code>Tested up to: 7.0<\/code> (current WordPress stable) in both files.<\/li>\n<li>Hardened TCPDF loading per WordPress.org review: the library is included only behind a <code>class_exists('TCPDF')<\/code> guard, and TCPDF's own <code>K_TCPDF_EXTERNAL_CONFIG<\/code> \/ <code>K_TCPDF_THROW_EXCEPTION_ERROR<\/code> constants are wrapped in <code>defined()<\/code> checks to avoid conflicts with other plugins bundling TCPDF.<\/li>\n<li>Fixed uninstall cleanup: rate-limit transients are now swept using the correct <code>estitofo_rl_<\/code> key prefix (previously the obsolete <code>wc_est_rl_<\/code> prefix left orphaned rows in <code>wp_options<\/code>).<\/li>\n<\/ul>\n\n<h4>3.16.8<\/h4>\n\n<ul>\n<li>Fixed mismatched \"Tested up to\" header: the plugin file header still said <code>Tested up to: 6.9<\/code> while <code>readme.txt<\/code> had already been bumped to <code>7.0<\/code>. Plugin Check's <code>mismatched_tested_up_to<\/code> rule (severity 7) was the only critical error remaining. Both files are now in sync at <code>7.0<\/code> (current WordPress major release).<\/li>\n<li>Plugin Check <code>missing_direct_file_access_protection<\/code> (severity 6) on all TCPDF vendor files: added <code>defined('ABSPATH') || exit;<\/code> immediately after the file-level docblock in each of the 31 PHP files under <code>tcpdf\/<\/code>. The check ships expects every PHP file in a plugin to refuse direct access. Since TCPDF is only ever loaded from inside WordPress (where <code>ABSPATH<\/code> is defined), this guard never triggers and library behavior is unchanged.<\/li>\n<\/ul>\n\n<h4>3.16.7<\/h4>\n\n<ul>\n<li>Renamed the main plugin file from <code>woocommerce-estimation-tool.php<\/code> to <code>estimation-tool-for-woocommerce.php<\/code> so it matches the WordPress.org plugin slug. Plugin Check's \"mismatched_plugin_slug\" rule (severity 7) was the only critical-level error remaining; this fixes it. Internal references use <code>__FILE__<\/code>\/<code>plugin_basename(__FILE__)<\/code> so no other code change was needed; the <code>.pot<\/code> translation template's file-reference comments were updated to match.<\/li>\n<li><code>uninstall.php<\/code> rewritten: all logic is now wrapped in <code>estitofo_uninstall_cleanup()<\/code> so no variables leak into the global scope. <code>@unlink()<\/code> calls replaced with <code>wp_delete_file()<\/code>. The <code>@rmdir()<\/code> call replaced with <code>$wp_filesystem-&gt;rmdir()<\/code> after loading <code>wp-admin\/includes\/file.php<\/code>.<\/li>\n<li><code>class-estimation-pdf.php<\/code>: every <code>error_log()<\/code> call is now tagged with <code>\/\/ phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log<\/code> (calls remain gated on <code>WP_DEBUG<\/code>, so they only fire in development). The three-line <code>fopen<\/code> \/ <code>fread<\/code> \/ <code>fclose<\/code> block in <code>png_has_alpha()<\/code> is now a single bounded <code>file_get_contents($path, false, null, 0, 26)<\/code> call.<\/li>\n<li><code>class-estimation-settings.php<\/code> import handler: <code>file_get_contents()<\/code> of the uploaded JSON file replaced with <code>$wp_filesystem-&gt;get_contents()<\/code> via the WP_Filesystem API.<\/li>\n<li><code>class-estimation-mailer.php<\/code>: the <code>@file_put_contents()<\/code> call that drops an empty <code>index.html<\/code> into the tmp directory (to prevent directory listing) replaced with <code>$wp_filesystem-&gt;put_contents()<\/code>.<\/li>\n<li><code>class-estimation-rest.php<\/code>: added <code>\/\/ phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query<\/code> on the optional <code>tax_query<\/code> branch \u2014 the query is opt-in and capped by <code>posts_per_page<\/code>.<\/li>\n<li>Public PDF endpoint (<code>Estitofo_Plugin::public_pdf<\/code>): wrapped <code>$_REQUEST<\/code> reads in a <code>phpcs:disable WordPress.Security.NonceVerification.Recommended<\/code> block. The endpoint is HMAC-token-gated (validated via <code>hash_equals()<\/code>), so a nonce isn't applicable.<\/li>\n<li>TCPDF vendor files: added a proper <code>\/** *\/<\/code> file-level docblock to the top of every PHP file under <code>tcpdf\/<\/code> so Plugin Check's <code>missing_doc_comment<\/code> rule (which doesn't honor <code>phpcs:ignoreFile<\/code>) is satisfied. The existing <code>phpcs:ignoreFile<\/code> pragma stays on the next line for PHPCS rules. TCPDF's own header comments below are unchanged.<\/li>\n<\/ul>\n\n<h4>3.16.6<\/h4>\n\n<ul>\n<li>Plugin Check cleanup: renamed the six intl-tel-input retina image files from <code>flags@2x.png<\/code> \/ <code>flags@2x.webp<\/code> \/ <code>globe@2x.png<\/code> \/ <code>globe@2x.webp<\/code> (and the <code>_light<\/code> variants) to dash-separated names (<code>flags-2x.*<\/code>, <code>globe-2x.*<\/code>). The <code>@<\/code> character isn't allowed in file names per WordPress.org's naming rules. CSS updated to reference the new names.<\/li>\n<li>Removed four unused intl-tel-input assets (<code>globe_light.png\/webp<\/code>, <code>globe_light@2x.png\/webp<\/code>) \u2014 never referenced by CSS or JS, removing them shrinks the plugin slightly.<\/li>\n<li>Deleted the unused <code>tcpdf\/config\/tcpdf_config.php<\/code> file (the 3.16.4 release switched TCPDF to load via <code>K_TCPDF_EXTERNAL_CONFIG = true<\/code>, so this config file was no longer loaded by anything and was only triggering Plugin Check noise).<\/li>\n<li>Added <code>\/\/ phpcs:ignoreFile<\/code> pragma to the top of every PHP file under <code>tcpdf\/<\/code> to signal \"this is a third-party library, exempt from WordPress coding standards\" to Plugin Check \/ PHPCS \/ WP.org's automated review. TCPDF's existing C-style file headers are preserved untouched on the lines below.<\/li>\n<\/ul>\n\n<h4>3.16.4<\/h4>\n\n<ul>\n<li>Fixed \"Constant K_TCPDF_THROW_EXCEPTION_ERROR already defined\" PHP warning that appeared on the first PDF generation after the 6.11.3 upgrade. TCPDF 6.11.x's <code>config\/tcpdf_config.php<\/code> dropped the <code>if (!defined(...))<\/code> guards on its <code>define()<\/code> calls, so any constant the host plugin pre-defines (we set <code>K_TCPDF_THROW_EXCEPTION_ERROR<\/code> to <code>true<\/code> so PDF errors throw catchable exceptions instead of <code>die()<\/code>-ing) triggered a redefinition warning. Fixed by setting <code>K_TCPDF_EXTERNAL_CONFIG = true<\/code> before loading TCPDF \u2014 <code>tcpdf_autoconfig.php<\/code> still supplies all the same <code>PDF_*<\/code> \/ <code>K_*<\/code> defaults but through guarded checks. No TCPDF source files modified.<\/li>\n<\/ul>\n\n<h4>3.16.3<\/h4>\n\n<ul>\n<li>Upgraded the bundled TCPDF library from 6.10.0 to 6.11.3 (latest stable). Brings PHP 8.5 deprecation fixes (curl_close, null array offset, imagedestroy, xml_parser_free), font-subsetting checksum fix, SVG rendering fix, image-on-footer fix, and the security\/path-traversal fixes from 6.9.1\u20136.9.3. No API changes affect this plugin \u2014 generated PDFs are byte-identical aside from a <code>\/Producer<\/code> header bump.<\/li>\n<li>Bumped \"Tested up to\" to WordPress 6.9 (was 6.7) so the plugin shows up correctly in the WordPress.org plugin directory's compatibility filter.<\/li>\n<li>Trimmed the TCPDF bundle further: dropped the unused <code>images\/<\/code> (demo logo), <code>tools\/<\/code> (CLI utilities), <code>Makefile<\/code>, and <code>CHANGELOG.TXT<\/code> from the shipped library \u2014 plugin remains under 4 MB.<\/li>\n<\/ul>\n\n<h4>3.16.2<\/h4>\n\n<ul>\n<li>Settings page layout cleanup: the form is now rendered as a single unified card. Previously, tabs with multiple stacked <code>form-table<\/code> blocks (especially Pro Features) showed inconsistent padding, asymmetric borders, and section headings floating between cards. Section <code>&lt;h2&gt;<\/code> headings inside the form are now styled as proper section dividers, row spacing is even across all tabs, and the submit-button row integrates with the card via a hairline rule.<\/li>\n<\/ul>\n\n<h4>3.16.1<\/h4>\n\n<ul>\n<li>Added an honest \"Premium add-on\" section to the readme description listing what the optional Pro add-on adds on top of the free plugin (WC order conversion, file uploads, reCAPTCHA, analytics, more PDF templates). Free plugin remains 100% fully functional on its own.<\/li>\n<li>Added one modest italicised line at the bottom of the Settings page mentioning the Pro add-on (hidden automatically when Pro is installed). Single sentence, single link \u2014 no buttons, no banners, no popups.<\/li>\n<li><code>estitofo_pro_purchase_url<\/code> filter so site owners \/ Pro can override the upsell URL.<\/li>\n<\/ul>\n\n<h4>3.16.0<\/h4>\n\n<ul>\n<li>WordPress.org review revision \u2014 addresses all reviewer feedback:\n\n<ul>\n<li>PDF and Notifications settings tabs are now <strong>fully functional in the free plugin<\/strong> (no locked UI, no Pro gating). All branding fields \u2014 company name, logo, footer, From name\/email, customer email body with tokens, multiple admin recipients, PDF attachment \u2014 are available to every user.<\/li>\n<li>Renamed internal prefix from generic <code>wc_estimation_*<\/code> \/ <code>WC_Estimation_*<\/code> to plugin-unique <code>estitofo_*<\/code> \/ <code>Estitofo_*<\/code> throughout: classes, defined constants, option keys, transients, AJAX action names, hook names, nonces, REST namespace, JS globals, shortcode (<code>[estitofo_form]<\/code>). The plugin slug and text domain stay the same.<\/li>\n<li>Inline <code>&lt;script&gt;<\/code> \/ <code>&lt;style&gt;<\/code> blocks in admin pages moved into a dedicated <code>assets\/js\/admin-settings.js<\/code> enqueued via <code>admin_enqueue_scripts<\/code>, and a new section in <code>assets\/css\/admin-estimation.css<\/code>. No more inline tags in PHP output.<\/li>\n<li><code>apply_filters('estitofo_save_settings', \u2026)<\/code> now receives an early-sanitized snapshot of the submitted array (every value run through <code>sanitize_text_field<\/code>) instead of the raw <code>$_POST<\/code> data. Add-ons can re-sanitize their own keys as needed.<\/li>\n<li>Added <code>Requires Plugins: woocommerce<\/code> header so WordPress can pre-check the WooCommerce dependency before activation.<\/li>\n<li>Pro upsell UI removed from the free plugin \u2014 buyers who want Pro find it on the readme description; the free plugin no longer promotes paid features in the admin.<\/li>\n<\/ul><\/li>\n<\/ul>\n\n<h4>3.15.3<\/h4>\n\n<ul>\n<li>Plugin header: Plugin URI and Author URI now differ \u2014 Plugin URI points to the WordPress.org listing for this plugin, Author URI stays as the author's homepage. Fixes the WP.org submission validation error.<\/li>\n<\/ul>\n\n<h4>3.15.2<\/h4>\n\n<ul>\n<li>Pro upsell links centralised behind a single constant <code>Estitofo_Plugin::PRO_PURCHASE_URL<\/code> so the destination is easy to swap when the Pro item is published. Default points to a CodeCanyon search URL (no 404) until the author updates it with the live CodeCanyon item URL.<\/li>\n<\/ul>\n\n<h4>3.15.1<\/h4>\n\n<ul>\n<li>Slimmer footprint: stripped unused TCPDF font assets (CJK fonts, Arabic fonts, DejaVu TTF source folders, .z compressed font streams) \u2014 the plugin uses only the built-in PDF14 core fonts (Helvetica \/ Courier \/ Times \/ Symbol \/ ZapfDingbats), so 24 MB of unused font data has been removed. Plugin now downloads at ~1.5 MB instead of 6+ MB. PDF generation is byte-identical.<\/li>\n<\/ul>\n\n<h4>3.15.0<\/h4>\n\n<ul>\n<li>New \"Frontend Subheading\" field on the General settings tab \u2014 the smaller line of text shown under the hero title is now fully editable. Leave it blank to hide the subheading entirely.<\/li>\n<li>Both heading and subheading are exposed as shortcode attributes (<code>[wc_estimation_tool heading=\"\u2026\" subheading=\"\u2026\"]<\/code>) and Elementor widget controls.<\/li>\n<li>General tab heading field gets a helpful description: \"Big title shown above the estimation form. Leave blank to use the default.\"<\/li>\n<\/ul>\n\n<h4>3.14.1<\/h4>\n\n<ul>\n<li>Search icon no longer overlaps the placeholder when themes override input padding. Forced <code>padding-left: 48px !important<\/code> on <code>.wc-search-input<\/code> (via a more specific <code>.wc-estimation-wrapper .wc-search-input<\/code> selector), pinned the icon to <code>left: 16px \/ width: 18px<\/code> with <code>!important<\/code>, and added <code>text-indent: 0 !important<\/code> to neutralise theme rules. Net result: at least 14px clear space between the icon's right edge and the start of the placeholder text on every theme.<\/li>\n<\/ul>\n\n<h4>3.14.0<\/h4>\n\n<ul>\n<li>Fixed: Default Phone Country setting (e.g. <code>bd<\/code>) wasn't reflecting on the frontend because <code>enqueue_assets<\/code> was reading the legacy standalone option <code>wc_estimation_default_country<\/code> instead of the consolidated <code>wc_estimation_settings<\/code> array. Now uses <code>Estitofo_Options::get('default_country')<\/code> so the saved value is honoured.<\/li>\n<li>Default \/ Restrict Country settings are now native <code>&lt;select&gt;<\/code> dropdowns with flag emoji, country name and dial code (e.g. \ud83c\udde7\ud83c\udde9 Bangladesh (+880)) \u2014 no more typing ISO codes from memory.<\/li>\n<li>Phone validator tightened back up: only the <strong>selected<\/strong> country is probed. Previous \"OR across many countries\" multi-check let invalid numbers slip through by accidentally matching a different country's pattern. New flow: try input as-typed \u2192 try leading-zero-stripped \u2192 both against the selected country only. Country mismatch (BD digits with US flag) is correctly rejected; valid BD <code>01851932715<\/code> with BD flag is correctly accepted.<\/li>\n<\/ul>\n\n<h4>3.13.0<\/h4>\n\n<ul>\n<li>Phone validator now runs the user's input through every libphonenumber call combination (<code>isValidNumber<\/code> + <code>isPossibleNumber<\/code>, with raw input + E.164 + leading-zero-stripped form, with both upper- and lower-case ISO country codes) and accepts if ANY of them returns true. Bangladesh's leading-zero national format like <code>01851932715<\/code> now reliably validates against the BD flag because at least one of those combinations will recognise it.<\/li>\n<li>Added an opt-in console diagnostic: append <code>?wcestphonedebug=1<\/code> to the URL and every validation prints <code>{input, country, accepted, tries: [...]}<\/code> to the browser console \u2014 makes it trivial to see which library call accepted\/rejected a given number.<\/li>\n<\/ul>\n\n<h4>3.12.0<\/h4>\n\n<ul>\n<li>Phone validation rewritten to trust intl-tel-input + libphonenumber directly \u2014 no more layered custom logic. Switched <code>nationalMode<\/code> to <code>true<\/code> so the library expects users to type their local format (e.g. <code>01851932715<\/code> with the leading zero), which is what people actually do. <code>isValidNumber()<\/code> is now the single source of truth: green tick when it returns true, specific error message via <code>getValidationError()<\/code> when false.<\/li>\n<\/ul>\n\n<h4>3.11.0<\/h4>\n\n<ul>\n<li>Phone validation now correctly rejects country\/number mismatches. Previous version had a \"7\u201315 digits\" universal fallback that accepted a Bangladesh number under the US flag. The new validator parses the input into E.164 (via <code>iti.getNumber()<\/code>), verifies the resulting prefix matches the selected country's dial code, then asks <code>isPossibleNumber()<\/code> whether the full E.164 is plausible for that country. Valid <code>01851932715<\/code> with \ud83c\udde7\ud83c\udde9 selected passes; the same digits with \ud83c\uddfa\ud83c\uddf8 selected now fail.<\/li>\n<\/ul>\n\n<h4>3.10.0<\/h4>\n\n<ul>\n<li>Phone validation: now correctly accepts national-prefix forms like Bangladesh's <code>01851932715<\/code>. Previously libphonenumber's <code>isValidNumber()<\/code> rejected the leading-zero national form even though it's how 99% of users actually type their number. The validator now layers four soft-pass checks: <code>isValidNumber()<\/code> \u2192 <code>isPossibleNumber()<\/code> \u2192 <code>getValidationError()<\/code> IS_POSSIBLE codes \u2192 digit-count plausibility (7\u201315). The wrong-country <code>+xx<\/code> prefix check remains a hard fail so the BD-on-US-flag case is still rejected.<\/li>\n<\/ul>\n\n<h4>3.9.2<\/h4>\n\n<ul>\n<li>Phone validation: real-world-tolerant. libphonenumber's <code>isValidNumber()<\/code> rejects perfectly-good numbers that aren't in the strictest canonical form (leading-zero national prefix, mobile\/landline-only ranges, etc.). The validator now treats those as acceptable when <code>getValidationError()<\/code> reports <code>IS_POSSIBLE<\/code> (0) or <code>IS_POSSIBLE_LOCAL_ONLY<\/code> (4) \u2014 the field clears silently. Only real format errors (wrong country, too short, too long, invalid length) block submission.<\/li>\n<li>Mismatched-country detection retained: typing <code>+xx\u2026<\/code> with a different flag selected still fails fast.<\/li>\n<\/ul>\n\n<h4>3.9.1<\/h4>\n\n<ul>\n<li>Hotfix: the bundled <code>utils.js<\/code> was distributed as an ES module ending with <code>export default utils;<\/code>. When loaded via a regular  tag (as we and intl-tel-input both do) the <code>export<\/code> keyword threw <code>Uncaught SyntaxError: Unexpected token 'export'<\/code>, the global <code>window.intlTelInputUtils<\/code> was never set, and all country-aware phone validation silently degraded to the lenient fallback. Replaced the trailing 3 lines so the file works as a classic script: assigns <code>window.intlTelInputUtils = window.intlTelInputUtilsTemp<\/code>.<\/li>\n<\/ul>\n\n<h4>3.9.0<\/h4>\n\n<ul>\n<li>Phone validation no longer hangs on \"Checking phone number\u2026\". The intl-tel-input <code>utils.js<\/code> library (country rules) is now pre-enqueued via <code>wp_enqueue_script<\/code> so it loads with the page instead of being lazy-loaded by intl-tel-input after init \u2014 eliminates the race window where validation got stuck waiting.<\/li>\n<li>If <code>utils.js<\/code> is still blocked (CSP \/ hardened host), the validator now falls back to a 7\u201315 digit length check after 3 seconds instead of staying in \"Checking\u2026\" forever.<\/li>\n<\/ul>\n\n<h4>3.8.0<\/h4>\n\n<ul>\n<li>Phone validation: strict mismatched-country detection. Selecting a US flag and typing a Bangladesh number (or vice versa) now fails validation instantly with \"Doesn't look like a valid United States number.\" Cross-checks <code>isValidNumber()<\/code> against <code>intlTelInputUtils.getRegionCodeForNumber()<\/code> and against any user-typed <code>+xx<\/code> prefix.<\/li>\n<li>Form submit is now blocked while <code>utils.js<\/code> is still loading instead of falling through to a lenient length check \u2014 eliminates the race window where an invalid number could slip in before country rules arrived.<\/li>\n<li>The submit button visually disables (greys out) while the phone field is invalid; the validation pill under the input shows a \u2713 or \u26a0 icon.<\/li>\n<li>Server-side: extra phone sanity check (7\u201315 digit length) and country-prefix verification when \"Restrict to Country\" is configured.<\/li>\n<\/ul>\n\n<h4>3.7.0<\/h4>\n\n<ul>\n<li>PNG logos with transparent backgrounds now stay transparent in the generated PDF instead of being flattened onto white. When GD or Imagick is available TCPDF's _parsepng() extracts the alpha channel and embeds it as a PDF soft mask \u2014 your brand mark blends with the brand-coloured header band.<\/li>\n<li>The white-background flatten is only used as a true fallback when neither extension is available.<\/li>\n<\/ul>\n\n<h4>3.6.0<\/h4>\n\n<ul>\n<li>Fixed: TCPDF crashed with <code>TypeError: in_array(): Argument #2 must be of type array, null given<\/code> and a chain of \"Undefined property\" warnings on PHP 8.2.x + opcache. Root cause: TCPDF 6.10 declares properties like <code>protected $fontkeys = array();<\/code> but never reassigns them in <code>__construct()<\/code>. On certain PHP\/opcache combinations, the class-property default isn't materialised on the instance, so every later read returns NULL. We now run a reflection-based property pre-seed right after <code>new TCPDF()<\/code> that force-initialises <code>fontkeys<\/code>, <code>numfonts<\/code>, <code>fonts<\/code>, <code>font_subsetting<\/code>, <code>pdfa_mode<\/code>, <code>isunicode<\/code>, <code>FillColor<\/code>, <code>DrawColor<\/code>, <code>TextColor<\/code>, <code>CoreFonts<\/code>, and 9 other dependencies. The fix is contained inside our class \u2014 TCPDF itself isn't patched.<\/li>\n<\/ul>\n\n<h4>3.5.0<\/h4>\n\n<ul>\n<li>Switched the default PDF font to core 'helvetica' instead of 'dejavusans'. Avoids loading external TTF font files \u2014 fixes the PHP 8.2+ \"Undefined property: TCPDF::$fontkeys\" warnings and the cascading <code>in_array(NULL)<\/code> fatal that hit hosts without the GD extension. Non-Latin currency symbols still display correctly via the ISO-code fallback in money().<\/li>\n<li>Logo diagnostic now gives <strong>specific, actionable instructions<\/strong> when it detects \"PNG with alpha + no GD + no Imagick\" \u2014 the most common silent-failure cause. Tells the admin to either (a) upload a JPG \/ flat PNG, (b) enable the PHP <code>gd<\/code> extension in php.ini, or (c) install Imagick.<\/li>\n<li>Removed an experimental pure-PHP PNG alpha-strip that would have produced visually-corrupt logos.<\/li>\n<\/ul>\n\n<h4>3.4.0<\/h4>\n\n<ul>\n<li>Logo: auto-backfills <code>logo_id<\/code> from <code>logo_url<\/code> on the PDF tab \u2014 older saved logos now work without re-uploading.<\/li>\n<li>Logo embed: two-stage fallback \u2014 first tries raw-bytes via TCPDF's <code>@<\/code> prefix, then falls back to a regular path-based <code>Image()<\/code> call. If both fail, WP_DEBUG logs the exact stage that broke.<\/li>\n<li>Logo diagnostic is now always visible (not hidden behind a \"details\" toggle) and shows: resolved path, readable flag, file size, extension, GD\/Imagick availability, TCPDF loaded, safe-path, and bytes loaded.<\/li>\n<\/ul>\n\n<h4>3.3.0<\/h4>\n\n<ul>\n<li>PDF logo: now embedded as raw image data via TCPDF's <code>@<\/code> prefix instead of a filesystem path. This bypasses every open_basedir \/ mixed-slash \/ hardened-host issue that was preventing the logo from appearing.<\/li>\n<li>New logo diagnostic readout in the Pro PDF tab \u2014 shows the resolved path, file size, and a precise reason if the logo can't be embedded.<\/li>\n<li>PDF design refinement: gradient-style header band (primary colour + accent triangle on the right + razor-thin lighter strip below), bigger total panel with a brand-coloured TOTAL stripe at the bottom, tighter spacing.<\/li>\n<li>Search icon: fixed overlap with placeholder text on themes that override input line-height. Input is now a fixed 50px tall with line-height matched for vertical centering, icon explicitly 20\u00d720 with <code>!important<\/code> size overrides.<\/li>\n<\/ul>\n\n<h4>3.2.0<\/h4>\n\n<ul>\n<li>Fixed: search icon was clipping into the input on some themes \u2014 search input is now a fixed 48px tall with absolute-positioned 20px icon at 16px left inset.<\/li>\n<li>Admin: complete settings page redesign \u2014 modern card layout, branded heading badge, soft shadows, sticky tab styling, focus rings on inputs.<\/li>\n<li>Admin: submissions list \u2014 coloured status pills (using <code>:has()<\/code> selector), animated modal with backdrop blur, customer info card in modal, cleaner action buttons.<\/li>\n<li>Admin: Pro features showcase card at the top of the General tab \u2014 8-feature grid with icons, shows \"Pro license active\" when licensed.<\/li>\n<li>Logo resolution: hardened <code>resolve_logo_path()<\/code> with path normalization (collapses mixed slashes), 4-stage fallback (attachment ID \u2192 URL parse \u2192 reverse-resolve URL \u2192 strip thumb suffix), and <code>WP_DEBUG<\/code> error_log diagnostics when resolution fails.<\/li>\n<\/ul>\n\n<h4>3.1.0<\/h4>\n\n<ul>\n<li>Modern frontend redesign: hero card with gradient + brand-tinted \"stardust\", soft-shadow product cards, animated total counter, confetti on successful submit.<\/li>\n<li>Featured \/ popular products grid auto-renders below the search bar when the cart is empty.<\/li>\n<li>3-step progress indicator (Browse \u2192 Your details \u2192 Done) advances visually as the customer moves through the flow.<\/li>\n<li>Country-aware phone validation \u2014 switching the flag re-validates against that country's format with a specific error message (\"Number is too short for Bangladesh\", etc.).<\/li>\n<li>Toast notifications replace inline yellow banners (success \/ warning \/ error variants).<\/li>\n<li>Sticky cart summary on mobile when the cart has items.<\/li>\n<li>PDF: complete redesign with brand-coloured header band, big \"ESTIMATION\" title, Bill-To card, zebra-striped product rows, total panel, and page numbers; product images render properly; PNG-with-alpha logos are auto-flattened; missing thumbnails get a vector placeholder; small quotations fit on a single page (no blank trailing page).<\/li>\n<li>Pro logo upload now stores attachment ID alongside the URL \u2014 PDF generation uses <code>get_attached_file()<\/code> for rock-solid path resolution. Logo preview + Remove button in the admin.<\/li>\n<li>Currency symbol HTML entities (e.g. <code>&amp;#2547;<\/code> for BDT) now decode correctly; non-Latin symbols fall back to the ISO code (BDT, INR, THB, KRW, \u2026) so amounts always render.<\/li>\n<li>Settings \u2192 Tools tab gets a \"How to add the form to a page\" onboarding card with copy-to-clipboard shortcode, Elementor widget instructions (with auto-detection pill), and PHP template snippet.<\/li>\n<li>Save &amp; resume: cart contents + contact fields persist to localStorage and survive a page refresh.<\/li>\n<li>Robust product search: simple \/ variable \/ grouped \/ external products all included; cascading fallbacks via <code>wc_get_products<\/code>, <code>name__like<\/code>, SKU, and finally <code>WP_Query<\/code>.<\/li>\n<\/ul>\n\n<h4>3.0.0<\/h4>\n\n<ul>\n<li>Settings consolidated under a single <code>wc_estimation_settings<\/code> option (one-time migration from v2.1 keys).<\/li>\n<li>Added save &amp; resume \u2014 customers email themselves a link that loads their estimation back in.<\/li>\n<li>Added admin workflow statuses (new \/ contacted \/ quoted \/ won \/ lost) with inline editing.<\/li>\n<li>Added admin dashboard widget (30-day rollup, latest leads, top products).<\/li>\n<li>Added CSV export from the admin list.<\/li>\n<li>Added REST API namespace <code>wc-estimation\/v1<\/code> (search, suggested, categories, save, resume) \u2014 front-end now uses REST with an admin-ajax fallback.<\/li>\n<li>Product search now matches simple, variable, grouped and external products (was simple-only).<\/li>\n<li>PDF customization and Notifications are now Pro features. The free plugin still generates PDFs and sends a basic admin notification using defaults; the tabs in Settings show a Pro upgrade teaser when Pro is not active.<\/li>\n<li>Pro add-on integration: tab filter, settings-save filter, default-settings filter.<\/li>\n<li>Uninstall now also cleans up rate-limit transients and tmp PDF directory.<\/li>\n<\/ul>\n\n<h4>2.1.0<\/h4>\n\n<ul>\n<li>Renamed to \"Estimation Tool for WooCommerce\" for trademark compliance.<\/li>\n<li>Added a Settings page for company name, logo, footer note, and store list (PDF branding is no longer hard-coded).<\/li>\n<li>International phone validation via intl-tel-input (no longer limited to Bangladesh); default and restricted country are configurable.<\/li>\n<li>Prefixed all AJAX action names with <code>wc_estimation_<\/code> to avoid collisions.<\/li>\n<li>Switched admin PDF download to <code>admin-post.php<\/code> POST with nonce in body.<\/li>\n<li>Added honeypot and per-IP rate limiting to public AJAX endpoints.<\/li>\n<li>Whitelisted list-table sort columns; sanitized all <code>$_REQUEST<\/code> reads.<\/li>\n<li>Replaced raw <code>date()<\/code> calls with <code>date_i18n()<\/code> \/ <code>gmdate()<\/code>.<\/li>\n<li>Removed debug <code>console.log<\/code>; internationalized all frontend strings via <code>wp_localize_script<\/code>.<\/li>\n<li>Replaced direct HTML interpolation in JS with safe DOM construction (XSS hardening).<\/li>\n<li>Declared compatibility with WooCommerce HPOS and Cart\/Checkout Blocks.<\/li>\n<li>Added Elementor minimum-version guard (3.5+).<\/li>\n<li>Added DB schema version option for forward-compatible migrations.<\/li>\n<li>Uninstall now removes plugin options as well as the data table.<\/li>\n<\/ul>\n\n<h4>2.0.0<\/h4>\n\n<ul>\n<li>Improved data sanitization and security.<\/li>\n<li>Added separate admin JS\/CSS assets.<\/li>\n<li>Added suggested product support for the frontend.<\/li>\n<li>Added uninstall cleanup and a WordPress readme file.<\/li>\n<\/ul>","raw_excerpt":"Let visitors build a WooCommerce product estimate, capture their contact details, and download a branded PDF quotation.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/318495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=318495"}],"author":[{"embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/ruyhan"}],"wp:attachment":[{"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=318495"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=318495"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=318495"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=318495"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=318495"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/sv.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=318495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}