Completed
Pull Request — master (#452)
by Mark
29:33
created

AccountPage_Controller::setdefaultbilling()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 1
cts 1
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * Account page shows order history and a form to allow
5
 * the member to edit his/her details.
6
 *
7
 * @package shop
8
 */
9
class AccountPage extends Page
10
{
11
    private static $icon = 'silvershop/images/icons/account';
12
13 1
    public function canCreate($member = null, $context = array())
14
    {
15 1
        return !self::get()->exists();
16
    }
17
18
    /**
19
     * Returns the link or the URLSegment to the account page on this site
20
     *
21
     * @param boolean $urlSegment Return the URLSegment only
22
     *
23 2
     * @return mixed
24
     */
25 2
    public static function find_link($urlSegment = false)
26 2
    {
27
        $page = self::get_if_account_page_exists();
28
        return ($urlSegment) ? $page->URLSegment : $page->Link();
29
    }
30
31
    /**
32
     * Return a link to view the order on the account page.
33
     *
34
     * @param int|string $orderID    ID of the order
35 1
     * @param boolean    $urlSegment Return the URLSegment only
36
     *
37 1
     * @return string
38 1
     */
39
    public static function get_order_link($orderID, $urlSegment = false)
40
    {
41 2
        $page = self::get_if_account_page_exists();
42
        return ($urlSegment ? $page->URLSegment . '/' : $page->Link()) . 'order/' . $orderID;
43 2
    }
44 2
45
    /**
46
     * @return AccountPage
47
     */
48
    protected static function get_if_account_page_exists()
49
    {
50
        if ($page = DataObject::get_one('AccountPage')) {
51
            return $page;
52 2
        }
53
        user_error(_t('AccountPage.NO_PAGE', 'No AccountPage was found. Please create one in the CMS!'), E_USER_ERROR);
54
        return null; // just to keep static analysis happy
55
    }
56
57
    /**
58
     * This module always requires a page model.
59
     */
60
    public function requireDefaultRecords()
61
    {
62
        parent::requireDefaultRecords();
63
        if (!self::get()->exists() && $this->config()->create_default_pages) {
64
            /** @var AccountPage $page */
65
            $page = self::create(
66 2
                array(
67
                    'Title'       => 'Account',
68
                    'URLSegment'  => AccountPage_Controller::config()->url_segment,
69
                    'ShowInMenus' => 0,
70
                )
71
            );
72
            $page->write();
73
            $page->publish('Stage', 'Live');
74
            $page->flushCache();
75
            DB::alteration_message('Account page created', 'created');
76
        }
77
    }
78
}
79
80
class AccountPage_Controller extends Page_Controller
81
{
82
    private static $url_segment     = 'account';
83
84
    private static $allowed_actions = array(
85
        'addressbook',
86
        'CreateAddressForm',
87 4
        'DefaultAddressForm',
88
        'editprofile',
89 4
        'EditAccountForm',
90 4
        'ChangePasswordForm',
91
        'changepassword', // redirects to editprofile
92 4
        'deleteaddress',
93 4
        'setdefaultbilling',
94
        'setdefaultshipping',
95
    );
96
97 4
    /** @var Member|ShopMember */
98 4
    protected $member;
99 4
100
    public function init()
101
    {
102 4
        parent::init();
103 4
        if (!Member::currentUserID()) {
104 4
            $messages = array(
105 4
                'default'    => _t(
106
                    'AccountPage.LOGIN',
107 3
                    'You\'ll need to login before you can access the account page.
108 3
					If you are not registered, you won\'t be able to access it until
109
					you make your first order, otherwise please enter your details below.'
110 3
                ),
111
                'logInAgain' => _t(
112 3
                    'AccountPage.LOGINAGAIN',
113 3
                    'You have been logged out. If you would like to log in again,
114
					please do so below.'
115
                ),
116
            );
117
            Security::permissionFailure($this, $messages);
118 3
        } else {
119
            $this->member = Member::currentUser();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Member::currentUser() can also be of type object<DataObject>. However, the property $member is declared as type object<Member>|object<ShopMember>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
120 3
        }
121
    }
122
123 2
    public function getTitle()
124
    {
125
        if ($this->dataRecord && $title = $this->dataRecord->Title) {
126 2
            return $title;
127 2
        }
128 2
        return _t('AccountPage.Title', "Account");
129
    }
130
131 2
    public function getMember()
132
    {
133 2
        return $this->member;
134 2
    }
135 2
136 2
    public function addressbook()
137 2
    {
138 2
        return array(
139 2
            'DefaultAddressForm' => $this->DefaultAddressForm(),
140 2
            'CreateAddressForm'  => $this->CreateAddressForm(),
141 2
        );
142 2
    }
143 2
144 2
    public function DefaultAddressForm()
145 2
    {
146 2
        $addresses = $this->member->AddressBook()->sort('Created', 'DESC');
147 2
        if ($addresses->exists()) {
148 2
            $fields = FieldList::create(
149 2
                DropdownField::create(
150 2
                    "DefaultShippingAddressID",
151 2
                    _t("Address.ShippingAddress", "Shipping Address"),
152
                    $addresses->map('ID', 'toString')->toArray()
153 2
                ),
154
                DropdownField::create(
155
                    "DefaultBillingAddressID",
156 2
                    _t("Address.BillingAddress", "Billing Address"),
157
                    $addresses->map('ID', 'toString')->toArray()
158
                )
159
            );
160
            $actions = FieldList::create(
161
                FormAction::create("savedefaultaddresses", _t("Address.SaveDefaults", "Save Defaults"))
162
            );
163
            $form = Form::create($this, "DefaultAddressForm", $fields, $actions);
164
            $form->loadDataFrom($this->member);
165
166
            return $form;
167
        }
168
169 2
        return false;
170
    }
171 2
172 2
    public function savedefaultaddresses($data, $form)
173 2
    {
174 2
        $form->saveInto($this->member);
175 2
        $this->member->write();
0 ignored issues
show
Bug introduced by
The method write does only exist in Member, but not in ShopMember.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
176 2
177 2
        $this->extend('updateDefaultAddressFormResponse', $form, $data, $response);
0 ignored issues
show
Bug introduced by
The variable $response does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
178 2
179 2
        return $response ?: $this->redirect($this->Link('addressbook'));
180
    }
181
182 2
    public function CreateAddressForm()
183
    {
184 2
        $singletonaddress = singleton('Address');
185 2
        $fields = $singletonaddress->getFrontEndFields();
186 2
        $actions = FieldList::create(
187 2
            FormAction::create("saveaddress", _t("Address.SaveNew", "Save New Address"))
188
        );
189
        $validator = RequiredFields::create($singletonaddress->getRequiredFields());
190 2
        $form = Form::create($this, "CreateAddressForm", $fields, $actions, $validator);
191 1
        $this->extend('updateCreateAddressForm', $form);
192 1
        return $form;
193
    }
194 2
195
    public function saveaddress($data, $form)
196 2
    {
197
        $member = $this->getMember();
198
        $address = Address::create();
199
        $form->saveInto($address);
200 2
        $address->MemberID = $member->ID;
201
202
        // Add value for Country if missing (due readonly field in form)
203
        if ($country = SiteConfig::current_site_config()->getSingleCountry()) {
204 2
            $address->Country = $country;
205
        }
206 2
207
        $address->write();
208 2
209
        if (!$member->DefaultShippingAddressID) {
210
            $member->DefaultShippingAddressID = $address->ID;
211
            $member->write();
0 ignored issues
show
Bug introduced by
The method write does only exist in Member, but not in ShopMember.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
212
        }
213
        if (!$member->DefaultBillingAddressID) {
214
            $member->DefaultBillingAddressID = $address->ID;
215
            $member->write();
216
        }
217
        $form->sessionMessage(_t("CreateAddressForm.SAVED", "Your address has been saved"), "good");
218
219
        $this->extend('updateCreateAddressFormResponse', $form, $data, $response);
0 ignored issues
show
Bug introduced by
The variable $response does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
220
221
        return $response ?: $this->redirect($this->Link('addressbook'));
222
    }
223
224
    public function editprofile()
225
    {
226
        return array();
227
    }
228
229
    /**
230
     * @param SS_HTTPRequest $req
231
     * @return SS_HTTPResponse
232
     */
233
    function deleteaddress($req)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
234
    {
235
        // NOTE: we don't want to fully delete the address because it's presumably still
236
        // attached to an order. Setting MemberID to 0 means it won't show up in the address
237
        // book any longer.
238
        $address = $this->member->AddressBook()->byID($req->param('ID'));
239
        if ($address) {
240
            $address->MemberID = 0;
241
            $address->write();
242
        } else {
243
            $this->httpError(404, 'Address not found');
244
        }
245
        return $this->redirectBack();
246
    }
247
248
    /**
249
     * @param SS_HTTPRequest $req
250
     * @return SS_HTTPResponse
251
     */
252 2
    function setdefaultbilling($req)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
253
    {
254
        $this->member->DefaultBillingAddressID = $req->param('ID');
255
        $this->member->write();
0 ignored issues
show
Bug introduced by
The method write does only exist in Member, but not in ShopMember.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
256
        return $this->redirectBack();
257
    }
258
259
    /**
260
     * @param SS_HTTPRequest $req
261
     * @return SS_HTTPResponse
262
     */
263
    function setdefaultshipping($req)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
264
    {
265
        $this->member->DefaultShippingAddressID = $req->param('ID');
266
        $this->member->write();
0 ignored issues
show
Bug introduced by
The method write does only exist in Member, but not in ShopMember.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
267
        return $this->redirectBack();
268
    }
269
270
    /**
271
     * Return a form allowing the user to edit their details.
272
     *
273
     * @return ShopAccountForm
274
     */
275
    public function EditAccountForm()
276
    {
277
        return ShopAccountForm::create($this, 'EditAccountForm');
278
    }
279
280
    public function ChangePasswordForm()
281
    {
282
        $form = ChangePasswordForm::create($this, "ChangePasswordForm");
283
        $this->extend('updateChangePasswordForm', $form);
284
        $this->data()->extend('updateChangePasswordForm', $form);
285
286
        if ($this->data()->hasMethod('updateChangePasswordForm')) {  // if accessing through the model
287
            Deprecation::notice(
288
                '2.0',
289
                'Please access updateChangePasswordForm through AccountPage_Controller instead of AccountPage (this extension point is due to be removed)'
290
            );
291
        }
292
293
        return $form;
294
    }
295
296
    /**
297
     * By default, ChangePasswordForm redirects to /account/changepassword when it's done.
298
     * This catches that and sends it back to editprofile, which seems easier and less error-prone
299
     * than the alternative of trying to manipulate the BackURL field.
300
     */
301
    public function changepassword()
302
    {
303
        $this->redirect($this->Link('editprofile'));
304
    }
305
}
306