Completed
Push — master ( 622ec2...ab344f )
by Nicolaas
09:34 queued 05:24
created

OrderConfirmationPage_Controller::sendreceipt()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 42
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
dl 0
loc 42
rs 8.439
c 0
b 0
f 0
eloc 34
nc 16
nop 1

2 Methods

Rating   Name   Duplication   Size   Complexity  
D OrderConfirmationPage_Controller::CheckoutSteps() 0 30 9
A OrderConfirmationPage_Controller::PercentageDone() 0 4 1
1
<?php
2
3
/**
4
 * @description:
5
 * The Order Confirmation page shows order history.
6
 * It also serves as the end point for the current order...
7
 * once submitted, the Order Confirmation page shows the
8
 * finalised detail of the order.
9
 *
10
 *
11
 * @authors: Nicolaas [at] Sunny Side Up .co.nz
12
 * @package: ecommerce
13
 * @sub-package: Pages
14
 * @inspiration: Silverstripe Ltd, Jeremy
15
 **/
16
class OrderConfirmationPage extends CartPage
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...
17
{
18
    /**
19
     * Standard SS variable.
20
     *
21
     * @var string
22
     */
23
    private static $icon = 'ecommerce/images/icons/OrderConfirmationPage';
0 ignored issues
show
Unused Code introduced by
The property $icon 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
24
25
    /**
26
     * Standard SS variable.
27
     *
28
     * @var array
29
     */
30
    private static $db = array(
0 ignored issues
show
Unused Code introduced by
The property $db 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
31
        'StartNewOrderLinkLabel' => 'Varchar(100)',
32
        'CopyOrderLinkLabel' => 'Varchar(100)',
33
        'OrderCancelledHeader' => 'Varchar(255)',
34
        'PaymentSuccessfulHeader' => 'Varchar(255)',
35
        'PaymentNotSuccessfulHeader' => 'Varchar(255)',
36
        'PaymentPendingHeader' => 'Varchar(255)',
37
        'OrderCancelledMessage' => 'HTMLText',
38
        'PaymentSuccessfulMessage' => 'HTMLText',
39
        'PaymentNotSuccessfulMessage' => 'HTMLText',
40
        'PaymentPendingMessage' => 'HTMLText',
41
        'EnableGoogleAnalytics' => 'Boolean',
42
    );
43
44
    /**
45
     * Standard SS variable.
46
     *
47
     * @var array
48
     */
49
    private static $defaults = array(
0 ignored issues
show
Unused Code introduced by
The property $defaults 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
50
        'ShowInMenus' => false,
51
        'ShowInSearch' => false,
52
        'StartNewOrderLinkLabel' => 'start new order',
53
        'CopyOrderLinkLabel' => 'copy order items into a new order',
54
        'OrderCancelledHeader' => 'Order has been cancelled',
55
        'PaymentSuccessfulHeader' => 'Payment Successful',
56
        'PaymentNotSuccessfulHeader' => 'Payment not Completed',
57
        'PaymentPendingHeader' => 'Payment Pending',
58
        'OrderCancelledMessage' => '<p>This order is no longer valid.</p>',
59
        'PaymentSuccessfulMessage' => '<p>Your order will be processed.</p>',
60
        'PaymentNotSuccessfulMessage' => '<p>Your order will not be processed until your payment has been completed.</p>',
61
        'PaymentPendingMessage' => '<p>Please complete your payment before the order can be processed.</p>',
62
    );
63
64
    private static $casting = array(
0 ignored issues
show
Unused Code introduced by
The property $casting 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
65
        "PaymentMessage" => "HTMLText"
66
    );
67
68
    /**
69
     * standard SS variable.
70
     *
71
     * @Var String
72
     */
73
    private static $singular_name = 'Order Confirmation Page';
0 ignored issues
show
Unused Code introduced by
The property $singular_name 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
74
    public function i18n_singular_name()
75
    {
76
        return _t('OrderConfirmationpage.SINGULARNAME', 'Order Confirmation Page');
77
    }
78
79
    /**
80
     * standard SS variable.
81
     *
82
     * @Var String
83
     */
84
    private static $plural_name = 'Order Confirmation Pages';
0 ignored issues
show
Unused Code introduced by
The property $plural_name 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
85
    public function i18n_plural_name()
86
    {
87
        return _t('OrderConfirmationpage.PLURALNAME', 'Order Confirmation Pages');
88
    }
89
90
    /**
91
     * Standard SS variable.
92
     *
93
     * @var string
94
     */
95
    private static $description = 'A page where the customer can view her or his submitted order. Every e-commerce site needs an Order Confirmation Page.';
0 ignored issues
show
Unused Code introduced by
The property $description 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
96
97
    /**
98
     * Standard SS function, we only allow for one OrderConfirmation Page to exist
99
     * but we do allow for extensions to exist at the same time.
100
     *
101
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|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...
102
     *
103
     * @return bool
104
     */
105
    public function canCreate($member = null)
106
    {
107
        return OrderConfirmationPage::get()->filter(array('ClassName' => 'OrderConfirmationPage'))->Count() ? false : $this->canEdit($member);
108
    }
109
110
    /**
111
     * Shop Admins can edit.
112
     *
113
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|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...
114
     *
115
     * @return bool
116
     */
117
    public function canEdit($member = null)
118
    {
119
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
120
            return true;
121
        }
122
123
        return parent::canEdit($member);
124
    }
125
126
    /**
127
     * Standard SS method.
128
     *
129
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|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...
130
     *
131
     * @return bool
132
     */
133
    public function canDelete($member = null)
134
    {
135
        return false;
136
    }
137
138
    /**
139
     * Standard SS method.
140
     *
141
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|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...
142
     *
143
     * @return bool
144
     */
145
    public function canPublish($member = null)
146
    {
147
        return $this->canEdit($member);
148
    }
149
150
    public function customFieldLabels()
151
    {
152
        $newLabels = array(
153
            'StartNewOrderLinkLabel' => _t('OrderConfirmationPage.STARTNEWORDERLINKLABEL', 'Label for starting new order - e.g. click here to start new order.'),
154
            'CopyOrderLinkLabel' => _t('OrderConfirmationPage.COPYORDERLINKLABEL', 'Label for copying order items into a new one  - e.g. click here start a new order with the current order items.'),
155
            'OrderCancelledHeader' => _t('OrderConfirmationPage.ORDERCANCELLEDHEADER', 'Header showing when order has been cancelled.'),
156
            'PaymentSuccessfulHeader' => _t('OrderConfirmationPage.PAYMENTSUCCESSFULHEADER', 'Header showing when order has been paid in full.'),
157
            'PaymentNotSuccessfulHeader' => _t('OrderConfirmationPage.PAYMENTNOTSUCCESSFULHEADER', 'Header showing when the order has not been paid in full.'),
158
            'PaymentPendingHeader' => _t('OrderConfirmationPage.PAYMENTPENDINGHEADER', 'Header showing when the order has not been paid in full - but the payment is pending.'),
159
            'OrderCancelledMessage' => _t('OrderConfirmationPage.ORDERCANCELLEDMESSAGE', 'Message showing when order has been paid cancelled.'),
160
            'PaymentSuccessfulMessage' => _t('OrderConfirmationPage.PAYMENTSUCCESSFULMESSAGE', 'Message showing when order has been paid in full.'),
161
            'PaymentNotSuccessfulMessage' => _t('OrderConfirmationPage.PAYMENTNOTSUCCESSFULMESSAGE', 'Message showing when the order has not been paid in full.'),
162
            'PaymentPendingMessage' => _t('OrderConfirmationPage.PAYMENTPENDINGMESSAGE', 'Message showing when the order has not been paid in full - but the payment is pending.'),
163
            'EnableGoogleAnalytics' => _t('OrderConfirmationPage.ENABLEGOOGLEANALYTICS', 'Enable E-commerce Google Analytics.  Make sure it is turned on in your Google Analytics account.'),
164
        );
165
166
        return $newLabels;
167
    }
168
169
    /**
170
     * standard SS method for decorators.
171
     *
172
     * @param bool - $includerelations: array of fields to start with
173
     *
174
     * @return array
175
     */
176
    public function fieldLabels($includerelations = true)
177
    {
178
        $defaultLabels = parent::fieldLabels();
179
        $newLabels = $this->customFieldLabels();
180
        $labels = array_merge($defaultLabels, $newLabels);
181
        $extendedArray = $this->extend('updateFieldLabels', $labels);
182
        if ($extendedArray !== null && is_array($extendedArray) && count($extendedArray)) {
183
            foreach ($extendedArray as $extendedResult) {
184
                $labels = array_merge($labels, $extendedResult);
185
            }
186
        }
187
188
        return $labels;
189
    }
190
191
    /**
192
     *@return FieldList
193
     **/
194
    public function getCMSFields()
195
    {
196
        $fields = parent::getCMSFields();
197
        $fields->removeFieldFromTab('Root.Messages.Messages.Actions', 'ProceedToCheckoutLabel');
198
        $fields->removeFieldFromTab('Root.Messages.Messages.Actions', 'ContinueShoppingLabel');
199
        $fields->removeFieldFromTab('Root.Messages.Messages.Actions', 'ContinuePageID');
200
        $fields->removeFieldFromTab('Root.Messages.Messages.Actions', 'SaveOrderLinkLabel');
201
        $fields->removeFieldFromTab('Root.Messages.Messages.Errors', 'NoItemsInOrderMessage');
202
        $fieldLabels = $this->fieldLabels();
203
        $fields->addFieldToTab('Root.Messages.Messages.Actions', new TextField('StartNewOrderLinkLabel', $fieldLabels['StartNewOrderLinkLabel']));
204
        $fields->addFieldToTab('Root.Messages.Messages.Actions', new TextField('CopyOrderLinkLabel', $fieldLabels['CopyOrderLinkLabel']));
205
        $fields->addFieldsToTab('Root.Messages.Messages.Payment', array(
206
            HeaderField::create('Successful'),
207
            TextField::create('PaymentSuccessfulHeader', $fieldLabels['PaymentSuccessfulHeader']),
208
            HTMLEditorField::create('PaymentSuccessfulMessage', $fieldLabels['PaymentSuccessfulMessage'])->setRows(3),
209
            HeaderField::create('Unsuccessful'),
210
            TextField::create('PaymentNotSuccessfulHeader', $fieldLabels['PaymentNotSuccessfulHeader']),
211
            HTMLEditorField::create('PaymentNotSuccessfulMessage', $fieldLabels['PaymentNotSuccessfulMessage'])->setRows(3),
212
            HeaderField::create('Pending'),
213
            TextField::create('PaymentPendingHeader', $fieldLabels['PaymentPendingHeader']),
214
            HTMLEditorField::create('PaymentPendingMessage', $fieldLabels['PaymentPendingMessage'])->setRows(3),
215
            HeaderField::create('Cancelled'),
216
            TextField::create('OrderCancelledHeader', $fieldLabels['OrderCancelledHeader']),
217
            HTMLEditorField::create('OrderCancelledMessage', $fieldLabels['OrderCancelledMessage'])->setRows(3),
218
        ));
219
        $fields->addFieldToTab('Root.Analytics', new CheckboxField('EnableGoogleAnalytics', $fieldLabels['EnableGoogleAnalytics']));
220
221
        return $fields;
222
    }
223
224
    /**
225
     * Returns the link or the Link to the OrderConfirmationPage page on this site.
226
     * @param string $action [optional]
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...
227
     * @return string (URLSegment)
228
     */
229
    public static function find_link($action = null)
230
    {
231
        if ($page = OrderConfirmationPage::get()->filter(array('ClassName' => 'OrderConfirmationPage'))->First()) {
232
            return $page->Link($action);
233
        } elseif ($page = OrderConfirmationPage::get()->First()) {
234
            return $page->Link($action);
235
        }
236
237
        return CartPage::find_link();
238
    }
239
240
    /**
241
     * Return a link to view the order on this page.
242
     *
243
     * @param int|string $orderID ID of the order
244
     *
245
     * @return string (URLSegment)
246
     */
247
    public static function get_order_link($orderID)
248
    {
249
        return OrderConfirmationPage::find_link().'showorder/'.$orderID.'/';
250
    }
251
252
    /**
253
     * Return a link to view the order on this page.
254
     *
255
     * @param int|string $orderID                ID of the order
256
     * @param string     $type                   - the type of email you want to send.
0 ignored issues
show
Bug introduced by
There is no parameter named $type. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
257
     * @param bool       $actuallySendEmail      - do we actually send the email
258
     * @param int        $alternativeOrderStepID - OrderStep to use
259
     *
260
     * @return string (URLSegment)
261
     */
262
    public static function get_email_link($orderID, $emailClassName = 'Order_StatusEmail', $actuallySendEmail = false, $alternativeOrderStepID = 0)
263
    {
264
        $link = OrderConfirmationPage::find_link().'sendemail/'.$orderID.'/'.$emailClassName;
265
        $getParams = array();
266
        if ($actuallySendEmail) {
267
            $getParams['send'] = 1;
268
        }
269
        if ($alternativeOrderStepID) {
270
            $getParams['use'] = $alternativeOrderStepID;
271
        }
272
        $getParams = http_build_query($getParams);
273
        $link .= '?'.$getParams;
274
275
        return $link;
276
    }
277
278
    /**
279
     * Return a link to view the order on this page.
280
     *
281
     * @param int|string $orderID ID of the order
282
     *
283
     * @return string (URLSegment)
284
     */
285
    public function getOrderLink($orderID)
286
    {
287
        return OrderConfirmationPage::get_order_link($orderID);
288
    }
289
290
    /**
291
     * returns the Checkout_StepDescription assocatiated with the final step: the order confirmation.
292
     *
293
     * @param bool $isCurrentStep
294
     *
295
     * @return Checkout_StepDescription
0 ignored issues
show
Documentation introduced by
Should the return type not be CheckoutPage_StepDescription?

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...
296
     */
297
    public function CurrentCheckoutStep($isCurrentStep = false)
298
    {
299
        $do = new CheckoutPage_StepDescription();
300
        $do->Link = $this->Link;
0 ignored issues
show
Documentation introduced by
The property Link does not exist on object<CheckoutPage_StepDescription>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
301
        $do->Heading = $this->MenuTitle;
0 ignored issues
show
Documentation introduced by
The property Heading does not exist on object<CheckoutPage_StepDescription>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
302
        $do->Code = $this->URLSegment;
0 ignored issues
show
Documentation introduced by
The property Code does not exist on object<CheckoutPage_StepDescription>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
303
        $do->LinkingMode = 'notCompleted';
0 ignored issues
show
Documentation introduced by
The property LinkingMode does not exist on object<CheckoutPage_StepDescription>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
304
        if ($isCurrentStep) {
305
            $do->LinkingMode .= ' current';
0 ignored issues
show
Documentation introduced by
The property LinkingMode does not exist on object<CheckoutPage_StepDescription>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
306
        }
307
        $do->Completed = 0;
0 ignored issues
show
Documentation introduced by
The property Completed does not exist on object<CheckoutPage_StepDescription>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
308
        $do->ID = 99;
309
310
        return $do;
311
    }
312
313
    /**
314
     * standard SS method for use in templates
315
     * we are overriding the code from the Cart Page here.
316
     *
317
     * @return string
318
     */
319
    public function LinkingMode()
320
    {
321
        return parent::LinkingMode();
322
    }
323
324
    /**
325
     * standard SS method for use in templates
326
     * we are overriding the code from the Cart Page here.
327
     *
328
     * @return string
329
     */
330
    public function LinkOrSection()
331
    {
332
        return parent::LinkOrSection();
333
    }
334
335
    /**
336
     * standard SS method for use in templates
337
     * we are overriding the code from the Cart Page here.
338
     *
339
     * @return string
340
     */
341
    public function LinkOrCurrent()
342
    {
343
        return parent::LinkOrCurrent();
344
    }
345
346
    public function requireDefaultRecords()
347
    {
348
        parent::requireDefaultRecords();
349
        $checkoutPage = CheckoutPage::get()->first();
350
        if ($checkoutPage) {
351
            $orderConfirmationPage = OrderConfirmationPage::get()->first();
352
            if (!$orderConfirmationPage) {
353
                $orderConfirmationPage = OrderConfirmationPage::create();
354
                $orderConfirmationPage->Title = 'Order Confirmation';
355
                $orderConfirmationPage->MenuTitle = 'Order Confirmation';
356
                $orderConfirmationPage->URLSegment = 'order-confirmation';
357
                $orderConfirmationPage->writeToStage('Stage');
358
                $orderConfirmationPage->publish('Stage', 'Live');
359
            }
360
        }
361
    }
362
}
363
364
class OrderConfirmationPage_Controller extends CartPage_Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
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...
365
{
366
    /**
367
     * @static array
368
     * standard SS variable
369
     * it is important that we list all the options here
370
     */
371
    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...
372
        'saveorder',
373
        'CreateAccountForm',
374
        'retrieveorder',
375
        'loadorder',
376
        'startneworder',
377
        'showorder',
378
        'copyorder',
379
        'sendemail',
380
        'CancelForm',
381
        'PaymentForm',
382
    );
383
384
    /**
385
     * standard controller function.
386
     **/
387
    public function init()
388
    {
389
        //we retrieve the order in the parent page
390
        //the parent page also takes care of the security
391
        if ($sessionOrderID = Session::get('CheckoutPageCurrentOrderID')) {
392
            $this->currentOrder = Order::get()->byID($sessionOrderID);
393
            if ($this->currentOrder) {
394
                $this->overrideCanView = true;
395
                //more than an hour has passed...
396
                if (strtotime($this->currentOrder->LastEdited) < (strtotime('Now') - 60 * 60)) {
397
                    Session::clear('CheckoutPageCurrentOrderID');
398
                    Session::clear('CheckoutPageCurrentOrderID');
399
                    Session::set('CheckoutPageCurrentOrderID', 0);
400
                    Session::save();
401
                    $this->overrideCanView = false;
402
                    $this->currentOrder = null;
403
                }
404
            }
405
        }
406
        parent::init();
407
        Requirements::themedCSS('Order', 'ecommerce');
408
        Requirements::themedCSS('Order_Print', 'ecommerce', 'print');
409
        Requirements::themedCSS('CheckoutPage', 'ecommerce');
410
        Requirements::javascript('ecommerce/javascript/EcomPayment.js');
411
        Requirements::javascript('ecommerce/javascript/EcomPrintAndMail.js');
412
        $this->includeGoogleAnalyticsCode();
413
    }
414
415
    /**
416
     * This method exists just so that template
417
     * sets CurrentOrder variable.
418
     *
419
     * @param HTTPRequest
420
     *
421
     * @return array
422
     **/
423
    public function showorder(SS_HTTPRequest $request)
0 ignored issues
show
Coding Style introduced by
showorder 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...
424
    {
425
        isset($project) ? $themeBaseFolder = $project : $themeBaseFolder = 'mysite';
0 ignored issues
show
Bug introduced by
The variable $project seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
426
        if (isset($_REQUEST['print'])) {
427
            Requirements::clear();
428
            Requirements::themedCSS('typography', $themeBaseFolder); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
429
            Requirements::themedCSS('OrderReport', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
430
            Requirements::themedCSS('Order_Invoice', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
431
            Requirements::themedCSS('Order_Invoice_Print_Only', 'ecommerce', 'print'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
432
            Config::nest();
433
            Config::inst()->update('SSViewer', 'theme_enabled', true);
434
            $html = $this->renderWith('Invoice');
435
            Config::unnest();
436
437
            return $html;
438
        } elseif (isset($_REQUEST['packingslip'])) {
439
            Requirements::clear();
440
            Requirements::themedCSS('typography', $themeBaseFolder); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
441
            Requirements::themedCSS('OrderReport', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
442
            Requirements::themedCSS('Order_PackingSlip', 'ecommerce'); // LEAVE HERE - NOT EASY TO INCLUDE VIA TEMPLATE
443
            Config::nest();
444
            Config::inst()->update('SSViewer', 'theme_enabled', true);
445
            $html = $this->renderWith('PackingSlip');
446
            Config::unnest();
447
448
            return $html;
449
        }
450
451
        return array();
452
    }
453
454
    /**
455
     * This is an additional way to look at an order.
456
     * The order is already retrieved from the init function.
457
     *
458
     * @return array
459
     **/
460
    public function retrieveorder(SS_HTTPRequest $request)
461
    {
462
        return array();
463
    }
464
465
    /**
466
     * copies either the current order into the shopping cart.
467
     *
468
     * TO DO: untested
469
     * TO DO: what to do with old order
470
     *
471
     * @param SS_HTTPRequest
472
     *
473
     * @return array
474
     */
475
    public function copyorder(SS_HTTPRequest $request)
476
    {
477
        self::set_message(_t('CartPage.ORDERLOADED', 'Order has been loaded.'));
478
        ShoppingCart::singleton()->copyOrder($this->currentOrder->ID);
479
480
        return $this->redirect(CheckoutPage::find_link());
481
    }
482
483
    /**
484
     * Returns a dataobject set of the checkout steps if
485
     * the OrderConfirmationPage is shown as part of the checkout process
486
     * We repeat these here so that you can show the user that (s)he has reached the last step.
487
     *
488
     * @param int $number - if set, it returns that one step.
489
     */
490
    public function CheckoutSteps($number = 0)
491
    {
492
        $where = '';
493
        if ($number) {
494
            $where = "\"CheckoutPage_StepDescription\".\"ID\" = $number";
495
        }
496
        if (EcommerceConfig::get('OrderConfirmationPage_Controller', 'include_as_checkout_step')) {
497
            if ($this->currentOrder->IsInSession()) {
498
                $dos = CheckoutPage_StepDescription::get()->where($where)->sort('ID', 'ASC');
499
                if ($number) {
500
                    if ($dos && $dos->count()) {
501
                        return $dos->First();
502
                    }
503
                }
504
                $arrayList = new ArrayList(array());
505
                foreach ($dos as $do) {
506
                    $do->LinkingMode = 'link completed';
507
                    $do->Completed = 1;
508
                    $do->Link = '';
509
                    $arrayList->push($do);
510
                }
511
                $do = $this->CurrentCheckoutStep(true);
512
                if ($do) {
513
                    $arrayList->push($do);
514
                }
515
516
                return $arrayList;
517
            }
518
        }
519
    }
520
521
    /**
522
     * returns the percentage of checkout steps done (0 - 100).
523
     *
524
     * @return int
525
     */
526
    public function PercentageDone()
527
    {
528
        return 100;
529
    }
530
531
    /**
532
     * @return string
533
     */
534
    public function PaymentHeader()
535
    {
536
        if ($order = $this->Order()) {
0 ignored issues
show
Unused Code introduced by
$order 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...
537
            if ($this->OrderIsCancelled()) {
538
                return $this->OrderCancelledHeader;
539
            } elseif ($this->PaymentIsPending()) {
540
                return $this->PaymentPendingHeader;
541
            } elseif ($this->IsPaid()) {
542
                return $this->PaymentSuccessfulHeader;
543
            } else {
544
                return $this->PaymentNotSuccessfulHeader;
545
            }
546
        }
547
    }
548
549
    /**
550
     * @return string | null
551
     */
552
    public function PaymentMessage()
553
    {
554
        if ($order = $this->Order()) {
0 ignored issues
show
Unused Code introduced by
$order 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...
555
            if ($this->OrderIsCancelled()) {
556
                return $this->OrderCancelledMessage;
557
            } elseif ($this->PaymentIsPending()) {
558
                return $this->PaymentPendingMessage;
559
            } elseif ($this->IsPaid()) {
560
                return $this->PaymentSuccessfulMessage;
561
            } else {
562
                return $this->PaymentNotSuccessfulMessage;
563
            }
564
        }
565
    }
566
567
    /**
568
     * @return string | null
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

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...
569
     */
570
    public function PaymentMessageType()
571
    {
572
        if ($order = $this->Order()) {
0 ignored issues
show
Unused Code introduced by
$order 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...
573
            if ($this->OrderIsCancelled()) {
574
                return "bad";
575
            } elseif ($this->PaymentIsPending()) {
576
                return "warning";
577
            } elseif ($this->IsPaid()) {
578
                return "good";
579
            } else {
580
                return "bad";
581
            }
582
        }
583
    }
584
585
    /**
586
     * @return bool
587
     */
588
    public function OrderIsCancelled()
589
    {
590
        if ($order = $this->Order()) {
591
            return $order->getIsCancelled();
592
        }
593
    }
594
595
    /**
596
     * Is the Order paid?
597
     * This can be useful for choosing what header to show.
598
     *
599
     * @return bool
600
     */
601
    public function IsPaid()
602
    {
603
        if ($order = $this->Order()) {
604
            return $order->IsPaid();
605
        }
606
    }
607
608
    /**
609
     * Are there any order Payments Pending
610
     * This can be useful for choosing what header to show.
611
     *
612
     * @return bool
613
     */
614
    public function PaymentIsPending()
615
    {
616
        if ($order = $this->Order()) {
617
            return $order->PaymentIsPending();
618
        }
619
    }
620
621
    /**
622
     * Returns the form to cancel the current order,
623
     * checking to see if they can cancel their order
624
     * first of all.
625
     *
626
     * @return OrderForm_Cancel
0 ignored issues
show
Documentation introduced by
Should the return type not be OrderConfirmationPage_Controller|array|null?

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...
627
     */
628
    public function CancelForm()
629
    {
630
        if ($this->Order()) {
631
            if ($this->currentOrder->canCancel()) {
632
                return OrderForm_Cancel::create($this, 'CancelForm', $this->currentOrder);
633
            }
634
        }
635
        //once cancelled, you will be redirected to main page - hence we need this...
636
        if ($this->orderID) {
637
            return array();
638
        }
639
    }
640
641
    /**
642
     * show the payment form.
643
     *
644
     * @return Form (OrderForm_Payment) or Null
0 ignored issues
show
Documentation introduced by
Should the return type not be OrderConfirmationPage_Controller|array?

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...
645
     **/
646
    public function PaymentForm()
647
    {
648
        if ($this->currentOrder) {
649
            if ($this->currentOrder->canPay()) {
650
                Requirements::javascript('ecommerce/javascript/EcomPayment.js');
651
652
                return OrderForm_Payment::create($this, 'PaymentForm', $this->currentOrder);
653
            }
654
        }
655
656
        return array();
657
    }
658
659
    /**
660
     * Can this page only show Submitted Orders (e.g. OrderConfirmationPage) ?
661
     *
662
     * @return bool
663
     */
664
    protected function onlyShowSubmittedOrders()
665
    {
666
        return true;
667
    }
668
669
    /**
670
     * Can this page only show Unsubmitted Orders (e.g. CartPage) ?
671
     *
672
     * @return bool
673
     */
674
    protected function onlyShowUnsubmittedOrders()
675
    {
676
        return false;
677
    }
678
679
    /**
680
     * sends an order email, which can be specified in the URL
681
     * and displays a sample email
682
     * typically this link is opened in a new window.
683
     *
684
     * @param SS_HTTPRequest $request
685
     *
686
     * @return HTML
687
     **/
688
    public function sendemail(SS_HTTPRequest $request)
689
    {
690
        if ($this->currentOrder) {
691
            $emailClassName = 'Order_ReceiptEmail';
692
            if (class_exists($request->param('OtherID'))) {
693
                if (is_a(singleton($request->param('OtherID')), Object::getCustomClass('Order_Email'))) {
694
                    $emailClassName = $request->param('OtherID');
695
                }
696
            }
697
            if ($statusID = intval($request->getVar('use'))) {
698
                $subject = _t('Account.TEST_ONLY', '--- TEST ONLY ---');
699
                $step = OrderStep::get()->byID($statusID);
700
                if ($step) {
701
                    $emailClassName = $step->getEmailClassName();
702
                }
703
                if ($request->getVar('send')) {
704
                    $email = filter_var($request->getVar('send'), FILTER_SANITIZE_EMAIL);
705
                    if(! $email) {
706
                        $email = true;
707
                    }
708
                    $this->currentOrder->sendEmail(
709
                        $subject,
710
                        $message,
0 ignored issues
show
Bug introduced by
The variable $message seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
711
                        $resend = true,
712
                        $adminOnlyOrToEmail = $email,
713
                        $emailClassName
714
                    );
715
                }
716
            }
717
            if ($request->getVar('send')) {
718
                if ($email = $this->currentOrder->getOrderEmail()) {
719
                    $subject = _t('Account.COPYONLY', '--- COPY ONLY ---');
720
                    $message = _t('Account.COPYONLY', '--- COPY ONLY ---');
721
                    if (
722
                        $this->currentOrder->sendEmail(
723
                            $subject,
724
                            $message,
725
                            $resend = true,
726
                            $adminOnlyOrToEmail = false,
727
                            $emailClassName
728
                        )
729
                    ) {
730
                        $this->message = _t('OrderConfirmationPage.RECEIPTSENT', 'An email has been sent to: ').$email.'.';
0 ignored issues
show
Bug introduced by
The property message cannot be accessed from this context as it is declared private in class CartPage_Controller.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
731
                    } else {
732
                        $this->message = _t('OrderConfirmationPage.RECEIPT_NOT_SENT', 'Email sent unsuccesfully to: ').$email.'. EMAIL NOT SENT.';
0 ignored issues
show
Bug introduced by
The property message cannot be accessed from this context as it is declared private in class CartPage_Controller.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
733
                    }
734
                } else {
735
                    $this->message = _t('OrderConfirmationPage.RECEIPTNOTSENTNOEMAIL', 'No customer details found.  EMAIL NOT SENT.');
0 ignored issues
show
Bug introduced by
The property message cannot be accessed from this context as it is declared private in class CartPage_Controller.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
736
                }
737
            }
738
            //display same data...
739
            Requirements::clear();
740
741
            return $this->currentOrder->renderOrderInEmailFormat($this->message, $emailClassName);
0 ignored issues
show
Bug introduced by
The property message cannot be accessed from this context as it is declared private in class CartPage_Controller.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
742
        } else {
743
            return _t('OrderConfirmationPage.RECEIPTNOTSENTNOORDER', 'Order could not be found.');
744
        }
745
    }
746
747
    protected function includeGoogleAnalyticsCode()
748
    {
749
        if ($this->EnableGoogleAnalytics && $this->currentOrder && Director::isLive()) {
750
            $currencyUsedObject = $this->currentOrder->CurrencyUsed();
751
            if ($currencyUsedObject) {
752
                $currencyUsedString = $currencyUsedObject->Code;
753
            }
754
            if (empty($currencyUsedString)) {
755
                $currencyUsedString = EcommerceCurrency::default_currency_code();
756
            }
757
            $js = '
758
            jQuery(document).ready(
759
                function(){
760
                    _gaq(\'require\', \'ecommerce\');
761
                    _gaq(
762
                        \'ecommerce:addTransaction\',
763
                        {
764
                            \'id\': \''.$this->currentOrder->ID.'\',
765
                            \'revenue\': \''.$this->currentOrder->getSubTotal().'\',
766
                            \'currency\': \''.$currencyUsedString.'\'
767
                        }
768
                    );
769
                    _gaq(\'ecommerce:send\');
770
                }
771
            );
772
';
773
            Requirements::customScript($js, 'GoogleAnalyticsEcommerce');
774
        }
775
    }
776
}
777