PageRenderTime 60ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 1ms

/php/Sources/Profile-Modify.php

https://github.com/dekoza/openshift-smf-2.0.7
PHP | 3428 lines | 2652 code | 338 blank | 438 comment | 549 complexity | b2a8651f2e0bd60a6f495117185d7839 MD5 | raw file
Possible License(s): BSD-3-Clause

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

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

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