PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/phpBB/phpbb/profilefields/manager.php

https://github.com/Marshalrusty/phpbb
PHP | 480 lines | 289 code | 66 blank | 125 comment | 27 complexity | 8191f55695ff4933ee458b11c29d6de3 MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * @package phpBB3
  5. * @copyright (c) 2014 phpBB Group
  6. * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
  7. *
  8. */
  9. namespace phpbb\profilefields;
  10. /**
  11. * Custom Profile Fields
  12. * @package phpBB3
  13. */
  14. class manager
  15. {
  16. /**
  17. * Auth object
  18. * @var \phpbb\auth\auth
  19. */
  20. protected $auth;
  21. /**
  22. * Database object
  23. * @var \phpbb\db\driver\driver_interface
  24. */
  25. protected $db;
  26. /**
  27. * Event dispatcher object
  28. * @var \phpbb\event\dispatcher
  29. */
  30. protected $dispatcher;
  31. /**
  32. * Request object
  33. * @var \phpbb\request\request
  34. */
  35. protected $request;
  36. /**
  37. * Template object
  38. * @var \phpbb\template\template
  39. */
  40. protected $template;
  41. /**
  42. * Service Collection object
  43. * @var \phpbb\di\service_collection
  44. */
  45. protected $type_collection;
  46. /**
  47. * User object
  48. * @var \phpbb\user
  49. */
  50. protected $user;
  51. protected $fields_table;
  52. protected $fields_language_table;
  53. protected $fields_data_table;
  54. protected $profile_cache = array();
  55. /**
  56. * Construct
  57. *
  58. * @param \phpbb\auth\auth $auth Auth object
  59. * @param \phpbb\db\driver\driver_interface $db Database object
  60. * @param \phpbb\event\dispatcher $dispatcher Event dispatcher object
  61. * @param \phpbb\request\request $request Request object
  62. * @param \phpbb\template\template $template Template object
  63. * @param \phpbb\di\service_collection $type_collection
  64. * @param \phpbb\user $user User object
  65. * @param string $fields_table
  66. * @param string $fields_language_table
  67. * @param string $fields_data_table
  68. */
  69. public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver_interface $db, \phpbb\event\dispatcher $dispatcher, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\di\service_collection $type_collection, \phpbb\user $user, $fields_table, $fields_language_table, $fields_data_table)
  70. {
  71. $this->auth = $auth;
  72. $this->db = $db;
  73. $this->dispatcher = $dispatcher;
  74. $this->request = $request;
  75. $this->template = $template;
  76. $this->type_collection = $type_collection;
  77. $this->user = $user;
  78. $this->fields_table = $fields_table;
  79. $this->fields_language_table = $fields_language_table;
  80. $this->fields_data_table = $fields_data_table;
  81. }
  82. /**
  83. * Assign editable fields to template, mode can be profile (for profile change) or register (for registration)
  84. * Called by ucp_profile and ucp_register
  85. */
  86. public function generate_profile_fields($mode, $lang_id)
  87. {
  88. $sql_where = '';
  89. switch ($mode)
  90. {
  91. case 'register':
  92. // If the field is required we show it on the registration page
  93. $sql_where .= ' AND f.field_show_on_reg = 1';
  94. break;
  95. case 'profile':
  96. // Show hidden fields to moderators/admins
  97. if (!$this->auth->acl_gets('a_', 'm_') && !$this->auth->acl_getf_global('m_'))
  98. {
  99. $sql_where .= ' AND f.field_show_profile = 1';
  100. }
  101. break;
  102. default:
  103. trigger_error('Wrong profile mode specified', E_USER_ERROR);
  104. break;
  105. }
  106. $sql = 'SELECT l.*, f.*
  107. FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . " f
  108. WHERE f.field_active = 1
  109. $sql_where
  110. AND l.lang_id = " . (int) $lang_id . '
  111. AND l.field_id = f.field_id
  112. ORDER BY f.field_order';
  113. $result = $this->db->sql_query($sql);
  114. while ($row = $this->db->sql_fetchrow($result))
  115. {
  116. // Return templated field
  117. $profile_field = $this->type_collection[$row['field_type']];
  118. $tpl_snippet = $profile_field->process_field_row('change', $row);
  119. $this->template->assign_block_vars('profile_fields', array(
  120. 'LANG_NAME' => $this->user->lang($row['lang_name']),
  121. 'LANG_EXPLAIN' => $this->user->lang($row['lang_explain']),
  122. 'FIELD' => $tpl_snippet,
  123. 'FIELD_ID' => $profile_field->get_field_ident($row),
  124. 'S_REQUIRED' => ($row['field_required']) ? true : false,
  125. ));
  126. }
  127. $this->db->sql_freeresult($result);
  128. }
  129. /**
  130. * Build profile cache, used for display
  131. */
  132. protected function build_cache()
  133. {
  134. $this->profile_cache = array();
  135. // Display hidden/no_view fields for admin/moderator
  136. $sql = 'SELECT l.*, f.*
  137. FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . ' f
  138. WHERE l.lang_id = ' . $this->user->get_iso_lang_id() . '
  139. AND f.field_active = 1 ' .
  140. ((!$this->auth->acl_gets('a_', 'm_') && !$this->auth->acl_getf_global('m_')) ? ' AND f.field_hide = 0 ' : '') . '
  141. AND f.field_no_view = 0
  142. AND l.field_id = f.field_id
  143. ORDER BY f.field_order';
  144. $result = $this->db->sql_query($sql);
  145. while ($row = $this->db->sql_fetchrow($result))
  146. {
  147. $this->profile_cache[$row['field_ident']] = $row;
  148. }
  149. $this->db->sql_freeresult($result);
  150. }
  151. /**
  152. * Submit profile field for validation
  153. */
  154. public function submit_cp_field($mode, $lang_id, &$cp_data, &$cp_error)
  155. {
  156. $sql_where = '';
  157. switch ($mode)
  158. {
  159. case 'register':
  160. // If the field is required we show it on the registration page
  161. $sql_where .= ' AND f.field_show_on_reg = 1';
  162. break;
  163. case 'profile':
  164. // Show hidden fields to moderators/admins
  165. if (!$this->auth->acl_gets('a_', 'm_') && !$this->auth->acl_getf_global('m_'))
  166. {
  167. $sql_where .= ' AND f.field_show_profile = 1';
  168. }
  169. break;
  170. default:
  171. trigger_error('Wrong profile mode specified', E_USER_ERROR);
  172. break;
  173. }
  174. $sql = 'SELECT l.*, f.*
  175. FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . ' f
  176. WHERE l.lang_id = ' . (int) $lang_id . "
  177. AND f.field_active = 1
  178. $sql_where
  179. AND l.field_id = f.field_id
  180. ORDER BY f.field_order";
  181. $result = $this->db->sql_query($sql);
  182. while ($row = $this->db->sql_fetchrow($result))
  183. {
  184. $profile_field = $this->type_collection[$row['field_type']];
  185. $cp_data['pf_' . $row['field_ident']] = $profile_field->get_profile_field($row);
  186. $check_value = $cp_data['pf_' . $row['field_ident']];
  187. if (($cp_result = $profile_field->validate_profile_field($check_value, $row)) !== false)
  188. {
  189. // If the result is not false, it's an error message
  190. $cp_error[] = $cp_result;
  191. }
  192. }
  193. $this->db->sql_freeresult($result);
  194. }
  195. /**
  196. * Update profile field data directly
  197. */
  198. public function update_profile_field_data($user_id, $cp_data)
  199. {
  200. if (!sizeof($cp_data))
  201. {
  202. return;
  203. }
  204. $sql = 'UPDATE ' . $this->fields_data_table . '
  205. SET ' . $this->db->sql_build_array('UPDATE', $cp_data) . '
  206. WHERE user_id = ' . (int) $user_id;
  207. $this->db->sql_query($sql);
  208. if (!$this->db->sql_affectedrows())
  209. {
  210. $cp_data = $this->build_insert_sql_array($cp_data);
  211. $cp_data['user_id'] = (int) $user_id;
  212. $this->db->sql_return_on_error(true);
  213. $sql = 'INSERT INTO ' . $this->fields_data_table . ' ' . $this->db->sql_build_array('INSERT', $cp_data);
  214. $this->db->sql_query($sql);
  215. $this->db->sql_return_on_error(false);
  216. }
  217. }
  218. /**
  219. * Generate the template arrays in order to display the column names
  220. *
  221. * @param string $restrict_option Restrict the published fields to a certain profile field option
  222. * @return array Returns an array with the template variables type, name and explain for the fields to display
  223. */
  224. public function generate_profile_fields_template_headlines($restrict_option = '')
  225. {
  226. if (!sizeof($this->profile_cache))
  227. {
  228. $this->build_cache();
  229. }
  230. $tpl_fields = array();
  231. // Go through the fields in correct order
  232. foreach ($this->profile_cache as $field_ident => $field_data)
  233. {
  234. if ($restrict_option && !$field_data[$restrict_option])
  235. {
  236. continue;
  237. }
  238. $profile_field = $this->type_collection[$field_data['field_type']];
  239. $tpl_fields[] = array(
  240. 'PROFILE_FIELD_TYPE' => $field_data['field_type'],
  241. 'PROFILE_FIELD_NAME' => $profile_field->get_field_name($field_data['lang_name']),
  242. 'PROFILE_FIELD_EXPLAIN' => $this->user->lang($field_data['lang_explain']),
  243. );
  244. }
  245. return $tpl_fields;
  246. }
  247. /**
  248. * Grab the user specific profile fields data
  249. *
  250. * @param int|array $user_ids Single user id or an array of ids
  251. * @return array Users profile fields data
  252. */
  253. public function grab_profile_fields_data($user_ids = 0)
  254. {
  255. if (!is_array($user_ids))
  256. {
  257. $user_ids = array($user_ids);
  258. }
  259. if (!sizeof($this->profile_cache))
  260. {
  261. $this->build_cache();
  262. }
  263. if (!sizeof($user_ids))
  264. {
  265. return array();
  266. }
  267. $sql = 'SELECT *
  268. FROM ' . $this->fields_data_table . '
  269. WHERE ' . $this->db->sql_in_set('user_id', array_map('intval', $user_ids));
  270. $result = $this->db->sql_query($sql);
  271. $field_data = array();
  272. while ($row = $this->db->sql_fetchrow($result))
  273. {
  274. $field_data[$row['user_id']] = $row;
  275. }
  276. $this->db->sql_freeresult($result);
  277. /**
  278. * Event to modify profile fields data retrieved from the database
  279. *
  280. * @event core.grab_profile_fields_data
  281. * @var array user_ids Single user id or an array of ids
  282. * @var array field_data Array with profile fields data
  283. * @since 3.1.0-b3
  284. */
  285. $vars = array('user_ids', 'field_data');
  286. extract($this->dispatcher->trigger_event('core.grab_profile_fields_data', compact($vars)));
  287. $user_fields = array();
  288. // Go through the fields in correct order
  289. foreach (array_keys($this->profile_cache) as $used_ident)
  290. {
  291. foreach ($field_data as $user_id => $row)
  292. {
  293. $user_fields[$user_id][$used_ident]['value'] = $row['pf_' . $used_ident];
  294. $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
  295. }
  296. foreach ($user_ids as $user_id)
  297. {
  298. if (!isset($user_fields[$user_id][$used_ident]) && $this->profile_cache[$used_ident]['field_show_novalue'])
  299. {
  300. $user_fields[$user_id][$used_ident]['value'] = '';
  301. $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
  302. }
  303. }
  304. }
  305. return $user_fields;
  306. }
  307. /**
  308. * Assign the user's profile fields data to the template
  309. *
  310. * @param array $profile_row Array with users profile field data
  311. * @param bool $use_contact_fields Should we display contact fields as such?
  312. * This requires special treatments (links should not be parsed in the values, and more)
  313. * @return array
  314. */
  315. public function generate_profile_fields_template_data($profile_row, $use_contact_fields = true)
  316. {
  317. // $profile_row == $user_fields[$row['user_id']];
  318. $tpl_fields = array();
  319. $tpl_fields['row'] = $tpl_fields['blockrow'] = array();
  320. /**
  321. * Event to modify data of the generated profile fields, before the template assignment loop
  322. *
  323. * @event core.generate_profile_fields_template_data_before
  324. * @var array profile_row Array with users profile field data
  325. * @var array tpl_fields Array with template data fields
  326. * @var bool use_contact_fields Should we display contact fields as such?
  327. * @since 3.1.0-b3
  328. */
  329. $vars = array('profile_row', 'tpl_fields', 'use_contact_fields');
  330. extract($this->dispatcher->trigger_event('core.generate_profile_fields_template_data_before', compact($vars)));
  331. foreach ($profile_row as $ident => $ident_ary)
  332. {
  333. $profile_field = $this->type_collection[$ident_ary['data']['field_type']];
  334. $value = $profile_field->get_profile_value($ident_ary['value'], $ident_ary['data']);
  335. if ($value === null)
  336. {
  337. continue;
  338. }
  339. $field_desc = $contact_url = '';
  340. if ($use_contact_fields)
  341. {
  342. $value = $profile_field->get_profile_contact_value($ident_ary['value'], $ident_ary['data']);
  343. $field_desc = $this->user->lang($ident_ary['data']['field_contact_desc']);
  344. if (strpos($field_desc, '%s') !== false)
  345. {
  346. $field_desc = sprintf($field_desc, $value);
  347. }
  348. $contact_url = '';
  349. if (strpos($ident_ary['data']['field_contact_url'], '%s') !== false)
  350. {
  351. $contact_url = sprintf($ident_ary['data']['field_contact_url'], $value);
  352. }
  353. }
  354. $tpl_fields['row'] += array(
  355. 'PROFILE_' . strtoupper($ident) . '_IDENT' => $ident,
  356. 'PROFILE_' . strtoupper($ident) . '_VALUE' => $value,
  357. 'PROFILE_' . strtoupper($ident) . '_CONTACT'=> $contact_url,
  358. 'PROFILE_' . strtoupper($ident) . '_DESC' => $field_desc,
  359. 'PROFILE_' . strtoupper($ident) . '_TYPE' => $ident_ary['data']['field_type'],
  360. 'PROFILE_' . strtoupper($ident) . '_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
  361. 'PROFILE_' . strtoupper($ident) . '_EXPLAIN'=> $this->user->lang($ident_ary['data']['lang_explain']),
  362. 'S_PROFILE_' . strtoupper($ident) . '_CONTACT' => $ident_ary['data']['field_is_contact'],
  363. 'S_PROFILE_' . strtoupper($ident) => true,
  364. );
  365. $tpl_fields['blockrow'][] = array(
  366. 'PROFILE_FIELD_IDENT' => $ident,
  367. 'PROFILE_FIELD_VALUE' => $value,
  368. 'PROFILE_FIELD_CONTACT' => $contact_url,
  369. 'PROFILE_FIELD_DESC' => $field_desc,
  370. 'PROFILE_FIELD_TYPE' => $ident_ary['data']['field_type'],
  371. 'PROFILE_FIELD_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
  372. 'PROFILE_FIELD_EXPLAIN' => $this->user->lang($ident_ary['data']['lang_explain']),
  373. 'S_PROFILE_CONTACT' => $ident_ary['data']['field_is_contact'],
  374. 'S_PROFILE_' . strtoupper($ident) => true,
  375. );
  376. }
  377. /**
  378. * Event to modify template data of the generated profile fields
  379. *
  380. * @event core.generate_profile_fields_template_data
  381. * @var array profile_row Array with users profile field data
  382. * @var array tpl_fields Array with template data fields
  383. * @var bool use_contact_fields Should we display contact fields as such?
  384. * @since 3.1.0-b3
  385. */
  386. $vars = array('profile_row', 'tpl_fields', 'use_contact_fields');
  387. extract($this->dispatcher->trigger_event('core.generate_profile_fields_template_data', compact($vars)));
  388. return $tpl_fields;
  389. }
  390. /**
  391. * Build Array for user insertion into custom profile fields table
  392. */
  393. public function build_insert_sql_array($cp_data)
  394. {
  395. $sql_not_in = array();
  396. foreach ($cp_data as $key => $null)
  397. {
  398. $sql_not_in[] = (strncmp($key, 'pf_', 3) === 0) ? substr($key, 3) : $key;
  399. }
  400. $sql = 'SELECT f.field_type, f.field_ident, f.field_default_value, l.lang_default_value
  401. FROM ' . $this->fields_language_table . ' l, ' . $this->fields_table . ' f
  402. WHERE l.lang_id = ' . $this->user->get_iso_lang_id() . '
  403. ' . ((sizeof($sql_not_in)) ? ' AND ' . $this->db->sql_in_set('f.field_ident', $sql_not_in, true) : '') . '
  404. AND l.field_id = f.field_id';
  405. $result = $this->db->sql_query($sql);
  406. while ($row = $this->db->sql_fetchrow($result))
  407. {
  408. $profile_field = $this->type_collection[$row['field_type']];
  409. $cp_data['pf_' . $row['field_ident']] = $profile_field->get_default_field_value($row);
  410. }
  411. $this->db->sql_freeresult($result);
  412. return $cp_data;
  413. }
  414. }