PageRenderTime 183ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 1ms

/forum/Sources/Profile-Modify.php

https://github.com/leftnode/nooges.com
PHP | 3301 lines | 2540 code | 323 blank | 438 comment | 526 complexity | f13a5509344f4a28addfe04d33411350 MD5 | raw file
  1. <?php
  2. /**********************************************************************************
  3. * Profile-Modify.php *
  4. ***********************************************************************************
  5. * SMF: Simple Machines Forum *
  6. * Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com) *
  7. * =============================================================================== *
  8. * Software Version: SMF 2.0 RC2 *
  9. * Software by: Simple Machines (http://www.simplemachines.org) *
  10. * Copyright 2006-2009 by: Simple Machines LLC (http://www.simplemachines.org) *
  11. * 2001-2006 by: Lewis Media (http://www.lewismedia.com) *
  12. * Support, News, Updates at: http://www.simplemachines.org *
  13. ***********************************************************************************
  14. * This program is free software; you may redistribute it and/or modify it under *
  15. * the terms of the provided license as published by Simple Machines LLC. *
  16. * *
  17. * This program is distributed in the hope that it is and will be useful, but *
  18. * WITHOUT ANY WARRANTIES; without even any implied warranty of MERCHANTABILITY *
  19. * or FITNESS FOR A PARTICULAR PURPOSE. *
  20. * *
  21. * See the "license.txt" file for details of the Simple Machines license. *
  22. * The latest version can always be found at http://www.simplemachines.org. *
  23. **********************************************************************************/
  24. if (!defined('SMF'))
  25. die('Hacking attempt...');
  26. /* This file has the primary job of showing and editing people's profiles.
  27. It also allows the user to change some of their or another's preferences,
  28. and such things. It uses the following functions:
  29. void loadProfileFields(bool force_reload = false)
  30. // !!!
  31. void setupProfileContext(array fields)
  32. // !!!
  33. void saveProfileFields()
  34. // !!!
  35. void saveProfileChanges(array &profile_variables, array &errors, int id_member)
  36. // !!!
  37. void makeThemeChanges(int id_member, int id_theme)
  38. // !!!
  39. void makeNotificationChanges(int id_member)
  40. // !!!
  41. void makeCustomFieldChanges(int id_member, string area, bool sanitize = true)
  42. // !!!
  43. void editBuddies(int id_member)
  44. // !!!
  45. void editIgnoreList(int id_member)
  46. // !!!
  47. void account(int id_member)
  48. // !!!
  49. void forumProfile(int id_member)
  50. // !!!
  51. void pmprefs(int id_member)
  52. // !!!
  53. array getAvatars(string directory, int level)
  54. // !!!
  55. void theme(int id_member)
  56. // !!!
  57. void authentication(int id_member, bool saving = false)
  58. // !!!
  59. void notification(int id_member)
  60. // !!!
  61. int list_getTopicNotificationCount(int memID)
  62. // !!!
  63. array list_getTopicNotifications(int start, int items_per_page, string sort, int memID)
  64. // !!!
  65. array list_getBoardNotifications(int start, int items_per_page, string sort, int memID)
  66. // !!!
  67. void loadThemeOptions(int id_member)
  68. // !!!
  69. void ignoreboards(int id_member)
  70. // !!!
  71. bool profileLoadLanguages()
  72. // !!!
  73. bool profileLoadGroups()
  74. // !!!
  75. bool profileLoadSignatureData()
  76. // !!!
  77. bool profileLoadAvatarData()
  78. // !!!
  79. bool profileSaveGroups(mixed &value)
  80. // !!!
  81. mixed profileSaveAvatarData(array &value)
  82. // !!!
  83. mixed profileValidateSignature(mixed &value)
  84. // !!!
  85. bool profileValidateEmail(string email, int id_member = none)
  86. // !!!
  87. void profileReloadUser()
  88. // !!!
  89. void profileSendActivation()
  90. // !!!
  91. void groupMembership(int id_member)
  92. // !!!
  93. mixed groupMembership2(array profile_vars, array post_erros, int id_member)
  94. // !!!
  95. Adding new fields to the profile:
  96. ---------------------------------------------------------------------------
  97. // !!!
  98. */
  99. // This defines every profile field known to man.
  100. function loadProfileFields($force_reload = false)
  101. {
  102. global $context, $profile_fields, $txt, $scripturl, $modSettings, $user_info, $old_profile, $smcFunc, $cur_profile, $language;
  103. // Don't load this twice!
  104. if (!empty($profile_fields) && !$force_reload)
  105. return;
  106. /* This horrific array defines all the profile fields in the whole world!
  107. In general each "field" has one array - the key of which is the database column name associated with said field. Each item
  108. can have the following attributes:
  109. string $type: The type of field this is - valid types are:
  110. - callback: This is a field which has its own callback mechanism for templating.
  111. - check: A simple checkbox.
  112. - hidden: This doesn't have any visual aspects but may have some validity.
  113. - password: A password box.
  114. - select: A select box.
  115. - text: A string of some description.
  116. string $label: The label for this item - default will be $txt[$key] if this isn't set.
  117. string $subtext: The subtext (Small label) for this item.
  118. int $size: Optional size for a text area.
  119. array $input_attr: An array of text strings to be added to the input box for this item.
  120. string $value: The value of the item. If not set $cur_profile[$key] is assumed.
  121. string $permission: Permission required for this item (Excluded _any/_own subfix which is applied automatically).
  122. function $input_validate: A runtime function which validates the element before going to the database. It is passed
  123. the relevant $_POST element if it exists and should be treated like a reference.
  124. Return types:
  125. - true: Element can be stored.
  126. - false: Skip this element.
  127. - a text string: An error occured - this is the error message.
  128. function $preload: A function that is used to load data required for this element to be displayed. Must return
  129. true to be displayed at all.
  130. string $cast_type: If set casts the element to a certain type. Valid types (bool, int, float).
  131. string $save_key: If the index of this element isn't the database column name it can be overriden
  132. with this string.
  133. bool $is_dummy: If set then nothing is acted upon for this element.
  134. bool $enabled: A test to determine whether this is even available - if not is unset.
  135. string $link_with: Key which links this field to an overall set.
  136. Note that all elements that have a custom input_validate must ensure they set the value of $cur_profile correct to enable
  137. the changes to be displayed correctly on submit of the form.
  138. */
  139. $profile_fields = array(
  140. 'aim' => array(
  141. 'type' => 'text',
  142. 'label' => $txt['aim'],
  143. 'subtext' => $txt['your_aim'],
  144. 'size' => 24,
  145. 'value' => strtr(empty($cur_profile['aim']) ? '' : $cur_profile['aim'], '+', ' '),
  146. 'permission' => 'profile_extra',
  147. 'input_validate' => create_function('&$value', '
  148. $value = strtr($value, \' \', \'+\');
  149. return true;
  150. '),
  151. ),
  152. 'avatar_choice' => array(
  153. 'type' => 'callback',
  154. 'callback_func' => 'avatar_select',
  155. // This handles the permissions too.
  156. 'preload' => 'profileLoadAvatarData',
  157. 'input_validate' => 'profileSaveAvatarData',
  158. 'save_key' => 'avatar',
  159. ),
  160. 'bday1' => array(
  161. 'type' => 'callback',
  162. 'callback_func' => 'birthdate',
  163. 'permission' => 'profile_extra',
  164. 'preload' => create_function('', '
  165. global $cur_profile, $context;
  166. // Split up the birthdate....
  167. list ($uyear, $umonth, $uday) = explode(\'-\', empty($cur_profile[\'birthdate\']) || $cur_profile[\'birthdate\'] == \'0001-01-01\' ? \'0000-00-00\' : $cur_profile[\'birthdate\']);
  168. $context[\'member\'][\'birth_date\'] = array(
  169. \'year\' => $uyear == \'0004\' ? \'0000\' : $uyear,
  170. \'month\' => $umonth,
  171. \'day\' => $uday,
  172. );
  173. return true;
  174. '),
  175. 'input_validate' => create_function('&$value', '
  176. global $profile_vars, $cur_profile;
  177. if (isset($_POST[\'bday2\'], $_POST[\'bday3\']) && $value > 0 && $_POST[\'bday2\'] > 0)
  178. {
  179. // Set to blank?
  180. if ((int) $_POST[\'bday3\'] == 1 && (int) $_POST[\'bday2\'] == 1 && (int) $value == 1)
  181. $value = \'0001-01-01\';
  182. else
  183. $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\';
  184. }
  185. else
  186. $value = \'0001-01-01\';
  187. $profile_vars[\'birthdate\'] = $value;
  188. $cur_profile[\'birthdate\'] = $value;
  189. return false;
  190. '),
  191. ),
  192. // Setting the birthdate the old style way?
  193. 'birthdate' => array(
  194. 'type' => 'hidden',
  195. 'permission' => 'profile_extra',
  196. 'input_validate' => create_function('&$value', '
  197. global $cur_profile;
  198. // !!! Should we check for this year and tell them they made a mistake :P? (based on coppa at least?)
  199. if (preg_match(\'/(\d{4})[\-\., ](\d{2})[\-\., ](\d{2})/\', $value, $dates) === 1)
  200. {
  201. $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\';
  202. return true;
  203. }
  204. else
  205. {
  206. $value = empty($cur_profile[\'birthdate\']) ? \'0001-01-01\' : $cur_profile[\'birthdate\'];
  207. return false;
  208. }
  209. '),
  210. ),
  211. 'date_registered' => array(
  212. 'type' => 'text',
  213. '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),
  214. 'label' => $txt['date_registered'],
  215. 'log_change' => true,
  216. 'permission' => 'moderate_forum',
  217. 'input_validate' => create_function('&$value', '
  218. global $txt, $user_info, $modSettings, $cur_profile, $context;
  219. // Bad date! Go try again - please?
  220. if (($value = strtotime($value)) === -1)
  221. {
  222. $value = $cur_profile[\'date_registered\'];
  223. 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));
  224. }
  225. // As long as it doesn\'t equal "N/A"...
  226. elseif ($value != $txt[\'not_applicable\'] && $value != strtotime(strftime(\'%Y-%m-%d\', $cur_profile[\'date_registered\'] + ($user_info[\'time_offset\'] + $modSettings[\'time_offset\']) * 3600)))
  227. $value = $value - ($user_info[\'time_offset\'] + $modSettings[\'time_offset\']) * 3600;
  228. else
  229. $value = $cur_profile[\'date_registered\'];
  230. return true;
  231. '),
  232. ),
  233. 'email_address' => array(
  234. 'type' => 'text',
  235. 'label' => $txt['email'],
  236. 'subtext' => $txt['valid_email'],
  237. 'log_change' => true,
  238. 'permission' => 'profile_identity',
  239. 'input_validate' => create_function('&$value', '
  240. global $context, $old_profile, $context, $profile_vars, $sourcedir, $modSettings;
  241. if (strtolower($value) == strtolower($old_profile[\'email_address\']))
  242. return false;
  243. $isValid = profileValidateEmail($value, $context[\'id_member\']);
  244. // Do they need to revalidate? If so schedule the function!
  245. if ($isValid === true && !empty($modSettings[\'send_validation_onChange\']) && !allowedTo(\'moderate_forum\'))
  246. {
  247. require_once($sourcedir . \'/Subs-Members.php\');
  248. $profile_vars[\'validation_code\'] = generateValidationCode();
  249. $profile_vars[\'is_activated\'] = 2;
  250. $context[\'profile_execute_on_save\'][] = \'profileSendActivation\';
  251. unset($context[\'profile_execute_on_save\'][\'reload_user\']);
  252. }
  253. return $isValid;
  254. '),
  255. ),
  256. 'gender' => array(
  257. 'type' => 'select',
  258. 'cast_type' => 'int',
  259. 'options' => 'return array(0 => \'\', 1 => $txt[\'male\'], 2 => $txt[\'female\']);',
  260. 'label' => $txt['gender'],
  261. 'permission' => 'profile_extra',
  262. ),
  263. 'hide_email' => array(
  264. 'type' => 'check',
  265. 'value' => empty($cur_profile['hide_email']) ? true : false,
  266. 'label' => $txt['allow_user_email'],
  267. 'permission' => 'profile_identity',
  268. 'input_validate' => create_function('&$value', '
  269. $value = $value == 0 ? 1 : 0;
  270. return true;
  271. '),
  272. ),
  273. 'icq' => array(
  274. 'type' => 'text',
  275. 'label' => $txt['icq'],
  276. 'subtext' => $txt['your_icq'],
  277. 'size' => 24,
  278. 'permission' => 'profile_extra',
  279. // Need to make sure ICQ doesn't equal 0.
  280. 'input_validate' => create_function('&$value', '
  281. if (empty($value))
  282. $value = \'\';
  283. else
  284. $value = (int) $value;
  285. return true;
  286. '),
  287. ),
  288. // Selecting group membership is a complicated one so we treat it separate!
  289. 'id_group' => array(
  290. 'type' => 'callback',
  291. 'callback_func' => 'group_manage',
  292. 'permission' => 'manage_membergroups',
  293. 'preload' => 'profileLoadGroups',
  294. 'input_validate' => 'profileSaveGroups',
  295. ),
  296. 'id_theme' => array(
  297. 'type' => 'callback',
  298. 'callback_func' => 'theme_pick',
  299. 'permission' => 'profile_extra',
  300. 'enabled' => $modSettings['theme_allow'] || allowedTo('admin_forum'),
  301. 'preload' => create_function('', '
  302. global $smcFunc, $context, $cur_profile, $txt;
  303. $request = $smcFunc[\'db_query\'](\'\', \'
  304. SELECT value
  305. FROM {db_prefix}themes
  306. WHERE id_theme = {int:id_theme}
  307. AND variable = {string:variable}
  308. LIMIT 1\', array(
  309. \'id_theme\' => $cur_profile[\'id_theme\'],
  310. \'variable\' => \'name\',
  311. )
  312. );
  313. list ($name) = $smcFunc[\'db_fetch_row\']($request);
  314. $smcFunc[\'db_free_result\']($request);
  315. $context[\'member\'][\'theme\'] = array(
  316. \'id\' => $cur_profile[\'id_theme\'],
  317. \'name\' => empty($cur_profile[\'id_theme\']) ? $txt[\'theme_forum_default\'] : $name
  318. );
  319. return true;
  320. '),
  321. 'input_validate' => create_function('&$value', '
  322. $value = (int) $value;
  323. return true;
  324. '),
  325. ),
  326. 'karma_good' => array(
  327. 'type' => 'callback',
  328. 'callback_func' => 'karma_modify',
  329. 'subtext' => $txt['your_icq'],
  330. 'permission' => 'admin_forum',
  331. // Set karma_bad too!
  332. 'input_validate' => create_function('&$value', '
  333. global $profile_vars, $cur_profile;
  334. $value = (int) $value;
  335. if (isset($_POST[\'karma_bad\']))
  336. {
  337. $profile_vars[\'karma_bad\'] = $_POST[\'karma_bad\'] != \'\' ? (int) $_POST[\'karma_bad\'] : 0;
  338. $cur_profile[\'karma_bad\'] = $_POST[\'karma_bad\'] != \'\' ? (int) $_POST[\'karma_bad\'] : 0;
  339. }
  340. return true;
  341. '),
  342. 'preload' => create_function('', '
  343. global $context, $cur_profile;
  344. $context[\'member\'][\'karma\'][\'good\'] = $cur_profile[\'karma_good\'];
  345. $context[\'member\'][\'karma\'][\'bad\'] = $cur_profile[\'karma_bad\'];
  346. return true;
  347. '),
  348. 'enabled' => !empty($modSettings['karmaMode']),
  349. ),
  350. 'lngfile' => array(
  351. 'type' => 'select',
  352. 'options' => 'return $context[\'profile_languages\'];',
  353. 'label' => $txt['preferred_language'],
  354. 'permission' => 'profile_identity',
  355. 'preload' => 'profileLoadLanguages',
  356. 'enabled' => !empty($modSettings['userLanguage']),
  357. 'value' => empty($cur_profile['lngfile']) ? $language : $cur_profile['lngfile'],
  358. 'input_validate' => create_function('&$value', '
  359. global $context, $cur_profile;
  360. // Load the languages.
  361. profileLoadLanguages();
  362. if (isset($context[\'profile_languages\'][$value]))
  363. {
  364. if ($context[\'user\'][\'is_owner\'])
  365. $_SESSION[\'language\'] = $value;
  366. return true;
  367. }
  368. else
  369. {
  370. $value = $cur_profile[\'lngfile\'];
  371. return false;
  372. }
  373. '),
  374. ),
  375. 'location' => array(
  376. 'type' => 'text',
  377. 'label' => $txt['location'],
  378. 'log_change' => true,
  379. 'size' => 50,
  380. 'permission' => 'profile_extra',
  381. ),
  382. // The username is not always editable - so adjust it as such.
  383. 'member_name' => array(
  384. 'type' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? 'text' : 'label',
  385. 'label' => $txt['username'],
  386. '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>)' : '',
  387. 'log_change' => true,
  388. 'permission' => 'profile_identity',
  389. 'prehtml' => allowedTo('admin_forum') && isset($_GET['changeusername']) ? '<div class="alert">' . $txt['username_warning'] . '</div>' : '',
  390. 'input_validate' => create_function('&$value', '
  391. global $sourcedir, $context, $user_info;
  392. if (allowedTo(\'admin_forum\'))
  393. {
  394. // We\'ll need this...
  395. require_once($sourcedir . \'/Subs-Auth.php\');
  396. // Maybe they are trying to change their password as well?
  397. $resetPassword = true;
  398. if (isset($_POST[\'passwrd2\']) && isset($_POST[\'passwrd1\']) && (($_POST[\'passwrd1\'] != $_POST[\'passwrd2\']) || (validatePassword($value, $value, array($user_info[\'name\'], $user_info[\'email\'])) == null)))
  399. $resetPassword = false;
  400. // Do the reset... this will send them an email too.
  401. if ($resetPassword)
  402. resetPassword($context[\'id_member\'], $value);
  403. elseif ($value !== null)
  404. {
  405. validateUsername($context[\'id_member\'], $value);
  406. updateMemberData($context[\'id_member\'], array(\'member_name\' => $value));
  407. }
  408. }
  409. return false;
  410. '),
  411. ),
  412. 'msn' => array(
  413. 'type' => 'text',
  414. 'label' => $txt['msn'],
  415. 'subtext' => $txt['msn_email_address'],
  416. 'size' => 24,
  417. 'permission' => 'profile_extra',
  418. 'input_validate' => create_function('&$value', '
  419. global $cur_profile;
  420. // Make sure the msn one is an email address, not something like \'none\' :P.
  421. if ($value != \'\' && preg_match(\'~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\\\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~\', $value) == 0)
  422. {
  423. $value = $cur_profile[\'msn\'];
  424. return false;
  425. }
  426. return true;
  427. '),
  428. ),
  429. 'passwrd1' => array(
  430. 'type' => 'password',
  431. 'label' => $txt['choose_pass'],
  432. 'subtext' => $txt['password_strength'],
  433. 'size' => 20,
  434. 'value' => '',
  435. 'enabled' => empty($cur_profile['openid_uri']),
  436. 'permission' => 'profile_identity',
  437. 'save_key' => 'passwd',
  438. // Note this will only work if passwrd2 also exists!
  439. 'input_validate' => create_function('&$value', '
  440. global $sourcedir, $user_info, $smcFunc, $cur_profile;
  441. // If we didn\'t try it then ignore it!
  442. if ($value == \'\')
  443. return false;
  444. // Do the two entries for the password even match?
  445. if (!isset($_POST[\'passwrd2\']) || $value != $_POST[\'passwrd2\'])
  446. return \'bad_new_password\';
  447. // Let\'s get the validation function into play...
  448. require_once($sourcedir . \'/Subs-Auth.php\');
  449. $passwordErrors = validatePassword($value, $cur_profile[\'member_name\'], array($cur_profile[\'real_name\'], $user_info[\'username\'], $user_info[\'name\'], $user_info[\'email\']));
  450. // Were there errors?
  451. if ($passwordErrors != null)
  452. return \'password_\' . $passwordErrors;
  453. // Set up the new password variable... ready for storage.
  454. $value = sha1(strtolower($cur_profile[\'member_name\']) . un_htmlspecialchars($value));
  455. return true;
  456. '),
  457. ),
  458. 'passwrd2' => array(
  459. 'type' => 'password',
  460. 'label' => $txt['verify_pass'],
  461. 'enabled' => empty($cur_profile['openid_uri']),
  462. 'size' => 20,
  463. 'value' => '',
  464. 'permission' => 'profile_identity',
  465. 'is_dummy' => true,
  466. ),
  467. 'personal_text' => array(
  468. 'type' => 'text',
  469. 'label' => $txt['personal_text'],
  470. 'log_change' => true,
  471. 'input_attr' => array('maxlength="50"'),
  472. 'size' => 50,
  473. 'permission' => 'profile_extra',
  474. ),
  475. // This does ALL the pm settings
  476. 'pm_prefs' => array(
  477. 'type' => 'callback',
  478. 'callback_func' => 'pm_settings',
  479. 'permission' => 'pm_read',
  480. 'preload' => create_function('', '
  481. global $context, $cur_profile;
  482. $context[\'display_mode\'] = $cur_profile[\'pm_prefs\'] & 3;
  483. $context[\'send_email\'] = $cur_profile[\'pm_email_notify\'];
  484. $context[\'receive_from\'] = $cur_profile[\'pm_receive_from\'];
  485. return true;
  486. '),
  487. 'input_validate' => create_function('&$value', '
  488. global $cur_profile, $profile_vars;
  489. // Simple validate and apply the two "sub settings"
  490. $value = max(min($value, 2), 0);
  491. $cur_profile[\'pm_email_notify\'] = $profile_vars[\'pm_email_notify\'] = max(min((int) $_POST[\'pm_email_notify\'], 2), 0);
  492. $cur_profile[\'pm_receive_from\'] = $profile_vars[\'pm_receive_from\'] = max(min((int) $_POST[\'pm_receive_from\'], 4), 0);
  493. return true;
  494. '),
  495. ),
  496. 'posts' => array(
  497. 'type' => 'int',
  498. 'label' => $txt['profile_posts'],
  499. 'log_change' => true,
  500. 'size' => 7,
  501. 'permission' => 'moderate_forum',
  502. 'input_validate' => create_function('&$value', '
  503. $value = $value != \'\' ? strtr($value, array(\',\' => \'\', \'.\' => \'\', \' \' => \'\')) : 0;
  504. return true;
  505. '),
  506. ),
  507. 'real_name' => array(
  508. 'type' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum') ? 'text' : 'label',
  509. 'label' => $txt['name'],
  510. 'subtext' => $txt['display_name_desc'],
  511. 'log_change' => true,
  512. 'input_attr' => array('maxlength="60"'),
  513. 'permission' => 'profile_identity',
  514. 'enabled' => !empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'),
  515. 'input_validate' => create_function('&$value', '
  516. global $context, $smcFunc, $sourcedir, $cur_profile;
  517. $value = trim(preg_replace(\'~[\s]~\' . ($context[\'utf8\'] ? \'u\' : \'\'), \' \', $value));
  518. if (trim($value) == \'\')
  519. return \'no_name\';
  520. elseif ($smcFunc[\'strlen\']($value) > 60)
  521. return \'name_too_long\';
  522. elseif ($cur_profile[\'real_name\'] != $value)
  523. {
  524. require_once($sourcedir . \'/Subs-Members.php\');
  525. if (isReservedName($value, $context[\'id_member\']))
  526. return \'name_taken\';
  527. }
  528. return true;
  529. '),
  530. ),
  531. 'secret_question' => array(
  532. 'type' => 'text',
  533. 'label' => $txt['secret_question'],
  534. 'subtext' => $txt['secret_desc'],
  535. 'size' => 50,
  536. 'permission' => 'profile_identity',
  537. ),
  538. 'secret_answer' => array(
  539. 'type' => 'text',
  540. 'label' => $txt['secret_answer'],
  541. 'subtext' => $txt['secret_desc2'],
  542. 'size' => 20,
  543. '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>',
  544. 'value' => '',
  545. 'permission' => 'profile_identity',
  546. 'input_validate' => create_function('&$value', '
  547. $value = $value != \'\' ? md5($value) : \'\';
  548. return true;
  549. '),
  550. ),
  551. 'signature' => array(
  552. 'type' => 'callback',
  553. 'callback_func' => 'signature_modify',
  554. 'permission' => 'profile_extra',
  555. 'enabled' => substr($modSettings['signature_settings'], 0, 1) == 1,
  556. 'preload' => 'profileLoadSignatureData',
  557. 'input_validate' => 'profileValidateSignature',
  558. ),
  559. 'show_online' => array(
  560. 'type' => 'check',
  561. 'label' => $txt['show_online'],
  562. 'permission' => 'profile_identity',
  563. 'enabled' => !empty($modSettings['allow_hideOnline']) || allowedTo('moderate_forum'),
  564. ),
  565. 'smiley_set' => array(
  566. 'type' => 'callback',
  567. 'callback_func' => 'smiley_pick',
  568. 'enabled' => !empty($modSettings['smiley_sets_enable']),
  569. 'permission' => 'profile_extra',
  570. 'preload' => create_function('', '
  571. global $modSettings, $context, $txt, $cur_profile;
  572. $context[\'member\'][\'smiley_set\'][\'id\'] = empty($cur_profile[\'smiley_set\']) ? \'\' : $cur_profile[\'smiley_set\'];
  573. $context[\'smiley_sets\'] = explode(\',\', \'none,,\' . $modSettings[\'smiley_sets_known\']);
  574. $set_names = explode("\n", $txt[\'smileys_none\'] . "\n" . $txt[\'smileys_forum_board_default\'] . "\n" . $modSettings[\'smiley_sets_names\']);
  575. foreach ($context[\'smiley_sets\'] as $i => $set)
  576. {
  577. $context[\'smiley_sets\'][$i] = array(
  578. \'id\' => $set,
  579. \'name\' => $set_names[$i],
  580. \'selected\' => $set == $context[\'member\'][\'smiley_set\'][\'id\']
  581. );
  582. if ($context[\'smiley_sets\'][$i][\'selected\'])
  583. $context[\'member\'][\'smiley_set\'][\'name\'] = $set_names[$i];
  584. }
  585. return true;
  586. '),
  587. 'input_validate' => create_function('&$value', '
  588. global $modSettings;
  589. $smiley_sets = explode(\',\', $modSettings[\'smiley_sets_known\']);
  590. if (!in_array($value, $smiley_sets) && $value != \'none\')
  591. $value = \'\';
  592. return true;
  593. '),
  594. ),
  595. // Pretty much a dummy entry - it populates all the theme settings.
  596. 'theme_settings' => array(
  597. 'type' => 'callback',
  598. 'callback_func' => 'theme_settings',
  599. 'permission' => 'profile_extra',
  600. 'is_dummy' => true,
  601. 'preload' => create_function('', '
  602. loadLanguage(\'Settings\');
  603. return true;
  604. '),
  605. ),
  606. 'time_format' => array(
  607. 'type' => 'callback',
  608. 'callback_func' => 'timeformat_modify',
  609. 'permission' => 'profile_extra',
  610. 'preload' => create_function('', '
  611. global $context, $user_info, $txt, $cur_profile, $modSettings;
  612. $context[\'easy_timeformats\'] = array(
  613. array(\'format\' => \'\', \'title\' => $txt[\'timeformat_default\']),
  614. array(\'format\' => \'%B %d, %Y, %I:%M:%S %p\', \'title\' => $txt[\'timeformat_easy1\']),
  615. array(\'format\' => \'%B %d, %Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy2\']),
  616. array(\'format\' => \'%Y-%m-%d, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy3\']),
  617. array(\'format\' => \'%d %B %Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy4\']),
  618. array(\'format\' => \'%d-%m-%Y, %H:%M:%S\', \'title\' => $txt[\'timeformat_easy5\'])
  619. );
  620. $context[\'member\'][\'time_format\'] = $cur_profile[\'time_format\'];
  621. $context[\'current_forum_time\'] = timeformat(time() - $user_info[\'time_offset\'] * 3600, false);
  622. $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);
  623. $context[\'current_forum_time_hour\'] = (int) strftime(\'%H\', forum_time(false));
  624. return true;
  625. '),
  626. ),
  627. 'time_offset' => array(
  628. 'type' => 'callback',
  629. 'callback_func' => 'timeoffset_modify',
  630. 'permission' => 'profile_extra',
  631. 'preload' => create_function('', '
  632. global $context, $cur_profile;
  633. $context[\'member\'][\'time_offset\'] = $cur_profile[\'time_offset\'];
  634. return true;
  635. '),
  636. 'input_validate' => create_function('&$value', '
  637. // Validate the time_offset...
  638. $value = (float) strtr($value, \',\', \'.\');
  639. if ($value < -23.5 || $value > 23.5)
  640. return \'bad_offset\';
  641. return true;
  642. '),
  643. ),
  644. 'usertitle' => array(
  645. 'type' => 'text',
  646. 'label' => $txt['custom_title'],
  647. 'log_change' => true,
  648. 'size' => 50,
  649. 'permission' => 'profile_title',
  650. 'enabled' => !empty($modSettings['titlesEnable']),
  651. ),
  652. 'website_title' => array(
  653. 'type' => 'text',
  654. 'label' => $txt['website_title'],
  655. 'subtext' => $txt['include_website_url'],
  656. 'size' => 50,
  657. 'permission' => 'profile_extra',
  658. 'link_with' => 'website',
  659. ),
  660. 'website_url' => array(
  661. 'type' => 'text',
  662. 'label' => $txt['website_url'],
  663. 'subtext' => $txt['complete_url'],
  664. 'size' => 50,
  665. 'permission' => 'profile_extra',
  666. // Fix the URL...
  667. 'input_validate' => create_function('&$value', '
  668. if (strlen(trim($value)) > 0 && strpos($value, \'://\') === false)
  669. $value = \'http://\' . $value;
  670. if (strlen($value) < 8)
  671. $value = \'\';
  672. return true;
  673. '),
  674. 'link_with' => 'website',
  675. ),
  676. 'yim' => array(
  677. 'type' => 'text',
  678. 'label' => $txt['yim'],
  679. 'subtext' => $txt['your_yim'],
  680. 'size' => 24,
  681. 'input_attr' => array('maxlength="32"'),
  682. 'permission' => 'profile_extra',
  683. ),
  684. );
  685. $disabled_fields = !empty($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
  686. // For each of the above let's take out the bits which don't apply - to save memory and security!
  687. foreach ($profile_fields as $key => $field)
  688. {
  689. // Do we have permission to do this?
  690. if (isset($field['permission']) && !allowedTo(($context['user']['is_owner'] ? array($field['permission'] . '_own', $field['permission'] . '_any') : $field['permission'] . '_any')) && !allowedTo($field['permission']))
  691. unset($profile_fields[$key]);
  692. // Is it enabled?
  693. if (isset($field['enabled']) && !$field['enabled'])
  694. unset($profile_fields[$key]);
  695. // Is it specifically disabled?
  696. if (in_array($key, $disabled_fields) || (isset($field['link_with']) && in_array($field['link_with'], $disabled_fields)))
  697. unset($profile_fields[$key]);
  698. }
  699. }
  700. // Setup the context for a page load!
  701. function setupProfileContext($fields)
  702. {
  703. global $profile_fields, $context, $cur_profile, $smcFunc;
  704. // Make sure we have this!
  705. loadProfileFields(true);
  706. // First check for any linked sets.
  707. foreach ($profile_fields as $key => $field)
  708. if (isset($field['link_with']) && in_array($field['link_with'], $fields))
  709. $fields[] = $key;
  710. // Some default bits.
  711. $context['profile_prehtml'] = '';
  712. $context['profile_posthtml'] = '';
  713. $context['profile_javascript'] = '';
  714. $context['profile_onsubmit_javascript'] = '';
  715. $i = 0;
  716. $last_type = '';
  717. foreach ($fields as $key => $field)
  718. {
  719. if (isset($profile_fields[$field]))
  720. {
  721. // Shortcut.
  722. $cur_field = &$profile_fields[$field];
  723. // Does it have a preload and does that preload succeed?
  724. if (isset($cur_field['preload']) && !$cur_field['preload']())
  725. continue;
  726. // If this is anything but complex we need to do more cleaning!
  727. if ($cur_field['type'] != 'callback' && $cur_field['type'] != 'hidden')
  728. {
  729. if (!isset($cur_field['label']))
  730. $cur_field['label'] = isset($txt[$field]) ? $txt[$field] : $field;
  731. // Everything has a value!
  732. if (!isset($cur_field['value']))
  733. {
  734. $cur_field['value'] = isset($cur_profile[$field]) ? $cur_profile[$field] : '';
  735. }
  736. // Any input attributes?
  737. $cur_field['input_attr'] = !empty($cur_field['input_attr']) ? implode(',', $cur_field['input_attr']) : '';
  738. }
  739. // Was there an error with this field on posting?
  740. if (isset($context['profile_errors'][$field]))
  741. $cur_field['is_error'] = true;
  742. // Any javascript stuff?
  743. if (!empty($cur_field['js_submit']))
  744. $context['profile_onsubmit_javascript'] .= $cur_field['js_submit'];
  745. if (!empty($cur_field['js']))
  746. $context['profile_javascript'] .= $cur_field['js'];
  747. // Any template stuff?
  748. if (!empty($cur_field['prehtml']))
  749. $context['profile_prehtml'] .= $cur_field['prehtml'];
  750. if (!empty($cur_field['posthtml']))
  751. $context['profile_posthtml'] .= $cur_field['posthtml'];
  752. // Finally put it into context?
  753. if ($cur_field['type'] != 'hidden')
  754. {
  755. $last_type = $cur_field['type'];
  756. $context['profile_fields'][$field] = &$profile_fields[$field];
  757. }
  758. }
  759. // Bodge in a line break - without doing two in a row ;)
  760. elseif ($field == 'hr' && $last_type != 'hr' && $last_type != '')
  761. {
  762. $last_type = 'hr';
  763. $context['profile_fields'][$i++]['type'] = 'hr';
  764. }
  765. }
  766. // Free up some memory.
  767. unset($profile_fields);
  768. }
  769. // Save the profile changes.
  770. function saveProfileFields()
  771. {
  772. global $profile_fields, $profile_vars, $context, $old_profile, $post_errors, $sourcedir, $modSettings, $cur_profile, $smcFunc;
  773. // Load them up.
  774. loadProfileFields();
  775. // This makes things easier...
  776. $old_profile = $cur_profile;
  777. // This allows variables to call activities when they save - by default just to reload their settings
  778. $context['profile_execute_on_save'] = array();
  779. if ($context['user']['is_owner'])
  780. $context['profile_execute_on_save']['reload_user'] = 'profileReloadUser';
  781. // Assume we log nothing.
  782. $context['log_changes'] = array();
  783. // Cycle through the profile fields working out what to do!
  784. foreach ($profile_fields as $key => $field)
  785. {
  786. if (!isset($_POST[$key]) || !empty($field['is_dummy']))
  787. continue;
  788. // What gets updated?
  789. $db_key = isset($field['save_key']) ? $field['save_key'] : $key;
  790. // Right - we have something that is enabled, we can act upon and has a value posted to it. Does it have a validation function?
  791. if (isset($field['input_validate']))
  792. {
  793. $is_valid = $field['input_validate']($_POST[$key]);
  794. // An error occured - set it as such!
  795. if ($is_valid !== true)
  796. {
  797. // Is this an actual error?
  798. if ($is_valid !== false)
  799. {
  800. $post_errors[$key] = $is_valid;
  801. $profile_fields[$key]['is_error'] = $is_valid;
  802. }
  803. // Retain the old value.
  804. $cur_profile[$key] = $_POST[$key];
  805. continue;
  806. }
  807. }
  808. // Are we doing a cast?
  809. $field['cast_type'] = empty($field['cast_type']) ? $field['type'] : $field['cast_type'];
  810. // Finally, clean up certain types.
  811. if ($field['cast_type'] == 'int')
  812. $_POST[$key] = (int) $_POST[$key];
  813. elseif ($field['cast_type'] == 'float')
  814. $_POST[$key] = (float) $_POST[$key];
  815. elseif ($field['cast_type'] == 'check')
  816. $_POST[$key] = !empty($_POST[$key]) ? 1 : 0;
  817. // If we got here we're doing OK.
  818. if ($field['type'] != 'hidden' && (!isset($old_profile[$key]) || $_POST[$key] != $old_profile[$key]))
  819. {
  820. // Set the save variable.
  821. $profile_vars[$db_key] = $_POST[$key];
  822. // And update the user profile.
  823. $cur_profile[$key] = $_POST[$key];
  824. // Are we logging it?
  825. if (!empty($field['log_change']) && isset($old_profile[$key]))
  826. $context['log_changes'][$key] = array(
  827. 'previous' => $old_profile[$key],
  828. 'new' => $_POST[$key],
  829. );
  830. }
  831. }
  832. //!!! Temporary
  833. if ($context['user']['is_owner'])
  834. $changeOther = allowedTo(array('profile_extra_any', 'profile_extra_own'));
  835. else
  836. $changeOther = allowedTo('profile_extra_any');
  837. if ($changeOther)
  838. {
  839. makeThemeChanges($context['id_member'], isset($_POST['id_theme']) ? (int) $_POST['id_theme'] : $old_profile['id_theme']);
  840. if (!empty($_REQUEST['sa']))
  841. makeCustomFieldChanges($context['id_member'], $_REQUEST['sa'], false);
  842. }
  843. // Free memory!
  844. unset($profile_fields);
  845. }
  846. // Save the profile changes....
  847. function saveProfileChanges(&$profile_vars, &$post_errors, $memID)
  848. {
  849. global $user_info, $txt, $modSettings, $user_profile;
  850. global $context, $settings, $sourcedir;
  851. global $smcFunc;
  852. // These make life easier....
  853. $old_profile = &$user_profile[$memID];
  854. // Permissions...
  855. if ($context['user']['is_owner'])
  856. {
  857. $changeIdentity = allowedTo(array('profile_identity_any', 'profile_identity_own'));
  858. $changeOther = allowedTo(array('profile_extra_any', 'profile_extra_own'));
  859. }
  860. else
  861. {
  862. $changeIdentity = allowedTo('profile_identity_any');
  863. $changeOther = allowedTo('profile_extra_any');
  864. }
  865. // Arrays of all the changes - makes things easier.
  866. $profile_bools = array(
  867. 'notify_announcements', 'notify_send_body',
  868. );
  869. $profile_ints = array(
  870. 'notify_regularity',
  871. 'notify_types',
  872. );
  873. $profile_floats = array(
  874. );
  875. $profile_strings = array(
  876. 'buddy_list',
  877. 'ignore_boards',
  878. );
  879. if (isset($_POST['sa']) && $_POST['sa'] == 'ignoreboards' && empty($_POST['ignore_brd']))
  880. $_POST['ignore_brd'] = array();
  881. unset($_POST['ignore_boards']); // Whatever it is set to is a dirty fithy thing. Kinda like our minds.
  882. if (isset($_POST['ignore_brd']))
  883. {
  884. if (!is_array($_POST['ignore_brd']))
  885. $_POST['ignore_brd'] = array ( $_POST['ignore_brd'] );
  886. foreach ($_POST['ignore_brd'] as $k => $d )
  887. {
  888. $d = (int) $d;
  889. if ($d != 0)
  890. $_POST['ignore_brd'][$k] = $d;
  891. else
  892. unset($_POST['ignore_brd'][$k]);
  893. }
  894. $_POST['ignore_boards'] = implode(',', $_POST['ignore_brd']);
  895. unset($_POST['ignore_brd']);
  896. }
  897. // Here's where we sort out all the 'other' values...
  898. if ($changeOther)
  899. {
  900. makeThemeChanges($memID, isset($_POST['id_theme']) ? (int) $_POST['id_theme'] : $old_profile['id_theme']);
  901. //makeAvatarChanges($memID, $post_errors);
  902. makeNotificationChanges($memID);
  903. if (!empty($_REQUEST['sa']))
  904. makeCustomFieldChanges($memID, $_REQUEST['sa'], false);
  905. foreach ($profile_bools as $var)
  906. if (isset($_POST[$var]))
  907. $profile_vars[$var] = empty($_POST[$var]) ? '0' : '1';
  908. foreach ($profile_ints as $var)
  909. if (isset($_POST[$var]))
  910. $profile_vars[$var] = $_POST[$var] != '' ? (int) $_POST[$var] : '';
  911. foreach ($profile_floats as $var)
  912. if (isset($_POST[$var]))
  913. $profile_vars[$var] = (float) $_POST[$var];
  914. foreach ($profile_strings as $var)
  915. if (isset($_POST[$var]))
  916. $profile_vars[$var] = $_POST[$var];
  917. }
  918. }
  919. // Make any theme changes that are sent with the profile..
  920. function makeThemeChanges($memID, $id_theme)
  921. {
  922. global $modSettings, $smcFunc, $context;
  923. $reservedVars = array(
  924. 'actual_theme_url',
  925. 'actual_images_url',
  926. 'base_theme_dir',
  927. 'base_theme_url',
  928. 'default_images_url',
  929. 'default_theme_dir',
  930. 'default_theme_url',
  931. 'default_template',
  932. 'images_url',
  933. 'number_recent_posts',
  934. 'smiley_sets_default',
  935. 'theme_dir',
  936. 'theme_id',
  937. 'theme_layers',
  938. 'theme_templates',
  939. 'theme_url',
  940. );
  941. // Can't change reserved vars.
  942. if ((isset($_POST['options']) && array_intersect($_POST['options'], $reservedVars) != array()) || (isset($_POST['default_options']) && array_intersect($_POST['default_options'], $reservedVars) != array()))
  943. fatal_lang_error('no_access', false);
  944. // Don't allow any overriding of custom fields with default or non-default options.
  945. $request = $smcFunc['db_query']('', '
  946. SELECT col_name
  947. FROM {db_prefix}custom_fields
  948. WHERE active = {int:is_active}',
  949. array(
  950. 'is_active' => 1,
  951. )
  952. );
  953. $custom_fields = array();
  954. while ($row = $smcFunc['db_fetch_assoc']($request))
  955. $custom_fields[] = $row['col_name'];
  956. $smcFunc['db_free_result']($request);
  957. // These are the theme changes...
  958. $themeSetArray = array();
  959. if (isset($_POST['options']) && is_array($_POST['options']))
  960. {
  961. foreach ($_POST['options'] as $opt => $val)
  962. {
  963. if (in_array($opt, $custom_fields))
  964. continue;
  965. // These need to be controlled.
  966. if ($opt == 'topics_per_page' || $opt == 'messages_per_page')
  967. $val = max(0, min($val, 50));
  968. $themeSetArray[] = array($memID, $id_theme, $opt, is_array($val) ? implode(',', $val) : $val);
  969. }
  970. }
  971. $erase_options = array();
  972. if (isset($_POST['default_options']) && is_array($_POST['default_options']))
  973. foreach ($_POST['default_options'] as $opt => $val)
  974. {
  975. if (in_array($opt, $custom_fields))
  976. continue;
  977. // These need to be controlled.
  978. if ($opt == 'topics_per_page' || $opt == 'messages_per_page')
  979. $val = max(0, min($val, 50));
  980. $themeSetArray[] = array($memID, 1, $opt, is_array($val) ? implode(',', $val) : $val);
  981. $erase_options[] = $opt;
  982. }
  983. // If themeSetArray isn't still empty, send it to the database.
  984. if (empty($context['password_auth_failed']))
  985. {
  986. if (!empty($themeSetArray))
  987. {
  988. $smcFunc['db_insert']('replace',
  989. '{db_prefix}themes',
  990. array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  991. $themeSetArray,
  992. array('id_member', 'id_theme', 'variable')
  993. );
  994. }
  995. if (!empty($erase_options))
  996. {
  997. $smcFunc['db_query']('', '
  998. DELETE FROM {db_prefix}themes
  999. WHERE id_theme != {int:id_theme}
  1000. AND variable IN ({array_string:erase_variables})
  1001. AND id_member = {int:id_member}',
  1002. array(
  1003. 'id_theme' => 1,
  1004. 'id_member' => $memID,
  1005. 'erase_variables' => $erase_options
  1006. )
  1007. );
  1008. }
  1009. $themes = explode(',', $modSettings['knownThemes']);
  1010. foreach ($themes as $t)
  1011. cache_put_data('theme_settings-' . $t . ':' . $memID, null, 60);
  1012. }
  1013. }
  1014. // Make any notification changes that need to be made.
  1015. function makeNotificationChanges($memID)
  1016. {
  1017. global $smcFunc;
  1018. // Update the boards they are being notified on.
  1019. if (isset($_POST['edit_notify_boards']) && !empty($_POST['notify_boards']))
  1020. {
  1021. // Make sure only integers are deleted.
  1022. foreach ($_POST['notify_boards'] as $index => $id)
  1023. $_POST['notify_boards'][$index] = (int) $id;
  1024. // id_board = 0 is reserved for topic notifications.
  1025. $_POST['notify_boards'] = array_diff($_POST['notify_boards'], array(0));
  1026. $smcFunc['db_query']('', '
  1027. DELETE FROM {db_prefix}log_notify
  1028. WHERE id_board IN ({array_int:board_list})
  1029. AND id_member = {int:selected_member}',
  1030. array(
  1031. 'board_list' => $_POST['notify_boards'],
  1032. 'selected_member' => $memID,
  1033. )
  1034. );
  1035. }
  1036. // We are editing topic notifications......
  1037. elseif (isset($_POST['edit_notify_topics']) && !empty($_POST['notify_topics']))
  1038. {
  1039. foreach ($_POST['notify_topics'] as $index => $id)
  1040. $_POST['notify_topics'][$index] = (int) $id;
  1041. // Make sure there are no zeros left.
  1042. $_POST['notify_topics'] = array_diff($_POST['notify_topics'], array(0));
  1043. $smcFunc['db_query']('', '
  1044. DELETE FROM {db_prefix}log_notify
  1045. WHERE id_topic IN ({array_int:topic_list})
  1046. AND id_member = {int:selected_member}',
  1047. array(
  1048. 'topic_list' => $_POST['notify_topics'],
  1049. 'selected_member' => $memID,
  1050. )
  1051. );
  1052. }
  1053. }
  1054. // Save any changes to the custom profile fields...
  1055. function makeCustomFieldChanges($memID, $area, $sanitize = true)
  1056. {
  1057. global $context, $smcFunc, $user_profile, $user_info, $modSettings;
  1058. if ($sanitize && isset($_POST['customfield']))
  1059. $_POST['customfield'] = htmlspecialchars__recursive($_POST['customfield']);
  1060. $where = $area == 'register' ? 'show_reg != 0' : 'show_profile = {string:area}';
  1061. // Load the fields we are saving too - make sure we save valid data (etc).
  1062. $request = $smcFunc['db_query']('', '
  1063. SELECT col_name, field_name, field_desc, field_type, field_length, field_options, default_value, show_reg, mask, private
  1064. FROM {db_prefix}custom_fields
  1065. WHERE ' . $where . '
  1066. AND active = {int:is_active}',
  1067. array(
  1068. 'is_active' => 1,
  1069. 'area' => $area,
  1070. )
  1071. );
  1072. $changes = array();
  1073. $log_changes = array();
  1074. while ($row = $smcFunc['db_fetch_assoc']($request))
  1075. {
  1076. /* This means don't save if:
  1077. - The user is NOT an admin.
  1078. - The data is not freely viewable and editable by users.
  1079. - The data is not invisible to users but editable by the owner (or if it is the user is not the owner)
  1080. - The area isn't registration, and if it is that the field is not suppossed to be shown there.
  1081. */
  1082. if ($row['private'] != 0 && !allowedTo('admin_forum') && ($memID != $user_info['id'] || $row['private'] != 2) && ($area != 'register' || $row['show_reg'] == 0))
  1083. continue;
  1084. // Validate the user data.
  1085. if ($row['field_type'] == 'check')
  1086. $value = isset($_POST['customfield'][$row['col_name']]) ? 1 : 0;
  1087. elseif ($row['field_type'] == 'select' || $row['field_type'] == 'radio')
  1088. {
  1089. $value = $row['default_value'];
  1090. foreach (explode(',', $row['field_options']) as $k => $v)
  1091. if (isset($_POST['customfield'][$row['col_name']]) && $_POST['customfield'][$row['col_name']] == $k)
  1092. $value = $v;
  1093. }
  1094. // Otherwise some form of text!
  1095. else
  1096. {
  1097. $value = isset($_POST['customfield'][$row['col_name']]) ? $_POST['customfield'][$row['col_name']] : '';
  1098. if ($row['field_length'])
  1099. $value = $smcFunc['substr']($value, 0, $row['field_length']);
  1100. // Any masks?
  1101. if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none')
  1102. {
  1103. //!!! We never error on this - just ignore it at the moment...
  1104. if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $value) === 0 || strlen($value) > 255))
  1105. $value = '';
  1106. elseif ($row['mask'] == 'number')
  1107. {
  1108. $value = (int) $value;
  1109. }
  1110. elseif (substr($row['mask'], 0, 5) == 'regex' && preg_match(substr($row['mask'], 5), $value) === 0)
  1111. $value = '';
  1112. }
  1113. }
  1114. // Did it change?
  1115. if (!isset($user_profile[$memID]['options'][$row['col_name']]) || $user_profile[$memID]['options'][$row['col_name']] != $value)
  1116. {
  1117. $log_changes[] = array(
  1118. 'action' => 'customfield_' . $row['col_name'],
  1119. 'id_log' => 2,
  1120. 'log_time' => time(),
  1121. 'id_member' => $memID,
  1122. 'ip' => $user_info['ip'],
  1123. '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'])),
  1124. );
  1125. $changes[] = array(1, $row['col_name'], $value, $memID);
  1126. $user_profile[$memID]['options'][$row['col_name']] = $value;
  1127. }
  1128. }
  1129. $smcFunc['db_free_result']($request);
  1130. // Make those changes!
  1131. if (!empty($changes) && empty($context['password_auth_failed']))
  1132. {
  1133. $smcFunc['db_insert']('replace',
  1134. '{db_prefix}themes',
  1135. array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534', 'id_member' => 'int'),
  1136. $changes,
  1137. array('id_theme', 'variable', 'id_member')
  1138. );
  1139. if (!empty($log_changes) && !empty($modSettings['modlog_enabled']))
  1140. $smcFunc['db_insert']('',
  1141. '{db_prefix}log_actions',
  1142. array(
  1143. 'action' => 'string', 'id_log' => 'int', 'log_time' => 'int', 'id_member' => 'int', 'ip' => 'string-16',
  1144. 'extra' => 'string-65534',
  1145. ),
  1146. $log_changes,
  1147. array('id_action')
  1148. );
  1149. }
  1150. }
  1151. // Show all the users buddies, as well as a add/delete interface.
  1152. function editBuddyIgnoreLists($memID)
  1153. {
  1154. global $sourcedir, $context, $txt, $scripturl, $modSettings, $user_profile;
  1155. // Do a quick check to ensure people aren't getting here illegally!
  1156. if (!$context['user']['is_owner'] || empty($modSettings['enable_buddylist']))
  1157. fatal_lang_error('no_access', false);
  1158. // Can we email the user direct?
  1159. $context['can_moderate_forum'] = allowedTo('moderate_forum');
  1160. $subActions = array(
  1161. 'buddies' => array('editBuddies', $txt['editBuddies']),
  1162. 'ignore' => array('editIgnoreList', $txt['editIgnoreList']),
  1163. );
  1164. $context['list_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : 'buddies';
  1165. // Create the tabs for the template.
  1166. $context[$context['profile_menu_name']]['tab_data'] = array(
  1167. 'title' => $txt['editBuddyIgnoreLists'],
  1168. 'description' => $txt['buddy_ignore_desc'],
  1169. 'tabs' => array(
  1170. 'buddies' => array(),
  1171. 'ignore' => array(),
  1172. ),
  1173. );
  1174. // Pass on to the actual function.
  1175. $context['sub_template'] = $subActions[$context['list_area']][0];
  1176. $subActions[$context['list_area']][0]($memID);
  1177. }
  1178. // Show all the users buddies, as well as a add/delete interface.
  1179. function editBuddies($memID)
  1180. {
  1181. global $txt, $scripturl, $modSettings;
  1182. global $context, $user_profile, $memberContext, $smcFunc;
  1183. // For making changes!
  1184. $buddiesArray = explode(',', $user_profile[$memID]['buddy_list']);
  1185. foreach ($buddiesArray as $k => $dummy)
  1186. if ($dummy == '')
  1187. unset($buddiesArray[$k]);
  1188. // Removing a buddy?
  1189. if (isset($_GET['remove']))
  1190. {
  1191. // Heh, I'm lazy, do it the easy way...
  1192. foreach ($buddiesArray as $key => $buddy)
  1193. if ($buddy == (int) $_GET['remove'])
  1194. unset($buddiesArray[$key]);
  1195. // Make the changes.
  1196. $user_profile[$memID]['buddy_list'] = implode(',', $buddiesArray);
  1197. updateMemberData($memID, array('buddy_list' => $user_profile[$memID]['buddy_list']));
  1198. // Redirect off the page because we don't like all this ugly query stuff to stick in the history.
  1199. redirectexit('action=profile;area=lists;sa=buddies;u=' . $memID);
  1200. }
  1201. elseif (isset($_POST['new_buddy']))
  1202. {
  1203. // Prepare the string for extraction...
  1204. $_POST['new_buddy'] = strtr($smcFunc['htmlspecialchars']($_POST['new_buddy'], ENT_QUOTES), array('&quot;' => '"'));
  1205. preg_match_all('~"([^"]+)"~', $_POST['new_buddy'], $matches);
  1206. $new_buddies = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST['new_buddy']))));
  1207. foreach ($new_buddies as $k => $dummy)
  1208. {
  1209. $new_buddies[$k] = strtr(trim($new_buddies[$k]), array('\'' => '&#039;'));
  1210. if (strlen($new_buddies[$k]) == 0)
  1211. unset($new_buddies[$k]);
  1212. }
  1213. if (!empty($new_buddies))
  1214. {
  1215. // Now find out the id_member of the buddy.
  1216. $request = $smcFunc['db_query']('', '
  1217. SELECT id_member
  1218. FROM {db_prefix}members
  1219. WHERE member_name IN ({array_string:new_buddies}) OR real_name IN ({array_string:new_buddies})
  1220. LIMIT {int:count_new_buddies}',
  1221. array(
  1222. 'new_buddies' => $new_buddies,
  1223. 'count_new_buddies' => count($new_buddies),
  1224. )
  1225. );
  1226. // Add the new member to the buddies array.
  1227. while ($row = $smcFunc['db_fetch_assoc']($request))
  1228. $buddiesArray[] = (int) $row['id_member'];
  1229. $smcFunc['db_free_result']($request);
  1230. // Now update the current users buddy list.
  1231. $user_profile[$memID]['buddy_list'] = implode(',', $buddiesArray);
  1232. updateMemberData($memID, array('buddy_list' => $user_profile[$memID]['buddy_list']));
  1233. }
  1234. // Back to the buddy list!
  1235. redirectexit('action=profile;area=lists;sa=buddies;u=' . $memID);
  1236. }
  1237. // Get all the users "buddies"...
  1238. $buddies = array();
  1239. if (!empty($buddiesArray))
  1240. {
  1241. $result = $smcFunc['db_query']('', '
  1242. SELECT id_member
  1243. FROM {db_prefix}members
  1244. WHERE id_member IN ({array_int:buddy_list})
  1245. ORDER BY real_name
  1246. LIMIT {int:buddy_list_count}',
  1247. array(
  1248. 'buddy_list' => $buddiesArray,
  1249. 'buddy_list_count' => substr_count($user_profile[$memID]['buddy_list'], ',') + 1,
  1250. )
  1251. );
  1252. while ($row = $smcFunc['db_fetch_assoc']($result))
  1253. $buddies[] = $row['id_member'];
  1254. $smcFunc['db_free_result']($result);
  1255. }
  1256. $context['buddy_count'] = count($buddies);
  1257. // Load all the members up.
  1258. loadMemberData($buddies, false, 'profile');
  1259. // Setup the context for each buddy.
  1260. $context['buddies'] = array();
  1261. foreach ($buddies as $buddy)
  1262. {
  1263. loadMemberContext($buddy);
  1264. $context['buddies'][$buddy] = $memberContext[$buddy];
  1265. }
  1266. }
  1267. // Allows the user to view their ignore list, as well as the option to manage members on it.
  1268. function editIgnoreList($memID)
  1269. {
  1270. global $txt, $scripturl, $modSettings;
  1271. global $context, $user_profile, $memberContext, $smcFunc;
  1272. // For making changes!
  1273. $ignoreArray = explode(',', $user_profile[$memID]['pm_ignore_list']);
  1274. foreach ($ignoreArray as $k => $dummy)
  1275. if ($dummy == '')
  1276. unset($ignoreArray[$k]);
  1277. // Removing a member from the ignore list?
  1278. if (isset($_GET['remove']))
  1279. {
  1280. // Heh, I'm lazy, do it the easy way...
  1281. foreach ($ignoreArray as $key => $id_remove)
  1282. if ($id_remove == (int) $_GET['remove'])
  1283. unset($ignoreArray[$key]);
  1284. // Make the changes.
  1285. $user_profile[$memID]['pm_ignore_list'] = implode(',', $ignoreArray);
  1286. updateMemberData($memID, array('pm_ignore_list' => $user_profile[$memID]['pm_ignore_list']));
  1287. // Redirect off the page because we don't like all this ugly query stuff to stick in the history.
  1288. redirectexit('action=profile;area=lists;sa=ignore;u=' . $memID);
  1289. }
  1290. elseif (isset($_POST['new_ignore']))
  1291. {
  1292. // Prepare the string for extraction...
  1293. $_POST['new_ignore'] = strtr($smcFunc['htmlspecialchars']($_POST['new_ignore'], ENT_QUOTES), array('&quot;' => '"'));
  1294. preg_match_all('~"([^"]+)"~', $_POST['new_ignore'], $matches);
  1295. $new_entries = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST['new_ignore']))));
  1296. foreach ($new_entries as $k => $dummy)
  1297. {
  1298. $new_entries[$k] = strtr(trim($new_entries[$k]), array('\'' => '&#039;'));
  1299. if (strlen($new_entries[$k]) == 0)
  1300. unset($new_entries[$k]);
  1301. }
  1302. if (!empty($new_entries))
  1303. {
  1304. // Now find out the id_member for the members in question.
  1305. $request = $smcFunc['db_query']('', '
  1306. SELECT id_member
  1307. FROM {db_prefix}members
  1308. WHERE member_name IN ({array_string:new_entries}) OR real_name IN ({array_string:new_entries})
  1309. LIMIT {int:count_new_entries}',
  1310. array(
  1311. 'new_entries' => $new_entries,
  1312. 'count_new_entries' => count($new_entries),
  1313. )
  1314. );
  1315. // Add the new member to the buddies array.
  1316. while ($row = $smcFunc['db_fetch_assoc']($request))
  1317. $ignoreArray[] = (int) $row['id_member'];
  1318. $smcFunc['db_free_result']($request);
  1319. // Now update the current users buddy list.
  1320. $user_profile[$memID]['pm_ignore_list'] = implode(',', $ignoreArray);
  1321. updateMemberData($memID, array('pm_ignore_list' => $user_profile[$memID]['pm_ignore_list']));
  1322. }
  1323. // Back to the list of pityful people!
  1324. redirectexit('action=profile;area=lists;sa=ignore;u=' . $memID);
  1325. }
  1326. // Initialise the list of members we're ignoring.
  1327. $ignored = array();
  1328. if (!empty($ignoreArray))
  1329. {
  1330. $result = $smcFunc['db_query']('', '
  1331. SELECT id_member
  1332. FROM {db_prefix}members
  1333. WHERE id_member IN ({array_int:ignore_list})
  1334. ORDER BY real_name
  1335. LIMIT {int:ignore_list_count}',
  1336. array(
  1337. 'ignore_list' => $ignoreArray,
  1338. 'ignore_list_count' => substr_count($user_profile[$memID]['pm_ignore_list'], ',') + 1,
  1339. )
  1340. );
  1341. while ($row = $smcFunc['db_fetch_assoc']($result))
  1342. $ignored[] = $row['id_member'];
  1343. $smcFunc['db_free_result']($result);
  1344. }
  1345. $context['ignore_count'] = count($ignored);
  1346. // Load all the members up.
  1347. loadMemberData($ignored, false, 'profile');
  1348. // Setup the context for each buddy.
  1349. $context['ignore_list'] = array();
  1350. foreach ($ignored as $ignore_member)
  1351. {
  1352. loadMemberContext($ignore_member);
  1353. $context['ignore_list'][$ignore_member] = $memberContext[$ignore_member];
  1354. }
  1355. }
  1356. function account($memID)
  1357. {
  1358. global $context, $txt;
  1359. loadThemeOptions($memID);
  1360. if (allowedTo(array('profile_identity_own', 'profile_identity_any')))
  1361. loadCustomFields($memID, 'account');
  1362. $context['sub_template'] = 'edit_options';
  1363. $context['page_desc'] = $txt['account_info'];
  1364. setupProfileContext(
  1365. array(
  1366. 'member_name', 'real_name', 'date_registered', 'posts', 'lngfile', 'hr',
  1367. 'id_group', 'hr',
  1368. 'email_address', 'hide_email', 'show_online', 'hr',
  1369. 'passwrd1', 'passwrd2', 'hr',
  1370. 'secret_question', 'secret_answer',
  1371. )
  1372. );
  1373. }
  1374. function forumProfile($memID)
  1375. {
  1376. global $context, $user_profile, $user_info, $txt, $modSettings;
  1377. loadThemeOptions($memID);
  1378. if (allowedTo(array('profile_extra_own', 'profile_extra_any')))
  1379. loadCustomFields($memID, 'forumProfile');
  1380. $context['sub_template'] = 'edit_options';
  1381. $context['page_desc'] = $txt['forumProfile_info'];
  1382. setupProfileContext(
  1383. array(
  1384. 'avatar_choice', 'hr', 'personal_text', 'hr',
  1385. 'bday1', 'location', 'gender', 'hr',
  1386. 'icq', 'aim', 'msn', 'yim', 'hr',
  1387. 'usertitle', 'signature', 'hr',
  1388. 'karma_good', 'hr',
  1389. 'website_title', 'website_url',
  1390. )
  1391. );
  1392. }
  1393. // Allow the edit of *someone elses* personal message settings.
  1394. function pmprefs($memID)
  1395. {
  1396. global $sourcedir, $context, $txt, $scripturl;
  1397. loadThemeOptions($memID);
  1398. loadCustomFields($memID, 'pmprefs');
  1399. $context['sub_template'] = 'edit_options';
  1400. $context['page_desc'] = $txt['pm_settings_desc'];
  1401. setupProfileContext(
  1402. array(
  1403. 'pm_prefs',
  1404. )
  1405. );
  1406. }
  1407. // Recursive function to retrieve avatar files
  1408. function getAvatars($directory, $level)
  1409. {
  1410. global $context, $txt, $modSettings;
  1411. $result = array();
  1412. // Open the directory..
  1413. $dir = dir($modSettings['avatar_directory'] . (!empty($directory) ? '/' : '') . $directory);
  1414. $dirs = array();
  1415. $files = array();
  1416. if (!$dir)
  1417. return array();
  1418. while ($line = $dir->read())
  1419. {
  1420. if (in_array($line, array('.', '..', 'blank.gif', 'index.php')))
  1421. continue;
  1422. if (is_dir($modSettings['avatar_directory'] . '/' . $directory . (!empty($directory) ? '/' : '') . $line))
  1423. $dirs[] = $line;
  1424. else
  1425. $files[] = $line;
  1426. }
  1427. $dir->close();
  1428. // Sort the results...
  1429. natcasesort($dirs);
  1430. natcasesort($files);
  1431. if ($level == 0)
  1432. {
  1433. $result[] = array(
  1434. 'filename' => 'blank.gif',
  1435. 'checked' => in_array($context['member']['avatar']['server_pic'], array('', 'blank.gif')),
  1436. 'name' => $txt['no_pic'],
  1437. 'is_dir' => false
  1438. );
  1439. }
  1440. foreach ($dirs as $line)
  1441. {
  1442. $tmp = getAvatars($directory . (!empty($directory) ? '/' : '') . $line, $level + 1);
  1443. if (!empty($tmp))
  1444. $result[] = array(
  1445. 'filename' => htmlspecialchars($line),
  1446. 'checked' => strpos($context['member']['avatar']['server_pic'], $line . '/') !== false,
  1447. 'name' => '[' . htmlspecialchars(str_replace('_', ' ', $line)) . ']',
  1448. 'is_dir' => true,
  1449. 'files' => $tmp
  1450. );
  1451. unset($tmp);
  1452. }
  1453. foreach ($files as $line)
  1454. {
  1455. $filename = substr($line, 0, (strlen($line) - strlen(strrchr($line, '.'))));
  1456. $extension = substr(strrchr($line, '.'), 1);
  1457. // Make sure it is an image.
  1458. if (strcasecmp($extension, 'gif') != 0 && strcasecmp($extension, 'jpg') != 0 && strcasecmp($extension, 'jpeg') != 0 && strcasecmp($extension, 'png') != 0 && strcasecmp($extension, 'bmp') != 0)
  1459. continue;
  1460. $result[] = array(
  1461. 'filename' => htmlspecialchars($line),
  1462. 'checked' => $line == $context['member']['avatar']['server_pic'],
  1463. 'name' => htmlspecialchars(str_replace('_', ' ', $filename)),
  1464. 'is_dir' => false
  1465. );
  1466. if ($level == 1)
  1467. $context['avatar_list'][] = $directory . '/' . $line;
  1468. }
  1469. return $result;
  1470. }
  1471. function theme($memID)
  1472. {
  1473. global $txt, $context, $user_profile, $modSettings, $settings, $user_info, $smcFunc;
  1474. loadThemeOptions($memID);
  1475. if (allowedTo(array('profile_extra_own', 'profile_extra_any')))
  1476. loadCustomFields($memID, 'theme');
  1477. $context['sub_template'] = 'edit_options';
  1478. $context['page_desc'] = $txt['theme_info'];
  1479. setupProfileContext(
  1480. array(
  1481. 'id_theme', 'smiley_set', 'hr',
  1482. 'time_format', 'time_offset', 'hr',
  1483. 'theme_settings',
  1484. )
  1485. );
  1486. }
  1487. // Changing authentication method? Only appropriate for people using OpenID.
  1488. function authentication($memID, $saving = false)
  1489. {
  1490. global $context, $cur_profile, $sourcedir, $txt, $post_errors, $modSettings;
  1491. loadLanguage('Login');
  1492. // We are saving?
  1493. if ($saving)
  1494. {
  1495. // Moving to password passed authentication?
  1496. if ($_POST['authenticate'] == 'passwd')
  1497. {
  1498. // Didn't enter anything?
  1499. if ($_POST['passwrd1'] == '')
  1500. $post_errors[] = 'no_password';
  1501. // Do the two entries for the password even match?
  1502. elseif (!isset($_POST['passwrd2']) || $_POST['passwrd1'] != $_POST['passwrd2'])
  1503. $post_errors[] = 'bad_new_password';
  1504. // Is it valid?
  1505. else
  1506. {
  1507. require_once($sourcedir . '/Subs-Auth.php');
  1508. $passwordErrors = validatePassword($_POST['passwrd1'], $cur_profile['member_name'], array($cur_profile['real_name'], $cur_profile['email_address']));
  1509. // Were there errors?
  1510. if ($passwordErrors != null)
  1511. $post_errors[] = 'password_' . $passwordErrors;
  1512. }
  1513. if (empty($post_errors))
  1514. {
  1515. // Integration?
  1516. if (isset($modSettings['integrate_reset_pass']) && function_exists($modSettings['integrate_reset_pass']))
  1517. call_user_func($modSettings['integrate_reset_pass'], $cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd1']);
  1518. // Go then.
  1519. $_POST['passwrd1'] = sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd1']));
  1520. // Do the important bits.
  1521. updateMemberData($memID, array('openid_uri' => '', 'passwd' => $_POST['passwrd1']));
  1522. if ($context['user']['is_owner'])
  1523. setLoginCookie(60 * $modSettings['cookieTime'], $memID, sha1(sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2'])) . $cur_profile['password_salt']));
  1524. redirectexit('action=profile;u=' . $memID);
  1525. }
  1526. return true;
  1527. }
  1528. // Not right yet!
  1529. elseif ($_POST['authenticate'] == 'openid' && !empty($_POST['openid_identifier']))
  1530. {
  1531. require_once($sourcedir . '/Subs-OpenID.php');
  1532. $_POST['openid_identifier'] = smf_openID_canonize($_POST['openid_identifier']);
  1533. if (smf_openid_member_exists($_POST['openid_identifier']))
  1534. $post_errors[] = 'openid_in_use';
  1535. elseif (empty($post_errors))
  1536. {
  1537. // Authenticate using the new OpenID URI first to make sure they didn't make a mistake.
  1538. if ($context['user']['is_owner'])
  1539. {
  1540. $_SESSION['new_openid_uri'] = $_POST['openid_identifier'];
  1541. smf_openID_validate($_POST['openid_identifier'], false, null, 'change_uri');
  1542. }
  1543. else
  1544. updateMemberData($memID, array('openid_uri' => $_POST['openid_identifier']));
  1545. }
  1546. }
  1547. }
  1548. // Some stuff.
  1549. $context['member']['openid_uri'] = $cur_profile['openid_uri'];
  1550. $context['auth_method'] = empty($cur_profile['openid_uri']) ? 'password' : 'openid';
  1551. $context['sub_template'] = 'authentication_method';
  1552. }
  1553. // Display the notifications and settings for changes.
  1554. function notification($memID)
  1555. {
  1556. global $txt, $scripturl, $user_profile, $user_info, $context, $modSettings, $smcFunc, $sourcedir, $settings;
  1557. // Gonna want this for the list.
  1558. require_once($sourcedir . '/Subs-List.php');
  1559. // Fine, start with the board list.
  1560. $listOptions = array(
  1561. 'id' => 'board_notification_list',
  1562. 'title' => '&nbsp;<img src="' . $settings['images_url'] . '/icons/notify_sm.gif" alt="" align="top" />&nbsp;' . $txt['notifications_boards'],
  1563. 'width' => '100%',
  1564. 'no_items_label' => $txt['notifications_boards_none'] . '<br /><br />' . $txt['notifications_boards_howto'],
  1565. 'no_items_align' => 'left',
  1566. 'base_href' => $scripturl . '?action=profile;area=notification;u=' . $memID,
  1567. 'default_sort_col' => 'board_name',
  1568. 'get_items' => array(
  1569. 'function' => 'list_getBoardNotifications',
  1570. 'params' => array(
  1571. $memID,
  1572. ),
  1573. ),
  1574. 'columns' => array(
  1575. 'board_name' => array(
  1576. 'header' => array(
  1577. 'value' => $txt['board'],
  1578. 'style' => 'text-align: left',
  1579. ),
  1580. 'data' => array(
  1581. 'function' => create_function('$board', '
  1582. global $settings, $txt;
  1583. $link = $board[\'link\'];
  1584. if ($board[\'new\'])
  1585. $link .= \' <a href="\' . $board[\'href\'] . \'"><img src="\' . $settings[\'lang_images_url\'] . \'/new.gif" alt="\' . $txt[\'new\'] . \'" /></a>\';
  1586. return $link;
  1587. '),
  1588. ),
  1589. 'sort' => array(
  1590. 'default' => 'name',
  1591. 'reverse' => 'name DESC',
  1592. ),
  1593. ),
  1594. 'delete' => array(
  1595. 'header' => array(
  1596. 'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);" />',
  1597. 'style' => 'width: 4%;',
  1598. ),
  1599. 'data' => array(
  1600. 'sprintf' => array(
  1601. 'format' => '<input type="checkbox" name="notify_boards[]" value="%1$d" class="input_check" />',
  1602. 'params' => array(
  1603. 'id' => false,
  1604. ),
  1605. ),
  1606. 'style' => 'text-align: center;',
  1607. ),
  1608. ),
  1609. ),
  1610. 'form' => array(
  1611. 'href' => $scripturl . '?action=profile;area=notification;save',
  1612. 'include_sort' => true,
  1613. 'include_start' => true,
  1614. 'hidden_fields' => array(
  1615. 'u' => $memID,
  1616. 'sa' => $context['menu_item_selected'],
  1617. $context['session_var'] => $context['session_id'],
  1618. ),
  1619. ),
  1620. 'additional_rows' => array(
  1621. array(
  1622. 'position' => 'bottom_of_list',
  1623. 'value' => '<input type="submit" name="edit_notify_boards" value="' . $txt['notifications_update'] . '" class="button_submit" />',
  1624. 'class' => 'windowbg',
  1625. 'align' => 'right',
  1626. ),
  1627. ),
  1628. );
  1629. // Create the board notification list.
  1630. createList($listOptions);
  1631. // Now do the topic notifications.
  1632. $listOptions = array(
  1633. 'id' => 'topic_notification_list',
  1634. 'title' => '&nbsp;<img src="' . $settings['images_url'] . '/icons/notify_sm.gif" alt="" align="top" />&nbsp;' . $txt['notifications_topics'],
  1635. 'width' => '100%',
  1636. 'items_per_page' => $modSettings['defaultMaxMessages'],
  1637. 'no_items_label' => $txt['notifications_topics_none'] . '<br /><br />' . $txt['notifications_topics_howto'],
  1638. 'no_items_align' => 'left',
  1639. 'base_href' => $scripturl . '?action=profile;area=notification;u=' . $memID,
  1640. 'default_sort_col' => 'last_post',
  1641. 'get_items' => array(
  1642. 'function' => 'list_getTopicNotifications',
  1643. 'params' => array(
  1644. $memID,
  1645. ),
  1646. ),
  1647. 'get_count' => array(
  1648. 'function' => 'list_getTopicNotificationCount',
  1649. 'params' => array(
  1650. $memID,
  1651. ),
  1652. ),
  1653. 'columns' => array(
  1654. 'subject' => array(
  1655. 'header' => array(
  1656. 'value' => $txt['subject'],
  1657. ),
  1658. 'data' => array(
  1659. 'function' => create_function('$topic', '
  1660. global $settings, $txt;
  1661. $link = $topic[\'link\'];
  1662. if ($topic[\'new\'])
  1663. $link .= \' <a href="\' . $topic[\'new_href\'] . \'"><img src="\' . $settings[\'lang_images_url\'] . \'/new.gif" alt="\' . $txt[\'new\'] . \'" /></a>\';
  1664. $link .= \'<br /><span class="smalltext"><em>\' . $txt[\'in\'] . \' \' . $topic[\'board_link\'] . \'</em></span>\';
  1665. return $link;
  1666. '),
  1667. ),
  1668. 'sort' => array(
  1669. 'default' => 'ms.subject',
  1670. 'reverse' => 'ms.subject DESC',
  1671. ),
  1672. ),
  1673. 'started_by' => array(
  1674. 'header' => array(
  1675. 'value' => $txt['started_by'],
  1676. ),
  1677. 'data' => array(
  1678. 'db' => 'poster_link',
  1679. ),
  1680. 'sort' => array(
  1681. 'default' => 'real_name_col',
  1682. 'reverse' => 'real_name_col DESC',
  1683. ),
  1684. ),
  1685. 'last_post' => array(
  1686. 'header' => array(
  1687. 'value' => $txt['last_post'],
  1688. ),
  1689. 'data' => array(
  1690. 'sprintf' => array(
  1691. 'format' => '<span class="smalltext">%1$s<br />' . $txt['by'] . ' %2$s</span>',
  1692. 'params' => array(
  1693. 'updated' => false,
  1694. 'poster_updated_link' => false,
  1695. ),
  1696. ),
  1697. ),
  1698. 'sort' => array(
  1699. 'default' => 'ml.id_msg DESC',
  1700. 'reverse' => 'ml.id_msg',
  1701. ),
  1702. ),
  1703. 'delete' => array(
  1704. 'header' => array(
  1705. 'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);" />',
  1706. 'style' => 'width: 4%;',
  1707. ),
  1708. 'data' => array(
  1709. 'sprintf' => array(
  1710. 'format' => '<input type="checkbox" name="notify_topics[]" value="%1$d" class="input_check" />',
  1711. 'params' => array(
  1712. 'id' => false,
  1713. ),
  1714. ),
  1715. 'style' => 'text-align: center;',
  1716. ),
  1717. ),
  1718. ),
  1719. 'form' => array(
  1720. 'href' => $scripturl . '?action=profile;area=notification;save',
  1721. 'include_sort' => true,
  1722. 'include_start' => true,
  1723. 'hidden_fields' => array(
  1724. 'u' => $memID,
  1725. 'sa' => $context['menu_item_selected'],
  1726. $context['session_var'] => $context['session_id'],
  1727. ),
  1728. ),
  1729. 'additional_rows' => array(
  1730. array(
  1731. 'position' => 'bottom_of_list',
  1732. 'value' => '<input type="submit" name="edit_notify_topics" value="' . $txt['notifications_update'] . '" class="button_submit" />',
  1733. 'class' => 'windowbg',
  1734. 'align' => 'right',
  1735. ),
  1736. ),
  1737. );
  1738. // Create the notification list.
  1739. createList($listOptions);
  1740. // What options are set?
  1741. $context['member'] += array(
  1742. 'notify_announcements' => $user_profile[$memID]['notify_announcements'],
  1743. 'notify_send_body' => $user_profile[$memID]['notify_send_body'],
  1744. 'notify_types' => $user_profile[$memID]['notify_types'],
  1745. 'notify_regularity' => $user_profile[$memID]['notify_regularity'],
  1746. );
  1747. loadThemeOptions($memID);
  1748. }
  1749. function list_getTopicNotificationCount($memID)
  1750. {
  1751. global $smcFunc, $user_info, $context, $modSettings;
  1752. $request = $smcFunc['db_query']('', '
  1753. SELECT COUNT(*)
  1754. FROM {db_prefix}log_notify AS ln' . (!$modSettings['postmod_active'] && $user_info['query_see_board'] === '1=1' ? '' : '
  1755. INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)') . ($user_info['query_see_board'] === '1=1' ? '' : '
  1756. INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)') . '
  1757. WHERE ln.id_member = {int:selected_member}' . ($user_info['query_see_board'] === '1=1' ? '' : '
  1758. AND {query_see_board}') . ($modSettings['postmod_active'] ? '
  1759. AND t.approved = {int:is_approved}' : ''),
  1760. array(
  1761. 'selected_member' => $memID,
  1762. 'is_approved' => 1,
  1763. )
  1764. );
  1765. list ($totalNotifications) = $smcFunc['db_fetch_row']($request);
  1766. $smcFunc['db_free_result']($request);
  1767. return $totalNotifications;
  1768. }
  1769. function list_getTopicNotifications($start, $items_per_page, $sort, $memID)
  1770. {
  1771. global $smcFunc, $txt, $scripturl, $user_info, $context, $modSettings;
  1772. // All the topics with notification on...
  1773. $request = $smcFunc['db_query']('', '
  1774. SELECT
  1775. IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from, b.id_board, b.name,
  1776. t.id_topic, ms.subject, ms.id_member, IFNULL(mem.real_name, ms.poster_name) AS real_name_col,
  1777. ml.id_msg_modified, ml.poster_time, ml.id_member AS id_member_updated,
  1778. IFNULL(mem2.real_name, ml.poster_name) AS last_real_name
  1779. FROM {db_prefix}log_notify AS ln
  1780. INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ')
  1781. INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board AND {query_see_board})
  1782. INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)
  1783. INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
  1784. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ms.id_member)
  1785. LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = ml.id_member)
  1786. LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
  1787. LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = b.id_board AND lmr.id_member = {int:current_member})
  1788. WHERE ln.id_member = {int:selected_member}
  1789. ORDER BY {raw:sort}
  1790. LIMIT {int:offset}, {int:items_per_page}',
  1791. array(
  1792. 'current_member' => $user_info['id'],
  1793. 'is_approved' => 1,
  1794. 'selected_member' => $memID,
  1795. 'sort' => $sort,
  1796. 'offset' => $start,
  1797. 'items_per_page' => $items_per_page,
  1798. )
  1799. );
  1800. $notification_topics = array();
  1801. while ($row = $smcFunc['db_fetch_assoc']($request))
  1802. {
  1803. censorText($row['subject']);
  1804. $notification_topics[] = array(
  1805. 'id' => $row['id_topic'],
  1806. 'poster_link' => empty($row['id_member']) ? $row['real_name_col'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name_col'] . '</a>',
  1807. 'poster_updated_link' => empty($row['id_member_updated']) ? $row['last_real_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['last_real_name'] . '</a>',
  1808. 'subject' => $row['subject'],
  1809. 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
  1810. 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['subject'] . '</a>',
  1811. 'new' => $row['new_from'] <= $row['id_msg_modified'],
  1812. 'new_from' => $row['new_from'],
  1813. 'updated' => timeformat($row['poster_time']),
  1814. 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . '#new',
  1815. 'new_link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . '#new">' . $row['subject'] . '</a>',
  1816. 'board_link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
  1817. );
  1818. }
  1819. $smcFunc['db_free_result']($request);
  1820. return $notification_topics;
  1821. }
  1822. function list_getBoardNotifications($start, $items_per_page, $sort, $memID)
  1823. {
  1824. global $smcFunc, $txt, $scripturl, $user_info;
  1825. $request = $smcFunc['db_query']('', '
  1826. SELECT b.id_board, b.name, IFNULL(lb.id_msg, 0) AS board_read, b.id_msg_updated
  1827. FROM {db_prefix}log_notify AS ln
  1828. INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
  1829. LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
  1830. WHERE ln.id_member = {int:selected_member}
  1831. AND {query_see_board}
  1832. ORDER BY ' . $sort,
  1833. array(
  1834. 'current_member' => $user_info['id'],
  1835. 'selected_member' => $memID,
  1836. )
  1837. );
  1838. $notification_boards = array();
  1839. while ($row = $smcFunc['db_fetch_assoc']($request))
  1840. $notification_boards[] = array(
  1841. 'id' => $row['id_board'],
  1842. 'name' => $row['name'],
  1843. 'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
  1844. 'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
  1845. 'new' => $row['board_read'] < $row['id_msg_updated']
  1846. );
  1847. $smcFunc['db_free_result']($request);
  1848. return $notification_boards;
  1849. }
  1850. function loadThemeOptions($memID)
  1851. {
  1852. global $context, $options, $cur_profile, $smcFunc;
  1853. if (isset($_POST['default_options']))
  1854. $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options'];
  1855. if ($context['user']['is_owner'])
  1856. {
  1857. $context['member']['options'] = $options;
  1858. foreach ($context['member']['options'] as $k => $v)
  1859. if (isset($_POST['options'][$k]))
  1860. $context['member']['options'][$k] = $_POST['options'][$k];
  1861. }
  1862. else
  1863. {
  1864. $request = $smcFunc['db_query']('', '
  1865. SELECT id_member, variable, value
  1866. FROM {db_prefix}themes
  1867. WHERE id_theme IN (1, {int:member_theme})
  1868. AND id_member IN (-1, {int:selected_member})',
  1869. array(
  1870. 'member_theme' => (int) $cur_profile['id_theme'],
  1871. 'selected_member' => $memID,
  1872. )
  1873. );
  1874. $temp = array();
  1875. while ($row = $smcFunc['db_fetch_assoc']($request))
  1876. {
  1877. if ($row['id_member'] == -1)
  1878. {
  1879. $temp[$row['variable']] = $row['value'];
  1880. continue;
  1881. }
  1882. if (isset($_POST['options'][$row['variable']]))
  1883. $row['value'] = $_POST['options'][$row['variable']];
  1884. $context['member']['options'][$row['variable']] = $row['value'];
  1885. }
  1886. $smcFunc['db_free_result']($request);
  1887. // Load up the default theme options for any missing.
  1888. foreach ($temp as $k => $v)
  1889. {
  1890. if (!isset($context['member']['options'][$k]))
  1891. $context['member']['options'][$k] = $v;
  1892. }
  1893. }
  1894. }
  1895. function ignoreboards($memID)
  1896. {
  1897. global $txt, $user_info, $context, $modSettings, $smcFunc, $cur_profile;
  1898. // Have the admins enabled this option?
  1899. if (empty($modSettings['allow_ignore_boards']))
  1900. fatal_lang_error('ignoreboards_disallowed', 'user');
  1901. // Find all the boards this user is allowed to see.
  1902. $request = $smcFunc['db_query']('order_by_board_order', '
  1903. SELECT b.id_cat, c.name AS cat_name, b.id_board, b.name, b.child_level,
  1904. '. (!empty($cur_profile['ignore_boards']) ? 'b.id_board IN ({array_int:ignore_boards})' : '0') . ' AS is_ignored
  1905. FROM {db_prefix}boards AS b
  1906. LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
  1907. WHERE {query_see_board}
  1908. AND redirect = {string:empty_string}',
  1909. array(
  1910. 'ignore_boards' => !empty($cur_profile['ignore_boards']) ? explode(',',$cur_profile['ignore_boards']) : array(),
  1911. 'empty_string' => '',
  1912. )
  1913. );
  1914. $context['num_boards'] = $smcFunc['db_num_rows']($request);
  1915. $context['categories'] = array();
  1916. while ($row = $smcFunc['db_fetch_assoc']($request))
  1917. {
  1918. // This category hasn't been set up yet..
  1919. if (!isset($context['categories'][$row['id_cat']]))
  1920. $context['categories'][$row['id_cat']] = array(
  1921. 'id' => $row['id_cat'],
  1922. 'name' => $row['cat_name'],
  1923. 'boards' => array()
  1924. );
  1925. // Set this board up, and let the template know when it's a child. (indent them..)
  1926. $context['categories'][$row['id_cat']]['boards'][$row['id_board']] = array(
  1927. 'id' => $row['id_board'],
  1928. 'name' => $row['name'],
  1929. 'child_level' => $row['child_level'],
  1930. 'selected' => $row['is_ignored'],
  1931. );
  1932. }
  1933. $smcFunc['db_free_result']($request);
  1934. // Now, let's sort the list of categories into the boards for templates that like that.
  1935. $temp_boards = array();
  1936. foreach ($context['categories'] as $category)
  1937. {
  1938. // Include a list of boards per category for easy toggling.
  1939. $context['categories'][$category['id']]['child_ids'] = array_keys($category['boards']);
  1940. $temp_boards[] = array(
  1941. 'name' => $category['name'],
  1942. 'child_ids' => array_keys($category['boards'])
  1943. );
  1944. $temp_boards = array_merge($temp_boards, array_values($category['boards']));
  1945. }
  1946. $max_boards = ceil(count($temp_boards) / 2);
  1947. if ($max_boards == 1)
  1948. $max_boards = 2;
  1949. // Now, alternate them so they can be shown left and right ;).
  1950. $context['board_columns'] = array();
  1951. for ($i = 0; $i < $max_boards; $i++)
  1952. {
  1953. $context['board_columns'][] = $temp_boards[$i];
  1954. if (isset($temp_boards[$i + $max_boards]))
  1955. $context['board_columns'][] = $temp_boards[$i + $max_boards];
  1956. else
  1957. $context['board_columns'][] = array();
  1958. }
  1959. loadThemeOptions($memID);
  1960. }
  1961. // Load all the languages for the profile.
  1962. function profileLoadLanguages()
  1963. {
  1964. global $context, $modSettings, $settings, $cur_profile, $language, $smcFunc;
  1965. $context['profile_languages'] = array();
  1966. // Do we have any languages?
  1967. if (empty($context['languages']))
  1968. getLanguages();
  1969. // Setup our languages.
  1970. foreach ($context['languages'] as $lang)
  1971. $context['profile_languages'][$lang['filename']] = strtr($lang['name'], array('-utf8' => ''));
  1972. ksort($context['profile_languages']);
  1973. // Return whether we should proceed with this.
  1974. return count($context['profile_languages']) > 1 ? true : false;
  1975. }
  1976. // Load all the group info for the profile.
  1977. function profileLoadGroups()
  1978. {
  1979. global $cur_profile, $txt, $context, $smcFunc, $user_settings;
  1980. $context['member_groups'] = array(
  1981. 0 => array(
  1982. 'id' => 0,
  1983. 'name' => $txt['no_primary_membergroup'],
  1984. 'is_primary' => $cur_profile['id_group'] == 0,
  1985. 'can_be_additional' => false,
  1986. 'can_be_primary' => true,
  1987. )
  1988. );
  1989. $curGroups = explode(',', $cur_profile['additional_groups']);
  1990. // Load membergroups, but only those groups the user can assign.
  1991. $request = $smcFunc['db_query']('', '
  1992. SELECT group_name, id_group, hidden
  1993. FROM {db_prefix}membergroups
  1994. WHERE id_group != {int:moderator_group}
  1995. AND min_posts = {int:min_posts}
  1996. ORDER BY min_posts, CASE WHEN id_group < {int:newbie_group} THEN id_group ELSE 4 END, group_name',
  1997. array(
  1998. 'moderator_group' => 3,
  1999. 'min_posts' => -1,
  2000. 'newbie_group' => 4,
  2001. )
  2002. );
  2003. while ($row = $smcFunc['db_fetch_assoc']($request))
  2004. {
  2005. // We should skip the administrator group if they don't have the admin_forum permission!
  2006. if ($row['id_group'] == 1 && !allowedTo('admin_forum'))
  2007. continue;
  2008. $context['member_groups'][$row['id_group']] = array(
  2009. 'id' => $row['id_group'],
  2010. 'name' => $row['group_name'],
  2011. 'is_primary' => $cur_profile['id_group'] == $row['id_group'],
  2012. 'is_additional' => in_array($row['id_group'], $curGroups),
  2013. 'can_be_additional' => true,
  2014. 'can_be_primary' => $row['hidden'] != 2,
  2015. );
  2016. }
  2017. $smcFunc['db_free_result']($request);
  2018. $context['member']['group_id'] = $user_settings['id_group'];
  2019. return true;
  2020. }
  2021. // Load key signature context data.
  2022. function profileLoadSignatureData()
  2023. {
  2024. global $modSettings, $context, $txt, $cur_profile, $smcFunc;
  2025. // Signature limits.
  2026. list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
  2027. $sig_limits = explode(',', $sig_limits);
  2028. $context['signature_enabled'] = isset($sig_limits[0]) ? $sig_limits[0] : 0;
  2029. $context['signature_limits'] = array(
  2030. 'max_length' => isset($sig_limits[1]) ? $sig_limits[1] : 0,
  2031. 'max_lines' => isset($sig_limits[2]) ? $sig_limits[2] : 0,
  2032. 'max_images' => isset($sig_limits[3]) ? $sig_limits[3] : 0,
  2033. 'max_smileys' => isset($sig_limits[4]) ? $sig_limits[4] : 0,
  2034. 'max_image_width' => isset($sig_limits[5]) ? $sig_limits[5] : 0,
  2035. 'max_image_height' => isset($sig_limits[6]) ? $sig_limits[6] : 0,
  2036. 'max_font_size' => isset($sig_limits[7]) ? $sig_limits[7] : 0,
  2037. 'bbc' => !empty($sig_bbc) ? explode(',', $sig_bbc) : array(),
  2038. );
  2039. // Kept this line in for backwards compatibility!
  2040. $context['max_signature_length'] = $context['signature_limits']['max_length'];
  2041. // Warning message for signature image limits?
  2042. $context['signature_warning'] = '';
  2043. if ($context['signature_limits']['max_image_width'] && $context['signature_limits']['max_image_height'])
  2044. $context['signature_warning'] = sprintf($txt['profile_error_signature_max_image_size'], $context['signature_limits']['max_image_width'], $context['signature_limits']['max_image_height']);
  2045. elseif ($context['signature_limits']['max_image_width'] || $context['signature_limits']['max_image_height'])
  2046. $context['signature_warning'] = sprintf($txt['profile_error_signature_max_image_' . ($context['signature_limits']['max_image_width'] ? 'width' : 'height')], $context['signature_limits'][$context['signature_limits']['max_image_width'] ? 'max_image_width' : 'max_image_height']);
  2047. $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
  2048. $context['member']['signature'] = empty($cur_profile['signature']) ? '' : str_replace(array('<br />', '<', '>', '"', '\''), array("\n", '&lt;', '&gt;', '&quot;', '&#039;'), $cur_profile['signature']);
  2049. return true;
  2050. }
  2051. // Load avatar context data.
  2052. function profileLoadAvatarData()
  2053. {
  2054. global $context, $cur_profile, $modSettings, $scripturl;
  2055. $context['avatar_url'] = $modSettings['avatar_url'];
  2056. // Default context.
  2057. $context['member']['avatar'] += array(
  2058. 'custom' => stristr($cur_profile['avatar'], 'http://') ? $cur_profile['avatar'] : 'http://',
  2059. 'selection' => $cur_profile['avatar'] == '' || stristr($cur_profile['avatar'], 'http://') ? '' : $cur_profile['avatar'],
  2060. 'id_attach' => $cur_profile['id_attach'],
  2061. 'filename' => $cur_profile['filename'],
  2062. 'allow_server_stored' => allowedTo('profile_server_avatar') || (!$context['user']['is_owner'] && allowedTo('profile_extra_any')),
  2063. 'allow_upload' => allowedTo('profile_upload_avatar') || (!$context['user']['is_owner'] && allowedTo('profile_extra_any')),
  2064. 'allow_external' => allowedTo('profile_remote_avatar') || (!$context['user']['is_owner'] && allowedTo('profile_extra_any')),
  2065. );
  2066. // Actually - nothing?
  2067. if (!$context['member']['avatar']['allow_external'] && !$context['member']['avatar']['allow_server_stored'] && !$context['member']['avatar']['allow_upload'])
  2068. return false;
  2069. if ($cur_profile['avatar'] == '' && $cur_profile['id_attach'] > 0 && $context['member']['avatar']['allow_upload'])
  2070. $context['member']['avatar'] += array(
  2071. 'choice' => 'upload',
  2072. 'server_pic' => 'blank.gif',
  2073. 'external' => 'http://'
  2074. );
  2075. elseif (stristr($cur_profile['avatar'], 'http://') && $context['member']['avatar']['allow_external'])
  2076. $context['member']['avatar'] += array(
  2077. 'choice' => 'external',
  2078. 'server_pic' => 'blank.gif',
  2079. 'external' => $cur_profile['avatar']
  2080. );
  2081. elseif (file_exists($modSettings['avatar_directory'] . '/' . $cur_profile['avatar']) && $context['member']['avatar']['allow_server_stored'])
  2082. $context['member']['avatar'] += array(
  2083. 'choice' => 'server_stored',
  2084. 'server_pic' => $cur_profile['avatar'] == '' ? 'blank.gif' : $cur_profile['avatar'],
  2085. 'external' => 'http://'
  2086. );
  2087. else
  2088. $context['member']['avatar'] += array(
  2089. 'choice' => 'server_stored',
  2090. 'server_pic' => 'blank.gif',
  2091. 'external' => 'http://'
  2092. );
  2093. // Get a list of all the avatars.
  2094. if ($context['member']['avatar']['allow_server_stored'])
  2095. {
  2096. $context['avatar_list'] = array();
  2097. $context['avatars'] = is_dir($modSettings['avatar_directory']) ? getAvatars('', 0) : array();
  2098. }
  2099. else
  2100. $context['avatars'] = array();
  2101. // Second level selected avatar...
  2102. $context['avatar_selected'] = substr(strrchr($context['member']['avatar']['server_pic'], '/'), 1);
  2103. return true;
  2104. }
  2105. // Save a members group.
  2106. function profileSaveGroups(&$value)
  2107. {
  2108. global $profile_vars, $old_profile, $context, $smcFunc, $cur_profile;
  2109. // The account page allows the change of your id_group - but not to admin!.
  2110. if (allowedTo('admin_forum') || ((int) $value != 1 && $old_profile['id_group'] != 1))
  2111. $value = (int) $value;
  2112. // ... otherwise it's the old group sir.
  2113. else
  2114. $value = $old_profile['id_group'];
  2115. // Find the additional membergroups (if any)
  2116. if (isset($_POST['additional_groups']) && is_array($_POST['additional_groups']))
  2117. {
  2118. foreach ($_POST['additional_groups'] as $i => $group_id)
  2119. {
  2120. if ((int) $group_id == 0 || (!allowedTo('admin_forum') && (int) $group_id == 1))
  2121. unset($_POST['additional_groups'][$i], $_POST['additional_groups'][$i]);
  2122. else
  2123. $_POST['additional_groups'][$i] = (int) $group_id;
  2124. }
  2125. // Put admin back in there if you don't have permission to take it away.
  2126. if (!allowedTo('admin_forum') && in_array(1, explode(',', $old_profile['additional_groups'])))
  2127. $_POST['additional_groups'][] = 1;
  2128. if (implode(',', $_POST['additional_groups']) !== $old_profile['additional_groups'])
  2129. {
  2130. $profile_vars['additional_groups'] = implode(',', $_POST['additional_groups']);
  2131. $cur_profile['additional_groups'] = implode(',', $_POST['additional_groups']);
  2132. }
  2133. }
  2134. // Too often, people remove delete their own account, or something.
  2135. if (in_array(1, explode(',', $old_profile['additional_groups'])) || $old_profile['id_group'] == 1)
  2136. {
  2137. $stillAdmin = $value == 1 || (isset($_POST['additional_groups']) && in_array(1, $_POST['additional_groups']));
  2138. // If they would no longer be an admin, look for any other...
  2139. if (!$stillAdmin)
  2140. {
  2141. $request = $smcFunc['db_query']('', '
  2142. SELECT id_member
  2143. FROM {db_prefix}members
  2144. WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups))
  2145. AND id_member != {int:selected_member}
  2146. LIMIT 1',
  2147. array(
  2148. 'admin_group' => 1,
  2149. 'selected_member' => $context['id_member'],
  2150. )
  2151. );
  2152. list ($another) = $smcFunc['db_fetch_row']($request);
  2153. $smcFunc['db_free_result']($request);
  2154. if (empty($another))
  2155. fatal_lang_error('at_least_one_admin', 'critical');
  2156. }
  2157. }
  2158. // If we are changing group status, update permission cache as necessary.
  2159. if ($value != $old_profile['id_group'] || isset($profile_vars['additional_groups']))
  2160. {
  2161. if ($context['user']['is_owner'])
  2162. $_SESSION['mc']['time'] = 0;
  2163. else
  2164. updateSettings(array('settings_updated' => time()));
  2165. }
  2166. return true;
  2167. }
  2168. // The avatar is incredibly complicated, what with the options... and what not.
  2169. function profileSaveAvatarData(&$value)
  2170. {
  2171. global $modSettings, $sourcedir, $smcFunc, $profile_vars, $cur_profile, $context;
  2172. $memID = $context['id_member'];
  2173. if (empty($memID) && !empty($context['password_auth_failed']))
  2174. return false;
  2175. // Reset the attach ID.
  2176. $cur_profile['id_attach'] = 0;
  2177. $cur_profile['attachment_type'] = 0;
  2178. $cur_profile['filename'] = '';
  2179. require_once($sourcedir . '/ManageAttachments.php');
  2180. // We need to know where we're going to be putting it..
  2181. if (!empty($modSettings['custom_avatar_enabled']))
  2182. {
  2183. $uploadDir = $modSettings['custom_avatar_dir'];
  2184. $id_folder = 1;
  2185. }
  2186. elseif (!empty($modSettings['currentAttachmentUploadDir']))
  2187. {
  2188. if (!is_array($modSettings['attachmentUploadDir']))
  2189. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
  2190. // Just use the current path for temp files.
  2191. $uploadDir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
  2192. $id_folder = $modSettings['currentAttachmentUploadDir'];
  2193. }
  2194. else
  2195. {
  2196. $uploadDir = $modSettings['attachmentUploadDir'];
  2197. $id_folder = 1;
  2198. }
  2199. $downloadedExternalAvatar = false;
  2200. if ($value == 'external' && allowedTo('profile_remote_avatar') && strtolower(substr($_POST['userpicpersonal'], 0, 7)) == 'http://' && strlen($_POST['userpicpersonal']) > 7 && !empty($modSettings['avatar_download_external']))
  2201. {
  2202. if (!is_writable($uploadDir))
  2203. fatal_lang_error('attachments_no_write', 'critical');
  2204. require_once($sourcedir . '/Subs-Package.php');
  2205. $url = parse_url($_POST['userpicpersonal']);
  2206. $contents = fetch_web_data('http://' . $url['host'] . (empty($url['port']) ? '' : ':' . $url['port']) . str_replace(' ', '%20', trim($url['path'])));
  2207. if ($contents != false && $tmpAvatar = fopen($uploadDir . '/avatar_tmp_' . $memID, 'wb'))
  2208. {
  2209. fwrite($tmpAvatar, $contents);
  2210. fclose($tmpAvatar);
  2211. $downloadedExternalAvatar = true;
  2212. $_FILES['attachment']['tmp_name'] = $uploadDir . '/avatar_tmp_' . $memID;
  2213. }
  2214. }
  2215. if ($value == 'server_stored' && allowedTo('profile_server_avatar'))
  2216. {
  2217. $profile_vars['avatar'] = strtr(empty($_POST['file']) ? (empty($_POST['cat']) ? '' : $_POST['cat']) : $_POST['file'], array('&amp;' => '&'));
  2218. $profile_vars['avatar'] = preg_match('~^([\w _!@%*=\-#()\[\]&.,]+/)?[\w _!@%*=\-#()\[\]&.,]+$~', $profile_vars['avatar']) != 0 && preg_match('/\.\./', $profile_vars['avatar']) == 0 && file_exists($modSettings['avatar_directory'] . '/' . $profile_vars['avatar']) ? ($profile_vars['avatar'] == 'blank.gif' ? '' : $profile_vars['avatar']) : '';
  2219. // Get rid of their old avatar. (if uploaded.)
  2220. removeAttachments(array('id_member' => $memID));
  2221. }
  2222. elseif ($value == 'external' && allowedTo('profile_remote_avatar') && strtolower(substr($_POST['userpicpersonal'], 0, 7)) == 'http://' && empty($modSettings['avatar_download_external']))
  2223. {
  2224. // Remove any attached avatar...
  2225. removeAttachments(array('id_member' => $memID));
  2226. $profile_vars['avatar'] = str_replace('%20', '', preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $_POST['userpicpersonal']));
  2227. if ($profile_vars['avatar'] == 'http://' || $profile_vars['avatar'] == 'http:///')
  2228. $profile_vars['avatar'] = '';
  2229. // Trying to make us do something we'll regret?
  2230. elseif (substr($profile_vars['avatar'], 0, 7) != 'http://')
  2231. return 'bad_avatar';
  2232. // Should we check dimensions?
  2233. elseif (!empty($modSettings['avatar_max_height_external']) || !empty($modSettings['avatar_max_width_external']))
  2234. {
  2235. // Now let's validate the avatar.
  2236. $sizes = url_image_size($profile_vars['avatar']);
  2237. if (is_array($sizes) && (($sizes[0] > $modSettings['avatar_max_width_external'] && !empty($modSettings['avatar_max_width_external'])) || ($sizes[1] > $modSettings['avatar_max_height_external'] && !empty($modSettings['avatar_max_height_external']))))
  2238. {
  2239. // Houston, we have a problem. The avatar is too large!!
  2240. if ($modSettings['avatar_action_too_large'] == 'option_refuse')
  2241. return 'bad_avatar';
  2242. elseif ($modSettings['avatar_action_too_large'] == 'option_download_and_resize')
  2243. {
  2244. require_once($sourcedir . '/Subs-Graphics.php');
  2245. if (downloadAvatar($profile_vars['avatar'], $memID, $modSettings['avatar_max_width_external'], $modSettings['avatar_max_height_external']))
  2246. {
  2247. $profile_vars['avatar'] = '';
  2248. $cur_profile['id_attach'] = $modSettings['new_avatar_data']['id'];
  2249. $cur_profile['filename'] = $modSettings['new_avatar_data']['filename'];
  2250. $cur_profile['attachment_type'] = $modSettings['new_avatar_data']['type'];
  2251. }
  2252. else
  2253. return 'bad_avatar';
  2254. }
  2255. }
  2256. }
  2257. }
  2258. elseif (($value == 'upload' && allowedTo('profile_upload_avatar')) || $downloadedExternalAvatar)
  2259. {
  2260. if ((isset($_FILES['attachment']['name']) && $_FILES['attachment']['name'] != '') || $downloadedExternalAvatar)
  2261. {
  2262. // Get the dimensions of the image.
  2263. if (!$downloadedExternalAvatar)
  2264. {
  2265. if (!is_writable($uploadDir))
  2266. fatal_lang_error('attachments_no_write', 'critical');
  2267. if (!move_uploaded_file($_FILES['attachment']['tmp_name'], $uploadDir . '/avatar_tmp_' . $memID))
  2268. fatal_lang_error('attach_timeout', 'critical');
  2269. $_FILES['attachment']['tmp_name'] = $uploadDir . '/avatar_tmp_' . $memID;
  2270. }
  2271. $sizes = @getimagesize($_FILES['attachment']['tmp_name']);
  2272. // No size, then it's probably not a valid pic.
  2273. if ($sizes === false)
  2274. return 'bad_avatar';
  2275. // Check whether the image is too large.
  2276. elseif ((!empty($modSettings['avatar_max_width_upload']) && $sizes[0] > $modSettings['avatar_max_width_upload']) || (!empty($modSettings['avatar_max_height_upload']) && $sizes[1] > $modSettings['avatar_max_height_upload']))
  2277. {
  2278. if (!empty($modSettings['avatar_resize_upload']))
  2279. {
  2280. // Attempt to chmod it.
  2281. @chmod($uploadDir . '/avatar_tmp_' . $memID, 0644);
  2282. require_once($sourcedir . '/Subs-Graphics.php');
  2283. if (!downloadAvatar($uploadDir . '/avatar_tmp_' . $memID, $memID, $modSettings['avatar_max_width_upload'], $modSettings['avatar_max_height_upload']))
  2284. return 'bad_avatar';
  2285. }
  2286. else
  2287. return 'bad_avatar';
  2288. }
  2289. elseif (is_array($sizes))
  2290. {
  2291. // Though not an exhaustive list, better safe than sorry.
  2292. $fp = fopen($_FILES['attachment']['tmp_name'], 'rb');
  2293. if (!$fp)
  2294. fatal_lang_error('attach_timeout');
  2295. // Now try to find an infection.
  2296. while (!feof($fp))
  2297. {
  2298. if (preg_match('~(iframe|\\<\\?php|\\<\\?[\s=]|\\<%[\s=]|html|eval|body|script\W)~', fgets($fp, 4096)) === 1)
  2299. {
  2300. if (file_exists($uploadDir . '/avatar_tmp_' . $memID))
  2301. @unlink($uploadDir . '/avatar_tmp_' . $memID);
  2302. fatal_lang_error('attach_timeout');
  2303. }
  2304. }
  2305. fclose($fp);
  2306. $extensions = array(
  2307. '1' => 'gif',
  2308. '2' => 'jpg',
  2309. '3' => 'png',
  2310. '6' => 'bmp'
  2311. );
  2312. $extension = isset($extensions[$sizes[2]]) ? $extensions[$sizes[2]] : 'bmp';
  2313. $mime_type = 'image/' . ($extension === 'jpg' ? 'jpeg' : ($extension === 'bmp' ? 'x-ms-bmp' : $extension));
  2314. $destName = 'avatar_' . $memID . '_' . time() . '.' . $extension;
  2315. list ($width, $height) = getimagesize($_FILES['attachment']['tmp_name']);
  2316. $file_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, false, null, true) : '';
  2317. // Remove previous attachments this member might have had.
  2318. removeAttachments(array('id_member' => $memID));
  2319. $smcFunc['db_insert']('',
  2320. '{db_prefix}attachments',
  2321. array(
  2322. 'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string', 'file_hash' => 'string', 'fileext' => 'string', 'size' => 'int',
  2323. 'width' => 'int', 'height' => 'int', 'mime_type' => 'string', 'id_folder' => 'int',
  2324. ),
  2325. array(
  2326. $memID, (empty($modSettings['custom_avatar_enabled']) ? 0 : 1), $destName, $file_hash, $extension, filesize($_FILES['attachment']['tmp_name']),
  2327. (int) $width, (int) $height, $mime_type, $id_folder,
  2328. ),
  2329. array('id_attach')
  2330. );
  2331. $cur_profile['id_attach'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
  2332. $cur_profile['filename'] = $destName;
  2333. $cur_profile['attachment_type'] = empty($modSettings['custom_avatar_enabled']) ? 0 : 1;
  2334. $destinationPath = $uploadDir . '/' . (empty($file_hash) ? $destName : $cur_profile['id_attach'] . '_' . $file_hash);
  2335. if (!rename($_FILES['attachment']['tmp_name'], $destinationPath))
  2336. {
  2337. // I guess a man can try.
  2338. removeAttachments(array('id_member' => $memID));
  2339. fatal_lang_error('attach_timeout', 'critical');
  2340. }
  2341. // Attempt to chmod it.
  2342. @chmod($uploadDir . '/' . $destinationPath, 0644);
  2343. }
  2344. $profile_vars['avatar'] = '';
  2345. // Delete any temporary file.
  2346. if (file_exists($uploadDir . '/avatar_tmp_' . $memID))
  2347. @unlink($uploadDir . '/avatar_tmp_' . $memID);
  2348. }
  2349. // Selected the upload avatar option and had one already uploaded before or didn't upload one.
  2350. else
  2351. $profile_vars['avatar'] = '';
  2352. }
  2353. else
  2354. $profile_vars['avatar'] = '';
  2355. // Setup the profile variables so it shows things right on display!
  2356. $cur_profile['avatar'] = $profile_vars['avatar'];
  2357. // If we're here we've done good - but don't save based on avatar_choice - skip it ;)
  2358. $profile_vars['avatar'] = $profile_vars['avatar'];
  2359. return false;
  2360. }
  2361. // Validate the signature!
  2362. function profileValidateSignature(&$value)
  2363. {
  2364. global $sourcedir, $modSettings, $smcFunc, $txt;
  2365. require_once($sourcedir . '/Subs-Post.php');
  2366. // Admins can do whatever they hell they want!
  2367. if (!allowedTo('admin_forum'))
  2368. {
  2369. // Load all the signature limits.
  2370. list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
  2371. $sig_limits = explode(',', $sig_limits);
  2372. $disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
  2373. $unparsed_signature = strtr(un_htmlspecialchars($value), array("\r" => '', '&#039' => '\''));
  2374. // Too long?
  2375. if (!empty($sig_limits[1]) && $smcFunc['strlen']($unparsed_signature) > $sig_limits[1])
  2376. {
  2377. $_POST['signature'] = trim(htmlspecialchars($smcFunc['substr']($unparsed_signature, 0, $sig_limits[1]), ENT_QUOTES));
  2378. $txt['profile_error_signature_max_length'] = sprintf($txt['profile_error_signature_max_length'], $sig_limits[1]);
  2379. return 'signature_max_length';
  2380. }
  2381. // Too many lines?
  2382. if (!empty($sig_limits[2]) && substr_count($unparsed_signature, "\n") >= $sig_limits[2])
  2383. {
  2384. $txt['profile_error_signature_max_lines'] = sprintf($txt['profile_error_signature_max_lines'], $sig_limits[2]);
  2385. return 'signature_max_lines';
  2386. }
  2387. // Too many images?!
  2388. if (!empty($sig_limits[3]) && (substr_count(strtolower($unparsed_signature), '[img') + substr_count(strtolower($unparsed_signature), '<img')) > $sig_limits[3])
  2389. {
  2390. $txt['profile_error_signature_max_image_count'] = sprintf($txt['profile_error_signature_max_image_count'], $sig_limits[3]);
  2391. return 'signature_max_image_count';
  2392. }
  2393. // What about too many smileys!
  2394. $smiley_parsed = $unparsed_signature;
  2395. parsesmileys($smiley_parsed);
  2396. if (!empty($sig_limits[4]) && (substr_count(strtolower($smiley_parsed), '<img') - substr_count(strtolower($unparsed_signature), '<img')) > $sig_limits[4])
  2397. {
  2398. $txt['profile_error_signature_max_smileys'] = sprintf($txt['profile_error_signature_max_smileys'], $sig_limits[4]);
  2399. return 'signature_max_smileys';
  2400. }
  2401. // Maybe we are abusing font sizes?
  2402. if (!empty($sig_limits[7]) && preg_match_all('~\[size=([\d\.]+)?(px|pt|em|x-large|larger)~i', $unparsed_signature, $matches) !== false && isset($matches[2]))
  2403. {
  2404. foreach ($matches[1] as $ind => $size)
  2405. {
  2406. $limit_broke = 0;
  2407. // Attempt to allow all sizes of abuse, so to speak.
  2408. if ($matches[2][$ind] == 'px' && $size > $sig_limits[7])
  2409. $limit_broke = $sig_limits[7] . 'px';
  2410. elseif ($matches[2][$ind] == 'pt' && $size > ($sig_limits[7] * 0.75))
  2411. $limit_broke = ((int) $sig_limits[7] * 0.75) . 'pt';
  2412. elseif ($matches[2][$ind] == 'em' && $size > ((float) $sig_limits[7] / 16))
  2413. $limit_broke = ((float) $sig_limits[7] / 16) . 'em';
  2414. elseif ($matches[2][$ind] != 'px' && $matches[2][$ind] != 'pt' && $matches[2][$ind] != 'em' && $sig_limits[7] < 18)
  2415. $limit_broke = 'large';
  2416. if ($limit_broke)
  2417. {
  2418. $txt['profile_error_signature_max_font_size'] = sprintf($txt['profile_error_signature_max_font_size'], $limit_broke);
  2419. return 'signature_max_font_size';
  2420. }
  2421. }
  2422. }
  2423. // The difficult one - image sizes! Don't error on this - just fix it.
  2424. if ((!empty($sig_limits[5]) || !empty($sig_limits[6])))
  2425. {
  2426. // Get all BBC tags...
  2427. preg_match_all('~\[img(\s+width=([\d]+))?(\s+height=([\d]+))?(\s+width=([\d]+))?\s*\](?:<br />)*([^<">]+?)(?:<br />)*\[/img\]~i', $unparsed_signature, $matches);
  2428. // ... and all HTML ones.
  2429. preg_match_all('~<img\s+src=(?:")?((?:http://|ftp://|https://|ftps://).+?)(?:")?(?:\s+alt=(?:")?(.*?)(?:")?)?(?:\s?/)?>~i', $unparsed_signature, $matches2, PREG_PATTERN_ORDER);
  2430. // And stick the HTML in the BBC.
  2431. if (!empty($matches2))
  2432. {
  2433. foreach ($matches2[0] as $ind => $dummy)
  2434. {
  2435. $matches[0][] = $matches2[0][$ind];
  2436. $matches[1][] = '';
  2437. $matches[2][] = '';
  2438. $matches[3][] = '';
  2439. $matches[4][] = '';
  2440. $matches[5][] = '';
  2441. $matches[6][] = '';
  2442. $matches[7][] = $matches2[1][$ind];
  2443. }
  2444. }
  2445. $replaces = array();
  2446. // Try to find all the images!
  2447. if (!empty($matches))
  2448. {
  2449. foreach ($matches[0] as $key => $image)
  2450. {
  2451. $width = -1; $height = -1;
  2452. // Does it have predefined restraints? Width first.
  2453. if ($matches[6][$key])
  2454. $matches[2][$key] = $matches[6][$key];
  2455. if ($matches[2][$key] && $sig_limits[5] && $matches[2][$key] > $sig_limits[5])
  2456. {
  2457. $width = $sig_limits[5];
  2458. $matches[4][$key] = $matches[4][$key] * ($width / $matches[2][$key]);
  2459. }
  2460. elseif ($matches[2][$key])
  2461. $width = $matches[2][$key];
  2462. // ... and height.
  2463. if ($matches[4][$key] && $sig_limits[6] && $matches[4][$key] > $sig_limits[6])
  2464. {
  2465. $height = $sig_limits[6];
  2466. if ($width != -1)
  2467. $width = $width * ($height / $matches[4][$key]);
  2468. }
  2469. elseif ($matches[4][$key])
  2470. $height = $matches[4][$key];
  2471. // If the dimensions are still not fixed - we need to check the actual image.
  2472. if (($width == -1 && $sig_limits[5]) || ($height == -1 && $sig_limits[6]))
  2473. {
  2474. $sizes = url_image_size($matches[7][$key]);
  2475. if (is_array($sizes))
  2476. {
  2477. // Too wide?
  2478. if ($sizes[0] > $sig_limits[5] && $sig_limits[5])
  2479. {
  2480. $width = $sig_limits[5];
  2481. $sizes[1] = $sizes[1] * ($width / $sizes[0]);
  2482. }
  2483. // Too high?
  2484. if ($sizes[1] > $sig_limits[6] && $sig_limits[6])
  2485. {
  2486. $height = $sig_limits[6];
  2487. if ($width == -1)
  2488. $width = $sizes[0];
  2489. $width = $width * ($height / $sizes[1]);
  2490. }
  2491. elseif ($width != -1)
  2492. $height = $sizes[1];
  2493. }
  2494. }
  2495. // Did we come up with some changes? If so remake the string.
  2496. if ($width != -1 || $height != -1)
  2497. $replaces[$image] = '[img' . ($width != -1 ? ' width=' . round($width) : '') . ($height != -1 ? ' height=' . round($height) : '') . ']' . $matches[7][$key] . '[/img]';
  2498. }
  2499. if (!empty($replaces))
  2500. $value = str_replace(array_keys($replaces), array_values($replaces), $unparsed_signature);
  2501. }
  2502. }
  2503. // Any disabled BBC?
  2504. $disabledSigBBC = implode('|', $disabledTags);
  2505. if (!empty($disabledSigBBC))
  2506. {
  2507. if (preg_match('~\[(' . $disabledSigBBC . ')~i', $unparsed_signature, $matches) !== false && isset($matches[1]))
  2508. {
  2509. $disabledTags = array_unique($disabledTags);
  2510. $txt['profile_error_signature_disabled_bbc'] = sprintf($txt['profile_error_signature_disabled_bbc'], implode(', ', $disabledTags));
  2511. return 'signature_disabled_bbc';
  2512. }
  2513. }
  2514. }
  2515. preparsecode($value);
  2516. return true;
  2517. }
  2518. // Validate an email address.
  2519. function profileValidateEmail($email, $memID = 0)
  2520. {
  2521. global $smcFunc, $context;
  2522. $email = strtr($email, array('&#039;' => '\''));
  2523. // Check the name and email for validity.
  2524. if (trim($email) == '')
  2525. return 'no_email';
  2526. if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $email) == 0)
  2527. return 'bad_email';
  2528. // Email addresses should be and stay unique.
  2529. $request = $smcFunc['db_query']('', '
  2530. SELECT id_member
  2531. FROM {db_prefix}members
  2532. WHERE ' . ($memID != 0 ? 'id_member != {int:selected_member} AND ' : '') . '
  2533. email_address = {string:email_address}
  2534. LIMIT 1',
  2535. array(
  2536. 'selected_member' => $memID,
  2537. 'email_address' => $email,
  2538. )
  2539. );
  2540. if ($smcFunc['db_num_rows']($request) > 0)
  2541. return 'email_taken';
  2542. $smcFunc['db_free_result']($request);
  2543. return true;
  2544. }
  2545. // Reload a users settings.
  2546. function profileReloadUser()
  2547. {
  2548. global $sourcedir, $modSettings, $context, $cur_profile, $smcFunc, $profile_vars;
  2549. // Log them back in - using the verify password as they must have matched and this one doesn't get changed by anyone!
  2550. if (isset($_POST['passwrd2']) && $_POST['passwrd2'] != '')
  2551. {
  2552. require_once($sourcedir . '/Subs-Auth.php');
  2553. setLoginCookie(60 * $modSettings['cookieTime'], $context['id_member'], sha1(sha1(strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2'])) . $cur_profile['password_salt']));
  2554. }
  2555. loadUserSettings();
  2556. writeLog();
  2557. }
  2558. // Send the user a new activation email if they need to reactivate!
  2559. function profileSendActivation()
  2560. {
  2561. global $sourcedir, $profile_vars, $txt, $context, $scripturl, $smcFunc, $cookiename, $cur_profile, $language, $modSettings;
  2562. require_once($sourcedir . '/Subs-Post.php');
  2563. // Shouldn't happen but just in case.
  2564. if (empty($profile_vars['email_address']))
  2565. return;
  2566. $replacements = array(
  2567. 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $context['id_member'] . ';code=' . $profile_vars['validation_code'],
  2568. 'ACTIVATIONCODE' => $profile_vars['validation_code'],
  2569. 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $context['id_member'],
  2570. );
  2571. // Send off the email.
  2572. $emaildata = loadEmailTemplate('activate_reactivate', $replacements, empty($cur_profile['lngfile']) || empty($modSettings['userLanguage']) ? $language : $cur_profile['lngfile']);
  2573. sendmail($profile_vars['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
  2574. // Log the user out.
  2575. $smcFunc['db_query']('', '
  2576. DELETE FROM {db_prefix}log_online
  2577. WHERE id_member = {int:selected_member}',
  2578. array(
  2579. 'selected_member' => $context['id_member'],
  2580. )
  2581. );
  2582. $_SESSION['log_time'] = 0;
  2583. $_SESSION['login_' . $cookiename] = serialize(array(0, '', 0));
  2584. if (isset($_COOKIE[$cookiename]))
  2585. $_COOKIE[$cookiename] = '';
  2586. loadUserSettings();
  2587. $context['user']['is_logged'] = false;
  2588. $context['user']['is_guest'] = true;
  2589. // Send them to the done-with-registration-login screen.
  2590. loadTemplate('Register');
  2591. $context += array(
  2592. 'page_title' => $txt['profile'],
  2593. 'sub_template' => 'after',
  2594. 'description' => $txt['activate_changed_email']
  2595. );
  2596. // We're gone!
  2597. obExit();
  2598. }
  2599. // Function to allow the user to choose group membership etc...
  2600. function groupMembership($memID)
  2601. {
  2602. global $txt, $scripturl, $user_profile, $user_info, $context, $modSettings, $smcFunc;
  2603. $curMember = $user_profile[$memID];
  2604. $context['primary_group'] = $curMember['id_group'];
  2605. // Can they manage groups?
  2606. $context['can_manage_membergroups'] = allowedTo('manage_membergroups');
  2607. $context['can_edit_primary'] = allowedTo('manage_membergroups');
  2608. $context['update_message'] = isset($_GET['msg']) && isset($txt['group_membership_msg_' . $_GET['msg']]) ? $txt['group_membership_msg_' . $_GET['msg']] : '';
  2609. // Get all the groups this user is a member of.
  2610. $groups = explode(',', $curMember['additional_groups']);
  2611. $groups[] = $curMember['id_group'];
  2612. // Ensure the query doesn't croak!
  2613. if (empty($groups))
  2614. $groups = array(0);
  2615. // Just to be sure...
  2616. foreach ($groups as $k => $v)
  2617. $groups[$k] = (int) $v;
  2618. // Get all the membergroups they can join.
  2619. $request = $smcFunc['db_query']('', '
  2620. SELECT mg.id_group, mg.group_name, mg.description, mg.group_type, mg.online_color, mg.hidden,
  2621. IFNULL(lgr.id_member, 0) AS pending
  2622. FROM {db_prefix}membergroups AS mg
  2623. LEFT JOIN {db_prefix}log_group_requests AS lgr ON (lgr.id_member = {int:selected_member} AND lgr.id_group = mg.id_group)
  2624. WHERE (mg.id_group IN ({array_int:group_list})
  2625. OR mg.group_type > {int:nonjoin_group_id})
  2626. AND mg.min_posts = {int:min_posts}
  2627. AND mg.id_group != {int:moderator_group}
  2628. ORDER BY group_name',
  2629. array(
  2630. 'group_list' => $groups,
  2631. 'selected_member' => $memID,
  2632. 'nonjoin_group_id' => 0,
  2633. 'min_posts' => -1,
  2634. 'moderator_group' => 3,
  2635. )
  2636. );
  2637. // This beast will be our group holder.
  2638. $context['groups'] = array(
  2639. 'member' => array(),
  2640. 'available' => array()
  2641. );
  2642. while ($row = $smcFunc['db_fetch_assoc']($request))
  2643. {
  2644. // Can they edit their primary group?
  2645. if (($row['id_group'] == $context['primary_group'] && $row['group_type'] != 0) || ($row['hidden'] != 2 && $context['primary_group'] == 0 && in_array($row['id_group'], $groups)))
  2646. $context['can_edit_primary'] = true;
  2647. // If they can't manage groups, and it's not publically joinable or already assigned, they can't see it.
  2648. if (!$context['can_manage_membergroups'] && $row['group_type'] == 0 && $row['id_group'] != $context['primary_group'])
  2649. continue;
  2650. $context['groups'][in_array($row['id_group'], $groups) ? 'member' : 'available'][$row['id_group']] = array(
  2651. 'id' => $row['id_group'],
  2652. 'name' => $row['group_name'],
  2653. 'desc' => $row['description'],
  2654. 'color' => $row['online_color'],
  2655. 'type' => $row['group_type'],
  2656. 'pending' => $row['pending'],
  2657. 'is_primary' => $row['id_group'] == $context['primary_group'],
  2658. 'can_be_primary' => $row['hidden'] != 2,
  2659. // Anything more than this needs to be done through account settings for security.
  2660. 'can_leave' => $row['id_group'] != 1 && $row['group_type'] != 0 ? true : false,
  2661. );
  2662. }
  2663. $smcFunc['db_free_result']($request);
  2664. // Add registered members on the end.
  2665. $context['groups']['member'][0] = array(
  2666. 'id' => 0,
  2667. 'name' => $txt['regular_members'],
  2668. 'desc' => $txt['regular_members_desc'],
  2669. 'type' => 0,
  2670. 'is_primary' => $context['primary_group'] == 0 ? true : false,
  2671. 'can_be_primary' => true,
  2672. 'can_leave' => 0,
  2673. );
  2674. // In the special case that someone is requesting membership of a group, setup some special context vars.
  2675. if (isset($_REQUEST['request']) && isset($context['groups']['available'][(int) $_REQUEST['request']]) && $context['groups']['available'][(int) $_REQUEST['request']]['type'] == 1)
  2676. $context['group_request'] = $context['groups']['available'][(int) $_REQUEST['request']];
  2677. }
  2678. // This function actually makes all the group changes...
  2679. function groupMembership2($profile_vars, $post_errors, $memID)
  2680. {
  2681. global $user_info, $sourcedir, $context, $user_profile, $modSettings, $txt, $smcFunc, $scripturl, $language;
  2682. // Let's be extra cautious...
  2683. if (!$context['user']['is_owner'] || empty($modSettings['show_group_membership']))
  2684. isAllowedTo('manage_membergroups');
  2685. if (!isset($_REQUEST['gid']) && !isset($_POST['primary']))
  2686. fatal_lang_error('no_access', false);
  2687. checkSession(isset($_GET['gid']) ? 'get' : 'post');
  2688. $old_profile = &$user_profile[$memID];
  2689. $context['can_manage_membergroups'] = allowedTo('manage_membergroups');
  2690. // By default the new primary is the old one.
  2691. $newPrimary = $old_profile['id_group'];
  2692. $addGroups = array_flip(explode(',', $old_profile['additional_groups']));
  2693. $canChangePrimary = $old_profile['id_group'] == 0 ? 1 : 0;
  2694. $changeType = isset($_POST['primary']) ? 'primary' : (isset($_POST['req']) ? 'request' : 'free');
  2695. // One way or another, we have a target group in mind...
  2696. $group_id = isset($_REQUEST['gid']) ? (int) $_REQUEST['gid'] : (int) $_POST['primary'];
  2697. $foundTarget = $changeType == 'primary' && $group_id == 0 ? true : false;
  2698. // Sanity check!!
  2699. if ($group_id == 1)
  2700. isAllowedTo('admin_forum');
  2701. // What ever we are doing, we need to determine if changing primary is possible!
  2702. $request = $smcFunc['db_query']('', '
  2703. SELECT id_group, group_type, hidden, group_name
  2704. FROM {db_prefix}membergroups
  2705. WHERE id_group IN ({int:group_list}, {int:current_group})',
  2706. array(
  2707. 'group_list' => $group_id,
  2708. 'current_group' => $old_profile['id_group'],
  2709. )
  2710. );
  2711. while ($row = $smcFunc['db_fetch_assoc']($request))
  2712. {
  2713. // Is this the new group?
  2714. if ($row['id_group'] == $group_id)
  2715. {
  2716. $foundTarget = true;
  2717. $group_name = $row['group_name'];
  2718. // Does the group type match what we're doing - are we trying to request a non-requestable group?
  2719. if ($changeType == 'request' && $row['group_type'] != 1)
  2720. fatal_lang_error('no_access', false);
  2721. // What about leaving a requestable group we are not a member of?
  2722. elseif ($changeType == 'free' && $row['group_type'] == 1 && $old_profile['id_group'] != $row['id_group'] && !isset($addGroups[$row['id_group']]))
  2723. fatal_lang_error('no_access', false);
  2724. elseif ($changeType == 'free' && $row['group_type'] != 2 && $row['group_type'] != 1)
  2725. fatal_lang_error('no_access', false);
  2726. // We can't change the primary group if this is hidden!
  2727. if ($row['hidden'] == 2)
  2728. $canChangePrimary = false;
  2729. }
  2730. // If this is their old primary, can we change it?
  2731. if ($row['id_group'] == $old_profile['id_group'] && ($row['group_type'] != 0 || $context['can_manage_membergroups']) && $canChangePrimary !== false)
  2732. $canChangePrimary = 1;
  2733. // If we are not doing a force primary move, don't do it automatically if current primary is not 0.
  2734. if ($changeType != 'primary' && $old_profile['id_group'] != 0)
  2735. $canChangePrimary = false;
  2736. // If this is the one we are acting on, can we even act?
  2737. if (!$context['can_manage_membergroups'] && $row['group_type'] == 0)
  2738. $canChangePrimary = false;
  2739. }
  2740. $smcFunc['db_free_result']($request);
  2741. // Didn't find the target?
  2742. if (!$foundTarget)
  2743. fatal_lang_error('no_access', false);
  2744. // Final security check, don't allow users to promote themselves to admin.
  2745. if ($context['can_manage_membergroups'] && !allowedTo('admin_forum'))
  2746. {
  2747. $request = $smcFunc['db_query']('', '
  2748. SELECT COUNT(permission)
  2749. FROM {db_prefix}permissions
  2750. WHERE id_group = {int:selected_group}
  2751. AND permission = {string:admin_forum}
  2752. AND add_deny = {int:not_denied}',
  2753. array(
  2754. 'selected_group' => $group_id,
  2755. 'not_denied' => 1,
  2756. 'admin_forum' => 'admin_forum',
  2757. )
  2758. );
  2759. list ($disallow) = $smcFunc['db_fetch_row']($request);
  2760. $smcFunc['db_free_result']($request);
  2761. if ($disallow)
  2762. isAllowedTo('admin_forum');
  2763. }
  2764. // If we're requesting, add the note then return.
  2765. if ($changeType == 'request')
  2766. {
  2767. $request = $smcFunc['db_query']('', '
  2768. SELECT id_member
  2769. FROM {db_prefix}log_group_requests
  2770. WHERE id_member = {int:selected_member}
  2771. AND id_group = {int:selected_group}',
  2772. array(
  2773. 'selected_member' => $memID,
  2774. 'selected_group' => $group_id,
  2775. )
  2776. );
  2777. if ($smcFunc['db_num_rows']($request) != 0)
  2778. fatal_lang_error('profile_error_already_requested_group');
  2779. $smcFunc['db_free_result']($request);
  2780. // Log the request.
  2781. $smcFunc['db_insert']('',
  2782. '{db_prefix}log_group_requests',
  2783. array(
  2784. 'id_member' => 'int', 'id_group' => 'int', 'time_applied' => 'int', 'reason' => 'string-65534',
  2785. ),
  2786. array(
  2787. $memID, $group_id, time(), $_POST['reason'],
  2788. ),
  2789. array('id_request')
  2790. );
  2791. // Send an email to all group moderators etc.
  2792. require_once($sourcedir . '/Subs-Post.php');
  2793. // Do we have any group moderators?
  2794. $request = $smcFunc['db_query']('', '
  2795. SELECT id_member
  2796. FROM {db_prefix}group_moderators
  2797. WHERE id_group = {int:selected_group}',
  2798. array(
  2799. 'selected_group' => $group_id,
  2800. )
  2801. );
  2802. $moderators = array();
  2803. while ($row = $smcFunc['db_fetch_assoc']($request))
  2804. $moderators[] = $row['id_member'];
  2805. $smcFunc['db_free_result']($request);
  2806. // Otherwise this is the backup!
  2807. if (empty($moderators))
  2808. {
  2809. require_once($sourcedir . '/Subs-Members.php');
  2810. $moderators = membersAllowedTo('manage_membergroups');
  2811. }
  2812. if (!empty($moderators))
  2813. {
  2814. $request = $smcFunc['db_query']('', '
  2815. SELECT id_member, email_address, lngfile, member_name
  2816. FROM {db_prefix}members
  2817. WHERE id_member IN ({array_int:moderator_list})
  2818. AND notify_types != {int:no_notifications}
  2819. ORDER BY lngfile',
  2820. array(
  2821. 'moderator_list' => $moderators,
  2822. 'no_notifications' => 4,
  2823. )
  2824. );
  2825. while ($row = $smcFunc['db_fetch_assoc']($request))
  2826. {
  2827. $replacements = array(
  2828. 'RECPNAME' => $row['member_name'],
  2829. 'APPYNAME' => $old_profile['member_name'],
  2830. 'GROUPNAME' => $group_name,
  2831. 'REASON' => $_POST['reason'],
  2832. 'MODLINK' => $scripturl . '?action=moderate;area=groups;sa=requests',
  2833. );
  2834. $emaildata = loadEmailTemplate('request_membership', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
  2835. sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
  2836. }
  2837. $smcFunc['db_free_result']($request);
  2838. }
  2839. return $changeType;
  2840. }
  2841. // Otherwise we are leaving/joining a group.
  2842. elseif ($changeType == 'free')
  2843. {
  2844. // Are we leaving?
  2845. if ($old_profile['id_group'] == $group_id || isset($addGroups[$group_id]))
  2846. {
  2847. if ($old_profile['id_group'] == $group_id)
  2848. $newPrimary = 0;
  2849. else
  2850. unset($addGroups[$group_id]);
  2851. }
  2852. // ... if not, must be joining.
  2853. else
  2854. {
  2855. // Can we change the primary, and do we want to?
  2856. if ($canChangePrimary)
  2857. {
  2858. if ($old_profile['id_group'] != 0)
  2859. $addGroups[$old_profile['id_group']] = -1;
  2860. $newPrimary = $group_id;
  2861. }
  2862. // Otherwise it's an additional group...
  2863. else
  2864. $addGroups[$group_id] = -1;
  2865. }
  2866. }
  2867. // Finally, we must be setting the primary.
  2868. elseif ($canChangePrimary)
  2869. {
  2870. if ($old_profile['id_group'] != 0)
  2871. $addGroups[$old_profile['id_group']] = -1;
  2872. if (isset($addGroups[$group_id]))
  2873. unset($addGroups[$group_id]);
  2874. $newPrimary = $group_id;
  2875. }
  2876. // Finally, we can make the changes!
  2877. foreach ($addGroups as $id => $dummy)
  2878. if (empty($id))
  2879. unset($addGroups[$id]);
  2880. $addGroups = implode(',', array_flip($addGroups));
  2881. // Ensure that we don't cache permissions if the group is changing.
  2882. if ($context['user']['is_owner'])
  2883. $_SESSION['mc']['time'] = 0;
  2884. else
  2885. updateSettings(array('settings_updated' => time()));
  2886. updateMemberData($memID, array('id_group' => $newPrimary, 'additional_groups' => $addGroups));
  2887. return $changeType;
  2888. }
  2889. ?>