PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.1
1.6.6 1.6.5 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 All 49 releases
fluent-cart / app / Services / Filter / LicenseFilter.php

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

324 lines 11.7 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 * `GET /licensing/licenses` requires `licenses/view`. That says nothing
71 * about customers or products, so those relations carry their own bar
72 * through every door below.
73 *
74 * SCREEN key — `admin_license_list` loads the three columns LicenseTable.js
75 * renders, with the product and variant selects narrowed to the titles the
76 * table actually prints.
77 *
78 * PUBLIC keys — `customer`, `product` and `productVariant` are the plain
79 * relation names a consumer can reasonably ask a licence endpoint for, and
80 * they were accepted values before this allow-list existed. They give the
81 * unnarrowed relation: the screen's two-column select is a property of that
82 * screen, not of the endpoint's published shape.
83 *
84 * `License::variation()` is a byte-identical duplicate of
85 * `productVariant()` and stays refused, so the same data has exactly one
86 * door.
87 *
88 * Deliberately NOT allowlisted: `activations` (site URLs and activation
89 * secrets), `order` and `subscription` (billing rows behind other
90 * permissions).
91 *
92 * @return array<string, callable>
93 */
94 protected function allowedWiths(): array
95 {
96 return [
97 'admin_license_list' => [$this, 'adminLicenseList'],
98
99 'customer' => [$this, 'publicCustomer'],
100 'product' => [$this, 'publicProduct'],
101 'productVariant' => [$this, 'publicProductVariant'],
102 ];
103 }
104
105 /**
106 * `GET /licensing/licenses`, sent by LicenseTable.js — the customer column
107 * plus the product and variant titles.
108 *
109 * No select on the customer: Customer::$appends (`full_name`, `photo`,
110 * `country_name`, `formatted_address`, `user_link`) reads first_name,
111 * last_name, country, state, city, postcode and user_id. The product and
112 * variant selects the client used to send as `product:ID,post_title` and
113 * `productVariant:id,variation_title` live here now — same columns, no
114 * longer client-controlled, and applied INSIDE the relation closures so
115 * they narrow those relations rather than the licence query.
116 *
117 * @param \FluentCart\Framework\Database\Orm\Builder $query
118 * @return \FluentCart\Framework\Database\Orm\Builder
119 */
120 protected function adminLicenseList($query)
121 {
122 // `licenses/view` says nothing about customers.
123 if ($this->userCanAny('customers/view')) {
124 $query->with(['customer']);
125 }
126
127 // …nor about the catalogue.
128 if ($this->userCanAny('products/view')) {
129 $query->with([
130 'product' => function ($productQuery) {
131 $productQuery->select(['ID', 'post_title']);
132 },
133 'productVariant' => function ($variantQuery) {
134 $variantQuery->select(['id', 'variation_title']);
135 },
136 ]);
137 }
138
139 return $query;
140 }
141
142 /**
143 * `with[]=customer` — a public entry point, carrying the same
144 * `customers/view` bar as the screen key.
145 *
146 * @param \FluentCart\Framework\Database\Orm\Builder $query
147 * @return bool|\FluentCart\Framework\Database\Orm\Builder
148 */
149 protected function publicCustomer($query)
150 {
151 if (!$this->userCanAny('customers/view')) {
152 return false;
153 }
154
155 return $query->with(['customer']);
156 }
157
158 /**
159 * `with[]=product` — a public entry point, carrying the same
160 * `products/view` bar as the screen key.
161 *
162 * @param \FluentCart\Framework\Database\Orm\Builder $query
163 * @return bool|\FluentCart\Framework\Database\Orm\Builder
164 */
165 protected function publicProduct($query)
166 {
167 if (!$this->userCanAny('products/view')) {
168 return false;
169 }
170
171 return $query->with(['product']);
172 }
173
174 /**
175 * `with[]=productVariant` — a public entry point, carrying the same
176 * `products/view` bar as the screen key.
177 *
178 * @param \FluentCart\Framework\Database\Orm\Builder $query
179 * @return bool|\FluentCart\Framework\Database\Orm\Builder
180 */
181 protected function publicProductVariant($query)
182 {
183 if (!$this->userCanAny('products/view')) {
184 return false;
185 }
186
187 return $query->with(['productVariant']);
188 }
189
190
191 public function applyActiveViewFilter(?string $activeView = null): void
192 {
193 $activeView = $activeView ?? $this->activeView;
194 $tabsMap = $this->tabsMap();
195
196 $this->query->when($activeView, function ($query, $activeView) use ($tabsMap) {
197
198 $validStatuses = [
199 'active',
200 'expired',
201 'disabled',
202 'inactive'
203 ];
204
205 if (in_array($activeView, $validStatuses)) {
206 if ($activeView == 'expired') {
207 $query->where('expiration_date', '<', DateTime::gmtNow());
208 } else if ($activeView == 'active') {
209 $query->where(function ($query) {
210 $query->where('expiration_date', '>', DateTime::gmtNow())
211 ->orWhereNull('expiration_date');
212 })
213 ->where('status', 'active');
214 } else {
215 $query->where('status', $activeView);
216 }
217 } else if ($activeView == 'inactive') {
218 $query->where('status', 'active')
219 ->whereDoesntHave('activations');
220 }
221
222 return $query;
223 });
224 }
225
226 public static function getSearchableFields(): array
227 {
228 return [
229 'key' => [
230 'column' => 'license_key',
231 'description' => 'license_key',
232 'type' => 'string'
233 ]
234 ];
235 }
236
237 public static function advanceFilterOptions(): array
238 {
239 $filters = [
240 'product' => [
241 'label' => __('Products', 'fluent-cart'),
242 'value' => 'product',
243 'children' => [
244 [
245 'label' => __('By Products', 'fluent-cart'),
246 'value' => 'product',
247 'column' => 'variation_id',
248 'filter_type' => 'relation',
249 'relation' => 'productVariant',
250 'remote_data_key' => 'product_variations',
251 'type' => 'remote_tree_select',
252 'limit' => 10,
253 ],
254 ],
255 ],
256 'customer' => [
257 'label' => __('Customer Property', 'fluent-cart'),
258 'value' => 'customer',
259 'children' => [
260 [
261 'label' => __('Customer first name', 'fluent-cart'),
262 'value' => 'customer_first_name',
263 'type' => 'text',
264 'filter_type' => 'relation',
265 'column' => 'first_name',
266 'relation' => 'customer',
267 ],
268 [
269 'label' => __('Customer last name', 'fluent-cart'),
270 'value' => 'customer_last_name',
271 'type' => 'text',
272 'filter_type' => 'relation',
273 'column' => 'last_name',
274 'relation' => 'customer',
275 ]
276 ],
277 ],
278 'license' => [
279 'label' => __('License Property', 'fluent-cart'),
280 'value' => 'license',
281 'children' => [
282 [
283 'label' => __('License key', 'fluent-cart'),
284 'value' => 'license_key',
285 'type' => 'text',
286 'filter_type' => 'column',
287 'column' => 'license_key',
288 ],
289 [
290 'label' => __('Status', 'fluent-cart'),
291 'value' => 'status',
292 'type' => 'selections',
293 'filter_type' => 'column',
294 'column' => 'status',
295 'options' => [
296 Status::LICENSE_ACTIVE => __('Active', 'fluent-cart'),
297 Status::LICENSE_DISABLED => __('Disabled', 'fluent-cart'),
298 Status::LICENSE_EXPIRED => __('Expired', 'fluent-cart'),
299 ],
300 'is_multiple' => true,
301 'is_only_in' => true
302 ],
303 [
304 'label' => __('Activation Count', 'fluent-cart'),
305 'value' => 'activation_count',
306 'type' => 'numeric',
307 'filter_type' => 'column',
308 'column' => 'activation_count',
309 ],
310 [
311 'label' => __('Expiration Date', 'fluent-cart'),
312 'value' => 'expiration_date',
313 'type' => 'dates',
314 'filter_type' => 'date',
315 'column' => 'expiration_date',
316 ]
317 ],
318 ],
319 ];
320 return LabelFilter::advanceFilterOptionsForOther($filters);
321 }
322
323 }
324