public function handleRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     // If the user already has a full session, just kick them out of here.
     $has_partial_session = $viewer->hasSession() && $viewer->getSession()->getIsPartial();
     if (!$has_partial_session) {
         return id(new AphrontRedirectResponse())->setURI('/');
     }
     $engine = new PhabricatorAuthSessionEngine();
     // If this cookie is set, the user is headed into a high security area
     // after login (normally because of a password reset) so if they are
     // able to pass the checkpoint we just want to put their account directly
     // into high security mode, rather than prompt them again for the same
     // set of credentials.
     $jump_into_hisec = $request->getCookie(PhabricatorCookies::COOKIE_HISEC);
     try {
         $token = $engine->requireHighSecuritySession($viewer, $request, '/logout/', $jump_into_hisec);
     } catch (PhabricatorAuthHighSecurityRequiredException $ex) {
         $form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm($ex->getFactors(), $ex->getFactorValidationResults(), $viewer, $request);
         return $this->newDialog()->setTitle(pht('Provide Multi-Factor Credentials'))->setShortTitle(pht('Multi-Factor Login'))->setWidth(AphrontDialogView::WIDTH_FORM)->addHiddenInput(AphrontRequest::TYPE_HISEC, true)->appendParagraph(pht('Welcome, %s. To complete the login process, provide your ' . 'multi-factor credentials.', phutil_tag('strong', array(), $viewer->getUsername())))->appendChild($form->buildLayoutView())->setSubmitURI($request->getPath())->addCancelButton($ex->getCancelURI())->addSubmitButton(pht('Continue'));
     }
     // Upgrade the partial session to a full session.
     $engine->upgradePartialSession($viewer);
     // TODO: It might be nice to add options like "bind this session to my IP"
     // here, even for accounts without multi-factor auth attached to them.
     $next = PhabricatorCookies::getNextURICookie($request);
     $request->clearCookie(PhabricatorCookies::COOKIE_NEXTURI);
     $request->clearCookie(PhabricatorCookies::COOKIE_HISEC);
     if (!PhabricatorEnv::isValidLocalURIForLink($next)) {
         $next = '/';
     }
     return id(new AphrontRedirectResponse())->setURI($next);
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getUser();
     $document = id(new LegalpadDocumentQuery())->setViewer($viewer)->withIDs(array($request->getURIData('id')))->needDocumentBodies(true)->executeOne();
     if (!$document) {
         return new Aphront404Response();
     }
     $information = $this->readSignerInformation($document, $request);
     if ($information instanceof AphrontResponse) {
         return $information;
     }
     list($signer_phid, $signature_data) = $information;
     $signature = null;
     $type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL;
     $is_individual = $document->getSignatureType() == $type_individual;
     switch ($document->getSignatureType()) {
         case LegalpadDocument::SIGNATURE_TYPE_NONE:
             // nothing to sign means this should be true
             $has_signed = true;
             // this is a status UI element
             $signed_status = null;
             break;
         case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
             if ($signer_phid) {
                 // TODO: This is odd and should probably be adjusted after
                 // grey/external accounts work better, but use the omnipotent
                 // viewer to check for a signature so we can pick up
                 // anonymous/grey signatures.
                 $signature = id(new LegalpadDocumentSignatureQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withDocumentPHIDs(array($document->getPHID()))->withSignerPHIDs(array($signer_phid))->executeOne();
                 if ($signature && !$viewer->isLoggedIn()) {
                     return $this->newDialog()->setTitle(pht('Already Signed'))->appendParagraph(pht('You have already signed this document!'))->addCancelButton('/' . $document->getMonogram(), pht('Okay'));
                 }
             }
             $signed_status = null;
             if (!$signature) {
                 $has_signed = false;
                 $signature = id(new LegalpadDocumentSignature())->setSignerPHID($signer_phid)->setDocumentPHID($document->getPHID())->setDocumentVersion($document->getVersions());
                 // If the user is logged in, show a notice that they haven't signed.
                 // If they aren't logged in, we can't be as sure, so don't show
                 // anything.
                 if ($viewer->isLoggedIn()) {
                     $signed_status = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setErrors(array(pht('You have not signed this document yet.')));
                 }
             } else {
                 $has_signed = true;
                 $signature_data = $signature->getSignatureData();
                 // 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 PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_NOTICE)->setErrors(array($signed_text));
             }
             $field_errors = array('name' => true, 'email' => true, 'agree' => true);
             $signature->setSignatureData($signature_data);
             break;
         case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
             $signature = id(new LegalpadDocumentSignature())->setDocumentPHID($document->getPHID())->setDocumentVersion($document->getVersions());
             if ($viewer->isLoggedIn()) {
                 $has_signed = false;
                 $signed_status = null;
             } else {
                 // This just hides the form.
                 $has_signed = true;
                 $login_text = pht('This document requires a corporate signatory. You must log in to ' . 'accept this document on behalf of a company you represent.');
                 $signed_status = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setErrors(array($login_text));
             }
             $field_errors = array('name' => true, 'address' => true, 'contact.name' => true, 'email' => true);
             $signature->setSignatureData($signature_data);
             break;
     }
     $errors = array();
     if ($request->isFormOrHisecPost() && !$has_signed) {
         // Require two-factor auth to sign legal documents.
         if ($viewer->isLoggedIn()) {
             $engine = new PhabricatorAuthSessionEngine();
             $engine->requireHighSecuritySession($viewer, $request, '/' . $document->getMonogram());
         }
         list($form_data, $errors, $field_errors) = $this->readSignatureForm($document, $request);
         $signature_data = $form_data + $signature_data;
         $signature->setSignatureData($signature_data);
         $signature->setSignatureType($document->getSignatureType());
         $signature->setSignerName((string) idx($signature_data, 'name'));
         $signature->setSignerEmail((string) idx($signature_data, 'email'));
         $agree = $request->getExists('agree');
         if (!$agree) {
             $errors[] = pht('You must check "I agree to the terms laid forth above."');
             $field_errors['agree'] = pht('Required');
         }
         if ($viewer->isLoggedIn() && $is_individual) {
             $verified = LegalpadDocumentSignature::VERIFIED;
         } else {
             $verified = LegalpadDocumentSignature::UNVERIFIED;
         }
         $signature->setVerified($verified);
         if (!$errors) {
             $signature->save();
             // If the viewer is logged in, signing for themselves, send them to
             // the document page, which will show that they have signed the
             // document. Unless of course they were required to sign the
             // document to use Phabricator; in that case try really hard to
             // re-direct them to where they wanted to go.
             //
             // Otherwise, send them to a completion page.
             if ($viewer->isLoggedIn() && $is_individual) {
                 $next_uri = '/' . $document->getMonogram();
                 if ($document->getRequireSignature()) {
                     $request_uri = $request->getRequestURI();
                     $next_uri = (string) $request_uri;
                 }
             } else {
                 $this->sendVerifySignatureEmail($document, $signature);
                 $next_uri = $this->getApplicationURI('done/');
             }
             return id(new AphrontRedirectResponse())->setURI($next_uri);
         }
     }
     $document_body = $document->getDocumentBody();
     $engine = id(new PhabricatorMarkupEngine())->setViewer($viewer);
     $engine->addObject($document_body, LegalpadDocumentBody::MARKUP_FIELD_TEXT);
     $engine->process();
     $document_markup = $engine->getOutput($document_body, LegalpadDocumentBody::MARKUP_FIELD_TEXT);
     $title = $document_body->getTitle();
     $manage_uri = $this->getApplicationURI('view/' . $document->getID() . '/');
     $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $document, PhabricatorPolicyCapability::CAN_EDIT);
     // Use the last content update as the modified date. We don't want to
     // show that a document like a TOS was "updated" by an incidental change
     // to a field like the preamble or privacy settings which does not acutally
     // affect the content of the agreement.
     $content_updated = $document_body->getDateCreated();
     // NOTE: We're avoiding `setPolicyObject()` here so we don't pick up
     // extra UI elements that are unnecessary and clutter the signature page.
     // These details are available on the "Manage" page.
     $header = id(new PHUIHeaderView())->setHeader($title)->setUser($viewer)->setEpoch($content_updated)->addActionLink(id(new PHUIButtonView())->setTag('a')->setIcon(id(new PHUIIconView())->setIconFont('fa-pencil'))->setText(pht('Manage'))->setHref($manage_uri)->setDisabled(!$can_edit)->setWorkflow(!$can_edit));
     $preamble_box = null;
     if (strlen($document->getPreamble())) {
         $preamble_text = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent($document->getPreamble()), 'default', $viewer);
         // NOTE: We're avoiding `setObject()` here so we don't pick up extra UI
         // elements like "Subscribers". This information is available on the
         // "Manage" page, but just clutters up the "Signature" page.
         $preamble = id(new PHUIPropertyListView())->setUser($viewer)->addSectionHeader(pht('Preamble'))->addTextContent($preamble_text);
         $preamble_box = new PHUIPropertyGroupView();
         $preamble_box->addPropertyList($preamble);
     }
     $content = id(new PHUIDocumentViewPro())->addClass('legalpad')->setHeader($header)->appendChild(array($signed_status, $preamble_box, $document_markup));
     $signature_box = null;
     if (!$has_signed) {
         $error_view = null;
         if ($errors) {
             $error_view = id(new PHUIInfoView())->setErrors($errors);
         }
         $signature_form = $this->buildSignatureForm($document, $signature, $field_errors);
         switch ($document->getSignatureType()) {
             default:
                 break;
             case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
             case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
                 $box = id(new PHUIObjectBoxView())->setHeaderText(pht('Agree and Sign Document'))->setForm($signature_form);
                 if ($error_view) {
                     $box->setInfoView($error_view);
                 }
                 $signature_box = phutil_tag_div('phui-document-view-pro-box', $box);
                 break;
         }
     }
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->setBorder(true);
     $crumbs->addTextCrumb($document->getMonogram());
     return $this->buildApplicationPage(array($crumbs, $content, $signature_box), array('title' => $title, 'pageObjects' => array($document->getPHID())));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $document = id(new LegalpadDocumentQuery())->setViewer($viewer)->withIDs(array($this->id))->needDocumentBodies(true)->executeOne();
     if (!$document) {
         return new Aphront404Response();
     }
     list($signer_phid, $signature_data) = $this->readSignerInformation($document, $request);
     $signature = null;
     $type_individual = LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL;
     $is_individual = $document->getSignatureType() == $type_individual;
     if ($is_individual) {
         if ($signer_phid) {
             // TODO: This is odd and should probably be adjusted after grey/external
             // accounts work better, but use the omnipotent viewer to check for a
             // signature so we can pick up anonymous/grey signatures.
             $signature = id(new LegalpadDocumentSignatureQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withDocumentPHIDs(array($document->getPHID()))->withSignerPHIDs(array($signer_phid))->executeOne();
             if ($signature && !$viewer->isLoggedIn()) {
                 return $this->newDialog()->setTitle(pht('Already Signed'))->appendParagraph(pht('You have already signed this document!'))->addCancelButton('/' . $document->getMonogram(), pht('Okay'));
             }
         }
         $signed_status = null;
         if (!$signature) {
             $has_signed = false;
             $signature = id(new LegalpadDocumentSignature())->setSignerPHID($signer_phid)->setDocumentPHID($document->getPHID())->setDocumentVersion($document->getVersions());
             // If the user is logged in, show a notice that they haven't signed.
             // If they aren't logged in, we can't be as sure, so don't show
             // anything.
             if ($viewer->isLoggedIn()) {
                 $signed_status = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setErrors(array(pht('You have not signed this document yet.')));
             }
         } else {
             $has_signed = true;
             $signature_data = $signature->getSignatureData();
             // 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($signed_text));
         }
         $field_errors = array('name' => true, 'email' => true, 'agree' => true);
     } else {
         $signature = id(new LegalpadDocumentSignature())->setDocumentPHID($document->getPHID())->setDocumentVersion($document->getVersions());
         if ($viewer->isLoggedIn()) {
             $has_signed = false;
             $signed_status = null;
         } else {
             // This just hides the form.
             $has_signed = true;
             $login_text = pht('This document requires a corporate signatory. You must log in to ' . 'accept this document on behalf of a company you represent.');
             $signed_status = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setErrors(array($login_text));
         }
         $field_errors = array('name' => true, 'address' => true, 'contact.name' => true, 'email' => true);
     }
     $signature->setSignatureData($signature_data);
     $errors = array();
     if ($request->isFormOrHisecPost() && !$has_signed) {
         // Require two-factor auth to sign legal documents.
         if ($viewer->isLoggedIn()) {
             $engine = new PhabricatorAuthSessionEngine();
             $engine->requireHighSecuritySession($viewer, $request, '/' . $document->getMonogram());
         }
         list($form_data, $errors, $field_errors) = $this->readSignatureForm($document, $request);
         $signature_data = $form_data + $signature_data;
         $signature->setSignatureData($signature_data);
         $signature->setSignatureType($document->getSignatureType());
         $signature->setSignerName((string) idx($signature_data, 'name'));
         $signature->setSignerEmail((string) idx($signature_data, 'email'));
         $agree = $request->getExists('agree');
         if (!$agree) {
             $errors[] = pht('You must check "I agree to the terms laid forth above."');
             $field_errors['agree'] = pht('Required');
         }
         if ($viewer->isLoggedIn() && $is_individual) {
             $verified = LegalpadDocumentSignature::VERIFIED;
         } else {
             $verified = LegalpadDocumentSignature::UNVERIFIED;
         }
         $signature->setVerified($verified);
         if (!$errors) {
             $signature->save();
             // If the viewer is logged in, send them to the document page, which
             // will show that they have signed the document. Otherwise, send them
             // to a completion page.
             if ($viewer->isLoggedIn() && $is_individual) {
                 $next_uri = '/' . $document->getMonogram();
             } else {
                 $this->sendVerifySignatureEmail($document, $signature);
                 $next_uri = $this->getApplicationURI('done/');
             }
             return id(new AphrontRedirectResponse())->setURI($next_uri);
         }
     }
     $document_body = $document->getDocumentBody();
     $engine = id(new PhabricatorMarkupEngine())->setViewer($viewer);
     $engine->addObject($document_body, LegalpadDocumentBody::MARKUP_FIELD_TEXT);
     $engine->process();
     $document_markup = $engine->getOutput($document_body, LegalpadDocumentBody::MARKUP_FIELD_TEXT);
     $title = $document_body->getTitle();
     $manage_uri = $this->getApplicationURI('view/' . $document->getID() . '/');
     $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $document, PhabricatorPolicyCapability::CAN_EDIT);
     $header = id(new PHUIHeaderView())->setHeader($title)->addActionLink(id(new PHUIButtonView())->setTag('a')->setIcon(id(new PHUIIconView())->setIconFont('fa-pencil'))->setText(pht('Manage Document'))->setHref($manage_uri)->setDisabled(!$can_edit)->setWorkflow(!$can_edit));
     $preamble = null;
     if (strlen($document->getPreamble())) {
         $preamble_text = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent($document->getPreamble()), 'default', $viewer);
         $preamble = id(new PHUIPropertyListView())->addSectionHeader(pht('Preamble'))->addTextContent($preamble_text);
     }
     $content = id(new PHUIDocumentView())->addClass('legalpad')->setHeader($header)->setFontKit(PHUIDocumentView::FONT_SOURCE_SANS)->appendChild(array($signed_status, $preamble, $document_markup));
     if (!$has_signed) {
         $error_view = null;
         if ($errors) {
             $error_view = id(new AphrontErrorView())->setErrors($errors);
         }
         $signature_form = $this->buildSignatureForm($document, $signature, $field_errors);
         $subheader = id(new PHUIHeaderView())->setHeader(pht('Agree and Sign Document'))->setBleedHeader(true);
         $content->appendChild(array($subheader, $error_view, $signature_form));
     }
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb($document->getMonogram());
     return $this->buildApplicationPage(array($crumbs, $content), array('title' => $title, 'pageObjects' => array($document->getPHID())));
 }