PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/sources/subs/Profile.subs.php

https://github.com/Arantor/Elkarte
PHP | 2297 lines | 1757 code | 210 blank | 330 comment | 414 complexity | 80f7cadff06881423ab3653a73ec2f4f MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. */
  16. if (!defined('ELKARTE'))
  17. die('No access...');
  18. /**
  19. * Setup the context for a page load!
  20. *
  21. * @param array $fields
  22. */
  23. function setupProfileContext($fields)
  24. {
  25. global $profile_fields, $context, $cur_profile, $smcFunc, $txt;
  26. // Make sure we have this!
  27. loadProfileFields(true);
  28. // First check for any linked sets.
  29. foreach ($profile_fields as $key => $field)
  30. if (isset($field['link_with']) && in_array($field['link_with'], $fields))
  31. $fields[] = $key;
  32. // Some default bits.
  33. $context['profile_prehtml'] = '';
  34. $context['profile_posthtml'] = '';
  35. $context['profile_javascript'] = '';
  36. $context['profile_onsubmit_javascript'] = '';
  37. $i = 0;
  38. $last_type = '';
  39. foreach ($fields as $key => $field)
  40. {
  41. if (isset($profile_fields[$field]))
  42. {
  43. // Shortcut.
  44. $cur_field = &$profile_fields[$field];
  45. // Does it have a preload and does that preload succeed?
  46. if (isset($cur_field['preload']) && !$cur_field['preload']())
  47. continue;
  48. // If this is anything but complex we need to do more cleaning!
  49. if ($cur_field['type'] != 'callback' && $cur_field['type'] != 'hidden')
  50. {
  51. if (!isset($cur_field['label']))
  52. $cur_field['label'] = isset($txt[$field]) ? $txt[$field] : $field;
  53. // Everything has a value!
  54. if (!isset($cur_field['value']))
  55. {
  56. $cur_field['value'] = isset($cur_profile[$field]) ? $cur_profile[$field] : '';
  57. }
  58. // Any input attributes?
  59. $cur_field['input_attr'] = !empty($cur_field['input_attr']) ? implode(',', $cur_field['input_attr']) : '';
  60. }
  61. // Was there an error with this field on posting?
  62. if (isset($context['profile_errors'][$field]))
  63. $cur_field['is_error'] = true;
  64. // Any javascript stuff?
  65. if (!empty($cur_field['js_submit']))
  66. $context['profile_onsubmit_javascript'] .= $cur_field['js_submit'];
  67. if (!empty($cur_field['js']))
  68. $context['profile_javascript'] .= $cur_field['js'];
  69. // Any template stuff?
  70. if (!empty($cur_field['prehtml']))
  71. $context['profile_prehtml'] .= $cur_field['prehtml'];
  72. if (!empty($cur_field['posthtml']))
  73. $context['profile_posthtml'] .= $cur_field['posthtml'];
  74. // Finally put it into context?
  75. if ($cur_field['type'] != 'hidden')
  76. {
  77. $last_type = $cur_field['type'];
  78. $context['profile_fields'][$field] = &$profile_fields[$field];
  79. }
  80. }
  81. // Bodge in a line break - without doing two in a row ;)
  82. elseif ($field == 'hr' && $last_type != 'hr' && $last_type != '')
  83. {
  84. $last_type = 'hr';
  85. $context['profile_fields'][$i++]['type'] = 'hr';
  86. }
  87. }
  88. // Free up some memory.
  89. unset($profile_fields);
  90. }
  91. /**
  92. * Load any custom fields for this area.
  93. * No area means load all, 'summary' loads all public ones.
  94. *
  95. * @param int $memID
  96. * @param string $area = 'summary'
  97. */
  98. function loadCustomFields($memID, $area = 'summary')
  99. {
  100. global $context, $txt, $user_profile, $smcFunc, $user_info, $settings, $scripturl;
  101. // Get the right restrictions in place...
  102. $where = 'active = 1';
  103. if (!allowedTo('admin_forum') && $area != 'register')
  104. {
  105. // If it's the owner they can see two types of private fields, regardless.
  106. if ($memID == $user_info['id'])
  107. $where .= $area == 'summary' ? ' AND private < 3' : ' AND (private = 0 OR private = 2)';
  108. else
  109. $where .= $area == 'summary' ? ' AND private < 2' : ' AND private = 0';
  110. }
  111. if ($area == 'register')
  112. $where .= ' AND show_reg != 0';
  113. elseif ($area != 'summary')
  114. $where .= ' AND show_profile = {string:area}';
  115. // Load all the relevant fields - and data.
  116. $request = $smcFunc['db_query']('', '
  117. SELECT
  118. col_name, field_name, field_desc, field_type, show_reg, field_length, field_options,
  119. default_value, bbc, enclose, placement
  120. FROM {db_prefix}custom_fields
  121. WHERE ' . $where,
  122. array(
  123. 'area' => $area,
  124. )
  125. );
  126. $context['custom_fields'] = array();
  127. $context['custom_fields_required'] = false;
  128. while ($row = $smcFunc['db_fetch_assoc']($request))
  129. {
  130. // Shortcut.
  131. $exists = $memID && isset($user_profile[$memID], $user_profile[$memID]['options'][$row['col_name']]);
  132. $value = $exists ? $user_profile[$memID]['options'][$row['col_name']] : '';
  133. // If this was submitted already then make the value the posted version.
  134. if (isset($_POST['customfield']) && isset($_POST['customfield'][$row['col_name']]))
  135. {
  136. $value = $smcFunc['htmlspecialchars']($_POST['customfield'][$row['col_name']]);
  137. if (in_array($row['field_type'], array('select', 'radio')))
  138. $value = ($options = explode(',', $row['field_options'])) && isset($options[$value]) ? $options[$value] : '';
  139. }
  140. // HTML for the input form.
  141. $output_html = $value;
  142. if ($row['field_type'] == 'check')
  143. {
  144. $true = (!$exists && $row['default_value']) || $value;
  145. $input_html = '<input type="checkbox" name="customfield[' . $row['col_name'] . ']" ' . ($true ? 'checked="checked"' : '') . ' class="input_check" />';
  146. $output_html = $true ? $txt['yes'] : $txt['no'];
  147. }
  148. elseif ($row['field_type'] == 'select')
  149. {
  150. $input_html = '<select name="customfield[' . $row['col_name'] . ']"><option value="-1"></option>';
  151. $options = explode(',', $row['field_options']);
  152. foreach ($options as $k => $v)
  153. {
  154. $true = (!$exists && $row['default_value'] == $v) || $value == $v;
  155. $input_html .= '<option value="' . $k . '"' . ($true ? ' selected="selected"' : '') . '>' . $v . '</option>';
  156. if ($true)
  157. $output_html = $v;
  158. }
  159. $input_html .= '</select>';
  160. }
  161. elseif ($row['field_type'] == 'radio')
  162. {
  163. $input_html = '<fieldset>';
  164. $options = explode(',', $row['field_options']);
  165. foreach ($options as $k => $v)
  166. {
  167. $true = (!$exists && $row['default_value'] == $v) || $value == $v;
  168. $input_html .= '<label for="customfield_' . $row['col_name'] . '_' . $k . '"><input type="radio" name="customfield[' . $row['col_name'] . ']" class="input_radio" id="customfield_' . $row['col_name'] . '_' . $k . '" value="' . $k . '" ' . ($true ? 'checked="checked"' : '') . ' />' . $v . '</label><br />';
  169. if ($true)
  170. $output_html = $v;
  171. }
  172. $input_html .= '</fieldset>';
  173. }
  174. elseif ($row['field_type'] == 'text')
  175. {
  176. $input_html = '<input type="text" name="customfield[' . $row['col_name'] . ']" ' . ($row['field_length'] != 0 ? 'maxlength="' . $row['field_length'] . '"' : '') . ' size="' . ($row['field_length'] == 0 || $row['field_length'] >= 50 ? 50 : ($row['field_length'] > 30 ? 30 : ($row['field_length'] > 10 ? 20 : 10))) . '" value="' . $value . '" class="input_text" />';
  177. }
  178. else
  179. {
  180. @list ($rows, $cols) = @explode(',', $row['default_value']);
  181. $input_html = '<textarea name="customfield[' . $row['col_name'] . ']" ' . (!empty($rows) ? 'rows="' . $rows . '"' : '') . ' ' . (!empty($cols) ? 'cols="' . $cols . '"' : '') . '>' . $value . '</textarea>';
  182. }
  183. // Parse BBCode
  184. if ($row['bbc'])
  185. $output_html = parse_bbc($output_html);
  186. elseif ($row['field_type'] == 'textarea')
  187. // Allow for newlines at least
  188. $output_html = strtr($output_html, array("\n" => '<br />'));
  189. // Enclosing the user input within some other text?
  190. if (!empty($row['enclose']) && !empty($output_html))
  191. $output_html = strtr($row['enclose'], array(
  192. '{SCRIPTURL}' => $scripturl,
  193. '{IMAGES_URL}' => $settings['images_url'],
  194. '{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
  195. '{INPUT}' => $output_html,
  196. ));
  197. $context['custom_fields'][] = array(
  198. 'name' => $row['field_name'],
  199. 'desc' => $row['field_desc'],
  200. 'type' => $row['field_type'],
  201. 'input_html' => $input_html,
  202. 'output_html' => $output_html,
  203. 'placement' => $row['placement'],
  204. 'colname' => $row['col_name'],
  205. 'value' => $value,
  206. 'show_reg' => $row['show_reg'],
  207. );
  208. $context['custom_fields_required'] = $context['custom_fields_required'] || $row['show_reg'];
  209. }
  210. $smcFunc['db_free_result']($request);
  211. call_integration_hook('integrate_load_custom_profile_fields', array($memID, $area));
  212. }
  213. /**
  214. * This defines every profile field known to man.
  215. *
  216. * @param bool $force_reload = false
  217. */
  218. function loadProfileFields($force_reload = false)
  219. {
  220. global $context, $profile_fields, $txt, $scripturl, $modSettings, $user_info, $old_profile, $smcFunc, $cur_profile, $language;
  221. // Don't load this twice!
  222. if (!empty($profile_fields) && !$force_reload)
  223. return;
  224. /* This horrific array defines all the profile fields in the whole world!
  225. In general each "field" has one array - the key of which is the database column name associated with said field. Each item
  226. can have the following attributes:
  227. string $type: The type of field this is - valid types are:
  228. - callback: This is a field which has its own callback mechanism for templating.
  229. - check: A simple checkbox.
  230. - hidden: This doesn't have any visual aspects but may have some validity.
  231. - password: A password box.
  232. - select: A select box.
  233. - text: A string of some description.
  234. string $label: The label for this item - default will be $txt[$key] if this isn't set.
  235. string $subtext: The subtext (Small label) for this item.
  236. int $size: Optional size for a text area.
  237. array $input_attr: An array of text strings to be added to the input box for this item.
  238. string $value: The value of the item. If not set $cur_profile[$key] is assumed.
  239. string $permission: Permission required for this item (Excluded _any/_own subfix which is applied automatically).
  240. function $input_validate: A runtime function which validates the element before going to the database. It is passed
  241. the relevant $_POST element if it exists and should be treated like a reference.
  242. Return types:
  243. - true: Element can be stored.
  244. - false: Skip this element.
  245. - a text string: An error occured - this is the error message.
  246. function $preload: A function that is used to load data required for this element to be displayed. Must return
  247. true to be displayed at all.
  248. string $cast_type: If set casts the element to a certain type. Valid types (bool, int, float).
  249. string $save_key: If the index of this element isn't the database column name it can be overriden
  250. with this string.
  251. bool $is_dummy: If set then nothing is acted upon for this element.
  252. bool $enabled: A test to determine whether this is even available - if not is unset.
  253. string $link_with: Key which links this field to an overall set.
  254. Note that all elements that have a custom input_validate must ensure they set the value of $cur_profile correct to enable
  255. the changes to be displayed correctly on submit of the form.
  256. */
  257. $profile_fields = array(
  258. 'avatar_choice' => array(
  259. 'type' => 'callback',
  260. 'callback_func' => 'avatar_select',
  261. // This handles the permissions too.
  262. 'preload' => 'profileLoadAvatarData',
  263. 'input_validate' => 'profileSaveAvatarData',
  264. 'save_key' => 'avatar',
  265. ),
  266. 'bday1' => array(
  267. 'type' => 'callback',
  268. 'callback_func' => 'birthdate',
  269. 'permission' => 'profile_extra',
  270. 'preload' => create_function('', '
  271. global $cur_profile, $context;
  272. // Split up the birthdate....
  273. list ($uyear, $umonth, $uday) = explode(\'-\', empty($cur_profile[\'birthdate\']) || $cur_profile[\'birthdate\'] == \'0001-01-01\' ? \'0000-00-00\' : $cur_profile[\'birthdate\']);
  274. $context[\'member\'][\'birth_date\'] = array(
  275. \'year\' => $uyear == \'0004\' ? \'0000\' : $uyear,
  276. \'month\' => $umonth,
  277. \'day\' => $uday,
  278. );
  279. return true;
  280. '),
  281. 'input_validate' => create_function('&$value', '
  282. global $profile_vars, $cur_profile;
  283. if (isset($_POST[\'bday2\'], $_POST[\'bday3\']) && $value > 0 && $_POST[\'bday2\'] > 0)
  284. {
  285. // Set to blank?
  286. if ((int) $_POST[\'bday3\'] == 1 && (int) $_POST[\'bday2\'] == 1 && (int) $value == 1)
  287. $value = \'0001-01-01\';
  288. else
  289. $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\';
  290. }
  291. else
  292. $value = \'0001-01-01\';
  293. $profile_vars[\'birthdate\'] = $value;
  294. $cur_profile[\'birthdate\'] = $value;
  295. return false;
  296. '),
  297. ),
  298. // Setting the birthdate the old style way?
  299. 'birthdate' => array(
  300. 'type' => 'hidden',
  301. 'permission' => 'profile_extra',
  302. 'input_validate' => create_function('&$value', '
  303. global $cur_profile;
  304. // @todo Should we check for this year and tell them they made a mistake :P? (based on coppa at least?)
  305. if (preg_match(\'/(\d{4})[\-\., ](\d{2})[\-\., ](\d{2})/\', $value, $dates) === 1)
  306. {
  307. $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\';
  308. return true;
  309. }
  310. else
  311. {
  312. $value = empty($cur_profile[\'birthdate\']) ? \'0001-01-01\' : $cur_profile[\'birthdate\'];
  313. return false;
  314. }
  315. '),
  316. ),
  317. 'date_registered' => array(
  318. 'type' => 'text',
  319. '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),
  320. 'label' => $txt['date_registered'],
  321. 'log_change' => true,
  322. 'permission' => 'moderate_forum',
  323. 'input_validate' => create_function('&$value', '
  324. global $txt, $user_info, $modSettings, $cur_profile, $context;
  325. // Bad date! Go try again - please?
  326. if (($value = strtotime($value)) === -1)
  327. {
  328. $value = $cur_profile[\'date_registered\'];
  329. 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));
  330. }
  331. // As long as it doesn\'t equal "N/A"...
  332. elseif ($value != $txt[\'not_applicable\'] && $value != strtotime(strftime(\'%Y-%m-%d\', $cur_profile[\'date_registered\'] + ($user_info[\'time_offset\'] + $modSettings[\'time_offset\']) * 3600)))
  333. $value = $value - ($user_info[\'time_offset\'] + $modSettings[\'time_offset\']) * 3600;
  334. else
  335. $value = $cur_profile[\'date_registered\'];
  336. return true;
  337. '),
  338. ),
  339. 'email_address' => array(
  340. 'type' => 'text',
  341. 'label' => $txt['user_email_address'],
  342. 'subtext' => $txt['valid_email'],
  343. 'log_change' => true,
  344. 'permission' => 'profile_identity',
  345. 'input_validate' => create_function('&$value', '
  346. global $context, $old_profile, $context, $profile_vars, $modSettings;
  347. if (strtolower($value) == strtolower($old_profile[\'email_address\']))
  348. return false;
  349. $isValid = profileValidateEmail($value, $context[\'id_member\']);
  350. // Do they need to revalidate? If so schedule the function!
  351. if ($isValid === true && !empty($modSettings[\'send_validation_onChange\']) && !allowedTo(\'moderate_forum\'))
  352. {
  353. require_once(SUBSDIR . \'/Members.subs.php\');
  354. $profile_vars[\'validation_code\'] = generateValidationCode();
  355. $profile_vars[\'is_activated\'] = 2;
  356. $context[\'profile_execute_on_save\'][] = \'profileSendActivation\';
  357. unset($context[\'profile_execute_on_save\'][\'reload_user\']);
  358. }
  359. return $isValid;
  360. '),
  361. ),
  362. 'gender' => array(
  363. 'type' => 'select',
  364. 'cast_type' => 'int',
  365. 'options' => 'return array(0 => \'\', 1 => $txt[\'male\'], 2 => $txt[\'female\']);',
  366. 'label' => $txt['gender'],
  367. 'permission' => 'profile_extra',
  368. ),
  369. 'hide_email' => array(
  370. 'type' => 'check',
  371. 'value' => empty($cur_profile['hide_email']) ? true : false,
  372. 'label' => $txt['allow_user_email'],
  373. 'permission' => 'profile_identity',
  374. 'input_validate' => create_function('&$value', '
  375. $value = $value == 0 ? 1 : 0;
  376. return true;
  377. '),
  378. ),
  379. // Selecting group membership is a complicated one so we treat it separate!
  380. 'id_group' => array(
  381. 'type' => 'callback',
  382. 'callback_func' => 'group_manage',
  383. 'permission' => 'manage_membergroups',
  384. 'preload' => 'profileLoadGroups',
  385. 'log_change' => true,
  386. 'input_validate' => 'profileSaveGroups',
  387. ),
  388. 'id_theme' => array(
  389. 'type' => 'callback',
  390. 'callback_func' => 'theme_pick',
  391. 'permission' => 'profile_extra',
  392. 'enabled' => $modSettings['theme_allow'] || allowedTo('admin_forum'),
  393. 'preload' => create_function('', '
  394. global $smcFunc, $context, $cur_profile, $txt;
  395. $request = $smcFunc[\'db_query\'](\'\', \'
  396. SELECT value
  397. FROM {db_prefix}themes
  398. WHERE id_theme = {int:id_theme}
  399. AND variable = {string:variable}
  400. LIMIT 1\', array(
  401. \'id_theme\' => $cur_profile[\'id_theme\'],
  402. \'variable\' => \'name\',
  403. )
  404. );
  405. list ($name) = $smcFunc[\'db_fetch_row\']($request);
  406. $smcFunc[\'db_free_result\']($request);
  407. $context[\'member\'][\'theme\'] = array(
  408. \'id\' => $cur_profile[\'id_theme\'],
  409. \'name\' => empty($cur_profile[\'id_theme\']) ? $txt[\'theme_forum_default\'] : $name
  410. );
  411. return true;
  412. '),
  413. 'input_validate' => create_function('&$value', '
  414. $value = (int) $value;
  415. return true;
  416. '),
  417. ),
  418. 'karma_good' => array(
  419. 'type' => 'callback',
  420. 'callback_func' => 'karma_modify',
  421. 'permission' => 'admin_forum',
  422. // Set karma_bad too!
  423. 'input_validate' => create_function('&$value', '
  424. global $profile_vars, $cur_profile;
  425. $value = (int) $value;
  426. if (isset($_POST[\'karma_bad\']))
  427. {
  428. $profile_vars[\'karma_bad\'] = $_POST[\'karma_bad\'] != \'\' ? (int) $_POST[\'karma_bad\'] : 0;
  429. $cur_profile[\'karma_bad\'] = $_POST[\'karma_bad\'] != \'\' ? (int) $_POST[\'karma_bad\'] : 0;
  430. }
  431. return true;
  432. '),
  433. 'preload' => create_function('', '
  434. global $context, $cur_profile;
  435. $context[\'member\'][\'karma\'][\'good\'] = $cur_profile[\'karma_good\'];
  436. $context[\'member\'][\'karma\'][\'bad\'] = $cur_profile[\'karma_bad\'];
  437. return true;
  438. '),
  439. 'enabled' => !empty($modSettings['karmaMode']),
  440. ),
  441. 'lngfile' => array(
  442. 'type' => 'select',
  443. 'options' => 'return $context[\'profile_languages\'];',
  444. 'label' => $txt['preferred_language'],
  445. 'permission' => 'profile_identity',
  446. 'preload' => 'profileLoadLanguages',
  447. 'enabled' => !empty($modSettings['userLanguage']),
  448. 'value' => empty($cur_profile['lngfile']) ? $language : $cur_profile['lngfile'],
  449. 'input_validate' => create_function('&$value', '
  450. global $context, $cur_profile;
  451. // Load the languages.
  452. profileLoadLanguages();
  453. if (isset($context[\'profile_languages\'][$value]))
  454. {
  455. if ($context[\'user\'][\'is_owner\'] && empty($context[\'password_auth_failed\']))
  456. $_SESSION[\'language\'] = $value;
  457. return true;
  458. }
  459. else
  460. {
  461. $value = $cur_profile[\'lngfile\'];
  462. return false;
  463. }
  464. '),
  465. ),
  466. 'location' => array(
  467. 'type' => 'text',
  468. 'label' => $txt['location'],
  469. 'log_change' => true,
  470. 'size' => 50,
  471. 'permission' => 'profile_extra',
  472. ),
  473. // The username is not always editable - so adjust it as such.
  474. 'member_name' => array(
  475. 'type' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? 'text' : 'label',
  476. 'label' => $txt['username'],
  477. '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>]' : '',
  478. 'log_change' => true,
  479. 'permission' => 'profile_identity',
  480. 'prehtml' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? '<div class="alert">' . $txt['username_warning'] . '</div>' : '',
  481. 'input_validate' => create_function('&$value', '
  482. global $context, $user_info, $cur_profile;
  483. if (allowedTo(\'admin_forum\'))
  484. {
  485. // We\'ll need this...
  486. require_once(SUBSDIR . \'/Auth.subs.php\');
  487. // Maybe they are trying to change their password as well?
  488. $resetPassword = true;
  489. 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)
  490. $resetPassword = false;
  491. // Do the reset... this will send them an email too.
  492. if ($resetPassword)
  493. resetPassword($context[\'id_member\'], $value);
  494. elseif ($value !== null)
  495. {
  496. validateUsername($context[\'id_member\'], $value);
  497. updateMemberData($context[\'id_member\'], array(\'member_name\' => $value));
  498. }
  499. }
  500. return false;
  501. '),
  502. ),
  503. 'passwrd1' => array(
  504. 'type' => 'password',
  505. 'label' => ucwords($txt['choose_pass']),
  506. 'subtext' => $txt['password_strength'],
  507. 'size' => 20,
  508. 'value' => '',
  509. 'enabled' => empty($cur_profile['openid_uri']),
  510. 'permission' => 'profile_identity',
  511. 'save_key' => 'passwd',
  512. // Note this will only work if passwrd2 also exists!
  513. 'input_validate' => create_function('&$value', '
  514. global $user_info, $smcFunc, $cur_profile;
  515. // If we didn\'t try it then ignore it!
  516. if ($value == \'\')
  517. return false;
  518. // Do the two entries for the password even match?
  519. if (!isset($_POST[\'passwrd2\']) || $value != $_POST[\'passwrd2\'])
  520. return \'bad_new_password\';
  521. // Let\'s get the validation function into play...
  522. require_once(SUBSDIR . \'/Auth.subs.php\');
  523. $passwordErrors = validatePassword($value, $cur_profile[\'member_name\'], array($cur_profile[\'real_name\'], $user_info[\'username\'], $user_info[\'name\'], $user_info[\'email\']));
  524. // Were there errors?
  525. if ($passwordErrors != null)
  526. return \'password_\' . $passwordErrors;
  527. // Set up the new password variable... ready for storage.
  528. $value = sha1(strtolower($cur_profile[\'member_name\']) . un_htmlspecialchars($value));
  529. return true;
  530. '),
  531. ),
  532. 'passwrd2' => array(
  533. 'type' => 'password',
  534. 'label' => ucwords($txt['verify_pass']),
  535. 'enabled' => empty($cur_profile['openid_uri']),
  536. 'size' => 20,
  537. 'value' => '',
  538. 'permission' => 'profile_identity',
  539. 'is_dummy' => true,
  540. ),
  541. 'personal_text' => array(
  542. 'type' => 'text',
  543. 'label' => $txt['personal_text'],
  544. 'log_change' => true,
  545. 'input_attr' => array('maxlength="50"'),
  546. 'size' => 50,
  547. 'permission' => 'profile_extra',
  548. 'input_validate' => create_function('&$value', '
  549. global $smcFunc;
  550. if ($smcFunc[\'strlen\']($value) > 50)
  551. return \'personal_text_too_long\';
  552. return true;
  553. '),
  554. ),
  555. // This does ALL the pm settings
  556. 'pm_prefs' => array(
  557. 'type' => 'callback',
  558. 'callback_func' => 'pm_settings',
  559. 'permission' => 'pm_read',
  560. 'preload' => create_function('', '
  561. global $context, $cur_profile;
  562. $context[\'display_mode\'] = $cur_profile[\'pm_prefs\'] & 3;
  563. $context[\'send_email\'] = $cur_profile[\'pm_email_notify\'];
  564. $context[\'receive_from\'] = !empty($cur_profile[\'pm_receive_from\']) ? $cur_profile[\'pm_receive_from\'] : 0;
  565. return true;
  566. '),
  567. 'input_validate' => create_function('&$value', '
  568. global $cur_profile, $profile_vars;
  569. // Simple validate and apply the two "sub settings"
  570. $value = max(min($value, 2), 0);
  571. $cur_profile[\'pm_email_notify\'] = $profile_vars[\'pm_email_notify\'] = max(min((int) $_POST[\'pm_email_notify\'], 2), 0);
  572. $cur_profile[\'pm_receive_from\'] = $profile_vars[\'pm_receive_from\'] = max(min((int) $_POST[\'pm_receive_from\'], 4), 0);
  573. return true;
  574. '),
  575. ),
  576. 'posts' => array(
  577. 'type' => 'int',
  578. 'label' => $txt['profile_posts'],
  579. 'log_change' => true,
  580. 'size' => 7,
  581. 'permission' => 'moderate_forum',
  582. 'input_validate' => create_function('&$value', '
  583. if (!is_numeric($value))
  584. return \'digits_only\';
  585. else
  586. $value = $value != \'\' ? strtr($value, array(\',\' => \'\', \'.\' => \'\', \' \' => \'\')) : 0;
  587. return true;
  588. '),
  589. ),
  590. 'real_name' => array(
  591. 'type' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum') ? 'text' : 'label',
  592. 'label' => $txt['name'],
  593. 'subtext' => $txt['display_name_desc'],
  594. 'log_change' => true,
  595. 'input_attr' => array('maxlength="60"'),
  596. 'permission' => 'profile_identity',
  597. 'enabled' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'),
  598. 'input_validate' => create_function('&$value', '
  599. global $context, $smcFunc, $cur_profile;
  600. $value = trim(preg_replace(\'~[\s]~u\', \' \', $value));
  601. if (trim($value) == \'\')
  602. return \'no_name\';
  603. elseif ($smcFunc[\'strlen\']($value) > 60)
  604. return \'name_too_long\';
  605. elseif ($cur_profile[\'real_name\'] != $value)
  606. {
  607. require_once(SUBSDIR . \'/Members.subs.php\');
  608. if (isReservedName($value, $context[\'id_member\']))
  609. return \'name_taken\';
  610. }
  611. return true;
  612. '),
  613. ),
  614. 'secret_question' => array(
  615. 'type' => 'text',
  616. 'label' => $txt['secret_question'],
  617. 'subtext' => $txt['secret_desc'],
  618. 'size' => 50,
  619. 'permission' => 'profile_identity',
  620. ),
  621. 'secret_answer' => array(
  622. 'type' => 'text',
  623. 'label' => $txt['secret_answer'],
  624. 'subtext' => $txt['secret_desc2'],
  625. 'size' => 20,
  626. 'postinput' => '<span class="smalltext" style="margin-left: 4ex;">[<a href="' . $scripturl . '?action=quickhelp;help=secret_why_blank" onclick="return reqOverlayDiv(this.href);">' . $txt['secret_why_blank'] . '</a>]</span>',
  627. 'value' => '',
  628. 'permission' => 'profile_identity',
  629. 'input_validate' => create_function('&$value', '
  630. $value = $value != \'\' ? md5($value) : \'\';
  631. return true;
  632. '),
  633. ),
  634. 'signature' => array(
  635. 'type' => 'callback',
  636. 'callback_func' => 'signature_modify',
  637. 'permission' => 'profile_extra',
  638. 'enabled' => substr($modSettings['signature_settings'], 0, 1) == 1,
  639. 'preload' => 'profileLoadSignatureData',
  640. 'input_validate' => 'profileValidateSignature',
  641. ),
  642. 'show_online' => array(
  643. 'type' => 'check',
  644. 'label' => $txt['show_online'],
  645. 'permission' => 'profile_identity',
  646. 'enabled' => !empty($modSettings['allow_hideOnline']) || allowedTo('moderate_forum'),
  647. ),
  648. 'smiley_set' => array(
  649. 'type' => 'callback',
  650. 'callback_func' => 'smiley_pick',
  651. 'enabled' => !empty($modSettings['smiley_sets_enable']),
  652. 'permission' => 'profile_extra',
  653. 'preload' => create_function('', '
  654. global $modSettings, $context, $txt, $cur_profile;
  655. $context[\'member\'][\'smiley_set\'][\'id\'] = empty($cur_profile[\'smiley_set\']) ? \'\' : $cur_profile[\'smiley_set\'];
  656. $context[\'smiley_sets\'] = explode(\',\', \'none,,\' . $modSettings[\'smiley_sets_known\']);
  657. $set_names = explode("\n", $txt[\'smileys_none\'] . "\n" . $txt[\'smileys_forum_board_default\'] . "\n" . $modSettings[\'smiley_sets_names\']);
  658. foreach ($context[\'smiley_sets\'] as $i => $set)
  659. {
  660. $context[\'smiley_sets\'][$i] = array(
  661. \'id\' => htmlspecialchars($set),
  662. \'name\' => htmlspecialchars($set_names[$i]),
  663. \'selected\' => $set == $context[\'member\'][\'smiley_set\'][\'id\']
  664. );
  665. if ($context[\'smiley_sets\'][$i][\'selected\'])
  666. $context[\'member\'][\'smiley_set\'][\'name\'] = $set_names[$i];
  667. }
  668. return true;
  669. '),
  670. 'input_validate' => create_function('&$value', '
  671. global $modSettings;
  672. $smiley_sets = explode(\',\', $modSettings[\'smiley_sets_known\']);
  673. if (!in_array($value, $smiley_sets) && $value != \'none\')
  674. $value = \'\';
  675. return true;
  676. '),
  677. ),
  678. // Pretty much a dummy entry - it populates all the theme settings.
  679. 'theme_settings' => array(
  680. 'type' => 'callback',
  681. 'callback_func' => 'theme_settings',
  682. 'permission' => 'profile_extra',
  683. 'is_dummy' => true,
  684. 'preload' => create_function('', '
  685. global $context, $user_info;
  686. loadLanguage(\'Settings\');
  687. $context[\'allow_no_censored\'] = false;
  688. if ($user_info[\'is_admin\'] || $context[\'user\'][\'is_owner\'])
  689. $context[\'allow_no_censored\'] = allowedTo(\'disable_censor\');
  690. return true;
  691. '),
  692. ),
  693. 'time_format' => array(
  694. 'type' => 'callback',
  695. 'callback_func' => 'timeformat_modify',
  696. 'permission' => 'profile_extra',
  697. 'preload' => create_function('', '
  698. global $context, $user_info, $txt, $cur_profile, $modSettings;
  699. $context[\'easy_timeformats\'] = array(
  700. array(\'format\' => \'\', \'title\' => $txt[\'timeformat_default\']),
  701. array(\'format\' => \'%B %d, %Y, %I:%M:%S %p\', \'title\' => $txt[\'timeformat_easy1\']),
  702. array(\'format\' => \'%B %d, %Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy2\']),
  703. array(\'format\' => \'%Y-%m-%d, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy3\']),
  704. array(\'format\' => \'%d %B %Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy4\']),
  705. array(\'format\' => \'%d-%m-%Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy5\'])
  706. );
  707. $context[\'member\'][\'time_format\'] = $cur_profile[\'time_format\'];
  708. $context[\'current_forum_time\'] = timeformat(time() - $user_info[\'time_offset\'] * 3600, false);
  709. $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);
  710. $context[\'current_forum_time_hour\'] = (int) strftime(\'%H\', forum_time(false));
  711. return true;
  712. '),
  713. ),
  714. 'time_offset' => array(
  715. 'type' => 'callback',
  716. 'callback_func' => 'timeoffset_modify',
  717. 'permission' => 'profile_extra',
  718. 'preload' => create_function('', '
  719. global $context, $cur_profile;
  720. $context[\'member\'][\'time_offset\'] = $cur_profile[\'time_offset\'];
  721. return true;
  722. '),
  723. 'input_validate' => create_function('&$value', '
  724. // Validate the time_offset...
  725. $value = (float) strtr($value, \',\', \'.\');
  726. if ($value < -23.5 || $value > 23.5)
  727. return \'bad_offset\';
  728. return true;
  729. '),
  730. ),
  731. 'usertitle' => array(
  732. 'type' => 'text',
  733. 'label' => $txt['custom_title'],
  734. 'log_change' => true,
  735. 'input_attr' => array('maxlength="50"'),
  736. 'size' => 50,
  737. 'permission' => 'profile_title',
  738. 'enabled' => !empty($modSettings['titlesEnable']),
  739. 'input_validate' => create_function('&$value', '
  740. global $smcFunc;
  741. if ($smcFunc[\'strlen\'] > 50)
  742. return \'user_title_too_long\';
  743. return true;
  744. '),
  745. ),
  746. 'website_title' => array(
  747. 'type' => 'text',
  748. 'label' => $txt['website_title'],
  749. 'subtext' => $txt['include_website_url'],
  750. 'size' => 50,
  751. 'permission' => 'profile_extra',
  752. 'link_with' => 'website',
  753. ),
  754. 'website_url' => array(
  755. 'type' => 'text',
  756. 'label' => $txt['website_url'],
  757. 'subtext' => $txt['complete_url'],
  758. 'size' => 50,
  759. 'permission' => 'profile_extra',
  760. // Fix the URL...
  761. 'input_validate' => create_function('&$value', '
  762. if (strlen(trim($value)) > 0 && strpos($value, \'://\') === false)
  763. $value = \'http://\' . $value;
  764. if (strlen($value) < 8 || (substr($value, 0, 7) !== \'http://\' && substr($value, 0, 8) !== \'https://\'))
  765. $value = \'\';
  766. return true;
  767. '),
  768. 'link_with' => 'website',
  769. ),
  770. );
  771. call_integration_hook('integrate_load_profile_fields', array($profile_fields));
  772. $disabled_fields = !empty($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
  773. // For each of the above let's take out the bits which don't apply - to save memory and security!
  774. foreach ($profile_fields as $key => $field)
  775. {
  776. // Do we have permission to do this?
  777. if (isset($field['permission']) && !allowedTo(($context['user']['is_owner'] ? array($field['permission'] . '_own', $field['permission'] . '_any') : $field['permission'] . '_any')) && !allowedTo($field['permission']))
  778. unset($profile_fields[$key]);
  779. // Is it enabled?
  780. if (isset($field['enabled']) && !$field['enabled'])
  781. unset($profile_fields[$key]);
  782. // Is it specifically disabled?
  783. if (in_array($key, $disabled_fields) || (isset($field['link_with']) && in_array($field['link_with'], $disabled_fields)))
  784. unset($profile_fields[$key]);
  785. }
  786. }
  787. /**
  788. * Save the profile changes.
  789. */
  790. function saveProfileFields()
  791. {
  792. global $profile_fields, $profile_vars, $context, $old_profile;
  793. global $post_errors, $modSettings, $cur_profile, $smcFunc;
  794. // Load them up.
  795. loadProfileFields();
  796. // This makes things easier...
  797. $old_profile = $cur_profile;
  798. // This allows variables to call activities when they save
  799. // - by default just to reload their settings
  800. $context['profile_execute_on_save'] = array();
  801. if ($context['user']['is_owner'])
  802. $context['profile_execute_on_save']['reload_user'] = 'profileReloadUser';
  803. // Assume we log nothing.
  804. $context['log_changes'] = array();
  805. // Cycle through the profile fields working out what to do!
  806. foreach ($profile_fields as $key => $field)
  807. {
  808. if (!isset($_POST[$key]) || !empty($field['is_dummy']) || (isset($_POST['preview_signature']) && $key == 'signature'))
  809. continue;
  810. // What gets updated?
  811. $db_key = isset($field['save_key']) ? $field['save_key'] : $key;
  812. // Right - we have something that is enabled, we can act upon and has a value posted to it. Does it have a validation function?
  813. if (isset($field['input_validate']))
  814. {
  815. $is_valid = $field['input_validate']($_POST[$key]);
  816. // An error occured - set it as such!
  817. if ($is_valid !== true)
  818. {
  819. // Is this an actual error?
  820. if ($is_valid !== false)
  821. {
  822. $post_errors[$key] = $is_valid;
  823. $profile_fields[$key]['is_error'] = $is_valid;
  824. }
  825. // Retain the old value.
  826. $cur_profile[$key] = $_POST[$key];
  827. continue;
  828. }
  829. }
  830. // Are we doing a cast?
  831. $field['cast_type'] = empty($field['cast_type']) ? $field['type'] : $field['cast_type'];
  832. // Finally, clean up certain types.
  833. if ($field['cast_type'] == 'int')
  834. $_POST[$key] = (int) $_POST[$key];
  835. elseif ($field['cast_type'] == 'float')
  836. $_POST[$key] = (float) $_POST[$key];
  837. elseif ($field['cast_type'] == 'check')
  838. $_POST[$key] = !empty($_POST[$key]) ? 1 : 0;
  839. // If we got here we're doing OK.
  840. if ($field['type'] != 'hidden' && (!isset($old_profile[$key]) || $_POST[$key] != $old_profile[$key]))
  841. {
  842. // Set the save variable.
  843. $profile_vars[$db_key] = $_POST[$key];
  844. // And update the user profile.
  845. $cur_profile[$key] = $_POST[$key];
  846. // Are we logging it?
  847. if (!empty($field['log_change']) && isset($old_profile[$key]))
  848. $context['log_changes'][$key] = array(
  849. 'previous' => $old_profile[$key],
  850. 'new' => $_POST[$key],
  851. );
  852. }
  853. // Logging group changes are a bit different...
  854. if ($key == 'id_group' && $field['log_change'])
  855. {
  856. profileLoadGroups();
  857. // Any changes to primary group?
  858. if ($_POST['id_group'] != $old_profile['id_group'])
  859. {
  860. $context['log_changes']['id_group'] = array(
  861. 'previous' => !empty($old_profile[$key]) && isset($context['member_groups'][$old_profile[$key]]) ? $context['member_groups'][$old_profile[$key]]['name'] : '',
  862. 'new' => !empty($_POST[$key]) && isset($context['member_groups'][$_POST[$key]]) ? $context['member_groups'][$_POST[$key]]['name'] : '',
  863. );
  864. }
  865. // Prepare additional groups for comparison.
  866. $additional_groups = array(
  867. 'previous' => !empty($old_profile['additional_groups']) ? explode(',', $old_profile['additional_groups']) : array(),
  868. 'new' => !empty($_POST['additional_groups']) ? array_diff($_POST['additional_groups'], array(0)) : array(),
  869. );
  870. sort($additional_groups['previous']);
  871. sort($additional_groups['new']);
  872. // What about additional groups?
  873. if ($additional_groups['previous'] != $additional_groups['new'])
  874. {
  875. foreach ($additional_groups as $type => $groups)
  876. {
  877. foreach ($groups as $id => $group)
  878. {
  879. if (isset($context['member_groups'][$group]))
  880. $additional_groups[$type][$id] = $context['member_groups'][$group]['name'];
  881. else
  882. unset($additional_groups[$type][$id]);
  883. }
  884. $additional_groups[$type] = implode(', ', $additional_groups[$type]);
  885. }
  886. $context['log_changes']['additional_groups'] = $additional_groups;
  887. }
  888. }
  889. }
  890. // @todo Temporary
  891. if ($context['user']['is_owner'])
  892. $changeOther = allowedTo(array('profile_extra_any', 'profile_extra_own'));
  893. else
  894. $changeOther = allowedTo('profile_extra_any');
  895. if ($changeOther && empty($post_errors))
  896. {
  897. makeThemeChanges($context['id_member'], isset($_POST['id_theme']) ? (int) $_POST['id_theme'] : $old_profile['id_theme']);
  898. if (!empty($_REQUEST['sa']))
  899. makeCustomFieldChanges($context['id_member'], $_REQUEST['sa'], false);
  900. }
  901. // Free memory!
  902. unset($profile_fields);
  903. }
  904. /**
  905. * Validate an email address.
  906. *
  907. * @param string $email
  908. * @param int $memID = 0
  909. * @return boolean|string
  910. */
  911. function profileValidateEmail($email, $memID = 0)
  912. {
  913. global $smcFunc, $context;
  914. $email = strtr($email, array('&#039;' => '\''));
  915. // Check the name and email for validity.
  916. if (trim($email) == '')
  917. return 'no_email';
  918. if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $email) == 0)
  919. return 'bad_email';
  920. // Email addresses should be and stay unique.
  921. $request = $smcFunc['db_query']('', '
  922. SELECT id_member
  923. FROM {db_prefix}members
  924. WHERE ' . ($memID != 0 ? 'id_member != {int:selected_member} AND ' : '') . '
  925. email_address = {string:email_address}
  926. LIMIT 1',
  927. array(
  928. 'selected_member' => $memID,
  929. 'email_address' => $email,
  930. )
  931. );
  932. if ($smcFunc['db_num_rows']($request) > 0)
  933. return 'email_taken';
  934. $smcFunc['db_free_result']($request);
  935. return true;
  936. }
  937. /**
  938. * Save the profile changes
  939. *
  940. * @param array &$profile_variables
  941. * @param array &$post_errors
  942. * @param int $memID id_member
  943. */
  944. function saveProfileChanges(&$profile_vars, &$post_errors, $memID)
  945. {
  946. global $user_info, $txt, $modSettings, $user_profile;
  947. global $context, $settings;
  948. global $smcFunc;
  949. // These make life easier....
  950. $old_profile = &$user_profile[$memID];
  951. // Permissions...
  952. if ($context['user']['is_owner'])
  953. {
  954. $changeIdentity = allowedTo(array('profile_identity_any', 'profile_identity_own'));
  955. $changeOther = allowedTo(array('profile_extra_any', 'profile_extra_own'));
  956. }
  957. else
  958. {
  959. $changeIdentity = allowedTo('profile_identity_any');
  960. $changeOther = allowedTo('profile_extra_any');
  961. }
  962. // Arrays of all the changes - makes things easier.
  963. $profile_bools = array(
  964. 'notify_announcements', 'notify_send_body',
  965. );
  966. $profile_ints = array(
  967. 'notify_regularity',
  968. 'notify_types',
  969. );
  970. $profile_floats = array(
  971. );
  972. $profile_strings = array(
  973. 'buddy_list',
  974. 'ignore_boards',
  975. );
  976. if (isset($_POST['sa']) && $_POST['sa'] == 'ignoreboards' && empty($_POST['ignore_brd']))
  977. $_POST['ignore_brd'] = array();
  978. unset($_POST['ignore_boards']); // Whatever it is set to is a dirty fithy thing. Kinda like our minds.
  979. if (isset($_POST['ignore_brd']))
  980. {
  981. if (!is_array($_POST['ignore_brd']))
  982. $_POST['ignore_brd'] = array ($_POST['ignore_brd']);
  983. foreach ($_POST['ignore_brd'] as $k => $d)
  984. {
  985. $d = (int) $d;
  986. if ($d != 0)
  987. $_POST['ignore_brd'][$k] = $d;
  988. else
  989. unset($_POST['ignore_brd'][$k]);
  990. }
  991. $_POST['ignore_boards'] = implode(',', $_POST['ignore_brd']);
  992. unset($_POST['ignore_brd']);
  993. }
  994. // Here's where we sort out all the 'other' values...
  995. if ($changeOther)
  996. {
  997. makeThemeChanges($memID, isset($_POST['id_theme']) ? (int) $_POST['id_theme'] : $old_profile['id_theme']);
  998. //makeAvatarChanges($memID, $post_errors);
  999. makeNotificationChanges($memID);
  1000. if (!empty($_REQUEST['sa']))
  1001. makeCustomFieldChanges($memID, $_REQUEST['sa'], false);
  1002. foreach ($profile_bools as $var)
  1003. if (isset($_POST[$var]))
  1004. $profile_vars[$var] = empty($_POST[$var]) ? '0' : '1';
  1005. foreach ($profile_ints as $var)
  1006. if (isset($_POST[$var]))
  1007. $profile_vars[$var] = $_POST[$var] != '' ? (int) $_POST[$var] : '';
  1008. foreach ($profile_floats as $var)
  1009. if (isset($_POST[$var]))
  1010. $profile_vars[$var] = (float) $_POST[$var];
  1011. foreach ($profile_strings as $var)
  1012. if (isset($_POST[$var]))
  1013. $profile_vars[$var] = $_POST[$var];
  1014. }
  1015. }
  1016. /**
  1017. * Make any theme changes that are sent with the profile.
  1018. *
  1019. * @param int $memID
  1020. * @param int $id_theme
  1021. */
  1022. function makeThemeChanges($memID, $id_theme)
  1023. {
  1024. global $modSettings, $smcFunc, $context, $user_info;
  1025. $reservedVars = array(
  1026. 'actual_theme_url',
  1027. 'actual_images_url',
  1028. 'base_theme_dir',
  1029. 'base_theme_url',
  1030. 'default_images_url',
  1031. 'default_theme_dir',
  1032. 'default_theme_url',
  1033. 'default_template',
  1034. 'images_url',
  1035. 'number_recent_posts',
  1036. 'smiley_sets_default',
  1037. 'theme_dir',
  1038. 'theme_id',
  1039. 'theme_layers',
  1040. 'theme_templates',
  1041. 'theme_url',
  1042. );
  1043. // Can't change reserved vars.
  1044. if ((isset($_POST['options']) && count(array_intersect(array_keys($_POST['options']), $reservedVars)) != 0) || (isset($_POST['default_options']) && count(array_intersect(array_keys($_POST['default_options']), $reservedVars)) != 0))
  1045. fatal_lang_error('no_access', false);
  1046. // Don't allow any overriding of custom fields with default or non-default options.
  1047. $request = $smcFunc['db_query']('', '
  1048. SELECT col_name
  1049. FROM {db_prefix}custom_fields
  1050. WHERE active = {int:is_active}',
  1051. array(
  1052. 'is_active' => 1,
  1053. )
  1054. );
  1055. $custom_fields = array();
  1056. while ($row = $smcFunc['db_fetch_assoc']($request))
  1057. $custom_fields[] = $row['col_name'];
  1058. $smcFunc['db_free_result']($request);
  1059. // These are the theme changes...
  1060. $themeSetArray = array();
  1061. if (isset($_POST['options']) && is_array($_POST['options']))
  1062. {
  1063. foreach ($_POST['options'] as $opt => $val)
  1064. {
  1065. if (in_array($opt, $custom_fields))
  1066. continue;
  1067. // These need to be controlled.
  1068. if ($opt == 'topics_per_page' || $opt == 'messages_per_page')
  1069. $val = max(0, min($val, 50));
  1070. // We don't set this per theme anymore.
  1071. elseif ($opt == 'allow_no_censored')
  1072. continue;
  1073. $themeSetArray[] = array($memID, $id_theme, $opt, is_array($val) ? implode(',', $val) : $val);
  1074. }
  1075. }
  1076. $erase_options = array();
  1077. if (isset($_POST['default_options']) && is_array($_POST['default_options']))
  1078. foreach ($_POST['default_options'] as $opt => $val)
  1079. {
  1080. if (in_array($opt, $custom_fields))
  1081. continue;
  1082. // These need to be controlled.
  1083. if ($opt == 'topics_per_page' || $opt == 'messages_per_page')
  1084. $val = max(0, min($val, 50));
  1085. // Only let admins and owners change the censor.
  1086. elseif ($opt == 'allow_no_censored' && !$user_info['is_admin'] && !$context['user']['is_owner'])
  1087. continue;
  1088. $themeSetArray[] = array($memID, 1, $opt, is_array($val) ? implode(',', $val) : $val);
  1089. $erase_options[] = $opt;
  1090. }
  1091. // If themeSetArray isn't still empty, send it to the database.
  1092. if (empty($context['password_auth_failed']))
  1093. {
  1094. if (!empty($themeSetArray))
  1095. {
  1096. $smcFunc['db_insert']('replace',
  1097. '{db_prefix}themes',
  1098. array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  1099. $themeSetArray,
  1100. array('id_member', 'id_theme', 'variable')
  1101. );
  1102. }
  1103. if (!empty($erase_options))
  1104. {
  1105. $smcFunc['db_query']('', '
  1106. DELETE FROM {db_prefix}themes
  1107. WHERE id_theme != {int:id_theme}
  1108. AND variable IN ({array_string:erase_variables})
  1109. AND id_member = {int:id_member}',
  1110. array(
  1111. 'id_theme' => 1,
  1112. 'id_member' => $memID,
  1113. 'erase_variables' => $erase_options
  1114. )
  1115. );
  1116. }
  1117. $themes = explode(',', $modSettings['knownThemes']);
  1118. foreach ($themes as $t)
  1119. cache_put_data('theme_settings-' . $t . ':' . $memID, null, 60);
  1120. }
  1121. }
  1122. /**
  1123. * Make any notification changes that need to be made.
  1124. *
  1125. * @param int $memID id_member
  1126. */
  1127. function makeNotificationChanges($memID)
  1128. {
  1129. global $smcFunc;
  1130. // Update the boards they are being notified on.
  1131. if (isset($_POST['edit_notify_boards']) && !empty($_POST['notify_boards']))
  1132. {
  1133. // Make sure only integers are deleted.
  1134. foreach ($_POST['notify_boards'] as $index => $id)
  1135. $_POST['notify_boards'][$index] = (int) $id;
  1136. // id_board = 0 is reserved for topic notifications.
  1137. $_POST['notify_boards'] = array_diff($_POST['notify_boards'], array(0));
  1138. $smcFunc['db_query']('', '
  1139. DELETE FROM {db_prefix}log_notify
  1140. WHERE id_board IN ({array_int:board_list})
  1141. AND id_member = {int:selected_member}',
  1142. array(
  1143. 'board_list' => $_POST['notify_boards'],
  1144. 'selected_member' => $memID,
  1145. )
  1146. );
  1147. }
  1148. // We are editing topic notifications......
  1149. elseif (isset($_POST['edit_notify_topics']) && !empty($_POST['notify_topics']))
  1150. {
  1151. foreach ($_POST['notify_topics'] as $index => $id)
  1152. $_POST['notify_topics'][$index] = (int) $id;
  1153. // Make sure there are no zeros left.
  1154. $_POST['notify_topics'] = array_diff($_POST['notify_topics'], array(0));
  1155. $smcFunc['db_query']('', '
  1156. DELETE FROM {db_prefix}log_notify
  1157. WHERE id_topic IN ({array_int:topic_list})
  1158. AND id_member = {int:selected_member}',
  1159. array(
  1160. 'topic_list' => $_POST['notify_topics'],
  1161. 'selected_member' => $memID,
  1162. )
  1163. );
  1164. }
  1165. }
  1166. /**
  1167. * Save any changes to the custom profile fields
  1168. *
  1169. * @param int $memID
  1170. * @param string $area
  1171. * @param bool $sanitize = true
  1172. */
  1173. function makeCustomFieldChanges($memID, $area, $sanitize = true)
  1174. {
  1175. global $context, $smcFunc, $user_profile, $user_info, $modSettings;
  1176. if ($sanitize && isset($_POST['customfield']))
  1177. $_POST['customfield'] = htmlspecialchars__recursive($_POST['customfield']);
  1178. $where = $area == 'register' ? 'show_reg != 0' : 'show_profile = {string:area}';
  1179. // Load the fields we are saving too - make sure we save valid data (etc).
  1180. $request = $smcFunc['db_query']('', '
  1181. SELECT col_name, field_name, field_desc, field_type, field_length, field_options, default_value, show_reg, mask, private
  1182. FROM {db_prefix}custom_fields
  1183. WHERE ' . $where . '
  1184. AND active = {int:is_active}',
  1185. array(
  1186. 'is_active' => 1,
  1187. 'area' => $area,
  1188. )
  1189. );
  1190. $changes = array();
  1191. $log_changes = array();
  1192. while ($row = $smcFunc['db_fetch_assoc']($request))
  1193. {
  1194. /* This means don't save if:
  1195. - The user is NOT an admin.
  1196. - The data is not freely viewable and editable by users.
  1197. - The data is not invisible to users but editable by the owner (or if it is the user is not the owner)
  1198. - The area isn't registration, and if it is that the field is not suppossed to be shown there.
  1199. */
  1200. if ($row['private'] != 0 && !allowedTo('admin_forum') && ($memID != $user_info['id'] || $row['private'] != 2) && ($area != 'register' || $row['show_reg'] == 0))
  1201. continue;
  1202. // Validate the user data.
  1203. if ($row['field_type'] == 'check')
  1204. $value = isset($_POST['customfield'][$row['col_name']]) ? 1 : 0;
  1205. elseif ($row['field_type'] == 'select' || $row['field_type'] == 'radio')
  1206. {
  1207. $value = $row['default_value'];
  1208. foreach (explode(',', $row['field_options']) as $k => $v)
  1209. if (isset($_POST['customfield'][$row['col_name']]) && $_POST['customfield'][$row['col_name']] == $k)
  1210. $value = $v;
  1211. }
  1212. // Otherwise some form of text!
  1213. else
  1214. {
  1215. $value = isset($_POST['customfield'][$row['col_name']]) ? $_POST['customfield'][$row['col_name']] : '';
  1216. if ($row['field_length'])
  1217. $value = $smcFunc['substr']($value, 0, $row['field_length']);
  1218. // Any masks?
  1219. if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none')
  1220. {
  1221. // @todo We never error on this - just ignore it at the moment...
  1222. if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $value) === 0 || strlen($value) > 255))
  1223. $value = '';
  1224. elseif ($row['mask'] == 'number')
  1225. {
  1226. $value = (int) $value;
  1227. }
  1228. elseif (substr($row['mask'], 0, 5) == 'regex' && preg_match(substr($row['mask'], 5), $value) === 0)
  1229. $value = '';
  1230. }
  1231. }
  1232. // Did it change?
  1233. if (!isset($user_profile[$memID]['options'][$row['col_name']]) || $user_profile[$memID]['options'][$row['col_name']] != $value)
  1234. {
  1235. $log_changes[] = array(
  1236. 'action' => 'customfield_' . $row['col_name'],
  1237. 'log_type' => 'user',
  1238. 'extra' => array(
  1239. 'previous' => !empty($user_profile[$memID]['options'][$row['col_name']]) ? $user_profile[$memID]['options'][$row['col_name']] : '',
  1240. 'new' => $value,
  1241. 'applicator' => $user_info['id'],
  1242. 'member_affected' => $memID,
  1243. ),
  1244. );
  1245. $changes[] = array(1, $row['col_name'], $value, $memID);
  1246. $user_profile[$memID]['options'][$row['col_name']] = $value;
  1247. }
  1248. }
  1249. $smcFunc['db_free_result']($request);
  1250. call_integration_hook('integrate_save_custom_profile_fields', array($changes, $log_changes, $memID, $area, $sanitize));
  1251. // Make those changes!
  1252. if (!empty($changes) && empty($context['password_auth_failed']))
  1253. {
  1254. $smcFunc['db_insert']('replace',
  1255. '{db_prefix}themes',
  1256. array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534', 'id_member' => 'int'),
  1257. $changes,
  1258. array('id_theme', 'variable', 'id_member')
  1259. );
  1260. if (!empty($log_changes) && !empty($modSettings['modlog_enabled']))
  1261. {
  1262. require_once(SOURCEDIR . '/Logging.php');
  1263. logActions($log_changes);
  1264. }
  1265. }
  1266. }
  1267. /**
  1268. * Send the user a new activation email if they need to reactivate!
  1269. */
  1270. function profileSendActivation()
  1271. {
  1272. global $profile_vars, $txt, $context, $scripturl, $smcFunc, $cookiename, $cur_profile, $language, $modSettings;
  1273. require_once(SUBSDIR . '/Mail.subs.php');
  1274. // Shouldn't happen but just in case.
  1275. if (empty($profile_vars['email_address']))
  1276. return;
  1277. $replacements = array(
  1278. 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $context['id_member'] . ';code=' . $profile_vars['validation_code'],
  1279. 'ACTIVATIONCODE' => $profile_vars['validation_code'],
  1280. 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $context['id_member'],
  1281. );
  1282. // Send off the email.
  1283. $emaildata = loadEmailTemplate('activate_reactivate', $replacements, empty($cur_profile['lngfile']) || empty($modSettings['userLanguage']) ? $language : $cur_profile['lngfile']);
  1284. sendmail($profile_vars['email_address'], $emaildata['subject'], $emaildata['body'],

Large files files are truncated, but you can click here to view the full file