PluginProbe ʕ •ᴥ•ʔ
Event Tickets with Ticket Scanner / 2.7.8
Event Tickets with Ticket Scanner v2.7.8
3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 trunk 2.6.0 2.7.0 2.7.1 2.7.10 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 2.9.8 2.9.9 3.0.0 3.0.1 3.0.2 3.0.3
event-tickets-with-ticket-scanner / backend.js
event-tickets-with-ticket-scanner Last commit date
3rd 9 months ago css 9 months ago img 9 months ago languages 9 months ago ticket 9 months ago vendors 9 months ago SASO_EVENTTICKETS.php 9 months ago backend.js 9 months ago changelog.txt 9 months ago db.php 9 months ago index.php 9 months ago init_file.php 9 months ago js_seatingplan.js 9 months ago order_details.js 9 months ago readme.txt 9 months ago saso-eventtickets-validator.js 9 months ago sasoEventtickets_AdminSettings.php 9 months ago sasoEventtickets_Authtoken.php 9 months ago sasoEventtickets_Base.php 9 months ago sasoEventtickets_Core.php 9 months ago sasoEventtickets_Frontend.php 9 months ago sasoEventtickets_Messenger.php 9 months ago sasoEventtickets_Options.php 9 months ago sasoEventtickets_PDF.php 9 months ago sasoEventtickets_Ticket.php 9 months ago sasoEventtickets_TicketBadge.php 9 months ago sasoEventtickets_TicketDesigner.php 9 months ago sasoEventtickets_TicketQR.php 9 months ago ticket_events.js 9 months ago ticket_scanner.js 9 months ago validator.js 9 months ago wc_backend.js 9 months ago wc_frontend.js 9 months ago woocommerce-hooks.php 9 months ago
backend.js
3706 lines
1 function sasoEventtickets(_myAjaxVar, doNotInit) {
2 const { __, _x, _n, sprintf } = wp.i18n;
3 let myAjax = _myAjaxVar;
4 let self = this;
5 let PREMIUM = null;
6 var $ = jQuery;
7 var PARAS = basics_ermittelURLParameter();
8 var DATA = {
9 /*action: '',*/
10 nonce: myAjax.nonce,
11 last_nonce_check: 0
12 };
13
14 var system = {is_debug:false, DYNJS:{}, DYNJS_CACHE:{}};
15 var FATAL_ERROR = false;
16 var DIV = null;
17 var LAYOUT = null;
18 var DATA_LISTS = null;
19 var DATA_AUTHTOKENS = null;
20 var OPTIONS = {
21 list:[], mapKeys:{},
22 versions:{mapKeys:{}},
23 meta_tags_keys:{list:[], mapKeys:{}},
24 infos:{},
25 tickets_for_testing:[],
26 options_special:{}
27 };
28
29 var STATE = null;
30
31 if (_myAjaxVar._doNotInit) doNotInit = true;
32
33 function time() {
34 return new Date().getTime();
35 }
36
37 function destroy_tags(t) {
38 if (t != null) {
39 t = t.replace("<", "").replace(">","");
40 }
41 return t;
42 }
43
44 function _requestURL(action, myData) {
45 let paras = '?action='+myAjax._action+'&a_sngmbh='+action+'&nonce='+ DATA.nonce+'&t='+time();
46 if (myData) {
47 for(let key in myData) paras += '&data['+key+']='+encodeURIComponent(myData[key]);
48 }
49 for(let key in DATA) paras += '&'+key+'='+encodeURIComponent(DATA[key]);
50 return myAjax.url + paras;
51 }
52
53 function _makePost(action, myData, cbf, ecbf, pcbf) {
54 if (FATAL_ERROR) return;
55 let _data = Object.assign({}, DATA);
56 _data.action = myAjax._action;
57 _data.a_sngmbh = action;
58 _data.t = new Date().getTime();
59 _data.nonce = DATA.nonce;
60 pcbf && pcbf();
61 for(var key in myData) _data['data['+key+']'] = myData[key];
62 $.post( myAjax.url, _data, function( response ) {
63 if (response && response.data && response.data.nonce) {
64 DATA.last_nonce_check = new Date().getTime();
65 DATA.nonce = response.data.nonce;
66 }
67 if (!response.success) {
68 if (ecbf) ecbf(response);
69 else LAYOUT.renderFatalError(response.data);
70 } else {
71 cbf && cbf(response.data);
72 }
73 });
74 }
75
76 function _makeGet(action, myData, cbf, ecbf, pcbf) {
77 if (FATAL_ERROR) return;
78 let _data = Object.assign({}, DATA);
79 _data.action = myAjax._action;
80 _data.a_sngmbh = action;
81 _data.t = new Date().getTime();
82 _data.nonce = DATA.nonce;
83 pcbf && pcbf();
84 for(var key in myData) _data['data['+key+']'] = myData[key];
85 $.get( myAjax.url, _data, function( response ) {
86 if (response && response.data && response.data.nonce) {
87 DATA.last_nonce_check = new Date().getTime();
88 DATA.nonce = response.data.nonce;
89 }
90 if (!response.success) {
91 if (ecbf) ecbf(response);
92 else LAYOUT.renderFatalError(response.data);
93 } else {
94 cbf && cbf(response.data);
95 }
96 });
97 }
98
99 function getOptionsFromServer(cbf, ecbf, pcbf) {
100 _makeGet('getOptions', {}, options=>{
101 _setOptions(options);
102 cbf && cbf(options);
103 }, ecbf, pcbf);
104 }
105
106 function _downloadFile(action, myData, filenameToStore, cbf, ecbf, pcbf) {
107 let _data = Object.assign({}, DATA);
108 _data.action = myAjax._action;
109 _data.a_sngmbh = action;
110 _data.t = new Date().getTime();
111 _data.nonce = DATA.nonce;
112 pcbf && pcbf();
113 for(var key in myData) _data['data['+key+']'] = myData[key];
114 let params = "";
115 for(var key in _data) params += key+"="+_data[key]+"&";
116 let url = myAjax.url+'?'+params;
117 let window_name = myData.code ? myData.code : '_blank';
118 let new_window = window.open(url, window_name);
119 //window.location.href = url;
120 //ajax_downloadFile(url, filenameToStore, cbf);
121 }
122 function ajax_downloadFile(urlToSend, fileName, cbf) {
123 var req = new XMLHttpRequest();
124 req.open("GET", urlToSend, true);
125 req.responseType = "blob";
126 req.onload = function (event) {
127 var blob = req.response;
128 //var fileName = req.getResponseHeader("X-fileName") //if you have the fileName header available
129 var link=document.createElement('a');
130 link.href=window.URL.createObjectURL(blob);
131 link.download=fileName;
132 link.click();
133 cbf && cbf();
134 };
135
136 req.send();
137 }
138
139 function speakOutLoud(v, display) {
140 if ('speechSynthesis' in window) {
141 var t = typeof v === 'object' ? 'Value is an object.' : v;
142 if (t.trim() == "") t = 'Value is empty';
143 var msg = new SpeechSynthesisUtterance(t);
144 msg.lang = "en-US";
145 window.speechSynthesis.speak(msg);
146 if (display) console.log("Speak:", v);
147 } else {
148 console.log(v);
149 }
150 }
151 function _setOptions(optionData) {
152 OPTIONS.list = optionData.options;
153 for (let a=0;a<OPTIONS.list.length;a++) {
154 let item = OPTIONS.list[a];
155 OPTIONS.mapKeys[item.key] = item;
156 OPTIONS.mapKeys[item.key].getValue = function(key) {
157 return function() {return _getOptions_getValByKey(key);};
158 }(item.key);
159 }
160 if (optionData.versions) {
161 if (!optionData.versions.IS_PRETTY_PERMALINK_ACTIVATED) {
162 LAYOUT.renderInfoBox(__("Warning", 'event-tickets-with-ticket-scanner'), __("In order to make the ticket detail view and the ticket scanner work, you need to set a permalink structure within the settings.<br>Please go to the settings->permalinks and choose a permalink structure, that is not 'plain'.", 'event-tickets-with-ticket-scanner'));
163 }
164 OPTIONS.versions.mapKeys = optionData.versions;
165 }
166 if (optionData.meta_tags_keys) {
167 OPTIONS.meta_tags_keys.list = optionData.meta_tags_keys;
168 OPTIONS.meta_tags_keys.mapKeys = {};
169 for (let a=0;a<OPTIONS.meta_tags_keys.list.length;a++) {
170 let item = OPTIONS.meta_tags_keys.list[a];
171 OPTIONS.meta_tags_keys.mapKeys[item.key] = item;
172 OPTIONS.meta_tags_keys.mapKeys[item.key].getValue = function(key) {
173 return function() {return _getOptions_Meta_getValByKey(key);};
174 }(item.key);
175 }
176 }
177 if (optionData.infos) {
178 OPTIONS.infos = optionData.infos;
179 }
180 if (optionData.tickets_for_testing) {
181 OPTIONS.tickets_for_testing = optionData.tickets_for_testing;
182 }
183 if (optionData.options_special) {
184 OPTIONS.options_special = optionData.options_special;
185 }
186
187 if (isPremium()) {
188 let serial = _getOptions_getValByKey('serial');
189 if (serial == '') {
190 if (STATE != "options") {
191 let errortext = __("You are using the premium version. Many thanks, please enter your serial key within the options", 'event-tickets-with-ticket-scanner');
192 let i = confirm(errortext);
193 if (i) {
194 _displayOptionsArea();
195 }
196 }
197 }
198 if (serial != "" && typeof OPTIONS.infos.premium_expiration !== "undefined") {
199 let expiration = OPTIONS.infos.premium_expiration;
200 if (expiration.last_run != 0 && expiration.timestamp > 0) {
201 let expirationDate = new Date(expiration.timestamp * 1000);
202 let toCheck = new Date();
203 toCheck.setDate(toCheck.getDate() + 21);
204 let today = new Date();
205 if (expirationDate <= today || toCheck >= expirationDate) {
206 let msg = typeof expiration.message !== "undefined" && expiration.message != "" ? '<br>'+expiration.message : '';
207 let info_box = $('<div style="background-color:red;color:white;padding:10px;">').html("Your premium license expires soon, at the "+expiration.expiration_date+ ' '+expiration.timezone+'<br>It will work, but no updates are possible for the premium plugin after the expiration date.<br>'+msg+'You can <a target="_blank" style="color:white;font-weight:bold;" href="https://vollstart.com/event-tickets-with-ticket-scanner/">renew your premium license here</a>.');
208 $('body').find('div[data-id="plugin_info_area"').html(info_box);
209 }
210 }
211 }
212 }
213 }
214
215 function _getOptions_getByKey(key) {
216 if (OPTIONS.mapKeys[key]) return OPTIONS.mapKeys[key];
217 return null;
218 }
219 function _getOptions_Meta_getByKey(key) {
220 if (OPTIONS.meta_tags_keys.mapKeys[key]) return OPTIONS.meta_tags_keys.mapKeys[key];
221 return null;
222 }
223 function _getOptions_Versions_getByKey(key) {
224 if (OPTIONS.versions.mapKeys[key]) return OPTIONS.versions.mapKeys[key];
225 return null;
226 }
227 function _getOptions_Infos_getByKey(key) {
228 if (OPTIONS.infos[key]) return OPTIONS.infos[key];
229 return null;
230 }
231 function _getOptions_isActivatedByKey(key) {
232 let po = _getOptions_getByKey(key);
233 if (po == null) return false;
234 return po.value == 1;
235 }
236 function _getOptions_Versions_isActivatedByKey(key) {
237 let po = _getOptions_Versions_getByKey(key);
238 if (po == null) return false;
239 return po == 1;
240 }
241 function _getOptions_getLabelByKey(key) {
242 let po = _getOptions_getByKey(key);
243 if (po == null) return "";
244 return po.label;
245 }
246 function _getOptions_Meta_getLabelByKey(key) {
247 let po = _getOptions_Meta_getByKey(key);
248 if (po == null) return "";
249 return po.label;
250 }
251 function _getOptions_getValByKey(key) {
252 let po = _getOptions_getByKey(key);
253 if (po == null) return "";
254 return po.value == "" ? po['default'] : po.value;
255 }
256 function _getOptions_Versions_getValByKey(key) {
257 let po = _getOptions_Versions_getByKey(key);
258 if (po == null) return "";
259 return po;
260 }
261
262 function basics_ermittelURLParameter() {
263 var parawerte = {};
264 var teile;
265 if (window.location.search !== "") {
266 teile = window.location.search.substring(1).split("&");
267 for (var a=0;a<teile.length;a++)
268 {
269 var pos = teile[a].indexOf("=");
270 if (pos < 0) {
271 parawerte[teile[a]] = true;
272 } else {
273 var key = teile[a].substring(0,pos);
274 parawerte[key] = decodeURIComponent(teile[a].substring(pos+1));
275 }
276 }
277 }
278 return parawerte;
279 }
280
281 function intval(v) {
282 let retv = parseInt(v,10);
283 if (isNaN(retv)) retv = 0;
284 return retv;
285 }
286
287 function DateTime2Text(millisek) {
288 return Date2Text(millisek, OPTIONS.options_special.format_datetime ? OPTIONS.options_special.format_datetime : "d.m.Y H:i");
289 }
290 function Date2Text(millisek, format, timezone_id) {
291 if (!millisek)
292 millisek = time(timezone_id);
293 var d = new Date(millisek);
294 if (!format)
295 //format = system.format_date ? system.format_date : "%d.%m.%Y";
296 format = OPTIONS.options_special.format_date ? OPTIONS.options_special.format_date : "d.m.Y";
297 //format = "%d.%m.%Y %H:%i";
298 var tage = [
299 _x('Sun', 'cal', 'event-tickets-with-ticket-scanner'),
300 _x('Mon', 'cal', 'event-tickets-with-ticket-scanner'),
301 _x('Tue', 'cal', 'event-tickets-with-ticket-scanner'),
302 _x('Wed', 'cal', 'event-tickets-with-ticket-scanner'),
303 _x('Thu', 'cal', 'event-tickets-with-ticket-scanner'),
304 _x('Fri', 'cal', 'event-tickets-with-ticket-scanner'),
305 _x('Sat', 'cal', 'event-tickets-with-ticket-scanner')
306 ];
307 var monate = [
308 _x('Jan', 'cal', 'event-tickets-with-ticket-scanner'),
309 _x('Feb', 'cal', 'event-tickets-with-ticket-scanner'),
310 _x('Mar', 'cal', 'event-tickets-with-ticket-scanner'),
311 _x('Apr', 'cal', 'event-tickets-with-ticket-scanner'),
312 _x('May', 'cal', 'event-tickets-with-ticket-scanner'),
313 _x('Jun', 'cal', 'event-tickets-with-ticket-scanner'),
314 _x('Jul', 'cal', 'event-tickets-with-ticket-scanner'),
315 _x('Aug', 'cal', 'event-tickets-with-ticket-scanner'),
316 _x('Sep', 'cal', 'event-tickets-with-ticket-scanner'),
317 _x('Oct', 'cal', 'event-tickets-with-ticket-scanner'),
318 _x('Nov', 'cal', 'event-tickets-with-ticket-scanner'),
319 _x('Dec', 'cal', 'event-tickets-with-ticket-scanner')
320 ];
321 var formate = {'d':d.getDate()<10?'0'+d.getDate():d.getDate(),
322 'j':d.getDate(),'D':tage[d.getDay()],'w':d.getDate(),'m':d.getMonth()+1<10?'0'+(d.getMonth()+1):d.getMonth()+1,'M':monate[d.getMonth()],
323 'n':d.getMonth()+1,'Y':d.getFullYear(),'y':d.getYear()>100?d.getYear().toString().substring(d.getYear().toString().length-2):d.getYear(),
324 'H':d.getHours()<10?'0'+d.getHours():d.getHours(),'h':d.getHours()>12?d.getHours()-12:d.getHours(),
325 'i':d.getMinutes()<10?'0'+d.getMinutes():d.getMinutes(),'s':d.getSeconds()<10?'0'+d.getSeconds():d.getSeconds()
326 };
327 for (var akey in formate) {
328 //var rg = new RegExp('%'+akey, "g");
329 var rg = new RegExp(akey, "g");
330 format = format.replace(rg, formate[akey]);
331 }
332 return format;
333 }
334
335 function _getMediaData(mediaid, cbf) {
336 _makeGet('getMediaData', {'mediaid':mediaid}, (ret)=>{
337 cbf && cbf(ret);
338 });
339 }
340
341 function getDataLists(cbf) {
342 if (DATA_LISTS !== null) cbf && cbf();
343 _makeGet('getLists', {}, data=>{
344 DATA_LISTS = data;
345 cbf && cbf(DATA_LISTS);
346 });
347 }
348
349 function getCodeObjectMeta(codeObj) {
350 if (codeObj.metaObj) return codeObj.metaObj;
351 try {
352 if (typeof codeObj.meta == "undefined" || codeObj.meta == "") {
353 codeObj.metaObj = null;
354 } else {
355 codeObj.metaObj = JSON.parse(codeObj.meta);
356 }
357 } catch(e) {
358 // new empty tickets have no meta
359 //console.log("Error should not happen. Meta is broken. ", codeObj);
360 codeObj.metaObj = null;
361 }
362 return codeObj.metaObj;
363 }
364
365 function updateCodeObject(codeObj, newCodeObj) {
366 for(var prop in newCodeObj) {
367 codeObj[prop] = newCodeObj[prop];
368 }
369 codeObj.metaObj = null;
370 }
371
372 function closeDialog(dlg) {
373 $(dlg).dialog( "close" );
374 $(dlg).html('');
375 $(dlg).dialog("destroy").remove();
376 $(dlg).empty();
377 $(dlg).remove();
378 $('.ui-dialog-content').dialog('destroy');
379 }
380
381 function getUseFulVideosHTML() {
382 return '<h3>Useful videos</h3><ul><li><span class="dashicons dashicons-external"></span><a href="https://youtu.be/yJcHMV7oAFc" target="_blank">Setup for use case Event Organizer (Youtube)</a></li><li><span class="dashicons dashicons-external"></span><a href="https://www.youtube.com/watch?v=TDMWI0R_HXQ" target="_blank">Setup for use case Club, Spa and Fitness clubs (Youtube)</a></li></ul>';
383 }
384
385 function getAuthtokens(cbf) {
386 if (DATA_AUTHTOKENS !== null) cbf && cbf();
387 _makeGet('getAuthtokens', {}, data=>{
388 DATA_AUTHTOKENS = data;
389 cbf && cbf(DATA_AUTHTOKENS);
390 });
391 }
392
393 function _displayAuthTokensArea() {
394 STATE = 'authtokens';
395 DIV.html('');
396 DIV.append(getBackButtonDiv());
397
398 DIV.append('<h3>'+_x('Auth Token', 'label', 'event-tickets-with-ticket-scanner')+'</h3>');
399 $('<p>').html(__('You can add auth tokens, that can be used to access your ticket scanner. Create an auth token and pass the QR code to the user or let the user scan it from your admin area. The used auth token will bypass any access restricton settings for the ticket scanner that are set in the options.', 'event-tickets-with-ticket-scanner')).appendTo(DIV);
400 $('<p>').html(__('The user scan the QR code for the auth token with the ticket scanner. Just like a normal ticket. The system will store the auth token to the browser.', 'event-tickets-with-ticket-scanner')).appendTo(DIV);
401 let loading = $('<div/>').html(_getSpinnerHTML()).appendTo(DIV);
402 let div2 = $('<div style="background:white;padding:15px;border-radius:15px;">').appendTo(DIV);
403 let tplace = $('<div style="background:white;padding:15px;border-radius:15px;"/>');
404
405 getOptionsFromServer(reply=>{
406 let tabelle_authtokens_datatable;
407 let btn_new = $('<button/>').addClass("button-primary").html(_x('Add', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
408 __showMaskAuthtoken(null);
409 });
410 $('<div/>').css('text-align', 'right').css('margin-bottom','10px').append(btn_new).appendTo(div2);
411 let div_tabelle = $('<div>');
412 loading.html("");
413 tplace.html("").append(div_tabelle).appendTo(div2);
414
415 function __showMaskAuthtoken(editValues) {
416 let _options = {
417 title: editValues !== null ? _x('Edit Auth Token', 'title', 'event-tickets-with-ticket-scanner') : _x('Add Auth Token', 'title', 'event-tickets-with-ticket-scanner'),
418 modal: true,
419 minWidth: 600,
420 minHeight: 400,
421 buttons: [
422 {
423 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
424 click: function() {
425 ___submitForm();
426 }
427 },
428 {
429 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
430 click: function() {
431 closeDialog(this);
432 }
433 }
434 ]
435 };
436 let dlg = $('<div/>').html('<form>'+_x('Name', 'label', 'event-tickets-with-ticket-scanner')+'<br><input name="inputName" type="text" style="width:100%;" required></form>');
437 dlg.dialog(_options);
438
439 dlg.find("form").append('<p>'+_x('Bound to product(s)', 'label', 'event-tickets-with-ticket-scanner')+'<br><input name="inputBoundToProducts" type="text" placeholder="'+_x('all products allowed to be redeemed', 'label', 'event-tickets-with-ticket-scanner')+'" style="width:100%;"><br>'+__('You can add comma seperated "," product ids. This will limit the user to redeem tickets only of products listed here. If left empty, all are allowed.', 'event-tickets-with-ticket-scanner')+'</p>');
440 dlg.dialog(_options);
441
442 dlg.find("form").append($('<p>'+_x('Description', 'label', 'event-tickets-with-ticket-scanner')+'<br><textarea name="desc" style="width:100%;"></textarea></p>'));
443 if (isPremium() && typeof PREMIUM.addAuthtokenMaskEditFields != "undefined") PREMIUM.addAuthtokenMaskEditFields(dlg, editValues);
444 dlg.find("form").append($('<p><input type="checkbox" name="aktiv">'+_x('is active', 'label', 'event-tickets-with-ticket-scanner')+'</p>'));
445
446 let form = dlg.find("form").on("submit", event=>{
447 event.preventDefault();
448 ___submitForm();
449 });
450
451 let metaObj = [];
452 if (editValues && typeof editValues.meta !== "undefined" && editValues.meta != "") {
453 try {
454 metaObj = JSON.parse(editValues.meta);
455 } catch(e) {}
456 }
457
458 if (editValues) {
459 form[0].elements['inputName'].value = editValues.name;
460 form[0].elements['inputName'].select();
461 form[0].elements['inputBoundToProducts'].value = editValues.metaObj.ticketscanner.bound_to_products;
462 form[0].elements['aktiv'].checked = editValues.aktiv == 1 ? true : false;
463 if (typeof metaObj.desc !== "undefined") {
464 form[0].elements['desc'].value = metaObj.desc;
465 }
466 }
467
468 function ___submitForm() {
469 let inputName = form[0].elements['inputName'].value.trim();
470 if (inputName === "") return;
471
472 dlg.html(_getSpinnerHTML());
473 let _data = {"name":inputName};
474 _data['aktiv'] = form[0].elements['aktiv'].checked ? 1 : 0;
475 _data['meta'] = {"desc":"", "ticketscanner":{"bound_to_products":""}};
476 _data['meta']['desc'] = form[0].elements['desc'].value.trim();
477 _data['meta']['ticketscanner']['bound_to_products'] = form[0].elements['inputBoundToProducts'].value.trim();
478 if (isPremium() && typeof PREMIUM.addAuthtokenMaskEditFieldsData != "undefined") PREMIUM.addAuthtokenMaskEditFieldsData(_data, form[0], editValues);
479
480 form[0].reset();
481 if (editValues) {
482 _data.id = editValues.id;
483 _makePost('editAuthtoken', _data, result=>{
484 DATA_AUTHTOKENS = null;
485 __renderTabelleAuthtokens();
486 //tabelle_authtokens_datatable.ajax.reload();
487 setTimeout(function(){closeDialog(dlg);},250);
488 }, ()=>{
489 closeDialog(dlg);
490 });
491 } else {
492 _makePost('addAuthtoken', _data, result=>{
493 DATA_AUTHTOKENS = null;
494 __renderTabelleAuthtokens();
495 closeDialog(dlg);
496 }, response=>{
497 closeDialog(dlg);
498 if (response.data.slice(0,1) === "#") {
499 FATAL_ERROR === false && LAYOUT.renderFatalError(response.data);
500 //FATAL_ERROR = true;
501 }
502 });
503 }
504 }
505 } // end __showMaskAuthtoken
506
507 function __renderTabelleAuthtokens() {
508 div_tabelle.html(_getSpinnerHTML());
509 getAuthtokens(()=>{
510 let table_id = myAjax.divPrefix+'_tabelle_authtokens';
511 let tabelle = $('<table/>').attr("id", table_id);
512 tabelle.html('<thead><tr><th></th><th align="left">'+_x('Name', 'label', 'event-tickets-with-ticket-scanner')+'</th><th align="left">'+_x('Created', 'label', 'event-tickets-with-ticket-scanner')+'</th><th>'+_x('Area', 'label', 'event-tickets-with-ticket-scanner')+'</th><th>'+_x('Status', 'label', 'event-tickets-with-ticket-scanner')+'</th><th></th></tr></thead>');
513 div_tabelle.html(tabelle);
514
515 let table = $('#'+table_id);
516 $(table).DataTable().clear().destroy();
517 tabelle_authtokens_datatable = $(table).DataTable({
518 "responsive": true,
519 "visible": true,
520 "searching": true,
521 "ordering": true,
522 "processing": true,
523 "serverSide": false,
524 "stateSave": true,
525 "data": DATA_AUTHTOKENS,
526 "order": [[ 1, "asc" ]],
527 "columns":[
528 {"data":null,"className":'details-control',"orderable":false,"defaultContent":'', "width":10},
529 {"data":"name", "orderable":true,
530 "render": ( data, type, row )=>{
531 return encodeURIComponent(data);
532 }
533 },
534 {"data":"time", "orderable":true, "width":80,
535 "render":function (data, type, row) {
536 return '<span style="display:none;">'+data+'</span>'+DateTime2Text(data);
537 }
538 },
539 {"data":"areacode", "orderable":true, "className":"dt-center", "width":80},
540 {"data":"aktiv", "orderable":true, "width":50, "className":"dt-center", "render":(data, type, row)=>{
541 return data == 1 ? 'active' : 'inactive';
542 }},
543 {"data":null,"orderable":false,"defaultContent":'',"className":"buttons dt-right","width":100,
544 "render": ( data, type, row )=>{
545 return '<button class="button-secondary" data-type="edit">'+_x('Edit', 'label', 'event-tickets-with-ticket-scanner')+'</button> <button class="button-secondary" data-type="delete">'+_x('Delete', 'label', 'event-tickets-with-ticket-scanner')+'</button>';
546 }
547 }
548 ]
549 });
550 tabelle.css("width", "100%");
551 table.on('click', 'button[data-type="edit"]', e=>{
552 let data = tabelle_authtokens_datatable.row( $(e.target).parents('tr') ).data();
553 __showMaskAuthtoken(data);
554 });
555 table.on('click', 'button[data-type="delete"]', e=>{
556 let data = tabelle_authtokens_datatable.row( $(e.target).parents('tr') ).data();
557 LAYOUT.renderYesNo(_x('Do you want to delete?', 'title', 'event-tickets-with-ticket-scanner'), __('Are you sure, you want to delete this auth token?', 'event-tickets-with-ticket-scanner')+'<br><p><b>'+data.name+'</b></p>'+__('The user with this auth token will not be able to use the server anymore. The user will need to add a new auth token from you.<p>The effect will be immediately.</p>', 'event-tickets-with-ticket-scanner'), ()=>{
558 let _data = {'id':data.id};
559 div_tabelle.html(_getSpinnerHTML());
560 _makePost('removeAuthtoken', _data, result=>{
561 DATA_AUTHTOKENS = null;
562 __renderTabelleAuthtokens();
563 //tabelle_authtokens_datatable.ajax.reload();
564 });
565 });
566 });
567 $('#'+table_id+' tbody').on('click', 'td.details-control', e=>{
568 function ___format(d) {
569 let metaObj = {};
570 if (d.metaObj) metaObj = d.metaObj;
571 if (d.meta && !d.metaObj) {
572 metaObj = JSON.parse(d.meta);
573 }
574 let id = 'qrcode_'+d.id+'_'+time();
575 let content = JSON.stringify({"type":"auth", "time":d.time, "name":d.name, "code":d.code, "areacode":d.areacode, "url":OPTIONS.infos.site.site_url});
576 let content2 = _getTicketScannerURL()+'&auth='+encodeURIComponent(content);
577
578 let div = $('<div/>');
579 $('<div>').html("<b>Authcode: </b>"+d.code).appendTo(div);
580 let div_wrapper = $('<div style="padding-top:10px;">').appendTo(div);
581
582 $('<div style="width:256px;float:left;text-align:center">').html('<b>Only Auth Token</b><div id="'+id+'" style="text-align:center;"></div><script>jQuery("#'+id+'").qrcode(\''+content+'\');</script>').appendTo(div_wrapper);
583 $('<div style="margin-left:20px;width:256px;float:left;text-align:center">').html('<b>With Ticket Scanner URL</b><div id="'+id+'2" style="text-align:center;"></div><script>jQuery("#'+id+'2").qrcode(\''+content2+'\');</script>').appendTo(div_wrapper);
584
585 let div_inner = $('<div style="float:left;padding-left:10px;">').appendTo(div_wrapper);
586 let _desc = metaObj.desc == "" ? "-" : metaObj.desc;
587 $('<div>').html('<b><a href="'+content2+'" target="_blank">Open Ticket Scanner with Auth Token</a></b>').appendTo(div_inner);
588 $('<div>').html('<b>Desc:</b> ').append($('<span>').text(_desc)).appendTo(div_inner);
589
590 let bound_to_products = metaObj.ticketscanner.bound_to_products == "" ? [] : metaObj.ticketscanner.bound_to_products.toString().split(",");
591 $("<div>").html("<b>Bound to product:</b> "+(bound_to_products.length == 0 ? "all products": bound_to_products.join(", "))).appendTo(div_inner);
592
593 return div;
594 }
595
596 var tr = $(e.target).parents('tr');
597 var row = tabelle_authtokens_datatable.row( tr );
598 if ( row.child.isShown() ) {
599 // This row is already open - close it
600 row.child.hide();
601 tr.removeClass('shown');
602 } else {
603 // Open this row
604 row.child( ___format(row.data()) ).show();
605 tr.addClass('shown');
606 }
607
608 });
609 });
610 }
611 __renderTabelleAuthtokens();
612 });
613 }
614
615 function _displayFAQArea() {
616 STATE = 'faq';
617 DIV.html(_getSpinnerHTML());
618
619 let questions = [
620 {
621 "q":'PDF is not rendering - critical error',
622 "t":'<p>The used PDF library cannot handle all the fancy HTML and CSS. Using these in the product description can lead to an error. If the ticket detail page is working, but the PDF not then you can try to remove the HTML tags or use the option to not print the product description to the ticket.<br>Please set the option <b>wcTicketPDFStripHTML</b> to remove the HTML and retry the PDF by reloading the browser or click again.</p><p>If your system is not live yet, you can use the debug mode first to see which HTML tags are used. The basics HTML tags are working well.</p><p>Try the option to remove the not supported HTML tags - this is not always great, because it removes the HTML tags that Wordpress is not supporting and could still lead to PDF issues, but a great start.<br>If this was not helping, then remove please the HTML tags in your product description for a test. You can also just deactivate the option <b>wcTicketDisplayShortDesc</b> to not use the short description of the product for a test.</p>'
623 },
624 {
625 "q":'Receiving 404 error page if calling the ticket view and/or PDF',
626 "t":'<p>Some installations have issues to open the ticket details view and/or the ticket scanner.<br>This could be because of your theme, other plugins or more stricter security settings.</p><p>If you experience to see the "file not found" page (404), then it could help if your activate the compatibility mode in the options.</p><p>For this configure the option <b>wcTicketCompatibilityModeURLPath</b> and/or <b>wcTicketCompatibilityMode</b>.</p><p>If this do not help, then the plugin will not work with your installation for now.</p>'
627 },
628 {
629 "q":'How to ask for a value of your ticket?',
630 "t":'<p>You can setup your product to ask your customer for up to 2 values. Free text and a value chosen from a dropdown.<br>You can checkout how it is done with <a href="https://youtu.be/2vTV39wgWNE" target="_blank">this video</a>.</p>'
631 },
632 {
633 "q":'(Pre)Create order with tickets in the backend',
634 "t":'<p>You can also checkout <a href="https://youtu.be/VxUV-s-SIpA" target="_blank">this video here</a>.<br>This video shows how to create an order from the backend and generate the tickets.<br>This approach is also good for free tickets. So you can create the order and have valid tickets. Do not forget to set the order to a redeemable status. The default is "completed".</p>'
635 },
636 {
637 "q":'How to display meta information of the purchased item?',
638 "t":'You can display the meta information of the item with TWIG.<br>Try TWIG code in the ticket template test designer, to see if this helps. First it is a good idea to check the whole meta values. You can achieve this, by displaying the values as JSON with this code.<p><b>{% for item_id, item in ORDER.get_items %}<br>{{ item.get_meta_data|json_encode() }}<br>{% endfor %}</b></p><p>You will see the key value pairs. Then grab your values. E.g.</p><p><b>{%- for item_id, item in ORDER.get_items -%}<br>{%- if item_id == METAOBJ.woocommerce.item_id -%}<br>&lt;br&gt;Date: {{ item.get_meta("Booked From", true) }} - {{ item.get_meta("Booked To", true) }}<br>{%- endif -%}<br>{%- endfor -%}</b></p>'
639 },
640 {
641 "q":"How to set the order immediately to 'completed' if the order is paid?",
642 "t":"You can activate the option wcTicketSetOrderToCompleteIfAllOrderItemsAreTickets to change the order status to completed if all purchased items in the order are tickets and the order status is processing. With this the order is fine and not paid orders are not automatically set to completed. This prevents frauds."
643 },
644 {
645 "q":"How to use own page with ticket scanner and have the QR code redirect to it?",
646 "t":"<p>You set up a page with the ticket scanner shortcode 'sasoEventTicketsValidator_ticket_scanner'.<br>Then adjust the URL for your tickets (scanner is included). The only option for now is the wcTicketCompatibilityModeURLPath. But this also changes the detail page of the ticket. Basically the system is adding to this URL just the '/scanner/?code='.</p><p>If you do not want this, you can adjust the QR content with the option qrOwnQRContent.</p><p>Set it to have the content:<br>https://domain-and-path/scanner/?code={WC_TICKET__PUBLIC_TICKET_ID}</p>"
647 }
648 ];
649
650 let div = $('<div>');
651 div.append("<h2>FAQ</h2>");
652 let div2 = $('<div style="background:white;padding:15px;border-radius:15px;">').appendTo(div);
653 div2.append(getUseFulVideosHTML()+'<br><br>');
654
655 questions.forEach(v=>{
656 let clicked = false;
657 div2.append($('<h3 style="cursor:pointer;">').html("+ "+v.q).on("click",e=>{
658 f1.css("display", clicked ? "none" : "block");
659 clicked = !clicked;
660 }));
661 let f1 = $('<div style="display:none;padding-bottom:15px;">').html(v.t).appendTo(div2);
662 });
663
664 DIV.html(getBackButtonDiv());
665 DIV.append(div);
666 }
667
668 function _load_seatingplanJS(paras, cbj) {
669 let filename = 'js_seatingplan';
670 if (typeof system.DYNJS === "undefined") system.DYNJS = {};
671 if (false && system.DYNJS[filename]) {
672 //eval(system.DYNJS[filename]);
673 sasoEventtickets_js_seatingplan(paras);
674 cbj && cbj();
675 } else {
676 $.getScript( myAjax._plugin_home_url+'/'+filename+".js", ( data, textStatus, jqxhr ) =>{
677 system.DYNJS[filename] = data;
678
679 eval(data); // inject code into the global scope
680 //addScriptCode(data, filename); // function is unaware of the global scope
681
682 sasoEventtickets_js_seatingplan(paras);
683 cbj && cbj();
684 });
685 }
686 }
687
688 function _displaySeatingplanArea() {
689 STATE = 'seatingplan';
690 DIV.html(getBackButtonDiv());
691
692 let div = $('<div>').html(_getSpinnerHTML());
693
694 _load_seatingplanJS({div:div}, ()=>{
695 //console.log("seatingplan loaded");
696 });
697
698 DIV.append(div);
699 }
700 function _displaySupportInfoArea() {
701 STATE = 'support';
702 DIV.html(_getSpinnerHTML());
703 getOptionsFromServer(reply=>{
704 let newline = '<br>';
705 let div_stats = $('<div/>').html(_getSpinnerHTML());
706
707 _makeGet('getSupportInfos', {}, infos=>{
708 div_stats.html("");
709 div_stats.append('<b>Codes:</b>: '+infos.amount.codes+newline);
710 div_stats.append('<b>Lists:</b>: '+infos.amount.lists+newline);
711 div_stats.append('<b>IPs:</b>: '+infos.amount.ips+newline);
712 });
713
714 let data = reply.options; // options values
715 let versions = reply.versions;
716
717 DIV.html(getBackButtonDiv());
718
719 // zeige support email
720 DIV.append(getUseFulVideosHTML);
721 DIV.append('<h3>Support Email</h3><b>support@vollstart.com</b>');
722 DIV.append('<h3>Support Context Information</h3><p>'+__('Please copy the following information, so that we can support you better and faster. Remove any critical information if needed.', 'event-tickets-with-ticket-scanner')+'</p>');
723 DIV.append('<b>Ticket Counter: </b> '+reply.infos.ticket.counter+newline);
724 DIV.append('<b>Wordpress Version:</b> '+versions.wp+newline);
725 DIV.append('<b>MySQL/Mariadb Version:</b> '+versions.mysql+newline);
726 DIV.append('<b>PHP Version:</b> '+versions.php+newline);
727 DIV.append('<b>Product:</b> Event Tickets with WooCommerce'+newline);
728 DIV.append('<b>Basic Plugin Version:</b> '+versions.basic+newline);
729 DIV.append('<b>Basic DB Version:</b> '+versions.db+newline);
730 if (versions.premium != "") {
731 DIV.append('<b>Premium Serial:</b> '+versions.premium_serial+newline);
732 DIV.append('<b>Premium Plugin Version:</b> '+versions.premium+newline);
733 DIV.append('<b>Premium DB Version:</b> '+versions.premium_db+newline);
734 }
735 DIV.append('<h4 style="margin-bottom:0;">Date</h4>');
736 DIV.append('<b>Your default timezone: </b> '+versions.date_default_timezone+newline);
737 DIV.append('<b>Your WP timezone: </b> '+versions.date_WP_timezone+newline);
738 DIV.append('<b>Your WP timezone full: </b> '+versions.date_WP_timezone_time+newline);
739 DIV.append('<b>Your date: </b> '+versions.date_default_timezone_time+newline);
740 DIV.append('<b>UTC date: </b> '+versions.date_UTC_timezone_time+newline);
741
742 DIV.append('<h4 style="margin-bottom:0;">Stats</h4>');
743 DIV.append(div_stats);
744 DIV.append('<h4 style="margin-bottom:0;">URLs</h4>');
745 DIV.append('<b>Mulitsite: </b> '+reply.infos.site.is_multisite+newline);
746 DIV.append('<b>Home: </b> '+reply.infos.site.home+newline);
747 DIV.append('<b>Network home: </b> '+reply.infos.site.network_home+newline);
748 DIV.append('<b>Site URL: </b> '+reply.infos.site.site_url+newline);
749
750 DIV.append('<h4 style="margin-bottom:0;">Ticket URLs</h4>');
751 //$wcTicketCompatibilityModeURLPath = trim(trim($wcTicketCompatibilityModeURLPath, "/"));
752 DIV.append('<b>Ticket Detail Own URL Path: </b> '+reply.infos.site.home+"/"+_getOptions_getValByKey("wcTicketCompatibilityModeURLPath")+newline);
753 DIV.append('<b>Ticket Scanner Own URL Path: </b> '+reply.infos.site.home+"/"+_getOptions_getValByKey("wcTicketCompatibilityModeURLPath")+'/scanner/'+newline);
754 DIV.append('<b>Ticket Default Plugin Detail URL: </b> '+reply.infos.ticket.ticket_base_url+newline);
755 DIV.append('<b>Ticket Default Plugin Scanner Path: </b> '+reply.infos.ticket.ticket_scanner_path+newline);
756 DIV.append('<b>Ticket Detail Default Plugin Path: </b> '+reply.infos.ticket.ticket_detail_path+newline);
757 DIV.append('<b>Ticket Scanner Default Plugin Path: </b> '+reply.infos.ticket.ticket_detail_path+'scanner/'+newline);
758
759 let tabelle_errorlogs_datatable;
760 DIV.append('<h3 style="margin-bottom:10px;">Error Logs</h3>');
761 $('<div style="text-align:right;margin-bottom:10px;">')
762 .append($('<button>').html(__('Refresh table', 'event-tickets-with-ticket-scanner')).addClass("button-secondary").on("click", ()=>{
763 tabelle_errorlogs_datatable.ajax.reload();
764 }))
765 .append($('<button>').html(__('Empty table', 'event-tickets-with-ticket-scanner')).addClass("sngmbh_btn-delete").on("click", ()=>{
766 LAYOUT.renderYesNo(__('Empty table', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: name of ticket table */__('Do you want to empty the "%s" table? All data will be lost.', 'event-tickets-with-ticket-scanner'), _x("Error Logs", 'title', 'event-tickets-with-ticket-scanner')), ()=>{
767 LAYOUT.renderYesNo(__('Empty table - last chance', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: name of ticket table */__('Are you sure? You will not be able to restore the data, except you have a backup of your database. All data will be lost.', 'event-tickets-with-ticket-scanner'), _x("Error Logs", 'title', 'event-tickets-with-ticket-scanner')), ()=>{
768 _makeGet('emptyTableErrorLogs', null, ()=>{
769 tabelle_errorlogs_datatable.ajax.reload();
770 });
771 });
772 });
773 }))
774 .appendTo(DIV);
775
776 let div_tabelle = $('<div style="margin-bottom:20px;">').appendTo(DIV);
777
778 let label_version = _x('Version', 'label', 'event-tickets-with-ticket-scanner');
779 DIV.append('<h3 style="margin-bottom:10px;">Used Libraries</h3>');
780 DIV.append('<p>'+__('The following libraries are used in the plugin.', 'event-tickets-with-ticket-scanner')+'</p>');
781 DIV.append('<p>'+__('The libraries are used in the frontend and backend.', 'event-tickets-with-ticket-scanner')+'</p>');
782 DIV.append('<ul>');
783 DIV.append('<li><b>jQuery</b> - '+label_version+': '+jQuery.fn.jquery+'</li>');
784 DIV.append('<li><b>jQuery UI</b> - '+label_version+': '+jQuery.ui.version+'</li>');
785 DIV.append('<li><b>jQuery UI CSS</b> - '+label_version+': '+jQuery.ui.version+'</li>');
786 DIV.append('<li><b>PHP TWIG template engine</b> https://twig.symfony.com/ - '+label_version+': 3.x</li>');
787 DIV.append('<li><b>PHP QR Code</b> http://sourceforge.net/projects/phpqrcode/ - '+label_version+': 1.1.4</li>');
788 DIV.append('<li><b>FPDI</b> '+label_version+': 2.3.7</li>');
789 DIV.append('<li><b>FPDF</b> '+label_version+': 1.8.5</li>');
790 DIV.append('<li><b>TCPDF</b> http://www.tcpdf.org - '+label_version+': 6.4.4</li>');
791 DIV.append('<li><b>Javascript QR code scanner:</b> https://github.com/nimiq/qr-scanner - '+label_version+': 1.4.2</li>');
792 DIV.append('<li><b>Javascript Datatable:</b> https://datatables.net/ - '+label_version+': 1.10.21</li>');
793 DIV.append('<li><b>Javascript Raphael:</b> http://raphaeljs.com/ - '+label_version+': 2.3.0</li>');
794 DIV.append('<li><b>Javascript Ace Editor</b></li>');
795 DIV.append('<li><b>html5-qrcode:</b> https://github.com/mebjas/html5-qrcode/ - '+label_version+': 2.3.8</li>');
796
797 DIV.append('<h3 style="margin-bottom:10px;">Options</h3>');
798 // liste alle optionen mit wert auf
799 data.forEach(v=>{
800 if (v.type != 'heading' && v.key != "serial") {
801 if (v.additional && v.additional.doNotRender && v.additional.doNotRender === 1) {}
802 else {
803 let value = v.value;
804 let def = '';
805 if (value == '') {
806 def = ' (DEFAULT used)';
807 value = v.default;
808 }
809 text = document.createTextNode(value);
810 DIV.append(`<b>${v.key}${def}:</b> `).append(text).append(`${newline}`);
811 }
812 }
813 });
814
815 /*
816 DIV.append('<h3 style="margin-bottom:0;">All available Options</h3>');
817 let list_elem = $('<div>').appendTo(DIV);
818 data.forEach(v=>{
819 if (v.type != 'heading' && v.key != "serial" && v.type != "desc") {
820 if (v.additional && v.additional.doNotRender && v.additional.doNotRender === 1) {}
821 else {
822 list_elem.append(v.key);
823 list_elem.append(' - ');
824 list_elem.append(v.label);
825 if (v.desc != "") {
826 list_elem.append(`${newline}`).append(v.desc);
827 }
828 list_elem.append(`${newline}`);
829 list_elem.append(`${newline}`);
830 }
831 } else {
832 if (v.type == 'heading') {
833 list_elem.append(`${newline}`);
834 list_elem.append('== '+v.label+' ==');
835 if (v.desc != "") {
836 //list_elem.append(`${newline}`).append(v.desc);
837 }
838 list_elem.append(`${newline}`);
839 }
840 }
841 });
842 */
843
844 // helper buttons
845 $('<button/>').css("margin-top", "30px").addClass("sngmbh_btn-delete").html(_x("Repair tables", 'label', 'event-tickets-with-ticket-scanner')).appendTo(DIV).on("click", ()=>{
846 LAYOUT.renderYesNo(__('Repair database tables?', 'event-tickets-with-ticket-scanner'), __('Do you realy want to try to repair your database table definitions for the plugin? It should be safe, but only needed in very rare cases. You might see errors messages during the page reload - that is normal. Why not asking support, if you should do it? ;)', 'event-tickets-with-ticket-scanner'), dlg=>{
847 dlg.html(_getSpinnerHTML());
848 dlg.dialog({
849 title:_x('Repaired', 'title', 'event-tickets-with-ticket-scanner'), modal:true, dialogClass: "no-close",
850 close: function(event, ui){ abort=true; },
851 buttons: [
852 {
853 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
854 click: function() {
855 $( this ).dialog( _x('Close', 'label', 'event-tickets-with-ticket-scanner') );
856 $( this ).html('');
857 }
858 }
859 ]
860 });
861 _makePost('repairTables', {}, result=>{
862 speakOutLoud(result, true);
863 dlg.html(result);
864 });
865 });
866 });
867
868 function __renderTabelleErrorLogs() {
869 div_tabelle.html(_getSpinnerHTML());
870 let table_id = myAjax.divPrefix+'_tabelle_errorlogs';
871 let tabelle = $('<table/>').attr("id", table_id);
872 tabelle.html('<thead><tr><th></th><th align="left">'+_x('Created', 'label', 'event-tickets-with-ticket-scanner')+'</th><th align="left">'+_x('Exception', 'label', 'event-tickets-with-ticket-scanner')+'</th><th>'+_x('Function', 'label', 'event-tickets-with-ticket-scanner')+'</th></tr></thead>');
873 div_tabelle.html(tabelle);
874
875 let table = $('#'+table_id);
876 $(table).DataTable().clear().destroy();
877 tabelle_errorlogs_datatable = $(table).DataTable({
878 "responsive": true,
879 "searching": true,
880 "ordering": true,
881 "processing": true,
882 "serverSide": true,
883 "stateSave": false,
884 "pageLength":50,
885 "ajax": {
886 url: _requestURL('getErrorLogs'),
887 type: 'POST',
888 },
889 "order": [[ 1, "desc" ]],
890 "columns":[
891 {"data":null,"className":'details-control',"orderable":false,"defaultContent":'', "width":10},
892 {"data":"time", "orderable":true, "width":80},
893 {"data":"exception_msg", "orderable":true},
894 {"data":"caller_name", "orderable":true},
895 ]
896 });
897 tabelle.css("width", "100%");
898 $('#'+table_id+' tbody').on('click', 'td.details-control', e=>{
899 var tr = $(e.target).parents('tr');
900 var row = tabelle_errorlogs_datatable.row( tr );
901 if ( row.child.isShown() ) {
902 // This row is already open - close it
903 row.child.hide();
904 tr.removeClass('shown');
905 } else {
906 // Open this row
907 let d = row.data();
908 row.child( "#"+d.id+'<br><pre>'+destroy_tags(d.msg)+'</pre>' ).show();
909 tr.addClass('shown');
910 }
911
912 });
913 }
914 __renderTabelleErrorLogs();
915
916 });
917 }
918
919 /**
920 * returns 0 if the versions are the same, 1 if version1 is greater, -1 if version2 is greater
921 */
922 function compareVersions(version1, version2) {
923 const v1 = version1.split('.').map(Number);
924 const v2 = version2.split('.').map(Number);
925
926 for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
927 const num1 = v1[i] || 0;
928 const num2 = v2[i] || 0;
929
930 if (num1 > num2) return 1;
931 if (num1 < num2) return -1;
932 }
933
934 /*
935 // Example usage:
936 const result = compareVersions('5.8.1', '5.8.2');
937 if (result > 0) {
938 console.log('Version 5.8.1 is greater than 5.8.2');
939 } else if (result < 0) {
940 console.log('Version 5.8.1 is less than 5.8.2');
941 } else {
942 console.log('Both versions are equal');
943 }
944 */
945
946 return 0;
947 }
948
949 function _displayOptionsArea() {
950 STATE = 'options';
951 DIV.html(_getSpinnerHTML());
952 getOptionsFromServer(reply=>{
953 let data = reply.options; // options values
954 let meta_tags_keys = reply.meta_tags_keys;
955
956 DIV.html(getBackButtonDiv());
957
958 // Create tabs
959 let tabs = $('<div class="tabs"/>');
960 let tabOptions = $('<div id="tab-options" class="tab-content"/>');
961
962 // Create tab navigation
963 let tabNav = $('<ul class="tab-nav"/>');
964 tabNav.append('<li><a href="#tab-options">Options</a></li>');
965 if (isPremium() && typeof PREMIUM.displayOptionsArea_Templates !== "undefined") {
966 tabNav.append(PREMIUM.displayOptionsArea_Tab);
967 }
968
969 tabs.append(tabNav);
970 tabs.append(tabOptions);
971 if (isPremium() && typeof PREMIUM.displayOptionsArea_Templates !== "undefined") {
972 //if (BASIC._compareVersions(prem_version, '1.5.0') < 0) { // check the version
973 // div_template.append("This is a premium feature is available with Premium Version 1.5.0. You need to update your premium plugin.");
974 //}
975 tabs.append(PREMIUM.displayOptionsArea_Templates(_getOptions_Versions_getByKey('premium')));
976 }
977 DIV.append(tabs);
978
979 // Populate Options tab
980 let div_options = $('<div/>');
981 let div_infos = $('<div style="padding-top: 50px;"/>');
982 let resetOption_div = $('<div class="reset_option_wrap" style="padding-top: 20px;"/>');
983 tabOptions.append(div_options);
984 tabOptions.append('<hr>');
985 tabOptions.append(resetOption_div);
986 $('<button class="button reset_btn_actn">').html(_x('Reset All Options', 'label', 'event-tickets-with-ticket-scanner'))
987 .on('click', ()=>{
988 LAYOUT.renderYesNo(_x('Reset All Options', 'title', 'event-tickets-with-ticket-scanner'), __('Do you really want to reset all the option?', 'event-tickets-with-ticket-scanner'), ()=>{
989 _makePost('resetOptions','', function(result) {
990 if(result){
991 _displayOptionsArea();
992 }
993 });
994 });
995 }).appendTo(resetOption_div);
996 tabOptions.append(div_infos);
997 div_infos.append('<a name="replacementtags"></a><h3>'+_x('Replacement Tags', 'title', 'event-tickets-with-ticket-scanner')+'</h3>').append('<p>'+__('You can use these replacement tags in your text messages and URLs for the meta ticket values', 'event-tickets-with-ticket-scanner')+'</p>');
998 meta_tags_keys.forEach(v=>{
999 let t = '<p><b>{'+v.key+'}</b>: '+v.label+'</p>';
1000 div_infos.append(t);
1001 });
1002
1003 //div_options.append('<h3>'+_x('Options', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
1004 div_options.append('<p><span class="dashicons dashicons-external"></span><a href="https://vollstart.com/event-tickets-with-ticket-scanner/docs/" target="_blank">Click here, to visit the documentation.</a></p>');
1005 div_options.append(getUseFulVideosHTML());
1006
1007 let menu_band = $('<div style="padding-top:10px;padding-bottom:15px;">').appendTo(div_options);
1008 let menu_values = [];
1009 data.forEach(v=>{
1010 if (v.type === "heading") {
1011 menu_values.push(v);
1012 }
1013 });
1014 menu_values.sort((a,b)=>{
1015 if(a.label < b.label) { return -1; }
1016 if(a.label > b.label) { return 1; }
1017 return 0;
1018 });
1019 menu_values.forEach(v=>{
1020 $('<a href="#'+v.key+'" style="padding:5px;padding-left:0;margin-right:10px;">').html(v.label).appendTo(menu_band);
1021 });
1022 $('<a href="#topMenu" style="text-decoration:none;position:fixed;bottom:50px;right:10px;background-color:#b225cb;color:white;border-radius:15px;border:1 px solid blue;display:inline-block;padding:10px;">').html('<i class="dashicons dashicons-arrow-up"></i> Top').appendTo(div_options);
1023
1024 // Add jQuery for tab functionality
1025 $('.tab-nav a').on('click', function(e) {
1026 e.preventDefault();
1027 $('.tab-content').hide();
1028 $($(this).attr('href')).show();
1029 $('.tab-nav a').removeClass('active');
1030 $(this).addClass('active');
1031 });
1032
1033 // Show the first tab by default
1034 if (typeof PARAS.subdisplay !== "undefined" && PARAS.subdisplay == 'templates') {
1035 $('.tab-nav a').eq(1).click();
1036 } else {
1037 $('.tab-nav a:first').click();
1038 }
1039
1040 function __createTicketTemplateChooserBox(ticket_template, editor) {
1041 return $('<div style="width:250px;display:inline-block;margin-right:5px;text-align:center;">')
1042 .append('<img style="width:250px;" src="'+myAjax._plugin_home_url+'/img/ticket_templates/'+ticket_template.image_url+'">')
1043 .append("<br>Zero-Padding: "+(ticket_template.wcTicketPDFZeroMarginTest ? "Yes" : "No"))
1044 .append(", Size: ("+ticket_template.wcTicketSizeWidthTest+'x'+ticket_template.wcTicketSizeHeightTest+")")
1045 .append("<br>")
1046 .append($('<button class="button button-primary">').text(__('Load template','event-tickets-with-ticket-scanner')).on("click", ()=>{
1047 LAYOUT.renderYesNo(_x('Load Template Ticket Code', 'title', 'event-tickets-with-ticket-scanner'),
1048 __('Do you want to replace the test ticket template code with this template?', 'event-tickets-with-ticket-scanner')+'<br><p><img style="width:250px;" src="'+myAjax._plugin_home_url+'/img/ticket_templates/'+ticket_template.image_url+'"></p><p>Following values will be changed:'
1049 +'<br><b>wcTicketPDFZeroMarginTest</b>: '+(ticket_template.wcTicketPDFZeroMarginTest ? "Yes" : "No")
1050 +'<br><b>wcTicketPDFisRTLTest</b>: '+(ticket_template.wcTicketPDFisRTLTest ? "Yes" : "No")
1051 +'<br><b>wcTicketSizeWidthTest</b>: '+ticket_template.wcTicketSizeWidthTest
1052 +'<br><b>wcTicketSizeHeightTest</b>: '+ticket_template.wcTicketSizeHeightTest
1053 +'<br><b>wcTicketQRSizeTest</b>: '+ticket_template.wcTicketQRSizeTest
1054 +'</p>'
1055 , ()=>{
1056 editor.wcTicketDesignerTemplateTest_editor.setValue(ticket_template.wcTicketDesignerTemplateTest);
1057 $('input[data-key="wcTicketPDFZeroMarginTest"').prop("checked",ticket_template.wcTicketPDFZeroMarginTest).trigger("change");
1058 $('input[data-key="wcTicketPDFisRTLTest"').prop("checked",ticket_template.wcTicketPDFisRTLTest).trigger("change");
1059 $('input[data-key="wcTicketSizeWidthTest"').val(ticket_template.wcTicketSizeWidthTest).trigger("change");
1060 $('input[data-key="wcTicketSizeHeightTest"').val(ticket_template.wcTicketSizeHeightTest).trigger("change");
1061 $('input[data-key="wcTicketQRSizeTest"').val(ticket_template.wcTicketQRSizeTest).trigger("change");
1062 let value = editor.wcTicketDesignerTemplateTest_editor.getValue().trim();
1063 __saveOptionValue("wcTicketDesignerTemplateTest", value);
1064 editor.wcTicketDesignerTemplateTest_btn.prop("disabled", true);
1065
1066 });
1067 }));
1068 }
1069
1070 // render die input felder
1071 function __getOptionByKey(key) {
1072 for(let a=0;a<data.length;a++) {
1073 if (key == data[a].key) return data[a];
1074 }
1075 return null;
1076 }
1077 function __saveOptionValue(key, value, cbf, pcbf) {
1078 _makePost('changeOption', {'key':key, 'value':value},
1079 ()=>{
1080 cbf && cbf();
1081 if (key == "wcTicketDesignerTemplateTest") {
1082 $("#wcTicketDesignerTemplateTest_button_PDF").prop("disabled", false).text(__('Preview Test Template Code as PDF', 'event-tickets-with-ticket-scanner'));
1083 }
1084 }, null,
1085 ()=>{
1086 pcbf && pcbf();
1087 if (key == "wcTicketDesignerTemplateTest") {
1088 $("#wcTicketDesignerTemplateTest_button_PDF").prop("disabled", true).text(__('saving...', 'event-tickets-with-ticket-scanner'));
1089 }
1090 });
1091
1092 }
1093
1094 let editor = {}; // for ace editor
1095 data.forEach(v=>{
1096 if (typeof v.additional !== "undefined" && v.additional.doNotRender) return;
1097 if (v.type == "heading") {
1098 let desc = v.desc;
1099 if (typeof v._doc_video !== "undefined" && v._doc_video != "") {
1100 desc += ' <span class="dashicons dashicons-external"></span> <a href="'+v._doc_video+'" target="_blank">Video Help</a>';
1101 }
1102 div_options.append('<hr>').append('<h3 id="'+v.key+'" '+(desc !== "" ? ' style="margin-bottom:0;"' : '')+'>'+v.label+'</h3>').append(desc !== "" ? '<div style="margin-bottom:15px;"><i>'+desc+'</i></div>':'');
1103 } else if (v.type =="desc") {
1104 let desc = v.desc+" ";
1105 if (typeof v._do_not_trim !== "undefined" && v._do_not_trim) {
1106 desc += 'To leave this value blank, enter a space. ';
1107 }
1108 if (typeof v._doc_video !== "undefined" && v._doc_video != "") {
1109 desc += '<span class="dashicons dashicons-external"></span> <a href="'+v._doc_video+'" target="_blank">Video Help</a>';
1110 }
1111 div_options.append('<div/>').css({"margin-bottom": "15px","margin-right": "15px"}).append('<b>'+v.label+'</b><br>'+desc+"<br>");
1112 } else {
1113 let elem_div = $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"});
1114 let elem_input = $('<input type="'+v.type+'">');
1115 elem_input.attr("placeholder", v.default);
1116 if (typeof v.additional !== "undefined" && typeof v.additional.disabled !== "undefined") {
1117 elem_input.attr("disabled", true);
1118 }
1119
1120 let cbf = null;
1121 let pcbf = null;
1122 let value = v.value;
1123 if (typeof v._do_not_trim !== "undefined" && v._do_not_trim) {
1124 } else {
1125 value = (""+v.value) !== "" ? (""+v.value).trim() : ""+v.default;
1126 }
1127
1128 v.label = v.label + ' <span style="color:grey;">{'+v.key+'}</span>';
1129 if (typeof v._doc_video !== "undefined" && v._doc_video != "") {
1130 v.label += ' <span class="dashicons dashicons-external"></span> <a href="'+v._doc_video+'" target="_blank">Video Help</a>';
1131 }
1132
1133 switch (v.type) {
1134 case "editor":
1135 elem_input = $('<div id="'+v.key+'_editor" style="height:'+(typeof v.additional !== "undefined" && typeof v.additional.height !== "undefined" ? v.additional.height : '500px')+';">').text(value.trim());
1136 break;
1137 case "textarea":
1138 elem_input = $('<textarea>');
1139 elem_input.attr("placeholder", v.default);
1140 //elem_input.val(value);
1141 elem_input.val(value);
1142 if (typeof v.additional !== "undefined" && typeof v.additional.rows !== "undefined") {
1143 elem_input.attr("rows", v.additional.rows);
1144 }
1145 break;
1146 case "checkbox":
1147 v.value = intval(v.value);
1148 elem_input.prop("checked",v.value === 1 ? true : false);
1149 elem_input.on("change", function(){
1150 _makePost('changeOption', {'key':v.key, 'value':elem_input[0].checked ? 1:0});
1151 });
1152 elem_div.html(elem_input).append(v.label).append(v.desc !== "" ? '<br><i>'+v.desc+'</i>':'');
1153 break;
1154 case "number":
1155 if (typeof v.additional.min !== "undefined") elem_input.attr("min", v.additional.min);
1156 break;
1157 case "dropdown":
1158 elem_input = $('<select>');
1159 if (v.additional.multiple) {
1160 elem_input.prop("multiple", true);
1161 }
1162 v.additional.values.forEach(_v=>{
1163 $('<option>').attr("value", _v.value).html(_v.label).appendTo(elem_input);
1164 });
1165 if (v.additional.multiple) {
1166 if (v.value.length == 0) {
1167 value = v.default;
1168 } else {
1169 value = v.value;
1170 }
1171 } else {
1172 if (value == "") value = 1;
1173 }
1174 elem_input.val(value);
1175 break;
1176 case "media":
1177 let image_info = $('<div>');
1178 let image = $('<image style="display:none;">');
1179 let image_btn_del = $('<button class="sngmbh_btn sngmbh_btn-delete" style="display:none;">').html(_x('Remove file', 'label', 'event-tickets-with-ticket-scanner'));
1180 image_btn_del.on('click', ()=>{
1181 LAYOUT.renderYesNo(_x('Remove file', 'title', 'event-tickets-with-ticket-scanner'), __('Do you really want to remove the file information from this option?', 'event-tickets-with-ticket-scanner'), ()=>{
1182 elem_input.val("");
1183 elem_input.trigger("change");
1184 _renderMedia(0, v, image_info, image, image_btn_del);
1185 });
1186 });
1187 if (typeof v.additional == "undefined") v.additional = {};
1188 if (v.additional.max) {
1189 if (v.additional.max.width) {
1190 image.css("max-width", v.additional.max.width+'px');
1191 }
1192 if (v.additional.max.height) {
1193 image.css("max-height", v.additional.max.height+'px');
1194 }
1195 }
1196 elem_input.attr("type", "hidden");
1197 let image_btn_add = $('<button style="display:block;" />').addClass("button-primary")
1198 .html(v.additional.button)
1199 .on("click", ()=>{
1200 let is_multiple = typeof v.additional.is_multiple != "undefined" ? v.additional.is_multiple : false;
1201 let imgContainer = null;
1202 let type_filter = typeof v.additional.type_filter != "undefined" ? v.additional.type_filter : null;
1203 _openMediaChooser(elem_input, is_multiple, imgContainer, type_filter);
1204 });
1205 $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"})
1206 .html(v.label+'<br>')
1207 .append(image_btn_add)
1208 .append(v.desc !== "" ? '<i>'+v.desc+'</i>':'')
1209 .append(elem_input)
1210 .append(image_info)
1211 .append(image)
1212 .append(image_btn_del)
1213 .appendTo(elem_div);
1214 _renderMedia(value, v, image_info, image, image_btn_del);
1215 pcbf = function() {
1216 image_info.html(_getSpinnerHTML());
1217 image.css('display', 'none');
1218 }
1219 cbf = function () {
1220 let value = elem_input.val();
1221 _renderMedia(value, v, image_info, image, image_btn_del);
1222 }
1223 break;
1224 }
1225
1226 if (v.type != "checkbox") {
1227 if (v.type != "media") {
1228 elem_div.html(v.label+'<br>').append(elem_input);
1229 let desc = v.desc+" ";
1230 if (typeof v._do_not_trim !== "undefined" && v._do_not_trim) {
1231 desc += 'To leave this value blank, enter a space. ';
1232 }
1233 desc = desc.trim();
1234 elem_div.append(desc !== "" ? '<br><i>'+desc+'</i>':'');
1235 }
1236 if (v.type != "number") {
1237 elem_input.css({"width":"90%"});
1238 }
1239 if (v.type != "dropdown" && v.type != "editor") {
1240 elem_input.attr("value",value);
1241 }
1242 if (v.type != "editor") {
1243 elem_input.on("change", ()=>{
1244 let value = elem_input.val();
1245 __saveOptionValue(v.key, value, cbf, pcbf);
1246 });
1247 }
1248 }
1249
1250 elem_input.attr("data-key", v.key);
1251
1252 if (v.key == "wcassignmentUseGlobalSerialFormatter") {
1253 let option = __getOptionByKey('wcassignmentUseGlobalSerialFormatter_values');
1254 let formatterValues = null;
1255 if (option.value != "") {
1256 try {
1257 formatterValues = JSON.parse(option.value);
1258 } catch (e) {
1259 //console.log(e);
1260 }
1261 }
1262 let extra_div = $('<div>').appendTo(elem_div).css("margin-top", "10px").css("margin-left", "50px").css("padding", "10px").css("border", "1px solid black");
1263 // render here den formatter
1264 let serialCodeFormatter = _form_fields_serial_format(extra_div);
1265 serialCodeFormatter.setNoNumberOptions();
1266 serialCodeFormatter.setFormatterValues(formatterValues);
1267 serialCodeFormatter.setCallbackHandle(_formatterValues=>{
1268 // speicher formatterValues
1269 _makePost('changeOption', {'key':'wcassignmentUseGlobalSerialFormatter_values', 'value':JSON.stringify(_formatterValues)});
1270 });
1271 serialCodeFormatter.render();
1272 }
1273
1274 if (v.key == "wcTicketDesignerTemplate") {
1275 $('<button class="button button-primary">').html("Show Default Template").on("click", e=>{
1276 LAYOUT.renderInfoBox(_x('Ticket Default Template', 'title', 'event-tickets-with-ticket-scanner'), $('<textarea style="width:100%;height:400px">').val(v.default));
1277 }).appendTo(div_options);
1278 }
1279
1280 if (v.type == "editor") {
1281 //https://ace.c9.io/#nav=howto
1282 let btn_group = $('<div>').prependTo(elem_div);
1283 editor[v.key+"_editor"] = null; // will be filled later
1284 editor[v.key+"_btn"] = $('<button class="button button-primary">').prop("disabled", true).html(_x('Save Template Code', 'title', 'event-tickets-with-ticket-scanner')).on("click", evt=>{
1285 let value = editor[v.key+"_editor"].getValue().trim();
1286 __saveOptionValue(v.key, value, cbf, pcbf);
1287 editor[v.key+"_btn"].prop("disabled", true);
1288 }).appendTo(btn_group);
1289 $('<button class="button button-danger">').html(_x('Copy Template Code To Live Code', 'title', 'event-tickets-with-ticket-scanner')).on("click", evt=>{
1290 LAYOUT.renderYesNo(_x('Replace Live Template Code', 'title', 'event-tickets-with-ticket-scanner'), __('Do you want to replace the live template code with the template code from the test?', 'event-tickets-with-ticket-scanner'), ()=>{
1291 let value = editor[v.key+"_editor"].getValue().trim();
1292 $('input[data-key="'+v.key.replace("Test", "")+'"').val(value).trigger("change");
1293 if (v.key == "wcTicketDesignerTemplateTest") {
1294 $('input[data-key="wcTicketPDFZeroMargin"').prop("checked",$('input[data-key="wcTicketPDFZeroMarginTest"').is(':checked')).trigger("change");
1295 $('input[data-key="wcTicketPDFisRTL"').prop("checked",$('input[data-key="wcTicketPDFisRTLTest"').is(':checked')).trigger("change");
1296 $('input[data-key="wcTicketSizeWidth"').val($('input[data-key="wcTicketSizeWidthTest"').val()).trigger("change");
1297 $('input[data-key="wcTicketSizeHeight"').val($('input[data-key="wcTicketSizeHeightTest"').val()).trigger("change");
1298 $('input[data-key="wcTicketQRSize"').val($('input[data-key="wcTicketQRSizeTest"').val()).trigger("change");
1299 }
1300 });
1301 }).appendTo(btn_group);
1302
1303 if (v.key == "wcTicketDesignerTemplateTest") {
1304 let ticket_test_chooser = $('<div>');
1305 let ticket_template_chooser = $('<div style="padding-top:5px;padding-bottom:20px;">').html('<b>Templates</b><br>You can choose from the templates below to have a starting point.<br>').appendTo(ticket_test_chooser);
1306 let ticket_test_select = $('<select>').appendTo(ticket_test_chooser);
1307 let ticket_test_direct_input = $('<input type="text" style="width:180px;" placeholder="or enter a public ticket number">');
1308 // display the template thumbnails
1309 for(let a=0;a<reply.ticket_templates.length;a++) {
1310 let ticket_template = reply.ticket_templates[a];
1311 __createTicketTemplateChooserBox(ticket_template, editor).appendTo(ticket_template_chooser);
1312 }
1313
1314 if (OPTIONS.tickets_for_testing.length > 0) {
1315 let option_values = [];
1316 for(let a=0;a<OPTIONS.tickets_for_testing.length;a++) {
1317 let ticket = OPTIONS.tickets_for_testing[a];
1318 let metaObj = null;
1319 try {
1320 metaObj = JSON.parse(ticket.meta);
1321 } catch(e) {}
1322 if (metaObj != null) {
1323 option_values.push({t:ticket, m:metaObj});
1324 }
1325 }
1326 if (option_values.length > 0) {
1327 for(let a=0;a<option_values.length;a++) {
1328 let item = option_values[a];
1329 $('<option value="'+item.m.wc_ticket._public_ticket_id+'">')
1330 .text("Order Id: "+item.t.order_id+" - "+item.m.wc_ticket._public_ticket_id+" - "+item.t._PRODUCT_NAME+" (#"+item.m.woocommerce.product_id+")")
1331 .attr("data-url-pdf", item.m.wc_ticket._url)
1332 .appendTo(ticket_test_select);
1333 }
1334 ticket_test_direct_input.appendTo(ticket_test_chooser);
1335 $('<button class="button button-primary" id="wcTicketDesignerTemplateTest_button_PDF">')
1336 .html(__('Preview Test Template Code as PDF', 'event-tickets-with-ticket-scanner')).
1337 appendTo(ticket_test_chooser).on("click", ()=>{
1338 let ticket_url = ticket_test_select.find(":selected").attr("data-url-pdf");
1339 let v = ticket_test_direct_input.val().trim();
1340 if (v != "") {
1341 ticket_url = reply.infos.ticket.ticket_base_url + v; // myAjax.ticket_base_url
1342 }
1343 iframe.attr("src", ticket_url+'?pdf&testDesigner=1&t='+time()+'&nonce='+DATA.nonce);
1344 iframe
1345 .css("width", "80%")
1346 .css("height", "500px")
1347 .css("margin-top", "10px")
1348 .css("display", "block");
1349 });
1350 let iframe = $('<iframe style="display:none;">').appendTo(ticket_test_chooser);
1351 } else {
1352 $('<option value="">').text(__("ticket cannot be used. Public Ticket Id missing.",'event-tickets-with-ticket-scanner')).appendTo(ticket_test_select);
1353 }
1354 } else {
1355 $('<option value="">').text(__("no ticket for preview available", 'event-tickets-with-ticket-scanner')).appendTo(ticket_test_select);
1356 }
1357 ticket_test_chooser.appendTo(elem_div);
1358 }
1359 }
1360
1361 elem_div.appendTo(div_options);
1362 }
1363 });
1364 if (window.location.hash != "") {
1365 window.setTimeout(()=>{
1366 let h = window.location.hash;
1367 window.location.hash = "";
1368 window.location.hash = h;
1369 }, 250);
1370 }
1371 window.setTimeout(()=>{
1372 for(var k in editor) {
1373 if (k.substring(k.length -7) == "_editor") {
1374 editor[k] = ace.edit(k);
1375 //editor.wcTicketDesignerTemplateTest_editor.setTheme("ace/theme/monokai");
1376 editor[k].session.setMode("ace/mode/twig");
1377 editor[k].setShowPrintMargin(false);
1378 editor[k].commands.addCommand({name:'save', bindKey:{win:'Ctrl-S', mac:'Command-S'}, readOnly:false, exec:myEditor=>{
1379 myEditor.trigger("change");
1380 }});
1381 editor[k].session.on("change", delta=>{
1382 editor[k.replace("_editor", "_btn")].prop("disabled", false);
1383 });
1384 }
1385 }
1386 }, 250)
1387
1388 });
1389 }
1390
1391 function getSuffixFromFilename(filename) {
1392 let extension = filename.slice(filename.lastIndexOf('.') + 1);
1393 return extension;
1394 }
1395 function _renderMedia(mediaId, v, image_info, image, image_btn_del) {
1396 if (mediaId != "" && parseInt(mediaId) != 0) {
1397 _getMediaData(mediaId, data=>{
1398 let suffix = getSuffixFromFilename(data.url.replace(/^.*[\\\/]/,'')).toLowerCase();
1399 let info = suffix != "pdf" ? '('+data.meta.width+'x'+data.meta.height+')' : '';
1400 image_info.html('<b>'+_x('Title', 'title', 'event-tickets-with-ticket-scanner')+':</b> '+data.title+' '+info);
1401 if (v.additional.max && v.additional.msg_error_max) {
1402 if (v.additional.max.width && v.additional.msg_error_max.width && data.meta.width > v.additional.max.width) image_info.append('<div style="color:red;">'+v.additional.msg_error_max.width+'</div>');
1403 if (v.additional.max.height && v.additional.msg_error_max.height && data.meta.height > v.additional.max.height) image_info.append('<div style="color:red;">'+v.additional.msg_error_max.height+'</div>');
1404 }
1405 if (suffix != "pdf") {
1406 image.attr("src", data.url).css("display","block");
1407 }
1408 image_btn_del.css("display", "block");
1409 });
1410 } else {
1411 image_info.html("");
1412 image.css("display", "none");
1413 image_btn_del.css("display", "none");
1414 }
1415 }
1416 function _openMediaChooser(input_elem, multiple, imgContainer, typeFilter) {
1417 var image_frame;
1418 if(image_frame){
1419 image_frame.open();
1420 }
1421 if (!typeFilter) typeFilter = 'image';
1422 multiple ? multiple = true : multiple = false;
1423 // Define image_frame as wp.media object
1424 image_frame = wp.media({
1425 title: _x('Select Media', 'title', 'event-tickets-with-ticket-scanner'),
1426 multiple : multiple,
1427 library : {
1428 type : typeFilter,
1429 }
1430 });
1431
1432 image_frame.on('close',function() {
1433 // On close, get selections and save to the hidden input
1434 // plus other AJAX stuff to refresh the image preview
1435 var selection = image_frame.state().get('selection');
1436
1437 if (imgContainer) { // zeige erstes bild an
1438 var attachment = selection.first().toJSON();
1439 imgContainer.html( '<img src="'+attachment.url+'" style="max-width:100%;"/>' );
1440 }
1441
1442 var gallery_ids = new Array();
1443 var my_index = 0;
1444 selection.each(function(attachment) {
1445 gallery_ids[my_index] = attachment['id'];
1446 my_index++;
1447 });
1448 var ids = gallery_ids.join(",");
1449 input_elem.val(ids);
1450 input_elem.trigger("change");
1451 });
1452
1453 image_frame.on('open',function() {
1454 // On open, get the id from the hidden input
1455 // and select the appropiate images in the media manager
1456 var selection = image_frame.state().get('selection');
1457 var ids = input_elem.val().split(',');
1458 ids.forEach(function(id) {
1459 var attachment = wp.media.attachment(id);
1460 attachment.fetch();
1461 selection.add( attachment ? [ attachment ] : [] );
1462 });
1463 });
1464 image_frame.open();
1465 } // ende openmediachooser
1466
1467 function getBackButtonDiv() {
1468 let div_buttons = $('<div style="display:flex;justify-content:space-between;">');
1469 let div = $('<div/>').append($('<button/>').addClass("button-primary").html(_x('Back', 'label', 'event-tickets-with-ticket-scanner')).css("margin-bottom", "10px").on("click", function(){
1470 LAYOUT.renderAdminPageLayout();
1471 }));
1472 div_buttons.append(div);
1473 div_buttons.append(_displaySettingAreaButton());
1474 return div_buttons;
1475 }
1476
1477 function _getTicketScannerURL() {
1478 let url = _getOptions_Infos_getByKey('ticket').ticket_scanner_path;
1479 let _urlpath = _getOptions_getValByKey("wcTicketCompatibilityModeURLPath");
1480 if (_urlpath != "") {
1481 url = OPTIONS.infos.site.home+"/"+_urlpath+'/scanner/';
1482 } else {
1483 url = OPTIONS.infos.ticket.ticket_scanner_url;
1484 }
1485 return url;
1486 }
1487 function _displaySettingAreaButton() {
1488 let btn_grp = $('<div id="topMenu"/>').addClass("btn-group");
1489 $('<button/>').addClass("button-primary").html(_x("Support Info", 'label', 'event-tickets-with-ticket-scanner'))
1490 .on("click", ()=>{
1491 _displaySupportInfoArea();
1492 })
1493 .appendTo(btn_grp);
1494 $('<button/>').addClass("button-primary").html(_x("FAQ", 'label', 'event-tickets-with-ticket-scanner'))
1495 .on("click", ()=>{
1496 _displayFAQArea();
1497 })
1498 .appendTo(btn_grp);
1499 if (typeof PARAS.seatingplan !== "undefined" && PARAS.seatingplan) {
1500 $('<button/>').addClass("button-primary").html(_x("Seating Plans", 'label', 'event-tickets-with-ticket-scanner'))
1501 .on("click", ()=>{
1502 _displaySeatingplanArea();
1503 })
1504 .appendTo(btn_grp);
1505 }
1506 //if (_getOptions_Versions_isActivatedByKey('is_wc_available')) {
1507 $('<button/>').addClass("button-primary").html(_x("Ticket Scanner", 'label', 'event-tickets-with-ticket-scanner'))
1508 .on("click", ()=>{
1509 let url = _getTicketScannerURL();
1510 window.open(url, 'ticketscanner');
1511 })
1512 .appendTo(btn_grp);
1513 //}
1514 $('<button/>').addClass("button-primary").html(_x('Auth Token', 'label', 'event-tickets-with-ticket-scanner'))
1515 .on("click", ()=>{
1516 _displayAuthTokensArea();
1517 })
1518 .appendTo(btn_grp);
1519 $('<button/>').addClass("button-primary").html(_x('Options', 'label', 'event-tickets-with-ticket-scanner'))
1520 .on("click", ()=>{
1521 _displayOptionsArea();
1522 })
1523 .appendTo(btn_grp);
1524 if (isPremium()) {
1525 btn_grp = PREMIUM.displaySettingAreaButton(btn_grp);
1526 }
1527 return btn_grp;
1528 }
1529
1530 function _form_fields_serial_format(appendToDiv) {
1531 let input_prefix_codes;
1532 let input_type_codes;
1533 let input_amount_letters;
1534 let input_letter_excl;
1535 let input_letter_style;
1536 let input_include_numbers;
1537 let input_serial_delimiter;
1538 let input_serial_delimiter_space;
1539 let input_number_start;
1540 let input_number_offset;
1541
1542 let noNumbersOptions = false;
1543 let cbk = null;
1544 let formatterValues;
1545
1546 function _setNoNumberOptions() {
1547 noNumbersOptions = true;
1548 }
1549 function _setCallbackHandle(_cbk) {
1550 cbk = _cbk;
1551 }
1552 function _callCallbackHandle() {
1553 cbk && cbk(_getFormatterValues());
1554 }
1555 function _setFormatterValues(values) {
1556 formatterValues = values;
1557 }
1558
1559 function __render() {
1560 $('<br>').appendTo(appendToDiv);
1561 // prefix
1562 let div_prefix_codes = _createDivInput(_x("Enter a prefix (optional)", 'label', 'event-tickets-with-ticket-scanner')).appendTo(appendToDiv);
1563 input_prefix_codes = $('<input type="text">').appendTo(div_prefix_codes);
1564 $('<div>').html(__('You can use date placeholder to have the prefix filled with the date of the confirmed purchase.', 'event-tickets-with-ticket-scanner')+'<br>'+__('You can use: {Y} = year, {m} = month, {d} = day, {H} = hour, {i} = minutes, {s} = seconds, {TIMESTAMP} = unix timestamp.', 'event-tickets-with-ticket-scanner')).appendTo(div_prefix_codes);
1565 if (formatterValues && formatterValues['input_prefix_codes'] != null) input_prefix_codes.val(formatterValues['input_prefix_codes']);
1566 input_prefix_codes.on("change", ()=>{
1567 _callCallbackHandle();
1568 });
1569 // type numbers/serials
1570 let div_type_codes = _createDivInput(_x("Choose type of ticket numbers", 'label', 'event-tickets-with-ticket-scanner')).appendTo(appendToDiv);
1571 input_type_codes = $('<select><option value="1" selected>'+_x('Serials', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="2">'+_x('Numbers', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select>').appendTo(div_type_codes);
1572 if (formatterValues && formatterValues['input_type_codes'] != null) input_type_codes.val(formatterValues['input_type_codes']);
1573
1574 if (noNumbersOptions) {
1575 input_type_codes.prop("disabled", true);
1576 }
1577 input_type_codes.on("change", function() {
1578 if (input_type_codes.val() === "2") {
1579 div_serials && div_serials.find("input").prop("disabled", true);
1580 div_serials && div_serials.find("select").prop("disabled", true);
1581 div_numbers && div_numbers.find("input").prop("disabled", false);
1582 div_numbers && div_numbers.find("select").prop("disabled", false);
1583 } else {
1584 div_serials && div_serials.find("input").prop("disabled", false);
1585 div_serials && div_serials.find("select").prop("disabled", false);
1586 div_numbers && div_numbers.find("input").prop("disabled", true);
1587 div_numbers && div_numbers.find("select").prop("disabled", true);
1588 }
1589 _callCallbackHandle();
1590 });
1591 // serials options
1592 let div_serials = $('<div>').html('<h4>'+_x('Serials options', 'title', 'event-tickets-with-ticket-scanner')+'</h4>').appendTo(appendToDiv);
1593 // anzahl letters
1594 let div_amount_letters = _createDivInput(_x('Amount of letter needed', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1595 input_amount_letters = $('<input type="number" required value="21" min="1" max="30">').appendTo(div_amount_letters);
1596 if (formatterValues && formatterValues['input_amount_letters'] != null) input_amount_letters.val(formatterValues['input_amount_letters']);
1597 input_amount_letters.on("change", function(){
1598 input_serial_delimiter.trigger("change");
1599 _callCallbackHandle();
1600 });
1601 // select letter exclusion
1602 let div_letter_excl = _createDivInput(_x('Letter exclusion', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1603 input_letter_excl = $('<select><option value="1">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="2" selected>i,l,o,p,q</option></select>').appendTo(div_letter_excl);
1604 if (formatterValues && formatterValues['input_letter_excl'] != null) input_letter_excl.val(formatterValues['input_letter_excl']);
1605 input_letter_excl.on("change", ()=>{
1606 _callCallbackHandle();
1607 });
1608 // radio button text gross/klein/both/none
1609 let div_letter_style = _createDivInput(_x('Letter style', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1610 input_letter_style = $('<select><option value="1" selected>'+_x('Uppercase', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="2">'+_x('Lowercase', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="3">'+_x('Both', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select>').appendTo(div_letter_style);
1611 if (formatterValues && formatterValues['input_letter_style'] != null) input_letter_style.val(formatterValues['input_letter_style']);
1612 input_letter_style.on("change", ()=>{
1613 _callCallbackHandle();
1614 });
1615 // radio button numbers/none
1616 let div_include_numbers = _createDivInput(_x('Numbers needed?', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1617 input_include_numbers = $('<select><option value="1">'+_x('No', 'label', 'event-tickets-with-ticket-scanner')+'</option><option value="2" selected>'+_x('Yes', 'label', 'event-tickets-with-ticket-scanner')+'</option><option value="3">'+_x('Only numbers', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select>').appendTo(div_include_numbers);
1618 if (formatterValues && formatterValues['input_include_numbers'] != null) input_include_numbers.val(formatterValues['input_include_numbers']);
1619 input_include_numbers.on("change", ()=>{
1620 _callCallbackHandle();
1621 });
1622 // select delimiter none/-/./space
1623 let div_serial_delimiter = _createDivInput(_x('Delimiter?', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1624 input_serial_delimiter = $('<select><option value="1">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="2" selected>-</option><option value="4">:</option><option value="3">'+_x('Space', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select>').appendTo(div_serial_delimiter);
1625 if (formatterValues && formatterValues['input_serial_delimiter'] != null) input_serial_delimiter.val(formatterValues['input_serial_delimiter']);
1626 function __refreshDelimiterSpace() {
1627 input_serial_delimiter_space.html("");
1628 if (input_serial_delimiter.val() !== "1") {
1629 let anzahl = parseInt(input_amount_letters.val(),10);
1630 if (anzahl > 0) {
1631 for(let a=1;a<anzahl;a++) input_serial_delimiter_space.append($('<option'+(anzahl > 2 && a === 7 ? " selected": "")+'>').attr("value",a).html(a));
1632 }
1633 }
1634 }
1635 input_serial_delimiter.on("change", function(){
1636 __refreshDelimiterSpace();
1637 _callCallbackHandle();
1638 });
1639 // choose delimiter space
1640 let div_serial_delimiter_space = _createDivInput(_x('After how many letters?', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1641 input_serial_delimiter_space = $('<select></select>').appendTo(div_serial_delimiter_space);
1642 if (formatterValues && formatterValues['input_serial_delimiter'] != null) {
1643 // setze Werte erstmal ein
1644 __refreshDelimiterSpace();
1645 }
1646 if (formatterValues && formatterValues['input_serial_delimiter_space'] != null) input_serial_delimiter_space.val(formatterValues['input_serial_delimiter_space']);
1647 input_serial_delimiter_space.on("change", ()=>{
1648 _callCallbackHandle();
1649 });
1650 // numbers options
1651 let div_numbers = $('<div>').html('<h4>'+_x('Numbers options', 'title', 'event-tickets-with-ticket-scanner')+'</h4>').appendTo(appendToDiv);
1652 if (noNumbersOptions) div_numbers.css("display","none");
1653 // number start
1654 let div_number_start = _createDivInput(_x('Start number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_numbers);
1655 input_number_start = $('<input type="number" disabled required value="10000" min="1">').appendTo(div_number_start);
1656 if (formatterValues && formatterValues['input_number_start'] != null) input_number_start.val(formatterValues['input_number_start']);
1657 input_number_start.on("change", ()=>{
1658 _callCallbackHandle();
1659 });
1660 // number offset
1661 let div_number_offset = _createDivInput(_x('Offset for each number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_numbers);
1662 input_number_offset = $('<input type="number" disabled required value="1" min="1">').appendTo(div_number_offset);
1663 if (formatterValues && formatterValues['input_number_offset'] != null) input_number_offset.val(formatterValues['input_number_offset']);
1664 input_number_offset.on("change", ()=>{
1665 _callCallbackHandle();
1666 });
1667 }
1668
1669 function __generateCode(length, cases, withnumbers, exclusion) {
1670 let charset = 'abcdefghijklmnopqrstuvwxyz';
1671 if (cases === 1) charset = charset.toUpperCase();
1672 if (cases === 3) charset += charset.toUpperCase();
1673 if (withnumbers === 2) charset += '0123456789';
1674 if (withnumbers === 3) charset = '0123456789';
1675 if (typeof exclusion !== "undefined") {
1676 exclusion.forEach(function(v){
1677 let regex = new RegExp(v, 'gi');
1678 charset = charset.replace(regex, "");
1679 });
1680 }
1681 let retVal = "";
1682 for (var i = 0, n = charset.length; i < length; ++i) {
1683 retVal += charset.charAt(Math.floor(Math.random() * n));
1684 }
1685 return retVal;
1686 }
1687 function __insertSeperator(str, serial_delimiter, serial_delimiter_space) {
1688 if (str !== "" && serial_delimiter !== "" && serial_delimiter_space > 0) {
1689 let result = [str[0]];
1690 for(let x=1; x<str.length; x++) {
1691 if (x%serial_delimiter_space === 0) {
1692 result.push(serial_delimiter, str[x]);
1693 } else {
1694 result.push(str[x]);
1695 }
1696 }
1697 return result.join('');
1698 }
1699 return str;
1700 }
1701
1702 function _isTypeNumbers() {
1703 return input_type_codes.val() === "2";
1704 }
1705 function _getPrefix() {
1706 return input_prefix_codes.val().trim();
1707 }
1708 function _getAmountLetters() {
1709 let amount_letters = parseInt(input_amount_letters.val().trim(),10);
1710 if (isNaN(amount_letters) || amount_letters < 1) {
1711 input_amount_letters.select();
1712 return alert(__("Amount of letters has to be higher", 'event-tickets-with-ticket-scanner'));
1713 }
1714 return amount_letters;
1715 }
1716 function _getLetterExclusion() {
1717 return input_letter_excl.val() === "2" ? ['i','l','o','p','q'] : [];
1718 }
1719 function _getLetterStyle() {
1720 return parseInt(input_letter_style.val(),10);
1721 }
1722 function _getIncludeNumbers() {
1723 return parseInt(input_include_numbers.val(),10);
1724 }
1725 function _getSerialDelimiter() {
1726 return ['','-',' ',':'][parseInt(input_serial_delimiter.val(),10)-1];
1727 }
1728 function _getSerialDelimiterSpace() {
1729 let serial_delimiter_space = 0;
1730 try {
1731 serial_delimiter_space = _getSerialDelimiter() !== "" ? parseInt(input_serial_delimiter_space.val(),10) : 0;
1732 } catch (e) {}
1733 return serial_delimiter_space;
1734 }
1735 function _getNumberStart() {
1736 let start_number = parseInt(input_number_start.val().trim(),10);
1737 if (isNaN(start_number) || start_number < 1) {
1738 input_number_start.select();
1739 return alert(__("Your start number is not correct. It has to be an integer bigger than 0", 'event-tickets-with-ticket-scanner'));
1740 }
1741 return start_number;
1742 }
1743 function _getNumberOffset() {
1744 let number_offset = parseInt(input_number_offset.val().trim(),10);
1745 if (isNaN(number_offset) || number_offset < 1) number_offset = 1;
1746 return number_offset;
1747 }
1748 function _generateSerialCode(offsetCounter) {
1749 let code;
1750 let prefix = _getPrefix();
1751 if (_isTypeNumbers()) { // numbers
1752 if (!offsetCounter) offsetCounter = 0;
1753 let number_offset = offsetCounter * _getNumberOffset();
1754 code = _getNumberStart() + number_offset;
1755 if (prefix !== '') code = prefix + code;
1756 } else {
1757 code = __generateCode(_getAmountLetters(), _getLetterStyle(), _getIncludeNumbers(), _getLetterExclusion());
1758 code = __insertSeperator(code, _getSerialDelimiter(), _getSerialDelimiterSpace());
1759 if (prefix !== '') code = prefix + code;
1760 }
1761 return code;
1762 }
1763 function _getFormatterValues() {
1764 return {
1765 input_prefix_codes:_getPrefix().replace('/', '-'),
1766 input_type_codes:input_type_codes.val(),
1767 input_amount_letters:_getAmountLetters(),
1768 input_letter_excl:input_letter_excl.val(),
1769 input_letter_style:_getLetterStyle(),
1770 input_include_numbers:input_include_numbers.val(),
1771 input_serial_delimiter:input_serial_delimiter.val(),
1772 input_serial_delimiter_space:input_serial_delimiter_space.val(),
1773 input_number_start:_getNumberStart(),
1774 input_number_offset:_getNumberOffset()
1775 };
1776 }
1777
1778 return {
1779 render:__render,
1780 getAmountLetters:_getAmountLetters,
1781 getLetterExclusion:_getLetterExclusion,
1782 getLetterStyle:_getLetterStyle,
1783 getIncludeNumbers:_getIncludeNumbers,
1784 getSerialDelimiter:_getSerialDelimiter,
1785 getSerialDelimiterSpace:_getSerialDelimiterSpace,
1786 getNumberStart:_getNumberStart,
1787 getNumberOffset:_getNumberOffset,
1788 isTypeNumbers:_isTypeNumbers,
1789 getPrefix:_getPrefix,
1790 generateSerialCode:_generateSerialCode,
1791 setNoNumberOptions:_setNoNumberOptions,
1792 getFormatterValues:_getFormatterValues,
1793 setCallbackHandle:_setCallbackHandle,
1794 setFormatterValues:_setFormatterValues
1795 };
1796 }
1797
1798 function _createDivInput(label) {
1799 return $('<div/>').css({
1800 "display": "inline-block",
1801 "margin-bottom": "15px",
1802 "margin-right": "15px"
1803 }).html(label+"<br>");
1804 }
1805
1806 class Layout {
1807 constructor(){
1808 DIV.addClass("sngmbh_container");
1809 this.div_liste = $('<div style="background:white;padding:15px;border-radius:15px;"/>').html(_getSpinnerHTML());
1810 this.div_codes = $('<div style="background:white;padding:15px;border-radius:15px;"/>').html(_getSpinnerHTML());
1811 this.div_spinner = $('<div style="display: none;position: fixed;z-index: 1031;top: 50%;right: 50%;margin-top: 0.5vh;background-color: white;margin-left: 0.5vw;border: 4px solid #2e74b5;padding: 10px;border-radius:10%;"/>').html(_getSpinnerHTML("loading"));
1812 $("body").append(this.div_spinner);
1813 }
1814 renderMainBody() {
1815 let premium_status = '<div style="color:red;font-weight:bold;">'+_x('FREE version', 'label', 'event-tickets-with-ticket-scanner')+'</div>';
1816 if (isPremium()) {
1817 premium_status = '<div style="color:green;font-weight:bold;">'+_x('PREMIUM', 'label', 'event-tickets-with-ticket-scanner')+'</div>';
1818 }
1819 $('body').find('td[data-id=plugin_info_area_premium_status]').html(premium_status);
1820
1821 /*
1822 $('body').find('div[data-id=plugin_addons]').html("")
1823 .css("display", "flex")
1824 .css("justify-content", "space-between")
1825 .css("width", "100%")
1826 .css("padding-bottom", "20px")
1827 .css("padding-top", "20px")
1828 .css("box-sizing", "border-box")
1829 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("How to start") )
1830 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("Quick start") )
1831 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("Ticket scanner") )
1832 ;
1833 if (isPremium() == false) {
1834 $('body').find('div[data-id=plugin_addons]')
1835 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("Upgrade now") );
1836 }
1837 */
1838
1839 let div_body = $('<div/>');
1840 div_body.append($('<div style="text-align:right;">').html(_displaySettingAreaButton()));
1841 div_body.append($('<h3/>').html(_x('List of tickets', 'title', 'event-tickets-with-ticket-scanner')));
1842 div_body.append($('<p/>').html(__("Organize your tickets in lists. You can assign tickets to a list.", 'event-tickets-with-ticket-scanner')));
1843 div_body.append(this.div_liste);
1844 div_body.append($('<hr/>'));
1845 div_body.append($('<h3/>').html(_x("Event Tickets", 'title', 'event-tickets-with-ticket-scanner')));
1846 div_body.append(this.div_codes);
1847 return div_body;
1848 }
1849 renderAddCodes() {
1850 DIV.html(_getSpinnerHTML());
1851 getDataLists(()=>{
1852 function __generateCodes() {
1853 // generate codes and
1854 let amount_codes = parseInt(input_amount_codes.val().trim(),10);
1855 if (isNaN(amount_codes) || amount_codes < 1) {
1856 input_amount_codes.select();
1857 return alert(_x("Enter an amount of how many ticket numbers you need", 'title', 'event-tickets-with-ticket-scanner'));
1858 }
1859 if (amount_codes > _maxCodes) {
1860 input_amount_codes.val(_maxCodes);
1861 amount_codes = _maxCodes;
1862
1863 }
1864 let uniq = {};
1865 let versuche = 0;
1866 if (serialCodeFormatterForm.isTypeNumbers()) { // numbers
1867 for(let a=0; a < amount_codes; a++) {
1868 let code = serialCodeFormatterForm.generateSerialCode( a );
1869 if (typeof uniq[code] !== "undefined") {
1870 continue;
1871 }
1872 uniq[code] = true;
1873 }
1874 versuche = amount_codes;
1875 } else {
1876 // erstmal kein check ob mit dem alphabet und die geforderte Menge an letters, unique codes erstellt werden können
1877 let counter = 0;
1878 let versuche_max = amount_codes * 1.5;
1879 while(counter < amount_codes && versuche < versuche_max) {
1880 versuche++;
1881 let code = serialCodeFormatterForm.generateSerialCode();
1882 if (typeof uniq[code] !== "undefined") {
1883 continue;
1884 }
1885 uniq[code] = true;
1886 counter++;
1887 }
1888 }
1889 return [Object.keys(uniq), versuche];
1890 } // __generateCodes
1891
1892 let div = $('<div>').append(getBackButtonDiv());
1893 // eingabe generator options
1894 let div_generator = $('<div/>').css("padding", "10px").css("border","1px solid black").html('<h3>'+_x('1. Ticket number generator (optional step)', 'title', 'event-tickets-with-ticket-scanner')+'</h3>').appendTo(div);
1895 div_generator.append($('<p>').html(__("You can generate ticket numbers.", 'event-tickets-with-ticket-scanner')));
1896 if (isPremium()) div_generator.append('<p>'+__('Up 100.000 tickets generation per run. The limit is to prevent performance issues.', 'event-tickets-with-ticket-scanner')+'<br>'+__('You can repeat the "store tickets" operations as often as needed.', 'event-tickets-with-ticket-scanner')+'</p>');
1897 // anzahl codes
1898 let div_amount_codes = _createDivInput(_x('Enter amount of needed ticket numbers', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_generator);
1899 let _maxCodes = myAjax._max.codes;
1900 if (!isPremium()) div_amount_codes.append(sprintf(/* translators: 1: amount of possible codes 2: premium info */__('%1$d max. %2$s up to 100.000 for each run', 'event-tickets-with-ticket-scanner'), _maxCodes, getLabelPremiumOnly())+'<br>');
1901 let input_amount_codes = $('<input type="number" required value="100" min="1" max="'+_maxCodes+'">').appendTo(div_amount_codes);
1902
1903 // predefine elements
1904 let serialCodeFormatterForm = _form_fields_serial_format(div_generator);
1905 serialCodeFormatterForm.render();
1906
1907 let elem_clean_codebox = $('<input checked type="checkbox" />');
1908 $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"})
1909 .html(elem_clean_codebox)
1910 .append(_x('Clear the ticket numbers list textarea field below to add fill in the new generated ticket numbers', 'label', 'event-tickets-with-ticket-scanner'))
1911 .appendTo(div_generator);
1912
1913 let elem_create_cvv = $('<input type="checkbox" />');
1914 $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"})
1915 .html(elem_create_cvv)
1916 .append(_x('Generate Code Verification Value (CVV) for each ticket number', 'label', 'event-tickets-with-ticket-scanner'))
1917 .appendTo(div_generator);
1918
1919 // button generate
1920 div_generator.append($('<button/>').addClass("button-secondary").html(_x('Generate ticket numbers', 'label', 'event-tickets-with-ticket-scanner')).on("click", function(){
1921 let time_start = performance.now();
1922 btn_store_codes.prop("disabled", false);
1923 input_textarea.prop("disabled", false);
1924 if (elem_clean_codebox[0].checked) {
1925 input_textarea.html("");
1926 }
1927 input_textarea.prop("disabled", true);
1928 div_textarea_info.css("padding-bottom", "50px").html(_getSpinnerHTML());
1929 setTimeout(function(){
1930 let r = __generateCodes();
1931 let codes = r[0];
1932 let secs = ((performance.now() - time_start) / 1000)+"";
1933 if (elem_create_cvv[0].checked) {
1934 codes = codes.map(v=>{
1935 return v += ';'+(Math.floor(Math.random() * 10000) + 10000).toString().substring(1);
1936 });
1937 }
1938 input_textarea.append(codes.join("\n")).append("\n");
1939 input_textarea.prop("disabled", false);
1940 div_textarea_info.html(sprintf(/* translators: 1: amount of created tickets 2: seconds 3: amount of runs */__('Created %1$d tickets. In %2$s seconds, with %3$d runs to find unique ticket numbers.', 'event-tickets-with-ticket-scanner'), codes.length, secs.slice(0,5), r[1]));
1941 _calcLinesOfCodeTextArea();
1942 },250);
1943 }));
1944
1945 // eingabe maske textarea
1946 function _calcLinesOfCodeTextArea() {
1947 let codesAmount = 0;
1948 input_textarea.val().trim().split('\n').forEach(v=>{
1949 if (v.trim() !== "") codesAmount++;
1950 });
1951 input_textarea_info.html(sprintf(/* translators: %d: amout of ticket numbers */__('contains %d tickets', 'event-tickets-with-ticket-scanner'), codesAmount));
1952 }
1953 let div_textarea = $('<div/>').html('<h3>'+_x('2. Ticket numbers to store on the server', 'title', 'event-tickets-with-ticket-scanner')+'</h3><p>'+__('One number per line and/or comma-separated (,). <br>If you want to add the CVV number then separate your ticket number with (;) and append your CVV number.<br>While storing the numbers to the server, it will check if the ticket number is unique and mark the ones, that are not.', 'event-tickets-with-ticket-scanner')+'</p>').appendTo(div);
1954 let div_textarea_info = $('<div/>').appendTo(div_textarea);
1955 let input_textarea = $('<textarea>').change(_calcLinesOfCodeTextArea).css("height","135px").css("width","100%").appendTo(div_textarea);
1956 let input_textarea_info = $('<div/>').appendTo(div_textarea);
1957 div_textarea.append("<br>");
1958 _calcLinesOfCodeTextArea();
1959 // list auswahl
1960 let div_code_list = _createDivInput(_x('Assign to this ticket list', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_textarea);
1961 let input_code_list = $('<select><option value="0">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</select></select>').appendTo(div_code_list);
1962 DATA_LISTS.forEach(v=>{
1963 input_code_list.append('<option value="'+v.id+'">'+v.name+'</option>');
1964 });
1965 div_textarea.append("<br>");
1966
1967 // additional prem fields
1968 if (isPremium() && PREMIUM.addAddCodeFields) {
1969 div_textarea.append(PREMIUM.addAddCodeFields());
1970 }
1971
1972 // button store codes
1973 if (!isPremium()) div_textarea.append('<b>'+sprintf(/* translators: 1: max amout of ticket numbers 2: premium info */__('You can store up to %1$d. %2$s unlimited', 'event-tickets-with-ticket-scanner'), myAjax._max.codes_total, getLabelPremiumOnly())+'<br>');
1974 let btn_store_codes = $('<button/>');
1975 btn_store_codes.addClass("button-primary").html(_x('Store ticket numbers', 'label', 'event-tickets-with-ticket-scanner')).on("click", function(){
1976 // extract codes and
1977 let codes = [];
1978 let codesLines = input_textarea.val().split("\n").map(x=>x.trim());
1979 codesLines.forEach(x=>{
1980 x.split(",").forEach(y=>{
1981 y = y.trim();
1982 y = destroy_tags(y);
1983 if (y != "") codes.push(y);
1984 });
1985 });
1986 if (codes.length === 0) return;
1987
1988 // sperre btn store codes
1989 btn_store_codes.prop("disabled", true);
1990 input_textarea.prop("disabled", true);
1991
1992 div_textarea_info.append($('<div/>').addClass("notice notice-info").html(__("Each entry will turn green (successfull stored) or red (NOT OK - duplicat entry on the server).<br>Scroll down and wait for all to finish.<br>In the textarea below you will find all the successful stored tickets.", 'event-tickets-with-ticket-scanner')));
1993 let _output = $('<ol/>').appendTo(div_textarea_info);
1994 div_textarea_info.append('<h3>'+_x('Successfull stored ticket numbers', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
1995 let output_textarea_codes_done = $('<textarea disabled style="4px solid green;width:100%;height:150px;"></textarea>').appendTo(div_textarea_info);
1996
1997 let list_id = parseInt(input_code_list.val(),10);
1998
1999 function __addCodesInChunks(chunk_size) {
2000 let dlg = $('<div/>').html(_getSpinnerHTML());
2001 dlg.dialog({title:_x('Importing', 'title', 'event-tickets-with-ticket-scanner'),closeOnEscape: true,modal: true, dialogClass: "no-close", close: function(event, ui){ abort=true; } });
2002
2003 let abort = false;
2004 let counter_ok = 0;
2005 let counter_notok = 0;
2006 let counter_all = codes.length;
2007 const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size));
2008 let chunks = array_chunks(codes, chunk_size);
2009 function _addCodeChunk(idx) {
2010 if (abort) return;
2011 if (idx >= chunks.length) {
2012 dlg.append('<p>'+__('Import process finished', 'event-tickets-with-ticket-scanner')+'</p>');
2013 $('<center/>').append($('<button class="button-primary" />').html(_x('Ok', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{ closeDialog(dlg); })).appendTo(dlg);
2014 return;
2015 }
2016 let arr = chunks[idx];
2017 arr.forEach(v=>{
2018 let div_info_entry = $('<li data-id="code_'+v+'"/>').html(v);
2019 _output.append(div_info_entry);
2020 });
2021 let attr = {"codes":arr, "list_id":list_id};
2022 if (isPremium() && PREMIUM.addAddCodeFieldsData) {
2023 attr = PREMIUM.addAddCodeFieldsData(div_textarea, attr);
2024 }
2025
2026 _makePost("addCodes", attr, function(data){
2027 counter_ok += data.ok.length;
2028 counter_notok += data.notok.length;
2029 if (myAjax._max.codes_total > 0 && myAjax._max.codes_total <= parseInt(data.total_size)) {
2030 div_textarea_info.prepend('<h3 style="color:red;">'+sprintf(/* translators: %d: total ticket count */_x('Your Limit of %d tickets is reached. Use the premium version to have unlimited tickets', 'title', 'event-tickets-with-ticket-scanner'),myAjax._max.codes_total)+'</h3>');
2031 }
2032 let per = Math.ceil(((counter_ok+counter_notok)/counter_all)*100);
2033 let info_content = '<div style="width:100%;border:1px solid #efefef;background-color:white;"><div style="text-align:center;height:20px;background-color:#428bca;color:white;width:'+per+'%;">'+per+'%</div></div>';
2034 info_content += '<p style="margin-top:20px;">'+_x('Amount', 'title', 'event-tickets-with-ticket-scanner')+': '+(counter_ok+counter_notok)+'/'+counter_all+'<br>'+_x('Ok', 'label', 'event-tickets-with-ticket-scanner')+': '+counter_ok+'<br>'+_x('Not Ok', 'label', 'event-tickets-with-ticket-scanner')+': '+counter_notok+'</p>';
2035 dlg.html(info_content);
2036 data.ok.forEach(_v=> {
2037 _output.find('li[data-id="code_'+_v+'"]').css("color","green").append(' ('+_x('Ok', 'label', 'event-tickets-with-ticket-scanner')+')');
2038 output_textarea_codes_done.append(_v+"\n");
2039 });
2040 data.notok.forEach(_v=> {
2041 _output.find('li[data-id="code_'+_v+'"]').css("color","red").append(' ('+_x('Not Ok', 'label', 'event-tickets-with-ticket-scanner')+')');
2042 });
2043 setTimeout(()=>{
2044 _addCodeChunk(idx+1);
2045 }, 100);
2046 }, function(response){
2047 if (response.data.slice(0,4) === "#208") {
2048 FATAL_ERROR === false && LAYOUT.renderFatalError(response.data);
2049 FATAL_ERROR = true;
2050 }
2051 });
2052 }
2053
2054 if (chunks.length === 0) {
2055 closeDialog(dlg);
2056 } else {
2057 _addCodeChunk(0);
2058 }
2059 } // __addCodesInChunks
2060 __addCodesInChunks(100);
2061
2062 // zeige ok button, der info area leer macht und den btn store codes wieder aktiviert
2063 div_textarea_info.append($('<button/>').addClass("button-primary").css("margin-bottom", "20px").html(_x('Ok', 'label', 'event-tickets-with-ticket-scanner')).on("click", function(){
2064 div_textarea_info.html("");
2065 btn_store_codes.prop("disabled", false);
2066 input_textarea.prop("disabled", false);
2067 window.scrollTo(0,0);
2068 }));
2069
2070 }).appendTo(div_textarea);
2071 DIV.html(div);
2072 });
2073 }
2074 renderAdminPageLayout(cbf) {
2075 function __showMaskExport(totalRecordCount) {
2076 if (!totalRecordCount) totalRecordCount = 0;
2077 let maxRange = totalRecordCount > 40000 ? 40000 : totalRecordCount;
2078 let _options = {
2079 title: _x('Export tickets', 'title', 'event-tickets-with-ticket-scanner'),
2080 modal: true,
2081 minWidth: 400,
2082 minHeight: 200,
2083 buttons: [
2084 {
2085 text: _x('Export', 'label', 'event-tickets-with-ticket-scanner'),
2086 click: function() {
2087 ___submitForm();
2088 }
2089 },
2090 {
2091 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
2092 click: function() {
2093 closeDialog(this);
2094 }
2095 }
2096 ]
2097 };
2098 let formdlg = $('<form/>').html('<b>'+_x('Choose your export settings', 'title', 'event-tickets-with-ticket-scanner')+'</b><p>');
2099 formdlg.append(_x('Choose the delimiter for the column values', 'label', 'event-tickets-with-ticket-scanner')+'<br><select name="delimiter"><option value="1">, ('+_x('Comma', 'option value', 'event-tickets-with-ticket-scanner')+')</option><option value="2">; ('+_x('Semicolon', 'option value', 'event-tickets-with-ticket-scanner')+')</option><option value="3">| ('+_x('Pipe', 'option value', 'event-tickets-with-ticket-scanner')+')</option></select><p>');
2100 formdlg.append(_x('Choose a file suffix', 'label', 'event-tickets-with-ticket-scanner')+'<br><select name="suffix"><option value="1">.csv</option><option value="2">.txt</option></select><p>');
2101
2102 let _listChooser = $('<select name="listchooser"><option value="0">'+_x('All', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select>');
2103 for(let a=0;a<DATA_LISTS.length;a++) {
2104 _listChooser.append('<option value="'+DATA_LISTS[a].id+'">'+DATA_LISTS[a].name+'</option>');
2105 }
2106 formdlg.append(_x('Limit export to ticket list', 'label', 'event-tickets-with-ticket-scanner')+'<br>').append(_listChooser).append('<p>');
2107
2108 formdlg.append(_x('Choose a sorting field', 'label', 'event-tickets-with-ticket-scanner')+'<br><select name="orderby"><option value="1" selected>'+_x('Creation date', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="2">'+__('Ticket number', 'event-tickets-with-ticket-scanner')+'</option><option value="3">'+__('Ticket display number', 'event-tickets-with-ticket-scanner')+'</option><option value="4">'+_x('List name', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select><p>');
2109 formdlg.append(_x('Choose a sorting direction', 'label', 'event-tickets-with-ticket-scanner')+'<br><select name="orderbydirection"><option value="1" selected>'+_x('Ascending', 'option value', 'event-tickets-with-ticket-scanner')+'</option><option value="2">'+_x('Descending', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select><p>');
2110 formdlg.append(_x('Set a range', 'label', 'event-tickets-with-ticket-scanner')+'<br><i>'+sprintf(/* translators: %d: total record count */__('You have %d tickets stored.', 'event-tickets-with-ticket-scanner'), totalRecordCount)+'<br>'+__('Some systems are slow and the connection timeout interupts the export, if you have too many tickets. In that case, you can export your tickets in several steps. e.g. 0 and 20000 amount and then 20001 and 20000 amount.', 'event-tickets-with-ticket-scanner')+'</i><br>'+__('Enter your row start (0 = from the first)', 'event-tickets-with-ticket-scanner')+'<br><input type="number" name="rangestart" value="0"><br>'+_x('Enter amount of tickets', 'label', 'event-tickets-with-ticket-scanner')+'<br><input type="number" name="rangeamount" value="'+maxRange+'"><p>');
2111 if (isPremium() && PREMIUM && PREMIUM.addExportTicketsInputFields) {
2112 formdlg.append(PREMIUM.addExportTicketsInputFields());
2113 }
2114 let dlg = $('<div/>').append(formdlg);
2115
2116 dlg.dialog(_options);
2117
2118 let form = dlg.find("form").on("submit", function(event) {
2119 event.preventDefault();
2120 ___submitForm();
2121 });
2122
2123 function ___submitForm() {
2124 let delimiter = dlg.find('select[name="delimiter"]').val();
2125 let filesuffix = dlg.find('select[name="suffix"]').val();
2126 let orderby = dlg.find('select[name="orderby"]').val();
2127 let orderbydirection = dlg.find('select[name="orderbydirection"]').val();
2128 let rangestart = dlg.find('input[name="rangestart"]').val();
2129 let rangeamount = dlg.find('input[name="rangeamount"]').val();
2130 let listchooser = dlg.find('select[name="listchooser"]').val();
2131
2132 let data = {'delimiter':delimiter, 'filesuffix':filesuffix, 'orderby':orderby, 'orderbydirection':orderbydirection, 'rangestart':rangestart, 'rangeamount':rangeamount, 'listchooser':listchooser};
2133 if (isPremium() && PREMIUM && PREMIUM.addExportTicketsInputFieldsData) {
2134 data = PREMIUM.addExportTicketsInputFieldsData(data, dlg);
2135 }
2136
2137 let url = _requestURL('exportTableCodes', data);
2138 closeDialog(dlg);
2139 window.open(url, "_blank");
2140 }
2141 }
2142 function __showMaskList(editValues){
2143 let _options = {
2144 title: editValues !== null ? _x('Edit List', 'title', 'event-tickets-with-ticket-scanner') : _x('Add List', 'title', 'event-tickets-with-ticket-scanner'),
2145 modal: true,
2146 minWidth: 600,
2147 minHeight: 400,
2148 open: function(e) {
2149 //$(e.target).parent().css('background-color','orangered');
2150 },
2151 buttons: [
2152 {
2153 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
2154 click: function() {
2155 ___submitForm();
2156 }
2157 },
2158 {
2159 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
2160 click: function() {
2161 closeDialog(this);
2162 }
2163 }
2164 ]
2165 };
2166 let dlg = $('<div/>').html('<form>'+_x('Name', 'label', 'event-tickets-with-ticket-scanner')+'<br><input name="inputName" type="text" style="width:100%;" required></form>');
2167 dlg.dialog(_options);
2168
2169 dlg.find("form").append($('<p>'+_x('Description', 'label', 'event-tickets-with-ticket-scanner')+'<br><textarea name="desc" style="width:100%;"></textarea></p>'));
2170
2171 if (isPremium()) PREMIUM.addListMaskEditFields(dlg, editValues);
2172 else {
2173 if (_getOptions_isActivatedByKey("oneTimeUseOfRegisterCode")) {
2174 dlg.append($('<p><b>'+sprintf(/* translators: %s: h4 option name */__('Overrule %s per Ticket list', 'event-tickets-with-ticket-scanner'), _getOptions_getLabelByKey("h4"))+'</b> '+getLabelPremiumOnly()+'</p>'));
2175 }
2176 }
2177
2178 let metaObj = [];
2179 if (editValues && typeof editValues.meta !== "undefined" && editValues.meta != "") {
2180 try {
2181 metaObj = JSON.parse(editValues.meta);
2182 } catch(e) {}
2183 }
2184
2185 if (_getOptions_isActivatedByKey("userJSRedirectActiv")) {
2186 dlg.find("form").append($('<p>'+_getOptions_getLabelByKey("userJSRedirectURL")+'<br><input type="text" name="redirecturl" style="width:100%;"></p>'));
2187 }
2188
2189 dlg.find("form").append($('<p><input name="serialformatter" type="checkbox"> '+_x('Overrule the ticket format settings', 'label', 'event-tickets-with-ticket-scanner')+'</p>'));
2190 let extra_div = $('<div>').appendTo(dlg).css("margin-top", "10px").css("margin-left", "24px").css("padding", "10px").css("border", "1px solid black")
2191 .html('<p><b>'+_x('Note', 'label', 'event-tickets-with-ticket-scanner')+':</b> '+__('Will be overriden if you set the ticket number format settings on the product!', 'event-tickets-with-ticket-scanner')+'</p>');
2192 let serialCodeFormatter = _form_fields_serial_format(extra_div);
2193 serialCodeFormatter.setNoNumberOptions();
2194 if (typeof metaObj.formatter !== "undefined" && metaObj.formatter.format != "") {
2195 let formatterValues;
2196 try {
2197 let o = metaObj.formatter.format.replace(new RegExp("\\\\", "g"), "").trim();
2198 formatterValues = JSON.parse(o);
2199 serialCodeFormatter.setFormatterValues(formatterValues);
2200 } catch (e) {}
2201 }
2202 serialCodeFormatter.render();
2203
2204 $('<hr>').appendTo(dlg);
2205 $('<h4>').html(_x('Webhook', 'heading', 'event-tickets-with-ticket-scanner')).appendTo(dlg);
2206 if (!_getOptions_isActivatedByKey("webhooksActiv")) {
2207 $('<div style="color:red">').html(_x('The webhook need to be activated first in the options to be executed, even if the URL is set here.', 'label', 'event-tickets-with-ticket-scanner')).appendTo(dlg);
2208 }
2209 $('<div>').html(_x('URL to your service if the WooCommerce ticket is sold', 'label', 'event-tickets-with-ticket-scanner')).appendTo(dlg);
2210 let meta_webhooks_webhookURLaddwcticketsold = $('<input name="meta_webhooks_webhookURLaddwcticketsold" type="text" style="width:100%;">').appendTo(dlg);
2211
2212 let form = dlg.find("form").on("submit", function(event) {
2213 event.preventDefault();
2214 ___submitForm();
2215 });
2216
2217 if (editValues) {
2218 form[0].elements['inputName'].value = editValues.name;
2219 form[0].elements['inputName'].select();
2220 if (typeof metaObj.desc !== "undefined") {
2221 form[0].elements['desc'].value = metaObj.desc;
2222 }
2223 if (typeof metaObj.formatter !== "undefined" && metaObj.formatter.active) {
2224 form[0].elements['serialformatter'].checked = true;
2225 }
2226 if (_getOptions_isActivatedByKey("userJSRedirectActiv") && typeof metaObj.redirect !== "undefined" && metaObj.redirect.url) {
2227 form[0].elements['redirecturl'].value = metaObj.redirect.url.trim();
2228 }
2229 if (typeof metaObj.webhooks != "undefined") {
2230 if (typeof metaObj.webhooks.webhookURLaddwcticketsold != "undefined") {
2231 meta_webhooks_webhookURLaddwcticketsold.val(metaObj.webhooks.webhookURLaddwcticketsold);
2232 }
2233 }
2234 }
2235
2236 function ___submitForm() {
2237 let inputName = form[0].elements['inputName'].value.trim();
2238 if (inputName === "") return;
2239
2240 dlg.html(_getSpinnerHTML());
2241 let _data = {"name":inputName};
2242 _data['meta'] = {"desc":"", "formatter":{}, "webhooks":{}};
2243 _data['meta']['desc'] = form[0].elements['desc'].value.trim();
2244 _data['meta']['formatter']['active'] = form[0].elements['serialformatter'].checked ? 1 : 0;
2245 _data['meta']['formatter']['format'] = JSON.stringify(serialCodeFormatter.getFormatterValues());
2246 if (_getOptions_isActivatedByKey("userJSRedirectActiv")) {
2247 _data['meta']['redirect'] = {"url":form[0].elements['redirecturl'].value.trim()};
2248 }
2249 _data['meta']['webhooks']['webhookURLaddwcticketsold'] = meta_webhooks_webhookURLaddwcticketsold.val().trim();
2250 if (isPremium()) PREMIUM.addListMaskEditFieldsData(_data, form[0], editValues);
2251
2252 form[0].reset();
2253 if (editValues) {
2254 _data.id = editValues.id;
2255 _makePost('editList', _data, result=>{
2256 DATA_LISTS = null;
2257 __renderTabelleListen();
2258 tabelle_codes_datatable.ajax.reload();
2259 setTimeout(function(){closeDialog(dlg);},250);
2260 }, function() {
2261 closeDialog(dlg);
2262 });
2263 } else {
2264 _makePost('addList', _data, result=>{
2265 DATA_LISTS = null;
2266 __renderTabelleListen();
2267 closeDialog(dlg);
2268 }, function(response) {
2269 closeDialog(dlg);
2270 if (response.data.slice(0,1) === "#") {
2271 FATAL_ERROR === false && LAYOUT.renderFatalError(response.data);
2272 FATAL_ERROR = true;
2273 }
2274 });
2275 }
2276 }
2277
2278 } // ende showmaskliste
2279
2280 function __showMaskCode(editValues){
2281 let _options = {
2282 title: editValues !== null ? _x('Edit Ticket', 'title', 'event-tickets-with-ticket-scanner') : _x('Add Ticket', 'title', 'event-tickets-with-ticket-scanner'),
2283 modal: true,
2284 minWidth: 400,
2285 minHeight: 200,
2286 buttons: [
2287 {
2288 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
2289 click: function() {
2290 ___submitForm();
2291 }
2292 },
2293 {
2294 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
2295 click: function() {
2296 $( this ).dialog( "close" );
2297 $( this ).html('');
2298 }
2299 }
2300 ]
2301 };
2302 let dlg = $('<div />').html('<form>'+_x('List', 'label', 'event-tickets-with-ticket-scanner')+'<br><select name="inputListId"><option value="0">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select></form>');
2303 DATA_LISTS.forEach(v=>{
2304 $(dlg).find('select[name="inputListId"]').append('<option '+(editValues && parseInt(editValues.list_id,10) === parseInt(v.id,10) ? 'selected ':'')+'value="'+v.id+'">'+v.name+'</option>');
2305 });
2306
2307 let elem_cvv = $('<input type="text" size="6" minlength="5" maxlength="4" />');
2308 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
2309 .html(_x('CVV - use 4 digits for best results', 'label', 'event-tickets-with-ticket-scanner')+'<br>')
2310 .append(elem_cvv)
2311 .append('<br><i>'+__('If CVV is set, then your user will be asked to enter also the CVV to check the ticket number.', 'event-tickets-with-ticket-scanner')+'</i>')
2312 .appendTo(dlg.find("form"));
2313
2314 let div_status = $('<div/>');
2315 div_status.append(
2316 $('<select name="inputStatus"/>')
2317 .append('<option '+(editValues.aktiv === "1"?'selected':'')+' value="1">'+_x('is activ', 'option value', 'event-tickets-with-ticket-scanner')+'</option>')
2318 .append('<option '+(editValues.aktiv === "0"?'selected':'')+' '+(!isPremium()?'disabled':'')+' value="0">'+_x('is inactiv', 'option value', 'event-tickets-with-ticket-scanner')+' '+(!isPremium()?getLabelPremiumOnly():'')+'</option>')
2319 .append('<option '+(editValues.aktiv === "2"?'selected':'')+' value="2">'+_x('is stolen', 'label', 'event-tickets-with-ticket-scanner')+'</option>')
2320 )
2321 .appendTo(dlg);
2322
2323 dlg.dialog(_options);
2324
2325 if (editValues) {
2326 if (editValues.cvv) elem_cvv.val(editValues.cvv);
2327 }
2328
2329 if (isPremium()) PREMIUM.addCodeMaskEditFields(dlg, editValues);
2330
2331 let form = dlg.find("form").on("submit", function(event) {
2332 event.preventDefault();
2333 ___submitForm();
2334 });
2335 function ___submitForm() {
2336 let inputListId = parseInt($(dlg).find('select[name="inputListId"]').val(),10);
2337 let inputStatusValue = $(dlg).find('select[name="inputStatus"]').val();
2338 dlg.html(_getSpinnerHTML());
2339 let _data = {"list_id":inputListId, "aktiv":inputStatusValue, "cvv":elem_cvv.val().trim()};
2340 if (isPremium()) PREMIUM.addCodeMaskEditFieldsData(_data, form[0], editValues);
2341 form[0].reset();
2342 if (editValues) {
2343 _data.code = editValues.code;
2344 _makeGet('editCode', _data, ()=>{
2345 tabelle_codes_datatable.ajax.reload();
2346 closeDialog(dlg);
2347 }, function() {
2348 closeDialog(dlg);
2349 });
2350 } else {
2351 alert(__("Use the add option", 'event-tickets-with-ticket-scanner'));
2352 }
2353 }
2354 } // ende __showMaskCode
2355
2356 let id_codes = myAjax.divPrefix+'_tabelle_codes';
2357 let tabelle_liste_datatable;
2358 let tabelle_codes_datatable;
2359 let tabelle_codes = $('<table/>').attr("id", id_codes);
2360 let tplace = $('<div/>');
2361
2362 function __renderTabelleListen() {
2363 getDataLists(()=>{
2364 let id_liste = myAjax.divPrefix+'_tabelle_liste';
2365 let tabelle_liste = $('<table/>').attr("id", id_liste);
2366 tabelle_liste.html('<thead><tr><th align="left">'+_x('Name', 'label', 'event-tickets-with-ticket-scanner')+'</th><th align="left">'+_x('Created', 'label', 'event-tickets-with-ticket-scanner')+'</th><th></th></tr></thead>');
2367 tplace.html(tabelle_liste);
2368
2369 let table = $('#'+id_liste);
2370 $(table).DataTable().clear().destroy();
2371 tabelle_liste_datatable = $(table).DataTable({
2372 "responsive": true,
2373 "visible": true,
2374 "searching": true,
2375 "ordering": true,
2376 "processing": true,
2377 "serverSide": false,
2378 "stateSave": true,
2379 "data": DATA_LISTS,
2380 "order": [[ 0, "asc" ]],
2381 "columns":[
2382 {"data":"name", "orderable":true},
2383 {"data":"time", "orderable":true, "width":80,
2384 "render":function (data, type, row) {
2385 return '<span style="display:none;">'+data+'</span>'+DateTime2Text(data);
2386 }
2387 },
2388 {"data":null,"orderable":false,"defaultContent":'',"className":"buttons dt-right","width":110,
2389 "render": function ( data, type, row ) {
2390 return '<button class="button-secondary" data-type="showCodes">'+_x('Tickets', 'label', 'event-tickets-with-ticket-scanner')+'</button> <button class="button-secondary" data-type="edit">'+_x('Edit', 'label', 'event-tickets-with-ticket-scanner')+'</button> <button class="button-secondary" data-type="delete">'+_x('Delete', 'label', 'event-tickets-with-ticket-scanner')+'</button>';
2391 }
2392 }
2393 ]
2394 });
2395 tabelle_liste.css("width", "100%");
2396 table.on('click', 'button[data-type="showCodes"]', e=>{
2397 let data = tabelle_liste_datatable.row( $(e.target).parents('tr') ).data();
2398 tabelle_codes_datatable.search("LIST:"+data.id).draw();
2399 });
2400 table.on('click', 'button[data-type="edit"]', e=>{
2401 let data = tabelle_liste_datatable.row( $(e.target).parents('tr') ).data();
2402 __showMaskList(data);
2403 });
2404 table.on('click', 'button[data-type="delete"]', e=>{
2405 let data = tabelle_liste_datatable.row( $(e.target).parents('tr') ).data();
2406 LAYOUT.renderYesNo(_x('Do you want to delete?', 'title', 'event-tickets-with-ticket-scanner'), __('Are you sure, you want to delete this list?', 'event-tickets-with-ticket-scanner')+'<br><p><b>'+data.name+'</b></p>'+__('No ticket will be deleted. Just the list.', 'event-tickets-with-ticket-scanner'), ()=>{
2407 let _data = {'id':data.id};
2408 _makePost('removeList', _data, result=>{
2409 __renderTabelleListen();
2410 tabelle_codes_datatable.ajax.reload();
2411 });
2412 });
2413 });
2414 }); // end of loading lists
2415 } // __renderTabelleListen
2416 tabelle_codes.css("width", "100%");
2417
2418 STATE = 'admin';
2419 DIV.html(_getSpinnerHTML());
2420 getOptionsFromServer(optionData=>{
2421 DIV.html('');
2422 DIV.append(this.renderMainBody());
2423
2424 let btn_liste_empty = $('<button/>').addClass("button-secondary").html(__('Empty table', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2425 LAYOUT.renderYesNo(__('Empty table', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: title list of tickets */__('Do you want to empty the "%s" table? All data will be lost. No ticket will be deleted. Just the lists.', 'event-tickets-with-ticket-scanner'), _x('List of tickets', 'title', 'event-tickets-with-ticket-scanner')), ()=>{
2426 LAYOUT.renderYesNo(__('Empty table - last chance', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: title list of tickets */__('Are you sure? You will not be able to restore the data, except you have a backup of your database. All data will be lost. No ticket will be deleted. Just the lists.', 'event-tickets-with-ticket-scanner'), _x('List of tickets', 'title', 'event-tickets-with-ticket-scanner')), ()=>{
2427 _makeGet('emptyTableLists', null, ()=>{
2428 tabelle_codes_datatable.ajax.reload();
2429 __renderTabelleListen();
2430 });
2431 });
2432 });
2433 });
2434 let btn_liste_new = $('<button/>').addClass("button-primary").html(_x('Add', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2435 __showMaskList(null);
2436 });
2437 this.div_liste.html($('<div/>').css('text-align', 'right').css('margin-bottom','10px').append(btn_liste_empty).append(isPremium()?'':' '+sprintf(/* translators: 1: max possible lists amount 2: link to premium */__('Max. %1$d list. Unlimited with %2$s', 'event-tickets-with-ticket-scanner'), myAjax._max.lists, getLabelPremiumOnly())+' ').append(btn_liste_new));
2438 this.div_liste.append(tplace);
2439
2440 __renderTabelleListen();
2441
2442 let additionalColumn = {customerName:'',customerCompany:'',redeemAmount:'',confirmedCount:''};
2443 if (_getOptions_isActivatedByKey('displayAdminAreaColumnConfirmedCount')) {
2444 additionalColumn.confirmedCount = '<th>'+_x('Confirmed Count', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2445 }
2446 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingName')) {
2447 additionalColumn.customerName = '<th>'+_x('Customer', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2448 }
2449 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingCompany')) {
2450 additionalColumn.customerCompany = '<th>'+_x('Company', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2451 }
2452 if (_getOptions_isActivatedByKey('displayAdminAreaColumnRedeemedInfo')) {
2453 additionalColumn.redeemAmount = '<th>'+_x('Redeem Amount', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2454 }
2455
2456 tabelle_codes.html('<thead><tr><th style="text-align:left;padding-left:10px;"><input type="checkbox" data-id="checkAll"></th><th>&nbsp;</th><th align="left">'
2457 +_x('Ticket', 'label', 'event-tickets-with-ticket-scanner')+'</th>'+additionalColumn.customerName+additionalColumn.customerCompany+'<th align="left">'
2458 +_x('List', 'label', 'event-tickets-with-ticket-scanner')+'</th><th align="left">'
2459 +_x('Created', 'label', 'event-tickets-with-ticket-scanner')+'</th>'+additionalColumn.confirmedCount+'<th align="left">'
2460 +_x('Redeemed', 'label', 'event-tickets-with-ticket-scanner')+'</th>'+additionalColumn.redeemAmount+'<th>'
2461 +_x('OrderId', 'label', 'event-tickets-with-ticket-scanner')+'</th><th>CVV</th><th>'
2462 +_x('Status', 'label', 'event-tickets-with-ticket-scanner')+'</th><th></th></tr></thead><tfoot><th colspan="10" style="text-align:left;font-weight:normal;padding-left:0;padding-bottom:0;"></th></tfoot>');
2463 tabelle_codes.find('input[data-id="checkAll"]').on('click', (e)=> {
2464 let isChecked = $(e.currentTarget).prop('checked');
2465 let found = false;
2466 tabelle_codes.find('input[data-type="select-checkbox"]').each((i,v)=>{
2467 $(v).prop('checked', isChecked);
2468 found = true;
2469 });
2470 if (isChecked && found) {
2471 //drop_codes_bulk.prop("disabled", false);
2472 } else {
2473 //drop_codes_bulk.prop("disabled", true);
2474 }
2475 });
2476 let btn_codes_new = $('<button/>').addClass("button-primary").html(_x('Add', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2477 if (!isPremium() && tabelle_codes_datatable.page.info().recordsTotal > myAjax._max.codes_total) {
2478 alert(__("You reached maximum amount of tickets. You need to delete tickets before you can add more new tickets or buy the premium version to have unlimited tickets.", 'event-tickets-with-ticket-scanner'));
2479 } else {
2480 LAYOUT.renderAddCodes();
2481 }
2482 });
2483 let btn_codes_empty = $('<button/>').addClass("button-secondary").html(__('Empty table', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2484 LAYOUT.renderYesNo(__('Empty table', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: name of ticket table */__('Do you want to empty the "%s" table? All data will be lost.', 'event-tickets-with-ticket-scanner'), _x("Event Tickets", 'title', 'event-tickets-with-ticket-scanner')), ()=>{
2485 LAYOUT.renderYesNo(__('Empty table - last chance', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: name of ticket table */__('Are you sure? You will not be able to restore the data, except you have a backup of your database. All data will be lost.', 'event-tickets-with-ticket-scanner'), _x("Event Tickets", 'title', 'event-tickets-with-ticket-scanner')), ()=>{
2486 _makeGet('emptyTableCodes', null, ()=>{
2487 tabelle_codes_datatable.ajax.reload();
2488 });
2489 });
2490 });
2491 });
2492 let btn_codes_reload = $('<button/>').addClass("button-secondary").html(__('Refresh table', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2493 LAYOUT.renderSpinnerShow();
2494 tabelle_codes_datatable.ajax.reload();
2495 window.setTimeout(()=>{LAYOUT.renderSpinnerHide();}, 1500);
2496 });
2497 let btn_codes_export = $('<button/>').addClass("button-secondary").html(_x('Export tickets', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2498 //let url = _requestURL('exportTableCodes', null);
2499 //window.open(url, "_blank");
2500 //console.log(tabelle_codes_datatable.page.info());
2501 __showMaskExport(tabelle_codes_datatable.page.info().recordsTotal);
2502 });
2503 let drop_codes_bulk = $('<select data-id="bulk-code-action" />')
2504 .html('<option value="">'+_x('Bulk Action', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2505 //.append('<option value="delete">'+_x('Delete', 'label', 'event-tickets-with-ticket-scanner')+'</option>');
2506 for (var key in BulkActions.codes) {
2507 let entry = BulkActions.codes[key];
2508 drop_codes_bulk.append('<option value="'+key+'">'+entry.label+'</option>');
2509 }
2510 drop_codes_bulk.on('change', ()=>{
2511 let val = drop_codes_bulk.val();
2512 if (val !== "") {
2513 let selectedElems = [];
2514 tabelle_codes.find('input[data-type="select-checkbox"]').each((i,v)=>{
2515 if ($(v).prop("checked")) selectedElems.push(v);
2516 });
2517 if (selectedElems.length) {
2518 let fkt = null;
2519 if (typeof BulkActions.codes[val] == "function") {
2520 fkt = BulkActions.codes[val];
2521 } else {
2522 fkt = BulkActions.codes[val].fkt;
2523 }
2524 fkt && fkt(selectedElems, tabelle_codes_datatable);
2525 }
2526 }
2527 drop_codes_bulk.val('');
2528 });
2529 let drop_search = $('<select data-id="filter_type" />');
2530 drop_search.append('<option value="">'+_x('Default search filter', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2531 drop_search.append('<option value="LIST:">'+_x('Filter for list id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2532 drop_search.append('<option value="ORDERID:">'+_x('Filter for order id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2533 drop_search.append('<option value="CVV:">'+_x('Filter for cvv value', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2534 drop_search.append('<option value="STATUS:">'+_x('Filter for status (1:active, 0:inactive, 2:stolen)', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2535 drop_search.append('<option value="REDEEMED:">'+_x('Filter for redeemed status (0:not redeemed yet, 1:redeemed)', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2536 drop_search.append('<option value="USERID:">'+_x('Filter for registered user id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2537 drop_search.append('<option value="CUSTOMER:">'+_x('Filter for customer name in billing first and last name', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2538 drop_search.append('<option value="PRODUCTID:">'+_x('Filter for product id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2539 drop_search.append('<option value="DAYPERTICKET:">'+_x('Filter for choosen date (enter YYYY-MM-DD)', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2540 drop_search.on("change", e=>{
2541 let old_search = tabelle_codes_datatable.search().trim();
2542 let search = drop_search.val();
2543 if (old_search && old_search.length > 0) {
2544 search = old_search + " & " + search;
2545 }
2546 tabelle_codes_datatable.search(search);
2547 });
2548 this.div_codes
2549 .html($('<div/>').css('text-align', 'right').css('margin-bottom','10px')
2550 .append(drop_codes_bulk)
2551 .append(drop_search)
2552 .append(btn_codes_export)
2553 .append(btn_codes_empty)
2554 .append(btn_codes_reload)
2555 .append(isPremium()?'':' '+sprintf(/* translators: 1: max amount tickets 2: premium link */__('Max. %1$d tickets. Unlimited with %2$s', 'event-tickets-with-ticket-scanner'), myAjax._max.codes_total, getLabelPremiumOnly())+' ').append(btn_codes_new));
2556 this.div_codes.append(tabelle_codes);
2557
2558 let table_columns = [
2559 {"data":null,"orderable":false,"defaultContent":'', "render":function (data, type, row) {
2560 return '<input type="checkbox" data-type="select-checkbox" data-key="'+data.id+'" data-code="'+data.code+'">';
2561 }},
2562 {"data":null,"className":'details-control',"orderable":false,"defaultContent":''},
2563 {"data":"code_display", "orderable":true, "render":(data,type,row)=>{
2564 return destroy_tags(data);
2565 }},
2566 {"data":"list_name", "orderable":true, "render":(data,type,row)=>{
2567 return destroy_tags(data);
2568 }},
2569 {"data":"time", "className":"dt-center", "orderable":true,
2570 "render":function (data, type, row) {
2571 return '<span style="display:none;">'+data+'</span>'+DateTime2Text(data);
2572 }
2573 },
2574 {"data":"redeemed", "orderable":true, "className":"dt-center", "render":function(data, type, row) {
2575 if (data == 1) {
2576 return 'yes';
2577 } else {
2578 return '';
2579 }
2580 }},
2581 {"data":"order_id", "className":"dt-right", "orderable":true},
2582 {"data":null, "orderable":false, "className":"dt-center", "render":function(data, type, row){
2583 return data.cvv === "" ? "" : '****';
2584 }},
2585 {"data":null, "orderable":true, "className":"dt-center", "render":function(data, type, row){
2586 let _stat = '';
2587 if (data.meta != "") {
2588 let metaObj = JSON.parse(data.meta);
2589 if (typeof metaObj['used'] !== "undefined") {
2590 if (metaObj.used.reg_request !== "") _stat = '/used';
2591 }
2592 }
2593 if (data.aktiv === "2") return '<span style="color:red;">'+_x('stolen', 'label', 'event-tickets-with-ticket-scanner')+'</span>'+_stat;
2594 return data.aktiv === "1" ? '<span style="color:green;">'+__('active', 'event-tickets-with-ticket-scanner')+'</span>'+_stat : '<span style="color:grey;">'+_x('is inactiv', 'label', 'event-tickets-with-ticket-scanner')+'</span>'+_stat;
2595 }},
2596 {"data":null,"orderable":false,"defaultContent":'',"className":"buttons dt-right",
2597 "render": function ( data, type, row ) {
2598 return '<button class="button-secondary" data-type="edit">'+_x('Edit', 'label', 'event-tickets-with-ticket-scanner')+'</button> <button class="button-secondary" data-type="delete">'+_x('Delete', 'label', 'event-tickets-with-ticket-scanner')+'</button>';
2599 }
2600 }
2601 ];
2602 let addition_column_offset = 0;
2603 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingName')) {
2604 addition_column_offset++;
2605 table_columns.splice(3, 0, {
2606 "data":"_customer_name","orderable":false
2607 });
2608 }
2609 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingCompany')) {
2610 addition_column_offset++;
2611 table_columns.splice(3, 0, {
2612 "data":"_customer_company","orderable":false
2613 });
2614 }
2615 if (_getOptions_isActivatedByKey('displayAdminAreaColumnConfirmedCount')) {
2616 addition_column_offset++;
2617 table_columns.splice(4+addition_column_offset, 0, {
2618 "data":null,"orderable":false,"defaultContent":'',"className":"dt-center",
2619 "render":function(data,type,row) {
2620 let ret = 0;
2621 let metaObj = getCodeObjectMeta(data);
2622 if(!metaObj) return ret;
2623 if (typeof metaObj.confirmedCount != "undefined") {
2624 ret = metaObj.confirmedCount;
2625 }
2626 return ret;
2627 }
2628 });
2629 }
2630 if (_getOptions_isActivatedByKey('displayAdminAreaColumnRedeemedInfo')) {
2631 addition_column_offset++;
2632 table_columns.splice(5+addition_column_offset, 0, {
2633 "data":null,"orderable":false,"defaultContent":'',"className":"dt-center",
2634 "render":function(data,type,row) {
2635 let ret = '';
2636 if (row._max_redeem_amount > 0) {
2637 ret = row._redeemed_counter+'/'+row._max_redeem_amount;
2638 } else {
2639 ret = row._redeemed_counter+'/unlimited';
2640 }
2641 return ret;
2642 }
2643 });
2644 }
2645
2646 tabelle_codes_datatable = $(this.div_codes).find('#'+id_codes).DataTable({
2647 "responsive": true,
2648 "search": {
2649 "search": typeof PARAS.code !== "undefined" ? encodeURIComponent(PARAS.code.trim()) : ''
2650 },
2651 footerCallback: function(row, data, start, end, display) {
2652 let data_anser = tabelle_codes_datatable.ajax.json();
2653 let text = sprintf(/* translators: 1: amount tickets 2: total amount tickets */__('Redeemed tickets: %1$d (filtered) of %2$d (total redeemed tickets)', 'event-tickets-with-ticket-scanner'), data_anser.redeemedRecordsFiltered, data_anser.redeemedRecordsTotal);
2654 var api = this.api();
2655 $(api.column(1).footer()).html(text);
2656 //$(api.tables().footer()).html(text);
2657 },
2658 "processing": true,
2659 "serverSide": true,
2660 "stateSave": false,
2661 "ajax": {
2662 "url": _requestURL('getCodes'),
2663 "type": 'GET'
2664 },
2665 "order": [[ 4 + (additionalColumn.customerName != "" ? 1 : 0), "desc" ]],
2666 "columns": table_columns,
2667 "initComplete": function () {
2668 LAYOUT.renderSpinnerHide();
2669 },
2670 "autowidth":true
2671 });
2672 tabelle_codes.on('click', 'button[data-type="edit"]', function (e) {
2673 let data = tabelle_codes_datatable.row( $(this).parents('tr') ).data();
2674 __showMaskCode(data);
2675 });
2676 tabelle_codes.on('click', 'button[data-type="delete"]', function (e) {
2677 let data = tabelle_codes_datatable.row( $(this).parents('tr') ).data();
2678 LAYOUT.renderYesNo(_x('Do you want to delete?', 'title', 'event-tickets-with-ticket-scanner'), __('Are you sure, you want to delete this ticket?', 'event-tickets-with-ticket-scanner')+'<br><br><b>'+data.code+'</b>', ()=>{
2679 let _data = {'id':data.id};
2680 _makePost('removeCode', _data, result=>{
2681 tabelle_codes_datatable.ajax.reload();
2682 });
2683 });
2684 });
2685 $('#'+id_codes+' tbody').on('click', 'td.details-control', function () {
2686 function ___format(d) {
2687 let metaObj = [];
2688 if (d.meta) {
2689 metaObj = JSON.parse(d.meta);
2690 }
2691 let div = $('<div/>');
2692
2693 // hole das aktuelle Metaobj
2694 function __getData(_codeObj) {
2695 div.html(_getSpinnerHTML());
2696 _makeGet('getMetaOfCode',{'code':d.code}, dataMeta=>{
2697 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
2698 _codeObj.meta = JSON.stringify(dataMeta);
2699 updateCodeObject(d, _codeObj);
2700 metaObj = getCodeObjectMeta(d);
2701 }
2702
2703 div.html("");
2704 d.meta = JSON.stringify(dataMeta);
2705 d.metaObj = dataMeta;
2706
2707 let btn_grp = $('<div/>').addClass("btn-group").appendTo(div);
2708 $('<button>').html(_x('Display QR with ticket number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2709 let id = 'qrcode_'+d.code+'_'+time();
2710 let content = _x('This QR image contains', 'label', 'event-tickets-with-ticket-scanner')+':<br><b>'+d.code+'</b><br><br><div id="'+id+'" style="text-align:center;"></div><script>jQuery("#'+id+'").qrcode("'+d.code+'");</script>';
2711 LAYOUT.renderInfoBox(_x('QR with ticket number', 'title', 'event-tickets-with-ticket-scanner'), content);
2712 });
2713 if (d.metaObj.wc_ticket.is_ticket && typeof d.metaObj.wc_ticket._public_ticket_id !== "undefined" && d.metaObj.wc_ticket._public_ticket_id != "") {
2714 $('<button>').html(_x('Display QR with PUBLIC ticket number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2715 let id = 'qrcode_'+d.code+'_'+time();
2716 let content = _x('This QR image contains', 'label', 'event-tickets-with-ticket-scanner')+':<br><b>'+d.metaObj.wc_ticket._public_ticket_id+'</b><br>'+_x('Can be used with the ticket scanner', 'label', 'event-tickets-with-ticket-scanner')+'<br><br><div id="'+id+'" style="text-align:center;"></div><script>jQuery("#'+id+'").qrcode("'+d.metaObj.wc_ticket._public_ticket_id+'");</script>';
2717 LAYOUT.renderInfoBox(_x('QR with ticket number', 'title', 'event-tickets-with-ticket-scanner'), content);
2718 });
2719 }
2720 if (d.metaObj.wc_ticket.is_ticket && typeof d.metaObj.wc_ticket._qr_content !== "undefined" && d.metaObj.wc_ticket._qr_content != "") {
2721 $('<button>').html(_x('Display QR with your own QR content', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2722 let id = 'qrcode_own_'+d.code+'_'+time();
2723 let content = _x('This QR image contains', 'label', 'event-tickets-with-ticket-scanner')+':<br><b>'+d.metaObj.wc_ticket._qr_content+'</b><br>'+_x('Can be used with the ticket scanner', 'label', 'event-tickets-with-ticket-scanner')+'<br><br><div id="'+id+'" style="text-align:center;"></div><script>jQuery("#'+id+'").qrcode("'+d.metaObj.wc_ticket._qr_content+'");</script>';
2724 LAYOUT.renderInfoBox(_x('QR with ticket number', 'title', 'event-tickets-with-ticket-scanner'), content);
2725 });
2726 }
2727 if (typeof d.metaObj._QR != "undefined" && typeof d.metaObj._QR.directURL != "undefined" && d.metaObj._QR.directURL != "") {
2728 $('<button>').html(_x('Display QR with URL', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2729 let id = 'qrcode_url_'+d.code+'_'+time();
2730 let qr_content = d.metaObj._QR.directURL;
2731 let content = _x('This QR image contains', 'label', 'event-tickets-with-ticket-scanner')+':<br><b>'+qr_content+'</b><br><br><div id="'+id+'" style="text-align:center;"></div><script>jQuery("#'+id+'").qrcode("'+qr_content+'");</script>';
2732 LAYOUT.renderInfoBox(_x('QR with URL and code', 'title', 'event-tickets-with-ticket-scanner'), content);
2733 });
2734 }
2735 div.append('<div/>');
2736
2737 // male die Inhalte
2738 div.append('#'+d.id+'<br><b>'+_x('Created', 'label', 'event-tickets-with-ticket-scanner')+':</b> '+DateTime2Text(d.time)+' ('+d.time+')<br><b>'+__('Ticket number', 'event-tickets-with-ticket-scanner')+':</b> '+d.code+'<br><b>'+__('Ticket display number', 'event-tickets-with-ticket-scanner')+':</b> '+d.code_display+'<br><b>'+_x('Code Verification Value (CVV)', 'label', 'event-tickets-with-ticket-scanner')+':</b> '+(d.cvv == "" ? '-' : d.cvv)+'<br><b>'+_x('is active', 'event-tickets-with-ticket-scanner')+':</b> '+(parseInt(d.aktiv,10) === 1?'True':'False'));
2739 div.append(_displayCodeDetails(d, metaObj, tabelle_codes_datatable));
2740
2741 div.append('<h3>'+_x('WooCommerce Order', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
2742 if (!_getOptions_Versions_isActivatedByKey("is_wc_available")) {
2743 div.append($("<div>").css("color", "red").html(__("WooCommerce not found", 'event-tickets-with-ticket-scanner')));
2744 }
2745 div.append('<b>'+_x('OrderId', 'label', 'event-tickets-with-ticket-scanner')+':</b> ' + (parseInt(d.order_id) === 0 ? '-' : '#'+d.order_id+' <a target="_blank" href="post.php?post='+d.order_id+'&action=edit">'+_x('Show in WooCommerce Orders', 'label', 'event-tickets-with-ticket-scanner')+'</a>'));
2746 if (typeof metaObj['woocommerce'] !== "undefined") {
2747 if (metaObj.woocommerce.order_id !== 0) {
2748 div.append($("<div>").html('<b>'+_x('Order from', 'label', 'event-tickets-with-ticket-scanner')+':</b> ').append($('<span>').text(DateTime2Text(metaObj.woocommerce.creation_date)+' ('+metaObj.woocommerce.creation_date+')')));
2749 div.append($("<div>").html('<b>'+_x('Product Id', 'label', 'event-tickets-with-ticket-scanner')+':</b> ').append($('<span>').html(metaObj.woocommerce.product_id+' <a target="_blank" href="post.php?post='+encodeURIComponent(metaObj.woocommerce.product_id)+'&action=edit">'+_x('Show Product', 'label', 'event-tickets-with-ticket-scanner')+'</a>')));
2750 }
2751 }
2752 if (parseInt(d.order_id) > 0) {
2753 div.append($('<div style="margin-top:10px;">').html($('<button>').addClass("button-delete").html(_x('Delete WooCommerce order info for this ticket', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2754 LAYOUT.renderYesNo(_x('Remove order', 'title', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: ticket number */__('Do you really want to remove your order information of this ticket "%s"? This will also remove the ticket number from the order! For the PREMIUM PLUGIN: It will only remove it from the position of the order. If you have in one order more than one item with ticket number, then it will only remove the ticket number(s) from this item on the order. For the BASIC PLUGIN, it will remove all tickets from all items on this order. Click OK to proceed the removal.', 'event-tickets-with-ticket-scanner'), d.code_display), ()=>{
2755 _makeGet('removeWoocommerceOrderInfoFromCode', {'code':d.code}, _codeObj=>{
2756 //tabelle_codes_datatable.ajax.reload();
2757 __getData(_codeObj);
2758 });
2759 });
2760 })));
2761 }
2762
2763 div.append('<h4>'+__('WooCommerce ticket sale', 'event-tickets-with-ticket-scanner')+'</h4>');
2764 div.append(_displayWCETicket(d, tabelle_codes_datatable));
2765
2766 div.append('<h3>'+__('WooCommerce Purchase Restriction', 'event-tickets-with-ticket-scanner')+'</h3>');
2767 if (typeof metaObj['wc_rp'] !== "undefined") {
2768 if (metaObj.wc_rp.order_id !== 0) {
2769 div.append($("<div>").html('<b>'+_x('Used for Order ID', 'label', 'event-tickets-with-ticket-scanner')+':</b> ').append($('<span>').html('#'+metaObj.wc_rp.order_id+' <a target="_blank" href="post.php?post='+encodeURIComponent(metaObj.wc_rp.order_id)+'&action=edit">'+_x('Open WooCommerce Order', 'label', 'event-tickets-with-ticket-scanner')+'</a>')));
2770 div.append($("<div>").html('<b>'+_x('Order from', 'label', 'event-tickets-with-ticket-scanner')+':</b> ').append($('<span>').text(metaObj.wc_rp.creation_date)));
2771 div.append($("<div>").html('<b>'+_x('Product Id', 'label', 'event-tickets-with-ticket-scanner')+'s:</b> ').append($('<span>').html(metaObj.wc_rp.product_id+' <a target="_blank" href="post.php?post='+encodeURIComponent(metaObj.wc_rp.product_id)+'&action=edit">'+_x('Show Product', 'label', 'event-tickets-with-ticket-scanner')+'</a>')));
2772 div.append($('<div style="margin-top:10px;">').html($('<button>').addClass("button-delete").html(__('Remove purchase ticket information', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2773 LAYOUT.renderYesNo(__('Remove purchase ticket information', 'event-tickets-with-ticket-scanner'), sprintf(/* translators: %s: ticket nummer */__('Do you really want to remove the purchase ticket information from the order of this ticket "%s"? This will remove the also the ticket(s) from the order items! This ticket can then be reused for purchases. Click OK to proceed the removal.', 'event-tickets-with-ticket-scanner'), d.code_display), ()=>{
2774 _makeGet('removeWoocommerceRstrPurchaseInfoFromCode', {'code':d.code}, _codeObj=>{
2775 //tabelle_codes_datatable.ajax.reload();
2776 __getData(_codeObj);
2777 });
2778 });
2779 })));
2780 } else {
2781 div.append($("<div>").html('<b>'+_x('Used for Order ID', 'label', 'event-tickets-with-ticket-scanner')+':</b> -'));
2782 }
2783 }
2784
2785 div.append('<h3>'+_x('Registered user', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
2786 div.append(_displayRegisteredUserForCode(d, metaObj, tabelle_codes_datatable));
2787
2788 div.append('<h3>Redeem operations</h3>');
2789 div.append(_displayRedeemOperationsForCode(d, metaObj));
2790
2791 div.append('<h3>'+_x('IP list checked for this ticket', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
2792 if (isPremium()) {
2793 div.append(PREMIUM.displayTrackedIPsForCode(d.code));
2794 } else {
2795 div.append(getLabelPremiumOnly());
2796 }
2797
2798 if (isPremium() && PREMIUM.displayCodeDetailsAtEnd) div.append(PREMIUM.displayCodeDetailsAtEnd(d, tabelle_codes_datatable, metaObj));
2799
2800 div.append("<hr>");
2801 });
2802 }
2803 __getData();
2804 return div;
2805 }
2806
2807 var tr = $(this).closest('tr');
2808 var row = tabelle_codes_datatable.row( tr );
2809 if ( row.child.isShown() ) {
2810 // This row is already open - close it
2811 row.child.hide();
2812 tr.removeClass('shown');
2813 } else {
2814 // Open this row
2815 row.child( ___format(row.data()) ).show();
2816 tr.addClass('shown');
2817 }
2818 });
2819 cbf && cbf();
2820 }); // end getOptions
2821 } // render layout
2822
2823 renderInfoBox(title, content, displayPlain) {
2824 let _options = {
2825 title: title,
2826 modal: true,
2827 minWidth: 400,
2828 minHeight: 200,
2829 buttons: [{text:_x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
2830 click: function() {
2831 $(this).dialog("close");
2832 $(this).html("");
2833 }}]
2834 };
2835 let dlg = $('<div/>');
2836 if (displayPlain) {
2837 dlg.text(content);
2838 } else {
2839 dlg.html(content);
2840 }
2841 dlg.dialog(_options);
2842 return dlg;
2843 }
2844 renderSpinnerShow() {
2845 this.div_spinner.css("display", "block");
2846 }
2847 renderSpinnerHide() {
2848 this.div_spinner.css("display", "none");
2849 }
2850 renderFatalError(content) {
2851 return LAYOUT.renderInfoBox(_x('Error', 'title', 'event-tickets-with-ticket-scanner'), content);
2852 }
2853 renderYesNo(title, content, cbfYes, cbfNo) {
2854 let _options = {
2855 title: title,
2856 modal: true,
2857 minWidth: 400,
2858 minHeight: 200,
2859 buttons: [{text:_x('Yes', 'label', 'event-tickets-with-ticket-scanner'), click:function(){
2860 $(this).dialog("close");
2861 $(this).html("");
2862 cbfYes && cbfYes(dlg);
2863 }},{text:_x('No', 'label', 'event-tickets-with-ticket-scanner'), click:function(){
2864 $(this).dialog("close");
2865 $(this).html("");
2866 cbfNo && cbfNo();
2867 }}]
2868 };
2869 let dlg = $('<div/>').html(content);
2870 dlg.dialog(_options);
2871 return dlg;
2872 }
2873 }
2874
2875 function _displayCodeDetails(codeObj, metaObj, tabelle) {
2876 let div = $('<div/>');
2877 function __getData(_codeObj) {
2878 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
2879 updateCodeObject(codeObj, _codeObj);
2880 }
2881
2882 div.html("");
2883 if (codeObj.meta !== "") {
2884 let metaObj = getCodeObjectMeta(codeObj);
2885 if (typeof metaObj.confirmedCount !== "undefined") {
2886 div.append($('<div/>').html('<b>Confirmed count:</b> '+metaObj.confirmedCount));
2887 if (metaObj.confirmedCount > 0 && metaObj.validation) {
2888 if (metaObj.validation.first_success != "") {
2889 div.append($('<div/>').html('<b>First successful validation at:</b> '+metaObj.validation.first_success));
2890 div.append($('<div/>').html('<b>First successful validation IP:</b> '+metaObj.validation.first_ip));
2891 }
2892 if (metaObj.validation.last_success != "" && metaObj.validation.last_success != metaObj.validation.first_success) {
2893 div.append($('<div/>').html('<b>Last successful validation at:</b> '+metaObj.validation.last_success));
2894 div.append($('<div/>').html('<b>Last successful validation IP:</b> '+metaObj.validation.last_ip));
2895 }
2896 }
2897 }
2898 let btngrp = $('<div style="margin-top:10px;">');
2899 if (typeof metaObj.used !== "undefined") {
2900 div.append("<h3>Code marked as used</h3>");
2901 if (metaObj.used.reg_request !== "") {
2902 div.append($("<div>").html("<b>Request from:</b> ").append($('<span>').text(DateTime2Text(metaObj.used.reg_request)+' ('+metaObj.used.reg_request+')')));
2903 div.append($("<div>").html("<b>Request by wordpress user:</b> ").append($('<span>').text(metaObj.used.reg_userid)));
2904 if (metaObj.used._reg_username) div.append($("<div>").html("<b>Request by wordpress user:</b> ").append($('<span>').text(metaObj.used._reg_username)));
2905 div.append($("<div>").html("<b>Request from IP:</b> ").append($('<span>').text(metaObj.used.reg_ip)));
2906
2907 btngrp.append($('<button/>').addClass("button-delete").html('Delete ticket used information').on("click", function(){
2908 LAYOUT.renderYesNo('Remove usage information', 'Do you really want to remove the usage information of this ticket "'+codeObj.code_display+'"? This will also reset the "Confirmed count" to 0.', ()=>{
2909 _makeGet('removeUsedInformationFromCode', {'code':codeObj.code}, _codeObj=>{
2910 //tabelle.ajax.reload();
2911 __getData(_codeObj);
2912 });
2913 });
2914 }));
2915 } else {
2916 div.append("Not used - still available");
2917 }
2918
2919 btngrp.append($('<button/>').addClass("button-edit").html('Edit wordpress user information').on("click", function(){
2920 // display eingabe maske für userid
2921 function __showMask(){
2922 let _options = {
2923 title: 'Edit requested wordpress user',
2924 modal: true,
2925 minWidth: 400,
2926 minHeight: 200,
2927 buttons: [
2928 {
2929 id: 'okBtn',
2930 text: "Ok",
2931 click: function() {
2932 ___submitForm();
2933 }
2934 },
2935 {
2936 text: "Cancel",
2937 click: function() {
2938 $( this ).dialog( "close" );
2939 $( this ).html('');
2940 }
2941 }
2942 ]
2943 };
2944 let dlg = $('<div />');
2945 let form = $('<form />').appendTo(dlg);
2946
2947 let elem_userid = $('<input type="number" min="0" value="'+metaObj.used.reg_userid+'" />');
2948 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
2949 .html('Requested wordpress userid<br>')
2950 .append(elem_userid)
2951 .appendTo(form);
2952
2953 dlg.append('<p>Changes will trigger the webhook, if activated.<br>The IP will be updated too. The requested date will only be changed, if it was not set already.</p>');
2954 dlg.dialog(_options);
2955
2956 form.on("submit", function(event) {
2957 event.preventDefault();
2958 ___submitForm();
2959 });
2960 function ___submitForm() {
2961 let reg_userid = intval(elem_userid.val().trim());
2962 dlg.html(_getSpinnerHTML());
2963 let _data = {"reg_userid":reg_userid};
2964 form[0].reset();
2965 _data.code = codeObj.code;
2966 $('#okBtn').remove();
2967 _makeGet('editUseridForUsedInformationFromCode', _data, _codeObj=>{
2968 //tabelle.ajax.reload();
2969 __getData(_codeObj);
2970 closeDialog(dlg);
2971 }, function() {
2972 closeDialog(dlg);
2973 });
2974 }
2975 } // ende __showMask
2976 __showMask();
2977 })); // end button-edit
2978 }
2979 div.append(btngrp);
2980
2981 if (isPremium()) div.append(PREMIUM.displayCodeDetails(codeObj, tabelle, metaObj));
2982 } // endif codeObj.meta !== ""
2983 }
2984 __getData();
2985 return div;
2986 }
2987
2988 function _displayWCETicket(codeObj, tabelle) {
2989 let div = $('<div/>');
2990 function __getData(_codeObj) {
2991 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
2992 updateCodeObject(codeObj, _codeObj);
2993 }
2994
2995 div.html("");
2996 let metaObj = getCodeObjectMeta(codeObj);
2997 if(metaObj) {
2998 if (typeof metaObj.wc_ticket != "undefined" && typeof metaObj.wc_ticket.day_per_ticket != "undefined") {
2999 div.append($('<div>').html('<b>Date per Ticket (choosen by customer):</b> '+metaObj.wc_ticket.day_per_ticket +" ").append(
3000 $("<button>").html("Edit").on("click", ()=>{
3001
3002 let _options = {
3003 title: 'Edit Ticket Date',
3004 modal: true,
3005 minWidth: 400,
3006 minHeight: 200,
3007 buttons: [
3008 {
3009 id: 'okBtn',
3010 text: "Ok",
3011 click: function() {
3012 ___submitForm();
3013 }
3014 },
3015 {
3016 text: "Cancel",
3017 click: function() {
3018 $( this ).dialog( "close" );
3019 $( this ).html('');
3020 }
3021 }
3022 ]
3023 };
3024 let dlg = $('<div />');
3025 let form = $('<form />').appendTo(dlg);
3026
3027 let elem_input = $('<input type="date" value="'+metaObj.wc_ticket.day_per_ticket+'" />');
3028 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3029 .html('Date per Ticket (yyyy-mm-dd).<br><b>Very important to not break the date format and syntax, if you want to change the date!</b><br>')
3030 .append(elem_input)
3031 .appendTo(form);
3032
3033 dlg.dialog(_options);
3034
3035 form.on("submit", function(event) {
3036 event.preventDefault();
3037 ___submitForm();
3038 });
3039 function ___submitForm() {
3040 let v = elem_input.val().trim();
3041 dlg.html(_getSpinnerHTML());
3042 let _data = {"value":v, "key":'wc_ticket.day_per_ticket'};
3043 form[0].reset();
3044 _data.code = codeObj.code;
3045 $('#okBtn').remove();
3046 _makeGet('editTicketMetaEntry', _data, _codeObj=>{
3047 //tabelle.ajax.reload();
3048 __getData(_codeObj);
3049 closeDialog(dlg);
3050 }, function() {
3051 closeDialog(dlg);
3052 });
3053 }
3054 })
3055 ));
3056 }
3057 if (typeof metaObj.wc_ticket != "undefined" && typeof metaObj.wc_ticket.name_per_ticket != "undefined") {
3058 div.append($('<div>').html('<b>Name per Ticket (product detail setting):</b> '+metaObj.wc_ticket.name_per_ticket +" ").append(
3059 $("<button>").html("Edit").on("click", ()=>{
3060
3061 let _options = {
3062 title: 'Edit Ticket Name',
3063 modal: true,
3064 minWidth: 400,
3065 minHeight: 200,
3066 buttons: [
3067 {
3068 id: 'okBtn',
3069 text: "Ok",
3070 click: function() {
3071 ___submitForm();
3072 }
3073 },
3074 {
3075 text: "Cancel",
3076 click: function() {
3077 $( this ).dialog( "close" );
3078 $( this ).html('');
3079 }
3080 }
3081 ]
3082 };
3083 let dlg = $('<div />');
3084 let form = $('<form />').appendTo(dlg);
3085
3086 let elem_input = $('<input type="text" value="'+metaObj.wc_ticket.name_per_ticket+'" />');
3087 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3088 .html('Name per Ticket<br>')
3089 .append(elem_input)
3090 .appendTo(form);
3091
3092 dlg.dialog(_options);
3093
3094 form.on("submit", function(event) {
3095 event.preventDefault();
3096 ___submitForm();
3097 });
3098 function ___submitForm() {
3099 let v = elem_input.val().trim();
3100 dlg.html(_getSpinnerHTML());
3101 let _data = {"value":v, "key":'wc_ticket.name_per_ticket'};
3102 form[0].reset();
3103 _data.code = codeObj.code;
3104 $('#okBtn').remove();
3105 _makeGet('editTicketMetaEntry', _data, _codeObj=>{
3106 //tabelle.ajax.reload();
3107 __getData(_codeObj);
3108 closeDialog(dlg);
3109 }, function() {
3110 closeDialog(dlg);
3111 });
3112 }
3113 })
3114 ));
3115 }
3116 if (typeof metaObj.wc_ticket != "undefined" && typeof metaObj.wc_ticket.value_per_ticket != "undefined") {
3117 div.append($('<div>').html('<b>Value per Ticket (product detail setting):</b> '+metaObj.wc_ticket.value_per_ticket +" ").append(
3118 $("<button>").html("Edit").on("click", ()=>{
3119
3120 let _options = {
3121 title: 'Edit Ticket Value',
3122 modal: true,
3123 minWidth: 400,
3124 minHeight: 200,
3125 buttons: [
3126 {
3127 id: 'okBtn',
3128 text: "Ok",
3129 click: function() {
3130 ___submitForm();
3131 }
3132 },
3133 {
3134 text: "Cancel",
3135 click: function() {
3136 $( this ).dialog( "close" );
3137 $( this ).html('');
3138 }
3139 }
3140 ]
3141 };
3142 let dlg = $('<div />');
3143 let form = $('<form />').appendTo(dlg);
3144
3145 let elem_input = $('<input type="text" value="'+metaObj.wc_ticket.value_per_ticket+'" />');
3146 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3147 .html('Value per Ticket<br>')
3148 .append(elem_input)
3149 .appendTo(form);
3150
3151 dlg.dialog(_options);
3152
3153 form.on("submit", function(event) {
3154 event.preventDefault();
3155 ___submitForm();
3156 });
3157 function ___submitForm() {
3158 let v = elem_input.val().trim();
3159 dlg.html(_getSpinnerHTML());
3160 let _data = {"value":v, "key":'wc_ticket.value_per_ticket'};
3161 form[0].reset();
3162 _data.code = codeObj.code;
3163 $('#okBtn').remove();
3164 _makeGet('editTicketMetaEntry', _data, _codeObj=>{
3165 //tabelle.ajax.reload();
3166 __getData(_codeObj);
3167 closeDialog(dlg);
3168 }, function() {
3169 closeDialog(dlg);
3170 });
3171 }
3172 })
3173 ));
3174 }
3175 if (typeof metaObj['woocommerce'] !== "undefined" && metaObj.woocommerce.order_id !== 0 && typeof metaObj.wc_ticket !== "undefined") {
3176 if (metaObj.wc_ticket.set_by_admin > 0) {
3177 div.append($("<div>").html("<b>Ticket set by admin user:</b> ").append($('<span>').text(metaObj.wc_ticket._set_by_admin_username+' ('+metaObj.wc_ticket.set_by_admin+') '+metaObj.wc_ticket.set_by_admin_date)));
3178 }
3179 if (metaObj.wc_ticket.redeemed_date != '') {
3180 div.append($("<div>").html("<b>Redeemed at:</b> ").append($('<span>').text(DateTime2Text(metaObj.wc_ticket.redeemed_date)+' ('+metaObj.wc_ticket.redeemed_date+')')));
3181 div.append($("<div>").html("<b>Redeemed by wordpress userid:</b> ").append($('<span>').text(metaObj.wc_ticket.userid)));
3182 if (metaObj.wc_ticket._username) div.append($("<div>").html("<b>Redeemed by wordpress user:</b> ").append($('<span>').text(metaObj.wc_ticket._username)));
3183 div.append($("<div>").html("<b>IP while redeemed:</b> ").append($('<span>').text(metaObj.wc_ticket.ip)));
3184 if (metaObj.wc_ticket.redeemed_by_admin > 0) {
3185 div.append($("<div>").html("<b>Redeemed by admin user:</b> ").append($('<span>').text(metaObj.wc_ticket._redeemed_by_admin_username+' ('+metaObj.wc_ticket.redeemed_by_admin+')')));
3186 }
3187 }
3188 if (metaObj.wc_ticket.is_ticket == 1) {
3189 let _max_redeem_amount = typeof metaObj.wc_ticket._max_redeem_amount !== "undefined" ? metaObj.wc_ticket._max_redeem_amount : 1;
3190 $("<div>").html("<b>Ticket number: </b>"+codeObj.code_display).appendTo(div);
3191 $("<div>").html("<b>Public Ticket number: </b>"+metaObj.wc_ticket._public_ticket_id).appendTo(div);
3192 if (typeof metaObj.wc_ticket.stats_redeemed !== "undefined") {
3193 $("<div>").html("<b>Redeem usage: </b>"+metaObj.wc_ticket.stats_redeemed.length + ' of ' + (_max_redeem_amount == 0 ? 'unlimited' : _max_redeem_amount)).appendTo(div);
3194 }
3195 $("<div>").html('<b>Ticket Page:</b> <a target="_blank" href="'+metaObj.wc_ticket._url+'">Open Ticket Detail Page</a>').appendTo(div);
3196 $("<div>").html('<b>Ticket Page Testmode:</b> <a target="_blank" href="'+metaObj.wc_ticket._url+'?testDesigner=1">Open Ticket Detail Page with template test code</a>').appendTo(div);
3197 $("<div>").html('<b>Ticket PDF:</b> <a target="_blank" href="'+metaObj.wc_ticket._url+'?pdf">Open Ticket PDF</a>').appendTo(div);
3198 $("<div>").html('<b>Ticket PDF Testmode:</b> <a target="_blank" href="'+metaObj.wc_ticket._url+'?pdf&testDesigner=1">Open Ticket PDF with template test code</a>').appendTo(div);
3199 $("<div>").html('<b>Order Ticket Page:</b> <a target="_blank" href="'+metaObj.wc_ticket._order_page_url+'">Open Order Ticket Page</a>').appendTo(div);
3200 $("<div>").html('<b>Order PDF:</b> <a target="_blank" href="'+metaObj.wc_ticket._order_url+'">Open Order Ticket PDF</a>').appendTo(div);
3201 }
3202
3203 let btngrp = $('<div style="margin-top:10px;">').appendTo(div);
3204 if (metaObj.wc_ticket.is_ticket == 1) {
3205 $('<button>').html("Download PDF").appendTo(btngrp).on("click", ()=>{
3206 _downloadFile('downloadPDFTicket', {'code':codeObj.code}, "eventticket_"+codeObj.code+".pdf");
3207 return false;
3208 });
3209 $('<button>').html("Download Ticket Badge").appendTo(btngrp).on("click", ()=>{
3210 _downloadFile('downloadPDFTicketBadge', {'code':codeObj.code}, "eventticket_badge_"+codeObj.code+".pdf");
3211 return false;
3212 });
3213 $('<button>').html("Display QR with URL to PDF").appendTo(btngrp).on("click", e=>{
3214 let id = 'qrcode_'+codeObj.code+'_'+time();
3215 let content = 'This QR image contains:<br><b>'+codeObj.code+'</b><br><br><div id="'+id+'" style="text-align:center;"></div><script>jQuery("#'+id+'").qrcode("'+metaObj.wc_ticket._url+'?pdf");</script>';
3216 LAYOUT.renderInfoBox('QR with URL to PDF', content);
3217 });
3218 }
3219 if (metaObj.wc_ticket.is_ticket == 0) {
3220 $('<button>').html("Set as ticket sale").on("click", ()=>{
3221 LAYOUT.renderYesNo('Set as a ticket', 'Do you want to set this purchased ticket number as a ticket sale?', ()=>{
3222 _makeGet('setWoocommerceTicketForCode', {'code':codeObj.code}, _codeObj=>{
3223 __getData(_codeObj);
3224 });
3225 });
3226 }).appendTo(btngrp);
3227 }
3228 let btn_redeem = $('<button>').addClass("button-delete").html('Redeem ticket').on("click", ()=>{
3229 let reg_userid = (metaObj.user && metaObj.user.reg_userid) ? metaObj.user.reg_userid : 0;
3230 LAYOUT.renderYesNo('Redeem ticket', 'Do you really want to redeem the ticket number "'+codeObj.code_display+'"? Click OK to redeem the ticket.', ()=>{
3231 let userid = prompt('Optional. You can enter a userid you redeem the ticket for', reg_userid);
3232 _makeGet('redeemWoocommerceTicketForCode', {'code':codeObj.code, 'userid':userid}, _codeObj=>{
3233 __getData(_codeObj);
3234 });
3235 });
3236 }).appendTo(btngrp);
3237 let _max_redeem_amount = typeof metaObj.wc_ticket._max_redeem_amount !== "undefined" ? metaObj.wc_ticket._max_redeem_amount : 1;
3238 if (metaObj.wc_ticket.is_ticket == 0 || _max_redeem_amount == 0 || metaObj.wc_ticket.stats_redeemed.length >= _max_redeem_amount) {
3239 btn_redeem.attr("disabled", true);
3240 }
3241
3242 let btn_unredeem = $('<button>').addClass("button-delete").html('Delete redeem information').on("click", ()=>{
3243 LAYOUT.renderYesNo('Remove ticket information', 'Do you really want to remove the information that the ticket number "'+codeObj.code_display+'" is redeemed? Click OK to un-redeem the ticket and allow your customer to use the ticket again.', ()=>{
3244 _makeGet('removeRedeemWoocommerceTicketForCode', {'code':codeObj.code}, _codeObj=>{
3245 __getData(_codeObj);
3246 });
3247 });
3248 }).appendTo(btngrp);
3249 if (metaObj.wc_ticket.is_ticket == 0 || metaObj.wc_ticket.redeemed_date == "") {
3250 btn_unredeem.attr("disabled", true);
3251 }
3252 if (metaObj.wc_ticket.is_ticket == 1 && metaObj.wc_ticket.redeemed_date == "") {
3253 $('<button>').addClass("button-delete").html("Unset Ticket").on("click", ()=>{
3254 LAYOUT.renderYesNo('Remove ticket', 'Do you really want to remove the ticket info from this ticket number? The WooCommerce sale will be set and you need to remove it manually.', ()=>{
3255 _makeGet('removeWoocommerceTicketForCode', {'code':codeObj.code}, _codeObj=>{
3256 __getData(_codeObj);
3257 });
3258 });
3259 }).appendTo(btngrp);
3260 }
3261 }
3262 }
3263 }
3264 __getData();
3265 return div;
3266 }
3267
3268 function _displayRedeemOperationsForCode(d, metaObj) {
3269 let div = $('<div/>');
3270 if (typeof metaObj.wc_ticket.stats_redeemed !== "undefined") {
3271 if (metaObj.wc_ticket.stats_redeemed.length > 0) {
3272 let t = $('<table>').appendTo(div);
3273 t.html('<tr><th>#</th><th>Date</th><th>IP</th><th>By admin</th><th>User ID</th></tr>').appendTo(t);
3274 metaObj.wc_ticket.stats_redeemed.forEach((v,idx)=>{
3275 let tr = $('<tr>').appendTo(t);
3276 $('<td>').html('#'+(idx+1)).appendTo(tr);
3277 $('<td>').html(DateTime2Text(v.redeemed_date)+' ('+v.redeemed_date+')').appendTo(tr);
3278 $('<td>').html(v.ip).appendTo(tr);
3279 $('<td>').html(v.redeemed_by_admin == 1 ? 'Yes' : 'No').appendTo(tr);
3280 $('<td>').html(v.userid).appendTo(tr);
3281 });
3282 } else {
3283 div.html("no redeem operations yet");
3284 }
3285 }
3286 return div;
3287 }
3288
3289 function _displayRegisteredUserForCode(codeObj, metaObj, tabelle) {
3290 let div = $('<div/>');
3291 function __getData(_codeObj) {
3292 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
3293 updateCodeObject(codeObj, _codeObj);
3294 }
3295 div.html("");
3296 let btngrp = $('<div style="margin-top:10px;">');
3297 if (typeof codeObj.meta !== "undefined" && codeObj.meta !== "") {
3298 let metaObj = getCodeObjectMeta(codeObj);
3299 if (metaObj.user.reg_request !== "") {
3300 div.append($("<div>").html("<b>Register value:</b> ").append($('<span>').text(metaObj.user.value)));
3301 div.append($("<div>").html("<b>Register by wordpress userid:</b> ").append($('<span>').text(metaObj.user.reg_userid)));
3302 if (metaObj.user._reg_username) div.append($("<div>").html("<b>Register by wordpress user:</b> ").append($('<span>').text(metaObj.user._reg_username)));
3303 div.append($("<div>").html("<b>Request from:</b> ").append($('<span>').text(metaObj.user.reg_request)));
3304 div.append($("<div>").html("<b>Request from IP:</b> ").append($('<span>').text(metaObj.user.reg_ip)));
3305 btngrp.append($('<button/>').addClass("button-delete").html('Delete registered user information').on("click", function(){
3306 LAYOUT.renderYesNo('Remove register user value', 'Do you really want to remove the registered user value of this ticket "'+codeObj.code_display+'"?', ()=>{
3307 // sende delete user from code operation zum server
3308 div.html(_getSpinnerHTML());
3309 _makeGet('removeUserRegistrationFromCode', {'code':codeObj.code}, _codeObj=>{
3310 //tabelle.ajax.reload();
3311 __getData(_codeObj);
3312 });
3313 });
3314 }));
3315 } else {
3316 div.append("No registration to this ticket done");
3317 }
3318
3319 btngrp.append($('<button/>').addClass("button-edit").html('Edit registered user information').on("click", function(){
3320 // display eingabe maske für value und userid
3321 function __showMask(){
3322 let _options = {
3323 title: 'Edit registered user',
3324 modal: true,
3325 minWidth: 400,
3326 minHeight: 200,
3327 buttons: [
3328 {
3329 id: 'okBtn',
3330 text: "Ok",
3331 click: function() {
3332 ___submitForm();
3333 }
3334 },
3335 {
3336 text: "Cancel",
3337 click: function() {
3338 $( this ).dialog( "close" );
3339 $( this ).html('');
3340 }
3341 }
3342 ]
3343 };
3344 let dlg = $('<div />');
3345 let form = $('<form />').appendTo(dlg);
3346
3347 let elem_value = $('<input type="text" value="'+metaObj.user.value+'" />');
3348 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3349 .html('Registered value<br>')
3350 .append(elem_value)
3351 //.append('<br><i>If CVV is set, then your user will be asked to enter also the CVV to check the serial code.</i>')
3352 .appendTo(form);
3353 let elem_userid = $('<input type="number" min="0" value="'+metaObj.user.reg_userid+'" />');
3354 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3355 .html('Registered wordpress userid<br>')
3356 .append(elem_userid)
3357 .appendTo(form);
3358
3359 dlg.append('<p>Changes will trigger the webhook, if activated.<br>The IP will updated too. The registered date will only be changed, if it was not set already.</p>');
3360 dlg.dialog(_options);
3361
3362 form.on("submit", function(event) {
3363 event.preventDefault();
3364 ___submitForm();
3365 });
3366 function ___submitForm() {
3367 let reg_userid = intval(elem_userid.val().trim());
3368 let reg_value = elem_value.val().trim();
3369 dlg.html(_getSpinnerHTML());
3370 let _data = {"value":reg_value, "reg_userid":reg_userid};
3371 form[0].reset();
3372 _data.code = codeObj.code;
3373 $('#okBtn').remove();
3374 _makeGet('editUseridForUserRegistrationFromCode', _data, _codeObj=>{
3375 //tabelle.ajax.reload();
3376 __getData(_codeObj);
3377 closeDialog(dlg);
3378 }, function() {
3379 closeDialog(dlg);
3380 });
3381 }
3382 } // ende __showMask
3383 __showMask();
3384 })); // end button-edit
3385 div.append(btngrp);
3386 if (isPremium()) div.append(PREMIUM.displayRegisteredUserForCode(codeObj, tabelle, metaObj));
3387 } // endif typeof codeObj.meta !== "undefined" && codeObj.meta !== ""
3388 }
3389 __getData();
3390 return div;
3391 }
3392
3393 function addStyleCode(content) {
3394 let c = document.createElement('style');
3395 c.innerHTML = content;
3396 document.getElementsByTagName("head")[0].appendChild(c);
3397 }
3398 function addStyleTag(url, id, onloadfkt, attrListe, loadLatest) {
3399 var script = document.createElement('link');
3400 script.type = 'text/css';
3401 script.rel = "stylesheet";
3402 let myId = id;
3403 if (!myId) myId = url;
3404 if (document.getElementById(id) && document.getElementById(id).src === url) {
3405 onloadfkt && onloadfkt();
3406 return; // prevent re-adding the same tag
3407 }
3408 script.id = id;
3409 if (attrListe) for(var attr in attrListe) script.setAttribute(attr, attrListe[attr]);
3410 script.href = url;
3411 if (loadLatest) script.href += '?t='+new Date().getTime();
3412 if (typeof onloadfkt !== "undefined") script.onload = onloadfkt;
3413 document.getElementsByTagName("head")[0].appendChild(script);
3414 }
3415 function addScriptCode(content, id) {
3416 if (typeof system.DYNJS_CACHE.scriptCodeElements === "undefined") {
3417 system.DYNJS_CACHE.scriptCodeElements = {};
3418 }
3419 let c;
3420 if (id && typeof system.DYNJS_CACHE.scriptCodeElements[id] !== "undefined") {
3421 c = system.DYNJS_CACHE.scriptCodeElements[id];
3422 document.getElementsByTagName("head")[0].removeChild(c);
3423 } else {
3424 c = document.createElement('script');
3425 }
3426 c.innerHTML = content;
3427 if (id) {
3428 system.DYNJS_CACHE.scriptCodeElements[id] = c;
3429 }
3430 document.getElementsByTagName("head")[0].appendChild(c);
3431 }
3432 function addScriptTag(url, id, onloadfkt, attrListe, loadLatest) {
3433 var head = document.getElementsByTagName("head")[0];
3434 var script = document.createElement('script');
3435 script.type = 'text/javascript';
3436 let myId = id;
3437 if (!myId) myId = url;
3438 if (document.getElementById(id) && document.getElementById(id).src === url) {
3439 onloadfkt && onloadfkt();
3440 return; // prevent re-adding the same tag
3441 }
3442 script.id = id;
3443 if (attrListe) for(var attr in attrListe) script.setAttribute(attr, attrListe[attr]);
3444 script.src = url;
3445 if (loadLatest) script.src += '?t='+new Date().getTime();
3446 if (typeof onloadfkt !== "undefined") script.onload = onloadfkt;
3447 head.appendChild(script);
3448 }
3449
3450 function getLabelPremiumOnly() {
3451 return '[<a href="https://vollstart.com/event-tickets-with-ticket-scanner/">PREMIUM ONLY</a>]';
3452 }
3453
3454 function _getSpinnerHTML() {
3455 return '<span class="lds-dual-ring"></span>';
3456 }
3457
3458 function _loadingJSDatatables(cbf) {
3459 let loaded = {};
3460 addStyleCode('table.dataTable tr.shown td.details-control {background: url('+myAjax._plugin_home_url+'/img/details_close.png) no-repeat center center;}td.details-control {background: url('+myAjax._plugin_home_url+'/img/details_open.png) no-repeat center center;cursor: pointer;}');
3461 addStyleTag(myAjax._plugin_home_url+'/3rd/datatables.min.css', 'jquery_dataTables', ()=>{
3462 loaded['1'] = true;
3463 if (loaded['2']) {
3464 cbf && cbf();
3465 }
3466 }, {'crossorigin':"anonymous"});
3467 addScriptTag(myAjax._plugin_home_url+"/3rd/datatables.min.js", 'jquery_dataTables', ()=>{
3468 loaded['2'] = true;
3469 if (loaded['1']) {
3470 cbf && cbf();
3471 }
3472 }, {'crossorigin':"anonymous", "charset":"utf8"});
3473 }
3474
3475 function isPremium() {
3476 return myAjax._isPremium == "1" || myAjax._isPremium === true;
3477 }
3478
3479 var BulkActions = {
3480 'codes': {
3481 'delete': {
3482 "label": _x('Delete', 'label', 'event-tickets-with-ticket-scanner'),
3483 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3484 LAYOUT.renderYesNo('Delete all selected tickets?', 'Are you sure, you want to delete all selected tickets?<br><br>'+selectedElems.length+' tickets will be deleted.', ()=>{
3485 let _data = {'ids':[]};
3486 selectedElems.forEach(v=>{
3487 _data.ids.push($(v).attr("data-key"));
3488 });
3489 _makePost('removeCodes', _data, result=>{
3490 tabelle_codes_datatable.ajax.reload();
3491 });
3492 });
3493 }
3494 },
3495 'remove_marked_used': {
3496 "label": _x("Remove marked as used", 'option', 'event-tickets-with-ticket-scanner'),
3497 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3498 LAYOUT.renderYesNo('Remove marked used?', 'Are you sure, you want to remove the used marked from all selected tickets?<br><br>'+selectedElems.length+' tickets will be changed.', ()=>{
3499 let _data = {'ids':[], 'codes':[]};
3500 selectedElems.forEach(v=>{
3501 _data.ids.push($(v).attr("data-key"));
3502 _data.codes.push($(v).attr("data-code"));
3503 });
3504 _makePost('removeUsedInformationFromCodeBulk', _data, result=>{
3505 tabelle_codes_datatable.ajax.reload();
3506 });
3507 });
3508 }
3509 },
3510 'remove_ticket_redeemed': {
3511 "label": _x("Delete Redeem Information", 'option', 'event-tickets-with-ticket-scanner'),
3512 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3513 LAYOUT.renderYesNo('Delete the redeem information?', 'Are you sure, you want to remove the the information about the redeem operation of the ticket?<br><br>'+selectedElems.length+' tickets will be changed.', ()=>{
3514 let _data = {'ids':[], 'codes':[]};
3515 selectedElems.forEach(v=>{
3516 _data.ids.push($(v).attr("data-key"));
3517 _data.codes.push($(v).attr("data-code"));
3518 });
3519 _makePost('removeRedeemWoocommerceTicketForCodeBulk', _data, result=>{
3520 tabelle_codes_datatable.ajax.reload();
3521 });
3522 });
3523 }
3524 },
3525 'generate_pdf': {
3526 "label": _x("Generate ticket PDF", 'option', 'event-tickets-with-ticket-scanner'),
3527 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3528 LAYOUT.renderYesNo('Generate the ticket PDF?', 'Are you sure, you want to generate the ticket PDFs for the selected tickets? This can take a while an could timeout the server.<br><br>'+selectedElems.length+' tickets will be added in one PDF.', ()=>{
3529 let _data = {'ids':[], 'codes':[]};
3530 selectedElems.forEach(v=>{
3531 _data.ids.push($(v).attr("data-key"));
3532 _data.codes.push($(v).attr("data-code"));
3533 });
3534 _downloadFile('generateOnePDFForTicketsBulk', _data, "tickets_merged.pdf");
3535 });
3536 }
3537 },
3538 'generate_badge': {
3539 "label": _x("Generate badge ticket", 'option', 'event-tickets-with-ticket-scanner'),
3540 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3541 LAYOUT.renderYesNo('Generate the ticket badge PDF?', 'Are you sure, you want to generate the ticket badge PDFs for the selected tickets? This can take a while an could timeout the server.<br><br>'+selectedElems.length+' badges will be added in one PDF.', ()=>{
3542 let _data = {'ids':[], 'codes':[]};
3543 selectedElems.forEach(v=>{
3544 _data.ids.push($(v).attr("data-key"));
3545 _data.codes.push($(v).attr("data-code"));
3546 });
3547 _downloadFile('generateOnePDFForBadgesBulk', _data, "ticketbadges_merged.pdf");
3548 });
3549 }
3550 },
3551 'move_to_list':{
3552 "label": _x("Move to ticket list", 'option', 'event-tickets-with-ticket-scanner'),
3553 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3554 let content = $('<div>');
3555 let div_code_list = _createDivInput(_x('Assign selected tickets to this ticket list', 'label', 'event-tickets-with-ticket-scanner')).appendTo(content);
3556 let input_code_list = $('<select><option value="0">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</select></select>').appendTo(div_code_list);
3557 DATA_LISTS.forEach(v=>{
3558 input_code_list.append('<option value="'+v.id+'">'+v.name+'</option>');
3559 });
3560 content.append("<br>");
3561 LAYOUT.renderYesNo('Move ticket(s) to ticket list', content, ()=>{
3562 let _data = {'ids':[], 'codes':[], 'list_id':input_code_list.val()};
3563 selectedElems.forEach(v=>{
3564 _data.ids.push($(v).attr("data-key"));
3565 _data.codes.push($(v).attr("data-code"));
3566 });
3567 _makePost('assignTicketListToTicketsBulk', _data, result=>{
3568 tabelle_codes_datatable.ajax.reload();
3569 });
3570 });
3571 }
3572 }
3573 }
3574 }
3575
3576 function addTabCSS() {
3577 $('<style>')
3578 .prop('type', 'text/css')
3579 .html(`
3580 .tabs {
3581 width: 100%;
3582 display: block;
3583 }
3584 .tab-nav {
3585 list-style: none;
3586 padding: 0;
3587 margin: 0;
3588 display: flex;
3589 border-bottom: 1px solid #ccc;
3590 }
3591 .tab-nav li {
3592 margin: 0;
3593 }
3594 .tab-nav a {
3595 display: block;
3596 padding: 10px 20px;
3597 text-decoration: none;
3598 color: #333;
3599 border: 1px solid #ccc;
3600 border-bottom: none;
3601 background: #f9f9f9;
3602 margin-right: 5px;
3603 border-radius: 5px 5px 0 0;
3604 }
3605 .tab-nav a.active {
3606 background: #fff;
3607 border-bottom: 1px solid #fff;
3608 font-weight: bold;
3609 }
3610 .tab-content {
3611 display: none;
3612 padding: 20px;
3613 border: 1px solid #ccc;
3614 border-radius: 0 5px 5px 5px;
3615 background: #fff;
3616 }
3617 `)
3618 .appendTo('head');
3619 }
3620
3621 function getHelperFunktions() {
3622 return {
3623 _getSpinnerHTML:_getSpinnerHTML,
3624 _makePost:_makePost,
3625 _makeGet:_makeGet,
3626 _downloadFile:_downloadFile,
3627 _requestURL:_requestURL,
3628 _getLAYOUT:function(){ return LAYOUT;},
3629 _getDIV:function(){ return DIV;},
3630 _BulkActions:BulkActions,
3631 _closeDialog:closeDialog,
3632 _OPTIONS:function(){ return OPTIONS;},
3633 _updateCodeObject:updateCodeObject,
3634 _getCodeObjectMeta:getCodeObjectMeta,
3635 _DateTime2Text:DateTime2Text,
3636 _compareVersions:compareVersions,
3637 _getBackButtonDiv:getBackButtonDiv
3638 };
3639 }
3640
3641 function refreshNoncePeriodically() {
3642 // check if the last check of nonce is older than 4 minutes
3643 // do a ping to get the new nonce
3644 setInterval(()=>{
3645 let last_check = DATA.last_nonce_check;
3646 if (last_check == null || last_check == "") {
3647 last_check = 0;
3648 }
3649 let now = new Date().getTime();
3650 if (now - last_check > 240000) {
3651 _makeGet('ping', [], data=>{
3652 });
3653 }
3654 }, 60000);
3655 }
3656
3657 function init() {
3658 addStyleCode('.lds-dual-ring {display:inline-block;width:64px;height:64px;}.lds-dual-ring:after {content:" ";display:block;width:46px;height:46px;margin:1px;border-radius:50%;border:5px solid #fff;border-color:#2e74b5 transparent #2e74b5 transparent;animation:lds-dual-ring 0.6s linear infinite;}@keyframes lds-dual-ring {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}');
3659 addStyleTag(myAjax._plugin_home_url+'/css/styles_backend.css');
3660
3661 addScriptTag(myAjax._plugin_home_url+'/3rd/ace/ace.js');
3662
3663 addTabCSS();
3664
3665 DIV = $('#'+myAjax.divId);
3666 DIV.html(_getSpinnerHTML());
3667 LAYOUT = new Layout();
3668 function _init() {
3669 _loadingJSDatatables(function() {
3670 if (typeof PARAS.display !== "undefined" && PARAS.display == 'options') {
3671 _displayOptionsArea();
3672 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'support') {
3673 _displaySupportInfoArea();
3674 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'authtokens') {
3675 _displayAuthTokensArea();
3676 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'faq') {
3677 _displayFAQArea();
3678 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'seatingplan') {
3679 _displaySeatingplanArea();
3680 } else {
3681 LAYOUT.renderAdminPageLayout();
3682 }
3683 });
3684 }
3685
3686 if (isPremium() && myAjax._premJS !== "") {
3687 addScriptTag(myAjax._premJS, null, function() {
3688 PREMIUM = new sasoEventticketsPremium(myAjax, getHelperFunktions());
3689 _init();
3690 });
3691 } else {
3692 _init();
3693 }
3694 $('#wpfooter').css('display', 'none');
3695 refreshNoncePeriodically();
3696 }
3697 if (!doNotInit) init();
3698 return {
3699 init: init,
3700 form_fields_serial_format:_form_fields_serial_format
3701 };
3702
3703 }
3704 if (typeof Ajax_sasoEventtickets !== "undefined") {
3705 window.sasoEventtickets_backend = sasoEventtickets(Ajax_sasoEventtickets);
3706 }