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 / mail / templates / stock.php
vikappointments / site / helpers / libraries / mail / templates Last commit date
admin.php 5 days ago cancellation.php 5 days ago customer.php 5 days ago employee.php 5 days ago index.html 5 days ago packadmin.php 5 days ago package.php 5 days ago stock.php 5 days ago waitlist.php 5 days ago
stock.php
409 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 VAPLoader::import('libraries.mail.template');
15
16 /**
17 * Wrapper used to handle mail notifications for the options with remaining units
18 * lower than the specified threshold.
19 *
20 * @since 1.7.7
21 */
22 class VAPMailTemplateStock implements VAPMailTemplate
23 {
24 /**
25 * The items list.
26 *
27 * @var object[]
28 */
29 protected $items;
30
31 /**
32 * An optional template file to use.
33 *
34 * @var string
35 */
36 protected $templateFile;
37
38 /**
39 * An array of options.
40 *
41 * @var array
42 */
43 protected $options;
44
45 /**
46 * Class constructor.
47 *
48 * @param array $options An array of options.
49 */
50 public function __construct(array $options = array())
51 {
52 if (empty($options['lang']))
53 {
54 // always use default language in case it is not specified
55 $options['lang'] = VikAppointments::getDefaultLanguage();
56 }
57
58 // register options
59 $this->options = $options;
60
61 // load given language to translate template contents
62 VikAppointments::loadLanguage($this->options['lang'], JPATH_SITE);
63
64 // fetch the products to notify
65 $this->items = $this->getItems($options);
66 }
67
68 /**
69 * Finds all the items having low stocks.
70 *
71 * @param array $options A configuration array.
72 *
73 * @return object[] A list of items.
74 */
75 public function getItems(array $options = [])
76 {
77 if (!is_null($this->items))
78 {
79 return $this->items;
80 }
81
82 $this->items = [];
83
84 $db = \JFactory::getDbo();
85
86 // load all the options that support a limited purchase
87 $query = $db->getQuery(true)
88 ->select($db->qn('o.id', 'optionId'))
89 ->select($db->qn('o.name', 'optionName'))
90 ->select($db->qn('o.stock', 'optionStock'))
91 ->select($db->qn('v.id', 'variationId'))
92 ->select($db->qn('v.name', 'variationName'))
93 ->select($db->qn('v.stock', 'variationStock'))
94 ->select(sprintf('IFNULL(%s, %s) AS %s', $db->qn('v.notify_below'), $db->qn('o.notify_below'), $db->qn('threshold')))
95 ->from($db->qn('#__vikappointments_option', 'o'))
96 ->leftjoin($db->qn('#__vikappointments_option_value', 'v') . ' ON ' . $db->qn('o.id') . ' = ' . $db->qn('v.id_option') . ' AND ' . $db->qn('v.stock') . ' = 1');
97
98 if ($this->options['test'] ?? false)
99 {
100 // take only the specified items
101 $start = $options['start'] ?? 0;
102 $limit = $options['limit'] ?? 5;
103
104 // prefer the options that support the stock system
105 $query->order($db->qn('o.stock') . ' DESC');
106 $query->order($db->qn('v.stock') . ' DESC');
107 }
108 else
109 {
110 // take only the products that haven't been already notified
111 $query->where(sprintf('IFNULL(%s, %s) = 0', $db->qn('v.stock_notified'), $db->qn('o.stock_notified')));
112
113 // take only the products that actually support the stock system
114 $query->andWhere([
115 $db->qn('o.stock') . ' = 1',
116 $db->qn('v.stock') . ' = 1',
117 ], 'OR');
118
119 // load all the products
120 $start = 0;
121 $limit = null;
122 }
123
124 $query->order($db->qn('o.ordering') . ' ASC');
125 $query->order($db->qn('v.ordering') . ' ASC');
126
127 $db->setQuery($query, $start, $limit);
128 $stockItems = $db->loadObjectList();
129
130 if (!$stockItems)
131 {
132 // there are no items supporing the stocks
133 return $this->items;
134 }
135
136 $optionModel = JModelVAP::getInstance('option');
137
138 foreach ($stockItems as $item)
139 {
140 // calculate the actual remaining number of units
141 $item->remaining = $optionModel->getStock($item->optionId, $item->variationId);
142
143 if ($item->remaining >= $item->threshold && empty($this->options['test']))
144 {
145 continue;
146 }
147
148 $this->items[] = $item;
149 }
150
151 // get translator
152 $translator = VAPFactory::getTranslator();
153
154 $option_ids = [];
155 $optvar_ids = [];
156
157 foreach ($this->items as $item)
158 {
159 $option_ids[] = $item->optionId;
160
161 if ($item->variationId)
162 {
163 $optvar_ids[] = $item->variationId;
164 }
165 }
166
167 // pre-load options translations
168 $optLang = $translator->load('option', array_unique($option_ids), $this->options['lang']);
169 // pre-load options variations translations
170 $varLang = $translator->load('optionvar', array_unique($optvar_ids), $this->options['lang']);
171
172 foreach ($this->items as $item)
173 {
174 // translate option name for the given language
175 $opt_tx = $optLang->getTranslation($item->optionId, $this->options['lang']);
176
177 if ($opt_tx)
178 {
179 $item->optionName = $opt_tx->name;
180 }
181
182 if ($item->variationId > 0)
183 {
184 // translate variation name for the given language
185 $var_tx = $varLang->getTranslation($item->variationId, $this->options['lang']);
186
187 if ($var_tx)
188 {
189 $item->variationName = $var_tx->name;
190 }
191 }
192
193 $item->fullName = $item->optionName . ($item->variationName ? ' - ' . $item->variationName : '');
194 }
195
196 return $this->items;
197 }
198
199 /**
200 * Returns the code of the template before being parsed.
201 *
202 * @param string $file An optional template file to use. If not specified,
203 * the one set in configuration will be used.
204 *
205 * @return void
206 */
207 public function setFile($file)
208 {
209 // use specified template file
210 $this->templateFile = $file;
211
212 // check if a filename or a path was passed
213 if ($file && !is_file($file))
214 {
215 // make sure we have a valid file path
216 $this->templateFile = VAPHELPERS . DIRECTORY_SEPARATOR . 'mail_tmpls' . DIRECTORY_SEPARATOR . $file;
217 }
218 }
219
220 /**
221 * Returns the code of the template before being parsed.
222 *
223 * @return string
224 */
225 public function getTemplate()
226 {
227 // copy items details in a local variable for being used directly
228 // within the template file
229 $items = $this->items;
230
231 if (!$this->templateFile)
232 {
233 // get template file from configuration
234 $file = VAPFactory::getConfig()->get('stockmailtmpl');
235
236 // build template path
237 $this->templateFile = VAPHELPERS . DIRECTORY_SEPARATOR . 'mail_tmpls' . DIRECTORY_SEPARATOR . $file;
238 }
239
240 // make sure the file exists
241 if (!is_file($this->templateFile))
242 {
243 // missing file, return empty string
244 return '';
245 }
246
247 // start output buffering
248 ob_start();
249 // include file to catch its contents
250 include $this->templateFile;
251 // write template contents within a variable
252 $content = ob_get_contents();
253 // clear output buffer
254 ob_end_clean();
255
256 // free space
257 unset($items);
258
259 return $content;
260 }
261
262 /**
263 * Fetches the subject to be used in the e-mail.
264 *
265 * @return string
266 */
267 public function getSubject()
268 {
269 // get company name
270 $fromname = VAPFactory::getConfig()->getString('agencyname');
271
272 /**
273 * Fetch subject.
274 *
275 * @since 1.7.7 Added the possibility to use a custom subject.
276 */
277 $subject = !empty($this->options['subject']) ? $this->options['subject'] : 'VAPLOWSTOCKEMAILSUBJECT';
278 $subject = JText::sprintf($subject, $fromname);
279
280 // let plugins manipulate the subject for this e-mail template
281 $res = VAPMailFactory::letPluginsManipulateMail('stock', 'subject', $subject, $this->items);
282
283 if ($res === false)
284 {
285 // a plugin prevented the e-mail sending
286 return '';
287 }
288
289 return $subject;
290 }
291
292 /**
293 * Parses the HTML of the template and returns it.
294 *
295 * @return string
296 */
297 public function getHtml()
298 {
299 $config = VAPFactory::getConfig();
300
301 // load template HTML
302 $tmpl = $this->getTemplate();
303
304 // fetch company logo image
305 $logo_str = $config->get('companylogo');
306
307 if ($logo_str && is_file(VAPMEDIA . DIRECTORY_SEPARATOR . $logo_str))
308 {
309 $logo_str = JHtml::fetch('vaphtml.media.display', $logo_str, [
310 'alt' => $config->get('agencyname'),
311 'small' => false,
312 'style' => 'max-width: 100%;',
313 ]);
314 }
315 else
316 {
317 $logo_str = '';
318 }
319
320 // build placeholders lookup
321 $placeholders = array(
322 'logo' => $logo_str,
323 'company_name' => $config->get('agencyname'),
324 'stocks_content' => \JText::translate('VAPLOWSTOCKCONTENT'),
325 'stocks_help' => \JText::translate('VAPLOWSTOCKHELP'),
326 );
327
328 if ($this->options['test'] ?? false)
329 {
330 $placeholders['stocks_content'] .= '<p><small><em>The following items are displayed for test purposes only.</em></small></p>';
331 }
332
333 // parse e-mail template placeholders
334 foreach ($placeholders as $tag => $value)
335 {
336 $tmpl = str_replace("{{$tag}}", $value, $tmpl);
337 }
338
339 // let plugins manipulate the content for this e-mail template
340 $res = VAPMailFactory::letPluginsManipulateMail('stock', 'content', $tmpl, $this->items);
341
342 if ($res === false)
343 {
344 // a plugin prevented the e-mail sending
345 return '';
346 }
347
348 return $tmpl;
349 }
350
351 /**
352 * Sends the HTML contents via e-mail.
353 *
354 * @return boolean
355 */
356 public function send()
357 {
358 $config = VAPFactory::getConfig();
359
360 // get administrators e-mail
361 $adminmails = VikAppointments::getAdminMailList();
362 // get sender e-mail address
363 $sendermail = VikAppointments::getSenderMail();
364 // get company name
365 $fromname = $config->getString('agencyname');
366
367 // fetch subject
368 $subject = $this->getSubject();
369
370 // parse e-mail template
371 $html = $this->getHtml();
372
373 if (empty($subject) || empty($html))
374 {
375 // do not send e-mail in case the subject or
376 // the content are empty
377 return false;
378 }
379
380 $sent = false;
381
382 foreach ($adminmails as $recipient)
383 {
384 // send the e-mail notification
385 $sent = VAPApplication::getInstance()->sendMail(
386 $sendermail,
387 $fromname,
388 $recipient,
389 $recipient,
390 $subject,
391 $html
392 ) || $sent;
393 }
394
395 return $sent;
396 }
397
398 /**
399 * Checks whether the notification should be sent.
400 *
401 * @return boolean
402 */
403 public function shouldSend()
404 {
405 // make sure there is at least a product to notify
406 return (bool) $this->items;
407 }
408 }
409