aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.air48
-rw-r--r--Zotlabs/Lib/Enotify.php14
-rw-r--r--Zotlabs/Module/Admin.php9
-rw-r--r--Zotlabs/Module/Admin/Accounts.php187
-rw-r--r--Zotlabs/Module/Admin/Site.php415
-rw-r--r--Zotlabs/Module/Display.php2
-rw-r--r--Zotlabs/Module/Invite.php629
-rw-r--r--Zotlabs/Module/Regate.php382
-rw-r--r--Zotlabs/Module/Register.php652
-rw-r--r--Zotlabs/Module/Settings/Account.php34
-rw-r--r--Zotlabs/Module/Sse_bs.php5
-rw-r--r--Zotlabs/Render/SmartyTemplate.php16
-rw-r--r--Zotlabs/Update/_1244.php15
-rw-r--r--boot.php2
-rw-r--r--doc/admin/zarlog_msgs.md112
-rw-r--r--doc/context/de/register/help.html12
-rw-r--r--doc/context/en/register/help.html12
-rw-r--r--images/default_profile_photos/human_confusion/300.pngbin0 -> 36281 bytes
-rw-r--r--images/default_profile_photos/human_confusion/48.pngbin0 -> 1740 bytes
-rw-r--r--images/default_profile_photos/human_confusion/80.pngbin0 -> 4054 bytes
-rw-r--r--images/zapax16.gifbin0 -> 826 bytes
-rw-r--r--include/account.php586
-rw-r--r--include/datetime.php36
-rw-r--r--include/security.php106
-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/de-de/invite.casual.subject.tpl1
-rw-r--r--view/de-de/invite.casual.tpl15
-rw-r--r--view/de-de/invite.formal.subject.tpl1
-rw-r--r--view/de-de/invite.formal.tpl19
-rw-r--r--view/de-de/register_verify_member.tpl57
-rw-r--r--view/en/invite.casual.subject.tpl1
-rw-r--r--view/en/invite.casual.tpl16
-rw-r--r--view/en/invite.formal.subject.tpl1
-rw-r--r--view/en/invite.formal.tpl32
-rw-r--r--view/en/invite.material.subject.tpl1
-rw-r--r--view/en/invite.material.tpl1
-rw-r--r--view/en/register_verify_member.tpl8
-rw-r--r--view/es-es/invite.casual.subject.tpl1
-rw-r--r--view/es-es/invite.casual.tpl14
-rw-r--r--view/es-es/invite.formal.subject.tpl1
-rw-r--r--view/es-es/invite.formal.tpl19
-rw-r--r--view/js/main.js2
-rw-r--r--view/js/mod_register.js101
-rw-r--r--view/tpl/admin_accounts.tpl113
-rw-r--r--view/tpl/admin_site.tpl99
-rw-r--r--view/tpl/field_checkbox.tpl22
-rw-r--r--view/tpl/field_duration.qmc.tpl87
-rw-r--r--view/tpl/field_input.tpl18
-rw-r--r--view/tpl/field_select.tpl17
-rw-r--r--view/tpl/invite.tpl130
-rw-r--r--view/tpl/plain.tpl37
-rw-r--r--view/tpl/regate.tpl29
-rw-r--r--view/tpl/register.tpl43
-rw-r--r--view/tpl/register_duty.tpl28
57 files changed, 3704 insertions, 581 deletions
diff --git a/CHANGELOG.air b/CHANGELOG.air
new file mode 100644
index 000000000..d1fd0f05e
--- /dev/null
+++ b/CHANGELOG.air
@@ -0,0 +1,48 @@
+"air" is a branch name for revision of Account-Invite-Register at the Hubzilla project
+
+Invite:
+* Rewritten and now language template driven
+* Selectable templates for the invite mails
+* Invitor may add personal notes in the mailtext
++ Invite codes are bound to the recipients email address
+* Invite mod never more creates accounts
+* new db scheme for table register
+* existing register table will be migrated to the new schema even when detected at runtime
+* Bugfix: creating invite codes when admin only calls Invite w/o any further action
+* account library revision also together with invite mod
+* Depending on config: Users may send invitations also
+* Invitations expires, controlled by the invitor
+* Changed and new configs:
+* * invitation_only As usual before
+* * invitation_also Beside other registration policies, invitations may be used also
+* * invitation_max_per_day defaults 50, may be changed in adminUI admin>site
+* * invitation_max_per_user defaults 4
+* Requirements:
+* * Addon language has to be installed
+
+Register:
+* Register panel (form) and js interaction changed
+* Unused registrations expire
+* Depending on config, anonymous registrations (w/o email) are supported
+* :... dont't panic, that may let grow security
+* Even anonymous users have to confirm their registration
+* Registrations may be enabled / disabled time driven for each day in the week (dudy)
+* Unsoliced registration floods may be blocked
+* Limited registrations from one single source ip
+* Using one additional log file, to easy interfare with f2b
+
+Account:
+* An user account always becomes created only if all depending conditions are satisfied
+* AdminUI for site configuration, accounts and registrations enhancements
+* Still untouched, but accountUI needs enhanced async control in case for mass delete
+ with deep level of recursion cascade of the dependencies (channels etc). An open TODO
+ since years for instances with many much users and channels.
+
+History:
+2020.03 Hubzilla Prod version 4.6 (master branch) of hubzilla/core was the base for AIR
+ that was assigned Version 4.6.2 at sn/core
+2021.02 Hubzilla Prod version 5.2.1 (master branch) of hubzilla/core was new base for AIR
+ that was assigned version 5.2.2 at sn/core (air.5)
+ plus adjustment of hubzilla 5.2.2 (master) to sn/core (air.5) version 5.2.9
+
+
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index d2a0f0abc..be93e655c 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -976,14 +976,20 @@ class Enotify {
static public function format_register($rr) {
+ $policy = intval(get_config('system','register_policy'));
+
+ $message = (($rr['reg_vfd']) ? t('verified') : t('not verified'));
+ if(!$rr['reg_vfd'] && $rr['reg_expires'] < datetime_convert())
+ $message = t('expired');
+
$x = [
'notify_link' => z_root() . '/admin/accounts',
- 'name' => $rr['account_email'],
- //'addr' => $rr['account_email'],
+ 'name' => (($rr['reg_email']) ? $rr['reg_email'] : $rr['reg_did2']),
+ //'addr' => '',
'photo' => z_root() . '/' . get_default_profile_photo(48),
- 'when' => datetime_convert('UTC', date_default_timezone_get(),$rr['account_created']),
+ 'when' => datetime_convert('UTC', date_default_timezone_get(),$rr['reg_created']),
'hclass' => ('notify-unseen'),
- 'message' => t('requires approval')
+ 'message' => $message
];
return $x;
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..856ff9155 100644
--- a/Zotlabs/Module/Admin/Accounts.php
+++ b/Zotlabs/Module/Admin/Accounts.php
@@ -5,7 +5,7 @@ namespace Zotlabs\Module\Admin;
class Accounts {
-
+
/**
* @brief Handle POST actions on accounts admin page.
*
@@ -15,14 +15,79 @@ class Accounts {
*
*/
+ const MYP = 'ZAR'; // ZAR2x
+ const VERSION = '2.0.0';
+
function post() {
$pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() );
$users = ( x($_POST, 'user') ? $_POST['user'] : array() );
$blocked = ( x($_POST, 'blocked') ? $_POST['blocked'] : array() );
-
+
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 = '×';
+ }
+ 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')) {
@@ -55,7 +120,7 @@ class Accounts {
account_deny($hash);
}
}
-
+
goaway(z_root() . '/admin/accounts' );
}
@@ -75,19 +140,21 @@ class Accounts {
$account = q("SELECT * FROM account WHERE account_id = %d",
intval($uid)
);
-
+
if (! $account) {
notice( t('Account not found') . EOL);
goaway(z_root() . '/admin/accounts' );
}
-
+
check_form_security_token_redirectOnErr('/admin/accounts', 'admin_accounts', 't');
-
+
+ $debug = '';
+
switch (argv(2)){
case 'delete':
// delete user
account_remove($uid,true,false);
-
+
notice( sprintf(t("Account '%s' deleted"), $account[0]['account_email']) . EOL);
break;
case 'block':
@@ -95,7 +162,7 @@ class Accounts {
intval(ACCOUNT_BLOCKED),
intval($uid)
);
-
+
notice( sprintf( t("Account '%s' blocked") , $account[0]['account_email']) . EOL);
break;
case 'unblock':
@@ -103,27 +170,61 @@ class Accounts {
intval(ACCOUNT_BLOCKED),
intval($uid)
);
-
+
notice( sprintf( t("Account '%s' unblocked"), $account[0]['account_email']) . EOL);
break;
}
-
+
goaway(z_root() . '/admin/accounts' );
}
-
- /* get pending */
- $pending = q("SELECT account.*, register.hash from account left join register on account_id = register.uid where (account_flags & %d )>0 ",
- intval(ACCOUNT_PENDING)
- );
-
+
+ $tao = 'tao.zar.zarax = ' . "'" . '<img src="' . z_root() . '/images/zapax16.gif">' . "';\n";
+
+ $pending = get_pending_accounts();
+
+ 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'] ] . '×';
+ }
+
+ $pending[$n]['status'] = t('Not verified');
+ if($pending[$n]['reg_vfd'])
+ $pending[$n]['status'] = t('Verified');
+
+ if(!$pending[$n]['reg_vfd'] && $pending[$n]['reg_expires'] < datetime_convert())
+ $pending[$n]['status'] = t('Expired');
+
+ // timezone adjust date_time for display
+ $pending[$n]['reg_created'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_created']);
+ $pending[$n]['reg_startup'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_startup']);
+ $pending[$n]['reg_expires'] = datetime_convert('UTC', date_default_timezone_get(), $pending[$n]['reg_expires']);
+
+ // 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 */
-
+
$total = q("SELECT count(*) as total FROM account");
if (count($total)) {
\App::set_pager_total($total[0]['total']);
\App::set_pager_itemspage(100);
}
-
+
$serviceclass = (($_REQUEST['class']) ? " and account_service_class = '" . dbesc($_REQUEST['class']) . "' " : '');
$key = (($_REQUEST['key']) ? dbesc($_REQUEST['key']) : 'account_id');
@@ -134,8 +235,8 @@ class Accounts {
$base = z_root() . '/admin/accounts?f=';
$odir = (($dir === 'asc') ? '0' : '1');
- $users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked,
- (SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac
+ $users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked,
+ (SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac
where true $serviceclass and account_flags != %d order by $key $dir limit %d offset %d ",
intval(ACCOUNT_BLOCKED),
db_concat('ch.channel_address', ' '),
@@ -143,15 +244,15 @@ class Accounts {
intval(\App::$pager['itemspage']),
intval(\App::$pager['start'])
);
-
+
// function _setup_users($e){
// $accounts = Array(
- // t('Normal Account'),
+ // t('Normal Account'),
// t('Soapbox Account'),
// t('Community/Celebrity Account'),
// t('Automatic Friend Account')
// );
-
+
// $e['page_flags'] = $accounts[$e['page-flags']];
// $e['register_date'] = relative_date($e['register_date']);
// $e['login_date'] = relative_date($e['login_date']);
@@ -159,49 +260,55 @@ class Accounts {
// return $e;
// }
// $users = array_map("_setup_users", $users);
-
+
$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('Select toggle'),
+ '$sel_deny' => t('Deny selected'),
+ '$sel_aprv' => t('Approve selected'),
'$h_pending' => t('Registrations waiting for confirm'),
- '$th_pending' => array( t('Request date'), t('Email') ),
+ '$th_pending' => array( t('Request date'), t('Verification status'), t('Timeframe'), 'dId2', t('specified,atip') ),
'$no_pending' => t('No registrations.'),
'$approve' => t('Approve'),
'$deny' => t('Deny'),
'$delete' => t('Delete'),
'$block' => t('Block'),
'$unblock' => t('Unblock'),
+ '$verified' => t('Verified'),
+ '$not_verified' => t('Not yet verified'),
'$odir' => $odir,
'$base' => $base,
'$h_users' => t('Accounts'),
- '$th_users' => array(
+ '$th_users' => array(
[ t('ID'), 'account_id' ],
[ t('Email'), 'account_email' ],
- [ t('All Channels'), 'channels' ],
+ [ t('All channels'), 'channels' ],
[ t('Register date'), 'account_created' ],
[ t('Last login'), 'account_lastlog' ],
[ 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?'),
-
+ [ t('Service class'), 'account_service_class'] ),
+
+ '$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,
+ '$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..bf47f8c62 100644
--- a/Zotlabs/Module/Admin/Site.php
+++ b/Zotlabs/Module/Admin/Site.php
@@ -5,14 +5,28 @@ namespace Zotlabs\Module\Admin;
class Site {
+ // system cfgs
+ const ivo = 'invitation_only';
+ const iva = 'invitation_also';
+
/**
* @brief POST handler for Admin Site Page.
*
*/
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 +38,17 @@ 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);
+ $register_wo_email = ((x($_POST,'register_wo_email')) ? intval(trim($_POST['register_wo_email'])) : 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);
- $invite_only = ((x($_POST,'invite_only')) ? True : False);
+ $reg_autochannel = ((x($_POST,'auto_channel_create')) ? True : False);
+ $invitation_only = ((x($_POST,'invitation_only')) ? True : False);
+ $invitation_also = ((x($_POST,'invitation_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 +92,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 +109,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 +152,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 +186,20 @@ 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','register_wo_email', $register_wo_email);
set_config('system','minimum_age', $minimum_age);
- set_config('system','invitation_only', $invite_only);
+ set_config('system','auto_channel_create', $reg_autochannel);
+ set_config('system',self::ivo, $invitation_only);
+ set_config('system',self::iva, $invitation_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 +323,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 +352,70 @@ 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_n = $regdelay_u = false;
+ $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 === false ? 0 : $regdelay_n),
+ 'min' => '0',
+ 'max' => '99',
+ 'size' => '2',
+ 'default' => ($regdelay_u === false ? 'i' : $regdelay_u)
+ ),
+ 'rabot' => $reg_rabots
+ )
+ );
+ $regexpire_n = $regexpire_u = false;
+ $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 === false ? 99 : $regexpire_n),
+ 'min' => '0',
+ 'max' => '99',
+ 'size' => '2',
+ 'default' => ($regexpire_u === false ? 'y' : $regexpire_u)
+ ),
+ 'rabot' => $reg_rabots
+ )
+ );
+ $invitation_only = get_config('system',self::ivo);
+ $invitation_also = get_config('system',self::iva);
+
+ $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 +432,97 @@ 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_wo_email' => array('register_wo_email',
+ t("Registration is also possible without having to enter an email address."),
+ get_config('system','register_wo_email'),
+ t("Registration is also supported without requiring an email address from the applicant. Instead of the email address an artificial identification is generated, which has to be confirmed in a separate dialog. The default value is (Off) and corresponds to the registration procedure up to version 5.4.x."),
+ "", "", 'ZAR0824C'),
+
+ '$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` '
+ . EOL . ' <a id="zar083a" class="zuia btn">' . t('Parse and test your input') . '</a>'. EOL
+ . t('If left empty, defaults to 24h closed everyday the week.') . ' '
+ . t('To 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'),
+
+ '$invitation_only' => array(self::ivo,
+ ($invitation_only === false ? '✗' : '✓') . ' ' . t("Invitation only"),
+ $invitation_only,
+ t("Only allow new member registrations with an invitation code. Above register policy must be set to Yes."),
+ "", "", 'ZAR0880C'),
+
+ '$invitation_also' => array(self::iva,
+ ($invitation_also === false ? '✗' : '✓') . ' ' . t("Invitation also"),
+ $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 +564,180 @@ 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 = '-:-';
+
+ $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/Display.php b/Zotlabs/Module/Display.php
index 62e7dbf8a..eb97e77a0 100644
--- a/Zotlabs/Module/Display.php
+++ b/Zotlabs/Module/Display.php
@@ -245,8 +245,6 @@ class Display extends \Zotlabs\Web\Controller {
$sql_extra = ((local_channel()) ? EMPTY_STR : item_permissions_sql(0, $observer_hash));
-
-
if($noscript_content || $load) {
require_once('include/channel.php');
diff --git a/Zotlabs/Module/Invite.php b/Zotlabs/Module/Invite.php
index 6359da54c..34f1858fd 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() {
-
- if(! local_channel()) {
- notice( t('Permission denied.') . EOL);
+
+ // zai02
+
+ 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;
- }
-
- 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;
+ }
+ 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;
+
+ // 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;
+ }
}
-
- else
- $nmessage = $message;
-
- $account = App::get_account();
-
- $res = z_mail(
- [
+ }
+
+ 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) {
+
+ $reonar['due'] = $due;
+ $reonar['to'] = $recip;
+ $reonar['txtpersonal'] = $mailtext;
+
+ // 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,267 @@ 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;
-
- 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 '';
+
+ $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';
+
+ // 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())
+
+ $langs = array_keys($tpla);
+ asort($langs);
+
+ $tplx = array_unique($tplx);
+ asort($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..d51927de1
--- /dev/null
+++ b/Zotlabs/Module/Regate.php
@@ -0,0 +1,382 @@
+<?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 = datetime_convert();
+ $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') || ($didx == 'i')) {
+ // check startup and expiration via [=[register
+ $r = q("SELECT * FROM register WHERE reg_vital = 1 AND reg_did2 = '%s' ORDER BY reg_created DESC ",
+ dbesc($did2)
+ );
+ if ($r && count($r)) {
+ $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' ORDER BY reg_created DESC ", dbesc($r['reg_did2']) );
+ if ( $re ) {
+ $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);
+ elseif ( $didx == 'i' )
+ $acpin = $r['reg_hash'];
+ 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');
+ $nextpage = 'regate/' . bin2hex($did2) . $didx;
+ 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));
+ zar_log($msg . ' ' . $cra['account']['account_email']
+ . ' ' . $cra['account']['account_language']);
+ $nextpage = 'new_channel';
+
+ $auto_create = (get_config('system','auto_channel_create') ? true : false);
+
+ if($auto_create) {
+ // prepare channel creation
+ 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) {
+ // create channel
+ $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;
+ $msg = 'ZAR1239I ' . t('Channel successfull created') . ' ' . $did2;
+ }
+ else {
+ $msg = 'ZAR1239E ' . t('Channel still not created') . ' ' . $did2;
+ }
+ zar_log($msg . ' ' . $reonar['chan.did1'] . ' (' . $reonar['chan.name'] . ')');
+ }
+ unset($_SESSION['login_return_url']);
+ }
+ else {
+ q("ROLLBACK");
+ $msg = 'ZAR1238E ' . 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) {
+ if(isset($_GET['reg_id'])) {
+ if ( preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $_GET['reg_id'] ) ) {
+ // dId2 E email
+ goaway(z_root() . '/regate/' . bin2hex($_GET['reg_id']) . 'e' );
+ }
+ if ( preg_match('/^d{1,1}[0-9]{5,10}$/', $_GET['reg_id'] ) ) {
+ // dId2 A artifical & anonymous
+ goaway(z_root() . '/regate/' . bin2hex($_GET['reg_id']) . 'a' );
+ }
+ notice(t('Identity unknown') . EOL);
+ }
+
+ $o = replace_macros(get_markup_template('plain.tpl'), [
+ '$title' => t('Your Registration ID'),
+ '$now' => '<form action="regate" method="get"><input type="text" name="reg_id" class="form-control form-group"><button class="btn btn-primary float-right">Submit</button></form>'
+ ]);
+
+ return $o;
+ }
+
+ 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 = datetime_convert();
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ $isduty = zar_register_dutystate();
+ $nowfmt = $isduty['nowfmt'];
+ $atform = $isduty['atform'];
+
+ $pin = $_SESSION['zar']['pin'] ?? '';
+ unset($_SESSION['zar']['pin']);
+
+
+ $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' ORDER BY reg_created DESC",
+ dbesc($didx),
+ dbesc($did2)
+ );
+
+ if ($r && count($r) && $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('Verification successful!') . EOL
+ . t('After your account has been approved by our administrator you will be able to login with your ID') . EOL
+ . $did2 . EOL
+ . t('and your provided password.')
+ ]);
+ }
+ 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' => $pin ? t('Please enter your validation token') . ' <code class="inline-code">' . $pin . '</code>' : t('Please enter your validation token'),
+ '$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,
+ '$countdown' => datetime_convert('UTC', 'UTC', $r['reg_startup'], 'c'),
+ '$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..95d9da3b7 100644
--- a/Zotlabs/Module/Register.php
+++ b/Zotlabs/Module/Register.php
@@ -1,25 +1,32 @@
<?php
+
namespace Zotlabs\Module;
+use App;
use Zotlabs\Web\Controller;
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) : '');
-
+
// Provide a stored request for somebody desiring a connection
// when they first need to register someplace. Once they've
- // created a channel, we'll try to revive the connection request
+ // created a channel, we'll try to revive the connection request
// and process it.
-
+
if($_REQUEST['connect'])
$_SESSION['connect'] = $_REQUEST['connect'];
-
+
switch($cmd) {
case 'invite_check.json':
$result = check_account_invite($_REQUEST['invite_code']);
@@ -30,50 +37,156 @@ class Register extends Controller {
case 'password_check.json':
$result = check_account_password($_REQUEST['password1']);
break;
- default:
+ default:
break;
}
if($result) {
json_return_and_die($result);
}
}
-
-
+
+
function post() {
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 (most posts are not accepted during off duty).
+ *
+ */
+
+
+ $act = q("SELECT COUNT(*) AS act FROM account")[0]['act'];
+ $duty = zar_register_dutystate();
+ $is247 = false;
+ $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']) : '');
+ $password2 = ( (x($arr,'password2')) ? trim($arr['password2']) : '');
+
+ $reonar = array();
+
+
+ // case when an invited prepares the own account by supply own pw, accept tos, prepage channel (if auto)
+ if ($email && $invite_code) {
+ if ( preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $email ) ) {
+ if ( preg_match('/^[a-z0-9]{12,12}$/', $invite_code ) ) {
+ $is247 = true;
+ }
+ }
+ }
+
+/*
+ // 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]{5,10}$/', $email ) ) {
+ // dId2 A artifical & anonymous
+ goaway(z_root() . '/regate/' . bin2hex($email) . 'a' );
+ }
+
+ }
+*/
+ $email_verify = get_config('system','verify_email');
+ if ($email_verify && ! $email) {
+ notice(t('Email address required') . EOL);
+ return;
+ }
+
+ if ($email) {
+ if ( ! preg_match('/^.{2,64}\@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/', $_POST['email'] ) ) {
+ // msg!
+ notice(t('Not a valid email address') . EOL);
+ return;
+ }
+ }
+
+ if ($act > 0 && !$is247 && !$duty['isduty']) {
+ // normally (except very 1st timr after install), that should never arrive here (ie js hack or sth like)
+ // log suitable for f2b also
+ $logmsg = 'ZAR0230S Unexpected registration request off duty';
+ zar_log($logmsg);
+ return;
+ }
+
+ if ($sameip && !$is247) {
+ $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);
+ if ($f && $f[0]['atip'] > $sameip) {
+ $logmsg = 'ZAR0239S Exceeding same ip register request of ' . $sameip;
+ zar_log($logmsg);
return;
}
}
-
+
+ // s2 max daily
+ // msg?
+ if ( !$is247 && self::check_reg_limits()['is'] ) return;
+
+ if(!$password) {
+ // msg!
+ notice(t('No password provided') . EOL);
+ return;
+ }
+
+ // pw1 == pw2
+ if($password !== $password2) {
+ // msg!
+ notice(t('Passwords do not match') . EOL);
+ return;
+ }
+
+ $password_result = check_account_password($password);
+ if(!empty($password_result['error'])) {
+ $msg = $password_result['message'];
+ notice($msg);
+ zar_log($msg . ' ' . $did2);
+ return;
+ }
+
+ // accept tos
if(! x($_POST,'tos')) {
- notice( t('Please indicate acceptance of the Terms of Service. Registration failed.') . EOL);
+ // msg!
+ notice(t('Terms of Service not accepted') . EOL);
return;
}
-
- $policy = get_config('system','register_policy');
-
- $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) {
-
+
case REGISTER_OPEN:
$flags = ACCOUNT_OK;
break;
-
+
case REGISTER_APPROVE:
- $flags = ACCOUNT_BLOCKED | ACCOUNT_PENDING;
+ $flags = ACCOUNT_PENDING;
break;
-
+
default:
case REGISTER_CLOSED:
if(! is_site_admin()) {
@@ -83,164 +196,318 @@ class Register extends Controller {
$flags = ACCOUNT_BLOCKED;
break;
}
-
- if($email_verify && $policy == REGISTER_OPEN)
- $flags = $flags | ACCOUNT_UNVERIFIED;
-
-
- if((! $_POST['password']) || ($_POST['password'] !== $_POST['password2'])) {
- notice( t('Passwords do not match.') . EOL);
- return;
- }
-
- $arr = $_POST;
+
+ if($email_verify && ($policy == REGISTER_OPEN || $policy == REGISTER_APPROVE) )
+ $flags = ($flags | ACCOUNT_UNVERIFIED);
+
+ // $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 ??
+
+ // transit ?
+
+ // update reg vital 0 off
+ //$icdone = q("UPDATE register SET reg_vital = 0 WHERE reg_id = %d ",
+ //intval($reg['reg_id'])
+ //);
+
+ // update DB flags, password
+ // TODO: what else?
+ q("UPDATE register set reg_flags = %d, reg_pass = '%s', reg_stuff = '%s' WHERE reg_id = '%s'",
+ intval($flags),
+ dbesc(bin2hex($password)),
+ intval($reg['reg_id'])
+ );
+
+ $msg = 'ZAR0237I ' . t('Invitation code succesfully applied');
+ zar_log($msg) . ', ' . $email;
+ // msg!
+ info($msg . EOL);
+
+ // the invitecode has verified us and we have all the info we need
+ // take the shortcut.
+
+ $mod = new Regate();
+ $_REQUEST['form_security_token'] = get_form_security_token("regate");
+ App::$argc = 2;
+ App::$argv[0] = 'regate';
+ App::$argv[1] = bin2hex($reg['reg_did2']) . 'i';
+ return $mod->post();
+
+ } else {
+ // msg!
+ 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.
-
- 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 ($well) {
+
+ if($policy == REGISTER_OPEN || $policy == REGISTER_APPROVE ) {
+
+ $cfgdelay = get_config( 'system', 'register_delay' );
+ $reg_delayed = calculate_adue( $cfgdelay );
+ $regdelay = (($reg_delayed) ? datetime_convert(date_default_timezone_get(), 'UTC', $reg_delayed['due']) : $now);
+
+ $cfgexpire = get_config('system','register_expire' );
+ $reg_expires = calculate_adue( $cfgexpire );
+ $regexpire = (($reg_expires) ? datetime_convert(date_default_timezone_get(), 'UTC', $reg_expires['due']) : datetime_convert('UTC', 'UTC', 'now + 99 years'));
+
+ // handle an email request that will be verified or an ivitation associated with an email address
+ if ($email > '' && $email_verify) {
+ // 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 . ' UTC' . t('and expire') . ' ' . $regexpire . ' UTC';
+ $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']));
+ }
+
+ $salt = random_string(32);
+ $password = $salt . ',' . hash('whirlpool', $salt . $password);
+
+ $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($now),
+ dbesc($regdelay),
+ dbesc($regexpire),
+ dbesc($email),
+ dbesc($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($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'])
+ );
+
+ zar_log( 'ZAR0239A ' . t('New register request') . ' d' . $didnew . ', '
+ . $regdelay . ' - ' . $regexpire);
+
+ if($reg_delayed) {
+ // this could be removed to make registration harder
+ $_SESSION['zar']['pin'] = $pass2;
+
+ $_SESSION['zar']['msg'] = t('Your validation token is') . EOL
+ . '<h3>' . $pass2 . '</h3>' . EOL
+ . t('Hold on, you can continue verification in')
+ . '<div class="d-none"><code class="inline-code"><span id="register_start" data-utc="' . datetime_convert('UTC', 'UTC', $regdelay, 'c') . '" class="register_date">' . datetime_convert('UTC', 'UTC', $regdelay, 'c') . '</span></code> ' . t('and') . ' <code class="inline-code"><span data-utc="' . datetime_convert('UTC', 'UTC', $regexpire, 'c') . '" class="register_date">' . datetime_convert('UTC', 'UTC', $regexpire, 'c') . '</span></code></div>'
+ //. t('Please come back to this page in the requested timeframe or wait for the countdown to complete.')
+ ;
+ }
+ else {
+ $_SESSION['zar']['pin'] = $pass2;
+ }
+
+ goaway(z_root() . '/regate/' . bin2hex('d' . $didnew) . 'a' );
+ }
+ else {
+ $msg = 'ZAR0239D,' . t('Error creating dId A');
+ notice( $msg );
+ zar_log( $msg . ' ' . $did2);
+ }
+ }
+ goaway(z_root() . '/regate/' . bin2hex($email) . $didx );
}
- 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);
-
}
-
-
-
+
+
+
function get() {
-
+
$registration_is = '';
$other_sites = '';
-
+
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;
}
- $mod = new Pubsites();
+ $mod = new Pubsites();
return $mod->get();
}
-
+
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>');
+ $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;
- }
+ $other_sites = '<a href="pubsites">' . t('Register at another affiliated hub') . '</a>';
+ } elseif (intval(get_config('system','invitation_also'))) {
+ $invitations = true;
}
+ $opal = self::check_reg_limits();
+ if ( $opal['is'])
+ $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';
-
+
$toslink = '<a href="' . $tosurl . '" target="_blank">' . t('Terms of Service') . '</a>';
-
+
// Configurable whether to restrict age or not - default is based on international legal requirements
// This can be relaxed if you are on a restricted server that does not share with public servers
-
+
if(get_config('system','no_age_restriction')) {
$label_tos = sprintf( t('I accept the %s for this website'), $toslink);
}
@@ -253,33 +520,65 @@ 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'])) : ""));
- $password = array('password', t('Choose a password'), '');
- $password2 = array('password2', t('Please re-enter your password'), '');
+
+ $auto_create = (get_config('system','auto_channel_create') ? true : false);
+ $default_role = get_config('system','default_permissions_role');
+ $email_verify = get_config('system','verify_email');
+
+ $emailval = ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : "");
+ $email = ['email',
+ t('Your email address'),
+ $emailval,
+ (($email_verify) ? t('Required') : t('Optional')),
+ (($email_verify) ? '*' : ''),
+ $duty['atform']
+ ];
+
+ $password = array('password', t('Choose a password'), '', '', '', $duty['atform']);
+ $password2 = array('password2', t('Please re-enter your password'), '', '', '', $duty['atform']);
+
$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);
+ $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');
- $email_verify = get_config('system','verify_email');
-
require_once('include/bbcode.php');
-
+
$o = replace_macros(get_markup_template('register.tpl'), array(
+ '$tao' => "typeof(window.tao) == 'undefined' ? window.tao = {} : '';\n"
+ . "tao.zar = { vsn: '2.0.0', form: {}, msg: {} };\n"
+ . "tao.zar.patano = /^d[0-9]{5,10}$/;\n"
+ . "tao.zar.patema = /^[a-z0-9.-]{2,64}@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/;\n"
+ . "tao.zar.msg.ZAR0239E = '" . t('Email address not valid') . "';\n",
+
'$form_security_token' => get_form_security_token("register"),
'$title' => t('Registration'),
'$reg_is' => $registration_is,
'$registertext' => bbcode(get_config('system','register_text')),
'$other_sites' => $other_sites,
+ '$msg' => $opal['rn'] . ',' . $opal['an'],
'$invitations' => $invitations,
'$invite_code' => $invite_code,
+ '$haveivc' => t('I have an invite code'),
+ '$now' => $duty['nowfmt'],
+ '$atform' => $duty['atform'],
'$auto_create' => $auto_create,
'$name' => $name,
'$role' => $role,
@@ -288,15 +587,48 @@ 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.') : '')
));
-
+
return $o;
-
}
-
-
+
+ function check_reg_limits() {
+ // check against register, account
+ $rear = array( 'is' => false, 'rn' => 0, 'an' => 0, 'msg' => '' );
+
+ $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')
+ );
+
+ $rear['is'] = ( $r && $r[0]['nr'] >= $max_dailies ) ? true : false;
+ $rear['rn'] = $r[0]['nr'];
+
+ if ( !$rear['is']) {
+ $r = q("SELECT COUNT(account_id) AS nr FROM account WHERE account_created > %s - INTERVAL %s",
+ db_utcnow(), db_quoteinterval('1 day')
+ );
+
+ $rear['is'] = ( $r && ($r[0]['nr'] + $rear['rn']) >= $max_dailies ) ? true : false;
+ $rear['ra'] = $r[0]['nr'];
+ }
+
+ if ( $rear['is']) {
+ $rear['msg'] = 'ZAR0333W ' . t('This site has exceeded the number of allowed daily account registrations');
+ zar_log($msg);
+ $rear['is'] = true;
+ }
+ }
+ return $rear;
+ }
}
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/Module/Sse_bs.php b/Zotlabs/Module/Sse_bs.php
index c7580971e..271d040fd 100644
--- a/Zotlabs/Module/Sse_bs.php
+++ b/Zotlabs/Module/Sse_bs.php
@@ -691,9 +691,8 @@ class Sse_bs extends Controller {
if(! (self::$vnotify & VNOTIFY_REGISTER))
return $result;
- $r = q("SELECT account_email, account_created from account where (account_flags & %d) > 0",
- intval(ACCOUNT_PENDING)
- );
+ $r = get_pending_accounts();
+
if($r) {
foreach($r as $rr) {
$result['register']['notifications'][] = Enotify::format_register($rr);
diff --git a/Zotlabs/Render/SmartyTemplate.php b/Zotlabs/Render/SmartyTemplate.php
index 61fb72f8a..2cb96521b 100644
--- a/Zotlabs/Render/SmartyTemplate.php
+++ b/Zotlabs/Render/SmartyTemplate.php
@@ -8,16 +8,16 @@ use App;
class SmartyTemplate implements TemplateEngine {
static $name ="smarty3";
-
+
public function __construct() {
// Cannot use get_config() here because it is called during installation when there is no DB.
// FIXME: this may leak private information such as system pathnames.
- $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system']))
+ $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system']))
? App::$config['system']['smarty3_folder'] : '');
if (! $basecompiledir) {
- $basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH;
+ $basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . TEMPLATE_BUILD_PATH;
}
if (! is_dir($basecompiledir)) {
@os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true);
@@ -30,7 +30,7 @@ class SmartyTemplate implements TemplateEngine {
}
App::$config['system']['smarty3_folder'] = $basecompiledir;
}
-
+
// TemplateEngine interface
public function replace_macros($s, $r) {
@@ -52,9 +52,9 @@ class SmartyTemplate implements TemplateEngine {
}
$s->assign($key, $value);
}
- return $s->parsed($template);
+ return $s->parsed($template);
}
-
+
public function get_markup_template($file, $root = '') {
$template_file = theme_include($file, $root);
if ($template_file) {
@@ -62,7 +62,7 @@ class SmartyTemplate implements TemplateEngine {
$template->filename = $template_file;
return $template;
- }
+ }
return EMPTY_STR;
}
@@ -84,7 +84,7 @@ class SmartyTemplate implements TemplateEngine {
$template = new SmartyInterface();
$template->filename = $template_file;
return $template;
- }
+ }
return "";
}
diff --git a/Zotlabs/Update/_1244.php b/Zotlabs/Update/_1244.php
new file mode 100644
index 000000000..0aebe4013
--- /dev/null
+++ b/Zotlabs/Update/_1244.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Zotlabs\Update;
+
+require_once('include/account.php');
+
+class _1244 {
+
+ function run() {
+
+ return verify_register_scheme();
+
+ }
+
+}
diff --git a/boot.php b/boot.php
index ec79b3dce..d91b57f1f 100644
--- a/boot.php
+++ b/boot.php
@@ -55,7 +55,7 @@ define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '5.5.3' );
define ( 'ZOT_REVISION', '6.0' );
-define ( 'DB_UPDATE_VERSION', 1243 );
+define ( 'DB_UPDATE_VERSION', 1244 );
define ( 'PROJECT_BASE', __DIR__ );
diff --git a/doc/admin/zarlog_msgs.md b/doc/admin/zarlog_msgs.md
new file mode 100644
index 000000000..0fe16a1e2
--- /dev/null
+++ b/doc/admin/zarlog_msgs.md
@@ -0,0 +1,112 @@
+~~~
+L regate ZAR1131E dId2 mistaken
+L ZAR1132E Identity unknown
+ ZAR1133A Sorry for any inconvience. Thank you for your response.
+L ZAR1134S email verfication denied {did2}
+L ZAR1135E not awaited url parameter received
+
+L regate ZAR1230S Unexpected registration verification request for
+L ZAR1231E dId2 mistaken
+L ZAR1232E Identity unknown
+L ZAR1234W Request not inside time frame
+L ZAR1235E Token verification failed
+ ZAR1236I Verify successfull
+L ZAR1236E Verify failed
+L ZAR1237D unexpected
+ (reason may be caused by new account flags implemented still not known)
+ ZAR1238I Email resent
+ ZAR1238E Resent failed
+L ZAR1239I Account successfull created
+L ZAR1239E Account creation error
+
+ register ZAR0130E Registration on this hub is disabled.
+ ZAR0131I Registration on this hub is by approval only.
+ Register at another affiliated hub in case when prefered
+ ZAR0132I Registration on this hub is by invitation only.
+ Register at another affiliated hub
+ ZAR0133I If the registation was already submitted with your data once ago,
+ enter your identity (like email) here and submit
+ ZAR0134I I have an invite code
+ ZAR0135I This site requires verification. After completing this form,
+ please check the notice or your email for further instructions.
+ ZAR0136I Your email address (or leave blank to register without email)
+
+L register ZAR0230S Unexpected registration request
+ ZAR0231E Email address mistake
+ ZAR0231E Passwords do not match.
+ ZAR0231E Please indicate acceptance of the Terms of Service. Registration failed.
+ ZAR0232E Invitations are not available
+L ZAR0233E Registration on this hub is by invitation only
+L ZAR0234S Invitation code failed
+L ZAR0235S Invitation email failed
+ ZAR0236E Invitation not in time or too late
+ ZAR0237I Invitation code succesfully applied
+L ZAR0238E Email address already in use
+L ZAR0239D Error creating dId A
+ ZAR0239I Your didital id is {did2} and your pin for is {pin}
+ Keep these infos and your entered password safe
+ Valid from ... nd expire ...
+L ZAR0239S Exceeding same ip register request of
+
+ ui:admin:site
+ ZAR0810C Register text
+ Will be displayed prominently on the registration page.
+ ZAR0820C register_policy
+ Does this site allow new member registration?
+ ZAR0830C Registration office on duty
+ The weekdays and hours the register office is open for registrations
+ ZAR0831I Testmode duties
+ (interactive)
+ ZAR0840C Account registrations max per day
+ How many registration requests the site accepts during one day. Unlimited if zero or no value.
+ ZAR0850C Account registrations from same ip
+ How many pending registration requests the site accepts from a same ip address.
+ ZAR0860C Account registration delay
+ How long a registration request has to wait before validation can perform
+ ZAR0862C Account registration expiration
+ How long a registration to confirm remains valid. Not expire if zero or no value
+ ZAR0870C Auto channel create
+ 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.
+ ZAR0880C Invitation only
+ Only allow new member registrations with an invitation code.
+ Above register policy must be set to Yes.
+ ZAR0881C Invitation also
+ Also allow new member registrations with an invitation code.
+ Above register policy must be set to Yes.
+ ZAR0890C Verify Email Addresses
+ Check to verify email addresses used in account registration (recommended).
+
+ invite ZAI0100E All users invitation limit exceeded
+ ZAI0101E Permission denied.
+ ZAI0102E Invite App (Not Installed)
+ ZAI0103E Invites not proposed by configuration. Contact the site admin
+ ZAI0104E Invites by users not enabled
+ ZAI0105W You have no more invitations available
+ ZAI0106I Invitations I am using
+ ZAI0107I Invitations we are using
+ ZAI0109E Not on xchan
+ ZAI0110I § Note, the email(s) sent will be recorded in the system logs
+ (see ZAI0208I @ L)
+ ZAI0111I Enter email addresses, one per line
+ ZAI0112I Your message
+ Here you may enter personal notes to the recipient(s)
+ ZAI0113I Invite template
+ ZAI0114I Note, the invitation code is valid up to ...
+
+ invite ZAI0201E Permission denied.
+ ZAI0202E Invite App (Not Installed)
+ ZAI0203E Not a valid email address
+ ZAI0204E Not a real email address
+ ZAI0205E Not allowed email address
+ ZAI0206E mail address already in use
+ ZAI0207I Note, the invitation code is valid up to
+ ZAI0208E Message delivery failed.
+ ZAI0208I Message delivery success.
+L ZAI0208I to {email} Message delivery success. ({account#}, {channel#}, from:{email}})
+ ZAI0209I Accepted email address
+ ZAI0210E Too many recipients for one invitation (max n)
+ ZAI0211E No recipients for this invitation
+ ZAI0212I n mail(s) sent, n mail error(s)
+ ZAI0213E Register is closed
+~~~
diff --git a/doc/context/de/register/help.html b/doc/context/de/register/help.html
new file mode 100644
index 000000000..9ee062ee8
--- /dev/null
+++ b/doc/context/de/register/help.html
@@ -0,0 +1,12 @@
+<dl class="dl-horizontal">
+ <dt>Allgemein</dt>
+ <dd>Auf dieser Seite können sich Besucher registrieren, um mit einer Anmeldungskennung Zugang zum Portal zu erhalten. Angemeldeten Benutzern sehen nicht nur die öffentlichen Inhalte, sondern können selber Inhalte veröffentlichen und Soziale Netzwerk Kommunikationen durchführen, und sehr viel mehr.</dd>
+ <dt>Arten der Registrierung</dt>
+ <dd>Eine Registrierung ist mit einer eMail Adresse möglich, oder auch anonym (dann das eMail Feld nicht ausfüllen). Vielleicht haben Sie auch einen Einladungscode erhalten, der dann mit der eMail Adresse angegeben werden kann. Der Link oberhalb des Feldes eMail ermöglich die Eingabe des Einladungscodes.</dd>
+ <dt>Ablauf der Registrierung</dt>
+ <dd>Für Anmeldungen nach einer erfolgreichen Registrierung ist ein eigenes Kennwort festzulegen. Es ist sicherheitshalber zweimal mit identischen Werten einzugeben, weil es nicht angezeigt wird. Das Kennwort ist geheim zu halten und nur für den eigenen Gebrauch bestimmt. Anonym registrierte Benutzer erhalten eine automatisch zugewiesene Kennung, und sollten das eigene Kennwort nicht vergessen, weil es im Gegensatz zur eMail Registrierung zunächst keine Rücksetzfunktion gibt. Je nach Konfiguration der Hub-Instanz kann eine Bestätigungsfunktion erforderlich sein. Benutzer der eMail Registrierung erhalten eine entsprechende Nachricht. Bei anonymen Registrierungen wird ein weiterer Dialog angezeigt, der die Zugangskennung und eine Pin zeigt. Jene Dialogseite sollte unbedingt sicher und langfristig aufbewahrt werden (z.B. durch Ausdruck, Screenshot, Foto), weil die Daten zu einem späten Zeitpunkt noch einmal zu bestätigen sind.</dd>
+ <dt>Die Digitale Identität</dt>
+ <dd>Je nach Konfiguration der Hub-Instanz kann bereits bei der Registrierung (alternativ auch bei der ersten Anmeldung) ein anzeigbarer Name und ein Spitzname ("nickname") eingegeben werden. Der Nickname hat eine sehr weitreichende Bedeutung und läßt sich nachträglich nicht mehr ändern. Es handelt sich dabei um eine eindeutige Digitale Identität (DID), die mit allen eigenen Aktivitäten verknüft ist, sein wird, und bleibt. Diese DID eignet sich nicht nur zur Anmeldung in dieser Hub-Instanz, sondern auch in allen verbundenen Instanzen des Federalen Netzwerkes. Im Sprachgebrauch der Federalen Netze ist diese digitale Identität ein "Kanal". Das ist vergleichbar etwa mit einer Rufnummer im Telefonnetz. Die DID hat das Format kanal@instanz.tld = nickname@instanz.tld und ist, wie gesagt, nachträglich nicht mehr änderbar. Obwohl sich das Format wie eine eMail Adresse darstellt, handelt es sich nicht um eine solche.</dd>
+ <dt>Bevor die Registrierung begonnen wird ...</dt>
+ <dd>sollte (oben rechts im Hamburger Menü <span class="fa fa-fw fa-bars"> </span>) die bevorzugte Sprache (Englisch, Spanisch, Deutsch z.B.) gewählt werden. Die aktuelle Sprache wird in den Folgeschritten und auch bei und nach der Anmeldung verwendet. Das läßt sich aber jederzeit und je nach Bedarf ändern. Auch sei darauf hingewiesen, dass diese Hub-Instanz nicht die einzige ist. Eine Übersicht über andere Hub-Instanzen ist <a href="./pubsites"> hier </a> zu finden.</dd>
+</dl>
diff --git a/doc/context/en/register/help.html b/doc/context/en/register/help.html
new file mode 100644
index 000000000..9e94ab762
--- /dev/null
+++ b/doc/context/en/register/help.html
@@ -0,0 +1,12 @@
+<dl class="dl-horizontal">
+ <dt>General</dt>
+ <dd>On this page visitors can register to get access to the portal with a login ID. Logged in users not only see the public content, but can publish content themselves and perform social network communications, and much more.</dd>
+ <dt>Modes of registration</dt>
+ <dd>Registration is possible with an eMail address, or anonymously (then do not fill in the eMail field). You may also have received an invitation code, which can then be entered with the eMail address. The link above the eMail field allows you to enter the invitation code.</dd>
+ <dt>Registration procedure</dt>
+ <dd>For logins after a successful registration, a separate password must be set. To be on the safe side, enter it twice with identical values, because it will not be displayed. The password is to be kept secret and is only intended for the user's own use. Anonymously registered users receive an automatically assigned ID, and should not forget their own password because, unlike eMail registration, there is initially no reset function. Depending on the configuration of the hub instance, a confirmation function may be required. Users of the eMail registration will receive a corresponding message. For anonymous registrations, another dialog will be displayed showing the access ID and a pin. This dialog page should be stored securely and for a long time (e.g. by printout, screenshot, photo), because the data must be confirmed again at a later point in time.</dd>
+ <dt>The Digital Identity</dt>
+ <dd>Depending on the configuration of the hub instance, a displayable name and a nickname can already be entered during registration (alternatively also during the first login). The nickname has a very extensive meaning and cannot be changed later. It is a unique Digital Identity (DID) that is, will be, and remains linked to all of one's activities. This DID is not only suitable for logging into this hub instance, but also into all connected instances of the federal network. In federal network parlance, this digital identity is a "channel". This is comparable to a telephone number in the telephone network. The DID has the format channel@instance.tld = nickname@instance.tld and, as mentioned, cannot be changed afterwards. Although the format looks like an eMail address, it is not.</dd>
+ <dt>Before starting the registration ...</dt>
+ <dd>... the preferred language (English, Spanish, German, for example) should be selected (top right in the hamburger menu <span class="fa fa-fw fa-bars"> </span>). The current language is used in the subsequent steps and also during and after login. However, this can be changed at any time and as needed. It should also be noted that this Hub instance is not the only one. An overview of other Hub instances can be found <a href="./pubsites"> here </a>.</dd>
+</dl>
diff --git a/images/default_profile_photos/human_confusion/300.png b/images/default_profile_photos/human_confusion/300.png
new file mode 100644
index 000000000..3f21ae68f
--- /dev/null
+++ b/images/default_profile_photos/human_confusion/300.png
Binary files differ
diff --git a/images/default_profile_photos/human_confusion/48.png b/images/default_profile_photos/human_confusion/48.png
new file mode 100644
index 000000000..5013a0aef
--- /dev/null
+++ b/images/default_profile_photos/human_confusion/48.png
Binary files differ
diff --git a/images/default_profile_photos/human_confusion/80.png b/images/default_profile_photos/human_confusion/80.png
new file mode 100644
index 000000000..5a32ab45f
--- /dev/null
+++ b/images/default_profile_photos/human_confusion/80.png
Binary files differ
diff --git a/images/zapax16.gif b/images/zapax16.gif
new file mode 100644
index 000000000..81468ad35
--- /dev/null
+++ b/images/zapax16.gif
Binary files differ
diff --git a/include/account.php b/include/account.php
index fefe61d15..7f8a8a2bf 100644
--- a/include/account.php
+++ b/include/account.php
@@ -35,7 +35,7 @@ function check_account_email($email) {
return $result;
if(! validate_email($email))
- $result['message'] .= t('Not a valid email address') . EOL;
+ $result['message'] .= t('Email address not valid') . EOL;
elseif(! allowed_email($email))
$result['message'] = t('Your email domain is not among those allowed on this site');
else {
@@ -71,17 +71,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;
@@ -107,8 +120,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'),
@@ -130,12 +143,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 }
@@ -259,10 +281,161 @@ 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 = datetime_convert();
+
+ // 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;
+ }
+
+ $password_parts = explode(',', $register[0]['reg_pass']);
+ $salt = $password_parts[0];
+ $password_encoded = $password_parts[1];
+
+ $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'])
@@ -271,11 +444,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;
@@ -284,13 +460,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'];
}
@@ -347,11 +526,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'])
);
@@ -425,7 +610,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)
);
@@ -433,57 +618,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'])
+ );
- $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($r1 && $r2) {
+ q("COMMIT");
- $res = z_mail(
- [
- 'toEmail' => $account[0]['account_email'],
- 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')),
- 'textVersion' => $email_msg,
- ]
- );
+ // <- hilmar]
- pop_lang();
+ push_lang($register[0]['reg_lang']);
- if(get_config('system','auto_channel_create'))
- auto_channel_create($register[0]['uid']);
+ $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 ($res) {
- info( t('Account approved.') . EOL );
- return true;
+ $res = z_mail(
+ [
+ 'toEmail' => $account[0]['account_email'],
+ 'messageSubject' => sprintf( t('Registration details for %s'), get_config('system','sitename')),
+ 'textVersion' => $email_msg,
+ ]
+ );
+
+ 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]
}
@@ -500,42 +717,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)
);
@@ -543,45 +783,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);
@@ -591,6 +844,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.
*
@@ -828,3 +1193,90 @@ 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';
+ $utc_now = datetime_convert(date_default_timezone_get(), 'UTC', $now, 'c');
+
+ $nowfmt = t('Registration is currently')
+ . ' (<span data-utc="' . $utc_now . '" class="register_date">' . $utc_now . '</span>) '
+ . $dutyis . '.<br>';
+
+
+ if (!$isduty) {
+ $pernext = zarIsDuty($day, $now, 'nextOpen');
+ $week_days = ['','monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
+ $utc_next_open = datetime_convert(date_default_timezone_get(), 'UTC', $week_days[$pernext[0]] . ' ' . $pernext[1], 'c');
+
+ if (is_array($pernext)) {
+ $nowfmt .= t('Next opens') . ' '
+ . '<span data-utc="' . $utc_next_open . '" class="register_date">' . $utc_next_open . '</span>.';
+
+ }
+ }
+ return array( 'isduty' => $isduty, 'nowfmt' => $nowfmt, 'atform' => $atform);
+
+}
+
+function get_pending_accounts() {
+
+ /* get pending */
+ // [hilmar ->
+ //~ $r = 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)
+ //~ );
+
+ // better useability at the moment to tell all (ACCOUNT_PENDING >= 0) instead of (> 0 for those need approval)
+
+ $r = q("SELECT reg_did2, reg_created, reg_startup, reg_expires, reg_email, reg_atip, reg_hash, reg_id,
+ CASE (reg_flags & %d) WHEN 0 THEN 1 WHEN 1 THEN 0 END AS reg_vfd
+ FROM register WHERE reg_vital = 1 AND (reg_flags & %d) >= 0",
+ intval(ACCOUNT_UNVERIFIED),
+ intval(ACCOUNT_PENDING)
+ );
+
+ return $r;
+}
diff --git a/include/datetime.php b/include/datetime.php
index ef0927ea4..4c7105138 100644
--- a/include/datetime.php
+++ b/include/datetime.php
@@ -534,3 +534,39 @@ 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);
+
+ if(!$durn)
+ return false;
+
+ $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 066b1dcf4..f433f8094 100644
--- a/include/security.php
+++ b/include/security.php
@@ -592,10 +592,9 @@ function check_form_security_token($typename = '', $formname = 'form_security_to
$hash = $_REQUEST[$formname];
$max_livetime = 10800; // 3 hours
- $min_livetime = 3; // 3 sec
$x = explode('.', $hash);
- if (time() > (IntVal($x[0]) + $max_livetime) || time() < (IntVal($x[0]) + $min_livetime))
+ if (time() > (IntVal($x[0]) + $max_livetime))
return false;
$sec_hash = hash('whirlpool', App::$observer['xchan_guid'] . ((local_channel()) ? App::$channel['channel_prvkey'] : '') . session_id() . $x[0] . $typename);
@@ -834,3 +833,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 b24b40d45..c6980c5f8 100644
--- a/include/text.php
+++ b/include/text.php
@@ -2322,6 +2322,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..52b44bd07 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/de-de/invite.casual.subject.tpl b/view/de-de/invite.casual.subject.tpl
new file mode 100644
index 000000000..b64cf59a9
--- /dev/null
+++ b/view/de-de/invite.casual.subject.tpl
@@ -0,0 +1 @@
+Schließe dich uns auf {{$projectname}} an! \ No newline at end of file
diff --git a/view/de-de/invite.casual.tpl b/view/de-de/invite.casual.tpl
new file mode 100644
index 000000000..ce587a739
--- /dev/null
+++ b/view/de-de/invite.casual.tpl
@@ -0,0 +1,15 @@
+de casual
+Bitte schließe dich meiner Community auf {{$projectname}} an.
+{{$linktxt}} {{$invite_where}}
+
+Verwende bei der Registrierung diesen Einladungscode:
+ {{$invite_code}}
+
+Oder:
+
+1. Registriere dich auf auf einer beliebigen {{$projectname}}-Instanz
+ (sie sind alle miteinander verbunden).
+2. Gib meine {{$Projectname}} Netzwerk Adresse in dem Suchfeld ein:
+ {{$invite_whereami}}
+ oder besuche {{$invite_whoami}}
+3. Klicke auf [Verbinden]
diff --git a/view/de-de/invite.formal.subject.tpl b/view/de-de/invite.formal.subject.tpl
new file mode 100644
index 000000000..0aa97e198
--- /dev/null
+++ b/view/de-de/invite.formal.subject.tpl
@@ -0,0 +1 @@
+Einladung für Ihren Netzwerkzugang auf {{$invite_loc}} \ No newline at end of file
diff --git a/view/de-de/invite.formal.tpl b/view/de-de/invite.formal.tpl
new file mode 100644
index 000000000..96bc64ba9
--- /dev/null
+++ b/view/de-de/invite.formal.tpl
@@ -0,0 +1,19 @@
+Wenn Sie mögen, können Sie sich den dezentralen Sozialen Netzwerken von {{$projectname}}
+hier anschließen: {{$invite_where}}
+Um den Zugang zu ermöglichen, werden Sie wahrscheinlich einmalig aufgefordert,
+diesen Einladungscode anzugeben: {{$invite_code}}
+Bei der Registrierung ist auch Ihre eMail Adresse zu nennen, sowie ein Kennwort festzulegen,
+das vertraulich ist allein Ihnen bekannt sein soll.
+
+Alternative Möglichkeiten:
+
+1. Eine Registrierung wird an jeder der öffentlich zugänglichen {{$projectname}} Instanzen unterstützt,
+ weil die alle miteinander verbunden sind. Eine Übersicht ist zu finden unter:
+ {{$invite_anywhere}}
+2. Geben Sie meine {{$projectname}} Netzwerkadresse {{$invite_whereami}} in die Suchleiste beim
+ Lupensymbol ein oder besuchen Sie mich unter {{$invite_whoami}}
+3. Klicken Sie auf [Verbinden]
+
+Falls auf einer besuchten {{$projectname}} Instanz fremdsprachige Anzeigen dargestellt sind,
+können Sie die Anzeige einfach auf eine eigene (oder bevorzugte) Sprache im
+Hamburger Menü  umschalten.
diff --git a/view/de-de/register_verify_member.tpl b/view/de-de/register_verify_member.tpl
new file mode 100644
index 000000000..01762cc32
--- /dev/null
+++ b/view/de-de/register_verify_member.tpl
@@ -0,0 +1,57 @@
+
+Vielen Dank für Ihre Registrierung auf {{$sitename}}.
+
+Bitte lesen Sie diese Nachricht aufmerksam durch, bevor Sie eine der beschriebenen Optionen ausführen.
+
+Die Details für die Anmeldung sind folgende:
+
+Portal Adresse: {{$siteurl}}
+Login Name: {{$email}}
+
+Die Anmeldung erfolgt mit dem von Ihnen bei der Registrierung festgelegten Kennwort. Falls Sie bereits bei der
+Registrierung einen Nicknamen für Ihren Kanal festgelegt hatten, können Sie den alternativ zur Email Adresse als
+Login Name benutzen. Falls noch kein Kanal besteht, werden Sie den unmittelbar nach der nächsten Anmeldung
+festlegen.
+
+Um Ihnen vollständigen Zugriff geben zu können, benötigen wir zur Überprüfung die Bestätigung Ihrer Email Adresse.
+
+Der Prüfungscode ist:
+
+{{$hash}}
+
+
+{{if $due}}{{$due}}{{/if}}
+
+
+
+Wir gehen davon aus, dass Sie dieses Anmeldkonto registriert haben und bitten Sie, den Prüungscode auf der folgenden Adresse
+zu bestätigen:
+
+{{$siteurl}}/regate/{{$mail}}
+
+
+Leider kommt es auch vor, dass Email Adressen missbräuchlich verwendet werden. Auf vielen Portalen im Internet können Böswillige versuchen, durch Eingabe einer beliebigen bekannten Email Adresse eine Benutzerkonto-Registrierung einzuleiten. Sollten Sie die
+Registrierung selber nicht beabsichtigt haben, raten wir aus Sicherheitsgründen dringend davon ab, den Vorgang zu bestätigen. Das
+gilt auch dann, wenn Sie nach Besuch des Portals nicht abgeneigt sein sollten, dort Zugang zu erhalten. Ein Böswiller hat die
+Registrierung vielleicht schon soweit vorbereitet, dass eine Bestätigung nicht Ihnen, sondern letztlich Unberechtigten Zugang
+zum Portal bewährt.
+Im Fall einer mißbräuchlich versuchten Registrierung können Sie uns helfen, indem Sie den Registrierungsvorgang stornieren.
+Zur Stornierung und Zurückweisung der Registrierung steht folgende Portal Adresse zur Verfügung:
+
+
+{{$siteurl}}/regate/{{$mail}}{{if $ko}}/{{$ko}}{{/if}}
+
+
+Falls Sie über die mißbräuchliche Nutzung Ihrer Email Adresse verunsichert sein sollten und Bedenken gegen die Durchführung der
+Stornierung haben, möchten wir Sie beruhigen und vergewissern, das der Registrierungsvorgang nach einer gewissen Zeit
+automatisch storniert wird. Jedenfalls bedauern wir die Unannehmlichkeiten.
+
+
+Vielen Dank für Ihre Aufwerksamkeit und Kooperation.
+
+Die Betreiber der Platform
+
+--
+Datenschutzerklärung,Terms Of Service:
+{{$siteurl}}/help/TermsOfService
+
diff --git a/view/en/invite.casual.subject.tpl b/view/en/invite.casual.subject.tpl
new file mode 100644
index 000000000..e3c7c91fc
--- /dev/null
+++ b/view/en/invite.casual.subject.tpl
@@ -0,0 +1 @@
+Please join us on {{$projectname}} {{$invite_loc}} \ No newline at end of file
diff --git a/view/en/invite.casual.tpl b/view/en/invite.casual.tpl
new file mode 100644
index 000000000..4fd031c63
--- /dev/null
+++ b/view/en/invite.casual.tpl
@@ -0,0 +1,16 @@
+{{* tpl EN casual, to invite friendly to those knowing me *}}
+Please join my community on {{$projectname}}.
+{{$linktxt}} {{$invite_where}}
+
+You will need to supply this invitation code:
+ {{$invite_code}}
+
+Or:
+
+1. Register at any {{$projectname}} location
+ (they are all inter-connected).
+2. Enter my {{$Projectname}} network address into the site searchbar.
+ {{$invite_whereami}}
+ or visit {{$invite_whoami}}
+3. Click [Connect]
+
diff --git a/view/en/invite.formal.subject.tpl b/view/en/invite.formal.subject.tpl
new file mode 100644
index 000000000..3c5a92763
--- /dev/null
+++ b/view/en/invite.formal.subject.tpl
@@ -0,0 +1 @@
+Invitation for your network access on {{$invite_loc}} \ No newline at end of file
diff --git a/view/en/invite.formal.tpl b/view/en/invite.formal.tpl
new file mode 100644
index 000000000..5f70de00a
--- /dev/null
+++ b/view/en/invite.formal.tpl
@@ -0,0 +1,32 @@
+{{* tpl EN formal, to invite most polite, like company members or business partners *}}
+Please join the communication platform {{$projectname}} and this message contains the
+essential data for initially connecting to.
+
+The site is to arrive here: {{$invite_whereami}}
+
+Your access is just prepared with an invite code
+
+ {{$invite_code}}
+
+what please is to enter into the field of the register form, that is shown when the
+link "I have an invite code" will be clicked. Please also enter your email address
+in the next field. We want to let you know, the invite code is bound to your email
+address and not transferable.
+
+For security reasons you have to supply an account password that is and should remain
+only known by yourself. The non visible password has to be typed in twice to prevent
+mistyping. The password is required later always when you want to login on the site.
+
+Depending on the site's configuration, you may receive another email to your address
+with a validation code, that is to enter into the form such email will point to. This
+kind of some inconvience helps to grow security for the site.
+
+Depending on the site's configuration, an instance administrator finally has to
+confirm your access. Please be patient because that will not performed immediatly
+in real time.
+
+Kind regards,
+and wish to have best success on the site.
+
+Disclaimer:
+... \ No newline at end of file
diff --git a/view/en/invite.material.subject.tpl b/view/en/invite.material.subject.tpl
new file mode 100644
index 000000000..b74c99aa4
--- /dev/null
+++ b/view/en/invite.material.subject.tpl
@@ -0,0 +1 @@
+Invitation {{$invite_loc}} \ No newline at end of file
diff --git a/view/en/invite.material.tpl b/view/en/invite.material.tpl
new file mode 100644
index 000000000..db1613083
--- /dev/null
+++ b/view/en/invite.material.tpl
@@ -0,0 +1 @@
+{{* tpl en material, to invite with the essential data in brief *}} \ No newline at end of file
diff --git a/view/en/register_verify_member.tpl b/view/en/register_verify_member.tpl
index 9bdd7fa51..5db51a2e2 100644
--- a/view/en/register_verify_member.tpl
+++ b/view/en/register_verify_member.tpl
@@ -15,15 +15,19 @@ Your validation code is
{{$hash}}
+{{if $due}}{{$due}}{{/if}}
+
+
+
If you registered this account, please enter the validation code when requested or visit the following link:
-{{$siteurl}}/regver/allow/{{$hash}}
+{{$siteurl}}/regate/{{$mail}}
To deny the request and remove the account, please visit:
-{{$siteurl}}/regver/deny/{{$hash}}
+{{$siteurl}}/regate/{{$mail}}{{if $ko}}/{{$ko}}{{/if}}
Thank you.
diff --git a/view/es-es/invite.casual.subject.tpl b/view/es-es/invite.casual.subject.tpl
new file mode 100644
index 000000000..bec594c3f
--- /dev/null
+++ b/view/es-es/invite.casual.subject.tpl
@@ -0,0 +1 @@
+Por favor, acompáñenos en {{$projectname}} \ No newline at end of file
diff --git a/view/es-es/invite.casual.tpl b/view/es-es/invite.casual.tpl
new file mode 100644
index 000000000..51d5ad3cb
--- /dev/null
+++ b/view/es-es/invite.casual.tpl
@@ -0,0 +1,14 @@
+Por favor, únase a mi comunidad en {{$projectname}}.
+{{$linktxt}} {{$invite_where}}
+
+Tendrá que proporcionar este código de invitación:
+ {{$invite_code}}
+
+O:
+
+1. Registrarse en cualquier lugar {{$projectname}}
+ (todos están interconectados).
+2. 2. Introduzca mi dirección de red {{$projectname}} en la barra de búsqueda del sitio.
+ {{$invite_whereami}}
+ o visite {{$invite_whoami}}
+3. 3. Haga clic en [Conectar].
diff --git a/view/es-es/invite.formal.subject.tpl b/view/es-es/invite.formal.subject.tpl
new file mode 100644
index 000000000..e4a104c46
--- /dev/null
+++ b/view/es-es/invite.formal.subject.tpl
@@ -0,0 +1 @@
+Invitación para su acceso a la red en {{$invite_loc}} \ No newline at end of file
diff --git a/view/es-es/invite.formal.tpl b/view/es-es/invite.formal.tpl
new file mode 100644
index 000000000..6c0841f84
--- /dev/null
+++ b/view/es-es/invite.formal.tpl
@@ -0,0 +1,19 @@
+Si lo desea, puede unirse a las redes sociales descentralizadas de {{$projectname}}
+aquí: {{$invite_where}}
+Probablemente se le pedirá una vez que permita el acceso,
+indicar este código de invitación: {{$invite_code}}
+Durante el proceso de registro también debe proporcionar su dirección de correo electrónico y establecer una contraseña,
+que es confidencial y sólo debe ser conocido por usted.
+
+Posibilidades alternativas:
+
+1. Eine Registrierung wird an jeder der öffentlich zugänglichen {{$projectname}} Instanzen unterstützt,
+ weil die alle miteinander verbunden sind. Eine Übersicht ist zu finden unter:
+ {{$invite_anywhere}}
+2. Geben Sie meine {{$projectname}} Netzwerkadresse {{$invite_whereami}} in die Suchleiste beim
+ Lupensymbol ein oder besuchen Sie mich unter {{$invite_whoami}}
+3. Klicken Sie auf [Verbinden]
+
+Falls auf einer besuchten {{$projectname}} Instanz fremdsprachige Anzeigen dargestellt sind,
+können Sie die Anzeige einfach auf eine eigene (oder bevorzugte) Sprache im
+Hamburger Menü  umschalten.
diff --git a/view/js/main.js b/view/js/main.js
index 16dcd9cfe..6c418b213 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -959,7 +959,7 @@ function liveUpdate(notify_id) {
var orgHeight = $("#region_2").height();
}
- if(page_cache.data && bParam_page == page_cache.page && page_cache.time > (Date.now() - 180000)) {
+ if(page_cache.data && bParam_page == page_cache.page) {
page_load = false;
scroll_next = false;
updateConvItems(update_mode,page_cache.data);
diff --git a/view/js/mod_register.js b/view/js/mod_register.js
index 6607579a2..16f9b6da1 100644
--- a/view/js/mod_register.js
+++ b/view/js/mod_register.js
@@ -1,56 +1,87 @@
$(document).ready(function() {
- $("#id_email").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);
- zFormError("#help_email",data.error);
- });
+
+ // set in Module
+ //typeof(window.tao) == 'undefined' ? window.tao = {} : '';
+ //tao.zar = { vsn: '2.0.0', form: {}, msg: {} };
+ //tao.zar.patano = /^d[0-9]{6}$/;
+ //tao.zar.patema = /^[a-z0-9.-]{2,64}@[a-z0-9.-]{4,32}\.[a-z]{2,12}$/;
+
+ $('#zar014').click( function () { $('#zar015').toggle(); });
+
+ $('#id_email').change(function() {
+ tao.zar.form.email = $('#id_email').val();
+ if (tao.zar.patano.test(tao.zar.form.email) == true ) {
+ //ano
+ } else {
+ if (tao.zar.patema.test(tao.zar.form.email) == false ) {
+ $('#help_email').removeClass('text-muted').addClass('zuirise').html(tao.zar.msg.ZAR0239E);
+ zFormError('#help_email',true);
+ } else {
+ $.get('register/email_check.json?f=&email=' + encodeURIComponent(tao.zar.form.email), function(data) {
+ $('#help_email').removeClass('text-muted').addClass('zuirise').html(data.message);
+ zFormError('#help_email',data.error);
+ });
+ }
+ }
+ if ($('#id_email').val().length > 0) {
+ $('#newchannel-submit-button').removeAttr('disabled');
+ }
});
- $("#id_password").blur(function() {
- if(($("#id_password").val()).length < 6 ) {
- $("#help_password").html(aStr.pwshort);
- zFormError("#help_password", true);
+
+ $('#id_password').change(function() {
+ if(($('#id_password').val()).length < 6 ) {
+ $('#help_password').removeClass('text-muted').addClass('zuirise').html(aStr.pwshort);
+ zFormError('#help_password', true);
}
else {
- $("#help_password").html("");
- zFormError("#help_password", false);
+ $('#help_password').html('');
+ zFormError('#help_password', false);
+ $('#id_password2').focus();
+ $('#id_password2').val().length > 0 ? $('#id_password2').trigger('change') : '';
}
});
- $("#id_password2").blur(function() {
- if($("#id_password").val() != $("#id_password2").val()) {
- $("#help_password2").html(aStr.pwnomatch);
- zFormError("#help_password2", true);
+ $('#id_password2').change(function() {
+ if($('#id_password').val() != $('#id_password2').val()) {
+ $('#help_password2').removeClass('text-muted').addClass('zuirise').html(aStr.pwnomatch);
+ zFormError('#help_password2', true);
+ $('#id_password').focus();
}
else {
- $("#help_password2").html("");
- zFormError("#help_password2", false);
+ $('#help_password2').html('');
+ zFormError('#help_password2', false);
}
});
- $("#id_name").blur(function() {
- $("#name-spinner").show();
- var zreg_name = $("#id_name").val();
- $.get("new_channel/autofill.json?f=&name=" + encodeURIComponent(zreg_name),function(data) {
- $("#id_nickname").val(data);
+ $('#id_name').blur(function() {
+ $('#name-spinner').show();
+ var zreg_name = $('#id_name').val();
+ $.get('new_channel/autofill.json?f=&name=' + encodeURIComponent(zreg_name),function(data) {
+ $('#id_nickname').val(data);
if(data.error) {
- $("#help_name").html("");
- zFormError("#help_name",data.error);
+ $('#help_name').html('');
+ zFormError('#help_name',data.error);
}
- $("#name-spinner").hide();
+ $('#name-spinner').hide();
});
});
-
- $("#id_nickname").blur(function() {
- $("#nick-spinner").show();
- var zreg_nick = $("#id_nickname").val();
- $.get("new_channel/checkaddr.json?f=&nick=" + encodeURIComponent(zreg_nick),function(data) {
- $("#id_nickname").val(data);
+ $('#id_nickname').blur(function() {
+ $('#nick-spinner').show();
+ var zreg_nick = $('#id_nickname').val();
+ $.get('new_channel/checkaddr.json?f=&nick=' + encodeURIComponent(zreg_nick),function(data) {
+ $('#id_nickname').val(data);
if(data.error) {
- $("#help_nickname").html("");
- zFormError("#help_nickname",data.error);
+ $('#help_nickname').html('');
+ zFormError('#help_nickname',data.error);
}
- $("#nick-spinner").hide();
+ $('#nick-spinner').hide();
});
});
+ //$("buttom[name='submit']").submit((function() {
+ $('#register-form').submit(function(e) {
+ if ( $('.zform-error').length > 0 ) {
+ e.preventDefault();
+ return false;
+ }
+ });
});
diff --git a/view/tpl/admin_accounts.tpl b/view/tpl/admin_accounts.tpl
index 2dd56c8dc..9c78dbe54 100644
--- 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,24 +16,36 @@
</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}}
+ </td>
+
+ <td class="status">{{$u.status}}</td>
+
+ <td class="created">{{$u.reg_startup}}<br>{{$u.reg_expires}}</td>
+ <td class="email">{{$u.reg_did2}}</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>
- <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}}" />
- </div>
+ {{* before, alternate:
+ *
+ <a href="#" onclick="return toggle_selectall('pending_ckbx');">{{$select_all}}</a>
+ *
+ *}}
+ <div class="selectall">
+ <a id="zar2sat" class="btn btn-sm btn-primary" href="javascript:;">{{$sel_tall}}</a>
+ <a id="zar2aas" class="zar2xas btn btn-sm btn-success" href="javascript:;"><i class="fa fa-check"></i> {{$sel_aprv}}</a>
+ <a id="zar2das" class="zar2xas btn btn-sm btn-danger" href="javascript:;"><i class="fa fa-close"></i> {{$sel_deny}}</a>
+ </div>
{{else}}
<p>{{$no_pending}}</p>
{{/if}}
@@ -83,7 +84,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 +98,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..0fff7029a 100644
--- 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,7 +33,17 @@
{{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="field_checkbox.tpl" field=$invite_only}}
+ {{include file="field_checkbox.tpl" field=$register_wo_email}}
+ {{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=$invitation_only}}
+ {{include file="field_checkbox.tpl" field=$invitation_also}}
+ {{include file="field_checkbox.tpl" field=$verify_email}}
+ {{include file="field_input.tpl" field=$abandon_days}}
{{include file="field_input.tpl" field=$minimum_age}}
{{include file="field_select.tpl" field=$access_policy}}
{{include file="field_input.tpl" field=$location}}
@@ -81,7 +55,6 @@
</div>
<h3>{{$corporate}}</h3>
- {{include file="field_checkbox.tpl" field=$verify_email}}
{{include file="field_checkbox.tpl" field=$feed_contacts}}
{{include file="field_checkbox.tpl" field=$force_publish}}
{{include file="field_checkbox.tpl" field=$disable_discover_tab}}
@@ -105,7 +78,6 @@
{{include file="field_input.tpl" field=$force_queue}}
{{include file="field_input.tpl" field=$poll_interval}}
{{include file="field_input.tpl" field=$maxloadavg}}
- {{include file="field_input.tpl" field=$abandon_days}}
{{include file="field_input.tpl" field=$default_expire_days}}
{{include file="field_input.tpl" field=$active_expire_days}}
@@ -115,3 +87,52 @@
</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; }'+
+ ' .zuia { cursor: pointer; font-weight: bold; }'+
+ '</style>');
+ // <-hilmar]
+</script>
diff --git a/view/tpl/field_checkbox.tpl b/view/tpl/field_checkbox.tpl
index f779f937c..b1665f75f 100644
--- a/view/tpl/field_checkbox.tpl
+++ b/view/tpl/field_checkbox.tpl
@@ -1,5 +1,25 @@
<div id="{{$field.0}}_container" class="clearfix form-group checkbox">
- <label for="id_{{$field.0}}">{{$field.1}}</label>
+ <label for="id_{{$field.0}}">{{$field.1}}{{if $field.6}}<sup class="required zuiqmid"> {{$field.6}}</sup>{{/if}}</label>
<div class="float-right"><input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="1" {{if $field.2}}checked="checked"{{/if}} {{if $field.5}}{{$field.5}}{{/if}} /><label class="switchlabel" for='id_{{$field.0}}'> <span class="onoffswitch-inner" data-on='{{if $field.4}}{{$field.4.1}}{{/if}}' data-off='{{if $field.4}}{{$field.4.0}}{{/if}}'></span><span class="onoffswitch-switch"></span></label></div>
<small class="form-text text-muted">{{$field.3}}</small>
</div>
+{{*
+ COMMENTS for this template:
+ @author hilmar runge, 2020.01
+ $field array index:
+ .0 field name: name=... for input, id=id_... for input, id=label_... for label, id=help_... for small text
+ .1 label text
+ .2 checked
+ .3 form text
+ .4 on/off value:
+ .4.0 off
+ .4.1 on
+ .5 additional operands for html input statement
+ .6 label text addition, used for qmc
+ css classes used:
+ .clearfix, .form_group, .checkbox
+ .floatright
+ .switchlabel, .onoffswitch-switch
+ .required, .code
+ .form-control, .form-text, .text-muted
+*}}
diff --git a/view/tpl/field_duration.qmc.tpl b/view/tpl/field_duration.qmc.tpl
new file mode 100644
index 000000000..5ef11a6b9
--- /dev/null
+++ b/view/tpl/field_duration.qmc.tpl
@@ -0,0 +1,87 @@
+{{if $wrapper!="no"}}<div id="{{$qmc}}{{$field.name}}_wrapper" class="form-group">{{/if}}
+
+<label for="{{$qmc}}{{$field.name}}fs">{{$label}}
+ {{if $qmcid}}<sup class="zuiqmid required">{{$qmcid}}</sup>{{/if}}
+</label>
+<fieldset name="{{$qmc}}{{$field.name}}fs" id="id_{{$qmc}}{{$field.name}}_fs" title="{{$field.title}}">
+
+<input id="{{$qmc}}{{$field.name}}n"
+ name="{{$qmc}}{{$field.name}}n"
+ class="inline-block mr-1 text-center" style="width: 5rem;"
+ type="number"
+{{if $field.min}} min="{{$field.min}}"{{/if}}
+{{if $field.max}} max="{{$field.max}}"{{/if}}
+ size="{{$field.size}}"
+ value="{{$field.value}}"
+ title="{{$field.title}}">
+
+{{foreach $rabot as $k=>$v}}
+ <input id="{{$qmc}}{{$field.name}}{{$k}}" name="{{$qmc}}{{$field.name}}"
+ type="radio" value="{{$k}}" {{if $field.default==$k}} checked="checked"{{/if}}>
+ <label for="{{$qmc}}{{$field.name}}{{$k}}">{{$v}}</label>
+{{/foreach}}
+
+</fieldset>
+
+<span id="{{$qmc}}{{$field.name}}_help" class="form-text text-muted">{{$help}}</span>
+
+{{if $wrapper!="no"}}</div>{{/if}}
+
+{{*
+ * Template field_duration.qmc.tpl
+ * **********************************
+ * Hilmar Runge, 2020.02
+ * The template generates one input field for numeric values and a radio button group, where one
+ * (and only one or no) selection can be active. The primary intented use is for entering time/date
+ * data in the form of amount (numeric) and the units (ie hours, days etc).
+ * Instead of using positional array parameters, keyed (named) parameters are treated. Imo, named parameters
+ * are easier to apply, the position does not matter and if one is not wanted or required, only omit it.
+ *
+ * The parameters in this template are:
+ * ************************************
+ * label A label for the whole. Optional.
+ * help An optional explanation text.
+ * qmc Optional a qualified message component prefix, best use case is 3 letters lowercase and depends
+ * on the module or component used in the system. Part of id's and names in html and css.
+ * qmcid The qmc message id. Optional. Should be qmc+4digits+1charsufffix (8 chars uppercase).
+ * field keyed array parameters:
+ * name The (unique) name of the elements also used for html ids,
+ * will be suffixed by 'n' for the numeric input and 'u' for the units
+ * title The title of the element
+ * legend a headline for the radio buttons (optional)
+ * rabot the keyed array of radio buttons, where:
+ * k the key becomes the submitted value
+ * v the string value is the label text for the radio button.
+ *
+ * Example to apply in php like:
+ * *****************************
+ $testcase = replace_macros(get_markup_template('field_radio_group.qmc.tpl'),
+ array(
+ 'label' => t('Exiration duration',
+ 'qmc' => 'zai', // not required
+ 'qmcid' => 'ZAI0000I', // not required
+ 'wrapper' => 'no', // when no wrapper around is desired
+ 'field' => // fieldset properties
+ array(
+ 'name' => 'due',
+ 'min' => "1", // the minimum value for the numeric input
+ 'max' => "99", // the maximum value for the numeric input
+ 'size' => "2", // the max digits for the numeric input
+ 'title' => 'time/date unit',
+ 'default' => 'd' // say 'default' => '' if none defaults (or omit)
+ ),
+ 'rabot' => // the radio buttons
+ array(
+ 'i' => 'Minute(s)',
+ 'h' => 'Hour(s)' ,
+ 'd' => 'Day(s)' ,
+ 'w' => 'Week(s)' ,
+ 'm' => 'Month(s)' ,
+ 'y' => 'Year(s)'
+ )
+ )
+ );
+ *
+ *}}
+
+
diff --git a/view/tpl/field_input.tpl b/view/tpl/field_input.tpl
index 65a837e5f..53139a0e7 100644
--- a/view/tpl/field_input.tpl
+++ b/view/tpl/field_input.tpl
@@ -1,5 +1,19 @@
<div id="id_{{$field.0}}_wrapper" class="form-group">
- <label for="id_{{$field.0}}" id="label_{{$field.0}}">{{$field.1}}{{if $field.4}}<span class="required"> {{$field.4}}</span>{{/if}}</label>
+ <label for="id_{{$field.0}}" id="label_{{$field.0}}">{{$field.1}}{{if $field.4}}<sup class="required zuiqmid"> {{$field.4}}</sup>{{/if}}</label>
<input class="form-control" name="{{$field.0}}" id="id_{{$field.0}}" type="text" value="{{$field.2}}"{{if $field.5}} {{$field.5}}{{/if}}>
- <small id="help_{{$field.0}}" class="form-text text-muted">{{$field.3}}</small>
+ <span id="help_{{$field.0}}" class="form-text text-muted">{{$field.3}}</span>
</div>
+{{*
+ COMMENTS for this template:
+ @author hilmar runge, 2020.01
+ $field array index:
+ .0 field name: name=... for input, id=id_... for input, id=label_... for label, id=help_... for text
+ .1 label text
+ .2 field value
+ .3 help text
+ .4 label text addition, used for qmc
+ .5 additional html attributes
+ css classes used:
+ .required, .code
+ .form-control, .form-text, .text-muted
+*}}
diff --git a/view/tpl/field_select.tpl b/view/tpl/field_select.tpl
index 7cc624fab..57be3b1ab 100644
--- a/view/tpl/field_select.tpl
+++ b/view/tpl/field_select.tpl
@@ -1,7 +1,22 @@
<div id="id_{{$field.0}}_wrapper" class="form-group">
- <label for="id_{{$field.0}}">{{$field.1}}</label>
+ <label for="id_{{$field.0}}">{{$field.1}}{{if $field.5}}<sup class="required zuiqmid"> {{$field.5}}</sup>{{/if}}</label>
<select class="form-control" name="{{$field.0}}" id="id_{{$field.0}}">
{{foreach $field.4 as $opt=>$val}}<option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
</select>
<small class="form-text text-muted">{{$field.3}}</small >
</div>
+{{*
+ COMMENTS for this template:
+ @author hilmar runge, 2020.01
+ $field array index:
+ .0 field name: name=... for input, id=id_... for input, id=label_... for label, id=help_... for small text
+ .1 label text
+ .2 selected field
+ .3 form text
+ .4 option value(s)
+ .5 label text addition, used for qmc
+ css classes used:
+ .required, .code
+ .form-group, .form-control, .form-text, .text-muted
+*}}
+
diff --git a/view/tpl/invite.tpl b/view/tpl/invite.tpl
index 440e1e02b..2712cfee4 100644
--- 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..e7aa9c63c
--- /dev/null
+++ b/view/tpl/plain.tpl
@@ -0,0 +1,37 @@
+<h2>{{$title}}</h2>
+{{if $now}}<div>{{$now}}</div>{{/if}}
+<div style="font-weight: normal; font-family: monospace;">{{$infos}}</div>
+<div id="countdown" class="h3"></div>
+<script>
+ $('.register_date').each( function () {
+ var date = new Date($(this).data('utc'));
+ $(this).html(date.toLocaleString());
+ });
+
+
+ var date = '{{$countdown}}';
+
+ date = date !== '' ? date : $('#register_start').data('utc');
+
+ if(date) {
+ doCountDown(date, 'countdown');
+ var x = setInterval(doCountDown, 1000, date, 'countdown');
+ }
+ function doCountDown(date, id) {
+ var countDownDate = new Date(date).getTime();
+ var now = new Date().getTime();
+ var distance = countDownDate - now;
+ var days = Math.floor(distance / (1000 * 60 * 60 * 24));
+ var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
+ var seconds = Math.floor((distance % (1000 * 60)) / 1000);
+
+ document.getElementById(id).innerHTML = days + "d " + hours + "h "+ minutes + "m " + seconds + "s ";
+
+ if (distance < 0) {
+ clearInterval(x);
+ document.getElementById(id).innerHTML = 'Reloading...';
+ window.location.reload();
+ }
+ }
+</script>
diff --git a/view/tpl/regate.tpl b/view/tpl/regate.tpl
new file mode 100644
index 000000000..04a9df1de
--- /dev/null
+++ b/view/tpl/regate.tpl
@@ -0,0 +1,29 @@
+<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>
+<script>
+ var week_days = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];
+ $('.register_date').each( function () {
+ var date = new Date($(this).data('utc'));
+ $(this).html(date.toLocaleString(undefined, {weekday: 'short', hour: 'numeric', minute: 'numeric'}));
+ });
+</script>
diff --git a/view/tpl/register.tpl b/view/tpl/register.tpl
index 1054c7567..b38507f92 100644
--- a/view/tpl/register.tpl
+++ b/view/tpl/register.tpl
@@ -5,24 +5,29 @@
<div class="section-content-wrapper">
<form action="register" method="post" id="register-form">
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
- {{if $reg_is}}
+ {{if $reg_is || $other_sites || $now}}
<div class="section-content-warning-wrapper">
+ <h3>{{$now}}</h3>
<div id="register-desc" class="descriptive-paragraph">{{$reg_is}}</div>
<div id="register-sites" class="descriptive-paragraph">{{$other_sites}}</div>
</div>
{{/if}}
-
{{if $registertext}}
<div class="section-content-info-wrapper">
<div id="register-text" class="descriptive-paragraph">{{$registertext}}</div>
</div>
{{/if}}
+ <div>
{{if $invitations}}
- {{include file="field_input.tpl" field=$invite_code}}
+ <a id="zar014" href="javascript:;" style="display: inline-block;">{{$haveivc}}</a>
+ <div id="zar015" style="display: none;">
+ {{include file="field_input.tpl" field=[$invite_code.0,$invite_code.1,"","",""]}}
+ </div>
{{/if}}
{{include file="field_input.tpl" field=$email}}
+ </div>
{{include file="field_password.tpl" field=$pass1}}
@@ -38,24 +43,42 @@
{{include file="field_select_grouped.tpl" field=$role}}
{{/if}}
- {{include file="field_input.tpl" field=$name}}
+ {{include file="field_input.tpl" field=[$name.0,$name.1,"","","",$atform]}}
<div id="name-spinner" class="spinner-wrapper"><div class="spinner m"></div></div>
- {{include file="field_input.tpl" field=$nickname}}
+ {{include file="field_input.tpl" field=[$nickname.0,$nickname.1,"","","",$atform]}}
<div id="nick-spinner" class="spinner-wrapper"><div class="spinner m"></div></div>
{{/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 />
- <div class="descriptive-text">{{$verify_note}}</div>
-
+ <br />
+ <div class="descriptive-text">{{$verify_note}} {{$msg}}</div>
</div>
</div>
+{{*
+ COMMENTS for this template:
+ hilmar, 2020.02
+*}}
+<script>
+ $('head').append(
+ '<style> '+
+ ' .zuiqmid { font-weight: normal; font-family: monospace; }'+
+ ' .zuirise { font-weight: bold; font-size: 100%; color: red; }'+
+ '</style>');
+
+ {{$tao}}
+
+ var week_days = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];
+ $('.register_date').each( function () {
+ var date = new Date($(this).data('utc'));
+ $(this).html(date.toLocaleString(undefined, {weekday: 'short', hour: 'numeric', minute: 'numeric'}));
+ });
+</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