PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / app / Services / Filter / LicenseFilter.php

LicenseFilter.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Services/Filter/LicenseFilter.php

338 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Filter;
4
5 use FluentCart\App\Helpers\Status;
6 use FluentCart\App\Services\DateTime\DateTime;
7 use FluentCartPro\App\Modules\Licensing\Models\License;
8
9
10 class LicenseFilter extends BaseFilter
11 {
12
13 public function applySimpleFilter(?string $search = null): void
14 {
15 $isApplied = $this->applySimpleOperatorFilter($search);
16 if ($isApplied) {
17 return;
18 }
19
20 $this->query->when($search ?? $this->search, function ($query, $search) {
21 // If search is an array, implode it
22 $search = is_array($search) ? implode(' ', $search) : $search;
23
24 // If search is empty or null, return the query
25 if (empty($search)) {
26 return $query;
27 }
28
29 $query->where(function ($query) use ($search) {
30 $query->where('license_key', 'like', '%' . $search . '%')
31 ->orWhere('order_id', 'like', '%' . $search . '%')
32 ->orWhereHas('customer', function ($query) use ($search) {
33 $query
34 ->whereRaw("CONCAT(first_name, ' ', last_name) LIKE ?", ["%{$search}%"])
35 ->orWhere('email', 'like', '%' . $search . '%');
36 })
37 ->orWhereHas('activations', function ($query) use ($search) {
38 $query->whereHas('site', function ($query) use ($search) {
39 $query->where('site_url', 'like', '%' . $search . '%');
40 });
41 })
42 ;
43 });
44
45 return $query;
46 });
47 }
48
49 public function tabsMap(): array
50 {
51 return [
52 'inactive' => __('Inactive', 'fluent-cart'),
53 'active' => __('Active', 'fluent-cart'),
54 'expired' => __('Expired', 'fluent-cart'),
55 'disabled' => __('disabled', 'fluent-cart')
56 ];
57 }
58
59 public function getModel(): string
60 {
61 return License::class;
62 }
63
64 public static function getFilterName(): string
65 {
66 return 'licenses';
67 }
68
69 /**
70 * @return array<string, array<string, mixed>>
71 */
72 protected static function sortableColumns(): array
73 {
74 return [
75 'order_id' => ['label' => __('Order ID', 'fluent-cart'), 'column' => 'order_id'],
76 'activation_count' => ['label' => __('Activation Count', 'fluent-cart'), 'column' => 'activation_count'],
77 'expiration_date' => ['label' => __('Expiration Date', 'fluent-cart'), 'column' => 'expiration_date'],
78 'created_at' => ['label' => __('Date', 'fluent-cart'), 'column' => 'created_at'],
79 'status' => ['label' => __('Status', 'fluent-cart'), 'column' => 'status'],
80 ];
81 }
82
83 /**
84 * `GET /licensing/licenses` requires `licenses/view`. That says nothing
85 * about customers or products, so those relations carry their own bar
86 * through every door below.
87 *
88 * SCREEN key — `admin_license_list` loads the three columns LicenseTable.js
89 * renders, with the product and variant selects narrowed to the titles the
90 * table actually prints.
91 *
92 * PUBLIC keys — `customer`, `product` and `productVariant` are the plain
93 * relation names a consumer can reasonably ask a licence endpoint for, and
94 * they were accepted values before this allow-list existed. They give the
95 * unnarrowed relation: the screen's two-column select is a property of that
96 * screen, not of the endpoint's published shape.
97 *
98 * `License::variation()` is a byte-identical duplicate of
99 * `productVariant()` and stays refused, so the same data has exactly one
100 * door.
101 *
102 * Deliberately NOT allowlisted: `activations` (site URLs and activation
103 * secrets), `order` and `subscription` (billing rows behind other
104 * permissions).
105 *
106 * @return array<string, callable>
107 */
108 protected function allowedWiths(): array
109 {
110 return [
111 'admin_license_list' => [$this, 'adminLicenseList'],
112
113 'customer' => [$this, 'publicCustomer'],
114 'product' => [$this, 'publicProduct'],
115 'productVariant' => [$this, 'publicProductVariant'],
116 ];
117 }
118
119 /**
120 * `GET /licensing/licenses`, sent by LicenseTable.js — the customer column
121 * plus the product and variant titles.
122 *
123 * No select on the customer: Customer::$appends (`full_name`, `photo`,
124 * `country_name`, `formatted_address`, `user_link`) reads first_name,
125 * last_name, country, state, city, postcode and user_id. The product and
126 * variant selects the client used to send as `product:ID,post_title` and
127 * `productVariant:id,variation_title` live here now — same columns, no
128 * longer client-controlled, and applied INSIDE the relation closures so
129 * they narrow those relations rather than the licence query.
130 *
131 * @param \FluentCart\Framework\Database\Orm\Builder $query
132 * @return \FluentCart\Framework\Database\Orm\Builder
133 */
134 protected function adminLicenseList($query)
135 {
136 // `licenses/view` says nothing about customers.
137 if ($this->userCanAny('customers/view')) {
138 $query->with(['customer']);
139 }
140
141 // …nor about the catalogue.
142 if ($this->userCanAny('products/view')) {
143 $query->with([
144 'product' => function ($productQuery) {
145 $productQuery->select(['ID', 'post_title']);
146 },
147 'productVariant' => function ($variantQuery) {
148 $variantQuery->select(['id', 'variation_title']);
149 },
150 ]);
151 }
152
153 return $query;
154 }
155
156 /**
157 * `with[]=customer` — a public entry point, carrying the same
158 * `customers/view` bar as the screen key.
159 *
160 * @param \FluentCart\Framework\Database\Orm\Builder $query
161 * @return bool|\FluentCart\Framework\Database\Orm\Builder
162 */
163 protected function publicCustomer($query)
164 {
165 if (!$this->userCanAny('customers/view')) {
166 return false;
167 }
168
169 return $query->with(['customer']);
170 }
171
172 /**
173 * `with[]=product` — a public entry point, carrying the same
174 * `products/view` bar as the screen key.
175 *
176 * @param \FluentCart\Framework\Database\Orm\Builder $query
177 * @return bool|\FluentCart\Framework\Database\Orm\Builder
178 */
179 protected function publicProduct($query)
180 {
181 if (!$this->userCanAny('products/view')) {
182 return false;
183 }
184
185 return $query->with(['product']);
186 }
187
188 /**
189 * `with[]=productVariant` — a public entry point, carrying the same
190 * `products/view` bar as the screen key.
191 *
192 * @param \FluentCart\Framework\Database\Orm\Builder $query
193 * @return bool|\FluentCart\Framework\Database\Orm\Builder
194 */
195 protected function publicProductVariant($query)
196 {
197 if (!$this->userCanAny('products/view')) {
198 return false;
199 }
200
201 return $query->with(['productVariant']);
202 }
203
204
205 public function applyActiveViewFilter(?string $activeView = null): void
206 {
207 $activeView = $activeView ?? $this->activeView;
208 $tabsMap = $this->tabsMap();
209
210 $this->query->when($activeView, function ($query, $activeView) use ($tabsMap) {
211
212 $validStatuses = [
213 'active',
214 'expired',
215 'disabled',
216 'inactive'
217 ];
218
219 if (in_array($activeView, $validStatuses)) {
220 if ($activeView == 'expired') {
221 $query->where('expiration_date', '<', DateTime::gmtNow());
222 } else if ($activeView == 'active') {
223 $query->where(function ($query) {
224 $query->where('expiration_date', '>', DateTime::gmtNow())
225 ->orWhereNull('expiration_date');
226 })
227 ->where('status', 'active');
228 } else {
229 $query->where('status', $activeView);
230 }
231 } else if ($activeView == 'inactive') {
232 $query->where('status', 'active')
233 ->whereDoesntHave('activations');
234 }
235
236 return $query;
237 });
238 }
239
240 public static function getSearchableFields(): array
241 {
242 return [
243 'key' => [
244 'column' => 'license_key',
245 'description' => 'license_key',
246 'type' => 'string'
247 ]
248 ];
249 }
250
251 public static function advanceFilterOptions(): array
252 {
253 $filters = [
254 'product' => [
255 'label' => __('Products', 'fluent-cart'),
256 'value' => 'product',
257 'children' => [
258 [
259 'label' => __('By Products', 'fluent-cart'),
260 'value' => 'product',
261 'column' => 'variation_id',
262 'filter_type' => 'relation',
263 'relation' => 'productVariant',
264 'remote_data_key' => 'product_variations',
265 'type' => 'remote_tree_select',
266 'limit' => 10,
267 ],
268 ],
269 ],
270 'customer' => [
271 'label' => __('Customer Property', 'fluent-cart'),
272 'value' => 'customer',
273 'children' => [
274 [
275 'label' => __('Customer first name', 'fluent-cart'),
276 'value' => 'customer_first_name',
277 'type' => 'text',
278 'filter_type' => 'relation',
279 'column' => 'first_name',
280 'relation' => 'customer',
281 ],
282 [
283 'label' => __('Customer last name', 'fluent-cart'),
284 'value' => 'customer_last_name',
285 'type' => 'text',
286 'filter_type' => 'relation',
287 'column' => 'last_name',
288 'relation' => 'customer',
289 ]
290 ],
291 ],
292 'license' => [
293 'label' => __('License Property', 'fluent-cart'),
294 'value' => 'license',
295 'children' => [
296 [
297 'label' => __('License key', 'fluent-cart'),
298 'value' => 'license_key',
299 'type' => 'text',
300 'filter_type' => 'column',
301 'column' => 'license_key',
302 ],
303 [
304 'label' => __('Status', 'fluent-cart'),
305 'value' => 'status',
306 'type' => 'selections',
307 'filter_type' => 'column',
308 'column' => 'status',
309 'options' => [
310 Status::LICENSE_ACTIVE => __('Active', 'fluent-cart'),
311 Status::LICENSE_DISABLED => __('Disabled', 'fluent-cart'),
312 Status::LICENSE_EXPIRED => __('Expired', 'fluent-cart'),
313 ],
314 'is_multiple' => true,
315 'is_only_in' => true
316 ],
317 [
318 'label' => __('Activation Count', 'fluent-cart'),
319 'value' => 'activation_count',
320 'type' => 'numeric',
321 'filter_type' => 'column',
322 'column' => 'activation_count',
323 ],
324 [
325 'label' => __('Expiration Date', 'fluent-cart'),
326 'value' => 'expiration_date',
327 'type' => 'dates',
328 'filter_type' => 'date',
329 'column' => 'expiration_date',
330 ]
331 ],
332 ],
333 ];
334 return LabelFilter::advanceFilterOptionsForOther($filters);
335 }
336
337 }
338