Completed
Push — 1 ( 1e411c...a166aa )
by Morven
03:24
created

Users_Register_Controller::RelativeLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
/**
4
 * Base controller class for users to register. Provides extension hooks to
5
 * allow third party overwriting of both index and register form actions
6
 *
7
 * This controller is also used to allow registered accounts to "verify"
8
 * their details via email.
9
 *
10
 * This is done by adding verified users to the groups stipulated by the
11
 * $verification_groups config variable
12
 *
13
 */
14
class Users_Register_Controller extends Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
15
{
16
17
    /**
18
     * URL That you can access this from
19
     *
20
     * @config
21
     */
22
    private static $url_segment = "users/register";
0 ignored issues
show
Unused Code introduced by
The property $url_segment is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
23
24
    /**
25
     * Current actions available to this controller
26
     *
27
     * @var array
28
     */
29
    private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
30
        "index",
31
        "sendverification",
32
        "verify",
33
        "RegisterForm"
34
    );
35
36
    /**
37
     * Internal function designed to allow us to send a verification
38
     * email from multiple locations
39
     *
40
     * @param $member Member object to send email to
41
     * @return boolean
42
     */
43
    protected function send_verification_email(Member $member)
44
    {
45
        if ($member) {
46
            $subject = _t("Users.PleaseVerify", "Please verify your account");
47
            if (Users::config()->send_email_from) {
48
                $from = Users::config()->send_email_from;
49
            } else {
50
                $from = Email::config()->admin_email;
51
            }
52
53
            $body = $this->renderWith(
54
                'UsersAccountVerification',
55
                array(
56
                    "Link" => Controller::join_links(
57
                        Director::absoluteBaseURL(),
58
                        $this->config()->url_segment,
59
                        "verify",
60
                        $member->ID,
61
                        $member->VerificationCode
62
                    )
63
                )
64
            );
65
66
            $email = new Email($from, $member->Email, $subject, $body);
67
            $email->sendPlain();
68
69
            return true;
70
        }
71
72
        return false;
73
    }
74
75
    /**
76
     * Get the link to this controller
77
     * 
78
     * @param string $action
0 ignored issues
show
Documentation introduced by
Should the type for parameter $action not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
79
     * @return string
80
     */
81
    public function Link($action = null)
82
    {
83
        return Controller::join_links(
84
            $this->config()->url_segment,
85
            $action
86
        );
87
    }
88
89
    /**
90
     * Get an absolute link to this controller
91
     *
92
     * @param string $action
0 ignored issues
show
Documentation introduced by
Should the type for parameter $action not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
93
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
94
     */
95
    public function AbsoluteLink($action = null)
96
    {
97
        return Director::absoluteURL($this->Link($action));
98
    }
99
100
    /**
101
     * Get a relative (to the root url of the site) link to this
102
     * controller
103
     *
104
     * @param string $action
0 ignored issues
show
Documentation introduced by
Should the type for parameter $action not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
105
     * @return string
106
     */
107
    public function RelativeLink($action = null)
108
    {
109
        return Controller::join_links(
110
            $this->Link($action)
111
        );
112
    }
113
114
    public function index(SS_HTTPRequest $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
115
    {
116
        $this->customise(array(
117
            'Title'     => "Register",
118
            'ClassName' => 'RegisterPage',
119
            'Content'   => '',
120
            'Form'      => $this->RegisterForm(),
121
        ));
122
123
        $this->extend("updateIndexAction");
124
125
        return $this->renderWith(array(
126
            "Users_Register",
127
            "Users",
128
            "Page"
129
        ));
130
    }
131
132
133
    /**
134
     * Send a verification email to the user provided (if verification
135
     * emails are enabled and account is not already verified)
136
     *
137
     */
138
    public function sendverification()
139
    {
140
        $sent = false;
0 ignored issues
show
Unused Code introduced by
$sent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
141
142
        if (Member::currentUserID()) {
143
            $member = Member::currentUser();
144
        } else {
145
            $member = Member::get()->byID($this->request->param("ID"));
146
        }
147
148
        if ($member && !$member->isVerified() && Users::config()->send_verification_email) {
149
            $sent = $this->send_verification_email($member);
150
        } else {
151
            $sent = false;
152
        }
153
154
        $this->customise(array(
155
            "ClassName" => "RegisterPage",
156
            "Sent" => $sent
157
        ));
158
159
        return $this->renderWith(array(
160
            "Users_Register_sendverification",
161
            "Users",
162
            "Page"
163
        ));
164
    }
165
166
167
    /**
168
     * Verify the provided user (ID) using the verification code (Other
169
     * ID) provided
170
     *
171
     */
172
    public function verify()
173
    {
174
        $member = Member::get()->byID($this->request->param("ID"));
175
        $code = $this->request->param("OtherID");
176
        $verify = false;
177
178
        // Check verification group exists, if not, make it
179
        // Add a verified users group (only used if we turn on
180
        // verification)
181
        $verify_groups = Group::get()
182
            ->filter("Code", Users::config()->verification_groups);
183
184
        $this->extend("onBeforeVerify", $member);
185
186
        if (($member && $code) && $code == $member->VerificationCode) {
187
            foreach ($verify_groups as $group) {
188
                $group->Members()->add($member);
189
                $verify = true;
190
            }
191
        }
192
193
        $this->customise(array(
194
            "ClassName" => "RegisterPage",
195
            "Verify" => $verify
196
        ));
197
198
        $this->extend("onAfterVerify", $member);
199
200
        return $this->renderWith(array(
201
            "Users_Register_verify",
202
            "Users",
203
            "Page"
204
        ));
205
    }
206
207
    /**
208
     * Registration form
209
     *
210
     * @return Form
211
     */
212
    public function RegisterForm()
0 ignored issues
show
Coding Style introduced by
RegisterForm uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
213
    {
214
215
        // If back URL set, push to session
216
        if (isset($_REQUEST['BackURL'])) {
217
            Session::set('BackURL', $_REQUEST['BackURL']);
218
        }
219
220
        // Setup form fields
221
        $fields = FieldList::create(
222
            TextField::create("FirstName"),
223
            TextField::create("Surname"),
224
            EmailField::create("Email"),
225
            ConfirmedPasswordField::create("Password")
226
        );
227
228
        // Setup form actions
229
        $actions = new FieldList(
230
            FormAction::create("doRegister", "Register")
231
                ->addExtraClass("btn")
232
                ->addExtraClass("btn-green")
233
        );
234
235
        // Setup required fields
236
        $required = new RequiredFields(array(
237
            "FirstName",
238
            "Surname",
239
            "Email",
240
            "Password"
241
        ));
242
243
        $form = Form::create($this, "RegisterForm", $fields, $actions, $required)
244
            ->addExtraClass("forms")
245
            ->addExtraClass("forms-columnar");
246
247
        $this->extend("updateRegisterForm", $form);
248
249
        $session_data = Session::get("Form.{$form->FormName()}.data");
250
251
        if ($session_data && is_array($session_data)) {
252
            $form->loadDataFrom($session_data);
253
            Session::clear("Form.{$form->FormName()}.data");
254
        }
255
256
        return $form;
257
    }
258
259
    /**
260
     * Register a new member. This action is deigned to be intercepted at 2
261
     * points:
262
     *
263
     *  - Modify the initial member filter (so that you can perfom bespoke
264
     *    member filtering
265
     *
266
     *  - Modify the member user before saving (so we can add extra permissions
267
     *    etc)
268
     *
269
     * @param array $data User submitted data
270
     * @param Form $form Registration form
271
     */
272
    public function doRegister($data, $form)
273
    {
274
        $filter = array();
275
276
        if (isset($data['Email'])) {
277
            $filter['Email'] = $data['Email'];
278
        }
279
280
        $this->extend("updateMemberFilter", $filter);
281
282
        // Check if a user already exists
283
        if ($member = Member::get()->filter($filter)->first()) {
284
            if ($member) {
285
                $form->addErrorMessage(
286
                    "Blurb",
287
                    "Sorry, an account already exists with those details.",
288
                    "bad"
289
                );
290
291
                // Load errors into session and post back
292
                unset($data["Password"]);
293
                Session::set("Form.{$form->FormName()}.data", $data);
0 ignored issues
show
Documentation introduced by
$data is of type array<string,?,{"Email":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
294
295
                return $this->redirectBack();
296
            }
297
        }
298
299
        $member = Member::create();
300
        $form->saveInto($member);
301
302
        // Set verification code for this user
303
        $member->VerificationCode = sha1(mt_rand() . mt_rand());
304
        $member->write();
305
306
        $this->extend("updateNewMember", $member, $data);
307
308
        // Add member to any groups that have been specified
309
        if (count(Users::config()->new_user_groups)) {
310
            $groups = Group::get()->filter(array(
311
                "Code" => Users::config()->new_user_groups
312
            ));
313
314
            foreach ($groups as $group) {
315
                $group->Members()->add($member);
316
                $group->write();
317
            }
318
        }
319
320
        // Send a verification email, if needed
321
        if (Users::config()->send_verification_email) {
322
            $sent = $this->send_verification_email($member);
0 ignored issues
show
Unused Code introduced by
$sent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
323
        } else {
324
            $sent = false;
0 ignored issues
show
Unused Code introduced by
$sent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
325
        }
326
327
        // Login (if enabled)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
328
        if (Users::config()->login_after_register) {
329
            $member->LogIn(isset($data['Remember']));
330
        }
331
332
        // If a back URL is used in session.
333
        if (Session::get("BackURL")) {
334
            $redirect_url = Session::get("BackURL");
335
        } else {
336
            $redirect_url = Controller::join_links(
337
                BASE_URL,
338
                Users_Account_Controller::config()->url_segment
339
            );
340
        }
341
342
        return $this->redirect($redirect_url);
343
    }
344
}
345