aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--Zotlabs/Module/Admin.php9
-rw-r--r--Zotlabs/Module/Admin/Accounts.php127
-rw-r--r--Zotlabs/Module/Admin/Site.php391
-rw-r--r--Zotlabs/Module/Invite.php599
-rw-r--r--Zotlabs/Module/Regate.php347
-rw-r--r--Zotlabs/Module/Register.php498
-rw-r--r--Zotlabs/Module/Settings/Account.php34
-rw-r--r--Zotlabs/Update/_1241.php15
-rwxr-xr-xboot.php4
-rw-r--r--include/account.php559
-rw-r--r--include/datetime.php32
-rw-r--r--include/security.php103
-rw-r--r--include/text.php12
-rw-r--r--install/schema_mysql.sql39
-rw-r--r--install/schema_postgres.sql42
-rw-r--r--view/css/mod_admin.css4
-rw-r--r--view/js/mod_register.js5
-rwxr-xr-xview/tpl/admin_accounts.tpl107
-rwxr-xr-xview/tpl/admin_site.tpl93
-rwxr-xr-xview/tpl/invite.tpl130
-rw-r--r--view/tpl/plain.tpl3
-rw-r--r--view/tpl/regate.tpl23
-rwxr-xr-xview/tpl/register.tpl35
-rw-r--r--view/tpl/register_duty.tpl28
25 files changed, 2810 insertions, 432 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 2c384ca97..5f3528a74 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,6 @@
+Hubzilla 5.2.2 /2021-01-23)
+ - Update and enhance Account, Invite, Register
+
Hubzilla 5.2.1 (2021-01-16)
- Fix attach_upgrade() to catch all broken entries in attach
- Fix collect_recipients() public policy filter for zot6
diff --git a/Zotlabs/Module/Admin.php b/Zotlabs/Module/Admin.php
index 88b84b9d2..44c10b339 100644
--- a/Zotlabs/Module/Admin.php
+++ b/Zotlabs/Module/Admin.php
@@ -101,11 +101,14 @@ class Admin extends \Zotlabs\Web\Controller {
// pending registrations
- $pdg = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d ) > 0 ",
- intval(ACCOUNT_PENDING)
+ // $pdg = q("SELECT account.*, register.reg_hash from account left join register on account_id = register.reg_uid // where (account_flags & %d ) > 0 ",
+ // intval(ACCOUNT_PENDING)
+ // );
+ $pdg = q("SELECT COUNT(*) AS pdg FROM register WHERE reg_vital = 1 AND reg_expires > '%s' ",
+ dbesc(date('Y-m-d H:i:s'))
);
- $pending = (($pdg) ? count($pdg) : 0);
+ $pending = ($pdg ? $pdg[0]['pdg'] : 0);
// available channels, primary and clones
$channels = array();
diff --git a/Zotlabs/Module/Admin/Accounts.php b/Zotlabs/Module/Admin/Accounts.php
index 0c7e089be..74e562a9c 100644
--- a/Zotlabs/Module/Admin/Accounts.php
+++ b/Zotlabs/Module/Admin/Accounts.php
@@ -15,6 +15,9 @@ class Accounts {
*
*/
+ const MYP = 'ZAR'; // ZAR2x
+ const VERSION = '2.0.0';
+
function post() {
$pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() );
@@ -23,6 +26,68 @@ class Accounts {
check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts');
+ $isajax = is_ajax();
+ $rc = 0;
+
+ If (!is_site_admin()) {
+ if ($isajax) {
+ killme();
+ exit;
+ }
+ goaway(z_root() . '/');
+ }
+
+ if ($isajax) {
+ //$debug = print_r($_SESSION[self::MYP],true);
+ $zarop = (x($_POST['zardo']) && preg_match('/^[ad]{1,1}$/', $_POST['zardo']) )
+ ? $_POST['zardo'] : '';
+ // zarat arrives with leading underscore _n
+ $zarat = (x($_POST['zarat']) && preg_match('/^_{1,1}[0-9]{1,6}$/', $_POST['zarat']) )
+ ? substr($_POST['zarat'],1) : '';
+ $zarse = (x($_POST['zarse']) && preg_match('/^[0-9a-f]{8,8}$/', $_POST['zarse']) )
+ ? hex2bin($_POST['zarse']) : '';
+
+ if ($zarop && $zarat >= 0 && $zarse && $zarse == $_SESSION[self::MYP]['h'][$zarat]) {
+
+ //
+ if ($zarop == 'd') {
+ $rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ",
+ intval($_SESSION[self::MYP]['i'][$zarat]),
+ dbesc($_SESSION[self::MYP]['h'][$zarat])
+ );
+ $rc = '× ' . count($rd);
+ }
+ elseif ($zarop == 'a') {
+ // approval, REGISTER_DENIED by user 0x0040, REGISTER_AGREED by user 0x0020 @Regate
+ $rd = q("UPDATE register SET reg_flags = (reg_flags & ~ 16), "
+ . " reg_vital = (CASE (reg_flags & ~ 48) WHEN 0 THEN 0 ELSE 1 END) "
+ . " WHERE reg_vital = 1 AND reg_id = %d AND SUBSTR(reg_hash,1,4) = '%s' ",
+ intval($_SESSION[self::MYP]['i'][$zarat]),
+ dbesc($_SESSION[self::MYP]['h'][$zarat])
+ );
+ $rc = 0;
+ $rs = q("SELECT * from register WHERE reg_id = %d ",
+ intval($_SESSION[self::MYP]['i'][$zarat])
+ );
+ if ($rs && ($rs[0]['reg_flags'] & ~ 48) == 0) {
+
+ // create account
+ $rc='ok'.$rs[0]['reg_id'];
+ $ac = create_account_from_register($rs[0]);
+ if ( $ac['success'] ) $rc .= '✔';
+
+ } else {
+ $rc='oh×';
+ }
+ }
+
+ //
+ echo json_encode(array('re' => $zarop, 'at' => '_' . $zarat, 'rc' => $rc));
+ }
+ killme();
+ exit;
+ }
+
// change to switch structure?
// account block/unblock button was submitted
if (x($_POST, 'page_accounts_block')) {
@@ -82,6 +147,8 @@ class Accounts {
}
check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts', 't');
+
+ $debug = '';
switch (argv(2)){
case 'delete':
@@ -112,9 +179,44 @@ class Accounts {
}
/* get pending */
- $pending = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d )>0 ",
+ // [hilmar ->
+ /*
+ $pending = q("SELECT account.*, reg_hash FROM account LEFT JOIN register ON account_id = reg_uid WHERE reg_vital = 1 AND (account_flags & %d) > 0",
+ intval(ACCOUNT_PENDING)
+ );
+ */
+ $tao = 'tao.zar.zarax = ' . "'" . '<img src="' . z_root() . '/images/zapax16.gif">' . "';\n";
+
+ $pending = q("SELECT @i:=@i+1 AS reg_n, @i MOD 2 AS reg_z, "
+ ." reg_did2, reg_created, reg_startup, reg_expires, reg_email, reg_atip, reg_hash, reg_id, "
+ ." CASE (reg_flags & %d) WHEN 0 THEN '✔ verified' WHEN 1 THEN '× not yet' END AS reg_vfd "
+ ." FROM register, (SELECT @i:=0) AS i "
+ ." WHERE reg_vital = 1 AND (reg_flags & %d) > 0 ",
+ intval(ACCOUNT_UNVERIFIED),
intval(ACCOUNT_PENDING)
);
+
+ unset($_SESSION[self::MYP]);
+ if ($pending) {
+ // collect and group all ip
+ $atips = q("SELECT reg_atip AS atip, COUNT(reg_atip) AS atips FROM register "
+ ." WHERE reg_vital = 1 GROUP BY reg_atip ");
+ $atips ? $atipn = array_column($atips, 'atips', 'atip') : $atipn = array('' => 0);
+
+ $tao .= 'tao.zar.zarar = {';
+ foreach ($pending as $n => $v) {
+ if (array_key_exists($v['reg_atip'], $atipn)) {
+
+ $pending[$n]['reg_atip'] = $v['reg_atip'] . ' ◄' . $atipn[ $v['reg_atip'] ] . '×';
+ }
+ // better secure
+ $tao .= $n . ": '" . substr(bin2hex($v['reg_hash']),0,8) . "',";
+ $_SESSION[self::MYP]['h'][] = substr($v['reg_hash'],0,4);
+ $_SESSION[self::MYP]['i'][] = $v['reg_id'];
+ }
+ $tao = rtrim($tao,',') . '};' . "\n";
+ }
+ // <- hilmar]
/* get accounts */
@@ -143,7 +245,7 @@ class Accounts {
intval(\App::$pager['itemspage']),
intval(\App::$pager['start'])
);
-
+
// function _setup_users($e){
// $accounts = Array(
// t('Normal Account'),
@@ -163,12 +265,16 @@ class Accounts {
$t = get_markup_template('admin_accounts.tpl');
$o = replace_macros($t, array(
// strings //
+ '$debug' => $debug,
'$title' => t('Administration'),
'$page' => t('Accounts'),
'$submit' => t('Submit'),
'$select_all' => t('select all'),
+ '$sel_tall' => t('SelectToggle'),
+ '$sel_deny' => t('× DenySelected'),
+ '$sel_aprv' => t('✔ ApproveSelected'),
'$h_pending' => t('Registrations waiting for confirm'),
- '$th_pending' => array( t('Request date'), t('Email') ),
+ '$th_pending' => array( t('Request date'), t('Startup,Expires'), 'dId2', t('specified,atip') ),
'$no_pending' => t('No registrations.'),
'$approve' => t('Approve'),
'$deny' => t('Deny'),
@@ -187,21 +293,22 @@ class Accounts {
[ t('Expires'), 'account_expires' ],
[ t('Service Class'), 'account_service_class'] ),
- '$confirm_delete_multi' => t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?'),
- '$confirm_delete' => t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?'),
+ '$confirm_delete_multi' => p2j(t('Selected accounts will be deleted!\n\nEverything these accounts had posted on this site will be permanently deleted!\n\nAre you sure?')),
+ '$confirm_delete' => p2j(t('The account {0} will be deleted!\n\nEverything this account has posted on this site will be permanently deleted!\n\nAre you sure?')),
'$form_security_token' => get_form_security_token("admin_accounts"),
// values //
- '$baseurl' => z_root(),
-
- '$pending' => $pending,
- '$users' => $users,
+ '$now' => date('Y-m-d H:i:s'),
+ '$baseurl' => z_root(),
+ '$tao' => $tao,
+ '$pending' => $pending,
+ '$users' => $users,
));
$o .= paginate($a);
return $o;
}
-
}
+
diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php
index 011bf3ce4..cb919746b 100644
--- a/Zotlabs/Module/Admin/Site.php
+++ b/Zotlabs/Module/Admin/Site.php
@@ -10,9 +10,19 @@ class Site {
*
*/
function post(){
+ // [hilmar->
+ $this->isajax = is_ajax();
+ $this->eol = $this->isajax ? "\n" : EOL;
+ // ]
if (!x($_POST, 'page_site')) {
- return;
+ // [
+ if (!$this->isajax)
+ // ]
+ return;
}
+ // [
+ $this->msgbg = '';
+ // <-hilmar]
check_form_security_token_redirectOnErr('/admin/site', 'admin_site');
@@ -24,14 +34,16 @@ class Site {
$siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : '');
$language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : '');
$theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : '');
-// $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : '');
-// $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : '');
+ // $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : '');
+ // $site_channel = ((x($_POST,'site_channel')) ? notags(trim($_POST['site_channel'])) : '');
$maximagesize = ((x($_POST,'maximagesize')) ? intval(trim($_POST['maximagesize'])) : 0);
$register_policy = ((x($_POST,'register_policy')) ? intval(trim($_POST['register_policy'])) : 0);
$minimum_age = ((x($_POST,'minimum_age')) ? intval(trim($_POST['minimum_age'])) : 13);
$access_policy = ((x($_POST,'access_policy')) ? intval(trim($_POST['access_policy'])) : 0);
+ $reg_autochannel = ((x($_POST,'auto_channel_create')) ? True : False);
$invite_only = ((x($_POST,'invite_only')) ? True : False);
+ $invite_also = ((x($_POST,'invite_also')) ? True : False);
$abandon_days = ((x($_POST,'abandon_days')) ? intval(trim($_POST['abandon_days'])) : 0);
$register_text = ((x($_POST,'register_text')) ? notags(trim($_POST['register_text'])) : '');
@@ -75,6 +87,16 @@ class Site {
$maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50);
$feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0);
$verify_email = ((x($_POST,'verify_email')) ? 1 : 0);
+ $register_perday = ((x($_POST,'register_perday')) ? intval(trim($_POST['register_perday'])) : 50);
+ $register_sameip = ((x($_POST,'register_sameip')) ? intval(trim($_POST['register_sameip'])) : 3);
+
+ $regdelayn = ((x($_POST,'zardelayn')) ? intval(trim($_POST['zardelayn'])) : 0);
+ $regdelayu = ((x($_POST,'zardelay')) ? notags(trim($_POST['zardelay'])) : '');
+ $reg_delay = (preg_match('/^[a-z]{1,1}$/', $regdelayu) ? $regdelayn . $regdelayu : '');
+ $regexpiren = ((x($_POST,'zarexpiren')) ? intval(trim($_POST['zarexpiren'])) : 0);
+ $regexpireu = ((x($_POST,'zarexpire')) ? notags(trim($_POST['zarexpire'])) : '');
+ $reg_expire = (preg_match('/^[a-z]{1,1}$/', $regexpireu) ? $regexpiren . $regexpireu : '');
+
$imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : '');
$force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 3000);
$pub_incl = escape_tags(trim($_POST['pub_incl']));
@@ -82,6 +104,35 @@ class Site {
$permissions_role = escape_tags(trim($_POST['permissions_role']));
+ // [hilmar->
+ $this->register_duty = ((x($_POST,'register_duty')) ? notags(trim($_POST['register_duty'])) : '');
+ if (! preg_match('/^[0-9 .,:\-]{0,191}$/', $this->register_duty)) {
+ $this->msgbg .= 'ZAR0131E,' . t('Invalid input') . $this->eol;
+ $this->error++;
+ } else {
+
+ $this->duty();
+
+ if ($this->isajax) {
+ echo json_encode(array('msgbg' => $this->msgbg, 'me' => 'zar'));
+ // that mission is complete
+ killme();
+ exit;
+
+ } else {
+
+ //logger( print_r( $this->msgbg, true) );
+ //logger( print_r( $this->joo, true) );
+ if ($this->error === 0) {
+ set_config('system', 'register_duty', $this->register_duty);
+ set_config('system', 'register_duty_jso', $this->joo);
+ } else {
+ notice('ZAR0130E,'.t('Errors') . ': ' . $this->error) . EOL . $this->msgfg;
+ }
+ }
+ }
+ // <-hilmar]
+
set_config('system', 'feed_contacts', $feed_contacts);
set_config('system', 'delivery_interval', $delivery_interval);
set_config('system', 'delivery_batch_count', $delivery_batch_count);
@@ -96,6 +147,10 @@ class Site {
set_config('system', 'login_on_homepage', $login_on_homepage);
set_config('system', 'enable_context_help', $enable_context_help);
set_config('system', 'verify_email', $verify_email);
+ set_config('system', 'max_daily_registrations', $register_perday);
+ set_config('system', 'register_sameip', $register_sameip);
+ set_config('system', 'register_delay', $reg_delay);
+ set_config('system', 'register_expire', $reg_expire);
set_config('system', 'default_expire_days', $default_expire_days);
set_config('system', 'active_expire_days', $active_expire_days);
set_config('system', 'reply_address', $reply_address);
@@ -126,17 +181,19 @@ class Site {
set_config('system','siteinfo',$siteinfo);
set_config('system', 'language', $language);
set_config('system', 'theme', $theme);
-// if ( $theme_mobile === '---' ) {
-// del_config('system', 'mobile_theme');
-// } else {
-// set_config('system', 'mobile_theme', $theme_mobile);
-// }
- // set_config('system','site_channel', $site_channel);
+ // if ( $theme_mobile === '---' ) {
+ // del_config('system', 'mobile_theme');
+ // } else {
+ // set_config('system', 'mobile_theme', $theme_mobile);
+ // }
+ // set_config('system','site_channel', $site_channel);
set_config('system','maximagesize', $maximagesize);
set_config('system','register_policy', $register_policy);
set_config('system','minimum_age', $minimum_age);
+ set_config('system','auto_channel_create', $reg_autochannel);
set_config('system','invitation_only', $invite_only);
+ set_config('system','invitation_also', $invite_also);
set_config('system','access_policy', $access_policy);
set_config('system','account_abandon_days', $abandon_days);
set_config('system','register_text', $register_text);
@@ -260,6 +317,9 @@ class Site {
REGISTER_APPROVE => t("Yes - with approval"),
REGISTER_OPEN => t("Yes")
);
+ $this->register_duty ? get_config('system', 'register_duty') : '';
+ $register_perday = get_config('system','max_daily_registrations');
+ $register_perday ? '' : $register_perday = 50;
/* Acess policy */
$access_choices = Array(
@@ -286,9 +346,66 @@ class Site {
$homelogin = get_config('system','login_on_homepage');
$enable_context_help = get_config('system','enable_context_help');
+ // for reuse reg_delay and reg_expire
+ $reg_rabots = array(
+ 'i' => t('Minute(s)'),
+ 'h' => t('Hour(s)') ,
+ 'd' => t('Day(s)') ,
+ 'w' => t('Week(s)') ,
+ 'm' => t('Month(s)') ,
+ 'y' => t('Year(s)')
+ );
+ $regdelay = get_config('system','register_delay');
+ if ($regdelay)
+ list($regdelay_n, $regdelay_u) = array(substr($regdelay,0,-1),substr($regdelay,-1));
+ $reg_delay = replace_macros(get_markup_template('field_duration.qmc.tpl'),
+ array(
+ 'label' => t('Account registration delay'),
+ 'qmc' => 'zar',
+ 'qmcid' => 'ZAR0860C',
+ 'help' => t('How long a registration request has to wait before validation can perform.'
+ . ' No delay if zero or no value.'),
+ 'field' => array(
+ 'name' => 'delay',
+ 'title' => t('duration up from now'),
+ 'value' => ($regdelay_n ? $regdelay_n : 90),
+ 'min' => '0',
+ 'max' => '99',
+ 'size' => '2',
+ 'default' => ($regdelay_u ? $regdelay_u : 'i')
+ ),
+ 'rabot' => $reg_rabots
+ )
+ );
+ $regexpire = get_config('system','register_expire');
+ if ($regexpire)
+ list($regexpire_n, $regexpire_u) = array(substr($regexpire,0,-1),substr($regexpire,-1));
+ $reg_expire = replace_macros(get_markup_template('field_duration.qmc.tpl'),
+ array(
+ 'label' => t('Account registration expiration'),
+ 'qmc' => 'zar',
+ 'qmcid' => 'ZAR0862C',
+ 'help' => t('How long a registration to confirm remains valid.'
+ . ' Not expire if zero or no value.'),
+ 'field' => array(
+ 'name' => 'expire',
+ 'title' => t('duration up from now'),
+ 'value' => ($regexpire_n ? $regexpire_n : 2),
+ 'min' => '0',
+ 'max' => '99',
+ 'size' => '2',
+ 'default' => ($regexpire_u ? $regexpire_u : 'i')
+ ),
+ 'rabot' => $reg_rabots
+ )
+ );
+
+ $tao = '';
$t = get_markup_template("admin_site.tpl");
return replace_macros($t, array(
'$title' => t('Administration'),
+ // interfacing js vars
+ '$tao' => $tao,
'$page' => t('Site'),
'$submit' => t('Submit'),
'$registration' => t('Registration'),
@@ -305,21 +422,88 @@ class Site {
'$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")),
'$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices),
'$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices),
-// '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
-// '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
+ // '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile),
+ // '$site_channel' => array('site_channel', t("Channel to use for this website's static pages"), get_config('system','site_channel'), t("Site Channel")),
'$feed_contacts' => array('feed_contacts', t('Allow Feeds as Connections'),get_config('system','feed_contacts'),t('(Heavy system resource usage)')),
'$maximagesize' => array('maximagesize', t("Maximum image size"), intval(get_config('system','maximagesize')), t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")),
- '$register_policy' => array('register_policy', t("Does this site allow new member registration?"), get_config('system','register_policy'), "", $register_choices),
- '$invite_only' => array('invite_only', t("Invitation only"), get_config('system','invitation_only'), t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes.")),
'$minimum_age' => array('minimum_age', t("Minimum age"), (x(get_config('system','minimum_age'))?get_config('system','minimum_age'):13), t("Minimum age (in years) for who may register on this site.")),
'$access_policy' => array('access_policy', t("Which best describes the types of account offered by this hub?"), get_config('system','access_policy'), t("This is displayed on the public server site list."), $access_choices),
- '$register_text' => array('register_text', t("Register text"), htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'), t("Will be displayed prominently on the registration page.")),
+
+ // Register
+ // [hilmar->
+ '$register_text' => array('register_text',
+ t("Register text"),
+ htmlspecialchars(get_config('system','register_text'), ENT_QUOTES, 'UTF-8'),
+ t("Will be displayed prominently on the registration page.") . ' '
+ . t('If you operate with register duties (see ZAR0830C), best practise is to tell cleartext about here'),
+ 'ZAR0810C'),
+ '$register_policy' => array('register_policy',
+ t("Does this site allow new member registration?"),
+ get_config('system','register_policy'),
+ "",
+ $register_choices,
+ 'ZAR0820C'),
+ '$register_duty' => array('register_duty',
+ t('Registration office on duty'),
+ $this->register_duty = get_config('system', 'register_duty'),
+ t('The weekdays and hours the register office is open for registrations') . '. '
+ . t('Split weekdays and hours per `:`') . '. '
+ . t('Separate weekday(s):hour(s) pairs with blank(s)') . '. '
+ . t('Several values or ranges are to split by comma') . '. '
+ . t('From-To ranges are joined with `-`') . '. '
+ . t('ie') . ' `1-5:0900-1200,1300-1700 6:900-1230` ' . t('or') .' `1-2,4-5:800-1800` '
+ . ' <a id="zar083a" href="javascript:;">' . t('Parse and test your input') . '</a>'. EOL
+ . t('If left empty, defaults to 24h closed everyday the week.') . ' '
+ . t('To keep open 24h everyday the week, short is `-:-`.') . ' '
+ . t('Note, ranges are specified as open-close pairs and in case of')
+ . ' 0900-1200 '
+ . t('results to: opens 9h and closes 12h. If meant open 9h to 12h exactly, say `0900-1201`'),
+ 'ZAR0830C'),
+ '$register_perday' => array('register_perday',
+ t('Account registrations max per day'),
+ (x(get_config('system', 'max_daily_registrations')))
+ ? get_config('system', 'max_daily_registrations') : 50,
+ t('How many registration requests the site accepts during one day. Unlimited if zero or no value. Default 50'),
+ 'ZAR0840C'),
+ '$register_sameip' => array('register_sameip',
+ t('Account registrations from same ip'),
+ (x(get_config('system', 'register_sameip')))
+ ? get_config('system', 'register_sameip') : 3,
+ t('How many pending registration requests the site accepts from a same ip address.'),
+ 'ZAR0850C'),
+ '$reg_delay'=>$reg_delay,
+ '$reg_expire'=>$reg_expire,
+ '$reg_autochannel' => array('auto_channel_create',
+ t("Auto channel create"),
+ get_config('system','auto_channel_create'),
+ t("Auto create a channel when register a new account. When On, the register form will show additional fields for the channel-name and the nickname."),
+ "", "", 'ZAR0870C'),
+ '$invite_only' => array('invite_only',
+ t("Invitation only"),
+ get_config('system','invitation_only'),
+ t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes."),
+ "", "", 'ZAR0880C'),
+ '$invite_also' => array('invite_also',
+ t("Invitation also"),
+ get_config('system','invitation_also'),
+ t("Also allow new member registrations with an invitation code. Above register policy must be set to Yes."),
+ "", "", 'ZAR0881C'),
+ '$verify_email' => array('verify_email',
+ t("Verify Email Addresses"),
+ get_config('system','verify_email'),
+ t("Check to verify email addresses used in account registration (recommended)."),
+ "", "", 'ZAR0890C'),
+ '$abandon_days' => array('abandon_days',
+ t('Accounts abandoned after x days'),
+ get_config('system','account_abandon_days'),
+ t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.'),
+ 'appears not to be implemented (2010.01)'),
+ // <-hilmar]
+
'$role' => $role,
'$frontpage' => array('frontpage', t("Site homepage to show visitors (default: login box)"), get_config('system','frontpage'), t("example: 'pubstream' to show public stream, 'page/sys/home' to show a system webpage called 'home' or 'include:home.html' to include a file.")),
'$mirror_frontpage' => array('mirror_frontpage', t("Preserve site homepage URL"), get_config('system','mirror_frontpage'), t('Present the site homepage in a frame at the original location instead of redirecting')),
- '$abandon_days' => array('abandon_days', t('Accounts abandoned after x days'), get_config('system','account_abandon_days'), t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')),
'$allowed_sites' => array('allowed_sites', t("Allowed friend domains"), get_config('system','allowed_sites'), t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")),
- '$verify_email' => array('verify_email', t("Verify Email Addresses"), get_config('system','verify_email'), t("Check to verify email addresses used in account registration (recommended).")),
'$force_publish' => array('publish_all', t("Force publish"), get_config('system','publish_all'), t("Check to force all profiles on this site to be listed in the site directory.")),
'$disable_discover_tab' => array('disable_discover_tab', t('Import Public Streams'), $discover_tab, t('Import and allow access to public content pulled from other sites. Warning: this content is unmoderated.')),
'$site_firehose' => array('site_firehose', t('Site only Public Streams'), get_config('system','site_firehose'), t('Allow access to public content originating only from this site if Imported Public Streams are disabled.')),
@@ -361,4 +545,179 @@ class Site {
));
}
+ /**
+ * @brief Admin page site common post submit and ajax interaction
+ * @author hilmar runge
+ * @since 2020-02-04
+ * Configure register office duty weekdays and hours
+ * Syntax: weekdays:hours [weekdays:hours]
+ * [.d[,d-d.]]]:hhmm-hhmm[,hhmm-hhmm...]
+ * ranges are between blanks, days are 1-7, where 1 = Monday
+ * hours are [h]hmm 3-4digit 24 clock values
+ * ie 0900-1200,1300-1800 for hours
+ * ie 1-2,4,5 for weekdays
+ * ie 1-2:900-1800 monday and tuesday open from 9 to 18h
+ *
+ * @var $register_duty is the input field from the admin -> site page
+ * @return the results are in the class vars $error, $msgbg and $jsoo
+ * $jsoo is
+ */
+
+ // 3-4 digit 24h clock regex
+ const regxTime34 = '/^(?:2[0-3]|[01][0-9]|[0-9])[0-5][0-9]$/';
+ var $wdconst = array('','mo','tu','we','th','fr','sa','so');
+
+ // in
+ var $register_duty;
+ // intermediate
+ var $isajax;
+ // return
+ var $jsoo;
+ var $msgbg;
+ var $error = 0;
+ var $msgfg = '';
+
+ private function duty() {
+
+ $aro=array_fill(1, 7, 0);
+
+ if ($this->isajax) {
+ $op = (preg_match('/[a-z]{2,4}/', $_REQUEST['zarop'])) ? $_REQUEST['zarop'] : '';
+ if ($op == 'zar083') {
+ $this->msgbg = 'ZAR0130I Testmode:' . $this->eol . $this->msgbg;
+ } else {
+ killme();
+ exit;
+ }
+ }
+
+ if (! $this->register_duty)
+ $this->register_duty = '1-7';
+ $ranges = preg_split('/\s+/', $this->register_duty);
+ $this->msgbg .= '..ranges: ' . print_r(count($ranges),true) . $this->eol;
+
+ foreach ($ranges as $rn => $range) {
+ list($ws,$hs,) = explode(':', $range);
+
+ $ws ? $arw = explode( ',', $ws) : $arw=array();
+ $this->msgbg .= ($rn+1).'.weekday ranges: ' . count($arw) . $this->eol;
+ // $this->msgbg .= print_r($arw,true);
+ $hs ? $arh = explode( ',', $hs) : $arh=array();
+ $this->msgbg .= ($rn+1).'.hour ranges: ' . count($arh) . $this->eol;
+
+ $this->msgbg .= ($rn+1).'.wdays: ' . ( $ws ? print_r($ws,true) : 'none') . ' : '
+ . ' hours: ' . print_r($hs,true) . $this->eol;
+
+ // several hs may belog to one wd
+ // aro[0] is tmp store
+ foreach ($arh as $hs) {
+ list($ho,$hc,) = explode( '-', $hs );
+
+ // no value forces open very early, and be sure having valid hhmm values
+ !$ho ? $ho = "0000" : '';
+ !$hc ? $hc = "0000" : ''; // pseudo
+ if (preg_match(self::regxTime34, $ho)
+ && preg_match(self::regxTime34, $hc)) {
+
+ // fix pseudo, allow no reverse range
+ $hc == "0000" || $ho > $hc ? $hc = "2400" : '';
+
+ $aro[0][$ho] = 0;
+ $aro[0][$hc] = 1;
+
+ $this->msgbg .= ($ho ? ' .open:' . $ho : '') . ($hc ? ' close:' . $hc : '') .$this->eol;
+ } else {
+ $this->msgbg .= ' .' . t('Invalid 24h time value (hhmm/hmm)') . $this->eol;
+ $this->msgfg .= ' .ZAR0132E,' . t('Invalid 24h time value (hhmm/hmm)') . $this->eol;
+ $this->error++;
+ }
+ }
+
+ // the weekday(s) values or ranges
+ foreach ($arw as $ds) {
+ $wd=explode('-', $ds);
+ array_key_exists("1", $wd) && $wd[1]=="" ? $wd[1] = "7" : ''; // a case 3-
+ array_key_exists("1", $wd) && $wd[0]=="" ? $wd[0] = "1" : ''; // a case -3
+ !array_key_exists("1", $wd) ? $wd[1] = $wd[0] : ''; // a case 3
+ if ($wd[0] > $wd[1]) continue; // reverse order will be ignored // a case 5-3
+ if (preg_match('/^[1-7]{1}$/', $wd[0])) {
+ if (preg_match('/^[1-7]{1}$/', $wd[1])) {
+ // $this->msgbg .= print_r($wd,true);
+ for ($i=$wd[0]; $i<=$wd[1]; $i++) {
+ // take the tmp store for the selected day(s)
+ $aro[$i]=$aro[0];
+ }
+ }
+ }
+ }
+ //$this->msgbg .= 'aro0: ' . print_r($aro,true) . $this->eol; // 4devels
+ // clear the tmp store
+ $aro[0]=array();
+ }
+ // discart the tmp store
+ unset($aro[0]);
+ // not configured days close at the beginning 0000h
+ for ($i=1;$i<=7;$i++) { is_array($aro[$i]) ? '' : $aro[$i] = array("0000" => 1); }
+ // $this->msgbg .= 'aro: ' . print_r($aro,true) . $this->eol; // 4devels
+
+ if ($this->isajax) {
+ // tell what we have
+ // $this->msgbg .= 'aro0: ' . print_r($aro,true) . $this->eol; // 4devels
+ $this->msgbg .= 'Duty time table:' . $this->eol;
+ foreach ($aro as $dow => $hrs) {
+ $this->msgbg .= ' ' . $this->wdconst[$dow] . ' ';
+ // $this->msgbg .= '**' . print_r($hrs,true);
+ foreach ($hrs as $h => $o) {
+ $this->msgbg .= ((!$o) ? $h . ':open' : $h . ':close') . ', ';
+ }
+ $this->msgbg = rtrim($this->msgbg, ', ') . $this->eol;
+ }
+
+ $this->msgbg .= 'Generating 6 random times to check duty hours: ' . $this->eol;
+ // we only need some random dates from anyway in past or future
+ // because only the weekday and the clock is to test
+ for ($i=0; $i<6; $i++) {
+ $adow = rand(1, 7); // 1 to 7 (days)
+ $cdow = $this->wdconst[$adow];
+ // below is the essential algo to verify a date (of format Hi) meets an open or closed condition
+ $t = date('Hi', ( rand(time(), 60*60*24+time()) ) );
+ $how='closed';
+ foreach ($aro[$adow] as $o => $v) {
+ // $this->msgbg .= 'debug: ' . $o . ' gt ' . $t . ' / ' . $v . $this->eol; // 4devels
+ if ($o > $t) {
+ $how = ($v ? 'open' : 'closed');
+ break;
+ }
+ }
+ // now we know
+ $this->msgbg .= ' ' . $cdow . '.' . $t . '=' . $how . ', ';
+ }
+ $this->msgbg = rtrim($this->msgbg, ', ') . $this->eol;
+ }
+
+ /*
+ //$jov1 = array( 'view1' => $aro, 'view2' => '');
+ $jov2=array();
+ foreach ($aro as $d => $ts) {
+ foreach ($ts as $t => $ft) {
+ $jov2['view2'][$ft][] = $d.$t;
+ //$ft=="1" && $t=="0000" ? $jov2['view2']["0"][] = $d."2400" : '';
+ }
+ }
+ $this->msgbg .= print_r($jov2, true) . $this->eol; // 4devels
+ */
+
+ $this->joo = json_encode($aro);
+ // $this->msgbg .= $this->joo . $this->eol; // 4devels
+ // $this->msgbg .= print_r($aro, true) . $this->eol; // 4devels
+ $okko = (json_decode($this->joo, true) ? true : false);
+ if (!$okko) {
+ $this->msgbg .= 'ZAR0139D,json 4 duty KO crash' . $this->eol;
+ $this->msgfg .= 'ZAR0139D,json 4 duty KO crash' . $this->eol;
+ $this->error++;
+ }
+ return ;
+ }
+
+
}
diff --git a/Zotlabs/Module/Invite.php b/Zotlabs/Module/Invite.php
index 6359da54c..24792c8c1 100644
--- a/Zotlabs/Module/Invite.php
+++ b/Zotlabs/Module/Invite.php
@@ -6,7 +6,7 @@ use Zotlabs\Lib\Apps;
use Zotlabs\Web\Controller;
/**
- * module: invite.php
+ * module: invitexv2.php
*
* send email invitations to join social network
*
@@ -15,91 +15,291 @@ use Zotlabs\Web\Controller;
class Invite extends Controller {
+ /**
+ * While coding this, I want to introduce a system of qualified messages and notifications.
+ * Each message consists of a 3 letter prefix, a 4 digit number and a one letter suffix (PREnnnnS).
+ * The spirit about is not from me, but many decades used by IBM inc. in devel with best success.
+ *
+ * The system prefix, used uppercase as system message id, lowercase as css and js prefix (classes, ids etc).
+ * Usually not used as self::MYP, but placed in the code dominant enough for easy to find.
+ *
+ * Concrete here:
+ * The prefix indicates Z for the Zlabs(core), A for Account stuff, I for Invite.
+ * The numbers scope will be 00xx within/for templates, 01xx for get, 02xx for post functions.
+ * Message qualification ends with a uppercase suffix, where
+ * I=Info(only),
+ * W=Warning(more then info and less then error),
+ * E=Error,
+ * F=Fatal(for unexpected errors).
+ * Btw, in case of using fail2ban, a scan of messages going to log is very much more with ease,
+ * esspecially in multi language driven systems where messages vary.
+ *
+ * @author Hilmar Runge
+ * @version 2.0.0
+ * @since 2020-01-20
+ *
+ */
+
+ const MYP = 'ZAI';
+ const VERSION = '2.0.0';
+
function post() {
+
+ // zai02
- if(! local_channel()) {
- notice( t('Permission denied.') . EOL);
+ if (! local_channel()) {
+ notice( 'ZAI0201E,' .t('Permission denied.') . EOL);
return;
}
- if(! Apps::system_app_installed(local_channel(), 'Invite')) {
+ if (! Apps::system_app_installed(local_channel(), 'Invite')) {
+ notice( 'ZAI0202E,' . t('Invite App') . ' (' . t('Not Installed') . ')' . EOL);
return;
}
check_form_security_token_redirectOnErr('/', 'send_invite');
-
- $max_invites = intval(get_config('system','max_invites'));
- if(! $max_invites)
- $max_invites = 50;
-
- $current_invites = intval(get_pconfig(local_channel(),'system','sent_invites'));
- if($current_invites > $max_invites) {
- notice( t('Total invitation limit exceeded.') . EOL);
+
+ $ok = $ko = 0;
+ $feedbk = '';
+ $isajax = is_ajax();
+ $eol = $isajax ? "\n" : EOL;
+ $policy = intval(get_config('system','register_policy'));
+ if ($policy == REGISTER_CLOSED) {
+ notice( 'ZAI0212E,' . t('Register is closed') . ')' . EOL);
return;
- };
-
-
- $recips = ((x($_POST,'recipients')) ? explode("\n",$_POST['recipients']) : array());
- $message = ((x($_POST,'message')) ? notags(trim($_POST['message'])) : '');
-
- $total = 0;
-
- if(get_config('system','invitation_only')) {
- $invonly = true;
- $x = get_pconfig(local_channel(),'system','invites_remaining');
- if((! $x) && (! is_site_admin()))
- return;
}
+ if ($policy == REGISTER_OPEN)
+ $flags = 0;
+ elseif ($policy == REGISTER_APPROVE)
+ $flags = ACCOUNT_PENDING;
+ $flags = ($flags | intval(get_config('system','verify_email')));
+
+ // how many max recipients in one mail submit
+ $maxto = get_config('system','invitation_max_recipients', 'na');
+ If (is_site_admin()) {
+ // set, if admin is operator, default to 12
+ if ($maxto === 'na') set_config('system','invitation_max_recipients', 12);
+ }
+ $maxto = ($maxto === 'na') ? 12 : $maxto;
- foreach($recips as $recip) {
-
- $recip = trim($recip);
- if(! $recip)
- continue;
-
- if(! validate_email($recip)) {
- notice( sprintf( t('%s : Not a valid email address.'), $recip) . EOL);
- continue;
+ // language code current for the invitation
+ $lcc = x($_POST['zailcc']) && preg_match('/[a-z\-]{2,5}/', $_POST['zailcc'])
+ ? $_POST['zailcc']
+ : '';
+
+ // expiration duration amount quantity, in case of doubts defaults 2
+ $durn = x($_POST['zaiexpiren']) && preg_match('/[0-9]{1,2}/', $_POST['zaiexpiren'])
+ ? trim(intval($_POST['zaiexpiren']))
+ : '2';
+ !$durn ? $durn = 2 : '';
+
+ // expiration duration unit 1st letter (day, weeks, months, years), defaults days
+ $durq = x($_POST['zaiexpire']) && preg_match('/[ihd]{1,1}/', $_POST['zaiexpire'])
+ ? $_POST['zaiexpire']
+ : 'd';
+
+ $dur = self::calcdue($durn.$durq);
+ $due = t('Note, the invitation code is valid up to') . ' ' . $dur['due'];
+
+ if ($isajax) {
+ $feedbk .= 'ZAI0207I ' . $due . $eol;
+ }
+
+ // take the received email addresses and discart duplicates
+ $recips = array_filter( array_unique( preg_replace('/^\s*$/', '',
+ ((x($_POST,'zaito')) ? explode( "\n",$_POST['zaito']) : array() ) )));
+
+ $havto = count($recips);
+
+ if ( $havto > $maxto) {
+ $feedbk .= 'ZAI0210E ' . sprintf( t('Too many recipients for one invitation (max %d)'), $maxto) . $eol;
+ $ko++;
+
+ } elseif ( $havto == 0 ) {
+ $feedbk .= 'ZAI0211E ' . t('No recipients for this invitation') . $eol;
+ $ko++;
+
+ } else {
+
+ // each email address
+ foreach($recips as $n => $recip) {
+
+ // if empty ignore
+ $recip = $recips[$n] = trim($recip);
+ if(! $recip) continue;
+
+ // see if we have an email address who@domain.tld
+ if (!preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $recip)) {
+ $feedbk .= 'ZAI0203E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a valid email address'), $recip) . $eol;
+ $ko++;
+ continue;
+ }
+ if(! validate_email($recip)) {
+ $feedbk .= 'ZAI0204E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not a real email address'), $recip) . $eol;
+ $ko++;
+ continue;
+ }
+
+ // do we accept the email (not black listed)
+ if(! allowed_email($recip)) {
+ $feedbk .= 'ZAI0205E ' . ($n+1) . ': ' . sprintf( t('(%s) : Not allowed email address'), $recip) . $eol;
+ $ko++;
+ continue;
+ }
+
+ // is the email address just in use for account or registered before
+ $r = q("SELECT account_email AS em FROM account WHERE account_email = '%s'"
+ . " UNION "
+ ."SELECT reg_email AS em FROM register WHERE reg_vital = 1 AND reg_email = '%s' LIMIT 1;",
+ dbesc($recip),
+ dbesc($recip)
+ );
+ if($r && $r[0]['em'] == $recip) {
+ $feedbk .= 'ZAI0206E ' . ($n+1) . ': ' . sprintf( t('(%s) : email address already in use'), $recip) . $eol;
+ $ko++;
+ continue;
+ }
+
+ if ($isajax) {
+ // seems we have an email address acceptable
+ $feedbk .= 'ZAI0209I ' . ($n+1) . ': ' . sprintf( t('(%s) : Accepted email address'), $recip) . $eol;
+ }
}
+ }
+
+ if ($isajax) {
+ // we are not silent on the ajax road
+ echo json_encode(array('feedbk' => $feedbk, 'due' => $due));
+
+ // that mission is complete
+ killme();
+ exit;
+ }
+
+ // Total ?todo notice( t('Invitation limit exceeded. Please contact your site administrator.') . EOL);
+
+ // any errors up to now in fg?
+
+
+ // down from here, only on the main road (no more ajax)
+
+ // tell if sth is to tell
+ $feedbk ? notice($feedbk) . $eol : '';
+
+ if ($ko > 0) return;
+
+ // the personal mailtext
+ $mailtext = ((x($_POST,'zaitxt')) ? notags(trim($_POST['zaitxt'])) : '');
+
+ // to log in db
+ $reonar = json_decode( ((x($_POST,'zaireon')) ? notags(trim($_POST['zaireon'])) : ''), TRUE, 8) ;
+
+ // me, the invitor
+ $account = App::get_account();
+ $reonar['from'] = $account['account_email'];
+ $reonar['date'] = datetime_convert();
+ $reonar['fromip'] = $_SERVER['REMOTE_ADDR'];
+
+ // who is the invitor on
+ $inby = local_channel();
+
+ $ok = $ko = 0;
+
+ // send the mail(s)
+ foreach($recips as $n => $recip) {
- else
- $nmessage = $message;
-
- $account = App::get_account();
+ $reonar['due'] = $due;
+ $reonar['to'] = $recip;
+ $reonar['txtpersonal'] = $mailtext;
- $res = z_mail(
+ // generate an invide code to store and pm
+ $invite_code = autoname(8) . rand(1000,9999);
+
+ // again the final localized templates $reonar['subject'] $reonar['lang'] $reonar['tpl']
+
+ // save current operators lc and take the desired to mail
+ push_lang($reonar['lang']);
+ // resolve
+ $tx = replace_macros(get_intltext_template('invite.'.$reonar['tpl'].'.tpl'),
+ array(
+ '$projectname' => t('$Projectname'),
+ '$invite_code' => $invite_code,
+ '$invite_where' => z_root() . '/register',
+ '$invite_whereami' => str_replace('@', '@+', $reonar['whereami']),
+ '$invite_whoami' => z_root() . '/channel/' . $reonar['whoami'],
+ '$invite_anywhere' => z_root() . '/pubsites'
+ )
+ );
+ // restore lc to operator
+ pop_lang();
+
+ $reonar['txttemplate'] = $tx;
+
+ // pm
+ $zem = z_mail(
[
'toEmail' => $recip,
'fromName' => ' ',
- 'fromEmail' => $account['account_email'],
- 'messageSubject' => t('Please join us on $Projectname'),
- 'textVersion' => $nmessage,
+ 'fromEmail' => $reonar['from'],
+ 'messageSubject' => $reonar['subject'],
+ 'textVersion' => ($mailtext ? $mailtext . "\n\n" : '') . $tx . "\n" . $due,
]
);
- if($res) {
- $total ++;
- $current_invites ++;
- set_pconfig(local_channel(),'system','sent_invites',$current_invites);
- if($current_invites > $max_invites) {
- notice( t('Invitation limit exceeded. Please contact your site administrator.') . EOL);
- return;
- }
- }
- else {
- notice( sprintf( t('%s : Message delivery failed.'), $recip) . EOL);
+ if(!$zem) {
+
+ $ko++;
+ $msg = 'ZAI0208E,' . sprintf( t('%s : Message delivery failed.'), $recip);
+
+ } else {
+
+ $ok++;
+ $msg = 'ZAI0208I ' . sprintf( t('To %s : Message delivery success.'), $recip);
+
+ // if verify_email is the rule, email becomes a dId2 - NO
+ // $did2 = ($flags & ACCOUNT_UNVERIFIED) == ACCOUNT_UNVERIFIED ? $recip : '';
+
+ // always enforce verify email with invitations, thus email becomes a dId2
+ $did2 = $recip;
+ $flags |= ACCOUNT_UNVERIFIED;
+
+ // defaults vital, reg_pass
+ $r = q("INSERT INTO register ("
+ . "reg_flags,reg_didx,reg_did2,reg_hash,reg_created,reg_startup,reg_expires,reg_email,reg_byc,reg_uid,reg_atip,reg_lang,reg_stuff)"
+ . " VALUES ( %d, 'i', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s') ",
+ intval($flags),
+ dbesc($did2),
+ dbesc($invite_code),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc($dur['due']),
+ dbesc($recip),
+ intval($inby),
+ intval($account['account_id']),
+ dbesc($reonar['fromip']),
+ dbesc($reonar['lang']),
+ dbesc(json_encode( array('reon' => $reonar) ))
+ );
}
-
+ $msg .= ' (a' . $account['account_id'] . ', c' . $inby . ', from:' . $reonar['from'] . ')';
+ zar_log( $msg);
}
- notice( sprintf( tt("%d message sent.", "%d messages sent.", $total) , $total) . EOL);
+
+ $ok + $ko > 0
+ ? notice( 'ZAI0212I ' . sprintf( t('%1$d mail(s) sent, %2$d mail error(s)'), $ok, $ko) . EOL)
+ : '';
+ //logger( print_r( $reonar, true) );
+
return;
}
function get() {
+
+ // zai1
if(! local_channel()) {
- notice( t('Permission denied.') . EOL);
+ notice( 'ZAI0101E,' . t('Permission denied.') . EOL);
return;
}
@@ -107,68 +307,263 @@ class Invite extends Controller {
//Do not display any associated widgets at this point
App::$pdl = '';
- $o = '<b>' . t('Invite App') . ' (' . t('Not Installed') . '):</b><br>';
- $o .= t('Send email invitations to join this network');
+ $o = 'ZAI0102E,' . t('Invite App') . ' (' . t('Not Installed') . ')' . EOL;
+ return $o;
+ }
+
+ if (! (get_config('system','invitation_also') || get_config('system','invitation_only')) ) {
+ $o = 'ZAI0103E,' . t('Invites not proposed by configuration') . '. ';
+ $o .= t('Contact the site admin');
return $o;
}
+ // invitation_by_user may still not configured, the default 'na' will tell this
+ // if configured, 0 disables invitations by users, other numbers are how many invites a user may propagate
+ $invuser = get_config('system','invitation_by_user', 'na');
+
+ // if the mortal user drives the invitation
+ If (! is_site_admin()) {
+
+ // when not configured, 4 is the default
+ $invuser = ($invuser === 'na') ? 4 : $invuser;
+
+ // a config value 0 disables invitation by users
+ if (!$invuser) {
+ $o = 'ZAI0104E, ' . t('Invites by users not enabled') . '. ';
+ return $o;
+ }
+
+ if ($ihave >= $invuser) {
+ notice( 'ZAI0105W,' . t('You have no more invitations available') . EOL);
+ return '';
+ }
+
+ } else {
+ // general deity admin invite limit infinite (theoretical)
+ if ($invuser === 'na') set_config('system','invitation_by_user', 4);
+ // for display only
+ $invuser = '∞';
+ }
+
+ // xchan record of the page observer
+ // while quoting matters the user, the sending is associated with a channel (of the user)
+ // also the admin may and should decide, which channel will told to the public
+ $ob = App::get_observer();
+ if(! $ob)
+ return 'ZAI0109F,' . t('Not on xchan') . EOL;
+ $whereami = $ob['xchan_addr'];
+ $channel = App::get_channel();
+ $whoami = $channel['channel_address'];
+
+ // to pass also to post()
+ $tao = 'tao.zai.whereami = ' . "'" . $whereami . "';\n"
+ . 'tao.zai.whoami = ' . "'" . $whoami . "';\n";
+
+ // expirations, duration interval
+ $dur = self::calcdue();
+ $tao .= 'tao.zai.expire = { durn: ' . $dur['durn']
+ . ', durq: ' . "'" . $dur['durq'] . "'"
+ . ', due: ' . "'" . $dur['due'] . "' };\n";
+
+ // to easy redisplay the empty form
nav_set_selected('Invite');
+ // inform about the count of invitations we have at all
+ $r = q("SELECT count(reg_id) as ct FROM register WHERE reg_vital = 1"); // where not admin TODO
+ $wehave = ($r ? $r[0]['ct'] : 0);
+
+ // invites max for all users except admins
+ $invmaxau = intval(get_config('system','invitations_max_users'));
+ if(! $invmaxau) {
+ $invmaxau = 50;
+ if (is_site_admin()) {
+ set_config('system','invitations_max_users',intval($invmaxau));
+ }
+ }
+
+ if ($wehave > $invmaxau) {
+ if (! is_site_admin()) {
+ $feedbk .= 'ZAI0200E,' . t('All users invitation limit exceeded.') . $eol;
+ }
+ }
+
+ // let see how many invites currently used by the user
+ $r = q("SELECT count(reg_id) AS n FROM register WHERE reg_vital = 1 AND reg_byc = %d",
+ intval(local_channel()));
+ $ihave = $r ? $r[0]['n'] : 0;
+
$tpl = get_markup_template('invite.tpl');
- $invonly = false;
+
+ $inv_rabots = array(
+ 'i' => t('Minute(s)'),
+ 'h' => t('Hour(s)') ,
+ 'd' => t('Day(s)')
+ );
+ $inv_expire = replace_macros(get_markup_template('field_duration.qmc.tpl'),
+ array(
+ 'label' => t('Invitation expires after'),
+ 'qmc' => 'zai',
+ 'qmcid' => 'ZAI0014I',
+ 'field' => array(
+ 'name' => 'expire',
+ 'title' => t('duration up from now'),
+ 'value' => ($invexpire_n ? $invexpire_n : 2),
+ 'min' => '1',
+ 'max' => '99',
+ 'size' => '2',
+ 'default' => ($invexpire_u ? $invexpire_u : 'd')
+ ),
+ 'rabot' => $inv_rabots
+ )
+ );
+
+ // let generate an invite code that here and never will be applied (only to fill displayed template)
+ // real invite codes become generated for each recipient when we store the new invitation(s)
+ // $invite_code = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 8) . rand(1000,9999);
+ // let take one descriptive for template (as said is never used)
+ $invite_code = 'INVITATE2020';
- if(get_config('system','invitation_only')) {
- $invonly = true;
- $x = get_pconfig(local_channel(),'system','invites_remaining');
- if((! $x) && (! is_site_admin())) {
- notice( t('You have no more invitations available') . EOL);
- return '';
+ // what languages we use now
+ $lccmy = ((isset(App::$config['system']['language'])) ? App::$config['system']['language'] : 'en');
+ // and all the localized templates belonging to invite
+ $tpls = glob('view/*/invite.*.tpl');
+
+ $tpla=$tplx=$tplxs=array();
+ foreach ($tpls as $tpli) {
+ list( $nop, $l, $t ) = explode( '/', $tpli);
+ if ( preg_match('/\.subject/', $t) =='1' ) {
+ // indicate a subject tpl exists
+ $t=str_replace(array('invite.', '.subject', '.tpl'), '', $t);
+ $tplxs[$l][$t]=true;
+ continue;
}
+ // collect unique template names cross all languages and
+ // tpla[language][]=template those available in each language
+ $tplx[] = $tpla[$l][] = str_replace( array('invite.', '.tpl'), '', $t);
}
-
- if($invonly && ($x || is_site_admin())) {
- $invite_code = autoname(8) . rand(1000,9999);
- $nmessage = str_replace('$invite_code',$invite_code,$message);
-
- $r = q("INSERT INTO register (hash,created,uid,password,lang) VALUES ('%s', '%s',0,'','') ",
- dbesc($invite_code),
- dbesc(datetime_convert())
+ asort( $langs = array_keys($tpla) );
+ asort( $tplx = array_unique($tplx) );
+
+ // prepare current language and the default standard template (causual) for js
+ // With and in js, I use a var 'tao' as a shortcut for top array object
+ // and also qualify the object with the prefix zai = tao.zai as my var used outsite functions
+ // can be unique within the overall included spaghette whirls
+ // one can say Im too lazy to write prototypes and just I can agree.
+ // tao simply applies the fact of using the same var as object and/or array in ja.
+ $tao.='tao.zai.lccmy = ' . "'" . $lccmy . "';\n" . 'tao.zai.itpl = ' . "'" . 'casual' . "';\n";
+
+ $lcclane=$tx=$tplin='';
+ //$lccsym='<span class="fa zai_fa zai_lccsym"></span>'; // alt 
+ $tplsym='<span class="fa zai_fa"></span>';
+
+ // I will uncomment for js console debug
+ // $tao.='tao.zai.debug = ' . "'" . json_encode($tplxs) . "';\n";
+
+ // running thru the localized templates (subjects and textmsgs) and bring them to tao
+ // lcc LanguageCountryCode,
+ // lcc2 is a 2 character and lcc5 a 5 character LanguageCountryCode
+ foreach($tpla as $l => $tn) {
+
+ // restyle lc to iso getttext format to avoid errors in js, hilite the current
+ $lcc = str_replace('-', '_', $l);
+ $hi = ($l == $lccmy) ? ' zai_hi' : '';
+ $lcc2 = strlen($l) == 2 ? ' zai_lcc2' : '';
+ $lcc5 = strlen($l) == 5 ? ' zai_lcc5' : '';
+ $lccg = ' zai_lccg' . substr( $l, 0, 2 );
+ $lcclane
+ .= '<span class="fa zai_fa zai_lccsym' . $lcc2 . $lcc5 . $lccg . '"></span>'
+ . '<a href="javascript:;" class="zai_lcc' . $lcc2 . $lcc5 . $lccg . $hi . '">' . $lcc . '</a>';
+ // textmsg
+ $tao .= 'tao.zai.t.' . $lcc . ' = {};' . "\n";
+ // subject
+ $tao .= 'tao.zai.s.' . $lcc . ' = {};' . "\n";
+
+ // resolve localized templates and take intented lc for
+ foreach($tn as $t1) {
+
+ // save current lc and take the desired
+ push_lang($l);
+
+ // resolve
+ $tx = replace_macros(get_intltext_template('invite.'.$t1.'.tpl'),
+ array(
+ '$projectname' => t('$Projectname'),
+ '$invite_code' => $invite_code,
+ '$invite_where' => z_root() . '/register',
+ '$invite_whereami' => $whereami,
+ '$invite_whoami' => z_root() . '/channel/' . $whoami,
+ '$invite_anywhere' => z_root() . '/pubsites'
+ )
);
-
- if(! is_site_admin()) {
- $x --;
- if($x >= 0)
- set_pconfig(local_channel(),'system','invites_remaining',$x);
- else
- return;
- }
+
+ // a default subject if no associated exists
+ $ts=t('Invitation');
+ if ( $tplxs[$l][$t1] )
+ $ts = replace_macros(get_intltext_template('invite.'.$t1.'.subject.tpl'),
+ array(
+ '$projectname' => t('$Projectname'),
+ '$invite_loc' => get_config('system','sitename')
+ )
+ );
+
+ // restore lc to current foreground
+ pop_lang();
+
+ // bring to tao as js like it
+ $tao .= 'tao.zai.t.' . $lcc . '.' . $t1 . " = '" . rawurlencode($tx) . "';\n";
+ $tao .= 'tao.zai.s.' . $lcc . '.' . $t1 . " = '" . rawurlencode($ts) . "';\n";
}
-
- $ob = App::get_observer();
- if(! $ob)
- return $o;
-
- $channel = App::get_channel();
-
+ }
+
+ // hilite the current defauls just from the beginning
+ foreach ($tplx as $t1) {
+ $hi = ($t1 == 'casual') ? ' zai_hi' : '';
+ $tplin .= $tplsym.'<a href="javascript:;" id="zai-' . $t1
+ . '" class="invites'.$hi.'">' . $t1 . '</a>';
+ }
+
+ // fill the form for foreground
$o = replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("send_invite"),
+ '$zai' => strtolower(self::MYP),
+ '$tao' => $tao,
'$invite' => t('Send invitations'),
- '$addr_text' => t('Enter email addresses, one per line:'),
- '$msg_text' => t('Your message:'),
- '$default_message' => t('Please join my community on $Projectname.') . "\r\n" . "\r\n"
- . $linktxt
- . (($invonly) ? "\r\n" . "\r\n" . t('You will need to supply this invitation code:') . " " . $invite_code . "\r\n" . "\r\n" : '')
- . t('1. Register at any $Projectname location (they are all inter-connected)')
- . "\r\n" . "\r\n" . z_root() . '/register'
- . "\r\n" . "\r\n" . t('2. Enter my $Projectname network address into the site searchbar.')
- . "\r\n" . "\r\n" . $ob['xchan_addr'] . ' (' . t('or visit') . " " . z_root() . '/channel/' . $channel['channel_address'] . ')'
- . "\r\n" . "\r\n"
- . t('3. Click [Connect]')
- . "\r\n" . "\r\n" ,
+ '$ihave' => 'ZAI0106I, ' . t('Invitations I am using') . ': ' . $ihave . ' / ' . $invuser,
+ '$wehave' => 'ZAI0107I, ' . t('Invitations we are using') . ': ' . $wehave . ' / ' . $invmaxau,
+ '$n10' => 'ZAI0010I', '$m10' => t('§ Note, the email(s) sent will be recorded in the system logs'),
+ '$n11' => 'ZAI0011I', '$m11' => t('Enter email addresses, one per line:'),
+ '$n12' => 'ZAI0012I', '$m12' => t('Your message:'),
+ '$n13' => 'ZAI0013I', '$m13' => t('Invite template'),
+ '$inv_expire' => $inv_expire,
+ '$subject_label' => t('Subject:'),
+ '$subject' => t('Invitation'),
+ '$lcclane' => $lcclane,
+ '$tplin' => $tplin,
+ '$standard_message' => '',
+ '$personal_message' => '',
+ '$personal_pointer' => t('Here you may enter personal notes to the recipient(s)'),
+ '$due' => t('Note, the invitation code is valid up to') . ' ' . $dur['due'],
'$submit' => t('Submit')
));
-
+
return $o;
}
-
+
+ function calcdue($duri=false) {
+ // expirations, duration interval
+ if ($duri===false)
+ $duri = get_config('system','register_expire', '2d');
+ if ( preg_match( '/^[0-9]{1,2}[ihdwmy]{1}$/', $duri ) ) {
+ $durq = 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'),
+ (':'.$durq))
+ ));
+ return array( 'durn' => $durn, 'durq' => $durq, 'due' => $due);
+ }
+ return false;
+ }
}
+
diff --git a/Zotlabs/Module/Regate.php b/Zotlabs/Module/Regate.php
new file mode 100644
index 000000000..8ec559332
--- /dev/null
+++ b/Zotlabs/Module/Regate.php
@@ -0,0 +1,347 @@
+<?php
+
+namespace Zotlabs\Module;
+
+require_once('include/security.php');
+
+/**
+ *
+ * @version 2.0.0
+ * @author hilmar runge
+ * @since 2020-03-03
+ * Check verification pin
+ * input field email address
+ * input field pin (told during register)
+ * check duty
+ * check startup and expire
+ * compare email address
+ * check pin
+ * limited tries to enter the correct pin/pass 2 handle via f2b
+ * on success create account and update register
+ *
+ */
+
+ define ( 'REGISTER_AGREED', 0x0020 );
+ define ( 'REGISTER_DENIED', 0x0040 );
+
+class Regate extends \Zotlabs\Web\Controller {
+
+ const MYP = 'ZAR'; //ZAR1x
+ const VERSION = '2.0.0';
+
+
+ function post() {
+
+ check_form_security_token_redirectOnErr('/', 'regate');
+
+ if ( argc() > 1 ) {
+ $did2 = hex2bin( substr( argv(1), 0, -1) );
+ $didx = substr( argv(1), -1 );
+ }
+
+ $msg = '';
+ $nextpage = '';
+
+ if ($did2) {
+
+ $nowhhmm = date('Hi');
+ $day = date('N');
+ $now = date('Y-m-d H:i:s');
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ $isduty = zar_register_dutystate();
+ if ($isduty['isduty'] !== false && $isduty['isduty'] != 1) {
+ // normally, that should never happen here
+ // log suitable for fail2ban also
+ $logmsg = 'ZAR1230S Unexpected registration verification request for '
+ . get_config('system','sitename') . ' arrived from § ' . $ip . ' §';
+ zar_log($logmsg);
+ goaway(z_root() . '/');
+ }
+
+ // do we have a valid dId2 ?
+ if ( ($didx == 'a' && substr( $did2 , -2) == substr( base_convert( md5( substr( $did2, 1, -2) ),16 ,10), -2))
+ || ($didx == 'e') ) {
+ // check startup and expiration via [=[register
+ $r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_did2 = '%s' ", dbesc($did2) );
+ if ( $r && count($r) == 1 ) {
+ $r = $r[0];
+ // check timeframe
+ if ( $r['reg_startup'] <= $now && $r['reg_expires'] >= $now ) {
+
+ if ( isset($_POST['resend']) && $didx == 'e' ) {
+ $re = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_didx = 'e' AND reg_did2 = '%s' ", dbesc($r['reg_did2']) );
+ if ( $re && count($re) == 1 ) {
+ $re = $re[0];
+ $reonar = json_decode($re['reg_stuff'],true);
+ $reonar['subject'] = 'Re,Fwd,' . $reonar['subject'];
+ if ($reonar) {
+ $zm = zar_reg_mail($reonar);
+ $msg = ($zm) ? 'ZAR1238I ' . t('Email resent')
+ : 'ZAR1238E ' . t('Resent failed');
+ zar_log($msg . ' ' . $r['reg_did2']);
+ info($msg);
+ goaway(z_root() . '/' . $nextpage);
+ }
+ }
+ }
+
+ // check hash
+ if ( $didx == 'a' )
+ $acpin = (preg_match('/^[0-9]{6,6}$/', $_POST['acpin']) ? $_POST['acpin'] : false);
+ elseif ( $didx == 'e' )
+ $acpin = (preg_match('/^[0-9a-f]{24,24}$/', $_POST['acpin']) ? $_POST['acpin'] : false);
+ else $acpin = false;
+
+ if ( $acpin && ($r['reg_hash'] == $acpin )) {
+
+ $flags = $r['reg_flags'];
+ if ( ($flags & ACCOUNT_UNVERIFIED ) == ACCOUNT_UNVERIFIED) {
+
+ // verification success
+ $msg = 'ZAR1237I' . ' ' . t('Verify successfull');
+ $reonar = json_decode( $r['reg_stuff'], true);
+ $reonar['valid'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg;
+ // clear flag
+ $flags &= $flags ^ ACCOUNT_UNVERIFIED;
+ // sth todo?
+ $vital = $flags == 0 ? 0 : 1;
+ // set flag
+ $flags |= REGISTER_AGREED;
+ zar_log($msg . ' ' . $did2 . ':flags' . $flags . ',rid' . $r['reg_id']);
+
+ q("START TRANSACTION");
+
+ $qu = q("UPDATE register SET reg_stuff = '%s', reg_vital = %d, reg_flags = %d "
+ ." WHERE reg_id = %d ",
+ dbesc(json_encode($reonar)),
+ intval($vital),
+ intval($flags),
+ intval($r['reg_id'])
+ );
+
+ if ( ($flags & ACCOUNT_PENDING ) == ACCOUNT_PENDING ) {
+ $msg .= "\n".t('Last step will be by an instance admin to agree your account request');
+ q("COMMIT");
+ }
+ elseif ( ($flags ^ REGISTER_AGREED) == 0) {
+
+ $cra = create_account_from_register([ 'reg_id' => $r['reg_id'] ]);
+
+ if ($cra['success']) {
+
+ q("COMMIT");
+ $msg = 'ZAR1238I' . t('Account successfull created');
+ zar_log($msg . ':' . print_r($cra, true));
+ $nextpage = '~';
+ $auto_create = (get_config('system','auto_channel_create') ? true : false);
+
+ if($auto_create) {
+ if($reonar['chan.name'])
+ set_aconfig($cra['account']['account_id'],
+ 'register','channel_name',$reonar['chan.name']);
+ if($reonar['chan.did1'])
+ set_aconfig($cra['account']['account_id'],
+ 'register','channel_address',$reonar['chan.did1']);
+ }
+
+ authenticate_success($cra['account'],null,true,false,true);
+
+ if($auto_create) {
+ $new_channel = auto_channel_create($cra['account']['account_id']);
+ if($new_channel['success']) {
+ $channel_id = $new_channel['channel']['channel_id'];
+ change_channel($channel_id);
+ $nextpage = 'profiles/' . $channel_id;
+ }
+ else {
+ zar_log($new_channel['message']
+ . ', ' . $reonar['chan.did1']
+ . ', ' . $reonar['chan.name']
+ );
+ }
+ }
+ unset($_SESSION['login_return_url']);
+ }
+ else {
+ q("ROLLBACK");
+ $msg = 'ZAR1239E ' . t('Account creation error');
+ zar_log($msg . ':' . print_r($cra, true));
+ }
+ }
+ else {
+ // new flags implemented and not recognized or sth like
+ zar_log('ZAR1237D unexpected,' . $flags);
+ }
+ }
+ else {
+ // nothing to confirm
+ $msg = 'ZAR1236E' . ' ' . t('Verify failed');
+ }
+ }
+ else {
+ $msg = 'ZAR1235E' . ' ' . t('Token verification failed');
+ }
+ }
+ else {
+ $msg = 'ZAR1234W' . ' ' . t('Request not inside time frame');
+ //info($r[0]['reg_startup'] . EOL . $r[0]['reg_expire'] );
+ }
+ }
+ else {
+ $msg = 'ZAR1232E' . ' ' . t('Identity unknown');
+ zar_log($msg . ':' . $did2 . $didx);
+ }
+ }
+ else {
+ $msg = 'ZAR1231E' . t('dId2 mistaken');
+ }
+
+ }
+
+ if ($msg > '') info($msg);
+ goaway( z_root() . '/' . $nextpage );
+ }
+
+
+ function get() {
+
+ if ( argc() > 1 ) {
+ $did2 = hex2bin( substr( argv(1), 0, -1) );
+ $didx = substr( argv(1), -1 );
+ $deny = argc() > 2 ? argv(2) : '';
+ $deny = preg_match('/^[0-9a-f]{8,8}$/', $deny) ? hex2bin($deny) : false;
+ }
+
+ if ($_SESSION['zar']['msg']) {
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => t('Your Registration'),
+ '$now' => '',
+ '$infos' => $_SESSION['zar']['msg'] . EOL,
+ ]);
+ unset($_SESSION['zar']['msg']);
+ return $o;
+ }
+
+ $now = date('Y-m-d H:i:s');
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ $isduty = zar_register_dutystate();
+ $nowfmt = $isduty['nowfmt'];
+ $atform = $isduty['atform'];
+
+ $title = t('Register Verification');
+
+ // do we have a valid dId2 ?
+ if ( ($didx == 'a' && substr( $did2 , -2) == substr( base_convert( md5( substr( $did2, 1, -2) ),16 ,10), -2))
+ || ($didx == 'e') ) {
+
+ $r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_didx = '%s' AND reg_did2 = '%s'",
+ dbesc($didx),
+ dbesc($did2)
+ );
+
+ if ( $r && count($r) == 1 && $r[0]['reg_flags'] &= (ACCOUNT_UNVERIFIED | ACCOUNT_PENDING)) {
+ $r = $r[0];
+
+ // provide a button in case
+ $resend = ($r['reg_didx'] == 'e') ? t('Resend') : false;
+
+ // is still only instance admins intervention required?
+ if ( $r['reg_flags'] == ACCOUNT_PENDING ) {
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => t('Register Verification Status'),
+ '$now' => $nowfmt,
+ '$infos' => t('Soon all is well.') . EOL
+ . t('Only one instance admin has still to agree your account request.') . EOL
+ . t('Please be patient') . EOL . EOL . 'ZAR1138I',
+ ]);
+ }
+ else {
+
+ if ($deny) {
+
+ if (substr($r['reg_hash'],0,4) == $deny) {
+
+ zar_log('ZAR1134S email verfication denied ' . $did2);
+
+ $msg = 'ZAR1133A' . ' ' . t('Sorry for any inconvience. Thank you for your response.');
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => t('Registration request denied'),
+ '$now' => $nowf,
+ '$infos' => $msg . EOL,
+ ]);
+
+ $reonar = json_decode( $r['reg_stuff'], true);
+ $reonar['deny'] = $now . ',' . $ip . ' ' . $did2 . ' ' . $msg;
+ $flags = ( $r['reg_flags'] &= ( $r['reg_flags'] ^ ACCOUNT_UNVERIFIED) )
+ | ( $r['reg_flags'] |= REGISTER_DENIED);
+ $rd = q("UPDATE register SET reg_stuff='%s', reg_vital=0, reg_flags=%d WHERE reg_id = %d ",
+ dbesc(json_encode($reonar)),
+ intval($flags),
+ intval($r['reg_id'])
+ );
+ }
+ else {
+ zar_log('ZAR1135E not awaited url parameter received');
+ goaway(z_root);
+ }
+ }
+ else {
+
+ if ( $r['reg_startup'] <= $now && $r['reg_expires'] >= $now) {
+ $o = replace_macros(get_markup_template('regate.tpl'), [
+ '$form_security_token' => get_form_security_token("regate"),
+ '$title' => $title,
+ '$desc' => t('You were given a validation token. Please enter that token here to continue the register verification step and allow some delay for proccessing.'),
+ '$did2' => bin2hex($did2) . $didx,
+ '$now' => $nowfmt,
+ '$atform' => $atform,
+ '$resend' => $resend,
+ '$submit' => t('Submit'),
+ '$acpin' => [ 'acpin', t('Validation token'),'','' ],
+ ]);
+ }
+ else {
+ // expired ?
+ if ( $now > $r['reg_expires'] ) {
+ $rd = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d ",
+ intval($r['reg_id'])
+ );
+ }
+
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => $title,
+ '$now' => $nowf,
+ '$infos' => 'ZAR1132W' . ' ' . t('Request not inside time frame') . EOL,
+ ]);
+ }
+ }
+ }
+ }
+ else {
+ $msg = 'ZAR1132E' . ' ' . t('Identity unknown');
+ zar_log($msg . ':' . $did2 . ',' . $didx);
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => $title,
+ '$now' => $nowf,
+ '$infos' => $msg . EOL,
+ ]);
+ }
+
+ }
+ else {
+ $msg = 'ZAR1131E ' . t('dId2 mistaken');
+ // $log = ' from § ' . $ip . ' §' . ' (' . dbesc($did2) . ')';
+ zar_log($msg);
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => $title,
+ '$now' => $nowf,
+ '$infos' => ($msg) . EOL,
+ ]);
+ }
+
+ return $o;
+ }
+}
+
diff --git a/Zotlabs/Module/Register.php b/Zotlabs/Module/Register.php
index 278cf15ca..d0a41b3c2 100644
--- a/Zotlabs/Module/Register.php
+++ b/Zotlabs/Module/Register.php
@@ -1,4 +1,5 @@
<?php
+
namespace Zotlabs\Module;
use Zotlabs\Web\Controller;
@@ -7,7 +8,12 @@ require_once('include/security.php');
class Register extends Controller {
+ const MYP = 'ZAR'; // ZAR0x
+ const VERSION = '2.0.0';
+
function init() {
+
+ // ZAR0
$result = null;
$cmd = ((argc() > 1) ? argv(1) : '');
@@ -43,25 +49,98 @@ class Register extends Controller {
check_form_security_token_redirectOnErr('/register', 'register');
- $max_dailies = intval(get_config('system','max_daily_registrations'));
- if($max_dailies) {
- $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s",
- db_utcnow(), db_quoteinterval('1 day')
+ /**
+ * [hilmar:]
+ * It may happen, the posted form arrives in a strange fashion. With the control of the duty hours
+ * for registration, the input form was disabled at html. While receiving posted data, checks are
+ * required if all is on the right road (no posts accepted during off duty).
+ *
+ */
+
+ $act = q("SELECT COUNT(*) AS act FROM account")[0]['act'];
+ $duty = zar_register_dutystate();
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $sameip = intval(get_config('system','register_sameip'));
+
+ $arr = $_POST;
+ $invite_code = ( (x($arr,'invite_code')) ? notags(trim($arr['invite_code'])) : '');
+ $email = ( (x($arr,'email')) ? notags(punify(trim($arr['email']))) : '');
+ $password = ( (x($arr,'password')) ? trim($arr['password']) : '');
+ $reonar = array();
+
+ // assume someone tries to validate (dId2 C/D/E), because only field email entered
+ if ( $email && ( ! $invite_code ) && ( ! $password ) && ( ! $_POST['password2'] ) ) {
+
+ // dId2 logic
+
+ if ( preg_match('/^\@{1,1}.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $email ) ) {
+ // dId2 C channel - ffu
+ }
+
+ if ( preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $email ) ) {
+ // dId2 E email
+ goaway(z_root() . '/regate/' . bin2hex($email) . 'e' );
+ }
+
+ if ( preg_match('/^d{1,1}[0-9]{1,10}$/', $email ) ) {
+ // dId2 A artifical & anonymous
+ goaway(z_root() . '/regate/' . bin2hex($email) . 'a' );
+ }
+
+ }
+
+ if ($act > 0 && !$duty['isduty']) {
+ // normally, that should never arrive here (ie js hack or sth like)
+ // log suitable for f2b also
+ $logmsg = 'ZAR0230S Unexpected registration request';
+ zar_log($logmsg);
+ goaway(z_root() . '/');
+ }
+
+ if ($sameip) {
+ $f = q("SELECT COUNT(reg_atip) AS atip FROM register WHERE reg_vital = 1 AND reg_atip = '%s' ",
+ dbesc($ip)
);
- if($r && $r[0]['total'] >= $max_dailies) {
- notice( t('Maximum daily site registrations exceeded. Please try again tomorrow.') . EOL);
- return;
+ if ($f && $f[0]['atip'] > $sameip) {
+ $logmsg = 'ZAR0239S Exceeding same ip register request of ' . $sameip;
+ zar_log($logmsg);
+ goaway(z_root() . '/');
}
}
-
+
+ // s2 max daily
+ if ( self::check_max_daily_exceeded() ) return;
+
+ // accept tos
if(! x($_POST,'tos')) {
- notice( t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL);
+ notice( 'ZAR0230E '
+ . t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL);
return;
}
- $policy = get_config('system','register_policy');
+ // pw1 == pw2
+ if((! $_POST['password']) || ($_POST['password'] !== $_POST['password2'])) {
+ notice( 'ZAR0230E '
+ . t('Passwords do not match.') . EOL);
+ return;
+ }
+
+
+ $email_verify = intval(get_config('system','verify_email'));
+
+ if ($email) {
+ if ( ! preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $_POST['email'] ) ) {
+ notice('ZAR0239E '
+ . t('Email address mistake') . EOL);
+ return;
+ }
+ }
- $email_verify = get_config('system','verify_email');
+ $policy = intval(get_config('system','register_policy'));
+ $invonly = intval(get_config('system','invitation_only'));
+ $invalso = intval(get_config('system','invitation_also'));
+ $auto_create = (get_config('system','auto_channel_create') ? true : false);
+ $auto_create = true;
switch($policy) {
@@ -71,7 +150,7 @@ class Register extends Controller {
break;
case REGISTER_APPROVE:
- $flags = ACCOUNT_BLOCKED | ACCOUNT_PENDING;
+ $flags = ACCOUNT_PENDING;
break;
default:
@@ -84,103 +163,224 @@ class Register extends Controller {
break;
}
- if($email_verify && $policy == REGISTER_OPEN)
- $flags = $flags | ACCOUNT_UNVERIFIED;
+ if($email_verify && ($policy == REGISTER_OPEN || $policy == REGISTER_APPROVE) )
+ $flags = ($flags | ACCOUNT_UNVERIFIED);
-
- if((! $_POST['password']) || ($_POST['password'] !== $_POST['password2'])) {
- notice( t('Passwords do not match.') . EOL);
- return;
- }
-
- $arr = $_POST;
+ // $arr has $_POST;
$arr['account_flags'] = $flags;
-
- $result = create_account($arr);
-
- if(! $result['success']) {
- notice($result['message']);
- return;
- }
- require_once('include/security.php');
-
-
- if($_REQUEST['name'])
- set_aconfig($result['account']['account_id'],'register','channel_name',$_REQUEST['name']);
- if($_REQUEST['nickname'])
- set_aconfig($result['account']['account_id'],'register','channel_address',$_REQUEST['nickname']);
- if($_REQUEST['permissions_role'])
- set_aconfig($result['account']['account_id'],'register','permissions_role',$_REQUEST['permissions_role']);
-
-
- $using_invites = intval(get_config('system','invitation_only'));
- $num_invites = intval(get_config('system','number_invites'));
- $invite_code = ((x($_POST,'invite_code')) ? notags(trim($_POST['invite_code'])) : '');
-
- if($using_invites && $invite_code) {
- q("delete from register where hash = '%s'", dbesc($invite_code));
- // @FIXME - this also needs to be considered when using 'invites_remaining' in mod/invite.php
- set_aconfig($result['account']['account_id'],'system','invites_remaining',$num_invites);
- }
-
- if($policy == REGISTER_OPEN ) {
- if($email_verify) {
- $res = verify_email_address($result);
- }
- else {
- $res = send_register_success_email($result['email'],$result['password']);
- }
- if($res) {
- if($invite_code) {
- info( t('Registration successful. Continue to create your first channel...') . EOL ) ;
- }
- else {
- info( t('Registration successful. Please check your email for validation instructions.') . EOL ) ;
+ $now = datetime_convert();
+ $well = false;
+
+ // s3
+ if ($invite_code) {
+
+ if ($invonly || $invalso) {
+
+ $reg = q("SELECT * from register WHERE reg_vital = 1 AND reg_didx = 'i' AND reg_hash = '%s'",
+ dbesc($invite_code));
+
+ if ( $reg && count($reg) == 1 ) {
+ $reg = $reg[0];
+ if ($reg['reg_email'] == ($email)) {
+
+ if ($reg['reg_startup'] <= $now && $reg['reg_expires'] >= $now) {
+
+ // is invitor admin
+ $isa = get_account_by_id($reg['reg_uid']);
+ $isa = ( $isa && ($isa['account_roles'] && ACCOUNT_ROLE_ADMIN) );
+
+ // approve contra invite by admin
+ if ($isa && $policy == REGISTER_APPROVE)
+ $flags &= $flags ^ ACCOUNT_PENDING;
+
+ // if $flags == 0 ??
+
+ // trans ?
+
+ // update reg vital 0 off
+ $icdone = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d ",
+ intval($reg['reg_id'])
+ );
+
+ info('ZAR0237I ' . t('Invitation code succesfully applied') . EOL);
+
+ $well = true;
+
+
+ } else {
+ notice('ZAR0236E ' . t('Invitation not in time or too late') . EOL);
+ goaway(z_root());
+ }
+
+ } else {
+ // no match email adr
+ $msg = 'ZAR0235S ' . t('Invitation email failed');
+ zar_log($msg);
+ notice($msg . EOL);
+ goaway(z_root());
+ }
+
+ } else {
+ // no match invitecode
+ $msg = 'ZAR0234S ' . t('Invitation code failed') ;
+ zar_log($msg);
+ notice( $msg . EOL);
+ goaway(z_root());
}
+
+ } else {
+ notice('ZAR0232E ' . t('Invitations are not available') . EOL);
+ goaway(z_root());
}
- }
- elseif($policy == REGISTER_APPROVE) {
- $res = send_reg_approval_email($result);
- if($res) {
- info( t('Your registration is pending approval by the site owner.') . EOL ) ;
- }
- else {
- notice( t('Your registration can not be processed.') . EOL);
+
+
+ } else {
+
+ $icdone = false;
+ // no ivc entered
+ if ( ! $invonly) {
+ // possibly the email is just in use ?
+ $reg = q("SELECT * from register WHERE reg_vital = 1 AND reg_email = '%s'",
+ dbesc('e' . $email));
+
+ if ( ! $reg)
+ $act = q("SELECT * from account WHERE account_email = '%s'", dbesc($email));
+
+ // in case an invitation was made but the invitecode was not entered, better ignore.
+ // goaway(z_root() . '/regate/' . bin2hex($reg['email']));
+
+ if ( ! $reg && ! $act) {
+ // email useable
+
+ $well = true;
+
+
+ } else {
+ $msg = 'ZAR0237E ' . t('Email address already in use') . EOL;
+ notice($msg);
+ // problem, the msg tells to anonymous about existant email addrs
+ // use another msg instead ? TODO ?
+ // on the other hand can play the fail2ban game
+ zar_log($msg . ' (' . $email . ')');
+ goaway(z_root());
+ }
+
+ } else {
+ $msg = 'ZAR0233E ' . t('Registration on this hub is by invitation only') . EOL;
+ notice($msg);
+ zar_log($msg);
+ goaway(z_root());
}
- goaway(z_root());
- }
-
- if($email_verify) {
- goaway(z_root() . '/email_validation/' . bin2hex($result['email']));
+
}
- // fall through and authenticate if no approvals or verifications were required.
+ if ($well) {
- authenticate_success($result['account'],null,true,false,true);
-
- $new_channel = false;
- $next_page = 'new_channel';
-
- if(get_config('system','auto_channel_create')) {
- $new_channel = auto_channel_create($result['account']['account_id']);
- if($new_channel['success']) {
- $channel_id = $new_channel['channel']['channel_id'];
- change_channel($channel_id);
- $next_page = '~';
+ if($policy == REGISTER_OPEN || $policy == REGISTER_APPROVE ) {
+
+ $cfgdelay = get_config( 'system', 'register_delay' );
+ $regdelay = calculate_adue( $cfgdelay );
+ $regdelay = $regdelay ? $regdelay['due'] : $now;
+
+ $cfgexpire = get_config('system','register_expire' );
+ $regexpire = calculate_adue( $cfgexpire );
+ $regexpire = $regexpire ? $regexpire['due'] : '2099-12-31 23:59:59';
+
+ // handle an email request that will be verified or an ivitation associated with an email address
+ if ( $email > '' && ($email_verify || $icdone) ) {
+ // enforce in case of icdone
+ $flags |= ACCOUNT_UNVERIFIED;
+ $empin = $pass2 = random_string(24);
+ $did2 = $email;
+ $didx = 'e';
+
+ push_lang(($reg['lang']) ? $reg['lang'] : 'en');
+ $reonar['from'] = get_config('system', 'from_email');
+ $reonar['to'] = $email;
+ $reonar['subject'] = sprintf( t('Registration confirmation for %s'), get_config('system','sitename'));
+ $reonar['txtpersonal']= t('Valid from') . ' ' . $regdelay . ' ' . t('and expire') . ' ' . $regexpire;
+ $reonar['txttemplate']= replace_macros(get_intltext_template('register_verify_member.tpl'),
+ [
+ '$sitename' => get_config('system','sitename'),
+ '$siteurl' => z_root(),
+ '$email' => $email,
+ '$due' => $reonar['txtpersonal'],
+ '$mail' => bin2hex($email) . 'e',
+ '$ko' => bin2hex(substr($empin,0,4)),
+ '$hash' => $empin
+ ]
+ );
+ pop_lang();
+ zar_reg_mail($reonar);
+
+ } else {
+ // that is an anonymous request without email or with email not to verify
+ $acpin = $pass2 = rand(100000,999999);
+ $did2 = rand(10,99);
+ $didx = 'a';
+ // enforce delayed verify
+ $flags = ($flags | ACCOUNT_UNVERIFIED);
+ if ($email) {
+ $reonar['email.untrust'] = $email;
+ $reonar['email.comment'] = 'received, but no need for';
+ }
+ }
+
+ if ( $auto_create ) {
+ $reonar['chan.name'] = notags(trim($arr['name']));
+ $reonar['chan.did1'] = notags(trim($arr['nickname']));
+ }
+
+ $reg = q("INSERT INTO register ("
+ . "reg_flags,reg_didx,reg_did2,reg_hash,reg_created,reg_startup,reg_expires,"
+ . "reg_email,reg_pass,reg_lang,reg_atip,reg_stuff)"
+ . " VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
+ intval($flags),
+ dbesc($didx),
+ dbesc($did2),
+ dbesc($pass2),
+ dbesc(datetime_convert('','',$now)),
+ dbesc(datetime_convert('','',$regdelay)),
+ dbesc(datetime_convert('','',$regexpire)),
+ dbesc($email),
+ dbesc(bin2hex($password)),
+ dbesc(substr(get_best_language(),0,2)),
+ dbesc($ip),
+ dbesc(json_encode( $reonar ))
+ );
+
+ if ($didx == 'a') {
+
+ $lid = q("SELECT reg_id FROM register WHERE reg_vital = 1 AND reg_did2 = '%s' AND reg_pass = '%s' ",
+ dbesc($did2), dbesc(bin2hex($password)) );
+
+ if ($lid && count($lid) == 1 ) {
+
+ $didnew = ( $lid[0]['reg_id'] . $did2 )
+ . ( substr( base_convert( md5( $lid[0]['reg_id'] . $did2 ), 16, 10 ),-2 ) );
+
+ $reg = q("UPDATE register SET reg_did2 = CONCAT('d','%s') WHERE reg_id = %d ",
+ dbesc($didnew), intval($lid[0]['reg_id'])
+ );
+
+ // notice( 'ZAR0239I,' . t( 'Your didital id is' ) . EOL . 'd' . $didnew . EOL
+ $_SESSION['zar']['msg'] = ( 'ZAR0239I,' . t( 'Your didital id is' ) . EOL . 'd' . $didnew . EOL
+ . t('and your pin for is') . ' ' . $pass2 . EOL
+ . t('Keep these infos and your entered password safe') . EOL
+ . t('Valid from') . ' ' . $regdelay . ' ' . t('and expire') . ' ' . $regexpire . EOL );
+
+ // acpin verify
+ // goaway(z_root() . '/regate/' . bin2hex('d' . $didnew) . 'a' );
+ goaway(z_root() . '/regate');
+ }
+ else {
+ $msg = 'ZAR0239D,' . t('Error creating dId A');
+ notice( $msg );
+ zar_log( $msg . ' ' . $did2);
+ }
+ }
}
- else
- $new_channel = false;
- }
-
- $x = get_config('system','workflow_register_next');
- if($x) {
- $next_page = $x;
- $_SESSION['workflow'] = true;
}
-
- unset($_SESSION['login_return_url']);
- goaway(z_root() . '/' . $next_page);
-
}
@@ -192,7 +392,7 @@ class Register extends Controller {
if(intval(get_config('system','register_policy')) === REGISTER_CLOSED) {
if(intval(get_config('system','directory_mode')) === DIRECTORY_MODE_STANDALONE) {
- notice( t('Registration on this hub is disabled.') . EOL);
+ notice( 'ZAR0130E ' . t('Registration on this hub is disabled.') . EOL);
return;
}
@@ -201,37 +401,34 @@ class Register extends Controller {
}
if(intval(get_config('system','register_policy')) == REGISTER_APPROVE) {
- $registration_is = t('Registration on this hub is by approval only.');
- $other_sites = t('<a href="pubsites">Register at another affiliated hub.</a>');
+ $registration_is = t('Registration on this hub is by approval only.') . '<sup>ZAR0131I</sup>';
+ $other_sites = '<a href="pubsites">' . t('Register at another affiliated hub in case when prefered') . '</a>';
}
+ if ( !get_config('system', 'register_duty_jso') ) {
+ // duty yet not configured
+ $duty = array( 'isduty' => false, 'atfrm' => '', 'nowfmt' => '');
+ } else {
+ $duty = zar_register_dutystate();
+ }
$invitations = false;
-
if(intval(get_config('system','invitation_only'))) {
$invitations = true;
- $registration_is = t('Registration on this hub is by invitation only.');
- $other_sites = t('<a href="pubsites">Register at another affiliated hub.</a>');
- }
-
- $max_dailies = intval(get_config('system','max_daily_registrations'));
- if($max_dailies) {
- $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s",
- db_utcnow(), db_quoteinterval('1 day')
- );
- if($r && $r[0]['total'] >= $max_dailies) {
- logger('max daily registrations exceeded.');
- notice( t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL);
- return;
- }
+ $registration_is = t('Registration on this hub is by invitation only.') . '<sup>ZAR0132I</sup>';
+ $other_sites = '<a href="pubsites">' . t('Register at another affiliated hub') . '</a>';
+ } elseif (intval(get_config('system','invitation_also'))) {
+ $invitations = true;
}
+ if ( self::check_max_daily_exceeded() )
+ $duty['atform'] = 'disabled';
+
$privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : "");
$perm_roles = \Zotlabs\Access\PermissionRoles::roles();
// Configurable terms of service link
-
$tosurl = get_config('system','tos_url');
if(! $tosurl)
$tosurl = z_root() . '/help/TermsOfService';
@@ -254,16 +451,35 @@ class Register extends Controller {
$enable_tos = 1 - intval(get_config('system','no_termsofservice'));
- $email = array('email', t('Your email address'), ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : ""));
+ $emailval = ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : "");
+ $email = array('email',
+ t('Your email address (or leave blank to register without email)') . ' <sup>ZAR0136I</sup>',
+ $emailval,
+ t('If the registation was already submitted with your data once ago, enter your identity (like email) here and submit') . '<sup>ZAR0133I</sup>'
+ );
+
$password = array('password', t('Choose a password'), '');
$password2 = array('password2', t('Please re-enter your password'), '');
+
$invite_code = array('invite_code', t('Please enter your invitation code'), ((x($_REQUEST,'invite_code')) ? strip_tags(trim($_REQUEST['invite_code'])) : ""));
- $name = array('name', t('Your Name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.'));
+
+ //
+ $name = array('name', t('Your Name'),
+ ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.'));
$nickhub = '@' . str_replace(array('http://','https://','/'), '', get_config('system','baseurl'));
- $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), sprintf( t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub));
- $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role for your usage needs and privacy requirements.') . ' <a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles);
- $tos = array('tos', $label_tos, '', '', array(t('no'),t('yes')));
+ $nickname = array('nickname', t('Choose a short nickname'),
+ ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''),
+ sprintf( t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'),
+ $nickhub));
+ $role = array('permissions_role' , t('Channel role and privacy'),
+ ($privacy_role) ? $privacy_role : 'social',
+ t('Select a channel permission role for your usage needs and privacy requirements.')
+ . ' <a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">'
+ . t('Read more about channel permission roles')
+ . '</a>',$perm_roles);
+ //
+ $tos = array('tos', $label_tos, '', '', array(t('no'),t('yes')));
$auto_create = (get_config('system','auto_channel_create') ? true : false);
$default_role = get_config('system','default_permissions_role');
@@ -280,6 +496,9 @@ class Register extends Controller {
'$other_sites' => $other_sites,
'$invitations' => $invitations,
'$invite_code' => $invite_code,
+ '$haveivc' => t('I have an invite code') . '.<sup>ZAR0134I</sup>',
+ '$now' => $duty['nowfmt'],
+ '$atform' => $duty['atform'],
'$auto_create' => $auto_create,
'$name' => $name,
'$role' => $role,
@@ -288,15 +507,40 @@ class Register extends Controller {
'$enable_tos' => $enable_tos,
'$tos' => $tos,
'$email' => $email,
+ '$validate' => $validate,
+ '$validate_link'=> $validate_link,
+ '$validate_here'=> $validate_here,
'$pass1' => $password,
'$pass2' => $password2,
'$submit' => t('Register'),
- '$verify_note' => (($email_verify) ? t('This site requires email verification. After completing this form, please check your email for further instructions.') : ''),
+ '$verify_note' => (($email_verify) ? t('This site requires verification. After completing this form, please check the notice or your email for further instructions.') . '<sup>ZAR0135I</sup>' : ''),
));
return $o;
-
}
-
-
+
+ function check_max_daily_exceeded() {
+ // check against register, account
+ $max_dailies = intval(get_config('system','max_daily_registrations'));
+ if ( $max_dailies ) {
+ $r = q("SELECT COUNT(reg_id) AS nr FROM register WHERE reg_vital = 1 AND reg_created > %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('1 day')
+ );
+ $re = ( $r && $r[0]['nr'] >= $max_dailies ) ? true : false;
+ if ( !$re ) {
+ $r = q("SELECT COUNT(account_id) AS nr FROM account WHERE account_created > %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('1 day')
+ );
+ $re = ( $r && $r[0]['nr'] >= $max_dailies ) ? true : false;
+ }
+ if ( $re ) {
+ zar_log('ZAR0333W max daily registrations exceeded.');
+ notice( 'ZAR0333W '
+ . t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.')
+ . EOL);
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/Zotlabs/Module/Settings/Account.php b/Zotlabs/Module/Settings/Account.php
index b40f516ca..97cc9389a 100644
--- a/Zotlabs/Module/Settings/Account.php
+++ b/Zotlabs/Module/Settings/Account.php
@@ -15,20 +15,23 @@ class Account {
$account = \App::get_account();
if($email != $account['account_email']) {
- if(! validate_email($email))
- $errs[] = t('Not valid email.');
- $adm = trim(get_config('system','admin_email'));
- if(($adm) && (strcasecmp($email,$adm) == 0)) {
- $errs[] = t('Protected email address. Cannot change to that email.');
- $email = \App::$account['account_email'];
- }
- if(! $errs) {
- $r = q("update account set account_email = '%s' where account_id = %d",
- dbesc($email),
- intval($account['account_id'])
- );
- if(! $r)
- $errs[] = t('System failure storing new email. Please try again.');
+ // a DId2 not an email addr does not allow to change to email addr
+ if (strpos($email, '@') > 0) {
+ if(! validate_email($email))
+ $errs[] = t('Not valid email.');
+ $adm = trim(get_config('system','admin_email'));
+ if(($adm) && (strcasecmp($email,$adm) == 0)) {
+ $errs[] = t('Protected email address. Cannot change to that email.');
+ $email = \App::$account['account_email'];
+ }
+ if(! $errs) {
+ $r = q("update account set account_email = '%s' where account_id = %d",
+ dbesc($email),
+ intval($account['account_id'])
+ );
+ if(! $r)
+ $errs[] = t('System failure storing new email. Please try again.');
+ }
}
}
@@ -92,6 +95,7 @@ class Account {
call_hooks('account_settings', $account_settings);
$email = \App::$account['account_email'];
+ $attremail = (!strpos($email, '@')) ? 'disabled="disabled"' : '';
$tpl = get_markup_template("settings_account.tpl");
$o .= replace_macros($tpl, array(
@@ -101,7 +105,7 @@ class Account {
'$password1'=> array('npassword', t('Enter New Password'), '', ''),
'$password2'=> array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')),
'$submit' => t('Submit'),
- '$email' => array('email', t('Email Address:'), $email, ''),
+ '$email' => array('email', t('DId2 or Email Address:'), $email, '', '', $attremail),
'$removeme' => t('Remove Account'),
'$removeaccount' => t('Remove this account including all its channels'),
'$account_settings' => $account_settings
diff --git a/Zotlabs/Update/_1241.php b/Zotlabs/Update/_1241.php
new file mode 100644
index 000000000..5f806b7d1
--- /dev/null
+++ b/Zotlabs/Update/_1241.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Zotlabs\Update;
+
+require_once('include/account.php');
+
+class _1241 {
+
+ function run() {
+
+ return verify_register_scheme();
+
+ }
+
+}
diff --git a/boot.php b/boot.php
index 61622a1fe..26a5ef4c4 100755
--- a/boot.php
+++ b/boot.php
@@ -50,10 +50,10 @@ require_once('include/attach.php');
require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' );
-define ( 'STD_VERSION', '5.2.1' );
+define ( 'STD_VERSION', '5.2.2' );
define ( 'ZOT_REVISION', '6.0' );
-define ( 'DB_UPDATE_VERSION', 1240 );
+define ( 'DB_UPDATE_VERSION', 1241 );
define ( 'PROJECT_BASE', __DIR__ );
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 '&quot;' . $s . '&quot;';
diff --git a/install/schema_mysql.sql b/install/schema_mysql.sql
index 977d26232..17ad1767e 100644
--- a/install/schema_mysql.sql
+++ b/install/schema_mysql.sql
@@ -1097,16 +1097,35 @@ CREATE TABLE IF NOT EXISTS `profile_check` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `register` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `hash` char(191) NOT NULL DEFAULT '',
- `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
- `uid` int(10) unsigned NOT NULL DEFAULT 0 ,
- `password` char(191) NOT NULL DEFAULT '',
- `lang` char(16) NOT NULL DEFAULT '',
- PRIMARY KEY (`id`),
- KEY `hash` (`hash`),
- KEY `created` (`created`),
- KEY `uid` (`uid`)
+ `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_vital` (`reg_vital`),
+ KEY `ix_reg_flags` (`reg_flags`),
+ KEY `ix_reg_didx` (`reg_didx`),
+ KEY `ix_reg_did2` (`reg_did2`),
+ KEY `ix_reg_hash` (`reg_hash`),
+ 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;
CREATE TABLE IF NOT EXISTS `session` (
diff --git a/install/schema_postgres.sql b/install/schema_postgres.sql
index c04ba9c67..cdca79cfc 100644
--- a/install/schema_postgres.sql
+++ b/install/schema_postgres.sql
@@ -1092,17 +1092,37 @@ create index "pc_sec" on profile_check ("sec");
create index "pc_expire" on profile_check ("expire");
CREATE TABLE "register" (
- "id" serial NOT NULL,
- "hash" text NOT NULL,
- "created" timestamp NOT NULL,
- "uid" bigint NOT NULL,
- "password" text NOT NULL,
- "lang" varchar(16) NOT NULL,
- PRIMARY KEY ("id")
-);
-create index "reg_hash" on register ("hash");
-create index "reg_created" on register ("created");
-create index "reg_uid" on register ("uid");
+ "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")
+);
+create index "ix_reg_vital" on register ("reg_vital");
+create index "ix_reg_flags" on register ("reg_flags");
+create index "ix_reg_didx" on register ("reg_didx");
+create index "ix_reg_did2" on register ("reg_did2");
+create index "ix_reg_hash" on register ("reg_hash");
+create index "ix_reg_email" on register ("reg_email");
+create index "ix_reg_created" on register ("reg_created");
+create index "ix_reg_startup" on register ("reg_startup");
+create index "ix_reg_expires" on register ("reg_expires");
+create index "ix_reg_byc" on register ("reg_byc");
+create index "ix_reg_uid" on register ("reg_uid");
+create index "ix_reg_atip" on register ("reg_atip");
+
CREATE TABLE "session" (
"id" serial,
"sid" text NOT NULL,
diff --git a/view/css/mod_admin.css b/view/css/mod_admin.css
index 409744402..8f1b98b61 100644
--- a/view/css/mod_admin.css
+++ b/view/css/mod_admin.css
@@ -70,3 +70,7 @@
margin-top: 0px !important;
margin-left: 0px !important;
}
+
+tr.zebra9 { background-color: #eafaf1; }
+tr.zebra0 { background-color: #fbeee6; }
+tr.zebra1 { background-color: #fef9e7; }
diff --git a/view/js/mod_register.js b/view/js/mod_register.js
index 6607579a2..ba3c4cab3 100644
--- a/view/js/mod_register.js
+++ b/view/js/mod_register.js
@@ -1,5 +1,8 @@
$(document).ready(function() {
- $("#id_email").blur(function() {
+ typeof(window.tao) == 'undefined' ? window.tao = {} : '';
+ tao.zar = { vsn: '2.0.0' };
+
+ $("#id_emailNOP").blur(function() {
var zreg_email = $("#id_email").val();
$.get("register/email_check.json?f=&email=" + encodeURIComponent(zreg_email), function(data) {
$("#help_email").html(data.message);
diff --git a/view/tpl/admin_accounts.tpl b/view/tpl/admin_accounts.tpl
index 2dd56c8dc..8d6506184 100755
--- a/view/tpl/admin_accounts.tpl
+++ b/view/tpl/admin_accounts.tpl
@@ -1,15 +1,3 @@
-<script>
- function confirm_delete(uname){
- return confirm( "{{$confirm_delete}}".format(uname));
- }
- function confirm_delete_multi(){
- return confirm("{{$confirm_delete_multi}}");
- }
- function toggle_selectall(cls){
- $("."+cls).prop("checked", !$("."+cls).prop("checked"));
- return false;
- }
-</script>
<div class="generic-content-wrapper-styled" id="adminpage">
<h1>{{$title}} - {{$page}}</h1>
@@ -17,6 +5,7 @@
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<h3>{{$h_pending}}</h3>
+ {{if $debug}}<div>{{$debug}}</div>{{/if}}
{{if $pending}}
<table id="pending">
<thead>
@@ -27,20 +16,34 @@
</tr>
</thead>
<tbody>
- {{foreach $pending as $u}}
- <tr>
- <td class="created">{{$u.account_created}}</td>
- <td class="email">{{$u.account_email}}</td>
- <td class="checkbox_bulkedit"><input type="checkbox" class="pending_ckbx" id="id_pending_{{$u.hash}}" name="pending[]" value="{{$u.hash}}"></td>
+ {{foreach $pending as $n => $u}}
+ <tr class="zebra zebra{{$u.reg_z}}">
+ <td class="created">{{$u.reg_created}}<br>{{$u.reg_n}}
+ {{if $u.reg_expires < $now}} ★EXPIRED★{{/if}}</td>
+ <td class="created">{{$u.reg_startup}}<br>{{$u.reg_expires}}</td>
+ <td class="email">{{$u.reg_did2}}<br>{{$u.reg_vfd}}</td>
+ <td class="email">{{$u.reg_email}}<br>{{$u.reg_atip}}</td>
+ <td class="checkbox_bulkedit"><input type="checkbox" class="pending_ckbx" id="id_pending_{{$n}}" name="pending[]" value="{{$n}}"></td>
<td class="tools">
- <a href="{{$baseurl}}/regmod/allow/{{$u.hash}}" class="btn btn-default btn-xs" title="{{$approve}}"><i class="fa fa-thumbs-o-up admin-icons"></i></a>
- <a href="{{$baseurl}}/regmod/deny/{{$u.hash}}" class="btn btn-default btn-xs" title="{{$deny}}"><i class="fa fa-thumbs-o-down admin-icons"></i></a>
+ <a id="zara_{{$n}}" {{* href="{{$baseurl}}/regmod/allow/{{$n}}" *}} class="zar2s zara btn btn-default btn-xs" title="{{$approve}}"><i class="fa fa-thumbs-o-up admin-icons"></i></a>
+ <a id="zard_{{$n}}" {{* href="{{$baseurl}}/regmod/deny/{{$n}}" *}} class="zar2s zard btn btn-default btn-xs" title="{{$deny}}"><i class="fa fa-thumbs-o-down admin-icons"></i></a>
+ <span id="zarreax_{{$n}}" class="zarreax"></span>
</td>
</tr>
{{/foreach}}
</tbody>
</table>
- <div class="selectall"><a href="#" onclick="return toggle_selectall('pending_ckbx');">{{$select_all}}</a></div>
+ {{* before, alternate:
+ *
+ <a href="#" onclick="return toggle_selectall('pending_ckbx');">{{$select_all}}</a>
+ *
+ *}}
+ <div class="selectall">
+ <a id="zar2aas" class="zar2xas btn btn-primary" href="javascript:;">{{$sel_aprv}}</a> ◄►
+ <a id="zar2das" class="zar2xas btn btn-primary" href="javascript:;">{{$sel_deny}}</a> ◄►
+ <a id="zar2sat" class="btn btn-primary" href="javascript:;">{{$sel_tall}}</a>
+ <br><br>
+ </div>
<div class="submit">
<input type="submit" name="page_accounts_deny" class="btn btn-primary" value="{{$deny}}" />
<input type="submit" name="page_accounts_approve" class="btn btn-primary" value="{{$approve}}" />
@@ -83,7 +86,11 @@
{{/foreach}}
</tbody>
</table>
+
+ <div class="selectall"><a id="zarckbxtoggle" href="javascript:;">{{$select_all}}</a></div>
+ {{*
<div class="selectall"><a href="#" onclick="return toggle_selectall('users_ckbx');">{{$select_all}}</a></div>
+ *}}
<div class="submit">
<input type="submit" name="page_accounts_block" class="btn btn-primary" value="{{$block}}/{{$unblock}}" />
<input type="submit" name="page_accounts_delete" class="btn btn-primary" onclick="return confirm_delete_multi()" value="{{$delete}}" />
@@ -93,3 +100,63 @@
{{/if}}
</form>
</div>
+{{*
+ COMMENTS for this template:
+ hilmar, 2020.01
+ script placed at the end
+*}}
+<script>
+ function confirm_delete(uname){
+ return confirm( "{{$confirm_delete}}".format(uname));
+ }
+ function confirm_delete_multi(){
+ return confirm("{{$confirm_delete_multi}}");
+ }
+ function toggle_selectall(cls){
+ $("."+cls).prop("checked", !$("."+cls).prop("checked"));
+ return false;
+ }
+ // @hilmar |->
+ typeof(window.tao) == 'undefined' ? window.tao = {} : '';
+ tao.zar = { vsn: '2.0.0', c2s: {}, t: {} };
+ {{$tao}}
+ $('#adminpage').on( 'click', '#zar2sat', function() {
+ $('input.pending_ckbx:checkbox').each( function() { this.checked = ! this.checked; });
+ });
+ $('#adminpage').on( 'click', '.zar2xas', function() {
+ tao.zar.c2s.x = $(this).attr('id').substr(4,1);
+ $('input.pending_ckbx:checkbox:checked').each( function() {
+ //if (this.checked)
+ // take the underscore with to prevent numeric 0 headdage
+ tao.zar.c2s.n = $(this).attr('id').substr(10);
+ $('#zarreax'+tao.zar.c2s.n).html(tao.zar.zarax);
+ zarCSC();
+ });
+ });
+ $('.zar2s').click( function() {
+ tao.zar.c2s.ix=$(this).attr('id');
+ if (tao.zar.c2s.ix=='') { return false; };
+ tao.zar.c2s.n=tao.zar.c2s.ix.substr(4);
+ tao.zar.c2s.x=tao.zar.c2s.ix.substr(3,1);
+ $('#zarreax'+tao.zar.c2s.n).html(tao.zar.zarax);
+ zarCSC();
+ });
+
+ function zarCSC() {
+ $.ajax({
+ type: 'POST', url: 'admin/accounts',
+ data: {
+ zarat: tao.zar.c2s.n,
+ zardo: tao.zar.c2s.x,
+ zarse: tao.zar.zarar[(tao.zar.c2s.n).substr(1)],
+ form_security_token: $("input[name='form_security_token']").val()
+ }
+ }).done( function(r) {
+ tao.zar.r = JSON.parse(r);
+ $('#zarreax'+tao.zar.r.at).html(tao.zar.r.re + ',' + tao.zar.r.rc);
+ $('#zara'+tao.zar.r.at+',#zard'+tao.zar.r.at+',#id_pending'+tao.zar.r.at).remove();
+ //$('#zar-remsg').text(tao.zar.r.feedbk);
+ })
+ }
+
+</script>
diff --git a/view/tpl/admin_site.tpl b/view/tpl/admin_site.tpl
index 8d32ba9a4..4896a2b26 100755
--- a/view/tpl/admin_site.tpl
+++ b/view/tpl/admin_site.tpl
@@ -1,39 +1,3 @@
-<script>
- $(function(){
-
- $("#cnftheme").colorbox({
- width: 800,
- onLoad: function(){
- var theme = $("#id_theme :selected").val();
- $("#cnftheme").attr('href',"{{$baseurl}}/admin/themes/"+theme);
- },
- onComplete: function(){
- $(this).colorbox.resize();
- $("#colorbox form").submit(function(e){
- var url = $(this).attr('action');
- // can't get .serialize() to work...
- var data={};
- $(this).find("input").each(function(){
- data[$(this).attr('name')] = $(this).val();
- });
- $(this).find("select").each(function(){
- data[$(this).attr('name')] = $(this).children(":selected").val();
- });
- console.log(":)", url, data);
-
- $.post(url, data, function(data) {
- if(timer) clearTimeout(timer);
- updateInit();
- $.colorbox.close();
- })
-
- return false;
- });
-
- }
- });
- });
-</script>
<div id="adminpage" class="generic-content-wrapper-styled">
<h1>{{$title}} - {{$page}}</h1>
@@ -69,10 +33,19 @@
{{include file="field_input.tpl" field=$register_text}}
{{include file="field_select_grouped.tpl" field=$role}}
{{include file="field_select.tpl" field=$register_policy}}
+ {{include file="register_duty.tpl" field=$register_duty}}
+ {{include file="field_input.tpl" field=$register_perday}}
+ {{include file="field_input.tpl" field=$register_sameip}}
+ {{$reg_delay}}
+ {{$reg_expire}}
+ {{include file="field_checkbox.tpl" field=$reg_autochannel}}
{{include file="field_checkbox.tpl" field=$invite_only}}
+ {{include file="field_checkbox.tpl" field=$invite_also}}
{{include file="field_input.tpl" field=$minimum_age}}
{{include file="field_select.tpl" field=$access_policy}}
{{include file="field_input.tpl" field=$location}}
+ {{include file="field_select.tpl" field=$access_policy}}
+ {{include file="field_input.tpl" field=$minimum_age}}
{{include file="field_input.tpl" field=$sellpage}}
{{include file="field_input.tpl" field=$first_page}}
@@ -115,3 +88,51 @@
</form>
</div>
+{{*
+ COMMENTS for this template:
+ hilmar, 2020.01
+ script placed at the end
+*}}
+<script>
+ $(function(){
+
+ $("#cnftheme").colorbox({
+ width: 800,
+ onLoad: function(){
+ var theme = $("#id_theme :selected").val();
+ $("#cnftheme").attr('href',"{{$baseurl}}/admin/themes/"+theme);
+ },
+ onComplete: function(){
+ $(this).colorbox.resize();
+ $("#colorbox form").submit(function(e){
+ var url = $(this).attr('action');
+ // can't get .serialize() to work...
+ var data={};
+ $(this).find("input").each(function(){
+ data[$(this).attr('name')] = $(this).val();
+ });
+ $(this).find("select").each(function(){
+ data[$(this).attr('name')] = $(this).children(":selected").val();
+ });
+ console.log(":)", url, data);
+
+ $.post(url, data, function(data) {
+ if(timer) clearTimeout(timer);
+ updateInit();
+ $.colorbox.close();
+ })
+
+ return false;
+ });
+
+ }
+ });
+ });
+ // [hilmar->
+ $('head').append(
+ '<style> '+
+ ' .zuiqmid { font-weight: normal; font-family: monospace; }'+
+ ' .zui_n { width: 5em; text-align: center; }'+
+ '</style>');
+ // <-hilmar]
+</script>
diff --git a/view/tpl/invite.tpl b/view/tpl/invite.tpl
index 440e1e02b..2712cfee4 100755
--- a/view/tpl/invite.tpl
+++ b/view/tpl/invite.tpl
@@ -1,6 +1,7 @@
<div id="invite" class="generic-content-wrapper">
<div class="section-title-wrapper">
- <h2>{{$invite}}</h2>
+ <h3 class="zai_il">{{$invite}}</h3>
+ <h4 class="zai_il">{{$lcclane}}</h4>
</div>
<div class="section-content-wrapper">
@@ -8,21 +9,136 @@
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
+ <pre>{{$ihave}}<br>{{$wehave}}</pre>
+
+ <div id="zai-re" style="visibility: hidden;">
+ <div class="zai_h0 fa"></div>
+ <pre id="zai-remsg"></pre>
+ </div>
+
<div id="invite-recipient-textarea" class="form-group field custom">
- <label for="recipients">{{$addr_text}}</label>
- <textarea id="invite-recipients" name="recipients" rows="6" class="form-control"></textarea>
+
+ <label for="zaito">{{$m11}}<sup class="zai_qmc">({{$n11}})</sup></label>
+ <textarea id="zai-to" name="zaito" rows="6" class="form-control"></textarea>
+
+ <span class="font-weight-bold">{{$m10}}<sup class="zai_qmc">({{$n10}})</sup></span>
+ <a id="zai-ax" href="javascript:;" class="zai_ax zai_b">check</a><br>
+
+ <hr>
+ {{$inv_expire}}
</div>
- <div id="invite-message-textarea" class="form-group field custom">
- <label for="message">{{$msg_text}}</label>
- <textarea id="invite-message" name="message" rows="12" class="form-control">{{$default_message}}</textarea>
+ <hr>
+
+ <div class="">
+ <div class="zai_h0">{{$subject_label}}
+ <span id="zai-subject">{{$subject}}</span>
+ </div>
+
+ <div id="invite-message-textarea" class="form-group field custom">
+ <label for="zaitxt">{{$m12}}<sup class="zai_qmc">({{$n12}})</sup></label>
+ <textarea id="zai-txt" name="zaitxt" rows="6" class="form-control">{{$personal_message}}</textarea>
+ </div>
</div>
+ <div class="zai_h0">{{$m13}}</div><sup class="zai_qmc">({{$n13}})</sup> {{$tplin}}<br>
+ <pre id="zai-ims">
+ {{$standard_message}}
+ </pre>
+ <pre id="zai-due">
+ {{$due}}
+ </pre>
+
<div id="invite-submit-wrapper" class="form-group">
<button class="btn btn-primary btn-sm" type="submit" id="invite-submit" name="submit" value="{{$submit}}">{{$submit}}</button>
</div>
-
+ <input type='hidden' id="zai-reon" name='zaireon' value=''>
</form>
</div>
</div>
+
+<script>
+ // @hilmar |->
+ typeof(window.tao) == 'undefined' ? window.tao = {} : '';
+ tao.zai = { vsn: '2.0.0', s: {}, t: {} };
+ {{$tao}}
+ $('head').append(
+ '<style> '+
+ ' .zai_h0 { font-size: 1.2rem; display: inline; }'+
+ ' .zai_hi { background: #ffc107; font-weight: bold; }'+
+ ' .zai_fa { margin: 0 0.2em 0 1em; }'+
+ ' .zai_lcc, .zai_qmc, .zuiqmid { font-family: monospace; text-transform: uppercase; }'+
+ ' .zai_lcc5 { display: none; }'+
+ ' .zai_ax { margin-inline: 8rem; }'+
+ ' .zai_il { display: inline; }'+
+ ' .zai_b { font-weight: bold; }'+
+ ' .zai_n { width: 5em; text-align: center; }'+
+ ' #id_zaiexpire_fs { display: inline-block; }'+
+ ' .invites { text-transform: capitalize; }'+
+ ' .jGrowl-message { font-family: monospace; }'+
+ '</style>');
+ $('#zai-txt').attr('placeholder','{{$personal_pointer}}');
+ zaitx();
+ $('.zuiqmid').removeClass('required');
+ $('#invite')
+ .delegate('.invites', 'click', function() {
+ tao.zai.itpl=$(this).text();
+ $('.invites').removeClass('zai_hi');
+ $('#zai-'+tao.zai.itpl).addClass('zai_hi');
+ zaitx();
+ })
+ .delegate('.zai_lcc', 'click', function() {
+ tao.zai.lcc=$(this).text();
+ if ( $(this).hasClass('zai_lcc2') ) {
+ tao.zai.lccg = '.zai_lccg' + tao.zai.lcc.substr(0,2);
+ $('.zai_lcc5:not('+tao.zai.lccg+')').hide();
+ if ( $(this).hasClass('zai_hi') ) {
+ $('.zai_lcc5'+tao.zai.lccg).toggle();
+ }
+ }
+ $('.zai_lcc').removeClass('zai_hi');
+ $(this).addClass('zai_hi');
+ $.each( tao.zai.t[tao.zai.lcc], function(k,v) {
+ tao.zai.lccmy=tao.zai.lcc;
+ });
+ zaitx();
+ });
+ $('#zai-ax').click( function() {
+ tao.zai.c2s={};
+ tao.zai.c2s.to=$('#zai-to').val();
+ if (tao.zai.c2s.to=='') { return false; };
+ // tao.zai.c2s.lcc=$('.zai_lcc.zai_hi').text();
+ $.ajax({
+ type: 'POST', url: 'invite',
+ data: {
+ zaito: tao.zai.c2s.to,
+ zailcc: tao.zai.lccmy,
+ zaidurn: $('#zaiexpiren').val(),
+ zaidurq: $('input[name="zaiexpire"]:checked').val(),
+ form_security_token: $("input[name='form_security_token']").val()
+ }
+ }).done( function(r) {
+ tao.zai.r = JSON.parse(r);
+ $('#zai-re').attr('style', 'visibility: show;');
+ $('#zai-remsg').text(tao.zai.r.feedbk);
+ $('#zai-due').text(tao.zai.r.due);
+ })
+ });
+ $('#invite-submit').click( function() {
+ // $('#zai-txt').val($('#zai-ims').text());
+ tao.zai.reon = {subject: $('#zai-subject').text(),
+ lang: tao.zai.lccmy, tpl: tao.zai.itpl,
+ whereami: tao.zai.whereami, whoami: tao.zai.whoami};
+ $('#zai-reon').val(JSON.stringify(tao.zai.reon));
+ });
+ function zaitx() {
+ typeof(tao.zai.s[tao.zai.lccmy][tao.zai.itpl]) !== 'undefined'
+ ? $('#zai-subject').text(decodeURIComponent(tao.zai.s[tao.zai.lccmy][tao.zai.itpl]))
+ : $('#zai-subject').text('Invitation');
+ typeof(tao.zai.t[tao.zai.lccmy][tao.zai.itpl]) !== 'undefined'
+ ? $('#zai-ims').text(decodeURIComponent(tao.zai.t[tao.zai.lccmy][tao.zai.itpl]))
+ : $('#zai-ims').text(' ');
+ }
+ // @hilmar <-|
+</script>
diff --git a/view/tpl/plain.tpl b/view/tpl/plain.tpl
new file mode 100644
index 000000000..9af76718e
--- /dev/null
+++ b/view/tpl/plain.tpl
@@ -0,0 +1,3 @@
+<h2>{{$title}}</h2>
+{{if $now}}<div>{{$now}}</div>{{/if}}
+<div style="font-weight: normal; font-family: monospace;">{{$infos}}</div>
diff --git a/view/tpl/regate.tpl b/view/tpl/regate.tpl
new file mode 100644
index 000000000..38135d424
--- /dev/null
+++ b/view/tpl/regate.tpl
@@ -0,0 +1,23 @@
+<h2>{{$title}}</h2>
+
+<h3>{{$now}}</h3>
+
+<div class="descriptive-paragraph" style="font-size: 1.2em;"><p>{{$desc}}</p></div>
+
+<form action="regate/{{$did2}}" method="post">
+<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
+{{include file="field_input.tpl" field=[$acpin.0,$acpin.1,"","","",$atform]}}
+
+<div class="pull-right submit-wrapper">
+ <button type="submit" name="submit" class="btn btn-primary"{{$atform}}>{{$submit}}</button>
+</div>
+
+{{if $resend > ''}}
+<div class="resend-email" >
+ <button type="submit" name="resend" class="btn btn-warning"{{$atform}}>{{$resend}}</button>
+</div>
+{{/if}}
+
+</form>
+<div class="clear"></div>
+
diff --git a/view/tpl/register.tpl b/view/tpl/register.tpl
index 1054c7567..451a16c2a 100755
--- a/view/tpl/register.tpl
+++ b/view/tpl/register.tpl
@@ -9,6 +9,7 @@
<div class="section-content-warning-wrapper">
<div id="register-desc" class="descriptive-paragraph">{{$reg_is}}</div>
<div id="register-sites" class="descriptive-paragraph">{{$other_sites}}</div>
+ <h2>{{$now}}</h2>
</div>
{{/if}}
@@ -19,14 +20,19 @@
{{/if}}
{{if $invitations}}
- {{include file="field_input.tpl" field=$invite_code}}
+ <div style="text-align: center;">
+ <a id="zar014" href="javascript:;" style="display: inline-block;">{{$haveivc}}</a>
+ </div>
+ <div id="zar015" style="display: none;">
+ {{include file="field_input.tpl" field=[$invite_code.0,$invite_code.1,"","","",$atform]}}
+ </div>
{{/if}}
- {{include file="field_input.tpl" field=$email}}
+ {{include file="field_input.tpl" field=[$email.0,$email.1,"",$email.3,"",""]}}
- {{include file="field_password.tpl" field=$pass1}}
+ {{include file="field_password.tpl" field=[$pass1.0,$pass1.1,"","","",$atform]}}
- {{include file="field_password.tpl" field=$pass2}}
+ {{include file="field_password.tpl" field=[$pass2.0,$pass2.1,"","","",$atform]}}
{{if $auto_create}}
{{if $default_role}}
@@ -46,12 +52,12 @@
{{/if}}
{{if $enable_tos}}
- {{include file="field_checkbox.tpl" field=$tos}}
+ {{include file="field_checkbox.tpl" field=[$tos.0,$tos.1,"","","",$atform]}}
{{else}}
<input type="hidden" name="tos" value="1" />
{{/if}}
- <button class="btn btn-primary" type="submit" name="submit" id="newchannel-submit-button" value="{{$submit}}">{{$submit}}</button>
+ <button class="btn btn-primary" type="submit" name="submit" id="newchannel-submit-button" value="{{$submit}}" {{$atform}}>{{$submit}}</button>
<div id="register-submit-end" class="register-field-end"></div>
</form>
<br />
@@ -59,3 +65,20 @@
</div>
</div>
+{{*
+ COMMENTS for this template:
+ hilmar, 2020.02
+*}}
+<script>
+ $('head').append(
+ '<style> '+
+ ' .zuiqmid { font-weight: normal; font-family: monospace; }'+
+ '</style>');
+ // does not work $('#id_email').off('blur');
+ $('#id_email').change( function() {
+ if ($('#id_email').val().length > 0) {
+ $('#newchannel-submit-button').removeAttr('disabled');
+ }
+ });
+ $('#zar014').click( function () { $('#zar015').toggle(); });
+</script>
diff --git a/view/tpl/register_duty.tpl b/view/tpl/register_duty.tpl
new file mode 100644
index 000000000..d5a66e556
--- /dev/null
+++ b/view/tpl/register_duty.tpl
@@ -0,0 +1,28 @@
+{{include file="field_input.tpl" field=$register_duty}}
+<pre id="zar083msg" class='zarhid'></pre>
+<script>
+ // @hilmar |->
+ typeof(window.tao) == 'undefined' ? window.tao = {} : '';
+ tao.zar = { vsn: '2.0.0', s: {}, t: {} };
+ {{$tao}}
+ $('head').append(
+ '<style> '+
+ ' .zarmsg { font-family: monospace; }'+
+ ' .zarhid { visibility: hidden; }'+
+ '</style>');
+ tao.zar.op = 'zar083';
+ $('#zar083a').click( function() {
+ $.ajax({
+ type: 'POST', url: 'admin/site',
+ data: {
+ zarop: tao.zar.op,
+ register_duty: $('#id_register_duty').val(),
+ form_security_token: $("input[name='form_security_token']").val()
+ }
+ }).done( function(r) {
+ tao.zar.r = JSON.parse(r);
+ $('#zar083msg').attr('style', 'visibility: visible;');
+ $('#zar083msg').text(tao.zar.r.msgbg);
+ })
+ });
+ </script> \ No newline at end of file