Example #1
0
function loadProfileFields($force_reload = false)
{
    global $context, $profile_fields, $txt, $scripturl, $modSettings, $user_info, $old_profile, $smcFunc, $cur_profile, $language;
    // Don't load this twice!
    if (!empty($profile_fields) && !$force_reload) {
        return;
    }
    /* This horrific array defines all the profile fields in the whole world!
    		In general each "field" has one array - the key of which is the database column name associated with said field. Each item
    		can have the following attributes:
    
    				string $type:			The type of field this is - valid types are:
    					- callback:		This is a field which has its own callback mechanism for templating.
    					- check:		A simple checkbox.
    					- hidden:		This doesn't have any visual aspects but may have some validity.
    					- password:		A password box.
    					- select:		A select box.
    					- text:			A string of some description.
    
    				string $label:			The label for this item - default will be $txt[$key] if this isn't set.
    				string $subtext:		The subtext (Small label) for this item.
    				int $size:			Optional size for a text area.
    				array $input_attr:		An array of text strings to be added to the input box for this item.
    				string $value:			The value of the item. If not set $cur_profile[$key] is assumed.
    				string $permission:		Permission required for this item (Excluded _any/_own subfix which is applied automatically).
    				function $input_validate:	A runtime function which validates the element before going to the database. It is passed
    								the relevant $_POST element if it exists and should be treated like a reference.
    
    								Return types:
    					- true:			Element can be stored.
    					- false:		Skip this element.
    					- a text string:	An error occured - this is the error message.
    
    				function $preload:		A function that is used to load data required for this element to be displayed. Must return
    								true to be displayed at all.
    
    				string $cast_type:		If set casts the element to a certain type. Valid types (bool, int, float).
    				string $save_key:		If the index of this element isn't the database column name it can be overriden
    								with this string.
    				bool $is_dummy:			If set then nothing is acted upon for this element.
    				bool $enabled:			A test to determine whether this is even available - if not is unset.
    				string $link_with:		Key which links this field to an overall set.
    
    		Note that all elements that have a custom input_validate must ensure they set the value of $cur_profile correct to enable
    		the changes to be displayed correctly on submit of the form.
    
    	*/
    $profile_fields = array('avatar_choice' => array('type' => 'callback_template', 'callback_name' => 'profile/avatar_select', 'preload' => 'profileLoadAvatarData', 'input_validate' => 'profileSaveAvatarData', 'save_key' => 'avatar'), 'bday1' => array('type' => 'callback_template', 'callback_name' => 'profile/birthdate_select', 'permission' => 'profile_extra', 'preload' => function () {
        global $cur_profile, $context;
        // Split up the birthdate....
        list($uyear, $umonth, $uday) = explode('-', empty($cur_profile['birthdate']) || $cur_profile['birthdate'] == '0001-01-01' ? '0000-00-00' : $cur_profile['birthdate']);
        $context['member']['birth_date'] = array('year' => $uyear == '0004' ? '0000' : $uyear, 'month' => $umonth, 'day' => $uday);
        return true;
    }, 'input_validate' => function (&$value) {
        global $profile_vars, $cur_profile;
        if (isset($_POST['bday2'], $_POST['bday3']) && $value > 0 && $_POST['bday2'] > 0) {
            // Set to blank?
            if ((int) $_POST['bday3'] == 1 && (int) $_POST['bday2'] == 1 && (int) $value == 1) {
                $value = '0001-01-01';
            } else {
                $value = checkdate($value, $_POST['bday2'], $_POST['bday3'] < 4 ? 4 : $_POST['bday3']) ? sprintf('%04d-%02d-%02d', $_POST['bday3'] < 4 ? 4 : $_POST['bday3'], $_POST['bday1'], $_POST['bday2']) : '0001-01-01';
            }
        } else {
            $value = '0001-01-01';
        }
        $profile_vars['birthdate'] = $value;
        $cur_profile['birthdate'] = $value;
        return false;
    }), 'birthdate' => array('type' => 'hidden', 'permission' => 'profile_extra', 'input_validate' => function (&$value) {
        global $cur_profile;
        // !!! Should we check for this year and tell them they made a mistake :P? (based on coppa at least?)
        if (preg_match('/(\\d{4})[\\-\\., ](\\d{2})[\\-\\., ](\\d{2})/', $value, $dates) === 1) {
            $value = checkdate($dates[2], $dates[3], $dates[1] < 4 ? 4 : $dates[1]) ? sprintf('%04d-%02d-%02d', $dates[1] < 4 ? 4 : $dates[1], $dates[2], $dates[3]) : '0001-01-01';
            return true;
        } else {
            $value = empty($cur_profile['birthdate']) ? '0001-01-01' : $cur_profile['birthdate'];
            return false;
        }
    }), 'date_registered' => array('type' => 'text', 'value' => empty($cur_profile['date_registered']) ? $txt['not_applicable'] : strftime('%Y-%m-%d', $cur_profile['date_registered'] + ($user_info['time_offset'] + $modSettings['time_offset']) * 3600), 'label' => $txt['date_registered'], 'log_change' => true, 'permission' => 'moderate_forum', 'input_validate' => function (&$value) {
        global $txt, $user_info, $modSettings, $cur_profile, $context;
        // Bad date!  Go try again - please?
        if (($value = strtotime($value)) === -1) {
            $value = $cur_profile['date_registered'];
            return $txt['invalid_registration'] . ' ' . strftime('%d %b %Y ' . (strpos($user_info['time_format'], '%H') !== false ? '%I:%M:%S %p' : '%H:%M:%S'), forum_time(false));
        } elseif ($value != $txt['not_applicable'] && $value != strtotime(strftime('%Y-%m-%d', $cur_profile['date_registered'] + ($user_info['time_offset'] + $modSettings['time_offset']) * 3600))) {
            $value = $value - ($user_info['time_offset'] + $modSettings['time_offset']) * 3600;
        } else {
            $value = $cur_profile['date_registered'];
        }
        return true;
    }), 'email_address' => array('type' => 'text', 'label' => $txt['email'], 'subtext' => $txt['valid_email'], 'log_change' => true, 'permission' => 'profile_identity', 'input_validate' => function (&$value) {
        global $context, $old_profile, $context, $profile_vars, $sourcedir, $modSettings;
        if (strtolower($value) == strtolower($old_profile['email_address'])) {
            return false;
        }
        $isValid = profileValidateEmail($value, $context['id_member']);
        // Do they need to revalidate? If so schedule the function!
        if ($isValid === true && !empty($modSettings['send_validation_onChange']) && !allowedTo('moderate_forum')) {
            require_once $sourcedir . '/lib/Subs-Members.php';
            $profile_vars['validation_code'] = generateValidationCode();
            $profile_vars['is_activated'] = 2;
            $context['profile_execute_on_save'][] = 'profileSendActivation';
            unset($context['profile_execute_on_save']['reload_user']);
        }
        return $isValid;
    }), 'gender' => array('type' => 'select', 'cast_type' => 'int', 'options' => 'return array(0 => \'\', 1 => $txt[\'male\'], 2 => $txt[\'female\']);', 'label' => $txt['gender'], 'permission' => 'profile_extra'), 'hide_email' => array('type' => 'check', 'value' => empty($cur_profile['hide_email']) ? true : false, 'label' => $txt['allow_user_email'], 'permission' => 'profile_identity', 'input_validate' => function (&$value) {
        $value = $value == 0 ? 1 : 0;
        return true;
    }), 'id_group' => array('type' => 'callback_template', 'callback_name' => 'profile/group_manage', 'permission' => 'manage_membergroups', 'preload' => 'profileLoadGroups', 'log_change' => true, 'input_validate' => 'profileSaveGroups'), 'id_theme' => array('type' => 'callback_template', 'callback_name' => 'profile/theme_pick', 'permission' => 'profile_extra', 'enabled' => $modSettings['theme_allow'] || allowedTo('admin_forum'), 'preload' => function () {
        global $context, $cur_profile, $txt;
        $request = smf_db_query('SELECT value
					FROM {db_prefix}themes
					WHERE id_theme = {int:id_theme}
						AND variable = {string:variable}
					LIMIT 1', array('id_theme' => $cur_profile['id_theme'], 'variable' => 'name'));
        list($name) = mysql_fetch_row($request);
        mysql_free_result($request);
        $context['member']['theme'] = array('id' => $cur_profile['id_theme'], 'name' => empty($cur_profile['id_theme']) ? $txt['theme_forum_default'] : $name);
        return true;
    }, 'input_validate' => function (&$value) {
        $value = (int) $value;
        return true;
    }), 'karma_good' => array('type' => 'callback_template', 'callback_name' => 'profile/reputation_display', 'permission' => 'admin_forum', 'input_validate' => function (&$value) {
        global $profile_vars, $cur_profile;
        $value = (int) $value;
        if (isset($_POST['karma_bad'])) {
            $profile_vars['karma_bad'] = $_POST['karma_bad'] != '' ? (int) $_POST['karma_bad'] : 0;
            $cur_profile['karma_bad'] = $_POST['karma_bad'] != '' ? (int) $_POST['karma_bad'] : 0;
        }
        return true;
    }, 'preload' => function () {
        global $context, $cur_profile;
        //$context['member']['karma']['good'] = $cur_profile['karma_good'];
        //$context['member']['karma']['bad'] = $cur_profile['karma_bad'];
        return true;
    }, 'enabled' => !empty($modSettings['karmaMode'])), 'lngfile' => array('type' => 'select', 'options' => 'return $context[\'profile_languages\'];', 'label' => $txt['preferred_language'], 'permission' => 'profile_identity', 'preload' => 'profileLoadLanguages', 'enabled' => !empty($modSettings['userLanguage']), 'value' => empty($cur_profile['lngfile']) ? $language : $cur_profile['lngfile'], 'input_validate' => function (&$value) {
        global $context, $cur_profile;
        // Load the languages.
        profileLoadLanguages();
        if (isset($context['profile_languages'][$value])) {
            if ($context['user']['is_owner']) {
                $_SESSION['language'] = $value;
            }
            return true;
        } else {
            $value = $cur_profile['lngfile'];
            return false;
        }
    }), 'location' => array('type' => 'text', 'label' => $txt['location'], 'log_change' => true, 'size' => 50, 'permission' => 'profile_extra'), 'member_name' => array('type' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? 'text' : 'label', 'label' => $txt['username'], 'subtext' => allowedTo('admin_forum') && !isset($_GET['changeusername']) ? '(<a href="' . $scripturl . '?action=profile;u=' . $context['id_member'] . ';area=account;changeusername" style="font-style: italic;">' . $txt['username_change'] . '</a>)' : '', 'log_change' => true, 'permission' => 'profile_identity', 'prehtml' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? '<div class="alert">' . $txt['username_warning'] . '</div>' : '', 'input_validate' => function (&$value) {
        global $sourcedir, $context, $user_info, $cur_profile;
        if (allowedTo('admin_forum')) {
            // We\'ll need this...
            require_once $sourcedir . '/lib/Subs-Auth.php';
            // Maybe they are trying to change their password as well?
            $resetPassword = true;
            if (isset($_POST['passwrd1']) && $_POST['passwrd1'] != '' && isset($_POST['passwrd2']) && $_POST['passwrd1'] == $_POST['passwrd2'] && validatePassword($_POST['passwrd1'], $value, array($cur_profile['real_name'], $user_info['username'], $user_info['name'], $user_info['email'])) == null) {
                $resetPassword = false;
            }
            // Do the reset... this will send them an email too.
            if ($resetPassword) {
                resetPassword($context['id_member'], $value);
            } elseif ($value !== null) {
                validateUsername($context['id_member'], $value);
                updateMemberData($context['id_member'], array('member_name' => $value));
            }
        }
        return false;
    }), 'passwrd1' => array('type' => 'password', 'label' => $txt['choose_pass'], 'subtext' => $txt['password_strength'], 'size' => 20, 'value' => '', 'enabled' => empty($cur_profile['openid_uri']), 'permission' => 'profile_identity', 'save_key' => 'passwd', 'input_validate' => function (&$value) {
        global $sourcedir, $user_info, $smcFunc, $cur_profile;
        // If we didn\'t try it then ignore it!
        if ($value == '') {
            return false;
        }
        // Do the two entries for the password even match?
        if (!isset($_POST['passwrd2']) || $value != $_POST['passwrd2']) {
            return 'bad_new_password';
        }
        // Let\'s get the validation function into play...
        require_once $sourcedir . '/lib/Subs-Auth.php';
        $passwordErrors = validatePassword($value, $cur_profile['member_name'], array($cur_profile['real_name'], $user_info['username'], $user_info['name'], $user_info['email']));
        // Were there errors?
        if ($passwordErrors != null) {
            return 'password_' . $passwordErrors;
        }
        // Set up the new password variable... ready for storage.
        $value = sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($value));
        return true;
    }), 'passwrd2' => array('type' => 'password', 'label' => $txt['verify_pass'], 'enabled' => empty($cur_profile['openid_uri']), 'size' => 20, 'value' => '', 'permission' => 'profile_identity', 'is_dummy' => true), 'personal_text' => array('type' => 'text', 'label' => $txt['personal_text'], 'log_change' => true, 'input_attr' => array('maxlength="50"'), 'size' => 50, 'permission' => 'profile_extra'), 'pm_prefs' => array('type' => 'callback_template', 'callback_name' => 'pm/settings', 'permission' => 'pm_read', 'preload' => function () {
        global $context, $cur_profile;
        $context['display_mode'] = $cur_profile['pm_prefs'] & 3;
        $context['send_email'] = $cur_profile['pm_email_notify'];
        $context['receive_from'] = !empty($cur_profile['pm_receive_from']) ? $cur_profile['pm_receive_from'] : 0;
        return true;
    }, 'input_validate' => function (&$value) {
        global $cur_profile, $profile_vars;
        // Simple validate and apply the two "sub settings"
        $value = max(min($value, 2), 0);
        $cur_profile['pm_email_notify'] = $profile_vars['pm_email_notify'] = max(min((int) $_POST['pm_email_notify'], 2), 0);
        $cur_profile['pm_receive_from'] = $profile_vars['pm_receive_from'] = max(min((int) $_POST['pm_receive_from'], 4), 0);
        return true;
    }), 'posts' => array('type' => 'int', 'label' => $txt['profile_posts'], 'log_change' => true, 'size' => 7, 'permission' => 'moderate_forum', 'input_validate' => function (&$value) {
        $value = $value != '' ? strtr($value, array(',' => '', '.' => '', ' ' => '')) : 0;
        return true;
    }), 'real_name' => array('type' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum') ? 'text' : 'label', 'label' => $txt['name'], 'subtext' => $txt['display_name_desc'], 'log_change' => true, 'input_attr' => array('maxlength="60"'), 'permission' => 'profile_identity', 'enabled' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'), 'input_validate' => function (&$value) {
        global $context, $smcFunc, $sourcedir, $cur_profile;
        $value = trim(preg_replace('~[\\s]~' . ($context['utf8'] ? 'u' : ''), ' ', $value));
        if (trim($value) == '') {
            return 'no_name';
        } elseif (CommonAPI::strlen($value) > 60) {
            return 'name_too_long';
        } elseif ($cur_profile['real_name'] != $value) {
            require_once $sourcedir . '/lib/Subs-Members.php';
            if (isReservedName($value, $context['id_member'])) {
                return 'name_taken';
            }
        }
        return true;
    }), 'secret_question' => array('type' => 'text', 'label' => $txt['secret_question'], 'subtext' => $txt['secret_desc'], 'size' => 50, 'permission' => 'profile_identity'), 'secret_answer' => array('type' => 'text', 'label' => $txt['secret_answer'], 'subtext' => $txt['secret_desc2'], 'size' => 20, 'postinput' => '<span class="smalltext" style="margin-left: 4ex;"><a href="' . $scripturl . '?action=helpadmin;help=secret_why_blank" onclick="return reqWin(this.href);">' . $txt['secret_why_blank'] . '</a></span>', 'value' => '', 'permission' => 'profile_identity', 'input_validate' => function (&$value) {
        $value = $value != '' ? md5($value) : '';
        return true;
    }), 'signature' => array('type' => 'callback_template', 'callback_name' => allowedTo('profile_signature') ? 'profile/signature_modify' : 'profile/signature_cannot_modify', 'permission' => 'profile_extra', 'enabled' => substr($modSettings['signature_settings'], 0, 1) == 1, 'preload' => 'profileLoadSignatureData', 'input_validate' => 'profileValidateSignature'), 'show_online' => array('type' => 'check', 'label' => $txt['show_online'], 'permission' => 'profile_identity', 'enabled' => !empty($modSettings['allow_hideOnline']) || allowedTo('moderate_forum')), 'smiley_set' => array('type' => 'callback_template', 'callback_name' => 'profile/smiley_pick', 'enabled' => !empty($modSettings['smiley_sets_enable']), 'permission' => 'profile_extra', 'preload' => function () {
        global $modSettings, $context, $txt, $cur_profile;
        $context['member']['smiley_set']['id'] = empty($cur_profile['smiley_set']) ? '' : $cur_profile['smiley_set'];
        $context['smiley_sets'] = explode(',', 'none,,' . $modSettings['smiley_sets_known']);
        $set_names = explode("\n", $txt['smileys_none'] . "\n" . $txt['smileys_forum_board_default'] . "\n" . $modSettings['smiley_sets_names']);
        foreach ($context['smiley_sets'] as $i => $set) {
            $context['smiley_sets'][$i] = array('id' => htmlspecialchars($set), 'name' => htmlspecialchars($set_names[$i]), 'selected' => $set == $context['member']['smiley_set']['id']);
            if ($context['smiley_sets'][$i]['selected']) {
                $context['member']['smiley_set']['name'] = $set_names[$i];
            }
        }
        return true;
    }, 'input_validate' => function (&$value) {
        global $modSettings;
        $smiley_sets = explode(',', $modSettings['smiley_sets_known']);
        if (!in_array($value, $smiley_sets) && $value != 'none') {
            $value = '';
        }
        return true;
    }), 'theme_settings' => array('type' => 'callback_template', 'callback_name' => 'profile/theme_settings', 'permission' => 'profile_extra', 'is_dummy' => true, 'preload' => function () {
        loadLanguage('Settings');
        return true;
    }), 'time_format' => array('type' => 'callback_template', 'callback_name' => 'profile/timeformat_modify', 'permission' => 'profile_extra', 'preload' => function () {
        global $context, $user_info, $txt, $cur_profile, $modSettings;
        $context['easy_timeformats'] = array(array('format' => '', 'title' => $txt['timeformat_default']), array('format' => '%B %d, %Y, %I:%M:%S %p', 'title' => $txt['timeformat_easy1']), array('format' => '%B %d, %Y, %H:%M:%S', 'title' => $txt['timeformat_easy2']), array('format' => '%Y-%m-%d, %H:%M:%S', 'title' => $txt['timeformat_easy3']), array('format' => '%d %B %Y, %H:%M:%S', 'title' => $txt['timeformat_easy4']), array('format' => '%d-%m-%Y, %H:%M:%S', 'title' => $txt['timeformat_easy5']));
        $context['member']['time_format'] = $cur_profile['time_format'];
        $context['current_forum_time'] = strftime($modSettings['time_format'], forum_time(false)) . ' ' . date_default_timezone_get();
        $context['current_forum_time_js'] = strftime('%Y,' . ((int) strftime('%m', time() + $modSettings['time_offset'] * 3600) - 1) . ',%d,%H,%M,%S', time() + $modSettings['time_offset'] * 3600);
        $context['current_forum_time_hour'] = (int) strftime('%H', forum_time(false));
        return true;
    }), 'time_offset' => array('type' => 'callback_template', 'callback_name' => 'profile/timeoffset_modify', 'permission' => 'profile_extra', 'preload' => function () {
        global $context, $cur_profile;
        $context['member']['time_offset'] = $cur_profile['time_offset'];
        return true;
    }, 'input_validate' => function (&$value) {
        // Validate the time_offset...
        $value = (double) strtr($value, ',', '.');
        if ($value < -23.5 || $value > 23.5) {
            return 'bad_offset';
        }
        return true;
    }), 'usertitle' => array('type' => 'text', 'label' => $txt['custom_title'], 'log_change' => true, 'size' => 50, 'permission' => 'profile_title', 'input_attr' => array('maxlength="50"'), 'enabled' => !empty($modSettings['titlesEnable'])));
    $disabled_fields = !empty($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
    // For each of the above let's take out the bits which don't apply - to save memory and security!
    foreach ($profile_fields as $key => $field) {
        // Do we have permission to do this?
        if (isset($field['permission']) && !allowedTo($context['user']['is_owner'] ? array($field['permission'] . '_own', $field['permission'] . '_any') : $field['permission'] . '_any') && !allowedTo($field['permission'])) {
            unset($profile_fields[$key]);
        }
        // Is it enabled?
        if (isset($field['enabled']) && !$field['enabled']) {
            unset($profile_fields[$key]);
        }
        // Is it specifically disabled?
        if (in_array($key, $disabled_fields) || isset($field['link_with']) && in_array($field['link_with'], $disabled_fields)) {
            unset($profile_fields[$key]);
        }
    }
}