1
0
Fork 0
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:
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',
'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',

View file

@ -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',
),
));
}

View file

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

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',
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) {

View file

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

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
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
========