mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 10:12:41 +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:
parent
20446252ff
commit
b6ea2735d7
10 changed files with 284 additions and 9 deletions
2
resources/sql/autopatches/20140701.legalexemption.1.sql
Normal file
2
resources/sql/autopatches/20140701.legalexemption.1.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
|
||||
ADD isExemption BOOL NOT NULL DEFAULT 0 AFTER verified;
|
2
resources/sql/autopatches/20140701.legalexemption.2.sql
Normal file
2
resources/sql/autopatches/20140701.legalexemption.2.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
|
||||
ADD exemptionPHID VARCHAR(64) COLLATE utf8_bin AFTER isExemption;
|
|
@ -874,10 +874,12 @@ phutil_register_library_map(array(
|
|||
'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php',
|
||||
'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php',
|
||||
'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php',
|
||||
'LegalpadDocumentSignatureAddController' => 'applications/legalpad/controller/LegalpadDocumentSignatureAddController.php',
|
||||
'LegalpadDocumentSignatureListController' => 'applications/legalpad/controller/LegalpadDocumentSignatureListController.php',
|
||||
'LegalpadDocumentSignatureQuery' => 'applications/legalpad/query/LegalpadDocumentSignatureQuery.php',
|
||||
'LegalpadDocumentSignatureSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php',
|
||||
'LegalpadDocumentSignatureVerificationController' => 'applications/legalpad/controller/LegalpadDocumentSignatureVerificationController.php',
|
||||
'LegalpadDocumentSignatureViewController' => 'applications/legalpad/controller/LegalpadDocumentSignatureViewController.php',
|
||||
'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php',
|
||||
'LegalpadReplyHandler' => 'applications/legalpad/mail/LegalpadReplyHandler.php',
|
||||
'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php',
|
||||
|
@ -3639,10 +3641,12 @@ phutil_register_library_map(array(
|
|||
0 => 'LegalpadDAO',
|
||||
1 => 'PhabricatorPolicyInterface',
|
||||
),
|
||||
'LegalpadDocumentSignatureAddController' => 'LegalpadController',
|
||||
'LegalpadDocumentSignatureListController' => 'LegalpadController',
|
||||
'LegalpadDocumentSignatureQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'LegalpadDocumentSignatureSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'LegalpadDocumentSignatureVerificationController' => 'LegalpadController',
|
||||
'LegalpadDocumentSignatureViewController' => 'LegalpadController',
|
||||
'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'LegalpadReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'LegalpadTransaction' => 'PhabricatorApplicationTransaction',
|
||||
|
|
|
@ -54,8 +54,11 @@ final class PhabricatorApplicationLegalpad extends PhabricatorApplication {
|
|||
'LegalpadDocumentSignatureVerificationController',
|
||||
'signatures/(?:(?P<id>\d+)/)?(?:query/(?P<queryKey>[^/]+)/)?' =>
|
||||
'LegalpadDocumentSignatureListController',
|
||||
'addsignature/(?P<id>\d+)/' => 'LegalpadDocumentSignatureAddController',
|
||||
'signature/(?P<id>\d+)/' => 'LegalpadDocumentSignatureViewController',
|
||||
'document/' => array(
|
||||
'preview/' => 'PhabricatorMarkupPreviewController'),
|
||||
'preview/' => 'PhabricatorMarkupPreviewController',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -101,14 +101,26 @@ final class LegalpadDocumentSignController extends LegalpadController {
|
|||
|
||||
// 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(
|
||||
pht(
|
||||
'You signed this document on %s.',
|
||||
phabricator_datetime($signed_at, $viewer)),
|
||||
));
|
||||
->setErrors(array($signed_text));
|
||||
}
|
||||
|
||||
$e_name = true;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
|
||||
}
|
|
@ -178,6 +178,11 @@ final class LegalpadDocumentSignatureSearchEngine
|
|||
'red',
|
||||
pht('Unverified Email'));
|
||||
|
||||
$sig_exemption = $this->renderIcon(
|
||||
'fa-asterisk',
|
||||
'indigo',
|
||||
pht('Exemption'));
|
||||
|
||||
id(new PHUIIconView())
|
||||
->setIconFont('fa-envelope', 'red')
|
||||
->addSigil('has-tooltip')
|
||||
|
@ -190,7 +195,18 @@ final class LegalpadDocumentSignatureSearchEngine
|
|||
|
||||
$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;
|
||||
} else if ($signature->getDocumentVersion() != $document->getVersions()) {
|
||||
$sig_icon = $sig_old;
|
||||
|
@ -242,8 +258,23 @@ final class LegalpadDocumentSignatureSearchEngine
|
|||
'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())
|
||||
->setHeaderText(pht('Signatures'))
|
||||
->setHeader($header)
|
||||
->appendChild($table);
|
||||
|
||||
if (!$this->document) {
|
||||
|
|
|
@ -14,6 +14,8 @@ final class LegalpadDocumentSignature
|
|||
protected $signerEmail;
|
||||
protected $signatureData = array();
|
||||
protected $verified;
|
||||
protected $isExemption = 0;
|
||||
protected $exemptionPHID;
|
||||
protected $secretKey;
|
||||
|
||||
private $document = self::ATTACHABLE;
|
||||
|
|
|
@ -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
|
||||
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
|
||||
========
|
||||
|
||||
|
|
Loading…
Reference in a new issue