Completed
Pull Request — master (#292)
by Jason
07:33
created

ProductPage::getDiscountFieldValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
ccs 0
cts 7
cp 0
cc 2
eloc 6
nc 2
nop 0
crap 6

1 Method

Rating   Name   Duplication   Size   Complexity  
A ProductPage::canCreate() 0 4 1
1
<?php
2
3
/**
4
 *
5
 * @package FoxyStripe
6
 *
7
 */
8
class ProductPage extends Page implements PermissionProvider
9
{
10
11
    private static $allowed_children = 'none';
0 ignored issues
show
Unused Code introduced by
The property $allowed_children 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...
12
    private static $default_parent = 'ProductHolder';
0 ignored issues
show
Unused Code introduced by
The property $default_parent 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...
13
    private static $can_be_root = false;
0 ignored issues
show
Unused Code introduced by
The property $can_be_root 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...
14
15
    /**
16
     * @var array
17
     */
18
    private static $db = array(
19
        'Price' => 'Currency',
20
        'Weight' => 'Decimal',
21
        'Code' => 'Varchar(100)',
22
        'ReceiptTitle' => 'HTMLVarchar(255)',
23
        'Featured' => 'Boolean',
24
        'Available' => 'Boolean',
25
    );
26
27
    /**
28
     * @var array
29
     */
30
    private static $has_one = array(
31
        'PreviewImage' => 'Image',
32
        'Category' => 'ProductCategory'
33
    );
34
35
    private static $has_many = array(
36
        'ProductImages' => 'ProductImage',
37
        'ProductOptions' => 'OptionItem',
38
        'OrderDetails' => 'OrderDetail',
39
    );
40
41
    private static $belongs_many_many = array(
42
        'ProductHolders' => 'ProductHolder'
43
    );
44
45
    private static $singular_name = 'Product';
46
    private static $plural_name = 'Products';
47
    private static $description = 'A product that can be added to the shopping cart';
48
49
    private static $indexes = array(
50
        'Code' => true // make unique
51
    );
52
53
    private static $defaults = array(
54
        'ShowInMenus' => false,
55
        'Available' => true,
56
        'Weight' => '1.0'
57
    );
58
59
    private static $summary_fields = array(
60
        'Title',
61
        'Code',
62
        'Price.Nice',
63
        'Category.Title'
64
    );
65
66
    private static $searchable_fields = array(
67
        'Title',
68
        'Code',
69
        'Featured',
70
        'Available',
71
        'Category.ID'
72
    );
73
74
    function fieldLabels($includerelations = true)
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...
75
    {
76
        $labels = parent::fieldLabels();
77
78
        $labels['Title'] = _t('ProductPage.TitleLabel', 'Name');
79
        $labels['Code'] = _t('ProductPage.CodeLabel', "Code");
80
        $labels['Price.Nice'] = _t('ProductPage.PriceLabel', 'Price');
81
        $labels['Featured.Nice'] = _t('ProductPage.NiceLabel', 'Featured');
82
        $labels['Available.Nice'] = _t('ProductPage.AvailableLabel', 'Available');
83
        $labels['Category.ID'] = _t('ProductPage.IDLabel', 'Category');
84
        $labels['Category.Title'] = _t('ProductPage.CategoryTitleLabel', 'Category');
85
86
        return $labels;
87
    }
88
89
    public function getCMSFields()
90
    {
91
        $fields = parent::getCMSFields();
92
93
        // allow extensions of ProductPage to override the PreviewImage field description
94
        $previewDescription = ($this->stat('customPreviewDescription')) ? $this->stat('customPreviewDescription') : _t('ProductPage.PreviewImageDescription',
95
            'Image used throughout site to represent this product');
96
97
        // Cateogry Dropdown field w/ add new
98
        $source = function () {
99
            return ProductCategory::get()->map()->toArray();
100
        };
101
        $catField = DropdownField::create('CategoryID', _t('ProductPage.Category', 'FoxyCart Category'), $source())
102
            ->setEmptyString('')
103
            ->setDescription(_t(
104
                'ProductPage.CategoryDescription',
105
                'Required, must also exist in 
106
                    <a href="https://admin.foxycart.com/admin.php?ThisAction=ManageProductCategories" target="_blank">
107
                        FoxyCart Categories
108
                    </a>.
109
                    Used to set category specific options like shipping and taxes. Managed in
110
                        <a href="admin/settings">
111
                            Settings > FoxyStripe > Categories
112
                        </a>'
113
            ));
114
        if (class_exists('QuickAddNewExtension')) {
115
            $catField->useAddNew('ProductCategory', $source);
116
        }
117
118
        // Product Images gridfield
119
        $config = GridFieldConfig_RelationEditor::create();
120
        if (class_exists('GridFieldSortableRows')) {
121
            $config->addComponent(new GridFieldSortableRows('SortOrder'));
122
        }
123
        if (class_exists('GridFieldBulkImageUpload')) {
124
            $config->addComponent(new GridFieldBulkUpload());
125
            $config->getComponentByType('GridFieldBulkUpload')->setUfConfig('folderName', 'Uploads/ProductImages');
126
        }
127
        $prodImagesField = GridField::create(
128
            'ProductImages',
129
            _t('ProductPage.ProductImages', 'Images'),
130
            $this->ProductImages(),
131
            $config
132
        );
133
134
        // Product Options field
135
        $config = GridFieldConfig_RelationEditor::create();
136
        if (class_exists('GridFieldBulkManager')) {
137
            $config->addComponent(new GridFieldBulkManager());
138
        }
139
        if (class_exists('GridFieldSortableRows')) {
140
            $config->addComponent(new GridFieldSortableRows('SortOrder'));
141
            $products = $this->ProductOptions()->sort('SortOrder');
142
        } else {
143
            $products = $this->ProductOptions();
144
        }
145
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
146
        $prodOptField = GridField::create(
147
            'ProductOptions',
148
            _t('ProductPage.ProductOptions', 'Options'),
149
            $products,
150
            $config
151
        );
152
153
        // Details tab
154
        $fields->addFieldsToTab('Root.Details', array(
155
            HeaderField::create('DetailHD', 'Product Details', 2),
156
            CheckboxField::create('Available')
157
                ->setTitle(_t('ProductPage.Available', 'Available for purchase'))
158
                ->setDescription(_t(
159
                    'ProductPage.AvailableDescription',
160
                    'If unchecked, will remove "Add to Cart" form and instead display "Currently unavailable"'
161
                )),
162
            TextField::create('Code')
163
                ->setTitle(_t('ProductPage.Code', 'Product Code'))
164
                ->setDescription(_t(
165
                    'ProductPage.CodeDescription',
166
                    'Required, must be unique. Product identifier used by FoxyCart in transactions'
167
                )),
168
            $catField,
169
            CurrencyField::create('Price')
170
                ->setTitle(_t('ProductPage.Price', 'Price'))
171
                ->setDescription(_t(
172
                    'ProductPage.PriceDescription',
173
                    'Base price for this product. Can be modified using Product Options'
174
                )),
175
            NumericField::create('Weight')
176
                ->setTitle(_t('ProductPage.Weight', 'Weight'))
177
                ->setDescription(_t(
178
                    'ProductPage.WeightDescription',
179
                    'Base weight for this product in lbs. Can be modified using Product Options'
180
                )),
181
            CheckboxField::create('Featured')
182
                ->setTitle(_t('ProductPage.Featured', 'Featured Product')),
183
            TextField::create('ReceiptTitle')
184
                ->setTitle(_t('ProductPage.ReceiptTitle', 'Product Title for Receipt'))
185
                ->setDescription(_t(
186
                    'ProductPage.ReceiptTitleDescription', 'Optional'
187
                ))
188
        ));
189
190
        // Images tab
191
        $fields->addFieldsToTab('Root.Images', array(
192
            HeaderField::create('MainImageHD', _t('ProductPage.MainImageHD', 'Product Image'), 2),
193
            UploadField::create('PreviewImage', '')
194
                ->setDescription($previewDescription)
195
                ->setFolderName('Uploads/Products')
196
                ->setAllowedExtensions(array('jpg', 'jpeg', 'gif', 'png'))
197
                ->setAllowedMaxFileNumber(1),
198
            HeaderField::create('ProductImagesHD', _t('ProductPage.ProductImagesHD' . 'Product Image Gallery'), 2),
199
            $prodImagesField
200
                ->setDescription(_t(
201
                    'ProductPage.ProductImagesDescription',
202
                    'Additional Product Images, shown in gallery on Product page'
203
                ))
204
        ));
205
206
        // Options Tab
207
        $fields->addFieldsToTab('Root.Options', array(
208
            HeaderField::create('OptionsHD', _t('ProductPage.OptionsHD', 'Product Options'), 2),
209
            LiteralField::create('OptionsDescrip', _t(
210
                'Page.OptionsDescrip',
211
                '<p>Product Options allow products to be customized by attributes such as size or color.
212
                    Options can also modify the product\'s price, weight or code.</p>'
213
            )),
214
            $prodOptField
215
        ));
216
217 View Code Duplication
        if (FoxyCart::store_name_warning() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
218
            $fields->addFieldToTab('Root.Main', LiteralField::create("StoreSubDomainHeaderWarning", _t(
219
                'ProductPage.StoreSubDomainHeaderWarning',
220
                "<p class=\"message error\">Store sub-domain must be entered in the <a href=\"/admin/settings/\">site settings</a></p>"
221
            )), 'Title');
222
        }
223
224
        // allows CMS fields to be extended
225
        $this->extend('updateCMSFields', $fields);
226
227
        return $fields;
228
    }
229
230 29
    public function onBeforeWrite()
231
    {
232 29
        parent::onBeforeWrite();
233 29
        if (!$this->CategoryID) {
234
            $default = ProductCategory::get()->filter(array('Code' => 'DEFAULT'))->first();
235
            $this->CategoryID = $default->ID;
236
        }
237
238
        //update many_many lists when multi-group is on
239 29
        if (SiteConfig::current_site_config()->MultiGroup) {
240
            $holders = $this->ProductHolders();
241
            $product = ProductPage::get()->byID($this->ID);
242
            if (isset($product->ParentID)) {
243
                $origParent = $product->ParentID;
244
            } else {
245
                $origParent = null;
246
            }
247
            $currentParent = $this->ParentID;
248
            if ($origParent != $currentParent) {
249
                if ($holders->find('ID', $origParent)) {
250
                    $holders->removeByID($origParent);
251
                }
252
253
            }
254
            $holders->add($currentParent);
255
        }
256
257 29
        $title = ltrim($this->Title);
258 29
        $title = rtrim($title);
259 29
        $this->Title = $title;
260
261
262 29
    }
263
264 29
    public function onAfterWrite()
265
    {
266 29
        parent::onAfterWrite();
267
268
269 29
    }
270
271 1
    public function onBeforeDelete()
272
    {
273 1
        if ($this->Status != "Published") {
274 1
            if ($this->ProductOptions()) {
275 1
                $options = $this->getComponents('ProductOptions');
276 1
                foreach ($options as $option) {
277
                    $option->delete();
278 1
                }
279 1
            }
280 1
            if ($this->ProductImages()) {
281
                //delete product image dataobjects, not the images themselves.
282 1
                $images = $this->getComponents('ProductImages');
283 1
                foreach ($images as $image) {
284
                    $image->delete();
285 1
                }
286 1
            }
287 1
        }
288 1
        parent::onBeforeDelete();
289 1
    }
290
291 4
    public function validate()
292
    {
293 4
        $result = parent::validate();
294
295
        /*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...
296
            if($this->Price <= 0) {
297
                $result->error('Must set a positive price value');
298
            }
299
            if($this->Weight <= 0){
300
                $result->error('Must set a positive weight value');
301
            }
302
            if($this->Code == ''){
303
                $result->error('Must set a product code');
304
            }
305
        }*/
306
307 4
        return $result;
308
    }
309
310
    public function getCMSValidator()
311
    {
312
        return new RequiredFields(array('CategoryID', 'Price', 'Weight', 'Code'));
313
    }
314
315
    public static function getGeneratedValue(
316
        $productCode = null,
317
        $optionName = null,
318
        $optionValue = null,
319
        $method = 'name',
320
        $output = false,
321
        $urlEncode = false
322
    ) {
323
        $optionName = ($optionName !== null) ? preg_replace('/\s/', '_', $optionName) : $optionName;
324
        return (SiteConfig::current_site_config()->CartValidation)
325
            ? FoxyCart_Helper::fc_hash_value($productCode, $optionName, $optionValue, $method, $output, $urlEncode) :
326
            $optionValue;
327
    }
328
329
    // get FoxyCart Store Name for JS call
330
    public function getCartScript()
331
    {
332
        return '<script src="https://cdn.foxycart.com/' . FoxyCart::getFoxyCartStoreName() . '/loader.js" async defer></script>';
333
    }
334
335
    /**
336
     * @param Member $member
337
     * @return boolean
338
     */
339
    public function canEdit($member = null)
340
    {
341
        return Permission::check('Product_CANCRUD');
342
    }
343
344
    public function canDelete($member = null)
345
    {
346
        return Permission::check('Product_CANCRUD');
347
    }
348
349
    public function canCreate($member = null)
350
    {
351
        return Permission::check('Product_CANCRUD');
352
    }
353
354 4
    public function canPublish($member = null)
355
    {
356 4
        return Permission::check('Product_CANCRUD');
357
    }
358
359
    public function providePermissions()
360
    {
361
        return array(
362
            'Product_CANCRUD' => 'Allow user to manage Products and related objects'
363
        );
364
    }
365
366
}
367
368
class ProductPage_Controller extends Page_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...
369
{
370
371
    private static $allowed_actions = array(
372
        'PurchaseForm'
373
    );
374
375
    public function init()
376
    {
377
        parent::init();
378
        Requirements::javascript("framework/thirdparty/jquery/jquery.js");
379
        if ($this->data()->Available && $this->ProductOptions()->exists()) {
380
            Requirements::javascript("foxystripe/javascript/outOfStock.min.js");
381
            Requirements::javascript("foxystripe/javascript/ProductOptions.min.js");
382
        }
383
384
        Requirements::customScript(<<<JS
385
		var productID = {$this->data()->ID};
386
JS
387
        );
388
    }
389
390
    /**
391
     * @return FoxyStripePurchaseForm
392
     */
393
    public function PurchaseForm()
394
    {
395
396
        $form = FoxyStripePurchaseForm::create($this, __FUNCTION__, null, null, null, $this->data());
397
398
        $this->extend('updateFoxyStripePurchaseForm', $form);
399
400
        return $form;
401
402
    }
403
}
404