Compare commits

...

22 Commits

Author SHA1 Message Date
Clemens Schwaighofer
4b3fbaa309 Updates based on latest phpstan run 2022-09-02 17:00:22 +09:00
Clemens Schwaighofer
1a6c65df0e Minor test updates, comment typo updates, DB_CONFIG_SET for default
$DB_CONFIG_SET is now default current selcted db config instead of
$DB_CONFIG so to not overwrite the array itself
2022-08-05 12:43:57 +09:00
Clemens Schwaighofer
24f553a17e Update comments for split element counter 2022-07-29 13:20:54 +09:00
Clemens Schwaighofer
9a3ea2f7db update strings class with split counter method 2022-07-29 13:19:45 +09:00
Clemens Schwaighofer
bcdb877d90 Phan/phpstan fixes 2022-07-29 11:06:53 +09:00
Clemens Schwaighofer
6d0e528c38 Combined\Datetime date/number to weekday conversion
Convert functions for date or weekday number to weekday name or date to
weekday number
2022-07-29 11:02:25 +09:00
Clemens Schwaighofer
7e6474195b String split format fix for non ascii characters
Currently just abort and return string as is
2022-07-29 10:38:32 +09:00
Clemens Schwaighofer
1795d3ba6c String convert class added
Currently with one method: splitFormatString

Converts a string to a string with separater characters based on a
format string
2022-07-29 07:00:02 +09:00
Clemens Schwaighofer
e1340acf55 Class header info string fix 2022-07-29 06:59:33 +09:00
Clemens Schwaighofer
b5ead3e266 Minor code block comment clean ups 2022-07-15 17:42:38 +09:00
Clemens Schwaighofer
f5daaca598 Fixes for Create\Email docstrings 2022-07-08 17:25:45 +09:00
Clemens Schwaighofer
6b4f310cd2 Email\Sending add dedicated kv folding flag
kv folding is now done via flag and only if an encoded is detected that
is japanese or UTF-8
2022-07-04 15:45:51 +09:00
Clemens Schwaighofer
7b5bddb529 Create\Email fixes
Remove not needed reaplce content count for first global replace
Move logger out of test only and log if a logger class is attached
2022-07-01 08:57:14 +09:00
Clemens Schwaighofer
0a6fdf1248 Use logged encoding from JSON debug block for non UTF-8 tests 2022-06-30 18:18:17 +09:00
Clemens Schwaighofer
3220180d58 Bug fix in Email for encoding subject/body with empty replace
Also store encoding in json log if test and debug print is given
2022-06-30 18:16:28 +09:00
Clemens Schwaighofer
8c8f14ec74 Fix logging per run to be setable not only on start
Move per run set into method.
Add set/get method and add set method override (set new) flag

Update phpUnit testing and move providers to test methods
2022-06-30 18:15:36 +09:00
Clemens Schwaighofer
643991c3fd Update Debug\Support, add Create\Email
update debug support to add html escape for html strings on request.
Default is keep as is. debugString gets new third parameter for this as
bool flag.

Add Create\Email to send basic text emails to several too addresses.
Content replace in subject and body is possible with {} entries.
Default encoding is UTF-8 but others can be set and content will be
converted to this.
The dynamic replace works on all data or can be set per receiver.
2022-06-28 17:29:31 +09:00
Clemens Schwaighofer
c81c46d426 Move read_env_file.php to deprecated folder 2022-06-23 14:46:19 +09:00
Clemens Schwaighofer
d97b173ee7 ACL\Login move public var to private: login
the former public var $login is now private and if it is set can be
checked with loginActionSet (true if login_login was in _POST as login
action.

Some info update for phpUnit ACL\Login test file
2022-06-23 09:12:46 +09:00
Clemens Schwaighofer
b61152f10e Skipped/Incomplete tests update 2022-06-23 07:09:19 +09:00
Clemens Schwaighofer
0c68ebe652 Login\ACL revalidate flow fixes
- DB function had wrong column name
- Queries in ACL\Login had wrong column name
- Renamed from login_user_id_last_login to login_user_id_last_revalidate
  to make it more clear what this column is
- add edit_user admin page output for this column
- add phpUnit test case for revalidate is needed and login with next
  loginUserId is ok again
2022-06-23 06:50:07 +09:00
Clemens Schwaighofer
31d0cdb8ad Fix revalidate after flow in ACL\Login
After revalidate time was reached, it was never reset because it used
the original loginUserId set date.
A new column has been added that gets reset every time the user logs in
with username and password if a loginUserId is set in the database
2022-06-22 19:38:03 +09:00
49 changed files with 2748 additions and 512 deletions

View File

@@ -270,8 +270,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;
@@ -595,12 +597,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the login uid, at least 32 chars
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated login id after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for login user id, but still allow normal login
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
@@ -629,11 +632,12 @@ COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'login id was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'login id is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'login id is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for login id, user can still login normal';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
-- END: table/edit_user.sql
-- START: table/edit_log.sql

View File

@@ -15,8 +15,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;

View File

@@ -55,12 +55,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the login uid, at least 32 chars
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated login id after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for login user id, but still allow normal login
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
@@ -89,9 +90,10 @@ COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'login id was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'login id is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'login id is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for login id, user can still login normal';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';

View File

@@ -0,0 +1,81 @@
--
SELECT
eu.cuid, eu.username,
eu.lock_until, eu.lock_after,
CASE WHEN (
(eu.lock_until IS NULL
OR (eu.lock_until IS NOT NULL AND NOW() >= eu.lock_until))
AND (eu.lock_after IS NULL
OR (eu.lock_after IS NOT NULL AND NOW() <= eu.lock_after))
) THEN 0::INT ELSE 1::INT END locked_period
FROM edit_user eu
WHERE eu.username = 'empty';
UPDATE edit_user SET
lock_until = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_after = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_until = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
lock_after = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET lock_until = NULL, lock_after = NULL WHERE username = 'empty';
--
SELECT
eu.cuid, eu.username,
eu.login_user_id, login_user_id_set_date, eu.login_user_id_last_revalidate,
(eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE AS reval_date, NOW()::DATE,
eu.login_user_id_valid_from, eu.login_user_id_valid_until,
eu.login_user_id_revalidate_after,
CASE WHEN (
(eu.login_user_id_valid_from IS NULL
OR (eu.login_user_id_valid_from IS NOT NULL AND NOW() >= eu.login_user_id_valid_from))
AND (eu.login_user_id_valid_until IS NULL
OR (eu.login_user_id_valid_until IS NOT NULL AND NOW() <= eu.login_user_id_valid_until))
) THEN 1::INT ELSE 0::INT END AS login_user_id_valid_date,
CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL
AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL
AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE <= NOW()::DATE
THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate
FROM edit_user eu
WHERE eu.username = 'empty';
-- init
UPDATE edit_user SET login_user_id = random_string(5) WHERE username = 'empty';
-- outside valid
UPDATE edit_user SET
login_user_id_valid_from = NOW() - '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
login_user_id_valid_until = NOW() + '1 day'::interval
WHERE username = 'empty';
-- inside valid
UPDATE edit_user SET
login_user_id_valid_from = NOW() + '1 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET
login_user_id_valid_until = NOW() - '1 day'::interval
WHERE username = 'empty';
-- revalidate must
UPDATE edit_user SET
login_user_id_last_revalidate = NOW() - '1 day'::interval,
login_user_id_revalidate_after = '1 day'::interval
WHERE username = 'empty';
-- revalidate not yet
UPDATE edit_user SET
login_user_id_last_revalidate = NOW(),
login_user_id_revalidate_after = '6 day'::interval
WHERE username = 'empty';
UPDATE edit_user SET login_user_id_set_date = NULL, login_user_id_last_revalidate = NULL, login_user_id_valid_from = NULL, login_user_id_valid_until = NULL, login_user_id_revalidate_after = NULL WHERE username = 'empty';

View File

@@ -1,11 +1,14 @@
-- 2022/6/17 update edit_user with login uid
-- !!! COPY TABLE ARRAY FOLDER !!!
-- the login uid, at least 32 chars
ALTER TABLE edit_user ADD login_user_id VARCHAR UNIQUE;
-- CREATE UNIQUE INDEX edit_user_login_user_id_key ON edit_user (login_user_id) WHERE login_user_id IS NOT NULL;
-- ALTER TABLE edit_user ADD CONSTRAINT edit_user_login_user_id_key UNIQUE (login_user_id);
-- when above uid was set
ALTER TABLE edit_user ADD login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE edit_user ADD login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE;
-- if set, from/until when the above uid is valid
ALTER TABLE edit_user ADD login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE edit_user ADD login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE;
@@ -33,8 +36,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;

View File

@@ -25,7 +25,7 @@ declare(strict_types=1);
* 1 for file loadable, but no data inside
* 2 for file not readable
* 3 for file not found
* @deprecated V6 Use \CoreLibs\Get\ReadEnvFile::readEnvFile()
* @deprecated V6 Use \CoreLibs\Get\DotEnv::readEnvFile()
*/
function readEnvFile(string $path = __DIR__, string $env_file = '.env'): int
{

View File

@@ -157,17 +157,18 @@ final class CoreLibsACLLoginTest extends TestCase
*/
public function loginProvider(): array
{
// 0: mock settings/override flag settings
// 2: get array IN
// 1: post array IN
// login_login, login_username, login_password, login_logout
// change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm
// 2: override session set
// 3: expected error code, 0 for all ok, 3000 for login page view
// note that 1000 (no db), 2000 (no session) must be tested too
// 4: expected return array, eg login_error code, or other info data to match
return [
// 0[mock] : mock settings/override flag settings
// 1[get] : get array IN
// 2[post] : post array IN
// login_login, login_username, login_password, login_logout
// change_password, pw_username, pw_old_password, pw_new_password,
// pw_new_password_confirm
// 3[session]: override session set
// 4[error] : expected error code, 0 for all ok, 3000 for login page view
// note that 1000 (no db), 2000 (no session) must be tested too
// 5[return] : expected return array, eg login_error code,
// or other info data to match
$tests = [
'load, no login' => [
// error code, only for exceptions
[
@@ -290,7 +291,7 @@ final class CoreLibsACLLoginTest extends TestCase
],
],
// login: all missing
'login: all missing' => [
'login: failed: all missing' => [
[
'page_name' => 'edit_users.php',
],
@@ -311,7 +312,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: missing username
'login: missing username' => [
'login: failed: missing username' => [
[
'page_name' => 'edit_users.php',
],
@@ -332,7 +333,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: missing password
'login: missing password' => [
'login: failed: missing password' => [
[
'page_name' => 'edit_users.php',
],
@@ -353,7 +354,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: user not found
'login: user not found' => [
'login: failed: user not found' => [
[
'page_name' => 'edit_users.php',
],
@@ -377,7 +378,7 @@ final class CoreLibsACLLoginTest extends TestCase
// 9999: not valid password encoding
// 1013: normal password failed
// 1012: plain password check failed
'login: invalid password' => [
'login: failed: invalid password' => [
[
'page_name' => 'edit_users.php',
],
@@ -399,7 +400,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: ok (but deleted)
'login: ok, but deleted' => [
'login: ok -> failed: but deleted' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -424,7 +425,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: ok (but not enabled)
'login: ok, but not enabled' => [
'login: ok -> failed: but not enabled' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -449,7 +450,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: ok (but locked)
'login: ok, but locked' => [
'login: ok -> failed: but locked' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -474,7 +475,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login: make user get locked strict
'login: ok, get locked, strict' => [
'login: ok -> failed: get locked, strict' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -498,7 +499,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but in locked period (until)
'login: ok, but locked period (until:on)' => [
'login: ok -> failed: but locked period (until:on)' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -552,7 +553,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but in locked period (after)
'login: ok, but locked period (after:on)' => [
'login: ok -> failed: but locked period (after:on)' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -577,7 +578,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but in locked period (until, after)
'login: ok, but locked period (until:on, after:on)' => [
'login: ok -> failed:, but locked period (until:on, after:on)' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -603,7 +604,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// login ok, but login user id locked
'login: ok, but login user id locked' => [
'login: ok -> failed:, but loginUserId locked' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -830,7 +831,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with revalidate on/off
'login: ok, but revalidate trigger, _GET loginUserId' => [
'login: ok -> failed:, but revalidate trigger, _GET loginUserId' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -886,7 +887,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with active time from only
'login: ok, _GET loginUserId, but outside valid (from:on) ' => [
'login: ok -> failed:, _GET loginUserId, but outside valid (from:on) ' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -942,7 +943,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with active time until only
'login: ok, _GET loginUserId, but outside valid (until:on) ' => [
'login: ok -> failed:, _GET loginUserId, but outside valid (until:on) ' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -968,7 +969,7 @@ final class CoreLibsACLLoginTest extends TestCase
]
],
// loginUserId check with active time from/until
'login: ok, _GET loginUserId, but outside valid (from:on,until:on) ' => [
'login: ok -> failed:, _GET loginUserId, but outside valid (from:on,until:on) ' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
@@ -994,11 +995,53 @@ final class CoreLibsACLLoginTest extends TestCase
. 'Login Failed - Login User ID is outside valid date range'
]
],
// TODO: Test that if we have n day check with login, that after login we can use parameter login again
'login: ok -> failed -> ok:, _GET loginUserId, but must revalidate, normal login, _GET loginUserId' => [
[
'page_name' => 'edit_users.php',
'edit_access_id' => 1,
'edit_access_uid' => 'AdminAccess',
'edit_access_data' => 'test',
'base_access' => 'list',
'page_access' => 'list',
'test_login_user_id_revalidate_reset' => true,
'test_login_user_id' => true,
'test_username' => 'admin',
'loginUserId' => '1234567890ABCDEFG',
// this error is thrown on first login round
'login_error' => 1101,
// get post as set sub arrays
'get' => [
'loginUserId' => '1234567890ABCDEFG',
],
'post' => [
'login_login' => 'Login',
'login_username' => 'admin',
'login_password' => 'admin',
],
],
// all empty get, post, session
[],
[],
[],
0,
[
'login_error' => 0,
'admin_flag' => true,
'check_access' => true,
'check_access_id' => 1,
'check_access_data' => 'value',
'base_access' => true,
'page_access' => true,
]
]
//
// other:
// login check edit access id of ID not null and not in array
// login OK, but during action user gets disabled/deleted/etc
];
return $tests;
}
/**
@@ -1196,7 +1239,6 @@ final class CoreLibsACLLoginTest extends TestCase
if (!empty($mock_settings['test_login_user_id'])) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_set_date = NOW(), "
. "login_user_id = "
. self::$db->dbEscapeLiteral($mock_settings['loginUserId'])
. " "
@@ -1207,10 +1249,10 @@ final class CoreLibsACLLoginTest extends TestCase
if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
$q_sub = '';
if ($mock_settings['test_login_user_id_revalidate_after'] == 'on') {
$q_sub = "login_user_id_set_date = NOW() - '1 day'::interval, "
$q_sub = "login_user_id_last_revalidate = NOW() - '1 day'::interval, "
. "login_user_id_revalidate_after = '1 day'::interval ";
} else {
$q_sub = "login_user_id_set_date = NOW(), "
$q_sub = "login_user_id_last_revalidate = NOW(), "
. "login_user_id_revalidate_after = '6 day'::interval ";
}
self::$db->dbExec(
@@ -1220,6 +1262,50 @@ final class CoreLibsACLLoginTest extends TestCase
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
if (!empty($mock_settings['test_login_user_id_revalidate_reset'])) {
// init dates data for revalidate frame,
// set to last revalidate 3 days ago and set revalidate frame to
// three days
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_last_revalidate = NOW() - '3 day'::interval, "
. "login_user_id_revalidate_after = '3 day'::interval "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
$_GET = $mock_settings['get'];
// login with loginUserId -> fail
try {
$login_mock->loginMainCall();
} catch (\Exception $e) {
$this->assertEquals(
$mock_settings['login_error'],
$login_mock->loginGetLastErrorCode(),
'loginUserId reset 1: Assert first loginUserId run failes'
);
}
$_GET = [];
// login with username and password -> reset -> ok
// set _POST data
$_POST = $mock_settings['post'];
try {
$login_mock->loginMainCall();
$this->assertEquals(
0,
$login_mock->loginGetLastErrorCode(),
'loginUserId reset 2: Assert username/password login is successful'
);
} catch (\Exception $e) {
// if we end up here we have an issue
$this->assertTrue(
false,
'loginUserId reset 2: FAILED successful login'
);
}
$_POST = [];
// logut and run normal login with loginUserId
$_GET = $mock_settings['get'];
}
if (
!empty($mock_settings['test_login_user_id_valid_from']) ||
!empty($mock_settings['test_login_user_id_valid_until'])
@@ -1461,6 +1547,14 @@ final class CoreLibsACLLoginTest extends TestCase
);
}
// if _POST login set check this is matching
if (!empty($post['login_login'])) {
$this->assertTrue(
$login_mock->loginActionRun(),
'Assert that post login_login was pressed'
);
}
// always check, even on error or not set
if (!$login_mock->loginGetLoginUserIdUnclean()) {
$this->assertEquals(
@@ -1540,36 +1634,38 @@ final class CoreLibsACLLoginTest extends TestCase
. self::$db->dbEscapeLiteral($post['login_username'])
);
}
// if (!empty($mock_settings['test_login_user_id'])) {
// self::$db->dbExec(
// "UPDATE edit_user SET "
// . "login_user_id = NULL, "
// . "login_user_id_set_date = NULL "
// . "WHERE LOWER(username) = "
// . self::$db->dbEscapeLiteral($mock_settings['test_username'])
// );
// }
// if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
// self::$db->dbExec(
// "UPDATE edit_user SET "
// . "login_user_id_set_date = NULL, "
// . "login_user_id_revalidate_after = NULL "
// . "WHERE LOWER(username) = "
// . self::$db->dbEscapeLiteral($mock_settings['test_username'])
// );
// }
// if (
// !empty($mock_settings['test_login_user_id_valid_from']) ||
// !empty($mock_settings['test_login_user_id_valid_until'])
// ) {
// self::$db->dbExec(
// "UPDATE edit_user SET "
// . "login_user_id_valid_from = NULL, "
// . "login_user_id_valid_until = NULL "
// . "WHERE LOWER(username) = "
// . self::$db->dbEscapeLiteral($mock_settings['test_username'])
// );
// }
if (!empty($mock_settings['test_login_user_id'])) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id = NULL, "
// below to rows are automatcially reset
. "login_user_id_set_date = NULL, "
. "login_user_id_last_revalidate = NULL "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
if (!empty($mock_settings['test_login_user_id_revalidate_after'])) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_last_revalidate = NULL, "
. "login_user_id_revalidate_after = NULL "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
if (
!empty($mock_settings['test_login_user_id_valid_from']) ||
!empty($mock_settings['test_login_user_id_valid_until'])
) {
self::$db->dbExec(
"UPDATE edit_user SET "
. "login_user_id_valid_from = NULL, "
. "login_user_id_valid_until = NULL "
. "WHERE LOWER(username) = "
. self::$db->dbEscapeLiteral($mock_settings['test_username'])
);
}
}
// - loginGetAclList (null, invalid,)

View File

@@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsCombinedDateTimeTest extends TestCase
{
/**
* timestamps
*
@@ -618,13 +617,169 @@ final class CoreLibsCombinedDateTimeTest extends TestCase
* @param array $expected
* @return void
*/
public function testCalcDaysInterval(string $input_a, string $input_b, bool $flag, $expected): void
{
public function testCalcDaysInterval(
string $input_a,
string $input_b,
bool $flag,
$expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::calcDaysInterval($input_a, $input_b, $flag)
);
}
/**
* Undocumented function
*
* @return array
*/
public function weekdayNumberProvider(): array
{
return [
'0 invalid' => [0, null, 'Inv',],
'0 invalid long' => [0, true, 'Invalid',],
'1 short' => [1, null, 'Mon',],
'1 long' => [1, true, 'Monday',],
'2 short' => [2, null, 'Tue',],
'2 long' => [2, true, 'Tuesday',],
'3 short' => [3, null, 'Wed',],
'3 long' => [3, true, 'Wednesday',],
'4 short' => [4, null, 'Thu',],
'4 long' => [4, true, 'Thursday',],
'5 short' => [5, null, 'Fri',],
'5 long' => [5, true, 'Friday',],
'6 short' => [6, null, 'Sat',],
'6 long' => [6, true, 'Saturday',],
'7 short' => [7, null, 'Sun',],
'7 long' => [7, true, 'Sunday',],
'8 invalid' => [8, null, 'Inv',],
'8 invalid long' => [8, true, 'Invalid',],
];
}
/**
* int weekday number to string weekday
*
* @covers ::setWeekdayNameFromIsoDow
* @dataProvider weekdayNumberProvider
* @testdox weekdayListProvider $input (short $flag) will be $expected [$_dataName]
*
* @param int $input
* @param bool|null $flag
* @param string $expected
* @return void
*/
public function testSetWeekdayNameFromIsoDow(
int $input,
?bool $flag,
string $expected
): void {
if ($flag === null) {
$output = \CoreLibs\Combined\DateTime::setWeekdayNameFromIsoDow($input);
} else {
$output = \CoreLibs\Combined\DateTime::setWeekdayNameFromIsoDow($input, $flag);
}
$this->assertEquals(
$expected,
$output
);
}
/**
* Undocumented function
*
* @return array
*/
public function weekdayDateProvider(): array
{
return [
'invalid date' => ['2022-02-31', -1],
'1: monday' => ['2022-07-25', 1],
'2: tuesday' => ['2022-07-26', 2],
'3: wednesday' => ['2022-07-27', 3],
'4: thursday' => ['2022-07-28', 4],
'5: friday' => ['2022-07-29', 5],
'6: saturday' => ['2022-07-30', 6],
'7: sunday' => ['2022-07-31', 7],
];
}
/**
* date to weekday number
*
* @covers ::setWeekdayNumberFromDate
* @dataProvider weekdayDateProvider
* @testdox setWeekdayNumberFromDate $input will be $expected [$_dataName]
*
* @param string $input
* @param int $expected
* @return void
*/
public function testSetWeekdayNumberFromDate(
string $input,
int $expected
): void {
$this->assertEquals(
$expected,
\CoreLibs\Combined\DateTime::setWeekdayNumberFromDate($input)
);
}
/**
* Undocumented function
*
* @return array
*/
public function weekdayDateNameProvider(): array
{
return [
'invalid date short' => ['2022-02-31', null, 'Inv'],
'invalid date long' => ['2022-02-31', true, 'Invalid'],
'Mon short' => ['2022-07-25', null, 'Mon'],
'Monday long' => ['2022-07-25', true, 'Monday'],
'Tue short' => ['2022-07-26', null, 'Tue'],
'Tuesday long' => ['2022-07-26', true, 'Tuesday'],
'Wed short' => ['2022-07-27', null, 'Wed'],
'Wednesday long' => ['2022-07-27', true, 'Wednesday'],
'Thu short' => ['2022-07-28', null, 'Thu'],
'Thursday long' => ['2022-07-28', true, 'Thursday'],
'Fri short' => ['2022-07-29', null, 'Fri'],
'Friday long' => ['2022-07-29', true, 'Friday'],
'Sat short' => ['2022-07-30', null, 'Sat'],
'Saturday long' => ['2022-07-30', true, 'Saturday'],
'Sun short' => ['2022-07-31', null, 'Sun'],
'Sunday long' => ['2022-07-31', true, 'Sunday'],
];
}
/**
* date to weekday name
*
* @covers ::setWeekdayNameFromDate
* @dataProvider weekdayDateNameProvider
* @testdox setWeekdayNameFromDate $input (short $flag) will be $expected [$_dataName]
*
* @param string $input
* @param bool|null $flag
* @param string $expected
* @return void
*/
public function testSetWeekdayNameFromDate(
string $input,
?bool $flag,
string $expected
): void {
if ($flag === null) {
$output = \CoreLibs\Combined\DateTime::setWeekdayNameFromDate($input);
} else {
$output = \CoreLibs\Combined\DateTime::setWeekdayNameFromDate($input, $flag);
}
$this->assertEquals(
$expected,
$output
);
}
}
// __END__

View File

@@ -0,0 +1,261 @@
<?php // phpcs:disable Generic.Files.LineLength
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Convert\Strings
* @coversDefaultClass \CoreLibs\Convert\Strings
* @testdox \CoreLibs\Convert\Strings method tests
*/
final class CoreLibsConvertStringsTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function splitFormatStringProvider(): array
{
// 0: input
// 1: format
// 2: split characters as string, null for default
// 3: expected
return [
'all empty string' => [
'',
'',
null,
''
],
'empty input string' => [
'',
'2-2',
null,
''
],
'empty format string string' => [
'1234',
'',
null,
'1234'
],
'string format match' => [
'1234',
'2-2',
null,
'12-34'
],
'string format trailing match' => [
'1234',
'2-2-',
null,
'12-34'
],
'string format leading match' => [
'1234',
'-2-2',
null,
'12-34'
],
'string format double inside match' => [
'1234',
'2--2',
null,
'12--34',
],
'string format short first' => [
'1',
'2-2',
null,
'1'
],
'string format match first' => [
'12',
'2-2',
null,
'12'
],
'string format short second' => [
'123',
'2-2',
null,
'12-3'
],
'string format too long' => [
'1234567',
'2-2',
null,
'12-34-567'
],
'string format invalid format string' => [
'1234',
'2_2',
null,
'1234'
],
'different split character' => [
'1234',
'2_2',
'_',
'12_34'
],
'mixed split characters' => [
'123456',
'2-2_2',
'-_',
'12-34_56'
],
'length mixed' => [
'ABCD12345568ABC13',
'2-4_5-2#4',
'-_#',
'AB-CD12_34556-8A#BC13'
],
'split with split chars in string' => [
'12-34',
'2-2',
null,
'12--3-4'
],
'mutltibyte string' => [
'あいうえ',
'2-2',
null,
'あいうえ'
],
'mutltibyte split string' => [
'1234',
'-',
null,
'1234'
],
];
}
/**
* split format string
*
* @covers ::splitFormatString
* @dataProvider splitFormatStringProvider
* @testdox splitFormatString $input with format $format and splitters $split_characters will be $expected [$_dataName]
*
* @param string $input
* @param string $format
* @param string|null $split_characters
* @param string $expected
* @return void
*/
public function testSplitFormatString(
string $input,
string $format,
?string $split_characters,
string $expected
): void {
if ($split_characters === null) {
$output = \CoreLibs\Convert\Strings::splitFormatString(
$input,
$format
);
} else {
$output = \CoreLibs\Convert\Strings::splitFormatString(
$input,
$format,
$split_characters
);
}
$this->assertEquals(
$expected,
$output
);
}
/**
* Undocumented function
*
* @return array
*/
public function countSplitPartsProvider(): array
{
return [
'0 elements' => [
'',
null,
0
],
'1 element' => [
'1',
null,
1,
],
'2 elements, trailing' => [
'1-2-',
null,
2
],
'2 elements, leading' => [
'-1-2',
null,
2
],
'2 elements, midde double' => [
'1--2',
null,
2
],
'4 elements' => [
'1-2-3-4',
null,
4
],
'3 elemenst, other splitter' => [
'2-3_3',
'-_',
3
],
'illegal splitter' => [
'あsdf',
null,
0
]
];
}
/**
* count split parts
*
* @covers ::countSplitParts
* @dataProvider countSplitPartsProvider
* @testdox countSplitParts $input with splitters $split_characters will be $expected [$_dataName]
*
* @param string $input
* @param string|null $split_characters
* @param int $expected
* @return void
*/
public function testCountSplitParts(
string $input,
?string $split_characters,
int $expected
): void {
if ($split_characters === null) {
$output = \CoreLibs\Convert\Strings::countSplitParts(
$input
);
} else {
$output = \CoreLibs\Convert\Strings::countSplitParts(
$input,
$split_characters
);
}
$this->assertEquals(
$expected,
$output
);
}
}
// __END__

View File

@@ -0,0 +1,692 @@
<?php
declare(strict_types=1);
namespace tests;
use PHPUnit\Framework\TestCase;
/**
* Test class for Create\Email
* @coversDefaultClass \CoreLibs\Create\Email
* @testdox \CoreLibs\Create\Email method tests
*/
final class CoreLibsCreateEmailTest extends TestCase
{
private static $log;
/**
* start DB conneciton, setup DB, etc
*
* @return void
*/
public static function setUpBeforeClass(): void
{
self::$log = new \CoreLibs\Debug\Logging([
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
'file_id' => 'CoreLibs-Create-Email-Test',
'debug_all' => true,
'echo_all' => false,
'print_all' => true,
]);
}
/**
* Undocumented function
*
* @return array
*/
public function encodeEmailNameProvider(): array
{
// 0: email
// 1: name
// 2: encoding
// 3: kv_folding
// 4: expected
return [
'all empty' => [
'',
null,
null,
null,
''
],
'email only' => [
'test@test.com',
null,
null,
null,
'test@test.com'
],
'email and name' => [
'test@test.com',
'Test Name',
null,
null,
'"Test Name" <test@test.com>'
],
'name in mime encoded, default UTF-8' => [
'test@test.com',
'日本語',
null,
null,
'"=?UTF-8?B?5pel5pys6Kqe?=" <test@test.com>'
],
'name in mime encoded with half width Katakana, default UTF-8' => [
'test@test.com',
'日本語カタカナパ',
null,
null,
'"=?UTF-8?B?5pel5pys6Kqe7722776A7722776F776K776f?=" <test@test.com>'
],
'name in mime encoded with half width Katakana, folding on, default UTF-8' => [
'test@test.com',
'日本語カタカナパ',
'UTF-8',
true,
'"=?UTF-8?B?5pel5pys6Kqe44Kr44K/44Kr44OK44OR?=" <test@test.com>'
],
'name in mime encoded, UTF-8 parameter' => [
'test@test.com',
'日本語',
'UTF-8',
null,
'"=?UTF-8?B?5pel5pys6Kqe?=" <test@test.com>'
],
// does internal UTF-8 to ISO-2022-JP convert
'encoding in ISO-2022-JP' => [
'test@test.com',
'日本語',
'ISO-2022-JP',
null,
'"=?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=" <test@test.com>'
],
'encoding with half width Katakana in ISO-2022-JP' => [
'test@test.com',
'日本語カタカナパ',
'ISO-2022-JP',
null,
'"=?ISO-2022-JP?B?GyRCRnxLXDhsGyhCPz8/Pz8/?=" <test@test.com>'
],
'encoding with half width Katakana, folding on in ISO-2022-JP' => [
'test@test.com',
'日本語カタカナパ',
'ISO-2022-JP',
true,
'"=?ISO-2022-JP?B?GyRCRnxLXDhsGyhCPz8/Pz8=?=" <test@test.com>'
]
];
}
/**
* Undocumented function
*
* @dataProvider encodeEmailNameProvider
* @testdox encode email $email, name $name, encoding $encoding will be $expected [$_dataName]
*
* @return void
*/
public function testEncodeEmailName(
string $email,
?string $name,
?string $encoding,
?bool $kv_folding,
string $expected
): void {
if ($name === null && $encoding === null && $kv_folding === null) {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email);
} elseif ($encoding === null && $kv_folding === null) {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name);
} elseif ($kv_folding === null) {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name, $encoding);
} else {
$encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name, $encoding, $kv_folding);
}
$this->assertEquals(
$expected,
$encoded_email
);
}
public function sendEmailProvider(): array
{
// 0: subject
// 1: body
// 2: from email
// 3: from name ('')
// 4: array for to email
// 5: replace content ([]/null)
// 6: encoding (UTF-8/null)
// 7: kv_folding
// 8: return status
// 9: expected content
return [
'all empty, fail -1' => [
'subject' => '',
'body' => '',
'from_email' => '',
'from_name' => '',
'to_email' => [],
'replace' => null,
'encoding' => null,
'kv_folding' => null,
'expected_status' => -1,
'expected_content' => [],
],
'missing to entry, fail -2' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [],
'replace' => null,
'encoding' => null,
'kv_folding' => null,
'expected_status' => -2,
'expected_content' => [],
],
'bad encoding, fail -3' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => ['to@test.com'],
'replace' => null,
'encoding' => 'IDONTEXISTENCODING',
'kv_folding' => null,
'expected_status' => -3,
'expected_content' => [],
],
'sending email 1' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT',
'body' => 'BODY',
]
],
],
'sending email 1, encoded' => [
'subject' => 'SUBJECT 日本語',
'body' => 'BODY 日本語',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6Kqe?=',
'body' => 'BODY 日本語',
]
],
],
'sending email 1, encoded, with half width katakanata' => [
'subject' => 'SUBJECT 日本語カタカナパ',
'body' => 'BODY 日本語',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => 'UTF-8',
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6Kqe7722776A7722776F776K776f?=',
'body' => 'BODY 日本語',
]
],
],
'sending email 1, encoded, with half width katakanata, folding on' => [
'subject' => 'SUBJECT 日本語カタカナパ',
'body' => 'BODY 日本語',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => 'UTF-8',
'kv_folding' => true,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6Kqe44Kr44K/44Kr44OK44OR?=',
'body' => 'BODY 日本語',
]
],
],
'sending email 1, encoded subject ISO-2022-JP' => [
'subject' => 'SUBJECT 日本語',
'body' => 'BODY 日本語',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => null,
'encoding' => 'ISO-2022-JP',
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=',
// body is stored as UTF-8 in log and here, so both must be translated
'body' => 'BODY 日本語',
]
],
],
'sending email 2' => [
'subject' => 'SUBJECT',
'body' => 'BODY',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'e1@test.com',
'e2@test.com'
],
'replace' => null,
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'e1@test.com',
'subject' => 'SUBJECT',
'body' => 'BODY',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'e2@test.com',
'subject' => 'SUBJECT',
'body' => 'BODY',
]
],
],
'sending email 1: dynamic' => [
'subject' => 'SUBJECT {FOO}',
'body' => 'BODY {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT foo',
'body' => 'BODY foo bar',
]
],
],
'sending email 1: dynamic encoded' => [
'subject' => 'SUBJECT 日本語 {FOO}',
'body' => 'BODY 日本語 {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
'test@test.com'
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbw==?=',
'body' => 'BODY 日本語 foo bar',
]
],
],
'sending email 1: dynamic, to override' => [
'subject' => 'SUBJECT {FOO}',
'body' => 'BODY {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 'test@test.com',
'replace' => [
'FOO' => 'foo to'
]
]
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT foo to',
'body' => 'BODY foo to bar',
]
],
],
'sending email 1: dynamic, to override encoded' => [
'subject' => 'SUBJECT 日本語 {FOO}',
'body' => 'BODY 日本語 {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 'test@test.com',
'replace' => [
'FOO' => 'foo to'
]
]
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 'test@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbyB0bw==?=',
'body' => 'BODY 日本語 foo to bar',
]
],
],
'sending email 3: dynamic, to mixed override' => [
'subject' => 'SUBJECT {FOO}',
'body' => 'BODY {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 't1@test.com',
'replace' => [
'FOO' => 'foo to 1'
]
],
[
'email' => 't2@test.com',
'replace' => [
'FOO' => 'foo to 2'
]
],
[
'email' => 't3@test.com',
],
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't1@test.com',
'subject' => 'SUBJECT foo to 1',
'body' => 'BODY foo to 1 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't2@test.com',
'subject' => 'SUBJECT foo to 2',
'body' => 'BODY foo to 2 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't3@test.com',
'subject' => 'SUBJECT foo',
'body' => 'BODY foo bar',
],
],
],
'sending email 3: dynamic, to mixed override encoded' => [
'subject' => 'SUBJECT 日本語 {FOO}',
'body' => 'BODY 日本語 {FOO} {VAR}',
'from_email' => 'test@test.com',
'from_name' => '',
'to_email' => [
[
'email' => 't1@test.com',
'replace' => [
'FOO' => 'foo to 1'
]
],
[
'email' => 't2@test.com',
'replace' => [
'FOO' => 'foo to 2'
]
],
[
'email' => 't3@test.com',
],
],
'replace' => [
'FOO' => 'foo',
'VAR' => 'bar',
],
'encoding' => null,
'kv_folding' => null,
'expected_status' => 2,
'expected_content' => [
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't1@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbyB0byAx?=',
'body' => 'BODY 日本語 foo to 1 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't2@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbyB0byAy?=',
'body' => 'BODY 日本語 foo to 2 bar',
],
[
'header' => [
'From' => 'test@test.com'
],
'to' => 't3@test.com',
'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6KqeIGZvbw==?=',
'body' => 'BODY 日本語 foo bar',
],
],
],
];
}
/**
* Undocumented function
*
* @dataProvider sendEmailProvider
* @testdox email sending with expected status $expected_status [$_dataName]
*
* @param string $subject
* @param string $body
* @param string $from_email
* @param string $from_name
* @param array $to_email
* @param array|null $replace
* @param string|null $encoding
* @param bool|null $kv_folding
* @param int $expected_status
* @param array $expected_content
* @return void
*/
public function testSendEmail(
string $subject,
string $body,
string $from_email,
string $from_name,
array $to_email,
?array $replace,
?string $encoding,
?bool $kv_folding,
int $expected_status,
array $expected_content
): void {
if ($replace === null) {
$replace = [];
}
if ($encoding === null) {
$encoding = 'UTF-8';
}
if ($kv_folding === null) {
$kv_folding = false;
}
// force new set for each run
self::$log->setLogUniqueId(true);
// set on of unique log id
self::$log->setLogPer('run', true);
// init logger
$status = \CoreLibs\Create\Email::sendEmail(
$subject,
$body,
$from_email,
$from_name,
$to_email,
$replace,
$encoding,
$kv_folding,
true,
self::$log
);
$this->assertEquals(
$expected_status,
$status,
'Assert sending status'
);
// assert content: must load JSON from log file
if ($status == 2) {
// open file, get last entry with 'SEND EMAIL JSON' key
$file = file_get_contents(self::$log->getLogFileName());
if ($file !== false) {
// extract SEND EMAIL JSON line
$found = preg_match_all("/^.* <SEND EMAIL JSON> - (.*)$/m", $file, $matches);
// print "Found: $found | EMAIL: " . print_r($matches, true) . "\n";
if (!empty($matches[1])) {
foreach ($matches[1] as $pos => $email_json) {
$email = \CoreLibs\Convert\Json::jsonConvertToArray($email_json);
// print "EMAIL: " . print_r($email, true) . "\n";
$this->assertEquals(
$expected_content[$pos]['header']['From'] ?? 'MISSING FROM',
$email['header']['From'] ?? '',
'Email check: assert header from'
);
$this->assertEquals(
'text/plain; charset=' . $encoding ?? 'UTF-8',
$email['header']['Content-type'] ?? '',
'Email check: assert header content type'
);
$this->assertEquals(
'1.0',
$email['header']['MIME-Version'] ?? '',
'Email check: assert header mime version'
);
$this->assertEquals(
$expected_content[$pos]['to'] ?? 'MISSING TO',
$email['to'] ?? '',
'Email check: assert to'
);
$this->assertEquals(
$expected_content[$pos]['subject'] ?? 'MISSING SUBJECT',
$email['subject'] ?? '',
'Email check: assert subject'
);
// body must be translated back to encoding if encoding is not UTF-8
$this->assertEquals(
$encoding != 'UTF-8' ?
mb_convert_encoding($expected_content[$pos]['body'] ?? '', $encoding, 'UTF-8') :
$expected_content[$pos]['body'] ?? 'MISSING BODY',
$email['encoding'] != 'UTF-8' ?
mb_convert_encoding($email['body'] ?? '', $email['encoding'], 'UTF-8') :
$email['body'] ?? '',
'Email check: assert body'
);
}
}
}
}
}
}
// __END__

View File

@@ -13,7 +13,11 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsCreateHashTest extends TestCase
{
/**
* Undocumented function
*
* @return array
*/
public function hashData(): array
{
return [

View File

@@ -36,9 +36,9 @@ final class CoreLibsDBExtendedArrayIOTest extends TestCase
*
* @return void
*/
public function testDBIO()
public function testArrayDBIO()
{
$this->assertTrue(true, 'DB Extended ArrayIO Tests not implemented');
// $this->assertTrue(true, 'DB Extended ArrayIO Tests not implemented');
$this->markTestIncomplete(
'DB\Extended\ArrayIO Tests have not yet been implemented'
);

View File

@@ -15,8 +15,6 @@ use PHPUnit\Framework\TestCase;
*/
final class CoreLibsDebugLoggingTest extends TestCase
{
public $log;
/**
* test set for options BASIC
*
@@ -85,6 +83,47 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* init logging class
*
* @dataProvider optionsProvider
* @testdox init test [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testClassInit(?array $options, array $expected, array $override): void
{
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if ($options === null) {
$log = new \CoreLibs\Debug\Logging();
} else {
$log = new \CoreLibs\Debug\Logging($options);
}
// check that settings match
$this->assertEquals(
$expected['log_folder'],
$log->getSetting('log_folder')
);
$this->assertEquals(
$expected['debug_all'],
$log->getSetting('debug_output_all')
);
$this->assertEquals(
$expected['print_all'],
$log->getSetting('print_output_all')
);
// print "LOG: " . $log->getSetting('log_folder') . "\n";
// print "DEBUG: " . $log->getSetting('debug_output_all') . "\n";
// print "PRINT: " . $log->getSetting('print_output_all') . "\n";
}
/**
* adds log ID settings based on basic options
*
@@ -173,6 +212,52 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* test the setting and getting of LogId
*
* @covers ::setLogId
* @dataProvider logIdOptionsProvider
* @testdox log id set/get tests [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testLogId(?array $options, array $expected, array $override): void
{
// we need to set with file_id option, globals LOG_FILE_ID, constant LOG_FILE_ID
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if (!empty($override['globals'])) {
foreach ($override['globals'] as $var => $value) {
$GLOBALS[$var] = $value;
}
}
if ($options === null) {
$log = new \CoreLibs\Debug\Logging();
} else {
$log = new \CoreLibs\Debug\Logging($options);
}
// check current
$this->assertEquals(
$log->getLogId(),
$expected['log_file_id']
);
// we need to override now too
if (!empty($override['values'])) {
// check if we have values, set them post and assert
$log->setLogId($override['values']['log_file_id']);
$this->assertEquals(
$log->getLogId(),
$expected['set_log_file_id']
);
}
}
/**
* Undocumented function
*
@@ -180,6 +265,10 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function logLevelAllProvider(): array
{
// 0: type
// 1: flag
// 2: expected set
// 3: expected get
return [
'debug all true' => [
'debug',
@@ -208,6 +297,38 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* check set/get for log level all flag
*
* @dataProvider logLevelAllProvider
* @testdox set/get all log level $type with flag $flag [$_dataName]
*
* @param string $type
* @param bool $flag
* @param bool $expected_set
* @param bool $expected_get
* @return void
*/
public function testSetGetLogLevelAll(
string $type,
bool $flag,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$log->setLogLevelAll($type, $flag),
$expected_set
);
// get and check
$this->assertEquals(
$log->getLogLevelAll($type),
$expected_get
);
}
/**
* Undocumented function
*
@@ -215,6 +336,12 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function logLevelProvider(): array
{
// 0: type
// 1: flag
// 2: debug on (array)
// 3: expected set
// 4: level
// 5: expected get
return [
'set debug on for level A,B,C and check full set' => [
'debug',
@@ -287,6 +414,43 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* checks setting for per log info level
*
* @covers ::setLogLevel
* @dataProvider logLevelProvider
* @testdox set/get log level $type to $flag check with $level [$_dataName]
*
* @param string $type
* @param string $flag
* @param array $debug_on
* @param bool $expected_set
* @param string|null $level
* @param bool|array<mixed> $expected_get
* @return void
*/
public function testSetGetLogLevel(
string $type,
string $flag,
array $debug_on,
bool $expected_set,
?string $level,
$expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set
$this->assertEquals(
$log->setLogLevel($type, $flag, $debug_on),
$expected_set
);
// get, if level is null compare to?
$this->assertEquals(
$log->getLogLevel($type, $flag, $level),
$expected_get
);
}
/**
* Undocumented function
*
@@ -294,6 +458,10 @@ final class CoreLibsDebugLoggingTest extends TestCase
*/
public function logPerProvider(): array
{
// 0: type
// 1: set
// 2: expected set
// 3: expected get
return [
'level set true' => [
'level',
@@ -328,6 +496,68 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* set and get per log
* for level/class/page/run flags
*
* @covers ::setLogPer
* @dataProvider logPerProvider
* @testdox set/get log per $type with $set [$_dataName]
*
* @param string $type
* @param boolean $set
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPer(
string $type,
bool $set,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$log->setLogPer($type, $set),
$expected_set
);
// get and check
$this->assertEquals(
$log->getLogPer($type),
$expected_get
);
}
/**
* set the print log file date part
*
* @covers ::setGetLogPrintFileDate
* @testWith [true, true, true]
* [false, false, false]
* @testdox set/get log file date to $input [$_dataName]
*
* @param boolean $input
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void
{
// neutral start with default
$log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$log->setGetLogPrintFileDate($input),
$expected_set
);
$this->assertEquals(
$log->setGetLogPrintFileDate(),
$expected_get
);
}
/**
* Undocumented function
*
@@ -369,6 +599,95 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* convert array to string with ## pre replace space holders
*
* @covers ::prAr
* @dataProvider prArProvider
* @testdox check prAr array to string conversion [$_dataName]
*
* @param array $input
* @param string $expected
* @return void
*/
public function testPrAr(array $input, string $expected): void
{
$log = new \CoreLibs\Debug\Logging();
$this->assertEquals(
$log->prAr($input),
$expected
);
}
/**
* Undocumented function
*
* @return array
*/
public function prBlProvider(): array
{
// 0: input flag (bool)
// 1: is true
// 2: is flase
// 3: epxected
return [
'true bool default' => [
true,
null,
null,
'true'
],
'false bool default' => [
false,
null,
null,
'false'
],
'true bool override' => [
true,
'ok',
'not ok',
'ok'
],
'false bool override' => [
false,
'ok',
'not ok',
'not ok'
],
];
}
/**
* check bool to string converter
*
* @covers ::prBl
* @dataProvider prBlProvider
* @testdox check prBl $input ($true/$false) is expected $false [$_dataName]
*
* @param bool $input
* @param string|null $true
* @param string|null $false
* @param string $expected
* @return void
*/
public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void
{
$log = new \CoreLibs\Debug\Logging();
$return = '';
if ($true === null && $false === null) {
$return = $log->prBl($input);
} elseif ($true !== null || $false !== null) {
$return = $log->prBl($input, $true ?? '', $false ?? '');
}
$this->assertEquals(
$expected,
$return
);
}
// from here are complex debug tests
/**
* Undocumented function
*
@@ -471,304 +790,6 @@ final class CoreLibsDebugLoggingTest extends TestCase
];
}
/**
* init logging class
*
* @dataProvider optionsProvider
* @testdox init test [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testClassInit(?array $options, array $expected, array $override): void
{
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if ($options === null) {
$this->log = new \CoreLibs\Debug\Logging();
} else {
$this->log = new \CoreLibs\Debug\Logging($options);
}
// check that settings match
$this->assertEquals(
$expected['log_folder'],
$this->log->getSetting('log_folder')
);
$this->assertEquals(
$expected['debug_all'],
$this->log->getSetting('debug_output_all')
);
$this->assertEquals(
$expected['print_all'],
$this->log->getSetting('print_output_all')
);
// print "LOG: " . $this->log->getSetting('log_folder') . "\n";
// print "DEBUG: " . $this->log->getSetting('debug_output_all') . "\n";
// print "PRINT: " . $this->log->getSetting('print_output_all') . "\n";
}
/**
* test the setting and getting of LogId
*
* @covers ::setLogId
* @dataProvider logIdOptionsProvider
* @testdox log id set/get tests [$_dataName]
*
* @param array|null $options
* @param array $expected
* @param array $override
* @return void
*/
public function testLogId(?array $options, array $expected, array $override): void
{
// we need to set with file_id option, globals LOG_FILE_ID, constant LOG_FILE_ID
if (!empty($override['constant'])) {
foreach ($override['constant'] as $var => $value) {
define($var, $value);
}
}
if (!empty($override['globals'])) {
foreach ($override['globals'] as $var => $value) {
$GLOBALS[$var] = $value;
}
}
if ($options === null) {
$this->log = new \CoreLibs\Debug\Logging();
} else {
$this->log = new \CoreLibs\Debug\Logging($options);
}
// check current
$this->assertEquals(
$this->log->getLogId(),
$expected['log_file_id']
);
// we need to override now too
if (!empty($override['values'])) {
// check if we have values, set them post and assert
$this->log->basicSetLogId($override['values']['log_file_id']);
$this->assertEquals(
$this->log->getLogId(),
$expected['set_log_file_id']
);
}
}
/**
* check set/get for log level all flag
*
* @dataProvider logLevelAllProvider
* @testdox set/get all log level $type with flag $flag [$_dataName]
*
* @param string $type
* @param bool $flag
* @param bool $expected_set
* @param bool $expected_get
* @return void
*/
public function testSetGetLogLevelAll(
string $type,
bool $flag,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$this->log->setLogLevelAll($type, $flag),
$expected_set
);
// get and check
$this->assertEquals(
$this->log->getLogLevelAll($type),
$expected_get
);
}
/**
* checks setting for per log info level
*
* @covers ::setLogLevel
* @dataProvider logLevelProvider
* @testdox set/get log level $type to $flag check with $level [$_dataName]
*
* @param string $type
* @param string $flag
* @param array $debug_on
* @param bool $expected_set
* @param string|null $level
* @param bool|array<mixed> $expected_get
* @return void
*/
public function testSetGetLogLevel(
string $type,
string $flag,
array $debug_on,
bool $expected_set,
?string $level,
$expected_get
): void {
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set
$this->assertEquals(
$this->log->setLogLevel($type, $flag, $debug_on),
$expected_set
);
// get, if level is null compare to?
$this->assertEquals(
$this->log->getLogLevel($type, $flag, $level),
$expected_get
);
}
/**
* set and get per log
* for level/class/page/run flags
*
* @covers ::setLogPer
* @dataProvider logPerProvider
* @testdox set/get log per $type with $set [$_dataName]
*
* @param string $type
* @param boolean $set
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPer(
string $type,
bool $set,
bool $expected_set,
bool $expected_get
): void {
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$this->log->setLogPer($type, $set),
$expected_set
);
// get and check
$this->assertEquals(
$this->log->getLogPer($type),
$expected_get
);
}
/**
* set the print log file date part
*
* @covers ::setGetLogPrintFileDate
* @testWith [true, true, true]
* [false, false, false]
* @testdox set/get log file date to $input [$_dataName]
*
* @param boolean $input
* @param boolean $expected_set
* @param boolean $expected_get
* @return void
*/
public function testSetGetLogPrintFileDate(bool $input, bool $expected_set, bool $expected_get): void
{
// neutral start with default
$this->log = new \CoreLibs\Debug\Logging();
// set and check
$this->assertEquals(
$this->log->setGetLogPrintFileDate($input),
$expected_set
);
$this->assertEquals(
$this->log->setGetLogPrintFileDate(),
$expected_get
);
}
/**
* convert array to string with ## pre replace space holders
*
* @covers ::prAr
* @dataProvider prArProvider
* @testdox check prAr array to string conversion [$_dataName]
*
* @param array $input
* @param string $expected
* @return void
*/
public function testPrAr(array $input, string $expected): void
{
$this->log = new \CoreLibs\Debug\Logging();
$this->assertEquals(
$this->log->prAr($input),
$expected
);
}
public function prBlProvider(): array
{
return [
'true bool default' => [
true,
null,
null,
'true'
],
'false bool default' => [
false,
null,
null,
'false'
],
'true bool override' => [
true,
'ok',
'not ok',
'ok'
],
'false bool override' => [
false,
'ok',
'not ok',
'not ok'
],
];
}
/**
* check bool to string converter
*
* @covers ::prBl
* @dataProvider prBlProvider
* @textdox check prBl $input ($true/$false) is expected $false [$_dataName]
*
* @param bool $input
* @param string|null $true
* @param string|null $false
* @param string $expected
* @return void
*/
public function testPrBl(bool $input, ?string $true, ?string $false, string $expected): void
{
$this->log = new \CoreLibs\Debug\Logging();
$return = '';
if ($true === null && $false === null) {
$return = $this->log->prBl($input);
} elseif ($true !== null || $false !== null) {
$return = $this->log->prBl($input, $true ?? '', $false ?? '');
}
$this->assertEquals(
$expected,
$return
);
}
// from here are complex debug tests
/**
* Test debug flow
*
@@ -824,11 +845,11 @@ final class CoreLibsDebugLoggingTest extends TestCase
// remove any files named /tmp/error_log_TestDebug*.log
array_map('unlink', glob($options['log_folder'] . 'error_msg_' . $options['file_id'] . '*.log'));
// init logger
$this->log = new \CoreLibs\Debug\Logging($options);
$log = new \CoreLibs\Debug\Logging($options);
// * debug (A/B)
// NULL check for strip/prefix
$this->assertEquals(
$this->log->debug(
$log->debug(
$debug_msg['level'],
$debug_msg['string'],
$debug_msg['strip'],
@@ -837,7 +858,7 @@ final class CoreLibsDebugLoggingTest extends TestCase
$expected_debug
);
// * if print check data in log file
$log_file = $this->log->getLogFileName();
$log_file = $log->getLogFileName();
if (!empty($options['debug_all']) && !empty($options['print_all'])) {
// file name matching
$this->assertStringStartsWith(
@@ -866,10 +887,10 @@ final class CoreLibsDebugLoggingTest extends TestCase
);
}
// ** ECHO ON
$log_string = $this->log->printErrorMsg();
$log_string = $log->printErrorMsg();
// * print
if (!empty($options['debug_all']) && !empty($options['echo_all'])) {
// print $this->log->printErrorMsg() . "\n";
// print $log->printErrorMsg() . "\n";
// echo string must start with
$this->assertStringStartsWith(
$expected_string_start,
@@ -893,6 +914,77 @@ final class CoreLibsDebugLoggingTest extends TestCase
);
}
}
// TODO: setLogUniqueId/getLogUniqueId
/**
* Undocumented function
*
* @return array
*/
public function logUniqueIdProvider(): array
{
return [
'option set' => [
'option' => true,
'override' => false,
],
'direct set' => [
'option' => false,
'override' => false,
],
'override set' => [
'option' => false,
'override' => true,
],
'option and override set' => [
'option' => false,
'override' => true,
],
];
}
/**
* Undocumented function
*
* @covers ::setLogUniqueId
* @covers ::getLogUniqueId
* @dataProvider logUniqueIdProvider
* @testdox per run log id set test: option: $option, override: $override [$_dataName]
*
* @param bool $option
* @param bool $override
* @return void
*/
public function testLogUniqueId(bool $option, bool $override): void
{
if ($option === true) {
$log = new \CoreLibs\Debug\Logging(['per_run' => $option]);
} else {
$log = new \CoreLibs\Debug\Logging();
$log->setLogUniqueId();
}
$per_run_id = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id,
'assert per log run id 1st'
);
if ($override === true) {
$log->setLogUniqueId(true);
$per_run_id_2nd = $log->getLogUniqueId();
$this->assertMatchesRegularExpression(
"/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
$per_run_id_2nd,
'assert per log run id 2nd'
);
$this->assertNotEquals(
$per_run_id,
$per_run_id_2nd,
'1st and 2nd don\'t match'
);
}
}
}
// __END__

View File

@@ -120,6 +120,16 @@ final class CoreLibsDebugSupportTest extends TestCase
null,
'a string',
],
'string with html chars, encode' => [
'a string with <> &',
true,
'a string with &lt;&gt; &amp;',
],
'string with html chars' => [
'a string with <> &',
null,
'a string with <> &',
],
'a number' => [
1234,
null,
@@ -180,22 +190,41 @@ final class CoreLibsDebugSupportTest extends TestCase
*/
public function debugStringProvider(): array
{
// 0: input string
// 1: replace
// 2: html flag
// 3: expected
return [
'null string, default' => [
0 => null,
1 => null,
2 => '-'
null,
null,
null,
'-'
],
'empty string, ... replace' => [
0 => '',
1 => '...',
2 => '...'
'',
'...',
null,
'...'
],
'filled string' => [
0 => 'some string',
1 => null,
2 => 'some string'
]
'some string',
null,
null,
'some string'
],
'string with html chars, encode' => [
'a string with <> &',
'-',
true,
'a string with &lt;&gt; &amp;',
],
'string with html chars' => [
'a string with <> &',
'-',
null,
'a string with <> &',
],
];
}
@@ -366,12 +395,14 @@ final class CoreLibsDebugSupportTest extends TestCase
if (count($compare) == 10) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::getCallerMethodList()
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected 10'
);
} else {
$this->assertEquals(
$expected_group,
\CoreLibs\Debug\Support::getCallerMethodList()
\CoreLibs\Debug\Support::getCallerMethodList(),
'assert expected group'
);
}
}
@@ -398,24 +429,33 @@ final class CoreLibsDebugSupportTest extends TestCase
*
* @cover ::debugString
* @dataProvider debugStringProvider
* @testdox debugString $input with replace $replace will be $expected [$_dataName]
* @testdox debugString $input with replace $replace and html $flag will be $expected [$_dataName]
*
* @param string|null $input
* @param string|null $replace
* @param string $expected
* @param bool|null $flag
* @param string $expected
* @return void
*/
public function testDebugString(?string $input, ?string $replace, string $expected)
public function testDebugString(?string $input, ?string $replace, ?bool $flag, string $expected): void
{
if ($replace === null) {
if ($replace === null && $flag === null) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input)
\CoreLibs\Debug\Support::debugString($input),
'assert all default'
);
} elseif ($flag === null) {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input, $replace),
'assert flag default'
);
} else {
$this->assertEquals(
$expected,
\CoreLibs\Debug\Support::debugString($input, $replace)
\CoreLibs\Debug\Support::debugString($input, $replace, $flag),
'assert all set'
);
}
}

View File

@@ -22,7 +22,7 @@ final class CoreLibsOutputFormElementsTest extends TestCase
*/
public function testOutputFormElements()
{
$this->assertTrue(true, 'Output Form Elements Tests not implemented');
// $this->assertTrue(true, 'Output Form Elements Tests not implemented');
$this->markTestIncomplete(
'Output\Form\Elements Tests have not yet been implemented'
);

View File

@@ -22,7 +22,7 @@ final class CoreLibsOutputFormTokenTest extends TestCase
*/
public function testOutputFormToken()
{
$this->assertTrue(true, 'Output Form Token Tests not implemented');
// $this->assertTrue(true, 'Output Form Token Tests not implemented');
$this->markTestIncomplete(
'Output\Form\Token Tests have not yet been implemented'
);

View File

@@ -22,7 +22,7 @@ final class CoreLibsOutputImageTest extends TestCase
*/
public function testOutputImage()
{
$this->assertTrue(true, 'Output Image Tests not implemented');
// $this->assertTrue(true, 'Output Image Tests not implemented');
$this->markTestIncomplete(
'Output\Image Tests have not yet been implemented'
);

View File

@@ -22,10 +22,10 @@ final class CoreLibsOutputProgressbarTest extends TestCase
*/
public function testOutputProgressbar()
{
/* $this->assertTrue(true, 'Output Progressbar Tests not implemented');
$this->markTestIncomplete(
'Output\Progressbar Tests have not yet been implemented'
); */
// $this->assertTrue(true, 'Output Progressbar Tests not implemented');
// $this->markTestIncomplete(
// 'Output\Progressbar Tests have not yet been implemented'
// );
$this->markTestSkipped('No implementation for Output\Progressbar at the moment');
}
}

View File

@@ -270,8 +270,10 @@ BEGIN
(OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
THEN
NEW.login_user_id_set_date = NOW();
NEW.login_user_id_last_revalidate = NOW();
ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
NEW.login_user_id_set_date = NULL;
NEW.login_user_id_last_revalidate = NULL;
END IF;
RETURN NEW;
END;
@@ -595,12 +597,13 @@ CREATE TABLE edit_user (
password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
password_reset_uid VARCHAR, -- the uid to access the password reset page
-- _GET login id for direct login
login_user_id VARCHAR UNIQUE, -- the login uid, at least 32 chars
login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated login id after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for login user id, but still allow normal login
login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
-- additional ACL json block
additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
) INHERITS (edit_generic) WITHOUT OIDS;
@@ -629,11 +632,12 @@ COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the
COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'login id was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'login id is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'login id is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for login id, user can still login normal';
COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
-- END: table/edit_user.sql
-- START: table/edit_log.sql

View File

@@ -1,72 +1,47 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$connection of function pg_connection_busy expects PgSql\\\\Connection, object\\|resource given\\.$#"
count: 3
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_connection_status expects PgSql\\\\Connection, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_escape_bytea expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_get_result expects PgSql\\\\Connection, object\\|resource given\\.$#"
count: 2
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_meta_data expects PgSql\\\\Connection, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_escape_identifier expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_send_query expects PgSql\\\\Connection, object\\|resource given\\.$#"
count: 2
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_socket expects PgSql\\\\Connection, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_escape_literal expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$connection of function pg_version expects PgSql\\\\Connection\\|null, object\\|resource given\\.$#"
count: 2
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_affected_rows expects PgSql\\\\Result, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_escape_string expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_fetch_all expects PgSql\\\\Result, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_execute expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_fetch_array expects PgSql\\\\Result, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_parameter_status expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_field_name expects PgSql\\\\Result, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_prepare expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_num_fields expects PgSql\\\\Result, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_query expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_num_rows expects PgSql\\\\Result, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php
-
message: "#^Parameter \\#1 \\$result of function pg_result_error expects PgSql\\\\Result, object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$connection of function pg_query_params expects PgSql\\\\Connection\\|string, object\\|resource given\\.$#"
count: 1
path: www/lib/CoreLibs/DB/SQL/PgSQL.php

View File

@@ -49,11 +49,11 @@ parameters:
- www/vendor
# ignore errores with
ignoreErrors:
- # this error is ignore because of the PHP 8.0 to 8.1 change for pg_*, only for 8.0 or lower
message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects resource(\\|null)?, object\\|resource(\\|bool)? given\\.$#"
path: %currentWorkingDirectory%/www/lib/CoreLibs/DB/SQL/PgSQL.php
#- # this error is ignore because of the PHP 8.0 to 8.1 change for pg_*, only for 8.0 or lower
# message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects resource(\\|null)?, object\\|resource(\\|bool)? given\\.$#"
# path: %currentWorkingDirectory%/www/lib/CoreLibs/DB/SQL/PgSQL.php
- # this is for 8.1 or newer
message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects PgSql\\\\(Result|Connection(\\|null)?), object\\|resource given\\.$#"
message: "#^Parameter \\#1 \\$(result|connection) of function pg_\\w+ expects PgSql\\\\(Result|Connection(\\|string)?(\\|null)?), object\\|resource given\\.$#"
path: %currentWorkingDirectory%/www/lib/CoreLibs/DB/SQL/PgSQL.php
# this is ignored for now
# - '#Expression in empty\(\) is always falsy.#'

View File

@@ -0,0 +1,144 @@
<?php // phpcs:ignore warning
/**
* @phan-file-suppress PhanTypeSuspiciousStringExpression
*/
declare(strict_types=1);
// will be overwritten in config.master.php depending on location
$DEBUG_ALL_OVERRIDE = true; // set to 1 to debug on live/remote server locations
$DEBUG_ALL = true;
$PRINT_ALL = true;
$ECHO_ALL = true;
$DB_DEBUG = true;
if ($DEBUG_ALL) {
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
}
ob_start();
// basic class test file
define('USE_DATABASE', false);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-create_email';
ob_end_flush();
// override echo all from config.master.php
$ECHO_ALL = true;
use CoreLibs\Create\Email;
use CoreLibs\Convert\Html;
$log = new CoreLibs\Debug\Logging([
'log_folder' => BASE . LOG,
'file_id' => $LOG_FILE_ID,
// add file date
'print_file_date' => true,
// set debug and print flags
'debug_all' => $DEBUG_ALL,
'echo_all' => $ECHO_ALL,
'print_all' => $PRINT_ALL,
]);
// define a list of from to color sets for conversion test
$PAGE_NAME = 'TEST CLASS: CREATE EMAIL';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$from_name = '日本語';
$from_email = 'test@test.com';
print "SET: $from_name / $from_email: "
. Html::htmlent(Email::encodeEmailName($from_email, $from_name)) . "<br>";
$status = Email::sendEmail(
'TEST',
'BODY',
'test@test.com',
'Test Name',
[
[
'name' => 'To 1',
'email' => 'to1@test.com'
],
],
[],
'UTF-8',
false,
true,
$log
);
print "SENDING A: " . $status . "<br>";
$status = Email::sendEmail(
'TEST {REPLACE}',
'BODY {OTHER}',
'test@test.com',
'Test Name',
[
[
'name' => 'To 1-A',
'email' => 'to1-a@test.com'
],
[
'name' => 'To 2-A',
'email' => 'to2-a@test.com',
'replace' => [
'OTHER' => '--FOR 2 A other--'
]
],
],
[
'REPLACE' => '**replaced**',
'OTHER' => '**other**'
],
'UTF-8',
false,
true,
$log
);
print "SENDING B: " . $status . "<br>";
$status = Email::sendEmail(
'TEST',
'BODY',
'test@test.com',
'Test Name',
['a@a.com', 'b@b.com'],
[],
'UTF-8',
false,
true,
$log
);
print "SENDING C: " . $status . "<br>";
// SUBJECT 日本語カタカナパ
$status = Email::sendEmail(
'TEST 日本語カタカナパカタカナバ',
'BODY 日本語カタカナパカタカナバ',
'test@test.com',
'Test Name 日本語カタカナパ',
[
['email' => 'a@a.com', 'name' => 'a 日本語カタカナパカタカナバ'],
['email' => 'b@b.com', 'name' => 'b 日本語プブガバケブプガバケ'],
],
[],
'UTF-8',
false,
true,
$log
);
print "SENDING D: " . $status . "<br>";
// error message
print $log->printErrorMsg();
print "</body></html>";
// __END__

View File

@@ -139,6 +139,22 @@ foreach ($compare_dates as $compare_date) {
. DgS::printAr(DateTime::calcDaysInterval($compare_date[0], $compare_date[1], true)) . "<br>";
}
// test date conversion
$dow = 2;
print "DOW[$dow]: " . DateTime::setWeekdayNameFromIsoDow($dow) . "<br>";
print "DOW[$dow],long: " . DateTime::setWeekdayNameFromIsoDow($dow, true) . "<br>";
$date = '2022-7-22';
print "DATE-dow[$date]: " . DateTime::setWeekdayNameFromDate($date) . "<br>";
print "DATE-dow[$date],long: " . DateTime::setWeekdayNameFromDate($date, true) . "<br>";
print "DOW-date[$date]: " . DateTime::setWeekdayNumberFromDate($date) . "<br>";
$dow = 11;
print "DOW[$dow];invalid: " . DateTime::setWeekdayNameFromIsoDow($dow) . "<br>";
print "DOW[$dow],long;invalid: " . DateTime::setWeekdayNameFromIsoDow($dow, true) . "<br>";
$date = '2022-70-242';
print "DATE-dow[$date];invalid: " . DateTime::setWeekdayNameFromDate($date) . "<br>";
print "DATE-dow[$date],long;invalid: " . DateTime::setWeekdayNameFromDate($date, true) . "<br>";
print "DOW-date[$date];invalid: " . DateTime::setWeekdayNumberFromDate($date) . "<br>";
// error message
print $log->printErrorMsg();

View File

@@ -209,13 +209,12 @@ print "INSERT WITH NO PRIMARY KEY WITH RETURNING STATUS: " . Support::printToStr
print "</pre>";
// READ PREPARE
if (
$db->dbPrepare(
'sel_test_foo',
"SELECT test_foo_id, test, some_bool, string_a, number_a, number_a_numeric, some_time "
. "FROM test_foo ORDER BY test_foo_id DESC LIMIT 5"
) === false
) {
$q_prep = "SELECT test_foo_id, test, some_bool, string_a, number_a, "
. "number_a_numeric, some_time "
. "FROM test_foo "
. "WHERE test = $1 "
. "ORDER BY test_foo_id DESC LIMIT 5";
if ($db->dbPrepare('sel_test_foo', $q_prep) === false) {
print "Error in sel_test_foo prepare<br>";
} else {
$max_rows = 6;
@@ -229,6 +228,29 @@ if (
$i++;
}
}
// prepre a second time on normal connection
if ($db->dbPrepare('sel_test_foo', $q_prep) === false) {
print "Error prepareing<br>";
print "ERROR (dbPrepare on same query): "
. $db->dbGetLastError() . "/" . $db->dbGetLastWarning() . "/"
. "<pre>" . print_r($db->dbGetCombinedErrorHistory(), true) . "</pre><br>";
}
// NOTE: try to replacate connection still exists if script is run a second time
// open pg bouncer connection
$db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'], $log);
print "[PGB] DBINFO: " . $db_pgb->dbInfo() . "<br>";
if ($db->dbPrepare('pgb_sel_test_foo', $q_prep) === false) {
print "[PGB] [1] Error in pgb_sel_test_foo prepare<br>";
} else {
print "[PGB] [1] pgb_sel_test_foo prepare OK<br>";
}
// second prepare
if ($db->dbPrepare('pgb_sel_test_foo', $q_prep) === false) {
print "[PGB] [2] Error in pgb_sel_test_foo prepare<br>";
} else {
print "[PGB] [2] pgb_sel_test_foo prepare OK<br>";
}
$db_pgb->dbClose();
# db write class test
$table = 'test_foo';

View File

@@ -71,6 +71,7 @@ print "S::PRINTBOOL(name): " . DebugSupport::printBool(true, 'Name') . "<br>";
print "S::PRINTBOOL(name, ok): " . DebugSupport::printBool(true, 'Name', 'ok') . "<br>";
print "S::PRINTBOOL(name, ok, not): " . DebugSupport::printBool(false, 'Name', 'ok', 'not') . "<br>";
print "S::DEBUSTRING(s): " . DebugSupport::debugString('SET') . "<br>";
print "S::DEBUSTRING(s&gt;): " . DebugSupport::debugString('<SET>') . "<br>";
print "S::DEBUSTRING(''): " . DebugSupport::debugString('') . "<br>";
print "S::DEBUSTRING(,s): " . DebugSupport::debugString(null, '{-}') . "<br>";

View File

@@ -63,12 +63,14 @@ print '<div><a href="class_test.password.php">Class Test: PASSWORD</a></div>';
print '<div><a href="class_test.math.php">Class Test: MATH</a></div>';
print '<div><a href="class_test.html.php">Class Test: HTML/ELEMENTS</a></div>';
print '<div><a href="class_test.email.php">Class Test: EMAIL</a></div>';
print '<div><a href="class_test.create_email.php">Class Test: CREATE EMAIL</a></div>';
print '<div><a href="class_test.uids.php">Class Test: UIDS</a></div>';
print '<div><a href="class_test.phpv.php">Class Test: PHP VERSION</a></div>';
print '<div><a href="class_test.hash.php">Class Test: HASH</a></div>';
print '<div><a href="class_test.encoding.php">Class Test: ENCODING (CHECK/CONVERT/MIME)</a></div>';
print '<div><a href="class_test.image.php">Class Test: IMAGE</a></div>';
print '<div><a href="class_test.byte.php">Class Test: BYTE CONVERT</a></div>';
print '<div><a href="class_test.strings.php">Class Test: STRING CONVERT</a></div>';
print '<div><a href="class_test.datetime.php">Class Test: DATE/TIME</a></div>';
print '<div><a href="class_test.array.php">Class Test: ARRAY HANDLER</a></div>';
print '<div><a href="class_test.file.php">Class Test: FILE</a></div>';

View File

@@ -0,0 +1,86 @@
<?php // phpcs:ignore warning
declare(strict_types=1);
$DEBUG_ALL_OVERRIDE = 0; // set to 1 to debug on live/remote server locations
$DEBUG_ALL = 1;
$PRINT_ALL = 1;
$DB_DEBUG = 1;
if ($DEBUG_ALL) {
error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR);
}
ob_start();
// basic class test file
define('USE_DATABASE', false);
// sample config
require 'config.php';
// define log file id
$LOG_FILE_ID = 'classTest-string';
ob_end_flush();
$log = new CoreLibs\Debug\Logging([
'log_folder' => BASE . LOG,
'file_id' => $LOG_FILE_ID,
// add file date
'print_file_date' => true,
// set debug and print flags
'debug_all' => $DEBUG_ALL ?? false,
'echo_all' => $ECHO_ALL ?? false,
'print_all' => $PRINT_ALL ?? false,
]);
$byte_class = 'CoreLibs\Convert\Strings';
$PAGE_NAME = 'TEST CLASS: STRINGS CONVERT';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title><head>";
print "<body>";
print '<div><a href="class_test.php">Class Test Master</a></div>';
print '<div><h1>' . $PAGE_NAME . '</h1></div>';
$split = '4-4-4';
$test_strings = [
'13',
'1234',
'12341',
'12341234',
'123412341',
'123412341234',
'1234123412341234512345',
];
foreach ($test_strings as $string) {
print "Convert: $string with $split to: "
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
. "<br>";
}
$split = '2_2';
$string = '1234';
print "Convert: $string with $split to: "
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
. "<br>";
$split = '2-2';
$string = 'あいうえ';
print "Convert: $string with $split to: "
. \CoreLibs\Convert\Strings::splitFormatString($string, $split)
. "<br>";
$test_splits = [
'',
'2',
'2-2',
'2-3-4',
];
foreach ($test_splits as $split) {
print "$split with count: " . \CoreLibs\Convert\Strings::countSplitParts($split) . "<br>";
}
// error message
print $log->printErrorMsg();
print "</body></html>";
// __END__

View File

@@ -17,7 +17,20 @@ $DB_CONFIG = [
'db_user' => $_ENV['DB_USER.TEST'] ?? '',
'db_pass' => $_ENV['DB_PASS.TEST'] ?? '',
'db_host' => $_ENV['DB_HOST.TEST'] ?? '',
'db_port' => 5432,
'db_port' => $_ENV['DB_PORT.PG'] ?? 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',
'db_ssl' => 'allow', // allow, disable, require, prefer
'db_debug' => true, // turn on logging or not
],
// same as above, but uses pg bouncer
'test_pgbouncer' => [
'db_name' => $_ENV['DB_NAME.TEST'] ?? '',
'db_user' => $_ENV['DB_USER.TEST'] ?? '',
'db_pass' => $_ENV['DB_PASS.TEST'] ?? '',
'db_host' => $_ENV['DB_HOST.TEST'] ?? '',
'db_port' => $_ENV['DB_PORT.PG_BOUNCER'] ?? 5432,
'db_schema' => 'public',
'db_type' => 'pgsql',
'db_encoding' => '',

View File

@@ -252,7 +252,7 @@ if ($is_secure) {
define('DB_CONFIG_NAME', $SITE_CONFIG[HOST_NAME]['db_host']);
define('DB_CONFIG', $DB_CONFIG[DB_CONFIG_NAME] ?? []);
// because we can't change constant, but we want to for db debug flag
$GLOBALS['DB_CONFIG'] = DB_CONFIG;
$GLOBALS['DB_CONFIG_SET'] = DB_CONFIG;
// define('DB_CONFIG_TARGET', SITE_CONFIG[$HOST_NAME]['db_host_target']);
// define('DB_CONFIG_OTHER', SITE_CONFIG[$HOST_NAME]['db_host_other']);
// override for login and global schemas

View File

@@ -2,9 +2,9 @@
/********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2008/08/14
* CREATED:
* SHORT DESCRIPTION:
* URL redirect header
*
* HISTORY:
*********************************************************************/

View File

@@ -2,8 +2,9 @@
/********************************************************************
* AUTHOR: Clemens Schwaighofer
* CREATED: 2008/08/01
* CREATED:
* SHORT DESCRIPTION:
*
* HISTORY:
*********************************************************************/
@@ -108,7 +109,7 @@ $data = [
];
// log action
// no log if login
if (!$login->login) {
if (!$login->loginActionRun()) {
$cms->adbEditLog('Submit', $data, 'BINARY');
}
//------------------------------ logging end

View File

@@ -404,6 +404,7 @@ if ($form->my_page_name == 'edit_order') {
$elements[] = $form->formCreateElement('password_change_interval');
$elements[] = $form->formCreateElement('login_user_id');
$elements[] = $form->formCreateElement('login_user_id_set_date');
$elements[] = $form->formCreateElement('login_user_id_last_revalidate');
$elements[] = $form->formCreateElement('login_user_id_locked');
$elements[] = $form->formCreateElement('login_user_id_revalidate_after');
$elements[] = $form->formCreateElement('login_user_id_valid_from');

View File

@@ -159,6 +159,12 @@ $edit_users = [
'type' => 'view',
'empty' => '-'
],
'login_user_id_last_revalidate' => [
'output_name' => 'loginUserId last revalidate date',
'value' => $GLOBALS['login_user_id_last_revalidate'] ?? '',
'type' => 'view',
'empty' => '-'
],
'login_user_id_locked' => [
'value' => $GLOBALS['login_user_id_locked'] ?? '',
'output_name' => 'loginUserId usage locked',

View File

@@ -14,9 +14,9 @@ if (!DEBUG) {
});
}*/
// open overlay boxes counter
var GL_OB_S = 30;
var GL_OB_BASE = 30;
// open overlay boxes counter for z-index
var GL_OB_S = 100;
var GL_OB_BASE = 100;
/**
* opens a popup window with winName and given features (string)

View File

@@ -79,12 +79,12 @@ class Login
/** @var string source, either _GET or _POST or empty */
private $login_user_id_source = '';
/** @var bool set to true if illegal characters where found in the login user id string */
private $login_unclear = false;
private $login_user_id_unclear = false;
// is set to one if login okay, or EUID is set and user is okay to access this page
/** @var bool */
private $permission_okay = false;
/** @var string pressed login */
public $login;
private $login = '';
/** @var string master action command */
private $action;
/** @var string login name */
@@ -540,6 +540,8 @@ class Login
. "eu.debug, eu.db_debug, "
// enabled
. "eu.enabled, eu.deleted, "
// for checks only
. "eu.login_user_id, "
// login id validation
. "CASE WHEN ("
. "(eu.login_user_id_valid_from IS NULL "
@@ -550,7 +552,7 @@ class Login
// check if user must login
. "CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL "
. "AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL "
. "AND (eu.login_user_id_set_date + eu.login_user_id_revalidate_after)::DATE "
. "AND (eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after)::DATE "
. "<= NOW()::DATE "
. "THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, "
. "eu.login_user_id_locked, "
@@ -653,6 +655,15 @@ class Login
// check if user is okay
$this->loginCheckPermissions();
if ($this->login_error == 0) {
if (
!empty($res['login_user_id']) &&
!empty($this->username) && !empty($this->password)
) {
$q = "UPDATE edit_user SET "
. "login_user_id_last_revalidate = NOW() "
. "WHERE edit_user_id = " . $this->euid;
$this->db->dbExec($q);
}
// now set all session vars and read page permissions
$_SESSION['DEBUG_ALL'] = $this->db->dbBoolean($res['debug']);
$_SESSION['DB_DEBUG'] = $this->db->dbBoolean($res['db_debug']);
@@ -1276,6 +1287,7 @@ class Login
. $strings['PASSWORD_CHANGE_BUTTON_VALUE']
. '" OnClick="ShowHideDiv(\'pw_change_div\');">'
]);
// TODO: submit or JS to set target page as ajax call
// NOTE: for the HTML block I ignore line lengths
// phpcs:disable
$this->login_template['password_change'] = <<<EOM
@@ -1318,9 +1330,11 @@ EOM;
}
// now check templates
// TODO: submit or JS to set target page as ajax call
if (!$this->login_template['template']) {
$this->login_template['template'] = <<<EOM
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>{HTML_TITLE}</title>
<style type="text/css">
@@ -1474,7 +1488,6 @@ EOM;
}
// initial the session if there is no session running already
// check if session exists and could be created
// TODO: move session creation and check to outside?
if ($this->session->checkActiveSession() === false) {
$this->login_error = 2;
echo '<b>No active session found</b>';
@@ -1553,7 +1566,7 @@ EOM;
);
// flag unclean input data
if ($login_user_id_changed > 0) {
$this->login_unclear = true;
$this->login_user_id_unclear = true;
// error for invalid user id?
$this->log->debug('LOGIN USER ID', 'Invalid characters: '
. $login_user_id_changed . ' in loginUserId: '
@@ -1891,7 +1904,7 @@ EOM;
// check if user must login
. "CASE WHEN eu.login_user_id_revalidate_after IS NOT NULL "
. "AND eu.login_user_id_revalidate_after > '0 days'::INTERVAL "
. "AND eu.login_user_id_set_date + eu.login_user_id_revalidate_after <= NOW()::DATE "
. "AND eu.login_user_id_last_revalidate + eu.login_user_id_revalidate_after <= NOW()::DATE "
. "THEN 1::INT ELSE 0::INT END AS login_user_id_revalidate, "
. "eu.login_user_id_locked "
//
@@ -2128,6 +2141,16 @@ EOM;
return false;
}
/**
* Returns true if login button was pressed
*
* @return bool If login action was run, return true
*/
public function loginActionRun(): bool
{
return empty($this->login) ? false : true;
}
/**
* Returns current set loginUserId or empty if unset
*
@@ -2156,7 +2179,7 @@ EOM;
*/
public function loginGetLoginUserIdUnclean(): bool
{
return $this->login_unclear;
return $this->login_user_id_unclear;
}
/**

View File

@@ -1,7 +1,7 @@
<?php
/*
* DEPRECATED: Use correct Json:: instead
* DEPRECATED: Use correct Convert\Json:: instead
*/
declare(strict_types=1);

View File

@@ -1,7 +1,7 @@
<?php
/*
* html convert functions
* array search and transform functions
*/
declare(strict_types=1);

View File

@@ -1,7 +1,7 @@
<?php
/*
* image thumbnail, rotate, etc
* date convert and check functions
*/
declare(strict_types=1);
@@ -193,6 +193,54 @@ class DateTime
}
}
/**
* Returns long or short day of week name based on ISO day of week number
* 1: Monday
* ...
* 7: Sunday
*
* @param int $isodow 1: Monday, 7: Sunday
* @param bool $long Default false 'Mon', if true 'Monday'
* @return string Day of week string either short 'Mon' or long 'Monday'
*/
public static function setWeekdayNameFromIsoDow(int $isodow, bool $long = false): string
{
// if not valid, set to invalid
if ($isodow < 1 || $isodow > 7) {
return $long ? 'Invalid' : 'Inv';
}
return date($long ? 'l' : 'D', strtotime("Sunday +{$isodow} days") ?: null);
}
/**
* Get the day of week Name from date
*
* @param string $date Any valid date
* @param bool $long Default false 'Mon', if true 'Monday'
* @return string Day of week string either short 'Mon' or long 'Monday'
*/
public static function setWeekdayNameFromDate(string $date, bool $long = false): string
{
if (!self::checkDate($date)) {
return $long ? 'Invalid' : 'Inv';
}
return date($long ? 'l' : 'D', strtotime($date) ?: null);
}
/**
* Get the day of week Name from date
*
* @param string $date Any valid date
* @return int ISO Weekday number 1: Monday, 7: Sunday, -1 for invalid date
*/
public static function setWeekdayNumberFromDate(string $date): int
{
if (!self::checkDate($date)) {
return -1;
}
return (int)date('N', strtotime($date) ?: null);
}
/**
* splits & checks date, wrap around for check_date function
*

View File

@@ -1,7 +1,7 @@
<?php
/*
* image thumbnail, rotate, etc
* byte conversion from and to human readable
*/
declare(strict_types=1);

View File

@@ -1,7 +1,7 @@
<?php
/*
* check if string is valid in target encoding
* convert string frmo one encdoing to another with auto detect flags
*/
declare(strict_types=1);

View File

@@ -0,0 +1,123 @@
<?php
/*
* string convert and transform functions
*/
declare(strict_types=1);
namespace CoreLibs\Convert;
class Strings
{
/**
* return the number of elements in the split list
* 0 if nothing / invalid split
* 1 if no split character found
* n for the numbers in the split list
*
* @param string $split_format
* @param string $split_characters
* @return int
*/
public static function countSplitParts(
string $split_format,
string $split_characters = '-'
): int {
if (
empty($split_format) ||
// non valid characters inside, abort
!preg_match("/^[0-9" . $split_characters . "]/", $split_format) ||
preg_match('/[^\x20-\x7e]/', $split_characters)
) {
return 0;
}
$split_list = preg_split(
// allowed split characters
"/([" . $split_characters . "]{1})/",
$split_format
);
if (!is_array($split_list)) {
return 0;
}
return count(array_filter($split_list));
}
/**
* split format a string base on a split format string
* split format string is eg
* 4-4-4 that means 4 characters DASH 4 characters DASH 4 characters
* So a string in the format of
* ABCD1234EFGH will be ABCD-1234-EFGH
* Note a string LONGER then the maxium will be attached with the LAST
* split character. In above exmaple
* ABCD1234EFGHTOOLONG will be ABCD-1234-EFGH-TOOLONG
*
* @param string $value string value to split
* @param string $split_format split format
* @param string $split_characters list of charcters with which we split
* if not set uses dash ('-')
* @return string split formatted string or original value if not chnaged
*/
public static function splitFormatString(
string $value,
string $split_format,
string $split_characters = '-'
): string {
if (
// abort if split format is empty
empty($split_format) ||
// if not in the valid ASCII character range for any of the strings
preg_match('/[^\x20-\x7e]/', $value) ||
// preg_match('/[^\x20-\x7e]/', $split_format) ||
preg_match('/[^\x20-\x7e]/', $split_characters) ||
// only numbers and split characters in split_format
!preg_match("/[0-9" . $split_characters . "]/", $split_format)
) {
return $value;
}
// split format list
$split_list = preg_split(
// allowed split characters
"/([" . $split_characters . "]{1})/",
$split_format,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
// if this is false, or only one array, abort split
if (!is_array($split_list) || count($split_list) == 1) {
return $value;
}
$out = '';
$pos = 0;
$last_split = '';
foreach ($split_list as $offset) {
if (is_numeric($offset)) {
$_part = substr($value, $pos, (int)$offset);
if (empty($_part)) {
break;
}
$out .= $_part;
$pos += (int)$offset;
} elseif ($pos) { // if first, do not add
$out .= $offset;
$last_split = $offset;
}
}
if (!empty($out) && $pos < strlen($value)) {
$out .= $last_split . substr($value, $pos);
}
// if last is not alphanumeric remove, remove
if (!strcspn(substr($out, -1, 1), $split_characters)) {
$out = substr($out, 0, -1);
}
// overwrite only if out is set
if (!empty($out)) {
return $out;
} else {
return $value;
}
}
}
// __END__

View File

@@ -0,0 +1,292 @@
<?php
/*
* Create email class
*/
declare(strict_types=1);
namespace CoreLibs\Create;
/**
* sending simple text emails
*/
class Email
{
/** @var array<string> allowed list for encodings that can do KV folding */
private static $encoding_kv_allowed = [
'UTF-8',
'EUC-JP',
'SJIS',
'SJIS-win',
'ISO-2022-JP',
'ISO-2022-JP-MS',
'JIS',
'JIS-ms',
];
/** @var string normaly this does not need to be changed */
private static $mb_convert_kana_mode = 'KV';
/**
* create mime encoded email part for to/from emails.
* If encoding is not UTF-8 it will convert the email name to target encoding
* FROM UTF-8
* Source data is ALWAYS seen as utf-8
*
* @param string $email E-Mail address
* @param string $email_name Name for the email address, in UTF-8, if not set, empty
* @param string $encoding Encoding, if not set UTF-8
* @param bool $kv_folding If set to true and a valid encoding, do KV folding
* @return string Correctly encoded and build email string
*/
public static function encodeEmailName(
string $email,
string $email_name = '',
string $encoding = 'UTF-8',
bool $kv_folding = false
): string {
if (!empty($email_name)) {
// if encoding is not UTF-8 then we convert
if ($encoding != 'UTF-8') {
$email_name = mb_convert_encoding($email_name, $encoding, 'UTF-8');
}
$email_name =
mb_encode_mimeheader(
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
mb_convert_kana(
$email_name,
self::$mb_convert_kana_mode,
$encoding
) :
$email_name,
$encoding
);
return '"' . $email_name . '" '
. '<' . (string)$email . '>';
} else {
return $email;
}
}
/**
* Subject/Body replace sub function
*
* @param string $subject Subject string, in UTF-8
* @param string $body Body string, in UTF-8
* @param array<string,string> $replace Replace the array as key -> value, in UTF-8
* @param string $encoding Encoding for subject encode mime header
* @param bool $kv_folding If set to true and a valid encoding,
* do KV folding
* @return array<string> Pos 0: Subject, Pos 1: Body
*/
private static function replaceContent(
string $subject,
string $body,
array $replace,
string $encoding,
bool $kv_folding
): array {
foreach (['subject', 'body'] as $element) {
$$element = str_replace(
array_map(
function ($key) {
return '{' . $key . '}';
},
array_keys($replace)
),
array_values($replace),
$$element
);
}
// if encoding is NOT UTF-8 convert to target
if ($encoding != 'UTF-8') {
$subject = mb_convert_encoding($subject, $encoding, 'UTF-8');
$body = mb_convert_encoding($body, $encoding, 'UTF-8');
}
// we need to encodde the subject
$subject = mb_encode_mimeheader(
in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ?
// for any non UTF-8 encoding convert kana
mb_convert_kana(
$subject,
self::$mb_convert_kana_mode,
$encoding
) :
$subject,
$encoding
);
return [$subject, $body];
}
/**
* Send plain text email with possible to replace subject/body data
* either global or per to email set.
* replace to tags are in {} in the subject or body
*
* @param string $subject Mail subject, mandatory, in UTF-8
* @param string $body Mail body, mandatory, in UTF-8
* @param string $from_email From email, mandatory
* @param string $from_name From email name, in UTF-8
* if empty '' then not set
* @param array<mixed> $send_to_emails to email or array for email/replace
* If array: name/email/replace[key,value]
* name and replace must be in UTF-8
* At least one must be set
* @param array<string,string> $replace_content Subject/Body replace as
* search -> replace, in UTF-8
* @param string $encoding E-Mail encoding, default UTF-8
* @param bool $kv_folding If set to true and a valid encoding,
* do KV folding
* @param bool $test test flag, default off
* @param \CoreLibs\Debug\Logging|null $log Logging class,
* only used if test flag is true
* @return int 2 test only, no sent
* 1 for ok,
* 0 for send not ok
* -1 for nothing set (emails, subject, body)
* -2 for empty to list
* -3 encoding target not valid or not installed
*/
public static function sendEmail(
string $subject,
string $body,
string $from_email,
string $from_name,
array $send_to_emails,
array $replace_content = [],
string $encoding = 'UTF-8',
bool $kv_folding = false,
bool $test = false,
?\CoreLibs\Debug\Logging $log = null
): int {
/** @var array<string> */
$to_emails = [];
/** @var array<string,array<string,string>> */
$to_replace = [];
/** @var string */
$out_subject = $subject;
/** @var string */
$out_body = $body;
// check basic set
if (empty($subject) || empty($body) || empty($from_email)) {
return -1;
}
if (
$encoding != 'UTF-8' &&
!in_array($encoding, mb_list_encodings())
) {
return -3;
}
// if not one valid to, abort
foreach ($send_to_emails as $to_email) {
// to_email can be string, then only to email
// else expect 'email' & 'name'
if (
is_array($to_email) &&
isset($to_email['email'])
) {
$_to_email = self::encodeEmailName(
$to_email['email'],
$to_email['name'] ?? '',
$encoding,
$kv_folding
);
$to_emails[] = $_to_email;
// if we have to replacement, this override replace content
if (isset($to_email['replace']) && count($to_email['replace'])) {
// merge with original replace content,
// to data will override original data
$to_replace[$_to_email] = array_merge(
$replace_content,
$to_email['replace']
);
}
} elseif (is_string($to_email)) {
$to_emails[] = $to_email;
}
}
if (!count($to_emails)) {
return -2;
}
// the email headers needed
$headers = [
'From' => self::encodeEmailName($from_email, $from_name, $encoding),
'Content-type' => "text/plain; charset=" . $encoding,
'MIME-Version' => "1.0",
];
// if we have a replace string, we need to do replace run
// only if there is no dedicated to replace
// also run replace if there is nothing to replace at all
// this will mime encode the subject
if (!count($to_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$replace_content,
$encoding,
$kv_folding
);
}
$mail_delivery_status = 1;
// send the email
foreach ($to_emails as $to_email) {
// default mail status is success
$mail_status = true;
// if there is a to replace, if not use the original replace content
if (count($to_replace)) {
$_replace = [];
if (!empty($to_replace[$to_email])) {
$_replace = $to_replace[$to_email];
} elseif (count($replace_content)) {
$_replace = $replace_content;
}
if (count($_replace)) {
list($out_subject, $out_body) = self::replaceContent(
$subject,
$body,
$_replace,
$encoding,
$kv_folding
);
}
}
// if we are in test mode, do not send an email and set status to 2
if ($test === false) {
$mail_status = mail($to_email, $out_subject, $out_body, $headers);
} else {
$mail_delivery_status = 2;
}
// log if an log instance exists
if ($log instanceof \CoreLibs\Debug\Logging) {
// build debug strings: convert to UTF-8 if not utf-8
$log->debug('SEND EMAIL', 'HEADERS: ' . $log->prAr($headers) . ', '
. 'ENCODING: ' . $encoding . ', '
. 'KV FOLDING: ' . $log->prBl($kv_folding) . ', '
. 'TO: ' . $to_email . ', '
. 'SUBJECT: ' . $out_subject . ', '
. 'BODY: ' . ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding)));
$log->debug('SEND EMAIL JSON', json_encode([
'encoding' => $encoding,
'kv_folding' => $kv_folding,
'header' => $headers,
'to' => $to_email,
'subject' => $out_subject,
'body' => ($encoding == 'UTF-8' ?
$out_body :
mb_convert_encoding($out_body, 'UTF-8', $encoding))
]) ?: '{}');
}
if (!$mail_status) {
$mail_delivery_status = 0;
}
}
return $mail_delivery_status;
}
}
// __END__

View File

@@ -1,7 +1,7 @@
<?php
/*
* html convert functions
* random key functions
*/
declare(strict_types=1);

View File

@@ -2580,9 +2580,9 @@ class IO
// loop through the write array and each field to build the query
foreach ($write_array as $field) {
if (
(empty($primary_key['value']) ||
(!empty($primary_key['value']) &&
!in_array($field, $not_write_update_array))
(
empty($primary_key['value']) ||
!in_array($field, $not_write_update_array)
) &&
!in_array($field, $not_write_array)
) {
@@ -2963,7 +2963,7 @@ class IO
* Either as single array level for single insert
* Or nested array for multiple insert values
*
* If key was set only returns tghose values directly or as array
* If key was set only returns those values directly or as array
*
* On multiple insert return the position for which to return can be set too
*
@@ -2978,7 +2978,7 @@ class IO
// return as is if key is null
if ($key === null) {
if (count($this->insert_id_arr) == 1) {
// return as nul if not found
// return as null if not found
return $this->insert_id_arr[0] ?? null;
} else {
return $this->insert_id_arr;

View File

@@ -173,7 +173,7 @@ class Logging
// can be overridden with basicSetLogFileId later
if (!empty($this->options['file_id'])) {
$this->setLogId($this->options['file_id'] ?? '');
$this->setLogId($this->options['file_id']);
} elseif (!empty($GLOBALS['LOG_FILE_ID'])) {
// legacy flow, should be removed and only set via options
$this->setLogId($GLOBALS['LOG_FILE_ID']);
@@ -293,15 +293,7 @@ class Logging
}
// set per run ID
if ($this->log_per_run) {
/* if (isset($GLOBALS['LOG_FILE_UNIQUE_ID'])) {
$this->log_file_unique_id = $GLOBALS['LOG_FILE_UNIQUE_ID'];
} */
if (!$this->log_file_unique_id) {
// $GLOBALS['LOG_FILE_UNIQUE_ID'] =
$this->log_file_unique_id =
date('Y-m-d_His') . '_U_'
. substr(hash('sha1', uniqid((string)mt_rand(), true)), 0, 8);
}
$this->setLogUniqueId();
}
}
@@ -394,7 +386,10 @@ class Logging
// write to file
// first check if max file size is is set and file is bigger
if ($this->log_max_filesize > 0 && ((filesize($fn) / 1024) > $this->log_max_filesize)) {
if (
$this->log_max_filesize > 0 &&
((filesize($fn) / 1024) > $this->log_max_filesize)
) {
// for easy purpose, rename file only to attach timestamp, nur sequence numbering
rename($fn, $fn . '.' . date("YmdHis"));
}
@@ -593,6 +588,10 @@ class Logging
return false;
}
$this->{'log_per_' . $type} = $set;
// if per run set unique id
if ($type == 'run' && $set == true) {
$this->setLogUniqueId();
}
return true;
}
@@ -610,6 +609,33 @@ class Logging
return $this->{'log_per_' . $type};
}
/**
* Sets a unique id based on current date (y/m/d, h:i:s) and a unique id (8 chars)
* if override is set to true it will be newly set, else if already set nothing changes
*
* @param bool $override True to force new set
* @return void
*/
public function setLogUniqueId(bool $override = false): void
{
if (!$this->log_file_unique_id || $override == true) {
$this->log_file_unique_id =
date('Y-m-d_His') . '_U_'
. substr(hash('sha1', uniqid((string)mt_rand(), true)), 0, 8);
}
}
/**
* Return current set log file unique id,
* empty string for not set
*
* @return string
*/
public function getLogUniqueId(): string
{
return $this->log_file_unique_id;
}
/**
* Set or get the log file date extension flag
* if null or empty parameter gets current flag

View File

@@ -8,6 +8,8 @@ declare(strict_types=1);
namespace CoreLibs\Debug;
use CoreLibs\Convert\Html;
class Support
{
/**
@@ -89,7 +91,7 @@ class Support
* Debug\Logging compatible output
*
* @param mixed $mixed
* @param bool $no_html set to true to use ##HTMLPRE##
* @param bool $no_html set to true to use ##HTMLPRE##or html escape
* @return string
*/
public static function printToString($mixed, bool $no_html = false): string
@@ -103,6 +105,12 @@ class Support
} elseif (is_array($mixed)) {
// use the pre one OR debug one
return self::printAr($mixed, $no_html);
} elseif (is_string($mixed)) {
if ($no_html) {
return Html::htmlent((string)$mixed);
} else {
return (string)$mixed;
}
} else {
// should be int/float/string
return (string)$mixed;
@@ -190,12 +198,19 @@ class Support
* @param string $replace [default '-'] What to replace the empty string with
* @return string String itself or the replaced value
*/
public static function debugString(?string $string, string $replace = '-'): string
{
public static function debugString(
?string $string,
string $replace = '-',
bool $no_html = false
): string {
if (empty($string)) {
return $replace;
$string = $replace;
}
if ($no_html) {
return Html::htmlent($string);
} else {
return $string;
}
return $string;
}
}

View File

@@ -423,7 +423,7 @@ class SmartyExtend extends \Smarty
$this->DATA['ADMIN'] = $cms->acl['admin'] ?? 0;
// top menu
$this->DATA['nav_menu'] = $cms->adbTopMenu();
$this->DATA['nav_menu_count'] = is_array($this->DATA['nav_menu']) ? count($this->DATA['nav_menu']) : 0;
$this->DATA['nav_menu_count'] = count($this->DATA['nav_menu']);
// messages = ['msg' =>, 'class' => 'error/warning/...']
$this->DATA['messages'] = $cms->messages;
} else { /** @phpstan-ignore-line Because I assume object for phpstan */

View File

@@ -27,6 +27,8 @@ return array(
'CoreLibs\\Convert\\Math' => $baseDir . '/lib/CoreLibs/Convert/Math.php',
'CoreLibs\\Convert\\MimeAppName' => $baseDir . '/lib/CoreLibs/Convert/MimeAppName.php',
'CoreLibs\\Convert\\MimeEncode' => $baseDir . '/lib/CoreLibs/Convert/MimeEncode.php',
'CoreLibs\\Convert\\Strings' => $baseDir . '/lib/CoreLibs/Convert/Strings.php',
'CoreLibs\\Create\\Email' => $baseDir . '/lib/CoreLibs/Create/Email.php',
'CoreLibs\\Create\\Hash' => $baseDir . '/lib/CoreLibs/Create/Hash.php',
'CoreLibs\\Create\\RandomKey' => $baseDir . '/lib/CoreLibs/Create/RandomKey.php',
'CoreLibs\\Create\\Session' => $baseDir . '/lib/CoreLibs/Create/Session.php',

View File

@@ -92,6 +92,8 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9
'CoreLibs\\Convert\\Math' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/Math.php',
'CoreLibs\\Convert\\MimeAppName' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/MimeAppName.php',
'CoreLibs\\Convert\\MimeEncode' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/MimeEncode.php',
'CoreLibs\\Convert\\Strings' => __DIR__ . '/../..' . '/lib/CoreLibs/Convert/Strings.php',
'CoreLibs\\Create\\Email' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Email.php',
'CoreLibs\\Create\\Hash' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Hash.php',
'CoreLibs\\Create\\RandomKey' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/RandomKey.php',
'CoreLibs\\Create\\Session' => __DIR__ . '/../..' . '/lib/CoreLibs/Create/Session.php',