Passed
Push — master ( 5a6133...83a442 )
by Jason
03:06
created

ProductPage::getCartScript()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Dynamic\FoxyStripe\Page;
4
5
use Dynamic\FoxyStripe\Model\FoxyCart;
6
use Dynamic\FoxyStripe\Model\FoxyStripeSetting;
7
use Dynamic\FoxyStripe\Model\OptionItem;
8
use Dynamic\FoxyStripe\Model\OrderDetail;
9
use Dynamic\FoxyStripe\Model\ProductCategory;
10
use Dynamic\FoxyStripe\Model\ProductImage;
11
use SilverStripe\AssetAdmin\Forms\UploadField;
12
use SilverStripe\Assets\Image;
13
use SilverStripe\Forms\CheckboxField;
14
use SilverStripe\Forms\CurrencyField;
15
use SilverStripe\Forms\DropdownField;
16
use SilverStripe\Forms\GridField\GridField;
17
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
18
use SilverStripe\Forms\HeaderField;
19
use SilverStripe\Forms\LiteralField;
20
use SilverStripe\Forms\NumericField;
21
use SilverStripe\Forms\RequiredFields;
22
use SilverStripe\Forms\TextField;
23
use SilverStripe\Security\Member;
24
use SilverStripe\Security\Permission;
25
use SilverStripe\Security\PermissionProvider;
26
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
27
28
/**
29
 * Class ProductPage
30
 * @package Dynamic\FoxyStripe\Page
31
 *
32
 * @property \SilverStripe\ORM\FieldType\DBCurrency Price
33
 * @property \SilverStripe\ORM\FieldType\DBDecimal Weight
34
 * @property \SilverStripe\ORM\FieldType\DBVarchar Code
35
 * @property \SilverStripe\ORM\FieldType\DBVarchar ReceiptTitle
36
 * @property \SilverStripe\ORM\FieldType\DBBoolean Featured
37
 * @property \SilverStripe\ORM\FieldType\DBBoolean Available
38
 *
39
 * @property int PreviewImageID
40
 * @method Image PreviewImage
41
 * @property int CategoryID
42
 * @method ProductCategory Category
43
 *
44
 *
45
 * @method \SilverStripe\ORM\HasManyList ProductImages
46
 * @method \SilverStripe\ORM\HasManyList ProductOptions
47
 * @method \SilverStripe\ORM\HasManyList OrderDetails
48
 *
49
 * @method \SilverStripe\ORM\ManyManyList ProductHolders
50
 */
51
class ProductPage extends \Page implements PermissionProvider
52
{
53
    /**
54
     * @var string
55
     */
56
    //private static $allowed_children = [];
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% 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...
57
58
    /**
59
     * @var string
60
     */
61
    private static $default_parent = ProductHolder::class;
0 ignored issues
show
introduced by
The private property $default_parent is not used, and could be removed.
Loading history...
62
63
    /**
64
     * @var bool
65
     */
66
    private static $can_be_root = false;
0 ignored issues
show
introduced by
The private property $can_be_root is not used, and could be removed.
Loading history...
67
68
    /**
69
     * @var array
70
     */
71
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
72
        'Price' => 'Currency',
73
        'Weight' => 'Decimal',
74
        'Code' => 'Varchar(100)',
75
        'ReceiptTitle' => 'HTMLVarchar(255)',
76
        'Featured' => 'Boolean',
77
        'Available' => 'Boolean',
78
    ];
79
80
    /**
81
     * @var array
82
     */
83
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
84
        'PreviewImage' => Image::class,
85
        'Category' => ProductCategory::class,
86
    ];
87
88
    /**
89
     * @var array
90
     */
91
    private static $has_many = [
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
92
        'ProductImages' => ProductImage::class,
93
        'ProductOptions' => OptionItem::class,
94
        'OrderDetails' => OrderDetail::class,
95
    ];
96
97
    /**
98
     * @var array
99
     */
100
    private static $belongs_many_many = [
0 ignored issues
show
introduced by
The private property $belongs_many_many is not used, and could be removed.
Loading history...
101
        'ProductHolders' => ProductHolder::class,
102
    ];
103
104
    /**
105
     * @var string
106
     */
107
    private static $singular_name = 'Product';
0 ignored issues
show
introduced by
The private property $singular_name is not used, and could be removed.
Loading history...
108
109
    /**
110
     * @var string
111
     */
112
    private static $plural_name = 'Products';
0 ignored issues
show
introduced by
The private property $plural_name is not used, and could be removed.
Loading history...
113
114
    /**
115
     * @var string
116
     */
117
    private static $description = 'A product that can be added to the shopping cart';
0 ignored issues
show
introduced by
The private property $description is not used, and could be removed.
Loading history...
118
119
    /**
120
     * @var array
121
     */
122
    private static $indexes = [
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
123
        'Code' => true, // make unique
124
    ];
125
126
    /**
127
     * @var array
128
     */
129
    private static $defaults = [
0 ignored issues
show
introduced by
The private property $defaults is not used, and could be removed.
Loading history...
130
        'ShowInMenus' => false,
131
        'Available' => true,
132
        'Weight' => '1.0',
133
    ];
134
135
    /**
136
     * @var array
137
     */
138
    private static $summary_fields = [
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
139
        'Title',
140
        'Code',
141
        'Price.Nice',
142
        'Category.Title',
143
    ];
144
145
    /**
146
     * @var array
147
     */
148
    private static $searchable_fields = [
0 ignored issues
show
introduced by
The private property $searchable_fields is not used, and could be removed.
Loading history...
149
        'Title',
150
        'Code',
151
        'Featured',
152
        'Available',
153
        'Category.ID',
154
    ];
155
156
    /**
157
     * @var string
158
     */
159
    private static $table_name = 'ProductPage';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
160
161
    /**
162
     * @param bool $includerelations
163
     *
164
     * @return array
165
     */
166
    public function fieldLabels($includerelations = true)
167
    {
168
        $labels = parent::fieldLabels();
169
170
        $labels['Title'] = _t('ProductPage.TitleLabel', 'Name');
171
        $labels['Code'] = _t('ProductPage.CodeLabel', 'Code');
172
        $labels['Price.Nice'] = _t('ProductPage.PriceLabel', 'Price');
173
        $labels['Featured.Nice'] = _t('ProductPage.NiceLabel', 'Featured');
174
        $labels['Available.Nice'] = _t('ProductPage.AvailableLabel', 'Available');
175
        $labels['Category.ID'] = _t('ProductPage.IDLabel', 'Category');
176
        $labels['Category.Title'] = _t('ProductPage.CategoryTitleLabel', 'Category');
177
178
        return $labels;
179
    }
180
181
    /**
182
     * @return \SilverStripe\Forms\FieldList
183
     */
184
    public function getCMSFields()
185
    {
186
        $fields = parent::getCMSFields();
187
188
        // allow extensions of ProductPage to override the PreviewImage field description
189
        $previewDescription = ($this->config()->get('customPreviewDescription')) ?
190
            $this->config()->get('customPreviewDescription') :
191
            _t(
192
                'ProductPage.PreviewImageDescription',
193
                'Image used throughout site to represent this product'
194
            );
195
196
        // Cateogry Dropdown field w/ add new
197
        $source = function () {
198
            return ProductCategory::get()->map()->toArray();
199
        };
200
        $catField = DropdownField::create('CategoryID', _t('ProductPage.Category', 'FoxyCart Category'), $source())
201
            ->setEmptyString('')
202
            ->setDescription(_t(
203
                'ProductPage.CategoryDescription',
204
                'Required, must also exist in 
205
                    <a href="https://admin.foxycart.com/admin.php?ThisAction=ManageProductCategories" target="_blank">
206
                        FoxyCart Categories
207
                    </a>.
208
                    Used to set category specific options like shipping and taxes. Managed in
209
                        <a href="admin/settings">
210
                            Settings > FoxyStripe > Categories
211
                        </a>'
212
            ));
213
        if (class_exists('QuickAddNewExtension')) {
214
            $catField->useAddNew('ProductCategory', $source);
215
        }
216
217
        // Product Images gridfield
218
        $config = GridFieldConfig_RelationEditor::create();
219
        $config->addComponent(new GridFieldOrderableRows('SortOrder'));
220
        $prodImagesField = GridField::create(
221
            'ProductImages',
222
            _t('ProductPage.ProductImages', 'Images'),
223
            $this->ProductImages(),
224
            $config
225
        );
226
227
        // Product Options field
228
        $config = GridFieldConfig_RelationEditor::create();
229
        $config->addComponent(new GridFieldOrderableRows('SortOrder'));
230
        $products = $this->ProductOptions()->sort('SortOrder');
231
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
232
        $prodOptField = GridField::create(
233
            'ProductOptions',
234
            _t('ProductPage.ProductOptions', 'Options'),
235
            $products,
236
            $config
237
        );
238
239
        // Details tab
240
        $fields->addFieldsToTab('Root.Details', [
241
            HeaderField::create('DetailHD', 'Product Details', 2),
242
            CheckboxField::create('Available')
243
                ->setTitle(_t('ProductPage.Available', 'Available for purchase'))
244
                ->setDescription(_t(
245
                    'ProductPage.AvailableDescription',
246
                    'If unchecked, will remove "Add to Cart" form and instead display "Currently unavailable"'
247
                )),
248
            TextField::create('Code')
249
                ->setTitle(_t('ProductPage.Code', 'Product Code'))
250
                ->setDescription(_t(
251
                    'ProductPage.CodeDescription',
252
                    'Required, must be unique. Product identifier used by FoxyCart in transactions'
253
                )),
254
            $catField,
255
            CurrencyField::create('Price')
256
                ->setTitle(_t('ProductPage.Price', 'Price'))
257
                ->setDescription(_t(
258
                    'ProductPage.PriceDescription',
259
                    'Base price for this product. Can be modified using Product Options'
260
                )),
261
            NumericField::create('Weight')
262
                ->setTitle(_t('ProductPage.Weight', 'Weight'))
263
                ->setDescription(_t(
264
                    'ProductPage.WeightDescription',
265
                    'Base weight for this product in lbs. Can be modified using Product Options'
266
                )),
267
            CheckboxField::create('Featured')
268
                ->setTitle(_t('ProductPage.Featured', 'Featured Product')),
269
            TextField::create('ReceiptTitle')
270
                ->setTitle(_t('ProductPage.ReceiptTitle', 'Product Title for Receipt'))
271
                ->setDescription(_t(
272
                    'ProductPage.ReceiptTitleDescription',
273
                    'Optional'
274
                )),
275
        ]);
276
277
        // Images tab
278
        $fields->addFieldsToTab('Root.Images', [
279
            HeaderField::create('MainImageHD', _t('ProductPage.MainImageHD', 'Product Image'), 2),
280
            UploadField::create('PreviewImage', '')
281
                ->setDescription($previewDescription)
282
                ->setFolderName('Uploads/Products')
283
                ->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']),
284
            HeaderField::create('ProductImagesHD', _t('ProductPage.ProductImagesHD', 'Product Image Gallery'), 2),
285
            $prodImagesField
286
                ->setDescription(_t(
287
                    'ProductPage.ProductImagesDescription',
288
                    'Additional Product Images, shown in gallery on Product page'
289
                )),
290
        ]);
291
292
        // Options Tab
293
        $fields->addFieldsToTab('Root.Options', [
294
            HeaderField::create('OptionsHD', _t('ProductPage.OptionsHD', 'Product Options'), 2),
295
            LiteralField::create('OptionsDescrip', _t(
296
                'Page.OptionsDescrip',
297
                '<p>Product Options allow products to be customized by attributes such as size or color.
298
                    Options can also modify the product\'s price, weight or code.</p>'
299
            )),
300
            $prodOptField,
301
        ]);
302
303
        if (FoxyCart::store_name_warning() !== null) {
304
            $fields->addFieldToTab('Root.Main', LiteralField::create('StoreSubDomainHeaderWarning', _t(
305
                'ProductPage.StoreSubDomainHeaderWarning',
306
                '<p class="message error">Store sub-domain must be entered in the 
307
                        <a href="/admin/settings/">site settings</a></p>'
308
            )), 'Title');
309
        }
310
311
        return $fields;
312
    }
313
314
    /**
315
     * @throws \Exception
316
     */
317 13
    public function onBeforeWrite()
318
    {
319 13
        parent::onBeforeWrite();
320 13
        if (!$this->CategoryID) {
321
            $default = ProductCategory::get()->filter(['Code' => 'DEFAULT'])->first();
322
            $this->CategoryID = $default->ID;
323
        }
324
325
        //update many_many lists when multi-group is on
326 13
        if (FoxyStripeSetting::current_foxystripe_setting()->MultiGroup) {
327
            $holders = $this->ProductHolders();
328
            $product = self::get()->byID($this->ID);
329
            if (isset($product->ParentID)) {
330
                $origParent = $product->ParentID;
331
            } else {
332
                $origParent = null;
333
            }
334
            $currentParent = $this->ParentID;
335
            if ($origParent != $currentParent) {
336
                if ($holders->find('ID', $origParent)) {
337
                    $holders->removeByID($origParent);
338
                }
339
            }
340
            $holders->add($currentParent);
341
        }
342
343 13
        $title = ltrim($this->Title);
344 13
        $title = rtrim($title);
345 13
        $this->Title = $title;
346
    }
347
348 13
    public function onAfterWrite()
349
    {
350 13
        parent::onAfterWrite();
351
    }
352
353 2
    public function onBeforeDelete()
354
    {
355 2
        if ($this->Status != 'Published') {
0 ignored issues
show
Bug Best Practice introduced by
The property Status does not exist on Dynamic\FoxyStripe\Page\ProductPage. Since you implemented __get, consider adding a @property annotation.
Loading history...
356 2
            if ($this->ProductOptions()) {
357 2
                $options = $this->getComponents('ProductOptions');
358 2
                foreach ($options as $option) {
359
                    $option->delete();
360
                }
361
            }
362 2
            if ($this->ProductImages()) {
363
                //delete product image dataobjects, not the images themselves.
364 2
                $images = $this->getComponents('ProductImages');
365 2
                foreach ($images as $image) {
366
                    $image->delete();
367
                }
368
            }
369
        }
370 2
        parent::onBeforeDelete();
371
    }
372
373 5
    public function validate()
374
    {
375 5
        $result = parent::validate();
376
377
        /*if($this->ID>0){
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
378
            if($this->Price <= 0) {
379
                $result->error('Must set a positive price value');
380
            }
381
            if($this->Weight <= 0){
382
                $result->error('Must set a positive weight value');
383
            }
384
            if($this->Code == ''){
385
                $result->error('Must set a product code');
386
            }
387
        }*/
388
389 5
        return $result;
390
    }
391
392
    public function getCMSValidator()
393
    {
394
        return new RequiredFields(['CategoryID', 'Price', 'Weight', 'Code']);
395
    }
396
397
    /**
398
     * @param null $productCode
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $productCode is correct as it would always require null to be passed?
Loading history...
399
     * @param null $optionName
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $optionName is correct as it would always require null to be passed?
Loading history...
400
     * @param null $optionValue
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $optionValue is correct as it would always require null to be passed?
Loading history...
401
     * @param string $method
402
     * @param bool $output
403
     * @param bool $urlEncode
404
     *
405
     * @return null|string
406
     */
407
    public static function getGeneratedValue(
408
        $productCode = null,
409
        $optionName = null,
410
        $optionValue = null,
411
        $method = 'name',
412
        $output = false,
413
        $urlEncode = false
414
    ) {
415
        $optionName = ($optionName !== null) ? preg_replace('/\s/', '_', $optionName) : $optionName;
0 ignored issues
show
introduced by
The condition $optionName !== null is always false.
Loading history...
416
417
        return (FoxyStripeSetting::current_foxystripe_setting()->CartValidation)
418
            ? \FoxyCart_Helper::fc_hash_value($productCode, $optionName, $optionValue, $method, $output, $urlEncode) :
419
            $optionValue;
420
    }
421
422
    /**
423
     * @param Member $member
424
     *
425
     * @return bool
426
     */
427
    public function canEdit($member = null)
428
    {
429
        return Permission::check('Product_CANCRUD', 'any', $member);
430
    }
431
432
    public function canDelete($member = null)
433
    {
434
        return Permission::check('Product_CANCRUD', 'any', $member);
435
    }
436
437
    public function canCreate($member = null, $context = [])
438
    {
439
        return Permission::check('Product_CANCRUD', 'any', $member);
440
    }
441
442
    public function canPublish($member = null)
443
    {
444
        return Permission::check('Product_CANCRUD', 'any', $member);
445
    }
446
447
    public function providePermissions()
448
    {
449
        return [
450
            'Product_CANCRUD' => 'Allow user to manage Products and related objects',
451
        ];
452
    }
453
}
454