mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 07:01:16 +00:00
174 lines
7.2 KiB
PHP
174 lines
7.2 KiB
PHP
<?php
|
|
|
|
/*
|
|
* The setup wizard's account, company and region steps (setup/index.php) -
|
|
* what was sent, checked: each returns [values, errors], the errors keyed by
|
|
* field and said in words, so a step that has something to say shows it
|
|
* beside the field with everything typed kept. And what Region suggests from
|
|
* the company's country.
|
|
*/
|
|
|
|
defined('FROM_SETUP') || die("Direct file access is not allowed");
|
|
|
|
const SETUP_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
|
|
/* The account step - name, email and the password, typed twice. The password is kept exactly as typed, as the sign-in page checks it. */
|
|
function setupUserForm(array $post) {
|
|
$values = [
|
|
'name' => trim((string) ($post['name'] ?? '')),
|
|
'email' => trim((string) ($post['email'] ?? '')),
|
|
'password' => (string) ($post['password'] ?? ''),
|
|
];
|
|
$errors = [];
|
|
if ($values['name'] === '') {
|
|
$errors['name'] = 'Your name, as your team will see it.';
|
|
} elseif (mb_strlen($values['name']) > 200) {
|
|
$errors['name'] = 'Up to 200 characters.';
|
|
}
|
|
if ($values['email'] === '') {
|
|
$errors['email'] = 'The email you will sign in with.';
|
|
} elseif (!filter_var($values['email'], FILTER_VALIDATE_EMAIL) || mb_strlen($values['email']) > 200) {
|
|
$errors['email'] = 'That is not an email address.';
|
|
}
|
|
if (($problem = userPasswordProblem($values['password'])) !== '') {
|
|
$errors['password'] = $problem;
|
|
} elseif (array_key_exists('password_confirm', $post) && (string) $post['password_confirm'] !== $values['password']) {
|
|
$errors['password_confirm'] = 'The two passwords are not the same.';
|
|
}
|
|
return [$values, $errors];
|
|
}
|
|
|
|
/*
|
|
* What is wrong with an uploaded picture - '' when nothing is, or nothing was
|
|
* sent. A real image of one of $types (IMAGETYPE_*), checked rather than
|
|
* trusted by its name, up to 5 MB.
|
|
*/
|
|
function setupImageProblem($file, array $extensions, array $types, $kinds) {
|
|
if (!is_array($file) || ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE || empty($file['name'])) {
|
|
return '';
|
|
}
|
|
if (in_array($file['error'], [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE], true) || ($file['size'] ?? 0) > SETUP_IMAGE_MAX_BYTES) {
|
|
return "That picture is too large - up to 5 MB.";
|
|
}
|
|
$image = checkFileUpload($file, $extensions) ? @getimagesize($file['tmp_name']) : false;
|
|
if (!$image || !in_array($image[2], $types, true)) {
|
|
return "That isn't a $kinds picture.";
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/* The company step - its name and country, and the rest as given. The website loses its http:// and trailing slash. */
|
|
function setupCompanyForm(array $post, array $countries) {
|
|
$values = [];
|
|
foreach (['name', 'country', 'address', 'city', 'state', 'zip', 'email', 'website', 'tax_id'] as $field) {
|
|
$values[$field] = mb_substr(trim((string) ($post[$field] ?? '')), 0, 200);
|
|
}
|
|
$values['phone_country_code'] = mb_substr(preg_replace('/[^0-9]/', '', (string) ($post['phone_country_code'] ?? '')), 0, 10);
|
|
$values['phone'] = mb_substr(preg_replace('/[^0-9]/', '', (string) ($post['phone'] ?? '')), 0, 200);
|
|
$values['website'] = rtrim(preg_replace('#^[a-z][a-z0-9+.-]*://#i', '', $values['website']), '/');
|
|
if ($values['phone'] === '') {
|
|
$values['phone_country_code'] = '';
|
|
}
|
|
|
|
$errors = [];
|
|
if ($values['name'] === '') {
|
|
$errors['name'] = "Your company's name, as it goes on invoices.";
|
|
}
|
|
if (!in_array($values['country'], $countries, true)) {
|
|
$errors['country'] = 'Pick the country the company is in.';
|
|
}
|
|
if ($values['email'] !== '' && !filter_var($values['email'], FILTER_VALIDATE_EMAIL)) {
|
|
$errors['email'] = 'That is not an email address.';
|
|
}
|
|
if ($values['website'] !== '' && !preg_match('/^[^\s\/]+\.[^\s\/]+(\/\S*)?$/', $values['website'])) {
|
|
$errors['website'] = 'A website looks like company.com.';
|
|
}
|
|
return [$values, $errors];
|
|
}
|
|
|
|
/* The region step - a language, currency and timezone from the lists offered. */
|
|
function setupRegionForm(array $post, array $locales, array $currencies, array $timezones) {
|
|
$values = [
|
|
'locale' => trim((string) ($post['locale'] ?? '')),
|
|
'currency_code' => strtoupper(trim((string) ($post['currency_code'] ?? ''))),
|
|
'timezone' => trim((string) ($post['timezone'] ?? '')),
|
|
];
|
|
$errors = [];
|
|
if (!isset($locales[$values['locale']])) {
|
|
$errors['locale'] = 'Pick the language dates and numbers are written in.';
|
|
}
|
|
if (!isset($currencies[$values['currency_code']])) {
|
|
$errors['currency_code'] = 'Pick the currency you bill in.';
|
|
}
|
|
if (!in_array($values['timezone'], $timezones, true)) {
|
|
$errors['timezone'] = 'Pick your timezone.';
|
|
}
|
|
return [$values, $errors];
|
|
}
|
|
|
|
/* The currency a country uses (intl knows) - when it is one ITFlow offers, or ''. */
|
|
function setupSuggestCurrency($iso2, array $currencies) {
|
|
$iso2 = strtoupper((string) $iso2);
|
|
if (!preg_match('/^[A-Z]{2}$/', $iso2) || !class_exists('NumberFormatter')) {
|
|
return '';
|
|
}
|
|
$code = (new NumberFormatter("und_$iso2", NumberFormatter::CURRENCY))->getTextAttribute(NumberFormatter::CURRENCY_CODE);
|
|
return isset($currencies[$code]) ? $code : '';
|
|
}
|
|
|
|
/*
|
|
* The language for a country - the browser's own language there when ITFlow
|
|
* has it (fr_CA for a French browser in Canada), then English there, then any
|
|
* language of that country; failing those the browser's language, then en_US.
|
|
*/
|
|
function setupSuggestLocale($iso2, $accept_language, array $locales) {
|
|
$iso2 = strtoupper((string) $iso2);
|
|
$browser = '';
|
|
if ($accept_language !== '' && function_exists('locale_accept_from_http')) {
|
|
$browser = (string) locale_accept_from_http($accept_language);
|
|
}
|
|
$language = $browser !== '' ? strtolower(strtok(str_replace('-', '_', $browser), '_')) : '';
|
|
|
|
$candidates = [];
|
|
if (preg_match('/^[A-Z]{2}$/', $iso2)) {
|
|
if ($language !== '') {
|
|
$candidates[] = "{$language}_$iso2";
|
|
}
|
|
$candidates[] = "en_$iso2";
|
|
foreach (array_keys($locales) as $locale) {
|
|
if (substr($locale, -3) === "_$iso2") {
|
|
$candidates[] = $locale;
|
|
}
|
|
}
|
|
}
|
|
if ($browser !== '') {
|
|
$candidates[] = str_replace('-', '_', $browser);
|
|
$candidates[] = $language;
|
|
}
|
|
$candidates[] = 'en_US';
|
|
foreach ($candidates as $candidate) {
|
|
if (isset($locales[$candidate])) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/* A country's timezone, when it has only the one - as the UK has, and Canada has not. */
|
|
function setupSuggestTimezone($iso2) {
|
|
$iso2 = strtoupper((string) $iso2);
|
|
if (!preg_match('/^[A-Z]{2}$/', $iso2)) {
|
|
return '';
|
|
}
|
|
$zones = @DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $iso2) ?: [];
|
|
return count($zones) === 1 ? $zones[0] : '';
|
|
}
|
|
|
|
/* The is-invalid class and the message under a field, when the step has one for it. */
|
|
function setupFieldInvalid(array $errors, $field) {
|
|
return isset($errors[$field]) ? ' is-invalid' : '';
|
|
}
|
|
function setupFieldError(array $errors, $field) {
|
|
return isset($errors[$field]) ? '<div class="invalid-feedback d-block" data-field-error="' . escapeHtml($field) . '">' . escapeHtml($errors[$field]) . '</div>' : '';
|
|
}
|