@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.

Legalpad - add signature page

Summary: Fixes T3481. Sort of - this thing be very ugly. Also it assumes that you'll "always" want to sign terms. I was thinking in a future diff that should be optional as well as configurable, though it was unclear to me if either was worth pursuing... Generally very hideous as the three main elements (PHUIDocument, AphrontErrorView, and AphrontForm with an AphrontFormInset) have never really played together before.

Test Plan: agreed to some test terms. noted UI displayed nicely. reloaded and noted UI told me I had signed it already. Went to different terms and filled them out wrong and got sensical errors.

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T3481

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

+260 -9
+2
resources/sql/patches/20130709.legalpadsignature.sql
··· 1 + ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature 2 + ADD signatureData LONGTEXT NOT NULL COLLATE utf8_bin AFTER signerPHID;
+2
src/__phutil_library_map__.php
··· 644 644 'LegalpadDocumentPreviewController' => 'applications/legalpad/controller/LegalpadDocumentPreviewController.php', 645 645 'LegalpadDocumentQuery' => 'applications/legalpad/query/LegalpadDocumentQuery.php', 646 646 'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php', 647 + 'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php', 647 648 'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php', 648 649 'LegalpadDocumentViewController' => 'applications/legalpad/controller/LegalpadDocumentViewController.php', 649 650 'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php', ··· 2573 2574 'LegalpadDocumentPreviewController' => 'LegalpadController', 2574 2575 'LegalpadDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2575 2576 'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', 2577 + 'LegalpadDocumentSignController' => 'LegalpadController', 2576 2578 'LegalpadDocumentSignature' => 'LegalpadDAO', 2577 2579 'LegalpadDocumentViewController' => 'LegalpadController', 2578 2580 'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver',
+1 -1
src/applications/legalpad/application/PhabricatorApplicationLegalpad.php
··· 40 40 41 41 public function getRoutes() { 42 42 return array( 43 - '/L(?P<id>\d+)/' => 'LegalpadDocumentViewController', 43 + '/L(?P<id>\d+)' => 'LegalpadDocumentSignController', 44 44 '/legalpad/' => array( 45 45 '' => 'LegalpadDocumentListController', 46 46 '(query/(?P<queryKey>[^/]+)/)?' => 'LegalpadDocumentListController',
+2 -2
src/applications/legalpad/controller/LegalpadDocumentListController.php
··· 37 37 $list->setUser($user); 38 38 foreach ($documents as $document) { 39 39 $last_updated = phabricator_date($document->getDateModified(), $user); 40 - $updater = $this->getHandle( 41 - reset($document->getRecentContributorPHIDs()))->renderLink(); 40 + $recent_contributors = $document->getRecentContributorPHIDs(); 41 + $updater = $this->getHandle(reset($recent_contributors))->renderLink(); 42 42 43 43 $title = $document->getTitle(); 44 44
+236
src/applications/legalpad/controller/LegalpadDocumentSignController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group legalpad 5 + */ 6 + final class LegalpadDocumentSignController extends LegalpadController { 7 + 8 + private $id; 9 + 10 + public function willProcessRequest(array $data) { 11 + $this->id = $data['id']; 12 + } 13 + 14 + public function processRequest() { 15 + $request = $this->getRequest(); 16 + $user = $request->getUser(); 17 + 18 + $document = id(new LegalpadDocumentQuery()) 19 + ->setViewer($user) 20 + ->withIDs(array($this->id)) 21 + ->needDocumentBodies(true) 22 + ->executeOne(); 23 + 24 + if (!$document) { 25 + return new Aphront404Response(); 26 + } 27 + 28 + $signature = id(new LegalpadDocumentSignature()) 29 + ->loadOneWhere( 30 + 'documentPHID = %s AND documentVersion = %d AND signerPHID = %s', 31 + $document->getPHID(), 32 + $document->getVersions(), 33 + $user->getPHID()); 34 + 35 + if (!$signature) { 36 + $has_signed = false; 37 + $error_view = null; 38 + $signature = id(new LegalpadDocumentSignature()) 39 + ->setSignerPHID($user->getPHID()) 40 + ->setDocumentPHID($document->getPHID()) 41 + ->setDocumentVersion($document->getVersions()); 42 + $data = array( 43 + 'name' => $user->getRealName(), 44 + 'email' => $user->loadPrimaryEmailAddress()); 45 + $signature->setSignatureData($data); 46 + } else { 47 + $has_signed = true; 48 + $error_view = id(new AphrontErrorView()) 49 + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) 50 + ->setTitle(pht('You have already agreed to these terms.')); 51 + $data = $signature->getSignatureData(); 52 + } 53 + 54 + $e_name = true; 55 + $e_email = true; 56 + $e_address_1 = true; 57 + $errors = array(); 58 + if ($request->isFormPost()) { 59 + $name = $request->getStr('name'); 60 + $email = $request->getStr('email'); 61 + $address_1 = $request->getStr('address_1'); 62 + $address_2 = $request->getStr('address_2'); 63 + $phone = $request->getStr('phone'); 64 + $agree = $request->getExists('agree'); 65 + 66 + if (!$name) { 67 + $e_name = pht('Required'); 68 + $errors[] = pht('Name field is required.'); 69 + } 70 + $data['name'] = $name; 71 + 72 + if (!$email) { 73 + $e_email = pht('Required'); 74 + $errors[] = pht('Email field is required.'); 75 + } else { 76 + $addr_obj = new PhutilEmailAddress($email); 77 + $domain = $addr_obj->getDomainName(); 78 + if (!$domain) { 79 + $e_email = pht('Invalid'); 80 + $errors[] = pht('A valid email is required.'); 81 + } 82 + } 83 + $data['email'] = $email; 84 + 85 + if (!$address_1) { 86 + $e_address_1 = pht('Required'); 87 + $errors[] = pht('Address line 1 field is required.'); 88 + } 89 + $data['address_1'] = $address_1; 90 + $data['address_2'] = $address_2; 91 + $data['phone'] = $phone; 92 + $signature->setSignatureData($data); 93 + 94 + if (!$agree) { 95 + $errors[] = pht( 96 + 'You must check "I agree to the terms laid forth above."'); 97 + } 98 + 99 + if (!$errors) { 100 + $signature->save(); 101 + $has_signed = true; 102 + $error_view = id(new AphrontErrorView()) 103 + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) 104 + ->setTitle(pht('Signature successful. Thank you.')); 105 + } else { 106 + $error_view = id(new AphrontErrorView()) 107 + ->setTitle(pht('Error in submission.')) 108 + ->setErrors($errors); 109 + } 110 + } 111 + 112 + $document_body = $document->getDocumentBody(); 113 + $engine = id(new PhabricatorMarkupEngine()) 114 + ->setViewer($user); 115 + $engine->addObject( 116 + $document_body, 117 + LegalpadDocumentBody::MARKUP_FIELD_TEXT); 118 + $engine->process(); 119 + 120 + $title = $document_body->getTitle(); 121 + 122 + $header = id(new PhabricatorHeaderView()) 123 + ->setHeader($title); 124 + 125 + $content = array( 126 + id(new PHUIDocumentView()) 127 + ->setHeader($header) 128 + ->appendChild($this->buildDocument($engine, $document_body)), 129 + $error_view, 130 + $this->buildSignatureForm( 131 + $document_body, 132 + $signature, 133 + $has_signed, 134 + $e_name, 135 + $e_email, 136 + $e_address_1)); 137 + 138 + return $this->buildApplicationPage( 139 + $content, 140 + array( 141 + 'title' => $title, 142 + 'device' => true, 143 + 'dust' => true, 144 + 'pageObjects' => array($document->getPHID()), 145 + )); 146 + } 147 + 148 + private function buildDocument( 149 + PhabricatorMarkupEngine 150 + $engine, LegalpadDocumentBody $body) { 151 + 152 + require_celerity_resource('legalpad-documentbody-css'); 153 + 154 + return phutil_tag( 155 + 'div', 156 + array( 157 + 'class' => 'legalpad-documentbody' 158 + ), 159 + $engine->getOutput($body, LegalpadDocumentBody::MARKUP_FIELD_TEXT)); 160 + 161 + } 162 + 163 + private function buildSignatureForm( 164 + LegalpadDocumentBody $body, 165 + LegalpadDocumentSignature $signature, 166 + $has_signed = false, 167 + $e_name = true, 168 + $e_email = true, 169 + $e_address_1 = true) { 170 + 171 + $user = $this->getRequest()->getUser(); 172 + if ($has_signed) { 173 + $instructions = pht('Thank you for signing and agreeing.'); 174 + } else { 175 + $instructions = pht('Please enter the following information.'); 176 + } 177 + 178 + $data = $signature->getSignatureData(); 179 + $form = id(new AphrontFormView()) 180 + ->setUser($user) 181 + ->setFlexible(true) 182 + ->appendChild( 183 + id(new AphrontFormInsetView()) 184 + ->setTitle(pht('Sign and Agree')) 185 + ->setDescription($instructions) 186 + ->setContent(phutil_tag('br', array())) 187 + ->appendChild( 188 + id(new AphrontFormTextControl()) 189 + ->setLabel(pht('Name')) 190 + ->setValue(idx($data, 'name', '')) 191 + ->setName('name') 192 + ->setError($e_name) 193 + ->setDisabled($has_signed)) 194 + ->appendChild( 195 + id(new AphrontFormTextControl()) 196 + ->setLabel(pht('Email')) 197 + ->setValue(idx($data, 'email', '')) 198 + ->setName('email') 199 + ->setError($e_email) 200 + ->setDisabled($has_signed)) 201 + ->appendChild( 202 + id(new AphrontFormTextControl()) 203 + ->setLabel(pht('Address line 1')) 204 + ->setValue(idx($data, 'address_1', '')) 205 + ->setName('address_1') 206 + ->setError($e_address_1) 207 + ->setDisabled($has_signed)) 208 + ->appendChild( 209 + id(new AphrontFormTextControl()) 210 + ->setLabel(pht('Address line 2')) 211 + ->setValue(idx($data, 'address_2', '')) 212 + ->setName('address_2') 213 + ->setDisabled($has_signed)) 214 + ->appendChild( 215 + id(new AphrontFormTextControl()) 216 + ->setLabel(pht('Phone')) 217 + ->setValue(idx($data, 'phone', '')) 218 + ->setName('phone') 219 + ->setDisabled($has_signed)) 220 + ->appendChild( 221 + id(new AphrontFormCheckboxControl()) 222 + ->addCheckbox( 223 + 'agree', 224 + 'agree', 225 + pht('I agree to the terms laid forth above.'), 226 + $has_signed) 227 + ->setDisabled($has_signed)) 228 + ->appendChild( 229 + id(new AphrontFormSubmitControl()) 230 + ->setValue(pht('Sign and Agree')) 231 + ->setDisabled($has_signed))); 232 + 233 + return $form; 234 + } 235 + 236 + }
-3
src/applications/legalpad/controller/LegalpadDocumentViewController.php
··· 49 49 ->setViewer($user); 50 50 $engine->addObject( 51 51 $document_body, 52 - LegalpadDocumentBody::MARKUP_FIELD_TITLE); 53 - $engine->addObject( 54 - $document_body, 55 52 LegalpadDocumentBody::MARKUP_FIELD_TEXT); 56 53 foreach ($xactions as $xaction) { 57 54 if ($xaction->getComment()) {
+1 -1
src/applications/legalpad/editor/LegalpadDocumentEditor.php
··· 169 169 170 170 $body->addTextSection( 171 171 pht('DOCUMENT DETAIL'), 172 - PhabricatorEnv::getProductionURI('/L'.$object->getID())); 172 + PhabricatorEnv::getProductionURI('/legalpad/view/'.$object->getID().'/')); 173 173 174 174 return $body; 175 175 }
-1
src/applications/legalpad/storage/LegalpadDocumentBody.php
··· 7 7 implements 8 8 PhabricatorMarkupInterface { 9 9 10 - const MARKUP_FIELD_TITLE = 'markup:title'; 11 10 const MARKUP_FIELD_TEXT = 'markup:text '; 12 11 13 12 protected $phid;
+12 -1
src/applications/legalpad/storage/LegalpadDocumentSignature.php
··· 6 6 final class LegalpadDocumentSignature extends LegalpadDAO { 7 7 8 8 protected $documentPHID; 9 - protected $documentversion; 9 + protected $documentVersion; 10 10 protected $signerPHID; 11 + protected $signatureData = array(); 12 + 13 + public function getConfiguration() { 14 + return array( 15 + self::CONFIG_SERIALIZATION => array( 16 + 'signatureData' => self::SERIALIZATION_JSON, 17 + ), 18 + ) + parent::getConfiguration(); 19 + } 20 + 21 + 11 22 12 23 }
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1430 1430 'type' => 'php', 1431 1431 'name' => $this->getPatchPath('20130703.legalpaddocdenorm.php'), 1432 1432 ), 1433 + '20130709.legalpadsignature.sql' => array( 1434 + 'type' => 'sql', 1435 + 'name' => $this->getPatchPath('20130709.legalpadsignature.sql'), 1436 + ), 1433 1437 '20130709.droptimeline.sql' => array( 1434 1438 'type' => 'sql', 1435 1439 'name' => $this->getPatchPath('20130709.droptimeline.sql'),