1 | <?php |
||
2 | |||
3 | namespace SilverShop\Cart; |
||
4 | |||
5 | use SilverShop\Extension\ViewableCartExtension; |
||
6 | use SilverShop\Model\Buyable; |
||
7 | use SilverShop\Model\Variation\Variation; |
||
8 | use SilverShop\Page\CartPage; |
||
9 | use SilverShop\Page\Product; |
||
10 | use SilverShop\ShopTools; |
||
11 | use SilverStripe\Control\Controller; |
||
12 | use SilverStripe\Control\Director; |
||
13 | use SilverStripe\Control\HTTPRequest; |
||
14 | use SilverStripe\Control\HTTPResponse; |
||
15 | use SilverStripe\Core\ClassInfo; |
||
16 | use SilverStripe\Dev\Debug; |
||
17 | use SilverStripe\ErrorPage\ErrorPage; |
||
0 ignored issues
–
show
|
|||
18 | use SilverStripe\ORM\DataObject; |
||
19 | use SilverStripe\Security\Permission; |
||
20 | use SilverStripe\Security\SecurityToken; |
||
21 | use SilverStripe\Versioned\Versioned; |
||
22 | use SilverStripe\View\Requirements; |
||
23 | |||
24 | /** |
||
25 | * Manipulate the cart via urls. |
||
26 | * |
||
27 | * @mixin ViewableCartExtension |
||
28 | * @method ShoppingCart Cart() |
||
29 | */ |
||
30 | class ShoppingCartController extends Controller |
||
31 | { |
||
32 | private static $url_segment = 'shoppingcart'; |
||
33 | |||
34 | /** |
||
35 | * Whether or not this controller redirects to the cart-page whenever an item was added |
||
36 | * |
||
37 | * @config |
||
38 | * @var bool |
||
39 | */ |
||
40 | private static $direct_to_cart_page = false; |
||
41 | |||
42 | /** |
||
43 | * @var ShoppingCart |
||
44 | */ |
||
45 | protected $cart; |
||
46 | |||
47 | /** |
||
48 | * @config |
||
49 | * @var array |
||
50 | */ |
||
51 | private static $url_handlers = [ |
||
0 ignored issues
–
show
|
|||
52 | '$Action/$Buyable/$ID' => 'handleAction', |
||
53 | ]; |
||
54 | |||
55 | /** |
||
56 | * @config |
||
57 | * @var array |
||
58 | */ |
||
59 | private static $allowed_actions = [ |
||
0 ignored issues
–
show
|
|||
60 | 'add', |
||
61 | 'additem', |
||
62 | 'remove', |
||
63 | 'removeitem', |
||
64 | 'removeall', |
||
65 | 'removeallitem', |
||
66 | 'setquantity', |
||
67 | 'setquantityitem', |
||
68 | 'clear', |
||
69 | 'debug', |
||
70 | ]; |
||
71 | |||
72 | public static function add_item_link(Buyable $buyable, $parameters = array()) |
||
73 | { |
||
74 | return self::build_url('add', $buyable, $parameters); |
||
75 | } |
||
76 | |||
77 | public static function remove_item_link(Buyable $buyable, $parameters = array()) |
||
78 | { |
||
79 | return self::build_url('remove', $buyable, $parameters); |
||
80 | } |
||
81 | |||
82 | public static function remove_all_item_link(Buyable $buyable, $parameters = array()) |
||
83 | { |
||
84 | return self::build_url('removeall', $buyable, $parameters); |
||
85 | } |
||
86 | |||
87 | public static function set_quantity_item_link(Buyable $buyable, $parameters = array()) |
||
88 | { |
||
89 | return self::build_url('setquantity', $buyable, $parameters); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Helper for creating a url |
||
94 | */ |
||
95 | protected static function build_url($action, $buyable, $params = []) |
||
96 | { |
||
97 | if (!$action || !$buyable) { |
||
98 | return false; |
||
99 | } |
||
100 | |||
101 | if (SecurityToken::is_enabled() && !self::config()->disable_security_token) { |
||
102 | $params[SecurityToken::inst()->getName()] = SecurityToken::inst()->getValue(); |
||
103 | } |
||
104 | |||
105 | $className = get_class($buyable); |
||
106 | |||
107 | $link = Controller::join_links( |
||
108 | [ |
||
109 | self::config()->url_segment, |
||
110 | $action, |
||
111 | ShopTools::sanitiseClassName($className), |
||
112 | $buyable->ID |
||
113 | ] |
||
114 | ); |
||
115 | |||
116 | return empty($params) ? $link : $link . '?' . http_build_query($params); |
||
117 | } |
||
118 | |||
119 | /** |
||
120 | * This is used here and in VariationForm and AddProductForm |
||
121 | * |
||
122 | * @param bool|string $status |
||
123 | * |
||
124 | * @return string|HTTPResponse |
||
125 | */ |
||
126 | public static function direct($status = true) |
||
127 | { |
||
128 | if (Director::is_ajax()) { |
||
129 | return (string)$status; |
||
130 | } |
||
131 | if (self::config()->direct_to_cart_page && ($cartlink = CartPage::find_link())) { |
||
132 | return Controller::curr()->redirect($cartlink); |
||
133 | } else { |
||
134 | return Controller::curr()->redirectBack(); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | public function init() |
||
139 | { |
||
140 | parent::init(); |
||
141 | $this->cart = ShoppingCart::singleton(); |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * @return Product|Variation|Buyable |
||
146 | * @throws \SilverStripe\Control\HTTPResponse_Exception |
||
147 | */ |
||
148 | protected function buyableFromRequest() |
||
149 | { |
||
150 | $request = $this->getRequest(); |
||
151 | if (SecurityToken::is_enabled() |
||
152 | && !self::config()->disable_security_token |
||
153 | && !SecurityToken::inst()->checkRequest($request) |
||
154 | ) { |
||
155 | return $this->httpError( |
||
156 | 400, |
||
157 | _t( |
||
158 | 'SilverShop\Cart\ShoppingCart.InvalidSecurityToken', |
||
159 | 'Invalid security token, possible CSRF attack.' |
||
160 | ) |
||
161 | ); |
||
162 | } |
||
163 | $id = (int)$request->param('ID'); |
||
164 | if (empty($id)) { |
||
165 | //TODO: store error message |
||
166 | return null; |
||
167 | } |
||
168 | $buyableclass = Product::class; |
||
169 | if ($class = $request->param('Buyable')) { |
||
170 | $buyableclass = ShopTools::unsanitiseClassName($class); |
||
171 | } |
||
172 | if (!ClassInfo::exists($buyableclass)) { |
||
173 | //TODO: store error message |
||
174 | return null; |
||
175 | } |
||
176 | //ensure only live products are returned, if they are versioned |
||
177 | $buyable = $buyableclass::has_extension(Versioned::class) |
||
178 | ? Versioned::get_by_stage($buyableclass, 'Live')->byID($id) |
||
179 | : DataObject::get($buyableclass)->byID($id); |
||
180 | |||
181 | if (!$buyable || !($buyable instanceof Buyable)) { |
||
0 ignored issues
–
show
|
|||
182 | //TODO: store error message |
||
183 | return null; |
||
184 | } |
||
185 | |||
186 | return $this->cart->getCorrectBuyable($buyable); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Action: add item to cart |
||
191 | * |
||
192 | * @param HTTPRequest $request |
||
193 | * |
||
194 | * @return HTTPResponse |
||
195 | * @throws \SilverStripe\Control\HTTPResponse_Exception |
||
196 | */ |
||
197 | public function add($request) |
||
198 | { |
||
199 | $result = false; |
||
200 | |||
201 | if ($product = $this->buyableFromRequest()) { |
||
202 | $quantity = (int)$request->getVar('quantity'); |
||
203 | if (!$quantity) { |
||
204 | $quantity = 1; |
||
205 | } |
||
206 | $result = $this->cart->add($product, $quantity, $request->getVars()); |
||
207 | } |
||
208 | |||
209 | $this->updateLocale($request); |
||
210 | $this->extend('updateAddResponse', $request, $response, $product, $quantity, $result); |
||
211 | return $response ? $response : self::direct(); |
||
0 ignored issues
–
show
|
|||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Action: remove a certain number of items from the cart |
||
216 | * |
||
217 | * @param HTTPRequest $request |
||
218 | * |
||
219 | * @return HTTPResponse |
||
220 | * @throws \SilverStripe\Control\HTTPResponse_Exception |
||
221 | */ |
||
222 | public function remove($request) |
||
223 | { |
||
224 | if ($product = $this->buyableFromRequest()) { |
||
225 | $this->cart->remove($product, $quantity = 1, $request->getVars()); |
||
226 | } |
||
227 | |||
228 | $this->updateLocale($request); |
||
229 | $this->extend('updateRemoveResponse', $request, $response, $product, $quantity); |
||
230 | return $response ? $response : self::direct(); |
||
0 ignored issues
–
show
|
|||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Action: remove all of an item from the cart |
||
235 | * |
||
236 | * @param HTTPRequest $request |
||
237 | * |
||
238 | * @return HTTPResponse |
||
239 | * @throws \SilverStripe\Control\HTTPResponse_Exception |
||
240 | */ |
||
241 | public function removeall($request) |
||
242 | { |
||
243 | if ($product = $this->buyableFromRequest()) { |
||
244 | $this->cart->remove($product, null, $request->getVars()); |
||
245 | } |
||
246 | |||
247 | $this->updateLocale($request); |
||
248 | $this->extend('updateRemoveAllResponse', $request, $response, $product); |
||
249 | return $response ? $response : self::direct(); |
||
0 ignored issues
–
show
|
|||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Action: update the quantity of an item in the cart |
||
254 | * |
||
255 | * @param HTTPRequest $request |
||
256 | * |
||
257 | * @return HTTPResponse |
||
258 | * @throws \SilverStripe\Control\HTTPResponse_Exception |
||
259 | */ |
||
260 | public function setquantity($request) |
||
261 | { |
||
262 | $product = $this->buyableFromRequest(); |
||
263 | $quantity = (int)$request->getVar('quantity'); |
||
264 | if ($product) { |
||
265 | $this->cart->setQuantity($product, $quantity, $request->getVars()); |
||
266 | } |
||
267 | |||
268 | $this->updateLocale($request); |
||
269 | $this->extend('updateSetQuantityResponse', $request, $response, $product, $quantity); |
||
270 | return $response ? $response : self::direct(); |
||
0 ignored issues
–
show
|
|||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Action: clear the cart |
||
275 | * |
||
276 | * @param HTTPRequest $request |
||
277 | * |
||
278 | * @return HTTPResponse |
||
279 | */ |
||
280 | public function clear($request) |
||
281 | { |
||
282 | $this->updateLocale($request); |
||
283 | $this->cart->clear(); |
||
284 | $this->extend('updateClearResponse', $request, $response); |
||
285 | return $response ? $response : self::direct(); |
||
0 ignored issues
–
show
|
|||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Handle index requests |
||
290 | * |
||
291 | * @throws \SilverStripe\Control\HTTPResponse_Exception |
||
292 | */ |
||
293 | public function index() |
||
294 | { |
||
295 | if ($cart = $this->Cart()) { |
||
296 | return $this->redirect($cart->CartLink); |
||
0 ignored issues
–
show
|
|||
297 | } elseif ($response = ErrorPage::response_for(404)) { |
||
298 | return $response; |
||
299 | } |
||
300 | return $this->httpError(404, _t('SilverShop\Cart\ShoppingCart.NoCartInitialised', 'no cart initialised')); |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * Displays order info and cart contents. |
||
305 | */ |
||
306 | public function debug() |
||
307 | { |
||
308 | if (Director::isDev() || Permission::check('ADMIN')) { |
||
309 | //TODO: allow specifying a particular id to debug |
||
310 | Requirements::css('silvershop/core: client/dist/css/cartdebug.css'); |
||
311 | $order = ShoppingCart::curr(); |
||
312 | $content = ($order) |
||
0 ignored issues
–
show
|
|||
313 | ? Debug::text($order) |
||
314 | : 'Cart has not been created yet. Add a product.'; |
||
315 | return ['Content' => $content]; |
||
316 | } |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * @param HTTPRequest $request |
||
321 | */ |
||
322 | protected function updateLocale($request) |
||
323 | { |
||
324 | $order = $this->cart->current(); |
||
325 | if ($request && $request->isAjax() && $order) { |
||
326 | ShopTools::install_locale($order->Locale); |
||
327 | } |
||
328 | } |
||
329 | } |
||
330 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths