PluginProbe ʕ •ᴥ•ʔ
Event Tickets with Ticket Scanner / 2.8.5
Event Tickets with Ticket Scanner v2.8.5
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 / index.php
event-tickets-with-ticket-scanner Last commit date
3rd 5 months ago css 5 months ago img 5 months ago includes 5 months ago js 5 months ago languages 5 months ago ticket 5 months ago vendors 5 months ago .gitignore 5 months ago SASO_EVENTTICKETS.php 5 months ago backend.js 5 months ago changelog.txt 5 months ago db.php 5 months ago index.php 5 months ago init_file.php 5 months ago order_details.js 5 months ago readme.txt 5 months ago saso-eventtickets-validator.js 5 months ago sasoEventtickets_AdminSettings.php 5 months ago sasoEventtickets_Authtoken.php 5 months ago sasoEventtickets_Base.php 5 months ago sasoEventtickets_Core.php 5 months ago sasoEventtickets_Frontend.php 5 months ago sasoEventtickets_Messenger.php 5 months ago sasoEventtickets_Options.php 5 months ago sasoEventtickets_PDF.php 5 months ago sasoEventtickets_Seating.php 5 months ago sasoEventtickets_Ticket.php 5 months ago sasoEventtickets_TicketBadge.php 5 months ago sasoEventtickets_TicketDesigner.php 5 months ago sasoEventtickets_TicketQR.php 5 months ago ticket_events.js 5 months ago ticket_scanner.js 5 months ago validator.js 5 months ago wc_backend.js 5 months ago wc_frontend.js 5 months ago woocommerce-hooks.php 5 months ago
index.php
1665 lines
1 <?php
2 /**
3 * Plugin Name: Event Tickets with Ticket Scanner
4 * Plugin URI: https://vollstart.com/event-tickets-with-ticket-scanner/docs/
5 * Description: You can create and generate tickets and codes. You can redeem the tickets at entrance using the built-in ticket scanner. You customer can download a PDF with the ticket information. The Premium allows you also to activate user registration and more. This allows your user to register them self to a ticket.
6 * Version: 2.8.5
7 * Author: Vollstart
8 * Author URI: https://vollstart.com
9 * Text Domain: event-tickets-with-ticket-scanner
10 *
11 * Event Tickets with Ticket Scanner is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16 // https://semver.org/
17 // https://developer.wordpress.org/plugins/security/securing-output/
18 // https://developer.wordpress.org/plugins/security/securing-input/
19
20 include_once(plugin_dir_path(__FILE__)."init_file.php");
21
22 if (!defined('SASO_EVENTTICKETS_PLUGIN_VERSION'))
23 define('SASO_EVENTTICKETS_PLUGIN_VERSION', '2.8.5');
24 if (!defined('SASO_EVENTTICKETS_PLUGIN_DIR_PATH'))
25 define('SASO_EVENTTICKETS_PLUGIN_DIR_PATH', plugin_dir_path(__FILE__));
26
27 include_once plugin_dir_path(__FILE__)."SASO_EVENTTICKETS.php";
28
29 class sasoEventtickets_fakeprem{}
30 class sasoEventtickets {
31 private $_js_version;
32 private $_js_file = 'saso-eventtickets-validator.js';
33 public $_js_nonce = 'sasoEventtickets';
34 public $_do_action_prefix = 'saso_eventtickets_';
35 public $_add_filter_prefix = 'saso_eventtickets_';
36 protected $_prefix = 'sasoEventtickets';
37 public $_prefix_session = 'sasoEventtickets_';
38 protected $_shortcode = 'sasoEventTicketsValidator';
39 protected $_shortcode_mycode = 'sasoEventTicketsValidator_code';
40 protected $_shortcode_eventviews = 'sasoEventTicketsValidator_eventsview';
41 protected $_shortcode_ticket_scanner = 'sasoEventTicketsValidator_ticket_scanner';
42 protected $_shortcode_feature_list = 'sasoEventTicketsValidator_feature_list';
43 protected $_shortcode_ticket_detail = 'sasoEventTicketsValidator_ticket_detail';
44 protected $_divId = 'sasoEventtickets';
45
46 private $_isPrem = null;
47 private $_premium_plugin_name = 'event-tickets-with-ticket-scanner-premium';
48 private $_premium_function_file = 'sasoEventtickets_PremiumFunctions.php';
49 private $PREMFUNCTIONS = null;
50 private $BASE = null;
51 private $CORE = null;
52 private $ADMIN = null;
53 private $FRONTEND = null;
54 private $OPTIONS = null;
55
56 private $isAllowedAccess = null;
57
58 public static function Instance() {
59 static $inst = null;
60 if ($inst === null) {
61 $inst = new self();
62 }
63 return $inst;
64 }
65
66 public function __construct() {
67 $this->_js_version = $this->getPluginVersion();
68 $this->initHandlers();
69 }
70
71 public function initHandlers() {
72 add_action( 'init', [$this, 'load_plugin_textdomain'] );
73 add_action( 'upgrader_process_complete', [$this, 'listener_upgrader_process_complete'], 10, 2 );
74 //add_action('admin_init', [$this, 'initialize_plugin']);
75 if (is_admin()) { // called in backend admin, admin-ajax!
76 $this->init_backend();
77 } else { // called in front end
78 $this->init_frontend();
79 }
80 add_action( 'sasoEventtickets_cronjob_daily', [$this, 'relay_sasoEventtickets_cronjob_daily'], 10, 0 ); // set in tickets.php
81 add_action( 'plugins_loaded', [$this, 'WooCommercePluginLoaded'], 20, 0 );
82 if (basename($_SERVER['SCRIPT_NAME']) == "admin-ajax.php") {
83 add_action('wp_ajax_nopriv_'.$this->_prefix.'_executeFrontend', [$this,'executeFrontend_a'], 10, 0); // nicht angemeldete user, sollen eine antwort erhalten
84 add_action('wp_ajax_'.$this->_prefix.'_executeFrontend', [$this,'executeFrontend_a'], 10, 0); // falls eingeloggt ist
85 add_action('wp_ajax_'.$this->_prefix.'_executeWCBackend', [$this,'executeWCBackend'], 10, 0); // falls eingeloggt ist
86 add_action('wp_ajax_'.$this->_prefix.'_downloadMyCodesAsPDF', [$this,'downloadMyCodesAsPDF'], 10, 0); // logged in users only
87 }
88 if (method_exists($this->getPremiumFunctions(), "initHandlers")) {
89 $this->getPremiumFunctions()->initHandlers();
90 }
91 $this->cronjob_daily_activate();
92 }
93 public function cronjob_daily_activate() {
94 $args = [];
95 if (! wp_next_scheduled ( 'sasoEventtickets_cronjob_daily', $args )) {
96 wp_schedule_event( strtotime("00:05"), 'daily', 'sasoEventtickets_cronjob_daily', $args );
97 }
98 }
99 public function cronjob_daily_deactivate() {
100 wp_clear_scheduled_hook( 'sasoEventtickets_cronjob_daily' );
101 }
102 public function load_plugin_textdomain() {
103 load_plugin_textdomain( 'event-tickets-with-ticket-scanner', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
104 }
105 public function getPluginPath() {
106 return SASO_EVENTTICKETS_PLUGIN_DIR_PATH;
107 }
108 public function getPluginVersion() {
109 return SASO_EVENTTICKETS_PLUGIN_VERSION;
110 }
111 public function getPluginVersions() {
112 $ret = ['basic'=>SASO_EVENTTICKETS_PLUGIN_VERSION, 'premium'=>'', 'debug'=>''];
113 if (defined('SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION')) {
114 $ret['premium'] = SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION;
115 }
116 if (defined('WP_DEBUG') && WP_DEBUG) {
117 $ret['debug'] = esc_html__('is active', 'event-tickets-with-ticket-scanner');
118 }
119 return $ret;
120 }
121 public function getDB() {
122 return SASO_EVENTTICKETS::getDB(plugin_dir_path(__FILE__), "sasoEventticketsDB", $this);
123 }
124 public function getBase() {
125 if ($this->BASE == null) {
126 if (!class_exists('sasoEventtickets_Base')) {
127 include_once plugin_dir_path(__FILE__)."sasoEventtickets_Base.php";
128 }
129 $this->BASE = new sasoEventtickets_Base($this);
130 }
131 return $this->BASE;
132 }
133 public function getCore() {
134 if ($this->CORE == null) {
135 if (!class_exists('sasoEventtickets_Core')) {
136 include_once plugin_dir_path(__FILE__)."sasoEventtickets_Core.php";
137 }
138 $this->CORE = new sasoEventtickets_Core($this);
139 }
140 return $this->CORE;
141 }
142 public function getAdmin() {
143 if ($this->ADMIN == null) {
144 if (!class_exists('sasoEventtickets_AdminSettings')) {
145 include_once plugin_dir_path(__FILE__)."sasoEventtickets_AdminSettings.php";
146 }
147 $this->ADMIN = new sasoEventtickets_AdminSettings($this);
148 }
149 return $this->ADMIN;
150 }
151 public function getFrontend() {
152 if ($this->FRONTEND == null) {
153 if (!class_exists('sasoEventtickets_Frontend')) {
154 include_once plugin_dir_path(__FILE__)."sasoEventtickets_Frontend.php";
155 }
156 $this->FRONTEND = new sasoEventtickets_Frontend($this);
157 }
158 return $this->FRONTEND;
159 }
160 public function getOptions() {
161 if ($this->OPTIONS == null) {
162 if (!class_exists('sasoEventtickets_Options')) {
163 include_once plugin_dir_path(__FILE__)."sasoEventtickets_Options.php";
164 }
165 $this->OPTIONS = new sasoEventtickets_Options($this, $this->_prefix);
166 $this->OPTIONS->initOptions();
167 }
168 return $this->OPTIONS;
169 }
170 public function getNewPDFObject() {
171 if (!class_exists('sasoEventtickets_PDF')) {
172 require_once("sasoEventtickets_PDF.php");
173 }
174 $pdf = new sasoEventtickets_PDF();
175 $pdf->setFontSize($this->getOptions()->getOptionValue('wcTicketPDFFontSize'));
176 $pdf->setFontFamily($this->getOptions()->getOptionValue('wcTicketPDFFontFamily'));
177 $pdf = apply_filters( $this->_add_filter_prefix.'main_getNewPDFObject', $pdf );
178 return $pdf;
179 }
180 public function loadOnce($className, $filename="") {
181 if (!class_exists($className)) {
182 if ($filename == "") $filename = $className;
183 include_once __DIR__.'/'.$filename.'.php';
184 }
185 }
186
187 /**
188 * Load class from /includes/ folder structure
189 *
190 * @param string $className The class name to load
191 * @param string $relativePath Path relative to plugin root (e.g., 'includes/woocommerce/class-base.php')
192 * @return void
193 */
194 private function loadClass(string $className, string $relativePath): void {
195 if (class_exists($className)) {
196 return; // Already loaded
197 }
198
199 $path = __DIR__ . '/' . $relativePath;
200
201 if (file_exists($path)) {
202 require_once $path;
203 }
204 }
205 public function getWC() {
206 $this->loadOnce('sasoEventtickets_WC', "woocommerce-hooks");
207 return sasoEventtickets_WC::Instance();
208 }
209 public function getTicketHandler() {
210 $this->loadOnce('sasoEventtickets_Ticket');
211 return sasoEventtickets_Ticket::Instance($_SERVER["REQUEST_URI"]);
212 }
213 public function getTicketDesignerHandler($template="") {
214 $this->loadOnce('sasoEventtickets_TicketDesigner');
215 return sasoEventtickets_TicketDesigner::Instance($this, $template);
216 }
217 public function getTicketBadgeHandler() {
218 $this->loadOnce('sasoEventtickets_TicketBadge');
219 return sasoEventtickets_TicketBadge::Instance();
220 }
221 public function getTicketQRHandler() {
222 $this->loadOnce('sasoEventtickets_TicketQR');
223 return sasoEventtickets_TicketQR::Instance();
224 }
225 public function getAuthtokenHandler() {
226 $this->loadOnce('sasoEventtickets_Authtoken');
227 return sasoEventtickets_Authtoken::Instance();
228 }
229 public function getSeating() {
230 $this->loadOnce('sasoEventtickets_Seating');
231 return sasoEventtickets_Seating::Instance($this);
232 }
233 public function getPremiumFunctions() {
234 if ($this->_isPrem == null && $this->PREMFUNCTIONS == null) {
235 $this->_isPrem = false;
236 $this->PREMFUNCTIONS = new sasoEventtickets_fakeprem();
237 if (class_exists('sasoEventtickets_PremiumFunctions')) {
238 $this->PREMFUNCTIONS = new sasoEventtickets_PremiumFunctions($this, plugin_dir_path(__FILE__), $this->_prefix, $this->getDB());
239 $this->_isPrem = $this->PREMFUNCTIONS->isPremium();
240 } else { // old approach before v 2.5.7
241 // this is causing issues with base_dir set
242 $premPluginFolder = $this->getPremiumPluginFolder();
243 if (!empty($premPluginFolder)) {
244 $file = $premPluginFolder.$this->_premium_function_file;
245 $premiumFile = plugin_dir_path(__FILE__)."../".$file;
246 if (file_exists($premiumFile)) { // check ob active ist nicht nötig, das das getPremiumPluginFolder schon macht
247 if (!class_exists('sasoEventtickets_PremiumFunctions')) {
248 include_once $premiumFile;
249 }
250 $this->PREMFUNCTIONS = new sasoEventtickets_PremiumFunctions($this, plugin_dir_path(__FILE__), $this->_prefix, $this->getDB());
251 $this->_isPrem = $this->PREMFUNCTIONS->isPremium();
252 }
253 }
254 }
255 }
256 return $this->PREMFUNCTIONS;
257 }
258 private function getPremiumPluginFolder() {
259 $plugins = get_option('active_plugins', []);
260 $premiumFile = "";
261 foreach($plugins as $plugin) {
262 if (strpos(" ".$plugin, $this->_premium_plugin_name) > 0) {
263 $premiumFile = plugin_dir_path($plugin);
264 break;
265 }
266 }
267 return $premiumFile;
268 }
269 public function isPremium() {
270 if ($this->_isPrem == null) $this->getPremiumFunctions();
271 return $this->_isPrem;
272 }
273 public function getPrefix() {
274 return $this->_prefix;
275 }
276 public function getMV() {
277 $v = ['storeip'=>false,'allowuserreg'=>false,'codes_total'=>0x13,'codes'=>0x12,'lists'=>5,'authtokens_total'=>3];
278 $v["codes"] = (int) hexdec(0x80 / 0x002) / 2;
279 $v["codes_total"] = (int) hexdec(0x80 / 0x002) / 2;
280 $v["seatingplans"] = (int) (0x04 >> 0x02);
281 $v["seats_per_plan"] = (int) (0x50 >> 0x02);
282 return $v;
283 }
284 public function listener_upgrader_process_complete( $upgrader_object, $options ) {
285 $current_plugin_path_name = plugin_basename( __FILE__ );
286 if ($options['action'] == 'update' && $options['type'] == 'plugin' ) {
287 if (isset($options['plugins'])) {
288 foreach($options['plugins'] as $each_plugin) {
289 if ($each_plugin==$current_plugin_path_name) {
290 // .......................... YOUR CODES .............
291 }
292 }
293 }
294 }
295 do_action( $this->_do_action_prefix.'main_listener_upgrader_process_complete' );
296 }
297 /**
298 * check for ticket detail page request
299 */
300 public function wc_checkTicketDetailPage() {
301 if( is_404() ){
302 include_once("SASO_EVENTTICKETS.php");
303 // /wp-content/plugins/event-tickets-with-ticket-scanner/ticket/
304 $p = $this->getCore()->getTicketURLPath(true);
305 $t = explode("/", $_SERVER["REQUEST_URI"]);
306 if (count($t) > 1) {
307 if ($t[count($t)-2] != "scanner") {
308 if(substr($_SERVER["REQUEST_URI"], 0, strlen($p)) == $p) {
309 $this->getTicketHandler()->initFilterAndActions();
310 } else {
311 $wcTicketCompatibilityModeURLPath = trim($this->getOptions()->getOptionValue('wcTicketCompatibilityModeURLPath'));
312 $wcTicketCompatibilityModeURLPath = trim(trim($wcTicketCompatibilityModeURLPath, "/"));
313 if (!empty($wcTicketCompatibilityModeURLPath)) {
314 $uri = trim($_SERVER["REQUEST_URI"]);
315 if (!empty($uri)) {
316 $pos = strpos($uri, $wcTicketCompatibilityModeURLPath);
317 if ($pos > 0) {
318 $this->getTicketHandler()->initFilterAndActions();
319 }
320 }
321 }
322 }
323 }
324
325 if ($t[count($t)-2] == "scanner") {
326 if(substr($_SERVER["REQUEST_URI"], 0, strlen($p)) == $p) {
327 //$this->replacingShortcodeTicketScanner();
328 $this->getTicketHandler()->initFilterAndActionsTicketScanner();
329 } else {
330 $wcTicketCompatibilityModeURLPath = trim($this->getOptions()->getOptionValue('wcTicketCompatibilityModeURLPath'));
331 $wcTicketCompatibilityModeURLPath = trim(trim($wcTicketCompatibilityModeURLPath, "/"));
332 if (!empty($wcTicketCompatibilityModeURLPath)) {
333 $uri = trim($_SERVER["REQUEST_URI"]);
334 if (!empty($uri)) {
335 $pos = strpos($_SERVER["REQUEST_URI"], $wcTicketCompatibilityModeURLPath."/scanner/");
336 if ($pos > 0) {
337 $this->getTicketHandler()->initFilterAndActionsTicketScanner();
338 }
339 }
340 }
341 }
342 }
343 }
344 } // endif 404
345 }
346 private function init_frontend() {
347 add_shortcode($this->_shortcode, [$this, 'replacingShortcode']);
348 add_shortcode($this->_shortcode_mycode, [$this, 'replacingShortcodeMyCode']);
349 add_shortcode($this->_shortcode_ticket_scanner, [$this, 'replacingShortcodeTicketScanner']);
350 add_shortcode($this->_shortcode_eventviews, [$this, 'replacingShortcodeEventViews']);
351 add_shortcode($this->_shortcode_feature_list, [$this, 'replacingShortcodeFeatureList']);
352 add_shortcode($this->_shortcode_ticket_detail, [$this, 'replacingShortcodeTicketDetail']);
353 do_action( $this->_do_action_prefix.'main_init_frontend' );
354 }
355 private function init_backend() {
356 add_action('admin_menu', [$this, 'register_options_page']);
357 register_activation_hook(__FILE__, [$this, 'plugin_activated']);
358 register_deactivation_hook( __FILE__, [$this, 'plugin_deactivated'] );
359 //register_uninstall_hook( __FILE__, 'sasoEventticketsDB::plugin_uninstall' ); // MUSS NOCH GETESTE WERDEN
360 add_action( 'plugins_loaded', [$this, 'plugins_loaded'] );
361 add_action( 'show_user_profile', [$this, 'show_user_profile'] );
362 add_action( 'admin_notices', [$this, 'showSubscriptionWarning'] );
363 add_action( 'admin_notices', [$this, 'showOutdatedPremiumWarning'] );
364
365 if (basename($_SERVER['SCRIPT_NAME']) == "admin-ajax.php") {
366 add_action('wp_ajax_'.$this->_prefix.'_executeAdminSettings', [$this,'executeAdminSettings_a'], 10, 0);
367 add_action('wp_ajax_'.$this->_prefix.'_executeSeatingAdmin', [$this,'executeSeatingAdmin_a'], 10, 0);
368 }
369
370 do_action( $this->_do_action_prefix.'main_init_backend' );
371 }
372 public function WooCommercePluginLoaded() {
373 // DON'T load WC here - let relay functions do lazy loading
374 //$this->getWC(); // um die wc handler zu laden
375 add_action('woocommerce_review_order_after_cart_contents', [$this, 'relay_woocommerce_review_order_after_cart_contents']);
376 add_action('woocommerce_checkout_process', [$this, 'relay_woocommerce_checkout_process']);
377 add_action('woocommerce_before_cart_table', [$this, 'relay_woocommerce_before_cart_table']);
378 add_action('woocommerce_cart_updated', [$this, 'relay_woocommerce_cart_updated']);
379 add_filter('woocommerce_email_attachments', [$this, 'relay_woocommerce_email_attachments'], 10, 3);
380 add_action('woocommerce_checkout_create_order_line_item', [$this, 'relay_woocommerce_checkout_create_order_line_item'], 20, 4 );
381 add_action('woocommerce_check_cart_items', [$this, 'relay_woocommerce_check_cart_items'] );
382 add_action('woocommerce_new_order', [$this, 'relay_woocommerce_new_order'], 10, 1);
383 add_action('woocommerce_checkout_update_order_meta', [$this, 'relay_woocommerce_checkout_update_order_meta'], 20, 2);
384 add_action('woocommerce_order_status_changed', [$this, 'relay_woocommerce_order_status_changed'], 10, 3);
385 add_filter('woocommerce_order_item_display_meta_key', [$this, 'relay_woocommerce_order_item_display_meta_key'], 20, 3 );
386 add_filter('woocommerce_order_item_display_meta_value', [$this, 'relay_woocommerce_order_item_display_meta_value'], 20, 3);
387 add_action('wpo_wcpdf_after_item_meta', [$this, 'relay_wpo_wcpdf_after_item_meta'], 20, 3 );
388 add_action('woocommerce_order_item_meta_start', [$this, 'relay_woocommerce_order_item_meta_start'], 201, 4);
389 add_action('woocommerce_product_after_variable_attributes', [$this, 'relay_woocommerce_product_after_variable_attributes'], 10, 3);
390 add_action('woocommerce_save_product_variation',[$this, 'relay_woocommerce_save_product_variation'], 10 ,2 );
391 add_action('woocommerce_email_order_meta', [$this, 'relay_woocommerce_email_order_meta'], 10, 4 );
392 add_action('woocommerce_thankyou', [$this, 'relay_woocommerce_thankyou'], 5);
393 if (wp_doing_ajax()) {
394 // erlaube ajax nonpriv und registriere handler
395 add_action('wp_ajax_nopriv_'.$this->getPrefix().'_executeWCFrontend', [$this,'relay_executeWCFrontend']); // nicht angemeldete user, sollen eine antwort erhalten
396 add_action('wp_ajax_'.$this->getPrefix().'_executeWCFrontend', [$this,'relay_executeWCFrontend']); // nicht angemeldete user, sollen eine antwort erhalten
397 // Seating Frontend AJAX (seat selection in shop)
398 add_action('wp_ajax_nopriv_'.$this->getPrefix().'_executeSeatingFrontend', [$this,'relay_executeSeatingFrontend']);
399 add_action('wp_ajax_'.$this->getPrefix().'_executeSeatingFrontend', [$this,'relay_executeSeatingFrontend']);
400 }
401 if (is_admin()) {
402 add_action('woocommerce_delete_order', [$this, 'relay_woocommerce_delete_order'], 10, 1 );
403 add_action('woocommerce_delete_order_item', [$this, 'relay_woocommerce_delete_order_item'], 20, 1);
404 add_action('woocommerce_pre_delete_order_refund', [$this, 'relay_woocommerce_pre_delete_order_refund'], 10, 3);
405 add_action('woocommerce_delete_order_refund', [$this, 'relay_woocommerce_delete_order_refund'], 10, 1 );
406 add_action('woocommerce_order_partially_refunded', [$this, 'relay_woocommerce_order_partially_refunded'], 10, 2);
407 add_filter('woocommerce_product_data_tabs', [$this, 'relay_woocommerce_product_data_tabs'], 98 );
408 add_action('woocommerce_product_data_panels', [$this, 'relay_woocommerce_product_data_panels'] );
409 add_action('woocommerce_process_product_meta', [$this, 'relay_woocommerce_process_product_meta'], 10, 2 );
410 add_action('add_meta_boxes', [$this, 'relay_add_meta_boxes'], 10, 2);
411 add_filter('manage_edit-product_columns', [$this, 'relay_manage_edit_product_columns']);
412 add_action('manage_product_posts_custom_column', [$this, 'relay_manage_product_posts_custom_column'], 2);
413 add_filter("manage_edit-product_sortable_columns", [$this, 'relay_manage_edit_product_sortable_columns']);
414 } else {
415 add_action('woocommerce_single_product_summary', [$this, 'relay_woocommerce_single_product_summary']);
416 }
417
418 // set routing -- NEEDS to be replaced by add_rewrite_rule later
419 add_action( 'template_redirect', [$this, 'wc_checkTicketDetailPage'], 1 );
420 //$this->wc_checkTicketDetailPage();
421 add_action('rest_api_init', function () {
422 SASO_EVENTTICKETS::setRestRoutesTicket();
423 });
424
425 add_action('woocommerce_after_shop_loop_item', [$this, 'relay_woocommerce_after_shop_loop_item'], 9); // with 9 we are just before the add to cart button
426 add_filter('woocommerce_add_to_cart_validation', [$this, 'relay_woocommerce_add_to_cart_validation'], 10, 3);
427 add_filter('woocommerce_add_cart_item_data', [$this, 'relay_woocommerce_add_cart_item_data'], 10, 3);
428 add_action('woocommerce_add_to_cart', [$this, 'relay_woocommerce_add_to_cart'], 10, 6);
429 add_action('woocommerce_cart_item_removed', [$this, 'relay_woocommerce_cart_item_removed'], 10, 2);
430 add_action('woocommerce_after_cart_item_quantity_update', [$this, 'relay_woocommerce_after_cart_item_quantity_update'], 10, 4);
431 add_filter('woocommerce_update_cart_validation', [$this, 'relay_woocommerce_update_cart_validation'], 10, 4);
432 add_action('woocommerce_before_add_to_cart_button', [$this, 'relay_woocommerce_before_add_to_cart_button'], 15);
433
434 do_action( $this->_do_action_prefix.'main_WooCommercePluginLoaded' );
435 }
436 public function relay_woocommerce_after_shop_loop_item() {
437 $this->getWC()->getFrontendManager()->woocommerce_after_shop_loop_item_handler();
438 }
439 public function relay_woocommerce_add_to_cart_validation() {
440 $args = func_get_args();
441 return $this->getWC()->getFrontendManager()->woocommerce_add_to_cart_validation_handler(...$args);
442 }
443 public function relay_woocommerce_add_cart_item_data() {
444 $args = func_get_args();
445 return $this->getWC()->getFrontendManager()->woocommerce_add_cart_item_data_handler(...$args);
446 }
447 public function relay_woocommerce_add_to_cart() {
448 $args = func_get_args();
449 return $this->getWC()->getFrontendManager()->woocommerce_add_to_cart_handler(...$args);
450 }
451 public function relay_woocommerce_cart_item_removed() {
452 $args = func_get_args();
453 $this->getWC()->getFrontendManager()->woocommerce_cart_item_removed_handler(...$args);
454 }
455 public function relay_woocommerce_after_cart_item_quantity_update() {
456 $args = func_get_args();
457 $this->getWC()->getFrontendManager()->woocommerce_after_cart_item_quantity_update_handler(...$args);
458 }
459 public function relay_woocommerce_update_cart_validation() {
460 $args = func_get_args();
461 return $this->getWC()->getFrontendManager()->woocommerce_update_cart_validation_handler(...$args);
462 }
463 public function relay_woocommerce_before_add_to_cart_button() {
464 $this->getWC()->getFrontendManager()->woocommerce_before_add_to_cart_button_handler();
465 }
466 public function relay_woocommerce_review_order_after_cart_contents() {
467 $this->getWC()->getFrontendManager()->woocommerce_review_order_after_cart_contents();
468 }
469 public function relay_woocommerce_checkout_process() {
470 $this->getWC()->getFrontendManager()->woocommerce_checkout_process();
471 }
472 public function relay_woocommerce_before_cart_table() {
473 $this->getWC()->getFrontendManager()->woocommerce_before_cart_table();
474 }
475 public function relay_woocommerce_cart_updated() {
476 $this->getWC()->getFrontendManager()->woocommerce_cart_updated_handler();
477 }
478 public function relay_woocommerce_email_attachments() {
479 $args = func_get_args();
480 return $this->getWC()->getEmailHandler()->woocommerce_email_attachments(...$args);
481 }
482 public function relay_woocommerce_checkout_create_order_line_item() {
483 $args = func_get_args();
484 return $this->getWC()->getOrderManager()->woocommerce_checkout_create_order_line_item(...$args);
485 }
486 public function relay_woocommerce_check_cart_items() {
487 $this->getWC()->getFrontendManager()->woocommerce_check_cart_items();
488 }
489 public function relay_woocommerce_new_order() {
490 $args = func_get_args();
491 return $this->getWC()->getOrderManager()->woocommerce_new_order(...$args);
492 }
493 public function relay_woocommerce_checkout_update_order_meta() {
494 $args = func_get_args();
495 return $this->getWC()->getOrderManager()->woocommerce_checkout_update_order_meta(...$args);
496 }
497 public function relay_executeWCFrontend() {
498 return $this->getWC()->getFrontendManager()->executeWCFrontend();
499 }
500 public function relay_executeSeatingFrontend() {
501 return $this->getSeating()->getFrontendManager()->executeSeatingFrontend();
502 }
503 public function relay_woocommerce_delete_order() {
504 $args = func_get_args();
505 $this->getWC()->getOrderManager()->woocommerce_delete_order(...$args);
506 }
507 public function relay_woocommerce_delete_order_item() {
508 $args = func_get_args();
509 $this->getWC()->getOrderManager()->woocommerce_delete_order_item(...$args);
510 }
511 public function relay_woocommerce_pre_delete_order_refund() {
512 $args = func_get_args();
513 $this->getWC()->getOrderManager()->woocommerce_pre_delete_order_refund(...$args);
514 }
515 public function relay_woocommerce_delete_order_refund() {
516 $args = func_get_args();
517 $this->getWC()->getOrderManager()->woocommerce_delete_order_refund(...$args);
518 }
519 public function relay_woocommerce_product_data_tabs() {
520 $args = func_get_args();
521 return $this->getWC()->getProductManager()->woocommerce_product_data_tabs(...$args);
522 }
523 public function relay_woocommerce_product_data_panels() {
524 $this->getWC()->getProductManager()->woocommerce_product_data_panels();
525 }
526 public function relay_woocommerce_process_product_meta() {
527 $args = func_get_args();
528 $this->getWC()->getProductManager()->woocommerce_process_product_meta(...$args);
529 }
530 public function relay_add_meta_boxes(...$args) {
531 $this->getWC()->add_meta_boxes(...$args);
532 }
533 public function relay_manage_edit_product_columns() {
534 $args = func_get_args();
535 return $this->getWC()->getProductManager()->manage_edit_product_columns(...$args);
536 }
537 public function relay_manage_product_posts_custom_column() {
538 $args = func_get_args();
539 $this->getWC()->getProductManager()->manage_product_posts_custom_column(...$args);
540 }
541 public function relay_manage_edit_product_sortable_columns() {
542 $args = func_get_args();
543 return $this->getWC()->getProductManager()->manage_edit_product_sortable_columns(...$args);
544 }
545 public function relay_woocommerce_single_product_summary() {
546 $this->getWC()->getFrontendManager()->woocommerce_single_product_summary();
547 }
548 public function relay_woocommerce_order_status_changed() {
549 $args = func_get_args();
550 $this->getWC()->getOrderManager()->woocommerce_order_status_changed(...$args);
551 }
552 public function relay_woocommerce_order_partially_refunded() {
553 $args = func_get_args();
554 $this->getWC()->getOrderManager()->woocommerce_order_partially_refunded(...$args);
555 }
556 public function relay_woocommerce_order_item_display_meta_key() {
557 $args = func_get_args();
558 return $this->getWC()->getOrderManager()->woocommerce_order_item_display_meta_key(...$args);
559 }
560 public function relay_woocommerce_order_item_display_meta_value() {
561 $args = func_get_args();
562 return $this->getWC()->getOrderManager()->woocommerce_order_item_display_meta_value(...$args);
563 }
564 public function relay_wpo_wcpdf_after_item_meta() {
565 $args = func_get_args();
566 $this->getWC()->getOrderManager()->wpo_wcpdf_after_item_meta(...$args);
567 }
568 public function relay_woocommerce_order_item_meta_start() {
569 $args = func_get_args();
570 $this->getWC()->getOrderManager()->woocommerce_order_item_meta_start(...$args);
571 }
572 public function relay_woocommerce_product_after_variable_attributes() {
573 $args = func_get_args();
574 $this->getWC()->getProductManager()->woocommerce_product_after_variable_attributes(...$args);
575 }
576 public function relay_woocommerce_save_product_variation() {
577 $args = func_get_args();
578 $this->getWC()->getProductManager()->woocommerce_save_product_variation(...$args);
579 }
580 public function relay_woocommerce_email_order_meta() {
581 $args = func_get_args();
582 $this->getWC()->getEmailHandler()->woocommerce_email_order_meta(...$args);
583 }
584 public function relay_woocommerce_thankyou() {
585 $args = func_get_args();
586 $this->getWC()->getFrontendManager()->woocommerce_thankyou(...$args);
587 }
588 public function relay_sasoEventtickets_cronjob_daily() {
589 $this->getTicketHandler()->cronJobDaily();
590 }
591
592 public function plugin_deactivated() {
593 $this->cronjob_daily_deactivate();
594 $this->getDB();
595 sasoEventticketsDB::plugin_deactivated();
596 do_action( $this->_do_action_prefix.'main_plugin_deactivated' );
597 }
598 public function plugin_uninstall() {
599 $this->getDB();
600 sasoEventticketsDB::plugin_uninstall();
601 $this->getAdmin();
602 sasoEventtickets_AdminSettings::plugin_uninstall();
603 do_action( $this->_do_action_prefix.'main_WooCommercePluginLoaded' );
604 }
605 public function plugin_activated($is_network_wide=false) { // und auch für updates, macht es einfacher
606 $this->getDB(); // um installiere Tabellen auszuführen
607 update_option('SASO_EVENTTICKETS_PLUGIN_VERSION', SASO_EVENTTICKETS_PLUGIN_VERSION);
608 $this->getAdmin()->generateFirstCodeList();
609 $this->cronjob_daily_activate();
610 do_action( $this->_do_action_prefix.'activated' );
611 do_action( $this->_do_action_prefix.'main_plugin_activated' );
612 }
613 public function plugins_loaded() {
614 if (SASO_EVENTTICKETS_PLUGIN_VERSION !== get_option('SASO_EVENTTICKETS_PLUGIN_VERSION', '')) $this->plugin_activated(); // vermutlich wurde die aktivierung übersprungen, bei änderungen direkt an den files
615 }
616 public function initialize_plugin() {
617 $this->getDB(); // um installiere Tabellen auszuführen
618 do_action( $this->_do_action_prefix.'initialized' );
619 do_action( $this->_do_action_prefix.'main_initialize_plugin' );
620 }
621 function show_user_profile($profileuser) {
622 $this->getAdmin()->show_user_profile($profileuser);
623 do_action( $this->_do_action_prefix.'main_show_user_profile' );
624 }
625 function register_options_page() {
626 $allowed = $this->isUserAllowedToAccessAdminArea();
627 $allowed = apply_filters( $this->_add_filter_prefix.'main_options_page', $allowed );
628 if ($allowed) {
629 add_options_page(__('Event Tickets', 'event-tickets-with-ticket-scanner'), 'Event Tickets', 'manage_options', 'event-tickets-with-ticket-scanner', [$this,'options_page']);
630 add_menu_page( __('Event Tickets', 'event-tickets-with-ticket-scanner'), 'Event Tickets', 'read', 'event-tickets-with-ticket-scanner', [$this,'options_page'], plugins_url( "",__FILE__ )."/img/icon_event-tickets-with-ticket-scanner_18px.gif", null );
631 }
632 do_action( $this->_do_action_prefix.'main_register_options_page' );
633 }
634
635 function options_page() {
636 $allowed = $this->isUserAllowedToAccessAdminArea();
637 $allowed = apply_filters( $this->_add_filter_prefix.'main_options_page', $allowed );
638 if ( !$allowed ) {
639 wp_die( __( 'You do not have sufficient permissions to access this page.', 'event-tickets-with-ticket-scanner' ) );
640 }
641
642 wp_enqueue_style("wp-jquery-ui-dialog");
643
644 $js_url = "jquery.qrcode.min.js?_v=".$this->_js_version;
645 wp_register_script('ajax_script2', plugins_url( "3rd/".$js_url,__FILE__ ), array('jquery', 'jquery-ui-dialog'));
646 wp_enqueue_script('ajax_script2');
647
648 wp_enqueue_media(); // um die js wp.media lib zu laden
649
650 // einbinden das js starter skript
651 $js_url = $this->_js_file."?_v=".$this->_js_version;
652 if (defined( 'WP_DEBUG')) $js_url .= '&debug=1';
653 wp_register_script('ajax_script_backend', plugins_url( $js_url,__FILE__ ), array('jquery', 'jquery-ui-dialog', 'wp-i18n'));
654 wp_enqueue_script('ajax_script_backend');
655 wp_set_script_translations('ajax_script_backend', 'event-tickets-with-ticket-scanner', __DIR__.'/languages');
656
657 // per script eine variable einbinden, die url hat den wp-admin prefix
658 // damit im backend.js dann die richtige callback url genutzt werden kann
659 $vars = array(
660 '_plugin_home_url' =>plugins_url( "",__FILE__ ),
661 '_plugin_version' => $this->getPluginVersion(),
662 '_action' => $this->_prefix.'_executeAdminSettings',
663 '_max'=>$this->getBase()->getMaxValues(),
664 '_isPremium'=>$this->isPremium(),
665 '_isUserLoggedin'=>is_user_logged_in(),
666 '_premJS'=>$this->isPremium() && method_exists($this->getPremiumFunctions(), "getJSBackendFile") ? $this->getPremiumFunctions()->getJSBackendFile() : '',
667 'url' => admin_url( 'admin-ajax.php' ),
668 'ticket_url' => $this->getCore()->getTicketURLPath(),
669 'nonce' => wp_create_nonce( $this->_js_nonce ),
670 'ajaxActionPrefix' => $this->_prefix,
671 'divPrefix' => $this->_prefix,
672 'divId' => $this->_divId,
673 'jsFiles' => plugins_url( 'backend.js?_v='.$this->_js_version,__FILE__ )
674 );
675 $vars = apply_filters( $this->_add_filter_prefix.'main_options_page', $vars );
676 wp_localize_script(
677 'ajax_script_backend',
678 'Ajax_'.$this->_prefix, // name der injected variable
679 $vars
680 );
681
682 do_action( $this->_do_action_prefix.'main_options_page' );
683
684 $versions = $this->getPluginVersions();
685 $versions_tail = $versions['basic'].($versions['premium'] != "" ? ', Premium: '.$versions['premium'] : '');
686 $version_tail_add = "";
687 if ($versions['debug'] != "") $version_tail_add .= 'DEBUG: '.$versions['debug'].', LANG: '.determine_locale();
688 ?>
689 <div class="event-tickets-with-ticket-scanner-admin-page">
690 <div class="event-tickets-with-ticket-scanner-header">
691 <div class="event-tickets-with-ticket-scanner-header-left">
692 <img src="<?php echo plugins_url( "",__FILE__ ); ?>/img/logo_event-tickets-with-ticket-scanner.gif"
693 alt="Event Tickets"
694 class="event-tickets-with-ticket-scanner-header-logo">
695
696 <div class="event-tickets-with-ticket-scanner-header-title">
697 <div class="event-tickets-with-ticket-scanner-header-name">
698 Event Tickets with Ticket Scanner
699 </div>
700 <div class="event-tickets-with-ticket-scanner-header-meta">
701 <?php esc_html_e('Version', 'event-tickets-with-ticket-scanner'); ?>: <?php echo $versions_tail; ?> <?php echo $version_tail_add; ?>
702 </div>
703 </div>
704 </div>
705
706 <div class="event-tickets-with-ticket-scanner-header-right" id="event-tickets-with-ticket-scanner-header-actions">
707 <!-- Button kommt via JS -->
708 </div>
709 </div>
710
711 <div style="clear:both;" data-id="plugin_addons"></div>
712 <div style="clear:both;" data-id="plugin_info_area"></div>
713 <div style="clear:both;" id="<?php echo esc_attr($this->_divId); ?>">...loading...</div>
714 <div style="margin-top:100px;">
715 <hr>
716 <a name="shortcodedetails"></a>
717 <h3>Documentation</h3>
718 <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 of this plugin.</a></p>
719 <h3><?php esc_html_e('Plugin Rating', 'event-tickets-with-ticket-scanner'); ?></h3>
720 <p><?php esc_html_e('If you like our plugin, then please give us a', 'event-tickets-with-ticket-scanner'); ?> <a target="_blank" href="https://wordpress.org/support/plugin/event-tickets-with-ticket-scanner/reviews?rate=5#new-post">�
721
722
723
724
725 5-Star Rating</a>.</p>
726 <h3><?php esc_html_e('Ticket Sale option', 'event-tickets-with-ticket-scanner'); ?></h3>
727 <p><?php esc_html_e('You can use this plugin to sell tickets and even redeem them. Check out the documentation for', 'event-tickets-with-ticket-scanner'); ?> <a target="_blank" href="https://vollstart.com/event-tickets-with-ticket-scanner/docs/#ticket"><?php esc_html_e('more details here', 'event-tickets-with-ticket-scanner'); ?></a>.</p>
728 <h3><?php esc_html_e('Premium Homepage', 'event-tickets-with-ticket-scanner'); ?></h3>
729 <p><?php esc_html_e('You can find more details about the', 'event-tickets-with-ticket-scanner'); ?> <a target="_blank" href="https://vollstart.com/event-tickets-with-ticket-scanner/"><?php esc_html_e('premium version here', 'event-tickets-with-ticket-scanner'); ?></a>.</p>
730 <!--
731 <h3>Shortcode parameter In- & Output</h3>
732 <a href="https://vollstart.com/event-tickets-with-ticket-scanner/docs/" target="_blank">Click here for more help about the options</a>
733 <p>You can use your own HTML input, output and trigger component. If you add the parameters (all 3 mandatory to use this feature), then the default input area will not be rendered.</p>
734 <ul>
735 <li><b>inputid</b><br>inputid="html-element-id". The value of this component will be taken. It need to be an HTML input element. We will access the value-parameter of it.</li>
736 <li><b>triggerid</b><br>triggerid="html-element-id". The onclick event of this component will be replaced by our function to call the server validation with the code.</li>
737 <li><b>outputid</b><br>outputid="html-element-id". The content of this component will be replaced by the server result after the check . We will use the innerHTML property of it, so use a DIV, SPAN, TD or similar for best results.</li>
738 </ul>
739 <h3>Shortcode parameter Javascript</h3>
740 <p>You can add your Javascript function name. Both parameters are optional and not required. If functions will be called before the code is sent to the server or displaying the result.</p>
741 <ul>
742 <li><b>jspre</b><br>jspre="function-name". The function will be called. The input parameter will be the code. If your function returns a value, than this returned value will be used otherwise the entered code will be used.</li>
743 <li><b>jsafter</b><br>jsafter="function-name". The function will be called. The input parameter will be the result JSON object from the server.</li>
744 </ul>
745 -->
746 <h3><?php esc_html_e('Shortcode to display the event calendar form within a page', 'event-tickets-with-ticket-scanner'); ?></h3>
747 <b>[<?php echo esc_html($this->_shortcode_eventviews); ?>]</b>
748 <p><?php esc_html_e('The event calendar form will be displayed. You can add the following parameters to change the output:', 'event-tickets-with-ticket-scanner'); ?></p>
749 <ul>
750 <!--<li>view<br>Values: calendar, list or calendar|list<br>Default: list</li>-->
751 <li>months_to_show<br>Values can be a number higher than 0. Default: 3</li>
752 </ul>
753 <p>CSS file: <a href="<?php echo plugins_url( "",__FILE__ ); ?>/css/calendar.css" target="_blank">calendar.css</a></p>
754 <h3><?php esc_html_e('Shortcode to display the assigned tickets and codes of an user within a page', 'event-tickets-with-ticket-scanner'); ?></h3>
755 <b>[<?php echo esc_html($this->_shortcode_mycode); ?>]</b>
756 <p><?php esc_html_e('Displays tickets assigned to the current logged-in user (default) or tickets from a specific order.', 'event-tickets-with-ticket-scanner'); ?></p>
757 <ul>
758 <li><b>order_id</b> - <?php esc_html_e('Show tickets from a specific order instead of user tickets. Security: User must own the order or have valid order key in URL.', 'event-tickets-with-ticket-scanner'); ?><br>
759 <?php esc_html_e('Example:', 'event-tickets-with-ticket-scanner'); ?> [<?php echo esc_html($this->_shortcode_mycode); ?> order_id="123"]</li>
760 <li><b>format</b> - <?php esc_html_e('Output format. Values: json', 'event-tickets-with-ticket-scanner'); ?></li>
761 <li><b>display</b> - <?php esc_html_e('Fields to show (comma-separated). Values: codes, validation, user, used, confirmedCount, woocommerce, wc_rp, wc_ticket', 'event-tickets-with-ticket-scanner'); ?></li>
762 <li><b>download_all_pdf</b> - <?php esc_html_e('Show download button for all tickets as one PDF. Values: true/false', 'event-tickets-with-ticket-scanner'); ?></li>
763 </ul>
764 <p>
765 <?php esc_html_e('Example with JSON output:', 'event-tickets-with-ticket-scanner'); ?> [<?php echo esc_html($this->_shortcode_mycode); ?> format="json" display="code,wc_ticket"]
766 </p>
767 <h3><?php esc_html_e('Shortcode to display the ticket scanner within a page', 'event-tickets-with-ticket-scanner'); ?></h3>
768 <?php esc_html_e('Useful if you cannot open the ticket scanner due to security issues.', 'event-tickets-with-ticket-scanner'); ?><br>
769 <b>[<?php echo esc_html($this->_shortcode_ticket_scanner); ?>]</b>
770 <h3><?php esc_html_e('Shortcode to display ticket detail view within a page', 'event-tickets-with-ticket-scanner'); ?></h3>
771 <?php esc_html_e('Useful if the /ticket/ URL path does not work on your server.', 'event-tickets-with-ticket-scanner'); ?><br>
772 <b>[<?php echo esc_html($this->_shortcode_ticket_detail); ?>]</b>
773 <p>
774 <?php esc_html_e('Usage: Add the shortcode to a page and access it with ?ticket=YOUR-TICKET-CODE in the URL.', 'event-tickets-with-ticket-scanner'); ?><br>
775 <?php esc_html_e('Example:', 'event-tickets-with-ticket-scanner'); ?> yoursite.com/ticket-page/?ticket=ABC-123-XYZ<br>
776 <?php esc_html_e('Or use the code attribute:', 'event-tickets-with-ticket-scanner'); ?> [<?php echo esc_html($this->_shortcode_ticket_detail); ?> code="ABC-123-XYZ"]
777 </p>
778 <h3><?php esc_html_e('PHP Filters', 'event-tickets-with-ticket-scanner'); ?></h3>
779 <p><?php esc_html_e('You can use PHP code to register your filter functions for the validation check.', 'event-tickets-with-ticket-scanner'); ?>
780 <a href="https://vollstart.com/event-tickets-with-ticket-scanner/docs/#filters" target="_blank"><?php esc_html_e('Click here for more help about the functions', 'event-tickets-with-ticket-scanner'); ?></a>
781 </p>
782 <ul>
783 <li>add_filter('<?php echo $this->_add_filter_prefix.'beforeCheckCodePre'; ?>', 'myfunc', 20, 1)</li>
784 <li>add_filter('<?php echo $this->_add_filter_prefix.'beforeCheckCode'; ?>', 'myfunc', 20, 1)</li>
785 <li>add_filter('<?php echo $this->_add_filter_prefix.'afterCheckCodePre'; ?>', 'myfunc', 20, 1)</li>
786 <li>add_filter('<?php echo $this->_add_filter_prefix.'afterCheckCode'; ?>', 'myfunc', 20, 1)</li>
787 </ul>
788 <p>More BETA filters and actions hooks can be found <a href="https://vollstart.com/event-tickets-with-ticket-scanner/docs/ticket-plugin-api/" target="_blank">here (NOT STABLE, be aware that they might be changed in the future)</a>.
789 <p style="text-align:center;"><a target="_blank" href="https://vollstart.com">VOLLSTART</a> - More plugins: <a target="_blank" href="https://wordpress.org/plugins/serial-codes-generator-and-validator/">Serial Code Validator</a></p>
790 </div>
791 </div>
792 <?php
793 do_action( $this->_do_action_prefix.'options_page' );
794 }
795
796 public function isUserAllowedToAccessAdminArea() {
797 if ($this->isAllowedAccess != null) return $this->isAllowedAccess;
798 if ($this->getOptions()->isOptionCheckboxActive('allowOnlySepcificRoleAccessToAdmin')) {
799 // check welche rollen
800 $user = wp_get_current_user();
801 $user_roles = (array) $user->roles;
802 if (in_array("administrator", $user_roles)) {
803 $this->isAllowedAccess = true;
804 } else {
805 $adminAreaAllowedRoles = $this->getOptions()->getOptionValue('adminAreaAllowedRoles');
806 if (!is_array($adminAreaAllowedRoles)) {
807 if (empty($adminAreaAllowedRoles)) {
808 $adminAreaAllowedRoles = [];
809 } else {
810 $adminAreaAllowedRoles = [$adminAreaAllowedRoles];
811 }
812 }
813 foreach($adminAreaAllowedRoles as $role_name) {
814 if (in_array($role_name, $user_roles)) {
815 $this->isAllowedAccess = true;
816 break;
817 };
818 }
819 }
820 } else {
821 // Standard: Only administrators have access
822 $this->isAllowedAccess = current_user_can('manage_options');
823 }
824 $this->isAllowedAccess = apply_filters( $this->_add_filter_prefix.'main_isUserAllowedToAccessAdminArea', $this->isAllowedAccess );
825 return $this->isAllowedAccess;
826 }
827
828 public function executeAdminSettings_a() {
829 if (!SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a_sngmbh not provided");
830 return $this->executeAdminSettings(SASO_EVENTTICKETS::getRequestPara('a_sngmbh')); // to prevent WP adds parameters
831 }
832
833 public function executeAdminSettings($a=0, $data=null) {
834 if (!$this->isUserAllowedToAccessAdminArea()) {
835 return wp_send_json_error("Access denied", 403);
836 }
837 if ($a === 0 && !SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a not provided");
838
839 if ($data == null) {
840 $data = SASO_EVENTTICKETS::issetRPara('data') ? SASO_EVENTTICKETS::getRequestPara('data') : [];
841 }
842 if ($a === 0 || empty($a) || trim($a) == "") {
843 $a = SASO_EVENTTICKETS::getRequestPara('a_sngmbh');
844 }
845 do_action( $this->_do_action_prefix.'executeAdminSettings', $a, $data );
846 return $this->getAdmin()->executeJSON($a, $data, false, false); // with nonce check
847 }
848
849 public function executeSeatingAdmin_a() {
850 return $this->executeSeatingAdmin(SASO_EVENTTICKETS::getRequestPara('a'));
851 }
852
853 public function executeSeatingAdmin($a = '', $data = null) {
854 if (!$this->isUserAllowedToAccessAdminArea()) {
855 return wp_send_json_error('Access denied', 403);
856 }
857 if (empty($a) && !SASO_EVENTTICKETS::issetRPara('a')) {
858 return wp_send_json_error('a not provided');
859 }
860 if ($data === null) {
861 $data = SASO_EVENTTICKETS::getRequest();
862 }
863 if (empty($a)) {
864 $a = SASO_EVENTTICKETS::getRequestPara('a');
865 }
866 return $this->getSeating()->getAdminHandler()->executeSeatingJSON($a, $data);
867 }
868
869 public function executeFrontend_a() {
870 return $this->executeFrontend(); // to prevent WP adds parameters
871 }
872
873 public function executeWCBackend() {
874 if (!$this->isUserAllowedToAccessAdminArea()) {
875 return wp_send_json_error("Access denied", 403);
876 }
877 if (!SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a_sngmbh not provided");
878 $data = SASO_EVENTTICKETS::issetRPara('data') ? SASO_EVENTTICKETS::getRequestPara('data') : [];
879 return $this->getWC()->executeJSON(SASO_EVENTTICKETS::getRequestPara('a_sngmbh'), $data);
880 }
881
882 public function executeFrontend($a=0, $data=null) {
883 $sasoEventtickets_Frontend = $this->getFrontend();
884 if ($a === 0 && !SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a not provided");
885
886 if ($data == null) {
887 $data = SASO_EVENTTICKETS::issetRPara('data') ? SASO_EVENTTICKETS::getRequestPara('data') : [];
888 }
889 if ($a === 0 || empty($a) || trim($a) == "") {
890 $a = SASO_EVENTTICKETS::getRequestPara('a_sngmbh');
891 }
892 do_action( $this->_do_action_prefix.'executeFrontend', $a, $data );
893 return $sasoEventtickets_Frontend->executeJSON($a, $data);
894 }
895
896 public function replacingShortcode($attr=[], $content = null, $tag = '') {
897 add_filter( $this->_add_filter_prefix.'replaceShortcode', [$this, 'replaceShortcode'], 10, 3 );
898 $ret = apply_filters( $this->_add_filter_prefix.'replaceShortcode', $attr, $content, $tag );
899 return $ret;
900 }
901
902 public function setTicketScannerJS() {
903 wp_enqueue_style("wp-jquery-ui-dialog");
904
905 $js_url = "jquery.qrcode.min.js?_v=".$this->getPluginVersion();
906 wp_enqueue_script(
907 'ajax_script2',
908 plugins_url( "3rd/".$js_url,__FILE__ ),
909 array('jquery', 'jquery-ui-dialog')
910 );
911
912 $js_url = plugin_dir_url(__FILE__)."3rd/html5-qrcode.min.js?_v=".$this->getPluginVersion();
913 wp_register_script('html5-qrcode', $js_url, array('jquery', 'jquery-ui-dialog'));
914 wp_enqueue_script('html5-qrcode');
915
916 // https://github.com/nimiq/qr-scanner - NEW scanner lib
917 $js_url = plugin_dir_url(__FILE__)."3rd/qr-scanner-1.4.2/qr-scanner.umd.min.js?_v=".$this->getPluginVersion();
918 wp_register_script('qr-scanner', $js_url, array('jquery', 'jquery-ui-dialog'));
919 wp_enqueue_script('qr-scanner');
920
921 $js_url = "ticket_scanner.js?_v=".$this->getPluginVersion();
922 if (defined('WP_DEBUG')) $js_url .= '&t='.time();
923 $js_url = plugins_url( $js_url,__FILE__ );
924 wp_register_script('ajax_script_ticket_scanner', $js_url, array('jquery', 'jquery-ui-dialog', 'wp-i18n'));
925 wp_enqueue_script('ajax_script_ticket_scanner');
926 wp_set_script_translations('ajax_script_ticket_scanner', 'event-tickets-with-ticket-scanner', __DIR__.'/languages');
927
928 $ticketScannerDontRememberCamChoice = $this->getOptions()->isOptionCheckboxActive("ticketScannerDontRememberCamChoice") ? true : false;
929
930 $vars = [
931 'root' => esc_url_raw( rest_url() ),
932 '_plugin_home_url' =>plugins_url( "",__FILE__ ),
933 '_action' => $this->_prefix.'_executeAdminSettings',
934 '_isPremium'=>$this->isPremium(),
935 '_isUserLoggedin'=>is_user_logged_in(),
936 '_userId'=>get_current_user_id(),
937 '_restPrefixUrl'=>SASO_EVENTTICKETS::getRESTPrefixURL(),
938 '_siteUrl'=>get_site_url(),
939 '_params'=>["auth"=>$this->getAuthtokenHandler()::$authtoken_param],
940 //'url' => admin_url( 'admin-ajax.php' ), // not used for now in ticketscanner.js
941 'url' => rest_get_server(), // not used for now in ticketscanner.js
942 'nonce' => wp_create_nonce( 'wp_rest' ),
943 //'nonce' => wp_create_nonce( $this->_js_nonce ),
944 'ajaxActionPrefix' => $this->_prefix,
945 'wcTicketCompatibilityModeRestURL' => $this->getOptions()->getOptionValue('wcTicketCompatibilityModeRestURL', ''),
946 'IS_PRETTY_PERMALINK_ACTIVATED' => get_option('permalink_structure') ? true :false,
947 'ticketScannerDontRememberCamChoice' => $ticketScannerDontRememberCamChoice,
948 'ticketScannerStartCamWithoutButtonClicked' => $this->getOptions()->isOptionCheckboxActive('ticketScannerStartCamWithoutButtonClicked'),
949 'ticketScannerDontShowOptionControls' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDontShowOptionControls'),
950 'ticketScannerScanAndRedeemImmediately' => $this->getOptions()->isOptionCheckboxActive('ticketScannerScanAndRedeemImmediately'),
951 'ticketScannerHideTicketInformation' => $this->getOptions()->isOptionCheckboxActive('ticketScannerHideTicketInformation'),
952 'ticketScannerHideTicketInformationShowShortDesc' => $this->getOptions()->isOptionCheckboxActive('ticketScannerHideTicketInformationShowShortDesc'),
953 'ticketScannerDontShowBtnPDF' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDontShowBtnPDF'),
954 'ticketScannerDontShowBtnBadge' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDontShowBtnBadge'),
955 'ticketScannerDisplayTimes' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDisplayTimes')
956 ];
957 $vars = apply_filters( $this->_add_filter_prefix.'main_setTicketScannerJS', $vars );
958 wp_localize_script(
959 'ajax_script_ticket_scanner',
960 'Ajax_'.$this->_prefix, // name der injected variable
961 $vars
962 );
963
964 do_action( $this->_do_action_prefix.'main_setTicketScannerJS', $js_url );
965 }
966
967 public function replacingShortcodeTicketScanner($attr=[], $content = null, $tag = '') {
968 $this->setTicketScannerJS();
969 return '
970 <center>
971 <div style="width:90%;max-width:1024px;">'.$this->getTicketHandler()->getTicketScannerHTMLBoilerplate().'
972 </div>
973 </center>
974 ';
975 }
976
977 /**
978 * Shortcode to display ticket detail view on any page
979 * Usage: [sasoEventTicketsValidator_ticket_detail] with ?ticket=CODE in URL
980 * Or: [sasoEventTicketsValidator_ticket_detail code="TICKET-CODE"]
981 */
982 public function replacingShortcodeTicketDetail($attr = [], $content = null, $tag = ''): string {
983 $code = '';
984 if (!empty($attr['code'])) {
985 $code = sanitize_text_field($attr['code']);
986 } elseif (isset($_GET['ticket'])) {
987 $code = sanitize_text_field($_GET['ticket']);
988 }
989
990 if (empty($code)) {
991 return '<p>' . esc_html__('No ticket code provided. Use ?ticket=YOUR-CODE in the URL.', 'event-tickets-with-ticket-scanner') . '</p>';
992 }
993
994 // Build a fake request URI for the ticket
995 $ticketPath = $this->getCore()->getTicketURLPath(true);
996 $fakeUri = $ticketPath . $code;
997
998 include_once plugin_dir_path(__FILE__) . "sasoEventtickets_Ticket.php";
999 $ticketInstance = sasoEventtickets_Ticket::Instance($fakeUri);
1000
1001 return $ticketInstance->renderTicketDetailForShortcode();
1002 }
1003
1004 public function getCodesTextAsShortList($codes) {
1005 $ret = "";
1006 if (count($codes) > 0) {
1007 $ret = '<table>';
1008 $wcTicketUserProfileDisplayTicketDetailURL = $this->getOptions()->isOptionCheckboxActive("wcTicketUserProfileDisplayTicketDetailURL");
1009 $wcTicketUserProfileDisplayRedeemAmount = $this->getOptions()->isOptionCheckboxActive("wcTicketUserProfileDisplayRedeemAmount");
1010
1011 $label_expired = $this->getOptions()->getOptionValue('wcTicketTransTicketExpired', 'EXPIRED');
1012 $label_stolen = $this->getOptions()->getOptionValue('wcTicketTransTicketIsStolen', 'REPORTED AS STOLEN');
1013 $label_notvalid = $this->getOptions()->getOptionValue('wcTicketTransTicketNotValid', 'DISABLED');
1014
1015 $myCodes = [];
1016 foreach($codes as $idx => $codeObj) {
1017 $metaObj = $this->getCore()->encodeMetaValuesAndFillObject($codeObj['meta'], $codeObj);
1018
1019 $_c = '<tr><td style="text-align:right;">'.($idx + 1).'.</td><td>'.$codeObj['code_display'].'</td><td>';
1020 if ($codeObj['aktiv'] == 1) {
1021 if ($this->getCore()->checkCodeExpired($codeObj)) {
1022 $_c .= $label_expired;
1023 }
1024 } else if ($codeObj['aktiv'] == 0) {
1025 $_c .= $label_notvalid;
1026 } else if ($codeObj['aktiv'] == 2) {
1027 $_c .= $label_stolen;
1028 }
1029 $_c .= '</td>';
1030
1031 if ($wcTicketUserProfileDisplayTicketDetailURL) {
1032 $_c .= "<td>";
1033 $url = $this->getCore()->getTicketURL($codeObj, $metaObj);
1034 if (!empty($url)) {
1035 $_c .= '<a href="'.$url.'" target="_blank">Ticket Details</a>';
1036 }
1037 $_c .= "</td>";
1038 }
1039 if ($wcTicketUserProfileDisplayRedeemAmount && function_exists("wc_get_product")) {
1040 $_c .= "<td>";
1041 $text_redeem_amount = $this->getTicketHandler()->getRedeemAmountText($codeObj, $metaObj, false);
1042 if (!empty($text_redeem_amount)) {
1043 $_c .= $text_redeem_amount;
1044 }
1045 $_c .= "<td>";
1046 }
1047 $_c .= "</tr>";
1048 $myCodes[] = $_c;
1049 }
1050 $ret .= implode("", $myCodes);
1051 $ret .= "</table>";
1052 }
1053 $ret = apply_filters( $this->_add_filter_prefix.'main_getCodesTextAsShortList', $ret, $codes );
1054 return $ret;
1055 }
1056
1057 public function getMyCodeText($user_id, $attr=[], $content = null, $tag = '', $codes = null) {
1058 $ret = '';
1059 // check ob eingeloggt
1060 $pre_text = $this->getOptions()->getOptionValue('userDisplayCodePrefix', '');
1061 if (!empty($pre_text)) $pre_text .= " ";
1062
1063 // If codes are provided (e.g., from order_id), use them; otherwise fetch by user_id
1064 if ($codes === null && $user_id > 0) {
1065 $codes = $this->getCore()->getCodesByRegUserId($user_id);
1066 }
1067
1068 if ($codes !== null && count($codes) > 0) {
1069 $ret .= "<b>".$pre_text."</b><br>";
1070 $ret .= $this->getCodesTextAsShortList($codes);
1071
1072 // Download All as PDF button
1073 $show_download_btn = isset($attr['download_all_pdf']) &&
1074 in_array(strtolower($attr['download_all_pdf']), ['true', '1', 'yes'], true);
1075
1076 if ($show_download_btn && count($codes) > 0) {
1077 $max_tickets = isset($attr['download_all_pdf_max']) ? intval($attr['download_all_pdf_max']) : 100;
1078 $btn_label = isset($attr['download_all_pdf_label']) ?
1079 sanitize_text_field($attr['download_all_pdf_label']) :
1080 __('Download All Tickets as PDF', 'event-tickets-with-ticket-scanner');
1081
1082 $ticket_count = count($codes);
1083 if ($ticket_count > $max_tickets) {
1084 $ret .= '<p><em>' . sprintf(
1085 /* translators: %d: maximum number of tickets */
1086 esc_html__('Too many tickets (%1$d). Maximum %2$d tickets can be downloaded at once.', 'event-tickets-with-ticket-scanner'),
1087 $ticket_count,
1088 $max_tickets
1089 ) . '</em></p>';
1090 } else {
1091 // Generate secure download URL
1092 $nonce = wp_create_nonce('download_my_codes_pdf_' . $user_id);
1093 $download_url = admin_url('admin-ajax.php') . '?' . http_build_query([
1094 'action' => $this->_prefix . '_downloadMyCodesAsPDF',
1095 'nonce' => $nonce
1096 ]);
1097 $ret .= '<p style="margin-top:10px;"><a href="' . esc_url($download_url) . '" class="button" target="_blank">' .
1098 esc_html($btn_label) . ' (' . $ticket_count . ')</a></p>';
1099 }
1100 }
1101 }
1102 if (empty($ret) && $this->getOptions()->isOptionCheckboxActive('userDisplayCodePrefixAlways')) {
1103 $ret .= $pre_text;
1104 }
1105 $ret = apply_filters( $this->_add_filter_prefix.'main_getMyCodeText', $ret, $user_id, $attr, $content, $tag);
1106 return $ret;
1107 }
1108
1109 /**
1110 * AJAX handler: Download all user's tickets as one PDF
1111 * Used by shortcode [sasoEventTicketsValidator_code download_all_pdf="true"]
1112 */
1113 public function downloadMyCodesAsPDF(): void {
1114 $user_id = get_current_user_id();
1115
1116 // Must be logged in
1117 if ($user_id <= 0) {
1118 wp_die(esc_html__('You must be logged in to download tickets.', 'event-tickets-with-ticket-scanner'), 403);
1119 }
1120
1121 // Verify nonce
1122 $nonce = isset($_GET['nonce']) ? sanitize_text_field($_GET['nonce']) : '';
1123 if (!wp_verify_nonce($nonce, 'download_my_codes_pdf_' . $user_id)) {
1124 wp_die(esc_html__('Security check failed. Please refresh the page and try again.', 'event-tickets-with-ticket-scanner'), 403);
1125 }
1126
1127 // Get user's codes
1128 $codes = $this->getCore()->getCodesByRegUserId($user_id);
1129
1130 if (empty($codes)) {
1131 wp_die(esc_html__('No tickets found.', 'event-tickets-with-ticket-scanner'), 404);
1132 }
1133
1134 // Limit to 100 tickets
1135 $max_tickets = 100;
1136 if (count($codes) > $max_tickets) {
1137 wp_die(
1138 sprintf(
1139 /* translators: %d: maximum number of tickets */
1140 esc_html__('Too many tickets. Maximum %d tickets can be downloaded at once.', 'event-tickets-with-ticket-scanner'),
1141 $max_tickets
1142 ),
1143 400
1144 );
1145 }
1146
1147 // Extract code strings
1148 $code_strings = array_map(function($codeObj) {
1149 return $codeObj['code'];
1150 }, $codes);
1151
1152 // Generate merged PDF
1153 $filename = 'my_tickets_' . wp_date('Ymd_Hi') . '.pdf';
1154
1155 try {
1156 $this->getTicketHandler()->generateOnePDFForCodes($code_strings, $filename, 'I');
1157 } catch (Exception $e) {
1158 $this->getAdmin()->logErrorToDB($e, null, 'downloadMyCodesAsPDF');
1159 wp_die(esc_html__('Error generating PDF. Please try again later.', 'event-tickets-with-ticket-scanner'), 500);
1160 }
1161
1162 exit;
1163 }
1164
1165 public function getMyCodeFormatted($user_id, $attr=[], $content = null, $tag = '', $codes = null) {
1166 $format = "json";
1167 if (isset($attr["format"])) {
1168 $format = strtolower(trim(sanitize_key($attr["format"])));
1169 }
1170 $display = ["codes"];
1171 if (isset($attr["display"])) {
1172
1173 $_d = trim(sanitize_text_field($attr["display"]));
1174 $_da = explode(",", $_d);
1175 if (count($_da) > 0) {
1176 $display = [];
1177 }
1178 foreach ($_da as $item) {
1179 $item = trim($item);
1180 $display[] = $item;
1181 }
1182 }
1183
1184 $output = [];
1185 //codes,validation,user,used,confirmedCount,woocommerce,wc_rp,wc_ticket
1186 // If codes are provided (e.g., from order_id), use them; otherwise fetch by user_id
1187 if ($codes === null) {
1188 $codes = $this->getCore()->getCodesByRegUserId($user_id);
1189 }
1190 $metas = [];
1191 foreach($codes as $codeObj) {
1192 $metas[$codeObj["code"]] = $this->getCore()->encodeMetaValuesAndFillObject($codeObj['meta'], $codeObj);
1193 }
1194 foreach ($display as $item) {
1195 $output[$item] = [];
1196 if ($item == "codes") {
1197 foreach($codes as $codeObj) {
1198 if (isset($codeObj["meta"])) {
1199 unset($codeObj["meta"]);
1200 }
1201 $output[$item][] = $codeObj;
1202 }
1203 } elseif($item == "confirmedCount") {
1204 foreach($metas as $key => $meta) {
1205 $output[$item][] = array_merge(["value"=>$meta[$item]], ["code"=>$key]);
1206 }
1207 } else {
1208 foreach($metas as $key => $m) {
1209 if (is_array($m) && isset($m[$item])) {
1210 $meta = $m[$item];
1211 if (isset($meta["stats_redeemed"])) {
1212 unset($meta["stats_redeemed"]);
1213 }
1214 if (isset($meta["set_by_admin"])) {
1215 unset($meta["set_by_admin"]);
1216 }
1217 if (isset($meta["redeemed_by_admin"])) {
1218 unset($meta["redeemed_by_admin"]);
1219 }
1220 $output[$item][] = array_merge($meta, ["code"=>$key]);
1221 }
1222 }
1223 }
1224 }
1225
1226 switch($format) {
1227 case "json":
1228 default:
1229 $ret = $this->getCore()->json_encode_with_error_handling($output);
1230 }
1231 $ret = apply_filters( $this->_add_filter_prefix.'main_getMyCodeFormatted', $ret, $user_id, $attr, $content, $tag, $output);
1232 return $ret;
1233 }
1234
1235 public function replacingShortcodeMyCode($attr=[], $content = null, $tag = '') {
1236 $user_id = get_current_user_id();
1237 $codes = null; // Will be set if order_id is used
1238
1239 // Check if order_id parameter is provided
1240 if (isset($attr['order_id']) && !empty($attr['order_id'])) {
1241 $order_id = intval($attr['order_id']);
1242 if ($order_id > 0) {
1243 // Security check: can current user access this order?
1244 if (!$this->canUserAccessOrder($order_id)) {
1245 return '<p>' . esc_html__('You do not have permission to view tickets for this order.', 'event-tickets-with-ticket-scanner') . '</p>';
1246 }
1247 // Get codes by order_id
1248 $codes = $this->getCore()->getCodesByOrderId($order_id);
1249 }
1250 }
1251
1252 if (count($attr) > 0 && isset($attr["format"])) {
1253 return $this->getMyCodeFormatted($user_id, $attr, $content, $tag, $codes);
1254 } else {
1255 return $this->getMyCodeText($user_id, $attr, $content, $tag, $codes);
1256 }
1257 }
1258
1259 /**
1260 * Check if current user can access a specific order's tickets
1261 *
1262 * @param int $order_id WooCommerce order ID
1263 * @return bool True if user can access, false otherwise
1264 */
1265 private function canUserAccessOrder(int $order_id): bool {
1266 $order = wc_get_order($order_id);
1267 if (!$order) {
1268 return false;
1269 }
1270
1271 // 1. Admin/Shop-Manager can access all orders
1272 if (current_user_can('manage_woocommerce')) {
1273 return true;
1274 }
1275
1276 $current_user_id = get_current_user_id();
1277
1278 // 2. Logged-in user owns this order
1279 if ($current_user_id > 0 && $order->get_user_id() == $current_user_id) {
1280 return true;
1281 }
1282
1283 // 3. Valid order key in URL (WooCommerce thank-you page pattern)
1284 $order_key_from_url = isset($_GET['key']) ? sanitize_text_field($_GET['key']) : '';
1285 if (!empty($order_key_from_url) && $order->get_order_key() === $order_key_from_url) {
1286 return true;
1287 }
1288
1289 return false;
1290 }
1291
1292 public function replacingShortcodeFeatureList($attr=[], $content = null, $tag = '') {
1293 //$features = $this->getAdmin()->getOptions();
1294 //$options = $features["options"];
1295 $options = $this->getOptions()->getOptions();
1296 $features = [];
1297 $act_heading = "";
1298 foreach ($options as $option) {
1299 if ($option["key"] == "serial") continue;
1300 if ($option["type"] == "heading") {
1301 $act_heading = $option["key"];
1302 $features[$act_heading] = ["heading"=>$option, "options"=>[]];
1303 } else {
1304 if ($act_heading != "") {
1305 $features[$act_heading]["options"][] = $option;
1306 }
1307 }
1308 }
1309
1310 $ret = "";
1311 uasort($features, function($a, $b) {
1312 return strnatcasecmp($a["heading"]["label"], $b["heading"]["label"]);
1313 });
1314 foreach ($features as $key => $feature) {
1315 $ret .= '<h3>'.$feature["heading"]["label"].'</h3>';
1316 $video = $feature["heading"]["_doc_video"] != "" ? ' <a href="'.$feature["heading"]["_doc_video"].'" target="_blank"><span class="dashicons dashicons-video-alt3"></span> Introduction video</a>' : "";
1317 $ret .= '<p>'.trim($feature["heading"]["desc"].$video).'</p>';
1318 if (count($feature["options"]) > 0) {
1319 $ret .= '<ul>';
1320 foreach ($feature["options"] as $option) {
1321 $label = $option["label"];
1322 $desc = $option["desc"];
1323 $desc .= $option["_doc_video"] != "" ? ' <a href="'.$option["_doc_video"].'" target="_blank"><span class="dashicons dashicons-video-alt3"></span> Introduction video</a>' : "";
1324 $desc = trim($desc);
1325 if ($desc != "") {
1326 $desc = '<p>'.$desc.'</p>';
1327 }
1328 $label = '<span class="dashicons dashicons-yes"></span> '.$label;
1329 $ret .= '<li>'.$label.$desc.'</li>';
1330 }
1331 $ret .= '</ul>';
1332 }
1333 $ret .= '<hr>';
1334 }
1335
1336 return $ret;
1337 }
1338
1339 public function replacingShortcodeEventViews($attr=[], $content = null, $tag = '') {
1340 // iterate over all woocommerce products and check if they are an event
1341 $view = "calendar|list";
1342 if (isset($attr["view"])) {
1343 $view = strtolower(trim(sanitize_key($attr["view"])));
1344 }
1345 $months_to_show = 3;
1346 if (isset($attr["months_to_show"])) {
1347 $m = intval($attr["months_to_show"]);
1348 if ($m > 0) $months_to_show = $m;
1349 }
1350
1351 $month_start = strtotime(wp_date("Y-m-1 00:00:00"));
1352 //$month_end = strtotime(wp_date("Y-m-t 23:59:59"));
1353 $month_end = strtotime(wp_date("Y-m-1 23:59:59", strtotime("+".$months_to_show." month", $month_start)));
1354
1355 $products_args = array(
1356 'post_type' => 'product',
1357 'post_status' => 'publish',
1358 'posts_per_page' => -1, // -1 zeigt alle Produkte an
1359 'meta_query' => array(
1360 [
1361 'key' => 'saso_eventtickets_is_ticket',
1362 'value' => 'yes',
1363 'compare' => '='
1364 ]
1365 )
1366 );
1367 $posts = get_posts($products_args); // get only ticket products
1368
1369 $list_infos = [
1370 'months_to_show'=>$months_to_show,
1371 'month_start'=>$month_start,
1372 'month_end'=>$month_end,
1373 'view'=>$view
1374 ];
1375 $products_to_show = [];
1376 // Ergebnisse überprüfen
1377 foreach ($posts as $post) {
1378 $product_ids = [];
1379 $product = wc_get_product( $post->ID );
1380
1381 // check if event is variant product
1382 $is_variable = $product->get_type() == "variable";
1383 if ($is_variable) {
1384 // check if event dates are the same for all variants
1385 $date_is_for_all_variants = get_post_meta( $product_id, 'saso_eventtickets_is_date_for_all_variants', true ) == "yes" ? true : false;
1386 if ($date_is_for_all_variants == false) {
1387 // if not add also the variants
1388 $childs = $product->get_children();
1389 foreach ($childs as $child_id) {
1390 if (get_post_meta($child_id, '_saso_eventtickets_is_not_ticket', true) == "yes") {
1391 continue;
1392 }
1393 $product_ids[] = $child_id;
1394 }
1395 }
1396 } else {
1397 $product_ids[] = $product->get_id();
1398 }
1399
1400 foreach ($product_ids as $product_id) {
1401 $product = wc_get_product( $product_id );
1402 $dates = $this->getTicketHandler()->calcDateStringAllowedRedeemFrom($product_id);
1403 //if ($dates['ticket_end_date_timestamp'] > $month_start && $dates['ticket_start_date_timestamp'] < $month_end) {
1404 if ($dates['ticket_end_date_timestamp'] >= $month_start || ($dates['ticket_start_date_timestamp'] >= $month_start && $dates['ticket_start_date_timestamp'] <= $month_end)) {
1405 // set product to hidden
1406 $product_data = array(
1407 'ID' => $product_id,
1408 'dates' => $dates,
1409 'event'=> [
1410 'location' => trim(get_post_meta( $product_id, 'saso_eventtickets_event_location', true )),
1411 'location_label' => wp_kses_post(trim($this->getAdmin()->getOptionValue("wcTicketTransLocation")))
1412 ],
1413 'product' => [
1414 'title' => $product->get_name(),
1415 'url' => get_permalink($product_id),
1416 'price' =>$product->get_price(),
1417 'price_html' => $product->get_price_html(),
1418 'type' => $product->get_type(),
1419 ]
1420 );
1421 $products_to_show[] = $product_data;
1422 }
1423 }
1424 }
1425
1426 $divId = "sasoEventTicketsValidator_eventsview";
1427
1428 // add js for the events
1429 wp_enqueue_style("wp-jquery-ui-dialog");
1430
1431 $js_url = "ticket_events.js?_v=".$this->getPluginVersion();
1432 if (defined('WP_DEBUG')) $js_url .= '&t='.time();
1433 $js_url = plugins_url( $js_url,__FILE__ );
1434 wp_register_script('ajax_script_ticket_events', $js_url, array('jquery', 'jquery-ui-dialog', 'wp-i18n'));
1435 wp_enqueue_script('ajax_script_ticket_events');
1436 wp_set_script_translations('ajax_script_ticket_events', 'event-tickets-with-ticket-scanner', __DIR__.'/languages');
1437 wp_enqueue_style("ticket_events_css", plugins_url( "",__FILE__ ).'/css/calendar.css', [], true);
1438
1439 // add all events as an array for max 3 months??? or config parameter
1440 $vars = [
1441 'root' => esc_url_raw( rest_url() ),
1442 '_plugin_home_url' =>plugins_url( "",__FILE__ ),
1443 '_action' => $this->_prefix.'_executeFrontendEvents',
1444 '_isPremium'=>$this->isPremium(),
1445 '_isUserLoggedin'=>is_user_logged_in(),
1446 '_userId'=>get_current_user_id(),
1447 '_premJS'=>$this->isPremium() && method_exists($this->getPremiumFunctions(), "getJSFrontEventFile") ? $this->getPremiumFunctions()->getJSFrontEventFile() : '',
1448 '_siteUrl'=>get_site_url(),
1449 'events' => $products_to_show,
1450 'list_infos' => $list_infos,
1451 'format_date' => $this->getOptions()->getOptionDateFormat(),
1452 'format_time' => $this->getOptions()->getOptionTimeFormat(),
1453 'format_datetime' => $this->getOptions()->getOptionDateTimeFormat(),
1454 'url' => admin_url( 'admin-ajax.php' ),
1455 'nonce' => wp_create_nonce( $this->_js_nonce ),
1456 'ajaxActionPrefix' => $this->_prefix,
1457 'divId' => $divId
1458 ];
1459 $vars = apply_filters( $this->_add_filter_prefix.'main_setTicketEventJS', $vars );
1460 wp_localize_script(
1461 'ajax_script_ticket_events',
1462 'Ajax_ticket_events_'.$this->_prefix, // name der injected variable
1463 $vars
1464 );
1465
1466 do_action( $this->_do_action_prefix.'main_setTicketEventJS', $js_url );
1467
1468 $ret = '';
1469 if (!isset($attr['divid']) || trim($attr['divid']) == "") {
1470 $ret .= '<div id="'.$divId.'">'.__('...loading...', 'event-tickets-with-ticket-scanner').'</div>';
1471 }
1472
1473 $ret = apply_filters( $this->_add_filter_prefix.'main_replacingShortcodeEventViews', $ret );
1474 do_action( $this->_do_action_prefix.'main_replacingShortcodeEventViews', $vars, $ret );
1475
1476 return $ret;
1477 }
1478
1479 public function replaceShortcode($attr=[], $content = null, $tag = '') {
1480 // einbinden das js starter skript
1481 $js_url = $this->_js_file."?_v=".$this->_js_version;
1482 if (defined( 'WP_DEBUG')) $js_url .= '&debug=1';
1483 $userDivId = !isset($attr['divid']) || trim($attr['divid']) == "" ? '' : trim($attr['divid']);
1484
1485 $attr = array_change_key_case( (array) $attr, CASE_LOWER );
1486
1487 wp_enqueue_script(
1488 'ajax_script_validator',
1489 plugins_url( $js_url,__FILE__ ),
1490 array('jquery', 'wp-i18n')
1491 );
1492 wp_set_script_translations('ajax_script_validator', 'event-tickets-with-ticket-scanner', __DIR__.'/languages');
1493
1494 $vars = array(
1495 'shortcode_attr'=>json_encode($attr),
1496 '_plugin_home_url' =>plugins_url( "",__FILE__ ),
1497 '_action' => $this->_prefix.'_executeFrontend',
1498 '_isPremium'=>$this->isPremium(),
1499 '_isUserLoggedin'=>is_user_logged_in(),
1500 '_premJS'=>$this->isPremium() && method_exists($this->getPremiumFunctions(), "getJSFrontFile") ? $this->getPremiumFunctions()->getJSFrontFile() : '',
1501 'url' => admin_url( 'admin-ajax.php' ),
1502 'nonce' => wp_create_nonce( $this->_js_nonce ),
1503 'ajaxActionPrefix' => $this->_prefix,
1504 'divPrefix' => $userDivId == "" ? $this->_prefix : $userDivId,
1505 'divId' => $this->_divId,
1506 'jsFiles' => plugins_url( 'validator.js?_v='.$this->_js_version, __FILE__ )
1507 );
1508 $vars['_messages'] = [
1509 'msgCheck0'=>$this->getOptions()->getOptionValue('textValidationMessage0'),
1510 'msgCheck1'=>$this->getOptions()->getOptionValue('textValidationMessage1'),
1511 'msgCheck2'=>$this->getOptions()->getOptionValue('textValidationMessage2'),
1512 'msgCheck3'=>$this->getOptions()->getOptionValue('textValidationMessage3'),
1513 'msgCheck4'=>$this->getOptions()->getOptionValue('textValidationMessage4'),
1514 'msgCheck5'=>$this->getOptions()->getOptionValue('textValidationMessage5'),
1515 'msgCheck6'=>$this->getOptions()->getOptionValue('textValidationMessage6')
1516 ];
1517 $vars = apply_filters( $this->_add_filter_prefix.'main_replaceShortcode', $vars );
1518
1519 if ($this->isPremium() && method_exists($this->getPremiumFunctions(), "addJSFrontFile")) $this->getPremiumFunctions()->addJSFrontFile();
1520
1521 wp_localize_script(
1522 'ajax_script_validator',
1523 'Ajax_'.$this->_prefix, // name of the injected variable
1524 $vars
1525 );
1526 $ret = '';
1527 if (!isset($attr['divid']) || trim($attr['divid']) == "") {
1528 $ret .= '<div id="'.$this->_divId.'">'.__('...loading...', 'event-tickets-with-ticket-scanner').'</div>';
1529 }
1530
1531 $ret = apply_filters( $this->_add_filter_prefix.'main_replaceShortcode_2', $ret );
1532 do_action( $this->_do_action_prefix.'main_replaceShortcode', $vars, $ret );
1533
1534 return $ret;
1535 }
1536
1537 /**
1538 * Show admin notice when premium subscription is about to expire or has expired
1539 *
1540 * - Warning: 14 days before expiration
1541 * - Error: After expiration (during grace period or after)
1542 *
1543 * @return void
1544 */
1545 public function showSubscriptionWarning(): void {
1546 // Only show for premium users
1547 if (!$this->isPremium()) {
1548 return;
1549 }
1550
1551 // Only show in admin
1552 if (!is_admin()) {
1553 return;
1554 }
1555
1556 // Only show to users who can manage options
1557 if (!current_user_can('manage_options')) {
1558 return;
1559 }
1560
1561 $info = $this->getTicketHandler()->get_expiration();
1562
1563 // No expiration date or lifetime license - no warning needed
1564 if (empty($info['timestamp']) || $info['timestamp'] <= 0 || $info['timestamp'] == -1) {
1565 return;
1566 }
1567
1568 // Lifetime subscription type - no warning needed
1569 if (isset($info['subscription_type']) && $info['subscription_type'] === 'lifetime') {
1570 return;
1571 }
1572
1573 $days_left = ($info['timestamp'] - time()) / 86400;
1574 $renewal_url = 'https://vollstart.com/product/event-tickets-with-ticket-scanner/';
1575
1576 // Warning 14 days before expiration
1577 if ($days_left <= 14 && $days_left > 0) {
1578 $date = date_i18n(get_option('date_format'), $info['timestamp']);
1579 echo '<div class="notice notice-warning is-dismissible"><p>';
1580 printf(
1581 /* translators: %1$s: expiration date, %2$s: renewal URL */
1582 esc_html__('Your Event-Tickets Premium subscription expires on %1$s. %2$sRenew now%3$s to keep all features.', 'event-tickets-with-ticket-scanner'),
1583 '<strong>' . esc_html($date) . '</strong>',
1584 '<a href="' . esc_url($renewal_url) . '" target="_blank">',
1585 '</a>'
1586 );
1587 echo '</p></div>';
1588 }
1589
1590 // Error after expiration
1591 if ($days_left <= 0) {
1592 $grace_days = isset($info['grace_period_days']) ? intval($info['grace_period_days']) : 7;
1593 $grace_left = $grace_days + $days_left; // days_left is negative
1594
1595 echo '<div class="notice notice-error"><p>';
1596 if ($grace_left > 0) {
1597 printf(
1598 /* translators: %1$d: days remaining in grace period, %2$s: renewal URL */
1599 esc_html__('Your Premium subscription has expired. You have %1$d days remaining before features are disabled. %2$sReactivate now%3$s', 'event-tickets-with-ticket-scanner'),
1600 ceil($grace_left),
1601 '<a href="' . esc_url($renewal_url) . '" target="_blank">',
1602 '</a>'
1603 );
1604 } else {
1605 printf(
1606 /* translators: %1$s: renewal URL */
1607 esc_html__('Your Premium subscription has expired. Premium features are now limited. %1$sReactivate now%2$s', 'event-tickets-with-ticket-scanner'),
1608 '<a href="' . esc_url($renewal_url) . '" target="_blank">',
1609 '</a>'
1610 );
1611 }
1612 echo '</p></div>';
1613 }
1614 }
1615
1616 /**
1617 * Show admin notice when premium plugin version is outdated
1618 *
1619 * Encourages users with old premium versions to upgrade
1620 *
1621 * @return void
1622 */
1623 public function showOutdatedPremiumWarning(): void {
1624 // Only show if premium plugin is active
1625 if (!defined('SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION')) {
1626 return;
1627 }
1628
1629 // Only show in admin
1630 if (!is_admin()) {
1631 return;
1632 }
1633
1634 // Only show to users who can manage options
1635 if (!current_user_can('manage_options')) {
1636 return;
1637 }
1638
1639 // Only show on our plugin pages
1640 $screen = get_current_screen();
1641 if ($screen === null || strpos($screen->id, 'saso-event-tickets') === false) {
1642 return;
1643 }
1644
1645 // Minimum recommended premium version
1646 $min_premium_version = '1.6.0';
1647
1648 if (version_compare(SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION, $min_premium_version, '<')) {
1649 $upgrade_url = 'https://vollstart.com/product/event-tickets-with-ticket-scanner/';
1650 echo '<div class="notice notice-warning is-dismissible"><p>';
1651 printf(
1652 /* translators: %1$s: current version, %2$s: recommended version, %3$s: upgrade URL */
1653 esc_html__('Your Event-Tickets Premium plugin (v%1$s) is outdated. Version %2$s+ includes new features and improvements. %3$sUpgrade now%4$s', 'event-tickets-with-ticket-scanner'),
1654 esc_html(SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION),
1655 esc_html($min_premium_version),
1656 '<a href="' . esc_url($upgrade_url) . '" target="_blank">',
1657 '</a>'
1658 );
1659 echo '</p></div>';
1660 }
1661 }
1662 }
1663 $sasoEventtickets = sasoEventtickets::Instance();
1664 ?>
1665