@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make user creation process simpler

Summary:
Fixes T4065. This divides user creation into separate "Standard User" and "Script/Bot" workflows which show only relevant fields and provide guidance.

This fixes the verification mess associated with script/bot users by verifying their email addresses automatically.

Test Plan:
- Created a standard user.
- Created a script/bot.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4065

Differential Revision: https://secure.phabricator.com/D8674

+328 -222
+4 -2
src/__phutil_library_map__.php
··· 1805 1805 'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php', 1806 1806 'PhabricatorPeopleCalendarController' => 'applications/people/controller/PhabricatorPeopleCalendarController.php', 1807 1807 'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php', 1808 + 'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php', 1808 1809 'PhabricatorPeopleDeleteController' => 'applications/people/controller/PhabricatorPeopleDeleteController.php', 1809 1810 'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php', 1810 - 'PhabricatorPeopleEditController' => 'applications/people/controller/PhabricatorPeopleEditController.php', 1811 1811 'PhabricatorPeopleEmpowerController' => 'applications/people/controller/PhabricatorPeopleEmpowerController.php', 1812 1812 'PhabricatorPeopleHovercardEventListener' => 'applications/people/event/PhabricatorPeopleHovercardEventListener.php', 1813 1813 'PhabricatorPeopleLdapController' => 'applications/people/controller/PhabricatorPeopleLdapController.php', 1814 1814 'PhabricatorPeopleListController' => 'applications/people/controller/PhabricatorPeopleListController.php', 1815 1815 'PhabricatorPeopleLogsController' => 'applications/people/controller/PhabricatorPeopleLogsController.php', 1816 + 'PhabricatorPeopleNewController' => 'applications/people/controller/PhabricatorPeopleNewController.php', 1816 1817 'PhabricatorPeoplePHIDTypeExternal' => 'applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php', 1817 1818 'PhabricatorPeoplePHIDTypeUser' => 'applications/people/phid/PhabricatorPeoplePHIDTypeUser.php', 1818 1819 'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php', ··· 4612 4613 'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController', 4613 4614 'PhabricatorPeopleCalendarController' => 'PhabricatorPeopleController', 4614 4615 'PhabricatorPeopleController' => 'PhabricatorController', 4616 + 'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController', 4615 4617 'PhabricatorPeopleDeleteController' => 'PhabricatorPeopleController', 4616 4618 'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController', 4617 - 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 4618 4619 'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController', 4619 4620 'PhabricatorPeopleHovercardEventListener' => 'PhabricatorEventListener', 4620 4621 'PhabricatorPeopleLdapController' => 'PhabricatorPeopleController', ··· 4624 4625 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 4625 4626 ), 4626 4627 'PhabricatorPeopleLogsController' => 'PhabricatorPeopleController', 4628 + 'PhabricatorPeopleNewController' => 'PhabricatorPeopleController', 4627 4629 'PhabricatorPeoplePHIDTypeExternal' => 'PhabricatorPHIDType', 4628 4630 'PhabricatorPeoplePHIDTypeUser' => 'PhabricatorPHIDType', 4629 4631 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
+18 -1
src/applications/people/application/PhabricatorApplicationPeople.php
··· 50 50 'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleDeleteController', 51 51 'rename/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleRenameController', 52 52 'welcome/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleWelcomeController', 53 - 'edit/' => 'PhabricatorPeopleEditController', 53 + 'create/' => 'PhabricatorPeopleCreateController', 54 + 'new/(?P<type>[^/]+)/' => 'PhabricatorPeopleNewController', 54 55 'ldap/' => 'PhabricatorPeopleLdapController', 55 56 'editprofile/(?P<id>[1-9]\d*)/' => 56 57 'PhabricatorPeopleProfileEditController', ··· 139 140 140 141 return $items; 141 142 } 143 + 144 + 145 + public function getQuickCreateItems(PhabricatorUser $viewer) { 146 + $items = array(); 147 + 148 + if ($viewer->getIsAdmin()) { 149 + $item = id(new PHUIListItemView()) 150 + ->setName(pht('User Account')) 151 + ->setAppIcon('people-dark') 152 + ->setHref($this->getBaseURI().'create/'); 153 + $items[] = $item; 154 + } 155 + 156 + return $items; 157 + } 158 + 142 159 143 160 }
+1 -1
src/applications/people/controller/PhabricatorPeopleController.php
··· 41 41 $crumbs->addAction( 42 42 id(new PHUIListItemView()) 43 43 ->setName(pht('Create New User')) 44 - ->setHref($this->getApplicationURI('edit')) 44 + ->setHref($this->getApplicationURI('create/')) 45 45 ->setIcon('create')); 46 46 } 47 47
+82
src/applications/people/controller/PhabricatorPeopleCreateController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleCreateController 4 + extends PhabricatorPeopleController { 5 + 6 + public function processRequest() { 7 + $request = $this->getRequest(); 8 + $admin = $request->getUser(); 9 + 10 + $v_type = 'standard'; 11 + if ($request->isFormPost()) { 12 + $v_type = $request->getStr('type'); 13 + 14 + if ($v_type == 'standard' || $v_type == 'bot') { 15 + return id(new AphrontRedirectResponse())->setURI( 16 + $this->getApplicationURI('new/'.$v_type.'/')); 17 + } 18 + } 19 + 20 + $title = pht('Create New User'); 21 + 22 + $standard_caption = pht( 23 + 'Create a standard user account. These users can log in to Phabricator, '. 24 + 'use the web interface and API, and receive email.'); 25 + 26 + $standard_admin = pht( 27 + 'Administrators are limited in their ability to access or edit these '. 28 + 'accounts after account creation.'); 29 + 30 + $bot_caption = pht( 31 + 'Create a bot/script user account, to automate interactions with other '. 32 + 'systems. These users can not use the web interface, but can use the '. 33 + 'API.'); 34 + 35 + $bot_admin = pht( 36 + 'Administrators have greater access to edit these accounts.'); 37 + 38 + $form = id(new AphrontFormView()) 39 + ->setUser($admin) 40 + ->appendRemarkupInstructions( 41 + pht( 42 + 'Choose the type of user account to create. For a detailed '. 43 + 'explanation of user account types, see [[ %s | User Guide: '. 44 + 'Account Roles ]].', 45 + PhabricatorEnv::getDoclink('User Guide: Account Roles'))) 46 + ->appendChild( 47 + id(new AphrontFormRadioButtonControl()) 48 + ->setLabel(pht('Account Type')) 49 + ->setName('type') 50 + ->setValue($v_type) 51 + ->addButton( 52 + 'standard', 53 + pht('Create Standard User'), 54 + hsprintf('%s<br /><br />%s', $standard_caption, $standard_admin)) 55 + ->addButton( 56 + 'bot', 57 + pht('Create Bot/Script User'), 58 + hsprintf('%s<br /><br />%s', $bot_caption, $bot_admin))) 59 + ->appendChild( 60 + id(new AphrontFormSubmitControl()) 61 + ->addCancelButton($this->getApplicationURI()) 62 + ->setValue(pht('Continue'))); 63 + 64 + $crumbs = $this->buildApplicationCrumbs(); 65 + $crumbs->addTextCrumb($title); 66 + 67 + $box = id(new PHUIObjectBoxView()) 68 + ->setHeaderText($title) 69 + ->appendChild($form); 70 + 71 + return $this->buildApplicationPage( 72 + array( 73 + $crumbs, 74 + $box, 75 + ), 76 + array( 77 + 'title' => $title, 78 + 'device' => true, 79 + )); 80 + } 81 + 82 + }
-218
src/applications/people/controller/PhabricatorPeopleEditController.php
··· 1 - <?php 2 - 3 - final class PhabricatorPeopleEditController 4 - extends PhabricatorPeopleController { 5 - 6 - public function processRequest() { 7 - 8 - $request = $this->getRequest(); 9 - $admin = $request->getUser(); 10 - 11 - $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); 12 - 13 - $user = new PhabricatorUser(); 14 - $base_uri = '/people/edit/'; 15 - $crumbs->addTextCrumb(pht('Create New User'), $base_uri); 16 - 17 - $content = array(); 18 - 19 - $response = $this->processBasicRequest($user); 20 - if ($response instanceof AphrontResponse) { 21 - return $response; 22 - } 23 - 24 - $content[] = $response; 25 - 26 - return $this->buildApplicationPage( 27 - array( 28 - $crumbs, 29 - $content, 30 - ), 31 - array( 32 - 'title' => pht('Edit User'), 33 - 'device' => true, 34 - )); 35 - } 36 - 37 - private function processBasicRequest(PhabricatorUser $user) { 38 - $request = $this->getRequest(); 39 - $admin = $request->getUser(); 40 - 41 - $e_username = true; 42 - $e_realname = true; 43 - $e_email = true; 44 - $errors = array(); 45 - 46 - $welcome_checked = true; 47 - 48 - $new_email = null; 49 - 50 - $request = $this->getRequest(); 51 - if ($request->isFormPost()) { 52 - $welcome_checked = $request->getInt('welcome'); 53 - 54 - $user->setUsername($request->getStr('username')); 55 - 56 - $new_email = $request->getStr('email'); 57 - if (!strlen($new_email)) { 58 - $errors[] = pht('Email is required.'); 59 - $e_email = pht('Required'); 60 - } else if (!PhabricatorUserEmail::isAllowedAddress($new_email)) { 61 - $e_email = pht('Invalid'); 62 - $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); 63 - } else { 64 - $e_email = null; 65 - } 66 - 67 - $user->setRealName($request->getStr('realname')); 68 - 69 - if (!strlen($user->getUsername())) { 70 - $errors[] = pht("Username is required."); 71 - $e_username = pht('Required'); 72 - } else if (!PhabricatorUser::validateUsername($user->getUsername())) { 73 - $errors[] = PhabricatorUser::describeValidUsername(); 74 - $e_username = pht('Invalid'); 75 - } else { 76 - $e_username = null; 77 - } 78 - 79 - if (!strlen($user->getRealName())) { 80 - $errors[] = pht('Real name is required.'); 81 - $e_realname = pht('Required'); 82 - } else { 83 - $e_realname = null; 84 - } 85 - 86 - if (!$errors) { 87 - try { 88 - 89 - $email = id(new PhabricatorUserEmail()) 90 - ->setAddress($new_email) 91 - ->setIsVerified(0); 92 - 93 - // Automatically approve the user, since an admin is creating them. 94 - $user->setIsApproved(1); 95 - 96 - id(new PhabricatorUserEditor()) 97 - ->setActor($admin) 98 - ->createNewUser($user, $email); 99 - 100 - if ($request->getStr('role') == 'agent') { 101 - id(new PhabricatorUserEditor()) 102 - ->setActor($admin) 103 - ->makeSystemAgentUser($user, true); 104 - } 105 - 106 - if ($welcome_checked) { 107 - $user->sendWelcomeEmail($admin); 108 - } 109 - 110 - $response = id(new AphrontRedirectResponse()) 111 - ->setURI('/p/'.$user->getUsername().'/'); 112 - return $response; 113 - } catch (AphrontQueryDuplicateKeyException $ex) { 114 - $errors[] = pht('Username and email must be unique.'); 115 - 116 - $same_username = id(new PhabricatorUser()) 117 - ->loadOneWhere('username = %s', $user->getUsername()); 118 - $same_email = id(new PhabricatorUserEmail()) 119 - ->loadOneWhere('address = %s', $new_email); 120 - 121 - if ($same_username) { 122 - $e_username = pht('Duplicate'); 123 - } 124 - 125 - if ($same_email) { 126 - $e_email = pht('Duplicate'); 127 - } 128 - } 129 - } 130 - } 131 - 132 - $form = new AphrontFormView(); 133 - $form->setUser($admin); 134 - $form->setAction('/people/edit/'); 135 - 136 - $is_immutable = false; 137 - 138 - $form 139 - ->appendChild( 140 - id(new AphrontFormTextControl()) 141 - ->setLabel(pht('Username')) 142 - ->setName('username') 143 - ->setValue($user->getUsername()) 144 - ->setError($e_username) 145 - ->setDisabled($is_immutable)) 146 - ->appendChild( 147 - id(new AphrontFormTextControl()) 148 - ->setLabel(pht('Real Name')) 149 - ->setName('realname') 150 - ->setValue($user->getRealName()) 151 - ->setError($e_realname)); 152 - 153 - $form->appendChild( 154 - id(new AphrontFormTextControl()) 155 - ->setLabel(pht('Email')) 156 - ->setName('email') 157 - ->setDisabled($is_immutable) 158 - ->setValue($new_email) 159 - ->setCaption(PhabricatorUserEmail::describeAllowedAddresses()) 160 - ->setError($e_email)); 161 - 162 - $form->appendChild($this->getRoleInstructions()); 163 - 164 - $form 165 - ->appendChild( 166 - id(new AphrontFormSelectControl()) 167 - ->setLabel(pht('Role')) 168 - ->setName('role') 169 - ->setValue('user') 170 - ->setOptions( 171 - array( 172 - 'user' => pht('Normal User'), 173 - 'agent' => pht('System Agent'), 174 - )) 175 - ->setCaption( 176 - pht('You can create a "system agent" account for bots, '. 177 - 'scripts, etc.'))) 178 - ->appendChild( 179 - id(new AphrontFormCheckboxControl()) 180 - ->addCheckbox( 181 - 'welcome', 182 - 1, 183 - pht('Send "Welcome to Phabricator" email.'), 184 - $welcome_checked)); 185 - 186 - $form 187 - ->appendChild( 188 - id(new AphrontFormSubmitControl()) 189 - ->addCancelButton($this->getApplicationURI()) 190 - ->setValue(pht('Save'))); 191 - 192 - $title = pht('Create New User'); 193 - 194 - $form_box = id(new PHUIObjectBoxView()) 195 - ->setHeaderText($title) 196 - ->setFormErrors($errors) 197 - ->setForm($form); 198 - 199 - return array($form_box); 200 - } 201 - 202 - private function getRoleInstructions() { 203 - $roles_link = phutil_tag( 204 - 'a', 205 - array( 206 - 'href' => PhabricatorEnv::getDoclink( 207 - 'article/User_Guide_Account_Roles.html'), 208 - 'target' => '_blank', 209 - ), 210 - pht('User Guide: Account Roles')); 211 - 212 - return phutil_tag( 213 - 'p', 214 - array('class' => 'aphront-form-instructions'), 215 - pht('For a detailed explanation of account roles, see %s.', $roles_link)); 216 - } 217 - 218 - }
+223
src/applications/people/controller/PhabricatorPeopleNewController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleNewController 4 + extends PhabricatorPeopleController { 5 + 6 + private $type; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->type = $data['type']; 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $admin = $request->getUser(); 15 + 16 + switch ($this->type) { 17 + case 'standard': 18 + $is_bot = false; 19 + break; 20 + case 'bot': 21 + $is_bot = true; 22 + break; 23 + default: 24 + return new Aphront404Response(); 25 + } 26 + 27 + $user = new PhabricatorUser(); 28 + 29 + $e_username = true; 30 + $e_realname = true; 31 + $e_email = true; 32 + $errors = array(); 33 + 34 + $welcome_checked = true; 35 + 36 + $new_email = null; 37 + 38 + $request = $this->getRequest(); 39 + if ($request->isFormPost()) { 40 + $welcome_checked = $request->getInt('welcome'); 41 + 42 + $user->setUsername($request->getStr('username')); 43 + 44 + $new_email = $request->getStr('email'); 45 + if (!strlen($new_email)) { 46 + $errors[] = pht('Email is required.'); 47 + $e_email = pht('Required'); 48 + } else if (!PhabricatorUserEmail::isAllowedAddress($new_email)) { 49 + $e_email = pht('Invalid'); 50 + $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); 51 + } else { 52 + $e_email = null; 53 + } 54 + 55 + $user->setRealName($request->getStr('realname')); 56 + 57 + if (!strlen($user->getUsername())) { 58 + $errors[] = pht("Username is required."); 59 + $e_username = pht('Required'); 60 + } else if (!PhabricatorUser::validateUsername($user->getUsername())) { 61 + $errors[] = PhabricatorUser::describeValidUsername(); 62 + $e_username = pht('Invalid'); 63 + } else { 64 + $e_username = null; 65 + } 66 + 67 + if (!strlen($user->getRealName())) { 68 + $errors[] = pht('Real name is required.'); 69 + $e_realname = pht('Required'); 70 + } else { 71 + $e_realname = null; 72 + } 73 + 74 + if (!$errors) { 75 + try { 76 + 77 + $email = id(new PhabricatorUserEmail()) 78 + ->setAddress($new_email) 79 + ->setIsVerified(0); 80 + 81 + // Automatically approve the user, since an admin is creating them. 82 + $user->setIsApproved(1); 83 + 84 + // If the user is a bot, approve their email too. 85 + if ($is_bot) { 86 + $email->setIsVerified(1); 87 + } 88 + 89 + id(new PhabricatorUserEditor()) 90 + ->setActor($admin) 91 + ->createNewUser($user, $email); 92 + 93 + if ($is_bot) { 94 + id(new PhabricatorUserEditor()) 95 + ->setActor($admin) 96 + ->makeSystemAgentUser($user, true); 97 + } 98 + 99 + if ($welcome_checked && !$is_bot) { 100 + $user->sendWelcomeEmail($admin); 101 + } 102 + 103 + $response = id(new AphrontRedirectResponse()) 104 + ->setURI('/p/'.$user->getUsername().'/'); 105 + return $response; 106 + } catch (AphrontQueryDuplicateKeyException $ex) { 107 + $errors[] = pht('Username and email must be unique.'); 108 + 109 + $same_username = id(new PhabricatorUser()) 110 + ->loadOneWhere('username = %s', $user->getUsername()); 111 + $same_email = id(new PhabricatorUserEmail()) 112 + ->loadOneWhere('address = %s', $new_email); 113 + 114 + if ($same_username) { 115 + $e_username = pht('Duplicate'); 116 + } 117 + 118 + if ($same_email) { 119 + $e_email = pht('Duplicate'); 120 + } 121 + } 122 + } 123 + } 124 + 125 + $form = id(new AphrontFormView()) 126 + ->setUser($admin); 127 + 128 + if ($is_bot) { 129 + $form->appendRemarkupInstructions( 130 + pht( 131 + 'You are creating a new **bot/script** user account.')); 132 + } else { 133 + $form->appendRemarkupInstructions( 134 + pht( 135 + 'You are creating a new **standard** user account.')); 136 + } 137 + 138 + $form 139 + ->appendChild( 140 + id(new AphrontFormTextControl()) 141 + ->setLabel(pht('Username')) 142 + ->setName('username') 143 + ->setValue($user->getUsername()) 144 + ->setError($e_username)) 145 + ->appendChild( 146 + id(new AphrontFormTextControl()) 147 + ->setLabel(pht('Real Name')) 148 + ->setName('realname') 149 + ->setValue($user->getRealName()) 150 + ->setError($e_realname)) 151 + ->appendChild( 152 + id(new AphrontFormTextControl()) 153 + ->setLabel(pht('Email')) 154 + ->setName('email') 155 + ->setValue($new_email) 156 + ->setCaption(PhabricatorUserEmail::describeAllowedAddresses()) 157 + ->setError($e_email)); 158 + 159 + if (!$is_bot) { 160 + $form->appendChild( 161 + id(new AphrontFormCheckboxControl()) 162 + ->addCheckbox( 163 + 'welcome', 164 + 1, 165 + pht('Send "Welcome to Phabricator" email with login instructions.'), 166 + $welcome_checked)); 167 + } 168 + 169 + $form 170 + ->appendChild( 171 + id(new AphrontFormSubmitControl()) 172 + ->addCancelButton($this->getApplicationURI()) 173 + ->setValue(pht('Create User'))); 174 + 175 + if ($is_bot) { 176 + $form 177 + ->appendChild(id(new AphrontFormDividerControl())) 178 + ->appendRemarkupInstructions( 179 + pht( 180 + '**Why do bot/script accounts need an email address?**'. 181 + "\n\n". 182 + 'Although bots do not normally receive email from Phabricator, '. 183 + 'they can interact with other systems which require an email '. 184 + 'address. Examples include:'. 185 + "\n\n". 186 + " - If the account takes actions which //send// email, we need ". 187 + " an address to use in the //From// header.\n". 188 + " - If the account creates commits, Git and Mercurial require ". 189 + " an email address for authorship.\n". 190 + " - If you send email //to// Phabricator on behalf of the ". 191 + " account, the address can identify the sender.\n". 192 + " - Some internal authentication functions depend on accounts ". 193 + " having an email address.\n". 194 + "\n\n". 195 + "The address will automatically be verified, so you do not need ". 196 + "to be able to receive mail at this address, and can enter some ". 197 + "invalid or nonexistent (but correctly formatted) address like ". 198 + "`bot@yourcompany.com` if you prefer.")); 199 + } 200 + 201 + 202 + $title = pht('Create New User'); 203 + 204 + $form_box = id(new PHUIObjectBoxView()) 205 + ->setHeaderText($title) 206 + ->setFormErrors($errors) 207 + ->setForm($form); 208 + 209 + $crumbs = $this->buildApplicationCrumbs(); 210 + $crumbs->addTextCrumb($title); 211 + 212 + return $this->buildApplicationPage( 213 + array( 214 + $crumbs, 215 + $form_box, 216 + ), 217 + array( 218 + 'title' => $title, 219 + 'device' => true, 220 + )); 221 + } 222 + 223 + }