Completed
Push — master ( bbec30...bba6c2 )
by Jason
03:33
created

src/Page/ProductPage.php (2 issues)

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;
62
63
    /**
64
     * @var bool
65
     */
66
    private static $can_be_root = false;
67
68
    /**
69
     * @var array
70
     */
71
    private static $db = [
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
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 = [
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 = [
101
        'ProductHolders' => ProductHolder::class,
102
    ];
103
104
    /**
105
     * @var string
106
     */
107
    private static $singular_name = 'Product';
108
109
    /**
110
     * @var string
111
     */
112
    private static $plural_name = 'Products';
113
114
    /**
115
     * @var string
116
     */
117
    private static $description = 'A product that can be added to the shopping cart';
118
119
    /**
120
     * @var array
121
     */
122
    private static $indexes = [
123
        'Code' => true, // make unique
124
    ];
125
126
    /**
127
     * @var array
128
     */
129
    private static $defaults = [
130
        'ShowInMenus' => false,
131
        'Available' => true,
132
        'Weight' => '1.0',
133
    ];
134
135
    /**
136
     * @var array
137
     */
138
    private static $summary_fields = [
139
        'Title',
140
        'Code',
141
        'Price.Nice',
142
        'Category.Title',
143
    ];
144
145
    /**
146
     * @var array
147
     */
148
    private static $searchable_fields = [
149
        'Title',
150
        'Code',
151
        'Featured',
152
        'Available',
153
        'Category.ID',
154
    ];
155
156
    /**
157
     * @var string
158
     */
159
    private static $table_name = 'ProductPage';
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
                ->setScale(2),
268
            CheckboxField::create('Featured')
269
                ->setTitle(_t('ProductPage.Featured', 'Featured Product')),
270
            TextField::create('ReceiptTitle')
271
                ->setTitle(_t('ProductPage.ReceiptTitle', 'Product Title for Receipt'))
272
                ->setDescription(_t(
273
                    'ProductPage.ReceiptTitleDescription',
274
                    'Optional'
275
                )),
276
        ]);
277
278
        // Images tab
279
        $fields->addFieldsToTab('Root.Images', [
280
            HeaderField::create('MainImageHD', _t('ProductPage.MainImageHD', 'Product Image'), 2),
281
            UploadField::create('PreviewImage', '')
282
                ->setDescription($previewDescription)
283
                ->setFolderName('Uploads/Products')
284
                ->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']),
285
            HeaderField::create('ProductImagesHD', _t('ProductPage.ProductImagesHD', 'Product Image Gallery'), 2),
286
            $prodImagesField
287
                ->setDescription(_t(
288
                    'ProductPage.ProductImagesDescription',
289
                    'Additional Product Images, shown in gallery on Product page'
290
                )),
291
        ]);
292
293
        // Options Tab
294
        $fields->addFieldsToTab('Root.Options', [
295
            HeaderField::create('OptionsHD', _t('ProductPage.OptionsHD', 'Product Options'), 2),
296
            LiteralField::create('OptionsDescrip', _t(
297
                'Page.OptionsDescrip',
298
                '<p>Product Options allow products to be customized by attributes such as size or color.
299
                    Options can also modify the product\'s price, weight or code.</p>'
300
            )),
301
            $prodOptField,
302
        ]);
303
304
        if (FoxyCart::store_name_warning() !== null) {
305
            $fields->addFieldToTab('Root.Main', LiteralField::create('StoreSubDomainHeaderWarning', _t(
306
                'ProductPage.StoreSubDomainHeaderWarning',
307
                '<p class="message error">Store sub-domain must be entered in the 
308
                        <a href="/admin/settings/">site settings</a></p>'
309
            )), 'Title');
310
        }
311
312
        return $fields;
313
    }
314
315
    /**
316
     * @throws \Exception
317
     */
318 13
    public function onBeforeWrite()
319
    {
320 13
        parent::onBeforeWrite();
321 13
        if (!$this->CategoryID) {
322
            $default = ProductCategory::get()->filter(['Code' => 'DEFAULT'])->first();
323
            $this->CategoryID = $default->ID;
324
        }
325
326
        //update many_many lists when multi-group is on
327 13
        if (FoxyStripeSetting::current_foxystripe_setting()->MultiGroup) {
328
            $holders = $this->ProductHolders();
329
            $product = self::get()->byID($this->ID);
330
            if (isset($product->ParentID)) {
331
                $origParent = $product->ParentID;
332
            } else {
333
                $origParent = null;
334
            }
335
            $currentParent = $this->ParentID;
336
            if ($origParent != $currentParent) {
337
                if ($holders->find('ID', $origParent)) {
338
                    $holders->removeByID($origParent);
339
                }
340
            }
341
            $holders->add($currentParent);
342
        }
343
344 13
        $this->Title = trim($this->Title);
345 13
        $this->Code = trim($this->Code);
346 13
        $this->ReceiptTitle = trim($this->ReceiptTitle);
347
    }
348
349 13
    public function onAfterWrite()
350
    {
351 13
        parent::onAfterWrite();
352
    }
353
354 2
    public function onBeforeDelete()
355
    {
356 2
        if ($this->Status != 'Published') {
357 2
            if ($this->ProductOptions()) {
358 2
                $options = $this->getComponents('ProductOptions');
359 2
                foreach ($options as $option) {
360
                    $option->delete();
361
                }
362
            }
363 2
            if ($this->ProductImages()) {
364
                //delete product image dataobjects, not the images themselves.
365 2
                $images = $this->getComponents('ProductImages');
366 2
                foreach ($images as $image) {
367
                    $image->delete();
368
                }
369
            }
370
        }
371 2
        parent::onBeforeDelete();
372
    }
373
374 5
    public function validate()
375
    {
376 5
        $result = parent::validate();
377
378
        /*if($this->ID>0){
379
            if($this->Price <= 0) {
380
                $result->error('Must set a positive price value');
381
            }
382
            if($this->Weight <= 0){
383
                $result->error('Must set a positive weight value');
384
            }
385
            if($this->Code == ''){
386
                $result->error('Must set a product code');
387
            }
388
        }*/
389
390 5
        return $result;
391
    }
392
393
    public function getCMSValidator()
394
    {
395
        return new RequiredFields(['CategoryID', 'Price', 'Weight', 'Code']);
396
    }
397
398
    /**
399
     * @param null $productCode
400
     * @param null $optionName
401
     * @param null $optionValue
402
     * @param string $method
403
     * @param bool $output
404
     * @param bool $urlEncode
405
     *
406
     * @return null|string
407
     */
408
    public static function getGeneratedValue(
409
        $productCode = null,
410
        $optionName = null,
411
        $optionValue = null,
412
        $method = 'name',
413
        $output = false,
414
        $urlEncode = false
415
    ) {
416
        $optionName = ($optionName !== null) ? preg_replace('/\s/', '_', $optionName) : $optionName;
417
418
        return (FoxyStripeSetting::current_foxystripe_setting()->CartValidation)
419
            ? \FoxyCart_Helper::fc_hash_value($productCode, $optionName, $optionValue, $method, $output, $urlEncode) :
420
            $optionValue;
421
    }
422
423
    /**
424
     * @param Member $member
425
     *
426
     * @return bool
427
     */
428
    public function canEdit($member = null)
429
    {
430
        return Permission::check('Product_CANCRUD', 'any', $member);
431
    }
432
433
    public function canDelete($member = null)
434
    {
435
        return Permission::check('Product_CANCRUD', 'any', $member);
436
    }
437
438
    public function canCreate($member = null, $context = [])
439
    {
440
        return Permission::check('Product_CANCRUD', 'any', $member);
441
    }
442
443
    public function canPublish($member = null)
444
    {
445
        return Permission::check('Product_CANCRUD', 'any', $member);
446
    }
447
448
    public function providePermissions()
449
    {
450
        return [
451
            'Product_CANCRUD' => 'Allow user to manage Products and related objects',
452
        ];
453
    }
454
}
455