<commit_before><?php

final class LegalpadDocumentSignController extends LegalpadController {

  public function shouldAllowPublic() {
    return true;
  }

  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();
    }

    list($signer_phid, $signature_data) = $this->readSignerInformation(
      $document,
      $request);

    $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 PHUIErrorView())
              ->setSeverity(PHUIErrorView::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 PHUIErrorView())
            ->setSeverity(PHUIErrorView::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 PHUIErrorView())
            ->setSeverity(PHUIErrorView::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, 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 PHUIErrorView())
          ->setErrors($errors);
      }

      $signature_form = $this->buildSignatureForm(
        $document,
        $signature,
        $field_errors);

      switch ($document->getSignatureType()) {
        case LegalpadDocument::SIGNATURE_TYPE_NONE:
          $subheader = null;
          break;
        case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
        case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
          $subheader = id(new PHUIHeaderView())
            ->setHeader(pht('Agree and Sign Document'))
            ->setBleedHeader(true);
          break;
      }

      $content->appendChild(
        array(
          $subheader,
          $error_view,
          $signature_form,
        ));
    }

    $crumbs = $this->buildApplicationCrumbs();
    $crumbs->setBorder(true);
    $crumbs->addTextCrumb($document->getMonogram());

    return $this->buildApplicationPage(
      array(
        $crumbs,
        $content,
      ),
      array(
        'title' => $title,
        'pageObjects' => array($document->getPHID()),
      ));
  }

  private function readSignerInformation(
    LegalpadDocument $document,
    AphrontRequest $request) {

    $viewer = $request->getUser();
    $signer_phid = null;
    $signature_data = array();

    switch ($document->getSignatureType()) {
      case LegalpadDocument::SIGNATURE_TYPE_NONE:
        break;
      case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
        if ($viewer->isLoggedIn()) {
          $signer_phid = $viewer->getPHID();
          $signature_data = array(
            'name' => $viewer->getRealName(),
            'email' => $viewer->loadPrimaryEmailAddress(),
          );
        } else if ($request->isFormPost()) {
          $email = new PhutilEmailAddress($request->getStr('email'));
          if (strlen($email->getDomainName())) {
            $email_obj = id(new PhabricatorUserEmail())
              ->loadOneWhere('address = %s', $email->getAddress());
            if ($email_obj) {
              return $this->signInResponse();
            }
            $external_account = id(new PhabricatorExternalAccountQuery())
              ->setViewer($viewer)
              ->withAccountTypes(array('email'))
              ->withAccountDomains(array($email->getDomainName()))
              ->withAccountIDs(array($email->getAddress()))
              ->loadOneOrCreate();
            if ($external_account->getUserPHID()) {
              return $this->signInResponse();
            }
            $signer_phid = $external_account->getPHID();
          }
        }
        break;
      case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
        $signer_phid = $viewer->getPHID();
        if ($signer_phid) {
          $signature_data = array(
            'contact.name' => $viewer->getRealName(),
            'email' => $viewer->loadPrimaryEmailAddress(),
            'actorPHID' => $viewer->getPHID(),
          );
        }
        break;
    }

    return array($signer_phid, $signature_data);
  }

  private function buildSignatureForm(
    LegalpadDocument $document,
    LegalpadDocumentSignature $signature,
    array $errors) {

    $viewer = $this->getRequest()->getUser();
    $data = $signature->getSignatureData();

    $form = id(new AphrontFormView())
      ->setUser($viewer);

    $signature_type = $document->getSignatureType();
    switch ($signature_type) {
      case LegalpadDocument::SIGNATURE_TYPE_NONE:
        // bail out of here quick
        return;
      case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
        $this->buildIndividualSignatureForm(
          $form,
          $document,
          $signature,
          $errors);
        break;
      case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
        $this->buildCorporateSignatureForm(
          $form,
          $document,
          $signature,
          $errors);
        break;
      default:
        throw new Exception(
          pht(
            'This document has an unknown signature type ("%s").',
            $signature_type));
    }

    $form
      ->appendChild(
        id(new AphrontFormCheckboxControl())
          ->setError(idx($errors, 'agree', null))
          ->addCheckbox(
            'agree',
            'agree',
            pht('I agree to the terms laid forth above.'),
            false))
      ->appendChild(
        id(new AphrontFormSubmitControl())
          ->setValue(pht('Sign Document'))
          ->addCancelButton($this->getApplicationURI()));

    return $form;
  }

  private function buildIndividualSignatureForm(
    AphrontFormView $form,
    LegalpadDocument $document,
    LegalpadDocumentSignature $signature,
    array $errors) {

    $data = $signature->getSignatureData();

    $form
      ->appendChild(
        id(new AphrontFormTextControl())
        ->setLabel(pht('Name'))
        ->setValue(idx($data, 'name', ''))
        ->setName('name')
        ->setError(idx($errors, 'name', null)));

    $viewer = $this->getRequest()->getUser();
    if (!$viewer->isLoggedIn()) {
      $form->appendChild(
        id(new AphrontFormTextControl())
          ->setLabel(pht('Email'))
          ->setValue(idx($data, 'email', ''))
          ->setName('email')
          ->setError(idx($errors, 'email', null)));
    }

    return $form;<commit_msg>Legalpad - make "Cancel" button "Log Out" button for required signature documents<commit_after><?php

final class LegalpadDocumentSignController extends LegalpadController {

  public function shouldAllowPublic() {
    return true;
  }

  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();
    }

    list($signer_phid, $signature_data) = $this->readSignerInformation(
      $document,
      $request);

    $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 PHUIErrorView())
              ->setSeverity(PHUIErrorView::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 PHUIErrorView())
            ->setSeverity(PHUIErrorView::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 PHUIErrorView())
            ->setSeverity(PHUIErrorView::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);

    $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 PHUIErrorView())
          ->setErrors($errors);
      }

      $signature_form = $this->buildSignatureForm(
        $document,
        $signature,
        $field_errors);

      switch ($document->getSignatureType()) {
        case LegalpadDocument::SIGNATURE_TYPE_NONE:
          $subheader = null;
          break;
        case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
        case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
          $subheader = id(new PHUIHeaderView())
            ->setHeader(pht('Agree and Sign Document'))
            ->setBleedHeader(true);
          break;
      }

      $content->appendChild(
        array(
          $subheader,
          $error_view,
          $signature_form,
        ));
    }

    $crumbs = $this->buildApplicationCrumbs();
    $crumbs->setBorder(true);
    $crumbs->addTextCrumb($document->getMonogram());

    return $this->buildApplicationPage(
      array(
        $crumbs,
        $content,
      ),
      array(
        'title' => $title,
        'pageObjects' => array($document->getPHID()),
      ));
  }

  private function readSignerInformation(
    LegalpadDocument $document,
    AphrontRequest $request) {

    $viewer = $request->getUser();
    $signer_phid = null;
    $signature_data = array();

    switch ($document->getSignatureType()) {
      case LegalpadDocument::SIGNATURE_TYPE_NONE:
        break;
      case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
        if ($viewer->isLoggedIn()) {
          $signer_phid = $viewer->getPHID();
          $signature_data = array(
            'name' => $viewer->getRealName(),
            'email' => $viewer->loadPrimaryEmailAddress(),
          );
        } else if ($request->isFormPost()) {
          $email = new PhutilEmailAddress($request->getStr('email'));
          if (strlen($email->getDomainName())) {
            $email_obj = id(new PhabricatorUserEmail())
              ->loadOneWhere('address = %s', $email->getAddress());
            if ($email_obj) {
              return $this->signInResponse();
            }
            $external_account = id(new PhabricatorExternalAccountQuery())
              ->setViewer($viewer)
              ->withAccountTypes(array('email'))
              ->withAccountDomains(array($email->getDomainName()))
              ->withAccountIDs(array($email->getAddress()))
              ->loadOneOrCreate();
            if ($external_account->getUserPHID()) {
              return $this->signInResponse();
            }
            $signer_phid = $external_account->getPHID();
          }
        }
        break;
      case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
        $signer_phid = $viewer->getPHID();
        if ($signer_phid) {
          $signature_data = array(
            'contact.name' => $viewer->getRealName(),
            'email' => $viewer->loadPrimaryEmailAddress(),
            'actorPHID' => $viewer->getPHID(),
          );
        }
        break;
    }

    return array($signer_phid, $signature_data);
  }

  private function buildSignatureForm(
    LegalpadDocument $document,
    LegalpadDocumentSignature $signature,
    array $errors) {

    $viewer = $this->getRequest()->getUser();
    $data = $signature->getSignatureData();

    $form = id(new AphrontFormView())
      ->setUser($viewer);

    $signature_type = $document->getSignatureType();
    switch ($signature_type) {
      case LegalpadDocument::SIGNATURE_TYPE_NONE:
        // bail out of here quick
        return;
      case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL:
        $this->buildIndividualSignatureForm(
          $form,
          $document,
          $signature,
          $errors);
        break;
      case LegalpadDocument::SIGNATURE_TYPE_CORPORATION:
        $this->buildCorporateSignatureForm(
          $form,
          $document,
          $signature,
          $errors);
        break;
      default:
        throw new Exception(
          pht(
            'This document has an unknown signature type ("%s").',
            $signature_type));
    }

    $form
      ->appendChild(
        id(new AphrontFormCheckboxControl())
          ->setError(idx($errors, 'agree', null))
          ->addCheckbox(
            'agree',
            'agree',
            pht('I agree to the terms laid forth above.'),
            false));
    if ($document->getRequireSignature()) {
      $cancel_uri = '/logout/';
      $cancel_text = pht('Log Out');
    } else {
      $cancel_uri = $this->getApplicationURI();
      $cancel_text = pht('Cancel');
    }
    $form
      ->appendChild(
        id(new AphrontFormSubmitControl())
          ->setValue(pht('Sign Document'))
          ->addCancelButton($cancel_uri, $cancel_text));

    return $form;
  }

  private function buildIndividualSignatureForm(
    AphrontFormView $form,
    LegalpadDocument $document,
    LegalpadDocumentSignature $signature,
    array $errors) {

    $data = $signature->getSignatureData();

    $form
      ->appendChild(
        id(new AphrontFormTextControl())
        ->setLabel(pht('Name'))
        ->setValue(idx($data, 'name', ''))
        ->setName('name')
        ->setError(idx($errors, 'name', null)));

    $viewer = $this->getRequest()->getUser();
    if (!$viewer->isLoggedIn()) {
      $form->appendChild(
        id(new AphrontFormTextControl())
          ->setLabel(pht('Email'))
          ->setValue(idx($data, 'email', ''))
          ->setName('email')
          ->setError(idx($errors, 'email', null)));
    }

    return $form;
{'fraction_non_alphanumeric': 0.13361932557403566, 'fraction_numerical': 0.00021097788248531946, 'mean_word_length': 2.245834284409952, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 346, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2654968', 'n_tokens_mistral': 8225, 'n_tokens_neox': 8020, 'n_words': 1547}