PageRenderTime 128ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 1ms

/web/system/UsersModule/Controller/AdminController.php

https://github.com/antoniom/core
PHP | 2540 lines | 1949 code | 191 blank | 400 comment | 335 complexity | 0dc34229062caee81f8f605a41ba28c9 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, MIT
  1. <?php
  2. /**
  3. * Copyright Zikula Foundation 2009 - Zikula Application Framework
  4. *
  5. * This work is contributed to the Zikula Foundation under one or more
  6. * Contributor Agreements and licensed to You under the following license:
  7. *
  8. * @license GNU/LGPLv3 (or at your option, any later version).
  9. * @package Zikula
  10. * @subpackage Users
  11. *
  12. * Please see the NOTICE file distributed with this source code for further
  13. * information regarding copyright and licensing.
  14. */
  15. namespace UsersModule\Controller;
  16. use Zikula\Core\Event\GenericEvent;
  17. use Zikula_View, SecurityUtil, ModUtil, UserUtil, DataUtil, DateUtil, LogUtil, System, FileUtil;
  18. use DateTime, DateTimeZone, Exception;
  19. use Zikula_Session;
  20. use \Zikula\Framework\Exception\ForbiddenException;
  21. use UsersModule\Constants as UsersConstant;
  22. use Zikula\Framework\Exception\FatalException;
  23. use Zikula\Core\Hook\ProcessHook;
  24. use Zikula\Core\Hook\ValidationProviders;
  25. use Zikula\Core\Hook\ValidationHook;
  26. /**
  27. * Administrator-initiated actions for the Users module.
  28. */
  29. class AdminController extends \Zikula\Framework\Controller\AbstractController
  30. {
  31. /**
  32. * Post initialise.
  33. *
  34. * Run after construction.
  35. *
  36. * @return void
  37. */
  38. protected function postInitialize()
  39. {
  40. // Disable caching by default.
  41. $this->view->setCaching(Zikula_View::CACHE_DISABLED);
  42. }
  43. /**
  44. * Determines if the user currently logged in has administrative access for the Users module.
  45. *
  46. * @return bool True if the current user is logged in and has administrator access for the Users
  47. * module; otherwise false.
  48. */
  49. private function currentUserIsAdmin()
  50. {
  51. return UserUtil::isLoggedIn() && SecurityUtil::checkPermission('Users::', '::', ACCESS_ADMIN);
  52. }
  53. /**
  54. * Redirects users to the "view" page.
  55. *
  56. * @return string HTML string containing the rendered view template.
  57. */
  58. public function indexAction()
  59. {
  60. // Security check will be done in view()
  61. return $this->redirect(ModUtil::url($this->name, 'admin', 'view'));
  62. }
  63. /**
  64. * Shows all items and lists the administration options.
  65. *
  66. * Parameters passed via GET:
  67. * --------------------------
  68. * numeric startnum The ordinal number at which to start displaying user records.
  69. * string letter The first letter of the user names to display.
  70. * string sort The field on which to sort the data.
  71. * string sortdir Either 'ASC' for an ascending sort (a to z) or 'DESC' for a descending sort (z to a).
  72. *
  73. * Parameters passed via POST:
  74. * ---------------------------
  75. * None.
  76. *
  77. * Parameters passed via SESSION:
  78. * ------------------------------
  79. * None.
  80. *
  81. * @return string HTML string containing the rendered template.
  82. *
  83. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access, or if the method of accessing this function is improper.
  84. */
  85. public function viewAction()
  86. {
  87. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_MODERATE)) {
  88. throw new \Zikula\Framework\Exception\ForbiddenException();
  89. }
  90. // we need this value multiple times, so we keep it
  91. $itemsPerPage = $this->getVar(UsersConstant::MODVAR_ITEMS_PER_PAGE);
  92. $sort = $this->request->query->get('sort', isset($args['sort']) ? $args['sort'] : 'uname');
  93. $sortDirection = $this->request->query->get('sortdir', isset($args['sortdir']) ? $args['sortdir'] : 'ASC');
  94. $sortArgs = array(
  95. $sort => $sortDirection,
  96. );
  97. if (!isset($sortArgs['uname'])) {
  98. $sortArgs['uname'] = 'ASC';
  99. }
  100. $getAllArgs = array(
  101. 'startnum' => $this->request->query->get('startnum', isset($args['startnum']) ? $args['startnum'] : null),
  102. 'numitems' => $itemsPerPage,
  103. 'letter' => $this->request->query->get('letter', isset($args['letter']) ? $args['letter'] : null),
  104. 'sort' => $sortArgs,
  105. );
  106. // Get all users as specified by the arguments.
  107. $userList = ModUtil::apiFunc($this->name, 'user', 'getAll', $getAllArgs);
  108. // Get all groups
  109. $groups = ModUtil::apiFunc('GroupsModule', 'user', 'getall');
  110. // check what groups can access the user
  111. $userGroupsAccess = array();
  112. $groupsArray = array();
  113. $canSeeGroups = !empty($groups);
  114. foreach ($groups as $group) {
  115. $userGroupsAccess[$group['gid']] = array('gid' => $group['gid']);
  116. // rewrite the groups array with the group id as key and the group name as value
  117. $groupsArray[$group['gid']] = array('name' => DataUtil::formatForDisplayHTML($group['name']));
  118. }
  119. // Get the current user's uid
  120. $currentUid = UserUtil::getVar('uid');
  121. // Determine the available options
  122. $currentUserHasReadAccess = SecurityUtil::checkPermission($this->name . '::', 'ANY', ACCESS_READ);
  123. $currentUserHasModerateAccess = SecurityUtil::checkPermission($this->name . '::', 'ANY', ACCESS_MODERATE);
  124. $currentUserHasEditAccess = SecurityUtil::checkPermission($this->name . '::', 'ANY', ACCESS_EDIT);
  125. $currentUserHasDeleteAccess = SecurityUtil::checkPermission($this->name . '::', 'ANY', ACCESS_DELETE);
  126. $availableOptions = array(
  127. 'lostUsername' => $currentUserHasModerateAccess,
  128. 'lostPassword' => $currentUserHasModerateAccess,
  129. 'toggleForcedPasswordChange' => $currentUserHasEditAccess,
  130. 'modify' => $currentUserHasEditAccess,
  131. 'deleteUsers' => $currentUserHasDeleteAccess,
  132. );
  133. // Loop through each returned item adding in the options that the user has over
  134. // each item based on the permissions the user has.
  135. foreach ($userList as $key => $userObj) {
  136. $isCurrentUser = ($userObj['uid'] == $currentUid);
  137. $isGuestAccount = ($userObj['uid'] == 1);
  138. $isAdminAccount = ($userObj['uid'] == 2);
  139. $hasUsersPassword = (!empty($userObj['pass']) && ($userObj['pass'] != UsersConstant::PWD_NO_USERS_AUTHENTICATION));
  140. $currentUserHasReadAccess = !$isGuestAccount && SecurityUtil::checkPermission($this->name . '::', "{$userObj['uname']}::{$userObj['uid']}", ACCESS_READ);
  141. $currentUserHasModerateAccess = !$isGuestAccount && SecurityUtil::checkPermission($this->name . '::', "{$userObj['uname']}::{$userObj['uid']}", ACCESS_MODERATE);
  142. $currentUserHasEditAccess = !$isGuestAccount && SecurityUtil::checkPermission($this->name . '::', "{$userObj['uname']}::{$userObj['uid']}", ACCESS_EDIT);
  143. $currentUserHasDeleteAccess = !$isGuestAccount && !$isAdminAccount && !$isCurrentUser && SecurityUtil::checkPermission($this->name . '::', "{$userObj['uname']}::{$userObj['uid']}", ACCESS_DELETE);
  144. $userList[$key]['options'] = array(
  145. 'lostUsername' => $currentUserHasModerateAccess,
  146. 'lostPassword' => $currentUserHasModerateAccess,
  147. 'toggleForcedPasswordChange'=> $hasUsersPassword && $currentUserHasEditAccess,
  148. 'modify' => $currentUserHasEditAccess,
  149. 'deleteUsers' => $currentUserHasDeleteAccess,
  150. );
  151. if ($isGuestAccount) {
  152. $userList[$key]['userGroupsView'] = array();
  153. } else {
  154. // get user groups
  155. $userGroups = ModUtil::apiFunc('GroupsModule', 'user', 'getusergroups', array(
  156. 'uid' => $userObj['uid'],
  157. 'clean' => 1
  158. ));
  159. // we need an associative array by the key to compare with the groups that the user can see
  160. $userGroupsByKey = array();
  161. foreach ($userGroups as $userGroup) {
  162. $userGroupsByKey[$userGroup['gid']] = array('gid' => $userGroup['gid']);
  163. }
  164. $userList[$key]['userGroupsView'] = array_intersect_key($userGroupsAccess, $userGroupsByKey);
  165. }
  166. // format the dates
  167. if (!empty($userObj['user_regdate']) && ($userObj['user_regdate'] != '0000-00-00 00:00:00') && ($userObj['user_regdate'] != '1970-01-01 00:00:00')) {
  168. $userList[$key]['user_regdate'] = DateUtil::formatDatetime($userObj['user_regdate'], $this->__('%m-%d-%Y'));
  169. } else {
  170. $userList[$key]['user_regdate'] = '---';
  171. }
  172. if (!empty($userObj['lastlogin']) && ($userObj['lastlogin'] != '0000-00-00 00:00:00') && ($userObj['lastlogin'] != '1970-01-01 00:00:00')) {
  173. $userList[$key]['lastlogin'] = DateUtil::formatDatetime($userObj['lastlogin'], $this->__('%m-%d-%Y'));
  174. } else {
  175. $userList[$key]['lastlogin'] = '---';
  176. }
  177. $userList[$key]['_Users_mustChangePassword'] = (isset($userObj['__ATTRIBUTES__']) && isset($userObj['__ATTRIBUTES__']['_Users_mustChangePassword']) && $userObj['__ATTRIBUTES__']['_Users_mustChangePassword']);
  178. }
  179. $pager = array(
  180. 'numitems' => ModUtil::apiFunc($this->name, 'user', 'countItems', array('letter' => $getAllArgs['letter'])),
  181. 'itemsperpage' => $itemsPerPage,
  182. );
  183. // Assign the items to the template & return output
  184. return $this->response($this->view->assign('usersitems', $userList)
  185. ->assign('pager', $pager)
  186. ->assign('allGroups', $groupsArray)
  187. ->assign('canSeeGroups', $canSeeGroups)
  188. ->assign('sort', $sort)
  189. ->assign('sortdir', $sortDirection)
  190. ->assign('available_options', $availableOptions)
  191. ->fetch('Admin/view.tpl'));
  192. }
  193. /**
  194. * Add a new user to the system.
  195. *
  196. * Parameters passed via GET:
  197. * --------------------------
  198. * None.
  199. *
  200. * Parameters passed via POST:
  201. * ---------------------------
  202. * See the definition of {@link Users_Controller_FormData_NewUserForm}.
  203. *
  204. * Parameters passed via SESSION:
  205. * ------------------------------
  206. * None.
  207. *
  208. * @return string HTML string containing the rendered template.
  209. *
  210. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have add access, or if the method of accessing this function is improper.
  211. */
  212. public function newUserAction()
  213. {
  214. // The user must have ADD access to submit a new user record.
  215. if (!SecurityUtil::checkPermission($this->name . '::', '::', ACCESS_ADD)) {
  216. throw new \Zikula\Framework\Exception\ForbiddenException();
  217. }
  218. // When new user registration is disabled, the user must have ADMIN access instead of ADD access.
  219. if (!$this->getVar(UsersConstant::MODVAR_REGISTRATION_ENABLED, false) && !SecurityUtil::checkPermission($this->name . '::', '::', ACCESS_ADMIN)) {
  220. $registrationUnavailableReason = $this->getVar(UsersConstant::MODVAR_REGISTRATION_DISABLED_REASON, $this->__('Sorry! New user registration is currently disabled.'));
  221. $this->registerError($registrationUnavailableReason);
  222. // TODO - The home page typically does not display errors.
  223. return $this->redirect(System::getHomepageUrl());
  224. }
  225. $proceedToForm = true;
  226. $formData = new FormData\NewUserForm('users_newuser', $this->container);
  227. $errorFields = array();
  228. $errorMessages = array();
  229. if ($this->request->getMethod() == 'POST') {
  230. // Returning from a form POST operation. Process the input.
  231. $this->checkCsrfToken();
  232. $formData->setFromRequestCollection($this->request->request->all());
  233. $registrationArgs = array(
  234. 'checkMode' => 'new',
  235. 'emailagain' => $formData->getField('emailagain')->getData(),
  236. 'setpass' => (bool)$formData->getField('setpass')->getData(),
  237. 'antispamanswer' => '',
  238. );
  239. $registrationArgs['passagain'] = $registrationArgs['setpass'] ? $formData->getField('passagain')->getData() : '';
  240. $registrationInfo = array(
  241. 'uname' => $formData->getField('uname')->getData(),
  242. 'pass' => $registrationArgs['setpass'] ? $formData->getField('pass')->getData() : '',
  243. 'passreminder' => $registrationArgs['setpass'] ? $this->__('(Password provided by site administrator)') : '',
  244. 'email' => mb_strtolower($formData->getField('email')->getData()),
  245. );
  246. $registrationArgs['reginfo'] = $registrationInfo;
  247. $sendPass = $formData->getField('sendpass')->getData();
  248. if ($formData->isValid()) {
  249. $errorFields = ModUtil::apiFunc($this->name, 'registration', 'getRegistrationErrors', $registrationArgs);
  250. } else {
  251. $errorFields = $formData->getErrorMessages();
  252. }
  253. $event = new GenericEvent($registrationInfo, array(), new ValidationProviders());
  254. $this->dispatcher->dispatch('module.users.ui.validate_edit.new_user', $event);
  255. $validators = $event->getData();
  256. $hook = new ValidationHook($validators);
  257. $this->dispatchHooks('users.ui_hooks.user.validate_edit', $hook);
  258. $validators = $hook->getValidators();
  259. if (empty($errorFields) && !$validators->hasErrors()) {
  260. // TODO - Future functionality to suppress e-mail notifications, see ticket #2351
  261. //$currentUserEmail = UserUtil::getVar('email');
  262. //$adminNotifyEmail = $this->getVar('reg_notifyemail', '');
  263. //$adminNotification = (strtolower($currentUserEmail) != strtolower($adminNotifyEmail));
  264. $registeredObj = ModUtil::apiFunc($this->name, 'registration', 'registerNewUser', array(
  265. 'reginfo' => $registrationInfo,
  266. 'sendpass' => $sendPass,
  267. 'usernotification' => true,
  268. 'adminnotification' => true,
  269. ));
  270. if (isset($registeredObj) && $registeredObj) {
  271. $event = new GenericEvent($registeredObj);
  272. $this->dispatcher->dispatch('module.users.ui.process_edit.new_user', $event);
  273. $hook = new ProcessHook($registeredObj['uid']);
  274. $this->dispatchHooks('users.ui_hooks.user.process_edit', $hook);
  275. if ($registeredObj['activated'] == UsersConstant::ACTIVATED_PENDING_REG) {
  276. $this->registerStatus($this->__('Done! Created new registration application.'));
  277. } elseif (isset($registeredObj['activated'])) {
  278. $this->registerStatus($this->__('Done! Created new user account.'));
  279. } else {
  280. $this->registerError($this->__('Warning! New user information has been saved, however there may have been an issue saving it properly.'));
  281. }
  282. $proceedToForm = false;
  283. } else {
  284. $this->registerError($this->__('Error! Could not create the new user account or registration application.'));
  285. }
  286. }
  287. } elseif (!$this->request->getMethod() == 'GET') {
  288. throw new \Zikula\Framework\Exception\ForbiddenException();
  289. }
  290. if ($proceedToForm) {
  291. return $this->response($this->view->assign_by_ref('formData', $formData)
  292. ->assign('mode', 'new')
  293. ->assign('errorMessages', $errorMessages)
  294. ->assign('errorFields', $errorFields)
  295. ->fetch('Admin/newuser.tpl'));
  296. } else {
  297. return $this->redirect(ModUtil::url($this->name, 'admin', 'view'));
  298. }
  299. }
  300. /**
  301. * Renders a user search form used by both the search operation and the mail users operation.
  302. *
  303. * @param string $callbackFunc Either 'search' or 'mailUsers', indicating which operation is calling this function.
  304. *
  305. * @return string The rendered output from the template, appropriate for the indicated operation.
  306. */
  307. protected function renderSearchForm($callbackFunc = 'search')
  308. {
  309. // get group items
  310. $groups = ModUtil::apiFunc('GroupsModule', 'user', 'getAll');
  311. return $this->response($this->view->assign('groups', $groups)
  312. ->assign('callbackFunc', $callbackFunc)
  313. ->fetch('Admin/search.tpl'));
  314. }
  315. /**
  316. * Gathers the user input from a rendered search form, and also makes the appropriate hook calls.
  317. *
  318. * Parameters passed via GET:
  319. * --------------------------
  320. * None.
  321. *
  322. * Parameters passed via POST:
  323. * ---------------------------
  324. * string uname A fragment of a user name on which to search using an SQL LIKE clause. The user name will be
  325. * surrounded by wildcards.
  326. * integer ugroup A group id in which to search (only users who are members of the specified group are returned).
  327. * string email A fragment of an e-mail address on which to search using an SQL LIKE clause. The e-mail address
  328. * will be surrounded by wildcards.
  329. * string regdateafter An SQL date-time (in the form '1970-01-01 00:00:00'); only user accounts with a registration date
  330. * after the date specified will be returned.
  331. * string regdatebefore An SQL date-time (in the form '1970-01-01 00:00:00'); only user accounts with a registration date
  332. * before the date specified will be returned.
  333. * array dynadata An array of search values to be passed to the designated profile module. Only those user records
  334. * also satisfying the profile module's search of its dataare returned.
  335. *
  336. * Parameters passed via SESSION:
  337. * ------------------------------
  338. * None.
  339. *
  340. * @param string $callbackFunc Either 'search' or 'mailUsers', indicating which operation is calling this function.
  341. *
  342. * @return array|boolean An array of search results, which may be empty; false if the search was unsuccessful.
  343. */
  344. protected function getSearchResults($callbackFunc = 'search')
  345. {
  346. $findUsersArgs = array(
  347. 'uname' => $this->request->request->get('uname', null),
  348. 'email' => $this->request->request->get('email', null),
  349. 'ugroup' => $this->request->request->get('ugroup', null),
  350. 'regdateafter' => $this->request->request->get('regdateafter', null),
  351. 'regdatebefore' => $this->request->request->get('regdatebefore', null),
  352. );
  353. if ($callbackFunc == 'mailUsers') {
  354. $processEditEvent = $this->dispatcher->dispatch('users.mailuserssearch.process_edit', new GenericEvent(null, array(), $findUsersArgs));
  355. } else {
  356. $processEditEvent = $this->dispatcher->dispatch('users.search.process_edit', new GenericEvent(null, array(), $findUsersArgs));
  357. }
  358. $findUsersArgs = $processEditEvent->getData();
  359. // call the api
  360. return ModUtil::apiFunc($this->name, 'admin', 'findUsers', $findUsersArgs);
  361. }
  362. /**
  363. * Displays a user account search form, or the search results from a post.
  364. *
  365. * Parameters passed via GET:
  366. * --------------------------
  367. * None.
  368. *
  369. * Parameters passed via POST:
  370. * ---------------------------
  371. * See the definition of {@link getSearchResults()}.
  372. *
  373. * Parameters passed via SESSION:
  374. * ------------------------------
  375. * None.
  376. *
  377. * @return string HTML string containing the rendered template.
  378. *
  379. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access, or if the method of accessing this function is improper.
  380. */
  381. public function searchAction()
  382. {
  383. if (!SecurityUtil::checkPermission($this->name . '::', '::', ACCESS_MODERATE)) {
  384. throw new \Zikula\Framework\Exception\ForbiddenException();
  385. }
  386. if ($this->request->getMethod() == 'POST') {
  387. $this->checkCsrfToken();
  388. $usersList = $this->getSearchResults();
  389. if ($usersList) {
  390. $currentUid = UserUtil::getVar('uid');
  391. $actions = array();
  392. foreach ($usersList as $key => $user) {
  393. $actions[$key] = array(
  394. 'modifyUrl' => false,
  395. 'deleteUrl' => false,
  396. );
  397. if ($user['uid'] != 1) {
  398. if (SecurityUtil::checkPermission($this->name.'::', $user['uname'].'::'.$user['uid'], ACCESS_EDIT)) {
  399. $actions[$key]['modifyUrl'] = ModUtil::url($this->name, 'admin', 'modify', array('userid' => $user['uid']));
  400. }
  401. if (($currentUid != $user['uid'])
  402. && SecurityUtil::checkPermission($this->name.'::', $user['uname'].'::'.$user['uid'], ACCESS_DELETE)) {
  403. $actions[$key]['deleteUrl'] = ModUtil::url($this->name, 'admin', 'deleteusers', array('userid' => $user['uid']));
  404. }
  405. }
  406. }
  407. } else {
  408. $this->registerError($this->__('Sorry! No matching users found.'));
  409. }
  410. }
  411. if (isset($usersList) && $usersList) {
  412. return $this->response($this->view->assign('items', $usersList)
  413. ->assign('actions', $actions)
  414. ->assign('deleteUsers', SecurityUtil::checkPermission($this->name . '::', '::', ACCESS_ADMIN))
  415. ->fetch('Admin/search_results.tpl'));
  416. } elseif ($this->request->getMethod() == 'GET' || ($this->request->getMethod() == 'POST' && (!isset
  417. ($usersList) || !$usersList))) {
  418. return $this->renderSearchForm('search');
  419. } else {
  420. throw new \Zikula\Framework\Exception\ForbiddenException();
  421. }
  422. }
  423. /**
  424. * Search for users and then compose an email to them.
  425. *
  426. * Parameters passed via GET:
  427. * --------------------------
  428. * None.
  429. *
  430. * Parameters passed via POST:
  431. * ---------------------------
  432. * string formid The form id posting to this function. Used to determine the workflow.
  433. *
  434. * See also the definition of {@link getSearchResults()}.
  435. *
  436. * Parameters passed via SESSION:
  437. * ------------------------------
  438. * None.
  439. *
  440. * @return string HTML string containing the rendered template.
  441. *
  442. * @throws \Zikula\Framework\Exception\FatalException Thrown if the function enters an unknown state.
  443. *
  444. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have comment access, or if the method of accessing this function is improper.
  445. */
  446. public function mailUsersAction()
  447. {
  448. if (!SecurityUtil::checkPermission($this->name . '::MailUsers', '::', ACCESS_COMMENT)) {
  449. throw new \Zikula\Framework\Exception\ForbiddenException();
  450. }
  451. if ($this->request->getMethod() == 'POST') {
  452. $this->checkCsrfToken();
  453. $formId = $this->request->request->get('formid', 'UNKNOWN');
  454. if ($formId == 'users_search') {
  455. $userList = $this->getSearchResults('mailUsers');
  456. if (!isset($userList) || !$userList) {
  457. $this->registerError($this->__('Sorry! No matching users found.'));
  458. }
  459. } elseif ($formId == 'users_mailusers') {
  460. $uid = $this->request->request->get('userid', null);
  461. $sendmail = $this->request->request->get('sendmail', null);
  462. $mailSent = ModUtil::apiFunc($this->name, 'admin', 'sendmail', array(
  463. 'uid' => $uid,
  464. 'sendmail' => $sendmail,
  465. ));
  466. } else {
  467. throw new \Zikula\Framework\Exception\FatalException($this->__f('An unknown form type was received by %1$s.', array('mailUsers')));
  468. }
  469. } elseif (!$this->request->getMethod() == 'GET') {
  470. throw new \Zikula\Framework\Exception\ForbiddenException();
  471. }
  472. if ($this->request->getMethod() == 'GET' || (($formId == 'users_search') && (!isset($userList) || !$userList)) || (($formId == 'users_mailusers') && !$mailSent)) {
  473. return $this->renderSearchForm('mailUsers');
  474. } elseif ($formId == 'users_search') {
  475. return $this->response($this->view->assign('items', $userList)
  476. ->assign('mailusers', SecurityUtil::checkPermission($this->name . '::MailUsers', '::', ACCESS_COMMENT))
  477. ->fetch('Admin/mailusers.tpl'));
  478. } elseif ($formId == 'users_mailusers') {
  479. return $this->redirect(ModUtil::url($this->name, 'admin', 'index'));
  480. } else {
  481. throw new \Zikula\Framework\Exception\FatalException($this->__f('The %1$s function has entered an unknown state.', array('mailUsers')));
  482. }
  483. }
  484. /**
  485. * Display a form to edit one user account, and process that edit request.
  486. *
  487. * Parameters passed via GET:
  488. * --------------------------
  489. * numeric userid The user id of the user to be modified.
  490. * string uname The user name of the user to be modified.
  491. *
  492. * Parameters passed via POST:
  493. * ---------------------------
  494. * array access_permissions An array used to modify a user's group membership.
  495. *
  496. * See also the definition of {@link Users_Controller_FormData_ModifyUserForm}.
  497. *
  498. * Parameters passed via SESSION:
  499. * ------------------------------
  500. * None.
  501. *
  502. * @return string HTML string containing the rendered template.
  503. *
  504. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have edit access, or if the method of accessing this function is improper.
  505. */
  506. public function modifyAction()
  507. {
  508. // security check for generic edit access
  509. if (!SecurityUtil::checkPermission('Users::', 'ANY', ACCESS_EDIT)) {
  510. throw new \Zikula\Framework\Exception\ForbiddenException();
  511. }
  512. $proceedToForm = true;
  513. $formData = new FormData\ModifyUserForm('users_modify', $this->container);
  514. if ($this->request->getMethod() == 'POST') {
  515. $this->checkCsrfToken();
  516. $formData->setFromRequestCollection($this->request->getPost());
  517. $accessPermissions = $this->request->request->get('access_permissions', null);
  518. $user = $formData->toUserArray(true);
  519. $originalUser = UserUtil::getVars($user['uid']);
  520. $userAttributes = isset($originalUser['__ATTRIBUTES__']) ? $originalUser['__ATTRIBUTES__'] : array();
  521. // security check for this record
  522. if (!SecurityUtil::checkPermission('Users::', "{$originalUser['uname']}::{$originalUser['uid']}", ACCESS_EDIT)) {
  523. throw new \Zikula\Framework\Exception\ForbiddenException();
  524. }
  525. if ($formData->isValid()) {
  526. $registrationArgs = array(
  527. 'checkmode' => 'modify',
  528. 'emailagain' => $formData->getField('emailagain')->getData(),
  529. 'setpass' => (bool)$formData->getField('setpass')->getData(),
  530. 'antispamanswer' => '',
  531. );
  532. $registrationArgs['passagain'] = $registrationArgs['setpass'] ? $formData->getField('passagain')->getData() : '';
  533. $registrationArgs['reginfo'] = $user;
  534. $errorFields = ModUtil::apiFunc($this->name, 'registration', 'getRegistrationErrors', $registrationArgs);
  535. } else {
  536. $errorFields = $formData->getErrorMessages();
  537. }
  538. $event = new GenericEvent($user, array(), new ValidationProviders());
  539. $this->dispatcher->dispatch('module.users.ui.validate_edit.modify_user', $event);
  540. $validators = $event->getData();
  541. $hook = new ValidationHook($validators);
  542. $this->dispatchHooks('users.ui_hooks.user.validate_edit', $hook);
  543. $validators = $hook->getValidators();
  544. if (!$errorFields && !$validators->hasErrors()) {
  545. if ($originalUser['uname'] != $user['uname']) {
  546. // UserUtil::setVar does not allow uname to be changed.
  547. // UserUtil::setVar('uname', $user['uname'], $originalUser['uid']);
  548. $updatedUserObj = $this->entityManager->find('UsersModule\Entity\User', $originalUser['uid']);
  549. $updatedUserObj['uname'] = $user['uname'];
  550. $this->entityManager->flush();
  551. $eventArgs = array(
  552. 'action' => 'setVar',
  553. 'field' => 'uname',
  554. 'attribute' => null,
  555. );
  556. $eventData = array(
  557. 'old_value' => $originalUser['uname'],
  558. );
  559. $updateEvent = new GenericEvent($updatedUserObj, $eventArgs, $eventData);
  560. $this->dispatcher->dispatch('user.account.update', $updateEvent);
  561. }
  562. if ($originalUser['email'] != $user['email']) {
  563. UserUtil::setVar('email', $user['email'], $originalUser['uid']);
  564. }
  565. if ($originalUser['activated'] != $user['activated']) {
  566. UserUtil::setVar('activated', $user['activated'], $originalUser['uid']);
  567. }
  568. if ($originalUser['theme'] != $user['theme']) {
  569. UserUtil::setVar('theme', $user['theme'], $originalUser['uid']);
  570. }
  571. if ($formData->getField('setpass')->getData()) {
  572. UserUtil::setPassword($user['pass'], $originalUser['uid']);
  573. UserUtil::setVar('passreminder', $user['passreminder'], $originalUser['uid']);
  574. }
  575. $user = UserUtil::getVars($user['uid'], true);
  576. // TODO - This all needs to move to a Groups module hook.
  577. if (isset($accessPermissions)) {
  578. // Fixing a high numitems to be sure to get all groups
  579. $groups = ModUtil::apiFunc('GroupsModule', 'user', 'getAll', array('numitems' => 10000));
  580. $curUserGroupMembership = ModUtil::apiFunc('GroupsModule', 'user', 'getUserGroups', array('uid' => $user['uid']));
  581. foreach ($groups as $group) {
  582. if (in_array($group['gid'], $accessPermissions)) {
  583. // Check if the user is already in the group
  584. $userIsMember = false;
  585. if ($curUserGroupMembership) {
  586. foreach ($curUserGroupMembership as $alreadyMemberOf) {
  587. if ($group['gid'] == $alreadyMemberOf['gid']) {
  588. $userIsMember = true;
  589. break;
  590. }
  591. }
  592. }
  593. if ($userIsMember == false) {
  594. // User is not in this group
  595. ModUtil::apiFunc('GroupsModule', 'admin', 'addUser', array(
  596. 'gid' => $group['gid'],
  597. 'uid' => $user['uid']
  598. ));
  599. $curUserGroupMembership[] = $group;
  600. }
  601. } else {
  602. // We don't need to do a complex check, if the user is not in the group, the SQL will not return
  603. // an error anyway.
  604. ModUtil::apiFunc('GroupsModule', 'admin', 'removeUser', array(
  605. 'gid' => $group['gid'],
  606. 'uid' => $user['uid']
  607. ));
  608. }
  609. }
  610. }
  611. $event = new GenericEvent($user);
  612. $this->dispatcher->dispatch('module.users.ui.process_edit.modify_user', $event);
  613. $hook = new ProcessHook($user['uid']);
  614. $this->dispatchHooks('users.ui_hooks.user.process_edit', $hook);
  615. $this->registerStatus($this->__("Done! Saved user's account information."));
  616. $proceedToForm = false;
  617. }
  618. } elseif ($this->request->getMethod() == 'GET') {
  619. $uid = $this->request->query->get('userid', null);
  620. $uname = $this->request->query->get('uname', null);
  621. // check arguments
  622. if (is_null($uid) && is_null($uname)) {
  623. $this->registerError(LogUtil::getErrorMsgArgs());
  624. $proceedToForm = false;
  625. }
  626. // retreive userid from uname
  627. if (is_null($uid) && !empty($uname)) {
  628. $uid = UserUtil::getIdFromName($uname);
  629. }
  630. // warning for guest account
  631. if ($uid == 1) {
  632. $this->registerError($this->__("Error! You can't edit the guest account."));
  633. $proceedToForm = false;
  634. }
  635. // get the user vars
  636. $originalUser = UserUtil::getVars($uid);
  637. if ($originalUser == false) {
  638. $this->registerError($this->__('Sorry! No such user found.'));
  639. $proceedToForm = false;
  640. }
  641. $userAttributes = isset($originalUser['__ATTRIBUTES__']) ? $originalUser['__ATTRIBUTES__'] : array();
  642. $formData->setFromArray($originalUser);
  643. $formData->getField('emailagain')->setData($originalUser['email']);
  644. $formData->getField('pass')->setData('');
  645. $accessPermissions = array();
  646. $errorFields = array();
  647. } else {
  648. throw new \Zikula\Framework\Exception\ForbiddenException();
  649. }
  650. if ($proceedToForm) {
  651. // security check for this record
  652. if (!SecurityUtil::checkPermission('Users::', "{$originalUser['uname']}::{$originalUser['uid']}", ACCESS_EDIT)) {
  653. throw new \Zikula\Framework\Exception\ForbiddenException();
  654. }
  655. // groups
  656. $gidsUserMemberOf = array();
  657. $allGroups = ModUtil::apiFunc('GroupsModule', 'user', 'getall');
  658. if (!empty($accessPermissions)) {
  659. $gidsUserMemberOf = $accessPermissions;
  660. $accessPermissions = array();
  661. } else {
  662. $groupsUserMemberOf = ModUtil::apiFunc('GroupsModule', 'user', 'getusergroups', array('uid' => $originalUser['uid']));
  663. foreach ($groupsUserMemberOf as $user_group) {
  664. $gidsUserMemberOf[] = $user_group['gid'];
  665. }
  666. }
  667. foreach ($allGroups as $group) {
  668. if (SecurityUtil::checkPermission('Groups::', "{$group['gid']}::", ACCESS_EDIT)) {
  669. $accessPermissions[$group['gid']] = array();
  670. $accessPermissions[$group['gid']]['name'] = $group['name'];
  671. if (in_array($group['gid'], $gidsUserMemberOf) || in_array($group['gid'], $gidsUserMemberOf)) {
  672. $accessPermissions[$group['gid']]['access'] = true;
  673. } else {
  674. $accessPermissions[$group['gid']]['access'] = false;
  675. }
  676. }
  677. }
  678. if (!isset($userAttributes['realname'])) {
  679. $userAttributes['realname'] = '';
  680. }
  681. return $this->response($this->view->assign_by_ref('formData', $formData)
  682. ->assign('user_attributes', $userAttributes)
  683. ->assign('defaultGroupId', ModUtil::getVar('Groups', 'defaultgroup', 1))
  684. ->assign('primaryAdminGroupId', ModUtil::getVar('Groups', 'primaryadmingroup', 2))
  685. ->assign('accessPermissions', $accessPermissions)
  686. ->assign('errorFields', $errorFields)
  687. ->fetch('Admin/modify.tpl'));
  688. } else {
  689. return $this->redirect(ModUtil::url($this->name, 'admin', 'view'));
  690. }
  691. }
  692. /**
  693. * Allows an administrator to send a user his user name via email.
  694. *
  695. * Parameters passed via GET:
  696. * --------------------------
  697. * numeric userid The user id of the user to be modified.
  698. *
  699. * Parameters passed via POST:
  700. * ---------------------------
  701. * numeric userid The user id of the user to be modified.
  702. *
  703. * Parameters passed via SESSION:
  704. * ------------------------------
  705. * None.
  706. *
  707. * @return void
  708. *
  709. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access.
  710. *
  711. * @todo The link on the view page should be a mini form, and should post.
  712. *
  713. * @todo This should have a confirmation page.
  714. */
  715. public function lostUsernameAction()
  716. {
  717. if ($this->request->getMethod() == 'POST') {
  718. $this->checkCsrfToken();
  719. $uid = $this->request->request->get('userid', null);
  720. } else {
  721. $this->checkCsrfToken($this->request->query->get('csrftoken'));
  722. $uid = $this->request->query->get('userid', null);
  723. }
  724. if (!isset($uid) || !is_numeric($uid) || ((int)$uid != $uid) || ($uid <= 1)) {
  725. $this->registerError(LogUtil::getErrorMsgArgs())
  726. ->redirect(ModUtil::url($this->name, 'admin', 'view'));
  727. }
  728. $user = UserUtil::getVars($uid);
  729. if (!$user) {
  730. $this->registerError($this->__('Sorry! Unable to retrieve information for that user id.'))
  731. ->redirect(ModUtil::url($this->name, 'admin', 'view'));
  732. }
  733. if (!SecurityUtil::checkPermission('Users::', "{$user['uname']}::{$user['uid']}", ACCESS_MODERATE)) {
  734. throw new \Zikula\Framework\Exception\ForbiddenException();
  735. }
  736. $userNameSent = ModUtil::apiFunc($this->name, 'user', 'mailUname', array(
  737. 'idfield' => 'uid',
  738. 'id' => $user['uid'],
  739. 'adminRequest' => true,
  740. ));
  741. if ($userNameSent) {
  742. $this->registerStatus($this->__f('Done! The user name for \'%s\' has been sent via e-mail.', $user['uname']))
  743. ->redirect(ModUtil::url($this->name, 'admin', 'view'));
  744. } elseif (!$this->request->getSession()->getFlashBag()->has(Zikula_Session::MESSAGE_ERROR)) {
  745. $this->registerError($this->__f('Sorry! There was an unknown error while trying to send the user name for \'%s\'.', $user['uname']))
  746. ->redirect(ModUtil::url($this->name, 'admin', 'view'));
  747. }
  748. }
  749. /**
  750. * Allows an administrator to send a user a password recovery verification code.
  751. *
  752. * Parameters passed via GET:
  753. * --------------------------
  754. * numeric userid The user id of the user to be modified.
  755. *
  756. * Parameters passed via POST:
  757. * ---------------------------
  758. * None.
  759. *
  760. * Parameters passed via SESSION:
  761. * ------------------------------
  762. * None.
  763. *
  764. * @return bool True on success and redirect; otherwise false.
  765. *
  766. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access.
  767. *
  768. * @todo The link on the view page should be a mini form, and should post.
  769. *
  770. * @todo This should have a confirmation page.
  771. */
  772. public function lostPasswordAction()
  773. {
  774. $this->checkCsrfToken($this->request->query->get('csrftoken'));
  775. $uid = $this->request->query->get('userid', null);
  776. if (!isset($uid) || !is_numeric($uid) || ((int)$uid != $uid) || ($uid <= 1)) {
  777. $this->registerError(LogUtil::getErrorMsgArgs())
  778. ->redirect(ModUtil::url($this->name, 'admin', 'view'));
  779. }
  780. $user = UserUtil::getVars($uid);
  781. if (!$user) {
  782. $this->registerError($this->__('Sorry! Unable to retrieve information for that user id.'));
  783. return false;
  784. }
  785. if (!SecurityUtil::checkPermission('Users::', "{$user['uname']}::{$user['uid']}", ACCESS_MODERATE)) {
  786. throw new \Zikula\Framework\Exception\ForbiddenException();
  787. }
  788. $confirmationCodeSent = ModUtil::apiFunc($this->name, 'user', 'mailConfirmationCode', array(
  789. 'idfield' => 'uid',
  790. 'id' => $user['uid'],
  791. 'adminRequest' => true,
  792. ));
  793. if ($confirmationCodeSent) {
  794. $this->registerStatus($this->__f('Done! The password recovery verification code for %s has been sent via e-mail.', $user['uname']));
  795. }
  796. return $this->redirect(ModUtil::url($this->name, 'admin', 'view'));
  797. }
  798. /**
  799. * Display a form to confirm the deletion of one user, and then process the deletion.
  800. *
  801. * Parameters passed via GET:
  802. * --------------------------
  803. * numeric userid The user id of the user to be deleted.
  804. * string uname The user name of the user to be deleted.
  805. *
  806. * Parameters passed via POST:
  807. * ---------------------------
  808. * array userid The array of user ids of the users to be deleted.
  809. * boolean process_delete True to process the posted userid list, and delete the corresponding accounts; false or null to confirm first.
  810. *
  811. * Parameters passed via SESSION:
  812. * ------------------------------
  813. * None.
  814. *
  815. * @return string HTML string containing the rendered template.
  816. *
  817. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have delete access, or if the method of accessing this function is improper.
  818. */
  819. public function deleteUsersAction()
  820. {
  821. // check permissions
  822. if (!SecurityUtil::checkPermission('Users::', 'ANY', ACCESS_DELETE)) {
  823. throw new \Zikula\Framework\Exception\ForbiddenException();
  824. }
  825. $proceedToForm = false;
  826. $processDelete = false;
  827. if ($this->request->getMethod() == 'POST') {
  828. $userid = $this->request->request->get('userid', null);
  829. $processDelete = $this->request->request->get('process_delete', false);
  830. $proceedToForm = !$processDelete;
  831. } elseif ($this->request->getMethod() == 'GET') {
  832. $userid = $this->request->query->get('userid', null);
  833. $uname = $this->request->query->get('uname', null);
  834. // retreive userid from uname
  835. if (empty($userid) && !empty($uname)) {
  836. $userid = UserUtil::getIdFromName($uname);
  837. }
  838. $proceedToForm = true;
  839. } else {
  840. throw new \Zikula\Framework\Exception\ForbiddenException();
  841. }
  842. if (empty($userid)) {
  843. $this->registerError($this->__('Sorry! No such user found.'));
  844. $proceedToForm = false;
  845. }
  846. if (!is_array($userid)) {
  847. $userid = array($userid);
  848. }
  849. $currentUser = UserUtil::getVar('uid');
  850. $users = array();
  851. foreach ($userid as $key => $uid) {
  852. if ($uid == 1) {
  853. $this->registerError($this->__("Error! You can't delete the guest account."));
  854. $proceedToForm = false;
  855. $processDelete = false;
  856. } elseif ($uid == 2) {
  857. $this->registerError($this->__("Error! You can't delete the primary administrator account."));
  858. $proceedToForm = false;
  859. $processDelete = false;
  860. } elseif ($uid == $currentUser) {
  861. $this->registerError($this->__("Error! You can't delete the account you are currently logged into."));
  862. $proceedToForm = false;
  863. $processDelete = false;
  864. }
  865. // get the user vars
  866. $users[$key] = UserUtil::getVars($uid);
  867. if ($users[$key] == false) {
  868. $this->registerError($this->__('Sorry! No such user found.'));
  869. $proceedToForm = false;
  870. $processDelete = false;
  871. }
  872. }
  873. if ($processDelete) {
  874. $valid = true;
  875. foreach ($userid as $uid) {
  876. $event = new GenericEvent(null, array('id' => $uid), new ValidationProviders());
  877. $validators = $this->dispatcher->dispatch('module.users.ui.validate_delete', $event)->getData();
  878. $hook = new ValidationHook($validators);
  879. $this->dispatchHooks('users.ui_hooks.user.validate_delete', $hook);
  880. $validators = $hook->getValidators();
  881. if ($validators->hasErrors()) {
  882. $valid = false;
  883. }
  884. }
  885. $proceedToForm = false;
  886. if ($valid) {
  887. $deleted = ModUtil::apiFunc($this->name, 'admin', 'deleteUser', array('uid' => $userid));
  888. if ($deleted) {
  889. foreach ($userid as $uid) {
  890. $event = new GenericEvent(null, array('id' => $uid));
  891. $this->dispatcher->dispatch('module.users.ui.process_delete', $event);
  892. $hook = new ProcessHook($uid);
  893. $this->dispatchHooks('users.ui_hooks.user.process_delete', $hook);
  894. }
  895. $count = count($userid);
  896. $this->registerStatus($this->_fn('Done! Deleted %1$d user account.', 'Done! Deleted %1$d user accounts.', $count, array($count)));
  897. }
  898. }
  899. }
  900. if ($proceedToForm) {
  901. return $this->response($this->view->assign('users', $users)
  902. ->fetch('Admin/deleteusers.tpl'));
  903. } else {
  904. return $this->redirect(ModUtil::url($this->name, 'admin', 'view'));
  905. }
  906. }
  907. /**
  908. * Constructs a list of various actions for a list of registrations appropriate for the current user.
  909. *
  910. * NOTE: Internal function.
  911. *
  912. * @param array $reglist The list of registration records.
  913. * @param string $restoreView Indicates where the calling function expects to return to; 'view' indicates
  914. * that the calling function expects to return to the registration list
  915. * and 'display' indicates that the calling function expects to return
  916. * to an individual registration record.
  917. *
  918. * @return array An array of valid action URLs for each registration record in the list.
  919. */
  920. protected function getActionsForRegistrations(array $reglist, $restoreView='view')
  921. {
  922. $actions = array();
  923. if (!empty($reglist)) {
  924. $approvalOrder = $this->getVar('moderation_order', UsersConstant::APPROVAL_BEFORE);
  925. // Don't try to put any visual elements here (images, titles, colors, css classes, etc.). Leave that to
  926. // the template, so that they can be customized without hacking the core code. In fact, all we really need here
  927. // is what options are enabled. The template could build everything else. We will put the URL for the action
  928. // in the array for convenience, but that could be done in the template too, really.
  929. //
  930. // Make certain that the following goes from most restricted to least (ADMIN...NONE order). Having the
  931. // security check as the outer if statement, and similar foreach loops within each saves on repeated checking
  932. // of permissions, speeding things up a bit.
  933. if (SecurityUtil::checkPermission('Users::', '::', ACCESS_ADMIN)) {
  934. $actions['count'] = 6;
  935. foreach ($reglist as $key => $reginfo) {
  936. $enableVerify = !$reginfo['isverified'];
  937. $enableApprove = !$reginfo['isapproved'];
  938. $enableForced = !$reginfo['isverified'] && isset($reginfo['pass']) && !empty($reginfo['pass']);
  939. $actions['list'][$reginfo['uid']] = array(
  940. 'display' => ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid'])),
  941. 'modify' => ModUtil::url($this->name, 'admin', 'modifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)),
  942. 'verify' => $enableVerify ? ModUtil::url($this->name, 'admin', 'verifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)) : false,
  943. 'approve' => $enableApprove ? ModUtil::url($this->name, 'admin', 'approveRegistration', array('uid' => $reginfo['uid'])) : false,
  944. 'deny' => ModUtil::url($this->name, 'admin', 'denyRegistration', array('uid' => $reginfo['uid'])),
  945. 'approveForce' => $enableForced ? ModUtil::url($this->name, 'admin', 'approveRegistration', array('uid' => $reginfo['uid'], 'force' => true)) : false,
  946. );
  947. }
  948. } elseif (SecurityUtil::checkPermission('Users::', '::', ACCESS_DELETE)) {
  949. $actions['count'] = 5;
  950. foreach ($reglist as $key => $reginfo) {
  951. $enableVerify = !$reginfo['isverified'] && (($approvalOrder != UsersConstant::APPROVAL_BEFORE) || $reginfo['isapproved']);
  952. $enableApprove = !$reginfo['isapproved'] && (($approvalOrder != UsersConstant::APPROVAL_AFTER) || $reginfo['isverified']);
  953. $actions['list'][$reginfo['uid']] = array(
  954. 'display' => ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid'])),
  955. 'modify' => ModUtil::url($this->name, 'admin', 'modifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)),
  956. 'verify' => $enableVerify ? ModUtil::url($this->name, 'admin', 'verifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)) : false,
  957. 'approve' => $enableApprove ? ModUtil::url($this->name, 'admin', 'approveRegistration', array('uid' => $reginfo['uid'])) : false,
  958. 'deny' => ModUtil::url($this->name, 'admin', 'denyRegistration', array('uid' => $reginfo['uid'])),
  959. );
  960. }
  961. } elseif (SecurityUtil::checkPermission('Users::', '::', ACCESS_ADD)) {
  962. $actions['count'] = 4;
  963. foreach ($reglist as $key => $reginfo) {
  964. $actionUrlArgs['uid'] = $reginfo['uid'];
  965. $enableVerify = !$reginfo['isverified'] && (($approvalOrder != UsersConstant::APPROVAL_BEFORE) || $reginfo['isapproved']);
  966. $enableApprove = !$reginfo['isapproved'] && (($approvalOrder != UsersConstant::APPROVAL_AFTER) || $reginfo['isverified']);
  967. $actions['list'][$reginfo['uid']] = array(
  968. 'display' => ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid'])),
  969. 'modify' => ModUtil::url($this->name, 'admin', 'modifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)),
  970. 'verify' => $enableVerify ? ModUtil::url($this->name, 'admin', 'verifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)) : false,
  971. 'approve' => $enableApprove ? ModUtil::url($this->name, 'admin', 'approveRegistration', array('uid' => $reginfo['uid'])) : false,
  972. );
  973. }
  974. } elseif (SecurityUtil::checkPermission('Users::', '::', ACCESS_EDIT)) {
  975. $actions['count'] = 3;
  976. foreach ($reglist as $key => $reginfo) {
  977. $actionUrlArgs['uid'] = $reginfo['uid'];
  978. $enableVerify = !$reginfo['isverified'] && (($approvalOrder != UsersConstant::APPROVAL_BEFORE) || $reginfo['isapproved']);
  979. $actions['list'][$reginfo['uid']] = array(
  980. 'display' => ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid'])),
  981. 'modify' => ModUtil::url($this->name, 'admin', 'modifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)),
  982. 'verify' => $enableVerify ? ModUtil::url($this->name, 'admin', 'verifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)) : false,
  983. );
  984. }
  985. } elseif (SecurityUtil::checkPermission('Users::', '::', ACCESS_MODERATE)) {
  986. $actions['count'] = 2;
  987. foreach ($reglist as $key => $reginfo) {
  988. $actionUrlArgs['uid'] = $reginfo['uid'];
  989. $enableVerify = !$reginfo['isverified'] && (($approvalOrder != UsersConstant::APPROVAL_BEFORE) || $reginfo['isapproved']);
  990. $actions['list'][$reginfo['uid']] = array(
  991. 'display' => ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid'])),
  992. 'verify' => $enableVerify ? ModUtil::url($this->name, 'admin', 'verifyRegistration', array('uid' => $reginfo['uid'], 'restoreview' => $restoreView)) : false,
  993. );
  994. }
  995. }
  996. }
  997. return $actions;
  998. }
  999. /**
  1000. * Shows all the registration requests (applications), and the options available to the current user.
  1001. *
  1002. * Parameters passed via GET:
  1003. * --------------------------
  1004. * string restorview If returning from an action, and the previous view should be restored, then the value should be 'view';
  1005. * otherwise not present.
  1006. * integer startnum The ordinal number of the first record to display, especially if using itemsperpage to limit
  1007. * the number of records on a single page.
  1008. *
  1009. * Parameters passed via POST:
  1010. * ---------------------------
  1011. * numeric userid The user id of the user to be deleted.
  1012. *
  1013. * Parameters passed via SESSION:
  1014. * ------------------------------
  1015. * Namespace: Zikula_Users
  1016. * Variable: Users_Controller_Admin_viewRegistrations
  1017. * Type: array
  1018. * Contents: An array containing the parameters to restore the view configuration prior to executing an action.
  1019. *
  1020. * @return string HTML string containing the rendered template.
  1021. *
  1022. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access.
  1023. */
  1024. public function viewRegistrationsAction()
  1025. {
  1026. // security check
  1027. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_MODERATE)) {
  1028. throw new \Zikula\Framework\Exception\ForbiddenException();
  1029. }
  1030. $regCount = ModUtil::apiFunc($this->name, 'registration', 'countAll');
  1031. $limitNumRows = $this->getVar(UsersConstant::MODVAR_ITEMS_PER_PAGE, UsersConstant::DEFAULT_ITEMS_PER_PAGE);
  1032. if (!is_numeric($limitNumRows) || ((int)$limitNumRows != $limitNumRows) || (($limitNumRows < 1) && ($limitNumRows != -1))) {
  1033. $limitNumRows = 25;
  1034. }
  1035. $backFromAction = $this->request->query->get('restoreview', false);
  1036. if ($backFromAction) {
  1037. $returnArgs = $this->request->getSession()->get('Users_Controller_Admin_viewRegistrations', array('startnum' => 1), 'Zikula_Users');
  1038. $this->request->getSession()->del('Users_admin_viewRegistrations', 'Zikula_Users');
  1039. if ($limitNumRows < 1) {
  1040. unset($returnArgs['startnum']);
  1041. } elseif (!isset($returnArgs['startnum']) || !is_numeric($returnArgs['startnum']) || empty($returnArgs['startnum'])
  1042. || ((int)$returnArgs['startnum'] != $returnArgs['startnum']) || ($returnArgs['startnum'] < 1)) {
  1043. $returnArgs['startnum'] = 1;
  1044. } elseif ($returnArgs['startnum'] > $regCount) {
  1045. // Probably deleted something. Reset to last page.
  1046. $returnArgs['startnum'] = $regCount - ($regCount % $limitNumRows) + 1;
  1047. } elseif (($returnArgs['startnum'] % $limitNumRows) != 1) {
  1048. // Probably deleted something. Reset to last page.
  1049. $returnArgs['startnum'] = $returnArgs['startnum'] - ($returnArgs['startnum'] % $limitNumRows) + 1;
  1050. }
  1051. // Reset the URL and load the proper page.
  1052. return $this->redirect(ModUtil::url($this->name, 'admin', 'viewRegistrations', $returnArgs));
  1053. } else {
  1054. $reset = false;
  1055. $startNum = $this->request->query->get('startnum', 1);
  1056. if (!is_numeric($startNum) || empty($startNum) || ((int)$startNum != $startNum) || ($startNum < 1)) {
  1057. $limitOffset = -1;
  1058. $reset = true;
  1059. } elseif ($limitNumRows < 1) {
  1060. $limitOffset = -1;
  1061. } elseif ($startNum > $regCount) {
  1062. // Probably deleted something. Reset to last page.
  1063. $limitOffset = $regCount - ($regCount % $limitNumRows);
  1064. $reset = (($regCount == 0) && ($startNum != 1));
  1065. } elseif (($startNum % $limitNumRows) != 1) {
  1066. // Reset to page boundary
  1067. $limitOffset = $startNum - ($startNum % $limitOffset);
  1068. $reset = true;
  1069. } else {
  1070. $limitOffset = $startNum - 1;
  1071. }
  1072. if ($reset) {
  1073. $returnArgs = array();
  1074. if ($limitOffset >= 0) {
  1075. $returnArgs['startnum'] = $limitOffset + 1;
  1076. }
  1077. return $this->redirect(ModUtil::url($this->name, 'admin', 'viewRegistrations', $returnArgs));
  1078. }
  1079. }
  1080. $sessionVars = array(
  1081. 'startnum' => ($limitOffset + 1),
  1082. );
  1083. $this->request->getSession()->set('Users_Controller_Admin_viewRegistrations', $sessionVars, 'Zikula_Users');
  1084. $reglist = ModUtil::apiFunc($this->name, 'registration', 'getAll', array('limitoffset' => $limitOffset, 'limitnumrows' => $limitNumRows));
  1085. if (($reglist === false) || !is_array($reglist)) {
  1086. if (!$this->request->getSession()->getFlashBag()->has(Zikula_Session::MESSAGE_ERROR)) {
  1087. $this->registerError($this->__('An error occurred while trying to retrieve the registration records.'));
  1088. }
  1089. return $this->redirect(ModUtil::url($this->name, 'admin'), null, 500);
  1090. }
  1091. $actions = $this->getActionsForRegistrations($reglist, 'view');
  1092. $pager = array();
  1093. if ($limitNumRows > 0) {
  1094. $pager = array(
  1095. 'rowcount' => $regCount,
  1096. 'limit' => $limitNumRows,
  1097. 'posvar' => 'startnum',
  1098. );
  1099. }
  1100. return $this->response($this->view->assign('reglist', $reglist)
  1101. ->assign('actions', $actions)
  1102. ->assign('pager', $pager)
  1103. ->fetch('Admin/viewregistrations.tpl'));
  1104. }
  1105. /**
  1106. * Displays the information on a single registration request.
  1107. *
  1108. * Parameters passed via GET:
  1109. * --------------------------
  1110. * numeric uid The id of the registration request (id) to retrieve and display.
  1111. *
  1112. * Parameters passed via POST:
  1113. * ---------------------------
  1114. * None.
  1115. *
  1116. * Parameters passed via SESSION:
  1117. * ------------------------------
  1118. * None.
  1119. *
  1120. * @return string HTML string containing the rendered template.
  1121. *
  1122. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access, or if the method of accessing this function is improper.
  1123. */
  1124. public function displayRegistrationAction()
  1125. {
  1126. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_MODERATE)) {
  1127. throw new \Zikula\Framework\Exception\ForbiddenException();
  1128. }
  1129. // Get parameters from whatever input we need.
  1130. // (Note that the name of the passed parameter is 'userid' but that it
  1131. // is actually a registration application id.)
  1132. if ($this->request->getMethod() == 'GET') {
  1133. $uid = $this->request->query->get('uid', null);
  1134. } else {
  1135. throw new \Zikula\Framework\Exception\ForbiddenException();
  1136. }
  1137. if (empty($uid) || !is_numeric($uid)) {
  1138. $this->registerError(LogUtil::getErrorMsgArgs());
  1139. return $this->redirect(ModUtil::url($this->name, 'admin', 'viewRegistrations', array('return' => true)));
  1140. }
  1141. $reginfo = ModUtil::apiFunc($this->name, 'registration', 'get', array('uid' => $uid));
  1142. if (!$reginfo) {
  1143. // get application could fail (return false) because of a nonexistant
  1144. // record, no permission to read an existing record, or a database error
  1145. $this->registerError($this->__('Unable to retrieve registration record. '
  1146. . 'The record with the specified id might not exist, or you might not have permission to access that record.'));
  1147. return false;
  1148. }
  1149. // So expiration can be displayed
  1150. $regExpireDays = $this->getVar('reg_expiredays', 0);
  1151. if (!$reginfo['isverified'] && !empty($reginfo['verificationsent']) && ($regExpireDays > 0)) {
  1152. try {
  1153. $expiresUTC = new DateTime($reginfo['verificationsent'], new DateTimeZone('UTC'));
  1154. } catch (Exception $e) {
  1155. $expiresUTC = new DateTime(UsersConstant::EXPIRED, new DateTimeZone('UTC'));
  1156. }
  1157. $expiresUTC->modify("+{$regExpireDays} days");
  1158. $reginfo['validuntil'] = DateUtil::formatDatetime($expiresUTC->format(UsersConstant::DATETIME_FORMAT),
  1159. $this->__('%m-%d-%Y %H:%M'));
  1160. }
  1161. $actions = $this->getActionsForRegistrations(array($reginfo), 'display');
  1162. return $this->response($this->view->assign('reginfo', $reginfo)
  1163. ->assign('actions', $actions)
  1164. ->fetch('Admin/displayregistration.tpl'));
  1165. }
  1166. /**
  1167. * Display a form to edit one tegistration account.
  1168. *
  1169. * Parameters passed via GET:
  1170. * --------------------------
  1171. * numeric uid The id of the registration request (id) to retrieve and display.
  1172. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1173. * otherwise not present.
  1174. *
  1175. * Parameters passed via POST:
  1176. * ---------------------------
  1177. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1178. * otherwise not present.
  1179. *
  1180. * See also the definition of {@link Users_Controller_FormData_ModifyRegistrationForm}.
  1181. *
  1182. * Parameters passed via SESSION:
  1183. * ------------------------------
  1184. * None.
  1185. *
  1186. * @return string|bool The rendered template; false on error.
  1187. *
  1188. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have edit access, or if the method of accessing this function is improper.
  1189. */
  1190. public function modifyRegistrationAction()
  1191. {
  1192. if (!SecurityUtil::checkPermission('Users::', 'ANY', ACCESS_EDIT)) {
  1193. throw new \Zikula\Framework\Exception\ForbiddenException();
  1194. }
  1195. $proceedToForm = true;
  1196. $formData = new Users_Controller_FormData_ModifyRegistrationForm('users_modifyreg', $this->container);
  1197. $errorFields = array();
  1198. $errorMessages = array();
  1199. if ($this->request->getMethod() == 'POST') {
  1200. $this->checkCsrfToken();
  1201. $formData->setFromRequestCollection($this->request->request);
  1202. $restoreView = $this->request->request->get('restoreview', 'view');
  1203. $registration = $formData->toUserArray(true);
  1204. $originalRegistration = UserUtil::getVars($registration['uid'], false, 'uid', true);
  1205. $userAttributes = isset($originalRegistration['__ATTRIBUTES__']) ? $originalRegistration['__ATTRIBUTES__'] : array();
  1206. // security check for this record
  1207. if (!SecurityUtil::checkPermission('Users::', "{$originalRegistration['uname']}::{$originalRegistration['uid']}", ACCESS_EDIT)) {
  1208. throw new \Zikula\Framework\Exception\ForbiddenException();
  1209. }
  1210. if ($formData->isValid()) {
  1211. $registrationArgs = array(
  1212. 'reginfo' => $registration,
  1213. 'checkmode' => 'modify',
  1214. 'emailagain' => $formData->getField('emailagain')->getData(),
  1215. 'setpass' => false,
  1216. 'passagain' => '',
  1217. 'antispamanswer' => '',
  1218. );
  1219. $errorFields = ModUtil::apiFunc($this->name, 'registration', 'getRegistrationErrors', $registrationArgs);
  1220. } else {
  1221. $errorFields = $formData->getErrorMessages();
  1222. }
  1223. $event = new GenericEvent($registration, array(), new ValidationProviders());
  1224. $this->dispatcher->dispatch('module.users.ui.validate_edit.modify_registration', $event);
  1225. $validators = $event->getData();
  1226. $hook = new ValidationHook($validators);
  1227. $this->dispatchHooks('users.ui_hooks.registration.validate_edit', $hook);
  1228. $validators = $hook->getValidators();
  1229. if (!$errorFields && !$validators->hasErrors()) {
  1230. $emailUpdated = false;
  1231. if ($originalRegistration['uname'] != $registration['uname']) {
  1232. // UserUtil::setVar does not allow uname to be changed.
  1233. // UserUtil::setVar('uname', $registration['uname'], $originalRegistration['uid']);
  1234. $updatedRegistrationObj = $this->entityManager->find('UsersModule\Entity\User', $originalRegistration['uid']);
  1235. $updatedRegistrationObj['uname'] = $registration['uname'];
  1236. $this->entityManager->flush();
  1237. $eventArgs = array(
  1238. 'action' => 'setVar',
  1239. 'field' => 'uname',
  1240. 'attribute' => null,
  1241. );
  1242. $eventData = array(
  1243. 'old_value' => $originalRegistration['uname'],
  1244. );
  1245. $updateEvent = new GenericEvent($updatedRegistrationObj, $eventArgs, $eventData);
  1246. $this->dispatcher->dispatch('user.registration.update', $updateEvent);
  1247. }
  1248. if ($originalRegistration['theme'] != $registration['theme']) {
  1249. UserUtil::setVar('theme', $registration['theme'], $originalRegistration['uid']);
  1250. }
  1251. if ($originalRegistration['email'] != $registration['email']) {
  1252. UserUtil::setVar('email', $registration['email'], $originalRegistration['uid']);
  1253. $emailUpdated = true;
  1254. }
  1255. $registration = UserUtil::getVars($registration['uid'], true, 'uid', true);
  1256. if ($emailUpdated) {
  1257. $approvalOrder = $this->getVar('moderation_order', UsersConstant::APPROVAL_BEFORE);
  1258. if (!$originalRegistration['isverified'] && (($approvalOrder != UsersConstant::APPROVAL_BEFORE) || $originalRegistration['isapproved'])) {
  1259. $verificationSent = ModUtil::apiFunc($this->name, 'registration', 'sendVerificationCode', array(
  1260. 'reginfo' => $registration,
  1261. 'force' => true,
  1262. ));
  1263. }
  1264. }
  1265. $event = new GenericEvent($registration);
  1266. $this->dispatcher->dispatch($event->getName(), $event);
  1267. $hook = new ProcessHook($registration['uid']);
  1268. $this->dispatchHooks('users.ui_hooks.registration.process_edit', $hook);
  1269. $this->registerStatus($this->__("Done! Saved user's account information."));
  1270. $proceedToForm = false;
  1271. }
  1272. } elseif ($this->request->getMethod() == 'GET') {
  1273. $uid = $this->request->query->get('uid', null);
  1274. if (!is_int($uid)) {
  1275. if (!is_numeric($uid) || ((string)((int)$uid) != $uid)) {
  1276. $this->registerError($this->__('Error! Invalid registration uid.'));
  1277. return $this->redirect(ModUtil::url($this->name, 'admin', 'viewRegistrations', array('restoreview' => true)));
  1278. }
  1279. }
  1280. $registration = ModUtil::apiFunc($this->name, 'registration', 'get', array('uid' => $uid));
  1281. if (!$registration) {
  1282. $this->registerError($this->__('Error! Unable to load registration record.'));
  1283. return $this->redirect(ModUtil::url($this->name, 'admin', 'viewRegistrations', array('restoreview' => true)));
  1284. }
  1285. $userAttributes = isset($registration['__ATTRIBUTES__']) ? $registration['__ATTRIBUTES__'] : array();
  1286. $formData->setFromArray($registration);
  1287. $formData->getField('emailagain')->setData($registration['email']);
  1288. $restoreView = $this->request->query->get('restoreview', 'view');
  1289. } else {
  1290. throw new \Zikula\Framework\Exception\ForbiddenException();
  1291. }
  1292. if ($proceedToForm) {
  1293. // security check for this record
  1294. if (!SecurityUtil::checkPermission('Users::', "{$registration['uname']}::{$registration['uid']}", ACCESS_EDIT)) {
  1295. throw new \Zikula\Framework\Exception\ForbiddenException();
  1296. }
  1297. $rendererArgs = array(
  1298. 'user_attributes' => $userAttributes,
  1299. 'errorMessages' => $errorMessages,
  1300. 'errorFields' => $errorFields,
  1301. 'restoreview' => $restoreView,
  1302. );
  1303. // Return the output that has been generated by this function
  1304. return $this->response($this->view->assign_by_ref('formData', $formData)
  1305. ->assign($rendererArgs)
  1306. ->fetch('Admin/modifyregistration.tpl'));
  1307. } else {
  1308. if ($restoreView == 'view') {
  1309. return $this->redirect(ModUtil::url($this->name, 'admin', 'viewRegistrations', array('restoreview' => true)));
  1310. } else {
  1311. return $this->redirect(ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $registration['uid'])));
  1312. }
  1313. }
  1314. }
  1315. /**
  1316. * Renders and processes the admin's force-verify form.
  1317. *
  1318. * Renders and processes a form confirming an administrators desire to skip verification for
  1319. * a registration record, approve it and add it to the users table.
  1320. *
  1321. * Parameters passed via GET:
  1322. * --------------------------
  1323. * numeric uid The id of the registration request (id) to verify.
  1324. * boolean force True to force the registration to be verified.
  1325. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1326. * otherwise not present.
  1327. *
  1328. * Parameters passed via POST:
  1329. * ---------------------------
  1330. * numeric uid The id of the registration request (uid) to verify.
  1331. * boolean force True to force the registration to be verified.
  1332. * boolean confirmed True to execute this function's action.
  1333. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1334. * otherwise not present.
  1335. *
  1336. * Parameters passed via SESSION:
  1337. * ------------------------------
  1338. * None.
  1339. *
  1340. * @return string|bool The rendered template; true on success; otherwise false.
  1341. *
  1342. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access, or if the method of accessing this function is improper.
  1343. */
  1344. public function verifyRegistrationAction()
  1345. {
  1346. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_MODERATE)) {
  1347. throw new \Zikula\Framework\Exception\ForbiddenException();
  1348. }
  1349. if ($this->request->getMethod() == 'GET') {
  1350. $uid = $this->request->query->get('uid', null);
  1351. $forceVerification = $this->currentUserIsAdmin() && $this->request->query->get('force', false);
  1352. $restoreView = $this->request->query->get('restoreview', 'view');
  1353. $confirmed = false;
  1354. } elseif ($this->request->getMethod() == 'POST') {
  1355. $this->checkCsrfToken();
  1356. $uid = $this->request->request->get('uid', null);
  1357. $forceVerification = $this->currentUserIsAdmin() && $this->request->request->get('force', false);
  1358. $restoreView = $this->request->request->get('restoreview', 'view');
  1359. $confirmed = $this->request->request->get('confirmed', false);
  1360. } else {
  1361. throw new \Zikula\Framework\Exception\ForbiddenException();
  1362. }
  1363. if (!isset($uid) || !is_numeric($uid) || ((int)$uid != $uid)) {
  1364. $this->registerError(LogUtil::getErrorMsgArgs());
  1365. return false;
  1366. }
  1367. // Got just a uid.
  1368. $reginfo = ModUtil::apiFunc($this->name, 'registration', 'get', array('uid' => $uid));
  1369. if (!$reginfo) {
  1370. $this->registerError($this->__f('Error! Unable to retrieve registration record with uid \'%1$s\'', $uid));
  1371. return false;
  1372. }
  1373. if ($restoreView == 'display') {
  1374. $cancelUrl = ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid']));
  1375. } else {
  1376. $cancelUrl = ModUtil::url($this->name, 'admin', 'viewRegistrations', array('restoreview' => true));
  1377. }
  1378. $approvalOrder = $this->getVar('moderation_order', UsersConstant::APPROVAL_BEFORE);
  1379. if ($reginfo['isverified']) {
  1380. $this->registerError($this->__f('Error! A verification code cannot be sent for the registration record for \'%1$s\'. It is already verified.', $reginfo['uname']));
  1381. return $this->redirect($cancelUrl);
  1382. } elseif (!$forceVerification && ($approvalOrder == UsersConstant::APPROVAL_BEFORE) && !$reginfo['isapproved']) {
  1383. $this->registerError($this->__f('Error! A verification code cannot be sent for the registration record for \'%1$s\'. It must first be approved.', $reginfo['uname']));
  1384. return $this->redirect($cancelUrl);
  1385. }
  1386. if (!$confirmed) {
  1387. // So expiration can be displayed
  1388. $regExpireDays = $this->getVar('reg_expiredays', 0);
  1389. if (!$reginfo['isverified'] && $reginfo['verificationsent'] && ($regExpireDays > 0)) {
  1390. try {
  1391. $expiresUTC = new DateTime($reginfo['verificationsent'], new DateTimeZone('UTC'));
  1392. } catch (Exception $e) {
  1393. $expiresUTC = new DateTime(UsersConstant::EXPIRED, new DateTimeZone('UTC'));
  1394. }
  1395. $expiresUTC->modify("+{$regExpireDays} days");
  1396. $reginfo['validuntil'] = DateUtil::formatDatetime($expiresUTC->format(UsersConstant::DATETIME_FORMAT),
  1397. $this->__('%m-%d-%Y %H:%M'));
  1398. }
  1399. return $this->response($this->view->assign('reginfo', $reginfo)
  1400. ->assign('restoreview', $restoreView)
  1401. ->assign('force', $forceVerification)
  1402. ->assign('cancelurl', $cancelUrl)
  1403. ->fetch('Admin/verifyregistration.tpl'));
  1404. } else {
  1405. $verificationSent = ModUtil::apiFunc($this->name, 'registration', 'sendVerificationCode', array(
  1406. 'reginfo' => $reginfo,
  1407. 'force' => $forceVerification,
  1408. ));
  1409. if (!$verificationSent) {
  1410. $this->registerError($this->__f('Sorry! There was a problem sending a verification code to \'%1$s\'.', $reginfo['uname']));
  1411. return $this->redirect($cancelUrl);
  1412. } else {
  1413. $this->registerStatus($this->__f('Done! Verification code sent to \'%1$s\'.', $reginfo['uname']));
  1414. return $this->redirect($cancelUrl);
  1415. }
  1416. }
  1417. }
  1418. /**
  1419. * Renders and processes a form confirming an administrators desire to approve a registration.
  1420. *
  1421. * If the registration record is also verified (or verification is not needed) a users table
  1422. * record is created.
  1423. *
  1424. * Parameters passed via GET:
  1425. * --------------------------
  1426. * numeric uid The id of the registration request (id) to approve.
  1427. * boolean force True to force the registration to be approved.
  1428. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1429. * otherwise not present.
  1430. *
  1431. * Parameters passed via POST:
  1432. * ---------------------------
  1433. * numeric uid The id of the registration request (uid) to approve.
  1434. * boolean force True to force the registration to be approved.
  1435. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1436. * otherwise not present.
  1437. *
  1438. * Parameters passed via SESSION:
  1439. * ------------------------------
  1440. * None.
  1441. *
  1442. * @return string|bool The rendered template; true on success; otherwise false.
  1443. *
  1444. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have moderate access, or if the method of accessing this function is improper.
  1445. */
  1446. public function approveRegistrationAction()
  1447. {
  1448. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_MODERATE)) {
  1449. throw new \Zikula\Framework\Exception\ForbiddenException();
  1450. }
  1451. if ($this->request->getMethod() == 'GET') {
  1452. $uid = $this->request->query->get('uid', null);
  1453. $forceVerification = $this->currentUserIsAdmin() && $this->request->query->get('force', false);
  1454. $restoreView = $this->request->query->get('restoreview', 'view');
  1455. } elseif ($this->request->getMethod() == 'POST') {
  1456. $uid = $this->request->request->get('uid', null);
  1457. $forceVerification = $this->currentUserIsAdmin() && $this->request->request->get('force', false);
  1458. $restoreView = $this->request->request->get('restoreview', 'view');
  1459. } else {
  1460. throw new \Zikula\Framework\Exception\ForbiddenException();
  1461. }
  1462. if (!isset($uid) || !is_numeric($uid) || ((int)$uid != $uid)) {
  1463. $this->registerError(LogUtil::getErrorMsgArgs());
  1464. return false;
  1465. }
  1466. // Got just an id.
  1467. $reginfo = ModUtil::apiFunc($this->name, 'registration', 'get', array('uid' => $uid));
  1468. if (!$reginfo) {
  1469. $this->registerError($this->__f('Error! Unable to retrieve registration record with uid \'%1$s\'', $uid));
  1470. return false;
  1471. }
  1472. if ($restoreView == 'display') {
  1473. $cancelUrl = ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid']));
  1474. } else {
  1475. $cancelUrl = ModUtil::url($this->name, 'admin', 'viewRegistrations', array('restoreview' => true));
  1476. }
  1477. $approvalOrder = $this->getVar('moderation_order', UsersConstant::APPROVAL_BEFORE);
  1478. if ($reginfo['isapproved'] && !$forceVerification) {
  1479. $this->registerError($this->__f('Warning! Nothing to do! The registration record with uid \'%1$s\' is already approved.', $reginfo['uid']));
  1480. return $this->redirect($cancelUrl);
  1481. } elseif (!$forceVerification && ($approvalOrder == UsersConstant::APPROVAL_AFTER) && !$reginfo['isapproved']
  1482. && !SecurityUtil::checkPermission('Users::', '::', ACCESS_ADMIN)) {
  1483. $this->registerError($this->__f('Error! The registration record with uid \'%1$s\' cannot be approved. The registration\'s e-mail address must first be verified.', $reginfo['uid']));
  1484. return $this->redirect($cancelUrl);
  1485. } elseif ($forceVerification && (!isset($reginfo['pass']) || empty($reginfo['pass']))) {
  1486. $this->registerError($this->__f('Error! E-mail verification cannot be skipped for \'%1$s\'. The user must establish a password as part of the verification process.', $reginfo['uname']));
  1487. return $this->redirect($cancelUrl);
  1488. }
  1489. $confirmed = $this->request->query->get('confirmed', $this->request->request->get('confirmed', false));
  1490. if (!$confirmed) {
  1491. // Bad or no auth key, or bad or no confirmation, so display confirmation.
  1492. // So expiration can be displayed
  1493. $regExpireDays = $this->getVar('reg_expiredays', 0);
  1494. if (!$reginfo['isverified'] && !empty($reginfo['verificationsent']) && ($regExpireDays > 0)) {
  1495. try {
  1496. $expiresUTC = new DateTime($reginfo['verificationsent'], new DateTimeZone('UTC'));
  1497. } catch (Exception $e) {
  1498. $expiresUTC = new DateTime(UsersConstant::EXPIRED, new DateTimeZone('UTC'));
  1499. }
  1500. $expiresUTC->modify("+{$regExpireDays} days");
  1501. $reginfo['validuntil'] = DateUtil::formatDatetime($expiresUTC->format(UsersConstant::DATETIME_FORMAT),
  1502. $this->__('%m-%d-%Y %H:%M'));
  1503. }
  1504. return $this->response($this->view->assign('reginfo', $reginfo)
  1505. ->assign('restoreview', $restoreView)
  1506. ->assign('force', $forceVerification)
  1507. ->assign('cancelurl', $cancelUrl)
  1508. ->fetch('Admin/approveregistration.tpl'));
  1509. } else {
  1510. $this->checkCsrfToken();
  1511. $approved = ModUtil::apiFunc($this->name, 'registration', 'approve', array(
  1512. 'reginfo' => $reginfo,
  1513. 'force' => $forceVerification,
  1514. ));
  1515. if (!$approved) {
  1516. $this->registerError($this->__f('Sorry! There was a problem approving the registration for \'%1$s\'.', $reginfo['uname']));
  1517. return $this->redirect($cancelUrl);
  1518. } else {
  1519. if (isset($approved['uid'])) {
  1520. $this->registerStatus($this->__f('Done! The registration for \'%1$s\' has been approved and a new user account has been created.', $reginfo['uname']));
  1521. return $this->redirect($cancelUrl);
  1522. } else {
  1523. $this->registerStatus($this->__f('Done! The registration for \'%1$s\' has been approved and is awaiting e-mail verification.', $reginfo['uname']));
  1524. return $this->redirect($cancelUrl);
  1525. }
  1526. }
  1527. }
  1528. }
  1529. /**
  1530. * Render and process a form confirming the administrator's rejection of a registration.
  1531. *
  1532. * If the denial is confirmed, the registration is deleted from the database.
  1533. *
  1534. * Parameters passed via GET:
  1535. * --------------------------
  1536. * numeric uid The id of the registration request (id) to deny.
  1537. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1538. * otherwise not present.
  1539. *
  1540. * Parameters passed via POST:
  1541. * ---------------------------
  1542. * numeric uid The id of the registration request (uid) to deny.
  1543. * boolean confirmed True to execute this function's action.
  1544. * boolean usernorify True to notify the user that his registration request was denied; otherwise false.
  1545. * string reason The reason the registration request was denied, included in the notification.
  1546. * string restorview To restore the main view to use the filtering options present prior to executing this function, then 'view',
  1547. * otherwise not present.
  1548. *
  1549. * Parameters passed via SESSION:
  1550. * ------------------------------
  1551. * None.
  1552. *
  1553. * @return string|bool The rendered template; true on success; otherwise false.
  1554. *
  1555. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the user does not have delete access, or if the method used to access this function is improper.
  1556. */
  1557. public function denyRegistrationAction()
  1558. {
  1559. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_DELETE)) {
  1560. throw new \Zikula\Framework\Exception\ForbiddenException();
  1561. }
  1562. if ($this->request->getMethod() == 'GET') {
  1563. $uid = $this->request->query->get('uid', null);
  1564. $restoreView = $this->request->query->get('restoreview', 'view');
  1565. $confirmed = false;
  1566. } elseif ($this->request->getMethod() == 'POST') {
  1567. $this->checkCsrfToken();
  1568. $uid = $this->request->request->get('uid', null);
  1569. $restoreView = $this->request->request->get('restoreview', 'view');
  1570. $sendNotification = $this->request->request->get('usernotify', false);
  1571. $reason = $this->request->request->get('reason', '');
  1572. $confirmed = $this->request->request->get('confirmed', false);
  1573. } else {
  1574. throw new \Zikula\Framework\Exception\ForbiddenException();
  1575. }
  1576. if (!isset($uid) || !is_numeric($uid) || ((int)$uid != $uid)) {
  1577. $this->registerError(LogUtil::getErrorMsgArgs());
  1578. return false;
  1579. }
  1580. // Got just a uid.
  1581. $reginfo = ModUtil::apiFunc($this->name, 'registration', 'get', array('uid' => $uid));
  1582. if (!$reginfo) {
  1583. $this->registerError($this->__f('Error! Unable to retrieve registration record with uid \'%1$s\'', $uid));
  1584. return false;
  1585. }
  1586. if ($restoreView == 'display') {
  1587. $cancelUrl = ModUtil::url($this->name, 'admin', 'displayRegistration', array('uid' => $reginfo['uid']));
  1588. } else {
  1589. $cancelUrl = ModUtil::url($this->name, 'admin', 'viewRegistrations', array('restoreview' => true));
  1590. }
  1591. if (!$confirmed) {
  1592. // Bad or no auth key, or bad or no confirmation, so display confirmation.
  1593. // So expiration can be displayed
  1594. $regExpireDays = $this->getVar('reg_expiredays', 0);
  1595. if (!$reginfo['isverified'] && !empty($reginfo['verificationsent']) && ($regExpireDays > 0)) {
  1596. try {
  1597. $expiresUTC = new DateTime($reginfo['verificationsent'], new DateTimeZone('UTC'));
  1598. } catch (Exception $e) {
  1599. $expiresUTC = new DateTime(UsersConstant::EXPIRED, new DateTimeZone('UTC'));
  1600. }
  1601. $expiresUTC->modify("+{$regExpireDays} days");
  1602. $reginfo['validuntil'] = DateUtil::formatDatetime($expiresUTC->format(UsersConstant::DATETIME_FORMAT),
  1603. $this->__('%m-%d-%Y %H:%M'));
  1604. }
  1605. return $this->response($this->view->assign('reginfo', $reginfo)
  1606. ->assign('restoreview', $restoreView)
  1607. ->assign('cancelurl', $cancelUrl)
  1608. ->fetch('Admin/denyregistration.tpl'));
  1609. } else {
  1610. $denied = ModUtil::apiFunc($this->name, 'registration', 'remove', array(
  1611. 'reginfo' => $reginfo,
  1612. ));
  1613. if (!$denied) {
  1614. $this->registerError($this->__f('Sorry! There was a problem deleting the registration for \'%1$s\'.', $reginfo['uname']));
  1615. return $this->redirect($cancelUrl);
  1616. } else {
  1617. if ($sendNotification) {
  1618. $siteurl = System::getBaseUrl();
  1619. $rendererArgs = array(
  1620. 'sitename' => System::getVar('sitename'),
  1621. 'siteurl' => substr($siteurl, 0, strlen($siteurl)-1),
  1622. 'reginfo' => $reginfo,
  1623. 'reason' => $reason,
  1624. );
  1625. $sent = ModUtil::apiFunc($this->name, 'user', 'sendNotification', array(
  1626. 'toAddress' => $reginfo['email'],
  1627. 'notificationType' => 'regdeny',
  1628. 'templateArgs' => $rendererArgs
  1629. ));
  1630. }
  1631. $this->registerStatus($this->__f('Done! The registration for \'%1$s\' has been denied and deleted.', $reginfo['uname']));
  1632. return $this->redirect($cancelUrl);
  1633. }
  1634. }
  1635. }
  1636. /**
  1637. * Edit and update module configuration settings.
  1638. *
  1639. * Parameters passed via GET:
  1640. * --------------------------
  1641. * None.
  1642. *
  1643. * Parameters passed via POST:
  1644. * ---------------------------
  1645. * See the definition of {@link Users_Controller_FormData_ConfigForm}.
  1646. *
  1647. * Parameters passed via SESSION:
  1648. * ------------------------------
  1649. * None.
  1650. *
  1651. * @return string The rendered configuration settings template.
  1652. *
  1653. * @throws \Zikula\Framework\Exception\FatalException Thrown if the function is accessed improperly.
  1654. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have admin access.
  1655. */
  1656. public function configAction()
  1657. {
  1658. // Security check
  1659. if (!(SecurityUtil::checkPermission($this->name . '::', '::', ACCESS_ADMIN))) {
  1660. throw new \Zikula\Framework\Exception\ForbiddenException();
  1661. }
  1662. $configData = new FormData\ConfigForm('users_config', $this->container);
  1663. $errorFields = array();
  1664. if ($this->request->getMethod() == 'POST') {
  1665. $this->checkCsrfToken();
  1666. $modVars = $this->request->request;
  1667. $configData->setFromRequestCollection($modVars);
  1668. if ($configData->isValid()) {
  1669. $modVars = $configData->toArray();
  1670. $this->setVars($modVars);
  1671. $this->registerStatus($this->__('Done! Users module settings have been saved.'));
  1672. $event = new GenericEvent(null, array(), $modVars);
  1673. $this->dispatcher->dispatch('module.users.config.updated', $event);
  1674. } else {
  1675. $errorFields = $configData->getErrorMessages();
  1676. $errorCount = count($errorFields);
  1677. $this->registerError($this->_fn('There was a problem with one of the module settings. Please review the message below, correct the error, and resubmit your changes.',
  1678. 'There were problems with %1$d module settings. Please review the messages below, correct the errors, and resubmit your changes.',
  1679. $errorCount, array($errorCount)));
  1680. }
  1681. } elseif (!$this->request->getMethod() == 'GET') {
  1682. throw new \Zikula\Framework\Exception\FatalException();
  1683. }
  1684. return $this->response($this->view->assign_by_ref('configData', $configData)
  1685. ->assign('errorFields', $errorFields)
  1686. ->fetch('Admin/config.tpl'));
  1687. }
  1688. /**
  1689. * Show the form to choose a CSV file and import several users from this file.
  1690. *
  1691. * Parameters passed via the $args array:
  1692. * --------------------------------------
  1693. * boolean $args['confirmed'] True if the user has confirmed the upload/import. Used as the default if $_POST['confirmed']
  1694. * is not set. Allows this function to be called internally, rather than as a result of a form post.
  1695. * array $args['importFile'] Information about the file to import. Used as the default if $_FILES['importFile'] is not set.
  1696. * Allows this function to be called internally, rather than as a result of a form post.
  1697. * integer $args['delimiter'] A code indicating the delimiter used in the file. Used as the default if $_POST['delimiter']
  1698. * is not set. Allows this function to be called internally, rather than as a result of a form post.
  1699. *
  1700. * Parameters passed via GET:
  1701. * --------------------------
  1702. * None.
  1703. *
  1704. * Parameters passed via POST:
  1705. * ---------------------------
  1706. * boolean confirmed True if the user has confirmed the upload/import.
  1707. * array importFile Structured information about the file to import, from <input type="file" name="fileFieldName" ... /> and stored
  1708. * in $_FILES['fileFieldName']. See http://php.net/manual/en/features.file-upload.post-method.php .
  1709. * integer delimiter A code indicating the type of delimiter found in the import file. 1 = comma, 2 = semicolon, 3 = colon.
  1710. *
  1711. * Parameters passed via SESSION:
  1712. * ------------------------------
  1713. * None.
  1714. *
  1715. * @param array $args All arguments passed to the function.
  1716. *
  1717. * @return redirect user to admin main page if success and show again the forn otherwise
  1718. *
  1719. * @throws \Zikula\Framework\Exception\FatalException Thrown if the $args parameter is not valid.
  1720. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have add access.
  1721. */
  1722. public function importAction(array $args = array())
  1723. {
  1724. // security check
  1725. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_ADD)) {
  1726. throw new \Zikula\Framework\Exception\ForbiddenException();
  1727. }
  1728. // get input values. Check for direct function call first because calling function might be either get or post
  1729. if (isset($args) && is_array($args) && !empty($args)) {
  1730. $confirmed = isset($args['confirmed']) ? $args['confirmed'] : false;
  1731. } elseif (isset($args) && !is_array($args)) {
  1732. throw new \Zikula\Framework\Exception\FatalException(LogUtil::getErrorMsgArgs());
  1733. } elseif ($this->request->getMethod() == 'GET') {
  1734. $confirmed = false;
  1735. } elseif ($this->request->getMethod() == 'POST') {
  1736. $this->checkCsrfToken();
  1737. $confirmed = $this->request->request->get('confirmed', false);
  1738. }
  1739. // set default parameters
  1740. $minpass = $this->getVar('minpass');
  1741. $defaultGroup = ModUtil::getVar('Groups', 'defaultgroup');
  1742. if ($confirmed) {
  1743. // get other import values
  1744. $importFile = $this->request->getFiles()->get('importFile', isset($args['importFile']) ? $args['importFile'] : null);
  1745. $delimiter = $this->request->request->get('delimiter', isset($args['delimiter']) ? $args['delimiter'] : null);
  1746. $importResults = $this->uploadImport($importFile, $delimiter);
  1747. if ($importResults == '') {
  1748. // the users have been imported successfully
  1749. $this->registerStatus($this->__('Done! Users imported successfully.'));
  1750. return $this->redirect(ModUtil::url($this->name, 'admin', 'index'));
  1751. }
  1752. }
  1753. // shows the form
  1754. $post_max_size = ini_get('post_max_size');
  1755. // get default group
  1756. $group = ModUtil::apiFunc('GroupsModule','user','get', array('gid' => $defaultGroup));
  1757. $defaultGroup = $defaultGroup . ' (' . $group['name'] . ')';
  1758. return $this->response($this->view->assign('importResults', isset($importResults) ? $importResults : '')
  1759. ->assign('post_max_size', $post_max_size)
  1760. ->assign('minpass', $minpass)
  1761. ->assign('defaultGroup', $defaultGroup)
  1762. ->fetch('Admin/import.tpl'));
  1763. }
  1764. /**
  1765. * Show the form to export a CSV file of users.
  1766. *
  1767. * Parameters passed via the $args array:
  1768. * --------------------------------------
  1769. * boolean $args['confirmed'] True if the user has confirmed the export.
  1770. * string $args['exportFile'] Filename of the file to export (optional) (default=users.csv)
  1771. * integer $args['delimiter'] A code indicating the type of delimiter found in the export file. 1 = comma, 2 = semicolon, 3 = colon, 4 = tab.
  1772. * integer $args['exportEmail'] Flag to export email addresses, 1 for yes.
  1773. * integer $args['exportTitles'] Flag to export a title row, 1 for yes.
  1774. * integer $args['exportLastLogin'] Flag to export the last login date/time, 1 for yes.
  1775. * integer $args['exportRegDate'] Flag to export the registration date/time, 1 for yes.
  1776. * integer $args['exportGroups'] Flag to export the group membership, 1 for yes.
  1777. *
  1778. * Parameters passed via GET:
  1779. * --------------------------
  1780. * None.
  1781. *
  1782. * Parameters passed via POST:
  1783. * ---------------------------
  1784. * boolean confirmed True if the user has confirmed the export.
  1785. * string exportFile Filename of the file to export (optional) (default=users.csv)
  1786. * integer delimiter A code indicating the type of delimiter found in the export file. 1 = comma, 2 = semicolon, 3 = colon, 4 = tab.
  1787. * integer exportEmail Flag to export email addresses, 1 for yes.
  1788. * integer exportTitles Flag to export a title row, 1 for yes.
  1789. * integer exportLastLogin Flag to export the last login date/time, 1 for yes.
  1790. * integer exportRegDate Flag to export the registration date/time, 1 for yes.
  1791. * integer exportGroups Flag to export the group membership, 1 for yes.
  1792. *
  1793. * Parameters passed via SESSION:
  1794. * ------------------------------
  1795. * None.
  1796. *
  1797. * @param array $args All arguments passed to the function.
  1798. *
  1799. * @return redirect user to the form if confirmed not 1, else export the csv file.
  1800. *
  1801. * @throws \Zikula\Framework\Exception\FatalException Thrown if parameters are passed via the $args array, but $args is invalid.
  1802. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have admin access, or method this function was accessed is invalid.
  1803. */
  1804. public function exporterAction(array $args = array())
  1805. {
  1806. // security check
  1807. if (!SecurityUtil::checkPermission('Users::', '::', ACCESS_ADMIN)) {
  1808. throw new \Zikula\Framework\Exception\ForbiddenException();
  1809. }
  1810. // get input values. Check for direct function call first because calling function might be either get or post
  1811. if (isset($args) && is_array($args) && !empty($args)) {
  1812. $confirmed = isset($args['confirmed']) ? $args['confirmed'] : false;
  1813. $exportFile = isset($args['exportFile']) ? $args['exportFile'] : null;
  1814. $delimiter = isset($args['delimiter']) ? $args['delimiter'] : null;
  1815. $email = isset($args['exportEmail']) ? $args['exportEmail'] : null;
  1816. $titles = isset($args['exportTitles']) ? $args['exportTitles'] : null;
  1817. $lastLogin = isset($args['exportLastLogin']) ? $args['exportLastLogin'] : null;
  1818. $regDate = isset($args['exportRegDate']) ? $args['exportRegDate'] : null;
  1819. $groups = isset($args['exportGroups']) ? $args['exportGroups'] : null;
  1820. } elseif (isset($args) && !is_array($args)) {
  1821. throw new \Zikula\Framework\Exception\FatalException(LogUtil::getErrorMsgArgs());
  1822. } elseif ($this->request->getMethod() == 'GET') {
  1823. $confirmed = false;
  1824. } elseif ($this->request->getMethod() == 'POST') {
  1825. $this->checkCsrfToken();
  1826. $confirmed = $this->request->request->get('confirmed', false);
  1827. $exportFile = $this->request->request->get('exportFile', null);
  1828. $delimiter = $this->request->request->get('delimiter', null);
  1829. $email = $this->request->request->get('exportEmail', null);
  1830. $titles = $this->request->request->get('exportTitles', null);
  1831. $lastLogin = $this->request->request->get('exportLastLogin', null);
  1832. $regDate = $this->request->request->get('exportRegDate', null);
  1833. $groups = $this->request->request->get('exportGroups', null);
  1834. } else {
  1835. throw new \Zikula\Framework\Exception\ForbiddenException();
  1836. }
  1837. if ($confirmed) {
  1838. // get other import values
  1839. $email = (!isset($email) || $email !=='1') ? false : true;
  1840. $titles = (!isset($titles) || $titles !== '1') ? false : true;
  1841. $lastLogin = (!isset($lastLogin) || $lastLogin !=='1') ? false : true;
  1842. $regDate = (!isset($regDate) || $regDate !== '1') ? false : true;
  1843. $groups = (!isset($groups) || $groups !== '1') ? false : true;
  1844. if (!isset($delimiter) || $delimiter == '') {
  1845. $delimiter = 1;
  1846. }
  1847. switch ($delimiter) {
  1848. case 1:
  1849. $delimiter = ",";
  1850. break;
  1851. case 2:
  1852. $delimiter = ";";
  1853. break;
  1854. case 3:
  1855. $delimiter = ":";
  1856. break;
  1857. case 4:
  1858. $delimiter = chr(9);
  1859. }
  1860. if (!isset($exportFile) || $exportFile == '') {
  1861. $exportFile = 'users.csv';
  1862. }
  1863. if (!strrpos($exportFile, '.csv')) {
  1864. $exportFile .= '.csv';
  1865. }
  1866. $colnames = array();
  1867. //get all user fields
  1868. if (ModUtil::available('ProfileModule')) {
  1869. $userfields = ModUtil::apiFunc('ProfileModule', 'user', 'getallactive');
  1870. foreach ($userfields as $item) {
  1871. $colnames[] = $item['prop_attribute_name'];
  1872. }
  1873. }
  1874. // title fields
  1875. if ($titles == 1) {
  1876. $titlerow = array('id', 'uname');
  1877. //titles for optional data
  1878. if ($email == 1) {
  1879. array_push($titlerow, 'email');
  1880. }
  1881. if ($regDate == 1) {
  1882. array_push($titlerow, 'user_regdate');
  1883. }
  1884. if ($lastLogin == 1) {
  1885. array_push($titlerow, 'lastlogin');
  1886. }
  1887. if ($groups == 1) {
  1888. array_push($titlerow, 'groups');
  1889. }
  1890. array_merge($titlerow, $colnames);
  1891. } else {
  1892. $titlerow = array();
  1893. }
  1894. //get all users
  1895. $users = ModUtil::apiFunc($this->name, 'user', 'getAll');
  1896. // get all groups
  1897. $allgroups = ModUtil::apiFunc('GroupsModule', 'user', 'getall');
  1898. $groupnames = array();
  1899. foreach ($allgroups as $groupitem) {
  1900. $groupnames[$groupitem['gid']] = $groupitem['name'];
  1901. }
  1902. // data for csv
  1903. $datarows = array();
  1904. //loop every user gettin user id and username and all user fields and push onto result array.
  1905. foreach ($users as $user) {
  1906. $uservars = UserUtil::getVars($user['uid']);
  1907. $result = array();
  1908. array_push($result, $uservars['uid'], $uservars['uname']);
  1909. //checks for optional data
  1910. if ($email == 1) {
  1911. array_push($result, $uservars['email']);
  1912. }
  1913. if ($regDate == 1) {
  1914. array_push($result, $uservars['user_regdate']);
  1915. }
  1916. if ($lastLogin == 1) {
  1917. array_push($result, $uservars['lastlogin']);
  1918. }
  1919. if ($groups == 1) {
  1920. $usergroups = ModUtil::apiFunc('GroupsModule', 'user', 'getusergroups',
  1921. array('uid' => $uservars['uid'],
  1922. 'clean' => true));
  1923. $groupstring = "";
  1924. foreach ($usergroups as $group) {
  1925. $groupstring .= $groupnames[$group] . chr(124);
  1926. }
  1927. $groupstring = rtrim($groupstring, chr(124));
  1928. array_push($result, $groupstring);
  1929. }
  1930. foreach ($colnames as $colname) {
  1931. array_push($result, $uservars['__ATTRIBUTES__'][$colname]);
  1932. }
  1933. array_push($datarows, $result);
  1934. }
  1935. //export the csv file
  1936. FileUtil::exportCSV($datarows, $titlerow, $delimiter, '"', $exportFile);
  1937. }
  1938. if (SecurityUtil::checkPermission('Groups::', '::', ACCESS_READ)) {
  1939. $this->view->assign('groups', '1');
  1940. }
  1941. return $this->response($this->view->fetch('Admin/export.tpl'));
  1942. }
  1943. /**
  1944. * Import several users from a CSV file. Checks needed values and format.
  1945. *
  1946. * Parameters passed via GET:
  1947. * --------------------------
  1948. * None.
  1949. *
  1950. * Parameters passed via POST:
  1951. * ---------------------------
  1952. * None.
  1953. *
  1954. * Parameters passed via SESSION:
  1955. * ------------------------------
  1956. * None.
  1957. *
  1958. * @param array $importFile Information about the file to import. Used as the default
  1959. * if $_FILES['importFile'] is not set. Allows this function to be called internally,
  1960. * rather than as a result of a form post.
  1961. * @param integer $delimiter A code indicating the delimiter used in the file. Used as the
  1962. * default if $_POST['delimiter'] is not set. Allows this function to be called internally,
  1963. * rather than as a result of a form post.
  1964. *
  1965. * @return a empty message if success or an error message otherwise
  1966. */
  1967. protected function uploadImport(array $importFile, $delimiter)
  1968. {
  1969. // get needed values
  1970. $is_admin = (SecurityUtil::checkPermission('Users::', '::', ACCESS_ADMIN)) ? true : false;
  1971. $minpass = $this->getVar('minpass');
  1972. $defaultGroup = ModUtil::getVar('Groups', 'defaultgroup'); // Create output object;
  1973. // calcs $pregcondition needed to verify illegal usernames
  1974. $reg_illegalusername = $this->getVar('reg_Illegalusername');
  1975. $pregcondition = '';
  1976. if (!empty($reg_illegalusername)) {
  1977. $usernames = explode(" ", $reg_illegalusername);
  1978. $count = count($usernames);
  1979. $pregcondition = "/((";
  1980. for ($i = 0; $i < $count; $i++) {
  1981. if ($i != $count-1) {
  1982. $pregcondition .= $usernames[$i] . ")|(";
  1983. } else {
  1984. $pregcondition .= $usernames[$i] . "))/iAD";
  1985. }
  1986. }
  1987. }
  1988. // get available groups
  1989. $allGroups = ModUtil::apiFunc('GroupsModule', 'user', 'getall');
  1990. // create an array with the groups identities where the user can add other users
  1991. $allGroupsArray = array();
  1992. foreach ($allGroups as $group) {
  1993. if (SecurityUtil::checkPermission('Groups::', $group['gid'] . '::', ACCESS_EDIT)) {
  1994. $allGroupsArray[] = $group['gid'];
  1995. }
  1996. }
  1997. // check if the user's email must be unique
  1998. $reg_uniemail = $this->getVar('reg_uniemail');
  1999. // get the CSV delimiter
  2000. switch ($delimiter) {
  2001. case 1:
  2002. $delimiterChar = ",";
  2003. break;
  2004. case 2:
  2005. $delimiterChar = ";";
  2006. break;
  2007. case 3:
  2008. $delimiterChar = ":";
  2009. break;
  2010. }
  2011. // check that the user have selected a file
  2012. $fileName = $importFile['name'];
  2013. if ($fileName == '') {
  2014. return $this->__("Error! You have not chosen any file.");
  2015. }
  2016. // check if user have selected a correct file
  2017. if (FileUtil::getExtension($fileName) != 'csv') {
  2018. return $this->__("Error! The file extension is incorrect. The only allowed extension is csv.");
  2019. }
  2020. // read the choosen file
  2021. if (!$lines = file($importFile['tmp_name'])) {
  2022. return $this->__("Error! It has not been possible to read the import file.");
  2023. }
  2024. $expectedFields = array('uname', 'pass', 'email', 'activated', 'sendmail', 'groups');
  2025. $counter = 0;
  2026. $importValues = array();
  2027. // read the lines and create an array with the values. Check if the values passed are correct and set the default values if it is necessary
  2028. foreach ($lines as $line_num => $line) {
  2029. $line = str_replace('"', '', trim($line));
  2030. if ($counter == 0) {
  2031. // check the fields defined in the first row
  2032. $firstLineArray = explode($delimiterChar, $line);
  2033. foreach ($firstLineArray as $field) {
  2034. if (!in_array(trim(strtolower($field)), $expectedFields)) {
  2035. return $this->__f("Error! The import file does not have the expected field %s in the first row. Please check your import file.", array($field));
  2036. }
  2037. }
  2038. $counter++;
  2039. continue;
  2040. }
  2041. // get and check the second and following lines
  2042. $lineArray = array();
  2043. $lineArray = DataUtil::formatForOS(explode($delimiterChar, $line));
  2044. // check if the line have all the needed values
  2045. if (count($lineArray) != count($firstLineArray)) {
  2046. return $this->__f('Error! The number of parameters in line %s is not correct. Please check your import file.', $counter);
  2047. }
  2048. $importValues[] = array_combine($firstLineArray, $lineArray);
  2049. // check all the obtained values
  2050. // check user name
  2051. $uname = trim($importValues[$counter - 1]['uname']);
  2052. if ($uname == '' || strlen($uname) > 25) {
  2053. return $this->__f('Sorry! The user name is not valid in line %s. The user name is mandatory and the maximum length is 25 characters. Please check your import file.',
  2054. $counter);
  2055. }
  2056. // check if it is a valid user name
  2057. // admins are allowed to add any usernames, even those defined as being illegal
  2058. if (!$is_admin && $pregcondition != '') {
  2059. // check for illegal usernames
  2060. if (preg_match($pregcondition, $uname)) {
  2061. return $this->__f('Sorry! The user name %1$s is reserved and cannot be registered in line %2$s. Please check your import file.', array($uname, $counter));
  2062. }
  2063. }
  2064. // check if the user name is valid because spaces or invalid characters
  2065. if (preg_match("/[[:space:]]/", $uname) || !System::varValidate($uname, 'uname')) {
  2066. return $this->__f('Sorry! The user name %1$s cannot contain spaces in line %2$s. Please check your import file.', array($uname, $counter));
  2067. }
  2068. // check if the user name is repeated
  2069. if (in_array($uname, $usersArray)) {
  2070. return $this->__f('Sorry! The user name %1$s is repeated in line %2$s, and it cannot be used twice for creating accounts. Please check your import file.',
  2071. array($uname, $counter));
  2072. }
  2073. $usersArray[] = $uname;
  2074. // check password
  2075. $pass = (string)trim($importValues[$counter - 1]['pass']);
  2076. if ($pass == '') {
  2077. return $this->__f('Sorry! You did not provide a password in line %s. Please check your import file.', $counter);
  2078. }
  2079. // check password length
  2080. if (strlen($pass) < $minpass) {
  2081. return $this->__f('Sorry! The password must be at least %1$s characters long in line %2$s. Please check your import file.', array($minpass, $counter));
  2082. }
  2083. // check email
  2084. $email = trim($importValues[$counter - 1]['email']);
  2085. if ($email == '') {
  2086. return $this->__f('Sorry! You did not provide a email in line %s. Please check your import file.', $counter);
  2087. }
  2088. // check email format
  2089. if (!System::varValidate($email, 'email')) {
  2090. return $this->__f('Sorry! The e-mail address you entered was incorrectly formatted or is unacceptable for other reasons in line %s. Please check your import file.', $counter);
  2091. }
  2092. // check if email is unique only if it is necessary
  2093. if ($reg_uniemail == 1) {
  2094. if (in_array($email, $emailsArray)) {
  2095. return $this->__f('Sorry! The %1$s e-mail address is repeated in line %2$s, and it cannot be used twice for creating accounts. Please check your import file.',
  2096. array($email, $counter));
  2097. }
  2098. $emailsArray[] = $email;
  2099. }
  2100. // validate activation value
  2101. $importValues[$counter - 1]['activated'] = isset($importValues[$counter - 1]['activated']) ? (int)$importValues[$counter - 1]['activated'] : UsersConstant::ACTIVATED_ACTIVE;
  2102. $activated = $importValues[$counter - 1]['activated'];
  2103. if (($activated != UsersConstant::ACTIVATED_INACTIVE) && ($activated != UsersConstant::ACTIVATED_ACTIVE)) {
  2104. return $this->__('Error! The CSV is not valid: the "activated" column must contain 0 or 1 only.');
  2105. }
  2106. // validate sendmail
  2107. $importValues[$counter - 1]['sendmail'] = isset($importValues[$counter - 1]['sendmail']) ? (int)$importValues[$counter - 1]['sendmail'] : 0;
  2108. if ($importValues[$counter - 1]['sendmail'] < 0 || $importValues[$counter - 1]['sendmail'] > 1) {
  2109. return $this->__('Error! The CSV is not valid: the "sendmail" column must contain 0 or 1 only.');
  2110. }
  2111. // check groups and set defaultGroup as default if there are not groups defined
  2112. $importValues[$counter - 1]['groups'] = isset($importValues[$counter - 1]['groups']) ? (int)$importValues[$counter - 1]['groups'] : '';
  2113. $groups = $importValues[$counter - 1]['groups'];
  2114. if ($groups == '') {
  2115. $importValues[$counter - 1]['groups'] = $defaultGroup;
  2116. } else {
  2117. $groupsArray = explode('|', $groups);
  2118. foreach ($groupsArray as $group) {
  2119. if (!in_array($group, $allGroupsArray)) {
  2120. return $this->__('Sorry! The identity of the group %1$s is not not valid in line %2$s. Perhaps it do not exist. Please check your import file.', array($group, $counter));
  2121. }
  2122. }
  2123. }
  2124. $counter++;
  2125. }
  2126. // seams that the import file is formated correctly and its values are valid
  2127. if (empty($importValues)) {
  2128. return $this->__("Error! The import file does not have values.");
  2129. }
  2130. // check if users exists in database
  2131. $usersInDB = ModUtil::apiFunc($this->name, 'admin', 'checkMultipleExistence',
  2132. array('valuesarray' => $usersArray,
  2133. 'key' => 'uname'));
  2134. if ($usersInDB === false) {
  2135. return $this->__("Error! Trying to read the existing user names in database.");
  2136. } else {
  2137. if (count($usersInDB) > 0) {
  2138. return $this->__("Sorry! One or more user names really exist in database. The user names must be uniques.");
  2139. }
  2140. }
  2141. // check if emails exists in data base in case the email have to be unique
  2142. if ($reg_uniemail == 1) {
  2143. $emailsInDB = ModUtil::apiFunc($this->name, 'admin', 'checkMultipleExistence',
  2144. array('valuesarray' => $emailsArray,
  2145. 'key' => 'email'));
  2146. if ($emailsInDB === false) {
  2147. return $this->__("Error! Trying to read the existing users' email addressess in database.");
  2148. } else {
  2149. if (count($emailsInDB) > 0) {
  2150. return $this->__("Sorry! One or more users' email addresses exist in the database. Each user's e-mail address must be unique.");
  2151. }
  2152. }
  2153. }
  2154. // seems that the values in import file are ready. Procceed creating users
  2155. if (!ModUtil::apiFunc($this->name, 'admin', 'createImport', array('importvalues' => $importValues))) {
  2156. return $this->__("Error! The creation of users has failed.");
  2157. }
  2158. return '';
  2159. }
  2160. /**
  2161. * Sets or resets a user's need to changed his password on his next attempt at logging ing.
  2162. *
  2163. * Parameters passed via GET:
  2164. * --------------------------
  2165. * numeric userid The uid of the user for whom a change of password should be forced (or canceled).
  2166. *
  2167. * Parameters passed via POST:
  2168. * ---------------------------
  2169. * numeric userid The uid of the user for whom a change of password should be forced (or canceled).
  2170. * boolean user_must_change_password True to force the user to change his password at his next log-in attempt, otherwise false.
  2171. *
  2172. * Parameters passed via SESSION:
  2173. * ------------------------------
  2174. * None.
  2175. *
  2176. * @return string The rendered output from either the template for confirmation.
  2177. *
  2178. * @throws \Zikula\Framework\Exception\FatalException Thrown if a user id is not specified, is invalid, or does not point to a valid account record,
  2179. * or the account record is not in a consistent state.
  2180. * @throws \Zikula\Framework\Exception\ForbiddenException Thrown if the current user does not have edit access for the account record.
  2181. */
  2182. public function toggleForcedPasswordChangeAction()
  2183. {
  2184. if ($this->request->getMethod() == 'GET') {
  2185. $uid = $this->request->query->get('userid', false);
  2186. if (!$uid || !is_numeric($uid) || ((int)$uid != $uid)) {
  2187. throw new \Zikula\Framework\Exception\FatalException(LogUtil::getErrorMsgArgs());
  2188. }
  2189. $userObj = UserUtil::getVars($uid);
  2190. if (!isset($userObj) || !$userObj || !is_array($userObj) || empty($userObj)) {
  2191. throw new \Zikula\Framework\Exception\FatalException(LogUtil::getErrorMsgArgs());
  2192. }
  2193. if (!SecurityUtil::checkPermission('Users::', "{$userObj['uname']}::{$uid}", ACCESS_EDIT)) {
  2194. throw new \Zikula\Framework\Exception\ForbiddenException();
  2195. }
  2196. $userMustChangePassword = UserUtil::getVar('_Users_mustChangePassword', $uid, false);
  2197. return $this->response($this->view->assign('user_obj', $userObj)
  2198. ->assign('user_must_change_password', $userMustChangePassword)
  2199. ->fetch('Admin/toggleforcedpasswordchange.tpl'));
  2200. } elseif ($this->request->getMethod() == 'POST') {
  2201. $this->checkCsrfToken();
  2202. $uid = $this->request->request->get('userid', false);
  2203. $userMustChangePassword = $this->request->request->get('user_must_change_password', false);
  2204. if (!$uid || !is_numeric($uid) || ((int)$uid != $uid)) {
  2205. throw new \Zikula\Framework\Exception\FatalException(LogUtil::getErrorMsgArgs());
  2206. }
  2207. // Force reload of User object into cache.
  2208. $userObj = UserUtil::getVars($uid);
  2209. if (!SecurityUtil::checkPermission('Users::', "{$userObj['uname']}::{$uid}", ACCESS_EDIT)) {
  2210. throw new \Zikula\Framework\Exception\ForbiddenException();
  2211. }
  2212. if ($userMustChangePassword) {
  2213. UserUtil::setVar('_Users_mustChangePassword', $userMustChangePassword, $uid);
  2214. } else {
  2215. UserUtil::delVar('_Users_mustChangePassword', $uid);
  2216. }
  2217. // Force reload of User object into cache.
  2218. $userObj = UserUtil::getVars($uid, true);
  2219. if ($userMustChangePassword) {
  2220. if (isset($userObj['__ATTRIBUTES__']) && isset($userObj['__ATTRIBUTES__']['_Users_mustChangePassword'])) {
  2221. $this->registerStatus($this->__f('Done! A password change will be required the next time %1$s logs in.', array($userObj['uname'])));
  2222. } else {
  2223. throw new \Zikula\Framework\Exception\FatalException();
  2224. }
  2225. } else {
  2226. if (isset($userObj['__ATTRIBUTES__']) && isset($userObj['__ATTRIBUTES__']['_Users_mustChangePassword'])) {
  2227. throw new \Zikula\Framework\Exception\FatalException();
  2228. } else {
  2229. $this->registerStatus($this->__f('Done! A password change will no longer be required for %1$s.', array($userObj['uname'])));
  2230. }
  2231. }
  2232. return $this->redirect(ModUtil::url($this->name, 'admin', 'view'));
  2233. } else {
  2234. throw new \Zikula\Framework\Exception\ForbiddenException();
  2235. }
  2236. }
  2237. }