Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20140703.legalcorp.1.sql b/resources/sql/autopatches/20140703.legalcorp.1.sql
new file mode 100644
index 0000000000..abc86c7768
--- /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
index 0000000000..82fe9c8eac
--- /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
index 0000000000..be18b7f3ec
--- /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
index 0000000000..c05f000ee6
--- /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
index 0000000000..b1e9ae76f1
--- /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
index f49287cc31..92452ba0e1 100644
--- 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
index 07ebe42980..5482f1b9cb 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentEditController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentEditController.php
@@ -1,193 +1,214 @@
<?php
-/**
- * @group legalpad
- */
final class LegalpadDocumentEditController extends LegalpadController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if (!$this->id) {
$is_create = true;
$this->requireApplicationCapability(
LegalpadCapabilityCreateDocuments::CAPABILITY);
$document = LegalpadDocument::initializeNewDocument($user);
$body = id(new LegalpadDocumentBody())
->setCreatorPHID($user->getPHID());
$document->attachDocumentBody($body);
$document->setDocumentBodyPHID(PhabricatorPHIDConstants::PHID_VOID);
- $title = null;
- $text = null;
} else {
$is_create = false;
$document = id(new LegalpadDocumentQuery())
->setViewer($user)
->needDocumentBodies(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->id))
->executeOne();
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;
if ($request->isFormPost()) {
$xactions = array();
$title = $request->getStr('title');
if (!strlen($title)) {
$e_title = pht('Required');
$errors[] = pht('The document title may not be blank.');
} else {
$xactions[] = id(new LegalpadTransaction())
->setTransactionType(LegalpadTransactionType::TYPE_TITLE)
->setNewValue($title);
}
$text = $request->getStr('text');
if (!strlen($text)) {
$e_text = pht('Required');
$errors[] = pht('The document may not be blank.');
} else {
$xactions[] = id(new LegalpadTransaction())
->setTransactionType(LegalpadTransactionType::TYPE_TEXT)
->setNewValue($text);
}
$can_view = $request->getStr('can_view');
$xactions[] = id(new LegalpadTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($can_view);
$can_edit = $request->getStr('can_edit');
$xactions[] = id(new LegalpadTransaction())
->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)
->setContinueOnNoEffect(true)
->setActor($user);
$xactions = $editor->applyTransactions($document, $xactions);
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI('view/'.$document->getID()));
}
}
if ($errors) {
// set these to what was specified in the form on post
$document->setViewPolicy($can_view);
$document->setEditPolicy($can_edit);
}
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormTextControl())
->setID('document-title')
->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')
->setLabel(pht('Text'))
->setError($e_text)
->setValue($text)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setName('text'));
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($document)
->execute();
$form
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($document)
->setPolicies($policies)
->setName('can_view'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($document)
->setPolicies($policies)
->setName('can_edit'));
$crumbs = $this->buildApplicationCrumbs($this->buildSideNav());
$submit = new AphrontFormSubmitControl();
if ($is_create) {
$submit->setValue(pht('Create Document'));
$submit->addCancelButton($this->getApplicationURI());
$title = pht('Create Document');
$short = pht('Create');
} else {
$submit->setValue(pht('Edit Document'));
$submit->addCancelButton(
$this->getApplicationURI('view/'.$document->getID()));
$title = pht('Edit Document');
$short = pht('Edit');
$crumbs->addTextCrumb(
$document->getMonogram(),
$this->getApplicationURI('view/'.$document->getID()));
}
$form
->appendChild($submit);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
$crumbs->addTextCrumb($short);
$preview = id(new PHUIRemarkupPreviewPanel())
->setHeader(pht('Document Preview'))
->setPreviewURI($this->getApplicationURI('document/preview/'))
->setControlID('document-text')
->setSkin('document');
return $this->buildApplicationPage(
array(
$crumbs,
$form_box,
$preview
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentManageController.php b/src/applications/legalpad/controller/LegalpadDocumentManageController.php
index 4823384059..21ebf1399b 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentManageController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentManageController.php
@@ -1,230 +1,234 @@
<?php
final class LegalpadDocumentManageController extends LegalpadController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
// NOTE: We require CAN_EDIT to view this page.
$document = id(new LegalpadDocumentQuery())
->setViewer($user)
->withIDs(array($this->id))
->needDocumentBodies(true)
->needContributors(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$xactions = id(new LegalpadTransactionQuery())
->setViewer($user)
->withObjectPHIDs(array($document->getPHID()))
->execute();
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$document->getPHID());
$document_body = $document->getDocumentBody();
$phids = array();
$phids[] = $document_body->getCreatorPHID();
foreach ($subscribers as $subscriber) {
$phids[] = $subscriber;
}
foreach ($document->getContributors() as $contributor) {
$phids[] = $contributor;
}
$this->loadHandles($phids);
$engine = id(new PhabricatorMarkupEngine())
->setViewer($user);
$engine->addObject(
$document_body,
LegalpadDocumentBody::MARKUP_FIELD_TEXT);
foreach ($xactions as $xaction) {
if ($xaction->getComment()) {
$engine->addObject(
$xaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
$title = $document_body->getTitle();
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($user)
->setPolicyObject($document);
$actions = $this->buildActionView($document);
$properties = $this->buildPropertyView($document, $engine, $actions);
$comment_form_id = celerity_generate_unique_node_id();
$xaction_view = id(new LegalpadTransactionView())
->setUser($this->getRequest()->getUser())
->setObjectPHID($document->getPHID())
->setTransactions($xactions)
->setMarkupEngine($engine);
$add_comment = $this->buildAddCommentView($document, $comment_form_id);
$crumbs = $this->buildApplicationCrumbs($this->buildSideNav());
$crumbs->setActionList($actions);
$crumbs->addTextCrumb(
$document->getMonogram(),
'/'.$document->getMonogram());
$crumbs->addTextCrumb(pht('Manage'));
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties)
->addPropertyList($this->buildDocument($engine, $document_body));
$content = array(
$crumbs,
$object_box,
$xaction_view,
$add_comment,
);
return $this->buildApplicationPage(
$content,
array(
'title' => $title,
'pageObjects' => array($document->getPHID()),
));
}
private function buildDocument(
PhabricatorMarkupEngine
$engine, LegalpadDocumentBody $body) {
$view = new PHUIPropertyListView();
$view->addClass('legalpad');
$view->addSectionHeader(pht('Document'));
$view->addTextContent(
$engine->getOutput($body, LegalpadDocumentBody::MARKUP_FIELD_TEXT));
return $view;
}
private function buildActionView(LegalpadDocument $document) {
$user = $this->getRequest()->getUser();
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($document);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$document,
PhabricatorPolicyCapability::CAN_EDIT);
$doc_id = $document->getID();
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil-square')
->setName(pht('View/Sign Document'))
->setHref('/'.$document->getMonogram()));
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Document'))
->setHref($this->getApplicationURI('/edit/'.$doc_id.'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-terminal')
->setName(pht('View Signatures'))
->setHref($this->getApplicationURI('/signatures/'.$doc_id.'/')));
return $actions;
}
private function buildPropertyView(
LegalpadDocument $document,
PhabricatorMarkupEngine $engine,
PhabricatorActionListView $actions) {
$user = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($user)
->setObject($document)
->setActionList($actions);
+ $properties->addProperty(
+ pht('Signature Type'),
+ $document->getSignatureTypeName());
+
$properties->addProperty(
pht('Last Updated'),
phabricator_datetime($document->getDateModified(), $user));
$properties->addProperty(
pht('Updated By'),
$this->getHandle(
$document->getDocumentBody()->getCreatorPHID())->renderLink());
$properties->addProperty(
pht('Versions'),
$document->getVersions());
$contributor_view = array();
foreach ($document->getContributors() as $contributor) {
$contributor_view[] = $this->getHandle($contributor)->renderLink();
}
$contributor_view = phutil_implode_html(', ', $contributor_view);
$properties->addProperty(
pht('Contributors'),
$contributor_view);
$properties->invokeWillRenderEvent();
return $properties;
}
private function buildAddCommentView(
LegalpadDocument $document,
$comment_form_id) {
$user = $this->getRequest()->getUser();
$draft = PhabricatorDraft::newFromUserAndKey($user, $document->getPHID());
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$title = $is_serious
? pht('Add Comment')
: pht('Debate Legislation');
$form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setObjectPHID($document->getPHID())
->setFormID($comment_form_id)
->setHeaderText($title)
->setDraft($draft)
->setSubmitButtonName(pht('Add Comment'))
->setAction($this->getApplicationURI('/comment/'.$document->getID().'/'))
->setRequestURI($this->getRequest()->getRequestURI());
return $form;
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php
index e95863f39a..e2522b52e5 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php
@@ -1,375 +1,632 @@
<?php
final class LegalpadDocumentSignController extends LegalpadController {
private $id;
public function shouldRequireLogin() {
return false;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$document = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->needDocumentBodies(true)
->executeOne();
if (!$document) {
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) {
// Require two-factor auth to sign legal documents.
if ($viewer->isLoggedIn()) {
$engine = new PhabricatorAuthSessionEngine();
$engine->requireHighSecuritySession(
$viewer,
$request,
'/'.$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;
}
$signature->setVerified($verified);
if (!$errors) {
$signature->save();
// 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/');
}
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
}
$document_body = $document->getDocumentBody();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer);
$engine->addObject(
$document_body,
LegalpadDocumentBody::MARKUP_FIELD_TEXT);
$engine->process();
$document_markup = $engine->getOutput(
$document_body,
LegalpadDocumentBody::MARKUP_FIELD_TEXT);
$title = $document_body->getTitle();
$manage_uri = $this->getApplicationURI('view/'.$document->getID().'/');
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$document,
PhabricatorPolicyCapability::CAN_EDIT);
$header = id(new PHUIHeaderView())
->setHeader($title)
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-pencil'))
->setText(pht('Manage Document'))
->setHref($manage_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$content = id(new PHUIDocumentView())
->addClass('legalpad')
->setHeader($header)
->setFontKit(PHUIDocumentView::FONT_SOURCE_SANS)
->appendChild(
array(
$signed_status,
$document_markup,
));
if (!$has_signed) {
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setErrors($errors);
}
$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'))
->setBleedHeader(true);
$content->appendChild(
array(
$subheader,
$error_view,
$signature_form,
));
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($document->getMonogram());
return $this->buildApplicationPage(
array(
$crumbs,
$content,
),
array(
'title' => $title,
'pageObjects' => array($document->getPHID()),
));
}
+ 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())
->addRawTos(array($email->getAddress()))
->setSubject(pht('[Legalpad] Signature Verification'))
->setBody($body)
->setRelatedPHID($signature->getDocumentPHID())
->saveAndSend();
}
private function signInResponse() {
return id(new Aphront403Response())
->setForbiddenText(pht(
'The email address specified is associated with an account. '.
'Please login to that account and sign this document again.'));
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
index a9585f4b00..e85e7a70c7 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
@@ -1,127 +1,168 @@
<?php
final class LegalpadDocumentSignatureAddController extends LegalpadController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$document = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->needDocumentBodies(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($this->id))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$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();
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
}
- $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'))
->setName('notes')
->setValue($v_notes));
return $this->newDialog()
->setTitle(pht('Add Signature Exemption'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->setErrors($errors)
->appendParagraph(
pht(
'You can record a signature exemption if a user has signed an '.
'equivalent document. Other applications will behave as through the '.
'user has signed this document.'))
->appendParagraph(null)
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Add Exemption'))
->addCancelButton($next_uri);
}
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php
index 6a61e226ac..7ad5d13946 100644
--- 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
index a21c1ebed4..6d58d1b693 100644
--- a/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
@@ -1,71 +1,112 @@
<?php
final class LegalpadDocumentSignatureViewController extends LegalpadController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$signature = id(new LegalpadDocumentSignatureQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$signature) {
return new Aphront404Response();
}
// NOTE: In order to see signature details (which include the relatively
// internal-feeling "notes" field) you must be able to edit the document.
// Essentially, this power is for document managers. Notably, this prevents
// users from seeing notes about their own exemptions by guessing their
// signature ID. This is purely a policy check.
$document = id(new LegalpadDocumentQuery())
->setViewer($viewer)
->withIDs(array($signature->getDocument()->getID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$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'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild($form->buildLayoutView())
->addCancelButton($next_uri, pht('Close'));
}
}
diff --git a/src/applications/legalpad/editor/LegalpadDocumentEditor.php b/src/applications/legalpad/editor/LegalpadDocumentEditor.php
index f24926c086..98205c46b9 100644
--- a/src/applications/legalpad/editor/LegalpadDocumentEditor.php
+++ b/src/applications/legalpad/editor/LegalpadDocumentEditor.php
@@ -1,196 +1,206 @@
<?php
/**
* @group legalpad
*/
final class LegalpadDocumentEditor
extends PhabricatorApplicationTransactionEditor {
private $isContribution = false;
private function setIsContribution($is_contribution) {
$this->isContribution = $is_contribution;
}
+
private function isContribution() {
return $this->isContribution;
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
$types[] = LegalpadTransactionType::TYPE_TITLE;
$types[] = LegalpadTransactionType::TYPE_TEXT;
+ $types[] = LegalpadTransactionType::TYPE_SIGNATURE_TYPE;
+
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case LegalpadTransactionType::TYPE_TITLE:
return $object->getDocumentBody()->getTitle();
case LegalpadTransactionType::TYPE_TEXT:
return $object->getDocumentBody()->getText();
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
+ return $object->getSignatureType();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case LegalpadTransactionType::TYPE_TITLE:
case LegalpadTransactionType::TYPE_TEXT:
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
return $xaction->getNewValue();
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case LegalpadTransactionType::TYPE_TITLE:
$object->setTitle($xaction->getNewValue());
$body = $object->getDocumentBody();
$body->setTitle($xaction->getNewValue());
$this->setIsContribution(true);
break;
case LegalpadTransactionType::TYPE_TEXT:
$body = $object->getDocumentBody();
$body->setText($xaction->getNewValue());
$this->setIsContribution(true);
break;
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
+ $object->setSignatureType($xaction->getNewValue());
+ break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
return;
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
if ($this->isContribution()) {
$object->setVersions($object->getVersions() + 1);
$body = $object->getDocumentBody();
$body->setVersion($object->getVersions());
$body->setDocumentPHID($object->getPHID());
$body->save();
$object->setDocumentBodyPHID($body->getPHID());
$actor = $this->getActor();
$type = PhabricatorEdgeConfig::TYPE_CONTRIBUTED_TO_OBJECT;
id(new PhabricatorEdgeEditor())
->addEdge($actor->getPHID(), $type, $object->getPHID())
->setActor($actor)
->save();
$type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_CONTRIBUTOR;
$contributors = PhabricatorEdgeQuery::loadDestinationPHIDs(
$object->getPHID(),
$type);
$object->setRecentContributorPHIDs(array_slice($contributors, 0, 3));
$object->setContributorCount(count($contributors));
$object->save();
}
return $xactions;
}
protected function mergeTransactions(
PhabricatorApplicationTransaction $u,
PhabricatorApplicationTransaction $v) {
$type = $u->getTransactionType();
switch ($type) {
case LegalpadTransactionType::TYPE_TITLE:
case LegalpadTransactionType::TYPE_TEXT:
+ case LegalpadTransactionType::TYPE_SIGNATURE_TYPE:
return $v;
}
return parent::mergeTransactions($u, $v);
}
/* -( Sending Mail )------------------------------------------------------- */
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new LegalpadReplyHandler())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$phid = $object->getPHID();
$title = $object->getDocumentBody()->getTitle();
return id(new PhabricatorMetaMTAMail())
->setSubject("L{$id}: {$title}")
->addHeader('Thread-Topic', "L{$id}: {$phid}");
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$object->getCreatorPHID(),
$this->requireActor()->getPHID(),
);
}
protected function shouldImplyCC(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case LegalpadTransactionType::TYPE_TEXT:
case LegalpadTransactionType::TYPE_TITLE:
return true;
}
return parent::shouldImplyCC($object, $xaction);
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$body->addTextSection(
pht('DOCUMENT DETAIL'),
PhabricatorEnv::getProductionURI('/legalpad/view/'.$object->getID().'/'));
return $body;
}
protected function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.legalpad.subject-prefix');
}
protected function shouldPublishFeedStory(
PhabricatorLiskDAO $object,
array $xactions) {
return false;
}
protected function supportsSearch() {
return false;
}
}
diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
index 7d2fe8d96d..9480273c7b 100644
--- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php
@@ -1,210 +1,215 @@
<?php
final class LegalpadDocumentSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Legalpad Documents');
}
public function getApplicationClassName() {
return 'PhabricatorApplicationLegalpad';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'creatorPHIDs',
$this->readUsersFromRequest($request, 'creators'));
$saved->setParameter(
'contributorPHIDs',
$this->readUsersFromRequest($request, 'contributors'));
$saved->setParameter(
'withViewerSignature',
$request->getBool('withViewerSignature'));
$saved->setParameter('createdStart', $request->getStr('createdStart'));
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new LegalpadDocumentQuery())
->needViewerSignatures(true);
$creator_phids = $saved->getParameter('creatorPHIDs', array());
if ($creator_phids) {
$query->withCreatorPHIDs($creator_phids);
}
$contributor_phids = $saved->getParameter('contributorPHIDs', array());
if ($contributor_phids) {
$query->withContributorPHIDs($contributor_phids);
}
if ($saved->getParameter('withViewerSignature')) {
$viewer_phid = $this->requireViewer()->getPHID();
if ($viewer_phid) {
$query->withSignerPHIDs(array($viewer_phid));
}
}
$start = $this->parseDateTime($saved->getParameter('createdStart'));
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
if ($start) {
$query->withDateCreatedAfter($start);
}
if ($end) {
$query->withDateCreatedBefore($end);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$creator_phids = $saved_query->getParameter('creatorPHIDs', array());
$contributor_phids = $saved_query->getParameter(
'contributorPHIDs', array());
$phids = array_merge($creator_phids, $contributor_phids);
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->requireViewer())
->withPHIDs($phids)
->execute();
$viewer_signature = $saved_query->getParameter('withViewerSignature');
if (!$this->requireViewer()->getPHID()) {
$viewer_signature = false;
}
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'withViewerSignature',
1,
pht('Show only documents I have signed.'),
$viewer_signature)
->setDisabled(!$this->requireViewer()->getPHID()))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setName('creators')
->setLabel(pht('Creators'))
->setValue(array_select_keys($handles, $creator_phids)))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setName('contributors')
->setLabel(pht('Contributors'))
->setValue(array_select_keys($handles, $contributor_phids)));
$this->buildDateRange(
$form,
$saved_query,
'createdStart',
pht('Created After'),
'createdEnd',
pht('Created Before'));
}
protected function getURI($path) {
return '/legalpad/'.$path;
}
public function getBuiltinQueryNames() {
$names = array();
if ($this->requireViewer()->isLoggedIn()) {
$names['signed'] = pht('Signed Documents');
}
$names['all'] = pht('All Documents');
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'signed':
return $query
->setParameter('withViewerSignature', true);
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $documents,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList(
array $documents,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($documents, 'LegalpadDocument');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($documents as $document) {
$last_updated = phabricator_date($document->getDateModified(), $viewer);
$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());
} else {
$signature = null;
}
if ($signature) {
$item->addAttribute(
array(
id(new PHUIIconView())->setIconFont('fa-check-square-o', 'green'),
' ',
pht(
'Signed on %s',
phabricator_date($signature->getDateCreated(), $viewer)),
));
} else {
$item->addAttribute(
array(
id(new PHUIIconView())->setIconFont('fa-square-o', 'grey'),
' ',
pht('Not Signed'),
));
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
index 7a897e7908..2883c6bbef 100644
--- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
@@ -1,312 +1,327 @@
<?php
final class LegalpadDocumentSignatureSearchEngine
extends PhabricatorApplicationSearchEngine {
private $document;
public function getResultTypeDescription() {
return pht('Legalpad Signatures');
}
public function getApplicationClassName() {
return 'PhabricatorApplicationLegalpad';
}
public function setDocument(LegalpadDocument $document) {
$this->document = $document;
return $this;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'signerPHIDs',
$this->readUsersFromRequest($request, 'signers'));
$saved->setParameter(
'documentPHIDs',
$this->readPHIDsFromRequest(
$request,
'documents',
array(
PhabricatorLegalpadPHIDTypeDocument::TYPECONST,
)));
$saved->setParameter('nameContains', $request->getStr('nameContains'));
$saved->setParameter('emailContains', $request->getStr('emailContains'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new LegalpadDocumentSignatureQuery());
$signer_phids = $saved->getParameter('signerPHIDs', array());
if ($signer_phids) {
$query->withSignerPHIDs($signer_phids);
}
if ($this->document) {
$query->withDocumentPHIDs(array($this->document->getPHID()));
} else {
$document_phids = $saved->getParameter('documentPHIDs', array());
if ($document_phids) {
$query->withDocumentPHIDs($document_phids);
}
}
$name_contains = $saved->getParameter('nameContains');
if (strlen($name_contains)) {
$query->withNameContains($name_contains);
}
$email_contains = $saved->getParameter('emailContains');
if (strlen($email_contains)) {
$query->withEmailContains($email_contains);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$document_phids = $saved_query->getParameter('documentPHIDs', array());
$signer_phids = $saved_query->getParameter('signerPHIDs', array());
$phids = array_merge($document_phids, $signer_phids);
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->requireViewer())
->withPHIDs($phids)
->execute();
if (!$this->document) {
$form
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/legalpaddocuments/')
->setName('documents')
->setLabel(pht('Documents'))
->setValue(array_select_keys($handles, $document_phids)));
}
$name_contains = $saved_query->getParameter('nameContains', '');
$email_contains = $saved_query->getParameter('emailContains', '');
$form
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setName('signers')
->setLabel(pht('Signers'))
->setValue(array_select_keys($handles, $signer_phids)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name Contains'))
->setName('nameContains')
->setValue($name_contains))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email Contains'))
->setName('emailContains')
->setValue($email_contains));
}
protected function getURI($path) {
if ($this->document) {
return '/legalpad/signatures/'.$this->document->getID().'/'.$path;
} else {
return '/legalpad/signatures/'.$path;
}
}
public function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Signatures'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $signatures,
PhabricatorSavedQuery $query) {
return array_merge(
mpull($signatures, 'getSignerPHID'),
mpull($signatures, 'getDocumentPHID'));
}
protected function renderResultList(
array $signatures,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($signatures, 'LegalpadDocumentSignature');
$viewer = $this->requireViewer();
Javelin::initBehavior('phabricator-tooltips');
$sig_good = $this->renderIcon(
'fa-check',
null,
pht('Verified, Current'));
+ $sig_corp = $this->renderIcon(
+ 'fa-building-o',
+ null,
+ pht('Verified, Corporate'));
+
$sig_old = $this->renderIcon(
'fa-clock-o',
'orange',
pht('Signed Older Version'));
$sig_unverified = $this->renderIcon(
'fa-envelope',
'red',
pht('Unverified Email'));
$sig_exemption = $this->renderIcon(
'fa-asterisk',
'indigo',
pht('Exemption'));
id(new PHUIIconView())
->setIconFont('fa-envelope', 'red')
->addSigil('has-tooltip')
->setMetadata(array('tip' => pht('Unverified Email')));
+ $type_corporate = LegalpadDocument::SIGNATURE_TYPE_CORPORATION;
+
$rows = array();
foreach ($signatures as $signature) {
$name = $signature->getSignerName();
$email = $signature->getSignerEmail();
$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',
array(
'href' => 'mailto:'.$email,
),
$email),
phabricator_datetime($signature->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No signatures match the query.'))
->setHeaders(
array(
'',
pht('Document'),
pht('Account'),
pht('Name'),
pht('Email'),
pht('Signed'),
))
->setColumnVisibility(
array(
true,
// Only show the "Document" column if we aren't scoped to a
// particular document.
!$this->document,
))
->setColumnClasses(
array(
'',
'',
'',
'',
'wide',
'right',
));
$header = id(new PHUIHeaderView())
->setHeader(pht('Signatures'));
if ($this->document) {
$document_id = $this->document->getID();
$header->addActionLink(
id(new PHUIButtonView())
->setText(pht('Add Signature Exemption'))
->setTag('a')
->setHref($this->getApplicationURI('addsignature/'.$document_id.'/'))
->setWorkflow(true)
->setIcon(id(new PHUIIconView())->setIconFont('fa-pencil')));
}
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
if (!$this->document) {
$policy_notice = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setErrors(
array(
pht(
'NOTE: You can only see your own signatures and signatures on '.
'documents you have permission to edit.'),
));
$box->setErrorView($policy_notice);
}
return $box;
}
private function renderIcon($icon, $color, $title) {
Javelin::initBehavior('phabricator-tooltips');
return array(
id(new PHUIIconView())
->setIconFont($icon, $color)
->addSigil('has-tooltip')
->setMetadata(array('tip' => $title)),
javelin_tag(
'span',
array(
'aural' => true,
),
$title),
);
}
}
diff --git a/src/applications/legalpad/storage/LegalpadDocument.php b/src/applications/legalpad/storage/LegalpadDocument.php
index a29047c4e8..7cc4864438 100644
--- a/src/applications/legalpad/storage/LegalpadDocument.php
+++ b/src/applications/legalpad/storage/LegalpadDocument.php
@@ -1,201 +1,228 @@
<?php
final class LegalpadDocument extends LegalpadDAO
implements
PhabricatorPolicyInterface,
PhabricatorSubscribableInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorDestructableInterface {
protected $title;
protected $contributorCount;
protected $recentContributorPHIDs = array();
protected $creatorPHID;
protected $versions;
protected $documentBodyPHID;
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;
private $signatures = self::ATTACHABLE;
private $userSignatures = array();
public static function initializeNewDocument(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorApplicationLegalpad'))
->executeOne();
$view_policy = $app->getPolicy(LegalpadCapabilityDefaultView::CAPABILITY);
$edit_policy = $app->getPolicy(LegalpadCapabilityDefaultEdit::CAPABILITY);
return id(new LegalpadDocument())
->setVersions(0)
->setCreatorPHID($actor->getPHID())
->setContributorCount(0)
->setRecentContributorPHIDs(array())
->attachSignatures(array())
+ ->setSignatureType(self::SIGNATURE_TYPE_INDIVIDUAL)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy);
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'recentContributorPHIDs' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorLegalpadPHIDTypeDocument::TYPECONST);
}
public function getDocumentBody() {
return $this->assertAttached($this->documentBody);
}
public function attachDocumentBody(LegalpadDocumentBody $body) {
$this->documentBody = $body;
return $this;
}
public function getContributors() {
return $this->assertAttached($this->contributors);
}
public function attachContributors(array $contributors) {
$this->contributors = $contributors;
return $this;
}
public function getSignatures() {
return $this->assertAttached($this->signatures);
}
public function attachSignatures(array $signatures) {
$this->signatures = $signatures;
return $this;
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function getMonogram() {
return 'L'.$this->getID();
}
public function getUserSignature($phid) {
return $this->assertAttachedKey($this->userSignatures, $phid);
}
public function attachUserSignature(
$user_phid,
LegalpadDocumentSignature $signature = null) {
$this->userSignatures[$user_phid] = $signature;
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 )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return ($this->creatorPHID == $phid);
}
public function shouldShowSubscribersProperty() {
return true;
}
public function shouldAllowSubscription($phid) {
return true;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
$policy = $this->viewPolicy;
break;
case PhabricatorPolicyCapability::CAN_EDIT:
$policy = $this->editPolicy;
break;
default:
$policy = PhabricatorPolicies::POLICY_NOONE;
break;
}
return $policy;
}
public function hasAutomaticCapability($capability, PhabricatorUser $user) {
return ($user->getPHID() == $this->getCreatorPHID());
}
public function describeAutomaticCapability($capability) {
return pht(
'The author of a document can always view and edit it.');
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new LegalpadDocumentEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new LegalpadTransaction();
}
/* -( PhabricatorDestructableInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$bodies = id(new LegalpadDocumentBody())->loadAllWhere(
'documentPHID = %s',
$this->getPHID());
foreach ($bodies as $body) {
$body->delete();
}
$signatures = id(new LegalpadDocumentSignature())->loadAllWhere(
'documentPHID = %s',
$this->getPHID());
foreach ($signatures as $signature) {
$signature->delete();
}
$this->saveTransaction();
}
}
diff --git a/src/applications/legalpad/storage/LegalpadDocumentSignature.php b/src/applications/legalpad/storage/LegalpadDocumentSignature.php
index 50dfa4cdc7..d56b684eca 100644
--- a/src/applications/legalpad/storage/LegalpadDocumentSignature.php
+++ b/src/applications/legalpad/storage/LegalpadDocumentSignature.php
@@ -1,77 +1,78 @@
<?php
final class LegalpadDocumentSignature
extends LegalpadDAO
implements PhabricatorPolicyInterface {
const VERIFIED = 0;
const UNVERIFIED = 1;
protected $documentPHID;
protected $documentVersion;
+ protected $signatureType;
protected $signerPHID;
protected $signerName;
protected $signerEmail;
protected $signatureData = array();
protected $verified;
protected $isExemption = 0;
protected $exemptionPHID;
protected $secretKey;
private $document = self::ATTACHABLE;
public function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'signatureData' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function save() {
if (!$this->getSecretKey()) {
$this->setSecretKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function isVerified() {
return ($this->getVerified() != self::UNVERIFIED);
}
public function getDocument() {
return $this->assertAttached($this->document);
}
public function attachDocument(LegalpadDocument $document) {
$this->document = $document;
return $this;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getDocument()->getPolicy(
PhabricatorPolicyCapability::CAN_EDIT);
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return ($viewer->getPHID() == $this->getSignerPHID());
}
public function describeAutomaticCapability($capability) {
return null;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jul 28, 6:53 PM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
187373
Default Alt Text
(89 KB)

Event Timeline