PluginProbe ʕ •ᴥ•ʔ
Event Tickets with Ticket Scanner / 2.7.3
Event Tickets with Ticket Scanner v2.7.3
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 1 year ago css 1 year ago img 1 year ago languages 1 year ago ticket 1 year ago vendors 1 year ago SASO_EVENTTICKETS.php 1 year ago backend.js 1 year ago changelog.txt 1 year ago db.php 1 year ago index.php 1 year ago init_file.php 1 year ago js_seatingplan.js 1 year ago order_details.js 1 year ago readme.txt 1 year ago saso-eventtickets-validator.js 1 year ago sasoEventtickets_AdminSettings.php 1 year ago sasoEventtickets_Authtoken.php 1 year ago sasoEventtickets_Base.php 1 year ago sasoEventtickets_Core.php 1 year ago sasoEventtickets_Frontend.php 1 year ago sasoEventtickets_Messenger.php 1 year ago sasoEventtickets_Options.php 1 year ago sasoEventtickets_PDF.php 1 year ago sasoEventtickets_Ticket.php 1 year ago sasoEventtickets_TicketBadge.php 1 year ago sasoEventtickets_TicketDesigner.php 1 year ago sasoEventtickets_TicketQR.php 1 year ago ticket_events.js 1 year ago ticket_scanner.js 1 year ago validator.js 1 year ago wc_backend.js 1 year ago wc_frontend.js 1 year ago woocommerce-hooks.php 1 year ago
backend.js
3688 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>Javascript QR code scanner:</b> https://github.com/nimiq/qr-scanner - '+label_version+': 1.4.2</li>');
789 DIV.append('<li><b>Javascript Datatable:</b> https://datatables.net/ - '+label_version+': 1.10.21</li>');
790 DIV.append('<li><b>Javascript Raphael:</b> http://raphaeljs.com/ - '+label_version+': 2.3.0</li>');
791 DIV.append('<li><b>Javascript Ace Editor</b></li>');
792 DIV.append('<li><b>html5-qrcode:</b> https://github.com/mebjas/html5-qrcode/ - '+label_version+': 2.3.8</li>');
793
794 DIV.append('<h3 style="margin-bottom:10px;">Options</h3>');
795 // liste alle optionen mit wert auf
796 data.forEach(v=>{
797 if (v.type != 'heading' && v.key != "serial") {
798 if (v.additional && v.additional.doNotRender && v.additional.doNotRender === 1) {}
799 else {
800 let value = v.value;
801 let def = '';
802 if (value == '') {
803 def = ' (DEFAULT used)';
804 value = v.default;
805 }
806 text = document.createTextNode(value);
807 DIV.append(`<b>${v.key}${def}:</b> `).append(text).append(`${newline}`);
808 }
809 }
810 });
811
812 DIV.append('<h3 style="margin-bottom:0;">All available Options</h3>');
813 let list_elem = $('<div>').appendTo(DIV);
814 data.forEach(v=>{
815 if (v.type != 'heading' && v.key != "serial" && v.type != "desc") {
816 if (v.additional && v.additional.doNotRender && v.additional.doNotRender === 1) {}
817 else {
818 list_elem.append('-');
819 list_elem.append(v.label);
820 if (v.desc != "") {
821 list_elem.append(`${newline}`).append(v.desc);
822 }
823 list_elem.append(`${newline}`);
824 list_elem.append(`${newline}`);
825 }
826 } else {
827 if (v.type == 'heading') {
828 list_elem.append(`${newline}`);
829 list_elem.append('== '+v.label+' ==');
830 if (v.desc != "") {
831 //list_elem.append(`${newline}`).append(v.desc);
832 }
833 list_elem.append(`${newline}`);
834 }
835 }
836 });
837
838 // helper buttons
839 $('<button/>').css("margin-top", "30px").addClass("sngmbh_btn-delete").html(_x("Repair tables", 'label', 'event-tickets-with-ticket-scanner')).appendTo(DIV).on("click", ()=>{
840 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=>{
841 dlg.html(_getSpinnerHTML());
842 dlg.dialog({
843 title:_x('Repaired', 'title', 'event-tickets-with-ticket-scanner'), modal:true, dialogClass: "no-close",
844 close: function(event, ui){ abort=true; },
845 buttons: [
846 {
847 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
848 click: function() {
849 $( this ).dialog( _x('Close', 'label', 'event-tickets-with-ticket-scanner') );
850 $( this ).html('');
851 }
852 }
853 ]
854 });
855 _makePost('repairTables', {}, result=>{
856 speakOutLoud(result, true);
857 dlg.html(result);
858 });
859 });
860 });
861
862 function __renderTabelleErrorLogs() {
863 div_tabelle.html(_getSpinnerHTML());
864 let table_id = myAjax.divPrefix+'_tabelle_errorlogs';
865 let tabelle = $('<table/>').attr("id", table_id);
866 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>');
867 div_tabelle.html(tabelle);
868
869 let table = $('#'+table_id);
870 $(table).DataTable().clear().destroy();
871 tabelle_errorlogs_datatable = $(table).DataTable({
872 "responsive": true,
873 "searching": true,
874 "ordering": true,
875 "processing": true,
876 "serverSide": true,
877 "stateSave": false,
878 "pageLength":50,
879 "ajax": {
880 url: _requestURL('getErrorLogs'),
881 type: 'POST',
882 },
883 "order": [[ 1, "desc" ]],
884 "columns":[
885 {"data":null,"className":'details-control',"orderable":false,"defaultContent":'', "width":10},
886 {"data":"time", "orderable":true, "width":80},
887 {"data":"exception_msg", "orderable":true},
888 {"data":"caller_name", "orderable":true},
889 ]
890 });
891 tabelle.css("width", "100%");
892 $('#'+table_id+' tbody').on('click', 'td.details-control', e=>{
893 var tr = $(e.target).parents('tr');
894 var row = tabelle_errorlogs_datatable.row( tr );
895 if ( row.child.isShown() ) {
896 // This row is already open - close it
897 row.child.hide();
898 tr.removeClass('shown');
899 } else {
900 // Open this row
901 let d = row.data();
902 row.child( "#"+d.id+'<br><pre>'+destroy_tags(d.msg)+'</pre>' ).show();
903 tr.addClass('shown');
904 }
905
906 });
907 }
908 __renderTabelleErrorLogs();
909
910 });
911 }
912
913 /**
914 * returns 0 if the versions are the same, 1 if version1 is greater, -1 if version2 is greater
915 */
916 function compareVersions(version1, version2) {
917 const v1 = version1.split('.').map(Number);
918 const v2 = version2.split('.').map(Number);
919
920 for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
921 const num1 = v1[i] || 0;
922 const num2 = v2[i] || 0;
923
924 if (num1 > num2) return 1;
925 if (num1 < num2) return -1;
926 }
927
928 /*
929 // Example usage:
930 const result = compareVersions('5.8.1', '5.8.2');
931 if (result > 0) {
932 console.log('Version 5.8.1 is greater than 5.8.2');
933 } else if (result < 0) {
934 console.log('Version 5.8.1 is less than 5.8.2');
935 } else {
936 console.log('Both versions are equal');
937 }
938 */
939
940 return 0;
941 }
942
943 function _displayOptionsArea() {
944 STATE = 'options';
945 DIV.html(_getSpinnerHTML());
946 getOptionsFromServer(reply=>{
947 let data = reply.options; // options values
948 let meta_tags_keys = reply.meta_tags_keys;
949
950 DIV.html(getBackButtonDiv());
951
952 // Create tabs
953 let tabs = $('<div class="tabs"/>');
954 let tabOptions = $('<div id="tab-options" class="tab-content"/>');
955
956 // Create tab navigation
957 let tabNav = $('<ul class="tab-nav"/>');
958 tabNav.append('<li><a href="#tab-options">Options</a></li>');
959 if (isPremium() && typeof PREMIUM.displayOptionsArea_Templates !== "undefined") {
960 tabNav.append(PREMIUM.displayOptionsArea_Tab);
961 }
962
963 tabs.append(tabNav);
964 tabs.append(tabOptions);
965 if (isPremium() && typeof PREMIUM.displayOptionsArea_Templates !== "undefined") {
966 //if (BASIC._compareVersions(prem_version, '1.5.0') < 0) { // check the version
967 // div_template.append("This is a premium feature is available with Premium Version 1.5.0. You need to update your premium plugin.");
968 //}
969 tabs.append(PREMIUM.displayOptionsArea_Templates(_getOptions_Versions_getByKey('premium')));
970 }
971 DIV.append(tabs);
972
973 // Populate Options tab
974 let div_options = $('<div/>');
975 let div_infos = $('<div style="padding-top: 50px;"/>');
976 let resetOption_div = $('<div class="reset_option_wrap" style="padding-top: 20px;"/>');
977 tabOptions.append(div_options);
978 tabOptions.append('<hr>');
979 tabOptions.append(resetOption_div);
980 $('<button class="button reset_btn_actn">').html(_x('Reset All Options', 'label', 'event-tickets-with-ticket-scanner'))
981 .on('click', ()=>{
982 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'), ()=>{
983 _makePost('resetOptions','', function(result) {
984 if(result){
985 _displayOptionsArea();
986 }
987 });
988 });
989 }).appendTo(resetOption_div);
990 tabOptions.append(div_infos);
991 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>');
992 meta_tags_keys.forEach(v=>{
993 let t = '<p><b>{'+v.key+'}</b>: '+v.label+'</p>';
994 div_infos.append(t);
995 });
996
997 //div_options.append('<h3>'+_x('Options', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
998 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>');
999 div_options.append(getUseFulVideosHTML());
1000
1001 let menu_band = $('<div style="padding-top:10px;padding-bottom:15px;">').appendTo(div_options);
1002 let menu_values = [];
1003 data.forEach(v=>{
1004 if (v.type === "heading") {
1005 menu_values.push(v);
1006 }
1007 });
1008 menu_values.sort((a,b)=>{
1009 if(a.label < b.label) { return -1; }
1010 if(a.label > b.label) { return 1; }
1011 return 0;
1012 });
1013 menu_values.forEach(v=>{
1014 $('<a href="#'+v.key+'" style="padding:5px;padding-left:0;margin-right:10px;">').html(v.label).appendTo(menu_band);
1015 });
1016 $('<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);
1017
1018 // Add jQuery for tab functionality
1019 $('.tab-nav a').on('click', function(e) {
1020 e.preventDefault();
1021 $('.tab-content').hide();
1022 $($(this).attr('href')).show();
1023 $('.tab-nav a').removeClass('active');
1024 $(this).addClass('active');
1025 });
1026
1027 // Show the first tab by default
1028 if (typeof PARAS.subdisplay !== "undefined" && PARAS.subdisplay == 'templates') {
1029 $('.tab-nav a').eq(1).click();
1030 } else {
1031 $('.tab-nav a:first').click();
1032 }
1033
1034 function __createTicketTemplateChooserBox(ticket_template, editor) {
1035 return $('<div style="width:250px;display:inline-block;margin-right:5px;text-align:center;">')
1036 .append('<img style="width:250px;" src="'+myAjax._plugin_home_url+'/img/ticket_templates/'+ticket_template.image_url+'">')
1037 .append("<br>Zero-Padding: "+(ticket_template.wcTicketPDFZeroMarginTest ? "Yes" : "No"))
1038 .append(", Size: ("+ticket_template.wcTicketSizeWidthTest+'x'+ticket_template.wcTicketSizeHeightTest+")")
1039 .append("<br>")
1040 .append($('<button class="button button-primary">').text(__('Load template','event-tickets-with-ticket-scanner')).on("click", ()=>{
1041 LAYOUT.renderYesNo(_x('Load Template Ticket Code', 'title', 'event-tickets-with-ticket-scanner'),
1042 __('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:'
1043 +'<br><b>wcTicketPDFZeroMarginTest</b>: '+(ticket_template.wcTicketPDFZeroMarginTest ? "Yes" : "No")
1044 +'<br><b>wcTicketPDFisRTLTest</b>: '+(ticket_template.wcTicketPDFisRTLTest ? "Yes" : "No")
1045 +'<br><b>wcTicketSizeWidthTest</b>: '+ticket_template.wcTicketSizeWidthTest
1046 +'<br><b>wcTicketSizeHeightTest</b>: '+ticket_template.wcTicketSizeHeightTest
1047 +'<br><b>wcTicketQRSizeTest</b>: '+ticket_template.wcTicketQRSizeTest
1048 +'</p>'
1049 , ()=>{
1050 editor.wcTicketDesignerTemplateTest_editor.setValue(ticket_template.wcTicketDesignerTemplateTest);
1051 $('input[data-key="wcTicketPDFZeroMarginTest"').prop("checked",ticket_template.wcTicketPDFZeroMarginTest).trigger("change");
1052 $('input[data-key="wcTicketPDFisRTLTest"').prop("checked",ticket_template.wcTicketPDFisRTLTest).trigger("change");
1053 $('input[data-key="wcTicketSizeWidthTest"').val(ticket_template.wcTicketSizeWidthTest).trigger("change");
1054 $('input[data-key="wcTicketSizeHeightTest"').val(ticket_template.wcTicketSizeHeightTest).trigger("change");
1055 $('input[data-key="wcTicketQRSizeTest"').val(ticket_template.wcTicketQRSizeTest).trigger("change");
1056 let value = editor.wcTicketDesignerTemplateTest_editor.getValue().trim();
1057 __saveOptionValue("wcTicketDesignerTemplateTest", value);
1058 editor.wcTicketDesignerTemplateTest_btn.prop("disabled", true);
1059
1060 });
1061 }));
1062 }
1063
1064 // render die input felder
1065 function __getOptionByKey(key) {
1066 for(let a=0;a<data.length;a++) {
1067 if (key == data[a].key) return data[a];
1068 }
1069 return null;
1070 }
1071 function __saveOptionValue(key, value, cbf, pcbf) {
1072 _makePost('changeOption', {'key':key, 'value':value},
1073 ()=>{
1074 cbf && cbf();
1075 if (key == "wcTicketDesignerTemplateTest") {
1076 $("#wcTicketDesignerTemplateTest_button_PDF").prop("disabled", false).text(__('Preview Test Template Code as PDF', 'event-tickets-with-ticket-scanner'));
1077 }
1078 }, null,
1079 ()=>{
1080 pcbf && pcbf();
1081 if (key == "wcTicketDesignerTemplateTest") {
1082 $("#wcTicketDesignerTemplateTest_button_PDF").prop("disabled", true).text(__('saving...', 'event-tickets-with-ticket-scanner'));
1083 }
1084 });
1085
1086 }
1087
1088 let editor = {}; // for ace editor
1089 data.forEach(v=>{
1090 if (typeof v.additional !== "undefined" && v.additional.doNotRender) return;
1091 if (v.type == "heading") {
1092 let desc = v.desc;
1093 if (typeof v._doc_video !== "undefined" && v._doc_video != "") {
1094 desc += ' <span class="dashicons dashicons-external"></span> <a href="'+v._doc_video+'" target="_blank">Video Help</a>';
1095 }
1096 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>':'');
1097 } else if (v.type =="desc") {
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('<div/>').css({"margin-bottom": "15px","margin-right": "15px"}).append('<b>'+v.label+'</b><br>'+desc+"<br>");
1103 } else {
1104 let elem_div = $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"});
1105 let elem_input = $('<input type="'+v.type+'">');
1106 elem_input.attr("placeholder", v.default);
1107 if (typeof v.additional !== "undefined" && typeof v.additional.disabled !== "undefined") {
1108 elem_input.attr("disabled", true);
1109 }
1110
1111 let cbf = null;
1112 let pcbf = null;
1113 let value = (""+v.value) !== "" ? (""+v.value).trim() : ""+v.default;
1114
1115 v.label = v.label + ' <span style="color:grey;">{'+v.key+'}</span>';
1116 if (typeof v._doc_video !== "undefined" && v._doc_video != "") {
1117 v.label += ' <span class="dashicons dashicons-external"></span> <a href="'+v._doc_video+'" target="_blank">Video Help</a>';
1118 }
1119
1120 switch (v.type) {
1121 case "editor":
1122 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());
1123 break;
1124 case "textarea":
1125 elem_input = $('<textarea>');
1126 elem_input.attr("placeholder", v.default);
1127 //elem_input.val(value);
1128 elem_input.val(v.value.trim());
1129 if (typeof v.additional !== "undefined" && typeof v.additional.rows !== "undefined") {
1130 elem_input.attr("rows", v.additional.rows);
1131 }
1132 break;
1133 case "checkbox":
1134 v.value = intval(v.value);
1135 elem_input.prop("checked",v.value === 1 ? true : false);
1136 elem_input.on("change", function(){
1137 _makePost('changeOption', {'key':v.key, 'value':elem_input[0].checked ? 1:0});
1138 });
1139 elem_div.html(elem_input).append(v.label).append(v.desc !== "" ? '<br><i>'+v.desc+'</i>':'');
1140 break;
1141 case "number":
1142 if (typeof v.additional.min !== "undefined") elem_input.attr("min", v.additional.min);
1143 break;
1144 case "dropdown":
1145 elem_input = $('<select>');
1146 if (v.additional.multiple) {
1147 elem_input.prop("multiple", true);
1148 }
1149 v.additional.values.forEach(_v=>{
1150 $('<option>').attr("value", _v.value).html(_v.label).appendTo(elem_input);
1151 });
1152 if (v.additional.multiple) {
1153 if (v.value.length == 0) {
1154 value = v.default;
1155 } else {
1156 value = v.value;
1157 }
1158 } else {
1159 if (value == "") value = 1;
1160 }
1161 elem_input.val(value);
1162 break;
1163 case "media":
1164 let image_info = $('<div>');
1165 let image = $('<image style="display:none;">');
1166 let image_btn_del = $('<button class="sngmbh_btn sngmbh_btn-delete" style="display:none;">').html(_x('Remove file', 'label', 'event-tickets-with-ticket-scanner'));
1167 image_btn_del.on('click', ()=>{
1168 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'), ()=>{
1169 elem_input.val("");
1170 elem_input.trigger("change");
1171 _renderMedia(0, v, image_info, image, image_btn_del);
1172 });
1173 });
1174 if (typeof v.additional == "undefined") v.additional = {};
1175 if (v.additional.max) {
1176 if (v.additional.max.width) {
1177 image.css("max-width", v.additional.max.width+'px');
1178 }
1179 if (v.additional.max.height) {
1180 image.css("max-height", v.additional.max.height+'px');
1181 }
1182 }
1183 elem_input.attr("type", "hidden");
1184 let image_btn_add = $('<button style="display:block;" />').addClass("button-primary")
1185 .html(v.additional.button)
1186 .on("click", ()=>{
1187 let is_multiple = typeof v.additional.is_multiple != "undefined" ? v.additional.is_multiple : false;
1188 let imgContainer = null;
1189 let type_filter = typeof v.additional.type_filter != "undefined" ? v.additional.type_filter : null;
1190 _openMediaChooser(elem_input, is_multiple, imgContainer, type_filter);
1191 });
1192 $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"})
1193 .html(v.label+'<br>')
1194 .append(image_btn_add)
1195 .append(v.desc !== "" ? '<i>'+v.desc+'</i>':'')
1196 .append(elem_input)
1197 .append(image_info)
1198 .append(image)
1199 .append(image_btn_del)
1200 .appendTo(elem_div);
1201 _renderMedia(value, v, image_info, image, image_btn_del);
1202 pcbf = function() {
1203 image_info.html(_getSpinnerHTML());
1204 image.css('display', 'none');
1205 }
1206 cbf = function () {
1207 let value = elem_input.val();
1208 _renderMedia(value, v, image_info, image, image_btn_del);
1209 }
1210 break;
1211 }
1212
1213 if (v.type != "checkbox") {
1214 if (v.type != "media") {
1215 elem_div.html(v.label+'<br>').append(elem_input);
1216 elem_div.append(v.desc !== "" ? '<br><i>'+v.desc+'</i>':'');
1217 }
1218 if (v.type != "number") {
1219 elem_input.css({"width":"90%"});
1220 }
1221 if (v.type != "dropdown" && v.type != "editor") {
1222 elem_input.attr("value",value);
1223 }
1224 if (v.type != "editor") {
1225 elem_input.on("change", ()=>{
1226 let value = elem_input.val();
1227 __saveOptionValue(v.key, value, cbf, pcbf);
1228 });
1229 }
1230 }
1231
1232 elem_input.attr("data-key", v.key);
1233
1234 if (v.key == "wcassignmentUseGlobalSerialFormatter") {
1235 let option = __getOptionByKey('wcassignmentUseGlobalSerialFormatter_values');
1236 let formatterValues = null;
1237 if (option.value != "") {
1238 try {
1239 formatterValues = JSON.parse(option.value);
1240 } catch (e) {
1241 //console.log(e);
1242 }
1243 }
1244 let extra_div = $('<div>').appendTo(elem_div).css("margin-top", "10px").css("margin-left", "50px").css("padding", "10px").css("border", "1px solid black");
1245 // render here den formatter
1246 let serialCodeFormatter = _form_fields_serial_format(extra_div);
1247 serialCodeFormatter.setNoNumberOptions();
1248 serialCodeFormatter.setFormatterValues(formatterValues);
1249 serialCodeFormatter.setCallbackHandle(_formatterValues=>{
1250 // speicher formatterValues
1251 _makePost('changeOption', {'key':'wcassignmentUseGlobalSerialFormatter_values', 'value':JSON.stringify(_formatterValues)});
1252 });
1253 serialCodeFormatter.render();
1254 }
1255
1256 if (v.key == "wcTicketDesignerTemplate") {
1257 $('<button class="button button-primary">').html("Show Default Template").on("click", e=>{
1258 LAYOUT.renderInfoBox(_x('Ticket Default Template', 'title', 'event-tickets-with-ticket-scanner'), $('<textarea style="width:100%;height:400px">').val(v.default));
1259 }).appendTo(div_options);
1260 }
1261
1262 if (v.type == "editor") {
1263 //https://ace.c9.io/#nav=howto
1264 let btn_group = $('<div>').prependTo(elem_div);
1265 editor[v.key+"_editor"] = null; // will be filled later
1266 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=>{
1267 let value = editor[v.key+"_editor"].getValue().trim();
1268 __saveOptionValue(v.key, value, cbf, pcbf);
1269 editor[v.key+"_btn"].prop("disabled", true);
1270 }).appendTo(btn_group);
1271 $('<button class="button button-danger">').html(_x('Copy Template Code To Live Code', 'title', 'event-tickets-with-ticket-scanner')).on("click", evt=>{
1272 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'), ()=>{
1273 let value = editor[v.key+"_editor"].getValue().trim();
1274 $('input[data-key="'+v.key.replace("Test", "")+'"').val(value).trigger("change");
1275 if (v.key == "wcTicketDesignerTemplateTest") {
1276 $('input[data-key="wcTicketPDFZeroMargin"').prop("checked",$('input[data-key="wcTicketPDFZeroMarginTest"').is(':checked')).trigger("change");
1277 $('input[data-key="wcTicketPDFisRTL"').prop("checked",$('input[data-key="wcTicketPDFisRTLTest"').is(':checked')).trigger("change");
1278 $('input[data-key="wcTicketSizeWidth"').val($('input[data-key="wcTicketSizeWidthTest"').val()).trigger("change");
1279 $('input[data-key="wcTicketSizeHeight"').val($('input[data-key="wcTicketSizeHeightTest"').val()).trigger("change");
1280 $('input[data-key="wcTicketQRSize"').val($('input[data-key="wcTicketQRSizeTest"').val()).trigger("change");
1281 }
1282 });
1283 }).appendTo(btn_group);
1284
1285 if (v.key == "wcTicketDesignerTemplateTest") {
1286 let ticket_test_chooser = $('<div>');
1287 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);
1288 let ticket_test_select = $('<select>').appendTo(ticket_test_chooser);
1289 let ticket_test_direct_input = $('<input type="text" style="width:180px;" placeholder="or enter a public ticket number">');
1290 // display the template thumbnails
1291 for(let a=0;a<reply.ticket_templates.length;a++) {
1292 let ticket_template = reply.ticket_templates[a];
1293 __createTicketTemplateChooserBox(ticket_template, editor).appendTo(ticket_template_chooser);
1294 }
1295
1296 if (OPTIONS.tickets_for_testing.length > 0) {
1297 let option_values = [];
1298 for(let a=0;a<OPTIONS.tickets_for_testing.length;a++) {
1299 let ticket = OPTIONS.tickets_for_testing[a];
1300 let metaObj = null;
1301 try {
1302 metaObj = JSON.parse(ticket.meta);
1303 } catch(e) {}
1304 if (metaObj != null) {
1305 option_values.push({t:ticket, m:metaObj});
1306 }
1307 }
1308 if (option_values.length > 0) {
1309 for(let a=0;a<option_values.length;a++) {
1310 let item = option_values[a];
1311 $('<option value="'+item.m.wc_ticket._public_ticket_id+'">')
1312 .text("Order Id: "+item.t.order_id+" - "+item.m.wc_ticket._public_ticket_id+" - "+item.t._PRODUCT_NAME+" (#"+item.m.woocommerce.product_id+")")
1313 .attr("data-url-pdf", item.m.wc_ticket._url)
1314 .appendTo(ticket_test_select);
1315 }
1316 ticket_test_direct_input.appendTo(ticket_test_chooser);
1317 $('<button class="button button-primary" id="wcTicketDesignerTemplateTest_button_PDF">')
1318 .html(__('Preview Test Template Code as PDF', 'event-tickets-with-ticket-scanner')).
1319 appendTo(ticket_test_chooser).on("click", ()=>{
1320 let ticket_url = ticket_test_select.find(":selected").attr("data-url-pdf");
1321 let v = ticket_test_direct_input.val().trim();
1322 if (v != "") {
1323 ticket_url = reply.infos.ticket.ticket_base_url + v; // myAjax.ticket_base_url
1324 }
1325 iframe.attr("src", ticket_url+'?pdf&testDesigner=1&t='+time()+'&nonce='+DATA.nonce);
1326 iframe
1327 .css("width", "80%")
1328 .css("height", "500px")
1329 .css("margin-top", "10px")
1330 .css("display", "block");
1331 });
1332 let iframe = $('<iframe style="display:none;">').appendTo(ticket_test_chooser);
1333 } else {
1334 $('<option value="">').text(__("ticket cannot be used. Public Ticket Id missing.",'event-tickets-with-ticket-scanner')).appendTo(ticket_test_select);
1335 }
1336 } else {
1337 $('<option value="">').text(__("no ticket for preview available", 'event-tickets-with-ticket-scanner')).appendTo(ticket_test_select);
1338 }
1339 ticket_test_chooser.appendTo(elem_div);
1340 }
1341 }
1342
1343 elem_div.appendTo(div_options);
1344 }
1345 });
1346 if (window.location.hash != "") {
1347 window.setTimeout(()=>{
1348 let h = window.location.hash;
1349 window.location.hash = "";
1350 window.location.hash = h;
1351 }, 250);
1352 }
1353 window.setTimeout(()=>{
1354 for(var k in editor) {
1355 if (k.substring(k.length -7) == "_editor") {
1356 editor[k] = ace.edit(k);
1357 //editor.wcTicketDesignerTemplateTest_editor.setTheme("ace/theme/monokai");
1358 editor[k].session.setMode("ace/mode/twig");
1359 editor[k].setShowPrintMargin(false);
1360 editor[k].commands.addCommand({name:'save', bindKey:{win:'Ctrl-S', mac:'Command-S'}, readOnly:false, exec:myEditor=>{
1361 myEditor.trigger("change");
1362 }});
1363 editor[k].session.on("change", delta=>{
1364 editor[k.replace("_editor", "_btn")].prop("disabled", false);
1365 });
1366 }
1367 }
1368 }, 250)
1369
1370 });
1371 }
1372
1373 function getSuffixFromFilename(filename) {
1374 let extension = filename.slice(filename.lastIndexOf('.') + 1);
1375 return extension;
1376 }
1377 function _renderMedia(mediaId, v, image_info, image, image_btn_del) {
1378 if (mediaId != "" && parseInt(mediaId) != 0) {
1379 _getMediaData(mediaId, data=>{
1380 let suffix = getSuffixFromFilename(data.url.replace(/^.*[\\\/]/,'')).toLowerCase();
1381 let info = suffix != "pdf" ? '('+data.meta.width+'x'+data.meta.height+')' : '';
1382 image_info.html('<b>'+_x('Title', 'title', 'event-tickets-with-ticket-scanner')+':</b> '+data.title+' '+info);
1383 if (v.additional.max && v.additional.msg_error_max) {
1384 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>');
1385 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>');
1386 }
1387 if (suffix != "pdf") {
1388 image.attr("src", data.url).css("display","block");
1389 }
1390 image_btn_del.css("display", "block");
1391 });
1392 } else {
1393 image_info.html("");
1394 image.css("display", "none");
1395 image_btn_del.css("display", "none");
1396 }
1397 }
1398 function _openMediaChooser(input_elem, multiple, imgContainer, typeFilter) {
1399 var image_frame;
1400 if(image_frame){
1401 image_frame.open();
1402 }
1403 if (!typeFilter) typeFilter = 'image';
1404 multiple ? multiple = true : multiple = false;
1405 // Define image_frame as wp.media object
1406 image_frame = wp.media({
1407 title: _x('Select Media', 'title', 'event-tickets-with-ticket-scanner'),
1408 multiple : multiple,
1409 library : {
1410 type : typeFilter,
1411 }
1412 });
1413
1414 image_frame.on('close',function() {
1415 // On close, get selections and save to the hidden input
1416 // plus other AJAX stuff to refresh the image preview
1417 var selection = image_frame.state().get('selection');
1418
1419 if (imgContainer) { // zeige erstes bild an
1420 var attachment = selection.first().toJSON();
1421 imgContainer.html( '<img src="'+attachment.url+'" style="max-width:100%;"/>' );
1422 }
1423
1424 var gallery_ids = new Array();
1425 var my_index = 0;
1426 selection.each(function(attachment) {
1427 gallery_ids[my_index] = attachment['id'];
1428 my_index++;
1429 });
1430 var ids = gallery_ids.join(",");
1431 input_elem.val(ids);
1432 input_elem.trigger("change");
1433 });
1434
1435 image_frame.on('open',function() {
1436 // On open, get the id from the hidden input
1437 // and select the appropiate images in the media manager
1438 var selection = image_frame.state().get('selection');
1439 var ids = input_elem.val().split(',');
1440 ids.forEach(function(id) {
1441 var attachment = wp.media.attachment(id);
1442 attachment.fetch();
1443 selection.add( attachment ? [ attachment ] : [] );
1444 });
1445 });
1446 image_frame.open();
1447 } // ende openmediachooser
1448
1449 function getBackButtonDiv() {
1450 let div_buttons = $('<div style="display:flex;justify-content:space-between;">');
1451 let div = $('<div/>').append($('<button/>').addClass("button-primary").html(_x('Back', 'label', 'event-tickets-with-ticket-scanner')).css("margin-bottom", "10px").on("click", function(){
1452 LAYOUT.renderAdminPageLayout();
1453 }));
1454 div_buttons.append(div);
1455 div_buttons.append(_displaySettingAreaButton());
1456 return div_buttons;
1457 }
1458
1459 function _getTicketScannerURL() {
1460 let url = _getOptions_Infos_getByKey('ticket').ticket_scanner_path;
1461 let _urlpath = _getOptions_getValByKey("wcTicketCompatibilityModeURLPath");
1462 if (_urlpath != "") {
1463 url = OPTIONS.infos.site.home+"/"+_urlpath+'/scanner/';
1464 } else {
1465 url = OPTIONS.infos.ticket.ticket_scanner_url;
1466 }
1467 return url;
1468 }
1469 function _displaySettingAreaButton() {
1470 let btn_grp = $('<div id="topMenu"/>').addClass("btn-group");
1471 $('<button/>').addClass("button-primary").html(_x("Support Info", 'label', 'event-tickets-with-ticket-scanner'))
1472 .on("click", ()=>{
1473 _displaySupportInfoArea();
1474 })
1475 .appendTo(btn_grp);
1476 $('<button/>').addClass("button-primary").html(_x("FAQ", 'label', 'event-tickets-with-ticket-scanner'))
1477 .on("click", ()=>{
1478 _displayFAQArea();
1479 })
1480 .appendTo(btn_grp);
1481 if (typeof PARAS.seatingplan !== "undefined" && PARAS.seatingplan) {
1482 $('<button/>').addClass("button-primary").html(_x("Seating Plans", 'label', 'event-tickets-with-ticket-scanner'))
1483 .on("click", ()=>{
1484 _displaySeatingplanArea();
1485 })
1486 .appendTo(btn_grp);
1487 }
1488 //if (_getOptions_Versions_isActivatedByKey('is_wc_available')) {
1489 $('<button/>').addClass("button-primary").html(_x("Ticket Scanner", 'label', 'event-tickets-with-ticket-scanner'))
1490 .on("click", ()=>{
1491 let url = _getTicketScannerURL();
1492 window.open(url, 'ticketscanner');
1493 })
1494 .appendTo(btn_grp);
1495 //}
1496 $('<button/>').addClass("button-primary").html(_x('Auth Token', 'label', 'event-tickets-with-ticket-scanner'))
1497 .on("click", ()=>{
1498 _displayAuthTokensArea();
1499 })
1500 .appendTo(btn_grp);
1501 $('<button/>').addClass("button-primary").html(_x('Options', 'label', 'event-tickets-with-ticket-scanner'))
1502 .on("click", ()=>{
1503 _displayOptionsArea();
1504 })
1505 .appendTo(btn_grp);
1506 if (isPremium()) {
1507 btn_grp = PREMIUM.displaySettingAreaButton(btn_grp);
1508 }
1509 return btn_grp;
1510 }
1511
1512 function _form_fields_serial_format(appendToDiv) {
1513 let input_prefix_codes;
1514 let input_type_codes;
1515 let input_amount_letters;
1516 let input_letter_excl;
1517 let input_letter_style;
1518 let input_include_numbers;
1519 let input_serial_delimiter;
1520 let input_serial_delimiter_space;
1521 let input_number_start;
1522 let input_number_offset;
1523
1524 let noNumbersOptions = false;
1525 let cbk = null;
1526 let formatterValues;
1527
1528 function _setNoNumberOptions() {
1529 noNumbersOptions = true;
1530 }
1531 function _setCallbackHandle(_cbk) {
1532 cbk = _cbk;
1533 }
1534 function _callCallbackHandle() {
1535 cbk && cbk(_getFormatterValues());
1536 }
1537 function _setFormatterValues(values) {
1538 formatterValues = values;
1539 }
1540
1541 function __render() {
1542 $('<br>').appendTo(appendToDiv);
1543 // prefix
1544 let div_prefix_codes = _createDivInput(_x("Enter a prefix (optional)", 'label', 'event-tickets-with-ticket-scanner')).appendTo(appendToDiv);
1545 input_prefix_codes = $('<input type="text">').appendTo(div_prefix_codes);
1546 $('<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);
1547 if (formatterValues && formatterValues['input_prefix_codes'] != null) input_prefix_codes.val(formatterValues['input_prefix_codes']);
1548 input_prefix_codes.on("change", ()=>{
1549 _callCallbackHandle();
1550 });
1551 // type numbers/serials
1552 let div_type_codes = _createDivInput(_x("Choose type of ticket numbers", 'label', 'event-tickets-with-ticket-scanner')).appendTo(appendToDiv);
1553 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);
1554 if (formatterValues && formatterValues['input_type_codes'] != null) input_type_codes.val(formatterValues['input_type_codes']);
1555
1556 if (noNumbersOptions) {
1557 input_type_codes.prop("disabled", true);
1558 }
1559 input_type_codes.on("change", function() {
1560 if (input_type_codes.val() === "2") {
1561 div_serials && div_serials.find("input").prop("disabled", true);
1562 div_serials && div_serials.find("select").prop("disabled", true);
1563 div_numbers && div_numbers.find("input").prop("disabled", false);
1564 div_numbers && div_numbers.find("select").prop("disabled", false);
1565 } else {
1566 div_serials && div_serials.find("input").prop("disabled", false);
1567 div_serials && div_serials.find("select").prop("disabled", false);
1568 div_numbers && div_numbers.find("input").prop("disabled", true);
1569 div_numbers && div_numbers.find("select").prop("disabled", true);
1570 }
1571 _callCallbackHandle();
1572 });
1573 // serials options
1574 let div_serials = $('<div>').html('<h4>'+_x('Serials options', 'title', 'event-tickets-with-ticket-scanner')+'</h4>').appendTo(appendToDiv);
1575 // anzahl letters
1576 let div_amount_letters = _createDivInput(_x('Amount of letter needed', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1577 input_amount_letters = $('<input type="number" required value="21" min="1" max="30">').appendTo(div_amount_letters);
1578 if (formatterValues && formatterValues['input_amount_letters'] != null) input_amount_letters.val(formatterValues['input_amount_letters']);
1579 input_amount_letters.on("change", function(){
1580 input_serial_delimiter.trigger("change");
1581 _callCallbackHandle();
1582 });
1583 // select letter exclusion
1584 let div_letter_excl = _createDivInput(_x('Letter exclusion', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1585 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);
1586 if (formatterValues && formatterValues['input_letter_excl'] != null) input_letter_excl.val(formatterValues['input_letter_excl']);
1587 input_letter_excl.on("change", ()=>{
1588 _callCallbackHandle();
1589 });
1590 // radio button text gross/klein/both/none
1591 let div_letter_style = _createDivInput(_x('Letter style', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1592 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);
1593 if (formatterValues && formatterValues['input_letter_style'] != null) input_letter_style.val(formatterValues['input_letter_style']);
1594 input_letter_style.on("change", ()=>{
1595 _callCallbackHandle();
1596 });
1597 // radio button numbers/none
1598 let div_include_numbers = _createDivInput(_x('Numbers needed?', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1599 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);
1600 if (formatterValues && formatterValues['input_include_numbers'] != null) input_include_numbers.val(formatterValues['input_include_numbers']);
1601 input_include_numbers.on("change", ()=>{
1602 _callCallbackHandle();
1603 });
1604 // select delimiter none/-/./space
1605 let div_serial_delimiter = _createDivInput(_x('Delimiter?', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1606 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);
1607 if (formatterValues && formatterValues['input_serial_delimiter'] != null) input_serial_delimiter.val(formatterValues['input_serial_delimiter']);
1608 function __refreshDelimiterSpace() {
1609 input_serial_delimiter_space.html("");
1610 if (input_serial_delimiter.val() !== "1") {
1611 let anzahl = parseInt(input_amount_letters.val(),10);
1612 if (anzahl > 0) {
1613 for(let a=1;a<anzahl;a++) input_serial_delimiter_space.append($('<option'+(anzahl > 2 && a === 7 ? " selected": "")+'>').attr("value",a).html(a));
1614 }
1615 }
1616 }
1617 input_serial_delimiter.on("change", function(){
1618 __refreshDelimiterSpace();
1619 _callCallbackHandle();
1620 });
1621 // choose delimiter space
1622 let div_serial_delimiter_space = _createDivInput(_x('After how many letters?', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_serials);
1623 input_serial_delimiter_space = $('<select></select>').appendTo(div_serial_delimiter_space);
1624 if (formatterValues && formatterValues['input_serial_delimiter'] != null) {
1625 // setze Werte erstmal ein
1626 __refreshDelimiterSpace();
1627 }
1628 if (formatterValues && formatterValues['input_serial_delimiter_space'] != null) input_serial_delimiter_space.val(formatterValues['input_serial_delimiter_space']);
1629 input_serial_delimiter_space.on("change", ()=>{
1630 _callCallbackHandle();
1631 });
1632 // numbers options
1633 let div_numbers = $('<div>').html('<h4>'+_x('Numbers options', 'title', 'event-tickets-with-ticket-scanner')+'</h4>').appendTo(appendToDiv);
1634 if (noNumbersOptions) div_numbers.css("display","none");
1635 // number start
1636 let div_number_start = _createDivInput(_x('Start number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_numbers);
1637 input_number_start = $('<input type="number" disabled required value="10000" min="1">').appendTo(div_number_start);
1638 if (formatterValues && formatterValues['input_number_start'] != null) input_number_start.val(formatterValues['input_number_start']);
1639 input_number_start.on("change", ()=>{
1640 _callCallbackHandle();
1641 });
1642 // number offset
1643 let div_number_offset = _createDivInput(_x('Offset for each number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_numbers);
1644 input_number_offset = $('<input type="number" disabled required value="1" min="1">').appendTo(div_number_offset);
1645 if (formatterValues && formatterValues['input_number_offset'] != null) input_number_offset.val(formatterValues['input_number_offset']);
1646 input_number_offset.on("change", ()=>{
1647 _callCallbackHandle();
1648 });
1649 }
1650
1651 function __generateCode(length, cases, withnumbers, exclusion) {
1652 let charset = 'abcdefghijklmnopqrstuvwxyz';
1653 if (cases === 1) charset = charset.toUpperCase();
1654 if (cases === 3) charset += charset.toUpperCase();
1655 if (withnumbers === 2) charset += '0123456789';
1656 if (withnumbers === 3) charset = '0123456789';
1657 if (typeof exclusion !== "undefined") {
1658 exclusion.forEach(function(v){
1659 let regex = new RegExp(v, 'gi');
1660 charset = charset.replace(regex, "");
1661 });
1662 }
1663 let retVal = "";
1664 for (var i = 0, n = charset.length; i < length; ++i) {
1665 retVal += charset.charAt(Math.floor(Math.random() * n));
1666 }
1667 return retVal;
1668 }
1669 function __insertSeperator(str, serial_delimiter, serial_delimiter_space) {
1670 if (str !== "" && serial_delimiter !== "" && serial_delimiter_space > 0) {
1671 let result = [str[0]];
1672 for(let x=1; x<str.length; x++) {
1673 if (x%serial_delimiter_space === 0) {
1674 result.push(serial_delimiter, str[x]);
1675 } else {
1676 result.push(str[x]);
1677 }
1678 }
1679 return result.join('');
1680 }
1681 return str;
1682 }
1683
1684 function _isTypeNumbers() {
1685 return input_type_codes.val() === "2";
1686 }
1687 function _getPrefix() {
1688 return input_prefix_codes.val().trim();
1689 }
1690 function _getAmountLetters() {
1691 let amount_letters = parseInt(input_amount_letters.val().trim(),10);
1692 if (isNaN(amount_letters) || amount_letters < 1) {
1693 input_amount_letters.select();
1694 return alert(__("Amount of letters has to be higher", 'event-tickets-with-ticket-scanner'));
1695 }
1696 return amount_letters;
1697 }
1698 function _getLetterExclusion() {
1699 return input_letter_excl.val() === "2" ? ['i','l','o','p','q'] : [];
1700 }
1701 function _getLetterStyle() {
1702 return parseInt(input_letter_style.val(),10);
1703 }
1704 function _getIncludeNumbers() {
1705 return parseInt(input_include_numbers.val(),10);
1706 }
1707 function _getSerialDelimiter() {
1708 return ['','-',' ',':'][parseInt(input_serial_delimiter.val(),10)-1];
1709 }
1710 function _getSerialDelimiterSpace() {
1711 let serial_delimiter_space = 0;
1712 try {
1713 serial_delimiter_space = _getSerialDelimiter() !== "" ? parseInt(input_serial_delimiter_space.val(),10) : 0;
1714 } catch (e) {}
1715 return serial_delimiter_space;
1716 }
1717 function _getNumberStart() {
1718 let start_number = parseInt(input_number_start.val().trim(),10);
1719 if (isNaN(start_number) || start_number < 1) {
1720 input_number_start.select();
1721 return alert(__("Your start number is not correct. It has to be an integer bigger than 0", 'event-tickets-with-ticket-scanner'));
1722 }
1723 return start_number;
1724 }
1725 function _getNumberOffset() {
1726 let number_offset = parseInt(input_number_offset.val().trim(),10);
1727 if (isNaN(number_offset) || number_offset < 1) number_offset = 1;
1728 return number_offset;
1729 }
1730 function _generateSerialCode(offsetCounter) {
1731 let code;
1732 let prefix = _getPrefix();
1733 if (_isTypeNumbers()) { // numbers
1734 if (!offsetCounter) offsetCounter = 0;
1735 let number_offset = offsetCounter * _getNumberOffset();
1736 code = _getNumberStart() + number_offset;
1737 if (prefix !== '') code = prefix + code;
1738 } else {
1739 code = __generateCode(_getAmountLetters(), _getLetterStyle(), _getIncludeNumbers(), _getLetterExclusion());
1740 code = __insertSeperator(code, _getSerialDelimiter(), _getSerialDelimiterSpace());
1741 if (prefix !== '') code = prefix + code;
1742 }
1743 return code;
1744 }
1745 function _getFormatterValues() {
1746 return {
1747 input_prefix_codes:_getPrefix().replace('/', '-'),
1748 input_type_codes:input_type_codes.val(),
1749 input_amount_letters:_getAmountLetters(),
1750 input_letter_excl:input_letter_excl.val(),
1751 input_letter_style:_getLetterStyle(),
1752 input_include_numbers:input_include_numbers.val(),
1753 input_serial_delimiter:input_serial_delimiter.val(),
1754 input_serial_delimiter_space:input_serial_delimiter_space.val(),
1755 input_number_start:_getNumberStart(),
1756 input_number_offset:_getNumberOffset()
1757 };
1758 }
1759
1760 return {
1761 render:__render,
1762 getAmountLetters:_getAmountLetters,
1763 getLetterExclusion:_getLetterExclusion,
1764 getLetterStyle:_getLetterStyle,
1765 getIncludeNumbers:_getIncludeNumbers,
1766 getSerialDelimiter:_getSerialDelimiter,
1767 getSerialDelimiterSpace:_getSerialDelimiterSpace,
1768 getNumberStart:_getNumberStart,
1769 getNumberOffset:_getNumberOffset,
1770 isTypeNumbers:_isTypeNumbers,
1771 getPrefix:_getPrefix,
1772 generateSerialCode:_generateSerialCode,
1773 setNoNumberOptions:_setNoNumberOptions,
1774 getFormatterValues:_getFormatterValues,
1775 setCallbackHandle:_setCallbackHandle,
1776 setFormatterValues:_setFormatterValues
1777 };
1778 }
1779
1780 function _createDivInput(label) {
1781 return $('<div/>').css({
1782 "display": "inline-block",
1783 "margin-bottom": "15px",
1784 "margin-right": "15px"
1785 }).html(label+"<br>");
1786 }
1787
1788 class Layout {
1789 constructor(){
1790 DIV.addClass("sngmbh_container");
1791 this.div_liste = $('<div style="background:white;padding:15px;border-radius:15px;"/>').html(_getSpinnerHTML());
1792 this.div_codes = $('<div style="background:white;padding:15px;border-radius:15px;"/>').html(_getSpinnerHTML());
1793 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"));
1794 $("body").append(this.div_spinner);
1795 }
1796 renderMainBody() {
1797 let premium_status = '<div style="color:red;font-weight:bold;">'+_x('FREE version', 'label', 'event-tickets-with-ticket-scanner')+'</div>';
1798 if (isPremium()) {
1799 premium_status = '<div style="color:green;font-weight:bold;">'+_x('PREMIUM', 'label', 'event-tickets-with-ticket-scanner')+'</div>';
1800 }
1801 $('body').find('td[data-id=plugin_info_area_premium_status]').html(premium_status);
1802
1803 /*
1804 $('body').find('div[data-id=plugin_addons]').html("")
1805 .css("display", "flex")
1806 .css("justify-content", "space-between")
1807 .css("width", "100%")
1808 .css("padding-bottom", "20px")
1809 .css("padding-top", "20px")
1810 .css("box-sizing", "border-box")
1811 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("How to start") )
1812 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("Quick start") )
1813 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("Ticket scanner") )
1814 ;
1815 if (isPremium() == false) {
1816 $('body').find('div[data-id=plugin_addons]')
1817 .append( $('<button style="flex-grow:1;margin-right:20px;background-color:cornflowerblue;border:none;color:white;padding:10px;">').html("Upgrade now") );
1818 }
1819 */
1820
1821 let div_body = $('<div/>');
1822 div_body.append($('<div style="text-align:right;">').html(_displaySettingAreaButton()));
1823 div_body.append($('<h3/>').html(_x('List of tickets', 'title', 'event-tickets-with-ticket-scanner')));
1824 div_body.append($('<p/>').html(__("Organize your tickets in lists. You can assign tickets to a list.", 'event-tickets-with-ticket-scanner')));
1825 div_body.append(this.div_liste);
1826 div_body.append($('<hr/>'));
1827 div_body.append($('<h3/>').html(_x("Event Tickets", 'title', 'event-tickets-with-ticket-scanner')));
1828 div_body.append(this.div_codes);
1829 return div_body;
1830 }
1831 renderAddCodes() {
1832 DIV.html(_getSpinnerHTML());
1833 getDataLists(()=>{
1834 function __generateCodes() {
1835 // generate codes and
1836 let amount_codes = parseInt(input_amount_codes.val().trim(),10);
1837 if (isNaN(amount_codes) || amount_codes < 1) {
1838 input_amount_codes.select();
1839 return alert(_x("Enter an amount of how many ticket numbers you need", 'title', 'event-tickets-with-ticket-scanner'));
1840 }
1841 if (amount_codes > _maxCodes) {
1842 input_amount_codes.val(_maxCodes);
1843 amount_codes = _maxCodes;
1844
1845 }
1846 let uniq = {};
1847 let versuche = 0;
1848 if (serialCodeFormatterForm.isTypeNumbers()) { // numbers
1849 for(let a=0; a < amount_codes; a++) {
1850 let code = serialCodeFormatterForm.generateSerialCode( a );
1851 if (typeof uniq[code] !== "undefined") {
1852 continue;
1853 }
1854 uniq[code] = true;
1855 }
1856 versuche = amount_codes;
1857 } else {
1858 // erstmal kein check ob mit dem alphabet und die geforderte Menge an letters, unique codes erstellt werden können
1859 let counter = 0;
1860 let versuche_max = amount_codes * 1.5;
1861 while(counter < amount_codes && versuche < versuche_max) {
1862 versuche++;
1863 let code = serialCodeFormatterForm.generateSerialCode();
1864 if (typeof uniq[code] !== "undefined") {
1865 continue;
1866 }
1867 uniq[code] = true;
1868 counter++;
1869 }
1870 }
1871 return [Object.keys(uniq), versuche];
1872 } // __generateCodes
1873
1874 let div = $('<div>').append(getBackButtonDiv());
1875 // eingabe generator options
1876 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);
1877 div_generator.append($('<p>').html(__("You can generate ticket numbers.", 'event-tickets-with-ticket-scanner')));
1878 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>');
1879 // anzahl codes
1880 let div_amount_codes = _createDivInput(_x('Enter amount of needed ticket numbers', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_generator);
1881 let _maxCodes = myAjax._max.codes;
1882 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>');
1883 let input_amount_codes = $('<input type="number" required value="100" min="1" max="'+_maxCodes+'">').appendTo(div_amount_codes);
1884
1885 // predefine elements
1886 let serialCodeFormatterForm = _form_fields_serial_format(div_generator);
1887 serialCodeFormatterForm.render();
1888
1889 let elem_clean_codebox = $('<input checked type="checkbox" />');
1890 $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"})
1891 .html(elem_clean_codebox)
1892 .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'))
1893 .appendTo(div_generator);
1894
1895 let elem_create_cvv = $('<input type="checkbox" />');
1896 $('<div/>').css({"margin-bottom": "15px","margin-right": "15px"})
1897 .html(elem_create_cvv)
1898 .append(_x('Generate Code Verification Value (CVV) for each ticket number', 'label', 'event-tickets-with-ticket-scanner'))
1899 .appendTo(div_generator);
1900
1901 // button generate
1902 div_generator.append($('<button/>').addClass("button-secondary").html(_x('Generate ticket numbers', 'label', 'event-tickets-with-ticket-scanner')).on("click", function(){
1903 let time_start = performance.now();
1904 btn_store_codes.prop("disabled", false);
1905 input_textarea.prop("disabled", false);
1906 if (elem_clean_codebox[0].checked) {
1907 input_textarea.html("");
1908 }
1909 input_textarea.prop("disabled", true);
1910 div_textarea_info.css("padding-bottom", "50px").html(_getSpinnerHTML());
1911 setTimeout(function(){
1912 let r = __generateCodes();
1913 let codes = r[0];
1914 let secs = ((performance.now() - time_start) / 1000)+"";
1915 if (elem_create_cvv[0].checked) {
1916 codes = codes.map(v=>{
1917 return v += ';'+(Math.floor(Math.random() * 10000) + 10000).toString().substring(1);
1918 });
1919 }
1920 input_textarea.append(codes.join("\n")).append("\n");
1921 input_textarea.prop("disabled", false);
1922 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]));
1923 _calcLinesOfCodeTextArea();
1924 },250);
1925 }));
1926
1927 // eingabe maske textarea
1928 function _calcLinesOfCodeTextArea() {
1929 let codesAmount = 0;
1930 input_textarea.val().trim().split('\n').forEach(v=>{
1931 if (v.trim() !== "") codesAmount++;
1932 });
1933 input_textarea_info.html(sprintf(/* translators: %d: amout of ticket numbers */__('contains %d tickets', 'event-tickets-with-ticket-scanner'), codesAmount));
1934 }
1935 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);
1936 let div_textarea_info = $('<div/>').appendTo(div_textarea);
1937 let input_textarea = $('<textarea>').change(_calcLinesOfCodeTextArea).css("height","135px").css("width","100%").appendTo(div_textarea);
1938 let input_textarea_info = $('<div/>').appendTo(div_textarea);
1939 div_textarea.append("<br>");
1940 _calcLinesOfCodeTextArea();
1941 // list auswahl
1942 let div_code_list = _createDivInput(_x('Assign to this ticket list', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div_textarea);
1943 let input_code_list = $('<select><option value="0">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</select></select>').appendTo(div_code_list);
1944 DATA_LISTS.forEach(v=>{
1945 input_code_list.append('<option value="'+v.id+'">'+v.name+'</option>');
1946 });
1947 div_textarea.append("<br>");
1948
1949 // additional prem fields
1950 if (isPremium() && PREMIUM.addAddCodeFields) {
1951 div_textarea.append(PREMIUM.addAddCodeFields());
1952 }
1953
1954 // button store codes
1955 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>');
1956 let btn_store_codes = $('<button/>');
1957 btn_store_codes.addClass("button-primary").html(_x('Store ticket numbers', 'label', 'event-tickets-with-ticket-scanner')).on("click", function(){
1958 // extract codes and
1959 let codes = [];
1960 let codesLines = input_textarea.val().split("\n").map(x=>x.trim());
1961 codesLines.forEach(x=>{
1962 x.split(",").forEach(y=>{
1963 y = y.trim();
1964 y = destroy_tags(y);
1965 if (y != "") codes.push(y);
1966 });
1967 });
1968 if (codes.length === 0) return;
1969
1970 // sperre btn store codes
1971 btn_store_codes.prop("disabled", true);
1972 input_textarea.prop("disabled", true);
1973
1974 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')));
1975 let _output = $('<ol/>').appendTo(div_textarea_info);
1976 div_textarea_info.append('<h3>'+_x('Successfull stored ticket numbers', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
1977 let output_textarea_codes_done = $('<textarea disabled style="4px solid green;width:100%;height:150px;"></textarea>').appendTo(div_textarea_info);
1978
1979 let list_id = parseInt(input_code_list.val(),10);
1980
1981 function __addCodesInChunks(chunk_size) {
1982 let dlg = $('<div/>').html(_getSpinnerHTML());
1983 dlg.dialog({title:_x('Importing', 'title', 'event-tickets-with-ticket-scanner'),closeOnEscape: true,modal: true, dialogClass: "no-close", close: function(event, ui){ abort=true; } });
1984
1985 let abort = false;
1986 let counter_ok = 0;
1987 let counter_notok = 0;
1988 let counter_all = codes.length;
1989 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));
1990 let chunks = array_chunks(codes, chunk_size);
1991 function _addCodeChunk(idx) {
1992 if (abort) return;
1993 if (idx >= chunks.length) {
1994 dlg.append('<p>'+__('Import process finished', 'event-tickets-with-ticket-scanner')+'</p>');
1995 $('<center/>').append($('<button class="button-primary" />').html(_x('Ok', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{ closeDialog(dlg); })).appendTo(dlg);
1996 return;
1997 }
1998 let arr = chunks[idx];
1999 arr.forEach(v=>{
2000 let div_info_entry = $('<li data-id="code_'+v+'"/>').html(v);
2001 _output.append(div_info_entry);
2002 });
2003 let attr = {"codes":arr, "list_id":list_id};
2004 if (isPremium() && PREMIUM.addAddCodeFieldsData) {
2005 attr = PREMIUM.addAddCodeFieldsData(div_textarea, attr);
2006 }
2007
2008 _makePost("addCodes", attr, function(data){
2009 counter_ok += data.ok.length;
2010 counter_notok += data.notok.length;
2011 if (myAjax._max.codes_total > 0 && myAjax._max.codes_total <= parseInt(data.total_size)) {
2012 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>');
2013 }
2014 let per = Math.ceil(((counter_ok+counter_notok)/counter_all)*100);
2015 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>';
2016 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>';
2017 dlg.html(info_content);
2018 data.ok.forEach(_v=> {
2019 _output.find('li[data-id="code_'+_v+'"]').css("color","green").append(' ('+_x('Ok', 'label', 'event-tickets-with-ticket-scanner')+')');
2020 output_textarea_codes_done.append(_v+"\n");
2021 });
2022 data.notok.forEach(_v=> {
2023 _output.find('li[data-id="code_'+_v+'"]').css("color","red").append(' ('+_x('Not Ok', 'label', 'event-tickets-with-ticket-scanner')+')');
2024 });
2025 setTimeout(()=>{
2026 _addCodeChunk(idx+1);
2027 }, 100);
2028 }, function(response){
2029 if (response.data.slice(0,4) === "#208") {
2030 FATAL_ERROR === false && LAYOUT.renderFatalError(response.data);
2031 FATAL_ERROR = true;
2032 }
2033 });
2034 }
2035
2036 if (chunks.length === 0) {
2037 closeDialog(dlg);
2038 } else {
2039 _addCodeChunk(0);
2040 }
2041 } // __addCodesInChunks
2042 __addCodesInChunks(100);
2043
2044 // zeige ok button, der info area leer macht und den btn store codes wieder aktiviert
2045 div_textarea_info.append($('<button/>').addClass("button-primary").css("margin-bottom", "20px").html(_x('Ok', 'label', 'event-tickets-with-ticket-scanner')).on("click", function(){
2046 div_textarea_info.html("");
2047 btn_store_codes.prop("disabled", false);
2048 input_textarea.prop("disabled", false);
2049 window.scrollTo(0,0);
2050 }));
2051
2052 }).appendTo(div_textarea);
2053 DIV.html(div);
2054 });
2055 }
2056 renderAdminPageLayout(cbf) {
2057 function __showMaskExport(totalRecordCount) {
2058 if (!totalRecordCount) totalRecordCount = 0;
2059 let maxRange = totalRecordCount > 40000 ? 40000 : totalRecordCount;
2060 let _options = {
2061 title: _x('Export tickets', 'title', 'event-tickets-with-ticket-scanner'),
2062 modal: true,
2063 minWidth: 400,
2064 minHeight: 200,
2065 buttons: [
2066 {
2067 text: _x('Export', 'label', 'event-tickets-with-ticket-scanner'),
2068 click: function() {
2069 ___submitForm();
2070 }
2071 },
2072 {
2073 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
2074 click: function() {
2075 closeDialog(this);
2076 }
2077 }
2078 ]
2079 };
2080 let formdlg = $('<form/>').html('<b>'+_x('Choose your export settings', 'title', 'event-tickets-with-ticket-scanner')+'</b><p>');
2081 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>');
2082 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>');
2083
2084 let _listChooser = $('<select name="listchooser"><option value="0">'+_x('All', 'option value', 'event-tickets-with-ticket-scanner')+'</option></select>');
2085 for(let a=0;a<DATA_LISTS.length;a++) {
2086 _listChooser.append('<option value="'+DATA_LISTS[a].id+'">'+DATA_LISTS[a].name+'</option>');
2087 }
2088 formdlg.append(_x('Limit export to ticket list', 'label', 'event-tickets-with-ticket-scanner')+'<br>').append(_listChooser).append('<p>');
2089
2090 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>');
2091 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>');
2092 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>');
2093 if (isPremium() && PREMIUM && PREMIUM.addExportTicketsInputFields) {
2094 formdlg.append(PREMIUM.addExportTicketsInputFields());
2095 }
2096 let dlg = $('<div/>').append(formdlg);
2097
2098 dlg.dialog(_options);
2099
2100 let form = dlg.find("form").on("submit", function(event) {
2101 event.preventDefault();
2102 ___submitForm();
2103 });
2104
2105 function ___submitForm() {
2106 let delimiter = dlg.find('select[name="delimiter"]').val();
2107 let filesuffix = dlg.find('select[name="suffix"]').val();
2108 let orderby = dlg.find('select[name="orderby"]').val();
2109 let orderbydirection = dlg.find('select[name="orderbydirection"]').val();
2110 let rangestart = dlg.find('input[name="rangestart"]').val();
2111 let rangeamount = dlg.find('input[name="rangeamount"]').val();
2112 let listchooser = dlg.find('select[name="listchooser"]').val();
2113
2114 let data = {'delimiter':delimiter, 'filesuffix':filesuffix, 'orderby':orderby, 'orderbydirection':orderbydirection, 'rangestart':rangestart, 'rangeamount':rangeamount, 'listchooser':listchooser};
2115 if (isPremium() && PREMIUM && PREMIUM.addExportTicketsInputFieldsData) {
2116 data = PREMIUM.addExportTicketsInputFieldsData(data, dlg);
2117 }
2118
2119 let url = _requestURL('exportTableCodes', data);
2120 closeDialog(dlg);
2121 window.open(url, "_blank");
2122 }
2123 }
2124 function __showMaskList(editValues){
2125 let _options = {
2126 title: editValues !== null ? _x('Edit List', 'title', 'event-tickets-with-ticket-scanner') : _x('Add List', 'title', 'event-tickets-with-ticket-scanner'),
2127 modal: true,
2128 minWidth: 600,
2129 minHeight: 400,
2130 open: function(e) {
2131 //$(e.target).parent().css('background-color','orangered');
2132 },
2133 buttons: [
2134 {
2135 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
2136 click: function() {
2137 ___submitForm();
2138 }
2139 },
2140 {
2141 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
2142 click: function() {
2143 closeDialog(this);
2144 }
2145 }
2146 ]
2147 };
2148 let dlg = $('<div/>').html('<form>'+_x('Name', 'label', 'event-tickets-with-ticket-scanner')+'<br><input name="inputName" type="text" style="width:100%;" required></form>');
2149 dlg.dialog(_options);
2150
2151 dlg.find("form").append($('<p>'+_x('Description', 'label', 'event-tickets-with-ticket-scanner')+'<br><textarea name="desc" style="width:100%;"></textarea></p>'));
2152
2153 if (isPremium()) PREMIUM.addListMaskEditFields(dlg, editValues);
2154 else {
2155 if (_getOptions_isActivatedByKey("oneTimeUseOfRegisterCode")) {
2156 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>'));
2157 }
2158 }
2159
2160 let metaObj = [];
2161 if (editValues && typeof editValues.meta !== "undefined" && editValues.meta != "") {
2162 try {
2163 metaObj = JSON.parse(editValues.meta);
2164 } catch(e) {}
2165 }
2166
2167 if (_getOptions_isActivatedByKey("userJSRedirectActiv")) {
2168 dlg.find("form").append($('<p>'+_getOptions_getLabelByKey("userJSRedirectURL")+'<br><input type="text" name="redirecturl" style="width:100%;"></p>'));
2169 }
2170
2171 dlg.find("form").append($('<p><input name="serialformatter" type="checkbox"> '+_x('Overrule the ticket format settings', 'label', 'event-tickets-with-ticket-scanner')+'</p>'));
2172 let extra_div = $('<div>').appendTo(dlg).css("margin-top", "10px").css("margin-left", "24px").css("padding", "10px").css("border", "1px solid black")
2173 .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>');
2174 let serialCodeFormatter = _form_fields_serial_format(extra_div);
2175 serialCodeFormatter.setNoNumberOptions();
2176 if (typeof metaObj.formatter !== "undefined" && metaObj.formatter.format != "") {
2177 let formatterValues;
2178 try {
2179 let o = metaObj.formatter.format.replace(new RegExp("\\\\", "g"), "").trim();
2180 formatterValues = JSON.parse(o);
2181 serialCodeFormatter.setFormatterValues(formatterValues);
2182 } catch (e) {}
2183 }
2184 serialCodeFormatter.render();
2185
2186 $('<hr>').appendTo(dlg);
2187 $('<h4>').html(_x('Webhook', 'heading', 'event-tickets-with-ticket-scanner')).appendTo(dlg);
2188 if (!_getOptions_isActivatedByKey("webhooksActiv")) {
2189 $('<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);
2190 }
2191 $('<div>').html(_x('URL to your service if the WooCommerce ticket is sold', 'label', 'event-tickets-with-ticket-scanner')).appendTo(dlg);
2192 let meta_webhooks_webhookURLaddwcticketsold = $('<input name="meta_webhooks_webhookURLaddwcticketsold" type="text" style="width:100%;">').appendTo(dlg);
2193
2194 let form = dlg.find("form").on("submit", function(event) {
2195 event.preventDefault();
2196 ___submitForm();
2197 });
2198
2199 if (editValues) {
2200 form[0].elements['inputName'].value = editValues.name;
2201 form[0].elements['inputName'].select();
2202 if (typeof metaObj.desc !== "undefined") {
2203 form[0].elements['desc'].value = metaObj.desc;
2204 }
2205 if (typeof metaObj.formatter !== "undefined" && metaObj.formatter.active) {
2206 form[0].elements['serialformatter'].checked = true;
2207 }
2208 if (_getOptions_isActivatedByKey("userJSRedirectActiv") && typeof metaObj.redirect !== "undefined" && metaObj.redirect.url) {
2209 form[0].elements['redirecturl'].value = metaObj.redirect.url.trim();
2210 }
2211 if (typeof metaObj.webhooks != "undefined") {
2212 if (typeof metaObj.webhooks.webhookURLaddwcticketsold != "undefined") {
2213 meta_webhooks_webhookURLaddwcticketsold.val(metaObj.webhooks.webhookURLaddwcticketsold);
2214 }
2215 }
2216 }
2217
2218 function ___submitForm() {
2219 let inputName = form[0].elements['inputName'].value.trim();
2220 if (inputName === "") return;
2221
2222 dlg.html(_getSpinnerHTML());
2223 let _data = {"name":inputName};
2224 _data['meta'] = {"desc":"", "formatter":{}, "webhooks":{}};
2225 _data['meta']['desc'] = form[0].elements['desc'].value.trim();
2226 _data['meta']['formatter']['active'] = form[0].elements['serialformatter'].checked ? 1 : 0;
2227 _data['meta']['formatter']['format'] = JSON.stringify(serialCodeFormatter.getFormatterValues());
2228 if (_getOptions_isActivatedByKey("userJSRedirectActiv")) {
2229 _data['meta']['redirect'] = {"url":form[0].elements['redirecturl'].value.trim()};
2230 }
2231 _data['meta']['webhooks']['webhookURLaddwcticketsold'] = meta_webhooks_webhookURLaddwcticketsold.val().trim();
2232 if (isPremium()) PREMIUM.addListMaskEditFieldsData(_data, form[0], editValues);
2233
2234 form[0].reset();
2235 if (editValues) {
2236 _data.id = editValues.id;
2237 _makePost('editList', _data, result=>{
2238 DATA_LISTS = null;
2239 __renderTabelleListen();
2240 tabelle_codes_datatable.ajax.reload();
2241 setTimeout(function(){closeDialog(dlg);},250);
2242 }, function() {
2243 closeDialog(dlg);
2244 });
2245 } else {
2246 _makePost('addList', _data, result=>{
2247 DATA_LISTS = null;
2248 __renderTabelleListen();
2249 closeDialog(dlg);
2250 }, function(response) {
2251 closeDialog(dlg);
2252 if (response.data.slice(0,1) === "#") {
2253 FATAL_ERROR === false && LAYOUT.renderFatalError(response.data);
2254 FATAL_ERROR = true;
2255 }
2256 });
2257 }
2258 }
2259
2260 } // ende showmaskliste
2261
2262 function __showMaskCode(editValues){
2263 let _options = {
2264 title: editValues !== null ? _x('Edit Ticket', 'title', 'event-tickets-with-ticket-scanner') : _x('Add Ticket', 'title', 'event-tickets-with-ticket-scanner'),
2265 modal: true,
2266 minWidth: 400,
2267 minHeight: 200,
2268 buttons: [
2269 {
2270 text: _x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
2271 click: function() {
2272 ___submitForm();
2273 }
2274 },
2275 {
2276 text: _x('Cancel', 'label', 'event-tickets-with-ticket-scanner'),
2277 click: function() {
2278 $( this ).dialog( "close" );
2279 $( this ).html('');
2280 }
2281 }
2282 ]
2283 };
2284 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>');
2285 DATA_LISTS.forEach(v=>{
2286 $(dlg).find('select[name="inputListId"]').append('<option '+(editValues && parseInt(editValues.list_id,10) === parseInt(v.id,10) ? 'selected ':'')+'value="'+v.id+'">'+v.name+'</option>');
2287 });
2288
2289 let elem_cvv = $('<input type="text" size="6" minlength="5" maxlength="4" />');
2290 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
2291 .html(_x('CVV - use 4 digits for best results', 'label', 'event-tickets-with-ticket-scanner')+'<br>')
2292 .append(elem_cvv)
2293 .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>')
2294 .appendTo(dlg.find("form"));
2295
2296 let div_status = $('<div/>');
2297 div_status.append(
2298 $('<select name="inputStatus"/>')
2299 .append('<option '+(editValues.aktiv === "1"?'selected':'')+' value="1">'+_x('is activ', 'option value', 'event-tickets-with-ticket-scanner')+'</option>')
2300 .append('<option '+(editValues.aktiv === "0"?'selected':'')+' '+(!isPremium()?'disabled':'')+' value="0">'+_x('is inactiv', 'option value', 'event-tickets-with-ticket-scanner')+' '+(!isPremium()?getLabelPremiumOnly():'')+'</option>')
2301 .append('<option '+(editValues.aktiv === "2"?'selected':'')+' value="2">'+_x('is stolen', 'label', 'event-tickets-with-ticket-scanner')+'</option>')
2302 )
2303 .appendTo(dlg);
2304
2305 dlg.dialog(_options);
2306
2307 if (editValues) {
2308 if (editValues.cvv) elem_cvv.val(editValues.cvv);
2309 }
2310
2311 if (isPremium()) PREMIUM.addCodeMaskEditFields(dlg, editValues);
2312
2313 let form = dlg.find("form").on("submit", function(event) {
2314 event.preventDefault();
2315 ___submitForm();
2316 });
2317 function ___submitForm() {
2318 let inputListId = parseInt($(dlg).find('select[name="inputListId"]').val(),10);
2319 let inputStatusValue = $(dlg).find('select[name="inputStatus"]').val();
2320 dlg.html(_getSpinnerHTML());
2321 let _data = {"list_id":inputListId, "aktiv":inputStatusValue, "cvv":elem_cvv.val().trim()};
2322 if (isPremium()) PREMIUM.addCodeMaskEditFieldsData(_data, form[0], editValues);
2323 form[0].reset();
2324 if (editValues) {
2325 _data.code = editValues.code;
2326 _makeGet('editCode', _data, ()=>{
2327 tabelle_codes_datatable.ajax.reload();
2328 closeDialog(dlg);
2329 }, function() {
2330 closeDialog(dlg);
2331 });
2332 } else {
2333 alert(__("Use the add option", 'event-tickets-with-ticket-scanner'));
2334 }
2335 }
2336 } // ende __showMaskCode
2337
2338 let id_codes = myAjax.divPrefix+'_tabelle_codes';
2339 let tabelle_liste_datatable;
2340 let tabelle_codes_datatable;
2341 let tabelle_codes = $('<table/>').attr("id", id_codes);
2342 let tplace = $('<div/>');
2343
2344 function __renderTabelleListen() {
2345 getDataLists(()=>{
2346 let id_liste = myAjax.divPrefix+'_tabelle_liste';
2347 let tabelle_liste = $('<table/>').attr("id", id_liste);
2348 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>');
2349 tplace.html(tabelle_liste);
2350
2351 let table = $('#'+id_liste);
2352 $(table).DataTable().clear().destroy();
2353 tabelle_liste_datatable = $(table).DataTable({
2354 "responsive": true,
2355 "visible": true,
2356 "searching": true,
2357 "ordering": true,
2358 "processing": true,
2359 "serverSide": false,
2360 "stateSave": true,
2361 "data": DATA_LISTS,
2362 "order": [[ 0, "asc" ]],
2363 "columns":[
2364 {"data":"name", "orderable":true},
2365 {"data":"time", "orderable":true, "width":80,
2366 "render":function (data, type, row) {
2367 return '<span style="display:none;">'+data+'</span>'+DateTime2Text(data);
2368 }
2369 },
2370 {"data":null,"orderable":false,"defaultContent":'',"className":"buttons dt-right","width":110,
2371 "render": function ( data, type, row ) {
2372 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>';
2373 }
2374 }
2375 ]
2376 });
2377 tabelle_liste.css("width", "100%");
2378 table.on('click', 'button[data-type="showCodes"]', e=>{
2379 let data = tabelle_liste_datatable.row( $(e.target).parents('tr') ).data();
2380 tabelle_codes_datatable.search("LIST:"+data.id).draw();
2381 });
2382 table.on('click', 'button[data-type="edit"]', e=>{
2383 let data = tabelle_liste_datatable.row( $(e.target).parents('tr') ).data();
2384 __showMaskList(data);
2385 });
2386 table.on('click', 'button[data-type="delete"]', e=>{
2387 let data = tabelle_liste_datatable.row( $(e.target).parents('tr') ).data();
2388 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'), ()=>{
2389 let _data = {'id':data.id};
2390 _makePost('removeList', _data, result=>{
2391 __renderTabelleListen();
2392 tabelle_codes_datatable.ajax.reload();
2393 });
2394 });
2395 });
2396 }); // end of loading lists
2397 } // __renderTabelleListen
2398 tabelle_codes.css("width", "100%");
2399
2400 STATE = 'admin';
2401 DIV.html(_getSpinnerHTML());
2402 getOptionsFromServer(optionData=>{
2403 DIV.html('');
2404 DIV.append(this.renderMainBody());
2405
2406 let btn_liste_empty = $('<button/>').addClass("button-secondary").html(__('Empty table', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2407 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')), ()=>{
2408 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')), ()=>{
2409 _makeGet('emptyTableLists', null, ()=>{
2410 tabelle_codes_datatable.ajax.reload();
2411 __renderTabelleListen();
2412 });
2413 });
2414 });
2415 });
2416 let btn_liste_new = $('<button/>').addClass("button-primary").html(_x('Add', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2417 __showMaskList(null);
2418 });
2419 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));
2420 this.div_liste.append(tplace);
2421
2422 __renderTabelleListen();
2423
2424 let additionalColumn = {customerName:'',customerCompany:'',redeemAmount:'',confirmedCount:''};
2425 if (_getOptions_isActivatedByKey('displayAdminAreaColumnConfirmedCount')) {
2426 additionalColumn.confirmedCount = '<th>'+_x('Confirmed Count', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2427 }
2428 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingName')) {
2429 additionalColumn.customerName = '<th>'+_x('Customer', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2430 }
2431 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingCompany')) {
2432 additionalColumn.customerCompany = '<th>'+_x('Company', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2433 }
2434 if (_getOptions_isActivatedByKey('displayAdminAreaColumnRedeemedInfo')) {
2435 additionalColumn.redeemAmount = '<th>'+_x('Redeem Amount', 'label', 'event-tickets-with-ticket-scanner')+'</th>';
2436 }
2437
2438 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">'
2439 +_x('Ticket', 'label', 'event-tickets-with-ticket-scanner')+'</th>'+additionalColumn.customerName+additionalColumn.customerCompany+'<th align="left">'
2440 +_x('List', 'label', 'event-tickets-with-ticket-scanner')+'</th><th align="left">'
2441 +_x('Created', 'label', 'event-tickets-with-ticket-scanner')+'</th>'+additionalColumn.confirmedCount+'<th align="left">'
2442 +_x('Redeemed', 'label', 'event-tickets-with-ticket-scanner')+'</th>'+additionalColumn.redeemAmount+'<th>'
2443 +_x('OrderId', 'label', 'event-tickets-with-ticket-scanner')+'</th><th>CVV</th><th>'
2444 +_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>');
2445 tabelle_codes.find('input[data-id="checkAll"]').on('click', (e)=> {
2446 let isChecked = $(e.currentTarget).prop('checked');
2447 let found = false;
2448 tabelle_codes.find('input[data-type="select-checkbox"]').each((i,v)=>{
2449 $(v).prop('checked', isChecked);
2450 found = true;
2451 });
2452 if (isChecked && found) {
2453 //drop_codes_bulk.prop("disabled", false);
2454 } else {
2455 //drop_codes_bulk.prop("disabled", true);
2456 }
2457 });
2458 let btn_codes_new = $('<button/>').addClass("button-primary").html(_x('Add', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2459 if (!isPremium() && tabelle_codes_datatable.page.info().recordsTotal > myAjax._max.codes_total) {
2460 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'));
2461 } else {
2462 LAYOUT.renderAddCodes();
2463 }
2464 });
2465 let btn_codes_empty = $('<button/>').addClass("button-secondary").html(__('Empty table', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2466 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')), ()=>{
2467 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')), ()=>{
2468 _makeGet('emptyTableCodes', null, ()=>{
2469 tabelle_codes_datatable.ajax.reload();
2470 });
2471 });
2472 });
2473 });
2474 let btn_codes_reload = $('<button/>').addClass("button-secondary").html(__('Refresh table', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2475 LAYOUT.renderSpinnerShow();
2476 tabelle_codes_datatable.ajax.reload();
2477 window.setTimeout(()=>{LAYOUT.renderSpinnerHide();}, 1500);
2478 });
2479 let btn_codes_export = $('<button/>').addClass("button-secondary").html(_x('Export tickets', 'label', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2480 //let url = _requestURL('exportTableCodes', null);
2481 //window.open(url, "_blank");
2482 //console.log(tabelle_codes_datatable.page.info());
2483 __showMaskExport(tabelle_codes_datatable.page.info().recordsTotal);
2484 });
2485 let drop_codes_bulk = $('<select data-id="bulk-code-action" />')
2486 .html('<option value="">'+_x('Bulk Action', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2487 //.append('<option value="delete">'+_x('Delete', 'label', 'event-tickets-with-ticket-scanner')+'</option>');
2488 for (var key in BulkActions.codes) {
2489 let entry = BulkActions.codes[key];
2490 drop_codes_bulk.append('<option value="'+key+'">'+entry.label+'</option>');
2491 }
2492 drop_codes_bulk.on('change', ()=>{
2493 let val = drop_codes_bulk.val();
2494 if (val !== "") {
2495 let selectedElems = [];
2496 tabelle_codes.find('input[data-type="select-checkbox"]').each((i,v)=>{
2497 if ($(v).prop("checked")) selectedElems.push(v);
2498 });
2499 if (selectedElems.length) {
2500 let fkt = null;
2501 if (typeof BulkActions.codes[val] == "function") {
2502 fkt = BulkActions.codes[val];
2503 } else {
2504 fkt = BulkActions.codes[val].fkt;
2505 }
2506 fkt && fkt(selectedElems, tabelle_codes_datatable);
2507 }
2508 }
2509 drop_codes_bulk.val('');
2510 });
2511 let drop_search = $('<select data-id="filter_type" />');
2512 drop_search.append('<option value="">'+_x('Default search filter', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2513 drop_search.append('<option value="LIST:">'+_x('Filter for list id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2514 drop_search.append('<option value="ORDERID:">'+_x('Filter for order id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2515 drop_search.append('<option value="CVV:">'+_x('Filter for cvv value', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2516 drop_search.append('<option value="STATUS:">'+_x('Filter for status (1:active, 0:inactive, 2:stolen)', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2517 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>');
2518 drop_search.append('<option value="USERID:">'+_x('Filter for registered user id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2519 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>');
2520 drop_search.append('<option value="PRODUCTID:">'+_x('Filter for product id', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2521 drop_search.append('<option value="DAYPERTICKET:">'+_x('Filter for choosen date (enter YYYY-MM-DD)', 'option value', 'event-tickets-with-ticket-scanner')+'</option>');
2522 drop_search.on("change", e=>{
2523 let old_search = tabelle_codes_datatable.search().trim();
2524 let search = drop_search.val();
2525 if (old_search && old_search.length > 0) {
2526 search = old_search + " & " + search;
2527 }
2528 tabelle_codes_datatable.search(search);
2529 });
2530 this.div_codes
2531 .html($('<div/>').css('text-align', 'right').css('margin-bottom','10px')
2532 .append(drop_codes_bulk)
2533 .append(drop_search)
2534 .append(btn_codes_export)
2535 .append(btn_codes_empty)
2536 .append(btn_codes_reload)
2537 .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));
2538 this.div_codes.append(tabelle_codes);
2539
2540 let table_columns = [
2541 {"data":null,"orderable":false,"defaultContent":'', "render":function (data, type, row) {
2542 return '<input type="checkbox" data-type="select-checkbox" data-key="'+data.id+'" data-code="'+data.code+'">';
2543 }},
2544 {"data":null,"className":'details-control',"orderable":false,"defaultContent":''},
2545 {"data":"code_display", "orderable":true, "render":(data,type,row)=>{
2546 return destroy_tags(data);
2547 }},
2548 {"data":"list_name", "orderable":true, "render":(data,type,row)=>{
2549 return destroy_tags(data);
2550 }},
2551 {"data":"time", "className":"dt-center", "orderable":true,
2552 "render":function (data, type, row) {
2553 return '<span style="display:none;">'+data+'</span>'+DateTime2Text(data);
2554 }
2555 },
2556 {"data":"redeemed", "orderable":true, "className":"dt-center", "render":function(data, type, row) {
2557 if (data == 1) {
2558 return 'yes';
2559 } else {
2560 return '';
2561 }
2562 }},
2563 {"data":"order_id", "className":"dt-right", "orderable":true},
2564 {"data":null, "orderable":false, "className":"dt-center", "render":function(data, type, row){
2565 return data.cvv === "" ? "" : '****';
2566 }},
2567 {"data":null, "orderable":true, "className":"dt-center", "render":function(data, type, row){
2568 let _stat = '';
2569 if (data.meta != "") {
2570 let metaObj = JSON.parse(data.meta);
2571 if (typeof metaObj['used'] !== "undefined") {
2572 if (metaObj.used.reg_request !== "") _stat = '/used';
2573 }
2574 }
2575 if (data.aktiv === "2") return '<span style="color:red;">'+_x('stolen', 'label', 'event-tickets-with-ticket-scanner')+'</span>'+_stat;
2576 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;
2577 }},
2578 {"data":null,"orderable":false,"defaultContent":'',"className":"buttons dt-right",
2579 "render": function ( data, type, row ) {
2580 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>';
2581 }
2582 }
2583 ];
2584 let addition_column_offset = 0;
2585 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingName')) {
2586 addition_column_offset++;
2587 table_columns.splice(3, 0, {
2588 "data":"_customer_name","orderable":false
2589 });
2590 }
2591 if (_getOptions_isActivatedByKey('displayAdminAreaColumnBillingCompany')) {
2592 addition_column_offset++;
2593 table_columns.splice(3, 0, {
2594 "data":"_customer_company","orderable":false
2595 });
2596 }
2597 if (_getOptions_isActivatedByKey('displayAdminAreaColumnConfirmedCount')) {
2598 addition_column_offset++;
2599 table_columns.splice(4+addition_column_offset, 0, {
2600 "data":null,"orderable":false,"defaultContent":'',"className":"dt-center",
2601 "render":function(data,type,row) {
2602 let ret = 0;
2603 let metaObj = getCodeObjectMeta(data);
2604 if(!metaObj) return ret;
2605 if (typeof metaObj.confirmedCount != "undefined") {
2606 ret = metaObj.confirmedCount;
2607 }
2608 return ret;
2609 }
2610 });
2611 }
2612 if (_getOptions_isActivatedByKey('displayAdminAreaColumnRedeemedInfo')) {
2613 addition_column_offset++;
2614 table_columns.splice(4+addition_column_offset, 0, {
2615 "data":null,"orderable":false,"defaultContent":'',"className":"dt-center",
2616 "render":function(data,type,row) {
2617 let ret = '';
2618 if (row._max_redeem_amount > 0) {
2619 ret = row._redeemed_counter+'/'+row._max_redeem_amount;
2620 } else {
2621 ret = row._redeemed_counter+'/unlimited';
2622 }
2623 return ret;
2624 }
2625 });
2626 }
2627
2628 tabelle_codes_datatable = $(this.div_codes).find('#'+id_codes).DataTable({
2629 "responsive": true,
2630 "search": {
2631 "search": typeof PARAS.code !== "undefined" ? encodeURIComponent(PARAS.code.trim()) : ''
2632 },
2633 footerCallback: function(row, data, start, end, display) {
2634 let data_anser = tabelle_codes_datatable.ajax.json();
2635 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);
2636 var api = this.api();
2637 $(api.column(1).footer()).html(text);
2638 //$(api.tables().footer()).html(text);
2639 },
2640 "processing": true,
2641 "serverSide": true,
2642 "stateSave": false,
2643 "ajax": {
2644 "url": _requestURL('getCodes'),
2645 "type": 'GET'
2646 },
2647 "order": [[ 4 + (additionalColumn.customerName != "" ? 1 : 0), "desc" ]],
2648 "columns": table_columns,
2649 "initComplete": function () {
2650 LAYOUT.renderSpinnerHide();
2651 },
2652 "autowidth":true
2653 });
2654 tabelle_codes.on('click', 'button[data-type="edit"]', function (e) {
2655 let data = tabelle_codes_datatable.row( $(this).parents('tr') ).data();
2656 __showMaskCode(data);
2657 });
2658 tabelle_codes.on('click', 'button[data-type="delete"]', function (e) {
2659 let data = tabelle_codes_datatable.row( $(this).parents('tr') ).data();
2660 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>', ()=>{
2661 let _data = {'id':data.id};
2662 _makePost('removeCode', _data, result=>{
2663 tabelle_codes_datatable.ajax.reload();
2664 });
2665 });
2666 });
2667 $('#'+id_codes+' tbody').on('click', 'td.details-control', function () {
2668 function ___format(d) {
2669 let metaObj = [];
2670 if (d.meta) {
2671 metaObj = JSON.parse(d.meta);
2672 }
2673 let div = $('<div/>');
2674
2675 // hole das aktuelle Metaobj
2676 function __getData(_codeObj) {
2677 div.html(_getSpinnerHTML());
2678 _makeGet('getMetaOfCode',{'code':d.code}, dataMeta=>{
2679 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
2680 _codeObj.meta = JSON.stringify(dataMeta);
2681 updateCodeObject(d, _codeObj);
2682 metaObj = getCodeObjectMeta(d);
2683 }
2684
2685 div.html("");
2686 d.meta = JSON.stringify(dataMeta);
2687 d.metaObj = dataMeta;
2688
2689 let btn_grp = $('<div/>').addClass("btn-group").appendTo(div);
2690 $('<button>').html(_x('Display QR with ticket number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2691 let id = 'qrcode_'+d.code+'_'+time();
2692 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>';
2693 LAYOUT.renderInfoBox(_x('QR with ticket number', 'title', 'event-tickets-with-ticket-scanner'), content);
2694 });
2695 if (d.metaObj.wc_ticket.is_ticket && typeof d.metaObj.wc_ticket._public_ticket_id !== "undefined" && d.metaObj.wc_ticket._public_ticket_id != "") {
2696 $('<button>').html(_x('Display QR with PUBLIC ticket number', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2697 let id = 'qrcode_'+d.code+'_'+time();
2698 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>';
2699 LAYOUT.renderInfoBox(_x('QR with ticket number', 'title', 'event-tickets-with-ticket-scanner'), content);
2700 });
2701 }
2702 if (d.metaObj.wc_ticket.is_ticket && typeof d.metaObj.wc_ticket._qr_content !== "undefined" && d.metaObj.wc_ticket._qr_content != "") {
2703 $('<button>').html(_x('Display QR with your own QR content', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2704 let id = 'qrcode_own_'+d.code+'_'+time();
2705 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>';
2706 LAYOUT.renderInfoBox(_x('QR with ticket number', 'title', 'event-tickets-with-ticket-scanner'), content);
2707 });
2708 }
2709 if (typeof d.metaObj._QR != "undefined" && typeof d.metaObj._QR.directURL != "undefined" && d.metaObj._QR.directURL != "") {
2710 $('<button>').html(_x('Display QR with URL', 'label', 'event-tickets-with-ticket-scanner')).appendTo(btn_grp).on("click", e=>{
2711 let id = 'qrcode_url_'+d.code+'_'+time();
2712 let qr_content = d.metaObj._QR.directURL;
2713 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>';
2714 LAYOUT.renderInfoBox(_x('QR with URL and code', 'title', 'event-tickets-with-ticket-scanner'), content);
2715 });
2716 }
2717 div.append('<div/>');
2718
2719 // male die Inhalte
2720 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'));
2721 div.append(_displayCodeDetails(d, metaObj, tabelle_codes_datatable));
2722
2723 div.append('<h3>'+_x('WooCommerce Order', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
2724 if (!_getOptions_Versions_isActivatedByKey("is_wc_available")) {
2725 div.append($("<div>").css("color", "red").html(__("WooCommerce not found", 'event-tickets-with-ticket-scanner')));
2726 }
2727 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>'));
2728 if (typeof metaObj['woocommerce'] !== "undefined") {
2729 if (metaObj.woocommerce.order_id !== 0) {
2730 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+')')));
2731 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>')));
2732 }
2733 }
2734 if (parseInt(d.order_id) > 0) {
2735 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", ()=>{
2736 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), ()=>{
2737 _makeGet('removeWoocommerceOrderInfoFromCode', {'code':d.code}, _codeObj=>{
2738 //tabelle_codes_datatable.ajax.reload();
2739 __getData(_codeObj);
2740 });
2741 });
2742 })));
2743 }
2744
2745 div.append('<h4>'+__('WooCommerce ticket sale', 'event-tickets-with-ticket-scanner')+'</h4>');
2746 div.append(_displayWCETicket(d, tabelle_codes_datatable));
2747
2748 div.append('<h3>'+__('WooCommerce Purchase Restriction', 'event-tickets-with-ticket-scanner')+'</h3>');
2749 if (typeof metaObj['wc_rp'] !== "undefined") {
2750 if (metaObj.wc_rp.order_id !== 0) {
2751 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>')));
2752 div.append($("<div>").html('<b>'+_x('Order from', 'label', 'event-tickets-with-ticket-scanner')+':</b> ').append($('<span>').text(metaObj.wc_rp.creation_date)));
2753 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>')));
2754 div.append($('<div style="margin-top:10px;">').html($('<button>').addClass("button-delete").html(__('Remove purchase ticket information', 'event-tickets-with-ticket-scanner')).on("click", ()=>{
2755 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), ()=>{
2756 _makeGet('removeWoocommerceRstrPurchaseInfoFromCode', {'code':d.code}, _codeObj=>{
2757 //tabelle_codes_datatable.ajax.reload();
2758 __getData(_codeObj);
2759 });
2760 });
2761 })));
2762 } else {
2763 div.append($("<div>").html('<b>'+_x('Used for Order ID', 'label', 'event-tickets-with-ticket-scanner')+':</b> -'));
2764 }
2765 }
2766
2767 div.append('<h3>'+_x('Registered user', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
2768 div.append(_displayRegisteredUserForCode(d, metaObj, tabelle_codes_datatable));
2769
2770 div.append('<h3>Redeem operations</h3>');
2771 div.append(_displayRedeemOperationsForCode(d, metaObj));
2772
2773 div.append('<h3>'+_x('IP list checked for this ticket', 'title', 'event-tickets-with-ticket-scanner')+'</h3>');
2774 if (isPremium()) {
2775 div.append(PREMIUM.displayTrackedIPsForCode(d.code));
2776 } else {
2777 div.append(getLabelPremiumOnly());
2778 }
2779
2780 if (isPremium() && PREMIUM.displayCodeDetailsAtEnd) div.append(PREMIUM.displayCodeDetailsAtEnd(d, tabelle_codes_datatable, metaObj));
2781
2782 div.append("<hr>");
2783 });
2784 }
2785 __getData();
2786 return div;
2787 }
2788
2789 var tr = $(this).closest('tr');
2790 var row = tabelle_codes_datatable.row( tr );
2791 if ( row.child.isShown() ) {
2792 // This row is already open - close it
2793 row.child.hide();
2794 tr.removeClass('shown');
2795 } else {
2796 // Open this row
2797 row.child( ___format(row.data()) ).show();
2798 tr.addClass('shown');
2799 }
2800 });
2801 cbf && cbf();
2802 }); // end getOptions
2803 } // render layout
2804
2805 renderInfoBox(title, content, displayPlain) {
2806 let _options = {
2807 title: title,
2808 modal: true,
2809 minWidth: 400,
2810 minHeight: 200,
2811 buttons: [{text:_x('Ok', 'label', 'event-tickets-with-ticket-scanner'),
2812 click: function() {
2813 $(this).dialog("close");
2814 $(this).html("");
2815 }}]
2816 };
2817 let dlg = $('<div/>');
2818 if (displayPlain) {
2819 dlg.text(content);
2820 } else {
2821 dlg.html(content);
2822 }
2823 dlg.dialog(_options);
2824 return dlg;
2825 }
2826 renderSpinnerShow() {
2827 this.div_spinner.css("display", "block");
2828 }
2829 renderSpinnerHide() {
2830 this.div_spinner.css("display", "none");
2831 }
2832 renderFatalError(content) {
2833 return LAYOUT.renderInfoBox(_x('Error', 'title', 'event-tickets-with-ticket-scanner'), content);
2834 }
2835 renderYesNo(title, content, cbfYes, cbfNo) {
2836 let _options = {
2837 title: title,
2838 modal: true,
2839 minWidth: 400,
2840 minHeight: 200,
2841 buttons: [{text:_x('Yes', 'label', 'event-tickets-with-ticket-scanner'), click:function(){
2842 $(this).dialog("close");
2843 $(this).html("");
2844 cbfYes && cbfYes(dlg);
2845 }},{text:_x('No', 'label', 'event-tickets-with-ticket-scanner'), click:function(){
2846 $(this).dialog("close");
2847 $(this).html("");
2848 cbfNo && cbfNo();
2849 }}]
2850 };
2851 let dlg = $('<div/>').html(content);
2852 dlg.dialog(_options);
2853 return dlg;
2854 }
2855 }
2856
2857 function _displayCodeDetails(codeObj, metaObj, tabelle) {
2858 let div = $('<div/>');
2859 function __getData(_codeObj) {
2860 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
2861 updateCodeObject(codeObj, _codeObj);
2862 }
2863
2864 div.html("");
2865 if (codeObj.meta !== "") {
2866 let metaObj = getCodeObjectMeta(codeObj);
2867 if (typeof metaObj.confirmedCount !== "undefined") {
2868 div.append($('<div/>').html('<b>Confirmed count:</b> '+metaObj.confirmedCount));
2869 if (metaObj.confirmedCount > 0 && metaObj.validation) {
2870 if (metaObj.validation.first_success != "") {
2871 div.append($('<div/>').html('<b>First successful validation at:</b> '+metaObj.validation.first_success));
2872 div.append($('<div/>').html('<b>First successful validation IP:</b> '+metaObj.validation.first_ip));
2873 }
2874 if (metaObj.validation.last_success != "" && metaObj.validation.last_success != metaObj.validation.first_success) {
2875 div.append($('<div/>').html('<b>Last successful validation at:</b> '+metaObj.validation.last_success));
2876 div.append($('<div/>').html('<b>Last successful validation IP:</b> '+metaObj.validation.last_ip));
2877 }
2878 }
2879 }
2880 let btngrp = $('<div style="margin-top:10px;">');
2881 if (typeof metaObj.used !== "undefined") {
2882 div.append("<h3>Code marked as used</h3>");
2883 if (metaObj.used.reg_request !== "") {
2884 div.append($("<div>").html("<b>Request from:</b> ").append($('<span>').text(DateTime2Text(metaObj.used.reg_request)+' ('+metaObj.used.reg_request+')')));
2885 div.append($("<div>").html("<b>Request by wordpress user:</b> ").append($('<span>').text(metaObj.used.reg_userid)));
2886 if (metaObj.used._reg_username) div.append($("<div>").html("<b>Request by wordpress user:</b> ").append($('<span>').text(metaObj.used._reg_username)));
2887 div.append($("<div>").html("<b>Request from IP:</b> ").append($('<span>').text(metaObj.used.reg_ip)));
2888
2889 btngrp.append($('<button/>').addClass("button-delete").html('Delete ticket used information').on("click", function(){
2890 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.', ()=>{
2891 _makeGet('removeUsedInformationFromCode', {'code':codeObj.code}, _codeObj=>{
2892 //tabelle.ajax.reload();
2893 __getData(_codeObj);
2894 });
2895 });
2896 }));
2897 } else {
2898 div.append("Not used - still available");
2899 }
2900
2901 btngrp.append($('<button/>').addClass("button-edit").html('Edit wordpress user information').on("click", function(){
2902 // display eingabe maske für userid
2903 function __showMask(){
2904 let _options = {
2905 title: 'Edit requested wordpress user',
2906 modal: true,
2907 minWidth: 400,
2908 minHeight: 200,
2909 buttons: [
2910 {
2911 id: 'okBtn',
2912 text: "Ok",
2913 click: function() {
2914 ___submitForm();
2915 }
2916 },
2917 {
2918 text: "Cancel",
2919 click: function() {
2920 $( this ).dialog( "close" );
2921 $( this ).html('');
2922 }
2923 }
2924 ]
2925 };
2926 let dlg = $('<div />');
2927 let form = $('<form />').appendTo(dlg);
2928
2929 let elem_userid = $('<input type="number" min="0" value="'+metaObj.used.reg_userid+'" />');
2930 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
2931 .html('Requested wordpress userid<br>')
2932 .append(elem_userid)
2933 .appendTo(form);
2934
2935 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>');
2936 dlg.dialog(_options);
2937
2938 form.on("submit", function(event) {
2939 event.preventDefault();
2940 ___submitForm();
2941 });
2942 function ___submitForm() {
2943 let reg_userid = intval(elem_userid.val().trim());
2944 dlg.html(_getSpinnerHTML());
2945 let _data = {"reg_userid":reg_userid};
2946 form[0].reset();
2947 _data.code = codeObj.code;
2948 $('#okBtn').remove();
2949 _makeGet('editUseridForUsedInformationFromCode', _data, _codeObj=>{
2950 //tabelle.ajax.reload();
2951 __getData(_codeObj);
2952 closeDialog(dlg);
2953 }, function() {
2954 closeDialog(dlg);
2955 });
2956 }
2957 } // ende __showMask
2958 __showMask();
2959 })); // end button-edit
2960 }
2961 div.append(btngrp);
2962
2963 if (isPremium()) div.append(PREMIUM.displayCodeDetails(codeObj, tabelle, metaObj));
2964 } // endif codeObj.meta !== ""
2965 }
2966 __getData();
2967 return div;
2968 }
2969
2970 function _displayWCETicket(codeObj, tabelle) {
2971 let div = $('<div/>');
2972 function __getData(_codeObj) {
2973 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
2974 updateCodeObject(codeObj, _codeObj);
2975 }
2976
2977 div.html("");
2978 let metaObj = getCodeObjectMeta(codeObj);
2979 if(metaObj) {
2980 if (typeof metaObj.wc_ticket != "undefined" && typeof metaObj.wc_ticket.day_per_ticket != "undefined") {
2981 div.append($('<div>').html('<b>Date per Ticket (choosen by customer):</b> '+metaObj.wc_ticket.day_per_ticket +" ").append(
2982 $("<button>").html("Edit").on("click", ()=>{
2983
2984 let _options = {
2985 title: 'Edit Ticket Date',
2986 modal: true,
2987 minWidth: 400,
2988 minHeight: 200,
2989 buttons: [
2990 {
2991 id: 'okBtn',
2992 text: "Ok",
2993 click: function() {
2994 ___submitForm();
2995 }
2996 },
2997 {
2998 text: "Cancel",
2999 click: function() {
3000 $( this ).dialog( "close" );
3001 $( this ).html('');
3002 }
3003 }
3004 ]
3005 };
3006 let dlg = $('<div />');
3007 let form = $('<form />').appendTo(dlg);
3008
3009 let elem_input = $('<input type="date" value="'+metaObj.wc_ticket.day_per_ticket+'" />');
3010 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3011 .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>')
3012 .append(elem_input)
3013 .appendTo(form);
3014
3015 dlg.dialog(_options);
3016
3017 form.on("submit", function(event) {
3018 event.preventDefault();
3019 ___submitForm();
3020 });
3021 function ___submitForm() {
3022 let v = elem_input.val().trim();
3023 dlg.html(_getSpinnerHTML());
3024 let _data = {"value":v, "key":'wc_ticket.day_per_ticket'};
3025 form[0].reset();
3026 _data.code = codeObj.code;
3027 $('#okBtn').remove();
3028 _makeGet('editTicketMetaEntry', _data, _codeObj=>{
3029 //tabelle.ajax.reload();
3030 __getData(_codeObj);
3031 closeDialog(dlg);
3032 }, function() {
3033 closeDialog(dlg);
3034 });
3035 }
3036 })
3037 ));
3038 }
3039 if (typeof metaObj.wc_ticket != "undefined" && typeof metaObj.wc_ticket.name_per_ticket != "undefined") {
3040 div.append($('<div>').html('<b>Name per Ticket (product detail setting):</b> '+metaObj.wc_ticket.name_per_ticket +" ").append(
3041 $("<button>").html("Edit").on("click", ()=>{
3042
3043 let _options = {
3044 title: 'Edit Ticket Name',
3045 modal: true,
3046 minWidth: 400,
3047 minHeight: 200,
3048 buttons: [
3049 {
3050 id: 'okBtn',
3051 text: "Ok",
3052 click: function() {
3053 ___submitForm();
3054 }
3055 },
3056 {
3057 text: "Cancel",
3058 click: function() {
3059 $( this ).dialog( "close" );
3060 $( this ).html('');
3061 }
3062 }
3063 ]
3064 };
3065 let dlg = $('<div />');
3066 let form = $('<form />').appendTo(dlg);
3067
3068 let elem_input = $('<input type="text" value="'+metaObj.wc_ticket.name_per_ticket+'" />');
3069 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3070 .html('Name per Ticket<br>')
3071 .append(elem_input)
3072 .appendTo(form);
3073
3074 dlg.dialog(_options);
3075
3076 form.on("submit", function(event) {
3077 event.preventDefault();
3078 ___submitForm();
3079 });
3080 function ___submitForm() {
3081 let v = elem_input.val().trim();
3082 dlg.html(_getSpinnerHTML());
3083 let _data = {"value":v, "key":'wc_ticket.name_per_ticket'};
3084 form[0].reset();
3085 _data.code = codeObj.code;
3086 $('#okBtn').remove();
3087 _makeGet('editTicketMetaEntry', _data, _codeObj=>{
3088 //tabelle.ajax.reload();
3089 __getData(_codeObj);
3090 closeDialog(dlg);
3091 }, function() {
3092 closeDialog(dlg);
3093 });
3094 }
3095 })
3096 ));
3097 }
3098 if (typeof metaObj.wc_ticket != "undefined" && typeof metaObj.wc_ticket.value_per_ticket != "undefined") {
3099 div.append($('<div>').html('<b>Value per Ticket (product detail setting):</b> '+metaObj.wc_ticket.value_per_ticket +" ").append(
3100 $("<button>").html("Edit").on("click", ()=>{
3101
3102 let _options = {
3103 title: 'Edit Ticket Value',
3104 modal: true,
3105 minWidth: 400,
3106 minHeight: 200,
3107 buttons: [
3108 {
3109 id: 'okBtn',
3110 text: "Ok",
3111 click: function() {
3112 ___submitForm();
3113 }
3114 },
3115 {
3116 text: "Cancel",
3117 click: function() {
3118 $( this ).dialog( "close" );
3119 $( this ).html('');
3120 }
3121 }
3122 ]
3123 };
3124 let dlg = $('<div />');
3125 let form = $('<form />').appendTo(dlg);
3126
3127 let elem_input = $('<input type="text" value="'+metaObj.wc_ticket.value_per_ticket+'" />');
3128 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3129 .html('Value per Ticket<br>')
3130 .append(elem_input)
3131 .appendTo(form);
3132
3133 dlg.dialog(_options);
3134
3135 form.on("submit", function(event) {
3136 event.preventDefault();
3137 ___submitForm();
3138 });
3139 function ___submitForm() {
3140 let v = elem_input.val().trim();
3141 dlg.html(_getSpinnerHTML());
3142 let _data = {"value":v, "key":'wc_ticket.value_per_ticket'};
3143 form[0].reset();
3144 _data.code = codeObj.code;
3145 $('#okBtn').remove();
3146 _makeGet('editTicketMetaEntry', _data, _codeObj=>{
3147 //tabelle.ajax.reload();
3148 __getData(_codeObj);
3149 closeDialog(dlg);
3150 }, function() {
3151 closeDialog(dlg);
3152 });
3153 }
3154 })
3155 ));
3156 }
3157 if (typeof metaObj['woocommerce'] !== "undefined" && metaObj.woocommerce.order_id !== 0 && typeof metaObj.wc_ticket !== "undefined") {
3158 if (metaObj.wc_ticket.set_by_admin > 0) {
3159 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)));
3160 }
3161 if (metaObj.wc_ticket.redeemed_date != '') {
3162 div.append($("<div>").html("<b>Redeemed at:</b> ").append($('<span>').text(DateTime2Text(metaObj.wc_ticket.redeemed_date)+' ('+metaObj.wc_ticket.redeemed_date+')')));
3163 div.append($("<div>").html("<b>Redeemed by wordpress userid:</b> ").append($('<span>').text(metaObj.wc_ticket.userid)));
3164 if (metaObj.wc_ticket._username) div.append($("<div>").html("<b>Redeemed by wordpress user:</b> ").append($('<span>').text(metaObj.wc_ticket._username)));
3165 div.append($("<div>").html("<b>IP while redeemed:</b> ").append($('<span>').text(metaObj.wc_ticket.ip)));
3166 if (metaObj.wc_ticket.redeemed_by_admin > 0) {
3167 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+')')));
3168 }
3169 }
3170 if (metaObj.wc_ticket.is_ticket == 1) {
3171 let _max_redeem_amount = typeof metaObj.wc_ticket._max_redeem_amount !== "undefined" ? metaObj.wc_ticket._max_redeem_amount : 1;
3172 $("<div>").html("<b>Ticket number: </b>"+codeObj.code_display).appendTo(div);
3173 $("<div>").html("<b>Public Ticket number: </b>"+metaObj.wc_ticket._public_ticket_id).appendTo(div);
3174 if (typeof metaObj.wc_ticket.stats_redeemed !== "undefined") {
3175 $("<div>").html("<b>Redeem usage: </b>"+metaObj.wc_ticket.stats_redeemed.length + ' of ' + (_max_redeem_amount == 0 ? 'unlimited' : _max_redeem_amount)).appendTo(div);
3176 }
3177 $("<div>").html('<b>Ticket Page:</b> <a target="_blank" href="'+metaObj.wc_ticket._url+'">Open Ticket Detail Page</a>').appendTo(div);
3178 $("<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);
3179 $("<div>").html('<b>Ticket PDF:</b> <a target="_blank" href="'+metaObj.wc_ticket._url+'?pdf">Open Ticket PDF</a>').appendTo(div);
3180 $("<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);
3181 $("<div>").html('<b>Order Ticket Page:</b> <a target="_blank" href="'+metaObj.wc_ticket._order_page_url+'">Open Order Ticket Page</a>').appendTo(div);
3182 $("<div>").html('<b>Order PDF:</b> <a target="_blank" href="'+metaObj.wc_ticket._order_url+'">Open Order Ticket PDF</a>').appendTo(div);
3183 }
3184
3185 let btngrp = $('<div style="margin-top:10px;">').appendTo(div);
3186 if (metaObj.wc_ticket.is_ticket == 1) {
3187 $('<button>').html("Download PDF").appendTo(btngrp).on("click", ()=>{
3188 _downloadFile('downloadPDFTicket', {'code':codeObj.code}, "eventticket_"+codeObj.code+".pdf");
3189 return false;
3190 });
3191 $('<button>').html("Download Ticket Badge").appendTo(btngrp).on("click", ()=>{
3192 _downloadFile('downloadPDFTicketBadge', {'code':codeObj.code}, "eventticket_badge_"+codeObj.code+".pdf");
3193 return false;
3194 });
3195 $('<button>').html("Display QR with URL to PDF").appendTo(btngrp).on("click", e=>{
3196 let id = 'qrcode_'+codeObj.code+'_'+time();
3197 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>';
3198 LAYOUT.renderInfoBox('QR with URL to PDF', content);
3199 });
3200 }
3201 if (metaObj.wc_ticket.is_ticket == 0) {
3202 $('<button>').html("Set as ticket sale").on("click", ()=>{
3203 LAYOUT.renderYesNo('Set as a ticket', 'Do you want to set this purchased ticket number as a ticket sale?', ()=>{
3204 _makeGet('setWoocommerceTicketForCode', {'code':codeObj.code}, _codeObj=>{
3205 __getData(_codeObj);
3206 });
3207 });
3208 }).appendTo(btngrp);
3209 }
3210 let btn_redeem = $('<button>').addClass("button-delete").html('Redeem ticket').on("click", ()=>{
3211 let reg_userid = (metaObj.user && metaObj.user.reg_userid) ? metaObj.user.reg_userid : 0;
3212 LAYOUT.renderYesNo('Redeem ticket', 'Do you really want to redeem the ticket number "'+codeObj.code_display+'"? Click OK to redeem the ticket.', ()=>{
3213 let userid = prompt('Optional. You can enter a userid you redeem the ticket for', reg_userid);
3214 _makeGet('redeemWoocommerceTicketForCode', {'code':codeObj.code, 'userid':userid}, _codeObj=>{
3215 __getData(_codeObj);
3216 });
3217 });
3218 }).appendTo(btngrp);
3219 let _max_redeem_amount = typeof metaObj.wc_ticket._max_redeem_amount !== "undefined" ? metaObj.wc_ticket._max_redeem_amount : 1;
3220 if (metaObj.wc_ticket.is_ticket == 0 || _max_redeem_amount == 0 || metaObj.wc_ticket.stats_redeemed.length >= _max_redeem_amount) {
3221 btn_redeem.attr("disabled", true);
3222 }
3223
3224 let btn_unredeem = $('<button>').addClass("button-delete").html('Delete redeem information').on("click", ()=>{
3225 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.', ()=>{
3226 _makeGet('removeRedeemWoocommerceTicketForCode', {'code':codeObj.code}, _codeObj=>{
3227 __getData(_codeObj);
3228 });
3229 });
3230 }).appendTo(btngrp);
3231 if (metaObj.wc_ticket.is_ticket == 0 || metaObj.wc_ticket.redeemed_date == "") {
3232 btn_unredeem.attr("disabled", true);
3233 }
3234 if (metaObj.wc_ticket.is_ticket == 1 && metaObj.wc_ticket.redeemed_date == "") {
3235 $('<button>').addClass("button-delete").html("Unset Ticket").on("click", ()=>{
3236 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.', ()=>{
3237 _makeGet('removeWoocommerceTicketForCode', {'code':codeObj.code}, _codeObj=>{
3238 __getData(_codeObj);
3239 });
3240 });
3241 }).appendTo(btngrp);
3242 }
3243 }
3244 }
3245 }
3246 __getData();
3247 return div;
3248 }
3249
3250 function _displayRedeemOperationsForCode(d, metaObj) {
3251 let div = $('<div/>');
3252 if (typeof metaObj.wc_ticket.stats_redeemed !== "undefined") {
3253 if (metaObj.wc_ticket.stats_redeemed.length > 0) {
3254 let t = $('<table>').appendTo(div);
3255 t.html('<tr><th>#</th><th>Date</th><th>IP</th><th>By admin</th><th>User ID</th></tr>').appendTo(t);
3256 metaObj.wc_ticket.stats_redeemed.forEach((v,idx)=>{
3257 let tr = $('<tr>').appendTo(t);
3258 $('<td>').html('#'+(idx+1)).appendTo(tr);
3259 $('<td>').html(DateTime2Text(v.redeemed_date)+' ('+v.redeemed_date+')').appendTo(tr);
3260 $('<td>').html(v.ip).appendTo(tr);
3261 $('<td>').html(v.redeemed_by_admin == 1 ? 'Yes' : 'No').appendTo(tr);
3262 $('<td>').html(v.userid).appendTo(tr);
3263 });
3264 } else {
3265 div.html("no redeem operations yet");
3266 }
3267 }
3268 return div;
3269 }
3270
3271 function _displayRegisteredUserForCode(codeObj, metaObj, tabelle) {
3272 let div = $('<div/>');
3273 function __getData(_codeObj) {
3274 if (_codeObj) { // um eine Aktualisierung in das codeObj aufzunehmen
3275 updateCodeObject(codeObj, _codeObj);
3276 }
3277 div.html("");
3278 let btngrp = $('<div style="margin-top:10px;">');
3279 if (typeof codeObj.meta !== "undefined" && codeObj.meta !== "") {
3280 let metaObj = getCodeObjectMeta(codeObj);
3281 if (metaObj.user.reg_request !== "") {
3282 div.append($("<div>").html("<b>Register value:</b> ").append($('<span>').text(metaObj.user.value)));
3283 div.append($("<div>").html("<b>Register by wordpress userid:</b> ").append($('<span>').text(metaObj.user.reg_userid)));
3284 if (metaObj.user._reg_username) div.append($("<div>").html("<b>Register by wordpress user:</b> ").append($('<span>').text(metaObj.user._reg_username)));
3285 div.append($("<div>").html("<b>Request from:</b> ").append($('<span>').text(metaObj.user.reg_request)));
3286 div.append($("<div>").html("<b>Request from IP:</b> ").append($('<span>').text(metaObj.user.reg_ip)));
3287 btngrp.append($('<button/>').addClass("button-delete").html('Delete registered user information').on("click", function(){
3288 LAYOUT.renderYesNo('Remove register user value', 'Do you really want to remove the registered user value of this ticket "'+codeObj.code_display+'"?', ()=>{
3289 // sende delete user from code operation zum server
3290 div.html(_getSpinnerHTML());
3291 _makeGet('removeUserRegistrationFromCode', {'code':codeObj.code}, _codeObj=>{
3292 //tabelle.ajax.reload();
3293 __getData(_codeObj);
3294 });
3295 });
3296 }));
3297 } else {
3298 div.append("No registration to this ticket done");
3299 }
3300
3301 btngrp.append($('<button/>').addClass("button-edit").html('Edit registered user information').on("click", function(){
3302 // display eingabe maske für value und userid
3303 function __showMask(){
3304 let _options = {
3305 title: 'Edit registered user',
3306 modal: true,
3307 minWidth: 400,
3308 minHeight: 200,
3309 buttons: [
3310 {
3311 id: 'okBtn',
3312 text: "Ok",
3313 click: function() {
3314 ___submitForm();
3315 }
3316 },
3317 {
3318 text: "Cancel",
3319 click: function() {
3320 $( this ).dialog( "close" );
3321 $( this ).html('');
3322 }
3323 }
3324 ]
3325 };
3326 let dlg = $('<div />');
3327 let form = $('<form />').appendTo(dlg);
3328
3329 let elem_value = $('<input type="text" value="'+metaObj.user.value+'" />');
3330 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3331 .html('Registered value<br>')
3332 .append(elem_value)
3333 //.append('<br><i>If CVV is set, then your user will be asked to enter also the CVV to check the serial code.</i>')
3334 .appendTo(form);
3335 let elem_userid = $('<input type="number" min="0" value="'+metaObj.user.reg_userid+'" />');
3336 $('<div/>').css({"margin-top":"10px","margin-bottom": "15px","margin-right": "15px"})
3337 .html('Registered wordpress userid<br>')
3338 .append(elem_userid)
3339 .appendTo(form);
3340
3341 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>');
3342 dlg.dialog(_options);
3343
3344 form.on("submit", function(event) {
3345 event.preventDefault();
3346 ___submitForm();
3347 });
3348 function ___submitForm() {
3349 let reg_userid = intval(elem_userid.val().trim());
3350 let reg_value = elem_value.val().trim();
3351 dlg.html(_getSpinnerHTML());
3352 let _data = {"value":reg_value, "reg_userid":reg_userid};
3353 form[0].reset();
3354 _data.code = codeObj.code;
3355 $('#okBtn').remove();
3356 _makeGet('editUseridForUserRegistrationFromCode', _data, _codeObj=>{
3357 //tabelle.ajax.reload();
3358 __getData(_codeObj);
3359 closeDialog(dlg);
3360 }, function() {
3361 closeDialog(dlg);
3362 });
3363 }
3364 } // ende __showMask
3365 __showMask();
3366 })); // end button-edit
3367 div.append(btngrp);
3368 if (isPremium()) div.append(PREMIUM.displayRegisteredUserForCode(codeObj, tabelle, metaObj));
3369 } // endif typeof codeObj.meta !== "undefined" && codeObj.meta !== ""
3370 }
3371 __getData();
3372 return div;
3373 }
3374
3375 function addStyleCode(content) {
3376 let c = document.createElement('style');
3377 c.innerHTML = content;
3378 document.getElementsByTagName("head")[0].appendChild(c);
3379 }
3380 function addStyleTag(url, id, onloadfkt, attrListe, loadLatest) {
3381 var script = document.createElement('link');
3382 script.type = 'text/css';
3383 script.rel = "stylesheet";
3384 let myId = id;
3385 if (!myId) myId = url;
3386 if (document.getElementById(id) && document.getElementById(id).src === url) {
3387 onloadfkt && onloadfkt();
3388 return; // prevent re-adding the same tag
3389 }
3390 script.id = id;
3391 if (attrListe) for(var attr in attrListe) script.setAttribute(attr, attrListe[attr]);
3392 script.href = url;
3393 if (loadLatest) script.href += '?t='+new Date().getTime();
3394 if (typeof onloadfkt !== "undefined") script.onload = onloadfkt;
3395 document.getElementsByTagName("head")[0].appendChild(script);
3396 }
3397 function addScriptCode(content, id) {
3398 if (typeof system.DYNJS_CACHE.scriptCodeElements === "undefined") {
3399 system.DYNJS_CACHE.scriptCodeElements = {};
3400 }
3401 let c;
3402 if (id && typeof system.DYNJS_CACHE.scriptCodeElements[id] !== "undefined") {
3403 c = system.DYNJS_CACHE.scriptCodeElements[id];
3404 document.getElementsByTagName("head")[0].removeChild(c);
3405 } else {
3406 c = document.createElement('script');
3407 }
3408 c.innerHTML = content;
3409 if (id) {
3410 system.DYNJS_CACHE.scriptCodeElements[id] = c;
3411 }
3412 document.getElementsByTagName("head")[0].appendChild(c);
3413 }
3414 function addScriptTag(url, id, onloadfkt, attrListe, loadLatest) {
3415 var head = document.getElementsByTagName("head")[0];
3416 var script = document.createElement('script');
3417 script.type = 'text/javascript';
3418 let myId = id;
3419 if (!myId) myId = url;
3420 if (document.getElementById(id) && document.getElementById(id).src === url) {
3421 onloadfkt && onloadfkt();
3422 return; // prevent re-adding the same tag
3423 }
3424 script.id = id;
3425 if (attrListe) for(var attr in attrListe) script.setAttribute(attr, attrListe[attr]);
3426 script.src = url;
3427 if (loadLatest) script.src += '?t='+new Date().getTime();
3428 if (typeof onloadfkt !== "undefined") script.onload = onloadfkt;
3429 head.appendChild(script);
3430 }
3431
3432 function getLabelPremiumOnly() {
3433 return '[<a href="https://vollstart.com/event-tickets-with-ticket-scanner/">PREMIUM ONLY</a>]';
3434 }
3435
3436 function _getSpinnerHTML() {
3437 return '<span class="lds-dual-ring"></span>';
3438 }
3439
3440 function _loadingJSDatatables(cbf) {
3441 let loaded = {};
3442 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;}');
3443 addStyleTag(myAjax._plugin_home_url+'/3rd/datatables.min.css', 'jquery_dataTables', ()=>{
3444 loaded['1'] = true;
3445 if (loaded['2']) {
3446 cbf && cbf();
3447 }
3448 }, {'crossorigin':"anonymous"});
3449 addScriptTag(myAjax._plugin_home_url+"/3rd/datatables.min.js", 'jquery_dataTables', ()=>{
3450 loaded['2'] = true;
3451 if (loaded['1']) {
3452 cbf && cbf();
3453 }
3454 }, {'crossorigin':"anonymous", "charset":"utf8"});
3455 }
3456
3457 function isPremium() {
3458 return myAjax._isPremium == "1" || myAjax._isPremium === true;
3459 }
3460
3461 var BulkActions = {
3462 'codes': {
3463 'delete': {
3464 "label": _x('Delete', 'label', 'event-tickets-with-ticket-scanner'),
3465 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3466 LAYOUT.renderYesNo('Delete all selected tickets?', 'Are you sure, you want to delete all selected tickets?<br><br>'+selectedElems.length+' tickets will be deleted.', ()=>{
3467 let _data = {'ids':[]};
3468 selectedElems.forEach(v=>{
3469 _data.ids.push($(v).attr("data-key"));
3470 });
3471 _makePost('removeCodes', _data, result=>{
3472 tabelle_codes_datatable.ajax.reload();
3473 });
3474 });
3475 }
3476 },
3477 'remove_marked_used': {
3478 "label": _x("Remove marked as used", 'option', 'event-tickets-with-ticket-scanner'),
3479 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3480 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.', ()=>{
3481 let _data = {'ids':[], 'codes':[]};
3482 selectedElems.forEach(v=>{
3483 _data.ids.push($(v).attr("data-key"));
3484 _data.codes.push($(v).attr("data-code"));
3485 });
3486 _makePost('removeUsedInformationFromCodeBulk', _data, result=>{
3487 tabelle_codes_datatable.ajax.reload();
3488 });
3489 });
3490 }
3491 },
3492 'remove_ticket_redeemed': {
3493 "label": _x("Delete Redeem Information", 'option', 'event-tickets-with-ticket-scanner'),
3494 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3495 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.', ()=>{
3496 let _data = {'ids':[], 'codes':[]};
3497 selectedElems.forEach(v=>{
3498 _data.ids.push($(v).attr("data-key"));
3499 _data.codes.push($(v).attr("data-code"));
3500 });
3501 _makePost('removeRedeemWoocommerceTicketForCodeBulk', _data, result=>{
3502 tabelle_codes_datatable.ajax.reload();
3503 });
3504 });
3505 }
3506 },
3507 'generate_pdf': {
3508 "label": _x("Generate ticket PDF", 'option', 'event-tickets-with-ticket-scanner'),
3509 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3510 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.', ()=>{
3511 let _data = {'ids':[], 'codes':[]};
3512 selectedElems.forEach(v=>{
3513 _data.ids.push($(v).attr("data-key"));
3514 _data.codes.push($(v).attr("data-code"));
3515 });
3516 _downloadFile('generateOnePDFForTicketsBulk', _data, "tickets_merged.pdf");
3517 });
3518 }
3519 },
3520 'generate_badge': {
3521 "label": _x("Generate badge ticket", 'option', 'event-tickets-with-ticket-scanner'),
3522 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3523 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.', ()=>{
3524 let _data = {'ids':[], 'codes':[]};
3525 selectedElems.forEach(v=>{
3526 _data.ids.push($(v).attr("data-key"));
3527 _data.codes.push($(v).attr("data-code"));
3528 });
3529 _downloadFile('generateOnePDFForBadgesBulk', _data, "ticketbadges_merged.pdf");
3530 });
3531 }
3532 },
3533 'move_to_list':{
3534 "label": _x("Move to ticket list", 'option', 'event-tickets-with-ticket-scanner'),
3535 "fkt": (selectedElems, tabelle_codes_datatable)=>{
3536 let content = $('<div>');
3537 let div_code_list = _createDivInput(_x('Assign selected tickets to this ticket list', 'label', 'event-tickets-with-ticket-scanner')).appendTo(content);
3538 let input_code_list = $('<select><option value="0">'+_x('None', 'option value', 'event-tickets-with-ticket-scanner')+'</select></select>').appendTo(div_code_list);
3539 DATA_LISTS.forEach(v=>{
3540 input_code_list.append('<option value="'+v.id+'">'+v.name+'</option>');
3541 });
3542 content.append("<br>");
3543 LAYOUT.renderYesNo('Move ticket(s) to ticket list', content, ()=>{
3544 let _data = {'ids':[], 'codes':[], 'list_id':input_code_list.val()};
3545 selectedElems.forEach(v=>{
3546 _data.ids.push($(v).attr("data-key"));
3547 _data.codes.push($(v).attr("data-code"));
3548 });
3549 _makePost('assignTicketListToTicketsBulk', _data, result=>{
3550 tabelle_codes_datatable.ajax.reload();
3551 });
3552 });
3553 }
3554 }
3555 }
3556 }
3557
3558 function addTabCSS() {
3559 $('<style>')
3560 .prop('type', 'text/css')
3561 .html(`
3562 .tabs {
3563 width: 100%;
3564 display: block;
3565 }
3566 .tab-nav {
3567 list-style: none;
3568 padding: 0;
3569 margin: 0;
3570 display: flex;
3571 border-bottom: 1px solid #ccc;
3572 }
3573 .tab-nav li {
3574 margin: 0;
3575 }
3576 .tab-nav a {
3577 display: block;
3578 padding: 10px 20px;
3579 text-decoration: none;
3580 color: #333;
3581 border: 1px solid #ccc;
3582 border-bottom: none;
3583 background: #f9f9f9;
3584 margin-right: 5px;
3585 border-radius: 5px 5px 0 0;
3586 }
3587 .tab-nav a.active {
3588 background: #fff;
3589 border-bottom: 1px solid #fff;
3590 font-weight: bold;
3591 }
3592 .tab-content {
3593 display: none;
3594 padding: 20px;
3595 border: 1px solid #ccc;
3596 border-radius: 0 5px 5px 5px;
3597 background: #fff;
3598 }
3599 `)
3600 .appendTo('head');
3601 }
3602
3603 function getHelperFunktions() {
3604 return {
3605 _getSpinnerHTML:_getSpinnerHTML,
3606 _makePost:_makePost,
3607 _makeGet:_makeGet,
3608 _downloadFile:_downloadFile,
3609 _requestURL:_requestURL,
3610 _getLAYOUT:function(){ return LAYOUT;},
3611 _getDIV:function(){ return DIV;},
3612 _BulkActions:BulkActions,
3613 _closeDialog:closeDialog,
3614 _OPTIONS:function(){ return OPTIONS;},
3615 _updateCodeObject:updateCodeObject,
3616 _getCodeObjectMeta:getCodeObjectMeta,
3617 _DateTime2Text:DateTime2Text,
3618 _compareVersions:compareVersions,
3619 _getBackButtonDiv:getBackButtonDiv
3620 };
3621 }
3622
3623 function refreshNoncePeriodically() {
3624 // check if the last check of nonce is older than 4 minutes
3625 // do a ping to get the new nonce
3626 setInterval(()=>{
3627 let last_check = DATA.last_nonce_check;
3628 if (last_check == null || last_check == "") {
3629 last_check = 0;
3630 }
3631 let now = new Date().getTime();
3632 if (now - last_check > 240000) {
3633 _makeGet('ping', [], data=>{
3634 });
3635 }
3636 }, 60000);
3637 }
3638
3639 function init() {
3640 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);}}');
3641 addStyleTag(myAjax._plugin_home_url+'/css/styles_backend.css');
3642
3643 addScriptTag(myAjax._plugin_home_url+'/3rd/ace/ace.js');
3644
3645 addTabCSS();
3646
3647 DIV = $('#'+myAjax.divId);
3648 DIV.html(_getSpinnerHTML());
3649 LAYOUT = new Layout();
3650 function _init() {
3651 _loadingJSDatatables(function() {
3652 if (typeof PARAS.display !== "undefined" && PARAS.display == 'options') {
3653 _displayOptionsArea();
3654 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'support') {
3655 _displaySupportInfoArea();
3656 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'authtokens') {
3657 _displayAuthTokensArea();
3658 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'faq') {
3659 _displayFAQArea();
3660 } else if (typeof PARAS.display !== "undefined" && PARAS.display == 'seatingplan') {
3661 _displaySeatingplanArea();
3662 } else {
3663 LAYOUT.renderAdminPageLayout();
3664 }
3665 });
3666 }
3667
3668 if (isPremium() && myAjax._premJS !== "") {
3669 addScriptTag(myAjax._premJS, null, function() {
3670 PREMIUM = new sasoEventticketsPremium(myAjax, getHelperFunktions());
3671 _init();
3672 });
3673 } else {
3674 _init();
3675 }
3676 $('#wpfooter').css('display', 'none');
3677 refreshNoncePeriodically();
3678 }
3679 if (!doNotInit) init();
3680 return {
3681 init: init,
3682 form_fields_serial_format:_form_fields_serial_format
3683 };
3684
3685 }
3686 if (typeof Ajax_sasoEventtickets !== "undefined") {
3687 window.sasoEventtickets_backend = sasoEventtickets(Ajax_sasoEventtickets);
3688 }