Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15381049
D9812.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
48 KB
Referenced Files
None
Subscribers
None
D9812.diff
View Options
diff --git a/resources/sql/autopatches/20140703.legalcorp.1.sql b/resources/sql/autopatches/20140703.legalcorp.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140703.legalcorp.1.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_legalpad.legalpad_document
+ ADD signatureType VARCHAR(4) NOT NULL COLLATE utf8_bin;
diff --git a/resources/sql/autopatches/20140703.legalcorp.2.sql b/resources/sql/autopatches/20140703.legalcorp.2.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140703.legalcorp.2.sql
@@ -0,0 +1,2 @@
+UPDATE {$NAMESPACE}_legalpad.legalpad_document
+ SET signatureType = 'user' WHERE signatureType = '';
diff --git a/resources/sql/autopatches/20140703.legalcorp.3.sql b/resources/sql/autopatches/20140703.legalcorp.3.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140703.legalcorp.3.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
+ ADD signatureType VARCHAR(4) NOT NULL COLLATE utf8_bin AFTER documentVersion;
diff --git a/resources/sql/autopatches/20140703.legalcorp.4.sql b/resources/sql/autopatches/20140703.legalcorp.4.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140703.legalcorp.4.sql
@@ -0,0 +1,2 @@
+UPDATE {$NAMESPACE}_legalpad.legalpad_documentsignature
+ SET signatureType = 'user' WHERE signatureType = '';
diff --git a/resources/sql/autopatches/20140703.legalcorp.5.sql b/resources/sql/autopatches/20140703.legalcorp.5.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140703.legalcorp.5.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
+ CHANGE signerPHID signerPHID VARCHAR(64) COLLATE utf8_bin;
diff --git a/src/applications/legalpad/constants/LegalpadTransactionType.php b/src/applications/legalpad/constants/LegalpadTransactionType.php
--- a/src/applications/legalpad/constants/LegalpadTransactionType.php
+++ b/src/applications/legalpad/constants/LegalpadTransactionType.php
@@ -1,11 +1,9 @@
<?php
-/**
- * @group legalpad
- */
final class LegalpadTransactionType extends LegalpadConstants {
const TYPE_TITLE = 'title';
const TYPE_TEXT = 'text';
+ const TYPE_SIGNATURE_TYPE = 'legalpad:signature-type';
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentEditController.php b/src/applications/legalpad/controller/LegalpadDocumentEditController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentEditController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentEditController.php
@@ -1,8 +1,5 @@
<?php
-/**
- * @group legalpad
- */
final class LegalpadDocumentEditController extends LegalpadController {
private $id;
@@ -26,8 +23,6 @@
->setCreatorPHID($user->getPHID());
$document->attachDocumentBody($body);
$document->setDocumentBodyPHID(PhabricatorPHIDConstants::PHID_VOID);
- $title = null;
- $text = null;
} else {
$is_create = false;
@@ -44,12 +39,15 @@
if (!$document) {
return new Aphront404Response();
}
- $title = $document->getDocumentBody()->getTitle();
- $text = $document->getDocumentBody()->getText();
}
$e_title = true;
$e_text = true;
+
+ $title = $document->getDocumentBody()->getTitle();
+ $text = $document->getDocumentBody()->getText();
+ $v_signature_type = $document->getSignatureType();
+
$errors = array();
$can_view = null;
$can_edit = null;
@@ -86,6 +84,13 @@
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($can_edit);
+ if ($is_create) {
+ $v_signature_type = $request->getStr('signatureType');
+ $xactions[] = id(new LegalpadTransaction())
+ ->setTransactionType(LegalpadTransactionType::TYPE_SIGNATURE_TYPE)
+ ->setNewValue($v_signature_type);
+ }
+
if (!$errors) {
$editor = id(new LegalpadDocumentEditor())
->setContentSourceFromRequest($request)
@@ -113,7 +118,23 @@
->setLabel(pht('Title'))
->setError($e_title)
->setValue($title)
- ->setName('title'))
+ ->setName('title'));
+
+ if ($is_create) {
+ $form->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setLabel(pht('Who Should Sign?'))
+ ->setName(pht('signatureType'))
+ ->setValue($v_signature_type)
+ ->setOptions(LegalpadDocument::getSignatureTypeMap()));
+ } else {
+ $form->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Who Should Sign?'))
+ ->setValue($document->getSignatureTypeName()));
+ }
+
+ $form
->appendChild(
id(new PhabricatorRemarkupControl())
->setID('document-text')
diff --git a/src/applications/legalpad/controller/LegalpadDocumentManageController.php b/src/applications/legalpad/controller/LegalpadDocumentManageController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentManageController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentManageController.php
@@ -174,6 +174,10 @@
->setActionList($actions);
$properties->addProperty(
+ pht('Signature Type'),
+ $document->getSignatureTypeName());
+
+ $properties->addProperty(
pht('Last Updated'),
phabricator_datetime($document->getDateModified(), $user));
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php
@@ -25,107 +25,116 @@
return new Aphront404Response();
}
- $signer_phid = null;
- $signature_data = array();
- if ($viewer->isLoggedIn()) {
- $signer_phid = $viewer->getPHID();
- $signature_data = array(
- 'name' => $viewer->getRealName(),
- 'email' => $viewer->loadPrimaryEmailAddress(),
- );
- } else if ($request->isFormPost()) {
- $email = new PhutilEmailAddress($request->getStr('email'));
- if (strlen($email->getDomainName())) {
- $email_obj = id(new PhabricatorUserEmail())
- ->loadOneWhere('address = %s', $email->getAddress());
- if ($email_obj) {
- return $this->signInResponse();
- }
- $external_account = id(new PhabricatorExternalAccountQuery())
- ->setViewer($viewer)
- ->withAccountTypes(array('email'))
- ->withAccountDomains(array($email->getDomainName()))
- ->withAccountIDs(array($email->getAddress()))
- ->loadOneOrCreate();
- if ($external_account->getUserPHID()) {
- return $this->signInResponse();
+ list($signer_phid, $signature_data) = $this->readSignerInformation(
+ $document,
+ $request);
+
+ $signature = null;
+
+ $type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL;
+ $is_individual = ($document->getSignatureType() == $type_individual);
+ if ($is_individual) {
+ if ($signer_phid) {
+ // TODO: This is odd and should probably be adjusted after grey/external
+ // accounts work better, but use the omnipotent viewer to check for a
+ // signature so we can pick up anonymous/grey signatures.
+
+ $signature = id(new LegalpadDocumentSignatureQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withDocumentPHIDs(array($document->getPHID()))
+ ->withSignerPHIDs(array($signer_phid))
+ ->executeOne();
+
+ if ($signature && !$viewer->isLoggedIn()) {
+ return $this->newDialog()
+ ->setTitle(pht('Already Signed'))
+ ->appendParagraph(pht('You have already signed this document!'))
+ ->addCancelButton('/'.$document->getMonogram(), pht('Okay'));
}
- $signer_phid = $external_account->getPHID();
}
- }
- $signature = null;
- if ($signer_phid) {
- // TODO: This is odd and should probably be adjusted after grey/external
- // accounts work better, but use the omnipotent viewer to check for a
- // signature so we can pick up anonymous/grey signatures.
-
- $signature = id(new LegalpadDocumentSignatureQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withDocumentPHIDs(array($document->getPHID()))
- ->withSignerPHIDs(array($signer_phid))
- ->executeOne();
-
- if ($signature && !$viewer->isLoggedIn()) {
- return $this->newDialog()
- ->setTitle(pht('Already Signed'))
- ->appendParagraph(pht('You have already signed this document!'))
- ->addCancelButton('/'.$document->getMonogram(), pht('Okay'));
+ $signed_status = null;
+ if (!$signature) {
+ $has_signed = false;
+ $signature = id(new LegalpadDocumentSignature())
+ ->setSignerPHID($signer_phid)
+ ->setDocumentPHID($document->getPHID())
+ ->setDocumentVersion($document->getVersions());
+
+ // If the user is logged in, show a notice that they haven't signed.
+ // If they aren't logged in, we can't be as sure, so don't show
+ // anything.
+ if ($viewer->isLoggedIn()) {
+ $signed_status = id(new AphrontErrorView())
+ ->setSeverity(AphrontErrorView::SEVERITY_WARNING)
+ ->setErrors(
+ array(
+ pht('You have not signed this document yet.'),
+ ));
+ }
+ } else {
+ $has_signed = true;
+ $signature_data = $signature->getSignatureData();
+
+ // In this case, we know they've signed.
+ $signed_at = $signature->getDateCreated();
+
+ if ($signature->getIsExemption()) {
+ $exemption_phid = $signature->getExemptionPHID();
+ $handles = $this->loadViewerHandles(array($exemption_phid));
+ $exemption_handle = $handles[$exemption_phid];
+
+ $signed_text = pht(
+ 'You do not need to sign this document. '.
+ '%s added a signature exemption for you on %s.',
+ $exemption_handle->renderLink(),
+ phabricator_datetime($signed_at, $viewer));
+ } else {
+ $signed_text = pht(
+ 'You signed this document on %s.',
+ phabricator_datetime($signed_at, $viewer));
+ }
+
+ $signed_status = id(new AphrontErrorView())
+ ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
+ ->setErrors(array($signed_text));
}
- }
- $signed_status = null;
- if (!$signature) {
- $has_signed = false;
+ $field_errors = array(
+ 'name' => true,
+ 'email' => true,
+ 'agree' => true,
+ );
+ } else {
$signature = id(new LegalpadDocumentSignature())
- ->setSignerPHID($signer_phid)
->setDocumentPHID($document->getPHID())
- ->setDocumentVersion($document->getVersions())
- ->setSignerName((string)idx($signature_data, 'name'))
- ->setSignerEmail((string)idx($signature_data, 'email'))
- ->setSignatureData($signature_data);
+ ->setDocumentVersion($document->getVersions());
- // If the user is logged in, show a notice that they haven't signed.
- // If they aren't logged in, we can't be as sure, so don't show anything.
if ($viewer->isLoggedIn()) {
+ $has_signed = false;
+
+ $signed_status = null;
+ } else {
+ // This just hides the form.
+ $has_signed = true;
+
+ $login_text = pht(
+ 'This document requires a corporate signatory. You must log in to '.
+ 'accept this document on behalf of a company you represent.');
$signed_status = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
- ->setErrors(
- array(
- pht('You have not signed this document yet.'),
- ));
- }
- } else {
- $has_signed = true;
- $signature_data = $signature->getSignatureData();
-
- // In this case, we know they've signed.
- $signed_at = $signature->getDateCreated();
-
- if ($signature->getIsExemption()) {
- $exemption_phid = $signature->getExemptionPHID();
- $handles = $this->loadViewerHandles(array($exemption_phid));
- $exemption_handle = $handles[$exemption_phid];
-
- $signed_text = pht(
- 'You do not need to sign this document. '.
- '%s added a signature exemption for you on %s.',
- $exemption_handle->renderLink(),
- phabricator_datetime($signed_at, $viewer));
- } else {
- $signed_text = pht(
- 'You signed this document on %s.',
- phabricator_datetime($signed_at, $viewer));
+ ->setErrors(array($login_text));
}
- $signed_status = id(new AphrontErrorView())
- ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
- ->setErrors(array($signed_text));
+ $field_errors = array(
+ 'name' => true,
+ 'address' => true,
+ 'contact.name' => true,
+ 'email' => true,
+ );
}
- $e_name = true;
- $e_email = true;
- $e_agree = null;
+ $signature->setSignatureData($signature_data);
$errors = array();
if ($request->isFormOrHisecPost() && !$has_signed) {
@@ -139,50 +148,25 @@
'/'.$document->getMonogram());
}
- $name = $request->getStr('name');
- $agree = $request->getExists('agree');
-
- if (!strlen($name)) {
- $e_name = pht('Required');
- $errors[] = pht('Name field is required.');
- } else {
- $e_name = null;
- }
- $signature_data['name'] = $name;
-
- if ($viewer->isLoggedIn()) {
- $email = $viewer->loadPrimaryEmailAddress();
- } else {
- $email = $request->getStr('email');
+ list($form_data, $errors, $field_errors) = $this->readSignatureForm(
+ $document,
+ $request);
- $addr_obj = null;
- if (!strlen($email)) {
- $e_email = pht('Required');
- $errors[] = pht('Email field is required.');
- } else {
- $addr_obj = new PhutilEmailAddress($email);
- $domain = $addr_obj->getDomainName();
- if (!$domain) {
- $e_email = pht('Invalid');
- $errors[] = pht('A valid email is required.');
- } else {
- $e_email = null;
- }
- }
- }
- $signature_data['email'] = $email;
+ $signature_data = $form_data + $signature_data;
+ $signature->setSignatureData($signature_data);
+ $signature->setSignatureType($document->getSignatureType());
$signature->setSignerName((string)idx($signature_data, 'name'));
$signature->setSignerEmail((string)idx($signature_data, 'email'));
- $signature->setSignatureData($signature_data);
+ $agree = $request->getExists('agree');
if (!$agree) {
$errors[] = pht(
'You must check "I agree to the terms laid forth above."');
- $e_agree = pht('Required');
+ $field_errors['agree'] = pht('Required');
}
- if ($viewer->isLoggedIn()) {
+ if ($viewer->isLoggedIn() && $is_individual) {
$verified = LegalpadDocumentSignature::VERIFIED;
} else {
$verified = LegalpadDocumentSignature::UNVERIFIED;
@@ -195,9 +179,13 @@
// If the viewer is logged in, send them to the document page, which
// will show that they have signed the document. Otherwise, send them
// to a completion page.
- if ($viewer->isLoggedIn()) {
+ if ($viewer->isLoggedIn() && $is_individual) {
$next_uri = '/'.$document->getMonogram();
} else {
+ $this->sendVerifySignatureEmail(
+ $document,
+ $signature);
+
$next_uri = $this->getApplicationURI('done/');
}
@@ -257,11 +245,9 @@
}
$signature_form = $this->buildSignatureForm(
- $document_body,
+ $document,
$signature,
- $e_name,
- $e_email,
- $e_agree);
+ $field_errors);
$subheader = id(new PHUIHeaderView())
->setHeader(pht('Agree and Sign Document'))
@@ -289,72 +275,343 @@
));
}
+ private function readSignerInformation(
+ LegalpadDocument $document,
+ AphrontRequest $request) {
+
+ $viewer = $request->getUser();
+ $signer_phid = null;
+ $signature_data = array();
+
+ switch ($document->getSignatureType()) {
+ case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
+ if ($viewer->isLoggedIn()) {
+ $signer_phid = $viewer->getPHID();
+ $signature_data = array(
+ 'name' => $viewer->getRealName(),
+ 'email' => $viewer->loadPrimaryEmailAddress(),
+ );
+ } else if ($request->isFormPost()) {
+ $email = new PhutilEmailAddress($request->getStr('email'));
+ if (strlen($email->getDomainName())) {
+ $email_obj = id(new PhabricatorUserEmail())
+ ->loadOneWhere('address = %s', $email->getAddress());
+ if ($email_obj) {
+ return $this->signInResponse();
+ }
+ $external_account = id(new PhabricatorExternalAccountQuery())
+ ->setViewer($viewer)
+ ->withAccountTypes(array('email'))
+ ->withAccountDomains(array($email->getDomainName()))
+ ->withAccountIDs(array($email->getAddress()))
+ ->loadOneOrCreate();
+ if ($external_account->getUserPHID()) {
+ return $this->signInResponse();
+ }
+ $signer_phid = $external_account->getPHID();
+ }
+ }
+ break;
+ case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
+ $signer_phid = $viewer->getPHID();
+ if ($signer_phid) {
+ $signature_data = array(
+ 'contact.name' => $viewer->getRealName(),
+ 'email' => $viewer->loadPrimaryEmailAddress(),
+ 'actorPHID' => $viewer->getPHID(),
+ );
+ }
+ break;
+ }
+
+ return array($signer_phid, $signature_data);
+ }
+
private function buildSignatureForm(
- LegalpadDocumentBody $body,
+ LegalpadDocument $document,
LegalpadDocumentSignature $signature,
- $e_name,
- $e_email,
- $e_agree) {
+ array $errors) {
$viewer = $this->getRequest()->getUser();
$data = $signature->getSignatureData();
$form = id(new AphrontFormView())
- ->setUser($viewer)
+ ->setUser($viewer);
+
+ $signature_type = $document->getSignatureType();
+ switch ($signature_type) {
+ case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
+ $this->buildIndividualSignatureForm(
+ $form,
+ $document,
+ $signature,
+ $errors);
+ break;
+ case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
+ $this->buildCorporateSignatureForm(
+ $form,
+ $document,
+ $signature,
+ $errors);
+ break;
+ default:
+ throw new Exception(
+ pht(
+ 'This document has an unknown signature type ("%s").',
+ $signature_type));
+ }
+
+ $form
+ ->appendChild(
+ id(new AphrontFormCheckboxControl())
+ ->setError(idx($errors, 'agree', null))
+ ->addCheckbox(
+ 'agree',
+ 'agree',
+ pht('I agree to the terms laid forth above.'),
+ false))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Sign Document'))
+ ->addCancelButton($this->getApplicationURI()));
+
+ return $form;
+ }
+
+ private function buildIndividualSignatureForm(
+ AphrontFormView $form,
+ LegalpadDocument $document,
+ LegalpadDocumentSignature $signature,
+ array $errors) {
+
+ $data = $signature->getSignatureData();
+
+ $form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setValue(idx($data, 'name', ''))
->setName('name')
- ->setError($e_name));
+ ->setError(idx($errors, 'name', null)));
+ $viewer = $this->getRequest()->getUser();
if (!$viewer->isLoggedIn()) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setValue(idx($data, 'email', ''))
->setName('email')
- ->setError($e_email));
+ ->setError(idx($errors, 'email', null)));
}
+ return $form;
+ }
+
+ private function buildCorporateSignatureForm(
+ AphrontFormView $form,
+ LegalpadDocument $document,
+ LegalpadDocumentSignature $signature,
+ array $errors) {
+
+ $data = $signature->getSignatureData();
+
$form
->appendChild(
- id(new AphrontFormCheckboxControl())
- ->setError($e_agree)
- ->addCheckbox(
- 'agree',
- 'agree',
- pht('I agree to the terms laid forth above.'),
- false))
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Company Name'))
+ ->setValue(idx($data, 'name', ''))
+ ->setName('name')
+ ->setError(idx($errors, 'name', null)))
->appendChild(
- id(new AphrontFormSubmitControl())
- ->setValue(pht('Sign Document'))
- ->addCancelButton($this->getApplicationURI()));
+ id(new AphrontFormTextAreaControl())
+ ->setLabel(pht('Company Address'))
+ ->setValue(idx($data, 'address', ''))
+ ->setName('address')
+ ->setError(idx($errors, 'address', null)))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Contact Name'))
+ ->setValue(idx($data, 'contact.name', ''))
+ ->setName('contact.name')
+ ->setError(idx($errors, 'contact.name', null)))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Contact Email'))
+ ->setValue(idx($data, 'email', ''))
+ ->setName('email')
+ ->setError(idx($errors, 'email', null)));
return $form;
}
+ private function readSignatureForm(
+ LegalpadDocument $document,
+ AphrontRequest $request) {
+
+ $signature_type = $document->getSignatureType();
+ switch ($signature_type) {
+ case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
+ $result = $this->readIndividualSignatureForm(
+ $document,
+ $request);
+ break;
+ case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
+ $result = $this->readCorporateSignatureForm(
+ $document,
+ $request);
+ break;
+ default:
+ throw new Exception(
+ pht(
+ 'This document has an unknown signature type ("%s").',
+ $signature_type));
+ }
+
+ return $result;
+ }
+
+ private function readIndividualSignatureForm(
+ LegalpadDocument $document,
+ AphrontRequest $request) {
+
+ $signature_data = array();
+ $errors = array();
+ $field_errors = array();
+
+
+ $name = $request->getStr('name');
+
+ if (!strlen($name)) {
+ $field_errors['name'] = pht('Required');
+ $errors[] = pht('Name field is required.');
+ } else {
+ $field_errors['name'] = null;
+ }
+ $signature_data['name'] = $name;
+
+ $viewer = $request->getUser();
+ if ($viewer->isLoggedIn()) {
+ $email = $viewer->loadPrimaryEmailAddress();
+ } else {
+ $email = $request->getStr('email');
+
+ $addr_obj = null;
+ if (!strlen($email)) {
+ $field_errors['email'] = pht('Required');
+ $errors[] = pht('Email field is required.');
+ } else {
+ $addr_obj = new PhutilEmailAddress($email);
+ $domain = $addr_obj->getDomainName();
+ if (!$domain) {
+ $field_errors['email'] = pht('Invalid');
+ $errors[] = pht('A valid email is required.');
+ } else {
+ $field_errors['email'] = null;
+ }
+ }
+ }
+ $signature_data['email'] = $email;
+
+ return array($signature_data, $errors, $field_errors);
+ }
+
+ private function readCorporateSignatureForm(
+ LegalpadDocument $document,
+ AphrontRequest $request) {
+
+ $viewer = $request->getUser();
+ if (!$viewer->isLoggedIn()) {
+ throw new Exception(
+ pht(
+ 'You can not sign a document on behalf of a corporation unless '.
+ 'you are logged in.'));
+ }
+
+ $signature_data = array();
+ $errors = array();
+ $field_errors = array();
+
+ $name = $request->getStr('name');
+
+ if (!strlen($name)) {
+ $field_errors['name'] = pht('Required');
+ $errors[] = pht('Company name is required.');
+ } else {
+ $field_errors['name'] = null;
+ }
+ $signature_data['name'] = $name;
+
+ $address = $request->getStr('address');
+ if (!strlen($address)) {
+ $field_errors['address'] = pht('Required');
+ $errors[] = pht('Company address is required.');
+ } else {
+ $field_errors['address'] = null;
+ }
+ $signature_data['address'] = $address;
+
+ $contact_name = $request->getStr('contact.name');
+ if (!strlen($contact_name)) {
+ $field_errors['contact.name'] = pht('Required');
+ $errors[] = pht('Contact name is required.');
+ } else {
+ $field_errors['contact.name'] = null;
+ }
+ $signature_data['contact.name'] = $contact_name;
+
+ $email = $request->getStr('email');
+ $addr_obj = null;
+ if (!strlen($email)) {
+ $field_errors['email'] = pht('Required');
+ $errors[] = pht('Contact email is required.');
+ } else {
+ $addr_obj = new PhutilEmailAddress($email);
+ $domain = $addr_obj->getDomainName();
+ if (!$domain) {
+ $field_errors['email'] = pht('Invalid');
+ $errors[] = pht('A valid email is required.');
+ } else {
+ $field_errors['email'] = null;
+ }
+ }
+ $signature_data['email'] = $email;
+
+ return array($signature_data, $errors, $field_errors);
+ }
+
private function sendVerifySignatureEmail(
LegalpadDocument $doc,
LegalpadDocumentSignature $signature) {
$signature_data = $signature->getSignatureData();
$email = new PhutilEmailAddress($signature_data['email']);
- $doc_link = PhabricatorEnv::getProductionURI($doc->getMonogram());
+ $doc_name = $doc->getTitle();
+ $doc_link = PhabricatorEnv::getProductionURI('/'.$doc->getMonogram());
$path = $this->getApplicationURI(sprintf(
'/verify/%s/',
$signature->getSecretKey()));
$link = PhabricatorEnv::getProductionURI($path);
+ $name = idx($signature_data, 'name');
+
$body = <<<EOBODY
-Hi {$signature_data['name']},
+{$name}:
-This email address was used to sign a Legalpad document ({$doc_link}).
-Please verify you own this email address by clicking this link:
+This email address was used to sign a Legalpad document in Phabricator:
+
+ {$doc_name}
+
+Please verify you own this email address and accept the agreement by clicking
+this link:
{$link}
-Your signature is invalid until you verify you own the email.
+Your signature is not valid until you complete this verification step.
+
+You can review the document here:
+
+ {$doc_link}
+
EOBODY;
id(new PhabricatorMetaMTAMail())
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
@@ -28,62 +28,90 @@
$next_uri = $this->getApplicationURI('signatures/'.$document->getID().'/');
+ $e_name = true;
$e_user = true;
$v_users = array();
$v_notes = '';
+ $v_name = '';
$errors = array();
+ $type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL;
+ $is_individual = ($document->getSignatureType() == $type_individual);
+
if ($request->isFormPost()) {
$v_notes = $request->getStr('notes');
$v_users = array_slice($request->getArr('users'), 0, 1);
+ $v_name = $request->getStr('name');
- $user_phid = head($v_users);
- if (!$user_phid) {
- $e_user = pht('Required');
- $errors[] = pht('You must choose a user to exempt.');
- } else {
- $user = id(new PhabricatorPeopleQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($user_phid))
- ->executeOne();
-
- if (!$user) {
- $e_user = pht('Invalid');
- $errors[] = pht('That user does not exist.');
+ if ($is_individual) {
+ $user_phid = head($v_users);
+ if (!$user_phid) {
+ $e_user = pht('Required');
+ $errors[] = pht('You must choose a user to exempt.');
} else {
- $signature = id(new LegalpadDocumentSignatureQuery())
+ $user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
- ->withDocumentPHIDs(array($document->getPHID()))
- ->withSignerPHIDs(array($user->getPHID()))
+ ->withPHIDs(array($user_phid))
->executeOne();
- if ($signature) {
- $e_user = pht('Signed');
- $errors[] = pht('That user has already signed this document.');
+
+ if (!$user) {
+ $e_user = pht('Invalid');
+ $errors[] = pht('That user does not exist.');
} else {
- $e_user = null;
+ $signature = id(new LegalpadDocumentSignatureQuery())
+ ->setViewer($viewer)
+ ->withDocumentPHIDs(array($document->getPHID()))
+ ->withSignerPHIDs(array($user->getPHID()))
+ ->executeOne();
+ if ($signature) {
+ $e_user = pht('Signed');
+ $errors[] = pht('That user has already signed this document.');
+ } else {
+ $e_user = null;
+ }
}
}
+ } else {
+ $company_name = $v_name;
+ if (!strlen($company_name)) {
+ $e_name = pht('Required');
+ $errors[] = pht('You must choose a company to add an exemption for.');
+ }
}
if (!$errors) {
- $name = $user->getRealName();
- $email = $user->loadPrimaryEmailAddress();
+ if ($is_individual) {
+ $name = $user->getRealName();
+ $email = $user->loadPrimaryEmailAddress();
+ $signer_phid = $user->getPHID();
+ $signature_data = array(
+ 'name' => $name,
+ 'email' => $email,
+ 'notes' => $v_notes,
+ );
+ } else {
+ $name = $company_name;
+ $email = '';
+ $signer_phid = null;
+ $signature_data = array(
+ 'name' => $name,
+ 'email' => null,
+ 'notes' => $v_notes,
+ 'actorPHID' => $viewer->getPHID(),
+ );
+ }
$signature = id(new LegalpadDocumentSignature())
->setDocumentPHID($document->getPHID())
->setDocumentVersion($document->getVersions())
- ->setSignerPHID($user->getPHID())
+ ->setSignerPHID($signer_phid)
->setSignerName($name)
->setSignerEmail($email)
+ ->setSignatureType($document->getSignatureType())
->setIsExemption(1)
->setExemptionPHID($viewer->getPHID())
->setVerified(LegalpadDocumentSignature::VERIFIED)
- ->setSignatureData(
- array(
- 'name' => $name,
- 'email' => $email,
- 'notes' => $v_notes,
- ));
+ ->setSignatureData($signature_data);
$signature->save();
@@ -91,18 +119,31 @@
}
}
- $user_handles = $this->loadViewerHandles($v_users);
-
$form = id(new AphrontFormView())
- ->setUser($viewer)
- ->appendChild(
- id(new AphrontFormTokenizerControl())
- ->setLabel(pht('Exempt User'))
- ->setName('users')
- ->setLimit(1)
- ->setDatasource('/typeahead/common/users/')
- ->setValue($user_handles)
- ->setError($e_user))
+ ->setUser($viewer);
+
+ if ($is_individual) {
+ $user_handles = $this->loadViewerHandles($v_users);
+ $form
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setLabel(pht('Exempt User'))
+ ->setName('users')
+ ->setLimit(1)
+ ->setDatasource('/typeahead/common/users/')
+ ->setValue($user_handles)
+ ->setError($e_user));
+ } else {
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Company Name'))
+ ->setName('name')
+ ->setError($e_name)
+ ->setValue($v_name));
+ }
+
+ $form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('Notes'))
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php
@@ -1,93 +1,99 @@
<?php
final class LegalpadDocumentSignatureVerificationController
-extends LegalpadController {
+ extends LegalpadController {
private $code;
- public function willProcessRequest(array $data) {
- $this->code = $data['code'];
- }
-
- public function shouldRequireEmailVerification() {
- return false;
+ public function shouldAllowPublic() {
+ return true;
}
- public function shouldRequireLogin() {
- return false;
+ public function willProcessRequest(array $data) {
+ $this->code = $data['code'];
}
public function processRequest() {
$request = $this->getRequest();
- $user = $request->getUser();
-
- // this page can be accessed by not logged in users to valid their
- // signatures. use the omnipotent user for these cases.
- if (!$user->isLoggedIn()) {
- $viewer = PhabricatorUser::getOmnipotentUser();
- } else {
- $viewer = $user;
- }
+ $viewer = $request->getUser();
+ // NOTE: We're using the omnipotent user to handle logged-out signatures
+ // and corporate signatures.
$signature = id(new LegalpadDocumentSignatureQuery())
- ->setViewer($viewer)
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
->withSecretKeys(array($this->code))
->executeOne();
if (!$signature) {
- $title = pht('Unable to Verify Signature');
- $content = pht(
- 'The verification code you provided is incorrect or the signature '.
- 'has been removed. '.
- 'Make sure you followed the link in the email correctly.');
- $uri = $this->getApplicationURI();
- $continue = pht('Rats!');
- } else {
- $document = id(new LegalpadDocumentQuery())
- ->setViewer($user)
- ->withPHIDs(array($signature->getDocumentPHID()))
- ->executeOne();
- // the document could be deleted or have its permissions changed
- // 4oh4 time
- if (!$document) {
- return new Aphront404Response();
- }
- $uri = '/'.$document->getMonogram();
- if ($signature->isVerified()) {
- $title = pht('Signature Already Verified');
- $content = pht(
- 'This signature has already been verified.');
- $continue = pht('Continue to Legalpad Document');
- } else {
- $guard = AphrontWriteGuard::beginScopedUnguardedWrites();
- $signature
- ->setVerified(LegalpadDocumentSignature::VERIFIED)
- ->save();
- unset($guard);
- $title = pht('Signature Verified');
- $content = pht('The signature is now verified.');
- $continue = pht('Continue to Legalpad Document');
- }
+ return $this->newDialog()
+ ->setTitle(pht('Unable to Verify Signature'))
+ ->appendParagraph(
+ pht(
+ 'The signature verification code is incorrect, or the signature '.
+ 'has been invalidated. Make sure you followed the link in the '.
+ 'email correctly.'))
+ ->addCancelButton('/', pht('Rats!'));
}
- $dialog = id(new AphrontDialogView())
- ->setUser($user)
- ->setTitle($title)
- ->setMethod('GET')
- ->addCancelButton($uri, $continue)
- ->appendChild($content);
+ if ($signature->isVerified()) {
+ return $this->newDialog()
+ ->setTitle(pht('Signature Already Verified'))
+ ->appendParagraph(
+ pht(
+ 'This signature has already been verified.'))
+ ->addCancelButton('/', pht('Okay'));
+ }
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Verify Signature'));
+ if ($request->isFormPost()) {
+ $signature
+ ->setVerified(LegalpadDocumentSignature::VERIFIED)
+ ->save();
- return $this->buildApplicationPage(
+ return $this->newDialog()
+ ->setTitle(pht('Signature Verified'))
+ ->appendParagraph(pht('The signature is now verified.'))
+ ->addCancelButton('/', pht('Okay'));
+ }
+
+ $document_link = phutil_tag(
+ 'a',
array(
- $crumbs,
- $dialog,
+ 'href' => '/'.$signature->getDocument()->getMonogram(),
+ 'target' => '_blank',
),
- array(
- 'title' => pht('Verify Signature'),
- ));
+ $signature->getDocument()->getTitle());
+
+ $signed_at = phabricator_datetime($signature->getDateCreated(), $viewer);
+
+ $name = $signature->getSignerName();
+ $email = $signature->getSignerEmail();
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendRemarkupInstructions(
+ pht('Please verify this document signature.'))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Document'))
+ ->setValue($document_link))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Signed At'))
+ ->setValue($signed_at))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Name'))
+ ->setValue($name))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Email'))
+ ->setValue($email));
+
+ return $this->newDialog()
+ ->setTitle(pht('Verify Signature?'))
+ ->appendChild($form->buildLayoutView())
+ ->addCancelButton('/')
+ ->addSubmitButton(pht('Verify Signature'));
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
@@ -44,22 +44,63 @@
$document_id = $signature->getDocument()->getID();
$next_uri = $this->getApplicationURI('signatures/'.$document_id.'/');
+ $data = $signature->getSignatureData();
+
$exemption_phid = $signature->getExemptionPHID();
- $handles = $this->loadViewerHandles(array($exemption_phid));
+ $actor_phid = idx($data, 'actorPHID');
+ $handles = $this->loadViewerHandles(
+ array(
+ $exemption_phid,
+ $actor_phid,
+ ));
$exemptor_handle = $handles[$exemption_phid];
-
- $data = $signature->getSignatureData();
+ $actor_handle = $handles[$actor_phid];
$form = id(new AphrontFormView())
- ->setUser($viewer)
- ->appendChild(
- id(new AphrontFormMarkupControl())
- ->setLabel(pht('Exemption By'))
- ->setValue($exemptor_handle->renderLink()))
- ->appendChild(
- id(new AphrontFormMarkupControl())
- ->setLabel(pht('Notes'))
- ->setValue(idx($data, 'notes')));
+ ->setUser($viewer);
+
+ if ($signature->getExemptionPHID()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Exemption By'))
+ ->setValue($exemptor_handle->renderLink()))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Notes'))
+ ->setValue(idx($data, 'notes')));
+ }
+
+ $type_corporation = LegalpadDocument::SIGNATURE_TYPE_CORPORATION;
+ if ($signature->getSignatureType() == $type_corporation) {
+ $form
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Signing User'))
+ ->setValue($actor_handle->renderLink()))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Company Name'))
+ ->setValue(idx($data, 'name')))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Address'))
+ ->setValue(phutil_escape_html_newlines(idx($data, 'address'))))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Contact Name'))
+ ->setValue(idx($data, 'contact.name')))
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Contact Email'))
+ ->setValue(
+ phutil_tag(
+ 'a',
+ array(
+ 'href' => 'mailto:'.idx($data, 'email'),
+ ),
+ idx($data, 'email'))));
+ }
return $this->newDialog()
->setTitle(pht('Signature Details'))
diff --git a/src/applications/legalpad/editor/LegalpadDocumentEditor.php b/src/applications/legalpad/editor/LegalpadDocumentEditor.php
--- a/src/applications/legalpad/editor/LegalpadDocumentEditor.php
+++ b/src/applications/legalpad/editor/LegalpadDocumentEditor.php
@@ -11,6 +11,7 @@
private function setIsContribution($is_contribution) {
$this->isContribution = $is_contribution;
}
+
private function isContribution() {
return $this->isContribution;
}
@@ -24,6 +25,8 @@
$types[] = LegalpadTransactionType::TYPE_TITLE;
$types[] = LegalpadTransactionType::TYPE_TEXT;
+ $types[] = LegalpadTransactionType::TYPE_SIGNATURE_TYPE;
+
return $types;
}
@@ -36,6 +39,8 @@
return $object->getDocumentBody()->getTitle();
case LegalpadTransactionType::TYPE_TEXT:
return $object->getDocumentBody()->getText();
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
+ return $object->getSignatureType();
}
}
@@ -46,6 +51,7 @@
switch ($xaction->getTransactionType()) {
case LegalpadTransactionType::TYPE_TITLE:
case LegalpadTransactionType::TYPE_TEXT:
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
return $xaction->getNewValue();
}
}
@@ -66,6 +72,9 @@
$body->setText($xaction->getNewValue());
$this->setIsContribution(true);
break;
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
+ $object->setSignatureType($xaction->getNewValue());
+ break;
}
}
@@ -116,6 +125,7 @@
switch ($type) {
case LegalpadTransactionType::TYPE_TITLE:
case LegalpadTransactionType::TYPE_TEXT:
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
return $v;
}
diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
--- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
@@ -169,13 +169,18 @@
$title = $document->getTitle();
+ $type_name = $document->getSignatureTypeName();
+ $type_icon = $document->getSignatureTypeIcon();
+
$item = id(new PHUIObjectItemView())
->setObjectName($document->getMonogram())
->setHeader($title)
->setHref('/'.$document->getMonogram())
->setObject($document)
- ->addIcon('none', pht('Version %d', $document->getVersions()))
- ->addIcon('none', pht('Updated %s', $last_updated));
+ ->addIcon($type_icon, $type_name)
+ ->addIcon(
+ 'fa-pencil grey',
+ pht('Version %d (%s)', $document->getVersions(), $last_updated));
if ($viewer->getPHID()) {
$signature = $document->getUserSignature($viewer->getPHID());
diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
--- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
@@ -168,6 +168,11 @@
null,
pht('Verified, Current'));
+ $sig_corp = $this->renderIcon(
+ 'fa-building-o',
+ null,
+ pht('Verified, Corporate'));
+
$sig_old = $this->renderIcon(
'fa-clock-o',
'orange',
@@ -188,6 +193,8 @@
->addSigil('has-tooltip')
->setMetadata(array('tip' => pht('Unverified Email')));
+ $type_corporate = LegalpadDocument::SIGNATURE_TYPE_CORPORATION;
+
$rows = array();
foreach ($signatures as $signature) {
$name = $signature->getSignerName();
@@ -196,28 +203,36 @@
$document = $signature->getDocument();
if ($signature->getIsExemption()) {
- $signature_href = $this->getApplicationURI(
- 'signature/'.$signature->getID().'/');
-
- $sig_icon = javelin_tag(
- 'a',
- array(
- 'href' => $signature_href,
- 'sigil' => 'workflow',
- ),
- $sig_exemption);
+ $sig_icon = $sig_exemption;
} else if (!$signature->isVerified()) {
$sig_icon = $sig_unverified;
} else if ($signature->getDocumentVersion() != $document->getVersions()) {
$sig_icon = $sig_old;
+ } else if ($signature->getSignatureType() == $type_corporate) {
+ $sig_icon = $sig_corp;
} else {
$sig_icon = $sig_good;
}
+ $signature_href = $this->getApplicationURI(
+ 'signature/'.$signature->getID().'/');
+
+ $sig_icon = javelin_tag(
+ 'a',
+ array(
+ 'href' => $signature_href,
+ 'sigil' => 'workflow',
+ ),
+ $sig_icon);
+
+ $signer_phid = $signature->getSignerPHID();
+
$rows[] = array(
$sig_icon,
$handles[$document->getPHID()]->renderLink(),
- $handles[$signature->getSignerPHID()]->renderLink(),
+ $signer_phid
+ ? $handles[$signer_phid]->renderLink()
+ : null,
$name,
phutil_tag(
'a',
diff --git a/src/applications/legalpad/storage/LegalpadDocument.php b/src/applications/legalpad/storage/LegalpadDocument.php
--- a/src/applications/legalpad/storage/LegalpadDocument.php
+++ b/src/applications/legalpad/storage/LegalpadDocument.php
@@ -16,6 +16,10 @@
protected $viewPolicy;
protected $editPolicy;
protected $mailKey;
+ protected $signatureType;
+
+ const SIGNATURE_TYPE_INDIVIDUAL = 'user';
+ const SIGNATURE_TYPE_CORPORATION = 'corp';
private $documentBody = self::ATTACHABLE;
private $contributors = self::ATTACHABLE;
@@ -37,6 +41,7 @@
->setContributorCount(0)
->setRecentContributorPHIDs(array())
->attachSignatures(array())
+ ->setSignatureType(self::SIGNATURE_TYPE_INDIVIDUAL)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy);
}
@@ -104,6 +109,28 @@
return $this;
}
+ public static function getSignatureTypeMap() {
+ return array(
+ self::SIGNATURE_TYPE_INDIVIDUAL => pht('Individuals'),
+ self::SIGNATURE_TYPE_CORPORATION => pht('Corporations'),
+ );
+ }
+
+ public function getSignatureTypeName() {
+ $type = $this->getSignatureType();
+ return idx(self::getSignatureTypeMap(), $type, $type);
+ }
+
+ public function getSignatureTypeIcon() {
+ $type = $this->getSignatureType();
+ $map = array(
+ self::SIGNATURE_TYPE_INDIVIDUAL => 'fa-user grey',
+ self::SIGNATURE_TYPE_CORPORATION => 'fa-building-o grey',
+ );
+
+ return idx($map, $type, 'fa-user grey');
+ }
+
/* -( PhabricatorSubscribableInterface )----------------------------------- */
diff --git a/src/applications/legalpad/storage/LegalpadDocumentSignature.php b/src/applications/legalpad/storage/LegalpadDocumentSignature.php
--- a/src/applications/legalpad/storage/LegalpadDocumentSignature.php
+++ b/src/applications/legalpad/storage/LegalpadDocumentSignature.php
@@ -9,6 +9,7 @@
protected $documentPHID;
protected $documentVersion;
+ protected $signatureType;
protected $signerPHID;
protected $signerName;
protected $signerEmail;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 15, 5:38 AM (3 w, 1 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/2t/sd/x3iyfh7353jkmbua
Default Alt Text
D9812.diff (48 KB)
Attached To
Mode
D9812: Allow Legalpad documents to designate corporate signers
Attached
Detach File
Event Timeline
Log In to Comment