1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42:41 +01:00

Legalpad - add signature page

Summary: Fixes T3481. Sort of - this thing be very ugly. Also it assumes that you'll "always" want to sign terms. I was thinking in a future diff that should be optional as well as configurable, though it was unclear to me if either was worth pursuing...  Generally very hideous as the three main elements (PHUIDocument, AphrontErrorView, and AphrontForm with an AphrontFormInset) have never really played together before.

Test Plan: agreed to some test terms. noted UI displayed nicely. reloaded and noted UI told me I had signed it already. Went to different terms and filled them out wrong and got sensical errors.

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T3481

Differential Revision: https://secure.phabricator.com/D6399
This commit is contained in:
Bob Trahan 2013-07-10 11:46:39 -07:00
parent d467c49a66
commit 4fb3b27f1d
10 changed files with 260 additions and 9 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature
ADD signatureData LONGTEXT NOT NULL COLLATE utf8_bin AFTER signerPHID;

View file

@ -644,6 +644,7 @@ phutil_register_library_map(array(
'LegalpadDocumentPreviewController' => 'applications/legalpad/controller/LegalpadDocumentPreviewController.php', 'LegalpadDocumentPreviewController' => 'applications/legalpad/controller/LegalpadDocumentPreviewController.php',
'LegalpadDocumentQuery' => 'applications/legalpad/query/LegalpadDocumentQuery.php', 'LegalpadDocumentQuery' => 'applications/legalpad/query/LegalpadDocumentQuery.php',
'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php', 'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php',
'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php',
'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php', 'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php',
'LegalpadDocumentViewController' => 'applications/legalpad/controller/LegalpadDocumentViewController.php', 'LegalpadDocumentViewController' => 'applications/legalpad/controller/LegalpadDocumentViewController.php',
'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php', 'LegalpadMockMailReceiver' => 'applications/legalpad/mail/LegalpadMockMailReceiver.php',
@ -2573,6 +2574,7 @@ phutil_register_library_map(array(
'LegalpadDocumentPreviewController' => 'LegalpadController', 'LegalpadDocumentPreviewController' => 'LegalpadController',
'LegalpadDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'LegalpadDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', 'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine',
'LegalpadDocumentSignController' => 'LegalpadController',
'LegalpadDocumentSignature' => 'LegalpadDAO', 'LegalpadDocumentSignature' => 'LegalpadDAO',
'LegalpadDocumentViewController' => 'LegalpadController', 'LegalpadDocumentViewController' => 'LegalpadController',
'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver', 'LegalpadMockMailReceiver' => 'PhabricatorObjectMailReceiver',

View file

@ -40,7 +40,7 @@ final class PhabricatorApplicationLegalpad extends PhabricatorApplication {
public function getRoutes() { public function getRoutes() {
return array( return array(
'/L(?P<id>\d+)/' => 'LegalpadDocumentViewController', '/L(?P<id>\d+)' => 'LegalpadDocumentSignController',
'/legalpad/' => array( '/legalpad/' => array(
'' => 'LegalpadDocumentListController', '' => 'LegalpadDocumentListController',
'(query/(?P<queryKey>[^/]+)/)?' => 'LegalpadDocumentListController', '(query/(?P<queryKey>[^/]+)/)?' => 'LegalpadDocumentListController',

View file

@ -37,8 +37,8 @@ final class LegalpadDocumentListController extends LegalpadController
$list->setUser($user); $list->setUser($user);
foreach ($documents as $document) { foreach ($documents as $document) {
$last_updated = phabricator_date($document->getDateModified(), $user); $last_updated = phabricator_date($document->getDateModified(), $user);
$updater = $this->getHandle( $recent_contributors = $document->getRecentContributorPHIDs();
reset($document->getRecentContributorPHIDs()))->renderLink(); $updater = $this->getHandle(reset($recent_contributors))->renderLink();
$title = $document->getTitle(); $title = $document->getTitle();

View file

@ -0,0 +1,236 @@
<?php
/**
* @group legalpad
*/
final class LegalpadDocumentSignController extends LegalpadController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$document = id(new LegalpadDocumentQuery())
->setViewer($user)
->withIDs(array($this->id))
->needDocumentBodies(true)
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$signature = id(new LegalpadDocumentSignature())
->loadOneWhere(
'documentPHID = %s AND documentVersion = %d AND signerPHID = %s',
$document->getPHID(),
$document->getVersions(),
$user->getPHID());
if (!$signature) {
$has_signed = false;
$error_view = null;
$signature = id(new LegalpadDocumentSignature())
->setSignerPHID($user->getPHID())
->setDocumentPHID($document->getPHID())
->setDocumentVersion($document->getVersions());
$data = array(
'name' => $user->getRealName(),
'email' => $user->loadPrimaryEmailAddress());
$signature->setSignatureData($data);
} else {
$has_signed = true;
$error_view = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle(pht('You have already agreed to these terms.'));
$data = $signature->getSignatureData();
}
$e_name = true;
$e_email = true;
$e_address_1 = true;
$errors = array();
if ($request->isFormPost()) {
$name = $request->getStr('name');
$email = $request->getStr('email');
$address_1 = $request->getStr('address_1');
$address_2 = $request->getStr('address_2');
$phone = $request->getStr('phone');
$agree = $request->getExists('agree');
if (!$name) {
$e_name = pht('Required');
$errors[] = pht('Name field is required.');
}
$data['name'] = $name;
if (!$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.');
}
}
$data['email'] = $email;
if (!$address_1) {
$e_address_1 = pht('Required');
$errors[] = pht('Address line 1 field is required.');
}
$data['address_1'] = $address_1;
$data['address_2'] = $address_2;
$data['phone'] = $phone;
$signature->setSignatureData($data);
if (!$agree) {
$errors[] = pht(
'You must check "I agree to the terms laid forth above."');
}
if (!$errors) {
$signature->save();
$has_signed = true;
$error_view = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle(pht('Signature successful. Thank you.'));
} else {
$error_view = id(new AphrontErrorView())
->setTitle(pht('Error in submission.'))
->setErrors($errors);
}
}
$document_body = $document->getDocumentBody();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($user);
$engine->addObject(
$document_body,
LegalpadDocumentBody::MARKUP_FIELD_TEXT);
$engine->process();
$title = $document_body->getTitle();
$header = id(new PhabricatorHeaderView())
->setHeader($title);
$content = array(
id(new PHUIDocumentView())
->setHeader($header)
->appendChild($this->buildDocument($engine, $document_body)),
$error_view,
$this->buildSignatureForm(
$document_body,
$signature,
$has_signed,
$e_name,
$e_email,
$e_address_1));
return $this->buildApplicationPage(
$content,
array(
'title' => $title,
'device' => true,
'dust' => true,
'pageObjects' => array($document->getPHID()),
));
}
private function buildDocument(
PhabricatorMarkupEngine
$engine, LegalpadDocumentBody $body) {
require_celerity_resource('legalpad-documentbody-css');
return phutil_tag(
'div',
array(
'class' => 'legalpad-documentbody'
),
$engine->getOutput($body, LegalpadDocumentBody::MARKUP_FIELD_TEXT));
}
private function buildSignatureForm(
LegalpadDocumentBody $body,
LegalpadDocumentSignature $signature,
$has_signed = false,
$e_name = true,
$e_email = true,
$e_address_1 = true) {
$user = $this->getRequest()->getUser();
if ($has_signed) {
$instructions = pht('Thank you for signing and agreeing.');
} else {
$instructions = pht('Please enter the following information.');
}
$data = $signature->getSignatureData();
$form = id(new AphrontFormView())
->setUser($user)
->setFlexible(true)
->appendChild(
id(new AphrontFormInsetView())
->setTitle(pht('Sign and Agree'))
->setDescription($instructions)
->setContent(phutil_tag('br', array()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setValue(idx($data, 'name', ''))
->setName('name')
->setError($e_name)
->setDisabled($has_signed))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setValue(idx($data, 'email', ''))
->setName('email')
->setError($e_email)
->setDisabled($has_signed))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Address line 1'))
->setValue(idx($data, 'address_1', ''))
->setName('address_1')
->setError($e_address_1)
->setDisabled($has_signed))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Address line 2'))
->setValue(idx($data, 'address_2', ''))
->setName('address_2')
->setDisabled($has_signed))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Phone'))
->setValue(idx($data, 'phone', ''))
->setName('phone')
->setDisabled($has_signed))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'agree',
'agree',
pht('I agree to the terms laid forth above.'),
$has_signed)
->setDisabled($has_signed))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Sign and Agree'))
->setDisabled($has_signed)));
return $form;
}
}

View file

@ -47,9 +47,6 @@ final class LegalpadDocumentViewController extends LegalpadController {
$engine = id(new PhabricatorMarkupEngine()) $engine = id(new PhabricatorMarkupEngine())
->setViewer($user); ->setViewer($user);
$engine->addObject(
$document_body,
LegalpadDocumentBody::MARKUP_FIELD_TITLE);
$engine->addObject( $engine->addObject(
$document_body, $document_body,
LegalpadDocumentBody::MARKUP_FIELD_TEXT); LegalpadDocumentBody::MARKUP_FIELD_TEXT);

View file

@ -169,7 +169,7 @@ final class LegalpadDocumentEditor
$body->addTextSection( $body->addTextSection(
pht('DOCUMENT DETAIL'), pht('DOCUMENT DETAIL'),
PhabricatorEnv::getProductionURI('/L'.$object->getID())); PhabricatorEnv::getProductionURI('/legalpad/view/'.$object->getID().'/'));
return $body; return $body;
} }

View file

@ -7,7 +7,6 @@ final class LegalpadDocumentBody extends LegalpadDAO
implements implements
PhabricatorMarkupInterface { PhabricatorMarkupInterface {
const MARKUP_FIELD_TITLE = 'markup:title';
const MARKUP_FIELD_TEXT = 'markup:text '; const MARKUP_FIELD_TEXT = 'markup:text ';
protected $phid; protected $phid;

View file

@ -6,7 +6,18 @@
final class LegalpadDocumentSignature extends LegalpadDAO { final class LegalpadDocumentSignature extends LegalpadDAO {
protected $documentPHID; protected $documentPHID;
protected $documentversion; protected $documentVersion;
protected $signerPHID; protected $signerPHID;
protected $signatureData = array();
public function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'signatureData' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
} }

View file

@ -1430,6 +1430,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'php', 'type' => 'php',
'name' => $this->getPatchPath('20130703.legalpaddocdenorm.php'), 'name' => $this->getPatchPath('20130703.legalpaddocdenorm.php'),
), ),
'20130709.legalpadsignature.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130709.legalpadsignature.sql'),
),
'20130709.droptimeline.sql' => array( '20130709.droptimeline.sql' => array(
'type' => 'sql', 'type' => 'sql',
'name' => $this->getPatchPath('20130709.droptimeline.sql'), 'name' => $this->getPatchPath('20130709.droptimeline.sql'),