PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / models / customer.php
vikappointments / site / helpers / libraries / models Last commit date
conversion.php 3 days ago customer.php 3 days ago customfields.php 3 days ago index.html 3 days ago locations.php 3 days ago orderstatus.php 3 days ago restrictions.php 3 days ago specialrates.php 3 days ago statistics.php 3 days ago subscriptions.php 3 days ago
customer.php
348 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * VikAppointments customer class handler.
16 *
17 * @since 1.7
18 */
19 class VAPCustomer extends JObject
20 {
21 /**
22 * A list of cached customers.
23 *
24 * @var VAPCustomer[]
25 */
26 protected static $instances = array();
27
28 /**
29 * Returns the details of the given customer.
30 *
31 * @param mixed $id The customer ID. If not specified, the customer assigned
32 * to the current user will be retrieved, if any.
33 *
34 * @return self A customer instance.
35 *
36 * @throws Exception
37 */
38 public static function getInstance($id = null)
39 {
40 $key = !$id ? 'auto' : (int) $id;
41
42 // check whether the customer has been already fetched
43 if (!isset(static::$instances[$key]))
44 {
45 // nope, create a new instance
46 static::$instances[$key] = new static($id);
47 }
48
49 return static::$instances[$key];
50 }
51
52 /**
53 * Class constructor.
54 *
55 * @param mixed $id The customer ID. If not specified, the customer assigned
56 * to the current user will be retrieved, if any.
57 *
58 * @throws Exception
59 */
60 public function __construct($id)
61 {
62 // initialize object with loaded details
63 parent::__construct($this->load($id));
64 }
65
66 /**
67 * Checks whether the current user owns an active subscription.
68 *
69 * @param mixed $id Either an array of services or a service ID.
70 * Leave empty to bypass the service validation.
71 * @param mixed $checkin An optional check-in date to make sure the
72 * user is still subscribed on that date.
73 *
74 * @return boolean
75 */
76 public function isSubscribed($id = null, $checkin = null)
77 {
78 // always treat the id as an array
79 $id = (array) $id;
80
81 $dispatcher = VAPFactory::getEventDispatcher();
82
83 /**
84 * This event can be used to apply additional conditions to the subscription validation.
85 * It can be used to skip the default system validations.
86 *
87 * @param VAPCustomer $customer The customer details.
88 * @param array $services A list of services to check.
89 * @param mixed $checkin An optional check-in date (UTC).
90 *
91 * @return boolean True to flag the customer as subscribed; false to flag it as expired
92 * or not yet subscribed, any other value to rely on the default system.
93 *
94 * @since 1.7
95 */
96 $results = $dispatcher->trigger('onBeforeValidateCustomerSubscription', array($this, $id, $checkin));
97
98 if (in_array(false, $results, true))
99 {
100 // customer not subscribed
101 return false;
102 }
103
104 if (in_array(true, $results, true))
105 {
106 // customer correctly subscribed
107 return false;
108 }
109
110 // Plugins did not manipulate the response, fallback to the default system.
111 // First of all, make sure the user is subscribed.
112 if (!$this->subscribed || !$this->subscription)
113 {
114 // user not subscribed or removed subscription plan
115 return false;
116 }
117
118 // Then make sure the specified services are covered by the subscription plan.
119 // Check only in case the list of assigned services is not empty, otherwise
120 // go ahead because the plan supports any created service.
121 if ($this->subscription['services'] && array_diff($id, $this->subscription['services']))
122 {
123 // at least one of the selected service is not covered...
124 return false;
125 }
126
127 // check whether the check-in date was specified
128 if (!VAPDateHelper::isNull($checkin))
129 {
130 // validate the selected check-in
131 if (VAPFactory::getConfig()->getBool('subscrthreshold') === true)
132 {
133 // validate the specified check-in against the expiration date
134 $threshold = $checkin;
135 }
136 else
137 {
138 // use the current day in place of the check-in date
139 $threshold = JFactory::getDate()->format('Y-m-d H:i:s');
140 }
141
142 // make sure the subscription plan is still active for the specified check-in (equals or higher)
143 if (!$this->lifetime && $this->active_to_date < $threshold)
144 {
145 // the subscription is not lifetime and the expiration date is
146 // lower than the given check-in date time
147 return false;
148 }
149 }
150
151 /**
152 * This event can be used to apply additional conditions to the subscription validation.
153 * When this hook triggers, the system already validated all the default conditions.
154 *
155 * @param VAPCustomer $customer The customer details.
156 * @param array $services A list of services to check.
157 * @param mixed $checkin An optional check-in date (UTC).
158 *
159 * @return boolean False to flag the customer subscription as expired.
160 *
161 * @since 1.7
162 */
163 if ($dispatcher->false('onAfterValidateCustomerSubscription', array($this, $id, $checkin)))
164 {
165 // a plugin extended the validation of the subscription and decided that this
166 // customer is not compliant with the searched query
167 return false;
168 }
169
170 // subscription active
171 return true;
172 }
173
174 /**
175 * Helper method used to load the customer details.
176 *
177 * @param mixed $id The customer ID. If not specified, the customer assigned
178 * to the current user will be retrieved, if any.
179 *
180 * @throws Exception
181 */
182 protected function load($id)
183 {
184 $jid = null;
185
186 if (is_null($id))
187 {
188 // get current CMS user
189 $user = JFactory::getUser();
190
191 // make sure the user is not a guest
192 if ($user->guest)
193 {
194 // user not logged in, throw an exception
195 throw new RangeException('User not logged in');
196 }
197
198 // get CMS user ID
199 $jid = $user->id;
200 }
201 else
202 {
203 // use given ID
204 $id = (int) $id;
205 }
206
207 $dispatcher = VAPFactory::getEventDispatcher();
208
209 $dbo = JFactory::getDbo();
210
211 $q = $dbo->getQuery(true);
212
213 // get customer details
214 $q->select('c.*');
215 $q->from($dbo->qn('#__vikappointments_users', 'c'));
216
217 // get billing country name
218 $q->select($dbo->qn('ctr.country_name', 'country'));
219 $q->leftjoin($dbo->qn('#__vikappointments_countries', 'ctr') . ' ON ' . $dbo->qn('ctr.country_2_code') . ' = ' . $dbo->qn('c.country_code'));
220
221 // get CMS user details
222 $q->select($dbo->qn('u.name', 'user_name'));
223 $q->select($dbo->qn('u.username', 'user_username'));
224 $q->select($dbo->qn('u.email', 'user_email'));
225 $q->leftjoin($dbo->qn('#__users', 'u') . ' ON ' . $dbo->qn('u.id') . ' = ' . $dbo->qn('c.jid'));
226
227 if (is_null($id))
228 {
229 // get customer by CMS user
230 $q->where($dbo->qn('u.id') . ' = ' . $jid);
231 }
232 else
233 {
234 // get customer by ID
235 $q->where($dbo->qn('c.id') . ' = ' . $id);
236 }
237
238 /**
239 * External plugins can attach to this hook in order to manipulate
240 * the query at runtime, in example to include additional properties.
241 *
242 * Notice that the query is always limited to 1 element, so it is not
243 * suggested to join here tables that may fetch several rows. Use a
244 * different hook for this purpose (@see onSetupCustomerDetails).
245 *
246 * @param mixed &$query A query builder instance.
247 * @param integer $id The ID of the customer.
248 *
249 * @return void
250 *
251 * @since 1.7
252 */
253 $dispatcher->trigger('onLoadCustomerDetails', array(&$q, $id));
254
255 $dbo->setQuery($q, 0, 1);
256 $app = $dbo->loadObject();
257
258 if (!$app)
259 {
260 // no matching customers
261 throw new RuntimeException('User not found', 404);
262 }
263
264 $customer = new stdClass;
265
266 foreach ($app as $k => $v)
267 {
268 // exclude CMS user fields, which will be added
269 // into a different property
270 if (!preg_match("/^user_/", $k))
271 {
272 $customer->{$k} = $v;
273 }
274 }
275
276 // decode customer fields
277 $customer->fields = (array) json_decode($customer->fields, true);
278
279 // create CMS user object
280 $customer->user = new stdClass;
281 $customer->user->id = $app->jid;
282 $customer->user->name = $app->user_name;
283 $customer->user->username = $app->user_username;
284 $customer->user->email = $app->user_email;
285
286 $now = JFactory::getDate();
287
288 // check whether the customer owns an active subscription
289 $customer->subscribed = $customer->lifetime || (!VAPDateHelper::isNull($customer->active_to_date) && $customer->active_to_date >= $now->toSql());
290
291 if ($customer->subscribed && !$customer->lifetime)
292 {
293 // calculate remaining days
294 $customer->daysLeft = VAPDateHelper::diff($customer->active_to_date, $now, 'days');
295 }
296 else
297 {
298 $customer->daysLeft = 0;
299 }
300
301 $customer->subscription = null;
302
303 VAPLoader::import('libraries.models.subscriptions');
304
305 // check whether the system is selling the subscriptions for the customers
306 if (!VAPDateHelper::isNull($customer->active_since) && VAPSubscriptions::has())
307 {
308 // fetch latest subscription purchased by this customer
309 $q = $dbo->getQuery(true)
310 ->select($dbo->qn('id_subscr'))
311 ->from($dbo->qn('#__vikappointments_subscr_order'))
312 ->where($dbo->qn('id_user') . ' = ' . $customer->id)
313 ->order($dbo->qn('id') . ' DESC');
314
315 // get any approved codes
316 $approved = JHtml::fetch('vaphtml.status.find', 'code', array('subscriptions' => 1, 'approved' => 1));
317
318 if ($approved)
319 {
320 // filter by approved status
321 $q->where($dbo->qn('status') . ' IN (' . implode(',', array_map(array($dbo, 'q'), $approved)) . ')');
322 }
323
324 $dbo->setQuery($q, 0, 1);
325
326 if ($subscrId = $dbo->loadResult())
327 {
328 // load subscription details
329 $customer->subscription = VAPSubscriptions::get($subscrId, $group = 0, $strict = false);
330 }
331 }
332
333 /**
334 * External plugins can use this event to manipulate the object holding
335 * the details of the customer. Useful to inject all the additional data
336 * fetched with the manipulation of the query.
337 *
338 * @param mixed $customer The customer details object.
339 * @param object $data The query resulting object.
340 *
341 * @return void
342 */
343 $dispatcher->trigger('onSetupCustomerDetails', array($customer, $app));
344
345 return $customer;
346 }
347 }
348