PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/include/api/user.php

https://bitbucket.org/webop/webop-forum
PHP | 3295 lines | 1158 code | 274 blank | 1863 comment | 281 complexity | b5ac9d94d404681b080f4d9f563fea24 MD5 | raw file
Possible License(s): LGPL-2.1

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

  1. <?php
  2. ////////////////////////////////////////////////////////////////////////////////
  3. // //
  4. // Copyright (C) 2010 Phorum Development Team //
  5. // http://www.phorum.org //
  6. // //
  7. // This program is free software. You can redistribute it and/or modify //
  8. // it under the terms of either the current Phorum License (viewable at //
  9. // phorum.org) or the Phorum License that was distributed with this file //
  10. // //
  11. // This program is distributed in the hope that it will be useful, //
  12. // but WITHOUT ANY WARRANTY, without even the implied warranty of //
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. //
  14. // //
  15. // You should have received a copy of the Phorum License //
  16. // along with this program. //
  17. // //
  18. ////////////////////////////////////////////////////////////////////////////////
  19. /**
  20. * This script implements the Phorum user API.
  21. *
  22. * The user API is used for managing users and user related data. The API
  23. * does also implement the Phorum session system, which is used for
  24. * remembering authenticated users. See the documentation for the function
  25. * {@link phorum_api_user_session_create()} for more information on
  26. * Phorum user sessions.
  27. *
  28. * The Phorum user API supports modules which can override Phorum's
  29. * authentication and session handling. And example module is provided
  30. * with the user API documentation.
  31. *
  32. * @package PhorumAPI
  33. * @subpackage UserAPI
  34. * @copyright 2010, Phorum Development Team
  35. * @license Phorum License, http://www.phorum.org/license.txt
  36. *
  37. * @example user_auth_module.php Authentication override module example
  38. *
  39. * @todo Document what fields are in a user record.
  40. *
  41. * @todo Create in line docs for all hook calls.
  42. *
  43. */
  44. if (!defined('PHORUM')) return;
  45. // {{{ Constant and variable definitions
  46. /**
  47. * If a user API is written as a replacement for the standard Phorum user API,
  48. * where the replacement API is incompatible with the standard API, then this
  49. * define should be set to FALSE. That will disable the user management
  50. * functions in the admin interface.
  51. */
  52. define("PHORUM_ORIGINAL_USER_CODE", TRUE);
  53. /**
  54. * Used for identifying long term sessions. The value is used as
  55. * the name for the session cookie for long term sessions.
  56. */
  57. define( 'PHORUM_SESSION_LONG_TERM' , 'phorum_session_v5' );
  58. /**
  59. * Used for identifying short term sessions. The value is used as
  60. * the name for the session cookie for short term sessions
  61. * (this is used by the tighter authentication scheme).
  62. */
  63. define( 'PHORUM_SESSION_SHORT_TERM', 'phorum_session_st' );
  64. /**
  65. * Used for identifying admin sessions. The value is used as
  66. * the name for the session cookie for admin sessions.
  67. */
  68. define( 'PHORUM_SESSION_ADMIN', 'phorum_admin_session' );
  69. /**
  70. * Function call parameter, which tells various functions that
  71. * a front end forum session has to be handled.
  72. */
  73. define('PHORUM_FORUM_SESSION', 1);
  74. /**
  75. * Function call parameter, which tells various functions that
  76. * an admin back end session has to be handled.
  77. */
  78. define('PHORUM_ADMIN_SESSION', 2);
  79. /**
  80. * Function call flag, which tells {@link phorum_api_user_set_active_user()}
  81. * that the short term forum session has to be activated.
  82. */
  83. define('PHORUM_FLAG_SESSION_ST', 1);
  84. /**
  85. * Function call flag, which tells {@link phorum_api_user_save()} that the
  86. * password field should be stored as is.
  87. * This can be used to feed Phorum MD5 encrypted passwords. Normally,
  88. * the password field would be MD5 encrypted by the function. This will
  89. * keep the phorum_api_user_save() function from double encrypting the password.
  90. */
  91. define('PHORUM_FLAG_RAW_PASSWORD', 1);
  92. /**
  93. * Function call flag, which tells {@link phorum_api_user_get_display_name()}
  94. * that the returned display names have to be HTML formatted, so they can be
  95. * used for showing the name in HTML pages.
  96. */
  97. define('PHORUM_FLAG_HTML', 1);
  98. /**
  99. * Function call flag, which tells {@link phorum_api_user_get_display_name()}
  100. * that the returned display names should be stripped down to plain text
  101. * format, so they can be used for showing the name in things like mail
  102. * messages and message quoting.
  103. */
  104. define('PHORUM_FLAG_PLAINTEXT', 2);
  105. /**
  106. * Function call parameter, which tells {@link phorum_api_user_session_create()}
  107. * that session ids have to be reset to new values as far as that is sensible
  108. * for a newly logged in user.
  109. */
  110. define('PHORUM_SESSID_RESET_LOGIN', 1);
  111. /**
  112. * Function call parameter, which tells {@link phorum_api_user_session_create()}
  113. * that all session ids have to be reset to new values. This is for example
  114. * appropriate after a user changed the password (so active sessions on
  115. * other computers or browsers will be ended).
  116. */
  117. define('PHORUM_SESSID_RESET_ALL', 2);
  118. /**
  119. * Function call parameter, which tells {@link phorum_api_user_get_list()}
  120. * that all users have to be returned.
  121. */
  122. define('PHORUM_GET_ALL', 0);
  123. /**
  124. * Function call parameter, which tells {@link phorum_api_user_get_list()}
  125. * that all active users have to be returned.
  126. */
  127. define('PHORUM_GET_ACTIVE', 1);
  128. /**
  129. * Function call parameter, which tells {@link phorum_api_user_get_list()}
  130. * that all inactive users have to be returned.
  131. */
  132. define('PHORUM_GET_INACTIVE', 2);
  133. /**
  134. * Function call parameter, which tells {@link phorum_api_user_check_access()}
  135. * and {@link phorum_api_user_check_group_access()} to return an array
  136. * of respectively forums or groups for which a user is granted access.
  137. */
  138. define('PHORUM_ACCESS_LIST', -1);
  139. /**
  140. * Function call parameter, which tells {@link phorum_api_user_check_access()}
  141. * and {@link phorum_api_user_check_group_access()} to check if the user
  142. * is granted access for respectively any forum or group.
  143. */
  144. define('PHORUM_ACCESS_ANY', -2);
  145. /**
  146. * User status, indicating that the user has not yet confirmed the registration
  147. * by email and that a user moderator will have to approve the registration
  148. * as well.
  149. */
  150. define("PHORUM_USER_PENDING_BOTH", -3);
  151. /**
  152. * User status, indicating that the user has not yet confirmed the registration
  153. * by email.
  154. */
  155. define("PHORUM_USER_PENDING_EMAIL", -2);
  156. /**
  157. * User status, indicating that the registration has not yet been approved
  158. * by a user moderator.
  159. */
  160. define("PHORUM_USER_PENDING_MOD", -1);
  161. /**
  162. * User status, indicating that the user has been deactivated.
  163. */
  164. define("PHORUM_USER_INACTIVE", 0);
  165. /**
  166. * User status, indicating that the registration has been completed and that
  167. * the user can access the forums.
  168. */
  169. define("PHORUM_USER_ACTIVE", 1);
  170. /**
  171. * Permission flag which allows users to read forum messages.
  172. */
  173. define('PHORUM_USER_ALLOW_READ', 1);
  174. /**
  175. * Permission flag which allows users to reply to forum messages.
  176. */
  177. define('PHORUM_USER_ALLOW_REPLY', 2);
  178. /**
  179. * Permission flag which allows users to edit their own forum messages.
  180. */
  181. define('PHORUM_USER_ALLOW_EDIT', 4);
  182. /**
  183. * Permission flag which allows users to start new forum topics.
  184. */
  185. define('PHORUM_USER_ALLOW_NEW_TOPIC', 8);
  186. /**
  187. * Permission flag which allows users to attach files to their forum messages.
  188. */
  189. define('PHORUM_USER_ALLOW_ATTACH', 32);
  190. /**
  191. * Permission flag which allows users to edit other users' messages.
  192. */
  193. define('PHORUM_USER_ALLOW_MODERATE_MESSAGES', 64);
  194. /**
  195. * Permission flag which allows users to moderate user signup
  196. * requests within the vroot.
  197. */
  198. define('PHORUM_USER_ALLOW_MODERATE_USERS', 128);
  199. /**
  200. * Group permission flag for users which are suspended by a group moderator.
  201. */
  202. define('PHORUM_USER_GROUP_SUSPENDED', -1);
  203. /**
  204. * Group permission flag for users which are not yet approved by
  205. * a group moderator.
  206. */
  207. define('PHORUM_USER_GROUP_UNAPPROVED', 0);
  208. /**
  209. * Group permission flag for users which are active approved group members.
  210. */
  211. define('PHORUM_USER_GROUP_APPROVED', 1);
  212. /**
  213. * Group permission flag for users which are group moderator.
  214. */
  215. define('PHORUM_USER_GROUP_MODERATOR', 2);
  216. /**
  217. * Subscription type, which tells Phorum explicitly that the user
  218. * does not have a subscription of any kind for the forum or thread.
  219. */
  220. define("PHORUM_SUBSCRIPTION_NONE", -1);
  221. /**
  222. * Subscription type, which tells Phorum to send out a mail message for
  223. * every new forum or thread that a user is subscribed to.
  224. */
  225. define("PHORUM_SUBSCRIPTION_MESSAGE", 0);
  226. /**
  227. * Subscription type, which tells Phorum to periodially send a mail message,
  228. * containing a list of new messages in forums or threads that a user is
  229. * subscribed to. There is currently no support for this type of subscription
  230. * in the Phorum core code.
  231. */
  232. define("PHORUM_SUBSCRIPTION_DIGEST", 1);
  233. /**
  234. * Subscription type, which tells Phorum to make the forums or threads that
  235. * a user is subscribed to accessible from the followed threads interface in
  236. * the control center. No mail is sent for new messages, but the user can
  237. * check for new messages using that interface.
  238. */
  239. define("PHORUM_SUBSCRIPTION_BOOKMARK", 2);
  240. /**
  241. * This array describes user data fields. It is mainly used internally
  242. * for configuring how to handle the fields and for doing checks on them.
  243. */
  244. $GLOBALS['PHORUM']['API']['user_fields'] = array
  245. (
  246. // Fields that are really in the Phorum users table.
  247. 'user_id' => 'int',
  248. 'username' => 'string',
  249. 'real_name' => 'string',
  250. 'display_name' => 'string',
  251. 'password' => 'string',
  252. 'password_temp' => 'string',
  253. 'sessid_lt' => 'string',
  254. 'sessid_st' => 'string',
  255. 'sessid_st_timeout' => 'string',
  256. 'email' => 'string',
  257. 'email_temp' => 'string',
  258. 'hide_email' => 'bool',
  259. 'active' => 'int',
  260. 'admin' => 'bool',
  261. 'signature' => 'string',
  262. 'posts' => 'int',
  263. 'date_added' => 'int',
  264. 'date_last_active' => 'int',
  265. 'last_active_forum' => 'int',
  266. 'threaded_list' => 'int',
  267. 'threaded_read' => 'int',
  268. 'hide_activity' => 'bool',
  269. 'show_signature' => 'bool',
  270. 'email_notify' => 'int',
  271. 'pm_email_notify' => 'bool',
  272. 'tz_offset' => 'float',
  273. 'is_dst' => 'bool',
  274. 'user_language' => 'string',
  275. 'user_template' => 'string',
  276. 'moderation_email' => 'bool',
  277. 'moderator_data' => 'array',
  278. 'settings_data' => 'array',
  279. // Fields that are used for passing on information about user related,
  280. // data, which is not stored in a standard user table field.
  281. 'forum_permissions' => 'array',
  282. // Fields that we do not use for saving data (yet?), but which might
  283. // be in the user data (e.g. if we store a user data array like it was
  284. // returned by phorum_api_user_get()).
  285. 'groups' => NULL,
  286. 'group_permissions' => NULL,
  287. 'permissions' => NULL,
  288. );
  289. // }}}
  290. // ----------------------------------------------------------------------
  291. // Handling user data.
  292. // ----------------------------------------------------------------------
  293. // {{{ Function: phorum_api_user_save()
  294. /**
  295. * Create or update Phorum users.
  296. *
  297. * This function can be used for both creating and updating Phorum users.
  298. * If the user_id in the user data is NULL, a new user will be created.
  299. * If a user_id is provided, then the existing user will be updated or a
  300. * new user with that user_id is created.
  301. *
  302. * Often when calling this function yourself, you will be doing that for
  303. * synchronizing a user from some external system with the Phorum database.
  304. * For those cases, the most basic use of this API function can be found
  305. * in the examples below.
  306. * <code>
  307. * $user = array(
  308. * "user_id" => 1234,
  309. * "username" => 'johndoe',
  310. * "password" => '#barbar#',
  311. * "email" => 'john.doe@example.com',
  312. * "admin" => 0,
  313. * "active" => PHORUM_USER_ACTIVE
  314. * );
  315. * phorum_api_user_save($user);
  316. * </code>
  317. *
  318. * If you do not have the plain text password available, but only an MD5
  319. * hash for the password, then you can use the following code instead.
  320. * <code>
  321. * $user = array(
  322. * "user_id" => 1234,
  323. * "username" => 'johndoe',
  324. * "password" => '5d61ed116ffdecf2d29cd1ed9bd9d4cb',
  325. * "email" => 'john.doe@example.com',
  326. * "admin" => 0,
  327. * "active" => PHORUM_USER_ACTIVE
  328. * );
  329. * phorum_api_user_save($user, PHORUM_FLAG_RAW_PASSWORD);
  330. * </code>
  331. *
  332. * @param array $user
  333. * An array containing user data. This array should at least contain
  334. * a field "user_id". This field can be NULL to create a new user
  335. * with an automatically assigned user_id. It can also be set to a
  336. * user_id to either update an existing user or to create a new user
  337. * with the provided user_id.
  338. * If a new user is created, then all user fields must be provided
  339. * in the user data.
  340. *
  341. * @param int $flags
  342. * If the flag {@link PHORUM_FLAG_RAW_PASSWORD} is set, then the
  343. * password fields ("password" and "password_temp") are considered to be
  344. * MD5 encrypted already. So this can be used to feed Phorum existing MD5
  345. * encrypted passwords.
  346. *
  347. * @return int
  348. * The user_id of the user. For new users, the newly assigned user_id
  349. * will be returned.
  350. */
  351. function phorum_api_user_save($user, $flags = 0)
  352. {
  353. global $PHORUM;
  354. include_once('./include/api/custom_profile_fields.php');
  355. // $user must be an array.
  356. if (!is_array($user)) {
  357. trigger_error(
  358. 'phorum_api_user_save(): $user argument is not an array',
  359. E_USER_ERROR
  360. );
  361. return NULL;
  362. }
  363. // We need at least the user_id field.
  364. if (!array_key_exists('user_id', $user)) {
  365. trigger_error(
  366. 'phorum_api_user_save(): missing field "user_id" in user data array',
  367. E_USER_ERROR
  368. );
  369. return NULL;
  370. }
  371. if ($user['user_id'] !== NULL && !is_numeric($user['user_id'])) {
  372. trigger_error(
  373. 'phorum_api_user_save(): field "user_id" not NULL or numerical',
  374. E_USER_ERROR
  375. );
  376. return NULL;
  377. }
  378. // Check if we are handling an existing or new user.
  379. $existing = NULL;
  380. if ($user['user_id'] !== NULL) {
  381. $existing = phorum_api_user_get($user['user_id'], TRUE, TRUE, TRUE);
  382. }
  383. // Create a user data array that is understood by the database layer.
  384. // We start out with the existing record, if we have one.
  385. $dbuser = $existing === NULL ? array() : $existing;
  386. // Merge in the fields from the $user argument.
  387. foreach ($user as $fld => $val) {
  388. $dbuser[$fld] = $val;
  389. }
  390. // Initialize storage for custom profile field data.
  391. $user_data = array();
  392. // Check and format the user data fields.
  393. foreach ($dbuser as $fld => $val)
  394. {
  395. // Determine the field type that we are handling.
  396. $fldtype = NULL;
  397. $custom = NULL;
  398. // Check if we are handling a custom profile field. We asume that any
  399. // field that is not in the user_fields array is a custom profile
  400. // field. If we find that it isn't such field, then we will ignore
  401. // the field (it's either a field that was manually added to the
  402. // user table or a custom profile field that was just deleted).
  403. if (!array_key_exists($fld, $PHORUM['API']['user_fields'])) {
  404. $custom = phorum_api_custom_profile_field_byname($fld);
  405. if ($custom === NULL) {
  406. $fldtype = 'ignore_field';
  407. } else {
  408. $fldtype = 'custom_profile_field';
  409. }
  410. } else {
  411. $fldtype = $PHORUM['API']['user_fields'][$fld];
  412. }
  413. switch ($fldtype)
  414. {
  415. // A field that has to be fully ignored.
  416. case NULL:
  417. break;
  418. case 'int':
  419. $dbuser[$fld] = $val === NULL ? NULL : (int) $val;
  420. break;
  421. case 'float':
  422. $dbuser[$fld] = $val === NULL ? NULL : (float) $val;
  423. break;
  424. case 'string':
  425. $dbuser[$fld] = $val === NULL ? NULL : trim($val);
  426. break;
  427. case 'bool':
  428. $dbuser[$fld] = $val ? 1 : 0;
  429. break;
  430. case 'array':
  431. // TODO: maybe check for real arrays here?
  432. $dbuser[$fld] = $val;
  433. break;
  434. case 'custom_profile_field':
  435. // Arrays and NULL values are left untouched.
  436. // Other values are truncated to their configured field length.
  437. if ($val !== NULL && !is_array($val)) {
  438. $val = substr($val, 0, $custom['length']);
  439. }
  440. $user_data[$custom['id']] = $val;
  441. unset($dbuser[$fld]);
  442. break;
  443. case 'ignore_field':
  444. unset($dbuser[$fld]);
  445. break;
  446. default:
  447. trigger_error(
  448. 'phorum_api_user_save(): Illegal field type used: ' .
  449. htmlspecialchars($fldtype),
  450. E_USER_ERROR
  451. );
  452. return NULL;
  453. break;
  454. }
  455. }
  456. // Add the custom profile field data to the user data.
  457. $dbuser['user_data'] = $user_data;
  458. // At this point, we should have a couple of mandatory fields available
  459. // in our data. Without these fields, the user record is not sane
  460. // enough to continue with.
  461. // We really need a username, so we can always generate a display name.
  462. if (!isset($dbuser['username']) || $dbuser['username'] == '') {
  463. trigger_error(
  464. 'phorum_api_user_save(): the username field for a user record ' .
  465. 'cannot be empty',
  466. E_USER_ERROR
  467. );
  468. return NULL;
  469. }
  470. // Phorum sends out mail messages on several occasions. So we need a
  471. // mail address for the user.
  472. if (!isset($dbuser['email']) || $dbuser['email'] == '') {
  473. trigger_error(
  474. 'phorum_api_user_save(): the email field for a user record ' .
  475. 'cannot be empty',
  476. E_USER_ERROR
  477. );
  478. return NULL;
  479. }
  480. // For new accounts only.
  481. if (!$existing)
  482. {
  483. if (empty($dbuser['date_added']))
  484. $dbuser['date_added'] = time();
  485. if (empty($dbuser['date_last_active']))
  486. $dbuser['date_last_active'] = time();
  487. }
  488. // Handle password encryption.
  489. foreach (array('password', 'password_temp') as $fld)
  490. {
  491. // Sometimes, this function is (accidentally) called with existing
  492. // passwords in the data. Prevent duplicate encryption.
  493. if ($existing && strlen($existing[$fld]) == 32 &&
  494. $existing[$fld] == $dbuser[$fld]) {
  495. continue;
  496. }
  497. // If the password field is empty, we should never store the MD5 sum
  498. // of an empty string as a safety precaution. Instead we store a
  499. // string which will never work as a password. This could happen in
  500. // case of bugs in the code or in case external user auth is used
  501. // (in which case Phorum can have empty passwords, since the Phorum
  502. // passwords are not used at all).
  503. if (!isset($dbuser[$fld]) || $dbuser[$fld] === NULL ||
  504. $dbuser[$fld] == '' || $dbuser[$fld] == '*NO PASSWORD SET*') {
  505. $dbuser[$fld] = '*NO PASSWORD SET*';
  506. continue;
  507. }
  508. // Only crypt the password using MD5, if the PHORUM_FLAG_RAW_PASSWORD
  509. // flag is not set.
  510. if (!($flags & PHORUM_FLAG_RAW_PASSWORD)) {
  511. $dbuser[$fld] = md5($dbuser[$fld]);
  512. }
  513. }
  514. // Determine the display name to use for the user. If the setting
  515. // $PHORUM["custom_display_name"] is enabled (a "secret" setting which
  516. // cannot be changed through the admin settings, but only through
  517. // modules that consciously set it), then Phorum expects that the display
  518. // name is a HTML formatted display_name field, which is provided by
  519. // 3rd party software. Otherwise, the username or real_name is used
  520. // (depending on the $PHORUM["display_name_source"] Phorum setting).
  521. if (empty($PHORUM['custom_display_name'])) {
  522. $display_name = $dbuser['username'];
  523. if ($PHORUM['display_name_source'] == 'real_name' &&
  524. isset($dbuser['real_name']) &&
  525. trim($dbuser['real_name']) != '') {
  526. $display_name = $dbuser['real_name'];
  527. }
  528. $dbuser['display_name'] = $display_name;
  529. }
  530. // If the 3rd party software provided no or an empty display_name,
  531. // then we save the day by using the username (just so users won't show up
  532. // empty on screen, this should not happen at all in the first place).
  533. // We HTML encode the username, because custom display names are supposed
  534. // to be provided in escaped HTML format.
  535. elseif (!isset($dbuser['display_name']) ||
  536. trim($dbuser['display_name']) == '') {
  537. $dbuser['display_name'] = htmlspecialchars($dbuser['username'], ENT_COMPAT, $PHORUM['DATA']['HCHARSET']);
  538. }
  539. /**
  540. * [hook]
  541. * user_save
  542. *
  543. * [description]
  544. * This hook can be used to handle the data that is going to be
  545. * stored in the database for a user. Modules can do some last
  546. * minute change on the data or keep some external system in sync
  547. * with the Phorum user data.<sbr/>
  548. * <sbr/>
  549. * In combination with the <hook>user_get</hook> hook, this hook
  550. * could also be used to store and retrieve some of the Phorum
  551. * user fields using some external system.
  552. *
  553. * [category]
  554. * User data handling
  555. *
  556. * [when]
  557. * Just before user data is stored in the database.
  558. *
  559. * [input]
  560. * An array containing user data that will be sent to the database.
  561. *
  562. * [output]
  563. * The same array as the one that was used for the hook call
  564. * argument, possibly with some updated fields in it.
  565. *
  566. * [example]
  567. * <hookcode>
  568. * function phorum_mod_foo_user_save($user)
  569. * {
  570. * // Add "[A]" in front of admin user real_name fields.
  571. * $A = $user["admin"] ? "[A]" : "";
  572. * $real_name = preg_replace('/^\[A\]/', $A, $user["real_name"]);
  573. * $user['real_name'] = $real_name;
  574. *
  575. * // Some fictional external system to keep in sync.
  576. * include("../coolsys.php");
  577. * coolsys_save($user);
  578. *
  579. * return $user;
  580. * }
  581. * </hookcode>
  582. */
  583. if (isset($PHORUM['hooks']['user_save'])) {
  584. $dbuser = phorum_hook('user_save', $dbuser);
  585. }
  586. /**
  587. * [hook]
  588. * user_register
  589. *
  590. * [description]
  591. * This hook is called when a user registration is completed by
  592. * setting the status for the user to PHORUM_USER_ACTIVE.
  593. * This hook will not be called right after filling in the
  594. * registration form (unless of course, the registration has been
  595. * setup to require no verification at all in which case the user
  596. * becomes active right away).
  597. *
  598. * [category]
  599. * User data handling
  600. *
  601. * [when]
  602. * Right after a new user registration becomes active.
  603. *
  604. * [input]
  605. * An array containing user data for the registered user.
  606. *
  607. * [output]
  608. * The same array as the one that was used for the hook call
  609. * argument, possibly with some updated fields in it.
  610. *
  611. * [example]
  612. * <hookcode>
  613. * function phorum_mod_foo_user_register($user)
  614. * {
  615. * // Log user registrations through syslog.
  616. * openlog("Phorum", LOG_PID | LOG_PERROR, LOG_LOCAL0);
  617. * syslog(LOG_NOTICE, "New user registration: $user[username]");
  618. *
  619. * return $user;
  620. * }
  621. * </hookcode>
  622. */
  623. if (isset($PHORUM['hooks']['user_register']))
  624. {
  625. // Only fire this hook if a user goes from some pending state
  626. // to the active state.
  627. if ($dbuser['active'] == PHORUM_USER_ACTIVE)
  628. {
  629. // For new users we have no existing status. For those we asume
  630. // a pending status, so below a switch to active status will mean
  631. // that the user registration is activated.
  632. $orig_status = $existing
  633. ? $existing['active']
  634. : PHORUM_USER_PENDING_MOD;
  635. if ($orig_status == PHORUM_USER_PENDING_BOTH ||
  636. $orig_status == PHORUM_USER_PENDING_EMAIL ||
  637. $orig_status == PHORUM_USER_PENDING_MOD) {
  638. $dbuser = phorum_hook('user_register', $dbuser);
  639. }
  640. }
  641. }
  642. // Add or update the user in the database.
  643. if ($existing) {
  644. phorum_db_user_save($dbuser);
  645. } else {
  646. $dbuser['user_id'] = phorum_db_user_add($dbuser);
  647. }
  648. // If the display name changed for the user, then we do need to run
  649. // updates throughout the Phorum database to make references to this
  650. // user to show up correctly.
  651. if ($existing && $existing['display_name'] != $dbuser['display_name']) {
  652. phorum_db_user_display_name_updates($dbuser);
  653. }
  654. // If user caching is enabled, we invalidate the cache for this user.
  655. if (!empty($PHORUM['cache_users'])) {
  656. phorum_cache_remove('user', $dbuser['user_id']);
  657. }
  658. // Are we handling the active Phorum user? Then refresh the user data.
  659. if (isset($PHORUM['user']) &&
  660. $PHORUM['user']['user_id'] == $dbuser['user_id']) {
  661. $PHORUM['user'] = phorum_api_user_get($user['user_id'], TRUE, TRUE);
  662. }
  663. return $dbuser['user_id'];
  664. }
  665. // }}}
  666. // {{{ Function: phorum_api_user_save_raw()
  667. /**
  668. * This function quickly updates the Phorum users table, using all fields in
  669. * the user data as real user table fields.
  670. *
  671. * This is the quickest way to update the user table. Care has to be taken
  672. * by the calling function though, to provide the information exactly as the
  673. * Phorum users table expects it. Only use this function if speed is really
  674. * an issue.
  675. *
  676. * @param array $user
  677. * An array containing user data. This array should at least contain
  678. * a field "user_id", pointing to the user_id of an existing user to
  679. * update. Besides "user_id", this array can contain other fields,
  680. * which should all be valid fields from the users table.
  681. */
  682. function phorum_api_user_save_raw($user)
  683. {
  684. if (empty($user['user_id'])) {
  685. trigger_error(
  686. 'phorum_api_user_save_raw(): the user_id field cannot be empty',
  687. E_USER_ERROR
  688. );
  689. return NULL;
  690. }
  691. // This hook is documented in phorum_api_user_save().
  692. if (isset($PHORUM['hooks']['user_save'])) {
  693. $user = phorum_hook('user_save', $user);
  694. }
  695. // Store the data in the database.
  696. phorum_db_user_save($user);
  697. // Invalidate the cache for the user, unless we are only updating
  698. // user activity tracking fields.
  699. if (!empty($GLOBALS['PHORUM']['cache_users']))
  700. {
  701. // Count the number of activity tracking fields in the data.
  702. $count = 1; // count user_id as an activity tracking field
  703. if (array_key_exists('date_last_active', $user)) $count ++;
  704. if (array_key_exists('last_active_forum', $user)) $count ++;
  705. // Invalidate the cache, if there are non-activity tracking fields.
  706. if ($count != count($user)) {
  707. phorum_cache_remove('user', $user['user_id']);
  708. }
  709. }
  710. }
  711. // }}}
  712. // {{{ Function: phorum_api_user_save_settings()
  713. /**
  714. * Create or update user settings for the active Phorum user.
  715. *
  716. * This function can be used to store arbitrairy settings for the active
  717. * Phorum user in the database. The main goal for this function is to store
  718. * user settings which are not available as a Phorum user table field in
  719. * the database. These are settings which do not really belong to the Phorum
  720. * core, but which are for example used for remembering some kind of state
  721. * in a user interface (templates). Since each user interface might require
  722. * different settings, a dynamic settings storage like this is required.
  723. *
  724. * If you are writing modules that need to store data for a user, then please
  725. * do not use this function. Instead, use custom profile fields. The data
  726. * that is stored using this function can be best looked at as if it were
  727. * session data.
  728. *
  729. * @param array $settings
  730. * An array of setting name => value pairs to store as user
  731. * settings in the database.
  732. */
  733. function phorum_api_user_save_settings($settings)
  734. {
  735. global $PHORUM;
  736. // Get the active user's user_id.
  737. if (empty($PHORUM['user']['user_id'])) return;
  738. $user_id = $PHORUM['user']['user_id'];
  739. // The settings data must always be an array.
  740. if (empty($PHORUM['user']['settings_data'])) {
  741. $PHORUM['user']['settings_data'] = array();
  742. }
  743. // Merge the setting with the existing settings.
  744. if (is_array($settings)) {
  745. foreach ($settings as $name => $value) {
  746. if ($value === NULL) {
  747. unset($PHORUM['user']['settings_data'][$name]);
  748. } else {
  749. $PHORUM['user']['settings_data'][$name] = $value;
  750. }
  751. }
  752. }
  753. // Save the settings in the database.
  754. phorum_db_user_save(array(
  755. 'user_id' => $user_id,
  756. 'settings_data' => $PHORUM['user']['settings_data']
  757. ));
  758. // If user caching is enabled, we remove the user from the cache.
  759. if (!empty($GLOBALS['PHORUM']['cache_users'])) {
  760. phorum_cache_remove('user', $user_id);
  761. }
  762. }
  763. // }}}
  764. // {{{ Function: phorum_api_user_get()
  765. /**
  766. * Retrieve data for Phorum users.
  767. *
  768. * @param mixed $user_id
  769. * Either a single user_id or an array of user_ids.
  770. *
  771. * @param boolean $detailed
  772. * If this parameter is TRUE (default is FALSE), then the user's
  773. * groups and permissions are included in the user data.
  774. *
  775. * @param boolean $use_write_server
  776. * This parameter is for internal use only. It is used to flag that
  777. * the database layer has to run the query against the master database
  778. * server (known as the "write server"; only applicable if the database
  779. * system is setup as a replicated master/slave environment). When you
  780. * are using this API call in your own code, then you most probably do
  781. * not need to use this parameter.
  782. *
  783. * @return mixed
  784. * If the $user_id parameter is a single user_id, then either an array
  785. * containing user data is returned or NULL if the user was not found.
  786. * If the $user_id parameter is an array of user_ids, then an array
  787. * of user data arrays is returned, indexed by the user_id.
  788. * Users for user_ids that are not found are not included in the
  789. * returned array.
  790. */
  791. function phorum_api_user_get($user_id, $detailed = FALSE, $use_write_server = FALSE, $raw_data = FALSE)
  792. {
  793. $PHORUM = $GLOBALS['PHORUM'];
  794. if (!is_array($user_id)) {
  795. $user_ids = array($user_id);
  796. } else {
  797. $user_ids = $user_id;
  798. }
  799. // Prepare the return data array. For each requested user_id,
  800. // a slot is prepared in this array. Also, turn the user id array
  801. // into an array which has the user_id as both the key and value.
  802. $users = array();
  803. $new_user_ids = array();
  804. foreach ($user_ids as $id) {
  805. $users[$id] = NULL;
  806. $new_user_ids[$id] = $id;
  807. }
  808. $user_ids = $new_user_ids;
  809. // First, try to retrieve user data from the user cache,
  810. // if user caching is enabled.
  811. if ($raw_data === FALSE && !empty($PHORUM['cache_users']))
  812. {
  813. $cached_users = phorum_cache_get('user', $user_ids);
  814. if (is_array($cached_users))
  815. {
  816. foreach ($cached_users as $id => $user) {
  817. $users[$id] = $user;
  818. unset($user_ids[$id]);
  819. }
  820. // We need to retrieve the data for some dynamic fields
  821. // from the database.
  822. $dynamic_data = phorum_db_user_get_fields(
  823. array_keys($cached_users),
  824. array('date_last_active','last_active_forum','posts')
  825. );
  826. // Store the results in the users array.
  827. foreach ($dynamic_data as $id => $data) {
  828. $users[$id] = array_merge($users[$id],$data);
  829. }
  830. }
  831. }
  832. // Retrieve user data for the users for which no data was
  833. // retrieved from the cache.
  834. if (count($user_ids))
  835. {
  836. $db_users = phorum_db_user_get($user_ids, $detailed, $use_write_server, $raw_data);
  837. foreach ($db_users as $id => $user)
  838. {
  839. // Merge the group and forum permissions into a final
  840. // permission value per forum. Forum permissions that are
  841. // assigned to a user directly override any group based
  842. // permission.
  843. if (!$user['admin']) {
  844. if (!empty($user['group_permissions'])) {
  845. foreach ($user['group_permissions'] as $fid => $perm) {
  846. if (!isset($user['permissions'][$fid])) {
  847. $user['permissions'][$fid] = $perm;
  848. } else {
  849. $user['permissions'][$fid] |= $perm;
  850. }
  851. }
  852. }
  853. if (!empty($user['forum_permissions'])) {
  854. foreach ($user['forum_permissions'] as $fid => $perm) {
  855. $user['permissions'][$fid] = $perm;
  856. }
  857. }
  858. }
  859. // If detailed information was requested, we store the data in
  860. // the cache. For non-detailed information, we do not cache the
  861. // data, because there is not much to gain there by caching.
  862. if ($detailed && !empty($PHORUM['cache_users']) && $raw_data === FALSE) {
  863. phorum_cache_put('user', $id, $user);
  864. }
  865. // Store the results in the users array.
  866. $users[$id] = $user;
  867. }
  868. }
  869. // Remove the users for which we did not find data from the array.
  870. foreach ($users as $id => $user) {
  871. if ($user === NULL) {
  872. unset($users[$id]);
  873. }
  874. }
  875. /**
  876. * [hook]
  877. * user_get
  878. *
  879. * [description]
  880. * This hook can be used to handle the data that was retrieved
  881. * from the database for a user. Modules can add and modify the
  882. * user data.<sbr/>
  883. * <sbr/>
  884. * In combination with the <hook>user_save</hook> hook, this hook
  885. * could also be used to store and retrieve some of the Phorum
  886. * user fields in some external system
  887. *
  888. * [category]
  889. * User data handling
  890. *
  891. * [when]
  892. * Just after user data has been retrieved from the database.
  893. *
  894. * [input]
  895. * This hook receives two arguments.<sbr/>
  896. * The first argument contains an array of users.
  897. * Each item in this array is an array containing data for
  898. * a single user, which can be updated.<sbr/>
  899. * The second argument contains a boolean that indicates whether
  900. * detailed information (i.e. including group info) is retrieved.
  901. *
  902. * [output]
  903. * The array that was used as the first argument for the hook call,
  904. * possibly with some updated users in it.
  905. *
  906. * [example]
  907. * <hookcode>
  908. * function phorum_mod_foo_user_get($user, $detailed)
  909. * {
  910. * // Let's asume that our usernames are based on the
  911. * // system users on a UNIX system. We could merge some
  912. * // info from the password file with the Phorum info here.
  913. *
  914. * // First try to lookup the password file entry.
  915. * // Return if this lookup fails.
  916. * $pw = posix_getpwnam($user['username']);
  917. * if (empty($pw)) return $user;
  918. *
  919. * // On a lot of systems, the "gecos" field contains
  920. * // the real name for the user.
  921. * $user['real_name'] = $pw["gecos"] != ''
  922. * ? $pw["gecos"]
  923. * : $user["real_name"];
  924. *
  925. * // If a custom profile field "shell" was created, then
  926. * // we could also put the user's shell in the data.
  927. * $user['shell'] = $pw['shell'];
  928. *
  929. * return $user;
  930. * }
  931. * </hookcode>
  932. */
  933. if (isset($PHORUM['hooks']['user_get'])) {
  934. $users = phorum_hook('user_get', $users, $detailed);
  935. }
  936. // Return the results.
  937. if (is_array($user_id)) {
  938. return $users;
  939. } else {
  940. return isset($users[$user_id]) ? $users[$user_id] : NULL;
  941. }
  942. }
  943. // }}}
  944. // {{{ Function: phorum_api_user_get_setting()
  945. /**
  946. * This function can be used to retrieve the value for a user setting
  947. * that was stored by the {@link phorum_api_user_save_settings()} function
  948. * for the active Phorum user.
  949. *
  950. * @param string $name
  951. * The name of the setting for which to retrieve the setting value.
  952. *
  953. * @return mixed
  954. * The value of the setting or NULL if it is not available.
  955. */
  956. function phorum_api_user_get_setting($name)
  957. {
  958. $PHORUM = $GLOBALS['PHORUM'];
  959. // No settings available at all?
  960. if (empty($PHORUM['user']['settings_data'])) return NULL;
  961. // The setting is available.
  962. if (array_key_exists($name, $PHORUM['user']['settings_data'])) {
  963. return $PHORUM['user']['settings_data'][$name];
  964. } else {
  965. return NULL;
  966. }
  967. }
  968. // }}}
  969. // {{{ Function: phorum_api_user_get_display_name()
  970. /**
  971. * Retrieve the display name to use for one or more users.
  972. *
  973. * The name to use depends on the "display_name_source" setting. This
  974. * one points to either the username or the real_name field of the
  975. * user. If the display_name is requested for an unknown user, then
  976. * a fallback name will be used.
  977. *
  978. * @param mixed $user_id
  979. * Either a single user_id, an array of user_ids or NULL to use the
  980. * user_id of the active Phorum user.
  981. *
  982. * @param mixed $fallback
  983. * The fallback display name to use in case the user is unknown or NULL
  984. * to use the "AnonymousUser" language string.
  985. *
  986. * @param mixed $flags
  987. * One of {@link PHORUM_FLAG_HTML} (the default) or
  988. * {@link PHORUM_FLAG_PLAINTEXT}. These determine what output format
  989. * is used for the display names.
  990. *
  991. * @return mixed
  992. * If the $user_id parameter was NULL or a single user_id, then this
  993. * function will return a single display name. If it was an array,
  994. * then this function will return an array of display names, indexed
  995. * by user_id.
  996. */
  997. function phorum_api_user_get_display_name($user_id = NULL, $fallback = NULL, $flags = PHORUM_FLAG_HTML)
  998. {
  999. $PHORUM = $GLOBALS['PHORUM'];
  1000. if ($fallback === NULL) {
  1001. $fallback = $PHORUM['DATA']['LANG']['AnonymousUser'];
  1002. }
  1003. // Use the user_id for the active user.
  1004. if ($user_id === NULL) {
  1005. $user_id = $PHORUM['user']['user_id'];
  1006. }
  1007. // From here on, we need an array of user_ids to lookup.
  1008. $user_ids = is_array($user_id) ? $user_id : array((int)$user_id);
  1009. // Lookup the users.
  1010. $users = phorum_api_user_get($user_ids, FALSE);
  1011. // Determine the display names.
  1012. $display_names = array();
  1013. foreach ($user_ids as $id)
  1014. {
  1015. $display_name = empty($users[$id])
  1016. ? $fallback
  1017. : $users[$id]['display_name'];
  1018. // Generate HTML based display names.
  1019. if ($flags == PHORUM_FLAG_HTML)
  1020. {
  1021. // If the setting $PHORUM["custom_display_name"] is enabled,
  1022. // then Phorum expects that the display name is a HTML
  1023. // formatted display_name field, which is provided by
  1024. // 3rd party software. So those do not have to be HTML escaped.
  1025. // Other names do have to be escaped.
  1026. if (empty($users[$id]) || empty($PHORUM['custom_display_name']))
  1027. {
  1028. $display_name = htmlspecialchars($display_name, ENT_COMPAT, $PHORUM['DATA']['HCHARSET']);
  1029. }
  1030. }
  1031. // Generate a plain text version of the display name. This is the
  1032. // display name as it can be found in the database. Only for
  1033. // custom_display_name cases, we need to strip HTML code.
  1034. elseif ($flags == PHORUM_FLAG_PLAINTEXT)
  1035. {
  1036. // Strip tags from the name. These might be in the
  1037. // name if the custom_display_name feature is enabled.
  1038. // So for custom display names we strip the HTML from the
  1039. // display name that we found above.
  1040. if (!empty($PHORUM['custom_display_name']))
  1041. {
  1042. $display_name = trim(strip_tags($display_name));
  1043. // If the name was 100% HTML code (so empty after stripping),
  1044. // then fallback to the default display_name that Phorum
  1045. // would use without the custom display name feature.
  1046. if ($display_name == '') {
  1047. if (empty($users[$id])) {
  1048. $display_name = $fallback;
  1049. } else {
  1050. $display_name = $users[$id]['username'];
  1051. if ($PHORUM['display_name_source'] == 'real_name' &&
  1052. trim($users[$id]['real_name']) != '') {
  1053. $display_name = $users[$id]['real_name'];
  1054. }
  1055. }
  1056. }
  1057. }
  1058. }
  1059. $display_names[$id] = $display_name;
  1060. }
  1061. if (is_array($user_id)) {
  1062. return $display_names;
  1063. } else {
  1064. return $display_names[$user_id];
  1065. }
  1066. }
  1067. // }}}
  1068. // {{{ Function: phorum_api_user_search()
  1069. /**
  1070. * Search for users, based on simple search conditions, which act on
  1071. * fields in the user table.
  1072. *
  1073. * The parameters $field, $value and $operator (which are used for defining
  1074. * the search condition) can be arrays or single values. If arrays are used,
  1075. * then all three parameter arrays must contain the same number of elements
  1076. * and the keys in the arrays must be the same.
  1077. *
  1078. * @param mixed $field
  1079. * The user table field (string) or fields (array) to search on.
  1080. *
  1081. * @param mixed $value
  1082. * The value (string) or values (array) to search for.
  1083. *
  1084. * @param mixed $operator
  1085. * The operator (string) or operators (array) to use. Valid operators are
  1086. * "=", "!=", "<>", "<", ">", ">=" and "<=", "*". The
  1087. * "*" operator is for executing a "LIKE '%value%'" matching query.
  1088. *
  1089. * @param boolean $return_array
  1090. * If this parameter has a true value, then an array of all matching
  1091. * user_ids will be returned. Else, a single user_id will be returned.
  1092. *
  1093. * @param string $type
  1094. * The type of search to perform. This can be one of:
  1095. * - AND match against all fields
  1096. * - OR match against any of the fields
  1097. *
  1098. * @param mixed $sort
  1099. * The field (string) or fields (array) to sort the results by. For
  1100. * ascending sort, "fieldname" or "+fieldname" can be used. For
  1101. * descending sort, "-fieldname" can be used. By default, the results
  1102. * will be sorted by user_id.
  1103. *
  1104. * @param integer $offset
  1105. * The result page offset starting with 0.
  1106. *
  1107. * @param integer $length
  1108. * The result page length (nr. of results per page)
  1109. * or 0 (zero, the default) to return all results.
  1110. *
  1111. * @param boolean $count_only
  1112. * Tells the function to just return the count of results for this
  1113. * search query.
  1114. *
  1115. * @return mixed
  1116. * An array of matching user_ids or a single user_id (based on the
  1117. * $return_array parameter) or a count of results (based on $count_only).
  1118. * If no user_ids can be found at all, then 0 (zero) will be returned.
  1119. */
  1120. function phorum_api_user_search($field, $value, $operator = '=', $return_array = FALSE, $type = 'AND', $sort = NULL, $offset = 0, $length = 0,$count_only=false)
  1121. {
  1122. return phorum_db_user_search($field, $value, $operator, $return_array, $type, $sort, $offset, $length,$count_only);
  1123. }
  1124. // }}}
  1125. // {{{ Function: phorum_api_user_search_custom_profile_field()
  1126. /**
  1127. * Search for users, based on a simple search condition,
  1128. * which can be used to search on custom profile fields.
  1129. *
  1130. * The parameters $field_id, $value and $operator (which are used for defining
  1131. * the search condition) can be arrays or single values. If arrays are used,
  1132. * then all three parameter arrays must contain the same number of elements
  1133. * and the keys in the arrays must be the same.
  1134. *
  1135. * @param mixed $field_id
  1136. * The custom profile field id (integer) or ids (array) to search on.
  1137. *
  1138. * @param mixed $value
  1139. * The value (string) or values (array) to search for.
  1140. *
  1141. * @param mixed $operator
  1142. * The operator (string) or operators (array) to use. Valid operators are
  1143. * "=", "!=", "<>", "<", ">", ">=" and "<=", "*". The
  1144. * "*" operator is for executing a "LIKE '%value%'" matching query.
  1145. *
  1146. * @param boolean $return_array
  1147. * If this parameter has a true value, then an array of all matching
  1148. * user_ids will be returned. Else, a single user_id will be returned.
  1149. *
  1150. * @param string $type
  1151. * The type of search to perform. This can be one of:
  1152. * - AND match against all fields
  1153. * - OR match against any of the fields
  1154. *
  1155. * @param integer $offset
  1156. * The result page offset starting with 0.
  1157. *
  1158. * @param integer $length
  1159. * The result page length (nr. of results per page)
  1160. * or 0 (zero, the default) to return all results.
  1161. *
  1162. * @return mixed
  1163. * An array of matching user_ids or a single user_id (based on the
  1164. * $return_array parameter). If no user_ids can be found at all,
  1165. * then 0 (zero) will be returned.
  1166. */
  1167. function phorum_api_user_search_custom_profile_field($field_id, $value, $operator = '=', $return_array = FALSE, $type = 'AND', $offset = 0, $length = 0)
  1168. {
  1169. return phorum_db_user_search_custom_profile_field($field_id, $value, $operator, $return_array, $type, $offset, $length);
  1170. }
  1171. // }}}
  1172. // {{{ Function: phorum_api_user_list()
  1173. /**
  1174. * Retrieve a list of Phorum users.
  1175. *
  1176. * @param int $type
  1177. * One of:
  1178. * - {@link PHORUM_GET_ALL}: retrieve a list of all users (the default)
  1179. * - {@link PHORUM_GET_ACTIVE}: retrieve a list of all active users
  1180. * - {@link PHORUM_GET_INACTIVE}: retrieve a list of all inactive users
  1181. *
  1182. * @return array
  1183. * An array of users, indexed by user_id. Each element in the array
  1184. * is an array, containing the fields "user_id", "username" and
  1185. * "display_name".
  1186. *
  1187. * @todo Do we really need phorum_api_user_list() or could we use the
  1188. * phorum_api_user_search() functionality in combination with
  1189. * phorum_api_user_get() instead?
  1190. */
  1191. function phorum_api_user_list($type = PHORUM_GET_ALL)
  1192. {
  1193. // Retrieve a list of users from the database.
  1194. $list = phorum_db_user_get_list($type);
  1195. /**
  1196. * [hook]
  1197. * user_list
  1198. *
  1199. * [description]
  1200. *
  1201. * This hook can be used for reformatting the list of users that
  1202. * is returned by the phorum_api_user_list() function. Reformatting
  1203. * could mean things like changing the sort order or modifying the
  1204. * fields in the user arrays.
  1205. *
  1206. * [category]
  1207. * User data handling
  1208. *
  1209. * [when]
  1210. * Each time the phorum_api_user_list() function is called. The core
  1211. * Phorum code calls the function for creating user drop down lists
  1212. * (if those are enabled in the Phorum general settings) for the
  1213. * group moderation interface in the control center and for sending
  1214. * private messages.
  1215. *
  1216. * [input]
  1217. * An array of user info arrays. Each user info array contains the
  1218. * fields "user_id", "username" and "display_name". The hook function
  1219. * is allowed to update the "username" and "display_name" fields.
  1220. *
  1221. * [output]
  1222. * The same array as was used for the hook call argument,
  1223. * possibly with some updated fields in it.
  1224. *
  1225. * [example]
  1226. * <hookcode>
  1227. * function phorum_mod_foo_user_list($users)
  1228. * {
  1229. * // Only run this hook code for authenticated users.
  1230. * if (empty($PHORUM["user"]["user_id"])) return $users;
  1231. *
  1232. * // Retrieve a list of buddies for the active user.
  1233. * // If there are no buddies, then no work is needed.
  1234. * $buddies = phorum_db_pm_buddy_list();
  1235. * if (empty($buddies)) return $users;
  1236. *
  1237. * // Flag buddies in the user list.
  1238. * $langstr = $GLOBALS["PHORUM"]["DATA"]["LANG"]["Buddy"];
  1239. * foreach ($buddies as $user_id => $info) {
  1240. * $users[$user_id]["display_name"] .= " ($langstr)";
  1241. * }
  1242. *
  1243. * return $users;
  1244. * }
  1245. * </hookcode>
  1246. */
  1247. if (isset($GLOBALS['PHORUM']['hooks']['user_list'])) {
  1248. $list = phorum_hook('user_list', $list);
  1249. }
  1250. return $list;
  1251. }
  1252. // }}}
  1253. // {{{ Function: phorum_api_user_increment_posts()
  1254. /**
  1255. * Increment the posts counter for a user.
  1256. *
  1257. * @param mixed $user_id
  1258. * The user_id for which to increment the posts counter
  1259. * or NULL (the default…

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