Page MenuHomePhabricator

D9795.diff
No OneTemporary

D9795.diff

diff --git a/resources/sql/autopatches/20140701.legalexemption.1.sql b/resources/sql/autopatches/20140701.legalexemption.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140701.legalexemption.1.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
+ ADD isExemption BOOL NOT NULL DEFAULT 0 AFTER verified;
diff --git a/resources/sql/autopatches/20140701.legalexemption.2.sql b/resources/sql/autopatches/20140701.legalexemption.2.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140701.legalexemption.2.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
+ ADD exemptionPHID VARCHAR(64) COLLATE utf8_bin AFTER isExemption;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -874,10 +874,12 @@
'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 @@
0 => 'LegalpadDAO',
1 => 'PhabricatorPolicyInterface',
),
+ 'LegalpadDocumentSignatureAddController' => 'LegalpadController',
'LegalpadDocumentSignatureListController' => 'LegalpadController',
'LegalpadDocumentSignatureQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'LegalpadDocumentSignatureSearchEngine' => 'PhabricatorApplicationSearchEngine',
'LegalpadDocumentSignatureVerificationController' => 'LegalpadController',
+ 'LegalpadDocumentSignatureViewController' => 'LegalpadController',
'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver',
'LegalpadReplyHandler' => 'PhabricatorMailReplyHandler',
'LegalpadTransaction' => 'PhabricatorApplicationTransaction',
diff --git a/src/applications/legalpad/application/PhabricatorApplicationLegalpad.php b/src/applications/legalpad/application/PhabricatorApplicationLegalpad.php
--- a/src/applications/legalpad/application/PhabricatorApplicationLegalpad.php
+++ b/src/applications/legalpad/application/PhabricatorApplicationLegalpad.php
@@ -54,8 +54,11 @@
'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',
+ ),
));
}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php
--- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php
@@ -101,14 +101,26 @@
// 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;
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureAddController.php
@@ -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);
+ }
+
+}
diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php b/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/legalpad/controller/LegalpadDocumentSignatureViewController.php
@@ -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'));
+ }
+
+}
diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
--- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
+++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php
@@ -178,6 +178,11 @@
'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 @@
$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 @@
'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) {
diff --git a/src/applications/legalpad/storage/LegalpadDocumentSignature.php b/src/applications/legalpad/storage/LegalpadDocumentSignature.php
--- a/src/applications/legalpad/storage/LegalpadDocumentSignature.php
+++ b/src/applications/legalpad/storage/LegalpadDocumentSignature.php
@@ -14,6 +14,8 @@
protected $signerEmail;
protected $signatureData = array();
protected $verified;
+ protected $isExemption = 0;
+ protected $exemptionPHID;
protected $secretKey;
private $document = self::ATTACHABLE;
diff --git a/src/docs/user/userguide/legalpad.diviner b/src/docs/user/userguide/legalpad.diviner
--- a/src/docs/user/userguide/legalpad.diviner
+++ b/src/docs/user/userguide/legalpad.diviner
@@ -61,6 +61,27 @@
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
========

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 29, 1:48 AM (8 h, 10 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6941477
Default Alt Text
D9795.diff (15 KB)

Event Timeline