query_select('f_custom_fields', array('id', 'cf_type', 'cf_name'), null, 'ORDER BY cf_order,' . $GLOBALS['FORUM_DB']->translate_field_ref('cf_name'))); // Headings $headings = member_get_csv_headings(); foreach ($cpfs as $i => $c) { // CPFs take precedence over normal fields of the same name $cpfs[$i]['_cf_name'] = get_translated_text($c['cf_name'], $GLOBALS['FORUM_DB']); $headings[$cpfs[$i]['_cf_name']] = strval($i); // We specially recognise numeric names as a map back to a CPF ID } // Subscription types $subscription_types = array(); if (addon_installed('ecommerce')) { require_lang('ecommerce'); $usergroup_subscription_rows = $GLOBALS['FORUM_DB']->query_select('f_usergroup_subs', array('id', 's_title')); foreach ($usergroup_subscription_rows as $usergroup_subscription_row) { $item_name = get_translated_text($usergroup_subscription_row['s_title'], $GLOBALS['FORUM_DB']); $heading_lang_strings = array( 'SUBSCRIPTION_START_TIME', 'SUBSCRIPTION_TERM_START_TIME', 'SUBSCRIPTION_TERM_END_TIME', 'SUBSCRIPTION_EXPIRY_TIME', 'PAYMENT_GATEWAY', 'STATUS', ); foreach ($heading_lang_strings as $heading_lang_string) { $headings[$item_name . ' (' . do_lang($heading_lang_string) . ')'] = ':' . str_replace('/', '\\', $item_name . ' (' . do_lang($heading_lang_string) . ')'); // Forward slashes are assumed as delimiters } $subscription_types['USERGROUP' . strval($usergroup_subscription_row['id'])] = $item_name; } } return array($headings, $cpfs, $subscription_types); } /** * Get field mapping data for CSV import/export. * * @return array A map of heading information (human name to field name/encoding details) */ function member_get_csv_headings() { $headings = array( 'ID' => 'id', 'Username' => 'm_username', 'E-mail address' => 'm_email_address', 'Password' => 'm_pass_hash_salted/m_pass_salt/m_password_compat_scheme' ); if (addon_installed('cns_member_avatars')) { $headings += array( 'Avatar' => '#m_avatar_url', ); } if (addon_installed('cns_member_photos')) { $headings += array( 'Photo' => '#m_photo_url', ); } $headings += array( 'Signature' => '*m_signature', 'Validated' => '!m_validated', 'Join time' => '&m_join_time', 'Last visit' => '&m_last_visit_time', 'Number of posts' => 'm_cache_num_posts', 'Usergroup' => '@m_primary_group', 'Banned' => '!m_is_perm_banned', 'Date of birth' => 'm_dob_year/m_dob_month/m_dob_day', 'Reveal age' => '!m_reveal_age', 'Language' => 'm_language', 'Accept member e-mails' => '!m_allow_emails', 'Opt-in' => '!m_allow_emails_from_staff', 'Auto mark read' => 'm_auto_mark_read', ); return $headings; } /** * Get a list of timezones. * * @param ?string $timezone Current timezone to select (null: server default) * @return Tempcode List of timezones */ function create_selection_list_timezone_list($timezone = null) { if (is_null($timezone)) { $timezone = get_site_timezone(); } $timezone_list = '';//new Tempcode(); $time_now = time(); foreach (get_timezone_list() as $_timezone => $timezone_nice) { $timezone_list .= ''; // XHTMLXHTML //$timezone_list->attach(do_template('CNS_AUTO_TIME_ZONE_ENTRY', array('_GUID' => '2aed8a9fcccb52e5d52b5a307a906b3a', 'HOUR' => date('H', tz_time($time_now, $_timezone)), 'DW' => date('w', tz_time(time(), $_timezone)), 'NAME' => $_timezone, 'SELECTED' => ($timezone == $_timezone), 'CLASS' => '', 'TEXT' => $timezone_nice))); } return make_string_tempcode($timezone_list); } /** * Validate an IP address, indirectly by passing through a confirmation code. */ function approve_ip_script() { require_code('site'); attach_to_screen_header(''); // XHTMLXHTML $keep = keep_symbol(array('1')); $code = either_param_string('code', ''); if ($code == '') { $title = get_screen_title('CONFIRM'); require_code('form_templates'); $fields = new Tempcode(); $fields->attach(form_input_codename(do_lang_tempcode('CODE'), '', 'code', '', true)); $submit_name = do_lang_tempcode('PROCEED'); $url = find_script('approve_ip') . $keep; $middle = do_template('FORM_SCREEN', array('_GUID' => 'd92ce4ec82dc709f920a4ce6760778de', 'TITLE' => $title, 'SKIP_WEBSTANDARDS' => true, 'HIDDEN' => '', 'URL' => $url, 'FIELDS' => $fields, 'TEXT' => do_lang_tempcode('MISSING_CONFIRM_CODE'), 'SUBMIT_ICON' => 'buttons__proceed', 'SUBMIT_NAME' => $submit_name)); $echo = globalise($middle, null, '', true, true); $echo->evaluate_echo(); exit(); } // If we're still here, we're ok to go require_lang('cns'); $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_member_known_login_ips', 'i_val_code', array('i_val_code' => $code)); if (is_null($test)) { warn_exit(do_lang_tempcode('ALREADY_VALIDATED')); } $GLOBALS['FORUM_DB']->query_update('f_member_known_login_ips', array('i_val_code' => ''), array('i_val_code' => $code), '', 1); $title = get_screen_title('CONFIRM'); $middle = redirect_screen($title, get_base_url() . $keep, do_lang_tempcode('SUCCESS')); $echo = globalise($middle, null, '', true, true); $echo->evaluate_echo(); exit(); } /** * If we are using human names for usernames, a conflict is likely. Store a suffixed variety. Maybe later Composr will strip these suffixes out in some contexts. * * @param SHORT_TEXT $username The desired human name for the member profile. * @return SHORT_TEXT A unique username. */ function get_username_from_human_name($username) { $username = preg_replace('# \(\d+\)$#', '', $username); $_username = $username; $i = 1; do { $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_members', 'id', array('m_username' => $_username)); if (!is_null($test)) { $i++; $_username = $username . ' (' . strval($i) . ')'; } } while (!is_null($test)); $username = $_username; return $username; } /** * Get a form for finishing off a member profile (such as for LDAP or httpauth, where a partial profile is automatically made, but needs completion). * * @param SHORT_TEXT $username The username for the member profile. * @param ID_TEXT $type The type of member profile we are finishing off. * @param string $email_address Auto-detected e-mail address (blank: none) * @param ?integer $dob_day Auto-detected DOB day (null: unknown) * @param ?integer $dob_month Auto-detected DOB month (null: unknown) * @param ?integer $dob_year Auto-detected DOB year (null: unknown) * @param ?ID_TEXT $timezone Auto-detected Timezone (null: unknown) * @param ?ID_TEXT $language Auto-detected Language (null: unknown) * @return Tempcode The form. */ function cns_member_external_linker_ask($username, $type, $email_address = '', $dob_day = null, $dob_month = null, $dob_year = null, $timezone = null, $language = null) { require_lang('cns'); // If somehow, we're not fully started up, or in a messy state require_code('urls'); cms_ob_end_clean(); // Emergency output, potentially, so kill off any active buffer $title = get_screen_title('FINISH_PROFILE'); if ($username != '') { $username = get_username_from_human_name($username); } list($fields, $hidden) = cns_get_member_fields(true, null, null, $email_address, 1, $dob_day, $dob_month, $dob_year, $timezone, null, null, 1, 0, null, $language, 1, 1, 1, null, $username, 0, $type); $hidden->attach(build_keep_post_fields()); $hidden->attach(form_input_hidden('finishing_profile', '1')); $text = do_lang_tempcode('ENTER_PROFILE_DETAILS_FINISH'); $submit_name = do_lang_tempcode('PROCEED'); $url = get_self_url(); return do_template('FORM_SCREEN', array('_GUID' => 'f3fa74f4842f3660f0831f8d708d256d', 'HIDDEN' => $hidden, 'TITLE' => $title, 'FIELDS' => $fields, 'TEXT' => $text, 'SUBMIT_ICON' => 'menu__site_meta__user_actions__join', 'SUBMIT_NAME' => $submit_name, 'URL' => $url)); } /** * Finishing off of a member profile (such as for LDAP or httpauth, where a partial profile is automatically made, but needs completion). * * @param SHORT_TEXT $username The username for the member profile. * @param SHORT_TEXT $password The password for the member profile. * @param ID_TEXT $type The type of member profile we are finishing off. * @param boolean $email_check Whether to check for duplicated email addresses. * @param string $email_address Auto-detected e-mail address (blank: none) * @param ?integer $dob_day Auto-detected DOB day (null: unknown) * @param ?integer $dob_month Auto-detected DOB month (null: unknown) * @param ?integer $dob_year Auto-detected DOB year (null: unknown) * @param ?ID_TEXT $timezone Auto-detected Timezone (null: unknown) * @param ?ID_TEXT $language Auto-detected Language (null: unknown) * @param ?URLPATH $avatar_url The URL to the member's avatar (blank: none) (null: choose one automatically). * @param URLPATH $photo_url The URL to the member's photo (blank: none). * @param URLPATH $photo_thumb_url The URL to the member's photo thumbnail (blank: none). * @return MEMBER The member ID for the finished off profile. */ function cns_member_external_linker($username, $password, $type, $email_check = true, $email_address = '', $dob_day = null, $dob_month = null, $dob_year = null, $timezone = null, $language = null, $avatar_url = null, $photo_url = '', $photo_thumb_url = '') { // Read in data $email_address = trim(post_param_string('email_address', $email_address)); require_code('temporal2'); list($dob_year, $dob_month, $dob_day) = post_param_date_components('dob', $dob_year, $dob_month, $dob_day); $reveal_age = post_param_integer('reveal_age', 0); // For default privacy, default off require_code('temporal'); if (is_null($timezone)) { $timezone = get_site_timezone(); } $timezone = post_param_string('timezone', $timezone); if (is_null($language)) { $language = get_site_default_lang(); } $language = post_param_string('language', $language); $allow_emails = post_param_integer('allow_emails', 0); // For default privacy, default off $allow_emails_from_staff = post_param_integer('allow_emails_from_staff', 0); // For default privacy, default off require_code('cns_groups'); $custom_fields = cns_get_all_custom_fields_match( cns_get_all_default_groups(true), // groups null, // public view null, // owner view null, // owner set null, // required null, // show in posts null, // show in post previews null, // special start true // show on join form ); $actual_custom_fields = cns_read_in_custom_fields($custom_fields); foreach ($actual_custom_fields as $key => $val) { if ($val == STRING_MAGIC_NULL) { $actual_custom_fields[$key] = ''; } } $groups = cns_get_all_default_groups(true); // $groups will contain the built in default primary group too (it is not $secondary_groups) $primary_group = post_param_integer('primary_group', null); if (($primary_group !== null) && (!in_array($primary_group, $groups)/*= not built in default, which is automatically ok to join without extra security*/)) { // Check security $test = $GLOBALS['FORUM_DB']->query_select_value('f_groups', 'g_is_presented_at_install', array('id' => $primary_group)); if ($test == 1) { $groups = cns_get_all_default_groups(false); // Get it so it does not include the built in default primary group $groups[] = $primary_group; // And add in the *chosen* primary group } else { $primary_group = null; } } else { $primary_group = null; } if ($primary_group === null) { // Security error, or built in default (which will already be in $groups) $primary_group = get_first_default_group(); } // Check that the given address isn't already used (if one_per_email_address on) if ((get_option('one_per_email_address') != '0') && ($email_address != '') && ($email_check)) { $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_members', 'm_username', array('m_email_address' => $email_address)); if (!is_null($test)) { global $MEMBER_CACHED; $MEMBER_CACHED = db_get_first_id(); $reset_url = build_url(array('page' => 'lost_password', 'email_address' => $email_address), get_module_zone('lost_password')); warn_exit(do_lang_tempcode('EMAIL_ADDRESS_IN_USE', escape_html(get_site_name()), escape_html($reset_url->evaluate()))); } } $require_new_member_validation = get_option('require_new_member_validation') == '1'; $validated = $require_new_member_validation ? 0 : 1; if ($require_new_member_validation) { require_code('site'); attach_message(do_lang_tempcode('AWAITING_MEMBER_VALIDATION'), 'notice'); } // Add member require_code('cns_members_action'); $ret = cns_make_member($username, $password, $email_address, $groups, $dob_day, $dob_month, $dob_year, $actual_custom_fields, $timezone, null, $validated, time(), time(), '', $avatar_url, '', 0, 1, $reveal_age, '', $photo_url, $photo_thumb_url, 1, 1, $language, $allow_emails, $allow_emails_from_staff, get_ip_address(), '', false, $type, ''); return $ret; } /** * Read in the custom profile field POST data. * * @param array $custom_fields The CPF field rows that we'll be reading in the member's values for. * @param ?MEMBER $member_id Member involved (null: new member) * @return array The CPF data. */ function cns_read_in_custom_fields($custom_fields, $member_id = null) { require_code('fields'); require_code('cns_members_action'); $actual_custom_fields = array(); foreach ($custom_fields as $custom_field) { $ob = get_fields_hook($custom_field['cf_type']); $old_value = is_null($member_id) ? null : $GLOBALS['FORUM_DB']->query_select_value_if_there('f_member_custom_fields', 'field_' . strval($custom_field['id']), array('mf_member_id' => $member_id)); // Field not required if not yet filled in but member already registered, if PRIVILEGE ON for that. Prevents annoyance for new required CPFs added later. if (!member_field_is_required($member_id, 'required_cpfs', $old_value)) { $custom_field['cf_required'] = 0; } $value = $ob->inputted_to_field_value($member_id !== null, $custom_field, 'uploads/cns_cpf_upload', ($old_value === null) ? null : array('cv_value' => $old_value)); // Required field validation (a standard for all field hooks) if (($custom_field['cf_required'] == 1) && (($value == '') || (($value == STRING_MAGIC_NULL) && !fractional_edit()))) { warn_exit(do_lang_tempcode('_REQUIRED_NOT_FILLED_IN', $custom_field['cf_name'])); } if ((fractional_edit()) && ($value != STRING_MAGIC_NULL)) { $rendered = $ob->render_field_value($custom_field, $value, 0, null, 'f_member_custom_fields', $member_id, 'ce_id', 'cf_id', 'field_' . strval($custom_field['id']), $member_id); $_POST['field_' . strval($custom_field['id']) . '__altered_rendered_output'] = is_object($rendered) ? $rendered->evaluate() : $rendered; } $actual_custom_fields[$custom_field['id']] = $value; } return $actual_custom_fields; } /** * Get form fields for adding/editing/finishing a member profile. * * @param boolean $mini_mode Whether we are only handling the essential details of a profile. * @param ?MEMBER $member_id The ID of the member we are handling (null: new member). * @param ?array $groups A list of usergroups (null: default/current usergroups). * @param SHORT_TEXT $email_address The e-mail address. * @param BINARY $preview_posts Whether posts are previewed before they are made. * @param ?integer $dob_day Day of date of birth (null: not known). * @param ?integer $dob_month Month of date of birth (null: not known). * @param ?integer $dob_year Year of date of birth (null: not known). * @param ?ID_TEXT $timezone The member timezone (null: site default). * @param ?array $custom_fields A map of custom fields values (field-id=>value) (null: not known). * @param ?ID_TEXT $theme The members default theme (null: not known). * @param BINARY $reveal_age Whether the members age may be shown. * @param BINARY $views_signatures Whether the member sees signatures in posts. * @param ?BINARY $auto_monitor_contrib_content Whether the member automatically is enabled for notifications for content they contribute to (null: get default from config). * @param ?LANGUAGE_NAME $language The members language (null: auto detect). * @param BINARY $allow_emails Whether the member allows e-mails via the site. * @param BINARY $allow_emails_from_staff Whether the member allows e-mails from staff via the site. * @param BINARY $validated Whether the profile has been validated. * @param ?GROUP $primary_group The members primary (null: not known). * @param SHORT_TEXT $username The username. * @param BINARY $is_perm_banned Whether the member is permanently banned. * @param ID_TEXT $special_type The special type of profile this is (blank: not a special type). * @param BINARY $highlighted_name Whether the member username will be highlighted. * @param SHORT_TEXT $pt_allow Usergroups that may PT the member. * @param LONG_TEXT $pt_rules_text Rules that other members must agree to before they may start a PT with the member. * @param ?TIME $on_probation_until When the member is on probation until (null: just finished probation / or effectively was never on it) * @return array A pair: The form fields, Hidden fields (both Tempcode). */ function cns_get_member_fields($mini_mode = true, $member_id = null, $groups = null, $email_address = '', $preview_posts = 0, $dob_day = null, $dob_month = null, $dob_year = null, $timezone = null, $custom_fields = null, $theme = null, $reveal_age = 1, $views_signatures = 1, $auto_monitor_contrib_content = null, $language = null, $allow_emails = 1, $allow_emails_from_staff = 1, $validated = 1, $primary_group = null, $username = '', $is_perm_banned = 0, $special_type = '', $highlighted_name = 0, $pt_allow = '*', $pt_rules_text = '', $on_probation_until = null) { $fields = new Tempcode(); $hidden = new Tempcode(); list($_fields, $_hidden) = cns_get_member_fields_settings($mini_mode, $member_id, $groups, $email_address, $preview_posts, $dob_day, $dob_month, $dob_year, $timezone, $theme, $reveal_age, $views_signatures, $auto_monitor_contrib_content, $language, $allow_emails, $allow_emails_from_staff, $validated, $primary_group, $username, $is_perm_banned, $special_type, $highlighted_name, $pt_allow, $pt_rules_text, $on_probation_until); $fields->attach($_fields); $hidden->attach($_hidden); if (!$mini_mode) { $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array( '_GUID' => '14205f6bf83c469a1404d24967d7b6f6', 'TITLE' => do_lang_tempcode('PROFILE'), 'SECTION_HIDDEN' => (get_page_name() == 'admin_cns_members'), ))); } list($_fields, $_hidden) = cns_get_member_fields_profile($mini_mode, $member_id, $groups, $custom_fields); $fields->attach($_fields); $hidden->attach($_hidden); return array($fields, $hidden); } /** * Get form fields for adding/editing/finishing a member profile. * * @param boolean $mini_mode Whether we are only handling the essential details of a profile. * @param ?MEMBER $member_id The ID of the member we are handling (null: new member). * @param ?array $groups A list of usergroups (null: default/current usergroups). * @param SHORT_TEXT $email_address The e-mail address. * @param ?BINARY $preview_posts Whether posts are previewed before they are made (null: calculate statistically). * @param ?integer $dob_day Day of date of birth (null: not known). * @param ?integer $dob_month Month of date of birth (null: not known). * @param ?integer $dob_year Year of date of birth (null: not known). * @param ?ID_TEXT $timezone The member timezone (null: site default). * @param ?ID_TEXT $theme The members default theme (null: not known). * @param BINARY $reveal_age Whether the members age may be shown. * @param BINARY $views_signatures Whether the member sees signatures in posts. * @param ?BINARY $auto_monitor_contrib_content Whether the member automatically is enabled for notifications for content they contribute to (null: get default from config). * @param ?LANGUAGE_NAME $language The members language (null: auto detect). * @param BINARY $allow_emails Whether the member allows e-mails via the site. * @param BINARY $allow_emails_from_staff Whether the member allows e-mails from staff via the site. * @param BINARY $validated Whether the profile has been validated. * @param ?GROUP $primary_group The members primary (null: not known). * @param SHORT_TEXT $username The username. * @param BINARY $is_perm_banned Whether the member is permanently banned. * @param ID_TEXT $special_type The special type of profile this is (blank: not a special type). * @param BINARY $highlighted_name Whether the member username will be highlighted. * @param SHORT_TEXT $pt_allow Usergroups that may PT the member. * @param LONG_TEXT $pt_rules_text Rules that other members must agree to before they may start a PT with the member. * @param ?TIME $on_probation_until When the member is on probation until (null: just finished probation / or effectively was never on it) * @return array A pair: The form fields, Hidden fields (both Tempcode). */ function cns_get_member_fields_settings($mini_mode = true, $member_id = null, $groups = null, $email_address = '', $preview_posts = null, $dob_day = null, $dob_month = null, $dob_year = null, $timezone = null, $theme = null, $reveal_age = 1, $views_signatures = 1, $auto_monitor_contrib_content = null, $language = null, $allow_emails = 1, $allow_emails_from_staff = 1, $validated = 1, $primary_group = null, $username = '', $is_perm_banned = 0, $special_type = '', $highlighted_name = 0, $pt_allow = '*', $pt_rules_text = '', $on_probation_until = null) { require_code('form_templates'); require_code('cns_members_action'); $preview_posts = take_param_int_modeavg($preview_posts, 'm_preview_posts', 'f_members', 0); require_code('cns_field_editability'); if (is_null($auto_monitor_contrib_content)) { $auto_monitor_contrib_content = (get_option('allow_auto_notifications') == '0') ? 0 : 1; } $hidden = new Tempcode(); if ($member_id === $GLOBALS['CNS_DRIVER']->get_guest_id()) { fatal_exit(do_lang_tempcode('INTERNAL_ERROR')); } require_code('form_templates'); require_code('encryption'); if (($special_type == '') && ($member_id !== null)) { $special_type = get_member_special_type($member_id); } if (is_null($groups)) { $groups = is_null($member_id) ? cns_get_all_default_groups(true) : $GLOBALS['CNS_DRIVER']->get_members_groups($member_id); } $fields = new Tempcode(); // Username if (cns_field_editable('username', $special_type)) { if ((is_null($member_id)) || (has_actual_page_access(get_member(), 'admin_cns_members')) || (has_privilege($member_id, 'rename_self'))) { $prohibit_username_whitespace = get_option('prohibit_username_whitespace'); if ($prohibit_username_whitespace == '1') { $pattern = '[^\s]*'; $pattern_error = do_lang('USERNAME_PASSWORD_WHITESPACE'); } else { $pattern = null; $pattern_error = null; } $fields->attach(form_input_line(do_lang_tempcode('USERNAME'), do_lang_tempcode('DESCRIPTION_USERNAME'), ($member_id === null) ? 'username' : 'edit_username', $username, true, null, null, 'text', null, $pattern, $pattern_error)); } } // Password if (cns_field_editable('password', $special_type)) { if ((is_null($member_id)) || ($member_id == get_member()) || (has_privilege(get_member(), 'assume_any_member'))) { $temporary_password = (!is_null($member_id)) && ($member_id == get_member() && ($GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_password_compat_scheme') == 'temporary')); if ($temporary_password) { $password_field_description = do_lang_tempcode('DESCRIPTION_PASSWORD_TEMPORARY'); } else { $password_field_description = do_lang_tempcode('DESCRIPTION_PASSWORD' . (!is_null($member_id) ? '_EDIT' : '')); } $fields->attach(form_input_password(do_lang_tempcode(is_null($member_id) ? 'PASSWORD' : 'NEW_PASSWORD'), $password_field_description, is_null($member_id) ? 'password' : 'edit_password', $mini_mode || $temporary_password)); $fields->attach(form_input_password(do_lang_tempcode('CONFIRM_PASSWORD'), '', 'password_confirm', $mini_mode || $temporary_password)); } } // Work out what options we need to present $doing_international = (get_option('allow_international') == '1'); $_langs = find_all_langs(); $doing_langs = multi_lang(); $doing_email_option = (get_option('allow_email_disable') == '1') && (addon_installed('cns_contact_member')); $doing_email_from_staff_option = (get_option('allow_email_from_staff_disable') == '1'); $unspecced_theme_zone_exists = $GLOBALS['SITE_DB']->query_value_if_there('SELECT COUNT(*) FROM ' . get_table_prefix() . 'zones WHERE ' . db_string_equal_to('zone_theme', '') . ' OR ' . db_string_equal_to('zone_theme', '-1')); $doing_theme_option = ($unspecced_theme_zone_exists != 0) && (!$mini_mode); $doing_local_forum_options = (addon_installed('cns_forum')) && (!$mini_mode); // E-mail address if (cns_field_editable('email', $special_type)) { if ($email_address == '') { $email_address = trim(get_param_string('email_address', '')); } $email_description = new Tempcode(); if ((get_option('valid_email_domains') != '') && ($mini_mode)) { // domain restriction only applies on public join form ($mini_mode) $email_description = do_lang_tempcode('MUST_BE_EMAIL_DOMAIN', '*.' . preg_replace('#\s*,\s*#', ', *.', escape_html(get_option('valid_email_domains'))) . '', escape_html(get_option('valid_email_domains'))); } else { if (get_option('email_confirm_join') == '1') { $email_description = do_lang_tempcode('MUST_BE_REAL_ADDRESS'); } } $email_address_required = member_field_is_required($member_id, 'email_address'); $fields->attach(form_input_email(do_lang_tempcode('EMAIL_ADDRESS'), $email_description, 'email_address', $email_address, $email_address_required)); if ((is_null($member_id)) && ($email_address == '') && (get_option('email_confirm_join') == '1')) { $fields->attach(form_input_email(do_lang_tempcode('CONFIRM_EMAIL_ADDRESS'), '', 'email_address_confirm', '', $email_address_required)); } } // E-mail privacy if ($doing_email_option) { $field_title = do_lang_tempcode('ALLOW_EMAILS'); if (cns_field_editable('email', $special_type)) { $field_title = do_lang_tempcode('RELATED_FIELD', $field_title); } $fields->attach(form_input_tick($field_title, do_lang_tempcode('DESCRIPTION_ALLOW_EMAILS'), 'allow_emails', $allow_emails == 1)); } if ($doing_email_from_staff_option) { $field_title = do_lang_tempcode('ALLOW_EMAILS_FROM_STAFF'); if (cns_field_editable('email', $special_type)) { $field_title = do_lang_tempcode('RELATED_FIELD', $field_title); } $fields->attach(form_input_tick($field_title, do_lang_tempcode('DESCRIPTION_ALLOW_EMAILS_FROM_STAFF'), 'allow_emails_from_staff', $allow_emails_from_staff == 1)); } // DOB if (cns_field_editable('dob', $special_type)) { $default_time = is_null($dob_month) ? null : usertime_to_utctime(mktime(0, 0, 0, $dob_month, $dob_day, $dob_year)); if (get_option('dobs') == '1') { $dob_required = member_field_is_required($member_id, 'dob'); $fields->attach(form_input_date(do_lang_tempcode($dob_required ? 'DATE_OF_BIRTH' : 'ENTER_YOUR_BIRTHDAY'), '', 'dob', $dob_required, false, false, $default_time, -130)); if (addon_installed('cns_forum')) { $fields->attach(form_input_tick(do_lang_tempcode('RELATED_FIELD', do_lang_tempcode('REVEAL_AGE')), do_lang_tempcode('DESCRIPTION_REVEAL_AGE'), 'reveal_age', $reveal_age == 1)); } } } /* if (!$mini_mode) { if (($doing_international) || ($doing_langs) || ($doing_email_option) || ($doing_wide_option) || ($doing_theme_option) || ($doing_local_forum_options)) { $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '3cd79bbea084ec1fe148edddad7d52b4', 'FORCE_OPEN' => is_null($member_id) ? true : null, 'TITLE' => do_lang_tempcode('SETTINGS')))); } } */ require_lang('config'); // Timezones, if enabled if ($doing_international) { $timezone_list = create_selection_list_timezone_list($timezone); $fields->attach(form_input_list(do_lang_tempcode('TIMEZONE'), do_lang_tempcode('DESCRIPTION_TIMEZONE_MEMBER'), 'timezone', $timezone_list)); } // Language choice, if we have multiple languages on site if ($doing_langs) { $lang_list = new Tempcode(); $no_lang_set = (is_null($language)) || ($language == ''); $allow_no_lang_set = (get_value('allow_no_lang_selection') === '1'); if ($allow_no_lang_set) { $lang_list->attach(form_input_list_entry('', $no_lang_set, do_lang_tempcode('UNSET'))); } else { if ($no_lang_set) { $language = user_lang(); } } $lang_list->attach(create_selection_list_langs($language)); $fields->attach(form_input_list(do_lang_tempcode('LANGUAGE'), '', 'language', $lang_list, null, false, !$allow_no_lang_set)); } if (!$mini_mode) { // Theme, if we have any zones giving a choice require_code('themes2'); $entries = create_selection_list_themes($theme, false, false, 'RELY_SITE_DEFAULT'); require_lang('themes'); if ($doing_theme_option) { $fields->attach(form_input_list(do_lang_tempcode('THEME'), do_lang_tempcode('DESCRIPTION_THEME'), 'theme', $entries)); } // Various forum options if (addon_installed('cns_forum')) { if (get_option('forced_preview_option') == '1') { $fields->attach(form_input_tick(do_lang_tempcode('PREVIEW_POSTS'), do_lang_tempcode('DESCRIPTION_PREVIEW_POSTS'), 'preview_posts', $preview_posts == 1)); } if (addon_installed('cns_signatures')) { if (get_option('enable_views_sigs_option', true) === '1') { $fields->attach(form_input_tick(do_lang_tempcode('VIEWS_SIGNATURES'), do_lang_tempcode('DESCRIPTION_VIEWS_SIGNATURES'), 'views_signatures', $views_signatures == 1)); } else { $hidden->attach(form_input_hidden('views_signatures', '1')); } } //$fields->attach(form_input_tick(do_lang_tempcode('AUTO_NOTIFICATION_CONTRIB_CONTENT'), do_lang_tempcode('DESCRIPTION_AUTO_NOTIFICATION_CONTRIB_CONTENT'), 'auto_monitor_contrib_content', $auto_monitor_contrib_content == 1)); Now on notifications tab, even though it is technically an account setting $usergroup_list = new Tempcode(); $lgroups = $GLOBALS['CNS_DRIVER']->get_usergroup_list(true, true, false, null, null, true); foreach ($lgroups as $key => $val) { if ($key != db_get_first_id()) { $usergroup_list->attach(form_input_list_entry(strval($key), ($pt_allow == '*') || count(array_intersect(array(strval($key)), explode(',', $pt_allow))) != 0, $val)); } } if (get_option('enable_pt_restrict') == '1') { $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '7e5deb351a7a5214fbff10049839e258', 'TITLE' => do_lang_tempcode('PRIVATE_TOPICS'), 'SECTION_HIDDEN' => ($pt_allow == '*') && ($pt_rules_text == '')))); $fields->attach(form_input_multi_list(do_lang_tempcode('PT_ALLOW'), addon_installed('chat') ? do_lang_tempcode('PT_ALLOW_DESCRIPTION_CHAT') : do_lang_tempcode('PT_ALLOW_DESCRIPTION'), 'pt_allow', $usergroup_list)); $fields->attach(form_input_text_comcode(do_lang_tempcode('PT_RULES_TEXT'), do_lang_tempcode('PT_RULES_TEXT_DESCRIPTION'), 'pt_rules_text', $pt_rules_text, false)); } if (get_option('is_on_automatic_mark_topic_read') == '0') { require_code('users'); $auto_mark_read = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_auto_mark_read'); $fields->attach(form_input_tick(do_lang_tempcode('ENABLE_AUTO_MARK_READ'), do_lang_tempcode('DESCRIPTION_ENABLE_AUTO_MARK_READ'), 'auto_mark_read', $auto_mark_read == 1)); } else { $hidden->attach(form_input_hidden('auto_mark_read', '1')); } } // Prepare list of usergroups, if maybe we are gonna let (a) usergroup-change field(s) $group_count = $GLOBALS['FORUM_DB']->query_select_value('f_groups', 'COUNT(*)'); $rows = $GLOBALS['FORUM_DB']->query_select('f_groups', array('id', 'g_name', 'g_hidden', 'g_open_membership'), ($group_count > 200) ? array('g_is_private_club' => 0) : null, 'ORDER BY g_order,' . $GLOBALS['FORUM_DB']->translate_field_ref('g_name')); $_groups = new Tempcode(); $default_primary_group = get_first_default_group(); $current_primary_group = null; foreach ($rows as $group) { if ($group['id'] != db_get_first_id()) { $selected = ($group['id'] == $primary_group) || (is_null($primary_group) && ($group['id'] == $default_primary_group)); if ($selected) { $current_primary_group = $group['id']; } $_groups->attach(form_input_list_entry(strval($group['id']), $selected, get_translated_text($group['g_name'], $GLOBALS['FORUM_DB']))); } } // Some admin options... if (has_privilege(get_member(), 'member_maintenance')) { $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '04422238c372edd0b11c11a05feb6267', 'TITLE' => do_lang_tempcode('MEMBER_ACCESS')))); // Probation if (has_privilege(get_member(), 'probate_members')) { if ((!is_null($member_id)) && ($member_id != get_member())) { // Can't put someone new on probation, and can't put yourself on probation $fields->attach(form_input_date(do_lang_tempcode('ON_PROBATION_UNTIL'), do_lang_tempcode('DESCRIPTION_ON_PROBATION_UNTIL'), 'on_probation_until', false, is_null($on_probation_until) || $on_probation_until <= time(), true, $on_probation_until, 2)); } } // Primary usergroup if (cns_field_editable('primary_group', $special_type)) { if (has_privilege(get_member(), 'assume_any_member')) { if ((is_null($member_id)) || (!$GLOBALS['FORUM_DRIVER']->is_super_admin($member_id)) || (count($GLOBALS['FORUM_DRIVER']->member_group_query($GLOBALS['FORUM_DRIVER']->get_super_admin_groups(), 2)) > 1)) { $fields->attach(form_input_list(do_lang_tempcode('PRIMARY_GROUP'), do_lang_tempcode('DESCRIPTION_PRIMARY_GROUP'), 'primary_group', $_groups)); } } } } // Secondary usergroups if (cns_field_editable('secondary_groups', $special_type)) { $_groups2 = new Tempcode(); $members_groups = is_null($member_id) ? array() : cns_get_members_groups($member_id, false, false, false); foreach ($rows as $group) { if (($group['g_hidden'] == 1) && (!array_key_exists($group['id'], $members_groups)) && (!has_privilege(get_member(), 'see_hidden_groups'))) { continue; } if (($group['id'] != db_get_first_id()) && ((array_key_exists($group['id'], $members_groups)) || (has_privilege(get_member(), 'assume_any_member')) || ($group['g_open_membership'] == 1))) { $selected = array_key_exists($group['id'], $members_groups) && ($group['id'] != $current_primary_group); $_groups2->attach(form_input_list_entry(strval($group['id']), $selected, get_translated_text($group['g_name'], $GLOBALS['FORUM_DB']))); } } $sec_url = build_url(array('page' => 'groups', 'type' => 'browse'), get_module_zone('groups')); if (!$_groups2->is_empty()) { $fields->attach(form_input_multi_list(do_lang_tempcode('SECONDARY_GROUP_MEMBERSHIP'), do_lang_tempcode('DESCRIPTION_SECONDARY_GROUP', escape_html($sec_url->evaluate())), 'secondary_groups', $_groups2)); } } // Special admin options if (has_privilege(get_member(), 'member_maintenance')) { if ($validated == 0) { $validated = get_param_integer('validated', 0); if (($validated == 1) && (addon_installed('unvalidated'))) { attach_message(do_lang_tempcode('WILL_BE_VALIDATED_WHEN_SAVING')); } } if (addon_installed('unvalidated')) { $fields->attach(form_input_tick(do_lang_tempcode('VALIDATED'), do_lang_tempcode('DESCRIPTION_MEMBER_VALIDATED'), 'validated', $validated == 1)); } if (get_option('enable_highlight_name') == '1') { $fields->attach(form_input_tick(do_lang_tempcode('HIGHLIGHTED_NAME'), do_lang_tempcode(addon_installed('pointstore') ? 'DESCRIPTION_HIGHLIGHTED_NAME_P' : 'DESCRIPTION_HIGHLIGHTED_NAME'), 'highlighted_name', $highlighted_name == 1)); } if ((!is_null($member_id)) && ($member_id != get_member())) {// Can't ban someone new, and can't ban yourself $fields->attach(form_input_tick(do_lang_tempcode('BANNED'), do_lang_tempcode('DESCRIPTION_MEMBER_BANNED'), 'is_perm_banned', $is_perm_banned == 1)); } } if (addon_installed('content_reviews')) { require_code('content_reviews2'); $fields->attach(content_review_get_fields('member', is_null($member_id) ? null : strval($member_id))); } } return array($fields, $hidden); } /** * Get form fields for adding/editing/finishing a member profile. * * @param boolean $mini_mode Whether we are only handling the essential details of a profile. * @param ?MEMBER $member_id The ID of the member we are handling (null: new member). * @param ?array $groups A list of usergroups (null: default/current usergroups). * @param ?array $custom_fields A map of custom fields values (field-id=>value) (null: not known). * @return array A pair: The form fields, Hidden fields (both Tempcode). */ function cns_get_member_fields_profile($mini_mode = true, $member_id = null, $groups = null, $custom_fields = null) { require_code('cns_members_action'); $fields = new Tempcode(); $hidden = new Tempcode(); if (is_null($groups)) { $groups = is_null($member_id) ? cns_get_all_default_groups(true) : $GLOBALS['CNS_DRIVER']->get_members_groups($member_id); } $_custom_fields = cns_get_all_custom_fields_match( $groups, // groups ($mini_mode || (is_null($member_id)) || ($member_id == get_member()) || (has_privilege(get_member(), 'view_any_profile_field'))) ? null : 1, // public view null, // owner view ($mini_mode || (is_null($member_id)) || ($member_id != get_member()) || (has_privilege(get_member(), 'view_any_profile_field'))) ? null : 1, // owner set null, // required null, // show in posts null, // show in post previews null, // special start $mini_mode ? true : null // show on join form ); $GLOBALS['NO_DEV_MODE_FULLSTOP_CHECK'] = true; $field_groups = array(); require_code('fields'); require_code('encryption'); foreach ($_custom_fields as $custom_field) { $ob = get_fields_hook($custom_field['cf_type']); list(, , $storage_type) = $ob->get_field_value_row_bits($custom_field); $existing_field = (!is_null($custom_fields)) && (array_key_exists($custom_field['trans_name'], $custom_fields)); if ($existing_field) { $value = $custom_fields[$custom_field['trans_name']]['RAW']; if (($custom_field['cf_encrypted'] == 1) && (is_encryption_enabled())) { $value = remove_magic_encryption_marker($value); } if (!member_field_is_required($member_id, 'required_cpfs', $value) && $custom_field['cf_type'] != 'tick'/*HACKHACK*/) { $custom_field['cf_required'] = 0; } } else { $value = $custom_field['cf_default']; if (!member_field_is_required($member_id, 'required_cpfs', '')) { $custom_field['cf_required'] = 0; } } $result = mixed(); $_description = escape_html(get_translated_text($custom_field['cf_description'], $GLOBALS['FORUM_DB'])); $field_cat = ''; $matches = array(); if (strpos($custom_field['trans_name'], ': ') !== false) { $field_cat = substr($custom_field['trans_name'], 0, strpos($custom_field['trans_name'], ': ')); if ($field_cat . ': ' == $custom_field['trans_name']) { $custom_field['trans_name'] = $field_cat; // Just been pulled out as heading, nothing after ": " } else { $custom_field['trans_name'] = substr($custom_field['trans_name'], strpos($custom_field['trans_name'], ': ') + 2); } } elseif (preg_match('#(^\([A-Z][^\)]*\) )|( \([A-Z][^\)]*\)$)#', $custom_field['trans_name'], $matches) != 0) { $field_cat = trim($matches[0], '() '); $custom_field['trans_name'] = str_replace($matches[0], '', $custom_field['trans_name']); } $result = $ob->get_field_inputter($custom_field['trans_name'], $_description, $custom_field, $value, !$existing_field); if (!array_key_exists($field_cat, $field_groups)) { $field_groups[$field_cat] = new Tempcode(); } if (is_array($result)) { $field_groups[$field_cat]->attach($result[0]); $hidden->attach($result[1]); } else { $field_groups[$field_cat]->attach($result); } } if (array_key_exists('', $field_groups)) { // Blank prefix must go first $field_groups_blank = $field_groups['']; unset($field_groups['']); $field_groups = array_merge(array($field_groups_blank), $field_groups); } foreach ($field_groups as $field_group_title => $extra_fields) { if (is_integer($field_group_title)) { $field_group_title = ($field_group_title == 0) ? '' : strval($field_group_title); } if ($field_group_title != '') { $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array( '_GUID' => 'af91e3c040a0a18a4d9cc1143c0d2007', 'TITLE' => $field_group_title, 'SECTION_HIDDEN' => (get_page_name() == 'admin_cns_members'), ))); } $fields->attach($extra_fields); } $GLOBALS['NO_DEV_MODE_FULLSTOP_CHECK'] = false; return array($fields, $hidden); } /** * Edit a member. * * @param AUTO_LINK $member_id The ID of the member. * @param ?SHORT_TEXT $email_address The e-mail address. (null: don't change) * @param ?BINARY $preview_posts Whether posts are previewed before they are made. (null: don't change) * @param ?integer $dob_day Day of date of birth. (null: don't change) (-1: deset) * @param ?integer $dob_month Month of date of birth. (null: don't change) (-1: deset) * @param ?integer $dob_year Year of date of birth. (null: don't change) (-1: deset) * @param ?ID_TEXT $timezone The member timezone. (null: don't change) * @param ?GROUP $primary_group The members primary (null: don't change). * @param array $custom_fields A map of custom fields values (field-id=>value). * @param ?ID_TEXT $theme The members default theme. (null: don't change) * @param ?BINARY $reveal_age Whether the members age may be shown. (null: don't change) * @param ?BINARY $views_signatures Whether the member sees signatures in posts. (null: don't change) * @param ?BINARY $auto_monitor_contrib_content Whether the member automatically is enabled for notifications for content they contribute to. (null: don't change) * @param ?LANGUAGE_NAME $language The members language. (null: don't change) * @param ?BINARY $allow_emails Whether the member allows e-mails via the site. (null: don't change) * @param ?BINARY $allow_emails_from_staff Whether the member allows e-mails from staff via the site. (null: don't change) * @param ?BINARY $validated Whether the profile has been validated (null: do not change this). (null: don't change) * @param ?string $username The username. (null: don't change) * @param ?string $password The password. (null: don't change) * @param ?BINARY $highlighted_name Whether the member username will be highlighted. (null: don't change) * @param ?SHORT_TEXT $pt_allow Usergroups that may PT the member. (null: don't change) * @param ?LONG_TEXT $pt_rules_text Rules that other members must agree to before they may start a PT with the member. (null: don't change) * @param ?TIME $on_probation_until When the member is on probation until (null: don't change) * @param ?BINARY $auto_mark_read Mark topics as read automatically (null: don't change) * @param ?TIME $join_time When the member joined (null: don't change) * @param ?URLPATH $avatar_url Avatar (null: don't change) * @param ?LONG_TEXT $signature Signature (null: don't change) * @param ?BINARY $is_perm_banned Banned status (null: don't change) * @param ?URLPATH $photo_url Photo URL (null: don't change) * @param ?URLPATH $photo_thumb_url URL of thumbnail of photo (null: don't change) * @param ?SHORT_TEXT $salt Password salt (null: don't change) * @param ?ID_TEXT $password_compatibility_scheme Password compatibility scheme (null: don't change) * @param boolean $skip_checks Whether to skip security checks and most of the change-triggered emails */ function cns_edit_member($member_id, $email_address, $preview_posts, $dob_day, $dob_month, $dob_year, $timezone, $primary_group, $custom_fields, $theme, $reveal_age, $views_signatures, $auto_monitor_contrib_content, $language, $allow_emails, $allow_emails_from_staff, $validated = null, $username = null, $password = null, $highlighted_name = null, $pt_allow = '*', $pt_rules_text = '', $on_probation_until = null, $auto_mark_read = null, $join_time = null, $avatar_url = null, $signature = null, $is_perm_banned = null, $photo_url = null, $photo_thumb_url = null, $salt = null, $password_compatibility_scheme = null, $skip_checks = false) { require_code('type_sanitisation'); require_code('cns_members_action'); $update = array(); if (!$skip_checks) { $old_email_address = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_email_address'); $email_address_required = member_field_is_required($member_id, 'email_address'); if ((!is_null($email_address)) && ($email_address != '') && ($email_address != STRING_MAGIC_NULL) && (!is_email_address($email_address))) { warn_exit(do_lang_tempcode('_INVALID_EMAIL_ADDRESS', escape_html($email_address))); } if ((get_option('one_per_email_address') != '0') && ($email_address != '') && ($email_address != $old_email_address) && ($email_address != STRING_MAGIC_NULL)) { $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_members', 'id', array('m_email_address' => $email_address)); if ((!is_null($test)) && ($test != $member_id)) { warn_exit(do_lang_tempcode('_EMAIL_ADDRESS_IN_USE')); } } } if (!is_null($username)) { if (!$skip_checks) { cns_check_name_valid($username, $member_id, $password); require_code('urls2'); suggest_new_idmoniker_for('members', 'view', strval($member_id), '', $username); } } if (!is_null($password)) { // Password change if ((is_null($password_compatibility_scheme)) && (get_value('no_password_hashing') === '1')) { $password_compatibility_scheme = 'plain'; $update['m_password_change_code'] = ''; $salt = ''; } if ((!is_null($salt)) || (!is_null($password_compatibility_scheme))) { if (!is_null($salt)) { $update['m_pass_salt'] = $salt; } if (!is_null($password_compatibility_scheme)) { $update['m_password_compat_scheme'] = $password_compatibility_scheme; } $update['m_pass_hash_salted'] = $password; } else { require_code('crypt'); $update['m_password_change_code'] = ''; $salt = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_pass_salt'); $update['m_pass_hash_salted'] = ratchet_hash($password, $salt); $update['m_password_compat_scheme'] = ''; } $password_change_days = get_option('password_change_days'); if (intval($password_change_days) > 0) { if ($password_compatibility_scheme == '') { require_code('password_rules'); bump_password_change_date($member_id, $password, $update['m_pass_hash_salted'], $salt, $skip_checks); $update['m_last_visit_time'] = time(); // Needed when an admin changing another password (but okay always): So that the password isn't assumed auto-expired, forcing them to reset it again } } } // Supplement custom field values given with defaults, and check constraints $all_fields = cns_get_all_custom_fields_match($GLOBALS['CNS_DRIVER']->get_members_groups($member_id)); foreach ($all_fields as $field) { $field_id = $field['id']; if (array_key_exists($field_id, $custom_fields)) { if (!$skip_checks) { if (($field['cf_owner_set'] == 0) && ($member_id == get_member()) && (!has_privilege(get_member(), 'view_any_profile_field'))) { access_denied('I_ERROR'); } } } } // Set custom profile field values $all_fields_types = collapse_2d_complexity('id', 'cf_type', $all_fields); $changes = array(); foreach ($custom_fields as $field_id => $value) { if (!array_key_exists($field_id, $all_fields_types)) { continue; // Trying to set a field we're not allowed to (doesn't apply to our group) } $change = cns_set_custom_field($member_id, $field_id, $value, $all_fields_types[$field_id], true); if (!is_null($change)) { $changes = array_merge($changes, $change); } } if (count($changes) != 0) { $GLOBALS['FORUM_DB']->query_update('f_member_custom_fields', $changes, array('mf_member_id' => $member_id), '', 1); } $old_primary_group = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_primary_group'); $_pt_rules_text = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_pt_rules_text'); $_signature = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_signature'); if (!is_null($theme)) { $update['m_theme'] = $theme; } if (!is_null($preview_posts)) { $update['m_preview_posts'] = $preview_posts; } if (!is_null($dob_day)) { $update['m_dob_day'] = ($dob_day == -1) ? null : $dob_day; } if (!is_null($dob_month)) { $update['m_dob_month'] = ($dob_month == -1) ? null : $dob_month; } if (!is_null($dob_year)) { $update['m_dob_year'] = ($dob_year == -1) ? null : $dob_year; } if (!is_null($timezone)) { $update['m_timezone_offset'] = $timezone; } if (!is_null($reveal_age)) { $update['m_reveal_age'] = $reveal_age; } if (!is_null($email_address)) { $update['m_email_address'] = $email_address; } if (!is_null($views_signatures)) { $update['m_views_signatures'] = $views_signatures; } if (!is_null($auto_monitor_contrib_content)) { $update['m_auto_monitor_contrib_content'] = $auto_monitor_contrib_content; } if (!is_null($language)) { $update['m_language'] = $language; } $doing_email_option = (get_option('allow_email_disable') == '1') && (addon_installed('cns_contact_member')); if ((!is_null($allow_emails)) && ($doing_email_option)) { $update['m_allow_emails'] = $allow_emails; } $doing_email_from_staff_option = (get_option('allow_email_from_staff_disable') == '1'); if ((!is_null($allow_emails_from_staff)) && ($doing_email_from_staff_option)) { $update['m_allow_emails_from_staff'] = $allow_emails_from_staff; } if (!is_null($pt_allow)) { $update['m_pt_allow'] = $pt_allow; } if (!is_null($pt_rules_text)) { $update += lang_remap_comcode('m_pt_rules_text', $_pt_rules_text, $pt_rules_text, $GLOBALS['FORUM_DB']); } if (($skip_checks) || (has_privilege(get_member(), 'probate_members'))) { $update['m_on_probation_until'] = $on_probation_until; } if (!is_null($auto_mark_read)) { $update['m_auto_mark_read'] = $auto_mark_read; } if (!is_null($join_time)) { $update['m_join_time'] = $join_time; } if (!is_null($avatar_url)) { $update['m_avatar_url'] = $avatar_url; } if (!is_null($signature)) { $update += lang_remap_comcode('m_signature', $_signature, $signature, $GLOBALS['FORUM_DB']); } if (!is_null($is_perm_banned)) { $update['m_is_perm_banned'] = $is_perm_banned; } if (!is_null($photo_url)) { $update['m_photo_url'] = $photo_url; } if (!is_null($photo_thumb_url)) { $update['m_photo_thumb_url'] = $photo_thumb_url; } $old_username = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_username'); if ((!is_null($username)) && (!is_null($old_username)) && ($username != $old_username) && (($skip_checks) || (has_actual_page_access(get_member(), 'admin_cns_members')) || (has_privilege($member_id, 'rename_self')))) { // Username change $update['m_username'] = $username; // Reassign personal galleries if (addon_installed('galleries')) { require_lang('galleries'); $personal_galleries = $GLOBALS['SITE_DB']->query('SELECT name,fullname,parent_id FROM ' . get_table_prefix() . 'galleries WHERE name LIKE \'member\_' . strval($member_id) . '\_%\''); foreach ($personal_galleries as $gallery) { $parent_title = get_translated_text($GLOBALS['SITE_DB']->query_select_value('galleries', 'fullname', array('name' => $gallery['parent_id']))); if (get_translated_text($gallery['fullname']) == do_lang('PERSONAL_GALLERY_OF', $old_username, $parent_title)) { $new_fullname = do_lang('PERSONAL_GALLERY_OF', $username, $parent_title); $GLOBALS['SITE_DB']->query_update('galleries', lang_remap_comcode('fullname', $gallery['fullname'], $new_fullname), array('name' => $gallery['name']), '', 1); } } } require_code('notifications'); $subject = do_lang('USERNAME_CHANGED_MAIL_SUBJECT', $username, $old_username, null, get_lang($member_id)); $mail = do_notification_lang('USERNAME_CHANGED_MAIL', comcode_escape(get_site_name()), comcode_escape($username), comcode_escape($old_username), get_lang($member_id)); dispatch_notification('cns_username_changed', null, $subject, $mail, array($member_id)); $subject = do_lang('STAFF_USERNAME_CHANGED_MAIL_SUBJECT', $username, $old_username, null, get_site_default_lang()); $mail = do_notification_lang('STAFF_USERNAME_CHANGED_MAIL', comcode_escape(get_site_name()), comcode_escape($username), comcode_escape($old_username), get_site_default_lang()); dispatch_notification('cns_username_changed_staff', null, $subject, $mail, null, get_member(), 3, false, false, null, null, '', '', '', '', null, true); if (addon_installed('news')) { $GLOBALS['SITE_DB']->query_update('news', array('author' => $username), array('author' => $old_username)); } update_member_username_caching($member_id, $username); } if (!is_null($password)) { // Password change // Security, clear out sessions from other people on this user - just in case the reset is due to suspicious activity $GLOBALS['SITE_DB']->query('DELETE FROM ' . get_table_prefix() . 'sessions WHERE member_id=' . strval($member_id) . ' AND ' . db_string_not_equal_to('the_session', get_session_id())); if (!$skip_checks) { if (($member_id == get_member()) || (get_value('disable_password_change_notifications_for_staff') !== '1')) { if (get_page_name() != 'admin_cns_members') { require_code('notifications'); $part_b = ''; if (!has_actual_page_access(get_member(), 'admin_cns_members')) { $part_b = do_lang('PASSWORD_CHANGED_MAIL_BODY_2', get_ip_address()); } $mail = do_notification_lang('PASSWORD_CHANGED_MAIL_BODY', get_site_name(), $part_b, null, get_lang($member_id)); dispatch_notification('cns_password_changed', null, do_lang('PASSWORD_CHANGED_MAIL_SUBJECT', null, null, null, get_lang($member_id)), $mail, array($member_id), null, 2); } } } } if (!is_null($validated)) { $update['m_validated_email_confirm_code'] = ''; if (addon_installed('unvalidated')) { $update['m_validated'] = $validated; if (($validated == 1) && ($GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_validated') == 0)) { $update['m_join_time'] = time(); // So welcome mails go out correctly } } } if (!is_null($highlighted_name)) { $update['m_highlighted_name'] = $highlighted_name; } if (!is_null($primary_group)) { $update['m_primary_group'] = $primary_group; if ($primary_group != $old_primary_group) { $GLOBALS['FORUM_DB']->query_insert('f_group_join_log', array( 'member_id' => $member_id, 'usergroup_id' => $primary_group, 'join_time' => time() )); log_it('MEMBER_PRIMARY_GROUP_CHANGED', strval($member_id), strval($primary_group)); } } $join_time = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_join_time'); $GLOBALS['FORUM_DB']->query_update('f_members', $update, array('id' => $member_id), '', 1); if (get_member() != $member_id) { log_it('EDIT_MEMBER_PROFILE', strval($member_id), $username); } $old_validated = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_validated'); if (($old_validated == 0) && ($validated == 1)) { require_code('mail'); $_login_url = build_url(array('page' => 'login'), get_module_zone('login'), null, false, false, true); $login_url = $_login_url->evaluate(); $_username = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_username'); // NB: Same mail also sent in settings.php (quick-validate feature) $vm_subject = do_lang('VALIDATED_MEMBER_SUBJECT', get_site_name(), null, get_lang($member_id)); $vm_body = do_lang('MEMBER_VALIDATED', get_site_name(), $_username, $login_url, get_lang($member_id)); mail_wrap($vm_subject, $vm_body, array($email_address), $_username, '', '', 3, null, false, null, false, false, false, 'MAIL', false, null, null, $join_time); } $old_email_address = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_email_address'); if ($old_email_address != $email_address) { $GLOBALS['FORUM_DB']->query_update('f_invites', array('i_email_address' => $old_email_address), array('i_email_address' => $email_address)); } delete_value('cns_newest_member_id'); delete_value('cns_newest_member_username'); // Decache from run-time cache unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); unset($GLOBALS['MEMBER_CACHE_FIELD_MAPPINGS'][$member_id]); unset($GLOBALS['TIMEZONE_MEMBER_CACHE'][$member_id]); unset($GLOBALS['USER_NAME_CACHE'][$member_id]); if ((addon_installed('commandr')) && (!running_script('install')) && (!get_mass_import_mode())) { require_code('resource_fs'); generate_resource_fs_moniker('member', strval($member_id)); } decache('main_members'); if (($GLOBALS['FORUM_DRIVER']->is_super_admin($member_id)) && ($old_email_address == '')) { decache('main_staff_checklist'); // As it tracks whether admin have e-mail address set } require_code('sitemap_xml'); notify_sitemap_node_edit('_SEARCH:members:view:' . strval($member_id), true); } /** * Delete a member. * * @param AUTO_LINK $member_id The ID of the member. */ function cns_delete_member($member_id) { $username = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_username'); $signature = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_signature'); require_code('attachments2'); require_code('attachments3'); delete_lang_comcode_attachments($signature, 'signature', strval($member_id), $GLOBALS['FORUM_DB']); $GLOBALS['FORUM_DB']->query_delete('f_members', array('id' => $member_id), '', 1); $GLOBALS['FORUM_DB']->query_delete('f_group_members', array('gm_member_id' => $member_id)); $GLOBALS['FORUM_DB']->query_update('f_groups', array('g_group_leader' => get_member()), array('g_group_leader' => $member_id)); $GLOBALS['SITE_DB']->query_delete('sessions', array('member_id' => $member_id)); require_code('fields'); // Delete custom profile fields $cpfs = $GLOBALS['FORUM_DB']->query_select('f_custom_fields'); $fields_row = $GLOBALS['FORUM_DB']->query_select('f_member_custom_fields', array('*'), array('mf_member_id' => $member_id), '', 1); if (array_key_exists(0, $fields_row)) { foreach ($cpfs as $field) { $l = $fields_row[0]['field_' . strval($field['id'])]; $object = get_fields_hook($field['cf_type']); list(, , $storage_type) = $object->get_field_value_row_bits($field); if (method_exists($object, 'cleanup')) { $object->cleanup(array('cv_value' => $l)); } if ((strpos($storage_type, '_trans') !== false) && (!is_null($l))) { if (true) { // Always do this just in case it is for attachments require_code('attachments2'); require_code('attachments3'); delete_lang_comcode_attachments($l, 'null', strval($member_id), $GLOBALS['FORUM_DB']); } else { delete_lang($l, $GLOBALS['FORUM_DB']); } } } } $GLOBALS['FORUM_DB']->query_delete('f_member_custom_fields', array('mf_member_id' => $member_id), '', 1); // Cleanup images $old = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_avatar_url'); if ((url_is_local($old)) && ((substr($old, 0, 20) == 'uploads/cns_avatars/') || (substr($old, 0, 16) == 'uploads/avatars/'))) { @unlink(get_custom_file_base() . '/' . rawurldecode($old)); sync_file(rawurldecode($old)); } $old = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_photo_url'); if ((url_is_local($old)) && ((substr($old, 0, 19) == 'uploads/cns_photos/') || (substr($old, 0, 15) == 'uploads/photos/'))) { @unlink(get_custom_file_base() . '/' . rawurldecode($old)); sync_file(rawurldecode($old)); } if (addon_installed('catalogues')) { update_catalogue_content_ref('member', strval($member_id), ''); } delete_value('cns_newest_member_id'); delete_value('cns_newest_member_username'); log_it('DELETE_MEMBER', strval($member_id), $username); if ((addon_installed('commandr')) && (!running_script('install')) && (!get_mass_import_mode())) { require_code('resource_fs'); expunge_resource_fs_moniker('member', strval($member_id)); } decache('main_members'); require_code('sitemap_xml'); notify_sitemap_node_delete('_SEARCH:members:view:' . strval($member_id)); } /** * Ban a member. * * @param AUTO_LINK $member_id The ID of the member. */ function cns_ban_member($member_id) { if ($GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_is_perm_banned') == 1) { return; } require_code('mail'); $username = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_username'); $email_address = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_email_address'); $join_time = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_join_time'); $GLOBALS['FORUM_DB']->query_update('f_members', array('m_is_perm_banned' => 1), array('id' => $member_id), '', 1); log_it('BAN_MEMBER', strval($member_id), $username); require_lang('cns'); $mail = do_lang('BAN_MEMBER_MAIL', $username, get_site_name(), array(), get_lang($member_id)); mail_wrap(do_lang('BAN_MEMBER_MAIL_SUBJECT', null, null, null, get_lang($member_id)), $mail, array($email_address), $username, '', '', 2, null, false, null, false, false, false, 'MAIL', false, null, null, $join_time); decache('main_members'); unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); } /** * Unban a member. * * @param AUTO_LINK $member_id The ID of the member. */ function cns_unban_member($member_id) { if ($GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_is_perm_banned') == 0) { return; } $username = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_username'); $email_address = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_email_address'); $join_time = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_join_time'); $GLOBALS['FORUM_DB']->query_update('f_members', array('m_is_perm_banned' => 0), array('id' => $member_id), '', 1); log_it('UNBAN_MEMBER', strval($member_id), $username); require_lang('cns'); $mail = do_lang('UNBAN_MEMBER_MAIL', $username, get_site_name(), array(), get_lang($member_id)); mail_wrap(do_lang('UNBAN_MEMBER_MAIL_SUBJECT', null, null, null, get_lang($member_id)), $mail, array($email_address), $username, '', '', 2, null, false, null, false, false, false, 'MAIL', false, null, null, $join_time); decache('main_members'); unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); } /** * Edit a custom profile field. * * @param AUTO_LINK $id The ID of the custom profile field. * @param SHORT_TEXT $name Name of the field. * @param SHORT_TEXT $description Description of the field. * @param LONG_TEXT $default The default value for the field. * @param BINARY $public_view Whether the field is publicly viewable. * @param BINARY $owner_view Whether the field is viewable by the owner. * @param BINARY $owner_set Whether the field may be set by the owner. * @param BINARY $encrypted Whether the field should be encrypted. * @param BINARY $required Whether the field is to be shown on the join form * @param BINARY $show_in_posts Whether this field is shown in posts and places where member details are highlighted (such as an image in a member gallery). * @param BINARY $show_in_post_previews Whether this field is shown in preview places, such as in the forum member tooltip. * @param ?integer $order The order of this field relative to other fields. (null: keep the current order) * @param LONG_TEXT $only_group The usergroups that this field is confined to (comma-separated list). * @param ID_TEXT $type The type of the field. * @set short_text long_text short_trans long_trans integer upload picture url list tick float * @param BINARY $show_on_join_form Whether it is required that every member have this field filled in. * @param SHORT_TEXT $options Field options */ function cns_edit_custom_field($id, $name, $description, $default, $public_view, $owner_view, $owner_set, $encrypted, $required, $show_in_posts, $show_in_post_previews, $order, $only_group, $type, $show_on_join_form, $options) { $dbs_back = $GLOBALS['NO_DB_SCOPE_CHECK']; $GLOBALS['NO_DB_SCOPE_CHECK'] = true; if ($only_group == '-1') { $only_group = ''; } $info = $GLOBALS['FORUM_DB']->query_select('f_custom_fields', array('cf_name', 'cf_description'), array('id' => $id), '', 1); $_name = $info[0]['cf_name']; $_description = $info[0]['cf_description']; $map = array( 'cf_default' => $default, 'cf_public_view' => $public_view, 'cf_owner_view' => $owner_view, 'cf_owner_set' => $owner_set, 'cf_required' => $required, 'cf_show_in_posts' => $show_in_posts, 'cf_show_in_post_previews' => $show_in_post_previews, 'cf_only_group' => $only_group, 'cf_type' => $type, 'cf_show_on_join_form' => $show_on_join_form, 'cf_options' => $options, ); $map += lang_remap('cf_name', $_name, $name, $GLOBALS['FORUM_DB']); $map += lang_remap('cf_description', $_description, $description, $GLOBALS['FORUM_DB']); if ($order !== null) { $map['cf_order'] = $order; } $GLOBALS['FORUM_DB']->query_update('f_custom_fields', $map, array('id' => $id), '', 1); require_code('cns_members_action'); list($_type, $index) = get_cpf_storage_for($type); require_code('database_action'); if (substr(get_db_type(), 0, 5) == 'mysql') { $GLOBALS['SITE_DB']->query('SET sql_mode=\'\'', null, null, true); // Turn off strict mode } $GLOBALS['FORUM_DB']->alter_table_field('f_member_custom_fields', 'field_' . strval($id), $_type); // LEGACY: Field type should not have changed, but bugs can happen, especially between CMS versions, so we allow a CPF edit as a "fixup" op build_cpf_indices($id, $index, $type, $_type); log_it('EDIT_CUSTOM_PROFILE_FIELD', strval($id), $name); if ((addon_installed('commandr')) && (!running_script('install')) && (!get_mass_import_mode())) { require_code('resource_fs'); generate_resource_fs_moniker('cpf', strval($id)); } $GLOBALS['NO_DB_SCOPE_CHECK'] = $dbs_back; if (function_exists('persistent_cache_delete')) { persistent_cache_delete('CUSTOM_FIELD_CACHE'); persistent_cache_delete('LIST_CPFS'); } decache('main_members'); } /** * Delete a custom profile field. * * @param AUTO_LINK $id The ID of the custom profile field. */ function cns_delete_custom_field($id) { $dbs_back = $GLOBALS['NO_DB_SCOPE_CHECK']; $GLOBALS['NO_DB_SCOPE_CHECK'] = true; $info = $GLOBALS['FORUM_DB']->query_select('f_custom_fields', array('cf_name', 'cf_description'), array('id' => $id), '', 1); if (!array_key_exists(0, $info)) { warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'cpf')); } $_name = $info[0]['cf_name']; $_description = $info[0]['cf_description']; require_code('database_action'); delete_lang($_name, $GLOBALS['FORUM_DB']); delete_lang($_description, $GLOBALS['FORUM_DB']); $GLOBALS['FORUM_DB']->delete_index_if_exists('f_member_custom_fields', 'mcf' . strval($id)); $GLOBALS['FORUM_DB']->delete_index_if_exists('f_member_custom_fields', '#mcf_ft_' . strval($id)); $GLOBALS['FORUM_DB']->delete_table_field('f_member_custom_fields', 'field_' . strval($id)); $GLOBALS['FORUM_DB']->query_delete('f_custom_fields', array('id' => $id), '', 1); $GLOBALS['NO_DB_SCOPE_CHECK'] = $dbs_back; global $TABLE_LANG_FIELDS_CACHE; unset($TABLE_LANG_FIELDS_CACHE['f_member_custom_fields']); log_it('DELETE_CUSTOM_PROFILE_FIELD', strval($id), get_translated_text($_name, $GLOBALS['FORUM_DB'])); if ((addon_installed('commandr')) && (!running_script('install')) && (!get_mass_import_mode())) { require_code('resource_fs'); expunge_resource_fs_moniker('cpf', strval($id)); } if (function_exists('persistent_cache_delete')) { persistent_cache_delete('CUSTOM_FIELD_CACHE'); persistent_cache_delete('LIST_CPFS'); } if (function_exists('decache')) { decache('main_members'); } } /** * Set a custom profile field for a member. * * @param MEMBER $member_id The member. * @param AUTO_LINK $field_id The field being set. * @param mixed $value The value of the field. For a trans-type field, this can be either a lang-ID to be copied (from forum DB), or an actual string. * @param ?ID_TEXT $type The field type (null: look it up). * @param boolean $defer Whether to defer the change, by returning a result change rather than doing it right away. * @return ?array Mapping change (null: none / can't defer). */ function cns_set_custom_field($member_id, $field_id, $value, $type = null, $defer = false) { if ($value === STRING_MAGIC_NULL) { return null; } if (is_null($type)) { $type = $GLOBALS['FORUM_DB']->query_select_value('f_custom_fields', 'cf_type', array('id' => $field_id)); } cns_get_custom_field_mappings($member_id); // This will do an auto-repair if CPF storage row is missing $db_fieldname = 'field_' . strval($field_id); global $ANY_FIELD_ENCRYPTED; if ($ANY_FIELD_ENCRYPTED === null) { $ANY_FIELD_ENCRYPTED = !is_null($GLOBALS['FORUM_DB']->query_select_value_if_there('f_custom_fields', 'cf_encrypted', array('cf_encrypted' => 1))); } if ($ANY_FIELD_ENCRYPTED) { $encrypted = $GLOBALS['FORUM_DB']->query_select_value('f_custom_fields', 'cf_encrypted', array('id' => $field_id)); if ($encrypted) { require_code('encryption'); $current = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_member_custom_fields', $db_fieldname, array('mf_member_id' => $member_id)); if ($current === null) { return null; } if ((remove_magic_encryption_marker($value) == remove_magic_encryption_marker($current)) && (is_data_encrypted($current))) { return null; } $value = encrypt_data($value); } } else { $encrypted = false; } require_code('fields'); $ob = get_fields_hook($type); list(, , $storage_type) = $ob->get_field_value_row_bits(array('id' => $field_id, 'cf_default' => '', 'cf_type' => $type)); static $done_one_posting_field = false; $ret = null; if (strpos($storage_type, '_trans') !== false) { if (is_integer($value)) { $value = get_translated_text($value, $GLOBALS['FORUM_DB']); } $map = array(); $current = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_member_custom_fields', $db_fieldname, array('mf_member_id' => $member_id)); if (is_null($current)) { if (($type == 'posting_field') && (!$done_one_posting_field)) { $done_one_posting_field = true; require_code('attachments2'); $map += insert_lang_comcode_attachments($db_fieldname, 3, $value, 'null', strval($member_id), $GLOBALS['FORUM_DB']); } else { $map += insert_lang_comcode($db_fieldname, $value, 3, $GLOBALS['FORUM_DB']); } $GLOBALS['FORUM_DB']->query_update('f_member_custom_fields', $map, array('mf_member_id' => $member_id), '', 1); } else { if (($type == 'posting_field') && (!$done_one_posting_field)) { $done_one_posting_field = true; require_code('attachments2'); require_code('attachments3'); $map += update_lang_comcode_attachments($db_fieldname, $current, $value, 'null', strval($member_id), $GLOBALS['FORUM_DB'], $member_id); } else { $map += lang_remap_comcode($db_fieldname, $current, $value, $GLOBALS['FORUM_DB']); } $GLOBALS['FORUM_DB']->query_update('f_member_custom_fields', $map, array('mf_member_id' => $member_id), '', 1); } } else { $change = array(); if (is_string($value)) { switch ($storage_type) { case 'short_trans': case 'long_trans': $change += insert_lang($db_fieldname, $value, 3, $GLOBALS['FORUM_DB']); break; case 'integer': $change[$db_fieldname] = ($value == '') ? null : intval($value); break; case 'float': $change[$db_fieldname] = ($value == '') ? null : floatval($value); break; default: $change[$db_fieldname] = $value; break; } } elseif ($value === null) { switch ($storage_type) { case 'integer': case 'float': $change[$db_fieldname] = $value; break; } } else { $change[$db_fieldname] = $value; } if (!$defer) { $GLOBALS['FORUM_DB']->query_update('f_member_custom_fields', $change, array('mf_member_id' => $member_id), '', 1); } $ret = $change; } if (function_exists('decache')) { decache('main_members'); } global $MEMBER_CACHE_FIELD_MAPPINGS; if ((isset($MEMBER_CACHE_FIELD_MAPPINGS)) && (isset($MEMBER_CACHE_FIELD_MAPPINGS[$member_id]))) { unset($MEMBER_CACHE_FIELD_MAPPINGS[$member_id]); } return $ret; } /** * Check a username is valid for adding, and possibly also the password. * * @param ?SHORT_TEXT $username The username (may get altered) (null: nothing to check). * @param ?MEMBER $member_id The member (null: member not actually added yet; this ID is only given for the duplication check, to make sure it doesn't think we are duplicating with ourself). * @param ?SHORT_TEXT $password The password (null: nothing to check). * @param boolean $return_errors Whether to return errors instead of dieing on them. * @return ?Tempcode Error (null: none). */ function cns_check_name_valid(&$username, $member_id = null, $password = null, $return_errors = false) { // Check it doesn't already exist if (!is_null($username)) { $test = is_null($member_id) ? null : $GLOBALS['FORUM_DB']->query_select_value_if_there('f_members', 'id', array('m_username' => $username, 'id' => $member_id)); // Precedence on an ID match in case there are duplicate usernames and user is trying to fix that if (is_null($test)) { $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_members', 'id', array('m_username' => $username)); } if ((!is_null($test)) && ($test !== $member_id)) { $error = do_lang_tempcode('USERNAME_ALREADY_EXISTS'); if ($return_errors) { return $error; } warn_exit($error); } $username_changed = is_null($test); } else { $username_changed = false; } if (!is_null($username)) { // Check for disallowed symbols in username $disallowed_characters = array(/*'<','>','&','"',"'",'$',','*/); // Actually we can tolerate this stuff foreach ($disallowed_characters as $disallowed_character) { if ((strpos($username, $disallowed_character) !== false) && ($username_changed)) { $error = do_lang_tempcode('USERNAME_BAD_SYMBOLS', escape_html($disallowed_character)); if ($return_errors) { return $error; } warn_exit($error); } } if ((strpos($username, '@') !== false) && (strpos($username, '.') !== false) && ($username_changed)) { $error = do_lang_tempcode('USERNAME_BAD_SYMBOLS', escape_html('@ / .')); if ($return_errors) { return $error; } warn_exit($error); } } // Check lengths if (get_page_name() != 'admin_cns_members') { if (!is_null($username)) { $_maximum_username_length = get_option('maximum_username_length'); $maximum_username_length = intval($_maximum_username_length); if ((cms_mb_strlen($username) > $maximum_username_length) && ($username_changed)) { $error = do_lang_tempcode('USERNAME_TOO_LONG', escape_html(integer_format($maximum_username_length))); if ($return_errors) { return $error; } warn_exit($error); } $_minimum_username_length = get_option('minimum_username_length'); $minimum_username_length = intval($_minimum_username_length); if ((cms_mb_strlen($username) < $minimum_username_length) && ($username_changed)) { $error = do_lang_tempcode('USERNAME_TOO_SHORT', escape_html(integer_format($minimum_username_length))); if ($return_errors) { return $error; } warn_exit($error); } } if (!is_null($password)) { require_code('password_rules'); $test = check_password_complexity($username, $password, $return_errors); if (!is_null($test)) { return $test; } } } // Check for whitespace if (!is_null($username)) { $prohibit_username_whitespace = get_option('prohibit_username_whitespace'); if (($prohibit_username_whitespace === '1') && (cms_preg_match_safe('#\s#', $username) != 0) && ($username_changed)) { $error = do_lang_tempcode('USERNAME_PASSWORD_WHITESPACE'); if ($return_errors) { return $error; } warn_exit($error); } } if (!is_null($password)) { $prohibit_password_whitespace = get_option('prohibit_password_whitespace'); if (($prohibit_password_whitespace === '1') && (cms_preg_match_safe('#\s#', $password) != 0) && ($username_changed)) { $error = do_lang_tempcode('USERNAME_PASSWORD_WHITESPACE'); if ($return_errors) { return $error; } warn_exit($error); } } // Check against restricted usernames if ((get_page_name() != 'admin_cns_members') && ($username_changed)) { $restricted_usernames = explode(',', get_option('restricted_usernames')); $restricted_usernames[] = do_lang('GUEST'); $restricted_usernames[] = do_lang('UNKNOWN'); $restricted_usernames[] = do_lang('SYSTEM'); foreach ($restricted_usernames as $_restricted_username) { $restricted_username = trim($_restricted_username); if ($restricted_username == '') { continue; } if (strpos($username, $restricted_username) !== false) { $error = do_lang_tempcode('USERNAME_BAD_SUBSTRING', escape_html($restricted_username)); if ($return_errors) { return $error; } warn_exit($error); } } } // Check it is not numeric if (is_numeric($username)) { $error = do_lang_tempcode('USERNAME_NUMERIC'); if ($return_errors) { return $error; } warn_exit($error); } return null; } /** * Edit a member's personal title, and check validity. * * @param SHORT_TEXT $new_title The new title. * @param ?MEMBER $member_id The member (null: the current member). */ function cns_member_choose_title($new_title, $member_id = null) { if (is_null($member_id)) { $member_id = get_member(); } $old_title = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_title'); if ($old_title == $new_title) { return; } if (cms_mb_strlen($new_title) > intval(get_option('max_member_title_length'))) { warn_exit(do_lang_tempcode('MEMBER_TITLE_TOO_BIG')); } $GLOBALS['FORUM_DB']->query_update('f_members', array('m_title' => $new_title), array('id' => $member_id), '', 1); // Decache from run-time cache unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); unset($GLOBALS['MEMBER_CACHE_FIELD_MAPPINGS'][$member_id]); decache('main_members'); } /** * Edit a member's signature, and check validity. * * @param LONG_TEXT $new_signature The new signature. * @param ?MEMBER $member_id The member (null: the current member). */ function cns_member_choose_signature($new_signature, $member_id = null) { if (is_null($member_id)) { $member_id = get_member(); } $max_sig_length = cns_get_member_best_group_property($member_id, 'max_sig_length_comcode'); if (cms_mb_strlen($new_signature) > $max_sig_length) { warn_exit(make_string_tempcode(escape_html(do_lang('SIGNATURE_TOO_BIG')))); } $_signature = $GLOBALS['CNS_DRIVER']->get_member_row_field($member_id, 'm_signature'); if (get_translated_text($_signature, $GLOBALS['FORUM_DB']) == $new_signature) { return; } require_code('attachments2'); require_code('attachments3'); $map = array(); $map += update_lang_comcode_attachments('m_signature', $_signature, $new_signature, 'cns_signature', strval($member_id), $GLOBALS['FORUM_DB'], $member_id); $GLOBALS['FORUM_DB']->query_update('f_members', $map, array('id' => $member_id), '', 1); require_code('notifications'); $subject = do_lang('CHOOSE_SIGNATURE_SUBJECT', $GLOBALS['FORUM_DRIVER']->get_username($member_id, true), $GLOBALS['FORUM_DRIVER']->get_username($member_id), null, get_lang($member_id)); $body = do_notification_lang('CHOOSE_SIGNATURE_BODY', $new_signature, $GLOBALS['FORUM_DRIVER']->get_username($member_id), $GLOBALS['FORUM_DRIVER']->get_username($member_id, true), get_lang($member_id)); dispatch_notification('cns_choose_signature', null, $subject, $body, null, get_member(), 3, false, false, null, null, '', '', '', '', null, true); // Decache from run-time cache unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); unset($GLOBALS['MEMBER_CACHE_FIELD_MAPPINGS'][$member_id]); } /** * Edit a member's avatar, and check validity. * * @param URLPATH $avatar_url The new avatar URL. * @param ?MEMBER $member_id The member (null: the current member). */ function cns_member_choose_avatar($avatar_url, $member_id = null) { if (is_null($member_id)) { $member_id = get_member(); } $old = $GLOBALS['FORUM_DB']->query_select_value('f_members', 'm_avatar_url', array('id' => $member_id)); if ($old == $avatar_url) { return; } // Check it has valid dimensions if ($avatar_url != '') { require_code('images'); if (!is_image($avatar_url, true)) { $ext = get_file_extension($avatar_url); warn_exit(do_lang_tempcode('UNKNOWN_FORMAT', escape_html($ext))); } $stub = url_is_local($avatar_url) ? (get_complex_base_url($avatar_url) . '/') : ''; if (function_exists('imagetypes')) { $file_path_stub = convert_url_to_path($stub . $avatar_url); if (!is_null($file_path_stub)) { $from_file = @file_get_contents($file_path_stub); } else { $from_file = http_download_file($stub . $avatar_url, 1024 * 1024 * 4/*reasonable limit*/, false); } if (is_null($from_file)) { warn_exit(do_lang_tempcode('MISSING_RESOURCE', do_lang_tempcode('URL'))); } $test = cms_getimagesizefromstring($from_file, get_file_extension($avatar_url)); if (!$test) { warn_exit(do_lang_tempcode('CORRUPT_FILE', escape_html($avatar_url))); } list($sx, $sy) = $test; require_code('cns_groups'); $width = cns_get_member_best_group_property($member_id, 'max_avatar_width'); $height = cns_get_member_best_group_property($member_id, 'max_avatar_height'); if (($sx > $width) || ($sy > $height)) { require_code('images'); $file_path = get_custom_file_base() . '/' . rawurldecode($avatar_url); if ((!is_saveable_image($file_path)) || (!url_is_local($avatar_url))) { if ((url_is_local($avatar_url)) && (substr($avatar_url, 0, 20) == 'uploads/cns_avatars/')) { unlink($file_path); sync_file(rawurldecode($avatar_url)); } warn_exit(do_lang_tempcode('IMAGE_BAD_DIMENSIONS', strval($width) . 'x' . strval($height), strval($sx) . 'x' . strval($sy))); } convert_image($file_path, $file_path, $width, $height, -1, false, get_file_extension($file_path), true, true); } } if ((substr($avatar_url, 0, 7) != 'themes/') && (addon_installed('cns_avatars'))) { require_code('notifications'); $subject = do_lang('CHOOSE_AVATAR_SUBJECT', $GLOBALS['FORUM_DRIVER']->get_username($member_id, true), $GLOBALS['FORUM_DRIVER']->get_username($member_id), null, get_lang($member_id)); $body = do_notification_lang('CHOOSE_AVATAR_BODY', $stub . $avatar_url, $GLOBALS['FORUM_DRIVER']->get_username($member_id), $GLOBALS['FORUM_DRIVER']->get_username($member_id, true), get_lang($member_id)); dispatch_notification('cns_choose_avatar', null, $subject, $body, null, get_member(), 3, false, false, null, null, '', '', '', '', null, true); } } // Cleanup old avatar if ((url_is_local($old)) && ((substr($old, 0, 20) == 'uploads/cns_avatars/') || (substr($old, 0, 16) == 'uploads/avatars/')) && ($old != $avatar_url)) { @unlink(get_custom_file_base() . '/' . rawurldecode($old)); sync_file(rawurldecode($old)); } $GLOBALS['FORUM_DB']->query_update('f_members', array('m_avatar_url' => $avatar_url), array('id' => $member_id), '', 1); // Decache from run-time cache unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); unset($GLOBALS['MEMBER_CACHE_FIELD_MAPPINGS'][$member_id]); decache('main_friends_list'); decache('main_members'); } /** * Edit a member's photo, and check validity. * * @param ID_TEXT $param_name The identifier for the name of the posted URL field. * @param ID_TEXT $upload_name The identifier for the name of the posted upload. * @param ?MEMBER $member_id The member (null: the current member). */ function cns_member_choose_photo($param_name, $upload_name, $member_id = null) { if (is_null($member_id)) { $member_id = get_member(); } require_code('uploads'); if (((!array_key_exists($upload_name, $_FILES)) || (!is_plupload()) && (!is_uploaded_file($_FILES[$upload_name]['tmp_name'])))) { $old = $GLOBALS['FORUM_DB']->query_select_value('f_members', 'm_photo_url', array('id' => $member_id)); $x = post_param_string($param_name, ''); if (($x != '') && (url_is_local($x))) { if (!$GLOBALS['FORUM_DRIVER']->is_super_admin(get_member())) { if ($old != $x) { access_denied('ASSOCIATE_EXISTING_FILE'); } } } if ($old == $x) { return; // Not changed, bomb out as we don't want to generate a thumbnail } } // Find photo URL set_images_cleanup_pipeline_settings(IMG_RECOMPRESS_LOSSLESS, null, null, true); // Code to strip GPS $urls = get_url($param_name, $upload_name, file_exists(get_custom_file_base() . '/uploads/photos') ? 'uploads/photos' : 'uploads/cns_photos', 0, CMS_UPLOAD_IMAGE, true, 'thumb_' . $param_name, $upload_name . '2', false, true); reset_images_cleanup_pipeline_settings(); if (!(strlen($urls[0]) > 1)) { $urls[1] = ''; } if (((get_base_url() != get_forum_base_url()) || ((!empty($GLOBALS['SITE_INFO']['on_msn'])) && ($GLOBALS['SITE_INFO']['on_msn'] == '1'))) && ($urls[0] != '') && (url_is_local($urls[0]))) { $urls[0] = get_base_url() . '/' . $urls[0]; } if (((get_base_url() != get_forum_base_url()) || ((!empty($GLOBALS['SITE_INFO']['on_msn'])) && ($GLOBALS['SITE_INFO']['on_msn'] == '1'))) && ($urls[1] != '') && (url_is_local($urls[1]))) { $urls[1] = get_base_url() . '/' . $urls[1]; } // At this point in the code, we know a photo was uploaded or changed to blank. // If we don't have GD, we need them to have uploaded a thumbnail too. if (!function_exists('imagetypes')) { if (((!array_key_exists($upload_name . '2', $_FILES)) || (!is_plupload()) && (!is_uploaded_file($_FILES[$upload_name . '2']['tmp_name'])))) { $field = post_param_string('thumb_' . $param_name, ''); if (($field == '') && ($urls[0] != '')) { warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN_UPLOAD')); } if (($field != '') && (url_is_local($field)) && (!$GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()))) { $old = $GLOBALS['FORUM_DB']->query_select_value('f_members', 'm_photo_thumb_url', array('id' => $member_id)); if ($old != $field) { access_denied('ASSOCIATE_EXISTING_FILE'); } } } } cns_member_choose_photo_concrete($urls[0], $urls[1], $member_id); decache('main_members'); } /** * Edit a member's photo. * * @param URLPATH $url URL to photo. * @param URLPATH $thumb_url URL to thumbnail photo. * @param ?MEMBER $member_id The member (null: the current member). */ function cns_member_choose_photo_concrete($url, $thumb_url, $member_id = null) { if (is_null($member_id)) { $member_id = get_member(); } // Cleanup old photo $old = $GLOBALS['FORUM_DB']->query_select_value('f_members', 'm_photo_url', array('id' => $member_id)); if ($old == $url) { return; } if ((url_is_local($old)) && ((substr($old, 0, 19) == 'uploads/cns_photos/') || (substr($old, 0, 15) == 'uploads/photos/'))) { sync_file(rawurldecode($old)); @unlink(get_custom_file_base() . '/' . rawurldecode($old)); } $GLOBALS['FORUM_DB']->query_update('f_members', array('m_photo_url' => $url, 'm_photo_thumb_url' => $thumb_url), array('id' => $member_id), '', 1); require_code('notifications'); $subject = do_lang('CHOOSE_PHOTO_SUBJECT', $GLOBALS['FORUM_DRIVER']->get_username($member_id, true), $GLOBALS['FORUM_DRIVER']->get_username($member_id), null, get_lang($member_id)); $body = do_notification_lang('CHOOSE_PHOTO_BODY', $url, $thumb_url, array($GLOBALS['FORUM_DRIVER']->get_username($member_id), $GLOBALS['FORUM_DRIVER']->get_username($member_id, true)), get_lang($member_id)); dispatch_notification('cns_choose_photo', null, $subject, $body, null, get_member(), 3, false, false, null, null, '', '', '', '', null, true); // If Avatars addon not installed, use photo for it if (!addon_installed('cns_avatars')) { $avatar_url = $url; if (function_exists('imagetypes')) { $stub = url_is_local($avatar_url) ? (get_complex_base_url($avatar_url) . '/') : ''; $file_path = convert_url_to_path($stub . $avatar_url); if (!is_null($file_path)) { $new_file_path = str_replace('/cns_photos/', '/cns_avatars/', $file_path); if (!file_exists($new_file_path)) { copy($file_path, $new_file_path); fix_permissions($new_file_path); sync_file($new_file_path); } $avatar_url = str_replace('/cns_photos/', '/cns_avatars/', $avatar_url); } } cns_member_choose_avatar($avatar_url, $member_id); } // Decache from run-time cache unset($GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED[$member_id]); unset($GLOBALS['MEMBER_CACHE_FIELD_MAPPINGS'][$member_id]); decache('main_friends_list'); decache('main_members'); } /** * Update caching against a member's username. This doesn't change the username in the actual member record -- it is assumed that this will be done elsewhere. * * @param MEMBER $member_id The member ID. * @param ID_TEXT $username The new username that is being set for them. */ function update_member_username_caching($member_id, $username) { // Fix caching for usernames $to_fix = array( 'f_forums/f_cache_last_username/f_cache_last_member_id', 'f_posts/p_poster_name_if_guest/p_poster', 'f_topics/t_cache_first_username/t_cache_first_member_id', 'f_topics/t_cache_last_username/t_cache_last_member_id', 'sessions/cache_username/member_id', ); foreach ($to_fix as $fix) { list($table, $field, $updating_field) = explode('/', $fix, 3); $con = $GLOBALS[(substr($table, 0, 2) == 'f_') ? 'FORUM_DB' : 'SITE_DB']; $con->query_update($table, array($field => $username), array($updating_field => $member_id)); } } /** * Delete a custom profile field from one of the predefined templates (this is often used by importers). * * @param ID_TEXT $field The identifier of the boiler custom profile field. */ function cns_delete_boiler_custom_field($field) { require_lang('cns_special_cpf'); $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_custom_fields', 'id', array($GLOBALS['SITE_DB']->translate_field_ref('cf_name') => do_lang('DEFAULT_CPF_' . $field . '_NAME'))); if (!is_null($test)) { require_code('cns_members_action'); cns_delete_custom_field($test); } } /** * Rebuild custom profile field indices. * * @param boolean $leave_existing Whether to leave existing indexes alone (may be useful as deleting then recreating indexes can be very slow). */ function rebuild_all_cpf_indices($leave_existing = false) { $GLOBALS['NO_QUERY_LIMIT'] = true; // TODO: Change in v11 $fields = $GLOBALS['FORUM_DB']->query_select('f_custom_fields', array('id', 'cf_type'), array(), 'ORDER BY cf_required+cf_show_on_join_form DESC,cf_public_view+cf_owner_set DESC,cf_order DESC'); // TODO: Respect searchable setting in v11 if (!$leave_existing) { // Delete existing indexes foreach ($fields as $field) { $id = $field['id']; $GLOBALS['FORUM_DB']->delete_index_if_exists('f_member_custom_fields', 'field_' . strval($id)); // LEGACY $GLOBALS['FORUM_DB']->delete_index_if_exists('f_member_custom_fields', 'mcf' . strval($id)); $GLOBALS['FORUM_DB']->delete_index_if_exists('f_member_custom_fields', '#mcf_ft_' . strval($id)); } // Delete any stragglers (already deleted fields or inconsistent naming) $GLOBALS['FORUM_DB']->query_delete('db_meta_indices', array('i_table' => 'f_member_custom_fields')); if (strpos(get_db_type(), 'mysql') !== false) { $indexes = $GLOBALS['FORUM_DB']->query('SHOW INDEXES FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_member_custom_fields WHERE Column_name<>\'mf_member_id\''); foreach ($indexes as $index) { $GLOBALS['FORUM_DB']->query('DROP INDEX ' . $index['Key_name'] . ' ON ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_member_custom_fields'); } } } // Rebuild indexes require_code('cns_members_action'); foreach ($fields as $field) { $id = $field['id']; $type = $field['cf_type']; list($_type, $index) = get_cpf_storage_for($type); $okay = build_cpf_indices($id, $index, $type, $_type); if (!$okay) { // Limit was hit break; } } }