16 Commits
Author SHA1 Message Date
siaupheng 11e17bd978 Refactor multiple JavaScript classes to use ES6 syntax, including Download, Pdf, Printer, PrinterPos, PoleDisplay, and Simulator. Update method names for consistency and improve error handling. Modify AJAX requests to enhance functionality and user experience. 2026-11-02 16:10:36 +07:00
siaupheng 929f3af79a Refactor PoleDisplay class and update Auth user_log method
- Converted PoleDisplay from a function-based structure to a class-based structure for improved readability and maintainability.
- Simplified the Open and Close methods to handle port state more clearly.
- Enhanced the Send method to support both string and array inputs for sending data.
- Updated the cd_Text method to use modern JavaScript features for padding calculations.
- Modified the user_log method in Auth class to streamline database fetch and update logic.
2026-11-01 15:55:28 +07:00
siaupheng cf100a86e2 update last modified date and refactor HTTP host retrieval in Menu class 2026-10-30 16:47:21 +07:00
siaupheng a538cc0cc5 update dependencies in composer.json for compatibility 2026-10-29 13:08:14 +07:00
siaupheng 4c8399cf64 add gen_uuidv4 and gen_uuidv7 2026-10-29 12:21:42 +07:00
siaupheng 2be02c36ec add hide header to xlsx and printhtml 2026-10-25 12:55:43 +07:00
siaupheng 0a47ddc422 corrected same function 2026-10-21 16:13:30 +07:00
siaupheng 17188f6134 reorganize class name 2026-10-06 14:05:52 +07:00
siaupheng 8a758e5875 fixed same bug 2026-09-30 14:33:11 +07:00
siaupheng bc95edbc0f adding user class 2026-09-10 13:26:46 +07:00
siaupheng bd93cca382 fixed change of framework 2026-09-10 11:43:07 +07:00
siaupheng a2978ae927 penambahan phpdoc dan perubahan semua class mengacu hanya pada $app 2026-08-27 12:01:54 +07:00
siaupheng 64e9c02c38 change namespace to Webapp\ 2026-08-26 14:01:47 +07:00
siaupheng 81ff3bc60c update composer.json 2026-08-22 12:57:34 +07:00
siaupheng e4f8baa003 Merge branch 'main' of https://git.trifonia.cloud/siaupheng/webapp 2026-08-22 12:54:36 +07:00
siaupheng e7bb9a6c90 upgrade all with dice compatible 2026-08-22 12:53:04 +07:00
43 changed files with 3152 additions and 3030 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
[![Software License][ico-license]](LICENSE.md) [![Software License][ico-license]](LICENSE.md)
[![Total Downloads][ico-downloads]][link-downloads] [![Total Downloads][ico-downloads]][link-downloads]
webapp adalah framework webbase aplikasi yang dibuat dengan basis kode PHP dan digabungkan dengan beberapa repo lain, sehingga menghasilkan webbase framework yang efisien dan efektif. Webapp adalah framework webbase aplikasi yang dibuat dengan basis kode PHP dan digabungkan dengan beberapa repo lain, sehingga menghasilkan webbase framework yang efisien dan efektif.
Maintenance versi repo tambahan, akan dilakukan secara periodik dengan mempertimbangkan kesiapan infrastruktur framework secara utuh. Maintenance versi repo tambahan, akan dilakukan secara periodik dengan mempertimbangkan kesiapan infrastruktur framework secara utuh.
+1 -1
View File
@@ -1882,7 +1882,7 @@ progress {
.table > :not(caption) > * > * { .table > :not(caption) > * > * {
padding: 0.5rem 0.5rem; padding: 0.5rem 0.5rem;
color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));
background-color: var(--bs-table-bg); /*background-color: var(--bs-table-bg);*/
border-bottom-width: var(--bs-border-width); border-bottom-width: var(--bs-border-width);
box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)));
} }
+21 -2
View File
@@ -4,9 +4,9 @@
Desc. : CSS Default Configuration Desc. : CSS Default Configuration
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : December 20th, 2007. Created Date : December 20th, 2007.
Last Modified : February 7th, 2026. Last Modified : September 27th, 2026.
(c) 2009 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
@@ -343,6 +343,14 @@ ul.dtr-details li.hidden { visibility: hidden !important; display: none !importa
cursor: pointer; cursor: pointer;
} }
.bootstrap-autocomplete.dropdown-menu .dropdown-item.active .text-primary {
color: #93f6c5 !important;
}
.bootstrap-autocomplete.dropdown-menu .dropdown-item.active .text-danger {
color: #f4c8d2 !important;
}
/******************************** text field ********************************/ /******************************** text field ********************************/
input.double, input.integer, input.number { input.double, input.integer, input.number {
@@ -493,3 +501,14 @@ input.double, input.integer, input.number {
background-color: #d8dbe0; background-color: #d8dbe0;
opacity: 1; opacity: 1;
} }
/** bashboard **/
.my-bashboard {
height: auto;
}
@media (min-width: 992px) {
.my-bashboard {
height: 500px;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
Created Date : October 7th, 2008. Created Date : October 7th, 2008.
Last Modified : November 24th, 2020. Last Modified : November 24th, 2020.
(c) 2008 - 2020, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
+227 -343
View File
@@ -1,84 +1,52 @@
var __extends = (undefined && undefined.__extends) || (function () { var AjaxResolver = class {
var extendStatics = function (d, b) { constructor(options) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var BaseResolver = (function () {
function BaseResolver(options) {
this._settings = $.extend(true, {}, this.getDefaults(), options); this._settings = $.extend(true, {}, this.getDefaults(), options);
} }
BaseResolver.prototype.getDefaults = function () { getDefaults() {
return {};
};
BaseResolver.prototype.getResults = function (limit, start, end) {
return this.results;
};
BaseResolver.prototype.search = function (q, cbk) {
cbk(this.getResults());
};
return BaseResolver;
}());
var AjaxResolver = (function (_super) {
__extends(AjaxResolver, _super);
function AjaxResolver(options) {
return _super.call(this, options) || this;
}
AjaxResolver.prototype.getDefaults = function () {
return { return {
url: '', url: '',
method: 'post', method: 'post',
queryKey: 'Auto_Query', queryKey: 'Auto_Query',
extraData: {}, param: [],
timeout: 0, timeout: 0,
requestThrottling: 500 requestThrottling: 500
}; };
};
AjaxResolver.prototype.search = function (q, cbk) {
var _this = this;
if (this.jqXHR != null) {
this.jqXHR.abort();
} }
search(q, cbk) {
if (this.jqXHR != null) this.jqXHR.abort();
var data = {}; var data = {};
data[this._settings.queryKey] = q; data[this._settings.queryKey] = q;
$.extend(data, this._settings.extraData); if (this._settings.param.length > 0) {
if (this.requestTID) { $.extend(data, FoniaJS.RequestParam(this._settings.param));
window.clearTimeout(this.requestTID);
} }
this.requestTID = window.setTimeout(function () {
_this.jqXHR = $.ajax(_this._settings.url, { if (this.requestTID) window.clearTimeout(this.requestTID);
method: _this._settings.method,
this.requestTID = window.setTimeout(() => {
this.jqXHR = $.ajax(this._settings.url, {
method: this._settings.method,
data: data, data: data,
timeout: _this._settings.timeout timeout: this._settings.timeout
}); });
_this.jqXHR.done(function (result) {
//gnips: result in object this.jqXHR.done(result => {
cbk((undefined !== result.autodata) ? result.autodata : result); cbk(result.autodata !== undefined ? result.autodata : result);
}); });
_this.jqXHR.fail(function (err) {
var _a; this.jqXHR.fail(err => {
(_a = _this._settings) === null || _a === void 0 ? void 0 : _a.fail(err); this._settings.fail?.(err);
}); });
_this.jqXHR.always(function () {
_this.jqXHR = null; this.jqXHR.always(() => {
this.jqXHR = null;
}); });
}, this._settings.requestThrottling); }, this._settings.requestThrottling);
}
}; };
return AjaxResolver;
}(BaseResolver));
var DropdownV5 = (function () { var DropdownV5 = class {
function DropdownV5(e, formatItemCbk, autoSelect, noResultsText) { constructor(e, formatItemCbk, autoSelect, noResultsText) {
this.initialized = false; this.initialized = false;
this.shown = false; this.shown = false;
this.items = []; this.items = [];
@@ -88,85 +56,67 @@ var DropdownV5 = (function () {
this.autoSelect = autoSelect; this.autoSelect = autoSelect;
this.noResultsText = noResultsText; this.noResultsText = noResultsText;
} }
DropdownV5.prototype.getElPos = function () {
var pos = $.extend({}, this._$el.position(), { getElPos() {
height: this._$el[0].offsetHeight return $.extend({}, this._$el.position(), { height: this._$el[0].offsetHeight });
}); }
return pos;
}; init() {
DropdownV5.prototype.init = function () {
var _this = this;
var pos = this.getElPos(); var pos = this.getElPos();
this._dd = $('<div />'); var fontsize = this._$el.css('font-size');
this._dd.addClass('bootstrap-autocomplete dropdown-menu');
this._dd.insertAfter(this._$el); this._dd = $('<div />')
this._dd.css({ top: pos.top + this._$el.outerHeight(), left: pos.left, width: this._$el.outerWidth() }); .addClass('bootstrap-autocomplete dropdown-menu')
this._dd.on('click', '.dropdown-item', function (evt) { .insertAfter(this._$el)
//console.log('clicked', evt.currentTarget); .css({
//console.log($(evt.currentTarget)); top: pos.top + this._$el.outerHeight(),
left: pos.left,
width: this._$el.outerWidth(),
'font-size': fontsize
});
this._dd.on('click', '.dropdown-item', evt => {
var item = $(evt.currentTarget).data('item'); var item = $(evt.currentTarget).data('item');
_this.itemSelectedLaunchEvent(item); this.itemSelectedLaunchEvent(item);
evt.preventDefault(); evt.preventDefault();
}); });
this._dd.on('keyup', function (evt) {
if (_this.shown) { this._dd.on('keyup', evt => {
switch (evt.which) { if (this.shown && evt.which === 27) {
case 27: this.hide();
_this.hide(); this._$el.focus();
_this._$el.focus();
break;
}
return false; return false;
} }
}); });
this._dd.on('mouseenter', function (evt) {
_this.ddMouseover = true; this._dd.on('mouseenter', () => { this.ddMouseover = true; });
}); this._dd.on('mouseleave', () => { this.ddMouseover = false; });
this._dd.on('mouseleave', function (evt) {
_this.ddMouseover = false; this._dd.on('mouseenter', '.dropdown-item', evt => {
}); if (this.haveResults) {
this._dd.on('mouseenter', '.dropdown-item', function (evt) {
if (_this.haveResults) {
$(evt.currentTarget).closest('div').find('.dropdown-item.active').removeClass('active'); $(evt.currentTarget).closest('div').find('.dropdown-item.active').removeClass('active');
$(evt.currentTarget).addClass('active'); $(evt.currentTarget).addClass('active');
_this.mouseover = true; this.mouseover = true;
} }
}); });
this._dd.on('mouseleave', '.dropdown-item', function (evt) {
_this.mouseover = false; this._dd.on('mouseleave', '.dropdown-item', () => { this.mouseover = false; });
});
this.initialized = true; this.initialized = true;
};
DropdownV5.prototype.checkInitialized = function () {
if (!this.initialized) {
this.init();
} }
};
Object.defineProperty(DropdownV5.prototype, "isMouseOver", { checkInitialized() {
get: function () { if (!this.initialized) this.init();
return this.mouseover; }
},
enumerable: false, get isMouseOver() { return this.mouseover; }
configurable: true get isDdMouseOver() { return this.ddMouseover; }
}); get haveResults() { return this.items.length > 0; }
Object.defineProperty(DropdownV5.prototype, "isDdMouseOver", {
get: function () { focusNextItem(reversed) {
return this.ddMouseover; if (!this.haveResults) return;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DropdownV5.prototype, "haveResults", {
get: function () {
return (this.items.length > 0);
},
enumerable: false,
configurable: true
});
DropdownV5.prototype.focusNextItem = function (reversed) {
if (this.haveResults) {
var currElem = this._dd.find('.dropdown-item.active'); var currElem = this._dd.find('.dropdown-item.active');
var nextElem = reversed ? currElem.prev() : currElem.next(); var nextElem = reversed ? currElem.prev() : currElem.next();
if (nextElem.length === 0) { if (nextElem.length === 0) {
nextElem = reversed ? this._dd.find('.dropdown-item').last() : this._dd.find('.dropdown-item').first(); nextElem = reversed ? this._dd.find('.dropdown-item').last() : this._dd.find('.dropdown-item').first();
} }
@@ -174,110 +124,91 @@ var DropdownV5 = (function () {
nextElem.addClass('active'); nextElem.addClass('active');
this.scrollTo(nextElem); this.scrollTo(nextElem);
} }
};
DropdownV5.prototype.focusPreviousItem = function () { focusPreviousItem() { this.focusNextItem(true); }
this.focusNextItem(true); selectFocusItem() { this._dd.find('.dropdown-item.active').trigger('click'); }
};
DropdownV5.prototype.selectFocusItem = function () { get isItemFocused() {
this._dd.find('.dropdown-item.active').trigger('click'); return !!(this._dd && this.isShown() && this._dd.find('.dropdown-item.active').length > 0);
};
Object.defineProperty(DropdownV5.prototype, "isItemFocused", {
get: function () {
if (this._dd && this.isShown() && (this._dd.find('.dropdown-item.active').length > 0)) {
return true;
} }
return false;
}, show() {
enumerable: false,
configurable: true
});
DropdownV5.prototype.show = function () {
if (!this.shown) { if (!this.shown) {
this._dd.addClass('show'); this._dd.addClass('show');
this.shown = true; this.shown = true;
this._$el.trigger('autocomplete.dd.shown'); this._$el.trigger('autocomplete.dd.shown');
} }
}; }
DropdownV5.prototype.isShown = function () {
return this.shown; isShown() { return this.shown; }
};
DropdownV5.prototype.hide = function () { hide() {
if (this.shown) { if (this.shown) {
this._dd.removeClass('show'); this._dd.removeClass('show');
this.shown = false; this.shown = false;
this._$el.trigger('autocomplete.dd.hidden'); this._$el.trigger('autocomplete.dd.hidden');
} }
}; }
DropdownV5.prototype.updateItems = function (items, searchText) {
updateItems(items, searchText) {
this.items = items; this.items = items;
this.searchText = searchText; this.searchText = searchText;
this.refreshItemList(); this.refreshItemList();
}; }
DropdownV5.prototype.showMatchedText = function (text, qry) {
showMatchedText(text, qry) {
var startIndex = text.toLowerCase().indexOf(qry.toLowerCase()); var startIndex = text.toLowerCase().indexOf(qry.toLowerCase());
if (startIndex > -1) { if (startIndex > -1) {
var endIndex = startIndex + qry.length; var endIndex = startIndex + qry.length;
return text.slice(0, startIndex) + '<b>' return text.slice(0, startIndex) + '<b>' + text.slice(startIndex, endIndex) + '</b>' + text.slice(endIndex);
+ text.slice(startIndex, endIndex) + '</b>'
+ text.slice(endIndex);
} }
return text; return text;
}; }
DropdownV5.prototype.refreshItemList = function () {
var _this = this; refreshItemList() {
this.checkInitialized(); this.checkInitialized();
this._dd.empty(); this._dd.empty();
var liList = []; var liList = [];
if (this.items.length > 0) { if (this.items.length > 0) {
this.items.forEach(function (item) { this.items.forEach(item => {
var itemFormatted = _this.formatItem(item); var itemFormatted = this.formatItem(item);
if (typeof itemFormatted === 'string') { if (typeof itemFormatted === 'string') itemFormatted = { text: itemFormatted };
itemFormatted = { text: itemFormatted };
} var itemText = this.showMatchedText(itemFormatted.text, this.searchText);
var itemText; var itemHtml = itemFormatted.html !== undefined ? itemFormatted.html : itemText;
var itemHtml;
itemText = _this.showMatchedText(itemFormatted.text, _this.searchText); var li = $('<a />')
if (itemFormatted.html !== undefined) { .addClass('dropdown-item')
itemHtml = itemFormatted.html;
}
else {
itemHtml = itemText;
}
var disabledItem = itemFormatted.disabled;
var li = $('<a >');
li.addClass('dropdown-item')
//css bisa dimasukkan ke style default + cursor select
//.css({ 'overflow': 'hidden', 'text-overflow': 'ellipsis' })
.html(itemHtml) .html(itemHtml)
.data('item', item); .data('item', item);
if (disabledItem) {
li.addClass('disabled'); if (itemFormatted.disabled) li.addClass('disabled');
}
liList.push(li); liList.push(li);
}); });
this._dd.append(liList); this._dd.append(liList);
this.show(); this.show();
} } else if (this.noResultsText !== '') {
else { var li = $('<a />').addClass('dropdown-item disabled').html(this.noResultsText);
if (this.noResultsText === '') {
this.hide();
}
else {
var li = $('<a >');
li.addClass('dropdown-item disabled')
.html(this.noResultsText);
liList.push(li); liList.push(li);
this._dd.append(liList); this._dd.append(liList);
this.show(); this.show();
} else {
this.hide();
} }
} }
};
DropdownV5.prototype.itemSelectedLaunchEvent = function (item) { itemSelectedLaunchEvent(item) {
this._$el.trigger('autocomplete.select', item); this._$el.trigger('autocomplete.select', item);
}; }
DropdownV5.prototype.scrollTo = function (el) {
var _he = this._dd.height(), _to = this._dd.scrollTop(), _ito = el.position().top, _ihe = el.outerHeight(), scrollTo(el) {
var _he = this._dd.height(),
_to = this._dd.scrollTop(),
_ito = el.position().top,
_ihe = el.outerHeight(),
_nto = _to, _wra = false; _nto = _to, _wra = false;
if (_ito < 0) { if (_ito < 0) {
_wra = el.is(':first-child'); _wra = el.is(':first-child');
_nto = _wra ? 0 : _to - _he / 2; _nto = _wra ? 0 : _to - _he / 2;
@@ -286,12 +217,11 @@ var DropdownV5 = (function () {
_nto = _wra ? (_ito + _ihe - _he) : _to + _he / 2; _nto = _wra ? (_ito + _ihe - _he) : _to + _he / 2;
} }
this._dd.scrollTop(_nto); this._dd.scrollTop(_nto);
}
}; };
return DropdownV5;
}());
var AutoComplete = (function () { var AutoComplete = class {
function AutoComplete(element, options) { constructor(element, options) {
this._selectedItem = null; this._selectedItem = null;
this._defaultValue = null; this._defaultValue = null;
this._defaultText = null; this._defaultText = null;
@@ -304,230 +234,185 @@ var AutoComplete = (function () {
autoSelect: true, autoSelect: true,
noResultsText: 'Pencarian Tidak Ketemu', noResultsText: 'Pencarian Tidak Ketemu',
preventEnter: false, preventEnter: false,
events: { events: { typed: null, searchPre: null, search: null, searchPost: null, select: null, focus: null },
typed: null, selectFunc: () => {},
searchPre: null, keyFunc: () => {},
search: null, breakFocus: false,
searchPost: null,
select: null,
focus: null,
},
//gnips: adding callback, keyfunc and breakvalue
selectFunc: function(){},
keyFunc: function(){},
breakValue: [] breakValue: []
}; };
this._el = element; this._el = element;
this._$el = $(this._el); this._$el = $(this._el);
this.manageInlineDataAttributes(); this.manageInlineDataAttributes();
if (typeof options === 'object') { if (typeof options === 'object') {
this._settings = $.extend(true, {}, this.getSettings(), options); this._settings = $.extend(true, {}, this.getSettings(), options);
} }
this.init(); this.init();
} }
AutoComplete.prototype.manageInlineDataAttributes = function () {
manageInlineDataAttributes() {
var s = this.getSettings(); var s = this.getSettings();
if (this._$el.data('url')) { if (this._$el.data('url')) s.resolverSettings.url = this._$el.data('url');
s.resolverSettings.url = this._$el.data('url'); if (this._$el.data('default-value')) this._defaultValue = this._$el.data('default-value');
if (this._$el.data('default-text')) this._defaultText = this._$el.data('default-text');
if (this._$el.data('noresults-text') !== undefined) s.noResultsText = this._$el.data('noresults-text');
} }
if (this._$el.data('default-value')) {
this._defaultValue = this._$el.data('default-value'); getSettings() { return this._settings; }
}
if (this._$el.data('default-text')) { init() {
this._defaultText = this._$el.data('default-text');
}
if (this._$el.data('noresults-text') !== undefined) {
s.noResultsText = this._$el.data('noresults-text');
}
};
AutoComplete.prototype.getSettings = function () {
return this._settings;
};
AutoComplete.prototype.init = function () {
this.bindDefaultEventListeners(); this.bindDefaultEventListeners();
if (this._settings.resolver === 'ajax') { if (this._settings.resolver === 'ajax') {
this.resolver = new AjaxResolver(this._settings.resolverSettings); this.resolver = new AjaxResolver(this._settings.resolverSettings);
} }
this._dd = new DropdownV5(this._$el, this._settings.formatResult, this._settings.autoSelect, this._settings.noResultsText); this._dd = new DropdownV5(this._$el, this._settings.formatResult, this._settings.autoSelect, this._settings.noResultsText);
}; }
AutoComplete.prototype.bindDefaultEventListeners = function () {
var _this = this; bindDefaultEventListeners() {
this._$el.on('keydown', function (evt) { this._$el.on('keydown', evt => {
//gnips: check breakValue and call keydown function var val = this._$el.val();
(('' == _this._$el.val() || ((_this._settings.breakValue.length > 0) && (_this._settings.breakValue.indexOf(_this._$el.val()) != -1))) && _this._settings.keyFunc(evt.which, _this._$el.val())); if ((val === '' || (this._settings.breakValue.length > 0 && this._settings.breakValue.indexOf(val) !== -1))) {
this._settings.keyFunc(evt.which, val);
}
switch (evt.which) { switch (evt.which) {
case 9: case 9:
if (_this._dd.isItemFocused) { if (!this._settings.breakFocus) {
_this._dd.selectFocusItem(); if (this._dd.isItemFocused) this._dd.selectFocusItem();
this._dd.hide();
} }
_this._dd.hide();
break; break;
case 13: case 13:
if (_this._dd.isItemFocused) { if (this._dd.isItemFocused) this._dd.selectFocusItem();
_this._dd.selectFocusItem(); this._dd.hide();
} if (this._settings.preventEnter) evt.preventDefault();
_this._dd.hide();
if (_this._settings.preventEnter) {
evt.preventDefault();
}
break; break;
case 40: case 40:
_this._dd.focusNextItem(); this._dd.focusNextItem();
break; break;
case 38: case 38:
_this._dd.focusPreviousItem(); this._dd.focusPreviousItem();
break; break;
} }
}); });
this._$el.on('keyup', function (evt) {
this._$el.on('keyup', evt => {
switch (evt.which) { switch (evt.which) {
case 16: case 16: case 17: case 18: case 39: case 37: case 36: case 35: case 40: case 38:
case 17:
case 18:
case 39:
case 37:
case 36:
case 35:
break; break;
case 13: case 13: case 27:
_this._dd.hide(); this._dd.hide();
break;
case 27:
_this._dd.hide();
break;
case 40:
break;
case 38:
break; break;
default: default:
_this._selectedItem = null; this._selectedItem = null;
var newValue = _this._$el.val(); this.handlerTyped(this._$el.val());
_this.handlerTyped(newValue);
} }
}); });
this._$el.on('blur', function (evt) {
if (!_this._dd.isMouseOver && _this._dd.isDdMouseOver && _this._dd.isShown()) { this._$el.on('blur', () => {
setTimeout(function () { _this._$el.focus(); }); if (this._settings.breakFocus) return;
_this._$el.focus(); if (!this._dd.isMouseOver && this._dd.isDdMouseOver && this._dd.isShown()) {
} setTimeout(() => { this._$el.focus(); });
else if (!_this._dd.isMouseOver) { this._$el.focus();
_this._dd.hide(); } else if (!this._dd.isMouseOver) {
this._dd.hide();
} }
}); });
this._$el.on('autocomplete.select', function (evt, item) {
_this._selectedItem = item; this._$el.on('autocomplete.select', (evt, item) => {
_this.itemSelectedDefaultHandler(item); this._selectedItem = item;
this.itemSelectedDefaultHandler(item);
}); });
this._$el.on('paste', function (evt) {
setTimeout(function () { this._$el.on('paste', evt => {
_this._$el.trigger('keyup', evt); setTimeout(() => { this._$el.trigger('keyup', evt); }, 0);
}, 0);
}); });
}; }
AutoComplete.prototype.handlerTyped = function (newValue) {
handlerTyped(newValue) {
if (this._settings.events.typed !== null) { if (this._settings.events.typed !== null) {
newValue = this._settings.events.typed(newValue, this._$el); newValue = this._settings.events.typed(newValue, this._$el);
if (!newValue) { if (!newValue) return;
return;
}
} }
if (newValue.length >= this._settings.minLength) { if (newValue.length >= this._settings.minLength) {
this._searchText = newValue; this._searchText = newValue;
this.handlerPreSearch(); this.handlerPreSearch();
} } else {
else {
this._dd.hide(); this._dd.hide();
} }
}; }
AutoComplete.prototype.handlerPreSearch = function () {
handlerPreSearch() {
if (this._settings.events.searchPre !== null) { if (this._settings.events.searchPre !== null) {
var newValue = this._settings.events.searchPre(this._searchText, this._$el); var newValue = this._settings.events.searchPre(this._searchText, this._$el);
if (!newValue) if (!newValue) return;
return;
this._searchText = newValue; this._searchText = newValue;
} }
this.handlerDoSearch(); this.handlerDoSearch();
};
AutoComplete.prototype.handlerDoSearch = function () {
var _this = this;
if (this._settings.events.search !== null) {
this._settings.events.search(this._searchText, function (results) {
_this.postSearchCallback(results);
}, this._$el);
} }
else {
if (this.resolver) { handlerDoSearch() {
this.resolver.search(this._searchText, function (results) { if (this._settings.events.search !== null) {
_this.postSearchCallback(results); this._settings.events.search(this._searchText, results => {
this.postSearchCallback(results);
}, this._$el);
} else if (this.resolver) {
this.resolver.search(this._searchText, results => {
this.postSearchCallback(results);
}); });
} }
} }
};
AutoComplete.prototype.postSearchCallback = function (results) { postSearchCallback(results) {
if (this._settings.events.searchPost) { if (this._settings.events.searchPost) {
results = this._settings.events.searchPost(results, this._$el); results = this._settings.events.searchPost(results, this._$el);
if ((typeof results === 'boolean') && !results) if (typeof results === 'boolean' && !results) return;
return;
} }
this.handlerStartShow(results); this.handlerStartShow(results);
}; }
AutoComplete.prototype.handlerStartShow = function (results) {
//gnips: adding auto select when barcode result of 1 item only handlerStartShow(results) {
if (1 == results.length && 1 == (void 0 !== results[0].bc ? results[0].bc : 0)) { var bcVal = results[0]?.bc ?? 0;
this._$el.trigger('autocomplete.select', results[0]), if (results.length === 1 && bcVal === 1) {
this._$el.trigger('autocomplete.select', results[0]);
this._dd.hide(); this._dd.hide();
} else this._dd.updateItems(results, this._searchText); } else {
}; this._dd.updateItems(results, this._searchText);
AutoComplete.prototype.itemSelectedDefaultHandler = function (item) { }
}
itemSelectedDefaultHandler(item) {
if (item != null) { if (item != null) {
var itemFormatted = this._settings.formatResult(item); var itemFormatted = this._settings.formatResult(item);
if (typeof itemFormatted === 'string') { if (typeof itemFormatted === 'string') itemFormatted = { text: itemFormatted };
itemFormatted = { text: itemFormatted };
}
this._$el.val(itemFormatted.text); this._$el.val(itemFormatted.text);
} } else {
else {
this._$el.val(''); this._$el.val('');
} }
this._selectedItem = item; this._selectedItem = item;
//gnips: callback with selected item
this._settings.selectFunc(item); this._settings.selectFunc(item);
this._dd.hide(); this._dd.hide();
};
AutoComplete.prototype.defaultFormatResult = function (item) {
if (typeof item === 'string') {
return { text: item };
} }
else if (item.text) {
return item; defaultFormatResult(item) {
} if (typeof item === 'string') return { text: item };
else { if (item.text) return item;
return { text: item.toString() }; return { text: item.toString() };
} }
};
AutoComplete.prototype.manageAPI = function (APICmd, params) { manageAPI(APICmd, params) {
if (APICmd === 'set') { if (APICmd === 'set') this.itemSelectedDefaultHandler(params);
this.itemSelectedDefaultHandler(params); else if (APICmd === 'clear') this.itemSelectedDefaultHandler(null);
} else if (APICmd === 'show') this._$el.trigger('keyup');
else if (APICmd === 'clear') { else if (APICmd === 'updateResolver') this.resolver = new AjaxResolver(params);
this.itemSelectedDefaultHandler(null);
}
else if (APICmd === 'show') {
this._$el.trigger('keyup');
}
else if (APICmd === 'updateResolver') {
this.resolver = new AjaxResolver(params);
} }
}; };
AutoComplete.NAME = 'autoComplete'; AutoComplete.NAME = 'autoComplete';
return AutoComplete;
}());
(function ($, window, document) { // jQuery plugin wrapper
$.fn[AutoComplete.NAME] = function (optionsOrAPI, optionalParams) { $.fn[AutoComplete.NAME] = function (optionsOrAPI, optionalParams) {
return this.each(function () { return this.each(function () {
var pluginClass; var pluginClass = $(this).data(AutoComplete.NAME);
pluginClass = $(this).data(AutoComplete.NAME);
if (!pluginClass) { if (!pluginClass) {
pluginClass = new AutoComplete(this, optionsOrAPI); pluginClass = new AutoComplete(this, optionsOrAPI);
$(this).data(AutoComplete.NAME, pluginClass); $(this).data(AutoComplete.NAME, pluginClass);
@@ -535,4 +420,3 @@ var AutoComplete = (function () {
pluginClass.manageAPI(optionsOrAPI, optionalParams); pluginClass.manageAPI(optionsOrAPI, optionalParams);
}); });
}; };
})(jQuery, window, document);
File diff suppressed because one or more lines are too long
+89
View File
@@ -0,0 +1,89 @@
/*******************************************************************
Module : /assets/js/download.js
Desc. : Fungsi-fungsi download data & laporan
Created By : Siau Pheng ([email protected]).
Created Date : April 1st, 2011.
Last Modified : November 2nd, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************
*
* Class Download
*
* Download atau Share file.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL File
* @param {string} [options.filename='file.bin'] - Default file name for download is header is missing
* @param {string} [options.title] - Title for share file
* @param {string} [options.text] - Text for share file
* @param {Array} [options.params] - Request payload
*/
class Download {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.filename = options.filename || 'file.bin';
this.title = options.title || '';
this.text = options.text || '';
this.params = options.params || [];
}
addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
}
async _exec(isShare = false) {
if (!this.url) {
console.warn('Url File Download dibutuhkan!');
return;
}
await $.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
xhrFields: { responseType: 'blob' }
})
.done((blob, status, xhr) => {
let filename = this.filename;
const disposition = xhr.getResponseHeader('Content-Disposition');
const match = disposition?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match?.[1]) filename = match[1].replace(/['"]/g, '');
if (!isShare) {
const url = URL.createObjectURL(blob);
const link = Object.assign(document.createElement('a'), {
href: url,
download: filename
});
link.click();
URL.revokeObjectURL(url);
} else {
const file = new File([blob], filename, { type: blob.type });
if (isShare && navigator.canShare?.({ files: [file] })) {
navigator.share({
files: [file],
title: this.title || filename,
text: this.text
});
}
file = null;
}
})
.fail((xhr, status, err) => console.error('Gagal download File karena : ', err));
}
share() {
return this._exec(true);
}
download() {
return this._exec(false);
}
}
-63
View File
@@ -1,63 +0,0 @@
/*******************************************************************
Module : /modules/js/export.js
Desc. : Fungsi-fungsi export data & laporan
Created By : Siau Pheng ([email protected]).
Created Date : April 1st, 2011.
Last Modified : January 19th, 2026.
(c) 2011 - 2026, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
var cEXPORT = function(url, options){
var _options = {
'url': '',
'filename': ''
};
var _params = [];
this.construct = function(url, options){
if (typeof url == 'object') {
$.extend(_options, url);
} else {
$.extend(_options, options);
_options.url = url;
}
};
this.set_url = function (url){
_options.url = url;
};
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
_params.push(_getvalue);
},
this.download = async function(){
if (!_options.url) {
console.warn('cek url !');
return false;
}
var _pro = (window.location.protocol=='https:') ? 'https://' : 'http://';
var _src = _pro+FoniaJS.WEB_HOST + _options.url + '?' + (FoniaJS.RequestParam(_params)).join('&');
try {
const response = await fetch(_src)
.then(response => {
const cDispo = response.headers.get('Content-Disposition');
if (cDispo && cDispo.indexOf('filename=') !== -1) {
_options.filename = cDispo.split('filename=')[1].split(';')[0].replace(/"/g, '');
}
return response;
})
const blob = await response.blob();
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = _options.filename;
link.click();
} catch (error) {
console.error('Download gagal:', error);
}
return this;
};
this.construct(url, options);
};
+640 -697
View File
File diff suppressed because it is too large Load Diff
+114 -54
View File
@@ -4,63 +4,123 @@
Desc. : JS - Cetak Laporan PDF Desc. : JS - Cetak Laporan PDF
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : April 6th, 2015. Created Date : April 6th, 2015.
Last Modified : October 18th, 2020. Last Modified : November 2nd, 2026.
(c) 2015 - 2020, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************
*
* Class Pdf
*
* Preview dan download file pdf.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint PDF File URL
* @param {number} [options.zoom=80] - Zoom percentage in PDF viewer
* @param {string} [options.filename='report.pdf'] - Default file name for download is header is missing
* @param {Array} [options.params] - Request payload
*/
var cPDF = function(url, options){ class Pdf {
var vars = { constructor(options = {}) {
'url': '', this.url = typeof options === 'string' ? options : options.url || '';
'zoom': 80, this.zoom = options.zoom || 80;
'param': [] this.filename = options.filename || 'report.pdf';
}; this.params = options.params || [];
this.construct = function(url, options){
$.extend(vars, options);
vars.url = url;
};
this.set_url = function (url){
vars.url = url;
};
this.set_zoom = function (zoom){
if (zoom) vars.zoom = zoom;
},
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
vars.param.push(_getvalue);
},
this.downloadPDF = function(){
if (!vars.url) {
console.warn(vars.param + ", cek url php!");
return false;
} }
vars.data = FoniaJS.RequestParam(vars.param);
$.ajax({ addParam(id, val) {
type: 'POST', this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
url: vars.url,
data: (vars.data).join('&'),
success: (typeof vars.onSuccess === "function") ? vars.onSuccess : null,
error: (typeof vars.onFailure === "function") ? vars.onFailure : null,
}).always(FoniaJS.FormDecorated);
return this; return this;
}; }
this.showPDF = function(){
var _proto = window.location.protocol + '//'; async downloadPdf() {
var _src = _proto + FoniaJS.WEB_HOST + vars.url + '?' + (FoniaJS.RequestParam(vars.param)).join('&') + '#zoom='+vars.zoom+',0,0&page=1'; if (!this.url) {
var _wtxt = console.warn('Url PDF dibutuhkan!');
'<div class="modal fade" id="winAPPPDF" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPDFLabel" aria-hidden="true">'+ return;
'<div class="modal-dialog modal-dialog-centered modal-fs" role="document">'+ }
'<div class="modal-content">'+ await $.ajax({
'<div class="modal-header"><h5 class="modal-title" id="winAPPPDFLabel">Preview PDF</h5><button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Close"></button></div>'+ type: 'POST',
'<div class="modal-body mb-0 p-0"><div class="ratio ratio-21x9"><object type="application/pdf" data="'+_src+'"><p>Tidak Mendukung Preview PDF</p></object></div></div>'+ url: this.url,
'</div></div></div>'; data: FoniaJS.RequestParam(this.params),
if ($('#winAPPPDF')) $('#winAPPPDF').modal('dispose').remove(); xhrFields: { responseType: 'blob' }
$(_wtxt).on('shown.bs.modal', function(e){ })
FoniaJS.FormDecorated(); .done((blob, status, xhr) => {
}).on('hidden.bs.modal', function(e){ let filename = this.filename;
const disposition = xhr.getResponseHeader('Content-Disposition');
const match = disposition?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match?.[1]) filename = match[1].replace(/['"]/g, '');
const url = URL.createObjectURL(blob);
const link = Object.assign(document.createElement('a'), {
href: url,
download: filename
});
link.click();
URL.revokeObjectURL(url);
})
.fail((xhr, status, err) => console.error('Gagal proses download PDF karena : ', err));
}
async showPdf() {
if (!this.url) {
console.warn('Url PDF dibutuhkan!');
return;
}
await $.ajax({
type: 'POST',
url: this.url,
data: FoniaJS.RequestParam(this.params),
xhrFields: { responseType: 'blob' }
})
.done((blob, status, xhr) => {
let filename = this.filename;
const disposition = xhr.getResponseHeader('Content-Disposition');
const match = disposition?.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match?.[1]) filename = match[1].replace(/['"]/g, '');
const objectUrl = URL.createObjectURL(blob);
const pdfSrc = `${objectUrl}#zoom=${zoom}&page=1`;
const modalHtml = `
<div class="modal fade" id="winAPPPDF" tabindex="-1" data-bs-backdrop="static" aria-labelledby="winAPPPDFLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl" style="height: 90vh;">
<div class="modal-content h-100">
<div class="modal-header">
<h5 class="modal-title" id="winAPPPDFLabel">${filename}</h5>
<div class="ms-auto d-flex align-items-center gap-2">
<a href="${objectUrl}" download="${filename}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-download"></i> Download
</a>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body p-0 h-100">
<object data="${pdfSrc}" type="application/pdf" class="w-100 h-100">
<iframe src="${pdfSrc}" class="w-100 h-100" style="border: none;">
<p class="p-3">Ada kendala dalam Preview PDF di browser Anda.
<a href="${objectUrl}" download="${filename}">Unduh ${filename}</a> instead.
</p>
</iframe>
</object>
</div>
</div>
</div>
</div>
`;
$('#winAPPPDF').modal('dispose').remove();
$(modalHtml)
.appendTo('body')
//.on('shown.bs.modal', () => FoniaJS.FormDecorated())
.on('hidden.bs.modal', function () {
URL.revokeObjectURL(objectUrl);
$(this).remove(); $(this).remove();
}).modal('show'); })
}; .modal('show');
this.construct(url, options); })
}; .fail((xhr, status, err) => console.error('Gagal proses preview PDF karena : ', err));
}
}
+58 -62
View File
@@ -4,90 +4,86 @@
Desc. : JS Class Tampil Data di Pole Display POS Desc. : JS Class Tampil Data di Pole Display POS
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : October 12th, 2025. Created Date : October 12th, 2025.
Last Modified : November 1st, 2026.
(c) 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
var cPOLEDISPLAY = function(port, options){ class PoleDisplay {
var _cd_port = ''; constructor(port, options = {}) {
var _cd_pole = ''; this.port = port;
var _options = { this.pole = '';
this.options = Object.assign({
rx: { start: '\x02', end: '\x0D', width: null }, rx: { start: '\x02', end: '\x0D', width: null },
baudRate: 9600, baudRate: 9600,
dataBits: 8, dataBits: 8,
stopBits: 1, stopBits: 1,
parity: 'NONE', parity: 'NONE',
flowControl: 'NONE' flowControl: 'NONE'
}; }, options);
this.construct = function (port, options){
$.extend(_options, options);
_cd_port = port;
};
this.Open = function (){
if (_cd_pole === '') {
qz.serial.openPort(_cd_port, _options).then(() => {
_cd_pole = _cd_port;
this.cd_Error(_cd_port+' opened');
}).then(() => {
qz.serial.sendData(_cd_pole, {type:'HEX',data:'x1Bx40'}, _options).catch(this.cd_Error);
}).catch(this.cd_Error);
} }
};
this.Close = function (){ Open() {
qz.serial.closePort(_cd_port).then(() => { if (this.pole) return;
_cd_pole = '';
this.cd_Error(_cd_port+' closed');
}).catch(this.cd_Error);
};
this.Send = function (txt){ qz.serial.openPort(this.port, this.options)
if (_cd_pole === '') return false; .then(() => {
var ascii_to_hexa = function(str) { this.pole = this.port;
var arr1 = []; this._conerr(`${this.port} opened`);
for (var n = 0, l = str.length; n < l; n++) { return qz.serial.sendData(this.pole, { type: 'HEX', data: 'x1Bx40' }, this.options);
var hex = Number(str.charCodeAt(n)).toString(16); })
if (hex.length==1) hex = '0' + hex; .catch(this._conerr);
arr1.push(hex.toUpperCase());
} }
return 'x' + arr1.join('x');
}; Close() {
var _txt = txt; if (!this.pole) return;
qz.serial.closePort(this.port)
.then(() => {
this.pole = '';
this._conerr(`${this.port} closed`);
})
.catch(this._conerr);
}
Send(txt) {
if (!this.pole) return false;
// Convert string to ASCII hex format
const asciiToHex = str =>
Array.from(str, char => char.charCodeAt(0).toString(16).padStart(2, '0').toUpperCase())
.join('x');
let textToSend = txt;
if (Array.isArray(txt)) { if (Array.isArray(txt)) {
_txt = ''; textToSend = txt.map(ele => this._aligntext(ele.t, ele.a)).join('');
txt.forEach((ele) => {
_txt += this.cd_Text(ele['t'], ele['a']);
});
} }
qz.serial.sendData(_cd_pole, {type:'HEX',data:'x10C'+ascii_to_hexa(_txt)}, _options).catch(this.cd_Error);
};
this.cd_Text = function (text, align){ const hexData = `x10C${asciiToHex(textToSend)}`;
const totWidth = 20; qz.serial.sendData(this.pole, { type: 'HEX', data: hexData }, this.options)
const txtLength = text.length; .catch(this._conerr);
if (txtLength >= totWidth) {
return text.substring(0, totWidth);
} }
const padding = totWidth - txtLength;
var lPadding = 0; _aligntext(text, align = 'L') {
var rPadding = padding; const totWidth = 20;
if (align === null) align = 'L'; if (text.length >= totWidth) return text.substring(0, totWidth);
if (align == 'C'){
const padding = totWidth - text.length;
let lPadding = 0, rPadding = padding;
if (align === 'C') {
lPadding = Math.floor(padding / 2); lPadding = Math.floor(padding / 2);
rPadding = Math.ceil(padding / 2); rPadding = Math.ceil(padding / 2);
} else if (align == 'R'){ } else if (align === 'R') {
lPadding = padding; lPadding = padding;
rPadding = 0; rPadding = 0;
} }
return " ".repeat(lPadding) + text + " ".repeat(rPadding);
};
this.cd_Error = function (err){ return ' '.repeat(lPadding) + text + ' '.repeat(rPadding);
}
_conerr(err) {
console.error(err); console.error(err);
}; }
}
this.construct(port, options);
};
+207 -173
View File
@@ -4,231 +4,265 @@
Desc. : JS Class Printer & Printer POS Desc. : JS Class Printer & Printer POS
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : May 27th, 2007. Created Date : May 27th, 2007.
Last Modified : December 26th, 2025. Last Modified : November 2nd, 2026.
(c) 2007 - 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************
*
* Class Printer
*
* Standard Web Document Printing & Report Preview Class
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {number} [options.zoom=90] - Zoom percentage in viewer
* @param {Array} [options.params] - Request payload
*/
var cPRINTER = function(url, options){ class Printer {
var vars = { constructor(options = {}) {
'url': '', this.url = typeof options === 'string' ? options : options.url || '';
'zoom': 90, this.zoom = options.zoom || 90;
'param': [] this.params = options.params || [];
}; }
this.construct = function(url, options){
$.extend(vars, options); addParam(id, val) {
vars.url = url; this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
}; return this;
this.set_url = function (url){ }
vars.url = url;
}; doPrint() {
this.set_zoom = function (zoom){ if (!this.url) {
if (zoom) vars.zoom = zoom; console.warn('Url Laporan dibutuhkan!');
}, return;
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
vars.param.push(_getvalue);
},
this.doPrint = function(){
if (!vars.url) {
console.warn(vars.param + ", cek url php!");
return false;
} }
vars.data = FoniaJS.RequestParam(vars.param);
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: vars.url, url: this.url,
data: (vars.data).join('&'), data: FoniaJS.RequestParam(this.params),
success: function(txt){ success: (txt) => {
var $printData = $('#dataPrint'); let $printData = $('#dataPrint');
if (!$printData.length) { if (!$printData.length) {
var $printData = $('<div/>', {id:'dataPrint','class':'d-none d-print-block print-body'}).appendTo('body'); $printData = $('<div/>', {
id: 'dataPrint',
class: 'd-none d-print-block print-body'
}).appendTo('body');
} }
$printData.html(txt); $printData.html(txt);
window.print(); window.print();
}, },
error: (typeof vars.onFailure === "function") ? vars.onFailure : null, error: typeof this.onFailure === 'function' ? this.onFailure : null
}); });
return this; return this;
}; }
this.doPreview = function(){
if (!vars.url) console.warn(vars.param + ", cek url php!"); doPreview() {
vars.data = FoniaJS.RequestParam(vars.param); if (!this.url) {
console.warn('Url Laporan dibutuhkan!');
return;
}
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: vars.url, url: this.url,
data: (vars.data).join('&'), data: FoniaJS.RequestParam(this.params),
success: function(txt){ success: (txt) => {
if ('' == txt) { if (!txt) {
FoniaJS.showInfo('Tidak ada data untuk pembuatan laporan !'); FoniaJS.showInfo('Tidak ada data untuk pembuatan laporan !');
return false; return false;
} }
var _wtxt = const modalHtml = `
'<div class="modal fade" id="winAPPPrint" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPrintLabel" aria-hidden="true">'+ <div class="modal fade" id="winAPPPrint" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPPrintLabel" aria-hidden="true">
'<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">'+ <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">
'<div class="modal-content">'+ <div class="modal-content">
'<div class="modal-header"><h5 class="modal-title" id="winAPPPrintLabel">Preview Laporan</h5><div class="ms-auto"><button class="btn me-2" id="tombCetak" type="button" aria-label="Cetak"><i class="bi-printer me-2"></i><span aria-hidden="true">Cetak</span></button><button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup"><i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span></button></div></div>'+ <div class="modal-header">
'<div class="modal-body print-body" id="divPrintP">'+txt+'</div>'+ <h5 class="modal-title" id="winAPPPrintLabel">Preview Laporan</h5>
'</div></div></div>'; <div class="ms-auto">
if ($('#winAPPPrint')) $('#winAPPPrint').modal('dispose').remove(); <button class="btn me-2" id="tombCetak" type="button" aria-label="Cetak">
$(_wtxt).on('shown.bs.modal', function(e){ <i class="bi-printer me-2"></i><span aria-hidden="true">Cetak</span>
$('#winAPPPrint').find('#tombCetak').click(function(){ </button>
var $printData = $('#dataPrint'); <button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup">
<i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span>
</button>
</div>
</div>
<div class="modal-body print-body" id="divPrintP">${txt}</div>
</div>
</div>
</div>
`;
$('#winAPPPrint').modal('dispose').remove();
$(modalHtml)
.on('shown.bs.modal', () => {
$('#winAPPPrint #tombCetak').off('click').on('click', () => {
let $printData = $('#dataPrint');
if (!$printData.length) { if (!$printData.length) {
var $printData = $('<div/>', {id:'dataPrint','class':'d-none d-print-block print-body'}).appendTo('body'); $printData = $('<div/>', {
id: 'dataPrint',
class: 'd-none d-print-block print-body'
}).appendTo('body');
} }
$printData.html($('#divPrintP').html()); $printData.html($('#divPrintP').html());
FoniaJS.closeWindow($('#winAPPPrint')); FoniaJS.closeWindow($('#winAPPPrint'));
window.print(); window.print();
}); });
}).on('hidden.bs.modal', function(e){ })
.on('hidden.bs.modal', function () {
$(this).remove(); $(this).remove();
}).modal('show'); })
.modal('show');
}, },
error: (typeof vars.onFailure === "function") ? vars.onFailure : null, error: typeof this.onFailure === 'function' ? this.onFailure : null
}); });
}; return this;
this.construct(url, options); }
}; }
var cPRINTERPOS = function(url, options) { /**
var b_default = false; * Class PrinterPos
var _options = { *
'url': '', * Direct Thermal POS Printer Class (via QZ Tray API)
'param': [], *
'printer': null, * @license MIT, https://www.3fonia.com
'cut_paper': "\x1D\x56\x41", * @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
'open_draw': "\x1B\x70\x30\x19\x19", *
'chr_norm': "\x1B\x21\x01", * @param {object} options
'chr_bold': "\x1B\x21\x10", * @param {string} options.url - Endpoint URL Print Page
'chr_sbold': "\x1B\x21\x31", * @param {number} [options.printer=FoniaJS._def_printer] - Printer Name
'chr_uline': "\x1B\x21\x80" * @param {Array} [options.params] - Request payload
}; */
this.construct = function(url, options) {
$.extend(_options, options); class PrinterPos {
_options.url = url; constructor(options = {}) {
if (FoniaJS._def_printer) _options.printer = FoniaJS._def_printer; this.url = typeof options === 'string' ? options : options.url || '';
}; this.printer = options.printer || (FoniaJS._def_printer ?? null);
this.detectPrinter = function() { this.cut_paper = options.cut_paper || '\x1D\x56\x41';
if (!qz.websocket.isActive()) { this.open_draw = options.open_draw || '\x1B\x70\x30\x19\x19';
this.chr_norm = options.chr_norm || '\x1B\x21\x01';
this.chr_bold = options.chr_bold || '\x1B\x21\x10';
this.chr_sbold = options.chr_sbold || '\x1B\x21\x31';
this.chr_uline = options.chr_uline || '\x1B\x21\x80';
this.params = options.params || [];
}
_detectPrinter() {
if (!qz?.websocket?.isActive?.()) {
FoniaJS.showInfo('Status Printer belum Active !'); FoniaJS.showInfo('Status Printer belum Active !');
return false; return false;
} }
return true; return true;
};
this.getPrinters = function() {
return [];
};
this.setPrinter = function(nprint) {
(nprint != null) && (_options.printer = nprint);
};
this.useDefaultPrinter = function() {
b_default = true;
};
this.set_url = function(url) {
_options.url = url;
};
this.add_param = function(par_id, par_val) {
var _getvalue = (par_val) ? par_id + '=' + escape(par_val) : par_id;
_options.param.push(_getvalue);
};
this.exec = function() {
if (!this.detectPrinter()) return;
if (this.b_default) {
var parent = this;
qz.printers.getDefault().then(function(data) {
var nama_p = data;
if (data.name != undefined) {
nama_p = data.name;
} }
parent._options.param.push('Printer='+nama_p);
return parent.do_print(nama_p, parent._options.url, parent._options.param); setPrinter(nprint) {
}, function(data) { if (nprint != null) this.printer = nprint;
FoniaJS.showInfo(data); return this;
});
} else {
var _nprinter = _options.printer;
if (typeof _nprinter === 'object' && _nprinter.name == undefined) {
if (_nprinter.file != undefined) _nprinter = _nprinter.file;
if (_nprinter.host != undefined) _nprinter = _nprinter.host + ":" + _nprinter.port;
} else {
if (_nprinter.name != undefined) _nprinter = _nprinter.name;
if (_nprinter== undefined) _nprinter = 'None';
} }
_options.param.push('Printer='+_nprinter);
this.do_print(_options.printer, _options.url, _options.param); addParam(id, val) {
this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
return this;
} }
};
this.do_print = function(nama_p, url, params) { async exec() {
var qzcfg = qz.configs.create(nama_p); if (!this._detectPrinter()) return;
new Request.JSON({
url: url, let printerName = this.printer;
if (typeof printerName === 'object' && printerName !== null) {
if (printerName.name !== undefined) {
printerName = printerName.name;
} else if (printerName.file !== undefined) {
printerName = printerName.file;
} else if (printerName.host !== undefined) {
printerName = `${printerName.host}:${printerName.port}`;
}
}
printerName = printerName ?? 'None';
this.params.push(`Printer=${printerName}`);
this.doPrint(this.printer, this.url, this.params);
}
doPrint(printerName, url, params) {
const qzcfg = qz.configs.create(printerName);
new Request({
url,
sendParam: params, sendParam: params,
onSuccess: function(ajson) { onSuccess: (ajson) => {
if (typeof ajson === "object") { if (typeof ajson === 'object' && ajson !== null) {
var hasil_cetak = (ajson.type == "escp") ? this.gen_escp(ajson) : this.gen_epl(ajson); const printPayload = ajson.type === 'escp' ? this.genEscp(ajson) : this.genEpl(ajson);
qz.print(qzcfg, hasil_cetak); qz.print(qzcfg, printPayload);
} else { } else {
FoniaJS.showInfo('Error dalam proses Cetak.'); FoniaJS.showInfo('Error dalam proses Cetak.');
} }
}.bind(this) }
}).send(); }).send();
};
this.gen_escp = function(adata) {
var ohead = [];
var osheet = [], ocetak = [];
ohead.push("\x1B\x40");
ohead.push("\x1B\x61\x01");
if (adata.config.logo != null) {
ohead.push({type:'raw',format:'image',data:adata.config.logo,options:{language:'ESCPOS',dotDensity:'double'}});
ohead.push("\x1B\x32");
} }
if (parseInt(adata.config.fontsize) == 2) {
_options.chr_norm = "\x1B\x21\x02"; genEscp(adata) {
const ohead = ['\x1B\x40', '\x1B\x61\x01'];
if (adata.config?.logo) {
ohead.push({
type: 'raw',
format: 'image',
flavor: 'base64',
data: adata.config.logo,
options: { language: 'ESCPOS', dotDensity: 'double' }
});
ohead.push('\x1B\x32');
} }
if (adata.header.length > 0) { if (parseInt(adata.config?.fontsize, 10) === 2) {
for (i = 0; i < adata.header.length; i++) { this.chr_norm = '\x1B\x21\x02';
if (i==0 && adata.config.logo==null) ohead.push(_options.chr_bold);
ohead.push(adata.header[i] + "\x0A");
if (i==0 && adata.config.logo==null) ohead.push(_options.chr_norm);
} }
if (Array.isArray(adata.header)) {
adata.header.forEach((headerLine, index) => {
if (index === 0 && !adata.config?.logo) ohead.push(this.chr_bold);
ohead.push(`${headerLine}\x0A`);
if (index === 0 && !adata.config?.logo) ohead.push(this.chr_norm);
});
} }
for (s = 0; s < adata.content.length; s++) {
if (adata.content[s].length == 0) continue; let ocetak = [];
osheet = ohead.slice(0); if (Array.isArray(adata.content)) {
for (i = 0; i < adata.content[s].length; i++) { adata.content.forEach((sheetContent) => {
var tmp_cont = adata.content[s][i]; if (!sheetContent?.length) return;
tmp_cont = tmp_cont.replace('_SBOLD_', _options.chr_sbold) const osheet = [...ohead];
.replace('_BOLD_', _options.chr_bold) sheetContent.forEach((line) => {
.replace('_NORM_', _options.chr_norm) const formattedLine = line
.replace('_ULINE_', _options.chr_uline); .replace('_SBOLD_', this.chr_sbold)
osheet.push(tmp_cont + "\x0A"); .replace('_BOLD_', this.chr_bold)
} .replace('_NORM_', this.chr_norm)
if (adata.config.cutpaper) { .replace('_ULINE_', this.chr_uline);
osheet.push(_options.cut_paper + "\x0A"); osheet.push(`${formattedLine}\x0A`);
});
if (adata.config?.cutpaper) {
osheet.push(`${this.cut_paper}\x0A`);
} }
ocetak = ocetak.concat(osheet); ocetak = ocetak.concat(osheet);
});
} }
var notaout = [];
if (adata.config.opendraw) { let notaout = [];
notaout.push(_options.open_draw); if (adata.config?.opendraw) {
notaout.push(this.open_draw);
} }
for (i = 0; i < adata.config.times; i++) { const times = adata.config?.times || 1;
for (let i = 0; i < times; i++) {
notaout = notaout.concat(ocetak); notaout = notaout.concat(ocetak);
} }
return notaout; return notaout;
};
this.gen_epl = function(adata) {
var cetak = [];
for (i = 0; i < adata.content.length; i++) {
cetak.push(adata.content[i] + "\r\n");
} }
var notaout = [];
for (i = 0; i < adata.config.times; i++) { genEpl(adata) {
const cetak = (adata.content || []).map((line) => `${line}\r\n`);
let notaout = [];
const times = adata.config?.times || 1;
for (let i = 0; i < times; i++) {
notaout = notaout.concat(cetak); notaout = notaout.concat(cetak);
} }
return notaout; return notaout;
}; }
this.construct(url, options); }
};
+36 -34
View File
@@ -1,19 +1,43 @@
/******************************************************************* /*******************************************************************
Module : /js/qz/qz-start.js Module : /assets/js/qz/qz-start.js
Desc. : JS Setup QZ Connection Desc. : Setup QZ Connection
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : May 27th, 2007. Created Date : May 27th, 2007.
Last Modified : February 8th, 2026. Last Modified : August 22nd, 2026.
(c) 2008 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
const _divStatus = '#divQZStatus';
if (typeof qz !== 'undefined') {
qz.security.setSignatureAlgorithm("SHA512");
qz.security.setCertificatePromise((resolve, reject) => {
jQuery.ajax({url:'qz/cert', cache:false, dataType:'text'}).then(resolve, reject);
});
qz.security.setSignaturePromise((toSign) => {
return (resolve, reject) => {
jQuery.ajax('qz/sign/'+toSign).then(resolve, reject);
};
});
qz.websocket.setClosedCallbacks((evt) => {
_updateState('Inactive');
if (evt.reason) console.info("Connection closed: " + evt.reason);
});
qz.websocket.setErrorCallbacks((evt) => {
_updateState('Error');
if (evt.target != undefined) {
var err = (evt.target.readyState >= 2) ? "Connection to QZ Tray was closed" : "A connection error occurred, check log for details";
console.info(err)
}
});
}
function launchQZ() { function launchQZ() {
if (!qz.websocket.isActive()) { if (!qz.websocket.isActive()) {
window.location.assign("qz:launch"); window.location.assign("qz:launch");
//Retry 5 times, pausing 1 second between each attempt
startConnection({ retries: 5, delay: 1 }); startConnection({ retries: 5, delay: 1 });
} }
} }
@@ -22,10 +46,10 @@ function startConnection(config) {
config = config ? config : {}; config = config ? config : {};
if (!qz.websocket.isActive()) { if (!qz.websocket.isActive()) {
_updateState('Waiting'); _updateState('Waiting');
qz.websocket.connect(config).then(() => { qz.websocket.connect({...config, host: 'localhost'}).then(() => {
_updateState('Active'); _updateState('Active');
_findDefaultPrinter(); _findDefaultPrinter();
}); }).catch(() => _updateState('Error'));
} else { } else {
console.info('An active connection with QZ already exists.'); console.info('An active connection with QZ already exists.');
} }
@@ -35,7 +59,7 @@ function endConnection(){
if (qz.websocket.isActive()) { if (qz.websocket.isActive()) {
qz.websocket.disconnect().then(() => { qz.websocket.disconnect().then(() => {
_updateState('Inactive'); _updateState('Inactive');
}); }).catch(() => _updateState('Error'));
} else { } else {
console.info('No active connection with QZ exists.'); console.info('No active connection with QZ exists.');
} }
@@ -64,30 +88,8 @@ function _updateState(text){
} else { } else {
$(_divStatus).addClass('border-warning'); $(_divStatus).addClass('border-warning');
} }
} $('li#qzL, li#qzS, li#qzZ').removeClass('d-none');
if (text !== "Error") {
const _divStatus = '#divQZStatus'; $('li#qzL').addClass('d-none');
} else $('li#qzS, li#qzZ').addClass('d-none');
if (typeof qz !== 'undefined') {
qz.security.setSignatureAlgorithm("SHA512");
qz.security.setCertificatePromise((resolve, reject) => {
jQuery.ajax({url:'qz/cert', cache:false, dataType:'text'}).then(resolve, reject);
});
qz.security.setSignaturePromise((toSign) => {
return (resolve, reject) => {
jQuery.ajax('qz/sign/'+toSign).then(resolve, reject);
};
});
qz.websocket.setClosedCallbacks((evt) => {
_updateState('Inactive');
if (evt.reason) console.info("Connection closed: " + evt.reason);
});
qz.websocket.setErrorCallbacks((evt) => {
_updateState('Error');
if (evt.target != undefined) {
var err = (evt.target.readyState >= 2) ? "Connection to QZ Tray was closed" : "A connection error occurred, check log for details";
console.info(err)
}
});
startConnection();
} }
-73
View File
@@ -1,73 +0,0 @@
/*******************************************************************
Module : /assets/js/share.js
Desc. : JS Class Share File Cross Browser
Created By : Siau Pheng ([email protected]).
Created Date : May 27th, 2007.
Last Modified : January 19th, 2026.
(c) 2007 - 2026, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
var cSHARE = function(url, options) {
var _options = {
'url': '',
'filename': '',
'title': '',
'text': 'share dari aplikasi'
};
var _params = [];
this.construct = function(url, options){
if (typeof url == 'object') {
$.extend(_options, url);
} else {
$.extend(_options, options);
_options.url = url;
}
};
this.set_url = function (url){
_options.url = url;
};
this.add_param = function(par_id, par_val) {
var _getvalue = (par_val) ? par_id + '=' + escape(par_val) : par_id;
_params.push(_getvalue);
};
this.share = async function() {
if (!_options.url) {
console.warn('cek url !');
return false;
}
var _pro = (window.location.protocol=='https:') ? 'https://' : 'http://';
var _src = _pro+FoniaJS.WEB_HOST + _options.url + '?' + (FoniaJS.RequestParam(_params)).join('&');
try {
const response = await fetch(_src)
.then(response => {
const cDispo = response.headers.get('Content-Disposition');
if (cDispo && cDispo.indexOf('filename=') !== -1) {
_options.filename = cDispo.split('filename=')[1].split(';')[0].replace(/"/g, '');
}
return response;
});
const blob = await response.blob();
const file = new File([blob], _options.filename, { type: blob.type });
if (navigator.canShare && navigator.canShare({ files: [file] })) {
await navigator.share({
files: [file],
title: _options.title ?? _options.filename,
text: _options.text
});
} else {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = _options.filename;
link.click();
}
} catch (error) {
console.error('Sharing gagal:', error);
}
};
this.construct(url, options);
};
+81 -54
View File
@@ -1,60 +1,87 @@
/******************************************************************* /*******************************************************************
Module : /assets/js/simulator.js Module : /assets/js/simulator.js
Desc. : JS Class Simulator Data dalam Code Desc. : JS Class Simulator Data dalam Code
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : March 18th, 2021. Created Date : March 18th, 2021.
Last Modified : December 26th, 2025. Last Modified : November 2nd, 2026.
(c) 2021 - 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************
*
* Class Simulator
*
* Simulator Data untuk sinkron data dengan JSON
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @param {object} options
* @param {string} options.url - Endpoint URL Print Page
* @param {Array} [options.params] - Request payload
*/
*******************************************************************/ class Simulator {
constructor(options = {}) {
this.url = typeof options === 'string' ? options : options.url || '';
this.params = options.params || [];
}
var cSIMULATOR = function(url, options){ addParam(id, val) {
var vars = { this.params.push(val !== undefined ? `${id}=${encodeURIComponent(val)}` : id);
'url': '', return this;
'param': [] }
};
this.construct = function(url, options){ doPreview() {
$.extend(vars, options); if (!this.url) {
vars.url = url; console.warn('Url Simulator dibutuhkan!');
}; return;
this.set_url = function (url){ }
vars.url = url;
};
this.add_param = function (par_id, par_val){
var _getvalue = (par_val) ? par_id + "=" + escape(par_val) : par_id;
vars.param.push(_getvalue);
},
this.doPreview = function(){
if (!vars.url) console.warn(vars.param + ", cek url php!");
vars.data = FoniaJS.RequestParam(vars.param);
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: vars.url, url: this.url,
data: (vars.data).join('&'), data: FoniaJS.RequestParam(this.params),
success: function(data){ dataType: 'json',
if (data.length == 0) { error: typeof onFailure === 'function' ? onFailure : null,
success: (data) => {
if (!data || data.length === 0) {
FoniaJS.showInfo('Tidak ada data !'); FoniaJS.showInfo('Tidak ada data !');
return false; return false;
} }
var _divd = '';
for (var no = 0; no < data.length; no++) { const divContent = data
_divd += '<div class="sim-code px-2 py-2 mb-1 border border-info">' + data[no] + '</div>'; .map(item => `<div class="sim-code px-2 py-2 mb-1 border border-info">${item}</div>`)
} .join('');
var _wtxt =
'<div class="modal fade" id="winAPPSim" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPSimLabel" aria-hidden="true">'+ const modalHtml = `
'<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">'+ <div class="modal fade" id="winAPPSim" tabindex="-1" role="dialog" data-bs-keyboard="false" data-bs-show="false" data-bs-backdrop="static" aria-labelledby="winAPPSimLabel" aria-hidden="true">
'<div class="modal-content">'+ <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fs" role="document">
'<div class="modal-header"><h5 class="modal-title" id="winAPPSimLabel">Code Simulator</h5><div class="ms-auto"><button class="btn me-2" id="tombSalin" type="button" aria-label="Salin Semua"><i class="bi-copy me-2"></i><span aria-hidden="true">Salin Semua</span></button><button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup"><i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span></button></div></div>'+ <div class="modal-content">
'<div class="modal-body">'+_divd+'</div>'+ <div class="modal-header">
'</div></div></div>'; <h5 class="modal-title" id="winAPPSimLabel">Code Simulator</h5>
if ($('#winAPPSim')) $('#winAPPSim').modal('dispose').remove(); <div class="ms-auto">
$(_wtxt).on('shown.bs.modal', function(e){ <button class="btn me-2" id="tombSalin" type="button" aria-label="Salin Semua">
$('#winAPPSim').find('#tombSalin').click(function(){ <i class="bi-copy me-2"></i><span aria-hidden="true">Salin Semua</span>
</button>
<button class="btn" type="button" data-bs-dismiss="modal" aria-label="Tutup">
<i class="bi-x-lg me-2"></i><span aria-hidden="true">Tutup</span>
</button>
</div>
</div>
<div class="modal-body">${divContent}</div>
</div>
</div>
</div>`;
$('#winAPPSim').modal('dispose').remove();
$(modalHtml)
.on('shown.bs.modal', function () {
const $modal = $(this);
// Copy all code blocks
$modal.find('#tombSalin').on('click', () => {
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
$('.sim-code').each(function(i, ele) { $('.sim-code').each((_, ele) => {
var range = document.createRange(); const range = document.createRange();
range.selectNode(ele); range.selectNode(ele);
window.getSelection().addRange(range); window.getSelection().addRange(range);
}); });
@@ -62,23 +89,23 @@ var cSIMULATOR = function(url, options){
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
FoniaJS.showInfo('Sukses salin semua Kode !'); FoniaJS.showInfo('Sukses salin semua Kode !');
}); });
$('#winAPPSim').find('.sim-code').click(function(){
var range = document.createRange(); // Copy individual code block and hide it
$modal.find('.sim-code').on('click', function () {
const range = document.createRange();
range.selectNode(this); range.selectNode(this);
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
window.getSelection().addRange(range); window.getSelection().addRange(range);
document.execCommand('copy'); document.execCommand('copy');
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
//$(this).addClass('bg-success');
$(this).addClass('d-none'); $(this).addClass('d-none');
}); });
}).on('hidden.bs.modal', function(e){ })
.on('hidden.bs.modal', function () {
$(this).remove(); $(this).remove();
}).modal('show'); })
}, .modal('show');
error: (typeof vars.onFailure === "function") ? vars.onFailure : null, }
dataType: 'json'
}); });
}; }
this.construct(url, options); }
};
+9 -8
View File
@@ -17,19 +17,20 @@
} }
], ],
"require": { "require": {
"php" : "^8.3", "php": ">=8.3",
"mpdf/mpdf": ">=8.0", "flightphp/core": "^3.19",
"matthiasmullie/minify": ">=1.3", "flightphp/session": "^0.3",
"endroid/qr-code": ">=4.4", "mpdf/mpdf": "^8.3",
"phpoffice/phpspreadsheet": ">=1.29", "avadim/fast-excel-writer": "^6.19",
"shuchkin/simplexlsxgen": "^1.3" "matthiasmullie/minify": "^1.3",
"endroid/qr-code": "^6.1"
}, },
"autoload": { "autoload": {
"classmap": ["src/"],
"psr-4": { "psr-4": {
"siaupheng\\webapp\\": "src/" "Webapp\\": "src/"
}, },
"files": [ "files": [
"src/Function/Auth.php",
"src/Function/Misc.php" "src/Function/Misc.php"
] ]
}, },
+141
View File
@@ -0,0 +1,141 @@
<?php
/*******************************************************************
Module : /App/Auth.php
Created By : Siau Pheng ([email protected]).
Created Date : March 16th, 2008.
Last Modified : October 31st, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
declare(strict_types=1);
namespace Webapp\App;
use \flight\Engine;
use \flight\Session;
use \flight\database\SimplePdo;
/**
* Class Auth
*
* Kontrol dan Manajemen User Session.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\App
*/
final class Auth {
private int $id_reg = 0;
protected Engine $app;
protected SimplePdo $db;
protected Session $session;
/**
* @param Engine $app
*/
public function __construct(Engine $app){
$this->app = $app;
$this->db = $app->db();
$this->session = $app->session();
$this->id_reg = (int) $this->session->get('WEB_APP', ['PARENT_CODE'=>0])['PARENT_CODE'];
}
public function user_check(string $user, string $pass): bool {
$i_hasil = (int) $this->db->fetchField('SELECT COUNT(*) FROM app_operator WHERE Username=? AND Password=MD5(?) AND Aktif=1', [$user, $pass]) ?? 0;
return ($i_hasil > 0 ? true : false);
}
public function user_data(string $user, bool $gen_module=true): array {
$adata = [ 'Grup_MenuID' => 0 ];
$xdata = $this->db->fetchRow('SELECT UserID,Grup_MenuID,Username,Nama,Posisi,Email,HP,Level,Last_IP,Last_Date FROM app_operator WHERE Username=?', [$user]);
if ($xdata) $adata = $xdata->getData();
if (empty($this->session->get('APP_TOKEN'))) {
$_token = $this->genToken(60);
$this->session->set('APP_TOKEN', $_token);
$this->db->insert('app_token', [ 'Token' => $_token, 'UserID' => $adata['UserID'], 'IP' => $this->app->request()->ip ]);
}
$adata['UNIX'] = time();
$adata['LOCALE'] = $this->session->get('WEB_APP', ['DEF_LOCALE'=>'id'])['DEF_LOCALE'];
$adata['MENU_MODUL'] = [];
if ($gen_module === true && !empty($this->session->get('WEB_APP')['APP_MODULE'])) {
$tallow = $this->db->fetchField('SELECT Modul_Akses FROM app_menu_grup WHERE Grup_MenuID=?', [$adata['Grup_MenuID']]) ?? '';
$aallow = explode(",", $tallow);
if (count($aallow) > 0) {
$stat = $this->db->runQuery('SELECT * FROM (SELECT APP_MODULE,APP_PATH FROM app_config) A LEFT JOIN (SELECT Modul,Urut,Judul,Icon FROM app_menu WHERE Level=0) B ON (A.APP_MODULE=B.Modul) ORDER BY B.Urut');
while ($row = $stat->fetch()) {
if (!in_array($row['Modul'], $aallow) || $row['Modul'] === $this->session->get('WEB_APP')['APP_MODULE']) continue;
$adata['MENU_MODUL'][] = $row;
}
}
}
return $adata;
}
public function user_check_level(string $user, int $level = 9): bool {
$i_hasil = (int) $this->db->fetchField('SELECT COUNT(*) FROM app_operator WHERE Username=? AND Level<=? AND Aktif=1', [$user, $level]);
return ($i_hasil > 0 ? true : false);
}
public function user_log(): void {
$_user_id = $this->session->get('WEB_USER')['UserID'] ?? 0;
if ($_user_id === 0) return;
$a_hasil = $this->db->fetchRow('SELECT * FROM app_logs WHERE UserID=? ORDER BY Last_Date DESC LIMIT 1', [$_user_id]);
if ($a_hasil) {
$this->db->update('app_operator', [ 'Last_IP' => $a_hasil['Last_IP'], 'Last_Date' => $a_hasil['Last_Date'] ], 'UserID=?', [$_user_id]);
}
$this->db->runQuery('INSERT IGNORE INTO app_logs SELECT UserID,Token,IP,DateStamp FROM app_token WHERE Token=?', [$this->session->get('APP_TOKEN')]);
}
public function logout(): void {
$this->db->delete('app_token', 'Token=?', [$this->session->get('APP_TOKEN')]);
}
public function token_check(string $token): bool {
$hasil = (bool) $this->db->fetchField('SELECT 1 FROM app_token WHERE Token=?', [$token ?? $this->session->get('APP_TOKEN')]);
if ($hasil === true) $this->db->runQuery('UPDATE app_token SET DateStamp=NOW() WHERE Token=?', [$token ?? $this->session->get('APP_TOKEN')]);
return $hasil;
}
public function token_update(): void {
$this->db->runQuery('UPDATE app_token SET DateStamp=NOW() WHERE Token=?', [$this->session->get('APP_TOKEN')]);
}
public function redirect(string $appUrl): string {
$source = parse_url($appUrl);
$desti = "";
$hasil = (bool)$this->db->fetchField('SELECT 1 FROM app_host WHERE Nama_Host=?', [$source['host']]);
if ($hasil) $desti = $source['scheme'].$source['host'].$source['path'].'?'.'auth='.$this->session->get('APP_TOKEN');
return $desti;
}
private function genToken(int $length = 16): string {
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($pool, 5)), 0, $length);
}
public function getUserByToken(string $token): string {
$hasil = $this->db->fetchField('SELECT Username FROM app_operator WHERE UserID IN (SELECT UserID FROM app_token WHERE Token=?) LIMIT 1', [$token ?? $this->session->get('APP_TOKEN')]) ?? '';
return $hasil;
}
public function pos_login(int $id_cab, int $id_user, int $unix, string $ip): void {
if (empty($id_cab) || empty($id_user) || empty($ip)) return;
$this->db->delete('disp_login', 'CabID=? AND UserID=?', [$id_cab, $id_user]);
$this->db->runQuery('INSERT INTO disp_login (IP, CabID, UserID, Unix, Date) VALUES (?, ?, ?, ?, NOW())', [ $ip, $id_cab, $id_user, $unix ?? 0 ]);
}
public function pos_logout(int $id_cab, int $id_user, int $unix, string $ip): void {
if (empty($id_cab) || empty($id_user) || empty($ip)) return;
$this->db->delete('disp_login', 'CabID=? AND UserID=? AND IP=? AND Unix=?', [$id_cab, $id_user, $ip, $unix ?? 0 ]);
}
}
?>
+58
View File
@@ -0,0 +1,58 @@
<?php
/*******************************************************************
Module : /App/Register.php
Created By : Siau Pheng ([email protected]).
Created Date : November 23th, 2011.
Last Modified : September 10th, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
declare(strict_types=1);
namespace Webapp\App;
use \flight\Engine;
use \flight\Session;
use \flight\database\SimplePdo;
/**
* Class Register
*
* Registrasi aplikasi.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\App
*/
final class Register {
public function __construct(){
}
public static function reg_id(Session $session): string {
$id = (string) $session->get('WEB_APP', ['REG_ID'=>''])['REG_ID'];
return (substr($id,0,3).".".substr($id,3,3).".".substr($id,6));
}
public static function reg_code(Session $session): string {
$id = (string) $session->get('WEB_APP', ['REG_CODE'=>''])['REG_CODE'];
return (substr($id,0,6)."-".substr($id,6,5)."-".substr($id,11,5)."-".substr($id,16,5)."-".substr($id,21,5)."-".substr($id,26,6));
}
public static function reg_check(Session $session): bool {
$webapp = $session->get('WEB_APP');
return (bool) (isset($webapp['REG_STATUS']) && $webapp['REG_STATUS']) ?? false;
}
public static function reg_status(SimplePdo $db, array $apps): array {
$kode_db = (bool) $db->fetchField('SELECT UPPER(CONVERT(MD5(CONCAT(REG_YEAR,CONVERT(MD5(CONCAT(REG_ID,REG_NAME)) using latin1),REG_ID,REG_NAME)) using latin1))=REG_CODE FROM app_config WHERE REG_ID=?', [$apps['APP_CODE'] ?? '']);
return [ 'REG_STATUS' => $kode_db === true ];
}
}
?>
+31 -22
View File
@@ -2,38 +2,48 @@
/******************************************************************* /*******************************************************************
Module : /Web/Update.php Module : /App/Update.php
Desc. : Class Update Aplikasi Otomatis
Created By : Siau Pheng (siaupheng@3fonia.com). Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : June 14th, 2013. Created Date : June 14th, 2013.
Last Modified : August 12th, 2026. Last Modified : August 22nd, 2026.
(c) 2013 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\App;
use flight\Engine; use \flight\Engine;
/**
* Class Update
*
* Update Otomatis Aplikasi berbasis GIT.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <siaupheng@3fonia.com>
*
* @package Webapp\App
*/
final class Update { final class Update {
protected Array $__conf = []; private Array $__conf = [];
protected Bool $is_git = false; private Bool $is_git = false;
protected Engine $app; protected Engine $app;
public function __construct(?Engine $app = null){ /**
$this->app = (empty($app) === true) ? \Flight::app() : $app; * @param Engine $app
$this->__conf = $app->session()->get('__WEB_APP'); */
public function __construct(Engine $app){
$this->app = $app;
$this->__conf = $app->session()->get('WEB_APP');
$this->current(); $this->current();
} }
public function current() { public function current(): void {
$_target = $this->__conf['REAL_PATH']; if (defined('PROJECT_ROOT')) chdir(PROJECT_ROOT);
if (isset($_target)) chdir($_target);
try { try {
$output = shell_exec("git log -1 --pretty=format:\"%H||%s||%ci\" --abbrev-commit 2>&1"); $output = shell_exec("git log -1 --pretty=format:\"%H||%s||%ci\" --abbrev-commit 2>&1");
if ($output) { if ($output) {
@@ -57,7 +67,7 @@ final class Update {
} }
} }
public function check() { public function check(): array {
$_ainfo['cur_version'] = $this->__conf['APP_BUILD']; $_ainfo['cur_version'] = $this->__conf['APP_BUILD'];
$_ainfo['cur_sha'] = substr($this->__conf['GIT_SHA1'], 0, 10); $_ainfo['cur_sha'] = substr($this->__conf['GIT_SHA1'], 0, 10);
$_ainfo['cur_sha1'] = $this->__conf['GIT_SHA1']; $_ainfo['cur_sha1'] = $this->__conf['GIT_SHA1'];
@@ -89,14 +99,13 @@ final class Update {
return $_ainfo; return $_ainfo;
} }
public function go() { public function go(): array {
$_target = $this->__conf['REAL_PATH'];
$_branch = $this->__conf['GIT_BRANCH'] ?? "main"; $_branch = $this->__conf['GIT_BRANCH'] ?? "main";
if (isset($_target)) { if (defined('PROJECT_ROOT')) {
chdir($_target); chdir(PROJECT_ROOT);
$output = shell_exec("git reset --hard HEAD && git pull origin {$_branch} 2>&1"); $output = shell_exec("git reset --hard HEAD && git pull origin {$_branch} 2>&1");
return ['success'=>true,'message'=>$output]; return ['success'=>true,'message'=>$output];
} else return ['success'=>false,'message'=>"Tanpa REAL_PATH !"]; } else return ['success'=>false,'message'=>"Tanpa PROJECT_ROOT !"];
} }
public function mysql() { public function mysql() {
+79
View File
@@ -0,0 +1,79 @@
<?php
/*******************************************************************
Module : /App/User.php
Created By : Siau Pheng ([email protected]).
Created Date : September 10th, 2026.
Last Modified : September 10th, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
declare(strict_types=1);
namespace Webapp\App;
use \flight\Engine;
/**
* Class User
*
* User & Role Class.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\App
*/
final class User {
private array $web_user;
protected Engine $app;
/**
* @param Engine $app
*/
public function __construct(Engine $app) {
$this->app = $app;
$this->web_user = (array) $app->session()->get('WEB_USER');
}
public function logged(): bool {
return !empty($this->web_user);
}
public function is_level(int $level = 9): bool {
if (($this->web_user['Level'] ?? 9) <= $level) return true;
return false;
}
public function not_level(int $level = 9): bool {
if (($this->web_user['Level'] ?? 9) !== $level) return true;
return false;
}
public function id(int $digit = 0): string|int {
$id_user = (int) $this->web_user['UserID'] ?? 0;
if ($digit > 0) return isi_nol($id_user, $digit);
return $id_user;
}
public function username(): string {
return (string) $this->web_user['Username'] ?? '';
}
public function level(): int {
return (int) $this->web_user['Level'] ?? 9;
}
public function unix(): string {
return (string) $this->web_user['UNIX'] ?? '0';
}
public function nama(): string {
return (string) $this->web_user['Nama'] ?? '';
}
}
+33 -23
View File
@@ -3,34 +3,44 @@
/******************************************************************* /*******************************************************************
Module : /DB/CheckSQL.php Module : /DB/CheckSQL.php
Desc. : Class Check SQL / Repair Database
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : May 11th, 2011. Created Date : May 11th, 2011.
Last Modified : August 13th, 2026. Last Modified : August 21st, 2026.
(c) 2011 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\DB; namespace Webapp\DB;
use Exception; use \Exception;
use flight\Engine; use \flight\Engine;
use flight\Session; use \flight\database\SimplePdo;
use flight\database\SimplePdo;
/**
* Class CheckSQL
*
* Check Database Mysql/MariaDB dan Repair.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\DB
*/
final class CheckSQL { final class CheckSQL {
private Array $__atabel = []; private Array $__atabel = [];
protected Engine $app; protected Engine $app;
protected SimplePdo $db; protected SimplePdo $db;
public function __construct(?Engine $app = null){ /**
$this->app = (empty($app) === true) ? \Flight::app() : $app; * @param Engine $app
$this->db = $this->app->db(); */
public function __construct(Engine $app) {
$this->app = $app;
$this->db = $app->db();
$nama_db = $this->db->fetchField('SELECT DATABASE()'); $nama_db = $this->db->fetchField('SELECT DATABASE()');
$arow = $this->db->fetchAll("SELECT TABLE_NAME,ENGINE,TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='".$nama_db."' AND TABLE_TYPE='BASE TABLE'"); $arow = $this->db->fetchAll("SELECT TABLE_NAME,ENGINE,TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='".$nama_db."' AND TABLE_TYPE='BASE TABLE'");
foreach ($arow as $row) { foreach ($arow as $row) {
@@ -38,14 +48,14 @@ final class CheckSQL {
} }
} }
public function ignore_table() { public function ignore_table(): void {
$tmp_arr = func_get_args(); $tmp_arr = func_get_args();
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0]; if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0];
$farray = array_filter($this->__atabel, fn($child) => !in_array($child[0], $tmp_arr)); $farray = array_filter($this->__atabel, fn($child) => !in_array($child[0], $tmp_arr));
$this->__atabel = array_values($farray); $this->__atabel = array_values($farray);
} }
private function _check_db(array $atabel=[]) { private function _check_db(array $atabel=[]): string {
$pesan = '<span style="color:blue;">OK</span>'; $pesan = '<span style="color:blue;">OK</span>';
$row = $this->db->runQuery('CHECK TABLE '.$atabel[0].' QUICK')->fetch(SimplePdo::FETCH_ASSOC); $row = $this->db->runQuery('CHECK TABLE '.$atabel[0].' QUICK')->fetch(SimplePdo::FETCH_ASSOC);
if (($row['Msg_text'] ?? '') !== 'OK') { if (($row['Msg_text'] ?? '') !== 'OK') {
@@ -57,24 +67,24 @@ final class CheckSQL {
$pesan .= ' -> Auto Repair <span style="color:red;">' . $e->getMessage() . '</span>'; $pesan .= ' -> Auto Repair <span style="color:red;">' . $e->getMessage() . '</span>';
} }
} }
return $atabel[0] . $atabel[3] ." ... ". $pesan ."<br>"; return $atabel[0] . $atabel[3] ." ... ". $pesan;
} }
private function _opti_db(array $atabel=[]) { private function _opti_db(array $atabel=[]): string {
$pesan = '<span style="color:orange;">SKIP</span>'; $pesan = '<span style="color:orange;">SKIP</span>';
if ($atabel[1] === 'MyISAM') { if ($atabel[1] === 'MyISAM') {
$pesan = '<span style="color:blue;">OK</span>'; $pesan = '<span style="color:blue;">OK</span>';
$row = $this->db->runQuery('OPTIMIZE TABLE '.$atabel[0])->fetch(SimplePdo::FETCH_ASSOC); $row = $this->db->runQuery('OPTIMIZE TABLE '.$atabel[0])->fetch(SimplePdo::FETCH_ASSOC);
if (!in_array(strtoupper($row['Msg_text'] ?? ''), ['OK','TABLE IS ALREADY UP TO DATE'])) $pesan = '<span style="color:red;">ERROR</span>'; if (!in_array(strtoupper($row['Msg_text'] ?? ''), ['OK','TABLE IS ALREADY UP TO DATE'])) $pesan = '<span style="color:red;">ERROR</span>';
} }
return $atabel[0] . $atabel[2] ." ... ". $pesan ."<br>"; return $atabel[0] . $atabel[2] ." ... ". $pesan;
} }
public function count_db() { public function count_db(): int {
return count($this->__atabel); return count($this->__atabel);
} }
public function check_db() { public function check_db(): string {
$hasil = "=== CEK DATABASE ===<br>"; $hasil = "=== CEK DATABASE ===<br>";
for ($n=0; $n<count($this->__atabel); $n++) { for ($n=0; $n<count($this->__atabel); $n++) {
$hasil .= $this->_check_db($this->__atabel[$n]); $hasil .= $this->_check_db($this->__atabel[$n]);
@@ -82,11 +92,11 @@ final class CheckSQL {
return $hasil; return $hasil;
} }
public function check_db_step($n) { public function check_db_step(int $n): string {
return $this->_check_db($this->__atabel[$n]); return $this->_check_db($this->__atabel[$n]);
} }
public function optimize_db() { public function optimize_db(): string {
$hasil = "=== OPTIMALISASI DATABASE ===<br>"; $hasil = "=== OPTIMALISASI DATABASE ===<br>";
for ($n=0;$n<count($this->__atabel);$n++) { for ($n=0;$n<count($this->__atabel);$n++) {
$hasil .= $this->_opti_db($this->__atabel[$n]); $hasil .= $this->_opti_db($this->__atabel[$n]);
@@ -94,7 +104,7 @@ final class CheckSQL {
return $hasil; return $hasil;
} }
public function optimize_db_step($n) { public function optimize_db_step(int $n): string {
return $this->_opti_db($this->__atabel[$n]); return $this->_opti_db($this->__atabel[$n]);
} }
} }
+8 -7
View File
@@ -8,13 +8,14 @@
Created Date : May 11th, 2011. Created Date : May 11th, 2011.
Last Modified : May 8th, 2021. Last Modified : May 8th, 2021.
(c) 2011 - 2021, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//todo: deprecated
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\DB; namespace Webapp\DB;
final class DumpSQL { final class DumpSQL {
private $__adata = array(); private $__adata = array();
@@ -23,7 +24,7 @@ final class DumpSQL {
public function __construct($db = null){ public function __construct($db = null){
if (is_resource($db)) { $this->__db = $db; } if (is_resource($db)) { $this->__db = $db; }
else { $this->__db = new \siaupheng\webapp\DB\MySQL(); } else { $this->__db = new Webapp\DB\MySQL(); }
$this->__adata = $this->__db->getConfig(); $this->__adata = $this->__db->getConfig();
} }
@@ -55,7 +56,7 @@ final class DumpSQL {
public function create_backup($folder="backups") { public function create_backup($folder="backups") {
$namafile = date("Ymd").".gz"; $namafile = date("Ymd").".gz";
$folder = $_SESSION['__WEB_APP']['REAL_PATH'].$folder."/".date("Y"); $folder = PROJECT_ROOT.$folder."/".date("Y");
if (!is_dir($folder)) mkdir($folder); if (!is_dir($folder)) mkdir($folder);
if (file_exists($folder."/".$namafile)) unlink($folder."/".$namafile); if (file_exists($folder."/".$namafile)) unlink($folder."/".$namafile);
$_cmd = "mysqldump -C --hex-blob -h ".$this->__adata['host']." -P ".$this->__adata['port']." -u ".$this->__adata['user']." -p".$this->__adata['pwd']." ".$this->__adata['db']; $_cmd = "mysqldump -C --hex-blob -h ".$this->__adata['host']." -P ".$this->__adata['port']." -u ".$this->__adata['user']." -p".$this->__adata['pwd']." ".$this->__adata['db'];
@@ -96,8 +97,8 @@ final class DumpSQL {
} }
public function disk_space() { public function disk_space() {
$_free = disk_free_space($_SESSION['__WEB_APP']['REAL_PATH']); $_free = disk_free_space(PROJECT_ROOT);
$_tot = disk_total_space($_SESSION['__WEB_APP']['REAL_PATH']); $_tot = disk_total_space(PROJECT_ROOT);
$_used = $_tot - $_free; $_used = $_tot - $_free;
$_aret = array(); $_aret = array();
$_aret['TOTAL'] = $this->format_bytes($_tot); $_aret['TOTAL'] = $this->format_bytes($_tot);
@@ -109,7 +110,7 @@ final class DumpSQL {
} }
public function update_list($folder="backups") { public function update_list($folder="backups") {
$folder = $_SESSION['__WEB_APP']['REAL_PATH'].$folder; $folder = PROJECT_ROOT.$folder;
$this->__db->Query("TRUNCATE TABLE app_backup"); $this->__db->Query("TRUNCATE TABLE app_backup");
$afile = $this->get_file_list($folder); $afile = $this->get_file_list($folder);
foreach($afile as $_key => $_val) { foreach($afile as $_key => $_val) {
+39 -23
View File
@@ -3,24 +3,34 @@
/******************************************************************* /*******************************************************************
Module : /DB/SQL.php Module : /DB/SQL.php
Desc. : Class Query database
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : February 2nd, 2007. Created Date : February 2nd, 2007.
Last Modified : August 14th, 2026. Last Modified : September 11th, 2026.
(c) 2008 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\DB; namespace Webapp\DB;
use flight\Engine; use ErrorException;
use flight\Session; use \flight\Engine;
use flight\database\SimplePdo; use \flight\Session;
use siaupheng\webapp\Web\Param; use \flight\database\SimplePdo;
use Webapp\Web\Param;
/**
* Class SQL
*
* Database CRUD dengan data dari GET/POST.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\DB
*/
final class SQL { final class SQL {
private string $_ttable; private string $_ttable;
@@ -28,13 +38,19 @@ final class SQL {
private array $_awhere = []; private array $_awhere = [];
protected Engine $app; protected Engine $app;
protected SimplePdo $db; protected SimplePdo $db;
protected array $webuser;
public function __construct(?Engine $app = null){ /**
$this->app = (empty($app) === true) ? \Flight::app() : $app; * @param Engine $app
$this->db = $this->app->db(); */
public function __construct(Engine $app){
$this->app = $app;
$this->db = $app->db();
$this->webuser = $app->session()->get('WEB_USER', ['UserID'=>0, 'UNIX'=>0]);
} }
public function SafeSQL($ttext = "") {//delete //todo: deprecated
public function SafeSQL($ttext = "") {
return $ttext; return $ttext;
} }
@@ -42,10 +58,10 @@ final class SQL {
$this->_ttable = $table; $this->_ttable = $table;
} }
public function add_param(string $param = "", string|int|null $value = null, bool $nomor = false): void { public function add_param(string $param = "", mixed $value = null, bool $nomor = false): void {
$this->_afield[] = [ $this->_afield[] = [
'field' => $param, 'field' => $param,
'value' => (is_null($value) && !empty(Param::Get($param))) ? Param::Get($param) : $value, 'value' => is_null($value) ? Param::Get($param) : $value,
'numbe' => $nomor, 'numbe' => $nomor,
]; ];
} }
@@ -53,7 +69,7 @@ final class SQL {
public function add_param_date(string $param = "", string|null $value = null): void { public function add_param_date(string $param = "", string|null $value = null): void {
$this->_afield[] = [ $this->_afield[] = [
'field' => $param, 'field' => $param,
'value' => tgl_sql2str((is_null($value) && !empty(Param::Get($param))) ? Param::Get($param) : $value), 'value' => tgl_sql2str(is_null($value) ? Param::Get($param, '00-00-0000') : $value),
'numbe' => false, 'numbe' => false,
]; ];
} }
@@ -69,7 +85,7 @@ final class SQL {
public function add_param_unix(): void { public function add_param_unix(): void {
$this->_afield[] = [ $this->_afield[] = [
'field' => 'Unix', 'field' => 'Unix',
'value' => user_unix(), 'value' => $this->webuser['UNIX'],
'numbe' => false, 'numbe' => false,
]; ];
} }
@@ -77,7 +93,7 @@ final class SQL {
public function add_param_userid(string $param = "UserID"): void { public function add_param_userid(string $param = "UserID"): void {
$this->_afield[] = [ $this->_afield[] = [
'field' => $param, 'field' => $param,
'value' => user_id(), 'value' => $this->webuser['UserID'],
'numbe' => false, 'numbe' => false,
]; ];
} }
@@ -85,7 +101,7 @@ final class SQL {
public function add_where_unix(): void { public function add_where_unix(): void {
$this->_awhere[] = [ $this->_awhere[] = [
'field' => 'Unix', 'field' => 'Unix',
'value' => user_id(), 'value' => $this->webuser['UNIX'],
'numbe' => false, 'numbe' => false,
]; ];
} }
@@ -98,10 +114,10 @@ final class SQL {
]; ];
} }
public function add_where(string $field = "", string|int|null $value = null, bool $nomor = false): void { public function add_where(string $field = "", mixed $value = null, bool $nomor = false): void {
$this->_awhere[] = [ $this->_awhere[] = [
'field' => $field, 'field' => $field,
'value' => (is_null($value) && !empty(Param::Get($field))) ? Param::Get($field) : $value, 'value' => is_null($value) ? Param::Get($field) : $value,
'numbe' => $nomor, 'numbe' => $nomor,
]; ];
} }
@@ -170,8 +186,8 @@ final class SQL {
$this->db->runQuery($tsql, $__aexec); $this->db->runQuery($tsql, $__aexec);
} }
public function LastInsertID(): int { public function LastInsertID(?string $name = null): mixed {
return $this->db->lastInsertId(); return $this->db->lastInsertId($name);
} }
} }
+20 -10
View File
@@ -2,32 +2,42 @@
/******************************************************************* /*******************************************************************
Module : /Data/CSV.php Module : /Data/Csv.php
Desc. : Class pembuatan file CSV
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : April 2rd, 2011. Created Date : April 2rd, 2011.
Last Modified : July 25th, 2026. Last Modified : August 21st, 2026.
(c) 2011 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Data; namespace Webapp\Data;
define( DELI_CSV, ";" ); define( DELI_CSV, ";" );
define( CRLF_CSV, "\r\n" ); define( CRLF_CSV, "\r\n" );
final class CSV { /**
* Class Csv
*
* Membuat file Csv.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Data
*/
final class Csv {
private $__avalue = []; private $__avalue = [];
private $__atabel = []; private $__atabel = [];
public function add_field($tnama="", $ffunc="") { public function add_field(string $nama, string $func=""): void {
$this->__atabel[] = [$tnama, $ffunc]; $this->__atabel[] = [ $nama, $func ];
} }
public function add_value() { public function add_value(): void {
$tmp_arr = func_get_args(); $tmp_arr = func_get_args();
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0]; if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0];
$tmp_out = ""; $tmp_out = "";
@@ -44,7 +54,7 @@ final class CSV {
$this->__avalue[] = $tmp_out; $this->__avalue[] = $tmp_out;
} }
public function download($nama_file="export.csv") { public function download(string $nama_file="export.csv"): void {
$out = ""; $tmp_out = ""; $out = ""; $tmp_out = "";
// add header // add header
for ($i=0; $i<count($this->__atabel); $i++) { for ($i=0; $i<count($this->__atabel); $i++) {
+171 -90
View File
@@ -3,70 +3,103 @@
/******************************************************************* /*******************************************************************
Module : /Data/Cache.php Module : /Data/Cache.php
Desc. : Class cache css dan js aplikasi
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : March 16th, 2008. Created Date : March 16th, 2008.
Last Modified : August 19th, 2026. Last Modified : October 12th, 2026.
(c) 2008 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Data; namespace Webapp\Data;
use flight\Engine; use \flight\Engine;
use FilesystemIterator; use \flight\Session;
use MatthiasMullie\Minify; use \flight\database\SimplePdo;
use SplFileInfo; use \FilesystemIterator;
use \MatthiasMullie\Minify;
use \SplFileInfo;
/**
* Class Cache
*
* Membuat Cache gabungan CSS dan JS sesuai dengan session.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Data
*/
final class Cache { final class Cache {
private $__cachedir = ""; private string $cachedir;
private $__cssdir = ""; private string $cssdir;
private $__localedir = ""; private string $localedir;
private $__jsdir = ""; private string $jsdir;
private $__jsapp = ""; private string $jsapp;
private $__rootdir = ""; private string $rootdir;
private $__ini = []; private array $ini;
private array $web_app;
private array $web_user;
private array $my_menu = [];
private bool $is_login;
private bool $IS_DEV = false;
protected Engine $app; protected Engine $app;
protected array $__asset = []; protected SimplePdo $db;
protected array $asset = [];
public function __construct(?Engine $app = null, array $asset = []) { /**
$this->app = (empty($app) === true) ? Flight::app() : $app; * @param Engine $app
$this->__asset = $asset; */
$rootdir = (defined('PROJECT_ROOT')) ? PROJECT_ROOT : $this->app->get('flight.root_dir');
$this->__cachedir = $rootdir . '/public/chunks/'; public function __construct(Engine $app) {
$this->__localedir = $rootdir . '/app/locale/'; $this->app = $app;
$this->__jsapp = $rootdir . '/app/scripts/'; $this->db = $app->db();
$this->__cssdir = __DIR__ . '/../../assets/css/'; $this->web_app = $app->session()->get('WEB_APP') ?? [];
$this->__jsdir = __DIR__ . '/../../assets/js/'; $this->web_user = $app->session()->get('WEB_USER') ?? [];
$this->__rootdir = $rootdir . '/'; $this->is_login = (count($this->web_user) > 0);
if (!is_dir($this->__cachedir)) mkdir($this->__cachedir, 0755, true);
if (!file_exists($this->__cachedir . "cache.ini")) { $rootdir = (defined('PROJECT_ROOT')) ? PROJECT_ROOT : $app->get('app.project_root');
$this->__ini = [ $this->cachedir = $rootdir . '/temp/cache/';
'login' => [ 'stamp' => 0, 'css' => "", 'js' => "", 'app' => "", ], $this->localedir = $rootdir . '/app/Scripts/locale/';
'index' => [ 'stamp' => 0, 'css' => "", 'js' => "", 'app' => "", ], $this->jsapp = $rootdir . '/app/Scripts/';
'display' => [ 'stamp' => 0, 'css' => "", 'js' => "", 'app' => "", ], $this->cssdir = realpath(__DIR__ . '/../../assets/css') . '/';
$this->jsdir = realpath(__DIR__ . '/../../assets/js') . '/';
$this->rootdir = $rootdir . '/';
if (!is_dir($this->cachedir)) mkdir($this->cachedir, 0755, true);
if (!file_exists($this->cachedir . "cache.ini")) {
$this->ini = [
'login' => [ 'css' => "", 'js' => "", 'app' => "", ],
'index' => [ 'css' => "", 'js' => "", 'app' => "", ],
'monitor' => [ 'css' => "", 'js' => "", 'app' => "", ],
]; ];
$this->save_ini(); $this->save_ini();
} else $this->__ini = parse_ini_file($this->__cachedir . "cache.ini", true); } else $this->ini = parse_ini_file($this->cachedir . "cache.ini", true);
} }
private function gen_file($mode = "index", $type = "css") { public function set_dev(bool $is_dev = false): void {
$this->IS_DEV = $is_dev;
}
public function set_asset(array $asset = []): void {
$this->asset = $asset;
}
private function gen_file(string $mode = "index", string $type = "css"): array {
$itime = 0; $itime = 0;
$afile = []; $afile = [];
if ($type == "css") { if ($type === "css") {
$_afile = array_unique($this->__asset['css'][$mode] ?? []); $_afile = array_unique($this->asset['css'][$mode] ?? []);
foreach ($_afile as $__key => $val) { foreach ($_afile as $__key => $val) {
if (!file_exists($this->__cssdir . $val)) continue; if (!file_exists($this->cssdir . $val)) continue;
$_file = new SplFileInfo($this->__cssdir . $val); $_file = new SplFileInfo($this->cssdir . $val);
$afile[] = $_file->getRealPath(); $afile[] = $_file->getRealPath();
$itime += $_file->getMTime(); $itime += $_file->getMTime();
} }
if (is_dir($this->__rootdir . 'app/css/')) { if (is_dir($this->rootdir . 'app/Styles/')) {
$iterator = new FilesystemIterator($this->__rootdir . 'app/css/', FilesystemIterator::SKIP_DOTS); $iterator = new FilesystemIterator($this->rootdir . 'app/Styles/', FilesystemIterator::SKIP_DOTS);
foreach ($iterator as $_file) { foreach ($iterator as $_file) {
if ($_file->isFile()) { if ($_file->isFile()) {
$afile[] = $_file->getRealPath(); $afile[] = $_file->getRealPath();
@@ -74,18 +107,18 @@ final class Cache {
} }
} }
} }
} else if ($type == "js") { } else if ($type === "js") {
$_afile = array_unique($this->__asset['js'][$mode] ?? []); $_afile = array_unique($this->asset['js'][$mode] ?? []);
foreach ($_afile as $__key => $val) { foreach ($_afile as $__key => $val) {
if (!file_exists($this->__jsdir . $val)) continue; if (!file_exists($this->jsdir . $val)) continue;
$_file = new SplFileInfo($this->__jsdir . $val); $_file = new SplFileInfo($this->jsdir . $val);
$afile[] = $_file->getRealPath(); $afile[] = $_file->getRealPath();
$itime += $_file->getMTime(); $itime += $_file->getMTime();
} }
// add locale file // add locale file
$ljs = ($this->app->session()->get('__WEB_USER')['LOCALE'] ?? $this->app->session()->get('__WEB_APP')['DEF_LOCALE']).'.js'; $ljs = ($this->web_user['LOCALE'] ?? $this->web_app['DEF_LOCALE']).'.js';
if (is_dir($this->__localedir)) { if (is_dir($this->localedir)) {
$iterator = new \FilesystemIterator($this->__localedir, \FilesystemIterator::SKIP_DOTS); $iterator = new \FilesystemIterator($this->localedir, \FilesystemIterator::SKIP_DOTS);
foreach ($iterator as $_file) { foreach ($iterator as $_file) {
if ($_file->isFile() && str_ends_with($_file->getFilename(), $ljs)) { if ($_file->isFile() && str_ends_with($_file->getFilename(), $ljs)) {
$afile[] = $_file->getRealPath(); $afile[] = $_file->getRealPath();
@@ -93,34 +126,40 @@ final class Cache {
} }
} }
} }
} else if ($type == "app") { } else if ($type === "app") {
if (is_dir($this->jsapp)) {
if (file_exists($this->jsapp . $mode . '.js')) {
$_file = new SplFileInfo($this->jsapp . $mode . '.js');
$afile[] = $_file->getRealPath();
$itime += $_file->getMTime();
}
if ($mode === 'index') { if ($mode === 'index') {
if (is_dir($this->__jsapp)) { $_ajs = ['system/profile', 'system/chpasswd'];
$dirIterator = new \RecursiveDirectoryIterator($this->__jsapp, \RecursiveDirectoryIterator::SKIP_DOTS); $amenu = array_values($this->my_menu ?? []);
$iterator = new \RecursiveIteratorIterator($dirIterator); $_ops = $this->db->fetchColumn("SELECT Link FROM app_menu WHERE Kode IN (?) AND Link<>'' ORDER BY Kode", [$amenu]);
if ($_ops) $_ajs = array_merge($_ajs, $_ops);
$dirIterator = new \RecursiveDirectoryIterator($this->jsapp, \RecursiveDirectoryIterator::SKIP_DOTS);
$dirFilter = new \RecursiveCallbackFilterIterator($dirIterator, fn($curr, $key, $iter) => !($curr->isDir() && in_array($curr->getFilename(), ['locale'])));
$iterator = new \RecursiveIteratorIterator($dirFilter);
foreach ($iterator as $_file) { foreach ($iterator as $_file) {
if ($_file->isFile() && !in_array($_file->getFilename(), ['login.js','display.js'])) { if ($_file->isFile() &&
!in_array($_file->getFilename(), ['login.js','index.js','monitor.js']) &&
array_any($_ajs, fn($js) => str_ends_with(str_replace(['_','\\'],['','/'],$_file->getRealPath()), $js.'.js'))) {
$afile[] = $_file->getRealPath(); $afile[] = $_file->getRealPath();
$itime += $_file->getMTime(); $itime += $_file->getMTime();
} }
} }
} }
} else {
if (file_exists($this->__jsapp . $mode . '.js')) {
$_file = new SplFileInfo($this->__jsapp . $mode . '.js');
$afile[] = $_file->getRealPath();
$itime += $_file->getMTime();
}
} }
} }
return [ $afile, $itime ]; return [ $afile, $itime ];
} }
private function create_cache($type = "css", $afile = [], $cachefile = "") { private function create_cache(string $type = "css", array $afile = [], string $cachefile = ""): void {
$contents = ''; $smini = null; $contents = ''; $smini = null;
$bpublic = ($this->app->request()->ip === '127.0.0.1') ? false : true; $bpublic = ($this->app->request()->ip === '127.0.0.1') ? false : true;
if ($bpublic == true) { if ($bpublic === true) {
if ($type=="css") { if ($type === "css") {
$smini = new Minify\CSS; $smini = new Minify\CSS;
} else { } else {
$smini = new Minify\JS; $smini = new Minify\JS;
@@ -128,7 +167,7 @@ final class Cache {
} }
foreach ($afile as $nfile) { foreach ($afile as $nfile) {
if (file_exists($nfile)) { if (file_exists($nfile)) {
if ($bpublic == true) { if ($bpublic === true) {
$smini->add($nfile); $smini->add($nfile);
} else { } else {
$contents .= "\n". file_get_contents($nfile); $contents .= "\n". file_get_contents($nfile);
@@ -136,61 +175,103 @@ final class Cache {
} }
} }
if ($bpublic) $contents = $smini->minify(); if ($bpublic) $contents = $smini->minify();
file_put_contents($this->__cachedir . $cachefile, $contents); file_put_contents($this->cachedir . $cachefile, $contents);
} }
private function save_ini() { private function save_ini(): void {
$content = ""; $content = "";
foreach ($this->__ini as $key => $elem) { unset ($this->ini['index']['app']);
$content .= ($content==""?"":"\n"). "[".$key."]\n"; foreach ($this->ini as $key => $elem) {
$content .= ($content === ""?"" : "\n"). "[".$key."]\n";
foreach ($elem as $key2 => $elem2) { foreach ($elem as $key2 => $elem2) {
if ($elem2=="") $content .= $key2." = \n"; if ($key)
if ($elem2 === "") $content .= $key2." = \n";
else $content .= $key2." = \"".$elem2."\"\n"; else $content .= $key2." = \"".$elem2."\"\n";
} }
} }
file_put_contents($this->__cachedir . "cache.ini", $content); file_put_contents($this->cachedir . "cache.ini", $content);
}
public function Cache(string $mode = "index"): array {
if ($mode === 'index') {
$this->ini['index']['app'] = (string) $this->db->fetchField("SELECT JS FROM app_operator_profile WHERE UserID=?", [$this->web_user['UserID']]) ?? '';
$_menu = $this->db->fetchField("SELECT CONVERT(DECODE(Menu,?) USING UTF8MB4) Menu FROM app_operator WHERE UserID=?", [$this->web_app['PARENT_CODE'], $this->web_user['UserID']]) ?? '';
$_gmenu = '';
if ($this->web_user['Grup_MenuID'] > 0) $_gmenu = $this->db->fetchField('SELECT CONVERT(DECODE(Menu,?) USING UTF8MB4) Menu FROM app_menu_grup WHERE Grup_MenuID=?', [$this->web_app['PARENT_CODE'], $this->web_user['Grup_MenuID']]) ?? '';
$this->my_menu = array_filter(array_unique(explode(",", $_gmenu .",". $_menu)));
} }
public function Cache($mode = "index") {
$is_modif = false; $is_modif = false;
foreach (['css','js','app'] as $type) { foreach (['css','js','app'] as $type) {
$is_index_app = ($mode === 'index' && $type === 'app');
if (empty($this->ini[$mode][$type]) || !file_exists($this->cachedir . $this->ini[$mode][$type]) || ($this->IS_DEV)) {
[ $afile, $lastmod ] = $this->gen_file($mode, $type); [ $afile, $lastmod ] = $this->gen_file($mode, $type);
$cachefile = md5($mode . $type . $lastmod) . ($type=='css'?'.css':'.js'); $cachefile = md5($mode . '-' . $type . '-' . $lastmod) . ($type === 'css' ? '.css' : '.js');
if (!file_exists($this->__cachedir . $cachefile) || ((time()-(int)$this->__ini[$mode]['stamp']) > (86400*14))) { if ($is_index_app) $cachefile = md5('index-app-' . (string) $this->web_user['UserID'] . $lastmod) . '.js';
$this->create_cache($type, $afile, $cachefile); $this->create_cache($type, $afile, $cachefile);
$this->__ini[$mode][$type] = $cachefile; $this->ini[$mode][$type] = $cachefile;
$this->__ini[$mode]['stamp'] = time(); if ($is_index_app) $this->db->runQuery("INSERT INTO app_operator_profile (UserID,JS) VALUES (?, ?) ON DUPLICATE KEY UPDATE JS=VALUES(JS)", [$this->web_user['UserID'], $cachefile]);
$is_modif = true; $is_modif = true;
} }
} }
if ($is_modif === true) $this->save_ini();
$acache = [ 'CSS' => [], 'JS' => [], 'JSX' => [] ];
foreach ($this->__ini[$mode] as $_type => $_file) {
if ($_type == 'stamp') continue;
if (file_exists($this->__cachedir . $_file)) {
if ($_type === 'css') {
$acache['CSS'][] = 'chunks/' . $_file;
} else $acache['JS'][] = 'chunks/' . $_file;
}
}
$session = $this->app->session(); $acache = [ 'CSS' => [], 'JS' => [], 'JSX' => [] ];
$aoption = $session->get('__WEB_APP'); foreach ($this->ini[$mode] as $_type => $_file) {
$auser = $session->get('__WEB_USER'); if (file_exists($this->cachedir . $_file)) {
if ($_type === 'css') {
$acache['CSS'][] = 'cache/' . $_file;
} else $acache['JS'][] = 'cache/' . $_file;
}
}
if ($is_modif === true) $this->save_ini();
$nonce = $this->app->get('csp_nonce'); $nonce = $this->app->get('csp_nonce');
$_secret = $aoption['APP_CODE'] .";". $aoption['APP_SID'] .";". $this->app->request()->getVar('HTTP_HOST') .";". "http://".$this->app->request()->getVar('HTTP_HOST') .";". isMobile() .";". ($aoption['AUTH_LEVEL']?$aoption['AUTH_LEVEL']:0); $_secret = $this->web_app['APP_CODE'] .";". $this->web_app['APP_SID'] .";". $this->app->request()->getVar('HTTP_HOST') .";". "http://".$this->app->request()->getVar('HTTP_HOST') .";". isMobile() .";". ($this->web_app['AUTH_LEVEL']?$this->web_app['AUTH_LEVEL']:0);
$_suser = (user_logged()) ? $auser['Username'] .";". $auser['UserID'] .";". $auser['Grup_MenuID'] .";". $auser['Level'] : "nobody;999;999;9"; $_suser = ($this->is_login) ? $this->web_user['Username'] .";". $this->web_user['UserID'] .";". $this->web_user['Grup_MenuID'] .";". $this->web_user['Level'] : "nobody;999;999;9";
$acache['JSX'][] = '<script nonce="'.$nonce.'" type="text/javascript">'; $acache['JSX'][] = '<script nonce="'.$nonce.'" type="text/javascript">';
$acache['JSX'][] = 'FoniaJS.setConfig("'.base64_encode($_secret).'", "'.base64_encode($_suser).'");'; $acache['JSX'][] = 'FoniaJS.setConfig("'.base64_encode($_secret).'", "'.base64_encode($_suser).'");';
$acache['JSX'][] = '$(document).ready(function(){'; $acache['JSX'][] = '$(document).ready(function(){';
if (user_logged()) $acache['JSX'][] = 'FoniaJS.loadMenu();'; if ($this->is_login) $acache['JSX'][] = 'FoniaJS.loadMenu();';
$acache['JSX'][] = '});'; $acache['JSX'][] = '});';
$acache['JSX'][] = '</script>'; $acache['JSX'][] = '</script>';
return $acache; return $acache;
} }
public function Make_Cache(array $amode = ['login', 'index', 'monitor']): array {
$is_modif = false;
foreach ($amode as $mode) {
foreach (['css','js','app'] as $type) {
if ($mode === 'index' && $type === 'app') continue;
if (empty($this->ini[$mode][$type]) || !file_exists($this->cachedir . $this->ini[$mode][$type])) {
[ $afile, $lastmod ] = $this->gen_file($mode, $type);
$cachefile = md5($mode . '-' . $type . '-' . $lastmod) . ($type === 'css' ? '.css' : '.js');
$this->create_cache($type, $afile, $cachefile);
$this->ini[$mode][$type] = $cachefile;
$is_modif = true;
}
}
}
if ($is_modif === true) $this->save_ini();
if (in_array('index', $amode)) {
$agrup = $this->db->fetchAll('SELECT Grup_MenuID,CONVERT(DECODE(Menu,?) USING UTF8MB4) Menu FROM app_menu_grup', [$this->web_app['PARENT_CODE']]);
$auser = $this->db->fetchAll("SELECT A.*,IFNULL(B.JS,'') JS FROM (SELECT UserID,Grup_MenuID,CONVERT(DECODE(Menu,?) USING UTF8MB4) Menu FROM app_operator) A LEFT JOIN app_operator_profile B ON (A.UserID=B.UserID)", [$this->web_app['PARENT_CODE']]);
foreach ($auser as $user) {
if (empty($user['JS']) || !file_exists($this->cachedir . $user['JS'])) {
$this->my_menu = array_filter(array_unique(explode(",", $user['Menu'] .",". ($agrup[$user['Grup_MenuID']]['Menu'] ?? ''))));
[ $afile, $lastmod ] = $this->gen_file('index', 'app');
$cachefile = md5('index-app-' . (string) $user['UserID'] . $lastmod) . '.js';
$this->create_cache('app', $afile, $cachefile);
$this->db->runQuery("INSERT INTO app_operator_profile (UserID,JS) VALUES (?, ?) ON DUPLICATE KEY UPDATE JS=VALUES(JS)", [$user['UserID'], $cachefile]);
}
}
}
return [ 'status' => 1, 'user_count' => count($auser) ];
}
} }
?> ?>
+75 -56
View File
@@ -2,78 +2,97 @@
/******************************************************************* /*******************************************************************
Module : /Data/PDF.php Module : /Data/Pdf.php
Desc. : Class pembuatan file PDF
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : April 18th, 2015. Created Date : April 18th, 2015.
Last Modified : July 25th, 2026. Last Modified : August 21st, 2026.
(c) 2015 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Data; namespace Webapp\Data;
use Mpdf; use \flight\Engine;
use \Mpdf;
final class PDF { /**
private $__avalue = []; * Class Pdf
private $__atabel = []; *
private $__theader = ""; * Membuat file Pdf.
private $__tcolname = ""; *
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Data
*/
public function __construct() { final class Pdf {
private array $avalue = [];
private array $atabel = [];
private string $__theader = "";
private string $__tcolname = "";
protected array $webapp;
protected array $webuser;
/**
* @param Engine $app
*/
public function __construct(Engine $app) {
$this->webapp = $app->session()->get('WEB_APP', ['REG_NAME'=>'', 'APP_NAME'=>'', 'APP_VER'=>'']);
$this->webuser = $app->session()->get('WEB_USER', ['UserID'=>0, 'Username'=>'']);
} }
public function add_field($tnama="", $nlebar=0, $talign="L", $ffunc="", $nkol=1) { public function add_field(string $nama, string|int $lebar=0, string $align="L", string $func="", int $kol=1): void {
$this->__atabel[] = array($tnama, $nlebar.'%', $ffunc, $talign, $nkol); $this->atabel[] = [ $nama, (string)$lebar.'%', $func, $align, $kol ];
} }
public function show_fields() { public function show_fields(): void {
$tmp_out = "<thead><tr>"; $tmp_out = "<thead><tr>";
for ($i=0; $i<count($this->__atabel); $i++) { for ($i=0; $i<count($this->atabel); $i++) {
if ($this->__atabel[$i][4]==0) continue; if ($this->atabel[$i][4]==0) continue;
$tmp_out .= $this->set_align($this->__atabel[$i][0], $this->__atabel[$i][1], "C", false, true, $this->__atabel[$i][4]); $tmp_out .= $this->set_align($this->atabel[$i][0], $this->atabel[$i][1], "C", false, true, $this->atabel[$i][4]);
} }
$tmp_out .= "</tr></thead>"; $tmp_out .= "</tr></thead>";
$this->__tcolname = $tmp_out; $this->__tcolname = $tmp_out;
} }
public function clear() { public function clear(): void {
$this->__avalue = []; $this->avalue = [];
$this->__theader = ""; $this->__theader = "";
$this->__tcolname = ""; $this->__tcolname = "";
} }
public function set_header($tlapor="", $tperiod="") { public function set_header(string $lapor, string $period): void {
$thasil = '<div style="text-align:center;font-size:12pt;"><b>'.strtoupper($_SESSION['__WEB_APP']['REG_NAME']); $thasil = '<div style="text-align:center;font-size:12pt;"><b>'.strtoupper($this->webapp['REG_NAME']);
$thasil .= (($tlapor=="") ? "" : '<br>'.$tlapor) . '</b>'; $thasil .= (($lapor=="") ? "" : '<br>'.$lapor) . '</b>';
$thasil .= ($tperiod=="") ? "" : '<br><span style="font-size:11pt;">'.$tperiod.'</span>'; $thasil .= ($period=="") ? "" : '<br><span style="font-size:11pt;">'.$period.'</span>';
$thasil .= '</div><br>'; $thasil .= '</div><br>';
$this->__theader = $thasil; $this->__theader = $thasil;
} }
public function add_value() { public function add_value(): void {
$tmp_arr = func_get_args(); $tmp_arr = func_get_args();
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0]; if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0];
$tmp_out = "<tr>"; $tmp_out = "<tr>";
for ($i=0; $i<count($this->__atabel); $i++) { for ($i=0; $i<count($this->atabel); $i++) {
$tmp_val = $tmp_arr[$i]; $tmp_val = $tmp_arr[$i];
if (is_array($this->__atabel[$i][2]) && trim($tmp_val)<>"") { if (is_array($this->atabel[$i][2]) && trim($tmp_val)<>"") {
$tmp_val = $this->__atabel[$i][2][$tmp_val]; $tmp_val = $this->atabel[$i][2][$tmp_val];
} else if (function_exists($this->__atabel[$i][2]) && trim($tmp_val)<>"") { } else if (function_exists($this->atabel[$i][2]) && trim($tmp_val)<>"") {
$tmp_val = call_user_func($this->__atabel[$i][2], $tmp_val); $tmp_val = call_user_func($this->atabel[$i][2], $tmp_val);
} }
$tmp_val = $this->set_align($tmp_val, $this->__atabel[$i][1], $this->__atabel[$i][3]); $tmp_val = $this->set_align($tmp_val, $this->atabel[$i][1], $this->atabel[$i][3]);
$tmp_out .= $tmp_val; $tmp_out .= $tmp_val;
} }
$tmp_out .= "</tr>"; $tmp_out .= "</tr>";
$this->__avalue[] = $tmp_out; $this->avalue[] = $tmp_out;
} }
public function add_span() { public function add_span(): void {
$tmp_arr = func_get_args(); $tmp_arr = func_get_args();
$tmp_out = "<tr>"; $tmp_out = "<tr>";
for ($i=0; $i<count($tmp_arr); $i++) { for ($i=0; $i<count($tmp_arr); $i++) {
@@ -85,22 +104,22 @@ final class PDF {
$tmp_out .= $tmp_val; $tmp_out .= $tmp_val;
} }
$tmp_out .= "</tr>"; $tmp_out .= "</tr>";
$this->__avalue[] = $tmp_out; $this->avalue[] = $tmp_out;
} }
public function add_empty() { public function add_empty(): void {
$this->__avalue[] = $this->set_align("&nbsp;", 0, "L,NB", true); $this->avalue[] = $this->set_align("&nbsp;", 0, "L,NB", true);
} }
public function add_stamp($ctk_ke=1, $bnama=false) { public function add_stamp(int $ctk_ke=1, bool $tampil_nama=false): void {
$this->__avalue[] = $this->set_align($_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):""), 0, "L,NB", true); $this->avalue[] = $this->set_align($this->get_stamp($ctk_ke, $tampil_nama), 0, "L,NB", true);
} }
public function get_stamp($ctk_ke=1, $bnama=false) { public function get_stamp(int $ctk_ke=1, bool $tampil_nama=false): string {
return $_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):""); return $this->webapp['APP_NAME']."v".$this->webapp['APP_VER'].date("\@ymd.His")."-".isi_nol($this->webuser['UserID'], 3).(($tampil_nama)?"/".strtoupper($this->webuser['Username']):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"");
} }
public function createPDF($nama_file = "Nama_File.pdf", $nfontz = 8, $orien = "P") { public function createPDF(string $nama_file = "Nama_File.pdf", int $fontz = 8, string $orien = "P"): void {
$html = ' $html = '
<style> <style>
#pdf_area { #pdf_area {
@@ -144,37 +163,37 @@ final class PDF {
$html .= '<tbody>'; $html .= '<tbody>';
// content out // content out
for ($i=0; $i<count($this->__avalue); $i++) { for ($i=0; $i<count($this->avalue); $i++) {
$html .= $this->__avalue[$i]; $html .= $this->avalue[$i];
} }
$html .= '</tbody></table></div></div></body>'; $html .= '</tbody></table></div></div></body>';
$mpdf = new mPDF('c', 'A4'.($orien=="L"?"-L":""), $nfontz, '', 5, 5, 5, 10, 5, 5); $mpdf = new mPDF('c', 'A4'.($orien==="L"?"-L":""), $fontz, '', 5, 5, 5, 10, 5, 5);
$mpdf->useSubstitutions = false; $mpdf->useSubstitutions = false;
$mpdf->WriteHTML($html); $mpdf->WriteHTML($html);
$mpdf->Output($nama_file.".pdf", "I"); $mpdf->Output($nama_file.".pdf", "I");
} }
public function set_align($ttext, $nlen, $tdir="L", $bline=false, $bhead=false, $ncol=1) { public function set_align(string $text, string|int $width, string $dir="L", bool $line=false, bool $head=false, int $col=1): string {
$tstyle = $this->create_style($tdir); $tstyle = $this->create_style($dir);
if (strpos(strtolower($ttext), "<tr>")===false) { //jika tidak ada <tr> up 2011-04-14 if (strpos(strtolower($text), "<tr>")===false) {
if ($bline===true) { if ($line===true) {
return '<tr><td colspan="'.count($this->__atabel).'" class="'.$tstyle.'">'.$ttext."</td></tr>"; return '<tr><td colspan="'.count($this->atabel).'" class="'.$tstyle.'">'.$text."</td></tr>";
} else { } else {
return (($bhead===true)?"<th":"<td").(($ncol>1)?' colspan="'.$ncol.'"':"").' class="'.$tstyle.'" '.(($bhead===true)?'width="'.$nlen.'"':"").">".$ttext.(($bhead===true)?"</th>":"</td>"); return (($head===true)?"<th":"<td").(($col>1)?' colspan="'.(string)$col.'"':"").' class="'.$tstyle.'" '.(($head===true)?'width="'.(string)$width.'"':"").">".$text.(($head===true)?"</th>":"</td>");
} }
} else { } else {
return (($bline===true) ? $ttext : (($bhead===true)?"<th":"<td").(($ncol>1)?" colspan=\"".$ncol."\"":"")." class=\"".$tstyle." \" ".(($bhead===true)?"width=\"".$nlen."\"":"").">".$ttext.(($bhead===true)?"</th>":"</td>")); return (($line===true) ? $text : (($head===true)?"<th":"<td").(($col>1)?" colspan=\"".(string)$col."\"":"")." class=\"".$tstyle." \" ".(($head===true)?"width=\"".(string)$width."\"":"").">".$text.(($head===true)?"</th>":"</td>"));
} }
} }
private function create_style($tstyle="L") { private function create_style(string $style="L"): string {
$apad = ["L"=>"left", "R"=>"right", "C"=>"center", "NB"=>"nb", "NL"=>"noleft", "NR"=>"noright", "B"=>"bold", "I"=>"italic"]; $apad = ["L"=>"left", "R"=>"right", "C"=>"center", "NB"=>"nb", "NL"=>"noleft", "NR"=>"noright", "B"=>"bold", "I"=>"italic"];
$_astyle = explode(",", $tstyle); $_astyle = explode(",", $style);
$thasil = ""; $ahasil = [];
foreach ($_astyle as $_key => $_val) { foreach ($_astyle as $_key => $_val) {
$thasil .= " ".$apad[$_val]; $ahasil[] = $apad[$_val];
} }
return substr($thasil,1); return implode(' ', $ahasil);
} }
} }
-163
View File
@@ -1,163 +0,0 @@
<?php
/*******************************************************************
Module : /Data/SimpleXLS.php
Desc. : Class pembuatan file Simple XLS
Created By : Siau Pheng ([email protected]).
Created Date : November 18th, 2012.
Last Modified : September 30th, 2025.
(c) 2012 - 2025, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
declare(strict_types=1);
namespace siaupheng\webapp\Data;
final class SimpleXLS {
private $__wbook = null;
private $__atabel = [];
private $__astyle = [];
private $__aheader = [];
private $__amerge = [];
private $__avalue = [];
private $__tsheet = null;
public function __construct($oprop=[]) {
$this->__wbook = new \Shuchkin\SimpleXLSXGen();
$this->__wbook
->setAuthor($_SESSION['__WEB_APP']['APP_NAME']." v".$_SESSION['__WEB_APP']['APP_VER'])
->setLastModifiedBy($_SESSION['__WEB_APP']['APP_NAME']." v".$_SESSION['__WEB_APP']['APP_VER'])
->setTitle($oprop['title'] ?? "")
->setSubject($oprop['subject'] ?? $_SESSION['__WEB_APP']['REG_NAME'])
->setDescription($oprop['desc'] ?? "")
->setCompany($_SESSION['__WEB_APP']['REG_NAME'])
->setDefaultFont("Calibri")
->setDefaultFontSize(11);
}
private function coord2cell($x, $y) {
$c = '';
for ($i = $x; $i >= 0; $i = ((int)($i / 26)) - 1) {
$c = chr(ord('A') + $i % 26) . $c;
}
return $c . ($y + 1);
}
private function create_style($tstyle="L") {
$_astyle = explode(",", $tstyle);
$_s_style = ""; $_e_style = "";
foreach ($_astyle as $_key => $_val) {
if ($_val == "L") {$_s_style .= '<left>'; $_e_style .= '</left>';}
if ($_val == "R") {$_s_style .= '<right>'; $_e_style .= '</right>';}
if ($_val == "C") {$_s_style .= '<center>'; $_e_style .= '</center>';}
if ($_val == "W") {$_s_style .= '<wraptext>'; $_e_style .= '</wraptext>';}
if ($_val == "B") {$_s_style .= '<b>'; $_e_style .= '</b>';}
if ($_val == "I") {$_s_style .= '<i>'; $_e_style .= '</i>';}
if ($_val == "S") {$_s_style .= "\0"; $_e_style .= '';}
if ($_val == "N") {$_s_style .= '<style nf="#,##0.00">'; $_e_style .= '</style>';}
if ($_val == "N0") {$_s_style .= '<style nf="#,##0">'; $_e_style .= '</style>';}
}
return ['s'=>$_s_style, 'e'=>$_e_style];
}
public function clear_field() {
$this->__atabel = [];
}
public function add_header($tjudul="") {
if ($tjudul=="") return;
$this->__aheader[] = $tjudul;
}
public function add_empty_row($nrow=1) {
$_jkolom = count($this->__atabel);
for ($i=0; $i<$nrow; $i++) {
$this->__avalue[] = array_fill(0,$_jkolom,"");
}
}
public function add_field($tnama="", $nlebar=0, $talign="L", $ffunc="") {
$this->__atabel[] = [$tnama, $nlebar, $ffunc];
$this->__astyle[] = $this->create_style($talign);
}
public function add_sheet($tnama_sheet="BARU", $bw_header=true) {
$this->__tsheet = $tnama_sheet;
$_jkolom = count($this->__atabel);
foreach ($this->__aheader as $_key => $_val) {
$a_out = ['<style font-size="12"><center>'.$_val."</center></style>"];
$this->__amerge[] = $this->coord2cell(0, count($this->__avalue)) . ":" . $this->coord2cell($_jkolom-1, count($this->__avalue));
$this->__avalue[] = $a_out;
}
if ($bw_header==true) $this->show_headers();
}
public function add_value() {
$tmp_arr = func_get_args();
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0];
$a_out = [];
for ($i=0; $i<count($this->__atabel); $i++) {
$tmp_val = $tmp_arr[$i];
if (is_array($this->__atabel[$i][2]) && trim($tmp_val)<>"") {
$tmp_val = $this->__atabel[$i][2][$tmp_val];
} else if (function_exists($this->__atabel[$i][2]) && trim($tmp_val)<>"") {
$tmp_val = call_user_func($this->__atabel[$i][2], $tmp_val);
}
$a_out[] = $this->__astyle[$i]['s'] . $tmp_val . $this->__astyle[$i]['e'];
}
$this->__avalue[] = $a_out;
}
public function add_span() {
$tmp_arr = func_get_args();
$a_out = [];
for ($i=0; $i<count($tmp_arr); $i++) {
$tmp_val = $tmp_arr[$i]; // CONTENT, COLSPAN=1, L, FUNC
if (!isset($tmp_val[1])) $tmp_val[1] = 1;
if (!isset($tmp_val[2])) $tmp_val[2] = "L";
if (isset($tmp_val[3])) {
if (function_exists($tmp_val[3]) && trim("".$tmp_val[0])<>"") {
$tmp_val[0] = call_user_func($tmp_val[3], $tmp_val[0]);
}
}
$tmp_style = $this->create_style($tmp_val[2]);
if ($tmp_val[1]>1) $this->__amerge[] = $this->coord2cell(count($a_out), count($this->__avalue)) . ":" . $this->coord2cell((count($a_out)+$tmp_val[1])-1, count($this->__avalue));
$a_out[] = $tmp_style['s'] . $tmp_val[0] . $tmp_style['e'];
for ($k=1; $k<$tmp_val[1]; $k++) $a_out[] = null;
}
$this->__avalue[] = $a_out;
}
public function add_stamp($ctk_ke=1, $bnama=false) {
$this->__avalue[] = ["<i>".$_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"")."</i>"];
}
public function get_stamp($ctk_ke=1, $bnama=false) {
return $_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"");
}
public function show_headers() {
$a_out = [];
for ($i=0; $i<count($this->__atabel); $i++) {
$a_out[] = '<style bgcolor="#D0D0D0"><center>'.$this->__atabel[$i][0].'</center></style>';
}
$this->__avalue[] = $a_out;
}
public function send($tnama_file="File_Excel") {
$this->__wbook->addSheet($this->__avalue, $this->__tsheet);
for ($i=0; $i<count($this->__atabel); $i++) {
$this->__wbook->setColWidth(($i+1), $this->__atabel[$i][1]);
}
for ($i=0; $i<count($this->__amerge); $i++) {
$this->__wbook->mergeCells($this->__amerge[$i]);
}
$this->__wbook->downloadAs($tnama_file);
}
}
?>
-155
View File
@@ -1,155 +0,0 @@
<?php
/*******************************************************************
Module : /Data/XLS.php
Desc. : Class pembuatan file XLS
Created By : Siau Pheng ([email protected]).
Created Date : November 18th, 2012.
Last Modified : October 26th, 2023.
(c) 2012 - 2023, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
declare(strict_types=1);
namespace siaupheng\webapp\Data;
use PhpOffice\PhpSpreadsheet\IOFactory;
final class XLS {
private $__tnamafile = null;
private $__bline = true;
private $__wbook = null;
private $__wsheet = null;
private $__baris = 0;
private $__kolom = 0;
private $__atabel = [];
private $__astyle =[];
private $__aheader = [];
private $__fhead = null;
private $__isheet = 0;
public function __construct($tnama_file="", $oprop=[]) {//oke
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() );
$this->__tnamafile = $tnama_file;
$this->__wbook = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$this->__wbook->getProperties()
->setCreator($_SESSION['__WEB_APP']['APP_NAME']." v".$_SESSION['__WEB_APP']['APP_VER'])
->setLastModifiedBy($_SESSION['__WEB_APP']['APP_NAME']." v".$_SESSION['__WEB_APP']['APP_VER'])
->setTitle($oprop['title'] ?? "")
->setSubject($oprop['subject'] ?? "")
->setDescription($oprop['desc'] ?? "")
->setCompany($_SESSION['__WEB_APP']['REG_NAME']);
$this->__wsheet = $this->__wbook->getActiveSheet();
$this->__fhead = [
'font' => ['bold' => true],
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER, 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER, 'wrapText' => true],
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['rgb'=>"D0D0D0"]],
'borders' => ['allBorders' => ['borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN, 'color' => ['rgb'=>"C0C0C0"]]]
];
}
public function create_style($tstyle="L") {//oke
$_astyle = explode(",", $tstyle);
$_tmp_style = [];
$_bline = $this->__bline;
foreach ($_astyle as $_key => $_val) {
if ($_val == "L") $_tmp_style['alignment'] = ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT];
if ($_val == "R") $_tmp_style['alignment'] = ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT];
if ($_val == "C") $_tmp_style['alignment'] = ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER];
if ($_val == "W") $_tmp_style['alignment'] = ['wrapText' => true];
if ($_val == "B") $_tmp_style['font'] = ['bold' => true];
if ($_val == "S") $_tmp_style['numberFormat'] = ['formatCode' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT];
if ($_val == "N") $_tmp_style['numberFormat'] = ['formatCode' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED2];
if ($_val == "N0") $_tmp_style['numberFormat'] = ['formatCode' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED2];
if ($_val == "NL") $_bline = false;
if ($_val == "H") {
$_bline = false;
$_tmp_style = $this->__fhead;
}
}
return $_tmp_style;
}
public function clear_field() {//up : 2014-06-19
$this->__atabel = [];
$this->__astyle = [];
}
public function set_pos($nbaris=0, $nkolom=0) {
$this->__baris = $nbaris;
$this->__kolom = $nkolom;
}
public function set_garis_bawah($bgaris=true) {}//todo: deleted
public function add_header($tjudul="") {//oke
if ($tjudul=="") return;
$this->__aheader[] = $tjudul;
}
public function add_empty_row($nrow=1) {//oke
$this->__baris += $nrow;
}
public function add_field($tnama="", $nlebar=0, $talign="L", $ffunc="") {//oke
$this->__atabel[] = [$tnama, $nlebar, $ffunc];
$this->__astyle[] = $this->create_style($talign);
}
public function add_sheet($tnama_sheet="BARU", $bw_header=true) {//not yet !!!
if ($this->__isheet > 0) {
$this->__wbook->createSheet();
$this->__wsheet = $this->__wbook->setActiveSheetIndex(++$this->__isheet);
}
$this->__wsheet->setTitle($tnama_sheet);
for ($i=0; $i<count($this->__atabel); $i++) {
//sampai disini dulu !!
$this->__wsheet->getColumnDimension($this->__kolom+$i)->setWidth($this->__atabel[$i][1]);
}
$_fjudul = $this->__wbook->addFormat();
$_fjudul->setBold();
$_fjudul->setAlign("center");
$_jkolom = count($this->__atabel);
if ($_jkolom>0) $_jkolom--;
foreach ($this->__aheader as $_key => $_val) {
$this->__wsheet->write($this->__baris,$this->__kolom,$_val,$_fjudul);
$this->__wsheet->setMerge($this->__baris,$this->__kolom,$this->__baris,$this->__kolom+$_jkolom);
$this->__baris++;
}
$this->__baris++;
if ($bw_header==true) $this->show_headers();
}
public function add_value() {
$tmp_arr = func_get_args();
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0];
$tmp_out = "";
for ($i=0; $i<count($this->__atabel); $i++) {
$tmp_val = $tmp_arr[$i];
if (is_array($this->__atabel[$i][1]) && trim($tmp_val)<>"") {
$tmp_val = $this->__atabel[$i][1][$tmp_val];
} else if (function_exists($this->__atabel[$i][1]) && trim($tmp_val)<>"") {
$tmp_val = call_user_func($this->__atabel[$i][1], $tmp_val);
}
$tmp_out .= DELI_CSV . $tmp_val;
}
$tmp_out = substr($tmp_out, 1);
$this->__avalue[] = $tmp_out;
}
public function send($tnama_file="File_Excel") {//oke
$this->__wsheet->setActiveSheetIndex(0);
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="'.$tnama_file.'"');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($this->__wsheet, 'Xlsx');
$writer->save('php://output');
}
}
?>
+155
View File
@@ -0,0 +1,155 @@
<?php
/*******************************************************************
PHP Class : /Data/Xlsx.php
Created By : Siau Pheng ([email protected]).
Created Date : November 18th, 2012.
Last Modified : October 24th, 2026.
(c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
declare(strict_types=1);
namespace Webapp\Data;
use \flight\Engine;
use \avadim\FastExcelWriter\Excel;
use \avadim\FastExcelWriter\Options;
use \avadim\FastExcelWriter\Sheet;
use \avadim\FastExcelWriter\Style\Style;
/**
* Class Xlsx
*
* Membuat file Excel XLSX
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Data
*/
final class Xlsx {
private Options $options;
private Excel $excel;
private Sheet $sheet;
private array $webapp;
private array $webuser;
private array $meta = [];
private array $titles = [];
private array $subtitles = [];
private array $atabel = [];
private int $baris = 1;
/**
* @param Engine $app
*/
public function __construct(Engine $app) {
$this->webapp = (array) $app->session()->get('WEB_APP', ['REG_NAME'=>'', 'APP_NAME'=>'', 'APP_VER'=>'']);
$this->webuser = (array) $app->session()->get('WEB_USER', ['UserID'=>0, 'Username'=>'']);
$this->meta = [
'company' => $this->webapp['REG_NAME'],
'subject' => $this->webapp['REG_NAME'],
'author' => $this->webapp['APP_NAME'].' v'.$this->webapp['APP_VER'],
];
$this->options = Options::create()
->DefaultFont([ Style::FONT_NAME => "Calibri", Style::FONT_SIZE => 11 ])
->defaultDateFormat('DD-MM-YYYY')
->defaultTimeFormat('HH:MM')
->defaultDateTimeFormat('DD-MM-YYYY HH:MM');
}
public function set_meta(array $meta): void {
if (!empty($meta)) $this->meta = array_merge($this->meta, $meta);
}
public function add_title(string|array $judul): void {
if (empty($judul)) return;
if (is_array($judul)) {
$this->titles = $judul;
} else array_push($this->titles, $judul);
}
public function add_sub_title(string|array $judul): void {
if (empty($judul)) return;
if (is_array($judul)) {
$this->subtitles = $judul;
} else array_push($this->subtitles, $judul);
}
public function add_field(string $nama="", int $lebar=0, string $style="@text", ?callable $func=null): void {
$this->atabel[] = ['n' => $nama, 'l' => $lebar, 's' => $style, 'f' => $func];
}
public function add_sheet(string $nama_sheet="BARU", bool $show_header=false): void {
if (empty($this->excel)) {
$this->excel = Excel::create([$nama_sheet], $this->options)
->setCompany($this->meta['company'] ?? "")
->setAuthor($this->meta['author'] ?? "")
->setTitle($this->meta['title'] ?? "")
->setDescription($this->meta['desc'] ?? "")
->setSubject($this->meta['subject'] ?? "");
$this->sheet = $this->excel->sheet(); //getSheet();
} else {
$this->sheet = $this->excel->makeSheet($nama_sheet);
}
if ($show_header === true) {
$this->show_titles();
$this->show_headers();
}
}
public function add_row(array $row): void {
if (is_array($row) && !empty($row)) $this->sheet->writeRow($row);
}
public function add_row_bold(array $row): void {
if (is_array($row) && !empty($row)) $this->sheet->writeRow($row)->applyFontStyleBold();
}
public function show_titles(): void {
$this->baris = $this->sheet->getCurrentRow();
$jum_kolom = count($this->atabel);
foreach ($this->titles as $title) {
$this->sheet->writeTo('A'.(string) $this->baris.':'.$this->excel->colLetter($jum_kolom).(string) $this->baris, $title)
->applyFontStyleBold()
->applyTextCenter();
$this->baris++;
}
if (count($this->subtitles) > 0) {
$this->baris++;
foreach ($this->subtitles as $title) {
$this->sheet->writeTo('A'.(string) $this->baris++, $title);
}
} else
$this->sheet->writeTo('A'.(string) $this->baris, '');
}
public function show_headers(bool $is_hide=false): void {
if (!$is_hide) $this->sheet->writeHeader(array_column($this->atabel, 'n'))
->applyFontStyleBold()
->applyTextCenter()
->applyBorder(Style::BORDER_STYLE_THIN);
$this->sheet->setColFormats(array_column($this->atabel, 's'))
->setColWidths(array_column($this->atabel, 'l'));
}
public function add_stamp(int $ctk_ke=1, bool $tampil_nama=false): void {
$this->sheet->nextRow();
$this->sheet->writeCell($this->get_stamp($ctk_ke, $tampil_nama), ['font-size' => 8, 'font-style' => 'italic']);
}
public function get_stamp(int $ctk_ke=1, bool $tampil_nama=false): string {
return $this->webapp['APP_NAME']."v".$this->webapp['APP_VER'].date("\@ymd.His")."-".isi_nol($this->webuser['UserID'], 3).(($tampil_nama)?"/".strtoupper($this->webuser['Username']):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"");
}
public function download(string $nama_file="File_Excel.xlsx"): void {
$this->excel->download($nama_file);
}
}
-63
View File
@@ -1,63 +0,0 @@
<?php
/*******************************************************************
Module : /Function/Auth.php
Desc. : Fungsi Custom Auth
Created By : Siau Pheng ([email protected]).
Created Date : January 27th, 2008.
Last Modified : January 21st, 2024.
(c) 2008 - 2024, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
//OK
declare(strict_types=1);
if (!function_exists('user_level')) {
function user_level($level = 9) {
if (Flight::session()->get('__WEB_USER', ['Level'=>9]) <= $level) return true;
return false;
}
}
if (!function_exists('user_level_not')) {
function user_level_not($level = 9) {
if (Flight::session()->get('__WEB_USER', ['Level'=>9]) !== $level) return true;
return false;
}
}
if (!function_exists('user_logged')) {
function user_logged() {
return !empty(Flight::session()->get('__WEB_USER'));
}
}
if (!function_exists('user_id')) {
function user_id($idigit = 0) {
if ($idigit > 0) return isi_nol((int)Flight::session()->get('__WEB_USER', ['UserID'=>0])['UserID'], $idigit);
return (int)Flight::session()->get('__WEB_USER', ['UserID'=>0])['UserID'];
}
}
if (!function_exists('user_user')) {
function user_user() {
return (string)Flight::session()->get('__WEB_USER', ['Username'=>''])['Username'];
}
}
if (!function_exists('user_unix')) {
function user_unix() {
return (string)Flight::session()->get('__WEB_USER', ['UNIX'=>'0'])['UNIX'];
}
}
if (!function_exists('user_nama')) {
function user_nama() {
return (string)Flight::session()->get('__WEB_USER', ['Nama'=>''])['Nama'];
}
}
?>
+122 -82
View File
@@ -6,9 +6,9 @@
Desc. : Fungsi Custom Lain Lain Desc. : Fungsi Custom Lain Lain
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : January 27th, 2008. Created Date : January 27th, 2008.
Last Modified : December 26th, 2025. Last Modified : October 27th, 2026.
(c) 2008 - 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
@@ -16,97 +16,99 @@ declare(strict_types=1);
/*************************** FORMAT ANGKA / CURRENCY *****************************/ /*************************** FORMAT ANGKA / CURRENCY *****************************/
if (!function_exists('rupiah')) {//oke if (!function_exists('rupiah')) {
function rupiah(string|float $angka = 0, int $desi = 0) { function rupiah(mixed $angka = 0, int $desi = 0): string {
return number_format((float) $angka, $desi, ",", "."); return number_format((float) $angka, $desi, ",", ".");
} }
} }
if (!function_exists('rupiah2')) { if (!function_exists('rupiah2')) {
function rupiah2($angka = 0) { function rupiah2(mixed $angka = 0): string {
return rupiah($angka, 2); return rupiah($angka, 2);
} }
} }
/* todo: deprecated
if (!function_exists('rupiah30')) { if (!function_exists('rupiah30')) {
function rupiah30($angka = 0) { function rupiah30($angka = 0): string {
$nilai = explode(".", $angka); $nilai = explode(".", $angka);
$hasil = (intval($nilai[1])==0) ? rupiah($angka, 0) : rupiah($angka, 3); $hasil = (intval($nilai[1])==0) ? rupiah($angka, 0) : rupiah($angka, 3);
return $hasil; return $hasil;
} }
} }*/
if (!function_exists('kurung_rup')) { if (!function_exists('kurung_rup')) {
function kurung_rup($angka = 0, $desi = 0) { function kurung_rup(mixed $angka = 0, int $desi = 0): string {
$thasil = rupiah(abs($angka), $desi); $thasil = rupiah(abs((float) $angka), $desi);
if ($angka<0) return "(".$thasil.")"; if ((float) $angka < 0) return "(".$thasil.")";
return $thasil." "; return $thasil." ";
} }
} }
if (!function_exists('kurung')) { if (!function_exists('kurung')) {
function kurung($angka = 0, $desi = 0) { function kurung(mixed $angka = 0, int $desi = 0): string {
$thasil = uang(abs($angka), $desi); $thasil = uang(abs((float) $angka), $desi);
if ($angka<0) return "(".$thasil.")"; if ((float) $angka < 0) return "(".$thasil.")";
return $thasil." "; return $thasil." ";
} }
} }
if (!function_exists('kurung2')) { if (!function_exists('kurung2')) {
function kurung2($angka = 0) { function kurung2(mixed $angka = 0): string {
return kurung($angka, 2); return kurung($angka, 2);
} }
} }
if (!function_exists('uang')) {//oke if (!function_exists('uang')) {
function uang(string|float $angka = '0', int $desi = 0) { function uang(mixed $angka = 0, int $desi = 0): string {
$angka = str_replace(",", "", (string) $angka); $angka = str_replace(",", "", (string) $angka);
return number_format((float) $angka, $desi); return number_format((float) $angka, $desi);
} }
} }
if (!function_exists('uang2')) { if (!function_exists('uang2')) {
function uang2($angka = 0) { function uang2(mixed $angka = 0): string {
return uang($angka, 2); return uang($angka, 2);
} }
} }
/* todo: deprecated
if (!function_exists('koma30')) { if (!function_exists('koma30')) {
function koma30($angka = 0) { function koma30($angka = 0) {
$nilai = explode(".", $angka); $nilai = explode(".", $angka);
$hasil = (intval($nilai[1])==0) ? uang($angka, 0) : uang($angka, 3); $hasil = (intval($nilai[1])==0) ? uang($angka, 0) : uang($angka, 3);
return $hasil; return $hasil;
} }
} }*/
if (!function_exists('items')) { if (!function_exists('items')) {
function items($item = 0) { function items(mixed $item = 0): string {
return rupiah($item)." Items"; return rupiah($item)." Items";
} }
} }
if (!function_exists('records')) { if (!function_exists('records')) {
function records($item = 0) { function records(mixed $item = 0): string {
return rupiah($item)." Records"; return rupiah($item)." Records";
} }
} }
if (!function_exists('baris')) { if (!function_exists('baris')) {
function baris($item = 0) { function baris(mixed $item = 0): string {
return rupiah($item)." Baris"; return rupiah($item)." Baris";
} }
} }
if (!function_exists('npwp')) { if (!function_exists('npwp')) {
function npwp($nomor = "000000000000000") { function npwp(string $nomor = "000000000000000"): string {
return substr($nomor,0,2).".".substr($nomor,2,3).".".substr($nomor,5,3).".".substr($nomor,8,1)."-".substr($nomor,9,3).".".substr($nomor,12,3); return substr($nomor,0,2).".".substr($nomor,2,3).".".substr($nomor,5,3).".".substr($nomor,8,1)."-".substr($nomor,9,3).".".substr($nomor,12,3);
} }
} }
if (!function_exists('romawi')) { if (!function_exists('romawi')) {
function romawi($n) { function romawi(int $n): string {
$hasil = ""; $hasil = "";
$iromawi = array("","I","II","III","IV","V","VI","VII","VIII","IX","X",20=>"XX",30=>"XXX",40=>"XL",50=>"L",60=>"LX",70=>"LXX",80=>"LXXX",90=>"XC",100=>"C",200=>"CC",300=>"CCC",400=>"CD",500=>"D",600=>"DC",700=>"DCC",800=>"DCCC",900=>"CM",1000=>"M",2000=>"MM",3000=>"MMM"); $iromawi = ["","I","II","III","IV","V","VI","VII","VIII","IX","X",20=>"XX",30=>"XXX",40=>"XL",50=>"L",60=>"LX",70=>"LXX",80=>"LXXX",90=>"XC",100=>"C",200=>"CC",300=>"CCC",400=>"CD",500=>"D",600=>"DC",700=>"DCC",800=>"DCCC",900=>"CM",1000=>"M",2000=>"MM",3000=>"MMM"];
if (array_key_exists($n, $iromawi)) { if (array_key_exists($n, $iromawi)) {
$hasil = $iromawi[$n]; $hasil = $iromawi[$n];
} else if ($n >= 11 && $n <= 99) { } else if ($n >= 11 && $n <= 99) {
@@ -124,7 +126,7 @@ if (!function_exists('romawi')) {
} }
if (!function_exists('abjad')) { if (!function_exists('abjad')) {
function abjad($n) { function abjad(int $n): string {
$_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; $_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return substr($_str, ($n-1), 1); return substr($_str, ($n-1), 1);
} }
@@ -133,8 +135,8 @@ if (!function_exists('abjad')) {
/*************************** FORM CONTROL *****************************/ /*************************** FORM CONTROL *****************************/
if (!function_exists('checkbox')) { if (!function_exists('checkbox')) {
function checkbox($nilai = 0) { function checkbox(mixed $nilai): string {
$thasil = ((is_string($nilai) && ($nilai=="N" || $nilai=="0")) or (is_numeric($nilai) && $nilai==0) or (is_bool($nilai) && $nilai==false)) ? "circle text-secondary" : "check-circle text-info"; $thasil = (empty($nilai) or (is_string($nilai) && ($nilai=="N" || $nilai=="0")) or (is_numeric($nilai) && $nilai==0) or (is_bool($nilai) && $nilai==false)) ? "circle text-secondary" : "check-circle text-info";
return '<i class="fs-6 bi-'.$thasil.'"></i>'; return '<i class="fs-6 bi-'.$thasil.'"></i>';
} }
} }
@@ -142,32 +144,32 @@ if (!function_exists('checkbox')) {
/*************************** FORMAT TANGGAL / KALENDER *****************************/ /*************************** FORMAT TANGGAL / KALENDER *****************************/
if (!function_exists('sqltgl')) { if (!function_exists('sqltgl')) {
function sqltgl($waktu = "0") { function sqltgl(string $waktu): string {
return tgl_sql2str($waktu); return tgl_sql2str($waktu);
} }
} }
if (!function_exists('sqltgls')) { if (!function_exists('sqltgls')) {
function sqltgls($waktu = "0") { function sqltgls(string $waktu): string {
return tgl_sql2str($waktu, false, true); return tgl_sql2str($waktu, false, true);
} }
} }
if (!function_exists('sqljam')) { if (!function_exists('sqljam')) {
function sqljam($waktu = "0") { function sqljam(string $waktu): string {
return tgl_sql2str($waktu, true); return tgl_sql2str($waktu, true);
} }
} }
if (!function_exists('sqljams')) { if (!function_exists('sqljams')) {
function sqljams($waktu = "0") { function sqljams(string $waktu): string {
return tgl_sql2str($waktu, true, true); return tgl_sql2str($waktu, true, true);
} }
} }
if (!function_exists('tgl_sql2str')) { if (!function_exists('tgl_sql2str')) {
function tgl_sql2str($waktu = "0", $jam = false, $short = false) { function tgl_sql2str(string $waktu, bool $jam = false, bool $short = false): string {
if ($waktu=="0" || $waktu=="") return; if (empty($waktu)) return '';
$atgl = explode(" ", $waktu); $atgl = explode(" ", $waktu);
$sjam = ($jam) ? ((empty($atgl[1])) ? date("H:i:s") : $atgl[1]) : ""; $sjam = ($jam) ? ((empty($atgl[1])) ? date("H:i:s") : $atgl[1]) : "";
$atgl = explode("-", $atgl[0]); $atgl = explode("-", $atgl[0]);
@@ -178,66 +180,66 @@ if (!function_exists('tgl_sql2str')) {
} }
if (!function_exists('tgl_ymd')) { if (!function_exists('tgl_ymd')) {
function tgl_ymd($jam = false) { function tgl_ymd(bool $jam = false): string {
return (($jam) ? date("Y-m-d H:i:s") : date("Y-m-d")); return (($jam) ? date("Y-m-d H:i:s") : date("Y-m-d"));
} }
} }
if (!function_exists('tgl_dmy')) { if (!function_exists('tgl_dmy')) {
function tgl_dmy($jam = false) { function tgl_dmy(bool $jam = false): string {
return (($jam) ? date("d-m-Y H:i:s") : date("d-m-Y")); return (($jam) ? date("d-m-Y H:i:s") : date("d-m-Y"));
} }
} }
if (!function_exists('tgl_operasi')) { if (!function_exists('tgl_operasi')) {
function tgl_operasi($dtgl = "", $nhari = 0, $nbln = 0, $nthn = 0) { function tgl_operasi(string $tgl, int $hari = 0, int $bln = 0, int $thn = 0): string {
$atgl = explode("-", $dtgl); $atgl = explode("-", $tgl);
return date("d-m-Y", mktime(0,0,0, $atgl[1]+$nbln, $atgl[0]+$nhari, $atgl[2]+$nthn)); return date("d-m-Y", mktime(0,0,0, (int) $atgl[1]+$bln, (int) $atgl[0]+$hari, (int) $atgl[2]+$thn));
} }
} }
if (!function_exists('tgl_range_hari')) { if (!function_exists('tgl_range_hari')) {
function tgl_range_hari($dftgl="", $dttgl="", $bhitung_awal=false) { function tgl_range_hari(string $tgl_from, string $tgl_to, bool $hitung_awal = false): float {
$_start = strtotime(tgl_sql2str($dftgl)); if (empty($tgl_to) or empty($tgl_from)) return 0;
$_end = strtotime(tgl_sql2str($dttgl)); $_start = strtotime(tgl_sql2str($tgl_from));
return ceil(abs($_end - $_start) / 86400) + (($bhitung_awal==true)?1:0); $_end = strtotime(tgl_sql2str($tgl_to));
return ceil(abs($_end - $_start) / 86400) + (($hitung_awal)?1:0);
} }
} }
if (!function_exists('tgl_akhbln')) { if (!function_exists('tgl_akhbln')) {
function tgl_akhbln($tgl) { function tgl_akhbln(string $tgl): string {
if (empty($tgl)) return '';
$atgl = explode("-", $tgl); $atgl = explode("-", $tgl);
$ablna = array(31,28,31,30,31,30,31,31,30,31,30,31); $ablna = ['31','28','31','30','31','30','31','31','30','31','30','31'];
// jika tahun masehi dan bulan 2 // jika tahun masehi dan bulan 2
if ( (round($atgl[2]/4)==($atgl[2]/4)) && intval($atgl[1])==2 ) return "29-02-".$atgl[2]; if ((round((int) $atgl[2]/4) === ((int) $atgl[2]/4)) && (int) $atgl[1] === 2) return "29-02-".$atgl[2];
return $ablna[(intval($atgl[1])-1)]."-".isi_nol($atgl[1],2)."-".$atgl[2]; return $ablna[(int) $atgl[1]-1]."-".isi_nol($atgl[1],2)."-".$atgl[2];
} }
} }
if (!function_exists('tgl_panjang')) { if (!function_exists('tgl_panjang')) {
function tgl_panjang($dtgl = "") { function tgl_panjang(string $tgl): string {
if (trim($dtgl)=="") $dtgl = tgl_dmy(); if (empty($tgl)) $tgl = tgl_dmy();
$atgl = explode("-", $dtgl); $atgl = explode("-", $tgl);
$abulan = bulan_array(); $abulan = bulan_array();
return $atgl[0]." ".$abulan[intval($atgl[1])]." ".$atgl[2]; return $atgl[0]." ".$abulan[$atgl[1]]." ".$atgl[2];
} }
} }
if (!function_exists('tgl_hari')) { if (!function_exists('tgl_hari')) {
function tgl_hari($dtgl = "") { function tgl_hari(string $tgl): string {
$nhari = date("w"); $nhari = date("w");
if (trim($dtgl)!="") { if (!empty($tgl)) {
//$atgl = getdate($dtgl); $nhari = date("w", strtotime(tgl_sql2str($tgl)));
//$nhari = $atgl[wday];
$nhari = date("w", strtotime(tgl_sql2str($dtgl)));
} }
$ahari = array("0"=>"Minggu","1"=>"Senin","2"=>"Selasa","3"=>"Rabu","4"=>"Kamis","5"=>"Jum'at","6"=>"Sabtu"); $ahari = ["0"=>"Minggu","1"=>"Senin","2"=>"Selasa","3"=>"Rabu","4"=>"Kamis","5"=>"Jum'at","6"=>"Sabtu"];
return $ahari[$nhari]; return $ahari[$nhari];
} }
} }
if (!function_exists('tgl_triwulan')) { if (!function_exists('tgl_triwulan')) {
function tgl_triwulan($dtgl = "") { function tgl_triwulan(string $tgl): int {
if ($dtgl == "") { if (empty($tgl)) {
$dtgl = date("m"); $dtgl = date("m");
} else { } else {
$atgl = explode("-", $tgl); $atgl = explode("-", $tgl);
@@ -253,13 +255,13 @@ if (!function_exists('tgl_triwulan')) {
} }
if (!function_exists('bulan_array')) { if (!function_exists('bulan_array')) {
function bulan_array() { function bulan_array(): array {
return array("1"=>"Januari","2"=>"Februari","3"=>"Maret","4"=>"April","5"=>"Mei","6"=>"Juni","7"=>"Juli","8"=>"Agustus","9"=>"September","10"=>"Oktober","11"=>"November","12"=>"Desember"); return ["1"=>"Januari","2"=>"Februari","3"=>"Maret","4"=>"April","5"=>"Mei","6"=>"Juni","7"=>"Juli","8"=>"Agustus","9"=>"September","10"=>"Oktober","11"=>"November","12"=>"Desember"];
} }
} }
if (!function_exists('triwulan_array')) { if (!function_exists('triwulan_array')) {
function triwulan_array() { function triwulan_array(): array {
return ["1"=>"Triwulan 1","2"=>"Triwulan 2","3"=>"Triwulan 3","4"=>"Triwulan 4"]; return ["1"=>"Triwulan 1","2"=>"Triwulan 2","3"=>"Triwulan 3","4"=>"Triwulan 4"];
} }
} }
@@ -267,32 +269,33 @@ if (!function_exists('triwulan_array')) {
/*************************** FORMAT TAMPILAN & ERROR *****************************/ /*************************** FORMAT TAMPILAN & ERROR *****************************/
if (!function_exists('subjudul')) { if (!function_exists('subjudul')) {
function subjudul($tjudul) { function subjudul(string $judul): string {
return '<table border="0" align="center" valign="middle" width="100%" height="26px"><tr><td width="50%"><hr /></td><td>&nbsp;&nbsp;<span style="text-align:center;font-weight:bold;white-space:nowrap;">'.$tjudul.'</span>&nbsp;&nbsp;</td><td width="50%"><hr /></td></tr></table>'; return '<table border="0" align="center" valign="middle" width="100%" height="26px"><tr><td width="50%"><hr /></td><td>&nbsp;&nbsp;<span style="text-align:center;font-weight:bold;white-space:nowrap;">'.$judul.'</span>&nbsp;&nbsp;</td><td width="50%"><hr /></td></tr></table>';
} }
} }
/* todo: deprecated
if (!function_exists('showerror')) { if (!function_exists('showerror')) {
function showerror($terror) { function showerror(string $error): void {
echo '<input type="hidden" id="_Error" class="error" value="'.$terror.'!" />'; echo '<input type="hidden" id="_Error" class="error" value="'.$error.'!" />';
}
} }
}*/
if (!function_exists('br2nl')) { if (!function_exists('br2nl')) {
function br2nl($string) { function br2nl(string $string): string {
return preg_replace('/\<br(\s*)?\/?\>/i', "\n\r", $string); return preg_replace('/\<br(\s*)?\/?\>/i', "\n\r", $string);
} }
} }
if (!function_exists('isi_nol')) { if (!function_exists('isi_nol')) {
function isi_nol($nilai = "0", $panjang = 1) { function isi_nol(string|int $nilai = 0, int $panjang = 1): string {
$nilai = strval($nilai); $nilai = (string)$nilai;
return ($panjang>strlen($nilai) ? str_repeat("0", $panjang-strlen($nilai)):'').$nilai; return ($panjang>strlen($nilai) ? str_repeat("0", $panjang-strlen($nilai)):'').$nilai;
} }
} }
if (!function_exists('isMobile')) { if (!function_exists('isMobile')) {
function isMobile() { function isMobile(): bool {
$agen = $_SERVER['HTTP_USER_AGENT']; $agen = $_SERVER['HTTP_USER_AGENT'];
$amobile = ['Android', 'webOS', 'iPhone', 'iPad', 'iPod', 'BlackBerry', 'Windows Phone', 'Mobile']; $amobile = ['Android', 'webOS', 'iPhone', 'iPad', 'iPod', 'BlackBerry', 'Windows Phone', 'Mobile'];
foreach ($amobile as $keyword) { foreach ($amobile as $keyword) {
@@ -303,7 +306,7 @@ if (!function_exists('isMobile')) {
} }
if (!function_exists('isLinux')) { if (!function_exists('isLinux')) {
function isLinux() { function isLinux(): bool {
$agen = $_SERVER['HTTP_USER_AGENT']; $agen = $_SERVER['HTTP_USER_AGENT'];
if (stripos($agen, 'linux') !== false) return true; if (stripos($agen, 'linux') !== false) return true;
return false; return false;
@@ -311,7 +314,7 @@ if (!function_exists('isLinux')) {
} }
if (!function_exists('isIE')) { if (!function_exists('isIE')) {
function isIE() { function isIE(): bool {
$agen = $_SERVER['HTTP_USER_AGENT']; $agen = $_SERVER['HTTP_USER_AGENT'];
if (stripos($agen, 'msie') !== false) return true; if (stripos($agen, 'msie') !== false) return true;
return false; return false;
@@ -319,7 +322,7 @@ if (!function_exists('isIE')) {
} }
if (!function_exists('isChrome')) { if (!function_exists('isChrome')) {
function isChrome() { function isChrome(): bool {
$agen = $_SERVER['HTTP_USER_AGENT']; $agen = $_SERVER['HTTP_USER_AGENT'];
if (stripos($agen, 'chrome') !== false) return true; if (stripos($agen, 'chrome') !== false) return true;
return false; return false;
@@ -327,22 +330,23 @@ if (!function_exists('isChrome')) {
} }
if (!function_exists('isFirefox')) { if (!function_exists('isFirefox')) {
function isFirefox() { function isFirefox(): bool {
$agen = $_SERVER['HTTP_USER_AGENT']; $agen = $_SERVER['HTTP_USER_AGENT'];
if (stripos($agen, 'firefox') !== false) return true; if (stripos($agen, 'firefox') !== false) return true;
return false; return false;
} }
} }
/* todo: deprecated
if (!function_exists('potong')) { if (!function_exists('potong')) {
function potong($nilai) {//utk rkpd function potong($nilai) {//utk rkpd
return substr($nilai, 0, 60); return substr($nilai, 0, 60);
} }
} }*/
if (!function_exists('format_bytes')) { if (!function_exists('format_bytes')) {
function format_bytes($bytes) { function format_bytes(int $bytes): string {
if ($bytes < 1024) return $bytes.' B'; if ($bytes < 1024) return (string) $bytes.' B';
elseif ($bytes < 1048576) return uang2($bytes / 1024).' KB'; elseif ($bytes < 1048576) return uang2($bytes / 1024).' KB';
elseif ($bytes < 1073741824) return uang2($bytes / 1048576).' MB'; elseif ($bytes < 1073741824) return uang2($bytes / 1048576).' MB';
elseif ($bytes < 1099511627776) return uang2($bytes / 1073741824).' GB'; elseif ($bytes < 1099511627776) return uang2($bytes / 1073741824).' GB';
@@ -351,12 +355,48 @@ if (!function_exists('format_bytes')) {
} }
if (!function_exists('random_color')) { if (!function_exists('random_color')) {
function random_color($trans = "0.6") { function random_color(string $trans = "0.6"): string {
$col_r = mt_rand( 0, 255 ); $col_r = (string) mt_rand( 0, 255 );
$col_g = mt_rand( 0, 255 ); $col_g = (string) mt_rand( 0, 255 );
$col_b = mt_rand( 0, 255 ); $col_b = (string) mt_rand( 0, 255 );
return "rgba(".$col_r.",".$col_g.",".$col_b.",".$trans.")"; return "rgba(".$col_r.",".$col_g.",".$col_b.",".$trans.")";
} }
} }
?> if (!function_exists('gen_uuidv4')) {
function gen_uuidv4(): string {
$data = random_bytes(16);
// Set version to 0100 (UUID v4)
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
// Set bits 6-7 to 10
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
}
if (!function_exists('gen_uuidv7')) {
function gen_uuidv7(int $time=0): string {
// Get timestamp in milliseconds (48 bits)
if (empty($time)) $time = (int) floor(microtime(true) * 1000);
// Generate 16 bytes of random data
$randomBytes = random_bytes(16);
// Pack the 48-bit timestamp into the first 6 bytes
$timeHex = str_pad(dechex($time), 12, '0', STR_PAD_LEFT);
$bytes = hex2bin($timeHex);
// Append 10 bytes of randomness (we need 16 total bytes)
$bytes .= substr($randomBytes, 0, 10);
// Apply the version bits (Version 7 -> 0b0111) onto byte 6
$bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x70);
// Apply the variant bits (Variant 1 -> 0b10xx) onto byte 8
$bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
// Unpack to hex string and format with hyphens
$hex = bin2hex($bytes);
return sprintf('%08s-%04s-%04s-%04s-%012s',
substr($hex, 0, 8),
substr($hex, 8, 4),
substr($hex, 12, 4),
substr($hex, 16, 4),
substr($hex, 20, 12)
);
}
}
+118 -84
View File
@@ -2,136 +2,170 @@
/******************************************************************* /*******************************************************************
Module : /Printing/PrintHTML.php Module : /Printer/PrintHTML.php
Desc. : Class cetak HTML
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : June 15th, 2008. Created Date : June 15th, 2008.
Last Modified : September 30th, 2025. Last Modified : October 24th, 2026.
(c) 2008 - 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Printer; namespace Webapp\Printer;
use \flight\Engine;
/**
* Class PrintHTML
*
* Menghasilkan bentuk laporan dalam format HTML.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Printer
*/
final class PrintHTML { final class PrintHTML {
private $__avalue = []; private array $avalue = [];
private $__atabel = []; private array $atabel = [];
private $__css = "table"; private string $css = "table";
private $__tstamp = ""; private string $stamp = "";
private array $titles = [];
private array $subtitles = [];
protected array $webapp;
protected array $webuser;
public function __construct() { /**
* @param Engine $app
*/
public function __construct(Engine $app) {
$this->webapp = (array) $app->session()->get('WEB_APP', ['APP_NAME'=>'', 'APP_VER'=>'']);
$this->webuser = (array) $app->session()->get('WEB_USER', ['UserID'=>0, 'Username'=>'']);
} }
public function set_css($style = "") { public function set_css(string $style): void {
if ($style=="") return; if (!empty($style)) $this->css = $style;
$this->__css = $style;
} }
public function add_field($tnama="", $nlebar=0, $talign="L", $ffunc="", $nkol=1) {//up 2012-03-07 public function add_title(string|array $judul): void {
$this->__atabel[] = [$tnama, $nlebar, $ffunc, $talign, $nkol]; if (empty($judul)) return;
if (is_array($judul)) {
$this->titles = $judul;
} else array_push($this->titles, $judul);
} }
public function show_fields() {//todo: deleted public function add_sub_title(string|array $judul): void {
if (empty($judul)) return;
if (is_array($judul)) {
$this->subtitles = $judul;
} else array_push($this->subtitles, $judul);
} }
public function clear() { public function add_field(string $nama, int $lebar=0, string $align="L", callable|array|null $func=null, int $kol=1): void {
$this->__avalue = []; $this->atabel[] = [ 'n' => $nama, 'l' => $lebar, 'f' => $func, 'a' => $align, 'k' => $kol ];
} }
public function add_value() { public function add_row(array $row): void {
$tmp_arr = func_get_args(); if (!is_array($row) || empty($row)) return;
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0]; //up 2009-09-27 $row_out = "<tr>";
$tmp_out = "<tr>"; foreach ($this->atabel as $i => $tabel) {
for ($i=0; $i<count($this->__atabel); $i++) { $col_val = $row[$i] ?? '';
$tmp_val = $tmp_arr[$i]; if (!empty($col_val)) {
if (is_array($this->__atabel[$i][2]) && trim("".$tmp_val)<>"") { //up 2010-05-28 if (is_array($tabel['f'])) {
$tmp_val = $this->__atabel[$i][2][$tmp_val]; $col_val = $tabel['f'][$col_val];
} else if (function_exists($this->__atabel[$i][2]) && trim("".$tmp_val)<>"") { } else if (is_callable($tabel['f'])) {
$tmp_val = call_user_func($this->__atabel[$i][2], $tmp_val); $col_val = call_user_func($tabel['f'], $col_val);
} }
$tmp_val = $this->set_align($tmp_val, $this->__atabel[$i][1], $this->__atabel[$i][3]);
$tmp_out .= $tmp_val;
} }
$tmp_out .= "</tr>"; $row_out .= $this->set_align((string) $col_val, $tabel['l'], $tabel['a']);
$this->__avalue[] = $tmp_out; }
$row_out .= "</tr>";
echo $row_out;
} }
public function add_span() { public function add_span(array $span): void {
$tmp_arr = func_get_args(); if (!is_array($span) || empty($span)) return;
$tmp_out = "<tr>"; $row_out = "<tr>";
for ($i=0; $i<count($tmp_arr); $i++) { foreach ($span as $col) {
$tmp_val = $tmp_arr[$i];// CONTENT, COLSPAN=1, L, FUNC, EXTRA // CONTENT, COLSPAN=1, L, FUNC, EXTRA
if (!isset($tmp_val[1])) $tmp_val[1] = 1; if (!isset($col[1])) $col[1] = 1;
if (!isset($tmp_val[2])) $tmp_val[2] = "L"; if (!isset($col[2])) $col[2] = "L";
if (!isset($tmp_val[4])) $tmp_val[4] = ""; if (!isset($col[4])) $col[4] = "";
if (isset($tmp_val[3])) { if (isset($col[3]) && is_callable($col[3])) {
if (function_exists($tmp_val[3]) && trim("".$tmp_val[0])<>"") { if (function_exists($col[3]) && !empty($col[0])) {
$tmp_val[0] = call_user_func($tmp_val[3], $tmp_val[0]); $col[0] = call_user_func($col[3], $col[0]);
} }
} }
$tmp_val = $this->set_align($tmp_val[0], 0, $tmp_val[2], false, false, $tmp_val[1], $tmp_val[4]); $row_out .= $this->set_align($col[0], 0, $col[2], false, false, $col[1], $col[4]);
$tmp_out .= $tmp_val;
} }
$tmp_out .= "</tr>"; $row_out .= "</tr>";
$this->__avalue[] = $tmp_out; echo $row_out;
} }
public function add_stamp($ctk_ke=1, $bnama=false) {//up 2023-10-13 public function show_titles(): void {
$this->__tstamp = $this->get_stamp($ctk_ke, $bnama); $_afs = [0=>"4", 1=>"5", 2=>"6"];
foreach ($this->titles as $i => $title) {
vprintf('<div class="fs-%s text-center">%s</div>', [($_afs[$i] ?? '6'), $title]);
}
if (count($this->subtitles) > 0) {
foreach ($this->subtitles as $title) {
vprintf('<div>%s</div>', [$title]);
}
} else
echo '<br />';
} }
public function get_stamp($ctk_ke=1, $bnama=false) {//up 2012-10-11 public function show_headers(bool $is_hide=false): void {
return $_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):""); vprintf('<table class="%s"><thead>', [$this->css]);
// 0-n, 1=l, 2-f, 3-a, 4-k
$head_out = "<tr>";
foreach ($this->atabel as $tabel) {
if ($tabel['k'] === 0) continue;
$head_out .= $this->set_align($tabel['n'], $tabel['l'], "C", false, true, $tabel['k'], ($is_hide?'d-none':''));
}
$head_out .= "</tr>";
echo $head_out."</thead><tbody>";
// start content row
} }
public function show($jumlah_ctk=1) {//up 2012-10-11 public function add_stamp(int $ctk_ke=1, bool $tampil_nama=false): void {
$out = "<table class=\"".$this->__css."\"><thead>"; $this->stamp = $this->get_stamp($ctk_ke, $tampil_nama);
// header out
$tmp_out = "<tr>";
for ($i=0; $i<count($this->__atabel); $i++) {
if ($this->__atabel[$i][4]==0) continue;
$tmp_out .= $this->set_align($this->__atabel[$i][0], $this->__atabel[$i][1], "C", false, true, $this->__atabel[$i][4]);
}
$tmp_out .= "</tr>";
$out .= "</thead>".$tmp_out."<tbody>";
// content out
for ($i=0; $i<count($this->__avalue); $i++) {
$tmp_str = $this->__avalue[$i];
if (strpos($tmp_str, "_LINE")>0) $tmp_str = "<hr />";
$out .= $tmp_str;
}
$out .= "</tbody></table>";
$out .= $this->__tstamp;
for ($i=1; $i<=$jumlah_ctk; $i++) echo $out;
} }
public function set_align($ttext, $nlen, $tdir="L", $bline=false, $bhead=false, $ncol=1, $textra="") {//up 2011-04-14 public function get_stamp(int $ctk_ke=1, bool $tampil_nama=false): string {
$tstyle = $this->create_style($tdir); return $this->webapp['APP_NAME']."v".$this->webapp['APP_VER'].date("\@ymd.His")."-".isi_nol($this->webuser['UserID'], 3).(($tampil_nama)?"/".strtoupper($this->webuser['Username']):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"");
if ($bline===true) { }
return "<tr><td colspan=\"".count($this->__atabel)."\" class=\"".$tstyle." ".$textra."\">".$ttext."</td></tr>";
public function show() {
// end content row
echo "</tbody></table>";
if (!empty($this->stamp)) vprintf('<span class="small fst-italic">%s</span>', [$this->stamp]);
}
public function set_align(mixed $text, int $width=0, string $dir="L", bool $line=false, bool $head=false, int $col=1, string $extra=""): string {
$tstyle = $this->create_style($dir);
if ($line===true) {
return "<tr><td colspan=\"".count($this->atabel)."\" class=\"".$tstyle." ".$extra."\">".$text."</td></tr>";
} else { } else {
return (($bhead===true)?"<th":"<td").(($ncol>1)?" colspan=\"".$ncol."\"":"")." class=\"".$tstyle." ".$textra."\" ".(($bhead===true)?"width=\"".$nlen."\"":"").">".$ttext.(($bhead===true)?"</th>":"</td>"); return (($head===true)?"<th":"<td").(($col>1)?" colspan=\"".(string) $col."\"":"")." class=\"".$tstyle." ".$extra."\" ".(($head===true)?"width=\"".(string) $width."\"":"").">".$text.(($head===true)?"</th>":"</td>");
} }
} }
private function create_style($tstyle="L") { //up 2014-10-12 private function create_style(string $style="L"): string {
// style = Left Right Center No-Border No-Left No-Right Bold Italic // style = Left Right Center No-Border No-Left No-Right Bold Italic
$apad = ["L"=>"l", "R"=>"r", "C"=>"c", "NB"=>"nb", "NL"=>"noleft", "NR"=>"noright", "B"=>"b", "I"=>"i"]; $apad = ["L"=>"l", "R"=>"r", "C"=>"c", "NB"=>"nb", "NL"=>"noleft", "NR"=>"noright", "B"=>"b", "I"=>"i"];
$_astyle = explode(",", $tstyle); $_astyle = explode(",", $style);
$thasil = ""; $ahasil = [];
foreach ($_astyle as $_key => $_val) { foreach ($_astyle as $_key => $_val) {
$thasil .= " ".$apad[$_val]; $ahasil[] = $apad[$_val];
} }
return substr($thasil,1); return implode(' ', $ahasil);
} }
public function add_pagebreak() {}//delete: not used anymore
} }
?> ?>
+84 -84
View File
@@ -2,145 +2,145 @@
/******************************************************************* /*******************************************************************
Module : /Printing/PrintPOS.php Module : /Printer/PrintPOS.php
Desc. : Class cetak POS
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : June 15th, 2008. Created Date : June 15th, 2008.
Last Modified : December 3rd, 2025. Last Modified : October 3rd, 2026.
(c) 2008 - 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Printer; namespace Webapp\Printer;
/* VERSI BARU DENGAN MULTI SHEET */ use \flight\Engine;
class PrintPOS {//up: 20251203
private $_header = []; /**
private $_avalue = []; * Class PrintPOS
private $_astyle = []; *
private $_ncont = 0; * Pembuatan Nota/Struk untuk cetak ke Printer POS.
private $_maxchr = 40; *
private $_config = [ * @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Printer
*/
class PrintPOS {
private int $sheet = 0;
private int $maxchr = 40;
private array $header = [];
private array $acontent = [];
private array $config = [
'fontsize' => 1, 'fontsize' => 1,
'opendraw' => false, 'opendraw' => false,
'cutpaper' => false, 'cutpaper' => false,
'logo' => null ]; 'logo' => null,
];
private array $webapp;
private array $webuser;
protected Engine $app;
public function __construct() { /**
* @param Engine $app
*/
public function __construct(Engine $app) {
$this->app = $app;
$this->webapp = $app->session()->get('WEB_APP', ['APP_NAME'=>'', 'APP_VER'=>'']);
$this->webuser = $app->session()->get('WEB_USER', ['UserID'=>0, 'Username'=>'']);
} }
public function set_opendrawer() { public function set_opendrawer(): void {
$this->_config['opendraw'] = true; $this->config['opendraw'] = true;
} }
public function set_cutpaper() { public function set_cutpaper(): void {
$this->_config['cutpaper'] = true; $this->config['cutpaper'] = true;
} }
public function set_maxchr($nmax = 0) {//up: 2015-03-22 public function set_maxchr(int $max = 0): void {
if ($nmax > 0) $this->_maxchr = $nmax; if ($max > 0) $this->maxchr = $max;
} }
public function set_fontsize($nsize = 1) {//up: 2015-09-30 public function set_fontsize(int $size = 1): void {
if ($nsize > 0) $this->_config['fontsize'] = $nsize; if ($size > 0) $this->config['fontsize'] = $size;
} }
public function set_printnv_logo() {}//delete public function set_header_logo(string $logo_file = ""): void {
if (!empty($logo_file) && file_exists(PROJECT_ROOT."public/assets/images/".$logo_file)) {
public function set_header_logo($logo_file = "") {//up: 2019-01-08 //$this->config['logo'] = PROJECT_BASE."assets/images/".$logo_file;
if ($logo_file=="") return; $this->config['logo'] = base64_encode(file_get_contents(PROJECT_ROOT."public/assets/images/".$logo_file));
if (file_exists($_SESSION['__WEB_APP']['REAL_PATH']."assets/images/".$logo_file)) {
$this->_config['logo'] = $_SESSION['__WEB_APP']['WEB_PATH']."assets/images/".$logo_file;
} }
} }
public function add_header() {//up: 2025-12-03 public function add_header(): void {
$extra = func_get_args(); $extra = func_get_args();
if (count($extra) > 0) { if (count($extra) > 0) $this->header = $extra;
foreach ($extra as $_val) {
$this->_header[] = $_val;
}
}
} }
public function add_sheet() {//up: 20170321 public function add_sheet(): void {
$this->_ncont++; $this->sheet++;
} }
public function add() {//up: 20180524 public function add(): void {
$val_arr = func_get_args(); $ttext = ""; $tstyle = "L"; $val_arr = func_get_args(); $ttext = ""; $tstyle = "L";
if (is_array($val_arr[0])) { if (is_array($val_arr[0])) {
foreach ($val_arr as $aval) { foreach ($val_arr as $aval) {
$ttext .= $this->set_align((isset($aval[0])?$aval[0]:""), (isset($aval[1])?$aval[1]:0), (isset($aval[2])?$aval[2]:$tstyle)); $ttext .= $this->set_align(($aval[0] ?? ""), ($aval[1] ?? 0), ($aval[2] ?? $tstyle));
} }
} else { } else {
$ttext = (isset($val_arr[0])) ? $val_arr[0] : ""; $ttext = $val_arr[0] ?? "";
$tstyle = (isset($val_arr[1])) ? $val_arr[1] : $tstyle; $tstyle = $val_arr[1] ?? $tstyle;
} }
$this->_avalue[$this->_ncont][] = $ttext; if (str_contains($ttext, "_LINE")) {
$this->_astyle[$this->_ncont][] = $tstyle; $tmp_arr = explode(" ", $ttext);
$ttext = str_pad($tmp_arr[1], $this->maxchr, $tmp_arr[1]);
}
$this->acontent[$this->sheet][] = ($tstyle === "X") ? $ttext : $this->set_align($ttext, $this->maxchr, $tstyle);
} }
public function addl($ttext = "") { public function addl(string $text = ""): void {
$this->add($ttext); $this->add($text);
} }
public function addr($ttext = "") { public function addr(string $text = ""): void {
$this->add($ttext, "R"); $this->add($text, "R");
} }
public function addc($ttext = "") { public function addc(string $text = ""): void {
$this->add($ttext, "C"); $this->add($text, "C");
} }
public function add_line($tmp = "-") { public function add_line(string $line = "-"): void {
$this->add("__LINE ".$tmp); $this->add("__LINE ".$line);
} }
public function add_empty($nrep = 1) { public function add_empty(int $baris = 1): void {
for ($i=0;$i<$nrep;$i++) $this->add(""); for ($i=0; $i<$baris; $i++) $this->add("");
} }
public function add_stamp($ctk_ke=1, $bnama=false) { public function add_stamp(int $ctk_ke=1, bool $cetak_nama=false): void {
$this->add($_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"")); $this->add($this->webapp['APP_NAME']."v".$this->webapp['APP_VER'].date("\@ymd.His")."-".isi_nol($this->webuser['UserID'], 3).(($cetak_nama)?"/".strtoupper($this->webuser['Username']):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):""));
} }
private function _json_out() {//up: 2016-11-12 public function show(int $jumlah_ctk=1): void {
$acontent = array(); $this->config['times'] = $jumlah_ctk;
$nmax = $this->_maxchr; $aout = ['type' => "escp"];
$aout['config'] = $this->config;
for ($s=0; $s<count($this->_avalue); $s++) { $aout['header'] = $this->header;
for ($i=0; $i<count($this->_avalue[$s]); $i++) { $aout['content'] = $this->acontent;
$tmp_str = $this->_avalue[$s][$i]; $this->app->json($aout);
if (strpos($tmp_str, "_LINE")>0) {
$tmp_arr = explode(" ", $tmp_str);
$tmp_str = str_pad($tmp_arr[1], $nmax, $tmp_arr[1]);
}
$acontent[$s][] = ($this->_astyle[$s][$i]=="X") ? $tmp_str : $this->set_align($tmp_str, $nmax, $this->_astyle[$s][$i]);
}
} }
$aout = array('type' => "escp"); public function set_align(string $text, int $len=0, string $dir="L"): string {
$aout['config'] = $this->_config; $apad = ["L"=>STR_PAD_RIGHT, "R"=>STR_PAD_LEFT, "C"=>STR_PAD_BOTH];
$aout['header'] = $this->_header; $nlen = ($len==0) ? strlen($text) : $len;
$aout['content'] = $acontent; $ttext = (strlen($text)>$len) ? substr($text, 0, $len) : $text;
send_json($aout); return str_pad($text, $len, " ", $apad[$dir]);
} }
public function show($jumlah_ctk=1) {
$this->_config['times'] = $jumlah_ctk;
$this->_json_out();
}
public function set_align($ttext, $nlen=0, $tdir="L") {//up: 20180524
$apad = array("L"=>STR_PAD_RIGHT, "R"=>STR_PAD_LEFT, "C"=>STR_PAD_BOTH);
$nlen = ($nlen==0) ? strlen($ttext) : $nlen;
$ttext = (strlen($ttext)>$nlen) ? substr($ttext, 0, $nlen) : $ttext;
return str_pad($ttext, $nlen, " ", $apad[$tdir]);
}
} }
?> ?>
+60 -47
View File
@@ -2,19 +2,31 @@
/******************************************************************* /*******************************************************************
Module : /Printing/PrintRAW.php Module : /Printer/PrintRAW.php
Desc. : Class cetak RAW
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : June 15th, 2008. Created Date : June 15th, 2008.
Last Modified : November 1st, 2023. Last Modified : November 1st, 2023.
(c) 2008 - 2023, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Printer; namespace Webapp\Printer;
use \flight\Engine;
/**
* Class PrintRAW
*
* Pembuatan Laporan dalam bentuk RAW / Natural.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Printer
*/
class PrintRAW { class PrintRAW {
private $_header = []; private $_header = [];
@@ -27,54 +39,59 @@ class PrintRAW {
'fontsize' => "01", 'fontsize' => "01",
'nvlogo' => false, 'nvlogo' => false,
'opendraw' => false, 'opendraw' => false,
'opendrawbegin' => false, 'cutpaper' => false,
'cutpaper' => false ]; ];
private array $webapp;
private array $webuser;
protected Engine $app;
public function __construct() { /**
* @param Engine $app
*/
public function __construct(Engine $app) {
$this->app = $app;
$this->webapp = $app->session()->get('WEB_APP', ['APP_NAME'=>'', 'APP_VER'=>'']);
$this->webuser = $app->session()->get('WEB_USER', ['UserID'=>0, 'Username'=>'']);
} }
public function set_opendrawer($bbegin = true) { public function set_opendrawer(): void {
$this->_config['opendraw'] = true; $this->_config['opendraw'] = true;
$this->_config['opendrawbegin'] = $bbegin;
} }
public function set_cutpaper() { public function set_cutpaper(): void {
$this->_config['cutpaper'] = true; $this->_config['cutpaper'] = true;
} }
public function set_maxchr($nmax = 0) {//up: 2015-03-22 public function set_maxchr(int $max = 0): void {
if ($nmax > 0) $this->_maxchr = $nmax; if ($max > 0) $this->_maxchr = $max;
} }
public function set_fontsize($nsize = 1) {//up: 2015-09-30 public function set_fontsize(int $size = 1): void {
if ($nsize > 0) $this->_config['fontsize'] = str_pad($nsize, 2, "0", STR_PAD_LEFT); if ($size > 0) $this->_config['fontsize'] = str_pad((string)$size, 2, "0", STR_PAD_LEFT);
} }
public function set_printnv_logo() {//up: 2015-03-23 public function set_printnv_logo(): void {
$this->_config['nvlogo'] = true; $this->_config['nvlogo'] = true;
} }
public function add_header($nvlogo = false, $extra = []) {//up: 2016-11-12 public function add_header(bool $nvlogo = false, array $extra = []): void {
$this->_config['nvlogo'] = $nvlogo; $this->_config['nvlogo'] = $nvlogo;
$this->_header[] = $_SESSION['__WEB_APP']['REG_NAME'];
if (count($extra) > 0) { if (count($extra) > 0) {
foreach ($extra as $_val) { foreach ($extra as $_val) {
$this->_header[] = $_val; $this->_header[] = $_val;
} }
} }
$this->_header[] = $_SESSION['__WEB_APP']['REG_CITY'];
} }
public function add_sheet() {//up: 20170321 public function add_sheet(): void {
$this->_ncont++; $this->_ncont++;
} }
public function add_field($tnama="", $nlebar=0, $talign="L", $ffunc="") { public function add_field(string $nama, int $lebar=0, string $align="L", string $func=""): void {
$this->_atabel[] = [$tnama, $nlebar, $ffunc, $talign]; $this->_atabel[] = [ $nama, $lebar, $func, $align ];
} }
public function show_fields() { public function show_fields(): void {
$tmp_out = "<b>"; $tmp_out = "<b>";
for ($i=0; $i<count($this->_atabel); $i++) { for ($i=0; $i<count($this->_atabel); $i++) {
$tmp_out .= $this->set_align($this->_atabel[$i][0], $this->_atabel[$i][1], "C"); $tmp_out .= $this->set_align($this->_atabel[$i][0], $this->_atabel[$i][1], "C");
@@ -86,13 +103,13 @@ class PrintRAW {
$this->_avalue[$this->_ncont][] = "__LINE -"; $this->_avalue[$this->_ncont][] = "__LINE -";
} }
public function add_value() { public function add_value(): void {
$tmp_arr = func_get_args(); $tmp_arr = func_get_args();
if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0]; //up 2009-09-27 if (is_array($tmp_arr[0])) $tmp_arr = $tmp_arr[0];
$tmp_out = ""; $tmp_out = "";
for ($i=0; $i<count($this->_atabel); $i++) { for ($i=0; $i<count($this->_atabel); $i++) {
$tmp_val = $tmp_arr[$i]; $tmp_val = $tmp_arr[$i];
if (is_array($this->_atabel[$i][2]) && trim($tmp_val)<>"") { //up 2010-05-28 if (is_array($this->_atabel[$i][2]) && trim($tmp_val)<>"") {
$tmp_val = $this->_atabel[$i][2][$tmp_val]; $tmp_val = $this->_atabel[$i][2][$tmp_val];
} else if (function_exists($this->_atabel[$i][2]) && trim($tmp_val)<>"") { } else if (function_exists($this->_atabel[$i][2]) && trim($tmp_val)<>"") {
$tmp_val = call_user_func($this->_atabel[$i][2], $tmp_val); $tmp_val = call_user_func($this->_atabel[$i][2], $tmp_val);
@@ -104,24 +121,24 @@ class PrintRAW {
$this->_avalue[$this->_ncont][] = $tmp_out; $this->_avalue[$this->_ncont][] = $tmp_out;
} }
public function add($ttext = "") { public function add(string $text): void {
$this->_avalue[$this->_ncont][] = $ttext; $this->_avalue[$this->_ncont][] = $text;
} }
public function add_line($tmp = "-") { public function add_line(string $line = "-"): void {
$this->add("__LINE ".$tmp); $this->add("__LINE ".$line);
} }
public function add_empty($nrep = 1) { public function add_empty(int $rep = 1): void {
for ($i=0;$i<$nrep;$i++) $this->add(""); for ($i=0;$i<$rep;$i++) $this->add("");
} }
public function add_stamp($ctk_ke=1, $bnama=false) { public function add_stamp(int $ctk_ke=1, bool $cetak_nama=false): void {
$this->add($_SESSION['__WEB_APP']['APP_NAME']."v".$_SESSION['__WEB_APP']['APP_VER'].date("\@ymd.His")."-".user_id(3).(($bnama==true)?"/".strtoupper(user_user()):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):"")); $this->add($this->webapp['APP_NAME']."v".$this->webapp['APP_VER'].date("\@ymd.His")."-".isi_nol($this->webuser['UserID'], 3).(($cetak_nama)?"/".strtoupper($this->webuser['Username']):"").(($ctk_ke>0)?"#".isi_nol($ctk_ke,2):""));
} }
private function _json_out() {//up: 2016-11-12 public function show(int $jumlah_ctk=1): void {
$acontent = array(); $acontent = [];
$nmax = $this->_maxchr; $nmax = $this->_maxchr;
for ($s=0; $s<count($this->_avalue); $s++) { for ($s=0; $s<count($this->_avalue); $s++) {
@@ -135,22 +152,18 @@ class PrintRAW {
} }
} }
$aout = array('type' => "escp"); $this->_config['times'] = $jumlah_ctk;
$aout = ['type' => "escp"];
$aout['config'] = $this->_config; $aout['config'] = $this->_config;
$aout['header'] = $this->_header; $aout['header'] = $this->_header;
$aout['content'] = $acontent; $aout['content'] = $acontent;
send_json($aout); $this->app->json($aout);
} }
public function show($jumlah_ctk=1) { public function set_align(string $text, int $len, string $dir="L"): string {
$this->_config['times'] = $jumlah_ctk; $apad = ["L"=>STR_PAD_RIGHT, "R"=>STR_PAD_LEFT, "C"=>STR_PAD_BOTH];
$this->_json_out(); $ttext = (strlen($text)>$len) ? substr($text, 0, $len) : $text;
} return str_pad($text, $len, " ", $apad[$dir]);
public function set_align($ttext, $nlen, $tdir="L") {
$apad = array("L"=>STR_PAD_RIGHT, "R"=>STR_PAD_LEFT, "C"=>STR_PAD_BOTH);
$ttext = (strlen($ttext)>$nlen) ? substr($ttext, 0, $nlen) : $ttext;
return str_pad($ttext, $nlen, " ", $apad[$tdir]);
} }
} }
-134
View File
@@ -1,134 +0,0 @@
<?php
/*******************************************************************
Module : /Web/Auth.php
Desc. : Class login dan user checking
Created By : Siau Pheng ([email protected]).
Created Date : March 16th, 2008.
Last Modified : April 3rd, 2022.
(c) 2008 - 2022, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
//OK
declare(strict_types=1);
namespace siaupheng\webapp\Web;
use flight\Engine;
use flight\Session;
use flight\database\SimplePdo;
final class Auth {
private int $__id_reg = 0;
protected Engine $app;
protected SimplePdo $db;
protected Session $session;
public function __construct(?Engine $app = null){
$this->app = (empty($app) === true) ? \Flight::app() : $app;
$this->db = $this->app->db();
$this->session = $this->app->session();
$this->__id_reg = (int)$this->session->get('__WEB_APP', ['REG_ID'=>0])['REG_ID'];
}
public function set_id_reg($id = 0) {
if ($id > 0) $this->__id_reg = (int)$id;
}
public function user_check($user, $pass) {
$i_hasil = $this->db->fetchField('SELECT COUNT(*) FROM app_operator WHERE Username=? AND Password=MD5(?) AND Aktif=1', [$user, $pass]);
return ($i_hasil > 0 ? true : false);
}
public function user_data($user) {
$adata = $this->db->fetchRow('SELECT *,DECODE(Menu,?) Menu2 FROM app_operator WHERE Username=?', [$this->__id_reg, $user])->getData();
$adata['Menu'] = $adata['Menu2'];
unset($adata['Menu2']);
if (empty($this->session->get('APP_TOKEN'))) {
$_token = $this->genToken(60);
$this->session->set('APP_TOKEN', $_token);
$this->db->insert('app_token', [ 'Token' => $_token, 'UserID' => $adata['UserID'], 'IP' => $this->app->request()->ip ]);
}
if ((int)$adata['Grup_MenuID'] > 0) $adata['Menu_Grup'] = $this->user_allmenu($adata['Grup_MenuID']);
$adata['UNIX'] = time();
$adata['LOCALE'] = $this->session->get('__WEB_APP', ['DEF_LOCALE'=>'id'])['DEF_LOCALE'];
return $adata;
}
public function gen_module_menu() {
$amodul = []; $aallow = []; $tallow = '';
if ($this->session->get('__WEB_APP', ['APP_MODULE'=>0])['APP_MODULE']) {
$tallow = $this->db->fetchField('SELECT Modul_Akses FROM app_menu_grup WHERE Grup_MenuID=?', [$this->session->get('__WEB_USER', ['Grup_MenuID'=>0])['Grup_MenuID']]);
$aallow = explode(",", $tallow);
if (count($aallow) > 0) {
$stat = $this->db->runQuery('SELECT * FROM (SELECT APP_MODULE,APP_PATH FROM app_config) A LEFT JOIN (SELECT Modul,Urut,Judul,Icon FROM app_menu WHERE Level=0) B ON (A.APP_MODULE=B.Modul) ORDER BY B.Urut');
while ($row = $stat->fetch()) {
if (!in_array($row['Modul'], $aallow) || $row['Modul']==$this->session->get('__WEB_APP')['APP_MODULE']) continue;
$amodul[] = $row;
}
}
}
$auser = $this->session->get('__WEB_USER', []);
$auser['MENU_MODUL'] = $amodul;
$this->session->set('__WEB_USER', $auser);
}
public function user_allmenu(int $id_grup = 0) {
$allmenu = $this->db->fetchField('SELECT DECODE(Menu,?) MenuX FROM app_menu_grup WHERE Grup_MenuID=?', [$this->__id_reg, $id_grup]);
return $allmenu;
}
public function user_check_level($user, $level = 9) {
$i_hasil = $this->db->fetchField('SELECT COUNT(*) FROM app_operator WHERE Username=? AND Level<=? AND Aktif=1', [$user, $level]);
return ($i_hasil > 0 ? true : false);
}
public function user_log() {
$a_hasil = $this->db->fetchRow('SELECT * FROM app_logs WHERE UserID=? ORDER BY Last_Date DESC LIMIT 1', [user_id()])->getData();
if (count($a_hasil) > 0) {
$this->db->update('app_operator', [ 'Last_IP' => $a_hasil['Last_IP'], 'Last_Date' => $a_hasil['Last_Date'] ], 'UserID=?', [user_id()]);
}
$this->db->runQuery('INSERT IGNORE INTO app_logs SELECT UserID,Token,IP,DateStamp FROM app_token WHERE Token=?', [$this->session->get('APP_TOKEN')]);
}
public function logout() {
$this->db->delete('app_token', 'Token=?', [$this->session->get('APP_TOKEN')]);
}
public function token_check(string $token) {
$hasil = (bool)$this->db->fetchField('SELECT 1 FROM app_token WHERE Token=?', [$token ?? $this->session->get('APP_TOKEN')]);
if ($hasil === true) $this->db->update('app_token', ['DateStamp' => 'NOW()'], 'Token=?', [$token ?? $this->session->get('APP_TOKEN')]);
return $hasil;
}
public function token_update() {
$this->db->update('app_token', ['DateStamp' => 'NOW()'], 'Token=?', [$app->session->get('APP_TOKEN')]);
}
public function redirect(string $appUrl) {
$source = parse_url($appUrl);
$desti = "";
$hasil = (bool)$this->db->fetchField('SELECT 1 FROM app_host WHERE Nama_Host=?', [$source['host']]);
if ($hasil === true) {
$desti = $source['scheme'].$source['host'].$source['path'].'?'.'auth='.$app->session->get('APP_TOKEN');
}
return $desti;
}
private function genToken(int $length = 16) {
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($pool, 5)), 0, $length);
}
public function getUserByToken(string $token) {
$hasil = $this->db->fetchField('SELECT Username FROM app_operator WHERE UserID IN (SELECT UserID FROM app_token WHERE Token=?) LIMIT 1', [$token ??$app->session->get('APP_TOKEN')]);
return $hasil;
}
}
?>
+35 -24
View File
@@ -3,58 +3,69 @@
/******************************************************************* /*******************************************************************
Module : /Web/Button.php Module : /Web/Button.php
Desc. : Button Generator (jQuery & Bootstrap)
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : February 27th, 2008. Created Date : February 27th, 2008.
Last Modified : January 22nd, 2024. Last Modified : August 22nd, 2026.
(c) 2008 - 2024, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\Web;
/**
* Class Button
*
* Pembuatan tombol aplikasi (jQuery & Bootstrap).
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Web
*/
class Button { class Button {
private $__abutton =[]; private $__abutton =[];
public function __construct() {} public function __construct() {}
private function add($type="info", $id="", $label="", $bmiss=false) {
if ($id=="") $id = "random".rand(1000, 10000); private function add(string $type="info", string $id="", string $label="", bool $miss=false): void {
$this->__abutton[] = ['type'=>$type, 'id'=>$id, 'label'=>$label, 'bmiss'=>$bmiss]; if (empty($id)) $id = "random".(string)rand(1000, 10000);
$this->__abutton[] = [ 'type' => $type, 'id' => $id, 'label' => $label, 'miss' => $miss ];
} }
public function separator() { public function separator(): void {
$this->__abutton[] = [ 'type' => "separator" ]; $this->__abutton[] = [ 'type' => "separator" ];
} }
public function primary($id="", $label="", $bmiss=false) { public function primary(string $id="", string $label="", bool $miss=false): void {
$this->add("primary", $id, $label, $bmiss); $this->add("primary", $id, $label, $miss);
} }
public function info($id="", $label="", $bmiss=false) { public function info(string $id="", string $label="", bool $miss=false): void {
$this->add("info", $id, $label, $bmiss); $this->add("info", $id, $label, $miss);
} }
public function danger($id="", $label="", $bmiss=false) { public function danger(string $id="", string $label="", bool $miss=false): void {
$this->add("danger", $id, $label, $bmiss); $this->add("danger", $id, $label, $miss);
} }
public function warning($id="", $label="", $bmiss=false) { public function warning(string $id="", string $label="", bool $miss=false): void {
$this->add("warning", $id, $label, $bmiss); $this->add("warning", $id, $label, $miss);
} }
public function success($id="", $label="", $bmiss=false) { public function success(string $id="", string $label="", bool $miss=false): void {
$this->add("success", $id, $label, $bmiss); $this->add("success", $id, $label, $miss);
} }
public function secondary($id="", $label="", $bmiss=false) { public function secondary(string $id="", string $label="", bool $miss=false): void {
$this->add("secondary", $id, $label, $bmiss); $this->add("secondary", $id, $label, $miss);
} }
public function Button($size=2) { public function Button(int $size=2): string {
if (0==count($this->__abutton)) return ""; if (0===count($this->__abutton)) return "";
$_out = '<div class="row mb-1"><div class="col-12 col-sm-6 my-0 mx-sm-0 text-center text-sm-start">'; $_ma = ' me-sm-2'; $_out = '<div class="row mb-1"><div class="col-12 col-sm-6 my-0 mx-sm-0 text-center text-sm-start">'; $_ma = ' me-sm-2';
foreach ($this->__abutton as $item) { foreach ($this->__abutton as $item) {
if ("separator" == $item['type']) { if ("separator" === $item['type']) {
$_out .= '</div><div class="col-12 col-sm-6 my-0 mx-sm-0 text-center text-sm-end">'; $_ma = ' ms-sm-2'; $_out .= '</div><div class="col-12 col-sm-6 my-0 mx-sm-0 text-center text-sm-end">'; $_ma = ' ms-sm-2';
} else { } else {
$_out .= '<button type="button" class="btn btn-'.$item['type'].' mb-2 mb-sm-0'.$_ma.' col-10 col-sm-auto shadow-none" id="'.$item['id'].'"'.(true==$item['bmiss']?' data-bs-dismiss="modal"':'').'>'.$item['label'].'</button>'; $_out .= '<button type="button" class="btn btn-'.$item['type'].' mb-2 mb-sm-0'.$_ma.' col-10 col-sm-auto shadow-none" id="'.$item['id'].'"'.(true===$item['miss']?' data-bs-dismiss="modal"':'').'>'.$item['label'].'</button>';
} }
} }
$_out .= '</div></div>'; $_out .= '</div></div>';
+65 -53
View File
@@ -3,23 +3,32 @@
/******************************************************************* /*******************************************************************
Module : /Web/Form.php Module : /Web/Form.php
Desc. : Form Generator (jQuery & Bootstrap)
Created By : Siau Pheng ([email protected]). Created By : Siau Pheng ([email protected]).
Created Date : February 27th, 2008. Created Date : February 27th, 2008.
Last Modified : August 19th, 2026. Last Modified : September 5th, 2026.
(c) 2008 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\Web;
use flight\Engine; use \flight\Engine;
use flight\Session; use \flight\Session;
use flight\database\SimplePdo; use \flight\database\SimplePdo;
/**
* Class Form
*
* Pembuatan form (jQuery & Bootstrap).
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
*
* @package Webapp\Web
*/
final class Form { final class Form {
private $__aform = []; private $__aform = [];
@@ -28,15 +37,18 @@ final class Form {
protected SimplePdo $db; protected SimplePdo $db;
protected Session $session; protected Session $session;
public function __construct(?Engine $app = null){ /**
$this->app = (empty($app) === true) ? \Flight::app() : $app; * @param Engine $app
$this->db = $this->app->db(); */
$this->session = $this->app->session(); public function __construct(Engine $app){
$this->app = $app;
$this->db = $app->db();
$this->session = $app->session();
} }
public function option(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null, bool $multi=false, bool $group=false): string {//oke public function option(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null, bool $multi=false, bool $group=false): string {
$_opsi = ''; $_opsi = '';
if (!empty($change)) $_opsi .= ' ochan="'.$change.'"'; if (!empty($change)) $_opsi .= ' ochan="'.rtrim($change, '();').'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
if ($req===true) $_opsi .= ' required=true'; if ($req===true) $_opsi .= ' required=true';
if ($multi===true) $_opsi .= ' multiple'; if ($multi===true) $_opsi .= ' multiple';
@@ -70,12 +82,12 @@ final class Form {
return $tout; return $tout;
} }
public function option_db(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, ?string $table=null, ?array $afield=null, ?string $where=null, ?array $aarray=null, ?string $help=null, ?string $change=null, bool $multi=false): string {//oke public function option_db(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, ?string $table=null, ?array $afield=null, ?string $where=null, ?array $aarray=null, ?string $help=null, ?string $change=null, bool $multi=false): string {
$_twhere = ''; $_twhere = '';
if (!is_array($afield)) $afield = ["ID", "Nama", "Label"]; if (!is_array($afield)) $afield = ["ID", "Nama", "Label"];
if (!empty($where)) { if (!empty($where)) {
$_twhere = ' WHERE '.$where; $_twhere = ' WHERE '.$where;
$_twhere .= (stripos($twhere,"ORDER BY")===false) ? ' ORDER BY '.$afield[1] : ''; $_twhere .= (stripos($where,"ORDER BY")===false) ? ' ORDER BY '.$afield[1] : '';
} }
$ahasil = []; $ahasil = [];
if (is_array($aarray)) $ahasil[$aarray[0]] = (!isset($afield[2])) ? $aarray[1] : [$aarray[1], $aarray[2]]; if (is_array($aarray)) $ahasil[$aarray[0]] = (!isset($afield[2])) ? $aarray[1] : [$aarray[1], $aarray[2]];
@@ -88,7 +100,7 @@ final class Form {
return $this->option($label, $field, $value, $enable, $req, $ahasil, $help, $change, $multi); return $this->option($label, $field, $value, $enable, $req, $ahasil, $help, $change, $multi);
} }
public function option_que(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, ?string $query=null, ?array $afield=null, ?array $aarray=null, ?string $help=null, ?string $change=null): string {//oke public function option_que(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, ?string $query=null, ?array $afield=null, ?array $aarray=null, ?string $help=null, ?string $change=null): string {
if (!is_array($afield)) $afield = ["ID", "Nama", "Label"]; if (!is_array($afield)) $afield = ["ID", "Nama", "Label"];
$ahasil = []; $ahasil = [];
if (is_array($aarray)) $ahasil[$aarray[0]] = (!isset($afield[2])) ? $aarray[1] : [$aarray[1], $aarray[2]]; if (is_array($aarray)) $ahasil[$aarray[0]] = (!isset($afield[2])) ? $aarray[1] : [$aarray[1], $aarray[2]];
@@ -101,19 +113,19 @@ final class Form {
return $this->option($label, $field, $value, $enable, $req, $ahasil, $help, $change); return $this->option($label, $field, $value, $enable, $req, $ahasil, $help, $change);
} }
public function option_jkl(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {//oke public function option_jkl(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {
return $this->option($label, $field, $value, $enable, $req, ["L" => "Laki-Laki", "P" => "Perempuan"], $help, $change); return $this->option($label, $field, $value, $enable, $req, ["L" => "Laki-Laki", "P" => "Perempuan"], $help, $change);
} }
public function option_ulevel(string $label, string $field, int $value=9, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {//oke public function option_ulevel(string $label, string $field, int $value=9, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {
return $this->option($label, $field, $value, $enable, $req, $this->session->get('__WEB_APP')['USER_LEVEL'], $help, $change); return $this->option($label, $field, $value, $enable, $req, $this->session->get('WEB_APP')['USER_LEVEL'], $help, $change);
} }
public function radio(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null): string {//oke public function radio(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null): string {
$_val = array_values($arr); $_val = array_keys($arr);
if (empty($value)) $value = $_val[0]; if (is_null($value)) $value = $_val[0];
$_opsi = ''; $_opsi = '';
if (!empty($change)) $_opsi .= ' ochan="'.$change.'"'; if (!empty($change)) $_opsi .= ' ochan="'.rtrim($change, '();').'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
if ($req===true) $_opsi .= ' required=true'; if ($req===true) $_opsi .= ' required=true';
$tout = '<input type="hidden" id="'.$field.'" value="'.$value.'" />'; $tout = '<input type="hidden" id="'.$field.'" value="'.$value.'" />';
@@ -128,20 +140,20 @@ final class Form {
return $tout; return $tout;
} }
public function radio_jkl(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {//oke public function radio_jkl(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {
return $this->radio($label, $field, $value, $enable, $req, ["L" => "Laki-Laki", "P" => "Perempuan"], $help, $change); return $this->radio($label, $field, $value, $enable, $req, ["L" => "Laki-Laki", "P" => "Perempuan"], $help, $change);
} }
public function radio_gd(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {//oke public function radio_gd(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {
if (!empty($value)) $value = 'X'; if (!empty($value)) $value = 'X';
return $this->radio($label, $field, $value, $enable, $req, ["A"=>"A", "B"=>"B", "AB"=>"AB", "O"=>"O", "X"=>"NONE"], $help, $change); return $this->radio($label, $field, $value, $enable, $req, ["A"=>"A", "B"=>"B", "AB"=>"AB", "O"=>"O", "X"=>"NONE"], $help, $change);
} }
public function checkbox(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null): string {//oke public function checkbox(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null): string {
$_val = array_values($arr); $_val = array_values($arr);
if (empty($value)) $value = $_val[1]; if (is_null($value)) $value = $_val[0];
$_opsi = ' odata="'.htmlspecialchars(json_encode($arr)).'"'; $_opsi = ' odata="'.htmlspecialchars(json_encode($arr)).'"';
if (!empty($change)) $_opsi .= ' ochan="'.$change.'"'; if (!empty($change)) $_opsi .= ' ochan="'.rtrim($change, '();').'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
if ($req===true) $_opsi .= ' required=true'; if ($req===true) $_opsi .= ' required=true';
$tout = '<div class="col-form-label form-check"><input type="checkbox" class="form-check-input" id="'.$field.'" name="'.$field.'" value="'.$value.'"'.$_opsi; $tout = '<div class="col-form-label form-check"><input type="checkbox" class="form-check-input" id="'.$field.'" name="'.$field.'" value="'.$value.'"'.$_opsi;
@@ -151,11 +163,11 @@ final class Form {
return $tout; return $tout;
} }
public function switch(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null): string {//oke public function switch(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, array $arr=[], ?string $help=null, ?string $change=null): string {
$_val = array_values($arr); $_val = array_values($arr);
if (empty($value)) $value = $_val[1]; if (is_null($value)) $value = $_val[0];
$_opsi = ' odata="'.htmlspecialchars(json_encode($arr)).'"'; $_opsi = ' odata="'.htmlspecialchars(json_encode($arr)).'"';
if (!empty($change)) $_opsi .= ' ochan="'.$change.'"'; if (!empty($change)) $_opsi .= ' ochan="'.rtrim($change, '();').'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
if ($req===true) $_opsi .= ' required=true'; if ($req===true) $_opsi .= ' required=true';
$tout = '<div class="col-form-label form-check form-switch"><input class="form-check-input" id="'.$field.'" name="'.$field.'" type="checkbox" role="switch" value="'.$value.'"'.$_opsi; $tout = '<div class="col-form-label form-check form-switch"><input class="form-check-input" id="'.$field.'" name="'.$field.'" type="checkbox" role="switch" value="'.$value.'"'.$_opsi;
@@ -165,10 +177,10 @@ final class Form {
return $tout; return $tout;
} }
public function textbox(string $label, string $field, string|int|null $value=null, bool $enable=true, bool $req=false, string $type="text", ?string $holdtxt=null, ?int $maxsize=null, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {//oke public function textbox(string $label, string $field, mixed $value=null, bool $enable=true, bool $req=false, string $type="text", ?string $holdtxt=null, ?int $maxsize=null, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {
$_opsi = ''; $_opsi = '';
if (!empty($keyup)) $_opsi .= ' okyup="'.$keyup.'"'; if (!empty($keyup)) $_opsi .= ' okyup="'.rtrim($keyup, '();').'"';
if (!empty($blur)) $_opsi .= ' oblur="'.$blur.'"'; if (!empty($blur)) $_opsi .= ' oblur="'.rtrim($blur, '();').'"';
if (!empty($holdtxt)) $_opsi .= ' placeholder="'.$holdtxt.'"'; if (!empty($holdtxt)) $_opsi .= ' placeholder="'.$holdtxt.'"';
if (!empty($maxsize)) $_opsi .= ' maxlength="'.$maxsize.'"'; if (!empty($maxsize)) $_opsi .= ' maxlength="'.$maxsize.'"';
if ($enable===false) $_opsi .= ' readonly'; if ($enable===false) $_opsi .= ' readonly';
@@ -179,13 +191,14 @@ final class Form {
if ($type === 'double') $value = uang2($value); if ($type === 'double') $value = uang2($value);
$type = "text"; $type = "text";
} }
$tout = '<input type="'.$type.'" class="form-control'.$_tclass.'" id="'.$field.'" name="'.$field.'" value="'.$value.'"'.$_opsi.' />'; $tout = '<input type="'.$type.'" class="form-control'.$_tclass.'" id="'.$field.'" name="'.$field.'" value="'.(string)$value.'"'.$_opsi.' />';
$this->__aform[$field] = [ 'label' => $label, 'help' => $help, 'form' => $tout ]; $this->__aform[$field] = [ 'label' => $label, 'help' => $help, 'form' => $tout ];
return $tout; return $tout;
} }
public function textauto(string $label, string $field, array $avalue=['value'=>0,'text'=>""], bool $enable=true, bool $req=false, ?string $holdtxt=null, ?int $maxsize=null, ?string $help=null): string {//oke public function textauto(string $label, string $field, array $avalue, bool $enable=true, bool $req=false, ?string $holdtxt=null, ?int $maxsize=null, ?string $help=null): string {
$_opsi = ''; $_opsi = '';
if (count($avalue) === 0) $avalue = [ 'value' => 0, 'text' => "" ];
if (!empty($holdtxt)) $_opsi .= ' placeholder="'.$holdtxt.'"'; if (!empty($holdtxt)) $_opsi .= ' placeholder="'.$holdtxt.'"';
if (!empty($maxsize)) $_opsi .= ' maxlength="'.$maxsize.'"'; if (!empty($maxsize)) $_opsi .= ' maxlength="'.$maxsize.'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
@@ -195,7 +208,7 @@ final class Form {
return $tout; return $tout;
} }
public function datebox(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {//oke public function datebox(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {
if (!empty($value)) $value = tgl_sql2str($value); if (!empty($value)) $value = tgl_sql2str($value);
$tout = '<div class="input-group date">'; $tout = '<div class="input-group date">';
$tout .= $this->textbox($label, $field, $value, $enable, $req, "pickdate", "dd-mm-yyyy", null, $help, $keyup, $blur); $tout .= $this->textbox($label, $field, $value, $enable, $req, "pickdate", "dd-mm-yyyy", null, $help, $keyup, $blur);
@@ -204,14 +217,14 @@ final class Form {
return $tout; return $tout;
} }
public function timebox(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {//oke public function timebox(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {
return $this->textbox($label, $field, $value, $enable, $req, "picktime", "hh:mm:ss", 10, $help, $keyup, $blur); return $this->textbox($label, $field, $value, $enable, $req, "picktime", "hh:mm:ss", 10, $help, $keyup, $blur);
} }
public function textarea(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, int $rows=1, ?string $holdtxt=null, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {//oke public function textarea(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, int $rows=1, ?string $holdtxt=null, ?string $help=null, ?string $keyup=null, ?string $blur=null): string {
$_opsi = ''; $_opsi = '';
if (!empty($keyup)) $_opsi .= ' okyup="'.$keyup.'"'; if (!empty($keyup)) $_opsi .= ' okyup="'.rtrim($keyup, '();').'"';
if (!empty($blur)) $_opsi .= ' oblur="'.$blur.'"'; if (!empty($blur)) $_opsi .= ' oblur="'.rtrim($blur, '();').'"';
if (!empty($holdtxt)) $_opsi .= ' placeholder="'.$holdtxt.'"'; if (!empty($holdtxt)) $_opsi .= ' placeholder="'.$holdtxt.'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
if ($req===true) $_opsi .= ' required=true'; if ($req===true) $_opsi .= ' required=true';
@@ -220,19 +233,19 @@ final class Form {
return $tout; return $tout;
} }
public function plaintext(string $label, string $field, string|int|null $value=null): string {//oke public function plaintext(string $label, string $field, mixed $value=null): string {
$tout = '<input type="text" class="form-control-plaintext" id="'.$field.'" name="'.$field.'" value="'.$value.'" readonly />'; $tout = '<input type="text" class="form-control-plaintext" id="'.$field.'" name="'.$field.'" value="'.$value.'" readonly />';
$this->__aform[$field] = ['label' => $label, 'help' => "", 'form' => $tout]; $this->__aform[$field] = ['label' => $label, 'help' => "", 'form' => $tout];
return $tout; return $tout;
} }
public function password(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $holdtxt=null, ?int $maxsize=null, ?string $help=null, ?string $keyup=null): string {//oke public function password(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $holdtxt=null, ?int $maxsize=null, ?string $help=null, ?string $keyup=null): string {
return $this->textbox($label, $field, $value, $enable, $req, "password", $holdtxt, $maxsize, $help, $keyup, null); return $this->textbox($label, $field, $value, $enable, $req, "password", $holdtxt, $maxsize, $help, $keyup, null);
} }
public function color(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {//oke public function color(string $label, string $field, ?string $value=null, bool $enable=true, bool $req=false, ?string $help=null, ?string $change=null): string {
$_opsi = ''; $_opsi = '';
if (!empty($change)) $_opsi .= ' ochan="'.$change.'"'; if (!empty($change)) $_opsi .= ' ochan="'.rtrim($change, '();').'"';
if ($enable===false) $_opsi .= ' disabled=true'; if ($enable===false) $_opsi .= ' disabled=true';
if ($req===true) $_opsi .= ' required=true'; if ($req===true) $_opsi .= ' required=true';
$tout = '<input type="color" class="form-control form-control-color" id="'.$field.'" name="'.$field.'" value="'.$value.'"'.$_opsi.' />'; $tout = '<input type="color" class="form-control form-control-color" id="'.$field.'" name="'.$field.'" value="'.$value.'"'.$_opsi.' />';
@@ -240,23 +253,22 @@ final class Form {
return $tout; return $tout;
} }
public function hidden(string $field, string|int|null $value=null, string $class="text"): string {//oke public function hidden(string $field, mixed $value=null, string $class="text"): string {
$tout = '<input type="hidden" id="'.$field.'" value="'.$value.'" class="'.$class.'" />'; $tout = '<input type="hidden" id="'.$field.'" value="'.$value.'" class="'.$class.'" />';
$this->__ahide[$field] = $tout; $this->__ahide[$field] = $tout;
return $tout; return $tout;
} }
public function clear() { public function clear(): void {
$this->__aform = []; $this->__aform = [];
} }
public function show($tstr) { public function show(int $lebar_kolom = 4): string {
if ($tstr === 0) $tstr = 4;//default $col_left = 'col-sm-' . ($lebar_kolom+2) . ' col-lg-' . $lebar_kolom;
$col_left = 'col-sm-' . ($tstr+2) . ' col-lg-' . $tstr; $col_right = 'col-sm-' . (10-$lebar_kolom) . ' col-lg-' . (12-$lebar_kolom);
$col_right = 'col-sm-' . (10-$tstr) . ' col-lg-' . (12-$tstr);
$tres = ''; $tres = '';
foreach ($this->__aform as $_key => $_val) { foreach ($this->__aform as $_key => $_val) {
$tres .= sprintf('<div class="row mb-1"><label class="%s col-form-label" for="%s">%s</label><div class="%s">%s %s</div></div>', $col_left, $_key, $_val['label'], $col_right, $_val['form'], (empty($_val['help'])?'':'<span class="help-block"><small>'.$_val['help'].'</small></span>')); $tres .= sprintf('<div class="row mb-1"><label class="%s col-form-label" for="%s">%s</label><div class="%s">%s %s</div></div>', $col_left, $_key, $_val['label'], $col_right.(str_contains($_val['form'],'type="radio"')?' d-flex gap-2':''), $_val['form'], (empty($_val['help'])?'':'<span class="help-block"><small>'.$_val['help'].'</small></span>'));
} }
foreach ($this->__ahide as $_key => $_val) { foreach ($this->__ahide as $_key => $_val) {
$tres .= $_val; $tres .= $_val;
+75 -32
View File
@@ -3,52 +3,70 @@
/******************************************************************* /*******************************************************************
Module : /Web/Menu.php Module : /Web/Menu.php
Desc. : Class menu
Created By : Siau Pheng (siaupheng@3fonia.com). Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : January 16th, 2008. Created Date : January 16th, 2008.
Last Modified : December 26th, 2025. Last Modified : October 30th, 2026.
(c) 2008 - 2025, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\Web;
use flight\Engine; use \flight\Engine;
use flight\Session; use \flight\Session;
use flight\database\SimplePdo; use \flight\database\SimplePdo;
/**
* Class Menu
*
* Manajemen Menu aplikasi.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <siaupheng@3fonia.com>
*
* @package Webapp\Web
*/
final class Menu { final class Menu {
private array $web_app;
private array $web_user;
private array $my_menu;
protected Engine $app; protected Engine $app;
protected SimplePdo $db; protected SimplePdo $db;
protected Session $session; protected Session $session;
public function __construct(?Engine $app = null){ /**
$this->app = (empty($app) === true) ? \Flight::app() : $app; * @param Engine $app
$this->db = $this->app->db(); */
$this->session = $this->app->session(); public function __construct(Engine $app){
$this->app = $app;
$this->db = $app->db();
$this->session = $app->session();
$this->web_app = $this->session->get('WEB_APP', ['APP_MODULE'=>'', 'PARENT_SITE'=>'', 'PARENT_SUB'=>false]);
$this->web_user = $this->session->get('WEB_USER', ['Menu'=>'', 'Menu_Grup'=>'', 'MENU_MODUL'=>[]]);
$_gmenu = '';
$_umenu = $this->db->fetchField("SELECT CONVERT(DECODE(Menu,?) USING UTF8MB4) Menu FROM app_operator WHERE UserID=?", [$this->web_app['PARENT_CODE'], $this->web_user['UserID']]) ?? '';
if ($this->web_user['Grup_MenuID'] > 0) $_gmenu = $this->db->fetchField('SELECT CONVERT(DECODE(Menu,?) USING UTF8MB4) Menu FROM app_menu_grup WHERE Grup_MenuID=?', [$this->web_app['PARENT_CODE'], $this->web_user['Grup_MenuID']]) ?? '';
$this->my_menu = array_unique(array_filter(explode(",", $_gmenu.",".$_umenu)));
} }
public function Menu_Sidebar() { public function Sidebar(): string {
$a_modul = []; $a_modul = [];
$is_modul = ($this->session->get('__WEB_APP', ['APP_MODULE'=>''])['APP_MODULE'] <> '') ? true : false; $is_modul = !empty($this->web_app['APP_MODULE']);
if ($is_modul == true) { if ($is_modul === true) {
$a_modul[] = $this->session->get('__WEB_APP')['APP_MODULE']; $a_modul[] = $this->web_app['APP_MODULE'];
} else { } else {
$atmp = $this->db->fetchAll('SELECT * FROM app_menu WHERE Level=0 AND Aktif=1 AND Modul<>"IND" ORDER BY Urut'); $atmp = $this->db->fetchAll('SELECT * FROM app_menu WHERE Level=0 AND Aktif=1 AND Modul<>"IND" ORDER BY Urut');
foreach ($atmp as $tmp) { foreach ($atmp as $tmp) $a_modul[] = $tmp['Modul'];
$a_modul[] = $tmp['Modul'];
}
} }
$my_menu = array_unique(array_filter(explode(",", $this->session->get('__WEB_USER', ['Menu_Grup'=>''])['Menu_Grup'].",".$this->session->get('__WEB_USER', ['Menu'=>''])['Menu'])));
$tsql = 'SELECT * FROM app_menu WHERE Aktif=1 AND Modul IN (?) AND Kode IN (?)'; $tsql = 'SELECT * FROM app_menu WHERE Aktif=1 AND Modul IN (?) AND Kode IN (?)';
if ($is_modul == true) $tsql .= ' AND Level>0'; if ($is_modul == true) $tsql .= ' AND Level>0';
$amodul = $this->db->fetchAll($tsql . ' ORDER BY Kode', [$a_modul, $my_menu]); $amodul = $this->db->fetchAll($tsql . ' ORDER BY Kode', [$a_modul, $this->my_menu]);
$thasil = ''; $thasil = '';
$last_level = 0; $last_level = 0;
@@ -70,12 +88,12 @@ final class Menu {
return $thasil; return $thasil;
} }
public function Menu_Header() { public function Header(): string {
$a_modul = []; $a_modul = [];
$is_modul = ($this->session->get('__WEB_APP', ['APP_MODULE'=>''])['APP_MODULE'] <> '') ? true : false; $is_modul = !empty($this->web_app['APP_MODULE']);
$mulai_level = 0; $mulai_level = 0;
if ($is_modul == true) { if ($is_modul === true) {
$a_modul[] = $this->session->get('__WEB_APP')['APP_MODULE']; $a_modul[] = $this->web_app['APP_MODULE'];
$mulai_level = 1; $mulai_level = 1;
} else { } else {
$atmp = $this->db->fetchAll('SELECT * FROM app_menu WHERE Level=0 AND Aktif=1 AND Modul<>"IND" ORDER BY Urut'); $atmp = $this->db->fetchAll('SELECT * FROM app_menu WHERE Level=0 AND Aktif=1 AND Modul<>"IND" ORDER BY Urut');
@@ -84,11 +102,10 @@ final class Menu {
} }
} }
$my_menu = array_unique(array_filter(explode(",", $this->session->get('__WEB_USER', ['Menu_Grup'=>''])['Menu_Grup'].",".$this->session->get('__WEB_USER', ['Menu'=>''])['Menu'])));
$tsql = 'SELECT * FROM app_menu WHERE Aktif=1 AND Modul IN (?) AND Kode IN (?)'; $tsql = 'SELECT * FROM app_menu WHERE Aktif=1 AND Modul IN (?) AND Kode IN (?)';
if ($is_modul == true) $tsql .= ' AND Level>0'; if ($is_modul == true) $tsql .= ' AND Level>0';
$amodul = $this->db->fetchAll($tsql . ' ORDER BY Kode', [$a_modul, $my_menu]); $amodul = $this->db->fetchAll($tsql . ' ORDER BY Kode', [$a_modul, $this->my_menu]);
$thasil = ''; $thasil = '';
$last_level = 0; $last_level = 0;
@@ -116,9 +133,35 @@ final class Menu {
return $thasil; return $thasil;
} }
static function MenuJSON($db = null, $page = "", $tquery = "") { public function Modul(): string {
$_mdb = (empty($db) === true) ? new SimplePdo() : $db; $t_modul = '';
$_query = ($tquery=="") ? "" : ",'".$tquery."'"; if (is_array($this->web_user['MENU_MODUL']) && count($this->web_user['MENU_MODUL'])>0) {
$t_modul = '<div class="dropdown-divider"></div>';
if ($this->app->request()->getVar('SERVER_ADDR') === '127.0.0.1') {
$href1 = '//127.0.0.1/';
$href2 = "";
} else {
$_host = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_HOST'] ?? $this->web_app['PARENT_SITE'];
$_host = str_replace(['http://','https://'], "", $_host);
if ($this->web_app['PARENT_SUB'] === true) {
$_subs = explode('.', $_host);
if (count($_subs) > 2) array_shift($_subs);
$href1 = "//";
$href2 = "." . implode('.', $_subs);
} else {
$href1 = "//" . $_host . '/';
$href2 = "";
}
}
foreach ($this->web_user['MENU_MODUL'] as $modul) {
$href = $href1 . $modul['APP_PATH'] . $href2 . '/' . $this->session->get('APP_TOKEN');
$t_modul .= '<a class="dropdown-item slink" href="'.$href.'"><i class="'.$modul['Icon'].' me-2"></i> '.$modul['Judul'].'</a>';
}
}
return $t_modul;
}
static function MenuJSON(SimplePdo $db, string $page = "", string $q = ""): array {
$ajson = ['login' => true]; $ajson = ['login' => true];
$apage = ['Link' => ""]; $apage = ['Link' => ""];
@@ -130,14 +173,14 @@ final class Menu {
if ($acus_page[$page] ?? false) { if ($acus_page[$page] ?? false) {
$apage = $acus_page[$page]; $apage = $acus_page[$page];
} else if ($page <> "") { } else if ($page <> "") {
$apage = $_mdb->fetchRow('SELECT * FROM app_menu WHERE Kode=?', [$page]); $apage = $db->fetchRow('SELECT * FROM app_menu WHERE Kode=?', [$page]);
$apage['Width'] = (int) $apage['Width']; $apage['Width'] = (int) $apage['Width'];
} }
$ajson['iswindow'] = ($apage['Width'] > 0) ? true : false; $ajson['iswindow'] = ($apage['Width'] > 0) ? true : false;
$ajson['width'] = $apage['Width']; $ajson['width'] = $apage['Width'];
$ajson['title'] = $apage['Judul']; $ajson['title'] = $apage['Judul'];
$ajson['button'] = is_json($apage['Button']) ? json_decode($apage['Button']) : $apage['Button']; $ajson['button'] = json_validate($apage['Button']) ? json_decode($apage['Button']) : $apage['Button'];
$ajson['php'] = $apage['Link']; $ajson['php'] = $apage['Link'];
$ajson['func'] = $apage['JS']; $ajson['func'] = $apage['JS'];
$ajson['kode'] = $apage['Kode']; $ajson['kode'] = $apage['Kode'];
+38 -27
View File
@@ -3,22 +3,31 @@
/******************************************************************* /*******************************************************************
Module : /Web/Param.php Module : /Web/Param.php
Desc. : Class data parameter dari client side
Created By : Siau Pheng (siaupheng@3fonia.com). Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : November 14th, 2013. Created Date : November 14th, 2013.
Last Modified : August 8th, 2026. Last Modified : October 16th, 2026.
(c) 2013 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\Web;
use \flight\Engine; use \flight\Engine;
/**
* Class Param
*
* Manajemen Variable Request dari GET / POST.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <siaupheng@3fonia.com>
*
* @package Webapp\Web
*/
final class Param { final class Param {
private static $__ahasil = []; private static $__ahasil = [];
@@ -26,44 +35,46 @@ final class Param {
if (count(self::$__ahasil)==0) self::__data(); if (count(self::$__ahasil)==0) self::__data();
} }
private static function __data() { private static function __data(): void {
global $app; global $app;
if (empty($app)) $app = \Flight::app(); if (empty($app)) $app = \Flight::app();
$_asumber = array_merge($app->request()->query->getData(), $app->request()->data->getData()); $_asumber = $app->request()->query->getData() + $app->request()->data->getData();
foreach ($_asumber as $__key => $__val) { foreach ($_asumber as $__key => $__val) {
self::$__ahasil[$__key] = self::safeSQL($__val); if (in_array($__val, ['true','false'])) { $__val = ($__val === 'true'); }
else if (is_string($__val)) $__val = urldecode(self::safeSQL($__val));
self::$__ahasil[$__key] = $__val;
} }
} }
public static function safeSQL($tstr = "") { public static function safeSQL(string $str = ""): string {
$_se = array("\\","\0","\n","\r","\x1a","'",'"'); $_se = ["\\","\0","\n","\r","\x1a","'",'"'];
$_re = array("\\\\","\\0","\\n","\\r","\Z","\'",'\"'); $_re = ["\\\\","\\0","\\n","\\r","\Z","\'",'\"'];
return str_replace($_se, $_re, $tstr); return str_replace($_se, $_re, $str);
} }
public static function Get($tkey = "", $tdef = null) { public static function Get(string $key = "", mixed $def = null): mixed {
if (count(self::$__ahasil)==0) self::__data(); if (empty(self::$__ahasil)) self::__data();
if (isset(self::$__ahasil[$tkey]) && self::$__ahasil[$tkey]) return self::$__ahasil[$tkey]; if (isset(self::$__ahasil[$key])) return self::$__ahasil[$key];
return $tdef; return $def;
} }
public static function Get_Text($tkey = "", $tdef = null) { public static function Int(string $key = "", int $def = 0): int {
if (count(self::$__ahasil)==0) self::__data(); if (empty(self::$__ahasil)) self::__data();
$_str = (isset(self::$__ahasil[$tkey])) ? self::$__ahasil[$tkey] : $tdef; if (isset(self::$__ahasil[$key])) return (int) self::$__ahasil[$key];
$_str = str_replace("\\r", "", $_str); return $def;
$_str = str_replace("\\n", " ", $_str);
$_str = str_replace("\\t", " ", $_str);
return (trim($_str));
} }
public static function Get_Array() { public static function Float(string $key = "", float $def = 0): float {
if (empty(self::$__ahasil)) self::__data();
if (isset(self::$__ahasil[$key])) return (float) self::$__ahasil[$key];
return $def;
}
public static function Get_All(): array {
if (count(self::$__ahasil)==0) self::__data(); if (count(self::$__ahasil)==0) self::__data();
return self::$__ahasil; return self::$__ahasil;
} }
public static function Get_ArrayCBOX() {//delete
return [];
}
} }
?> ?>
-55
View File
@@ -1,55 +0,0 @@
<?php
/*******************************************************************
Module : /Web/Register.php
Desc. : Class Registrasi Aplikasi
Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : November 23th, 2011.
Last Modified : August 11th, 2026.
(c) 2011 - 2026, 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/
//OK
declare(strict_types=1);
namespace siaupheng\webapp\Web;
use flight\Engine;
use flight\database\SimplePdo;
final class Register {
protected Engine $app;
protected SimplePdo $db;
public function __construct(?Engine $app = null){
$this->app = (empty($app) === true) ? \Flight::app() : $app;
$this->db = $this->app->db();
}
public static function reg_id() {
$webapp = (array)\Flight::app()->session()->get('__WEB_APP');
$id = (string)$webapp['REG_ID'] ?? '';
return (substr($id,0,3).".".substr($id,3,3).".".substr($id,6));
}
public static function reg_code() {
$webapp = (array)\Flight::app()->session()->get('__WEB_APP');
$id = (string)$webapp['REG_CODE'] ?? '';
return (substr($id,0,6)."-".substr($id,6,5)."-".substr($id,11,5)."-".substr($id,16,5)."-".substr($id,21,5)."-".substr($id,26,6));
}
public static function reg_check() {
$webapp = (array)\Flight::app()->session()->get('__WEB_APP');
return (bool)(isset($webapp['REG_STATUS']) && $webapp['REG_STATUS']) ?? false;
}
public function reg_status(array $apps) {
$kode_db = (bool)$this->db->fetchField('SELECT UPPER(CONVERT(MD5(CONCAT(REG_YEAR,CONVERT(MD5(CONCAT(REG_ID,REG_NAME)) using latin1),REG_ID,REG_NAME)) using latin1))=REG_CODE FROM app_config WHERE REG_ID=?', [$apps['APP_CODE'] ?? '']);
return [ 'REG_STATUS' => $kode_db === true ];
}
}
?>
+37 -17
View File
@@ -3,22 +3,31 @@
/******************************************************************* /*******************************************************************
Module : /Web/Table.php Module : /Web/Table.php
Desc. : HTML Table (jQuery & Bootstrap)
Created By : Siau Pheng (siaupheng@3fonia.com). Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : January 16th, 2008. Created Date : January 16th, 2008.
Last Modified : August 16th, 2026. Last Modified : September 16th, 2026.
(c) 2008 - 2026, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
//OK
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\Web;
use flight\Engine; use \flight\Engine;
use flight\database\SimplePdo; use \flight\database\SimplePdo;
/**
* Class Table
*
* Pembuatan DataTable (jQuery & Bootstrap).
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <siaupheng@3fonia.com>
*
* @package Webapp\Web
*/
final class Table { final class Table {
private $__ttable = ""; private $__ttable = "";
@@ -46,9 +55,12 @@ final class Table {
protected SimplePdo $db; protected SimplePdo $db;
protected array $data; protected array $data;
public function __construct(?Engine $app = null) { /**
$this->app = (empty($app) === true) ? \Flight::app() : $app; * @param Engine $app
$this->db = $this->app->db(); */
public function __construct(Engine $app) {
$this->app = $app;
$this->db = $app->db();
$this->data = $this->app->request()->data->getData(); $this->data = $this->app->request()->data->getData();
} }
@@ -82,7 +94,7 @@ final class Table {
} }
public function set_row_color(string $field = "", string $val = "", string $css = ""): void { public function set_row_color(string $field = "", string $val = "", string $css = ""): void {
if (epmty($field)) return; if (empty($field)) return;
$this->__arowcol[] = [ 'F' => $field, 'V' => $val, 'C' => $css ]; $this->__arowcol[] = [ 'F' => $field, 'V' => $val, 'C' => $css ];
} }
@@ -132,7 +144,7 @@ final class Table {
$this->__bnobutton = true; $this->__bnobutton = true;
} }
public function add_field(string $field = "", string $header = "", string $style = "l", string $format = "", string $footer = "", string $otype = ""): void { public function add_field(string $field = "", string $header = "", string $style = "l", mixed $format = "", string $footer = "", string $otype = ""): void {
$this->__afield[] = [ 'a' => $field, 'h' => $header, 's' => $style, 'f' => $format, 't' => $footer, 'o' => $otype ]; $this->__afield[] = [ 'a' => $field, 'h' => $header, 's' => $style, 'f' => $format, 't' => $footer, 'o' => $otype ];
} }
@@ -150,7 +162,7 @@ final class Table {
$row = $this->db->fetchRow("SELECT ".$this->__tfooter." FROM ".$this->__ttable.$this->__twhere, $this->__aparam); $row = $this->db->fetchRow("SELECT ".$this->__tfooter." FROM ".$this->__ttable.$this->__twhere, $this->__aparam);
foreach ($this->__afield as $col) { foreach ($this->__afield as $col) {
$tval = $row[$col['a']] ?? ''; $tval = $row[$col['a']] ?? '';
if (is_set($col['t']) && !empty($col['t'])) { if (isset($col['t']) && !empty($col['t'])) {
if (is_array($col['t'])) { if (is_array($col['t'])) {
$tval2 = $col['t'][$tval] ?? ''; $tval2 = $col['t'][$tval] ?? '';
} else if (function_exists($col['t'])) { } else if (function_exists($col['t'])) {
@@ -174,8 +186,7 @@ final class Table {
// adding all option // adding all option
foreach ($this->__aoption as $opsi) { foreach ($this->__aoption as $opsi) {
if (in_array($opsi['type'], ["button","button-dt"])) { if (in_array($opsi['type'], ["button","button-dt"])) {
if (!isset($opsi['field'])) continue; if (!empty($opsi['field']) && ((string) $row[$opsi['field']] !== (string) $opsi['value'])) {} else {
if (!empty($opsi['field']) && ($row[$opsi['field']] !== (!empty($opsi['value']) ? $opsi['value'] : (string)$this->app->session()->get('__WEB_USER')['UNIX']))) {} else {
$aoption['button'][] = [ $opsi['icon'], (empty($opsi['field_css'])?'':$opsi['avalue_css'][$row[$opsi['field_css']]]??'') ]; $aoption['button'][] = [ $opsi['icon'], (empty($opsi['field_css'])?'':$opsi['avalue_css'][$row[$opsi['field_css']]]??'') ];
} }
} }
@@ -197,6 +208,11 @@ final class Table {
$tval = $row[$col['a']]; $tval = $row[$col['a']];
if (is_array($col['f'])) { if (is_array($col['f'])) {
$tval = $col['f'][$tval]; $tval = $col['f'][$tval];
} else if (!is_null($col['f']) && str_contains($col['f'], '::')) {
$tclass = explode("::", $col['f']);
if (class_exists($tclass[0]) && method_exists($tclass[0], $tclass[1])) {
$tval = call_user_func($col['f'], $tval);
}
} else if (!is_null($col['f']) && function_exists($col['f'])) { } else if (!is_null($col['f']) && function_exists($col['f'])) {
$tval = call_user_func($col['f'], $tval); $tval = call_user_func($col['f'], $tval);
} else if (is_string($tval)) { } else if (is_string($tval)) {
@@ -258,8 +274,7 @@ final class Table {
// adding all option // adding all option
foreach ($this->__aoption as $opsi) { foreach ($this->__aoption as $opsi) {
if (in_array($opsi['type'], ["button","button-dt"])) { if (in_array($opsi['type'], ["button","button-dt"])) {
if (!isset($opsi['field'])) continue; if (!empty($opsi['field']) && ((string) $row[$opsi['field']] !== (string) $opsi['value'])) {} else {
if (!empty($opsi['field']) && ($row[$opsi['field']] !== (!empty($opsi['value']) ? $opsi['value'] : $this->app->session()->get('__WEB_USER')['UNIX']))) {} else {
$aoption['button'][] = [ $opsi['icon'], (empty($opsi['field_css'])?'':$opsi['avalue_css'][$row[$opsi['field_css']]]??'') ]; $aoption['button'][] = [ $opsi['icon'], (empty($opsi['field_css'])?'':$opsi['avalue_css'][$row[$opsi['field_css']]]??'') ];
} }
} }
@@ -281,6 +296,11 @@ final class Table {
$tval = $row[$col['a']]; $tval = $row[$col['a']];
if (is_array($col['f'])) { if (is_array($col['f'])) {
$tval = $col['f'][$tval]; $tval = $col['f'][$tval];
} else if (!is_null($col['f']) && str_contains($col['f'], '::')) {
$tclass = explode("::", $col['f']);
if (class_exists($tclass[0]) && method_exists($tclass[0], $tclass[1])) {
$tval = call_user_func($col['f'], $tval);
}
} else if (!is_null($col['f']) && function_exists($col['f'])) { } else if (!is_null($col['f']) && function_exists($col['f'])) {
$tval = call_user_func($col['f'], $tval); $tval = call_user_func($col['f'], $tval);
} else if (is_string($tval)) { } else if (is_string($tval)) {
+21 -11
View File
@@ -3,28 +3,38 @@
/******************************************************************* /*******************************************************************
Module : /Web/Terbilang.php Module : /Web/Terbilang.php
Desc. : Class Format Angka & Terbilang
Created By : Siau Pheng (siaupheng@3fonia.com). Created By : Siau Pheng (siaupheng@3fonia.com).
Created Date : February 12th, 2009. Created Date : February 12th, 2009.
Last Modified : May 6th, 2021. Last Modified : August 22nd, 2026.
(c) 2009 - 2021, 3FONIA Software; WWW.3FONIA.COM. (c) 3FONIA Software; WWW.3FONIA.COM.
*******************************************************************/ *******************************************************************/
declare(strict_types=1); declare(strict_types=1);
namespace siaupheng\webapp\Web; namespace Webapp\Web;
/**
* Class Terbilang
*
* Pembuatan text terbilang.
*
* @license MIT, https://www.3fonia.com
* @copyright Copyright (c) 2008-2026, Siau Pheng <siaupheng@3fonia.com>
*
* @package Webapp\Web
*/
final class Terbilang { final class Terbilang {
private $numb = array("","Satu","Dua","Tiga","Empat","Lima","Enam","Tujuh","Delapan","Sembilan"); private $numb = ["","Satu","Dua","Tiga","Empat","Lima","Enam","Tujuh","Delapan","Sembilan"];
public function __construct() {} public function __construct() {}
public function Terbilang($nilai=0, $muang="Rupiah") { public function Terbilang(float $nilai=0, string $muang="Rupiah"): string {
$number = 0; $number = 0;
$hasil = ""; $hasil = "";
$int = trim($nilai); $int = (string)$nilai;
if (is_int(strpos($int,"-"))) { if (is_int(strpos($int,"-"))) {
$number = substr($int,strpos($int,"-")+1,strlen($int)); $number = substr($int,strpos($int,"-")+1,strlen($int));
$hasil = "Minus"; $hasil = "Minus";
@@ -36,11 +46,11 @@ final class Terbilang {
$xpos = strpos($number,"."); $xpos = strpos($number,".");
if (is_int($xpos)) { if (is_int($xpos)) {
# round : Untuk pembulatan angka desimal, strlen : untuk menghitung jumlah karakter # round : Untuk pembulatan angka desimal, strlen : untuk menghitung jumlah karakter
$pecahan = round(substr($number,$xpos,strlen($number)),2); $pecahan = round((float)substr($number,$xpos,strlen($number)),2);
$last = substr($number,0,$xpos); $last = substr($number,0,$xpos);
} else { } else {
$pecahan = ""; $pecahan = "";
$last = $number; $last = (float)$number;
} }
if ($last==0 || $number==0) { if ($last==0 || $number==0) {
return $hasil."NOL ".$muang; return $hasil."NOL ".$muang;
@@ -71,11 +81,11 @@ final class Terbilang {
return $hasil . $kata; return $hasil . $kata;
} }
private function mod($a, $b) { private function mod(float $a, float $b): float {
return $a-$b*floor($a/$b); return $a-$b*floor($a/$b);
} }
private function tigadigit($amount, $suffix="") { private function tigadigit(float $amount, string $suffix=""): string {
$last = (int)$amount; $last = (int)$amount;
$kata = ""; $kata = "";
if ($last == 1 && $suffix=="Ribu") { if ($last == 1 && $suffix=="Ribu") {