.. ==================================================
.. ==================================================
.. ==================================================
.. Header hierarchy
.. ==
..  --
..   ^^
..    ""
..     ;;
..      ,,
..
.. --------------------------------------------used to the update the records specified ------
.. Best Practice T3 reST: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/CheatSheet.html
..             Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html
.. Italic *italic*
.. Bold **bold**
.. Code ``text``
.. External Links: `Bootstrap <http://getbootstrap.com/>`_
.. Internal Link: :ref:`downloadButton` (default url text) or :ref:`download Button<downloadButton>` (explicit url text)
.. Add Images:    .. image:: ../Images/a4.jpg
..
..
.. Admonitions
..           .. note::   .. important::     .. tip::     .. warning::
.. Color:   (blue)       (orange)           (green)      (red)
..
.. Definition:
.. some text becomes strong (only one line)
..      description has to indented

.. -*- coding: utf-8 -*- with BOM.

.. include:: Includes.txt

.. _`use-case`:

Use Case
========

To install the following use cases, please:

* Create the page and tt-content records.
* Copy the JSON form code to a new empty form (open Form in JSON mode).


.. _`self-registration`:

Self Registration
-----------------

**Concept:**

* The T3 FE User record will be created on the fly in the *last* step, when the user set's the password.
* Form *Registration*

  * Input: *last name, first name, email*.
  * Create record in table `Person`, if no name or email sibling is found. Else give a hint to do a password reset.
  * Set a new ``Person.auth`` token.
  * Set ``Person.authExpired = NOW() + INTERVAL 1 DAY``.
  * Send an email to the given email address with a password reset link (incl. the token) which is time limited.
  * Further steps: See *Set password*.

* Form *Request password reset link*

  * This is optional, but useful to offer the opportunity to reset the own password.
  * Input: *email address*.
  * Send an email to the given email address with a password reset link (incl. a token) which is time limited.

    * Only send email if the email address is known!
    * If email is not found, the user should not be noticed. This prohibits misusing the form to validate email adresses.
    * Set a new ``Person.auth`` token.
    * Set ``Person.authExpired = NOW() + INTERVAL 1 DAY``.

* Form *Set password*

  * The user types in the new password.
  * On save:

    * If the FE account does not exist, it will be created.
    * Set the FE user password.
    * Clear ``Person.authExpired``.

Table: Person
^^^^^^^^^^^^^
::

    CREATE TABLE `Person` (
      `id` int(11) UNSIGNED NOT NULL,
      `lastName` varchar(64) NOT NULL DEFAULT '',
      `firstName` varchar(64) NOT NULL DEFAULT '',
      `email` varchar(128) NOT NULL,
      `account` varchar(128) NOT NULL,
      `auth`  varchar(32) NOT NULL,
      `authExpire` datetime NOT NULL,
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 PACK_KEYS=1;

Registration
^^^^^^^^^^^^

Page: Registration
""""""""""""""""""

* Page Alias: registration

QFQ content record::

    #
    # {{action:SE}}: thanksRegistration
    #


    form={{SELECT IF('{{action:SE}}' = '','registration','') }}

    20.sql = SELECT "<p>Thank you for your registration.</p><p>An email with further instructions has been sent to you.</p>"
                    , "<p>You should receive the mail during the next 5 minutes. If not, please check your SPAM folder.</p>"

                FROM (SELECT '') AS fake
                WHERE '{{action:SE}}' = 'thanksRegistration'
                  AND '{{form:SE}}'=''


Form: registration
""""""""""""""""""
::

    {
        "title": "Registration",
        "tableName": "Person",
        "permitNew": "always",
        "permitEdit": "never",
        "showButton": "close,save",
        "forwardMode": "url-sip-skip-history",
        "forwardPage": "?id=registration&action=thanksRegistration",
        "parameter": "submitButtonText=Register",
        "FormElement_ff": [
            {
                "enabled": "yes",
                "name": "firstName",
                "label": "First name",
                "mode": "required",
                "type": "text"
            },
            {
                "enabled": "yes",
                "name": "lastName",
                "label": "Last name",
                "mode": "required",
                "type": "text"
            },
            {
                "enabled": "yes",
                "name": "email",
                "label": "Email",
                "mode": "required",
                "class": "native",
                "type": "text",
                "checkType": "email",
                "parameter": "retype\r\nretypeLabel=Retype email"
            },
            {
                "enabled": "yes",
                "label": "Check for double registration",
                "mode": "show",
                "modeSql": "",
                "class": "action",
                "type": "beforeSave",
                "parameter": "sqlValidate={{!SELECT p.id FROM Person AS p WHERE p.email='{{email:F:alnumx}}'  OR ('{{firstName:F:allbut}}'=p.firstName AND  '{{lastName:F:allbut}}'=p.lastName ) LIMIT 1 }}\r\n expectRecords=0\r\nmessageFail=Sorry, person already registered by name or email. Please just reset the password under <a href='?id=reset'>reset</a>"
            },
            {
                "enabled": "yes",
                "label": "auth, deadline",
                "class": "action",
                "type": "afterSave",
                "parameter": "# Set token & expiration\r\nsqlBefore={{UPDATE Person SET auth='{{random:V}}', authExpire=DATE_ADD(NOW(), INTERVAL 1 DAY) WHERE id={{id:R}} }}"
            },
            {
                "enabled": "yes",
                "label": "a) sendEmail, b) fe user",
                "mode": "show",
                "class": "action",
                "type": "sendMail",
                "value": "Dear new user\r\nPlease set a new password under {{baseUrl:Y}}?id=set&auth={{auth:R}}\r\nRegards.",
                "parameter": "sendMailTo={{email:R}}\r\nsendMailSubject=Registration confirmation\r\nsendMailFrom={{ADMINISTRATIVE_EMAIL:Y}}\r\n\r\n# Create User in T3\r\nsqlAfter={{INSERT INTO {{dbNameT3:Y}}.fe_users (pid, usergroup, username, password, first_name, last_name, name, email) VALUES (6, '1', '{{email:F:alnumx}}','$p$initial invalid hash',  '{{firstName:R}}', '{{lastName:R}}', '{{firstName:R}} {{lastName:R}}', '{{email:F:alnumx}}') }}"
            }
        ]
    }

Request password reset link
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Page: reset
"""""""""""

* Page Alias: reset

QFQ content record::

    #
    # {{action:SE}}: passwordReset
    #
    # Empty: {{action:SE}}='' > shows the form angezeigt 'passwordReset'.
    # Given: {{action:SE}}='confirmation' > show confirmation.
    #
    form={{SELECT IF('{{action:SE}}' = '','passwordReset','') }}
    r=1

    10 {
      sql = SELECT '' FROM (SELECT '') AS fake WHERE '{{action:SE}}'='confirmation'
      head = <div class="alert alert-success">
               <p>Thank you.</p>
               <p>If the email address is known in our database, we sent a password reset link to it.</p>
               <p>The mail should be received during the next minutes. If not, please check you junk folder.</p>
               <p>To set a new password, please click on the link provided in the email.</p>
             </div>
    }

Form: passwordReset
"""""""""""""""""""

.. note::

  Take care that there is one dummy person record with person.id=1

Form 'passwordReset'::

    {
        "title": "Password reset",
        "tableName": "Person",
        "permitNew": "never",
        "permitEdit": "always",
        "escapeTypeDefault": "c",
        "render": "bootstrap",
        "dirtyMode": "exclusive",
        "showButton": "save",
        "multiMode": "none",
        "forwardMode": "url-sip-skip-history",
        "forwardPage": "?id={{pageAlias:T}}&action=confirmation",
        "labelAlign": "default",
        "parameter": "submitButtonText = Send password reset email",
        "deleted": "no",
        "FormElement_ff": [
            {
                "enabled": "yes",
                "name": "emailValue",
                "label": "Email",
                "mode": "show",
                "class": "native",
                "type": "text",
                "encode": "specialchar",
                "checkType": "email",
                "ord": 10
            },
            {
                "enabled": "yes",
                "label": "Check for *example.com email",
                "mode": "show",
                "class": "action",
                "type": "beforeSave",
                "ord": 20,
                "parameter": "sqlValidate={{!SELECT 'fake' FROM (SELECT '')  AS fake WHERE '{{emailValue:F:alnumx}}' LIKE '%example.com'  }}\r\n expectRecords=0\r\nmessageFail=Sorry, Password reset is not possible for *example.com."
            },
            {
                "enabled": "yes",
                "label": "a) set auth, expire, b) send email",
                "mode": "show",
                "class": "action",
                "type": "sendMail",
                "encode": "specialchar",
                "checkType": "auto",
                "ord": 50,
                "value": "Dear new user\r\nPlease set a new password under {{baseUrl:Y}}?id=set&auth={{auth:V}}\r\nRegards.",
                "parameter": "fillStoreVar={{!SELECT CONCAT(p.firstName , ' ', p.lastName) AS name, p.id AS _pId, @expire:=DATE_ADD(NOW(), INTERVAL 4 DAY) AS expireTs, QDATE_FORMAT(@expire) AS expire, p.email, '{{random:V}}' AS auth FROM Person AS p WHERE p.email='{{emailValue:F:alnumx}}' AND p.email!='' LIMIT 1}}\r\n\r\nsendMailTo={{email:VE}}\r\nsendMailSubject=Password Reset\r\nsendMailFrom=webmaster@example.com\r\nsendMailGrId=123\r\nsendMailXId=456\r\n\r\n# Set token & expiration\r\nsqlAfter = {{UPDATE Person SET auth='{{auth:V}}', authExpire='{{expireTs:V}}' WHERE email='{{emailValue:F:alnumx}}' AND email!='' LIMIT 1}}"
            }
        ]
    }

Set new password
^^^^^^^^^^^^^^^^

Page: set
"""""""""

* Page

  * Alias: set
  * Hide in menu: yes

QFQ content record::

    #
    # {{auth:CE}} - empty >> Form 'setPassword'
    # {{auth:CE}} - unknown | expired >> Error message
    # {{auth:SE}} - valid >> Set Password
    #
    # {{action:CE}} - 'thanks'

    form={{SELECT IF( ISNULL(p.id), '', 'passwordSet' )
              FROM (SELECT '') AS fake
              LEFT JOIN Person AS p
                ON p.auth='{{auth:C:alnumx}}'
                  AND p.auth!=''
                  AND NOW()<p.authExpire }}

    r={{SELECT IFNULL(p.id, 0) FROM (SELECT '') AS fake LEFT JOIN Person AS p ON p.auth='{{auth:C:alnumx}}' AND p.auth!='' AND NOW()<p.authExpire}}


    10 {
      sql = SELECT IF( ISNULL(p.id)
                       , 'Token invalid'
                       , IF( NOW()<p.authExpire
                            ,''
                            , IF( p.authExpire=0, 'Password already set', 'Token expired') )
                     )
              FROM (SELECT '') AS fake
              LEFT JOIN Person AS p
                ON p.auth='{{auth:C:alnumx}}'
                  AND p.auth!=''
              WHERE '{{action:SE}}'=''
                  AND (ISNULL(p.id) OR NOW()>=p.authExpire)
      head = <div class="alert alert-warning" role="alert">
      tail = </div>
    }

    20.sql = SELECT 'Thanks for setting the password. Please <a href="?id=login">log in</a> now.'
               FROM (SELECT '') AS fake
               WHERE '{{action:SE}}'='thanks'

Form: passwordSet
"""""""""""""""""

.. note::

    Please adjust the numbers for ``usergroup`` and ``pid`` in FormElement `Update fe_user.password > parameter > sqlInsert`
    to your needs. Remember that every FE-User needs at least one FE-Group to log in successfully.

Form 'passwordSet'::

    {
        "title": "Set password",
        "tableName": "Person",
        "permitNew": "never",
        "permitEdit": "always",
        "escapeTypeDefault": "c",
        "render": "bootstrap",
        "dirtyMode": "exclusive",
        "showButton": "save",
        "multiMode": "none",
        "forwardMode": "url-sip-skip-history",
        "forwardPage": "?{{pageAlias:T}}&action=thanks",
        "parameter": "submitButtonText='Set password'",
        "FormElement_ff": [
            {
                "enabled": "yes",
                "name": "myValue",
                "label": "Password",
                "mode": "show",
                "class": "native",
                "type": "password",
                "checkType": "pattern",
                "checkPattern": "[a-zA-Z0-9-_+ *\\\/.,:;]{10,}",
                "ord": 10,
                "parameter": "retype\r\nretypeLabel=Retype password\r\ndata-pattern-error=At least 10 characters are required. Valid characters: a-z A-Z 0-9 -_+*\/.,:;\r\nextraButtonPassword"
            },
            {
                "enabled": "yes",
                "label": "Update fe_user.password",
                "mode": "show",
                "class": "action",
                "type": "afterSave",
                "encode": "specialchar",
                "ord": 20,
                "parameter": "slaveId={{SELECT fe.uid FROM {{dbNameT3:Y}}.fe_users AS fe WHERE fe.username='{{email:RE}}' AND fe.username!='' AND fe.deleted=0 LIMIT 1}}\r\n\r\n# Create FE User. Please update values of `pid`, `usergroup` to your setup.\r\nsqlInsert = {{INSERT INTO {{dbNameT3:Y}}.fe_users ( `pid`, `usergroup`, `username`, `email`, `name`, `password`,`crdate`) VALUES ( 5 , 1 , '{{email:RE}}', '{{email:RE}}', '{{lastName:RE}}, {{firstName:RE}}', '{{myValue:FE:all:p}}',  UNIX_TIMESTAMP() ) }}\r\n\r\nsqlUpdate = {{UPDATE {{dbNameT3:Y}}.fe_users SET password='{{myValue:FE:all:p}}' WHERE uid={{slaveId:V0}}  }}\r\n\r\nsqlAfter={{UPDATE Person SET authExpire=0 WHERE id={{id:R}} }}"
            }
        ]
    }