1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-27 14:09:11 +01:00

Allow Legalpad document managers to add signature exemptions

Summary:
Ref T5532. Allow document managers to add exemptions, which act like signatures but are tracked a little differently.

The primary use case for us is users who sign a corporate CLA and need a user-level exemption if they don't want to sign an individual CLA.

Test Plan: See screenshots.

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: epriestley

Maniphest Tasks: T5532

Differential Revision: https://secure.phabricator.com/D9795
This commit is contained in:
epriestley 2014-07-02 04:59:35 -07:00
parent 20446252ff
commit b6ea2735d7
10 changed files with 284 additions and 9 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
ADD isExemption BOOL NOT NULL DEFAULT 0 AFTER verified;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
ADD exemptionPHID VARCHAR(64) COLLATE utf8_bin AFTER isExemption;

View file

@ -874,10 +874,12 @@ phutil_register_library_map(array(
'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php', 'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php',
'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php', 'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php',
'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php', 'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php',
'LegalpadDocumentSignatureAddController' => 'applications/legalpad/controller/LegalpadDocumentSignatureAddController.php',
'LegalpadDocumentSignatureListController' => 'applications/legalpad/controller/LegalpadDocumentSignatureListController.php', 'LegalpadDocumentSignatureListController' => 'applications/legalpad/controller/LegalpadDocumentSignatureListController.php',
'LegalpadDocumentSignatureQuery' => 'applications/legalpad/query/LegalpadDocumentSignatureQuery.php', 'LegalpadDocumentSignatureQuery' => 'applications/legalpad/query/LegalpadDocumentSignatureQuery.php',
'LegalpadDocumentSignatureSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php', 'LegalpadDocumentSignatureSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php',
'LegalpadDocumentSignatureVerificationController' => 'applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php', 'LegalpadDocumentSignatureVerificationController' => 'applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php',
'LegalpadDocumentSignatureViewController' => 'applications/legalpad/controller/LegalpadDocumentSignatureViewController.php',
'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php', 'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php',
'LegalpadReplyHandler' => 'applications/legalpad/mail/LegalpadReplyHandler.php', 'LegalpadReplyHandler' => 'applications/legalpad/mail/LegalpadReplyHandler.php',
'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php', 'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php',
@ -3639,10 +3641,12 @@ phutil_register_library_map(array(
0 => 'LegalpadDAO', 0 => 'LegalpadDAO',
1 => 'PhabricatorPolicyInterface', 1 => 'PhabricatorPolicyInterface',
), ),
'LegalpadDocumentSignatureAddController' => 'LegalpadController',
'LegalpadDocumentSignatureListController' => 'LegalpadController', 'LegalpadDocumentSignatureListController' => 'LegalpadController',
'LegalpadDocumentSignatureQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'LegalpadDocumentSignatureQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'LegalpadDocumentSignatureSearchEngine' => 'PhabricatorApplicationSearchEngine', 'LegalpadDocumentSignatureSearchEngine' => 'PhabricatorApplicationSearchEngine',
'LegalpadDocumentSignatureVerificationController' => 'LegalpadController', 'LegalpadDocumentSignatureVerificationController' => 'LegalpadController',
'LegalpadDocumentSignatureViewController' => 'LegalpadController',
'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver', 'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver',
'LegalpadReplyHandler' => 'PhabricatorMailReplyHandler', 'LegalpadReplyHandler' => 'PhabricatorMailReplyHandler',
'LegalpadTransaction' => 'PhabricatorApplicationTransaction', 'LegalpadTransaction' => 'PhabricatorApplicationTransaction',

View file

@ -54,8 +54,11 @@ final class PhabricatorApplicationLegalpad extends PhabricatorApplication {
'LegalpadDocumentSignatureVerificationController', 'LegalpadDocumentSignatureVerificationController',
'signatures/(?:(?P<id>\d+)/)?(?:query/(?P<queryKey>[^/]+)/)?' => 'signatures/(?:(?P<id>\d+)/)?(?:query/(?P<queryKey>[^/]+)/)?' =>
'LegalpadDocumentSignatureListController', 'LegalpadDocumentSignatureListController',
'addsignature/(?P<id>\d+)/' => 'LegalpadDocumentSignatureAddController',
'signature/(?P<id>\d+)/' => 'LegalpadDocumentSignatureViewController',
'document/' => array( 'document/' => array(
'preview/' => 'PhabricatorMarkupPreviewController'), 'preview/' => 'PhabricatorMarkupPreviewController',
),
)); ));
} }

View file

@ -101,14 +101,26 @@ final class LegalpadDocumentSignController extends LegalpadController {
// In this case, we know they've signed. // In this case, we know they've signed.
$signed_at = $signature->getDateCreated(); $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()) $signed_status = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE) ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setErrors( ->setErrors(array($signed_text));
array(
pht(
'You signed this document on %s.',
phabricator_datetime($signed_at, $viewer)),
));
} }
$e_name = true; $e_name = true;

View file

@ -0,0 +1,127 @@
<?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_user = true;
$v_users = array();
$v_notes = '';
$errors = array();
if ($request->isFormPost()) {
$v_notes = $request->getStr('notes');
$v_users = array_slice($request->getArr('users'), 0, 1);
$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.');
} else {
$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;
}
}
}
if (!$errors) {
$name = $user->getRealName();
$email = $user->loadPrimaryEmailAddress();
$signature = id(new LegalpadDocumentSignature())
->setDocumentPHID($document->getPHID())
->setDocumentVersion($document->getVersions())
->setSignerPHID($user->getPHID())
->setSignerName($name)
->setSignerEmail($email)
->setIsExemption(1)
->setExemptionPHID($viewer->getPHID())
->setVerified(LegalpadDocumentSignature::VERIFIED)
->setSignatureData(
array(
'name' => $name,
'email' => $email,
'notes' => $v_notes,
));
$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))
->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);
}
}

View file

@ -0,0 +1,71 @@
<?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.'/');
$exemption_phid = $signature->getExemptionPHID();
$handles = $this->loadViewerHandles(array($exemption_phid));
$exemptor_handle = $handles[$exemption_phid];
$data = $signature->getSignatureData();
$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')));
return $this->newDialog()
->setTitle(pht('Signature Details'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild($form->buildLayoutView())
->addCancelButton($next_uri, pht('Close'));
}
}

View file

@ -178,6 +178,11 @@ final class LegalpadDocumentSignatureSearchEngine
'red', 'red',
pht('Unverified Email')); pht('Unverified Email'));
$sig_exemption = $this->renderIcon(
'fa-asterisk',
'indigo',
pht('Exemption'));
id(new PHUIIconView()) id(new PHUIIconView())
->setIconFont('fa-envelope', 'red') ->setIconFont('fa-envelope', 'red')
->addSigil('has-tooltip') ->addSigil('has-tooltip')
@ -190,7 +195,18 @@ final class LegalpadDocumentSignatureSearchEngine
$document = $signature->getDocument(); $document = $signature->getDocument();
if (!$signature->isVerified()) { if ($signature->getIsExemption()) {
$signature_href = $this->getApplicationURI(
'signature/'.$signature->getID().'/');
$sig_icon = javelin_tag(
'a',
array(
'href' => $signature_href,
'sigil' => 'workflow',
),
$sig_exemption);
} else if (!$signature->isVerified()) {
$sig_icon = $sig_unverified; $sig_icon = $sig_unverified;
} else if ($signature->getDocumentVersion() != $document->getVersions()) { } else if ($signature->getDocumentVersion() != $document->getVersions()) {
$sig_icon = $sig_old; $sig_icon = $sig_old;
@ -242,8 +258,23 @@ final class LegalpadDocumentSignatureSearchEngine
'right', '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()) $box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Signatures')) ->setHeader($header)
->appendChild($table); ->appendChild($table);
if (!$this->document) { if (!$this->document) {

View file

@ -14,6 +14,8 @@ final class LegalpadDocumentSignature
protected $signerEmail; protected $signerEmail;
protected $signatureData = array(); protected $signatureData = array();
protected $verified; protected $verified;
protected $isExemption = 0;
protected $exemptionPHID;
protected $secretKey; protected $secretKey;
private $document = self::ATTACHABLE; private $document = self::ATTACHABLE;

View file

@ -61,6 +61,27 @@ users from seeing content until they sign the document:
Users will now only be able to take the action (for example, view or edit the Users will now only be able to take the action (for example, view or edit the
object) if they have signed the specified documents. object) if they have signed the specified documents.
Adding Exemptions
=================
If you have users who have signed an alternate form of a document (for example,
you have a hard copy on file), or an equivalent document, or who are otherwise
exempt from needing to sign a document in Legalpad, you can add a signature
exemption for them.
Other applications will treat users with a signature exemption as though they
had signed the document, although the UI will show the signature as an exemption
rather than a normal signature.
To add an exemption, go to **Manage Document**, then **View Signatures**, then
**Add Signature Exemption**.
You can optionally add notes about why a user is exempt from signing a document.
To review the notes later (and see who added the exemption), click the colored
asterisk in the list view.
Roadmap Roadmap
======== ========