event-tickets-with-ticket-scanner
Last commit date
3rd
3 months ago
css
3 months ago
img
3 months ago
includes
3 months ago
js
3 months ago
languages
3 months ago
ticket
3 months ago
vendors
3 months ago
SASO_EVENTTICKETS.php
3 months ago
backend.js
3 months ago
changelog-features.json
3 months ago
changelog.txt
3 months ago
db.php
3 months ago
index.php
3 months ago
init_file.php
3 months ago
order_details.js
3 months ago
pwa-sw.js
3 months ago
readme.txt
3 months ago
saso-eventtickets-validator.js
3 months ago
sasoEventtickets_AdminSettings.php
3 months ago
sasoEventtickets_Authtoken.php
3 months ago
sasoEventtickets_Base.php
3 months ago
sasoEventtickets_Core.php
3 months ago
sasoEventtickets_Frontend.php
3 months ago
sasoEventtickets_Messenger.php
3 months ago
sasoEventtickets_Options.php
3 months ago
sasoEventtickets_PDF.php
3 months ago
sasoEventtickets_Seating.php
3 months ago
sasoEventtickets_Ticket.php
3 months ago
sasoEventtickets_TicketBadge.php
3 months ago
sasoEventtickets_TicketDesigner.php
3 months ago
sasoEventtickets_TicketQR.php
3 months ago
ticket_events.js
3 months ago
ticket_scanner.js
3 months ago
validator.js
3 months ago
wc_backend.js
3 months ago
wc_frontend.js
3 months ago
woocommerce-hooks.php
3 months ago
index.php
2178 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.9.9 |
| 7 | * Author: Vollstart |
| 8 | * Author URI: https://vollstart.com |
| 9 | * Requires at least: 6.0 |
| 10 | * Tested up to: 6.9 |
| 11 | * Requires PHP: 8.1 |
| 12 | * Text Domain: event-tickets-with-ticket-scanner |
| 13 | * License: GPLv2 or later |
| 14 | * License URI: https://www.gnu.org/licenses/gpl-2.0.html |
| 15 | * |
| 16 | * Event Tickets with Ticket Scanner is distributed in the hope that it will be useful, |
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | * GNU General Public License for more details. |
| 20 | */ |
| 21 | // https://semver.org/ |
| 22 | // https://developer.wordpress.org/plugins/security/securing-output/ |
| 23 | // https://developer.wordpress.org/plugins/security/securing-input/ |
| 24 | |
| 25 | include_once(plugin_dir_path(__FILE__)."init_file.php"); |
| 26 | |
| 27 | if (!defined('SASO_EVENTTICKETS_PLUGIN_VERSION')) |
| 28 | define('SASO_EVENTTICKETS_PLUGIN_VERSION', '2.9.9'); |
| 29 | if (!defined('SASO_EVENTTICKETS_PLUGIN_DIR_PATH')) |
| 30 | define('SASO_EVENTTICKETS_PLUGIN_DIR_PATH', plugin_dir_path(__FILE__)); |
| 31 | |
| 32 | include_once plugin_dir_path(__FILE__)."SASO_EVENTTICKETS.php"; |
| 33 | |
| 34 | class sasoEventtickets_fakeprem{} |
| 35 | class sasoEventtickets { |
| 36 | private $_js_version; |
| 37 | private $_js_file = 'saso-eventtickets-validator.js'; |
| 38 | public $_js_nonce = 'sasoEventtickets'; |
| 39 | public $_do_action_prefix = 'saso_eventtickets_'; |
| 40 | public $_add_filter_prefix = 'saso_eventtickets_'; |
| 41 | protected $_prefix = 'sasoEventtickets'; |
| 42 | public $_prefix_session = 'sasoEventtickets_'; |
| 43 | protected $_shortcode = 'sasoEventTicketsValidator'; |
| 44 | protected $_shortcode_mycode = 'sasoEventTicketsValidator_code'; |
| 45 | protected $_shortcode_eventviews = 'sasoEventTicketsValidator_eventsview'; |
| 46 | protected $_shortcode_ticket_scanner = 'sasoEventTicketsValidator_ticket_scanner'; |
| 47 | protected $_shortcode_feature_list = 'sasoEventTicketsValidator_feature_list'; |
| 48 | protected $_shortcode_ticket_detail = 'sasoEventTicketsValidator_ticket_detail'; |
| 49 | protected $_divId = 'sasoEventtickets'; |
| 50 | |
| 51 | private $_isPrem = null; |
| 52 | private $_isCheckingSubscription = false; |
| 53 | private $_premium_plugin_name = 'event-tickets-with-ticket-scanner-premium'; |
| 54 | private $_premium_function_file = 'sasoEventtickets_PremiumFunctions.php'; |
| 55 | private $PREMFUNCTIONS = null; |
| 56 | private $_oldPremiumDetected = false; |
| 57 | private $_starterOrStopDetected = false; |
| 58 | private $BASE = null; |
| 59 | private $CORE = null; |
| 60 | private $ADMIN = null; |
| 61 | private $FRONTEND = null; |
| 62 | private $OPTIONS = null; |
| 63 | |
| 64 | private $isAllowedAccess = null; |
| 65 | |
| 66 | public static function Instance() { |
| 67 | static $inst = null; |
| 68 | if ($inst === null) { |
| 69 | $inst = new self(); |
| 70 | } |
| 71 | return $inst; |
| 72 | } |
| 73 | |
| 74 | public function __construct() { |
| 75 | global $sasoEventtickets; |
| 76 | $sasoEventtickets = $this; // Set early to prevent circular dependency with sasoEventtickets_Ticket |
| 77 | $this->_js_version = $this->getPluginVersion() . (defined('WP_DEBUG') && WP_DEBUG ? '.' . time() : ''); |
| 78 | $this->initHandlers(); |
| 79 | } |
| 80 | |
| 81 | public function initHandlers() { |
| 82 | add_action( 'init', [$this, 'load_plugin_textdomain'] ); |
| 83 | add_action( 'upgrader_process_complete', [$this, 'listener_upgrader_process_complete'], 10, 2 ); |
| 84 | //add_action('admin_init', [$this, 'initialize_plugin']); |
| 85 | if (is_admin()) { // called in backend admin, admin-ajax! |
| 86 | $this->init_backend(); |
| 87 | } else { // called in front end |
| 88 | $this->init_frontend(); |
| 89 | } |
| 90 | add_action( 'sasoEventtickets_cronjob_daily', [$this, 'relay_sasoEventtickets_cronjob_daily'], 10, 0 ); // set in tickets.php |
| 91 | add_action( 'plugins_loaded', [$this, 'WooCommercePluginLoaded'], 20, 0 ); |
| 92 | if (basename($_SERVER['SCRIPT_NAME']) == "admin-ajax.php") { |
| 93 | add_action('wp_ajax_nopriv_'.$this->_prefix.'_executeFrontend', [$this,'executeFrontend_a'], 10, 0); // nicht angemeldete user, sollen eine antwort erhalten |
| 94 | add_action('wp_ajax_'.$this->_prefix.'_executeFrontend', [$this,'executeFrontend_a'], 10, 0); // falls eingeloggt ist |
| 95 | add_action('wp_ajax_'.$this->_prefix.'_executeWCBackend', [$this,'executeWCBackend'], 10, 0); // falls eingeloggt ist |
| 96 | add_action('wp_ajax_'.$this->_prefix.'_downloadMyCodesAsPDF', [$this,'downloadMyCodesAsPDF'], 10, 0); // logged in users only |
| 97 | } |
| 98 | if (method_exists($this->getPremiumFunctions(), "initHandlers")) { |
| 99 | $this->getPremiumFunctions()->initHandlers(); |
| 100 | } |
| 101 | $this->cronjob_daily_activate(); |
| 102 | } |
| 103 | public function cronjob_daily_activate() { |
| 104 | $args = []; |
| 105 | if (! wp_next_scheduled ( 'sasoEventtickets_cronjob_daily', $args )) { |
| 106 | wp_schedule_event( strtotime("00:05"), 'daily', 'sasoEventtickets_cronjob_daily', $args ); |
| 107 | } |
| 108 | } |
| 109 | public function cronjob_daily_deactivate() { |
| 110 | wp_clear_scheduled_hook( 'sasoEventtickets_cronjob_daily' ); |
| 111 | } |
| 112 | public function load_plugin_textdomain() { |
| 113 | load_plugin_textdomain( 'event-tickets-with-ticket-scanner', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); |
| 114 | } |
| 115 | public function getPluginPath() { |
| 116 | return SASO_EVENTTICKETS_PLUGIN_DIR_PATH; |
| 117 | } |
| 118 | public function getPluginVersion() { |
| 119 | return SASO_EVENTTICKETS_PLUGIN_VERSION; |
| 120 | } |
| 121 | public function getPluginVersions() { |
| 122 | $ret = ['basic'=>SASO_EVENTTICKETS_PLUGIN_VERSION, 'premium'=>'', 'debug'=>'']; |
| 123 | if (defined('SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION')) { |
| 124 | $ret['premium'] = SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION; |
| 125 | } |
| 126 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 127 | $ret['debug'] = esc_html__('is active', 'event-tickets-with-ticket-scanner'); |
| 128 | } |
| 129 | return $ret; |
| 130 | } |
| 131 | public function getDB() { |
| 132 | return SASO_EVENTTICKETS::getDB(plugin_dir_path(__FILE__), "sasoEventticketsDB", $this); |
| 133 | } |
| 134 | public function getBase() { |
| 135 | if ($this->BASE == null) { |
| 136 | if (!class_exists('sasoEventtickets_Base')) { |
| 137 | include_once plugin_dir_path(__FILE__)."sasoEventtickets_Base.php"; |
| 138 | } |
| 139 | $this->BASE = new sasoEventtickets_Base($this); |
| 140 | } |
| 141 | return $this->BASE; |
| 142 | } |
| 143 | public function getCore() { |
| 144 | if ($this->CORE == null) { |
| 145 | if (!class_exists('sasoEventtickets_Core')) { |
| 146 | include_once plugin_dir_path(__FILE__)."sasoEventtickets_Core.php"; |
| 147 | } |
| 148 | $this->CORE = new sasoEventtickets_Core($this); |
| 149 | } |
| 150 | return $this->CORE; |
| 151 | } |
| 152 | public function getAdmin() { |
| 153 | if ($this->ADMIN == null) { |
| 154 | if (!class_exists('sasoEventtickets_AdminSettings')) { |
| 155 | include_once plugin_dir_path(__FILE__)."sasoEventtickets_AdminSettings.php"; |
| 156 | } |
| 157 | $this->ADMIN = new sasoEventtickets_AdminSettings($this); |
| 158 | } |
| 159 | return $this->ADMIN; |
| 160 | } |
| 161 | public function getFrontend() { |
| 162 | if ($this->FRONTEND == null) { |
| 163 | if (!class_exists('sasoEventtickets_Frontend')) { |
| 164 | include_once plugin_dir_path(__FILE__)."sasoEventtickets_Frontend.php"; |
| 165 | } |
| 166 | $this->FRONTEND = new sasoEventtickets_Frontend($this); |
| 167 | } |
| 168 | return $this->FRONTEND; |
| 169 | } |
| 170 | public function getOptions() { |
| 171 | if ($this->OPTIONS == null) { |
| 172 | if (!class_exists('sasoEventtickets_Options')) { |
| 173 | include_once plugin_dir_path(__FILE__)."sasoEventtickets_Options.php"; |
| 174 | } |
| 175 | $this->OPTIONS = new sasoEventtickets_Options($this, $this->_prefix); |
| 176 | $this->OPTIONS->initOptions(); |
| 177 | } |
| 178 | return $this->OPTIONS; |
| 179 | } |
| 180 | public function getNewPDFObject() { |
| 181 | if (!class_exists('sasoEventtickets_PDF')) { |
| 182 | require_once("sasoEventtickets_PDF.php"); |
| 183 | } |
| 184 | $pdf = new sasoEventtickets_PDF(); |
| 185 | $pdf->setFontSize($this->getOptions()->getOptionValue('wcTicketPDFFontSize')); |
| 186 | $pdf->setFontFamily($this->getOptions()->getOptionValue('wcTicketPDFFontFamily')); |
| 187 | $pdf = apply_filters( $this->_add_filter_prefix.'main_getNewPDFObject', $pdf ); |
| 188 | return $pdf; |
| 189 | } |
| 190 | public function loadOnce($className, $filename="") { |
| 191 | if (!class_exists($className)) { |
| 192 | if ($filename == "") $filename = $className; |
| 193 | include_once __DIR__.'/'.$filename.'.php'; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * Load class from /includes/ folder structure |
| 199 | * |
| 200 | * @param string $className The class name to load |
| 201 | * @param string $relativePath Path relative to plugin root (e.g., 'includes/woocommerce/class-base.php') |
| 202 | * @return void |
| 203 | */ |
| 204 | private function loadClass(string $className, string $relativePath): void { |
| 205 | if (class_exists($className)) { |
| 206 | return; // Already loaded |
| 207 | } |
| 208 | |
| 209 | $path = __DIR__ . '/' . $relativePath; |
| 210 | |
| 211 | if (file_exists($path)) { |
| 212 | require_once $path; |
| 213 | } |
| 214 | } |
| 215 | public function getWC() { |
| 216 | $this->loadOnce('sasoEventtickets_WC', "woocommerce-hooks"); |
| 217 | return sasoEventtickets_WC::Instance(); |
| 218 | } |
| 219 | public function getTicketHandler() { |
| 220 | $this->loadOnce('sasoEventtickets_Ticket'); |
| 221 | return sasoEventtickets_Ticket::Instance($_SERVER["REQUEST_URI"]); |
| 222 | } |
| 223 | public function getTicketDesignerHandler($template="") { |
| 224 | $this->loadOnce('sasoEventtickets_TicketDesigner'); |
| 225 | return sasoEventtickets_TicketDesigner::Instance($this, $template); |
| 226 | } |
| 227 | public function getTicketBadgeHandler() { |
| 228 | $this->loadOnce('sasoEventtickets_TicketBadge'); |
| 229 | return sasoEventtickets_TicketBadge::Instance(); |
| 230 | } |
| 231 | public function getTicketQRHandler() { |
| 232 | $this->loadOnce('sasoEventtickets_TicketQR'); |
| 233 | return sasoEventtickets_TicketQR::Instance(); |
| 234 | } |
| 235 | public function getAuthtokenHandler() { |
| 236 | $this->loadOnce('sasoEventtickets_Authtoken'); |
| 237 | return sasoEventtickets_Authtoken::Instance(); |
| 238 | } |
| 239 | public function getSeating() { |
| 240 | $this->loadOnce('sasoEventtickets_Seating'); |
| 241 | return sasoEventtickets_Seating::Instance($this); |
| 242 | } |
| 243 | |
| 244 | public function isOldPremiumDetected(): bool { |
| 245 | return $this->_oldPremiumDetected; |
| 246 | } |
| 247 | |
| 248 | public function isStarterOrStopDetected(): bool { |
| 249 | return $this->_starterOrStopDetected; |
| 250 | } |
| 251 | |
| 252 | public function getPremiumFunctions() { |
| 253 | if ($this->_isPrem == null && $this->PREMFUNCTIONS == null) { |
| 254 | $this->_isPrem = false; |
| 255 | $this->PREMFUNCTIONS = new sasoEventtickets_fakeprem(); |
| 256 | |
| 257 | // Check for Premium class - function-based compatibility check |
| 258 | if (class_exists('sasoEventtickets_PremiumFunctions')) { |
| 259 | // Check if this is Starter or Stop plugin (both are "update-only" placeholders) |
| 260 | $is_starter_or_stop = defined('SASO_EVENTTICKETS_STARTER_VERSION') || defined('SASO_EVENTTICKETS_STOP_VERSION'); |
| 261 | |
| 262 | if ($is_starter_or_stop) { |
| 263 | // Starter/Stop plugin detected - treat as "premium not active" |
| 264 | // Show message: Enter license, then update via plugin area |
| 265 | $this->_starterOrStopDetected = true; |
| 266 | $this->PREMFUNCTIONS = new sasoEventtickets_fakeprem(); |
| 267 | } elseif (!defined('SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION')) { |
| 268 | // Premium plugin exists but no version defined - likely very old or corrupted |
| 269 | $this->_oldPremiumDetected = true; |
| 270 | } else { |
| 271 | // Check version AND function compatibility |
| 272 | $min_premium_version = '1.6.0'; |
| 273 | |
| 274 | if (version_compare(SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION, $min_premium_version, '<')) { |
| 275 | // Version too old - check if required functions exist |
| 276 | $prem_class = new sasoEventtickets_PremiumFunctions($this, plugin_dir_path(__FILE__), $this->_prefix, $this->getDB()); |
| 277 | |
| 278 | // Check for critical methods that were added/changed in 1.6.0 |
| 279 | $has_required_methods = method_exists($prem_class, 'maxValues') |
| 280 | && method_exists($prem_class, 'getSeatingPlanById') |
| 281 | && method_exists($prem_class, 'generateTicketPdf'); |
| 282 | |
| 283 | if (!$has_required_methods) { |
| 284 | // Missing required functions even though version might say otherwise |
| 285 | $this->_oldPremiumDetected = true; |
| 286 | } else { |
| 287 | // Functions exist - load it |
| 288 | try { |
| 289 | $this->PREMFUNCTIONS = $prem_class; |
| 290 | $this->_isPrem = $this->PREMFUNCTIONS->isPremium(); |
| 291 | } catch (Exception $e) { |
| 292 | // Error loading premium - fall back to free |
| 293 | $this->_oldPremiumDetected = true; |
| 294 | } |
| 295 | } |
| 296 | } else { |
| 297 | // Version is OK - try to load |
| 298 | try { |
| 299 | $this->PREMFUNCTIONS = new sasoEventtickets_PremiumFunctions($this, plugin_dir_path(__FILE__), $this->_prefix, $this->getDB()); |
| 300 | $this->_isPrem = $this->PREMFUNCTIONS->isPremium(); |
| 301 | } catch (Exception $e) { |
| 302 | // Error loading premium - fall back to free |
| 303 | $this->_oldPremiumDetected = true; |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | } else { |
| 308 | // Premium class not yet available — basic plugin loaded before premium. |
| 309 | // Defer premium setup until all plugins are loaded (plugins_loaded hook). |
| 310 | $premPluginFolder = $this->getPremiumPluginFolder(); |
| 311 | if (!empty($premPluginFolder)) { |
| 312 | add_action('plugins_loaded', [$this, '_lateLoadPremium'], 1); |
| 313 | } |
| 314 | } |
| 315 | } |
| 316 | return $this->PREMFUNCTIONS; |
| 317 | } |
| 318 | /** |
| 319 | * Late-load premium plugin when basic loaded before premium (plugin loading order). |
| 320 | * Hooked to plugins_loaded with priority 1 so it runs before WooCommercePluginLoaded (priority 20). |
| 321 | */ |
| 322 | public function _lateLoadPremium(): void { |
| 323 | if (!class_exists('sasoEventtickets_PremiumFunctions')) { |
| 324 | return; // Premium plugin not installed/active |
| 325 | } |
| 326 | if ($this->PREMFUNCTIONS instanceof sasoEventtickets_PremiumFunctions) { |
| 327 | return; // Already loaded |
| 328 | } |
| 329 | |
| 330 | // Check for Starter/Stop plugin first (update-only placeholders, no premium features) |
| 331 | if (defined('SASO_EVENTTICKETS_STARTER_VERSION') || defined('SASO_EVENTTICKETS_STOP_VERSION')) { |
| 332 | $this->_starterOrStopDetected = true; |
| 333 | return; |
| 334 | } |
| 335 | |
| 336 | $min_premium_version = '1.6.0'; |
| 337 | if (!defined('SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION') |
| 338 | || version_compare(SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION, $min_premium_version, '<')) { |
| 339 | $this->_oldPremiumDetected = true; |
| 340 | return; |
| 341 | } |
| 342 | |
| 343 | try { |
| 344 | $this->PREMFUNCTIONS = new sasoEventtickets_PremiumFunctions($this, plugin_dir_path(__FILE__), $this->_prefix, $this->getDB()); |
| 345 | $this->_isPrem = $this->PREMFUNCTIONS->isPremium(); |
| 346 | |
| 347 | if (method_exists($this->PREMFUNCTIONS, 'initHandlers')) { |
| 348 | $this->PREMFUNCTIONS->initHandlers(); |
| 349 | } |
| 350 | } catch (\Throwable $e) { |
| 351 | // Premium loading failed — degrade gracefully to free mode |
| 352 | $this->PREMFUNCTIONS = new sasoEventtickets_fakeprem(); |
| 353 | $this->_isPrem = false; |
| 354 | error_log('Event Tickets: Premium late-load failed: ' . $e->getMessage()); |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | public function getPremiumPluginFolder(): string { |
| 359 | $plugins = get_option('active_plugins', []); |
| 360 | $premiumFile = ""; |
| 361 | foreach($plugins as $plugin) { |
| 362 | if (strpos(" ".$plugin, $this->_premium_plugin_name) > 0) { |
| 363 | $premiumFile = plugin_dir_path($plugin); |
| 364 | break; |
| 365 | } |
| 366 | } |
| 367 | return $premiumFile; |
| 368 | } |
| 369 | public function isPremium() { |
| 370 | if ($this->_isPrem === null) { |
| 371 | $this->getPremiumFunctions(); |
| 372 | // If PREMFUNCTIONS already existed, getPremiumFunctions() skipped re-evaluation. |
| 373 | // Re-query the premium plugin directly. |
| 374 | if ($this->_isPrem === null && $this->PREMFUNCTIONS !== null) { |
| 375 | $this->_isPrem = ($this->PREMFUNCTIONS instanceof sasoEventtickets_PremiumFunctions) |
| 376 | ? $this->PREMFUNCTIONS->isPremium() |
| 377 | : false; |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | // Eigene Verifikation: auch wenn Premium-Plugin "ja" sagt, |
| 382 | // Basic Plugin prüft den gespeicherten Lizenzstatus selbst. |
| 383 | // Rekursionsschutz: isPremium() -> getTicketHandler() -> Konstruktor -> getOptions() -> isPremium() |
| 384 | if ($this->_isPrem === true && !$this->_isCheckingSubscription) { |
| 385 | $this->_isCheckingSubscription = true; |
| 386 | try { |
| 387 | if (!$this->getTicketHandler()->isSubscriptionActive()) { |
| 388 | $this->_isPrem = false; |
| 389 | } |
| 390 | } finally { |
| 391 | $this->_isCheckingSubscription = false; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | return $this->_isPrem; |
| 396 | } |
| 397 | /** |
| 398 | * Invalidate the cached premium status so the next isPremium() call re-evaluates. |
| 399 | */ |
| 400 | public function invalidatePremiumCache(): void { |
| 401 | $this->_isPrem = null; |
| 402 | $this->PREMFUNCTIONS = null; |
| 403 | } |
| 404 | public function getPrefix() { |
| 405 | return $this->_prefix; |
| 406 | } |
| 407 | public function getMV() { |
| 408 | $v = ['storeip'=>false,'allowuserreg'=>false,'codes_total'=>0x13,'codes'=>0x12,'lists'=>5,'authtokens_total'=>3]; |
| 409 | $v["codes"] = (int) hexdec(0x80 / 0x002) / 2; |
| 410 | $v["codes_total"] = (int) hexdec(0x80 / 0x002) / 2; |
| 411 | $v["seatingplans"] = (int) (0x04 >> 0x02); |
| 412 | $v["seats_per_plan"] = (int) (0x50 >> 0x02); |
| 413 | return $v; |
| 414 | } |
| 415 | public function listener_upgrader_process_complete( $upgrader_object, $options ) { |
| 416 | $current_plugin_path_name = plugin_basename( __FILE__ ); |
| 417 | if ($options['action'] == 'update' && $options['type'] == 'plugin' ) { |
| 418 | if (isset($options['plugins'])) { |
| 419 | foreach($options['plugins'] as $each_plugin) { |
| 420 | if ($each_plugin==$current_plugin_path_name) { |
| 421 | // .......................... YOUR CODES ............. |
| 422 | } |
| 423 | } |
| 424 | } |
| 425 | } |
| 426 | do_action( $this->_do_action_prefix.'main_listener_upgrader_process_complete' ); |
| 427 | } |
| 428 | /** |
| 429 | * check for ticket detail page request |
| 430 | */ |
| 431 | public function wc_checkTicketDetailPage() { |
| 432 | if( is_404() ){ |
| 433 | include_once("SASO_EVENTTICKETS.php"); |
| 434 | // /wp-content/plugins/event-tickets-with-ticket-scanner/ticket/ |
| 435 | $p = $this->getCore()->getTicketURLPath(true); |
| 436 | $t = explode("/", $_SERVER["REQUEST_URI"]); |
| 437 | if (count($t) > 1) { |
| 438 | if ($t[count($t)-2] != "scanner") { |
| 439 | if(substr($_SERVER["REQUEST_URI"], 0, strlen($p)) == $p) { |
| 440 | $this->getTicketHandler()->initFilterAndActions(); |
| 441 | } else { |
| 442 | $wcTicketCompatibilityModeURLPath = trim($this->getOptions()->getOptionValue('wcTicketCompatibilityModeURLPath')); |
| 443 | $wcTicketCompatibilityModeURLPath = trim(trim($wcTicketCompatibilityModeURLPath, "/")); |
| 444 | if (!empty($wcTicketCompatibilityModeURLPath)) { |
| 445 | $uri = trim($_SERVER["REQUEST_URI"]); |
| 446 | if (!empty($uri)) { |
| 447 | $pos = strpos($uri, $wcTicketCompatibilityModeURLPath); |
| 448 | if ($pos > 0) { |
| 449 | $this->getTicketHandler()->initFilterAndActions(); |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | if ($t[count($t)-2] == "scanner") { |
| 457 | if(substr($_SERVER["REQUEST_URI"], 0, strlen($p)) == $p) { |
| 458 | //$this->replacingShortcodeTicketScanner(); |
| 459 | $this->getTicketHandler()->initFilterAndActionsTicketScanner(); |
| 460 | } else { |
| 461 | $wcTicketCompatibilityModeURLPath = trim($this->getOptions()->getOptionValue('wcTicketCompatibilityModeURLPath')); |
| 462 | $wcTicketCompatibilityModeURLPath = trim(trim($wcTicketCompatibilityModeURLPath, "/")); |
| 463 | if (!empty($wcTicketCompatibilityModeURLPath)) { |
| 464 | $uri = trim($_SERVER["REQUEST_URI"]); |
| 465 | if (!empty($uri)) { |
| 466 | $pos = strpos($_SERVER["REQUEST_URI"], $wcTicketCompatibilityModeURLPath."/scanner/"); |
| 467 | if ($pos > 0) { |
| 468 | $this->getTicketHandler()->initFilterAndActionsTicketScanner(); |
| 469 | } |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | } |
| 474 | } |
| 475 | } // endif 404 |
| 476 | } |
| 477 | private function init_frontend() { |
| 478 | add_shortcode($this->_shortcode, [$this, 'replacingShortcode']); |
| 479 | add_shortcode($this->_shortcode_mycode, [$this, 'replacingShortcodeMyCode']); |
| 480 | add_shortcode($this->_shortcode_ticket_scanner, [$this, 'replacingShortcodeTicketScanner']); |
| 481 | add_shortcode($this->_shortcode_eventviews, [$this, 'replacingShortcodeEventViews']); |
| 482 | add_shortcode($this->_shortcode_feature_list, [$this, 'replacingShortcodeFeatureList']); |
| 483 | add_shortcode($this->_shortcode_ticket_detail, [$this, 'replacingShortcodeTicketDetail']); |
| 484 | do_action( $this->_do_action_prefix.'main_init_frontend' ); |
| 485 | } |
| 486 | private function init_backend() { |
| 487 | add_action('admin_menu', [$this, 'register_options_page']); |
| 488 | register_activation_hook(__FILE__, [$this, 'plugin_activated']); |
| 489 | register_deactivation_hook( __FILE__, [$this, 'plugin_deactivated'] ); |
| 490 | //register_uninstall_hook( __FILE__, 'sasoEventticketsDB::plugin_uninstall' ); // MUSS NOCH GETESTE WERDEN |
| 491 | add_action( 'plugins_loaded', [$this, 'plugins_loaded'] ); |
| 492 | add_action( 'show_user_profile', [$this, 'show_user_profile'] ); |
| 493 | add_action( 'admin_init', [$this, 'handleFormatWarningDismiss'] ); |
| 494 | add_action( 'admin_notices', [$this, 'showSubscriptionWarning'] ); |
| 495 | add_action( 'admin_notices', [$this, 'showFomoBanner'] ); |
| 496 | add_action( 'admin_notices', [$this, 'showOutdatedPremiumWarning'] ); |
| 497 | add_action( 'admin_notices', [$this, 'showFormatWarning'] ); |
| 498 | add_action( 'admin_notices', [$this, 'showPhpVersionWarning'] ); |
| 499 | add_action( 'admin_notices', [$this, 'showOptionsMigrationNotice'] ); |
| 500 | add_action( 'wp_ajax_saso_et_dismiss_fomo', [$this, 'ajaxDismissFomo'] ); |
| 501 | |
| 502 | if (basename($_SERVER['SCRIPT_NAME']) == "admin-ajax.php") { |
| 503 | add_action('wp_ajax_'.$this->_prefix.'_executeAdminSettings', [$this,'executeAdminSettings_a'], 10, 0); |
| 504 | add_action('wp_ajax_'.$this->_prefix.'_executeSeatingAdmin', [$this,'executeSeatingAdmin_a'], 10, 0); |
| 505 | } |
| 506 | |
| 507 | add_action('admin_init', [$this, 'periodicLicenseCheck']); |
| 508 | |
| 509 | do_action( $this->_do_action_prefix.'main_init_backend' ); |
| 510 | } |
| 511 | |
| 512 | /** |
| 513 | * Periodic license check on admin page loads. |
| 514 | * Runs at most once per 24h to catch cases where WP Cron is disabled. |
| 515 | */ |
| 516 | public function periodicLicenseCheck(): void { |
| 517 | // Also run when premium plugin is installed with a serial key but isPremium() is false |
| 518 | // This breaks the deadlock where isPremium()=false prevents the license check from ever running |
| 519 | $hasPremiumPlugin = class_exists('sasoEventtickets_PremiumFunctions'); |
| 520 | $hasSerial = !empty(trim(get_option("saso-event-tickets-premium_serial", ""))); |
| 521 | if (!$this->isPremium() && !($hasPremiumPlugin && $hasSerial)) return; |
| 522 | |
| 523 | $info = $this->getTicketHandler()->get_expiration(); |
| 524 | $last_run = intval($info['last_run']); |
| 525 | |
| 526 | // Maximal 1x pro 24h, nicht bei jedem Page Load |
| 527 | // In recovery mode (not premium but has serial), check more frequently (every 1h) |
| 528 | $interval = $this->isPremium() ? 86400 : 3600; |
| 529 | if ($last_run > 0 && (time() - $last_run) < $interval) return; |
| 530 | |
| 531 | // Check ausführen |
| 532 | $this->getTicketHandler()->checkForPremiumSerialExpiration(); |
| 533 | |
| 534 | // isPremium Cache invalidieren damit neuer Wert gilt |
| 535 | $this->invalidatePremiumCache(); |
| 536 | } |
| 537 | public function WooCommercePluginLoaded() { |
| 538 | // DON'T load WC here - let relay functions do lazy loading |
| 539 | //$this->getWC(); // um die wc handler zu laden |
| 540 | add_action('woocommerce_review_order_after_cart_contents', [$this, 'relay_woocommerce_review_order_after_cart_contents']); |
| 541 | add_action('woocommerce_checkout_process', [$this, 'relay_woocommerce_checkout_process']); |
| 542 | add_action('woocommerce_before_cart_table', [$this, 'relay_woocommerce_before_cart_table']); |
| 543 | add_action('woocommerce_cart_updated', [$this, 'relay_woocommerce_cart_updated']); |
| 544 | add_filter('woocommerce_email_attachments', [$this, 'relay_woocommerce_email_attachments'], 10, 3); |
| 545 | add_action('woocommerce_checkout_create_order_line_item', [$this, 'relay_woocommerce_checkout_create_order_line_item'], 20, 4 ); |
| 546 | add_action('woocommerce_check_cart_items', [$this, 'relay_woocommerce_check_cart_items'] ); |
| 547 | add_action('woocommerce_new_order', [$this, 'relay_woocommerce_new_order'], 10, 1); |
| 548 | add_action('woocommerce_checkout_update_order_meta', [$this, 'relay_woocommerce_checkout_update_order_meta'], 20, 2); |
| 549 | add_action('woocommerce_order_status_changed', [$this, 'relay_woocommerce_order_status_changed'], 10, 3); |
| 550 | add_filter('woocommerce_order_item_display_meta_key', [$this, 'relay_woocommerce_order_item_display_meta_key'], 20, 3 ); |
| 551 | add_filter('woocommerce_order_item_display_meta_value', [$this, 'relay_woocommerce_order_item_display_meta_value'], 20, 3); |
| 552 | add_action('wpo_wcpdf_after_item_meta', [$this, 'relay_wpo_wcpdf_after_item_meta'], 20, 3 ); |
| 553 | add_action('woocommerce_order_item_meta_start', [$this, 'relay_woocommerce_order_item_meta_start'], 201, 4); |
| 554 | add_action('woocommerce_product_after_variable_attributes', [$this, 'relay_woocommerce_product_after_variable_attributes'], 10, 3); |
| 555 | add_action('woocommerce_save_product_variation',[$this, 'relay_woocommerce_save_product_variation'], 10 ,2 ); |
| 556 | add_action('woocommerce_email_order_meta', [$this, 'relay_woocommerce_email_order_meta'], 10, 4 ); |
| 557 | add_action('woocommerce_thankyou', [$this, 'relay_woocommerce_thankyou'], 5); |
| 558 | if (wp_doing_ajax()) { |
| 559 | // erlaube ajax nonpriv und registriere handler |
| 560 | add_action('wp_ajax_nopriv_'.$this->getPrefix().'_executeWCFrontend', [$this,'relay_executeWCFrontend']); // nicht angemeldete user, sollen eine antwort erhalten |
| 561 | add_action('wp_ajax_'.$this->getPrefix().'_executeWCFrontend', [$this,'relay_executeWCFrontend']); // nicht angemeldete user, sollen eine antwort erhalten |
| 562 | // Seating Frontend AJAX (seat selection in shop) |
| 563 | add_action('wp_ajax_nopriv_'.$this->getPrefix().'_executeSeatingFrontend', [$this,'relay_executeSeatingFrontend']); |
| 564 | add_action('wp_ajax_'.$this->getPrefix().'_executeSeatingFrontend', [$this,'relay_executeSeatingFrontend']); |
| 565 | } |
| 566 | if (is_admin()) { |
| 567 | add_action('woocommerce_delete_order', [$this, 'relay_woocommerce_delete_order'], 10, 1 ); |
| 568 | add_action('woocommerce_delete_order_item', [$this, 'relay_woocommerce_delete_order_item'], 20, 1); |
| 569 | add_action('woocommerce_pre_delete_order_refund', [$this, 'relay_woocommerce_pre_delete_order_refund'], 10, 3); |
| 570 | add_action('woocommerce_delete_order_refund', [$this, 'relay_woocommerce_delete_order_refund'], 10, 1 ); |
| 571 | add_action('woocommerce_order_partially_refunded', [$this, 'relay_woocommerce_order_partially_refunded'], 10, 2); |
| 572 | add_filter('woocommerce_product_data_tabs', [$this, 'relay_woocommerce_product_data_tabs'], 98 ); |
| 573 | add_action('woocommerce_product_data_panels', [$this, 'relay_woocommerce_product_data_panels'] ); |
| 574 | add_action('woocommerce_process_product_meta', [$this, 'relay_woocommerce_process_product_meta'], 10, 2 ); |
| 575 | add_action('add_meta_boxes', [$this, 'relay_add_meta_boxes'], 10, 2); |
| 576 | add_filter('manage_edit-product_columns', [$this, 'relay_manage_edit_product_columns']); |
| 577 | add_action('manage_product_posts_custom_column', [$this, 'relay_manage_product_posts_custom_column'], 2); |
| 578 | add_filter("manage_edit-product_sortable_columns", [$this, 'relay_manage_edit_product_sortable_columns']); |
| 579 | } else { |
| 580 | add_action('woocommerce_single_product_summary', [$this, 'relay_woocommerce_single_product_summary']); |
| 581 | } |
| 582 | |
| 583 | // set routing -- NEEDS to be replaced by add_rewrite_rule later |
| 584 | add_action( 'template_redirect', [$this, 'wc_checkTicketDetailPage'], 1 ); |
| 585 | //$this->wc_checkTicketDetailPage(); |
| 586 | add_action('rest_api_init', function () { |
| 587 | SASO_EVENTTICKETS::setRestRoutesTicket(); |
| 588 | }); |
| 589 | |
| 590 | 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 |
| 591 | add_filter('woocommerce_add_to_cart_validation', [$this, 'relay_woocommerce_add_to_cart_validation'], 10, 3); |
| 592 | add_filter('woocommerce_add_cart_item_data', [$this, 'relay_woocommerce_add_cart_item_data'], 10, 3); |
| 593 | add_action('woocommerce_add_to_cart', [$this, 'relay_woocommerce_add_to_cart'], 10, 6); |
| 594 | add_action('woocommerce_cart_item_removed', [$this, 'relay_woocommerce_cart_item_removed'], 10, 2); |
| 595 | add_action('woocommerce_after_cart_item_quantity_update', [$this, 'relay_woocommerce_after_cart_item_quantity_update'], 10, 4); |
| 596 | add_filter('woocommerce_update_cart_validation', [$this, 'relay_woocommerce_update_cart_validation'], 10, 4); |
| 597 | add_action('woocommerce_before_add_to_cart_button', [$this, 'relay_woocommerce_before_add_to_cart_button'], 15); |
| 598 | |
| 599 | do_action( $this->_do_action_prefix.'main_WooCommercePluginLoaded' ); |
| 600 | } |
| 601 | public function relay_woocommerce_after_shop_loop_item() { |
| 602 | $this->getWC()->getFrontendManager()->woocommerce_after_shop_loop_item_handler(); |
| 603 | } |
| 604 | public function relay_woocommerce_add_to_cart_validation() { |
| 605 | $args = func_get_args(); |
| 606 | return $this->getWC()->getFrontendManager()->woocommerce_add_to_cart_validation_handler(...$args); |
| 607 | } |
| 608 | public function relay_woocommerce_add_cart_item_data() { |
| 609 | $args = func_get_args(); |
| 610 | return $this->getWC()->getFrontendManager()->woocommerce_add_cart_item_data_handler(...$args); |
| 611 | } |
| 612 | public function relay_woocommerce_add_to_cart() { |
| 613 | $args = func_get_args(); |
| 614 | return $this->getWC()->getFrontendManager()->woocommerce_add_to_cart_handler(...$args); |
| 615 | } |
| 616 | public function relay_woocommerce_cart_item_removed() { |
| 617 | $args = func_get_args(); |
| 618 | $this->getWC()->getFrontendManager()->woocommerce_cart_item_removed_handler(...$args); |
| 619 | } |
| 620 | public function relay_woocommerce_after_cart_item_quantity_update() { |
| 621 | $args = func_get_args(); |
| 622 | $this->getWC()->getFrontendManager()->woocommerce_after_cart_item_quantity_update_handler(...$args); |
| 623 | } |
| 624 | public function relay_woocommerce_update_cart_validation() { |
| 625 | $args = func_get_args(); |
| 626 | return $this->getWC()->getFrontendManager()->woocommerce_update_cart_validation_handler(...$args); |
| 627 | } |
| 628 | public function relay_woocommerce_before_add_to_cart_button() { |
| 629 | $this->getWC()->getFrontendManager()->woocommerce_before_add_to_cart_button_handler(); |
| 630 | } |
| 631 | public function relay_woocommerce_review_order_after_cart_contents() { |
| 632 | $this->getWC()->getFrontendManager()->woocommerce_review_order_after_cart_contents(); |
| 633 | } |
| 634 | public function relay_woocommerce_checkout_process() { |
| 635 | $this->getWC()->getFrontendManager()->woocommerce_checkout_process(); |
| 636 | } |
| 637 | public function relay_woocommerce_before_cart_table() { |
| 638 | $this->getWC()->getFrontendManager()->woocommerce_before_cart_table(); |
| 639 | } |
| 640 | public function relay_woocommerce_cart_updated() { |
| 641 | $this->getWC()->getFrontendManager()->woocommerce_cart_updated_handler(); |
| 642 | } |
| 643 | public function relay_woocommerce_email_attachments() { |
| 644 | $args = func_get_args(); |
| 645 | return $this->getWC()->getEmailHandler()->woocommerce_email_attachments(...$args); |
| 646 | } |
| 647 | public function relay_woocommerce_checkout_create_order_line_item() { |
| 648 | $args = func_get_args(); |
| 649 | return $this->getWC()->getOrderManager()->woocommerce_checkout_create_order_line_item(...$args); |
| 650 | } |
| 651 | public function relay_woocommerce_check_cart_items() { |
| 652 | $this->getWC()->getFrontendManager()->woocommerce_check_cart_items(); |
| 653 | } |
| 654 | public function relay_woocommerce_new_order() { |
| 655 | $args = func_get_args(); |
| 656 | return $this->getWC()->getOrderManager()->woocommerce_new_order(...$args); |
| 657 | } |
| 658 | public function relay_woocommerce_checkout_update_order_meta() { |
| 659 | $args = func_get_args(); |
| 660 | return $this->getWC()->getOrderManager()->woocommerce_checkout_update_order_meta(...$args); |
| 661 | } |
| 662 | public function relay_executeWCFrontend() { |
| 663 | return $this->getWC()->getFrontendManager()->executeWCFrontend(); |
| 664 | } |
| 665 | public function relay_executeSeatingFrontend() { |
| 666 | return $this->getSeating()->getFrontendManager()->executeSeatingFrontend(); |
| 667 | } |
| 668 | public function relay_woocommerce_delete_order() { |
| 669 | $args = func_get_args(); |
| 670 | $this->getWC()->getOrderManager()->woocommerce_delete_order(...$args); |
| 671 | } |
| 672 | public function relay_woocommerce_delete_order_item() { |
| 673 | $args = func_get_args(); |
| 674 | $this->getWC()->getOrderManager()->woocommerce_delete_order_item(...$args); |
| 675 | } |
| 676 | public function relay_woocommerce_pre_delete_order_refund() { |
| 677 | $args = func_get_args(); |
| 678 | $this->getWC()->getOrderManager()->woocommerce_pre_delete_order_refund(...$args); |
| 679 | } |
| 680 | public function relay_woocommerce_delete_order_refund() { |
| 681 | $args = func_get_args(); |
| 682 | $this->getWC()->getOrderManager()->woocommerce_delete_order_refund(...$args); |
| 683 | } |
| 684 | public function relay_woocommerce_product_data_tabs() { |
| 685 | $args = func_get_args(); |
| 686 | return $this->getWC()->getProductManager()->woocommerce_product_data_tabs(...$args); |
| 687 | } |
| 688 | public function relay_woocommerce_product_data_panels() { |
| 689 | $this->getWC()->getProductManager()->woocommerce_product_data_panels(); |
| 690 | } |
| 691 | public function relay_woocommerce_process_product_meta() { |
| 692 | $args = func_get_args(); |
| 693 | $this->getWC()->getProductManager()->woocommerce_process_product_meta(...$args); |
| 694 | } |
| 695 | public function relay_add_meta_boxes(...$args) { |
| 696 | $this->getWC()->add_meta_boxes(...$args); |
| 697 | } |
| 698 | public function relay_manage_edit_product_columns() { |
| 699 | $args = func_get_args(); |
| 700 | return $this->getWC()->getProductManager()->manage_edit_product_columns(...$args); |
| 701 | } |
| 702 | public function relay_manage_product_posts_custom_column() { |
| 703 | $args = func_get_args(); |
| 704 | $this->getWC()->getProductManager()->manage_product_posts_custom_column(...$args); |
| 705 | } |
| 706 | public function relay_manage_edit_product_sortable_columns() { |
| 707 | $args = func_get_args(); |
| 708 | return $this->getWC()->getProductManager()->manage_edit_product_sortable_columns(...$args); |
| 709 | } |
| 710 | public function relay_woocommerce_single_product_summary() { |
| 711 | $this->getWC()->getFrontendManager()->woocommerce_single_product_summary(); |
| 712 | } |
| 713 | public function relay_woocommerce_order_status_changed() { |
| 714 | $args = func_get_args(); |
| 715 | $this->getWC()->getOrderManager()->woocommerce_order_status_changed(...$args); |
| 716 | } |
| 717 | public function relay_woocommerce_order_partially_refunded() { |
| 718 | $args = func_get_args(); |
| 719 | $this->getWC()->getOrderManager()->woocommerce_order_partially_refunded(...$args); |
| 720 | } |
| 721 | public function relay_woocommerce_order_item_display_meta_key() { |
| 722 | $args = func_get_args(); |
| 723 | return $this->getWC()->getOrderManager()->woocommerce_order_item_display_meta_key(...$args); |
| 724 | } |
| 725 | public function relay_woocommerce_order_item_display_meta_value() { |
| 726 | $args = func_get_args(); |
| 727 | return $this->getWC()->getOrderManager()->woocommerce_order_item_display_meta_value(...$args); |
| 728 | } |
| 729 | public function relay_wpo_wcpdf_after_item_meta() { |
| 730 | $args = func_get_args(); |
| 731 | $this->getWC()->getOrderManager()->wpo_wcpdf_after_item_meta(...$args); |
| 732 | } |
| 733 | public function relay_woocommerce_order_item_meta_start() { |
| 734 | $args = func_get_args(); |
| 735 | $this->getWC()->getOrderManager()->woocommerce_order_item_meta_start(...$args); |
| 736 | } |
| 737 | public function relay_woocommerce_product_after_variable_attributes() { |
| 738 | $args = func_get_args(); |
| 739 | $this->getWC()->getProductManager()->woocommerce_product_after_variable_attributes(...$args); |
| 740 | } |
| 741 | public function relay_woocommerce_save_product_variation() { |
| 742 | $args = func_get_args(); |
| 743 | $this->getWC()->getProductManager()->woocommerce_save_product_variation(...$args); |
| 744 | } |
| 745 | public function relay_woocommerce_email_order_meta() { |
| 746 | $args = func_get_args(); |
| 747 | $this->getWC()->getEmailHandler()->woocommerce_email_order_meta(...$args); |
| 748 | } |
| 749 | public function relay_woocommerce_thankyou() { |
| 750 | $args = func_get_args(); |
| 751 | $this->getWC()->getFrontendManager()->woocommerce_thankyou(...$args); |
| 752 | } |
| 753 | public function relay_sasoEventtickets_cronjob_daily() { |
| 754 | $this->getTicketHandler()->cronJobDaily(); |
| 755 | $this->getAdmin()->cleanupOptionsHistory(); |
| 756 | } |
| 757 | |
| 758 | public function plugin_deactivated() { |
| 759 | $this->cronjob_daily_deactivate(); |
| 760 | $this->getDB(); |
| 761 | sasoEventticketsDB::plugin_deactivated(); |
| 762 | do_action( $this->_do_action_prefix.'main_plugin_deactivated' ); |
| 763 | } |
| 764 | public function plugin_uninstall() { |
| 765 | $this->getDB(); |
| 766 | sasoEventticketsDB::plugin_uninstall(); |
| 767 | $this->getAdmin(); |
| 768 | sasoEventtickets_AdminSettings::plugin_uninstall(); |
| 769 | do_action( $this->_do_action_prefix.'main_WooCommercePluginLoaded' ); |
| 770 | } |
| 771 | public function plugin_activated($is_network_wide=false) { // und auch für updates, macht es einfacher |
| 772 | $this->getDB(); // um installiere Tabellen auszuführen |
| 773 | update_option('SASO_EVENTTICKETS_PLUGIN_VERSION', SASO_EVENTTICKETS_PLUGIN_VERSION); |
| 774 | // Only reset migration flag if options exist in wp_options (downgrade scenario). |
| 775 | // Do NOT blindly delete — the migration already removed options from wp_options, |
| 776 | // so deleting the flag would cause one request to read empty wp_options → all defaults. |
| 777 | $sentinelKey = $this->_prefix . 'qrAttachQRFilesToMailAsOnePDF'; |
| 778 | if (get_option($sentinelKey, '__NOT_SET__') !== '__NOT_SET__') { |
| 779 | delete_option('saso_eventtickets_options_migrated'); |
| 780 | } |
| 781 | $this->getAdmin()->generateFirstCodeList(); |
| 782 | $this->cronjob_daily_activate(); |
| 783 | do_action( $this->_do_action_prefix.'activated' ); |
| 784 | do_action( $this->_do_action_prefix.'main_plugin_activated' ); |
| 785 | } |
| 786 | public function plugins_loaded() { |
| 787 | 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 |
| 788 | } |
| 789 | public function initialize_plugin() { |
| 790 | $this->getDB(); // um installiere Tabellen auszuführen |
| 791 | do_action( $this->_do_action_prefix.'initialized' ); |
| 792 | do_action( $this->_do_action_prefix.'main_initialize_plugin' ); |
| 793 | } |
| 794 | function show_user_profile($profileuser) { |
| 795 | $this->getAdmin()->show_user_profile($profileuser); |
| 796 | do_action( $this->_do_action_prefix.'main_show_user_profile' ); |
| 797 | } |
| 798 | function register_options_page() { |
| 799 | $allowed = $this->isUserAllowedToAccessAdminArea(); |
| 800 | $allowed = apply_filters( $this->_add_filter_prefix.'main_options_page', $allowed ); |
| 801 | if ($allowed) { |
| 802 | add_options_page(__('Event Tickets', 'event-tickets-with-ticket-scanner'), 'Event Tickets', 'manage_options', 'event-tickets-with-ticket-scanner', [$this,'options_page']); |
| 803 | 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 ); |
| 804 | } |
| 805 | do_action( $this->_do_action_prefix.'main_register_options_page' ); |
| 806 | } |
| 807 | |
| 808 | function options_page() { |
| 809 | $allowed = $this->isUserAllowedToAccessAdminArea(); |
| 810 | $allowed = apply_filters( $this->_add_filter_prefix.'main_options_page', $allowed ); |
| 811 | if ( !$allowed ) { |
| 812 | wp_die( __( 'You do not have sufficient permissions to access this page.', 'event-tickets-with-ticket-scanner' ) ); |
| 813 | } |
| 814 | |
| 815 | wp_enqueue_style("wp-jquery-ui-dialog"); |
| 816 | |
| 817 | $js_url = "jquery.qrcode.min.js?_v=".$this->_js_version; |
| 818 | wp_register_script('ajax_script2', plugins_url( "3rd/".$js_url,__FILE__ ), array('jquery', 'jquery-ui-dialog')); |
| 819 | wp_enqueue_script('ajax_script2'); |
| 820 | |
| 821 | wp_enqueue_media(); // um die js wp.media lib zu laden |
| 822 | |
| 823 | // einbinden das js starter skript |
| 824 | $js_url = $this->_js_file."?_v=".$this->_js_version; |
| 825 | if (defined( 'WP_DEBUG')) $js_url .= '&debug=1'; |
| 826 | wp_register_script('ajax_script_backend', plugins_url( $js_url,__FILE__ ), array('jquery', 'jquery-ui-dialog', 'wp-i18n')); |
| 827 | wp_enqueue_script('ajax_script_backend'); |
| 828 | wp_set_script_translations('ajax_script_backend', 'event-tickets-with-ticket-scanner', __DIR__.'/languages'); |
| 829 | |
| 830 | // per script eine variable einbinden, die url hat den wp-admin prefix |
| 831 | // damit im backend.js dann die richtige callback url genutzt werden kann |
| 832 | $vars = array( |
| 833 | '_plugin_home_url' =>plugins_url( "",__FILE__ ), |
| 834 | '_plugin_version' => $this->getPluginVersion(), |
| 835 | '_action' => $this->_prefix.'_executeAdminSettings', |
| 836 | '_max'=>$this->getBase()->getMaxValues(), |
| 837 | '_isPremium'=>$this->isPremium(), |
| 838 | '_isUserLoggedin'=>is_user_logged_in(), |
| 839 | '_premJS'=>$this->isPremium() && method_exists($this->getPremiumFunctions(), "getJSBackendFile") ? $this->getPremiumFunctions()->getJSBackendFile() : '', |
| 840 | 'url' => admin_url( 'admin-ajax.php' ), |
| 841 | 'ticket_url' => $this->getCore()->getTicketURLPath(), |
| 842 | 'nonce' => wp_create_nonce( $this->_js_nonce ), |
| 843 | 'ajaxActionPrefix' => $this->_prefix, |
| 844 | 'divPrefix' => $this->_prefix, |
| 845 | 'divId' => $this->_divId, |
| 846 | 'jsFiles' => plugins_url( 'backend.js?_v='.$this->_js_version.'&_f='.filemtime(__DIR__.'/backend.js'),__FILE__ ) |
| 847 | ); |
| 848 | $vars = apply_filters( $this->_add_filter_prefix.'main_options_page', $vars ); |
| 849 | wp_localize_script( |
| 850 | 'ajax_script_backend', |
| 851 | 'Ajax_'.$this->_prefix, // name der injected variable |
| 852 | $vars |
| 853 | ); |
| 854 | |
| 855 | do_action( $this->_do_action_prefix.'main_options_page' ); |
| 856 | |
| 857 | $versions = $this->getPluginVersions(); |
| 858 | $versions_tail = $versions['basic'].($versions['premium'] != "" ? ', Premium: '.$versions['premium'] : ''); |
| 859 | $version_tail_add = ""; |
| 860 | if ($versions['debug'] != "") $version_tail_add .= 'DEBUG: '.$versions['debug'].', LANG: '.determine_locale(); |
| 861 | ?> |
| 862 | <style>.event-tickets-with-ticket-scanner-admin-page{opacity:0;transition:opacity .3s ease}.event-tickets-with-ticket-scanner-admin-page.et-ready{opacity:1}</style> |
| 863 | <div class="event-tickets-with-ticket-scanner-admin-page"> |
| 864 | <div class="event-tickets-with-ticket-scanner-header"> |
| 865 | <div class="event-tickets-with-ticket-scanner-header-left"> |
| 866 | <img src="<?php echo plugins_url( "",__FILE__ ); ?>/img/logo_event-tickets-with-ticket-scanner.gif" |
| 867 | alt="Event Tickets" |
| 868 | class="event-tickets-with-ticket-scanner-header-logo"> |
| 869 | |
| 870 | <div class="event-tickets-with-ticket-scanner-header-title"> |
| 871 | <div class="event-tickets-with-ticket-scanner-header-name"> |
| 872 | Event Tickets with Ticket Scanner |
| 873 | </div> |
| 874 | <div class="event-tickets-with-ticket-scanner-header-meta"> |
| 875 | <?php esc_html_e('Version', 'event-tickets-with-ticket-scanner'); ?>: <?php echo $versions_tail; ?> <?php echo $version_tail_add; ?> |
| 876 | </div> |
| 877 | </div> |
| 878 | </div> |
| 879 | |
| 880 | <div class="event-tickets-with-ticket-scanner-header-right" id="event-tickets-with-ticket-scanner-header-actions"> |
| 881 | <!-- Button kommt via JS --> |
| 882 | </div> |
| 883 | </div> |
| 884 | |
| 885 | <div style="clear:both;" data-id="plugin_addons"></div> |
| 886 | <div style="clear:both;" data-id="plugin_info_area"></div> |
| 887 | <div style="clear:both;" id="<?php echo esc_attr($this->_divId); ?>" class="et-content-area"> |
| 888 | <div class="et-loading"><span class="lds-dual-ring"></span></div> |
| 889 | </div> |
| 890 | <div class="et-footer"> |
| 891 | <a name="shortcodedetails"></a> |
| 892 | <div class="et-footer-grid"> |
| 893 | <div class="et-card et-footer-card"> |
| 894 | <div class="et-card-header"> |
| 895 | <span class="dashicons dashicons-book" style="color:#9333ea;margin-right:6px;"></span> |
| 896 | Documentation |
| 897 | </div> |
| 898 | <p><a href="https://vollstart.com/event-tickets-with-ticket-scanner/docs/" target="_blank"><?php esc_html_e('Click here, to visit the documentation of this plugin.', 'event-tickets-with-ticket-scanner'); ?></a></p> |
| 899 | <p style="margin-top:12px;"><?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> |
| 900 | </div> |
| 901 | <div class="et-card et-footer-card"> |
| 902 | <div class="et-card-header"> |
| 903 | <span class="dashicons dashicons-star-filled" style="color:#f59e0b;margin-right:6px;"></span> |
| 904 | <?php esc_html_e('Plugin Rating', 'event-tickets-with-ticket-scanner'); ?> |
| 905 | </div> |
| 906 | <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">5-Star Rating</a>.</p> |
| 907 | </div> |
| 908 | <div class="et-card et-footer-card"> |
| 909 | <div class="et-card-header"> |
| 910 | <span class="dashicons dashicons-superhero" style="color:#9333ea;margin-right:6px;"></span> |
| 911 | <?php esc_html_e('Premium Homepage', 'event-tickets-with-ticket-scanner'); ?> |
| 912 | </div> |
| 913 | <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> |
| 914 | </div> |
| 915 | </div> |
| 916 | |
| 917 | <div class="et-card et-footer-shortcodes"> |
| 918 | <div class="et-card-header"> |
| 919 | <span class="dashicons dashicons-shortcode" style="color:#9333ea;margin-right:6px;"></span> |
| 920 | <?php esc_html_e('Shortcodes', 'event-tickets-with-ticket-scanner'); ?> |
| 921 | </div> |
| 922 | |
| 923 | <h3><?php esc_html_e('Shortcode to display the event calendar form within a page', 'event-tickets-with-ticket-scanner'); ?></h3> |
| 924 | <b>[<?php echo esc_html($this->_shortcode_eventviews); ?>]</b> |
| 925 | <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> |
| 926 | <ul> |
| 927 | <li>months_to_show - Values can be a number higher than 0. Default: 3</li> |
| 928 | </ul> |
| 929 | <p>CSS file: <a href="<?php echo plugins_url( "",__FILE__ ); ?>/css/calendar.css" target="_blank">calendar.css</a></p> |
| 930 | |
| 931 | <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> |
| 932 | <b>[<?php echo esc_html($this->_shortcode_mycode); ?>]</b> |
| 933 | <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> |
| 934 | <ul> |
| 935 | <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> |
| 936 | <?php esc_html_e('Example:', 'event-tickets-with-ticket-scanner'); ?> [<?php echo esc_html($this->_shortcode_mycode); ?> order_id="123"]</li> |
| 937 | <li><b>format</b> - <?php esc_html_e('Output format. Values: json', 'event-tickets-with-ticket-scanner'); ?></li> |
| 938 | <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> |
| 939 | <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> |
| 940 | </ul> |
| 941 | <p> |
| 942 | <?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"] |
| 943 | </p> |
| 944 | |
| 945 | <h3><?php esc_html_e('Shortcode to display the ticket scanner within a page', 'event-tickets-with-ticket-scanner'); ?></h3> |
| 946 | <?php esc_html_e('Useful if you cannot open the ticket scanner due to security issues.', 'event-tickets-with-ticket-scanner'); ?><br> |
| 947 | <b>[<?php echo esc_html($this->_shortcode_ticket_scanner); ?>]</b> |
| 948 | |
| 949 | <h3><?php esc_html_e('Shortcode to display ticket detail view within a page', 'event-tickets-with-ticket-scanner'); ?></h3> |
| 950 | <?php esc_html_e('Useful if the /ticket/ URL path does not work on your server.', 'event-tickets-with-ticket-scanner'); ?><br> |
| 951 | <b>[<?php echo esc_html($this->_shortcode_ticket_detail); ?>]</b> |
| 952 | <p> |
| 953 | <?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> |
| 954 | <?php esc_html_e('Example:', 'event-tickets-with-ticket-scanner'); ?> yoursite.com/ticket-page/?ticket=ABC-123-XYZ<br> |
| 955 | <?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"] |
| 956 | </p> |
| 957 | |
| 958 | <h3><?php esc_html_e('PHP Filters', 'event-tickets-with-ticket-scanner'); ?></h3> |
| 959 | <p><?php esc_html_e('You can use PHP code to register your filter functions for the validation check.', 'event-tickets-with-ticket-scanner'); ?> |
| 960 | <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> |
| 961 | </p> |
| 962 | <ul> |
| 963 | <li>add_filter('<?php echo $this->_add_filter_prefix.'beforeCheckCodePre'; ?>', 'myfunc', 20, 1)</li> |
| 964 | <li>add_filter('<?php echo $this->_add_filter_prefix.'beforeCheckCode'; ?>', 'myfunc', 20, 1)</li> |
| 965 | <li>add_filter('<?php echo $this->_add_filter_prefix.'afterCheckCodePre'; ?>', 'myfunc', 20, 1)</li> |
| 966 | <li>add_filter('<?php echo $this->_add_filter_prefix.'afterCheckCode'; ?>', 'myfunc', 20, 1)</li> |
| 967 | </ul> |
| 968 | <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>.</p> |
| 969 | </div> |
| 970 | |
| 971 | <div class="et-footer-credits"> |
| 972 | <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> |
| 973 | </div> |
| 974 | </div> |
| 975 | </div> |
| 976 | <?php |
| 977 | do_action( $this->_do_action_prefix.'options_page' ); |
| 978 | } |
| 979 | |
| 980 | public function isUserAllowedToAccessAdminArea() { |
| 981 | if ($this->isAllowedAccess != null) return $this->isAllowedAccess; |
| 982 | if ($this->getOptions()->isOptionCheckboxActive('allowOnlySepcificRoleAccessToAdmin')) { |
| 983 | // check welche rollen |
| 984 | $user = wp_get_current_user(); |
| 985 | $user_roles = (array) $user->roles; |
| 986 | if (in_array("administrator", $user_roles)) { |
| 987 | $this->isAllowedAccess = true; |
| 988 | } else { |
| 989 | $adminAreaAllowedRoles = $this->getOptions()->getOptionValue('adminAreaAllowedRoles'); |
| 990 | if (!is_array($adminAreaAllowedRoles)) { |
| 991 | if (empty($adminAreaAllowedRoles)) { |
| 992 | $adminAreaAllowedRoles = []; |
| 993 | } else { |
| 994 | $adminAreaAllowedRoles = [$adminAreaAllowedRoles]; |
| 995 | } |
| 996 | } |
| 997 | foreach($adminAreaAllowedRoles as $role_name) { |
| 998 | if (in_array($role_name, $user_roles)) { |
| 999 | $this->isAllowedAccess = true; |
| 1000 | break; |
| 1001 | }; |
| 1002 | } |
| 1003 | } |
| 1004 | } else { |
| 1005 | // Standard: Only administrators have access |
| 1006 | $this->isAllowedAccess = current_user_can('manage_options'); |
| 1007 | } |
| 1008 | $this->isAllowedAccess = apply_filters( $this->_add_filter_prefix.'main_isUserAllowedToAccessAdminArea', $this->isAllowedAccess ); |
| 1009 | return $this->isAllowedAccess; |
| 1010 | } |
| 1011 | |
| 1012 | public function executeAdminSettings_a() { |
| 1013 | if (!SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a_sngmbh not provided"); |
| 1014 | return $this->executeAdminSettings(SASO_EVENTTICKETS::getRequestPara('a_sngmbh')); // to prevent WP adds parameters |
| 1015 | } |
| 1016 | |
| 1017 | public function executeAdminSettings($a=0, $data=null) { |
| 1018 | if (!$this->isUserAllowedToAccessAdminArea()) { |
| 1019 | return wp_send_json_error("Access denied", 403); |
| 1020 | } |
| 1021 | if ($a === 0 && !SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a not provided"); |
| 1022 | |
| 1023 | if ($data == null) { |
| 1024 | $data = SASO_EVENTTICKETS::issetRPara('data') ? SASO_EVENTTICKETS::getRequestPara('data') : []; |
| 1025 | } |
| 1026 | if ($a === 0 || empty($a) || trim($a) == "") { |
| 1027 | $a = SASO_EVENTTICKETS::getRequestPara('a_sngmbh'); |
| 1028 | } |
| 1029 | do_action( $this->_do_action_prefix.'executeAdminSettings', $a, $data ); |
| 1030 | return $this->getAdmin()->executeJSON($a, $data, false, false); // with nonce check |
| 1031 | } |
| 1032 | |
| 1033 | public function executeSeatingAdmin_a() { |
| 1034 | return $this->executeSeatingAdmin(SASO_EVENTTICKETS::getRequestPara('a')); |
| 1035 | } |
| 1036 | |
| 1037 | public function executeSeatingAdmin($a = '', $data = null) { |
| 1038 | if (!$this->isUserAllowedToAccessAdminArea()) { |
| 1039 | return wp_send_json_error('Access denied', 403); |
| 1040 | } |
| 1041 | if (empty($a) && !SASO_EVENTTICKETS::issetRPara('a')) { |
| 1042 | return wp_send_json_error('a not provided'); |
| 1043 | } |
| 1044 | if ($data === null) { |
| 1045 | $data = SASO_EVENTTICKETS::getRequest(); |
| 1046 | } |
| 1047 | if (empty($a)) { |
| 1048 | $a = SASO_EVENTTICKETS::getRequestPara('a'); |
| 1049 | } |
| 1050 | return $this->getSeating()->getAdminHandler()->executeSeatingJSON($a, $data); |
| 1051 | } |
| 1052 | |
| 1053 | public function executeFrontend_a() { |
| 1054 | return $this->executeFrontend(); // to prevent WP adds parameters |
| 1055 | } |
| 1056 | |
| 1057 | public function executeWCBackend() { |
| 1058 | if (!$this->isUserAllowedToAccessAdminArea()) { |
| 1059 | return wp_send_json_error("Access denied", 403); |
| 1060 | } |
| 1061 | if (!SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a_sngmbh not provided"); |
| 1062 | $data = SASO_EVENTTICKETS::issetRPara('data') ? SASO_EVENTTICKETS::getRequestPara('data') : []; |
| 1063 | return $this->getWC()->executeJSON(SASO_EVENTTICKETS::getRequestPara('a_sngmbh'), $data); |
| 1064 | } |
| 1065 | |
| 1066 | public function executeFrontend($a=0, $data=null) { |
| 1067 | $sasoEventtickets_Frontend = $this->getFrontend(); |
| 1068 | if ($a === 0 && !SASO_EVENTTICKETS::issetRPara('a_sngmbh')) return wp_send_json_success("a not provided"); |
| 1069 | |
| 1070 | if ($data == null) { |
| 1071 | $data = SASO_EVENTTICKETS::issetRPara('data') ? SASO_EVENTTICKETS::getRequestPara('data') : []; |
| 1072 | } |
| 1073 | if ($a === 0 || empty($a) || trim($a) == "") { |
| 1074 | $a = SASO_EVENTTICKETS::getRequestPara('a_sngmbh'); |
| 1075 | } |
| 1076 | do_action( $this->_do_action_prefix.'executeFrontend', $a, $data ); |
| 1077 | return $sasoEventtickets_Frontend->executeJSON($a, $data); |
| 1078 | } |
| 1079 | |
| 1080 | public function replacingShortcode($attr=[], $content = null, $tag = '') { |
| 1081 | add_filter( $this->_add_filter_prefix.'replaceShortcode', [$this, 'replaceShortcode'], 10, 3 ); |
| 1082 | $ret = apply_filters( $this->_add_filter_prefix.'replaceShortcode', $attr, $content, $tag ); |
| 1083 | return $ret; |
| 1084 | } |
| 1085 | |
| 1086 | public function setTicketScannerJS() { |
| 1087 | wp_enqueue_style("wp-jquery-ui-dialog"); |
| 1088 | |
| 1089 | $js_url = "jquery.qrcode.min.js?_v=".$this->getPluginVersion(); |
| 1090 | wp_enqueue_script( |
| 1091 | 'ajax_script2', |
| 1092 | plugins_url( "3rd/".$js_url,__FILE__ ), |
| 1093 | array('jquery', 'jquery-ui-dialog') |
| 1094 | ); |
| 1095 | |
| 1096 | $js_url = plugin_dir_url(__FILE__)."3rd/html5-qrcode.min.js?_v=".$this->getPluginVersion(); |
| 1097 | wp_register_script('html5-qrcode', $js_url, array('jquery', 'jquery-ui-dialog')); |
| 1098 | wp_enqueue_script('html5-qrcode'); |
| 1099 | |
| 1100 | // https://github.com/nimiq/qr-scanner - NEW scanner lib |
| 1101 | $js_url = plugin_dir_url(__FILE__)."3rd/qr-scanner-1.4.2/qr-scanner.umd.min.js?_v=".$this->getPluginVersion(); |
| 1102 | wp_register_script('qr-scanner', $js_url, array('jquery', 'jquery-ui-dialog')); |
| 1103 | wp_enqueue_script('qr-scanner'); |
| 1104 | |
| 1105 | $js_url = "ticket_scanner.js?_v=".$this->getPluginVersion(); |
| 1106 | if (defined('WP_DEBUG')) $js_url .= '&t='.time(); |
| 1107 | $js_url = plugins_url( $js_url,__FILE__ ); |
| 1108 | wp_register_script('ajax_script_ticket_scanner', $js_url, array('jquery', 'jquery-ui-dialog', 'wp-i18n')); |
| 1109 | wp_enqueue_script('ajax_script_ticket_scanner'); |
| 1110 | wp_set_script_translations('ajax_script_ticket_scanner', 'event-tickets-with-ticket-scanner', __DIR__.'/languages'); |
| 1111 | |
| 1112 | $ticketScannerDontRememberCamChoice = $this->getOptions()->isOptionCheckboxActive("ticketScannerDontRememberCamChoice") ? true : false; |
| 1113 | |
| 1114 | $pwaEnabled = $this->getOptions()->isOptionCheckboxActive('ticketScannerPWA'); |
| 1115 | $vars = [ |
| 1116 | 'root' => esc_url_raw( rest_url() ), |
| 1117 | '_plugin_home_url' =>plugins_url( "",__FILE__ ), |
| 1118 | '_action' => $this->_prefix.'_executeAdminSettings', |
| 1119 | '_isPremium'=>$this->isPremium(), |
| 1120 | '_isUserLoggedin'=>is_user_logged_in(), |
| 1121 | '_userId'=>get_current_user_id(), |
| 1122 | '_restPrefixUrl'=>SASO_EVENTTICKETS::getRESTPrefixURL(), |
| 1123 | '_siteUrl'=>get_site_url(), |
| 1124 | '_params'=>["auth"=>$this->getAuthtokenHandler()::$authtoken_param], |
| 1125 | '_pwaSWUrl'=>$pwaEnabled ? rest_url(SASO_EVENTTICKETS::getRESTPrefixURL().'/ticket/scanner/pwa-sw') : '', |
| 1126 | //'url' => admin_url( 'admin-ajax.php' ), // not used for now in ticketscanner.js |
| 1127 | 'url' => rest_get_server(), // not used for now in ticketscanner.js |
| 1128 | 'nonce' => wp_create_nonce( 'wp_rest' ), |
| 1129 | //'nonce' => wp_create_nonce( $this->_js_nonce ), |
| 1130 | 'ajaxActionPrefix' => $this->_prefix, |
| 1131 | 'wcTicketCompatibilityModeRestURL' => $this->getOptions()->getOptionValue('wcTicketCompatibilityModeRestURL', ''), |
| 1132 | 'IS_PRETTY_PERMALINK_ACTIVATED' => get_option('permalink_structure') ? true :false, |
| 1133 | 'ticketScannerDontRememberCamChoice' => $ticketScannerDontRememberCamChoice, |
| 1134 | 'ticketScannerStartCamWithoutButtonClicked' => $this->getOptions()->isOptionCheckboxActive('ticketScannerStartCamWithoutButtonClicked'), |
| 1135 | 'ticketScannerDontShowOptionControls' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDontShowOptionControls'), |
| 1136 | 'ticketScannerScanAndRedeemImmediately' => $this->getOptions()->isOptionCheckboxActive('ticketScannerScanAndRedeemImmediately'), |
| 1137 | 'ticketScannerHideTicketInformation' => $this->getOptions()->isOptionCheckboxActive('ticketScannerHideTicketInformation'), |
| 1138 | 'ticketScannerHideTicketInformationShowShortDesc' => $this->getOptions()->isOptionCheckboxActive('ticketScannerHideTicketInformationShowShortDesc'), |
| 1139 | 'ticketScannerDontShowBtnPDF' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDontShowBtnPDF'), |
| 1140 | 'ticketScannerDontShowBtnBadge' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDontShowBtnBadge'), |
| 1141 | 'ticketScannerDisplayTimes' => $this->getOptions()->isOptionCheckboxActive('ticketScannerDisplayTimes'), |
| 1142 | 'ticketScannerThemeColor' => $this->getOptions()->getOptionValue('ticketScannerThemeColor', '#2e74b5'), |
| 1143 | 'ticketScannerVibrate' => $this->getOptions()->isOptionCheckboxActive('ticketScannerVibrate') |
| 1144 | ]; |
| 1145 | $vars = apply_filters( $this->_add_filter_prefix.'main_setTicketScannerJS', $vars ); |
| 1146 | wp_localize_script( |
| 1147 | 'ajax_script_ticket_scanner', |
| 1148 | 'Ajax_'.$this->_prefix, // name der injected variable |
| 1149 | $vars |
| 1150 | ); |
| 1151 | |
| 1152 | do_action( $this->_do_action_prefix.'main_setTicketScannerJS', $js_url ); |
| 1153 | } |
| 1154 | |
| 1155 | public function replacingShortcodeTicketScanner($attr=[], $content = null, $tag = '') { |
| 1156 | $this->setTicketScannerJS(); |
| 1157 | return ' |
| 1158 | <center> |
| 1159 | <div style="width:90%;max-width:1024px;">'.$this->getTicketHandler()->getTicketScannerHTMLBoilerplate().' |
| 1160 | </div> |
| 1161 | </center> |
| 1162 | '; |
| 1163 | } |
| 1164 | |
| 1165 | /** |
| 1166 | * Shortcode to display ticket detail view on any page |
| 1167 | * Usage: [sasoEventTicketsValidator_ticket_detail] with ?ticket=CODE in URL |
| 1168 | * Or: [sasoEventTicketsValidator_ticket_detail code="TICKET-CODE"] |
| 1169 | */ |
| 1170 | public function replacingShortcodeTicketDetail($attr = [], $content = null, $tag = ''): string { |
| 1171 | $code = ''; |
| 1172 | if (!empty($attr['code'])) { |
| 1173 | $code = sanitize_text_field($attr['code']); |
| 1174 | } elseif (isset($_GET['ticket'])) { |
| 1175 | $code = sanitize_text_field($_GET['ticket']); |
| 1176 | } |
| 1177 | |
| 1178 | if (empty($code)) { |
| 1179 | return '<p>' . esc_html__('No ticket code provided. Use ?ticket=YOUR-CODE in the URL.', 'event-tickets-with-ticket-scanner') . '</p>'; |
| 1180 | } |
| 1181 | |
| 1182 | // Build a fake request URI for the ticket |
| 1183 | $ticketPath = $this->getCore()->getTicketURLPath(true); |
| 1184 | $fakeUri = $ticketPath . $code; |
| 1185 | |
| 1186 | include_once plugin_dir_path(__FILE__) . "sasoEventtickets_Ticket.php"; |
| 1187 | $ticketInstance = sasoEventtickets_Ticket::Instance($fakeUri); |
| 1188 | |
| 1189 | return $ticketInstance->renderTicketDetailForShortcode(); |
| 1190 | } |
| 1191 | |
| 1192 | public function getCodesTextAsShortList($codes) { |
| 1193 | $ret = ""; |
| 1194 | if (count($codes) > 0) { |
| 1195 | $ret = '<table>'; |
| 1196 | $wcTicketUserProfileDisplayTicketDetailURL = $this->getOptions()->isOptionCheckboxActive("wcTicketUserProfileDisplayTicketDetailURL"); |
| 1197 | $wcTicketUserProfileDisplayRedeemAmount = $this->getOptions()->isOptionCheckboxActive("wcTicketUserProfileDisplayRedeemAmount"); |
| 1198 | |
| 1199 | $label_expired = $this->getOptions()->getOptionValue('wcTicketTransTicketExpired', 'EXPIRED'); |
| 1200 | $label_stolen = $this->getOptions()->getOptionValue('wcTicketTransTicketIsStolen', 'REPORTED AS STOLEN'); |
| 1201 | $label_notvalid = $this->getOptions()->getOptionValue('wcTicketTransTicketNotValid', 'DISABLED'); |
| 1202 | |
| 1203 | $myCodes = []; |
| 1204 | foreach($codes as $idx => $codeObj) { |
| 1205 | $metaObj = $this->getCore()->encodeMetaValuesAndFillObject($codeObj['meta'], $codeObj); |
| 1206 | |
| 1207 | $_c = '<tr><td style="text-align:right;">'.($idx + 1).'.</td><td>'.$codeObj['code_display'].'</td><td>'; |
| 1208 | if ($codeObj['aktiv'] == 1) { |
| 1209 | if ($this->getCore()->checkCodeExpired($codeObj)) { |
| 1210 | $_c .= $label_expired; |
| 1211 | } |
| 1212 | } else if ($codeObj['aktiv'] == 0) { |
| 1213 | $_c .= $label_notvalid; |
| 1214 | } else if ($codeObj['aktiv'] == 2) { |
| 1215 | $_c .= $label_stolen; |
| 1216 | } |
| 1217 | $_c .= '</td>'; |
| 1218 | |
| 1219 | if ($wcTicketUserProfileDisplayTicketDetailURL) { |
| 1220 | $_c .= "<td>"; |
| 1221 | $url = $this->getCore()->getTicketURL($codeObj, $metaObj); |
| 1222 | if (!empty($url)) { |
| 1223 | $_c .= '<a href="'.$url.'" target="_blank">Ticket Details</a>'; |
| 1224 | } |
| 1225 | $_c .= "</td>"; |
| 1226 | } |
| 1227 | if ($wcTicketUserProfileDisplayRedeemAmount && function_exists("wc_get_product")) { |
| 1228 | $_c .= "<td>"; |
| 1229 | $text_redeem_amount = $this->getTicketHandler()->getRedeemAmountText($codeObj, $metaObj, false); |
| 1230 | if (!empty($text_redeem_amount)) { |
| 1231 | $_c .= $text_redeem_amount; |
| 1232 | } |
| 1233 | $_c .= "<td>"; |
| 1234 | } |
| 1235 | $_c .= "</tr>"; |
| 1236 | $myCodes[] = $_c; |
| 1237 | } |
| 1238 | $ret .= implode("", $myCodes); |
| 1239 | $ret .= "</table>"; |
| 1240 | } |
| 1241 | $ret = apply_filters( $this->_add_filter_prefix.'main_getCodesTextAsShortList', $ret, $codes ); |
| 1242 | return $ret; |
| 1243 | } |
| 1244 | |
| 1245 | public function getMyCodeText($user_id, $attr=[], $content = null, $tag = '', $codes = null) { |
| 1246 | $ret = ''; |
| 1247 | // check ob eingeloggt |
| 1248 | $pre_text = $this->getOptions()->getOptionValue('userDisplayCodePrefix', ''); |
| 1249 | if (!empty($pre_text)) $pre_text .= " "; |
| 1250 | |
| 1251 | // If codes are provided (e.g., from order_id), use them; otherwise fetch by user_id |
| 1252 | if ($codes === null && $user_id > 0) { |
| 1253 | $codes = $this->getCore()->getCodesByRegUserId($user_id); |
| 1254 | } |
| 1255 | |
| 1256 | if ($codes !== null && count($codes) > 0) { |
| 1257 | $ret .= "<b>".$pre_text."</b><br>"; |
| 1258 | $ret .= $this->getCodesTextAsShortList($codes); |
| 1259 | |
| 1260 | // Download All as PDF button |
| 1261 | $show_download_btn = isset($attr['download_all_pdf']) && |
| 1262 | in_array(strtolower($attr['download_all_pdf']), ['true', '1', 'yes'], true); |
| 1263 | |
| 1264 | if ($show_download_btn && count($codes) > 0) { |
| 1265 | $max_tickets = isset($attr['download_all_pdf_max']) ? intval($attr['download_all_pdf_max']) : 100; |
| 1266 | $btn_label = isset($attr['download_all_pdf_label']) ? |
| 1267 | sanitize_text_field($attr['download_all_pdf_label']) : |
| 1268 | __('Download All Tickets as PDF', 'event-tickets-with-ticket-scanner'); |
| 1269 | |
| 1270 | $ticket_count = count($codes); |
| 1271 | if ($ticket_count > $max_tickets) { |
| 1272 | $ret .= '<p><em>' . sprintf( |
| 1273 | /* translators: %d: maximum number of tickets */ |
| 1274 | esc_html__('Too many tickets (%1$d). Maximum %2$d tickets can be downloaded at once.', 'event-tickets-with-ticket-scanner'), |
| 1275 | $ticket_count, |
| 1276 | $max_tickets |
| 1277 | ) . '</em></p>'; |
| 1278 | } else { |
| 1279 | // Generate secure download URL |
| 1280 | $nonce = wp_create_nonce('download_my_codes_pdf_' . $user_id); |
| 1281 | $download_url = admin_url('admin-ajax.php') . '?' . http_build_query([ |
| 1282 | 'action' => $this->_prefix . '_downloadMyCodesAsPDF', |
| 1283 | 'nonce' => $nonce |
| 1284 | ]); |
| 1285 | $ret .= '<p style="margin-top:10px;"><a href="' . esc_url($download_url) . '" class="button" target="_blank">' . |
| 1286 | esc_html($btn_label) . ' (' . $ticket_count . ')</a></p>'; |
| 1287 | } |
| 1288 | } |
| 1289 | } |
| 1290 | if (empty($ret) && $this->getOptions()->isOptionCheckboxActive('userDisplayCodePrefixAlways')) { |
| 1291 | $ret .= $pre_text; |
| 1292 | } |
| 1293 | $ret = apply_filters( $this->_add_filter_prefix.'main_getMyCodeText', $ret, $user_id, $attr, $content, $tag); |
| 1294 | return $ret; |
| 1295 | } |
| 1296 | |
| 1297 | /** |
| 1298 | * AJAX handler: Download all user's tickets as one PDF |
| 1299 | * Used by shortcode [sasoEventTicketsValidator_code download_all_pdf="true"] |
| 1300 | */ |
| 1301 | public function downloadMyCodesAsPDF(): void { |
| 1302 | $user_id = get_current_user_id(); |
| 1303 | |
| 1304 | // Must be logged in |
| 1305 | if ($user_id <= 0) { |
| 1306 | wp_die(esc_html__('You must be logged in to download tickets.', 'event-tickets-with-ticket-scanner'), 403); |
| 1307 | } |
| 1308 | |
| 1309 | // Verify nonce |
| 1310 | $nonce = isset($_GET['nonce']) ? sanitize_text_field($_GET['nonce']) : ''; |
| 1311 | if (!wp_verify_nonce($nonce, 'download_my_codes_pdf_' . $user_id)) { |
| 1312 | wp_die(esc_html__('Security check failed. Please refresh the page and try again.', 'event-tickets-with-ticket-scanner'), 403); |
| 1313 | } |
| 1314 | |
| 1315 | // Get user's codes |
| 1316 | $codes = $this->getCore()->getCodesByRegUserId($user_id); |
| 1317 | |
| 1318 | if (empty($codes)) { |
| 1319 | wp_die(esc_html__('No tickets found.', 'event-tickets-with-ticket-scanner'), 404); |
| 1320 | } |
| 1321 | |
| 1322 | // Limit to 100 tickets |
| 1323 | $max_tickets = 100; |
| 1324 | if (count($codes) > $max_tickets) { |
| 1325 | wp_die( |
| 1326 | sprintf( |
| 1327 | /* translators: %d: maximum number of tickets */ |
| 1328 | esc_html__('Too many tickets. Maximum %d tickets can be downloaded at once.', 'event-tickets-with-ticket-scanner'), |
| 1329 | $max_tickets |
| 1330 | ), |
| 1331 | 400 |
| 1332 | ); |
| 1333 | } |
| 1334 | |
| 1335 | // Extract code strings |
| 1336 | $code_strings = array_map(function($codeObj) { |
| 1337 | return $codeObj['code']; |
| 1338 | }, $codes); |
| 1339 | |
| 1340 | // Generate merged PDF |
| 1341 | $filename = 'my_tickets_' . wp_date('Ymd_Hi') . '.pdf'; |
| 1342 | |
| 1343 | try { |
| 1344 | $this->getTicketHandler()->generateOnePDFForCodes($code_strings, $filename, 'I'); |
| 1345 | } catch (Exception $e) { |
| 1346 | $this->getAdmin()->logErrorToDB($e, null, 'downloadMyCodesAsPDF'); |
| 1347 | wp_die(esc_html__('Error generating PDF. Please try again later.', 'event-tickets-with-ticket-scanner'), 500); |
| 1348 | } |
| 1349 | |
| 1350 | exit; |
| 1351 | } |
| 1352 | |
| 1353 | public function getMyCodeFormatted($user_id, $attr=[], $content = null, $tag = '', $codes = null) { |
| 1354 | $format = "json"; |
| 1355 | if (isset($attr["format"])) { |
| 1356 | $format = strtolower(trim(sanitize_key($attr["format"]))); |
| 1357 | } |
| 1358 | $display = ["codes"]; |
| 1359 | if (isset($attr["display"])) { |
| 1360 | |
| 1361 | $_d = trim(sanitize_text_field($attr["display"])); |
| 1362 | $_da = explode(",", $_d); |
| 1363 | if (count($_da) > 0) { |
| 1364 | $display = []; |
| 1365 | } |
| 1366 | foreach ($_da as $item) { |
| 1367 | $item = trim($item); |
| 1368 | $display[] = $item; |
| 1369 | } |
| 1370 | } |
| 1371 | |
| 1372 | $output = []; |
| 1373 | //codes,validation,user,used,confirmedCount,woocommerce,wc_rp,wc_ticket |
| 1374 | // If codes are provided (e.g., from order_id), use them; otherwise fetch by user_id |
| 1375 | if ($codes === null) { |
| 1376 | $codes = $this->getCore()->getCodesByRegUserId($user_id); |
| 1377 | } |
| 1378 | $metas = []; |
| 1379 | foreach($codes as $codeObj) { |
| 1380 | $metas[$codeObj["code"]] = $this->getCore()->encodeMetaValuesAndFillObject($codeObj['meta'], $codeObj); |
| 1381 | } |
| 1382 | foreach ($display as $item) { |
| 1383 | $output[$item] = []; |
| 1384 | if ($item == "codes") { |
| 1385 | foreach($codes as $codeObj) { |
| 1386 | if (isset($codeObj["meta"])) { |
| 1387 | unset($codeObj["meta"]); |
| 1388 | } |
| 1389 | $output[$item][] = $codeObj; |
| 1390 | } |
| 1391 | } elseif($item == "confirmedCount") { |
| 1392 | foreach($metas as $key => $meta) { |
| 1393 | $output[$item][] = array_merge(["value"=>$meta[$item]], ["code"=>$key]); |
| 1394 | } |
| 1395 | } else { |
| 1396 | foreach($metas as $key => $m) { |
| 1397 | if (is_array($m) && isset($m[$item])) { |
| 1398 | $meta = $m[$item]; |
| 1399 | if (isset($meta["stats_redeemed"])) { |
| 1400 | unset($meta["stats_redeemed"]); |
| 1401 | } |
| 1402 | if (isset($meta["set_by_admin"])) { |
| 1403 | unset($meta["set_by_admin"]); |
| 1404 | } |
| 1405 | if (isset($meta["redeemed_by_admin"])) { |
| 1406 | unset($meta["redeemed_by_admin"]); |
| 1407 | } |
| 1408 | $output[$item][] = array_merge($meta, ["code"=>$key]); |
| 1409 | } |
| 1410 | } |
| 1411 | } |
| 1412 | } |
| 1413 | |
| 1414 | switch($format) { |
| 1415 | case "json": |
| 1416 | default: |
| 1417 | $ret = $this->getCore()->json_encode_with_error_handling($output); |
| 1418 | } |
| 1419 | $ret = apply_filters( $this->_add_filter_prefix.'main_getMyCodeFormatted', $ret, $user_id, $attr, $content, $tag, $output); |
| 1420 | return $ret; |
| 1421 | } |
| 1422 | |
| 1423 | public function replacingShortcodeMyCode($attr=[], $content = null, $tag = '') { |
| 1424 | $user_id = get_current_user_id(); |
| 1425 | $codes = null; // Will be set if order_id is used |
| 1426 | |
| 1427 | // Check if order_id parameter is provided |
| 1428 | if (isset($attr['order_id']) && !empty($attr['order_id'])) { |
| 1429 | $order_id = intval($attr['order_id']); |
| 1430 | if ($order_id > 0) { |
| 1431 | // Security check: can current user access this order? |
| 1432 | if (!$this->canUserAccessOrder($order_id)) { |
| 1433 | return '<p>' . esc_html__('You do not have permission to view tickets for this order.', 'event-tickets-with-ticket-scanner') . '</p>'; |
| 1434 | } |
| 1435 | // Get codes by order_id |
| 1436 | $codes = $this->getCore()->getCodesByOrderId($order_id); |
| 1437 | } |
| 1438 | } |
| 1439 | |
| 1440 | if (count($attr) > 0 && isset($attr["format"])) { |
| 1441 | return $this->getMyCodeFormatted($user_id, $attr, $content, $tag, $codes); |
| 1442 | } else { |
| 1443 | return $this->getMyCodeText($user_id, $attr, $content, $tag, $codes); |
| 1444 | } |
| 1445 | } |
| 1446 | |
| 1447 | /** |
| 1448 | * Check if current user can access a specific order's tickets |
| 1449 | * |
| 1450 | * @param int $order_id WooCommerce order ID |
| 1451 | * @return bool True if user can access, false otherwise |
| 1452 | */ |
| 1453 | private function canUserAccessOrder(int $order_id): bool { |
| 1454 | $order = wc_get_order($order_id); |
| 1455 | if (!$order) { |
| 1456 | return false; |
| 1457 | } |
| 1458 | |
| 1459 | // 1. Admin/Shop-Manager can access all orders |
| 1460 | if (current_user_can('manage_woocommerce')) { |
| 1461 | return true; |
| 1462 | } |
| 1463 | |
| 1464 | $current_user_id = get_current_user_id(); |
| 1465 | |
| 1466 | // 2. Logged-in user owns this order |
| 1467 | if ($current_user_id > 0 && $order->get_user_id() == $current_user_id) { |
| 1468 | return true; |
| 1469 | } |
| 1470 | |
| 1471 | // 3. Valid order key in URL (WooCommerce thank-you page pattern) |
| 1472 | $order_key_from_url = isset($_GET['key']) ? sanitize_text_field($_GET['key']) : ''; |
| 1473 | if (!empty($order_key_from_url) && $order->get_order_key() === $order_key_from_url) { |
| 1474 | return true; |
| 1475 | } |
| 1476 | |
| 1477 | return false; |
| 1478 | } |
| 1479 | |
| 1480 | public function replacingShortcodeFeatureList($attr=[], $content = null, $tag = '') { |
| 1481 | //$features = $this->getAdmin()->getOptions(); |
| 1482 | //$options = $features["options"]; |
| 1483 | $options = $this->getOptions()->getOptions(); |
| 1484 | $features = []; |
| 1485 | $act_heading = ""; |
| 1486 | foreach ($options as $option) { |
| 1487 | if ($option["key"] == "serial") continue; |
| 1488 | if ($option["type"] == "heading") { |
| 1489 | $act_heading = $option["key"]; |
| 1490 | $features[$act_heading] = ["heading"=>$option, "options"=>[]]; |
| 1491 | } else { |
| 1492 | if ($act_heading != "") { |
| 1493 | $features[$act_heading]["options"][] = $option; |
| 1494 | } |
| 1495 | } |
| 1496 | } |
| 1497 | |
| 1498 | $ret = ""; |
| 1499 | uasort($features, function($a, $b) { |
| 1500 | return strnatcasecmp($a["heading"]["label"], $b["heading"]["label"]); |
| 1501 | }); |
| 1502 | foreach ($features as $key => $feature) { |
| 1503 | $ret .= '<h3>'.$feature["heading"]["label"].'</h3>'; |
| 1504 | $video = $feature["heading"]["_doc_video"] != "" ? ' <a href="'.$feature["heading"]["_doc_video"].'" target="_blank"><span class="dashicons dashicons-video-alt3"></span> Introduction video</a>' : ""; |
| 1505 | $ret .= '<p>'.trim($feature["heading"]["desc"].$video).'</p>'; |
| 1506 | if (count($feature["options"]) > 0) { |
| 1507 | $ret .= '<ul>'; |
| 1508 | foreach ($feature["options"] as $option) { |
| 1509 | $label = $option["label"]; |
| 1510 | $desc = $option["desc"]; |
| 1511 | $desc .= $option["_doc_video"] != "" ? ' <a href="'.$option["_doc_video"].'" target="_blank"><span class="dashicons dashicons-video-alt3"></span> Introduction video</a>' : ""; |
| 1512 | $desc = trim($desc); |
| 1513 | if ($desc != "") { |
| 1514 | $desc = '<p>'.$desc.'</p>'; |
| 1515 | } |
| 1516 | $label = '<span class="dashicons dashicons-yes"></span> '.$label; |
| 1517 | $ret .= '<li>'.$label.$desc.'</li>'; |
| 1518 | } |
| 1519 | $ret .= '</ul>'; |
| 1520 | } |
| 1521 | $ret .= '<hr>'; |
| 1522 | } |
| 1523 | |
| 1524 | return $ret; |
| 1525 | } |
| 1526 | |
| 1527 | public function replacingShortcodeEventViews($attr=[], $content = null, $tag = '') { |
| 1528 | // iterate over all woocommerce products and check if they are an event |
| 1529 | $view = "calendar|list"; |
| 1530 | if (isset($attr["view"])) { |
| 1531 | $view = strtolower(trim(sanitize_key($attr["view"]))); |
| 1532 | } |
| 1533 | $months_to_show = 3; |
| 1534 | if (isset($attr["months_to_show"])) { |
| 1535 | $m = intval($attr["months_to_show"]); |
| 1536 | if ($m > 0) $months_to_show = $m; |
| 1537 | } |
| 1538 | |
| 1539 | $month_start = strtotime(wp_date("Y-m-1 00:00:00")); |
| 1540 | //$month_end = strtotime(wp_date("Y-m-t 23:59:59")); |
| 1541 | $month_end = strtotime(wp_date("Y-m-1 23:59:59", strtotime("+".$months_to_show." month", $month_start))); |
| 1542 | |
| 1543 | $products_args = array( |
| 1544 | 'post_type' => 'product', |
| 1545 | 'post_status' => 'publish', |
| 1546 | 'posts_per_page' => -1, // -1 zeigt alle Produkte an |
| 1547 | 'meta_query' => array( |
| 1548 | [ |
| 1549 | 'key' => 'saso_eventtickets_is_ticket', |
| 1550 | 'value' => 'yes', |
| 1551 | 'compare' => '=' |
| 1552 | ] |
| 1553 | ) |
| 1554 | ); |
| 1555 | $posts = get_posts($products_args); // get only ticket products |
| 1556 | |
| 1557 | $list_infos = [ |
| 1558 | 'months_to_show'=>$months_to_show, |
| 1559 | 'month_start'=>$month_start, |
| 1560 | 'month_end'=>$month_end, |
| 1561 | 'view'=>$view |
| 1562 | ]; |
| 1563 | $products_to_show = []; |
| 1564 | // Ergebnisse überprüfen |
| 1565 | foreach ($posts as $post) { |
| 1566 | $product_ids = []; |
| 1567 | $product = wc_get_product( $post->ID ); |
| 1568 | |
| 1569 | // check if event is variant product |
| 1570 | $is_variable = $product->get_type() == "variable"; |
| 1571 | if ($is_variable) { |
| 1572 | // check if event dates are the same for all variants |
| 1573 | $date_is_for_all_variants = get_post_meta( $product_id, 'saso_eventtickets_is_date_for_all_variants', true ) == "yes" ? true : false; |
| 1574 | if ($date_is_for_all_variants == false) { |
| 1575 | // if not add also the variants |
| 1576 | $childs = $product->get_children(); |
| 1577 | foreach ($childs as $child_id) { |
| 1578 | if (get_post_meta($child_id, '_saso_eventtickets_is_not_ticket', true) == "yes") { |
| 1579 | continue; |
| 1580 | } |
| 1581 | $product_ids[] = $child_id; |
| 1582 | } |
| 1583 | } |
| 1584 | } else { |
| 1585 | $product_ids[] = $product->get_id(); |
| 1586 | } |
| 1587 | |
| 1588 | foreach ($product_ids as $product_id) { |
| 1589 | $product = wc_get_product( $product_id ); |
| 1590 | $dates = $this->getTicketHandler()->calcDateStringAllowedRedeemFrom($product_id); |
| 1591 | //if ($dates['ticket_end_date_timestamp'] > $month_start && $dates['ticket_start_date_timestamp'] < $month_end) { |
| 1592 | if ($dates['ticket_end_date_timestamp'] >= $month_start || ($dates['ticket_start_date_timestamp'] >= $month_start && $dates['ticket_start_date_timestamp'] <= $month_end)) { |
| 1593 | // set product to hidden |
| 1594 | $product_data = array( |
| 1595 | 'ID' => $product_id, |
| 1596 | 'dates' => $dates, |
| 1597 | 'event'=> [ |
| 1598 | 'location' => trim(get_post_meta( $product_id, 'saso_eventtickets_event_location', true )), |
| 1599 | 'location_label' => wp_kses_post(trim($this->getAdmin()->getOptionValue("wcTicketTransLocation"))) |
| 1600 | ], |
| 1601 | 'product' => [ |
| 1602 | 'title' => $product->get_name(), |
| 1603 | 'url' => get_permalink($product_id), |
| 1604 | 'price' =>$product->get_price(), |
| 1605 | 'price_html' => $product->get_price_html(), |
| 1606 | 'type' => $product->get_type(), |
| 1607 | ] |
| 1608 | ); |
| 1609 | $products_to_show[] = $product_data; |
| 1610 | } |
| 1611 | } |
| 1612 | } |
| 1613 | |
| 1614 | $divId = "sasoEventTicketsValidator_eventsview"; |
| 1615 | |
| 1616 | // add js for the events |
| 1617 | wp_enqueue_style("wp-jquery-ui-dialog"); |
| 1618 | |
| 1619 | $js_url = "ticket_events.js?_v=".$this->getPluginVersion(); |
| 1620 | if (defined('WP_DEBUG')) $js_url .= '&t='.time(); |
| 1621 | $js_url = plugins_url( $js_url,__FILE__ ); |
| 1622 | wp_register_script('ajax_script_ticket_events', $js_url, array('jquery', 'jquery-ui-dialog', 'wp-i18n')); |
| 1623 | wp_enqueue_script('ajax_script_ticket_events'); |
| 1624 | wp_set_script_translations('ajax_script_ticket_events', 'event-tickets-with-ticket-scanner', __DIR__.'/languages'); |
| 1625 | wp_enqueue_style("ticket_events_css", plugins_url( "",__FILE__ ).'/css/calendar.css', [], true); |
| 1626 | |
| 1627 | // add all events as an array for max 3 months??? or config parameter |
| 1628 | $vars = [ |
| 1629 | 'root' => esc_url_raw( rest_url() ), |
| 1630 | '_plugin_home_url' =>plugins_url( "",__FILE__ ), |
| 1631 | '_action' => $this->_prefix.'_executeFrontendEvents', |
| 1632 | '_isPremium'=>$this->isPremium(), |
| 1633 | '_isUserLoggedin'=>is_user_logged_in(), |
| 1634 | '_userId'=>get_current_user_id(), |
| 1635 | '_premJS'=>$this->isPremium() && method_exists($this->getPremiumFunctions(), "getJSFrontEventFile") ? $this->getPremiumFunctions()->getJSFrontEventFile() : '', |
| 1636 | '_siteUrl'=>get_site_url(), |
| 1637 | 'events' => $products_to_show, |
| 1638 | 'list_infos' => $list_infos, |
| 1639 | 'format_date' => $this->getOptions()->getOptionDateFormat(), |
| 1640 | 'format_time' => $this->getOptions()->getOptionTimeFormat(), |
| 1641 | 'format_datetime' => $this->getOptions()->getOptionDateTimeFormat(), |
| 1642 | 'url' => admin_url( 'admin-ajax.php' ), |
| 1643 | 'nonce' => wp_create_nonce( $this->_js_nonce ), |
| 1644 | 'ajaxActionPrefix' => $this->_prefix, |
| 1645 | 'divId' => $divId |
| 1646 | ]; |
| 1647 | $vars = apply_filters( $this->_add_filter_prefix.'main_setTicketEventJS', $vars ); |
| 1648 | wp_localize_script( |
| 1649 | 'ajax_script_ticket_events', |
| 1650 | 'Ajax_ticket_events_'.$this->_prefix, // name der injected variable |
| 1651 | $vars |
| 1652 | ); |
| 1653 | |
| 1654 | do_action( $this->_do_action_prefix.'main_setTicketEventJS', $js_url ); |
| 1655 | |
| 1656 | $ret = ''; |
| 1657 | if (!isset($attr['divid']) || trim($attr['divid']) == "") { |
| 1658 | $ret .= '<div id="'.$divId.'">'.__('...loading...', 'event-tickets-with-ticket-scanner').'</div>'; |
| 1659 | } |
| 1660 | |
| 1661 | $ret = apply_filters( $this->_add_filter_prefix.'main_replacingShortcodeEventViews', $ret ); |
| 1662 | do_action( $this->_do_action_prefix.'main_replacingShortcodeEventViews', $vars, $ret ); |
| 1663 | |
| 1664 | return $ret; |
| 1665 | } |
| 1666 | |
| 1667 | public function replaceShortcode($attr=[], $content = null, $tag = '') { |
| 1668 | // einbinden das js starter skript |
| 1669 | $js_url = $this->_js_file."?_v=".$this->_js_version; |
| 1670 | if (defined( 'WP_DEBUG')) $js_url .= '&debug=1'; |
| 1671 | $userDivId = !isset($attr['divid']) || trim($attr['divid']) == "" ? '' : trim($attr['divid']); |
| 1672 | |
| 1673 | $attr = array_change_key_case( (array) $attr, CASE_LOWER ); |
| 1674 | |
| 1675 | wp_enqueue_script( |
| 1676 | 'ajax_script_validator', |
| 1677 | plugins_url( $js_url,__FILE__ ), |
| 1678 | array('jquery', 'wp-i18n') |
| 1679 | ); |
| 1680 | wp_set_script_translations('ajax_script_validator', 'event-tickets-with-ticket-scanner', __DIR__.'/languages'); |
| 1681 | |
| 1682 | $vars = array( |
| 1683 | 'shortcode_attr'=>json_encode($attr), |
| 1684 | '_plugin_home_url' =>plugins_url( "",__FILE__ ), |
| 1685 | '_action' => $this->_prefix.'_executeFrontend', |
| 1686 | '_isPremium'=>$this->isPremium(), |
| 1687 | '_isUserLoggedin'=>is_user_logged_in(), |
| 1688 | '_premJS'=>$this->isPremium() && method_exists($this->getPremiumFunctions(), "getJSFrontFile") ? $this->getPremiumFunctions()->getJSFrontFile() : '', |
| 1689 | 'url' => admin_url( 'admin-ajax.php' ), |
| 1690 | 'nonce' => wp_create_nonce( $this->_js_nonce ), |
| 1691 | 'ajaxActionPrefix' => $this->_prefix, |
| 1692 | 'divPrefix' => $userDivId == "" ? $this->_prefix : $userDivId, |
| 1693 | 'divId' => $this->_divId, |
| 1694 | 'jsFiles' => plugins_url( 'validator.js?_v='.$this->_js_version, __FILE__ ) |
| 1695 | ); |
| 1696 | $vars['_messages'] = [ |
| 1697 | 'msgCheck0'=>$this->getOptions()->getOptionValue('textValidationMessage0'), |
| 1698 | 'msgCheck1'=>$this->getOptions()->getOptionValue('textValidationMessage1'), |
| 1699 | 'msgCheck2'=>$this->getOptions()->getOptionValue('textValidationMessage2'), |
| 1700 | 'msgCheck3'=>$this->getOptions()->getOptionValue('textValidationMessage3'), |
| 1701 | 'msgCheck4'=>$this->getOptions()->getOptionValue('textValidationMessage4'), |
| 1702 | 'msgCheck5'=>$this->getOptions()->getOptionValue('textValidationMessage5'), |
| 1703 | 'msgCheck6'=>$this->getOptions()->getOptionValue('textValidationMessage6') |
| 1704 | ]; |
| 1705 | $vars = apply_filters( $this->_add_filter_prefix.'main_replaceShortcode', $vars ); |
| 1706 | |
| 1707 | if ($this->isPremium() && method_exists($this->getPremiumFunctions(), "addJSFrontFile")) $this->getPremiumFunctions()->addJSFrontFile(); |
| 1708 | |
| 1709 | wp_localize_script( |
| 1710 | 'ajax_script_validator', |
| 1711 | 'Ajax_'.$this->_prefix, // name of the injected variable |
| 1712 | $vars |
| 1713 | ); |
| 1714 | $ret = ''; |
| 1715 | if (!isset($attr['divid']) || trim($attr['divid']) == "") { |
| 1716 | $ret .= '<div id="'.$this->_divId.'">'.__('...loading...', 'event-tickets-with-ticket-scanner').'</div>'; |
| 1717 | } |
| 1718 | |
| 1719 | $ret = apply_filters( $this->_add_filter_prefix.'main_replaceShortcode_2', $ret ); |
| 1720 | do_action( $this->_do_action_prefix.'main_replaceShortcode', $vars, $ret ); |
| 1721 | |
| 1722 | return $ret; |
| 1723 | } |
| 1724 | |
| 1725 | /** |
| 1726 | * Show admin notice when premium subscription is about to expire or has expired |
| 1727 | * |
| 1728 | * - Warning: 14 days before expiration |
| 1729 | * - Error: After expiration (during grace period or after) |
| 1730 | * |
| 1731 | * @return void |
| 1732 | */ |
| 1733 | public function showSubscriptionWarning(): void { |
| 1734 | // Only show when premium plugin is installed (not dependent on subscription status, |
| 1735 | // otherwise the expiration warning itself would never be shown) |
| 1736 | if (!class_exists('sasoEventtickets_PremiumFunctions')) { |
| 1737 | return; |
| 1738 | } |
| 1739 | |
| 1740 | // Only show in admin |
| 1741 | if (!is_admin()) { |
| 1742 | return; |
| 1743 | } |
| 1744 | |
| 1745 | // Only show to users who can manage options |
| 1746 | if (!current_user_can('manage_options')) { |
| 1747 | return; |
| 1748 | } |
| 1749 | |
| 1750 | $info = $this->getTicketHandler()->get_expiration(); |
| 1751 | |
| 1752 | // No expiration date or lifetime license - no warning needed |
| 1753 | if (empty($info['timestamp']) || $info['timestamp'] <= 0 || $info['timestamp'] == -1) { |
| 1754 | return; |
| 1755 | } |
| 1756 | |
| 1757 | // Lifetime subscription type - no warning needed |
| 1758 | if (isset($info['subscription_type']) && $info['subscription_type'] === 'lifetime') { |
| 1759 | return; |
| 1760 | } |
| 1761 | |
| 1762 | $days_left = ($info['timestamp'] - time()) / 86400; |
| 1763 | $renewal_url = 'https://vollstart.com/event-tickets-with-ticket-scanner/'; |
| 1764 | |
| 1765 | // Warning 14 days before expiration |
| 1766 | if ($days_left <= 14 && $days_left > 0) { |
| 1767 | $date = date_i18n(get_option('date_format'), $info['timestamp']); |
| 1768 | echo '<div class="notice notice-warning is-dismissible"><p>'; |
| 1769 | printf( |
| 1770 | /* translators: %1$s: expiration date, %2$s: renewal URL */ |
| 1771 | 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'), |
| 1772 | '<strong>' . esc_html($date) . '</strong>', |
| 1773 | '<a href="' . esc_url($renewal_url) . '" target="_blank">', |
| 1774 | '</a>' |
| 1775 | ); |
| 1776 | echo '</p></div>'; |
| 1777 | } |
| 1778 | |
| 1779 | // Error after expiration |
| 1780 | if ($days_left <= 0) { |
| 1781 | $grace_days = isset($info['grace_period_days']) ? intval($info['grace_period_days']) : 7; |
| 1782 | $grace_left = $grace_days + $days_left; // days_left is negative |
| 1783 | |
| 1784 | echo '<div class="notice notice-error"><p>'; |
| 1785 | if ($grace_left > 0) { |
| 1786 | printf( |
| 1787 | /* translators: %1$d: days remaining in grace period, %2$s: renewal URL */ |
| 1788 | 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'), |
| 1789 | ceil($grace_left), |
| 1790 | '<a href="' . esc_url($renewal_url) . '" target="_blank">', |
| 1791 | '</a>' |
| 1792 | ); |
| 1793 | } else { |
| 1794 | printf( |
| 1795 | /* translators: %1$s: renewal URL */ |
| 1796 | esc_html__('Your Premium subscription has expired. Premium features are now limited. %1$sReactivate now%2$s', 'event-tickets-with-ticket-scanner'), |
| 1797 | '<a href="' . esc_url($renewal_url) . '" target="_blank">', |
| 1798 | '</a>' |
| 1799 | ); |
| 1800 | } |
| 1801 | echo '</p></div>'; |
| 1802 | } |
| 1803 | } |
| 1804 | |
| 1805 | /** |
| 1806 | * FOMO banner: show missed premium features to users with expired subscriptions. |
| 1807 | * Dismissable for 30 days, then re-appears with an even longer feature list. |
| 1808 | */ |
| 1809 | public function showFomoBanner(): void { |
| 1810 | if (!current_user_can('manage_options')) return; |
| 1811 | if ($this->isPremium()) return; |
| 1812 | if (!class_exists('sasoEventtickets_PremiumFunctions')) return; |
| 1813 | if ($this->isStarterOrStopDetected()) return; |
| 1814 | if ($this->isOldPremiumDetected()) return; |
| 1815 | |
| 1816 | $info = $this->getTicketHandler()->get_expiration(); |
| 1817 | |
| 1818 | if (empty($info['expiration_date'])) return; |
| 1819 | if (isset($info['subscription_type']) && $info['subscription_type'] === 'lifetime') return; |
| 1820 | |
| 1821 | $expired_ts = strtotime($info['expiration_date']); |
| 1822 | if (!$expired_ts || $expired_ts > time()) return; |
| 1823 | |
| 1824 | if (get_transient('saso_et_fomo_dismissed')) return; |
| 1825 | |
| 1826 | $json_path = plugin_dir_path(__FILE__) . 'changelog-features.json'; |
| 1827 | if (!file_exists($json_path)) return; |
| 1828 | |
| 1829 | $changelog = json_decode(file_get_contents($json_path), true); |
| 1830 | if (!is_array($changelog)) return; |
| 1831 | |
| 1832 | $missed = []; |
| 1833 | foreach ($changelog as $release) { |
| 1834 | $release_ts = strtotime($release['date'] ?? ''); |
| 1835 | if ($release_ts && $release_ts > $expired_ts && !empty($release['features'])) { |
| 1836 | foreach ($release['features'] as $feat) { |
| 1837 | $missed[] = $feat; |
| 1838 | } |
| 1839 | } |
| 1840 | } |
| 1841 | |
| 1842 | if (empty($missed)) return; |
| 1843 | |
| 1844 | $total = count($missed); |
| 1845 | $show = array_slice($missed, 0, 5); |
| 1846 | $renewal_url = 'https://vollstart.com/event-tickets-with-ticket-scanner/'; |
| 1847 | $nonce = wp_create_nonce('saso_et_dismiss_fomo'); |
| 1848 | |
| 1849 | echo '<div class="notice notice-info is-dismissible" id="saso-et-fomo-banner" style="border-left-color:#9333ea;">'; |
| 1850 | echo '<p><strong>'; |
| 1851 | printf( |
| 1852 | /* translators: %d: number of missed features */ |
| 1853 | esc_html__("You're missing %d new premium features!", 'event-tickets-with-ticket-scanner'), |
| 1854 | $total |
| 1855 | ); |
| 1856 | echo '</strong></p>'; |
| 1857 | echo '<ul style="list-style:disc;margin-left:20px;">'; |
| 1858 | foreach ($show as $feat) { |
| 1859 | echo '<li>' . esc_html($feat) . '</li>'; |
| 1860 | } |
| 1861 | if ($total > 5) { |
| 1862 | printf('<li><em>' . esc_html__('...and %d more', 'event-tickets-with-ticket-scanner') . '</em></li>', $total - 5); |
| 1863 | } |
| 1864 | echo '</ul>'; |
| 1865 | echo '<p><a href="' . esc_url($renewal_url) . '" target="_blank" class="button button-primary" style="background:#9333ea;border-color:#7c22d0;">'; |
| 1866 | esc_html_e('Renew now', 'event-tickets-with-ticket-scanner'); |
| 1867 | echo '</a></p>'; |
| 1868 | echo '</div>'; |
| 1869 | echo '<script>jQuery(function($){$("#saso-et-fomo-banner").on("click",".notice-dismiss",function(){$.post(ajaxurl,{action:"saso_et_dismiss_fomo",_wpnonce:"' . $nonce . '"});});});</script>'; |
| 1870 | } |
| 1871 | |
| 1872 | /** |
| 1873 | * AJAX handler: dismiss FOMO banner for 30 days |
| 1874 | */ |
| 1875 | public function ajaxDismissFomo(): void { |
| 1876 | check_ajax_referer('saso_et_dismiss_fomo'); |
| 1877 | set_transient('saso_et_fomo_dismissed', 1, 30 * DAY_IN_SECONDS); |
| 1878 | wp_send_json_success(); |
| 1879 | } |
| 1880 | |
| 1881 | /** |
| 1882 | * Show admin notice when premium plugin version is outdated or incompatible |
| 1883 | * |
| 1884 | * Shows a prominent, non-dismissible error when old premium is detected. |
| 1885 | * Old premium (< 1.6.0) is NOT loaded to prevent fatal errors. |
| 1886 | * |
| 1887 | * @return void |
| 1888 | */ |
| 1889 | public function showOutdatedPremiumWarning(): void { |
| 1890 | if (!is_admin() || !current_user_can('manage_options')) { |
| 1891 | return; |
| 1892 | } |
| 1893 | |
| 1894 | $starter_or_stop = $this->isStarterOrStopDetected(); |
| 1895 | $old_premium = $this->isOldPremiumDetected(); |
| 1896 | |
| 1897 | if (!$starter_or_stop && !$old_premium) { |
| 1898 | return; |
| 1899 | } |
| 1900 | |
| 1901 | $old_version = defined('SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION') |
| 1902 | ? SASO_EVENTTICKETS_PREMIUM_PLUGIN_VERSION |
| 1903 | : __('unknown', 'event-tickets-with-ticket-scanner'); |
| 1904 | |
| 1905 | $upgrade_url = 'https://vollstart.com/event-tickets-with-ticket-scanner/'; |
| 1906 | $support_url = 'https://vollstart.com/support/'; |
| 1907 | $premium_url = 'https://vollstart.com/downloads/event-tickets-with-ticket-scanner/'; |
| 1908 | |
| 1909 | echo '<div class="notice notice-error" style="border-left-color:#dc3232;padding:15px 20px;">'; |
| 1910 | echo '<p style="font-size:15px;font-weight:bold;margin:0 0 10px 0;color:#dc2626;">'; |
| 1911 | echo '⚠ '; |
| 1912 | |
| 1913 | if ($starter_or_stop) { |
| 1914 | $is_stop = defined('SASO_EVENTTICKETS_STOP_VERSION'); |
| 1915 | if ($is_stop) { |
| 1916 | printf( |
| 1917 | esc_html__('Event Tickets: Your Premium subscription has expired. Please renew your license to continue using Premium features.', 'event-tickets-with-ticket-scanner') |
| 1918 | ); |
| 1919 | } else { |
| 1920 | printf( |
| 1921 | esc_html__('Event Tickets: The Starter plugin is installed. Please update to the latest Premium version within the Plugins page.', 'event-tickets-with-ticket-scanner') |
| 1922 | ); |
| 1923 | } |
| 1924 | } else { |
| 1925 | printf( |
| 1926 | esc_html__('Event Tickets: Your Premium plugin (v%s) is outdated and not compatible with this version.', 'event-tickets-with-ticket-scanner'), |
| 1927 | esc_html($old_version) |
| 1928 | ); |
| 1929 | } |
| 1930 | echo '</p>'; |
| 1931 | |
| 1932 | if ($starter_or_stop) { |
| 1933 | // Starter/Stop plugin - different messages |
| 1934 | $is_stop = defined('SASO_EVENTTICKETS_STOP_VERSION'); |
| 1935 | $hasSerial = !empty(trim(get_option("saso-event-tickets-premium_serial", ""))); |
| 1936 | |
| 1937 | echo '<div style="background:#f8f9fa;border-left:4px solid #2563eb;padding:12px;margin:12px 0;">'; |
| 1938 | |
| 1939 | if ($is_stop) { |
| 1940 | // Stop plugin - subscription expired, PUC can update once license is renewed |
| 1941 | echo '<p style="margin:0 0 10px 0;"><strong>' . esc_html__('Solution: Renew Your Premium License', 'event-tickets-with-ticket-scanner') . '</strong></p>'; |
| 1942 | echo '<ol style="margin:0 0 12px 20px;padding-left:20px;">'; |
| 1943 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Renew your license', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1944 | if ($hasSerial) { |
| 1945 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Update your license key in Event Tickets settings', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1946 | } else { |
| 1947 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Enter your license key in Event Tickets settings', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1948 | } |
| 1949 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Click "Check License" to verify your new subscription', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1950 | echo '<li style="margin:0;">' . esc_html__('Update the Premium plugin via Plugins > Updates', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1951 | echo '</ol>'; |
| 1952 | printf( |
| 1953 | '<p style="margin:0 0 8px 0;"><a href="%s" class="button button-primary button-hero" target="_blank">%s</a></p>', |
| 1954 | esc_url($upgrade_url . '?utm_source=plugin&utm_medium=stop-notice&utm_campaign=renew-license'), |
| 1955 | esc_html__('Renew License', 'event-tickets-with-ticket-scanner') |
| 1956 | ); |
| 1957 | echo '<p style="margin:0;font-size:13px;color:#666;">'; |
| 1958 | esc_html_e('Contact support via support@vollstart.com if you think this is not correct.', 'event-tickets-with-ticket-scanner'); |
| 1959 | echo '</p>'; |
| 1960 | } else { |
| 1961 | // Starter plugin - just update within plugins area |
| 1962 | echo '<p style="margin:0 0 10px 0;"><strong>' . esc_html__('Solution: Update to Premium via Plugins Page', 'event-tickets-with-ticket-scanner') . '</strong></p>'; |
| 1963 | echo '<p style="margin:0 0 8px 0;">'; |
| 1964 | esc_html_e('The Starter plugin needs to be updated to Premium. Please update within the Plugins page:', 'event-tickets-with-ticket-scanner'); |
| 1965 | echo '</p>'; |
| 1966 | echo '<ol style="margin:0 0 12px 20px;padding-left:20px;">'; |
| 1967 | if (!$hasSerial) { |
| 1968 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Enter your license key in Event Tickets settings', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1969 | } |
| 1970 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Go to Plugins > Add New > Upload Plugin', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1971 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Upload the Premium ZIP file', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1972 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('WordPress will replace the Starter plugin with Premium', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1973 | echo '<li style="margin:0;">' . esc_html__('Click "Replace current with uploaded" and activate', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 1974 | echo '</ol>'; |
| 1975 | } |
| 1976 | |
| 1977 | echo '</div>'; |
| 1978 | } else { |
| 1979 | // Old premium detected - update instructions |
| 1980 | echo '<div style="background:#f8f9fa;border-left:4px solid #2563eb;padding:12px;margin:12px 0;">'; |
| 1981 | echo '<p style="margin:0 0 10px 0;"><strong>' . esc_html__('Solution: Update Premium Plugin', 'event-tickets-with-ticket-scanner') . '</strong></p>'; |
| 1982 | echo '<p style="margin:0 0 8px 0;">'; |
| 1983 | esc_html_e('Your current Premium plugin is outdated and incompatible. Premium features have been temporarily disabled to prevent errors. Your tickets and data are safe.', 'event-tickets-with-ticket-scanner'); |
| 1984 | echo '</p>'; |
| 1985 | echo '<p style="margin:0 0 8px 0;">'; |
| 1986 | printf( |
| 1987 | esc_html__('Required version: 1.6.0 or higher. You have: %s', 'event-tickets-with-ticket-scanner'), |
| 1988 | '<strong>' . esc_html($old_version) . '</strong>' |
| 1989 | ); |
| 1990 | echo '</p>'; |
| 1991 | echo '<p style="margin:0 0 12px 0;">'; |
| 1992 | esc_html_e('To restore premium features:', 'event-tickets-with-ticket-scanner'); |
| 1993 | echo '</p>'; |
| 1994 | echo '<ol style="margin:0 0 12px 20px;padding-left:20px;">'; |
| 1995 | echo '<li style="margin:0 0 6px 0;">' . sprintf( |
| 1996 | /* translators: %s: URL to account */ |
| 1997 | esc_html__('Download the latest Premium from your %1$saccount%2$s', 'event-tickets-with-ticket-scanner'), |
| 1998 | '<a href="' . esc_url($upgrade_url) . '" target="_blank"><strong>', |
| 1999 | '</strong></a>' |
| 2000 | ) . '</li>'; |
| 2001 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Go to Plugins > Add New > Upload Plugin', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 2002 | echo '<li style="margin:0 0 6px 0;">' . esc_html__('Upload the new Premium ZIP file', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 2003 | echo '<li style="margin:0;">' . esc_html__('WordPress will ask to replace - confirm to update', 'event-tickets-with-ticket-scanner') . '</li>'; |
| 2004 | echo '</ol>'; |
| 2005 | echo '<p style="margin:0;">'; |
| 2006 | printf( |
| 2007 | '<a href="' . esc_url($support_url) . '" class="button" style="margin-right:8px;" target="_blank">%s</a>', |
| 2008 | esc_html__('Contact Support', 'event-tickets-with-ticket-scanner') |
| 2009 | ); |
| 2010 | echo '</p>'; |
| 2011 | } |
| 2012 | |
| 2013 | echo '</div>'; // End gray box |
| 2014 | |
| 2015 | echo '<p style="margin:12px 0 0 0;font-style:italic;color:#666;font-size:13px;">'; |
| 2016 | esc_html_e('Your tickets, orders, and data are completely safe. You can continue using the basic features while updating.', 'event-tickets-with-ticket-scanner'); |
| 2017 | echo '</p>'; |
| 2018 | |
| 2019 | $downgrade_url = 'https://plugins.trac.wordpress.org/browser/event-tickets-with-ticket-scanner/tags'; |
| 2020 | echo '<p style="margin:6px 0 0 0;font-style:italic;color:#666;font-size:13px;">'; |
| 2021 | printf( |
| 2022 | esc_html__('Alternatively, your old Premium plugin still works with basic plugin versions below 2.8.0. You can %1$sdowngrade the basic plugin here%2$s.', 'event-tickets-with-ticket-scanner'), |
| 2023 | '<a href="' . esc_url($downgrade_url) . '" target="_blank">', |
| 2024 | '</a>' |
| 2025 | ); |
| 2026 | echo '</p>'; |
| 2027 | |
| 2028 | echo '</div>'; // End notice |
| 2029 | } |
| 2030 | |
| 2031 | /** |
| 2032 | * Show admin notice when ticket format is running out of combinations |
| 2033 | * |
| 2034 | * Checks all ticket lists for format warnings and displays notice |
| 2035 | * |
| 2036 | * @return void |
| 2037 | */ |
| 2038 | public function showPhpVersionWarning(): void { |
| 2039 | if (version_compare(PHP_VERSION, '8.1.0', '>=')) { |
| 2040 | return; |
| 2041 | } |
| 2042 | printf( |
| 2043 | '<div class="notice notice-warning is-dismissible"><p><strong>Event Tickets with Ticket Scanner:</strong> %s</p></div>', |
| 2044 | sprintf( |
| 2045 | /* translators: 1: current PHP version 2: required PHP version */ |
| 2046 | esc_html__('Your server is running PHP %1$s. This plugin requires PHP %2$s or higher. Please upgrade PHP to ensure full compatibility.', 'event-tickets-with-ticket-scanner'), |
| 2047 | PHP_VERSION, |
| 2048 | '8.1' |
| 2049 | ) |
| 2050 | ); |
| 2051 | } |
| 2052 | |
| 2053 | /** |
| 2054 | * Handle format warning dismiss — runs on admin_init (before output) so wp_redirect() works. |
| 2055 | */ |
| 2056 | public function handleFormatWarningDismiss(): void { |
| 2057 | if (!isset($_GET['saso_eventtickets_clear_format_warning']) || !isset($_GET['_wpnonce'])) { |
| 2058 | return; |
| 2059 | } |
| 2060 | if (!current_user_can('manage_options')) { |
| 2061 | return; |
| 2062 | } |
| 2063 | $list_id = intval($_GET['saso_eventtickets_clear_format_warning']); |
| 2064 | $nonce = sanitize_text_field($_GET['_wpnonce']); |
| 2065 | if (wp_verify_nonce($nonce, 'clear_format_warning_' . $list_id)) { |
| 2066 | $this->getAdmin()->clearFormatWarning($list_id); |
| 2067 | wp_redirect(remove_query_arg(['saso_eventtickets_clear_format_warning', '_wpnonce'])); |
| 2068 | exit; |
| 2069 | } |
| 2070 | } |
| 2071 | |
| 2072 | /** |
| 2073 | * Show admin notice when options migration from wp_options to custom table is incomplete. |
| 2074 | * Includes a button to manually trigger the migration. |
| 2075 | */ |
| 2076 | public function showOptionsMigrationNotice(): void { |
| 2077 | if (!current_user_can('manage_options')) { |
| 2078 | return; |
| 2079 | } |
| 2080 | // Fast path: migration already done |
| 2081 | if (get_option('saso_eventtickets_options_migrated', '0') === '1') { |
| 2082 | return; |
| 2083 | } |
| 2084 | // Check if custom table exists (DB upgrade may not have run yet) |
| 2085 | global $wpdb; |
| 2086 | $table = $wpdb->prefix . 'saso_eventtickets_options'; |
| 2087 | if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== $table) { |
| 2088 | return; |
| 2089 | } |
| 2090 | // Check sentinel: is the last known option still in wp_options? |
| 2091 | $sentinelKey = $this->_prefix . 'qrAttachQRFilesToMailAsOnePDF'; |
| 2092 | if (get_option($sentinelKey, '__NOT_SET__') === '__NOT_SET__') { |
| 2093 | // Sentinel gone — migration probably completed, set flag |
| 2094 | update_option('saso_eventtickets_options_migrated', '1'); |
| 2095 | return; |
| 2096 | } |
| 2097 | // Migration incomplete — show admin notice with button |
| 2098 | $nonce = wp_create_nonce($this->_prefix); |
| 2099 | printf( |
| 2100 | '<div class="notice notice-warning"><p><strong>Event Tickets:</strong> %s <button class="button button-primary" onclick="sasoEventticketsMigrateOptions(this)" data-nonce="%s">%s</button></p></div>', |
| 2101 | esc_html__('Options migration to custom database table is incomplete.', 'event-tickets-with-ticket-scanner'), |
| 2102 | esc_attr($nonce), |
| 2103 | esc_html__('Migrate Options', 'event-tickets-with-ticket-scanner') |
| 2104 | ); |
| 2105 | } |
| 2106 | |
| 2107 | public function showFormatWarning(): void { |
| 2108 | // Only show in admin |
| 2109 | if (!is_admin()) { |
| 2110 | return; |
| 2111 | } |
| 2112 | |
| 2113 | // Only show to users who can manage options |
| 2114 | if (!current_user_can('manage_options')) { |
| 2115 | return; |
| 2116 | } |
| 2117 | |
| 2118 | try { |
| 2119 | // Get all ticket lists |
| 2120 | $lists = $this->getAdmin()->getLists([], false); |
| 2121 | |
| 2122 | foreach ($lists as $list) { |
| 2123 | $warning = $this->getAdmin()->getFormatWarning($list['id']); |
| 2124 | |
| 2125 | if ($warning) { |
| 2126 | $list_name = esc_html($warning['list_name']); |
| 2127 | $attempts = intval($warning['attempts']); |
| 2128 | |
| 2129 | if ($warning['type'] === 'critical') { |
| 2130 | // Critical - format exhausted |
| 2131 | $clear_url = wp_nonce_url( |
| 2132 | add_query_arg(['saso_eventtickets_clear_format_warning' => $list['id']]), |
| 2133 | 'clear_format_warning_' . $list['id'] |
| 2134 | ); |
| 2135 | |
| 2136 | echo '<div class="notice notice-error"><p>'; |
| 2137 | printf( |
| 2138 | /* translators: 1: list name, 2: attempts, 3: clear URL */ |
| 2139 | esc_html__('⚠️ CRITICAL: Ticket format for "%1$s" is exhausted! It took %2$d attempts to generate a code. Future ticket sales may fail. %3$sEdit list%4$s | %5$sDismiss%4$s', 'event-tickets-with-ticket-scanner'), |
| 2140 | $list_name, |
| 2141 | $attempts, |
| 2142 | '<a href="' . esc_url(admin_url('admin.php?page=event-tickets-with-ticket-scanner')) . '">', |
| 2143 | '</a>', |
| 2144 | '<a href="' . esc_url($clear_url) . '">' |
| 2145 | ); |
| 2146 | echo '</p></div>'; |
| 2147 | } else { |
| 2148 | // Warning - running out |
| 2149 | $clear_url = wp_nonce_url( |
| 2150 | add_query_arg(['saso_eventtickets_clear_format_warning' => $list['id']]), |
| 2151 | 'clear_format_warning_' . $list['id'] |
| 2152 | ); |
| 2153 | |
| 2154 | echo '<div class="notice notice-warning is-dismissible"><p>'; |
| 2155 | printf( |
| 2156 | /* translators: 1: list name, 2: attempts, 3: clear URL */ |
| 2157 | esc_html__('⚠️ WARNING: Ticket format for "%1$s" is running out of combinations. It took %2$d attempts to generate a code. Consider increasing code length. %3$sEdit list%4$s | %5$sDismiss%4$s', 'event-tickets-with-ticket-scanner'), |
| 2158 | $list_name, |
| 2159 | $attempts, |
| 2160 | '<a href="' . esc_url(admin_url('admin.php?page=event-tickets-with-ticket-scanner')) . '">', |
| 2161 | '</a>', |
| 2162 | '<a href="' . esc_url($clear_url) . '">' |
| 2163 | ); |
| 2164 | echo '</p></div>'; |
| 2165 | } |
| 2166 | |
| 2167 | // Only show one warning at a time |
| 2168 | break; |
| 2169 | } |
| 2170 | } |
| 2171 | } catch (Exception $e) { |
| 2172 | // Silently fail - don't break the admin |
| 2173 | } |
| 2174 | } |
| 2175 | } |
| 2176 | $sasoEventtickets = sasoEventtickets::Instance(); |
| 2177 | ?> |
| 2178 |