Files
opensourcepos/app/Views/items/manage.php
objecttothisandTravis Garrison 5c9b1b81e6 fix(xss): remove redundant escaping that double-encoded item attribute values (#4628)
* fix(xss): remove redundant escaping that double-encoded item attribute values

- Remove esc()/html_entity_decode() calls now that output is escaped
  at render time by the framework, preventing double-encoding of
  special characters in attribute names, units, and definition values
- Fix employee_name form_input value fields to stop pre-escaping
  before form_input applies its own escaping
- Reorder Items.php use statements and add missing BaseConnection import
- Change items/manage.php start_date from let to plain assignment for
  proper reassignment scope

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(sales): add regression tests for permission checks on sales endpoints

- Ensure role-based permissions correctly restrict access to sensitive actions like price edits, receipt/invoice views, and report generation.
- Add tests for both granted and restricted user scenarios to validate the behavior.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(attributes): validate `attribute_value` before processing

- Add checks to ensure `attribute_value` is a non-empty string in `postSaveAttributeValue` and `postDeleteDropdownAttributeValue` methods.
- Return error response if validation fails to prevent invalid data handling.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(attributes): improve error handling and optimize affected items processing

- Use `array_column` for extracting item IDs to streamline logic.
- Add JSON validation with `JSON_THROW_ON_ERROR` and return proper error response for invalid `definition_values`.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(sales): enable database refresh for consistent test state

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(sales): assert unauthorized message is displayed on restricted access

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* refactor(attributes): use camelCase for `attributeValue` in controller methods

- Standardize variable naming in `postSaveAttributeValue` and `postDeleteDropdownAttributeValue` methods by switching to camelCase.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-08-09 11:37:38 +04:00

132 lines
5.3 KiB
PHP

<?php
/**
* @var string $controller_name
* @var string $table_headers
* @var array $filters
* @var array $stock_locations
* @var int $stock_location
* @var array $config
* @var string|null $start_date
* @var string|null $end_date
* @var array $selected_filters
*/
use App\Models\Employee;
?>
<?= view('partial/header') ?>
<script type="text/javascript">
$(document).ready(function() {
$('#generate_barcodes').click(function() {
window.open(
'index.php/items/generateBarcodes/' + table_support.selected_ids().join(':'),
'_blank'
);
});
// Load the preset daterange picker
<?= view('partial/daterangepicker') ?>
// Set the beginning of time as starting date
$('#daterangepicker').data('daterangepicker').setStartDate("<?= date($config['dateformat'], mktime(0, 0, 0, 01, 01, 2010)) ?>");
// Update the hidden inputs with the selected dates before submitting the search data
start_date = "<?= date('Y-m-d', mktime(0, 0, 0, 01, 01, 2010)) ?>";
// Override dates from server if provided
<?php if (isset($start_date) && $start_date): ?>
start_date = "<?= esc($start_date) ?>";
<?php endif; ?>
<?php if (isset($end_date) && $end_date): ?>
end_date = "<?= esc($end_date) ?>";
<?php endif; ?>
<?php
echo view('partial/bootstrap_tables_locale');
$employee = model(Employee::class);
?>
table_support.init({
employee_id: <?= $employee->get_logged_in_employee_info()->person_id ?>,
resource: '<?= esc($controller_name) ?>',
headers: <?= $table_headers ?>,
pageSize: <?= $config['lines_per_page'] ?>,
uniqueId: 'items.item_id',
queryParams: function() {
return $.extend(arguments[0], {
"start_date": start_date,
"end_date": end_date,
"stock_location": $("#stock_location").val(),
"filters": $("#filters").val()
});
},
onLoadSuccess: function(response) {
$('a.rollover').imgPreview({
imgCSS: {
width: 200
},
distanceFromCursor: {
top: 10,
left: -210
}
})
}
});
});
</script>
<?= view('partial/table_filter_persistence', ['additional_params' => ['stock_location']]) ?>
<div id="title_bar" class="btn-toolbar print_hide">
<button class="btn btn-info btn-sm pull-right modal-dlg" data-btn-submit="<?= lang('Common.submit') ?>" data-href="<?= "$controller_name/csvImport" ?>" title="<?= lang('Items.import_items_csv') ?>">
<span class="glyphicon glyphicon-import">&nbsp;</span><?= lang('Common.import_csv') ?>
</button>
<button class="btn btn-info btn-sm pull-right modal-dlg" data-btn-new="<?= lang('Common.new') ?>" data-btn-submit="<?= lang('Common.submit') ?>" data-href="<?= "$controller_name/view" ?>" title="<?= lang(ucfirst($controller_name) . '.new') ?>">
<span class="glyphicon glyphicon-tag">&nbsp;</span><?= lang(ucfirst($controller_name) . '.new') ?>
</button>
</div>
<div id="toolbar">
<div class="pull-left form-inline" role="toolbar">
<button id="delete" class="btn btn-default btn-sm print_hide">
<span class="glyphicon glyphicon-trash">&nbsp;</span><?= lang('Common.delete') ?>
</button>
<button id="bulk_edit" class="btn btn-default btn-sm modal-dlg print_hide" data-btn-submit="<?= lang('Common.submit') ?>" data-href="<?= "items/bulkEdit" ?>" title="<?= lang('Items.edit_multiple_items') ?>">
<span class="glyphicon glyphicon-edit">&nbsp;</span><?= lang('Items.bulk_edit') ?>
</button>
<button id="generate_barcodes" class="btn btn-default btn-sm print_hide" data-href="<?= "$controller_name/generateBarcodes" ?>" title="<?= lang('Items.generate_barcodes') ?>">
<span class="glyphicon glyphicon-barcode">&nbsp;</span><?= lang('Items.generate_barcodes') ?>
</button>
<?= form_input(['name' => 'daterangepicker', 'class' => 'form-control input-sm', 'id' => 'daterangepicker']) ?>
<?= form_multiselect('filters[]', $filters, $selected_filters ?? [], [
'id' => 'filters',
'class' => 'selectpicker show-menu-arrow',
'data-none-selected-text' => lang('Common.none_selected_text'),
'data-selected-text-format' => 'count > 1',
'data-style' => 'btn-default btn-sm',
'data-width' => 'fit'
]) ?>
<?php
if (count($stock_locations) > 1) {
echo form_dropdown(
'stock_location',
$stock_locations,
$stock_location,
[
'id' => 'stock_location',
'class' => 'selectpicker show-menu-arrow',
'data-style' => 'btn-default btn-sm',
'data-width' => 'fit'
]
);
}
?>
</div>
</div>
<div id="table_holder">
<table id="table"></table>
</div>
<?= view('partial/footer') ?>