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

Allow Legalpad documents to designate corporate signers

Summary:
Ref T5532. This adds:

- Documents can designate that they should be signed by "Corporations" or "Individuals".
- Corporate documents get different fields and a different exemption process.
- Basically everything works the same but this is like a zillion lines of form code.

Test Plan: See screenshots.

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: epriestley

Maniphest Tasks: T5532

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

+723 -287
+2
resources/sql/autopatches/20140703.legalcorp.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_legalpad.legalpad_document 2 + ADD signatureType VARCHAR(4) NOT NULL COLLATE utf8_bin;
+2
resources/sql/autopatches/20140703.legalcorp.2.sql
··· 1 + UPDATE {$NAMESPACE}_legalpad.legalpad_document 2 + SET signatureType = 'user' WHERE signatureType = '';
+2
resources/sql/autopatches/20140703.legalcorp.3.sql
··· 1 + ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature 2 + ADD signatureType VARCHAR(4) NOT NULL COLLATE utf8_bin AFTER documentVersion;
+2
resources/sql/autopatches/20140703.legalcorp.4.sql
··· 1 + UPDATE {$NAMESPACE}_legalpad.legalpad_documentsignature 2 + SET signatureType = 'user' WHERE signatureType = '';
+2
resources/sql/autopatches/20140703.legalcorp.5.sql
··· 1 + ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature 2 + CHANGE signerPHID signerPHID VARCHAR(64) COLLATE utf8_bin;
+1 -3
src/applications/legalpad/constants/LegalpadTransactionType.php
··· 1 1 <?php 2 2 3 - /** 4 - * @group legalpad 5 - */ 6 3 final class LegalpadTransactionType extends LegalpadConstants { 7 4 8 5 const TYPE_TITLE = 'title'; 9 6 const TYPE_TEXT = 'text'; 7 + const TYPE_SIGNATURE_TYPE = 'legalpad:signature-type'; 10 8 11 9 }
+29 -8
src/applications/legalpad/controller/LegalpadDocumentEditController.php
··· 1 1 <?php 2 2 3 - /** 4 - * @group legalpad 5 - */ 6 3 final class LegalpadDocumentEditController extends LegalpadController { 7 4 8 5 private $id; ··· 26 23 ->setCreatorPHID($user->getPHID()); 27 24 $document->attachDocumentBody($body); 28 25 $document->setDocumentBodyPHID(PhabricatorPHIDConstants::PHID_VOID); 29 - $title = null; 30 - $text = null; 31 26 } else { 32 27 $is_create = false; 33 28 ··· 44 39 if (!$document) { 45 40 return new Aphront404Response(); 46 41 } 47 - $title = $document->getDocumentBody()->getTitle(); 48 - $text = $document->getDocumentBody()->getText(); 49 42 } 50 43 51 44 $e_title = true; 52 45 $e_text = true; 46 + 47 + $title = $document->getDocumentBody()->getTitle(); 48 + $text = $document->getDocumentBody()->getText(); 49 + $v_signature_type = $document->getSignatureType(); 50 + 53 51 $errors = array(); 54 52 $can_view = null; 55 53 $can_edit = null; ··· 85 83 $xactions[] = id(new LegalpadTransaction()) 86 84 ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) 87 85 ->setNewValue($can_edit); 86 + 87 + if ($is_create) { 88 + $v_signature_type = $request->getStr('signatureType'); 89 + $xactions[] = id(new LegalpadTransaction()) 90 + ->setTransactionType(LegalpadTransactionType::TYPE_SIGNATURE_TYPE) 91 + ->setNewValue($v_signature_type); 92 + } 88 93 89 94 if (!$errors) { 90 95 $editor = id(new LegalpadDocumentEditor()) ··· 113 118 ->setLabel(pht('Title')) 114 119 ->setError($e_title) 115 120 ->setValue($title) 116 - ->setName('title')) 121 + ->setName('title')); 122 + 123 + if ($is_create) { 124 + $form->appendChild( 125 + id(new AphrontFormSelectControl()) 126 + ->setLabel(pht('Who Should Sign?')) 127 + ->setName(pht('signatureType')) 128 + ->setValue($v_signature_type) 129 + ->setOptions(LegalpadDocument::getSignatureTypeMap())); 130 + } else { 131 + $form->appendChild( 132 + id(new AphrontFormMarkupControl()) 133 + ->setLabel(pht('Who Should Sign?')) 134 + ->setValue($document->getSignatureTypeName())); 135 + } 136 + 137 + $form 117 138 ->appendChild( 118 139 id(new PhabricatorRemarkupControl()) 119 140 ->setID('document-text')
+4
src/applications/legalpad/controller/LegalpadDocumentManageController.php
··· 174 174 ->setActionList($actions); 175 175 176 176 $properties->addProperty( 177 + pht('Signature Type'), 178 + $document->getSignatureTypeName()); 179 + 180 + $properties->addProperty( 177 181 pht('Last Updated'), 178 182 phabricator_datetime($document->getDateModified(), $user)); 179 183
+402 -145
src/applications/legalpad/controller/LegalpadDocumentSignController.php
··· 25 25 return new Aphront404Response(); 26 26 } 27 27 28 - $signer_phid = null; 29 - $signature_data = array(); 30 - if ($viewer->isLoggedIn()) { 31 - $signer_phid = $viewer->getPHID(); 32 - $signature_data = array( 33 - 'name' => $viewer->getRealName(), 34 - 'email' => $viewer->loadPrimaryEmailAddress(), 35 - ); 36 - } else if ($request->isFormPost()) { 37 - $email = new PhutilEmailAddress($request->getStr('email')); 38 - if (strlen($email->getDomainName())) { 39 - $email_obj = id(new PhabricatorUserEmail()) 40 - ->loadOneWhere('address = %s', $email->getAddress()); 41 - if ($email_obj) { 42 - return $this->signInResponse(); 28 + list($signer_phid, $signature_data) = $this->readSignerInformation( 29 + $document, 30 + $request); 31 + 32 + $signature = null; 33 + 34 + $type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL; 35 + $is_individual = ($document->getSignatureType() == $type_individual); 36 + if ($is_individual) { 37 + if ($signer_phid) { 38 + // TODO: This is odd and should probably be adjusted after grey/external 39 + // accounts work better, but use the omnipotent viewer to check for a 40 + // signature so we can pick up anonymous/grey signatures. 41 + 42 + $signature = id(new LegalpadDocumentSignatureQuery()) 43 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 44 + ->withDocumentPHIDs(array($document->getPHID())) 45 + ->withSignerPHIDs(array($signer_phid)) 46 + ->executeOne(); 47 + 48 + if ($signature && !$viewer->isLoggedIn()) { 49 + return $this->newDialog() 50 + ->setTitle(pht('Already Signed')) 51 + ->appendParagraph(pht('You have already signed this document!')) 52 + ->addCancelButton('/'.$document->getMonogram(), pht('Okay')); 43 53 } 44 - $external_account = id(new PhabricatorExternalAccountQuery()) 45 - ->setViewer($viewer) 46 - ->withAccountTypes(array('email')) 47 - ->withAccountDomains(array($email->getDomainName())) 48 - ->withAccountIDs(array($email->getAddress())) 49 - ->loadOneOrCreate(); 50 - if ($external_account->getUserPHID()) { 51 - return $this->signInResponse(); 52 - } 53 - $signer_phid = $external_account->getPHID(); 54 54 } 55 - } 56 55 57 - $signature = null; 58 - if ($signer_phid) { 59 - // TODO: This is odd and should probably be adjusted after grey/external 60 - // accounts work better, but use the omnipotent viewer to check for a 61 - // signature so we can pick up anonymous/grey signatures. 56 + $signed_status = null; 57 + if (!$signature) { 58 + $has_signed = false; 59 + $signature = id(new LegalpadDocumentSignature()) 60 + ->setSignerPHID($signer_phid) 61 + ->setDocumentPHID($document->getPHID()) 62 + ->setDocumentVersion($document->getVersions()); 63 + 64 + // If the user is logged in, show a notice that they haven't signed. 65 + // If they aren't logged in, we can't be as sure, so don't show 66 + // anything. 67 + if ($viewer->isLoggedIn()) { 68 + $signed_status = id(new AphrontErrorView()) 69 + ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 70 + ->setErrors( 71 + array( 72 + pht('You have not signed this document yet.'), 73 + )); 74 + } 75 + } else { 76 + $has_signed = true; 77 + $signature_data = $signature->getSignatureData(); 62 78 63 - $signature = id(new LegalpadDocumentSignatureQuery()) 64 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 65 - ->withDocumentPHIDs(array($document->getPHID())) 66 - ->withSignerPHIDs(array($signer_phid)) 67 - ->executeOne(); 79 + // In this case, we know they've signed. 80 + $signed_at = $signature->getDateCreated(); 68 81 69 - if ($signature && !$viewer->isLoggedIn()) { 70 - return $this->newDialog() 71 - ->setTitle(pht('Already Signed')) 72 - ->appendParagraph(pht('You have already signed this document!')) 73 - ->addCancelButton('/'.$document->getMonogram(), pht('Okay')); 74 - } 75 - } 82 + if ($signature->getIsExemption()) { 83 + $exemption_phid = $signature->getExemptionPHID(); 84 + $handles = $this->loadViewerHandles(array($exemption_phid)); 85 + $exemption_handle = $handles[$exemption_phid]; 76 86 77 - $signed_status = null; 78 - if (!$signature) { 79 - $has_signed = false; 80 - $signature = id(new LegalpadDocumentSignature()) 81 - ->setSignerPHID($signer_phid) 82 - ->setDocumentPHID($document->getPHID()) 83 - ->setDocumentVersion($document->getVersions()) 84 - ->setSignerName((string)idx($signature_data, 'name')) 85 - ->setSignerEmail((string)idx($signature_data, 'email')) 86 - ->setSignatureData($signature_data); 87 + $signed_text = pht( 88 + 'You do not need to sign this document. '. 89 + '%s added a signature exemption for you on %s.', 90 + $exemption_handle->renderLink(), 91 + phabricator_datetime($signed_at, $viewer)); 92 + } else { 93 + $signed_text = pht( 94 + 'You signed this document on %s.', 95 + phabricator_datetime($signed_at, $viewer)); 96 + } 87 97 88 - // If the user is logged in, show a notice that they haven't signed. 89 - // If they aren't logged in, we can't be as sure, so don't show anything. 90 - if ($viewer->isLoggedIn()) { 91 98 $signed_status = id(new AphrontErrorView()) 92 - ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 93 - ->setErrors( 94 - array( 95 - pht('You have not signed this document yet.'), 96 - )); 99 + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) 100 + ->setErrors(array($signed_text)); 97 101 } 102 + 103 + $field_errors = array( 104 + 'name' => true, 105 + 'email' => true, 106 + 'agree' => true, 107 + ); 98 108 } else { 99 - $has_signed = true; 100 - $signature_data = $signature->getSignatureData(); 101 - 102 - // In this case, we know they've signed. 103 - $signed_at = $signature->getDateCreated(); 109 + $signature = id(new LegalpadDocumentSignature()) 110 + ->setDocumentPHID($document->getPHID()) 111 + ->setDocumentVersion($document->getVersions()); 104 112 105 - if ($signature->getIsExemption()) { 106 - $exemption_phid = $signature->getExemptionPHID(); 107 - $handles = $this->loadViewerHandles(array($exemption_phid)); 108 - $exemption_handle = $handles[$exemption_phid]; 113 + if ($viewer->isLoggedIn()) { 114 + $has_signed = false; 109 115 110 - $signed_text = pht( 111 - 'You do not need to sign this document. '. 112 - '%s added a signature exemption for you on %s.', 113 - $exemption_handle->renderLink(), 114 - phabricator_datetime($signed_at, $viewer)); 116 + $signed_status = null; 115 117 } else { 116 - $signed_text = pht( 117 - 'You signed this document on %s.', 118 - phabricator_datetime($signed_at, $viewer)); 118 + // This just hides the form. 119 + $has_signed = true; 120 + 121 + $login_text = pht( 122 + 'This document requires a corporate signatory. You must log in to '. 123 + 'accept this document on behalf of a company you represent.'); 124 + $signed_status = id(new AphrontErrorView()) 125 + ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 126 + ->setErrors(array($login_text)); 119 127 } 120 128 121 - $signed_status = id(new AphrontErrorView()) 122 - ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) 123 - ->setErrors(array($signed_text)); 129 + $field_errors = array( 130 + 'name' => true, 131 + 'address' => true, 132 + 'contact.name' => true, 133 + 'email' => true, 134 + ); 124 135 } 125 136 126 - $e_name = true; 127 - $e_email = true; 128 - $e_agree = null; 137 + $signature->setSignatureData($signature_data); 129 138 130 139 $errors = array(); 131 140 if ($request->isFormOrHisecPost() && !$has_signed) { ··· 139 148 '/'.$document->getMonogram()); 140 149 } 141 150 142 - $name = $request->getStr('name'); 143 - $agree = $request->getExists('agree'); 144 - 145 - if (!strlen($name)) { 146 - $e_name = pht('Required'); 147 - $errors[] = pht('Name field is required.'); 148 - } else { 149 - $e_name = null; 150 - } 151 - $signature_data['name'] = $name; 151 + list($form_data, $errors, $field_errors) = $this->readSignatureForm( 152 + $document, 153 + $request); 152 154 153 - if ($viewer->isLoggedIn()) { 154 - $email = $viewer->loadPrimaryEmailAddress(); 155 - } else { 156 - $email = $request->getStr('email'); 155 + $signature_data = $form_data + $signature_data; 157 156 158 - $addr_obj = null; 159 - if (!strlen($email)) { 160 - $e_email = pht('Required'); 161 - $errors[] = pht('Email field is required.'); 162 - } else { 163 - $addr_obj = new PhutilEmailAddress($email); 164 - $domain = $addr_obj->getDomainName(); 165 - if (!$domain) { 166 - $e_email = pht('Invalid'); 167 - $errors[] = pht('A valid email is required.'); 168 - } else { 169 - $e_email = null; 170 - } 171 - } 172 - } 173 - $signature_data['email'] = $email; 174 - 157 + $signature->setSignatureData($signature_data); 158 + $signature->setSignatureType($document->getSignatureType()); 175 159 $signature->setSignerName((string)idx($signature_data, 'name')); 176 160 $signature->setSignerEmail((string)idx($signature_data, 'email')); 177 - $signature->setSignatureData($signature_data); 178 161 162 + $agree = $request->getExists('agree'); 179 163 if (!$agree) { 180 164 $errors[] = pht( 181 165 'You must check "I agree to the terms laid forth above."'); 182 - $e_agree = pht('Required'); 166 + $field_errors['agree'] = pht('Required'); 183 167 } 184 168 185 - if ($viewer->isLoggedIn()) { 169 + if ($viewer->isLoggedIn() && $is_individual) { 186 170 $verified = LegalpadDocumentSignature::VERIFIED; 187 171 } else { 188 172 $verified = LegalpadDocumentSignature::UNVERIFIED; ··· 195 179 // If the viewer is logged in, send them to the document page, which 196 180 // will show that they have signed the document. Otherwise, send them 197 181 // to a completion page. 198 - if ($viewer->isLoggedIn()) { 182 + if ($viewer->isLoggedIn() && $is_individual) { 199 183 $next_uri = '/'.$document->getMonogram(); 200 184 } else { 185 + $this->sendVerifySignatureEmail( 186 + $document, 187 + $signature); 188 + 201 189 $next_uri = $this->getApplicationURI('done/'); 202 190 } 203 191 ··· 257 245 } 258 246 259 247 $signature_form = $this->buildSignatureForm( 260 - $document_body, 248 + $document, 261 249 $signature, 262 - $e_name, 263 - $e_email, 264 - $e_agree); 250 + $field_errors); 265 251 266 252 $subheader = id(new PHUIHeaderView()) 267 253 ->setHeader(pht('Agree and Sign Document')) ··· 289 275 )); 290 276 } 291 277 278 + private function readSignerInformation( 279 + LegalpadDocument $document, 280 + AphrontRequest $request) { 281 + 282 + $viewer = $request->getUser(); 283 + $signer_phid = null; 284 + $signature_data = array(); 285 + 286 + switch ($document->getSignatureType()) { 287 + case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL: 288 + if ($viewer->isLoggedIn()) { 289 + $signer_phid = $viewer->getPHID(); 290 + $signature_data = array( 291 + 'name' => $viewer->getRealName(), 292 + 'email' => $viewer->loadPrimaryEmailAddress(), 293 + ); 294 + } else if ($request->isFormPost()) { 295 + $email = new PhutilEmailAddress($request->getStr('email')); 296 + if (strlen($email->getDomainName())) { 297 + $email_obj = id(new PhabricatorUserEmail()) 298 + ->loadOneWhere('address = %s', $email->getAddress()); 299 + if ($email_obj) { 300 + return $this->signInResponse(); 301 + } 302 + $external_account = id(new PhabricatorExternalAccountQuery()) 303 + ->setViewer($viewer) 304 + ->withAccountTypes(array('email')) 305 + ->withAccountDomains(array($email->getDomainName())) 306 + ->withAccountIDs(array($email->getAddress())) 307 + ->loadOneOrCreate(); 308 + if ($external_account->getUserPHID()) { 309 + return $this->signInResponse(); 310 + } 311 + $signer_phid = $external_account->getPHID(); 312 + } 313 + } 314 + break; 315 + case LegalpadDocument::SIGNATURE_TYPE_CORPORATION: 316 + $signer_phid = $viewer->getPHID(); 317 + if ($signer_phid) { 318 + $signature_data = array( 319 + 'contact.name' => $viewer->getRealName(), 320 + 'email' => $viewer->loadPrimaryEmailAddress(), 321 + 'actorPHID' => $viewer->getPHID(), 322 + ); 323 + } 324 + break; 325 + } 326 + 327 + return array($signer_phid, $signature_data); 328 + } 329 + 292 330 private function buildSignatureForm( 293 - LegalpadDocumentBody $body, 331 + LegalpadDocument $document, 294 332 LegalpadDocumentSignature $signature, 295 - $e_name, 296 - $e_email, 297 - $e_agree) { 333 + array $errors) { 298 334 299 335 $viewer = $this->getRequest()->getUser(); 300 336 $data = $signature->getSignatureData(); 301 337 302 338 $form = id(new AphrontFormView()) 303 - ->setUser($viewer) 339 + ->setUser($viewer); 340 + 341 + $signature_type = $document->getSignatureType(); 342 + switch ($signature_type) { 343 + case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL: 344 + $this->buildIndividualSignatureForm( 345 + $form, 346 + $document, 347 + $signature, 348 + $errors); 349 + break; 350 + case LegalpadDocument::SIGNATURE_TYPE_CORPORATION: 351 + $this->buildCorporateSignatureForm( 352 + $form, 353 + $document, 354 + $signature, 355 + $errors); 356 + break; 357 + default: 358 + throw new Exception( 359 + pht( 360 + 'This document has an unknown signature type ("%s").', 361 + $signature_type)); 362 + } 363 + 364 + $form 365 + ->appendChild( 366 + id(new AphrontFormCheckboxControl()) 367 + ->setError(idx($errors, 'agree', null)) 368 + ->addCheckbox( 369 + 'agree', 370 + 'agree', 371 + pht('I agree to the terms laid forth above.'), 372 + false)) 373 + ->appendChild( 374 + id(new AphrontFormSubmitControl()) 375 + ->setValue(pht('Sign Document')) 376 + ->addCancelButton($this->getApplicationURI())); 377 + 378 + return $form; 379 + } 380 + 381 + private function buildIndividualSignatureForm( 382 + AphrontFormView $form, 383 + LegalpadDocument $document, 384 + LegalpadDocumentSignature $signature, 385 + array $errors) { 386 + 387 + $data = $signature->getSignatureData(); 388 + 389 + $form 304 390 ->appendChild( 305 391 id(new AphrontFormTextControl()) 306 392 ->setLabel(pht('Name')) 307 393 ->setValue(idx($data, 'name', '')) 308 394 ->setName('name') 309 - ->setError($e_name)); 395 + ->setError(idx($errors, 'name', null))); 310 396 397 + $viewer = $this->getRequest()->getUser(); 311 398 if (!$viewer->isLoggedIn()) { 312 399 $form->appendChild( 313 400 id(new AphrontFormTextControl()) 314 401 ->setLabel(pht('Email')) 315 402 ->setValue(idx($data, 'email', '')) 316 403 ->setName('email') 317 - ->setError($e_email)); 404 + ->setError(idx($errors, 'email', null))); 318 405 } 319 406 407 + return $form; 408 + } 409 + 410 + private function buildCorporateSignatureForm( 411 + AphrontFormView $form, 412 + LegalpadDocument $document, 413 + LegalpadDocumentSignature $signature, 414 + array $errors) { 415 + 416 + $data = $signature->getSignatureData(); 417 + 320 418 $form 321 419 ->appendChild( 322 - id(new AphrontFormCheckboxControl()) 323 - ->setError($e_agree) 324 - ->addCheckbox( 325 - 'agree', 326 - 'agree', 327 - pht('I agree to the terms laid forth above.'), 328 - false)) 420 + id(new AphrontFormTextControl()) 421 + ->setLabel(pht('Company Name')) 422 + ->setValue(idx($data, 'name', '')) 423 + ->setName('name') 424 + ->setError(idx($errors, 'name', null))) 329 425 ->appendChild( 330 - id(new AphrontFormSubmitControl()) 331 - ->setValue(pht('Sign Document')) 332 - ->addCancelButton($this->getApplicationURI())); 426 + id(new AphrontFormTextAreaControl()) 427 + ->setLabel(pht('Company Address')) 428 + ->setValue(idx($data, 'address', '')) 429 + ->setName('address') 430 + ->setError(idx($errors, 'address', null))) 431 + ->appendChild( 432 + id(new AphrontFormTextControl()) 433 + ->setLabel(pht('Contact Name')) 434 + ->setValue(idx($data, 'contact.name', '')) 435 + ->setName('contact.name') 436 + ->setError(idx($errors, 'contact.name', null))) 437 + ->appendChild( 438 + id(new AphrontFormTextControl()) 439 + ->setLabel(pht('Contact Email')) 440 + ->setValue(idx($data, 'email', '')) 441 + ->setName('email') 442 + ->setError(idx($errors, 'email', null))); 333 443 334 444 return $form; 335 445 } 336 446 447 + private function readSignatureForm( 448 + LegalpadDocument $document, 449 + AphrontRequest $request) { 450 + 451 + $signature_type = $document->getSignatureType(); 452 + switch ($signature_type) { 453 + case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL: 454 + $result = $this->readIndividualSignatureForm( 455 + $document, 456 + $request); 457 + break; 458 + case LegalpadDocument::SIGNATURE_TYPE_CORPORATION: 459 + $result = $this->readCorporateSignatureForm( 460 + $document, 461 + $request); 462 + break; 463 + default: 464 + throw new Exception( 465 + pht( 466 + 'This document has an unknown signature type ("%s").', 467 + $signature_type)); 468 + } 469 + 470 + return $result; 471 + } 472 + 473 + private function readIndividualSignatureForm( 474 + LegalpadDocument $document, 475 + AphrontRequest $request) { 476 + 477 + $signature_data = array(); 478 + $errors = array(); 479 + $field_errors = array(); 480 + 481 + 482 + $name = $request->getStr('name'); 483 + 484 + if (!strlen($name)) { 485 + $field_errors['name'] = pht('Required'); 486 + $errors[] = pht('Name field is required.'); 487 + } else { 488 + $field_errors['name'] = null; 489 + } 490 + $signature_data['name'] = $name; 491 + 492 + $viewer = $request->getUser(); 493 + if ($viewer->isLoggedIn()) { 494 + $email = $viewer->loadPrimaryEmailAddress(); 495 + } else { 496 + $email = $request->getStr('email'); 497 + 498 + $addr_obj = null; 499 + if (!strlen($email)) { 500 + $field_errors['email'] = pht('Required'); 501 + $errors[] = pht('Email field is required.'); 502 + } else { 503 + $addr_obj = new PhutilEmailAddress($email); 504 + $domain = $addr_obj->getDomainName(); 505 + if (!$domain) { 506 + $field_errors['email'] = pht('Invalid'); 507 + $errors[] = pht('A valid email is required.'); 508 + } else { 509 + $field_errors['email'] = null; 510 + } 511 + } 512 + } 513 + $signature_data['email'] = $email; 514 + 515 + return array($signature_data, $errors, $field_errors); 516 + } 517 + 518 + private function readCorporateSignatureForm( 519 + LegalpadDocument $document, 520 + AphrontRequest $request) { 521 + 522 + $viewer = $request->getUser(); 523 + if (!$viewer->isLoggedIn()) { 524 + throw new Exception( 525 + pht( 526 + 'You can not sign a document on behalf of a corporation unless '. 527 + 'you are logged in.')); 528 + } 529 + 530 + $signature_data = array(); 531 + $errors = array(); 532 + $field_errors = array(); 533 + 534 + $name = $request->getStr('name'); 535 + 536 + if (!strlen($name)) { 537 + $field_errors['name'] = pht('Required'); 538 + $errors[] = pht('Company name is required.'); 539 + } else { 540 + $field_errors['name'] = null; 541 + } 542 + $signature_data['name'] = $name; 543 + 544 + $address = $request->getStr('address'); 545 + if (!strlen($address)) { 546 + $field_errors['address'] = pht('Required'); 547 + $errors[] = pht('Company address is required.'); 548 + } else { 549 + $field_errors['address'] = null; 550 + } 551 + $signature_data['address'] = $address; 552 + 553 + $contact_name = $request->getStr('contact.name'); 554 + if (!strlen($contact_name)) { 555 + $field_errors['contact.name'] = pht('Required'); 556 + $errors[] = pht('Contact name is required.'); 557 + } else { 558 + $field_errors['contact.name'] = null; 559 + } 560 + $signature_data['contact.name'] = $contact_name; 561 + 562 + $email = $request->getStr('email'); 563 + $addr_obj = null; 564 + if (!strlen($email)) { 565 + $field_errors['email'] = pht('Required'); 566 + $errors[] = pht('Contact email is required.'); 567 + } else { 568 + $addr_obj = new PhutilEmailAddress($email); 569 + $domain = $addr_obj->getDomainName(); 570 + if (!$domain) { 571 + $field_errors['email'] = pht('Invalid'); 572 + $errors[] = pht('A valid email is required.'); 573 + } else { 574 + $field_errors['email'] = null; 575 + } 576 + } 577 + $signature_data['email'] = $email; 578 + 579 + return array($signature_data, $errors, $field_errors); 580 + } 581 + 337 582 private function sendVerifySignatureEmail( 338 583 LegalpadDocument $doc, 339 584 LegalpadDocumentSignature $signature) { 340 585 341 586 $signature_data = $signature->getSignatureData(); 342 587 $email = new PhutilEmailAddress($signature_data['email']); 343 - $doc_link = PhabricatorEnv::getProductionURI($doc->getMonogram()); 588 + $doc_name = $doc->getTitle(); 589 + $doc_link = PhabricatorEnv::getProductionURI('/'.$doc->getMonogram()); 344 590 $path = $this->getApplicationURI(sprintf( 345 591 '/verify/%s/', 346 592 $signature->getSecretKey())); 347 593 $link = PhabricatorEnv::getProductionURI($path); 594 + 595 + $name = idx($signature_data, 'name'); 348 596 349 597 $body = <<<EOBODY 350 - Hi {$signature_data['name']}, 598 + {$name}: 599 + 600 + This email address was used to sign a Legalpad document in Phabricator: 601 + 602 + {$doc_name} 351 603 352 - This email address was used to sign a Legalpad document ({$doc_link}). 353 - Please verify you own this email address by clicking this link: 604 + Please verify you own this email address and accept the agreement by clicking 605 + this link: 354 606 355 607 {$link} 356 608 357 - Your signature is invalid until you verify you own the email. 609 + Your signature is not valid until you complete this verification step. 610 + 611 + You can review the document here: 612 + 613 + {$doc_link} 614 + 358 615 EOBODY; 359 616 360 617 id(new PhabricatorMetaMTAMail())
+81 -40
src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
··· 28 28 29 29 $next_uri = $this->getApplicationURI('signatures/'.$document->getID().'/'); 30 30 31 + $e_name = true; 31 32 $e_user = true; 32 33 $v_users = array(); 33 34 $v_notes = ''; 35 + $v_name = ''; 34 36 $errors = array(); 35 37 38 + $type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL; 39 + $is_individual = ($document->getSignatureType() == $type_individual); 40 + 36 41 if ($request->isFormPost()) { 37 42 $v_notes = $request->getStr('notes'); 38 43 $v_users = array_slice($request->getArr('users'), 0, 1); 44 + $v_name = $request->getStr('name'); 39 45 40 - $user_phid = head($v_users); 41 - if (!$user_phid) { 42 - $e_user = pht('Required'); 43 - $errors[] = pht('You must choose a user to exempt.'); 44 - } else { 45 - $user = id(new PhabricatorPeopleQuery()) 46 - ->setViewer($viewer) 47 - ->withPHIDs(array($user_phid)) 48 - ->executeOne(); 49 - 50 - if (!$user) { 51 - $e_user = pht('Invalid'); 52 - $errors[] = pht('That user does not exist.'); 46 + if ($is_individual) { 47 + $user_phid = head($v_users); 48 + if (!$user_phid) { 49 + $e_user = pht('Required'); 50 + $errors[] = pht('You must choose a user to exempt.'); 53 51 } else { 54 - $signature = id(new LegalpadDocumentSignatureQuery()) 52 + $user = id(new PhabricatorPeopleQuery()) 55 53 ->setViewer($viewer) 56 - ->withDocumentPHIDs(array($document->getPHID())) 57 - ->withSignerPHIDs(array($user->getPHID())) 54 + ->withPHIDs(array($user_phid)) 58 55 ->executeOne(); 59 - if ($signature) { 60 - $e_user = pht('Signed'); 61 - $errors[] = pht('That user has already signed this document.'); 56 + 57 + if (!$user) { 58 + $e_user = pht('Invalid'); 59 + $errors[] = pht('That user does not exist.'); 62 60 } else { 63 - $e_user = null; 61 + $signature = id(new LegalpadDocumentSignatureQuery()) 62 + ->setViewer($viewer) 63 + ->withDocumentPHIDs(array($document->getPHID())) 64 + ->withSignerPHIDs(array($user->getPHID())) 65 + ->executeOne(); 66 + if ($signature) { 67 + $e_user = pht('Signed'); 68 + $errors[] = pht('That user has already signed this document.'); 69 + } else { 70 + $e_user = null; 71 + } 64 72 } 73 + } 74 + } else { 75 + $company_name = $v_name; 76 + if (!strlen($company_name)) { 77 + $e_name = pht('Required'); 78 + $errors[] = pht('You must choose a company to add an exemption for.'); 65 79 } 66 80 } 67 81 68 82 if (!$errors) { 69 - $name = $user->getRealName(); 70 - $email = $user->loadPrimaryEmailAddress(); 83 + if ($is_individual) { 84 + $name = $user->getRealName(); 85 + $email = $user->loadPrimaryEmailAddress(); 86 + $signer_phid = $user->getPHID(); 87 + $signature_data = array( 88 + 'name' => $name, 89 + 'email' => $email, 90 + 'notes' => $v_notes, 91 + ); 92 + } else { 93 + $name = $company_name; 94 + $email = ''; 95 + $signer_phid = null; 96 + $signature_data = array( 97 + 'name' => $name, 98 + 'email' => null, 99 + 'notes' => $v_notes, 100 + 'actorPHID' => $viewer->getPHID(), 101 + ); 102 + } 71 103 72 104 $signature = id(new LegalpadDocumentSignature()) 73 105 ->setDocumentPHID($document->getPHID()) 74 106 ->setDocumentVersion($document->getVersions()) 75 - ->setSignerPHID($user->getPHID()) 107 + ->setSignerPHID($signer_phid) 76 108 ->setSignerName($name) 77 109 ->setSignerEmail($email) 110 + ->setSignatureType($document->getSignatureType()) 78 111 ->setIsExemption(1) 79 112 ->setExemptionPHID($viewer->getPHID()) 80 113 ->setVerified(LegalpadDocumentSignature::VERIFIED) 81 - ->setSignatureData( 82 - array( 83 - 'name' => $name, 84 - 'email' => $email, 85 - 'notes' => $v_notes, 86 - )); 114 + ->setSignatureData($signature_data); 87 115 88 116 $signature->save(); 89 117 90 118 return id(new AphrontRedirectResponse())->setURI($next_uri); 91 119 } 92 120 } 93 - 94 - $user_handles = $this->loadViewerHandles($v_users); 95 121 96 122 $form = id(new AphrontFormView()) 97 - ->setUser($viewer) 98 - ->appendChild( 99 - id(new AphrontFormTokenizerControl()) 100 - ->setLabel(pht('Exempt User')) 101 - ->setName('users') 102 - ->setLimit(1) 103 - ->setDatasource('/typeahead/common/users/') 104 - ->setValue($user_handles) 105 - ->setError($e_user)) 123 + ->setUser($viewer); 124 + 125 + if ($is_individual) { 126 + $user_handles = $this->loadViewerHandles($v_users); 127 + $form 128 + ->appendChild( 129 + id(new AphrontFormTokenizerControl()) 130 + ->setLabel(pht('Exempt User')) 131 + ->setName('users') 132 + ->setLimit(1) 133 + ->setDatasource('/typeahead/common/users/') 134 + ->setValue($user_handles) 135 + ->setError($e_user)); 136 + } else { 137 + $form 138 + ->appendChild( 139 + id(new AphrontFormTextControl()) 140 + ->setLabel(pht('Company Name')) 141 + ->setName('name') 142 + ->setError($e_name) 143 + ->setValue($v_name)); 144 + } 145 + 146 + $form 106 147 ->appendChild( 107 148 id(new AphrontFormTextAreaControl()) 108 149 ->setLabel(pht('Notes'))
+72 -66
src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php
··· 1 1 <?php 2 2 3 3 final class LegalpadDocumentSignatureVerificationController 4 - extends LegalpadController { 4 + extends LegalpadController { 5 5 6 6 private $code; 7 7 8 - public function willProcessRequest(array $data) { 9 - $this->code = $data['code']; 10 - } 11 - 12 - public function shouldRequireEmailVerification() { 13 - return false; 8 + public function shouldAllowPublic() { 9 + return true; 14 10 } 15 11 16 - public function shouldRequireLogin() { 17 - return false; 12 + public function willProcessRequest(array $data) { 13 + $this->code = $data['code']; 18 14 } 19 15 20 16 public function processRequest() { 21 17 $request = $this->getRequest(); 22 - $user = $request->getUser(); 18 + $viewer = $request->getUser(); 23 19 24 - // this page can be accessed by not logged in users to valid their 25 - // signatures. use the omnipotent user for these cases. 26 - if (!$user->isLoggedIn()) { 27 - $viewer = PhabricatorUser::getOmnipotentUser(); 28 - } else { 29 - $viewer = $user; 30 - } 31 - 20 + // NOTE: We're using the omnipotent user to handle logged-out signatures 21 + // and corporate signatures. 32 22 $signature = id(new LegalpadDocumentSignatureQuery()) 33 - ->setViewer($viewer) 23 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 34 24 ->withSecretKeys(array($this->code)) 35 25 ->executeOne(); 36 26 37 27 if (!$signature) { 38 - $title = pht('Unable to Verify Signature'); 39 - $content = pht( 40 - 'The verification code you provided is incorrect or the signature '. 41 - 'has been removed. '. 42 - 'Make sure you followed the link in the email correctly.'); 43 - $uri = $this->getApplicationURI(); 44 - $continue = pht('Rats!'); 45 - } else { 46 - $document = id(new LegalpadDocumentQuery()) 47 - ->setViewer($user) 48 - ->withPHIDs(array($signature->getDocumentPHID())) 49 - ->executeOne(); 50 - // the document could be deleted or have its permissions changed 51 - // 4oh4 time 52 - if (!$document) { 53 - return new Aphront404Response(); 54 - } 55 - $uri = '/'.$document->getMonogram(); 56 - if ($signature->isVerified()) { 57 - $title = pht('Signature Already Verified'); 58 - $content = pht( 59 - 'This signature has already been verified.'); 60 - $continue = pht('Continue to Legalpad Document'); 61 - } else { 62 - $guard = AphrontWriteGuard::beginScopedUnguardedWrites(); 63 - $signature 64 - ->setVerified(LegalpadDocumentSignature::VERIFIED) 65 - ->save(); 66 - unset($guard); 67 - $title = pht('Signature Verified'); 68 - $content = pht('The signature is now verified.'); 69 - $continue = pht('Continue to Legalpad Document'); 70 - } 28 + return $this->newDialog() 29 + ->setTitle(pht('Unable to Verify Signature')) 30 + ->appendParagraph( 31 + pht( 32 + 'The signature verification code is incorrect, or the signature '. 33 + 'has been invalidated. Make sure you followed the link in the '. 34 + 'email correctly.')) 35 + ->addCancelButton('/', pht('Rats!')); 71 36 } 72 37 73 - $dialog = id(new AphrontDialogView()) 74 - ->setUser($user) 75 - ->setTitle($title) 76 - ->setMethod('GET') 77 - ->addCancelButton($uri, $continue) 78 - ->appendChild($content); 38 + if ($signature->isVerified()) { 39 + return $this->newDialog() 40 + ->setTitle(pht('Signature Already Verified')) 41 + ->appendParagraph( 42 + pht( 43 + 'This signature has already been verified.')) 44 + ->addCancelButton('/', pht('Okay')); 45 + } 46 + 47 + if ($request->isFormPost()) { 48 + $signature 49 + ->setVerified(LegalpadDocumentSignature::VERIFIED) 50 + ->save(); 79 51 80 - $crumbs = $this->buildApplicationCrumbs(); 81 - $crumbs->addTextCrumb(pht('Verify Signature')); 52 + return $this->newDialog() 53 + ->setTitle(pht('Signature Verified')) 54 + ->appendParagraph(pht('The signature is now verified.')) 55 + ->addCancelButton('/', pht('Okay')); 56 + } 82 57 83 - return $this->buildApplicationPage( 58 + $document_link = phutil_tag( 59 + 'a', 84 60 array( 85 - $crumbs, 86 - $dialog, 61 + 'href' => '/'.$signature->getDocument()->getMonogram(), 62 + 'target' => '_blank', 87 63 ), 88 - array( 89 - 'title' => pht('Verify Signature'), 90 - )); 64 + $signature->getDocument()->getTitle()); 65 + 66 + $signed_at = phabricator_datetime($signature->getDateCreated(), $viewer); 67 + 68 + $name = $signature->getSignerName(); 69 + $email = $signature->getSignerEmail(); 70 + 71 + $form = id(new AphrontFormView()) 72 + ->setUser($viewer) 73 + ->appendRemarkupInstructions( 74 + pht('Please verify this document signature.')) 75 + ->appendChild( 76 + id(new AphrontFormMarkupControl()) 77 + ->setLabel(pht('Document')) 78 + ->setValue($document_link)) 79 + ->appendChild( 80 + id(new AphrontFormMarkupControl()) 81 + ->setLabel(pht('Signed At')) 82 + ->setValue($signed_at)) 83 + ->appendChild( 84 + id(new AphrontFormMarkupControl()) 85 + ->setLabel(pht('Name')) 86 + ->setValue($name)) 87 + ->appendChild( 88 + id(new AphrontFormMarkupControl()) 89 + ->setLabel(pht('Email')) 90 + ->setValue($email)); 91 + 92 + return $this->newDialog() 93 + ->setTitle(pht('Verify Signature?')) 94 + ->appendChild($form->buildLayoutView()) 95 + ->addCancelButton('/') 96 + ->addSubmitButton(pht('Verify Signature')); 91 97 } 92 98 93 99 }
+53 -12
src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
··· 44 44 $document_id = $signature->getDocument()->getID(); 45 45 $next_uri = $this->getApplicationURI('signatures/'.$document_id.'/'); 46 46 47 + $data = $signature->getSignatureData(); 48 + 47 49 $exemption_phid = $signature->getExemptionPHID(); 48 - $handles = $this->loadViewerHandles(array($exemption_phid)); 50 + $actor_phid = idx($data, 'actorPHID'); 51 + $handles = $this->loadViewerHandles( 52 + array( 53 + $exemption_phid, 54 + $actor_phid, 55 + )); 49 56 $exemptor_handle = $handles[$exemption_phid]; 57 + $actor_handle = $handles[$actor_phid]; 50 58 51 - $data = $signature->getSignatureData(); 59 + $form = id(new AphrontFormView()) 60 + ->setUser($viewer); 61 + 62 + if ($signature->getExemptionPHID()) { 63 + $form 64 + ->appendChild( 65 + id(new AphrontFormMarkupControl()) 66 + ->setLabel(pht('Exemption By')) 67 + ->setValue($exemptor_handle->renderLink())) 68 + ->appendChild( 69 + id(new AphrontFormMarkupControl()) 70 + ->setLabel(pht('Notes')) 71 + ->setValue(idx($data, 'notes'))); 72 + } 52 73 53 - $form = id(new AphrontFormView()) 54 - ->setUser($viewer) 55 - ->appendChild( 56 - id(new AphrontFormMarkupControl()) 57 - ->setLabel(pht('Exemption By')) 58 - ->setValue($exemptor_handle->renderLink())) 59 - ->appendChild( 60 - id(new AphrontFormMarkupControl()) 61 - ->setLabel(pht('Notes')) 62 - ->setValue(idx($data, 'notes'))); 74 + $type_corporation = LegalpadDocument::SIGNATURE_TYPE_CORPORATION; 75 + if ($signature->getSignatureType() == $type_corporation) { 76 + $form 77 + ->appendChild( 78 + id(new AphrontFormMarkupControl()) 79 + ->setLabel(pht('Signing User')) 80 + ->setValue($actor_handle->renderLink())) 81 + ->appendChild( 82 + id(new AphrontFormMarkupControl()) 83 + ->setLabel(pht('Company Name')) 84 + ->setValue(idx($data, 'name'))) 85 + ->appendChild( 86 + id(new AphrontFormMarkupControl()) 87 + ->setLabel(pht('Address')) 88 + ->setValue(phutil_escape_html_newlines(idx($data, 'address')))) 89 + ->appendChild( 90 + id(new AphrontFormMarkupControl()) 91 + ->setLabel(pht('Contact Name')) 92 + ->setValue(idx($data, 'contact.name'))) 93 + ->appendChild( 94 + id(new AphrontFormMarkupControl()) 95 + ->setLabel(pht('Contact Email')) 96 + ->setValue( 97 + phutil_tag( 98 + 'a', 99 + array( 100 + 'href' => 'mailto:'.idx($data, 'email'), 101 + ), 102 + idx($data, 'email')))); 103 + } 63 104 64 105 return $this->newDialog() 65 106 ->setTitle(pht('Signature Details'))
+10
src/applications/legalpad/editor/LegalpadDocumentEditor.php
··· 11 11 private function setIsContribution($is_contribution) { 12 12 $this->isContribution = $is_contribution; 13 13 } 14 + 14 15 private function isContribution() { 15 16 return $this->isContribution; 16 17 } ··· 24 25 25 26 $types[] = LegalpadTransactionType::TYPE_TITLE; 26 27 $types[] = LegalpadTransactionType::TYPE_TEXT; 28 + $types[] = LegalpadTransactionType::TYPE_SIGNATURE_TYPE; 29 + 27 30 return $types; 28 31 } 29 32 ··· 36 39 return $object->getDocumentBody()->getTitle(); 37 40 case LegalpadTransactionType::TYPE_TEXT: 38 41 return $object->getDocumentBody()->getText(); 42 + case LegalpadTransactionType::TYPE_SIGNATURE_TYPE: 43 + return $object->getSignatureType(); 39 44 } 40 45 } 41 46 ··· 46 51 switch ($xaction->getTransactionType()) { 47 52 case LegalpadTransactionType::TYPE_TITLE: 48 53 case LegalpadTransactionType::TYPE_TEXT: 54 + case LegalpadTransactionType::TYPE_SIGNATURE_TYPE: 49 55 return $xaction->getNewValue(); 50 56 } 51 57 } ··· 65 71 $body = $object->getDocumentBody(); 66 72 $body->setText($xaction->getNewValue()); 67 73 $this->setIsContribution(true); 74 + break; 75 + case LegalpadTransactionType::TYPE_SIGNATURE_TYPE: 76 + $object->setSignatureType($xaction->getNewValue()); 68 77 break; 69 78 } 70 79 } ··· 116 125 switch ($type) { 117 126 case LegalpadTransactionType::TYPE_TITLE: 118 127 case LegalpadTransactionType::TYPE_TEXT: 128 + case LegalpadTransactionType::TYPE_SIGNATURE_TYPE: 119 129 return $v; 120 130 } 121 131
+7 -2
src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
··· 169 169 170 170 $title = $document->getTitle(); 171 171 172 + $type_name = $document->getSignatureTypeName(); 173 + $type_icon = $document->getSignatureTypeIcon(); 174 + 172 175 $item = id(new PHUIObjectItemView()) 173 176 ->setObjectName($document->getMonogram()) 174 177 ->setHeader($title) 175 178 ->setHref('/'.$document->getMonogram()) 176 179 ->setObject($document) 177 - ->addIcon('none', pht('Version %d', $document->getVersions())) 178 - ->addIcon('none', pht('Updated %s', $last_updated)); 180 + ->addIcon($type_icon, $type_name) 181 + ->addIcon( 182 + 'fa-pencil grey', 183 + pht('Version %d (%s)', $document->getVersions(), $last_updated)); 179 184 180 185 if ($viewer->getPHID()) { 181 186 $signature = $document->getUserSignature($viewer->getPHID());
+26 -11
src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
··· 168 168 null, 169 169 pht('Verified, Current')); 170 170 171 + $sig_corp = $this->renderIcon( 172 + 'fa-building-o', 173 + null, 174 + pht('Verified, Corporate')); 175 + 171 176 $sig_old = $this->renderIcon( 172 177 'fa-clock-o', 173 178 'orange', ··· 188 193 ->addSigil('has-tooltip') 189 194 ->setMetadata(array('tip' => pht('Unverified Email'))); 190 195 196 + $type_corporate = LegalpadDocument::SIGNATURE_TYPE_CORPORATION; 197 + 191 198 $rows = array(); 192 199 foreach ($signatures as $signature) { 193 200 $name = $signature->getSignerName(); ··· 196 203 $document = $signature->getDocument(); 197 204 198 205 if ($signature->getIsExemption()) { 199 - $signature_href = $this->getApplicationURI( 200 - 'signature/'.$signature->getID().'/'); 201 - 202 - $sig_icon = javelin_tag( 203 - 'a', 204 - array( 205 - 'href' => $signature_href, 206 - 'sigil' => 'workflow', 207 - ), 208 - $sig_exemption); 206 + $sig_icon = $sig_exemption; 209 207 } else if (!$signature->isVerified()) { 210 208 $sig_icon = $sig_unverified; 211 209 } else if ($signature->getDocumentVersion() != $document->getVersions()) { 212 210 $sig_icon = $sig_old; 211 + } else if ($signature->getSignatureType() == $type_corporate) { 212 + $sig_icon = $sig_corp; 213 213 } else { 214 214 $sig_icon = $sig_good; 215 215 } 216 216 217 + $signature_href = $this->getApplicationURI( 218 + 'signature/'.$signature->getID().'/'); 219 + 220 + $sig_icon = javelin_tag( 221 + 'a', 222 + array( 223 + 'href' => $signature_href, 224 + 'sigil' => 'workflow', 225 + ), 226 + $sig_icon); 227 + 228 + $signer_phid = $signature->getSignerPHID(); 229 + 217 230 $rows[] = array( 218 231 $sig_icon, 219 232 $handles[$document->getPHID()]->renderLink(), 220 - $handles[$signature->getSignerPHID()]->renderLink(), 233 + $signer_phid 234 + ? $handles[$signer_phid]->renderLink() 235 + : null, 221 236 $name, 222 237 phutil_tag( 223 238 'a',
+27
src/applications/legalpad/storage/LegalpadDocument.php
··· 16 16 protected $viewPolicy; 17 17 protected $editPolicy; 18 18 protected $mailKey; 19 + protected $signatureType; 20 + 21 + const SIGNATURE_TYPE_INDIVIDUAL = 'user'; 22 + const SIGNATURE_TYPE_CORPORATION = 'corp'; 19 23 20 24 private $documentBody = self::ATTACHABLE; 21 25 private $contributors = self::ATTACHABLE; ··· 37 41 ->setContributorCount(0) 38 42 ->setRecentContributorPHIDs(array()) 39 43 ->attachSignatures(array()) 44 + ->setSignatureType(self::SIGNATURE_TYPE_INDIVIDUAL) 40 45 ->setViewPolicy($view_policy) 41 46 ->setEditPolicy($edit_policy); 42 47 } ··· 102 107 LegalpadDocumentSignature $signature = null) { 103 108 $this->userSignatures[$user_phid] = $signature; 104 109 return $this; 110 + } 111 + 112 + public static function getSignatureTypeMap() { 113 + return array( 114 + self::SIGNATURE_TYPE_INDIVIDUAL => pht('Individuals'), 115 + self::SIGNATURE_TYPE_CORPORATION => pht('Corporations'), 116 + ); 117 + } 118 + 119 + public function getSignatureTypeName() { 120 + $type = $this->getSignatureType(); 121 + return idx(self::getSignatureTypeMap(), $type, $type); 122 + } 123 + 124 + public function getSignatureTypeIcon() { 125 + $type = $this->getSignatureType(); 126 + $map = array( 127 + self::SIGNATURE_TYPE_INDIVIDUAL => 'fa-user grey', 128 + self::SIGNATURE_TYPE_CORPORATION => 'fa-building-o grey', 129 + ); 130 + 131 + return idx($map, $type, 'fa-user grey'); 105 132 } 106 133 107 134
+1
src/applications/legalpad/storage/LegalpadDocumentSignature.php
··· 9 9 10 10 protected $documentPHID; 11 11 protected $documentVersion; 12 + protected $signatureType; 12 13 protected $signerPHID; 13 14 protected $signerName; 14 15 protected $signerEmail;