PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/include/api/custom_profile_fields.php

https://bitbucket.org/webop/webop-forum
PHP | 411 lines | 179 code | 41 blank | 191 comment | 35 complexity | 2993bf9b3e3018f43ccf2e122cdd50bd MD5 | raw file
Possible License(s): LGPL-2.1
  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 custom profile fields API.
  21. *
  22. * Custom profile fields are a way of dynamically extending the available
  23. * data fields for a user, without having to extend the user database table
  24. * with additional fields.
  25. *
  26. * This API can be used for handling the configuration of these custom
  27. * profile fields. The actual use of the fields is fully integrated in the
  28. * Phorum user API.
  29. *
  30. * @package PhorumAPI
  31. * @subpackage CustomProfileFieldAPI
  32. * @copyright 2010, Phorum Development Team
  33. * @license Phorum License, http://www.phorum.org/license.txt
  34. */
  35. if (!defined('PHORUM')) return;
  36. // {{{ Constant and variable definitions
  37. // Reserved custom profile field names.
  38. $GLOBALS['PHORUM']['API']['cpf_reserved'] = array(
  39. 'panel', 'name', 'value', 'error'
  40. );
  41. /**
  42. * The maximum size that can be used for storing data for a single
  43. * custom profile field.
  44. */
  45. define('PHORUM_MAX_CPLENGTH', 65000);
  46. // }}}
  47. // {{{ Function: phorum_api_custom_profile_field_configure
  48. /**
  49. * Create or update the configuration for a custom profile field.
  50. *
  51. * @param array $field
  52. * This parameter holds the field configuration to save. This array
  53. * must contain the following fields:
  54. *
  55. * - id: If a new field has to be created, then use NULL for this field.
  56. * If a profile field has to be updated, then use the existing
  57. * profile field's id.
  58. *
  59. * - name: The name that has to be assigned to the custom profile field.
  60. * This name can only contain letters, numbers and underscores
  61. * (_) and it has to start with a letter.
  62. *
  63. * The following fields are optional. If they are missing, then a default
  64. * value will be used for them.
  65. *
  66. * - length: The maximum length for the field data. This will make sure
  67. * that the data that is stored in the custom profile field will
  68. * be truncated in case its length surpasses the configured
  69. * custom profile field length. If this field is missing or set
  70. * to NULL, then the default length 255 will be used.
  71. *
  72. * - html_disabled: If this field is set to a true value, then
  73. * special HTML characters are not usable in this field. When
  74. * displaying the custom field's data, Phorum will automatically
  75. * escape these characters. Only use a false value for this
  76. * field if the data that will be saved in the field is really safe
  77. * for direct use in a web page (to learn about the security risks
  78. * involved, search for "XSS" and "cross site scripting" on
  79. * the internet). If this field is missing or set to NULL, then
  80. * the default setting TRUE will be used.
  81. *
  82. * - show_in_admin: If this field is set to a true value, then the field
  83. * will be displayed on the details page for a user in the admin
  84. * "Edit Users" section. If this field is missing or set to NULL,
  85. * then the default setting FALSE will be used.
  86. *
  87. * @return array
  88. * This function returns the profile field data in an array, containing
  89. * the same fields as the {@link $field} function parameter. If a new
  90. * field was created, then the "file_id" field will be set to the new
  91. * custom profile field id. The fields "length" and "html_disabled" will
  92. * also be updated to their defaults if they were set to NULL in
  93. * the $field argument.
  94. */
  95. function phorum_api_custom_profile_field_configure($field)
  96. {
  97. global $PHORUM;
  98. // The available fields and their defaults. NULL indicates a mandatory
  99. // field. The field "id" can be NULL though, when creating a new
  100. // custom profile field.
  101. $fields = array(
  102. 'id' => NULL,
  103. 'name' => NULL,
  104. 'length' => 255,
  105. 'html_disabled' => TRUE,
  106. 'show_in_admin' => FALSE
  107. );
  108. // Check if all required fields are in the $field argument.
  109. // Assign default values for missing or NULL fields or trigger
  110. // or an error if the field is mandatory.
  111. foreach ($fields as $f => $default) {
  112. if (!array_key_exists($f, $field)) {
  113. if ($default === NULL) trigger_error(
  114. 'phorum_api_custom_profile_field_configure(): Missing field ' .
  115. "in \$field parameter: $f",
  116. E_USER_ERROR
  117. );
  118. $field[$f] = $default;
  119. }
  120. elseif ($f != 'id' && $field[$f] === NULL) trigger_error(
  121. 'phorum_api_custom_profile_field_configure(): Field $f in ' .
  122. "\$field parameter cannot be NULL",
  123. E_USER_ERROR
  124. );
  125. }
  126. $field['id'] = $field['id'] === NULL ? NULL : (int)$field['id'];
  127. $field['name'] = trim($field['name']);
  128. settype($field['length'], 'int');
  129. settype($field['html_disabled'], 'bool');
  130. settype($field['show_in_admin'], 'bool');
  131. // Check the profile field name.
  132. if (!preg_match('/^[a-z][\w_]*$/i', $field['name'])) {
  133. return phorum_api_error_set(
  134. PHORUM_ERRNO_INVALIDINPUT,
  135. 'Field names can only contain letters, numbers and ' .
  136. 'underscores (_) and they must start with a letter.'
  137. );
  138. }
  139. // Check if the profile field name isn't an internally used name.
  140. // This is either one of the reserved names or a field that is
  141. // already used as a user data field.
  142. if (in_array($field['name'], $PHORUM['API']['cpf_reserved']) ||
  143. isset($GLOBALS['PHORUM']['API']['user_fields'][$field['name']])) {
  144. return phorum_api_error_set(
  145. PHORUM_ERRNO_INVALIDINPUT,
  146. "The name \"{$field['name']}\" is reserved for internal use " .
  147. 'by Phorum. Please choose a different name for your custom ' .
  148. 'profile field.'
  149. );
  150. }
  151. // Check the bounds for the field length.
  152. if ($field['length'] > PHORUM_MAX_CPLENGTH) {
  153. return phorum_api_error_set(
  154. PHORUM_ERRNO_INVALIDINPUT,
  155. "The length \"{$field['length']}\" for the custom profile " .
  156. 'field is too large. The maximum length that can be used ' .
  157. 'is ' . PHORUM_MAX_CPLENGTH . '.'
  158. );
  159. }
  160. if ($field['length'] <= 0) {
  161. return phorum_api_error_set(
  162. PHORUM_ERRNO_INVALIDINPUT,
  163. "The length for the custom profile field must be above zero."
  164. );
  165. }
  166. // For new fields, check if the name isn't already in use.
  167. if ($field['id'] === NULL &&
  168. phorum_api_custom_profile_field_byname($field['name'])) {
  169. return phorum_api_error_set(
  170. PHORUM_ERRNO_INVALIDINPUT,
  171. "A custom profile field with the name \"{$field['name']}\" " .
  172. 'already exists. Please choose a different name for your ' .
  173. 'custom profile field.'
  174. );
  175. }
  176. // For existing fields, check if the field id really exists.
  177. if ($field['id'] !== NULL &&
  178. !isset($PHORUM['PROFILE_FIELDS'][$field['id']])) {
  179. return phorum_api_error_set(
  180. PHORUM_ERRNO_INVALIDINPUT,
  181. "A custom profile field with id \"{$field['id']}\" does not " .
  182. 'exist. Maybe the field was deleted before you updated its ' .
  183. 'settings.'
  184. );
  185. }
  186. // If we have to create a new field, then find a new id for it.
  187. // For indexing, we use the "num_fields" profile field configuration
  188. // setting. This field is more an auto increment index counter than
  189. // the number of fields. For historical reasons, we keep this name
  190. // in here (some module contain code which makes use of num_fields
  191. // directly).
  192. if ($field['id'] === NULL)
  193. {
  194. // Since there are modules meddling with the data, we do not
  195. // fully trust the num_fields. If we see a field with an id
  196. // higher than what's in num_fields, then we move the counter up.
  197. $high = isset($PHORUM['PROFILE_FIELDS']['num_fields'])
  198. ? (int) $PHORUM['PROFILE_FIELDS']['num_fields'] : 0;
  199. foreach ($PHORUM['PROFILE_FIELDS'] as $checkid => $profile_field) {
  200. if ($checkid > $high) $high = $checkid;
  201. }
  202. // Use the next available value as our id.
  203. $field['id'] = $high + 1;
  204. // Update the index.
  205. $PHORUM['PROFILE_FIELDS']['num_fields'] = $field['id'];
  206. }
  207. // Update the profile fields information in the settings.
  208. $PHORUM['PROFILE_FIELDS'][$field['id']] = $field;
  209. phorum_db_update_settings(array(
  210. 'PROFILE_FIELDS' => $PHORUM['PROFILE_FIELDS']
  211. ));
  212. return $field;
  213. }
  214. // }}}
  215. // {{{ Function: phorum_api_custom_profile_field_byname
  216. /**
  217. * Retrieve the information for a custom profile field by its name.
  218. *
  219. * @param string $name
  220. * The name of the profile field to lookup.
  221. *
  222. * @return mixed
  223. * If no profile field could be found for the name, then NULL will
  224. * be returned. Otherwise the field configuration will be returned.
  225. * The field configuration is an array, containing the fields:
  226. * "id", "name", "length" and "html_disabled".
  227. *
  228. * If the field was marked as deleted by the
  229. * {@link phorum_api_custom_profile_field_delete()} function, then the
  230. * field "deleted" will be available and set to a true value.
  231. */
  232. function phorum_api_custom_profile_field_byname($name)
  233. {
  234. foreach ($GLOBALS['PHORUM']['PROFILE_FIELDS'] as $id => $profile_field) {
  235. if ($id !== 'num_fields' && $profile_field['name'] == $name)
  236. {
  237. // Fix custom profile fields that were created the 5.1 way
  238. // (most probably by modules that handle configuration of these
  239. // fields on their own).
  240. if (empty($profile_field['id'])) $profile_field['id'] = $id;
  241. return $profile_field;
  242. }
  243. }
  244. return NULL;
  245. }
  246. // }}}
  247. // {{{ Function: phorum_api_custom_profile_field_delete
  248. /**
  249. * Delete a custom profile field.
  250. *
  251. * @param int $id
  252. * The id of the custom profile field to delete.
  253. *
  254. * @param bool $hard_delete
  255. * If this parameter is set to a false value (the default), then the
  256. * profile field will only be marked as deleted. The configuration
  257. * will be kept intact in the database. This way, we can help admins
  258. * in restoring fields that were deleted by accident.
  259. *
  260. * If it is set to a true value, then the configuration will be
  261. * fully deleted.
  262. */
  263. function phorum_api_custom_profile_field_delete($id, $hard_delete = FALSE)
  264. {
  265. settype($id, "int");
  266. settype($hard_delete, "bool");
  267. // Only act if we really have something to delete.
  268. if (isset($GLOBALS["PHORUM"]["PROFILE_FIELDS"][$id]))
  269. {
  270. if ($hard_delete) {
  271. unset($GLOBALS["PHORUM"]["PROFILE_FIELDS"][$id]);
  272. } else {
  273. $GLOBALS["PHORUM"]["PROFILE_FIELDS"][$id]["deleted"] = TRUE;
  274. }
  275. phorum_db_update_settings(array(
  276. 'PROFILE_FIELDS' => $GLOBALS["PHORUM"]['PROFILE_FIELDS']
  277. ));
  278. }
  279. }
  280. // }}}
  281. // {{{ Function: phorum_api_custom_profile_field_restore
  282. /**
  283. * Restore a previously deleted custom profile field.
  284. *
  285. * If a profile field is deleted, it's settings and data are not deleted.
  286. * The field is only flagged as deleted. This function can be used for
  287. * reverting the delete action.
  288. *
  289. * @param int $id
  290. * The id of the custom profile field to restore.
  291. *
  292. * @return bool
  293. * TRUE if the restore was successfull or FALSE if there was an error.
  294. * The functions {@link phorum_api_strerror()} and
  295. * {@link phorum_api_errno()} can be used to retrieve information about
  296. * the error which occurred.
  297. */
  298. function phorum_api_custom_profile_field_restore($id)
  299. {
  300. settype($id, "int");
  301. if (isset($GLOBALS["PHORUM"]["PROFILE_FIELDS"][$id]))
  302. {
  303. $f = $GLOBALS["PHORUM"]["PROFILE_FIELDS"][$id];
  304. if (isset($f['deleted']) && $f['deleted']) $f['deleted'] = 0;
  305. $GLOBALS["PHORUM"]["PROFILE_FIELDS"][$id] = $f;
  306. phorum_db_update_settings(array(
  307. 'PROFILE_FIELDS' => $GLOBALS["PHORUM"]['PROFILE_FIELDS']
  308. ));
  309. }
  310. else return phorum_api_error_set(
  311. PHORUM_ERRNO_NOTFOUND,
  312. "Unable to restore custom profile field $id: " .
  313. "no configuration found."
  314. );
  315. return TRUE;
  316. }
  317. // }}}
  318. // {{{ Function: phorum_api_custom_profile_field_checkconfig()
  319. /**
  320. * Check and fix the custom profile field configuration.
  321. *
  322. * This function has mainly been implemented for fixing problems that
  323. * are introduced by modules that create custom profile fields on their
  324. * own. Besides that, it was also written to upgrade the profile field
  325. * configuration, because Phorum 5.2 introduced some new fields in
  326. * the config.
  327. */
  328. function phorum_api_custom_profile_field_checkconfig()
  329. {
  330. global $PHORUM;
  331. // Used to find the real maximum used field id.
  332. $max_id = isset($PHORUM['PROFILE_FIELDS']['num_fields'])
  333. ? (int) $PHORUM['PROFILE_FIELDS']['num_fields'] : 0;
  334. foreach ($PHORUM['PROFILE_FIELDS'] as $id => $config)
  335. {
  336. // Keep track of the highest id that we see.
  337. if ($id > $max_id) $max_id = $id;
  338. // The least that should be in the config, is the name of the
  339. // field. If there is no name, then we don't bother at all.
  340. if (!isset($config['name']) || $config['name'] == '') continue;
  341. // 5.2 includes the id in the field configuration.
  342. if (empty($config['id'])) {
  343. $PHORUM['PROFILE_FIELDS'][$id]['id'] = $id;
  344. }
  345. // Some default values.
  346. if (!array_key_exists('length', $config)) {
  347. $PHORUM['PROFILE_FIELDS'][$id]['length'] = 255;
  348. }
  349. if (!array_key_exists('html_disabled', $config)) {
  350. $PHORUM['PROFILE_FIELDS'][$id]['html_disabled'] = TRUE;
  351. }
  352. if (!array_key_exists('show_in_admin', $config)) {
  353. $PHORUM['PROFILE_FIELDS'][$id]['show_in_admin'] = FALSE;
  354. }
  355. // Some typecasting won't hurt.
  356. settype($PHORUM['PROFILE_FIELDS'][$id]['id'], 'int');
  357. settype($PHORUM['PROFILE_FIELDS'][$id]['name'], 'string');
  358. settype($PHORUM['PROFILE_FIELDS'][$id]['length'], 'int');
  359. settype($PHORUM['PROFILE_FIELDS'][$id]['html_disabled'], 'bool');
  360. settype($PHORUM['PROFILE_FIELDS'][$id]['show_in_admin'], 'bool');
  361. }
  362. // Set the maximum field id that we've seen.
  363. $PHORUM['PROFILE_FIELDS']['num_fields'] = $max_id;
  364. // Save the custom profile field settings to the database.
  365. phorum_db_update_settings(array(
  366. 'PROFILE_FIELDS' => $PHORUM['PROFILE_FIELDS']
  367. ));
  368. }
  369. // }}}
  370. ?>