PluginProbe
Bulk Page Generator – LPagery / trunk
Bulk Page Generator – LPagery vtrunk
2.5.8 2.5.7 1.4.24 1.4.25 1.4.26 1.4.27 1.4.28 1.4.29 1.4.3 1.4.31 1.4.32 1.4.33 1.4.5 1.4.7 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.5 1.5.6 2.0.0 2.0.1 2.0.10 2.0.11 All 154 releases
lpagery / lpagery.php

lpagery.php in Bulk Page Generator – LPagery trunk, at lpagery.php

950 lines 40.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 Plugin Name: LPagery
5 Plugin URI: https://lpagery.io/
6 Description: Create hundreds or even thousands of landingpages for local businesses, services etc.
7 Version: 2.5.8
8 Author: LPagery
9 License: GPLv2 or later
10 */
11 // Create a helper function for easy SDK access.
12 use Kucrut\Vite;
13 use LPagery\data\LPageryDao;
14 use LPagery\factories\GoogleSheetSyncProcessHandlerFactory;
15 use LPagery\io\Mapper;
16 use LPagery\model\ProcessSheetSyncParams;
17 use LPagery\service\image_lookup\AttachmentBasenameService;
18 use LPagery\service\InstallationDateHandler;
19 use LPagery\service\settings\SettingsController;
20 use LPagery\service\sheet_sync\GoogleSheetQueueWorkerFactory;
21 use LPagery\service\sheet_sync\GoogleSheetSyncControllerFactory;
22 use LPagery\service\TrackingPermissionService;
23 use LPagery\utils\Utils;
24 if ( !defined( 'ABSPATH' ) ) {
25 exit;
26 }
27 if ( function_exists( 'lpagery_fs' ) ) {
28 lpagery_fs()->set_basename( false, __FILE__ );
29 } else {
30 // DO NOT REMOVE THIS IF, IT IS ESSENTIAL FOR THE `function_exists` CALL ABOVE TO PROPERLY WORK.
31 /** @phpstan-ignore booleanNot.alwaysTrue */
32 if ( !function_exists( 'lpagery_fs' ) ) {
33 function lpagery_fs() {
34 global $lpagery_fs;
35 if ( !isset( $lpagery_fs ) ) {
36 // Include Freemius SDK.
37 require_once dirname( __FILE__ ) . '/freemius/start.php';
38 $lpagery_fs = fs_dynamic_init( array(
39 'id' => '9985',
40 'slug' => 'lpagery',
41 'premium_slug' => 'lpagery-pro',
42 'type' => 'plugin',
43 'public_key' => 'pk_708ce9268236202bb1fd0aceb0be2',
44 'is_premium' => false,
45 'premium_suffix' => 'Pro',
46 'has_addons' => false,
47 'has_paid_plans' => true,
48 'has_affiliation' => 'customers',
49 'menu' => array(
50 'slug' => 'lpagery',
51 'contact' => false,
52 'support' => false,
53 ),
54 'is_live' => true,
55 'is_org_compliant' => true,
56 ) );
57 }
58 return $lpagery_fs;
59 }
60
61 // Init Freemius.
62 lpagery_fs();
63 // Signal that SDK was initiated.
64 do_action( 'lpagery_fs_loaded' );
65 }
66 require __DIR__ . "/vendor/autoload.php";
67 $plugin_data = get_file_data( __FILE__, array(
68 'Version' => 'Version',
69 ) );
70 $lpagery_version = $plugin_data['Version'];
71 define( 'LPAGERY_VERSION', $lpagery_version );
72 function lpagery_activate() {
73 LPageryDao::get_instance()->init_db();
74 if ( !get_option( "lpagery_queue_create_post_secret" ) ) {
75 add_option( "lpagery_queue_create_post_secret", Utils::generateRandomString( 32 ) );
76 }
77 }
78
79 register_activation_hook( __FILE__, 'lpagery_activate' );
80 add_action( 'admin_menu', 'lpagery_setup_menu' );
81 function lpagery_setup_menu() {
82 $icon_base64 = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjIuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MjcuMTYgNjc0LjQ1IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MjcuMTYgNjc0LjQ1OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0ZGRkZGRjt9Cgkuc3Qxe2ZpbGw6bm9uZTtzdHJva2U6I0ZGRkZGRjtzdHJva2Utd2lkdGg6MztzdHJva2UtbWl0ZXJsaW1pdDoxMDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yNTAuNDUsMzQ3LjYySDExMi4zOWMwLTAuMDEsMC0wLjAyLDAtMC4wMmwtMC4wMSwwLjAxbDAtMTg0LjQ5YzAtMzEuMDMtMjUuMTUtNTYuMTgtNTYuMTgtNTYuMTgKCWMwLDAsMCwwLTAuMDEsMEMyNS4xNiwxMDYuOTMsMCwxMzIuMDksMCwxNjMuMTFsMCwyNDAuNjJjMCwyOS44OSwyMi4wOCw1NC4yOSw1MS40OSw1Ni4wNGMxLjU4LDAuMTMsMy4xNiwwLjIyLDQuNzcsMC4yMgoJbDg5LjkxLTAuMTRsMzQuMzktMC4wMmwwLjAzLTAuMDNsMi4wMSwwTDI1MC40NSwzNDcuNjJ6Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MDMuODcsMjg2Ljc1Yy0xLjMyLTAuOTYtMi42OC0xLjg5LTQuMS0yLjc1TDM4OC43LDIxNi43OGwtMC4wMSwwbDAsMGwtMTAuNTUtNi4zOWwtMzIuMDItMTcuOTlsLTI5LjU1LDQ4LjMzCglsLTI4LjM5LDQ2LjMxbDEwNS4zMSw2My45YzAsMC4wMS0wLjAxLDAuMDMtMC4wMSwwLjAzbDAuMDIsMGwtOTUuNzIsMTU3LjcyYy0xNi4wOSwyNi41My03LjY0LDYxLjA5LDE4Ljg5LDc3LjE4CgljMjYuNTMsMTYuMSw2MS4wOSw3LjY0LDc3LjE4LTE4Ljg5bDEyNC44My0yMDUuNzFDNTM0LjE2LDMzNS43Nyw1MjcuOTgsMzAzLjUyLDUwMy44NywyODYuNzV6Ii8+CjxsaW5lIGNsYXNzPSJzdDEiIHgxPSI1Ni45NyIgeTE9IjY2NS4yNCIgeDI9IjQ2My43OCIgeTI9IjAiLz4KPC9zdmc+Cg==';
83 $icon_data_uri = 'data:image/svg+xml;base64,' . $icon_base64;
84 // Get current view parameter
85 $current_view = ( isset( $_GET['view'] ) ? $_GET['view'] : '' );
86 add_menu_page(
87 'LPagery',
88 'LPagery',
89 'manage_options',
90 'lpagery',
91 'bootstrap',
92 $icon_data_uri
93 );
94 // Add submenu items for Pro version
95 add_submenu_page(
96 'lpagery',
97 'Create Pages',
98 'Create Pages',
99 'manage_options',
100 'lpagery&view=create',
101 'bootstrap'
102 );
103 add_submenu_page(
104 'lpagery',
105 'Update Pages',
106 'Update Pages',
107 'manage_options',
108 'lpagery&view=update',
109 'bootstrap'
110 );
111 add_submenu_page(
112 'lpagery',
113 'Manage Pages',
114 'Manage Pages',
115 'manage_options',
116 'lpagery&view=manage',
117 'bootstrap'
118 );
119 add_submenu_page(
120 'lpagery',
121 'Settings',
122 'Settings',
123 'manage_options',
124 'lpagery&view=settings',
125 'bootstrap'
126 );
127 global $submenu;
128 if ( isset( $submenu['lpagery'] ) ) {
129 // Remove the first item which is the duplicate
130 unset($submenu['lpagery'][0]);
131 }
132 // Add filter to modify current menu parent
133 add_filter( 'parent_file', function ( $parent_file ) use($current_view) {
134 global $submenu_file;
135 if ( isset( $_GET['page'] ) && $_GET['page'] === 'lpagery' ) {
136 $submenu_file = 'lpagery&view=' . $current_view;
137 }
138 return $parent_file;
139 } );
140 if ( isset( $_GET['page'] ) && $_GET['page'] === 'lpagery' && isset( $_GET['authorize'] ) && $_GET['authorize'] === 'true' ) {
141 if ( !is_user_logged_in() ) {
142 // Get the current URL
143 $current_url = (( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on' ? "https" : "http" )) . "://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
144 // Create the login URL with proper redirect
145 $login_url = wp_login_url( $current_url );
146 // Redirect to login
147 wp_redirect( $login_url );
148 exit;
149 }
150 // Generate a unique code for this authorization
151 $code = wp_generate_password( 32, false );
152 $user_id = get_current_user_id();
153 // Store the code with user ID and additional data in transients
154 $nonce = wp_create_nonce( 'suite_oauth_' . $code );
155 set_transient( 'suite_oauth_code_' . $code, [
156 'user_id' => $user_id,
157 'timestamp' => time(),
158 'app_user_mail_address' => sanitize_email( $_GET["app_user_mail_address"] ),
159 'nonce' => $nonce,
160 ], 300 );
161 $suite_origin = 'https://app.lpagery.io';
162 ?>
163 <!DOCTYPE html>
164 <html>
165 <head>
166 <meta charset="utf-8">
167 <title>LPagery Authorization</title>
168 </head>
169 <body>
170 <script>
171 (function() {
172 const code = '<?php
173 echo esc_js( $code );
174 ?>';
175 const nonce = '<?php
176 echo esc_js( $nonce );
177 ?>';
178 const user_id = '<?php
179 echo esc_js( strval( $user_id ) );
180 ?>';
181 if (window.opener) {
182 window.opener.postMessage({
183 type: 'wordpress_auth',
184 nonce:nonce,
185 user_id:user_id,
186 code:code
187 }, '<?php
188 echo esc_js( $suite_origin );
189 ?>');
190
191 }
192 })();
193 </script>
194 <p>Authorization completed. You can close this window.</p>
195 </body>
196 </html>
197 <?php
198 exit;
199 }
200 add_action( 'admin_footer', function () {
201 ?>
202 <script>
203 window.addEventListener('lpageryHeaderChange', function(e) {
204 // Find and update the active menu state
205 jQuery('#adminmenu .wp-submenu li').removeClass('current');
206 jQuery('#adminmenu .wp-submenu a').removeClass('current');
207
208 // Find the matching menu item and highlight both the link and its parent li
209 var $menuLink = jQuery('#adminmenu .wp-submenu a[href*="lpagery&view=' + e.detail.header + '"]');
210 $menuLink.addClass('current');
211 $menuLink.parent('li').addClass('current');
212
213 // Also update the first menu item if we're on the main view
214 if (!e.detail.header || e.detail.header === '') {
215 jQuery('#adminmenu .wp-submenu li:first-child').addClass('current');
216 jQuery('#adminmenu .wp-submenu li:first-child a').addClass('current');
217 }
218 });
219 </script>
220 <?php
221 } );
222 if ( !TrackingPermissionService::get_instance( InstallationDateHandler::get_instance() )->getPermissions()->getIntercom() ) {
223 add_submenu_page(
224 'lpagery',
225 // Parent slug
226 'Contact Us',
227 // Page title
228 'Contact Us',
229 // Menu title
230 'manage_options',
231 // Capability
232 'lpagery_contact',
233 // Menu slug
234 function () {
235 // Callback function to handle redirect
236 wp_redirect( 'https://lpagery.io/contact/' );
237 exit;
238 }
239 );
240 // Add JavaScript to handle the redirect
241 add_action( 'admin_footer', function () {
242 ?>
243 <script>
244 jQuery(document).ready(function($) {
245 // Directly modify the menu item when the page loads
246 $('a[href*="admin.php?page=lpagery_contact"]').attr('href', 'https://lpagery.io/contact/').attr('target', '_blank');
247
248 // Prevent the default navigation and redirect if someone clicks before JS runs
249 $(document).on('click', 'a[href*="admin.php?page=lpagery_contact"]', function(e) {
250 e.preventDefault();
251 window.open('https://lpagery.io/contact/', '_blank');
252 });
253 });
254 </script>
255 <?php
256 } );
257 }
258 }
259
260 include_once plugin_dir_path( __FILE__ ) . '/src/io/AjaxActions.php';
261 lpagery_fs()->add_filter( 'permission_list', 'add_lpagery_permssions' );
262 function add_lpagery_permssions( $permissions ) {
263 $permissions['tracking'] = array(
264 'id' => 'tracking',
265 'icon-class' => 'dashicons dashicons-cloud',
266 'label' => lpagery_fs()->get_text_inline( 'View User Behaviour', 'tracking' ),
267 'desc' => lpagery_fs()->get_text_inline( 'Allow tracking of user behaviour to improve the plugin', 'permissions-tracking' ),
268 'tooltip' => lpagery_fs()->get_text_inline( 'We do not track any personal or sensitive data. We just want to understand what our users do, to make the plugin better.', 'permissions-tracking' ),
269 'priority' => 35,
270 );
271 $permissions['error_monitoring'] = array(
272 'id' => 'error_monitoring',
273 'icon-class' => 'dashicons dashicons-warning',
274 'label' => lpagery_fs()->get_text_inline( 'Error Monitoring', 'error_monitoring' ),
275 'desc' => lpagery_fs()->get_text_inline( 'Allow monitoring of errors to improve the plugin', 'permissions-error-monitoring' ),
276 'tooltip' => lpagery_fs()->get_text_inline( 'We do not track any personal or sensitive data. We just want to understand what errors occur, to make the plugin better.', 'permissions-error-monitoring' ),
277 'priority' => 36,
278 );
279 if ( lpagery_fs()->is_premium() ) {
280 $permissions["intercom"] = array(
281 'id' => 'intercom',
282 'icon-class' => 'dashicons dashicons-admin-comments',
283 'label' => lpagery_fs()->get_text_inline( 'Intercom', 'intercom' ),
284 'desc' => lpagery_fs()->get_text_inline( 'Allow Intercom to be shown in the plugin', 'permissions-intercom' ),
285 'tooltip' => lpagery_fs()->get_text_inline( 'We use Intercom to provide support and help you with the plugin.', 'permissions-intercom' ),
286 'priority' => 37,
287 );
288 }
289 return $permissions;
290 }
291
292 function lpagery_info_log( $message ) {
293 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
294 error_log( $message );
295 }
296 }
297
298 new \LPagery\suite\SuiteRestApi();
299 add_action( 'admin_enqueue_scripts', function ( $page ) : void {
300 if ( $page !== 'toplevel_page_lpagery' ) {
301 return;
302 }
303 Vite\enqueue_asset( __DIR__ . '/frontend/dist', 'src/index.tsx', [
304 'handle' => 'lpagery_scripts',
305 'dependencies' => ['react', 'react-dom'],
306 'css-media' => 'all',
307 'css-only' => false,
308 'in-footer' => true,
309 ] );
310 global $wpdb;
311 $table_name_tokens = $wpdb->prefix . 'lpagery_app_tokens';
312 $table_name_process = $wpdb->prefix . 'lpagery_process';
313 $app_connected = $wpdb->get_var( "SELECT EXISTS (SELECT * FROM {$table_name_tokens})" );
314 $process_from_app_exists = $wpdb->get_var( "SELECT EXISTS (SELECT * FROM {$table_name_process} WHERE managing_system = 'app')" );
315 $installationDateHandler = InstallationDateHandler::get_instance();
316 $lpagery_scripts_object = array(
317 'is_free_plan' => (bool) lpagery_fs()->is_free_plan(),
318 'is_premium_code' => (bool) lpagery_fs()->is_premium(),
319 'has_features_enabled_license' => (bool) lpagery_fs()->has_features_enabled_license(),
320 'is_extended_plan' => (bool) lpagery_fs()->is_plan_or_trial__premium_only( "extended" ),
321 'is_standard_plan' => (bool) lpagery_fs()->is_plan_or_trial__premium_only( "standard" ),
322 'ajax_url' => admin_url( 'admin-ajax.php' ),
323 'nonce' => wp_create_nonce( "lpagery_ajax" ),
324 'plugin_dir' => plugin_dir_url( dirname( __FILE__ ) ),
325 'upload_dir' => wp_upload_dir(),
326 'tracking_permissions' => TrackingPermissionService::get_instance( $installationDateHandler )->getPermissions(),
327 'allowed_placeholders' => $installationDateHandler->get_placeholder_counts(),
328 'max_pages_per_run' => $installationDateHandler->get_max_pages_per_run(),
329 'version' => LPAGERY_VERSION,
330 'username' => wp_get_current_user()->display_name,
331 'app_connected' => (bool) $app_connected,
332 'process_from_app_exists' => (bool) $process_from_app_exists,
333 'wpml_installed' => (bool) defined( 'ICL_SITEPRESS_VERSION' ),
334 );
335 // Encode the data as JSON and output it inline
336 wp_add_inline_script( 'lpagery_scripts', 'const lpagery_scripts_object = ' . wp_json_encode( $lpagery_scripts_object ) . ';', 'before' );
337 } );
338 function bootstrap() : void {
339 printf( '<div id="lpagery-container" class="lpagery-tailwind" ></div>' );
340 }
341
342 add_action( 'admin_init', 'lpagery_admin_init' );
343 function lpagery_admin_init() {
344 LPageryDao::get_instance()->init_db();
345 }
346
347 function suppress_all_admin_notices_for_lpagery() {
348 $screen = get_current_screen();
349 if ( $screen && strpos( $screen->id, 'toplevel_page_lpagery' ) !== false ) {
350 // Replace 'LPagery' with your plugin's screen ID if different
351 remove_all_actions( 'admin_notices' );
352 }
353 }
354
355 add_action( 'admin_head', 'suppress_all_admin_notices_for_lpagery' );
356 add_filter(
357 'posts_where',
358 'lpagery_source_filter',
359 10,
360 2
361 );
362 function lpagery_source_filter( $where, $query ) {
363 global $wpdb;
364 // Only apply filter on admin post listing pages
365 if ( !is_admin() || !$query->is_main_query() ) {
366 return $where;
367 }
368 $table_name_process_post = $wpdb->prefix . 'lpagery_process_post';
369 if ( !isset( $_GET['lpagery_process'] ) && !isset( $_GET['lpagery_template'] ) ) {
370 return $where;
371 }
372 if ( isset( $_GET['lpagery_template'] ) ) {
373 $lpagery_template_id = $_GET['lpagery_template'];
374 if ( $lpagery_template_id != '' ) {
375 $lpagery_template_id = intval( $lpagery_template_id );
376 $where .= $wpdb->prepare( " AND EXISTS (\n SELECT pp.id \n FROM {$table_name_process_post} pp\n WHERE pp.template_id = %d \n AND pp.post_id = {$wpdb->posts}.id\n )", $lpagery_template_id );
377 }
378 } else {
379 $lpagery_process_id = $_GET['lpagery_process'];
380 if ( $lpagery_process_id != '' ) {
381 $lpagery_process_id = intval( $lpagery_process_id );
382 $where .= $wpdb->prepare( " AND EXISTS (\n SELECT pp.id\n FROM {$table_name_process_post} pp\n WHERE pp.lpagery_process_id = %d\n AND pp.post_id = {$wpdb->posts}.id\n )", $lpagery_process_id );
383 }
384 }
385 return $where;
386 }
387
388 add_action( 'restrict_manage_posts', 'lpagery_customized_filters' );
389 function lpagery_customized_filters() {
390 ?>
391 <input id="lpagery_reset_filter" class="button" type="button" value="Reset LPagery Filter"
392 style="display: none">
393 <?php
394 }
395
396 // Hide generated pages filter
397 add_filter(
398 'posts_where',
399 'lpagery_hide_generated_pages_filter',
400 10,
401 2
402 );
403 function lpagery_hide_generated_pages_filter( $where, $query ) {
404 global $wpdb;
405 // Only apply if the setting is enabled
406 if ( !SettingsController::get_instance()->isHideGeneratedPagesEnabled() ) {
407 return $where;
408 }
409 // Only apply filter on admin post listing pages
410 if ( !is_admin() || !$query->is_main_query() ) {
411 return $where;
412 }
413 // Don't filter when user is intentionally viewing LPagery pages
414 if ( isset( $_GET['lpagery_process'] ) || isset( $_GET['lpagery_template'] ) ) {
415 return $where;
416 }
417 // Only apply on edit screens
418 $screen = get_current_screen();
419 if ( !$screen || !in_array( $screen->base, ['edit'] ) ) {
420 return $where;
421 }
422 $table_name_process_post = $wpdb->prefix . 'lpagery_process_post';
423 // Exclude posts that are LPagery-generated
424 $where .= " AND NOT EXISTS (\n SELECT 1 \n FROM {$table_name_process_post} lpp \n WHERE lpp.post_id = {$wpdb->posts}.ID\n )";
425 return $where;
426 }
427
428 // Adjust post counts to exclude generated pages
429 add_filter(
430 'wp_count_posts',
431 'lpagery_adjust_post_counts',
432 10,
433 3
434 );
435 function lpagery_adjust_post_counts( $counts, $type, $perm ) {
436 global $wpdb;
437 // Only apply if the setting is enabled
438 if ( !SettingsController::get_instance()->isHideGeneratedPagesEnabled() ) {
439 return $counts;
440 }
441 // Only adjust in admin
442 if ( !is_admin() ) {
443 return $counts;
444 }
445 // Don't adjust when viewing LPagery pages
446 if ( isset( $_GET['lpagery_process'] ) || isset( $_GET['lpagery_template'] ) ) {
447 return $counts;
448 }
449 $table_name_process_post = $wpdb->prefix . 'lpagery_process_post';
450 // Count LPagery-generated posts by status (COUNT DISTINCT to avoid
451 // double-counting posts that appear in multiple processes)
452 $lpagery_counts = $wpdb->get_results( $wpdb->prepare( "\n SELECT p.post_status, COUNT(DISTINCT p.ID) as count\n FROM {$wpdb->posts} p\n INNER JOIN {$table_name_process_post} lpp ON p.ID = lpp.post_id\n WHERE p.post_type = %s\n GROUP BY p.post_status\n ", $type ), ARRAY_A );
453 // Subtract LPagery counts from the total counts
454 foreach ( $lpagery_counts as $row ) {
455 $status = $row['post_status'];
456 $count = (int) $row['count'];
457 if ( isset( $counts->{$status} ) ) {
458 $counts->{$status} = max( 0, $counts->{$status} - $count );
459 }
460 }
461 return $counts;
462 }
463
464 add_action( 'admin_footer', 'lpagery_add_filter_text_process' );
465 add_action( 'admin_footer', 'lpagery_add_filter_text_template_post' );
466 function lpagery_add_filter_text_process() {
467 if ( !isset( $_GET['lpagery_process'] ) ) {
468 return;
469 }
470 $lpagery_process_id = intval( $_GET['lpagery_process'] );
471 if ( !$lpagery_process_id ) {
472 return;
473 }
474 $process = LPageryDao::get_instance()->get_instance()->lpagery_get_process_by_id( $lpagery_process_id );
475 if ( empty( $process ) ) {
476 return;
477 }
478 $mapper = Mapper::get_instance();
479 $process = $mapper->lpagery_map_process( $process );
480 $post_id = $process["post_id"];
481 $purpose = $process["display_purpose"];
482 $post = get_post( $post_id );
483 if ( !$post || !current_user_can( 'edit_post', $post->ID ) ) {
484 return;
485 }
486 $post_title = $post->post_title;
487 $permalink = get_permalink( $post_id );
488 if ( $post_title ) {
489 ?>
490 <script>
491 jQuery(function ($) {
492 let test = $('<span><?php
493 echo esc_js( $purpose );
494 ?> with Template: <a href="<?php
495 echo esc_url( $permalink );
496 ?>"> <?php
497 echo esc_js( $post_title );
498 ?><a/></span')
499 $('<div style="margin-bottom:5px;"></div>').append(test).insertAfter('#wpbody-content .wrap h2:eq(0)');
500 });
501 </script><?php
502 }
503 }
504
505 function lpagery_add_filter_text_template_post() {
506 if ( !isset( $_GET['lpagery_template'] ) ) {
507 return;
508 }
509 $lpagery_template_id = intval( $_GET['lpagery_template'] );
510 if ( !$lpagery_template_id ) {
511 return;
512 }
513 $post = get_post( $lpagery_template_id );
514 if ( !$post || !current_user_can( 'edit_post', $post->ID ) ) {
515 return;
516 }
517 $post_title = $post->post_title;
518 $permalink = get_permalink( $post );
519 if ( $post_title ) {
520 ?>
521 <script>
522 jQuery(function ($) {
523 let test = $('<span>Show all created pages with Template: <a href="<?php
524 echo esc_url( $permalink );
525 ?>"> <?php
526 echo esc_js( $post_title );
527 ?><a/></span')
528 $('<div style="margin-bottom:5px;"></div>').append(test).insertAfter('#wpbody-content .wrap h2:eq(0)');
529 });
530 </script><?php
531 }
532 }
533
534 function lpagery_filter_add_export_row_action( $actions, WP_Post $post ) {
535 $post_id = $post->ID;
536 // Use cached data if available, otherwise fall back to database query
537 $process_id = null;
538 if ( isset( $GLOBALS['lpagery_template_cache'][$post_id] ) ) {
539 // Cache hit: process_id set = template, null = not a template (batch already checked)
540 $process_id = $GLOBALS['lpagery_template_cache'][$post_id]['process_id'];
541 } else {
542 // Fallback for single post views or when batch cache was not populated
543 $process_id_result = LPageryDao::get_instance()->lpagery_get_process_id_by_template( $post_id );
544 if ( $process_id_result ) {
545 $process_id = $process_id_result["process_id"];
546 }
547 }
548 if ( $process_id ) {
549 $nonce = wp_create_nonce( "lpagery_ajax" );
550 $actions['lpagery_export_page'] = sprintf( '<a href="%1$s" target="_blank">%2$s</a>', get_admin_url( null, 'admin-ajax.php' ) . '?action=lpagery_download_post_json&process_id=' . $process_id . '&_ajax_nonce=' . $nonce, __( 'LPagery: Export Template Page', 'lpagery' ) );
551 }
552 return $actions;
553 }
554
555 // Batch-load template data for row actions
556 add_filter(
557 'the_posts',
558 'lpagery_batch_load_template_data',
559 10,
560 2
561 );
562 function lpagery_batch_load_template_data( $posts, $query ) {
563 global $wpdb;
564 static $template_cache = array();
565 // Only in admin and main query
566 if ( !is_admin() || !$query->is_main_query() ) {
567 return $posts;
568 }
569 // Only on edit screens
570 $screen = get_current_screen();
571 if ( !$screen || !in_array( $screen->base, ['edit'] ) ) {
572 return $posts;
573 }
574 if ( empty( $posts ) ) {
575 return $posts;
576 }
577 // Collect all post IDs
578 $post_ids = array_map( function ( $post ) {
579 return $post->ID;
580 }, $posts );
581 if ( empty( $post_ids ) ) {
582 return $posts;
583 }
584 // Batch query to get template data
585 $table_name_process_post = $wpdb->prefix . 'lpagery_process_post';
586 $placeholders = implode( ',', array_fill( 0, count( $post_ids ), '%d' ) );
587 $query_string = $wpdb->prepare( "SELECT template_id, lpagery_process_id FROM {$table_name_process_post} WHERE template_id IN ({$placeholders}) GROUP BY template_id", ...$post_ids );
588 $results = $wpdb->get_results( $query_string );
589 // Cache results: templates get process_id, non-templates get null
590 $template_ids_from_results = array();
591 foreach ( $results as $row ) {
592 $template_cache[$row->template_id] = array(
593 'process_id' => $row->lpagery_process_id,
594 );
595 $template_ids_from_results[$row->template_id] = true;
596 }
597 // Mark post IDs that were checked but are NOT templates (avoids fallback queries)
598 foreach ( $post_ids as $post_id ) {
599 if ( !isset( $template_ids_from_results[$post_id] ) ) {
600 $template_cache[$post_id] = array(
601 'process_id' => null,
602 );
603 }
604 }
605 // Store in a global for access in row action filter
606 $GLOBALS['lpagery_template_cache'] = $template_cache;
607 return $posts;
608 }
609
610 add_filter(
611 'post_row_actions',
612 'lpagery_filter_add_export_row_action',
613 2,
614 2
615 );
616 add_filter(
617 'page_row_actions',
618 'lpagery_filter_add_export_row_action',
619 2,
620 2
621 );
622 if ( !function_exists( 'str_contains' ) ) {
623 function str_contains( $haystack, $needle ) {
624 return '' === $needle || false !== strpos( $haystack, $needle );
625 }
626
627 }
628 if ( !function_exists( 'str_starts_with' ) ) {
629 function str_starts_with( $haystack, $needle ) {
630 if ( '' === $needle ) {
631 return true;
632 }
633 return 0 === strpos( $haystack, $needle );
634 }
635
636 }
637 if ( !function_exists( 'str_ends_with' ) ) {
638 function str_ends_with( $haystack, $needle ) {
639 if ( '' === $haystack && '' !== $needle ) {
640 return false;
641 }
642 $len = strlen( $needle );
643 return 0 === substr_compare(
644 $haystack,
645 $needle,
646 -$len,
647 $len
648 );
649 }
650
651 }
652 add_shortcode( 'lpagery_urls', 'add_lpagery_urls_shortcode' );
653 function add_lpagery_urls_shortcode( $atts ) {
654 if ( isset( $atts["id"] ) ) {
655 $post_ids = LPageryDao::get_instance()->lpagery_get_posts_by_process( $atts["id"] );
656 if ( !empty( $post_ids ) ) {
657 $list_items = '';
658 foreach ( $post_ids as $record ) {
659 $post_id = $record->id;
660 $post_title = get_the_title( $post_id );
661 $post_permalink = get_permalink( $post_id );
662 $list_items .= "<li class='lpagery_created_page_item'><a class='lpagery_created_page_anchor' href='" . esc_url( $post_permalink ) . "'>" . esc_html( $post_title ) . "</a></li>";
663 }
664 return "<ul class='lpagery_created_page_list'>{$list_items}</ul>";
665 }
666 }
667 return null;
668 }
669
670 add_shortcode( 'lpagery_link', 'add_lpagery_link_shortcode' );
671 function add_lpagery_link_shortcode( $atts ) {
672 $post_id = get_the_ID();
673 $plan_post_created = get_post_meta( $post_id, '_lpagery_plan', true );
674 if ( !($plan_post_created === 'PRO' || lpagery_fs()->is_plan_or_trial( 'EXTENDED' )) ) {
675 return null;
676 }
677 $slug = $atts['slug'] ?? null;
678 $position = $atts['position'] ?? null;
679 $circle = filter_var( $atts['circle'], FILTER_VALIDATE_BOOLEAN );
680 $title = $atts['title'] ?? null;
681 $target = $atts['target'] ?? '_self';
682 $allowed_targets = [
683 '_blank',
684 '_self',
685 '_parent',
686 '_top'
687 ];
688 if ( !in_array( $target, $allowed_targets ) ) {
689 $target = '_self';
690 // Default to _self if target is not valid
691 }
692 $found_post = null;
693 if ( $slug ) {
694 $slug = sanitize_title( $slug );
695 $found_post = LPageryDao::get_instance()->lpagery_get_post_by_slug_for_link( $slug );
696 } elseif ( $position ) {
697 $position = sanitize_text_field( $position );
698 $allowed_positions = [
699 'FIRST',
700 'LAST',
701 'NEXT',
702 'PREV'
703 ];
704 if ( !in_array( $position, $allowed_positions ) ) {
705 return null;
706 }
707 $found_post = LPageryDao::get_instance()->lpagery_get_post_at_position_in_process( $post_id, $position, $circle );
708 }
709 if ( $found_post ) {
710 $title = ( empty( $title ) ? $found_post['post_title'] : $title );
711 return '<a class="lpagery_link_anchor" href="' . esc_url( get_permalink( $found_post['id'] ) ) . '" target="' . esc_attr( $target ) . '">' . esc_html( $title ) . '</a>';
712 }
713 return null;
714 }
715
716 function lpagery_add_media_fields( $form_fields, $post ) {
717 $settingsController = SettingsController::get_instance();
718 if ( $settingsController->isImageProcessingEnabled() ) {
719 $form_fields['lpagery_replace_filename'] = array(
720 'label' => '<img width="25px" height ="25px" src="' . plugin_dir_url( dirname( __FILE__ ) ) . "/" . plugin_basename( dirname( __FILE__ ) ) . '/assets/lpagery.png"/>Download Filename',
721 'input' => 'text',
722 'value' => get_post_meta( $post->ID, '_lpagery_replace_filename', true ),
723 'helps' => 'The name for LPagery to be taken for downloading images when using this image as an placeholder. The ending will be populated automatically. Please add placeholders from the input file here (e.g. "my-image-in-{city}")',
724 );
725 $form_fields['lpagery_update_metadata'] = array(
726 'label' => '<img width="25px" height="25px" src="' . plugin_dir_url( dirname( __FILE__ ) ) . "/" . plugin_basename( dirname( __FILE__ ) ) . '/assets/lpagery.png"/>Update Metadata',
727 'input' => 'html',
728 'html' => '<input type="checkbox" name="attachments[' . $post->ID . '][lpagery_update_metadata]" value="1" ' . checked( get_post_meta( $post->ID, '_lpagery_update_metadata', true ), '1', false ) . ' />',
729 'helps' => 'Check this box to update the image metadata of existing replacement images when processing this image',
730 );
731 }
732 return $form_fields;
733 }
734
735 add_filter(
736 'attachment_fields_to_edit',
737 'lpagery_add_media_fields',
738 10,
739 2
740 );
741 // Keep basename lookup table in sync for fast attachment searches
742 // Note: add_attachment fires BEFORE _wp_attached_file metadata is set, so we use hooks that fire after
743 // Hook into attachment metadata generation (fires after _wp_attached_file is set for new uploads)
744 add_filter(
745 'wp_generate_attachment_metadata',
746 'lpagery_on_attachment_metadata_generated',
747 10,
748 2
749 );
750 function lpagery_on_attachment_metadata_generated( $metadata, $attachment_id ) {
751 $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
752 if ( $file ) {
753 AttachmentBasenameService::get_instance()->insert( $attachment_id, $file );
754 }
755 return $metadata;
756 }
757
758 // Hook into attachment metadata updates
759 add_filter(
760 'wp_update_attachment_metadata',
761 'lpagery_on_attachment_metadata_updated',
762 10,
763 2
764 );
765 function lpagery_on_attachment_metadata_updated( $metadata, $attachment_id ) {
766 $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
767 if ( $file ) {
768 AttachmentBasenameService::get_instance()->insert( $attachment_id, $file );
769 }
770 return $metadata;
771 }
772
773 // Hook into post meta addition (catches _wp_attached_file being set)
774 add_action(
775 'added_post_meta',
776 'lpagery_on_attachment_file_meta_added',
777 10,
778 4
779 );
780 function lpagery_on_attachment_file_meta_added(
781 $meta_id,
782 $post_id,
783 $meta_key,
784 $meta_value
785 ) {
786 if ( $meta_key === '_wp_attached_file' && $meta_value ) {
787 AttachmentBasenameService::get_instance()->insert( $post_id, $meta_value );
788 }
789 }
790
791 // Hook into post meta updates (catches _wp_attached_file being updated)
792 add_action(
793 'updated_post_meta',
794 'lpagery_on_attachment_file_meta_updated',
795 10,
796 4
797 );
798 function lpagery_on_attachment_file_meta_updated(
799 $meta_id,
800 $post_id,
801 $meta_key,
802 $meta_value
803 ) {
804 if ( $meta_key === '_wp_attached_file' && $meta_value ) {
805 AttachmentBasenameService::get_instance()->insert( $post_id, $meta_value );
806 }
807 }
808
809 // Hook into attachment edits
810 add_action( 'edit_attachment', 'lpagery_on_edit_attachment' );
811 function lpagery_on_edit_attachment( $attachment_id ) {
812 $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
813 if ( $file ) {
814 AttachmentBasenameService::get_instance()->insert( $attachment_id, $file );
815 }
816 }
817
818 // Hook into attachment updates (fires when attachment post is updated)
819 add_action(
820 'attachment_updated',
821 'lpagery_on_attachment_updated',
822 10,
823 3
824 );
825 function lpagery_on_attachment_updated( $post_id, $post_after, $post_before ) {
826 $file = get_post_meta( $post_id, '_wp_attached_file', true );
827 if ( $file ) {
828 AttachmentBasenameService::get_instance()->insert( $post_id, $file );
829 }
830 }
831
832 // Hook into REST API attachment creation/updates
833 add_action(
834 'rest_after_insert_attachment',
835 'lpagery_on_rest_attachment_insert',
836 10,
837 3
838 );
839 function lpagery_on_rest_attachment_insert( $attachment, $request, $creating ) {
840 $file = get_post_meta( $attachment->ID, '_wp_attached_file', true );
841 if ( $file ) {
842 AttachmentBasenameService::get_instance()->insert( $attachment->ID, $file );
843 }
844 }
845
846 // Clean up basename lookup table when attachment is deleted
847 add_action( 'delete_attachment', 'lpagery_delete_attachment_basename' );
848 function lpagery_delete_attachment_basename( $attachment_id ) {
849 AttachmentBasenameService::get_instance()->delete( $attachment_id );
850 }
851
852 // Daily cron job to backfill the attachment basename lookup table
853 add_action( 'lpagery_backfill_attachment_basename', 'lpagery_backfill_attachment_basename' );
854 function lpagery_backfill_attachment_basename() {
855 AttachmentBasenameService::get_instance()->backfill();
856 }
857
858 // Schedule the daily backfill cron if not already scheduled
859 if ( !wp_next_scheduled( 'lpagery_backfill_attachment_basename' ) ) {
860 wp_schedule_event( time(), 'daily', 'lpagery_backfill_attachment_basename' );
861 }
862 function lpagery_save_replace_filename_field( $post, $attachment ) {
863 if ( isset( $attachment['lpagery_replace_filename'] ) ) {
864 // Update or add the custom field value
865 update_post_meta( $post['ID'], '_lpagery_replace_filename', $attachment['lpagery_replace_filename'] );
866 }
867 if ( isset( $attachment['lpagery_update_metadata'] ) ) {
868 update_post_meta( $post['ID'], '_lpagery_update_metadata', '1' );
869 } else {
870 update_post_meta( $post['ID'], '_lpagery_update_metadata', '0' );
871 }
872 return $post;
873 }
874
875 add_filter(
876 'attachment_fields_to_save',
877 'lpagery_save_replace_filename_field',
878 10,
879 2
880 );
881 if ( !lpagery_fs()->is_plan_or_trial( 'extended' ) ) {
882 wp_clear_scheduled_hook( "lpagery_sync_google_sheet" );
883 wp_clear_scheduled_hook( "lpagery_queue_worker_cron_event" );
884 wp_clear_scheduled_hook( "lpagery_trigger_cron_started_syncs" );
885 }
886 add_action(
887 'save_post',
888 'lpagery_catch_manual_post_update',
889 10,
890 3
891 );
892 function lpagery_catch_manual_post_update( $post_id, $post, $update ) {
893 $current_user_id = get_current_user_id();
894 if ( !$current_user_id ) {
895 return;
896 }
897 if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
898 return;
899 }
900 if ( defined( 'DOING_LPAGERY_CREATION' ) && DOING_LPAGERY_CREATION ) {
901 return;
902 }
903 if ( !$_POST ) {
904 return;
905 }
906 if ( isset( $_POST["action"] ) && str_starts_with( $_POST["action"], "wpil_" ) ) {
907 return;
908 }
909 if ( !$update ) {
910 return;
911 }
912 global $wpdb;
913 $table_name_process_post = $wpdb->prefix . 'lpagery_process_post';
914 // Combine both updates into a single query
915 $wpdb->query( $wpdb->prepare(
916 "UPDATE {$table_name_process_post} SET page_manually_updated_at = %s, page_manually_updated_by = %d WHERE post_id = %d",
917 current_time( 'mysql' ),
918 $current_user_id,
919 $post_id
920 ) );
921 }
922
923 lpagery_fs()->add_filter( 'pricing_url', function () {
924 return "https://lpagery.io/pricing/?utm_source=free_version&utm_medium=menu_item&utm_campaign=free";
925 } );
926 lpagery_fs()->add_filter( 'plugin_icon', function () {
927 return dirname( __FILE__ ) . '/assets/lpagery.png';
928 } );
929 // Add this new function to handle the menu click tracking
930 add_action( 'admin_footer', 'lpagery_add_menu_tracking' );
931 function lpagery_add_menu_tracking() {
932 ?>
933 <script>
934 jQuery(document).ready(function($) {
935 // Target main menu item and specific submenu items
936 $('a[href="admin.php?page=lpagery"], ' +
937 'a[href*="lpagery&view=create"], ' +
938 'a[href*="lpagery&view=update"], ' +
939 'a[href*="lpagery&view=manage"], ' +
940 'a[href*="lpagery&view=settings"]').on('click', function() {
941 if (!localStorage.getItem('lpagery_intro_showed_free')) {
942 localStorage.setItem('lpagery_intro_showed_free', 'true');
943 }
944 });
945 });
946 </script>
947 <?php
948 }
949
950 }