| 1 |
<?php |
| 2 |
/** |
| 3 |
* Plugin Name: Termin-Kalender |
| 4 |
* Plugin URI: https://termin-kalender.pro |
| 5 |
* Description: Termin-Kalender is your easy monthly planner. This calendar app provides a clear, monthly overview to keep you organized. |
| 6 |
* Version: 1.3.4 |
| 7 |
* Requires at least: 6.0 |
| 8 |
* Requires PHP: 8.0 |
| 9 |
* Author: beat.k |
| 10 |
* License: GPL2+ |
| 11 |
* Text Domain: termin-kalender |
| 12 |
* Domain Path: /languages/ |
| 13 |
* |
| 14 |
*/ |
| 15 |
|
| 16 |
defined('ABSPATH') || exit; |
| 17 |
|
| 18 |
//------------------------------------------------ |
| 19 |
/* |
| 20 |
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/. |
| 21 |
Limitation of Liability |
| 22 |
By using this software, you acknowledge and agree that the authors, developers, and copyright holders shall not be held liable for any damages, claims, or liabilities arising out of or in connection with the use of this software, including its integration into commercial products or applications. You assume all responsibility for ensuring compliance with applicable laws, regulations, and standards in such use cases. |
| 23 |
*/ |
| 24 |
//------------------------------------------------ |
| 25 |
class Termin_Kalender_Plugin |
| 26 |
{ |
| 27 |
// Define the plugin constants |
| 28 |
const TER_KAL_TK_VERSION = '1.3.4'; |
| 29 |
const TER_KAL_SHORTCODES = ['termin-kalender-calendar', 'my-termin-kalender', 'termin-kalender-simple-list', 'termin-kalender-pro-list', 'termin-kalender-fullpage', 'termin-kalender-pro-reservation', 'termin-kalender-pro-ical', 'termin-kalender-pro-todo', 'termin-kalender-calendar-editable', 'termin-kalender-pro-teammessage', 'ter_kal_editable_frontend']; |
| 30 |
const TER_KAL_BLOCKS = ['termin-kalender/simplelist', 'termin-kalender/kalender-block', 'termin-kalender/list-pro', 'termin-kalender/reservation-pro', 'termin-kalender/ical-block', 'termin-kalender/todo-list', 'termin-kalender/post-meta-header']; |
| 31 |
const LOCALE_MAP = [ |
| 32 |
'de' => 'de_DE', |
| 33 |
'fr' => 'fr_FR', |
| 34 |
'en' => 'en_US' |
| 35 |
]; |
| 36 |
// Define a static property for allowed admin page hook suffixes. |
| 37 |
public static $ter_kal_admin_pages_hook_suffixes = []; |
| 38 |
public static $plugin_url; |
| 39 |
public static $plugin_dir_path; |
| 40 |
public static $ajax_url; |
| 41 |
protected $manager; |
| 42 |
protected $block_list; |
| 43 |
//------------------------------------------------------------------------------------------------- |
| 44 |
public function __construct() |
| 45 |
{ |
| 46 |
self::$ajax_url = admin_url('admin-ajax.php'); |
| 47 |
self::$plugin_url = plugin_dir_url(__FILE__); |
| 48 |
self::$plugin_dir_path = plugin_dir_path(__FILE__); |
| 49 |
// actions |
| 50 |
add_action('init', [$this, 'load_textdomain']); |
| 51 |
add_action('init', [$this, 'init_termin_kalender_update']); |
| 52 |
add_action('admin_enqueue_scripts', [$this, 'ter_kal_enqueue_admin_assets']); |
| 53 |
// filters |
| 54 |
// require |
| 55 |
require_once plugin_dir_path(__FILE__) . 'includes/dashicons.php'; |
| 56 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-functions.php'; |
| 57 |
new Termin_Kalender_Functions(); |
| 58 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-event-repository.php'; |
| 59 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-rest-api.php'; |
| 60 |
new Termin_Kalender_REST_API(); |
| 61 |
require_once plugin_dir_path(__FILE__) . 'classes/class_termin_kalender_manager.php'; |
| 62 |
$this->manager = Termin_Kalender_Manager::get_instance(); |
| 63 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-admin-page.php'; |
| 64 |
new Termin_Kalender_Admin_Page(); |
| 65 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-page-overview.php'; |
| 66 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-admin-menu.php'; |
| 67 |
Termin_Kalender_Admin_Menu::init(); |
| 68 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-magic-pages.php'; |
| 69 |
new Termin_Kalender_Magic_Pages(); |
| 70 |
require_once plugin_dir_path(__FILE__) . 'includes/admin_page_initial_setup_rest_api.php'; |
| 71 |
require_once plugin_dir_path(__FILE__) . 'includes/admin_page_initial_setup.php'; // 14 |
| 72 |
//-------------------------------------------------------------------------------- |
| 73 |
require_once plugin_dir_path(__FILE__) . 'includes/admin_page_block_calendar.php'; // 16 |
| 74 |
require_once plugin_dir_path(__FILE__) . 'includes/admin_page_block_simple_list.php'; // 18 |
| 75 |
if (!defined('TERMIN_KALENDER_PRO_ON')) { |
| 76 |
require_once plugin_dir_path(__FILE__) . 'includes/admin_page_PRO_info.php'; // 90 |
| 77 |
} |
| 78 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-block-list.php'; |
| 79 |
$this->block_list = Termin_Kalender_Block_List::get_instance(); |
| 80 |
require_once plugin_dir_path(__FILE__) . 'includes/plugin_rating_request.php'; |
| 81 |
} |
| 82 |
//------------------------------------------------------------------------------------------------- |
| 83 |
//------------------------------------------------------------------------------------------------- |
| 84 |
//------------------------------------------------------------------------------------------------- |
| 85 |
//------------------------------------------------------------------------------------------------- |
| 86 |
//------------------------------------------------------------------------------------------------- |
| 87 |
//------------------------------------------------------------------------------------------------- |
| 88 |
public function load_textdomain() |
| 89 |
{ |
| 90 |
// Engage JIT JS filter |
| 91 |
add_filter('load_script_translation_file', [$this, 'map_script_translations'], 10, 3); |
| 92 |
|
| 93 |
// Engage JIT PHP filters |
| 94 |
// Covers lightning fast PHP .l10n.php loading introduced in WP 6.5 |
| 95 |
add_filter('load_translation_file', [$this, 'force_local_translation'], 10, 2); |
| 96 |
// Fallback for WP 6.0 to 6.4 (since the baseline requirement is WP 6.0) |
| 97 |
add_filter('load_textdomain_mofile', [$this, 'force_local_translation'], 10, 2); |
| 98 |
|
| 99 |
// Trigger the standard WP process internally, the filters above will intercept it natively |
| 100 |
/* NOTE FOR REPO REVIEWERS: |
| 101 |
* We intentionally use load_plugin_textdomain() to bundle and serve local translation files |
| 102 |
* (de_DE, en_US, fr_FR) directly with the plugin. This guarantees |
| 103 |
* terminology consistency without relying entirely on external GlotPress community syncs. |
| 104 |
*/ |
| 105 |
// phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound |
| 106 |
load_plugin_textdomain('termin-kalender', false, dirname(plugin_basename(__FILE__)) . '/languages/'); |
| 107 |
} |
| 108 |
//------------------------------------------------------------------------------------------------- |
| 109 |
public function force_local_translation($file, $domain) |
| 110 |
{ |
| 111 |
if ('termin-kalender' !== $domain) { |
| 112 |
return $file; |
| 113 |
} |
| 114 |
|
| 115 |
$locale = determine_locale(); |
| 116 |
$prefix = substr($locale, 0, 2); |
| 117 |
|
| 118 |
// English: skip loading entirely to use original source texts |
| 119 |
if ('en' === $prefix) { |
| 120 |
return false; |
| 121 |
} |
| 122 |
|
| 123 |
// Only evaluate local files for DE and FR, skipping disk checks for other languages |
| 124 |
if (!in_array($prefix, ['de', 'fr'], true)) { |
| 125 |
return $file; |
| 126 |
} |
| 127 |
|
| 128 |
$main_locale = self::LOCALE_MAP[$prefix] ?? $locale; |
| 129 |
$languages_dir = plugin_dir_path(__FILE__) . 'languages/'; |
| 130 |
|
| 131 |
// Check for WP 6.5+ lightning-fast PHP translation file |
| 132 |
$local_l10n = $languages_dir . 'termin-kalender-' . $main_locale . '.l10n.php'; |
| 133 |
if (is_file($local_l10n)) { |
| 134 |
return $local_l10n; // Force our local file, ignoring global wp-content/languages |
| 135 |
} |
| 136 |
|
| 137 |
// Fallback to older MO file format for WP 6.0 - 6.4 users |
| 138 |
$local_mo = $languages_dir . 'termin-kalender-' . $main_locale . '.mo'; |
| 139 |
if (is_file($local_mo)) { |
| 140 |
return $local_mo; |
| 141 |
} |
| 142 |
|
| 143 |
return $file; |
| 144 |
} |
| 145 |
//------------------------------------------------------------------------------------------------- |
| 146 |
public function map_script_translations($file, $handle, $domain) |
| 147 |
{ |
| 148 |
// Don't process empty files or different domains |
| 149 |
if ('termin-kalender' !== $domain || empty($file)) { |
| 150 |
return $file; |
| 151 |
} |
| 152 |
|
| 153 |
$locale = determine_locale(); |
| 154 |
$prefix = substr($locale, 0, 2); |
| 155 |
|
| 156 |
// English: return false to use native JS texts via wp.i18n.__ fallback without file loads |
| 157 |
if ('en' === $prefix) { |
| 158 |
return false; |
| 159 |
} |
| 160 |
|
| 161 |
// Skip optimizing if language is not supported locally |
| 162 |
if (!in_array($prefix, ['de', 'fr'], true)) { |
| 163 |
return $file; |
| 164 |
} |
| 165 |
|
| 166 |
$main_locale = self::LOCALE_MAP[$prefix] ?? $locale; |
| 167 |
|
| 168 |
// Extract filename from the generated string |
| 169 |
$filename = basename($file); |
| 170 |
|
| 171 |
if ($main_locale !== $locale) { |
| 172 |
$filename = str_replace('-' . $locale . '-', '-' . $main_locale . '-', $filename); |
| 173 |
} |
| 174 |
|
| 175 |
$local_file = plugin_dir_path(__FILE__) . 'languages/' . $filename; |
| 176 |
|
| 177 |
// Force return local JSON to completely bypass WordPress Repo fallbacks, ONLY if it exists natively |
| 178 |
if (is_file($local_file)) { |
| 179 |
return $local_file; |
| 180 |
} |
| 181 |
|
| 182 |
return $file; |
| 183 |
} |
| 184 |
//------------------------------------------------------------------------------------------------- |
| 185 |
//------------------------------------------------------------------------------------------------- |
| 186 |
//------------------------------------------------------------------------------------------------- |
| 187 |
public function ter_kal_enqueue_admin_assets($hook) |
| 188 |
{ |
| 189 |
//------------------------------------------------------------------------- |
| 190 |
if (class_exists('Termin_Kalender_PRO_Plugin')) { |
| 191 |
$pro_version = Termin_Kalender_PRO_Plugin::TER_KAL_PRO_TK_VERSION; |
| 192 |
if ($pro_version && version_compare($pro_version, '1.2.3.9', '<')) { |
| 193 |
deactivate_plugins('termin-kalender-pro/termin-kalender-pro.php', true); |
| 194 |
add_action('admin_notices', function () use ($pro_version) { |
| 195 |
echo '<div class="notice notice-warning"><p>'; |
| 196 |
echo '<strong>Termin-Kalender PRO ' . esc_html($pro_version) . ': </strong>'; |
| 197 |
echo esc_html__('You are using an outdated version. Please update both plugins.', 'termin-kalender'); |
| 198 |
echo '</p></div>'; |
| 199 |
}); |
| 200 |
} |
| 201 |
} |
| 202 |
//------------------------------------------------------------------------- |
| 203 |
//------------------------------------------------------------------------- |
| 204 |
wp_enqueue_script('termin_kalender_namespace', Termin_Kalender_Plugin::$plugin_url . 'js/termin_kalender_namespace.js', ['jquery'], Termin_Kalender_Plugin::TER_KAL_TK_VERSION, true); |
| 205 |
//------------------------------------------------------------------------- |
| 206 |
// enqueue wenn frontend seite oder post edit |
| 207 |
//$screen = get_current_screen(); |
| 208 |
// moved inside toplevel_page_termin-kalender |
| 209 |
//------------------------------------------------------------------------- |
| 210 |
$is_free_admin = in_array($hook, Termin_Kalender_Plugin::$ter_kal_admin_pages_hook_suffixes, true); |
| 211 |
//------------------------------------------------------------------------------------------------ |
| 212 |
if (!$is_free_admin) { |
| 213 |
return; |
| 214 |
} |
| 215 |
//------------------------------------------------------------------------- |
| 216 |
// brauchen wir nur für die admin seiten |
| 217 |
//------------------------------------------------------------------------- |
| 218 |
require_once plugin_dir_path(__FILE__) . 'includes/enqueue_jquery_base.php'; |
| 219 |
//------------------------------------------------------------------------- |
| 220 |
wp_enqueue_script('fullcalendar_js', self::$plugin_url . 'dist/index.global.min.js', ['jquery', 'moment'], self::TER_KAL_TK_VERSION, true); |
| 221 |
wp_enqueue_script('fullcalendar_locales', self::$plugin_url . 'dist/locales-all.global.min.js', ['fullcalendar_js'], self::TER_KAL_TK_VERSION, true); |
| 222 |
wp_enqueue_script('rrule_js', self::$plugin_url . 'dist/rrule.js', [], self::TER_KAL_TK_VERSION, true); // keep for downgrade |
| 223 |
wp_register_script('terminkalender_fullcalendar-rrule', self::$plugin_url . 'dist/@fullcalendar/rrule/index.global.min.js', ['rrule_js'], self::TER_KAL_TK_VERSION, true); // keep for downgrade |
| 224 |
wp_enqueue_script('termin_kalender_common', Termin_Kalender_Plugin::$plugin_url . 'js/common.js', ['termin_kalender_namespace', 'jquery', 'fullcalendar_js', 'fullcalendar_locales', 'rrule_js', 'terminkalender_fullcalendar-rrule'], Termin_Kalender_Plugin::TER_KAL_TK_VERSION, true); |
| 225 |
wp_enqueue_script('termin_kalender_common_admin', Termin_Kalender_Plugin::$plugin_url . 'js/common_admin.js', ['termin_kalender_namespace', 'jquery'], Termin_Kalender_Plugin::TER_KAL_TK_VERSION, true); |
| 226 |
//------------------------------------------------------------------------- |
| 227 |
wp_enqueue_style('termin_kalender', self::$plugin_url . 'css/termin_kalender.css', [], self::TER_KAL_TK_VERSION, 'all'); |
| 228 |
wp_enqueue_style('termin_kalender_admin', self::$plugin_url . 'css/termin_kalender_admin.css', [], self::TER_KAL_TK_VERSION, 'all'); |
| 229 |
//------------------------------------------------------------------------- |
| 230 |
|
| 231 |
//------------------------------------------------------------------------- |
| 232 |
if ($hook == 'toplevel_page_termin-kalender') { |
| 233 |
$terminKalenderData = $this->manager->get_termin_kalender_data(true); |
| 234 |
|
| 235 |
wp_enqueue_style('termin_kalender_ui_dialog', Termin_Kalender_Plugin::$plugin_url . 'css/termin_kalender_ui_dialog.css', ['termin_kalender'], Termin_Kalender_Plugin::TER_KAL_TK_VERSION, 'all'); |
| 236 |
wp_enqueue_style('terminkalender_calendar_css', Termin_Kalender_Plugin::$plugin_url . 'css/termin_kalender_calendar.css', ['termin_kalender'], Termin_Kalender_Plugin::TER_KAL_TK_VERSION, 'all'); |
| 237 |
wp_enqueue_style('flatpickr', self::$plugin_url . 'dist/flatpickr/flatpickr.min.css', [], self::TER_KAL_TK_VERSION, 'all'); |
| 238 |
wp_enqueue_style('flatpickr-theme', self::$plugin_url . 'dist/flatpickr/themes/light.css', ['flatpickr'], self::TER_KAL_TK_VERSION, 'all'); |
| 239 |
wp_enqueue_style('termin_kalender_flatpickr', self::$plugin_url . 'css/termin_kalender_flatpickr.css', ['flatpickr-theme'], self::TER_KAL_TK_VERSION, 'all'); |
| 240 |
wp_enqueue_script('flatpickr', self::$plugin_url . 'dist/flatpickr/flatpickr.min.js', [], '4.6.13', true); |
| 241 |
$lang = $terminKalenderData['ter_kal_lang']; |
| 242 |
if ($lang !== 'en') { |
| 243 |
$locale_file = self::$plugin_url . 'dist/flatpickr/l10n/' . $lang . '.js'; |
| 244 |
wp_enqueue_script('flatpickr-locale', $locale_file, ['termin_kalender_namespace', 'flatpickr'], '4.6.13', true); |
| 245 |
} |
| 246 |
//------------------------------------------------------------------------- |
| 247 |
wp_enqueue_script('termin_kalender_api', self::$plugin_url . 'js/termin-kalender-api.js', ['termin_kalender_namespace'], self::TER_KAL_TK_VERSION, true); |
| 248 |
wp_enqueue_script('termin_kalender_dialogs', self::$plugin_url . 'js/termin-kalender-dialogs.js', ['termin_kalender_api', 'termin_kalender_namespace', 'jquery', 'jquery-ui-dialog'], self::TER_KAL_TK_VERSION, true); |
| 249 |
wp_enqueue_script('termin_kalender_calendar', self::$plugin_url . 'js/calendar.js', ['termin_kalender_api', 'termin_kalender_dialogs', 'termin_kalender_namespace', 'fullcalendar_js', 'fullcalendar_locales', 'rrule_js', 'terminkalender_fullcalendar-rrule', 'flatpickr', 'termin_kalender_common'], self::TER_KAL_TK_VERSION, true); |
| 250 |
//error_log(print_r($terminKalenderData, true)); |
| 251 |
wp_localize_script('termin_kalender_calendar', 'terminKalenderData', $terminKalenderData); |
| 252 |
////// |
| 253 |
wp_set_script_translations('termin_kalender_calendar', 'termin-kalender', self::$plugin_dir_path . 'languages'); |
| 254 |
////// |
| 255 |
// nicht fuer frontend: |
| 256 |
wp_enqueue_script('google_calendar', self::$plugin_url . 'dist/@fullcalendar/google-calendar/index.global.min.js', ['jquery'], self::TER_KAL_TK_VERSION, true); // keep for downgrade |
| 257 |
wp_enqueue_script('termin_kalender_format_buttons', self::$plugin_url . 'js/termin_kalender_format_buttons.js', ['termin_kalender_namespace'], self::TER_KAL_TK_VERSION, true); |
| 258 |
wp_set_script_translations('termin_kalender_format_buttons', 'termin-kalender', self::$plugin_dir_path . 'languages'); |
| 259 |
} |
| 260 |
//------------------------------------------------------------------ |
| 261 |
} |
| 262 |
//------------------------------------------------------------------ |
| 263 |
|
| 264 |
public static function activate() |
| 265 |
{ |
| 266 |
|
| 267 |
$options = get_option('termin_kalender_options', array()); |
| 268 |
$options['termin_kalender_version'] = self::TER_KAL_TK_VERSION; |
| 269 |
update_option('termin_kalender_options', $options); |
| 270 |
|
| 271 |
// Attempt table creation immediately so the fallback flag is set |
| 272 |
// correctly before first_run() writes any demo data. |
| 273 |
require_once plugin_dir_path(__FILE__) . 'classes/class-termin-kalender-event-repository.php'; |
| 274 |
Termin_Kalender_Event_Repository::create_table(); |
| 275 |
|
| 276 |
require_once plugin_dir_path(__FILE__) . 'includes/first_run_cleanup_old_versions.php'; |
| 277 |
termin_kalender_cleanup_old_versions(); |
| 278 |
require_once plugin_dir_path(__FILE__) . 'includes/dashicons.php'; |
| 279 |
require_once plugin_dir_path(__FILE__) . 'includes/first_run.php'; |
| 280 |
termin_kalender_first_run(); |
| 281 |
} |
| 282 |
|
| 283 |
// to run cleanup logic also at plugin update. |
| 284 |
public static function init_termin_kalender_update() |
| 285 |
{ |
| 286 |
// ---------------------------------------------------- |
| 287 |
// Auto-Migration to DB Table without requiring reactivation |
| 288 |
// ---------------------------------------------------- |
| 289 |
if (class_exists('Termin_Kalender_Event_Repository') && get_option('termin_kalender_events') !== false) { |
| 290 |
Termin_Kalender_Event_Repository::migrate_from_option(); |
| 291 |
} |
| 292 |
|
| 293 |
$options = get_option('termin_kalender_options', array()); |
| 294 |
$stored_version = isset($options['termin_kalender_version']) ? $options['termin_kalender_version'] : '0'; |
| 295 |
// Run cleanup if version mismatch (new install or update) |
| 296 |
if (version_compare($stored_version, self::TER_KAL_TK_VERSION, '<')) { |
| 297 |
require_once plugin_dir_path(__FILE__) . 'includes/first_run_cleanup_old_versions.php'; |
| 298 |
termin_kalender_cleanup_old_versions(); |
| 299 |
// Keep rewrite flushing limited to actual upgrades. |
| 300 |
flush_rewrite_rules(); |
| 301 |
$options['termin_kalender_version'] = self::TER_KAL_TK_VERSION; |
| 302 |
update_option('termin_kalender_options', $options); |
| 303 |
} |
| 304 |
} |
| 305 |
|
| 306 |
/** |
| 307 |
* Plugin deactivation hook callback. |
| 308 |
Auto data-backup, cleanup, DB deletion? IF PRO: ter_kal_backup_selected_tables() |
| 309 |
Ask if DB entries should be deleted. |
| 310 |
Remove options |
| 311 |
*/ |
| 312 |
public static function deactivate() |
| 313 |
{ |
| 314 |
flush_rewrite_rules(); |
| 315 |
} |
| 316 |
|
| 317 |
public static function uninstall() |
| 318 |
{ |
| 319 |
// Delete here with user confirmation |
| 320 |
delete_option('termin_kalender_options'); |
| 321 |
remove_role('termin_kalender_user'); |
| 322 |
remove_role('termin_kalender_manager'); |
| 323 |
remove_role('termin_kalender_team'); |
| 324 |
$editor = get_role('editor'); // keep admin |
| 325 |
if ($editor) { |
| 326 |
foreach (['edit', 'delete'] as $action) { |
| 327 |
$editor->remove_cap("termin_kalender_{$action}"); |
| 328 |
} |
| 329 |
} |
| 330 |
} |
| 331 |
|
| 332 |
} |
| 333 |
// Register activation and deactivation hooks. |
| 334 |
register_activation_hook(__FILE__, ['Termin_Kalender_Plugin', 'activate']); |
| 335 |
register_deactivation_hook(__FILE__, ['Termin_Kalender_Plugin', 'deactivate']); |
| 336 |
register_uninstall_hook(__FILE__, ['Termin_Kalender_Plugin', 'uninstall']); |
| 337 |
// ---------------------------- |
| 338 |
function termin_kalender_load_plugin() |
| 339 |
{ |
| 340 |
new Termin_Kalender_Plugin(); |
| 341 |
if (function_exists('wp_register_data_request_type')) { |
| 342 |
wp_register_data_request_type('termin-kalender', [ |
| 343 |
'supports_consent' => true, |
| 344 |
'categories' => ['functional'], |
| 345 |
]); |
| 346 |
} |
| 347 |
} |
| 348 |
add_action('plugins_loaded', 'termin_kalender_load_plugin', 10); |
| 349 |
// ---------------------------- |
| 350 |
|