| 838 | | >**Caution** |
|---|
| 839 | | >The following part of the tutorial refers to code that would have appeared in the 1.0 |
|---|
| 840 | | > version of the sandbox, and has not been updated to take account of symfony 1.1. It will |
|---|
| 841 | | >take a while longer to convert this section. Please bear with us! |
|---|
| 842 | | |
|---|
| 843 | | This new module will be used to handle the login form and the request. |
|---|
| 844 | | Edit the `apps/backend/modules/security/templates/indexSuccess.php` to |
|---|
| 845 | | create the login form: |
|---|
| 846 | | |
|---|
| 847 | | [php] |
|---|
| 848 | | <h2>Authentication</h2> |
|---|
| 849 | | |
|---|
| 850 | | <?php if ($sf_request->hasErrors()): ?> |
|---|
| 851 | | Identification failed - please try again |
|---|
| 852 | | <?php endif; ?> |
|---|
| 853 | | |
|---|
| 854 | | <?php echo form_tag('security/login') ?> |
|---|
| 855 | | <label for="login">login:</label> |
|---|
| 856 | | <?php echo input_tag('login', $sf_params->get('login')) ?> |
|---|
| 857 | | |
|---|
| 858 | | <label for="password">password:</label> |
|---|
| 859 | | <?php echo input_password_tag('password') ?> |
|---|
| 860 | | |
|---|
| 861 | | <?php echo submit_tag('submit', 'class=default') ?> |
|---|
| 862 | | </form> |
|---|
| 863 | | |
|---|
| 864 | | Add the `login` action that is called by the form to the `security` module |
|---|
| 865 | | (in the `apps/backend/modules/security/actions/actions.class.php` file): |
|---|
| 866 | | |
|---|
| 867 | | [php] |
|---|
| 868 | | public function executeLogin($request) |
|---|
| 869 | | { |
|---|
| 870 | | if ($request->getParameter('login') == 'admin' && $request->getParameter('password') == 'password') |
|---|
| | 839 | This new module will be used to handle the login form. Rather than generate the form |
|---|
| | 840 | automatically, as we did with posts and comments, we will create this manually. To do |
|---|
| | 841 | this, create a new file in `/sf_sandbox/lib/form/LoginForm.class.php` and enter the |
|---|
| | 842 | following code: |
|---|
| | 843 | |
|---|
| | 844 | [php] |
|---|
| | 845 | class LoginForm extends sfForm |
|---|
| | 846 | { |
|---|
| | 847 | private $accounts; |
|---|
| | 848 | |
|---|
| | 849 | /** |
|---|
| | 850 | * Associative array of users => passwords is passed to the form |
|---|
| | 851 | */ |
|---|
| | 852 | public function __construct($accounts) |
|---|
| 878 | | $request->setError('login', 'incorrect entry'); |
|---|
| 879 | | |
|---|
| 880 | | return $this->forward('security', 'index'); |
|---|
| | 864 | $this->setWidgets(array( |
|---|
| | 865 | 'username' => new sfWidgetFormInput(), |
|---|
| | 866 | 'password' => new sfWidgetFormInputPassword(), |
|---|
| | 867 | )); |
|---|
| | 868 | |
|---|
| | 869 | $this->setValidators(array( |
|---|
| | 870 | 'username' => new sfValidatorString(array('required' => true), array('required' => 'A username is required')), |
|---|
| | 871 | 'password' => new sfValidatorString(array('required' => true), array('required' => 'A password is required')), |
|---|
| | 872 | )); |
|---|
| | 873 | |
|---|
| | 874 | // We also want to check the password |
|---|
| | 875 | $this->validatorSchema->setPostValidator(new sfValidatorCallback(array('callback' => array($this, 'checkPassword')))); |
|---|
| | 876 | |
|---|
| | 877 | $this->widgetSchema->setNameFormat('login[%s]'); |
|---|
| | 878 | |
|---|
| | 879 | parent::setup(); |
|---|
| 882 | | } |
|---|
| 883 | | |
|---|
| 884 | | Like for the `main` module, remove the default code in the `index` action: |
|---|
| 885 | | |
|---|
| 886 | | [php] |
|---|
| 887 | | public function executeIndex() |
|---|
| 888 | | { |
|---|
| 889 | | } |
|---|
| | 881 | |
|---|
| | 882 | /** |
|---|
| | 883 | * Callback function that lets us create our own customised validator |
|---|
| | 884 | */ |
|---|
| | 885 | public function checkPassword($validator, $values) |
|---|
| | 886 | { |
|---|
| | 887 | // Get the values we need |
|---|
| | 888 | $username = $values['username']; |
|---|
| | 889 | $password = $values['password']; |
|---|
| | 890 | |
|---|
| | 891 | // Only validate if a username and password have been supplied |
|---|
| | 892 | if ($username && $password) |
|---|
| | 893 | { |
|---|
| | 894 | if (!array_key_exists($username, $this->accounts) || $this->accounts[$username] != $password) |
|---|
| | 895 | { |
|---|
| | 896 | $error = new sfValidatorError($validator, 'Password wrong - try again'); |
|---|
| | 897 | |
|---|
| | 898 | // Bind the error to the password field |
|---|
| | 899 | throw new sfValidatorErrorSchema($validator, array('password' => $error)); |
|---|
| | 900 | } |
|---|
| | 901 | } |
|---|
| | 902 | |
|---|
| | 903 | // Note that we can clean some values here if we wish |
|---|
| | 904 | return $values; |
|---|
| | 905 | } |
|---|
| | 906 | } |
|---|
| | 907 | |
|---|
| | 908 | After adding the class, clear your cache again (`php symfony cc`), so symfony rescans |
|---|
| | 909 | its lib folders for classes it 'knows' about. |
|---|
| | 910 | |
|---|
| | 911 | Okay, so let's look at this code section by section. Firstly, when this class is |
|---|
| | 912 | instantiated, we have to pass to the constructor an associated array of users and |
|---|
| | 913 | passwords. This is then saved in the object for later reference. Of course in a real |
|---|
| | 914 | system this would not be necessary, since we'd just query a user table in a database |
|---|
| | 915 | instead, or call some kind of authentication service. |
|---|
| | 916 | |
|---|
| | 917 | In the same way as the CRUD-generated forms, we have a setup() method, which is called |
|---|
| | 918 | by symfony automatically. This creates the necessary elements in the form (username and |
|---|
| | 919 | password) and sets up some basic validators. Most of this is fairly self-explanatory: |
|---|
| | 920 | the elements are set to have an appropriate type (input box and password box |
|---|
| | 921 | respectively) and both require some input to validate ('required' => true). |
|---|
| | 922 | |
|---|
| | 923 | The last validator, which checks the password, is worth some extra explanation. |
|---|
| | 924 | Firstly, rather than using a standard validator, it uses a "callback" validator, which |
|---|
| | 925 | allows the programmer the flexibility of defining their own validation test. Secondly, |
|---|
| | 926 | in our example we have called this validator as part of the "post validator" sequence, |
|---|
| | 927 | which is called once all the individual element validators have been executed. This is |
|---|
| | 928 | useful as at this later stage, we get access to cleaned copies of all the values in |
|---|
| | 929 | the form. |
|---|
| | 930 | |
|---|
| | 931 | We can also see from the code that the password will only be checked if the username |
|---|
| | 932 | and password have been supplied: if they are not, the form will be invalid anyway |
|---|
| | 933 | from the 'required' rules. However if we do have a username and password, the |
|---|
| | 934 | existence of the user and the correctness of the password can be checked, and a |
|---|
| | 935 | validation error thrown if either check fails. |
|---|
| | 936 | |
|---|
| | 937 | So, now we can plug the new form into our system. In the actions class of your backend |
|---|
| | 938 | security module (`/sf_sandbox/apps/backend/modules/security/actions/actions.class.php`), |
|---|
| | 939 | paste this class definition (overwriting the existing contents): |
|---|
| | 940 | |
|---|
| | 941 | [php] |
|---|
| | 942 | class securityActions extends sfActions |
|---|
| | 943 | { |
|---|
| | 944 | /** |
|---|
| | 945 | * Executes index action |
|---|
| | 946 | * |
|---|
| | 947 | * @param sfRequest $request A request object |
|---|
| | 948 | */ |
|---|
| | 949 | public function executeIndex($request) |
|---|
| | 950 | { |
|---|
| | 951 | $this->form = new LoginForm(array('admin' => 'password')); |
|---|
| | 952 | |
|---|
| | 953 | if ($request->isMethod('post')) |
|---|
| | 954 | { |
|---|
| | 955 | $this->form->bind($request->getParameter('login')); |
|---|
| | 956 | if ($this->form->isValid()) |
|---|
| | 957 | { |
|---|
| | 958 | // Login here |
|---|
| | 959 | $this->getUser()->setAuthenticated(true); |
|---|
| | 960 | |
|---|
| | 961 | // Go to the post module |
|---|
| | 962 | $this->redirect('post/index'); |
|---|
| | 963 | } |
|---|
| | 964 | } |
|---|
| | 965 | |
|---|
| | 966 | } |
|---|
| | 967 | } |
|---|
| | 968 | |
|---|
| | 969 | The action code works in much the same way as the generated actions we looked at |
|---|
| | 970 | previously. A form instance is instantiated, then if the operation is a POST *and* |
|---|
| | 971 | the form is valid then the system authorises the user and redirects them to the |
|---|
| | 972 | post page. |
|---|
| | 973 | |
|---|
| | 974 | Let's now make the post administration module the default one in our backend system. To |
|---|
| | 975 | do this, open up the `apps/backend/config/routing.yml` file and locate the |
|---|
| | 976 | 'homepage' key. Change the module from 'default' to 'post'. |
|---|