From 67db1c6e9bc474c34cb10029794b64be8c85a393 Mon Sep 17 00:00:00 2001 From: Hilmar R Date: Sat, 23 Jan 2021 15:24:24 +0100 Subject: melt diff prod fork 4.6.2 air onto 5.2.1 to 5.2.2 DB 1241 --- include/account.php | 559 +++++++++++++++++++++++++++++++++++++++++++++------ include/datetime.php | 32 +++ include/security.php | 103 ++++++++++ include/text.php | 12 ++ 4 files changed, 640 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/include/account.php b/include/account.php index bea84cea7..ef79d5bb1 100644 --- a/include/account.php +++ b/include/account.php @@ -69,17 +69,30 @@ function check_account_password($password) { function check_account_invite($invite_code) { $result = array('error' => false, 'message' => ''); - $using_invites = get_config('system','invitation_only'); + // [hilmar -> + $using_invites = (get_config('system','invitation_only') + || get_config('system','invitation_also')); if($using_invites) { + if(! $invite_code) { - $result['message'] .= t('An invitation is required.') . EOL; - } - $r = q("select * from register where hash = '%s' limit 1", dbesc($invite_code)); - if(! $r) { - $result['message'] .= t('Invitation could not be verified.') . EOL; + + $result['message'] + .= 'ZAR0510E,' . t('An invitation is required.') . EOL; + + } else { + + // check if invite code exists + $r = q("SELECT * FROM register WHERE reg_hash = '%s' AND reg_vital = 1 LIMIT 1", + dbesc($invite_code)); + if(! $r) { + $result['message'] + .= 'ZAR0511E,' . t('Invitation could not be verified.') . EOL; + } } } + // <- hilmar] + if(strlen($result['message'])) $result['error'] = true; @@ -105,8 +118,8 @@ function account_total() { return false; } - -function account_store_lowlevel($arr) { +// legacy +function account_store_lowlevel_IS_OBSOLETE($arr) { $store = [ 'account_parent' => ((array_key_exists('account_parent',$arr)) ? $arr['account_parent'] : '0'), @@ -128,12 +141,21 @@ function account_store_lowlevel($arr) { 'account_password_changed' => ((array_key_exists('account_password_changed',$arr)) ? $arr['account_password_changed'] : '0001-01-01 00:00:00') ]; + // never ever is this a create table but a pdo insert into account + // strange function placement in text.php (obscure by design :-) return create_table_from_array('account',$store); - + // the TODO may be to adjust others using create_table_from_array(): + // channel.php + // connections.php + // event.php + // hubloc.php + // import.php } -function create_account($arr) { + +// legacy +function create_account_IS_OBSOLETE($arr) { // Required: { email, password } @@ -257,10 +279,160 @@ function create_account($arr) { return $result; } +/** + * create_account_from_register + * @author hilmar runge + * @since 2020-02-20 + * + * Account creation only happens via table register. + * This function creates the account when all conditions are solved. + * + */ +function create_account_from_register($arr) { + + $result = array('success' => false, 'message' => 'rid:' . $arr['reg_id']); + $now = date('Y-m-d H:i:s'); + + // reg_flags 0x0020 = REGISTER_AGREED = register request verified by user @ regate + $register = q("SELECT * FROM register WHERE reg_id = %d AND (reg_flags & 31) = 0 " + . " AND reg_startup < '%s' AND reg_expires > '%s' ", + intval($arr['reg_id']), + dbesc($now), + dbesc($now) + ); + + if ( ! $register ) return $result; + + // account + $expires = NULL_DATE; + + $default_service_class = get_config('system','default_service_class'); + if($default_service_class === false) + $default_service_class = ''; + + $roles = 0; + // prevent form hackery + if($roles & ACCOUNT_ROLE_ADMIN) { + $admin_result = check_account_admin($arr); + if(! $admin_result) { + $roles = 0; + } + } + + // any accounts available ? + $isa = q("SELECT COUNT(*) AS isa FROM account"); + if ($isa && $isa[0]['isa'] == 0) { + $roles = ACCOUNT_ROLE_ADMIN; + } + + $salt = random_string(32); + $password_encoded = hash('whirlpool', $salt . (hex2bin($register[0]['reg_pass']))); + + $ri = q( + "INSERT INTO account (" + . " account_parent, account_salt, account_password, account_email, " + . " account_language, account_created, account_flags, account_roles, account_level, " + . " account_expires, account_service_class) VALUES( " + . " %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s' ) ", + intval($parent), + dbesc($salt), + dbesc($password_encoded), + dbesc($register[0]['reg_did2']), + dbesc($register[0]['reg_lang']), + dbesc($now), + intval($register[0]['reg_flags'] & 31), // off REGISTER_AGREE at ACCOUNT + intval($roles), + intval(5), + dbesc($expires), + dbesc($default_service_class) + ); + + if(! $ri) { + logger('create_account: DB INSERT failed.'); + $result['message'] = 'ZAR ' . t('Failed to store account information.'); + return($result); + } + + $r = q("SELECT * FROM account WHERE account_email = '%s' AND account_password = '%s' LIMIT 1", + dbesc($register[0]['reg_did2']), + dbesc($password_encoded) + ); + if($r && count($r)) { + $result['account'] = $r[0]; + } + else { + logger('create_account: could not retrieve newly created account'); + } + + // Set the parent record to the current record_id if no parent was provided + + if(! $parent) { + $r = q("UPDATE account SET account_parent = %d WHERE account_id = %d", + intval($result['account']['account_id']), + intval($result['account']['account_id']) + ); + if(! $r) { + logger('create_account: failed to set parent'); + } + $result['account']['parent'] = $result['account']['account_id']; + } + + $result['success'] = true; + //call_hooks('register_account',$result); + + return $result; +} + +/** + * @brief as far to see, email validation for register account verification + * @param array (account) + * @param array ('resend' => true, 'email' = > email) + * + */ function verify_email_address($arr) { + // $hash = random_string(24); + + // [hilmar -> + $reg = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_email = 's%' ", + dbesc($arr['email']) + ); + if ( ! $reg) + return false; + + push_lang(($reg[0]['email']) ? $reg[0]['email'] : 'en'); + + $email_msg = replace_macros(get_intltext_template('register_verify_member.tpl'), + [ + '$sitename' => get_config('system','sitename'), + '$siteurl' => z_root(), + '$email' => $arr['email'], + '$uid' => 1, + '$hash' => $hash, + '$details' => '' + ] + ); + + $res = z_mail( + [ + 'toEmail' => $arr['email'], + 'messageSubject' => sprintf( t('Registration confirmation for %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, + ] + ); + + pop_lang(); + + if(! $res) + logger('send_reg_approval_email: failed to account_id: ' . $arr['account']['account_id']); + + return $res; +} + +function verify_email_addressNOP($arr) { + if(array_key_exists('resend',$arr)) { $a = q("select * from account where account_email = '%s' limit 1", dbesc($arr['email']) @@ -269,11 +441,14 @@ function verify_email_address($arr) { return false; } $account = $a[0]; - $v = q("select * from register where uid = %d and password = 'verify' limit 1", + // [hilmar -> + $v = q("SELECT * FROM register WHERE reg_uid = %d AND reg_vital = 1 " + . " AND reg_pass = 'verify' LIMIT 1", intval($account['account_id']) ); + // <- hilmar] if($v) { - $hash = $v[0]['hash']; + $hash = $v[0]['reg_hash']; } else { return false; @@ -282,13 +457,16 @@ function verify_email_address($arr) { else { $hash = random_string(24); - q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + // [hilmar -> + q("INSERT INTO register ( reg_hash, reg_created, reg_uid, reg_pass, reg_lang, reg_stuff ) " + ." VALUES ( '%s', '%s', %d, '%s', '%s', '' ) ", dbesc($hash), dbesc(datetime_convert()), intval($arr['account']['account_id']), dbesc('verify'), dbesc($arr['account']['account_language']) ); + // <- hilmar] $account = $arr['account']; } @@ -345,11 +523,17 @@ function send_reg_approval_email($arr) { $hash = random_string(); - $r = q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + // [hilmar -> + // code before fetches the $admins as recipients for the approval request mail + // $arr has a user (self registered) account + // ... $arr['email'] ??? + // ... reg expiration ? + $r = q("INSERT INTO register ( reg_hash, reg_email, reg_created, reg_uid, reg_pass, reg_lang, reg_stuff )" + . " VALUES ( '%s', '%s', '%s', %d, '', '%s', '' ) ", dbesc($hash), + dbesc($arr['account']['account_email']), dbesc(datetime_convert()), intval($arr['account']['account_id']), - dbesc(''), dbesc($arr['account']['account_language']) ); @@ -423,7 +607,7 @@ function account_allow($hash) { $ret = array('success' => false); - $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", + $register = q("SELECT * FROM register WHERE reg_hash = '%s' LIMIT 1", dbesc($hash) ); @@ -431,57 +615,89 @@ function account_allow($hash) { return $ret; $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); + // a register entry without account assigned to if(! $account) return $ret; - q("DELETE FROM register WHERE hash = '%s'", - dbesc($register[0]['hash']) + // [hilmar -> + + q("START TRANSACTION"); + //q("DELETE FROM register WHERE reg_hash = '%s'", + // dbesc($register[0]['reg_hash']) + //); + $r1 = q("UPDATE register SET reg_vital = 0 WHERE reg_hash = '%s'", + dbesc($register[0]['reg_hash']) ); - q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + /* instead of ... + + // unblock + q("UPDATE account SET account_flags = (account_flags & ~%d) " + . " WHERE (account_flags & %d)>0 AND account_id = %d", intval(ACCOUNT_BLOCKED), intval(ACCOUNT_BLOCKED), - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); - q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", + // unpend + q("UPDATE account SET account_flags = (account_flags & ~%d) " + . " WHERE (account_flags & %d)>0 AND account_id = %d", intval(ACCOUNT_PENDING), intval(ACCOUNT_PENDING), - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); - push_lang($register[0]['lang']); + */ + // together unblock and unpend + $r2 = q("UPDATE account SET account_flags = %d WHERE account_id = %d", + intval($account['account_flags'] + &= $account['account_flags'] ^ (ACCOUNT_BLOCKED | ACCOUNT_PENDING)), + intval($register[0]['reg_uid']) + ); + + if($r1 && $r2) { + q("COMMIT"); - $email_tpl = get_intltext_template("register_open_eml.tpl"); - $email_msg = replace_macros($email_tpl, array( - '$sitename' => get_config('system','sitename'), - '$siteurl' => z_root(), - '$username' => $account[0]['account_email'], - '$email' => $account[0]['account_email'], - '$password' => '', - '$uid' => $account[0]['account_id'] - )); + // <- hilmar] - $res = z_mail( - [ - 'toEmail' => $account[0]['account_email'], - 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')), - 'textVersion' => $email_msg, - ] - ); + push_lang($register[0]['reg_lang']); - pop_lang(); + $email_tpl = get_intltext_template("register_open_eml.tpl"); + $email_msg = replace_macros($email_tpl, array( + '$sitename' => get_config('system','sitename'), + '$siteurl' => z_root(), + '$username' => $account[0]['account_email'], + '$email' => $account[0]['account_email'], + '$password' => '', + '$uid' => $account[0]['account_id'] + )); - if(get_config('system','auto_channel_create')) - auto_channel_create($register[0]['uid']); + $res = z_mail( + [ + 'toEmail' => $account[0]['account_email'], + 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')), + 'textVersion' => $email_msg, + ] + ); - if ($res) { - info( t('Account approved.') . EOL ); - return true; + pop_lang(); + + if(get_config('system','auto_channel_create')) + auto_channel_create($register[0]['uid']); + + if ($res) { + info( t('Account approved.') . EOL ); + return true; + } + + // [hilmar -> + } else { + q("ROLLBACK"); } + // <- hilmar] } @@ -498,42 +714,65 @@ function account_allow($hash) { function account_deny($hash) { - $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", + // [hilmar-> + $register = q("SELECT * FROM register WHERE reg_hash = '%s' AND reg_vital = 1 LIMIT 1", dbesc($hash) ); + // <-hilmar] if(! count($register)) return false; $account = q("SELECT account_id, account_email FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); if(! $account) return false; - q("DELETE FROM account WHERE account_id = %d", - intval($register[0]['uid']) - ); + // [hilmar -> + q("START TRANSACTION"); - q("DELETE FROM register WHERE id = %d", - dbesc($register[0]['id']) + $r1 = q("DELETE FROM account WHERE account_id = %d", + intval($register[0]['reg_uid']) + ); + // q("DELETE FROM register WHERE reg_id = %d", + // dbesc($register[0]['reg_id']) + //); + $r2 = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND reg_vital = 1", + dbesc($register[0]['reg_id']) ); - notice( sprintf(t('Registration revoked for %s'), $account[0]['account_email']) . EOL); - return true; + if($r1 && $r2) { + q("COMMIT"); + notice( 'ZAR0512I,' . sprintf( t('Registration revoked for %s'), + $account[0]['account_email']) . EOL); + return true; -} + } else { -// called from regver to activate an account from the email verification link + q("ROLLBACK"); + notice( 'ZAR0513F,' . sprintf( t('Could not revoke registration for %s'), + $account[0]['account_email']) . EOL); + return false; + } + // <- hilmar] +} +/** + * called from Regver to allow/revoke an account + * Use case is under REGISTER_OPEN with APPROVAL + * Ref Regver, Email_validation, Email_resend + * ZAR052+ + */ function account_approve($hash) { $ret = false; // Note: when the password in the register table is 'verify', the uid actually contains the account_id + // hmm - $register = q("SELECT * FROM register WHERE hash = '%s' and password = 'verify' LIMIT 1", + $register = q("SELECT * FROM register WHERE reg_hash = '%s' and reg_pass = 'verify' LIMIT 1", dbesc($hash) ); @@ -541,45 +780,58 @@ function account_approve($hash) { return $ret; $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); if(! $account) return $ret; - q("DELETE FROM register WHERE hash = '%s' and password = 'verify'", - dbesc($register[0]['hash']) + // tr ? + + q("DELETE FROM register WHERE reg_hash = '%s' and reg_pass = 'verify'", + dbesc($register[0]['reg_hash']) ); q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_BLOCKED), intval(ACCOUNT_BLOCKED), - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_PENDING), intval(ACCOUNT_PENDING), - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); q("update account set account_flags = (account_flags & ~%d) where (account_flags & %d)>0 and account_id = %d", intval(ACCOUNT_UNVERIFIED), intval(ACCOUNT_UNVERIFIED), - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); + /* + // together unblock unpend and verified + q("UPDATE account SET account_flags = %d WHERE account_id = %d", + intval($account['account_flags'] + &= $account['account_flags'] + ^ (ACCOUNT_BLOCKED | ACCOUNT_PENDING | ACCOUNT_UNVERIFIED)), + intval($register[0]['reg_uid']) + ); + */ + + // get a fresh copy after we've modified it. $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", - intval($register[0]['uid']) + intval($register[0]['reg_uid']) ); if(! $account) return $ret; if(get_config('system','auto_channel_create')) - auto_channel_create($register[0]['uid']); + auto_channel_create($register[0]['reg_uid']); else { $_SESSION['login_return_url'] = 'new_channel'; authenticate_success($account[0],null,true,true,false,true); @@ -589,6 +841,118 @@ function account_approve($hash) { } +function verify_register_scheme() { + + $dbc = db_columns('register'); + if ($dbc) { + + if ($dbc[0]=='id') { + // v1 format + q("START TRANSACTION"); + + if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { + $r1 = q("ALTER TABLE register RENAME TO register100;"); + + $r2 = q("CREATE TABLE register (" + . "reg_id serial NOT NULL," + . "reg_vital int DEFAULT 1 NOT NULL," + . "reg_flags bigint DEFAULT 0 NOT NULL," + . "reg_didx char(1) DEFAULT '' NOT NULL," + . "reg_did2 text DEFAULT '' NOT NULL," + . "reg_hash text DEFAULT '' NOT NULL," + . "reg_email text DEFAULT '' NOT NULL," + . "reg_created timestamp NOT NULL," + . "reg_startup timestamp NOT NULL," + . "reg_expires timestamp NOT NULL," + . "reg_byc bigint DEFAULT 0 NOT NULL," + . "reg_uid bigint DEFAULT 0 NOT NULL," + . "reg_atip text DEFAULT '' NOT NULL," + . "reg_pass text DEFAULT '' NOT NULL," + . "reg_lang varchar(16) DEFAULT '' NOT NULL," + . "reg_stuff text NOT NULL," + . "PRIMARY KEY (reg_id) );" + ); + $r0 = q("CREATE INDEX ix_reg_vital ON register (reg_vital);"); + $r0 = q("CREATE INDEX ix_reg_flags ON register (reg_flags);"); + $r0 = q("CREATE INDEX ix_reg_didx ON register (reg_didx);"); + $r0 = q("CREATE INDEX ix_reg_did2 ON register (reg_did2);"); + $r0 = q("CREATE INDEX ix_reg_hash ON register (reg_hash);"); + $r0 = q("CREATE INDEX ix_reg_email ON register (reg_email);"); + $r0 = q("CREATE INDEX ix_reg_created ON register (reg_created);"); + $r0 = q("CREATE INDEX ix_reg_startup ON register (reg_startup);"); + $r0 = q("CREATE INDEX ix_reg_expires ON register (reg_expires);"); + $r0 = q("CREATE INDEX ix_reg_byc ON register (reg_byc);"); + $r0 = q("CREATE INDEX ix_reg_uid ON register (reg_uid);"); + $r0 = q("CREATE INDEX ix_reg_atip ON register (reg_atip);"); + + $r3 = q("INSERT INTO register (reg_id, reg_hash, reg_created, reg_uid, reg_pass, reg_lang, reg_stuff) " + . "SELECT id, hash, created, uid, password, lang, '' FROM register100;"); + + $r4 = q("DROP TABLE register100"); + + } + else { + $r1 = q("RENAME TABLE register TO register100;"); + + $r2 = q("CREATE TABLE IF NOT EXISTS register (" + . "reg_id int(10) UNSIGNED NOT NULL AUTO_INCREMENT," + . "reg_vital int(10) UNSIGNED NOT NULL DEFAULT 1," + . "reg_flags int(10) UNSIGNED NOT NULL DEFAULT 0," + . "reg_didx char(1) NOT NULL DEFAULT ''," + . "reg_did2 char(191) NOT NULL DEFAULT ''," + . "reg_hash char(191) NOT NULL DEFAULT ''," + . "reg_email char(191) NOT NULL DEFAULT ''," + . "reg_created datetime NOT NULL DEFAULT '0001-01-01 00:00:00'," + . "reg_startup datetime NOT NULL DEFAULT '0001-01-01 00:00:00'," + . "reg_expires datetime NOT NULL DEFAULT '0001-01-01 00:00:00'," + . "reg_byc int(10) UNSIGNED NOT NULL DEFAULT 0 ," + . "reg_uid int(10) UNSIGNED NOT NULL DEFAULT 0 ," + . "reg_atip char(191) NOT NULL DEFAULT ''," + . "reg_pass char(191) NOT NULL DEFAULT ''," + . "reg_lang char(16) NOT NULL DEFAULT ''," + . "reg_stuff text NOT NULL," + . "PRIMARY KEY (reg_id)," + . "KEY ix_reg_hash (reg_hash)," + . "KEY ix_reg_vital (reg_vital)," + . "KEY ix_reg_flags (reg_flags)," + . "KEY ix_reg_didx (reg_didx)," + . "KEY ix_reg_did2 (reg_did2)," + . "KEY ix_reg_email (reg_email)," + . "KEY ix_reg_created (reg_created)," + . "KEY ix_reg_startup (reg_startup)," + . "KEY ix_reg_expires (reg_expires)," + . "KEY ix_reg_byc (reg_byc)," + . "KEY ix_reg_uid (reg_uid)," + . "KEY ix_reg_atip (reg_atip)" + . ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" + ); + + $r3 = q("INSERT INTO register (reg_id, reg_hash, reg_created, reg_uid, reg_pass, reg_lang, reg_stuff) " + . "SELECT id, hash, created, uid, password, lang, '' FROM register100;"); + + $r4 = q("DROP TABLE register100"); + } + + // $r = ($r1 && $r2 && $r3 && $r4); + // the only important + $r = $r2; + + if($r) { + q("COMMIT"); + return UPDATE_SUCCESS; + } + + q("ROLLBACK"); + return UPDATE_FAILED; + } + elseif ( count($dbc) != 16 ) { + // ffu + // fields in v2.0.0 = 16 + } + } +} + + /** * @brief Checks for accounts that have past their expiration date. * @@ -824,3 +1188,66 @@ function get_account_techlevel($account_id = 0) { return (5); } + +function zar_log($msg='') { + file_put_contents('./zar.log', + date('Y-m-d_H:i:s') . ' ' . $msg . ', ip: § ' . $_SERVER['REMOTE_ADDR'] . ' §' . "\n", FILE_APPEND); + return; +} + +function zar_reg_mail($reonar=false) { + if ($reonar) { + $zem = z_mail( + [ + 'toEmail' => $reonar['to'], + 'fromName' => ' ', + 'fromEmail' => $reonar['from'], + 'messageSubject' => $reonar['subject'], + 'textVersion' => $reonar['txttemplate'], + ] + ); + return $zem; + } +} + +/** + * ckeck current day and time against register duties + * + * @author Hilmar Runge + * @since 2020-02-25 + * @param the current date and time is taken as default + * @return ['isduty'] true/false + * ['nowfmt'] the textmsg about the current state + * ['atform'] the disabled html attribute for form input fields + * + */ +function zar_register_dutystate( $now=NULL, $day=NULL ) { + + is_null($now) ? $now = date('Hi') : ''; + is_null($day) ? $day = date('N') : ''; + + $isduty = zarIsDuty($day, $now, 'isOpen'); + + if ( $isduty === false ) { + return array( 'isduty' => $isduty, 'nowfmt' => '', 'atform' => '' ); + } + + $dutyis = $isduty ? t('open') : t('closed'); + $atform = $isduty ? '' : 'disabled'; + + $nowfmt = t('Registration is currently') + . ' ('.substr($now,0,2) . ':' . substr($now,-2) . ') ' + . ' ' . $dutyis; + + if (!$isduty) { + $pernext = zarIsDuty($day, $now, 'nextOpen'); + + if (is_array($pernext)) + $nowfmt .= '. ' . t('Next opens') . ' ' + . ucfirst( array('','mo','tu','we','th','fr','sa','so')[$pernext[0]]) . ' ' + . substr($pernext[1],0,2) . ':' . substr($pernext[1],-2); + } + return array( 'isduty' => $isduty, 'nowfmt' => $nowfmt, 'atform' => $atform); + +} + diff --git a/include/datetime.php b/include/datetime.php index ef0927ea4..0b8722b4f 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -534,3 +534,35 @@ function update_birthdays() { } } } + +/** + * + * Calculate a due by interval + * based on the current datetime the interval is added/subtracted + * @author Hilmar Runge + * @since 2020-02-20 + * @param $duri the interval in the format n[n]i + * where n is a 1-2 digit numeric amount and i is a unit + * example $duri='1w' represents one week + * unit may be one of i(minutes), h(hours), d(days), w(weeks), m(months, y(years)) + * @return array['due'] computed datetime in format 'Y-m-d H:i:s' + * ['durn'] the amount + * ['duru'] the unit + * or false + */ + function calculate_adue($duri=false, $sign='+') { + if ( preg_match( '/^[0-9]{1,2}[ihdwmy]{1}$/', $duri ) && ($sign == '+' || $sign == '-') ) { + $duru = substr( $duri, -1); + $durn = substr( $duri, 0, -1); + $due = date( 'Y-m-d H:i:s', strtotime( + '+' . $durn . ' ' + . str_replace( array(':i',':h',':d',':w',':m',':y'), + array('minutes', 'hours', 'days', 'weeks', 'months', 'years'), + ( ':'. $duru ) + ) + ) + ); + return array( 'durn' => $durn, 'duru' => $duru, 'due' => $due); + } + return false; + } diff --git a/include/security.php b/include/security.php index c9df00f1e..97bf002d8 100644 --- a/include/security.php +++ b/include/security.php @@ -835,3 +835,106 @@ function stream_perms_xchans($perms = NULL ) { return $str; } + + +/** + * Duty day / time checks for account register + * @author hilmar runge + * @since 2020.02.10 + * @param $op what to test: isOpen, nextOpen + * @param $wd weekday according ISO-8601 (1 monday, 7 sunday) + * @param $hhmm a 24h clock value hours and minutes + * if no params are given, the values are taken from the current time + * return is bool(false) if register_duty is not available + */ +function zarIsDuty($wd=NULL, $hhmm=NULL, $op='isOpen') { + + $isduty = get_config('system', 'register_duty_jso'); + + if (!$isduty) + return (bool)false; + + is_null($wd) ? $wd = date('N') : ''; + is_null($hhmm) ? $hhmm = date('Hi') : ''; + + if (!intval($wd . $hhmm)) return (bool)false; + + // be sure to have a valid weekday as index + $wd = (7 + $wd) % 7; + $wd === 0 ? $wd = 7 : ''; + + $duty = json_decode($isduty, true); + if (!$duty) + return (bool)false; + + switch ($op) { + case 'isOpen': + /** + * Check if registration is open + * @return int(0) for not close (open) or int(1) for closed. + * return is bool(false) if register_duty is not available + */ + if (!$duty[$wd]) return (bool)false; + $dutyis = 0; + foreach ($duty[$wd] as $o => $tf) { + if ($o > $hhmm) { + $dutyis = $tf; + break; + } + } + return $dutyis; + break; + + case 'nextOpen': + /** + * Look for next period opens + * @return "=>N =>Hi" date value of the next period register is open for requests + * where N is a weekday (1=monday ... 7=sunday) according ISO-8601 + * where Hi is a 24h clock value hhmm by hours and minutes. + * If no next period open is available, return results to false. + */ + $myd = $wd; + $myh = $hhmm; + $is1 = false; + + // $myd = "5"; // testcase only + // $myh = "1110"; // testcase only + + // a 1st match may be applied below my time and is to see as a cycle to the next week + // but looking is also for a open time after my time is available this week + foreach ($duty as $dd => $dhs) { + + if ($is1 && $dd < $myd) + continue; + + foreach ($dhs as $dh => $tf) { + + if ($tf) continue; // close + + // a weeks 1st open + if (!$is1) $is1 = array($dd, $dh); + + // but is a match after now? + //if ($dd == $myd && $myh >= $dh && $myh <= $dh) continue; + + // if the day is not (more) today start find early morning + if ($dd > $myd) $myh = "0000"; + + // a next period after now in the remainder of the week + if ($dd >= $myd && $dh >= $myh && !$tf) + return array($dd, $dh); + else + continue; + } + } + return $is1; // false or array + break; + + default: + // + break; + } + +} + + diff --git a/include/text.php b/include/text.php index d6b196f1e..96e5c7882 100644 --- a/include/text.php +++ b/include/text.php @@ -2313,6 +2313,18 @@ function undo_post_tagging($s) { return $s; } +/** + * @brief php to js string transfer + * Hilmar, 20200227 + * String values built in php for using as content for js variables become sanitized. Often required + * in cases, where some content will be translated by t(any text...) and is furthermore transfered via + * templates to the outputted html stream. Redecoding in js not required nor useful. + * Apply like: p2j(t('any text\nI will place on a "next line"')); + */ +function p2j($string) { + return preg_replace('/\r?\n/', '\\n', addslashes($string)); +} + function quote_tag($s) { if(strpos($s,' ') !== false) return '"' . $s . '"'; -- cgit v1.2.3