feat(export): gate XLSX export behind ext-zip availability

Move ExportPdfWriter out of export_helper.php into its own class
file under App\Libraries so the PDF writer can be autoloaded and
reused independently of the helper functions.

- Add runtime detection of the PHP zip extension and surface it to
  the frontend via window.ospos_ext_zip_enabled (app/Views/partial/lang_lines.php)
- Disable the XLSX export menu item in manage_tables.js and
  report_tables.js when ext-zip is missing, with a tooltip
  explaining why (new Common.export_xlsx_disabled_tooltip lang line)
- Show Zip extension status on the system info page
  (app/Views/configs/system_info.php)
- Document the new php-zip requirement and its optional nature for
  Excel/XLSX export in docs/BUILD.md and docs/INSTALL.md
- Add explicit namespaces to helper test files
  (export_helperTest.php, tabular_helperTest.php, tax_helperTest.php)
  for consistency with PSR-4 autoloading

This keeps the app fully functional without ext-zip, only disabling
the Excel export feature that depends on openspout/openspout.

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
This commit is contained in:
objecttothis committed 2026-10-23 12:39:19 +04:00
1 parent 2a42079262
commit 8a3331f9d9
12 files changed
+87 -29

No files matched your search

+2 -26
View File
@@ -1,5 +1,7 @@
<?php
use App\Libraries\ExportPdfWriter;
/**
* Shared server-side export file generation for manage-table "Export all" downloads.
*
@@ -498,32 +500,6 @@ function exportPdfFormatRowValues(array $values): array
return $values;
}
class ExportPdfWriter extends tFPDF
{
/** @var float[] */
public array $exportColumnWidths = [];
/** @var string[] */
public array $exportColumnAlignments = [];
/** @var string[] */
public array $exportHeaderValues = [];
public function Header(): void
{
$this->SetFont('DejaVuSans', 'B', EXPORT_PDF_FONT_SIZE);
foreach ($this->exportHeaderValues as $index => $value) {
$width = $this->exportColumnWidths[$index];
$text = exportPdfTruncateCellText($this, $value, $width);
$this->Cell($width, EXPORT_PDF_ROW_HEIGHT, $text, 1, 0, 'C');
}
$this->Ln();
$this->SetFont('DejaVuSans', '', EXPORT_PDF_FONT_SIZE);
}
}
// Columns rendered centered rather than left-aligned.
const EXPORT_PDF_CENTERED_COLUMNS = ['category', 'cost_price', 'unit_price', 'quantity', 'tax_percents'];
+1
View File
@@ -28,6 +28,7 @@ return [
'export_all' => 'Export All',
'export_page' => 'Export Page',
'export_scope_toggle' => 'Export all rows or current page only',
'export_xlsx_disabled_tooltip' => 'ext-zip is required in PHP for Excel export',
'fields_required_message' => 'Fields in red are required',
'fields_required_message_unique' => '',
'first_name' => 'First Name',
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Libraries;
class ExportPdfWriter extends \tFPDF
{
/** @var float[] */
public array $exportColumnWidths = [];
/** @var string[] */
public array $exportColumnAlignments = [];
/** @var string[] */
public array $exportHeaderValues = [];
public function Header(): void
{
$this->SetFont('DejaVuSans', 'B', EXPORT_PDF_FONT_SIZE);
foreach ($this->exportHeaderValues as $index => $value) {
$width = $this->exportColumnWidths[$index];
$text = exportPdfTruncateCellText($this, $value, $width);
$this->Cell($width, EXPORT_PDF_ROW_HEIGHT, $text, 1, 0, 'C');
}
$this->Ln();
$this->SetFont('DejaVuSans', '', EXPORT_PDF_FONT_SIZE);
}
}
+2 -1
View File
@@ -28,7 +28,8 @@ use Config\OSPOS;
echo "&#187; Multibyte String: ", extension_loaded('mbstring') ? '<span class="text-success">&#x2713 Enabled</span>' : '<span class="text-danger">&#x2717 Disabled</span>', '<br>';
echo "&#187; cURL: ", extension_loaded('curl') ? '<span class="text-success">&#x2713 Enabled</span>' : '<span class="text-danger">&#x2717 Disabled</span>', '<br>';
echo "&#187; JSON: ", extension_loaded('json') ? '<span class="text-success">&#x2713 Enabled</span>' : '<span class="text-danger">&#x2717 Disabled</span>', '<br>';
echo "&#187; XML: ", extension_loaded('xml') ? '<span class="text-success">&#x2713 Enabled</span>' : '<span class="text-danger">&#x2717 Disabled</span>', '<br><br>';
echo "&#187; XML: ", extension_loaded('xml') ? '<span class="text-success">&#x2713 Enabled</span>' : '<span class="text-danger">&#x2717 Disabled</span>', '<br>';
echo "&#187; Zip: ", extension_loaded('zip') ? '<span class="text-success">&#x2713 Enabled</span>' : '<span class="text-danger">&#x2717 Disabled</span>', '<br><br>';
?>
</div>
</div>
+4 -1
View File
@@ -6,7 +6,8 @@
'common_close': "<?= lang('Common.close') ?>",
'common_export_all': "<?= lang('Common.export_all') ?>",
'common_export_page': "<?= lang('Common.export_page') ?>",
'common_export_scope_toggle': "<?= lang('Common.export_scope_toggle') ?>"
'common_export_scope_toggle': "<?= lang('Common.export_scope_toggle') ?>",
'common_export_xlsx_disabled_tooltip': "<?= lang('Common.export_xlsx_disabled_tooltip') ?>"
};
$.extend(lang, {
@@ -16,4 +17,6 @@
});
})(window.lang = window.lang || {}, jQuery);
window.ospos_ext_zip_enabled = <?= extension_loaded('zip') ? 'true' : 'false' ?>;
</script>
+2
View File
@@ -32,6 +32,8 @@ That's all there is to it.
Note: If you receive messages similar to 'codeigniter4/framework v4.3.1 requires ext-intl', this is an indicator that you do not have intl enabled in php.ini
Note: If you receive messages similar to 'openspout/openspout requires ext-zip', enable the `zip` extension in php.ini. It is only needed for Excel/XLSX export; the rest of the application builds and runs fine without it, but Excel export will be disabled at runtime.
After the build tasks are complete, if you have the database set up and a preconfigured copy of .env, just drop the .env file into the root of the working folder. You should be ready to go.
If you do not have an existing (and upgraded) database, then you will need to continue from this point forward with the standard installation instructions, but at this point you have a runnable version of OSPOS.
+1 -1
View File
@@ -1,6 +1,6 @@
## Server Requirements
- PHP version `8.2.35` to `8.4.27` are supported (minimum patch levels required for CVE-2026-109618 / CVE-2026-109619 security fixes). PHP version `≤ 8.1` is NOT supported. Please note that PHP needs to have the extensions `php-json`, `php-gd`, `php-bcmath`, `php-intl`, `php-openssl`, `php-mbstring`, `php-curl` and `php-xml` installed and enabled. An unstable master build can be downloaded in the releases section.
- PHP version `8.2.35` to `8.4.27` are supported (minimum patch levels required for CVE-2026-109618 / CVE-2026-109619 security fixes). PHP version `≤ 8.1` is NOT supported. Please note that PHP needs to have the extensions `php-json`, `php-gd`, `php-bcmath`, `php-intl`, `php-openssl`, `php-mbstring`, `php-curl`, `php-xml` and `php-zip` installed and enabled. `php-zip` is required for Excel/XLSX export; without it the application still runs normally, but the Excel export option is disabled. An unstable master build can be downloaded in the releases section.
- MySQL `5.7` is supported, also MariaDB replacement `10.x` is supported and might offer better performance.
- Apache `2.4` is supported. Nginx should work fine too, see [wiki page here](https://github.com/opensourcepos/opensourcepos/wiki/Local-Deployment-using-LEMP).
- Raspberry PI based installations proved to work, see [wiki page here](<https://github.com/opensourcepos/opensourcepos/wiki/Installing-on-Raspberry-PI---Orange-PI-(Headless-OSPOS)>).
+20
View File
@@ -273,9 +273,29 @@
toggle_column_visibility();
init_export_all_toggle();
init_export_all_intercept();
init_xlsx_export_gate();
dialog_support.init("button.modal-dlg");
};
const init_xlsx_export_gate = function () {
if (window.ospos_ext_zip_enabled) {
return;
}
const $xlsxItem = $('.export [data-type="xlsx"], .export[data-type="xlsx"]');
$xlsxItem
.addClass('disabled')
.attr('aria-disabled', 'true')
.attr('title', lang.line('common_export_xlsx_disabled_tooltip'))
.off('click')
.on('click', function(event) {
event.preventDefault();
event.stopImmediatePropagation();
})
.tooltip({ container: 'body' });
};
let export_all_enabled = false;
const init_export_all_intercept = function () {
+20
View File
@@ -62,9 +62,29 @@ const report_export_toggle = (function () {
}, true);
};
const init_xlsx_export_gate = function () {
if (window.ospos_ext_zip_enabled) {
return;
}
const $xlsxItem = $('.export [data-type="xlsx"], .export[data-type="xlsx"]');
$xlsxItem
.addClass('disabled')
.attr('aria-disabled', 'true')
.attr('title', lang.line('common_export_xlsx_disabled_tooltip'))
.off('click')
.on('click', function(event) {
event.preventDefault();
event.stopImmediatePropagation();
})
.tooltip({ container: 'body' });
};
const init = function () {
init_export_all_toggle();
init_export_all_intercept();
init_xlsx_export_gate();
};
return { init };
+2
View File
@@ -1,5 +1,7 @@
<?php
namespace Tests\Helpers;
use CodeIgniter\Test\CIUnitTestCase;
use Tests\Support\OsposConfigFixtureTrait;
+2
View File
@@ -1,5 +1,7 @@
<?php
namespace Tests\Helpers;
use CodeIgniter\Config\Factories;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
+2
View File
@@ -1,5 +1,7 @@
<?php
namespace Tests\Helpers;
use App\Models\Enums\Rounding_mode;
use CodeIgniter\Test\CIUnitTestCase;