| @@ -69,12 +69,40 @@ | ||
| 69 | 69 | public static function can_edit_posts() { |
| 70 | 70 | return current_user_can('edit_posts'); |
| 71 | 71 | } |
| 72 | 72 | |
| 73 | + public static function can_edit_pages() { | |
| 74 | + return current_user_can('edit_pages'); | |
| 75 | + } | |
| 76 | + | |
| 77 | + public static function can_publish_pages() { | |
| 78 | + return current_user_can('publish_pages'); | |
| 79 | + } | |
| 80 | + | |
| 81 | + public static function can_publish_posts() { | |
| 82 | + return current_user_can('publish_posts'); | |
| 83 | + } | |
| 84 | + | |
| 85 | + public static function can_delete_pages() { | |
| 86 | + return current_user_can('delete_pages'); | |
| 87 | + } | |
| 88 | + | |
| 89 | + public static function can_delete_posts() { | |
| 90 | + return current_user_can('delete_posts'); | |
| 91 | + } | |
| 92 | + | |
| 93 | + public static function can_edit_theme_options() { | |
| 94 | + return current_user_can('edit_theme_options'); | |
| 95 | + } | |
| 96 | + | |
| 73 | 97 | public static function can_manage_options() { |
| 74 | 98 | return current_user_can('manage_options'); |
| 75 | 99 | } |
| 76 | 100 | |
| 101 | + public static function can_upload_files() { | |
| 102 | + return current_user_can('upload_files'); | |
| 103 | + } | |
| 104 | + | |
| 77 | 105 | // ------------------------------------------------------------------ |
| 78 | 106 | // Shared helpers |
| 79 | 107 | // ------------------------------------------------------------------ |
| 80 | 108 | |
| @@ -123,8 +151,11 @@ | ||
| 123 | 151 | return $out; |
| 124 | 152 | } |
| 125 | 153 | |
| 126 | 154 | public static function maybe_update_global_styles($input) { |
| 155 | + if (!current_user_can('manage_options')) { | |
| 156 | + return; | |
| 157 | + } | |
| 127 | 158 | if (isset($input['global_colors'])) { |
| 128 | 159 | update_option('pagelayer_global_colors', json_encode(self::normalize_global_map($input['global_colors']))); |
| 129 | 160 | } |
| 130 | 161 | if (isset($input['global_fonts'])) { |
| @@ -2078,8 +2109,9 @@ | ||
| 2078 | 2109 | 'required' => array('title', 'type', 'pagelayer_data'), |
| 2079 | 2110 | 'additionalProperties' => false |
| 2080 | 2111 | ), |
| 2081 | 2112 | 'execute' => array(__CLASS__, 'execute_create_template'), |
| 2113 | + 'perm' => array(__CLASS__, 'can_edit_theme_options'), | |
| 2082 | 2114 | ), |
| 2083 | 2115 | 'update_template' => array( |
| 2084 | 2116 | 'label' => __('Update Template', 'pagelayer'), |
| 2085 | 2117 | 'description' => __('Update an existing Pagelayer theme template title, layout data, or display conditions. Same header/footer rules as create_template: Include / Full Site is enforced, and a header needs a Primary Menu widget with a real nav_list.', 'pagelayer'), |
| @@ -2097,8 +2129,9 @@ | ||
| 2097 | 2129 | 'required' => array('template_id'), |
| 2098 | 2130 | 'additionalProperties' => false |
| 2099 | 2131 | ), |
| 2100 | 2132 | 'execute' => array(__CLASS__, 'execute_update_template'), |
| 2133 | + 'perm' => array(__CLASS__, 'can_edit_theme_options'), | |
| 2101 | 2134 | ), |
| 2102 | 2135 | 'delete_template' => array( |
| 2103 | 2136 | 'label' => __('Delete Template', 'pagelayer'), |
| 2104 | 2137 | 'description' => __('Delete a Pagelayer theme template by ID.', 'pagelayer'), |
| @@ -2109,8 +2142,9 @@ | ||
| 2109 | 2142 | 'required' => array('template_id'), |
| 2110 | 2143 | 'additionalProperties' => false |
| 2111 | 2144 | ), |
| 2112 | 2145 | 'execute' => array(__CLASS__, 'execute_delete_template'), |
| 2146 | + 'perm' => array(__CLASS__, 'can_edit_theme_options'), | |
| 2113 | 2147 | ), |
| 2114 | 2148 | 'save_template' => array( |
| 2115 | 2149 | 'label' => __('Save Section Template', 'pagelayer'), |
| 2116 | 2150 | 'description' => __('Save a specific page section or layout to the local template library.', 'pagelayer'), |
| @@ -2125,8 +2159,9 @@ | ||
| 2125 | 2159 | 'required' => array('name', 'post_id'), |
| 2126 | 2160 | 'additionalProperties' => false |
| 2127 | 2161 | ), |
| 2128 | 2162 | 'execute' => array(__CLASS__, 'execute_save_template'), |
| 2163 | + 'perm' => array(__CLASS__, 'can_edit_theme_options'), | |
| 2129 | 2164 | ), |
| 2130 | 2165 | 'insert_template' => array( |
| 2131 | 2166 | 'label' => __('Insert Template', 'pagelayer'), |
| 2132 | 2167 | 'description' => __('Insert a saved template layout structure into a target container on a page.', 'pagelayer'), |
| @@ -2232,8 +2267,9 @@ | ||
| 2232 | 2267 | 'required' => array('site_name', 'pages'), |
| 2233 | 2268 | 'additionalProperties' => false |
| 2234 | 2269 | ), |
| 2235 | 2270 | 'execute' => array(__CLASS__, 'execute_create_website'), |
| 2271 | + 'perm' => array(__CLASS__, 'can_manage_options'), | |
| 2236 | 2272 | ), |
| 2237 | 2273 | 'create_page' => array( |
| 2238 | 2274 | 'label' => __('Create Page', 'pagelayer'), |
| 2239 | 2275 | 'description' => __('Create one page with Pagelayer builder data, status and global styles. FASTEST PATH: send section specs ({"section":"hero",...}) instead of hand-written node trees — see fast_path_sections in get_data_structure. Enforced quality gate: rejected unsaved if any widget keeps its placeholder text, uses an attr not in its schema, sets a gated attr without its companion, or uses an unregistered tag. Styling must live in attrs — an inline style attribute or <style> block in rich text is rejected and cannot be bypassed. Missing images are only a warning. Read get_data_structure and get_widget_schema first.', 'pagelayer'), |
| @@ -2254,8 +2290,9 @@ | ||
| 2254 | 2290 | 'required' => array('title', 'pagelayer_data'), |
| 2255 | 2291 | 'additionalProperties' => false |
| 2256 | 2292 | ), |
| 2257 | 2293 | 'execute' => array(__CLASS__, 'execute_create_page'), |
| 2294 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2258 | 2295 | ), |
| 2259 | 2296 | 'update_page' => array( |
| 2260 | 2297 | 'label' => __('Update Page', 'pagelayer'), |
| 2261 | 2298 | 'description' => __('Update page title, status, or the WHOLE pagelayer_data tree. Sending pagelayer_data REPLACES the entire layout and discards any human edits made in the editor since — to change part of a page use update_element/add_element/change_styles instead, which are far cheaper and non-destructive. Same enforced quality gate as create_page, including the no-inline-CSS-in-rich-text rule.', 'pagelayer'), |
| @@ -2272,8 +2309,9 @@ | ||
| 2272 | 2309 | 'required' => array('post_id'), |
| 2273 | 2310 | 'additionalProperties' => false |
| 2274 | 2311 | ), |
| 2275 | 2312 | 'execute' => array(__CLASS__, 'execute_update_page'), |
| 2313 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2276 | 2314 | ), |
| 2277 | 2315 | 'get_page' => array( |
| 2278 | 2316 | 'label' => __('Get Page', 'pagelayer'), |
| 2279 | 2317 | 'description' => __('Page details plus a compact outline of its elements (id, tag, text preview) — enough to locate anything you want to edit. Pass element_id for one node in full, or mode:"full" for the whole raw tree (large; only needed to rewrite the entire layout).', 'pagelayer'), |
| @@ -2314,8 +2352,9 @@ | ||
| 2314 | 2352 | 'required' => array('post_id'), |
| 2315 | 2353 | 'additionalProperties' => false |
| 2316 | 2354 | ), |
| 2317 | 2355 | 'execute' => array(__CLASS__, 'execute_publish_page'), |
| 2356 | + 'perm' => array(__CLASS__, 'can_publish_pages'), | |
| 2318 | 2357 | ), |
| 2319 | 2358 | 'duplicate_page' => array( |
| 2320 | 2359 | 'label' => __('Duplicate Page', 'pagelayer'), |
| 2321 | 2360 | 'description' => __('Clone an existing page and regenerate all Pagelayer element IDs.', 'pagelayer'), |
| @@ -2329,8 +2368,9 @@ | ||
| 2329 | 2368 | 'required' => array('post_id'), |
| 2330 | 2369 | 'additionalProperties' => false |
| 2331 | 2370 | ), |
| 2332 | 2371 | 'execute' => array(__CLASS__, 'execute_duplicate_page'), |
| 2372 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2333 | 2373 | ), |
| 2334 | 2374 | 'delete_page' => array( |
| 2335 | 2375 | 'label' => __('Delete Page', 'pagelayer'), |
| 2336 | 2376 | 'description' => __('Trash or delete a page by ID.', 'pagelayer'), |
| @@ -2344,8 +2384,9 @@ | ||
| 2344 | 2384 | 'required' => array('post_id'), |
| 2345 | 2385 | 'additionalProperties' => false |
| 2346 | 2386 | ), |
| 2347 | 2387 | 'execute' => array(__CLASS__, 'execute_delete_page'), |
| 2388 | + 'perm' => array(__CLASS__, 'can_delete_pages'), | |
| 2348 | 2389 | ), |
| 2349 | 2390 | 'preview_page' => array( |
| 2350 | 2391 | 'label' => __('Preview Page', 'pagelayer'), |
| 2351 | 2392 | 'description' => __('Retrieve the live view/preview URL for a post or page.', 'pagelayer'), |
| @@ -2386,8 +2427,9 @@ | ||
| 2386 | 2427 | 'required' => array('post_id', 'pagelayer_data'), |
| 2387 | 2428 | 'additionalProperties' => false |
| 2388 | 2429 | ), |
| 2389 | 2430 | 'execute' => array(__CLASS__, 'execute_create_design_ui'), |
| 2431 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2390 | 2432 | ), |
| 2391 | 2433 | 'edit_layout' => array( |
| 2392 | 2434 | 'label' => __('Edit Page Layout', 'pagelayer'), |
| 2393 | 2435 | 'description' => __('Replace the full layout structure of a Pagelayer page. ENFORCED content-quality gate on the new layout — no placeholder text, no missing images, no unregistered widgets. Pass skip_validation:true to bypass for an intentional draft.', 'pagelayer'), |
| @@ -2404,8 +2446,9 @@ | ||
| 2404 | 2446 | 'required' => array('post_id', 'pagelayer_data'), |
| 2405 | 2447 | 'additionalProperties' => false |
| 2406 | 2448 | ), |
| 2407 | 2449 | 'execute' => array(__CLASS__, 'execute_edit_layout'), |
| 2450 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2408 | 2451 | ), |
| 2409 | 2452 | 'change_styles' => array( |
| 2410 | 2453 | 'label' => __('Change Element Styles', 'pagelayer'), |
| 2411 | 2454 | 'description' => __('Batch update style properties on page elements by ID or widget tag.', 'pagelayer'), |
| @@ -2419,8 +2462,9 @@ | ||
| 2419 | 2462 | 'required' => array('post_id', 'styles'), |
| 2420 | 2463 | 'additionalProperties' => false |
| 2421 | 2464 | ), |
| 2422 | 2465 | 'execute' => array(__CLASS__, 'execute_change_styles'), |
| 2466 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2423 | 2467 | ), |
| 2424 | 2468 | 'get_data_structure' => array( |
| 2425 | 2469 | 'label' => __('Get Data Structure Guide', 'pagelayer'), |
| 2426 | 2470 | 'description' => __('How pagelayer_data nodes, global $color tokens and gated attributes work. Default topic covers editing; pass topic:"quality"/"widgets"/"workflow"/"all" when building a page from scratch.', 'pagelayer'), |
| @@ -2481,8 +2525,9 @@ | ||
| 2481 | 2525 | 'required' => array('post_id', 'element_id'), |
| 2482 | 2526 | 'additionalProperties' => false |
| 2483 | 2527 | ), |
| 2484 | 2528 | 'execute' => array(__CLASS__, 'execute_update_element'), |
| 2529 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2485 | 2530 | ), |
| 2486 | 2531 | 'add_element' => array( |
| 2487 | 2532 | 'label' => __('Add Element', 'pagelayer'), |
| 2488 | 2533 | 'description' => __('Insert a new element node into a parent container at an index. Enforced quality gate on the new element (no placeholder text, valid attrs, registered tag); skip_validation:true bypasses it. Inline CSS in rich text is always rejected.', 'pagelayer'), |
| @@ -2499,8 +2544,9 @@ | ||
| 2499 | 2544 | 'required' => array('post_id', 'element'), |
| 2500 | 2545 | 'additionalProperties' => false |
| 2501 | 2546 | ), |
| 2502 | 2547 | 'execute' => array(__CLASS__, 'execute_add_element'), |
| 2548 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2503 | 2549 | ), |
| 2504 | 2550 | 'delete_element' => array( |
| 2505 | 2551 | 'label' => __('Delete Element', 'pagelayer'), |
| 2506 | 2552 | 'description' => __('Remove an element from a page by its pagelayer-id.', 'pagelayer'), |
| @@ -2514,8 +2560,9 @@ | ||
| 2514 | 2560 | 'required' => array('post_id', 'element_id'), |
| 2515 | 2561 | 'additionalProperties' => false |
| 2516 | 2562 | ), |
| 2517 | 2563 | 'execute' => array(__CLASS__, 'execute_delete_element'), |
| 2564 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2518 | 2565 | ), |
| 2519 | 2566 | 'move_element' => array( |
| 2520 | 2567 | 'label' => __('Move Element', 'pagelayer'), |
| 2521 | 2568 | 'description' => __('Relocate an element to a target parent container or index.', 'pagelayer'), |
| @@ -2531,8 +2578,9 @@ | ||
| 2531 | 2578 | 'required' => array('post_id', 'element_id'), |
| 2532 | 2579 | 'additionalProperties' => false |
| 2533 | 2580 | ), |
| 2534 | 2581 | 'execute' => array(__CLASS__, 'execute_move_element'), |
| 2582 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2535 | 2583 | ), |
| 2536 | 2584 | 'duplicate_element' => array( |
| 2537 | 2585 | 'label' => __('Duplicate Element', 'pagelayer'), |
| 2538 | 2586 | 'description' => __('Clone an element by ID, generating new IDs for all child nodes.', 'pagelayer'), |
| @@ -2546,8 +2594,9 @@ | ||
| 2546 | 2594 | 'required' => array('post_id', 'element_id'), |
| 2547 | 2595 | 'additionalProperties' => false |
| 2548 | 2596 | ), |
| 2549 | 2597 | 'execute' => array(__CLASS__, 'execute_duplicate_element'), |
| 2598 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2550 | 2599 | ), |
| 2551 | 2600 | 'begin_transaction' => array( |
| 2552 | 2601 | 'label' => __('Begin Transaction', 'pagelayer'), |
| 2553 | 2602 | 'description' => __('Backup page layout state before multi-step modifications.', 'pagelayer'), |
| @@ -2558,8 +2607,9 @@ | ||
| 2558 | 2607 | 'required' => array('post_id'), |
| 2559 | 2608 | 'additionalProperties' => false |
| 2560 | 2609 | ), |
| 2561 | 2610 | 'execute' => array(__CLASS__, 'execute_begin_transaction'), |
| 2611 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2562 | 2612 | ), |
| 2563 | 2613 | 'commit_transaction' => array( |
| 2564 | 2614 | 'label' => __('Commit Transaction', 'pagelayer'), |
| 2565 | 2615 | 'description' => __('Commit layout changes and delete backup state.', 'pagelayer'), |
| @@ -2570,8 +2620,9 @@ | ||
| 2570 | 2620 | 'required' => array('post_id'), |
| 2571 | 2621 | 'additionalProperties' => false |
| 2572 | 2622 | ), |
| 2573 | 2623 | 'execute' => array(__CLASS__, 'execute_commit_transaction'), |
| 2624 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2574 | 2625 | ), |
| 2575 | 2626 | 'rollback_transaction' => array( |
| 2576 | 2627 | 'label' => __('Rollback Transaction', 'pagelayer'), |
| 2577 | 2628 | 'description' => __('Restore original page layout state from transaction backup.', 'pagelayer'), |
| @@ -2582,8 +2633,9 @@ | ||
| 2582 | 2633 | 'required' => array('post_id'), |
| 2583 | 2634 | 'additionalProperties' => false |
| 2584 | 2635 | ), |
| 2585 | 2636 | 'execute' => array(__CLASS__, 'execute_rollback_transaction'), |
| 2637 | + 'perm' => array(__CLASS__, 'can_edit_pages'), | |
| 2586 | 2638 | ), |
| 2587 | 2639 | ); |
| 2588 | 2640 | |
| 2589 | 2641 | foreach ($abilities as $id => $def) { |
| @@ -2614,8 +2666,9 @@ | ||
| 2614 | 2666 | 'required' => array('title', 'pagelayer_data'), |
| 2615 | 2667 | 'additionalProperties' => false |
| 2616 | 2668 | ), |
| 2617 | 2669 | 'execute' => array(__CLASS__, 'execute_create_post'), |
| 2670 | + 'perm' => array(__CLASS__, 'can_edit_posts'), | |
| 2618 | 2671 | ), |
| 2619 | 2672 | 'update_post' => array( |
| 2620 | 2673 | 'label' => __('Update Individual Post', 'pagelayer'), |
| 2621 | 2674 | 'description' => __('Update an existing blog post title, layout data, categories, tags, excerpt, or status.', 'pagelayer'), |
| @@ -2635,8 +2688,9 @@ | ||
| 2635 | 2688 | 'required' => array('post_id'), |
| 2636 | 2689 | 'additionalProperties' => false |
| 2637 | 2690 | ), |
| 2638 | 2691 | 'execute' => array(__CLASS__, 'execute_update_post'), |
| 2692 | + 'perm' => array(__CLASS__, 'can_edit_posts'), | |
| 2639 | 2693 | ), |
| 2640 | 2694 | 'get_post' => array( |
| 2641 | 2695 | 'label' => __('Get Individual Post', 'pagelayer'), |
| 2642 | 2696 | 'description' => __('Retrieve blog post details, categories, tags, excerpt, featured image, and pagelayer_data.', 'pagelayer'), |
| @@ -2674,8 +2728,9 @@ | ||
| 2674 | 2728 | 'required' => array('post_id'), |
| 2675 | 2729 | 'additionalProperties' => false |
| 2676 | 2730 | ), |
| 2677 | 2731 | 'execute' => array(__CLASS__, 'execute_publish_post'), |
| 2732 | + 'perm' => array(__CLASS__, 'can_publish_posts'), | |
| 2678 | 2733 | ), |
| 2679 | 2734 | 'duplicate_post' => array( |
| 2680 | 2735 | 'label' => __('Duplicate Individual Post', 'pagelayer'), |
| 2681 | 2736 | 'description' => __('Clone an existing blog post with new Pagelayer element IDs.', 'pagelayer'), |
| @@ -2689,8 +2744,9 @@ | ||
| 2689 | 2744 | 'required' => array('post_id'), |
| 2690 | 2745 | 'additionalProperties' => false |
| 2691 | 2746 | ), |
| 2692 | 2747 | 'execute' => array(__CLASS__, 'execute_duplicate_post'), |
| 2748 | + 'perm' => array(__CLASS__, 'can_edit_posts'), | |
| 2693 | 2749 | ), |
| 2694 | 2750 | 'delete_post' => array( |
| 2695 | 2751 | 'label' => __('Delete Individual Post', 'pagelayer'), |
| 2696 | 2752 | 'description' => __('Trash or delete a blog post by ID.', 'pagelayer'), |
| @@ -2704,8 +2760,9 @@ | ||
| 2704 | 2760 | 'required' => array('post_id'), |
| 2705 | 2761 | 'additionalProperties' => false |
| 2706 | 2762 | ), |
| 2707 | 2763 | 'execute' => array(__CLASS__, 'execute_delete_post'), |
| 2764 | + 'perm' => array(__CLASS__, 'can_delete_posts'), | |
| 2708 | 2765 | ), |
| 2709 | 2766 | ); |
| 2710 | 2767 | |
| 2711 | 2768 | foreach ($abilities as $id => $def) { |
| @@ -2728,8 +2785,9 @@ | ||
| 2728 | 2785 | 'required' => array('url'), |
| 2729 | 2786 | 'additionalProperties' => false |
| 2730 | 2787 | ), |
| 2731 | 2788 | 'execute' => array(__CLASS__, 'execute_upload_media'), |
| 2789 | + 'perm' => array(__CLASS__, 'can_upload_files'), | |
| 2732 | 2790 | ), |
| 2733 | 2791 | ); |
| 2734 | 2792 | |
| 2735 | 2793 | foreach ($abilities as $id => $def) { |
| @@ -2985,8 +3043,12 @@ | ||
| 2985 | 3043 | if (empty($section_id) || !$post_id || !get_post($post_id)) { |
| 2986 | 3044 | return new \WP_Error('invalid_input', __('Valid section_id and post_id are required.', 'pagelayer')); |
| 2987 | 3045 | } |
| 2988 | 3046 | |
| 3047 | + if (!current_user_can('edit_post', $post_id)) { | |
| 3048 | + return new \WP_Error('forbidden', __('You are not allowed to edit this post.', 'pagelayer'), array('status' => 403)); | |
| 3049 | + } | |
| 3050 | + | |
| 2989 | 3051 | global $pagelayer; |
| 2990 | 3052 | $license_key = !empty($pagelayer->license['license']) ? $pagelayer->license['license'] : ''; |
| 2991 | 3053 | $url = 'https://api.pagelayer.com/library.php?give_id=' . rawurlencode($section_id) . '&license=' . rawurlencode($license_key) . '&url=' . rawurlencode(site_url()); |
| 2992 | 3054 | |
| @@ -3044,10 +3106,10 @@ | ||
| 3044 | 3106 | * and its own judgement about layout, matching the requested niche/brand. |
| 3045 | 3107 | */ |
| 3046 | 3108 | public static function execute_scrape_website_content($input) { |
| 3047 | 3109 | $url = isset($input['url']) ? esc_url_raw($input['url']) : ''; |
| 3048 | - if (empty($url)) { | |
| 3049 | - return new \WP_Error('missing_url', __('URL is required.', 'pagelayer')); | |
| 3110 | + if (empty($url) || !wp_http_validate_url($url)) { | |
| 3111 | + return new \WP_Error('invalid_url', __('A valid public URL is required.', 'pagelayer')); | |
| 3050 | 3112 | } |
| 3051 | 3113 | |
| 3052 | 3114 | $response = wp_remote_get($url, array( |
| 3053 | 3115 | 'timeout' => 25, |
| @@ -3158,8 +3220,11 @@ | ||
| 3158 | 3220 | ); |
| 3159 | 3221 | } |
| 3160 | 3222 | |
| 3161 | 3223 | public static function execute_update_styles($input) { |
| 3224 | + if (!current_user_can('manage_options')) { | |
| 3225 | + return new \WP_Error('forbidden', __('You do not have permission to update global styles.', 'pagelayer'), array('status' => 403)); | |
| 3226 | + } | |
| 3162 | 3227 | self::maybe_update_global_styles($input); |
| 3163 | 3228 | return array('success' => true); |
| 3164 | 3229 | } |
| 3165 | 3230 | |
| @@ -3662,8 +3727,12 @@ | ||
| 3662 | 3727 | unset($node); |
| 3663 | 3728 | } |
| 3664 | 3729 | |
| 3665 | 3730 | public static function execute_create_template($input) { |
| 3731 | + if (!current_user_can('edit_theme_options')) { | |
| 3732 | + return new \WP_Error('forbidden', __('You are not allowed to create theme templates.', 'pagelayer'), array('status' => 403)); | |
| 3733 | + } | |
| 3734 | + | |
| 3666 | 3735 | $title = isset($input['title']) ? sanitize_text_field($input['title']) : ''; |
| 3667 | 3736 | $type = isset($input['type']) ? sanitize_text_field($input['type']) : 'general'; |
| 3668 | 3737 | $p_data = isset($input['pagelayer_data']) && is_array($input['pagelayer_data']) ? $input['pagelayer_data'] : array(); |
| 3669 | 3738 | |
| @@ -3769,8 +3838,12 @@ | ||
| 3769 | 3838 | if (!$template_id || get_post_type($template_id) !== 'pagelayer-template') { |
| 3770 | 3839 | return new \WP_Error('invalid_template', __('Template not found.', 'pagelayer')); |
| 3771 | 3840 | } |
| 3772 | 3841 | |
| 3842 | + if (!current_user_can('edit_theme_options') || !current_user_can('edit_post', $template_id)) { | |
| 3843 | + return new \WP_Error('forbidden', __('You are not allowed to edit this template.', 'pagelayer'), array('status' => 403)); | |
| 3844 | + } | |
| 3845 | + | |
| 3773 | 3846 | $type = isset($input['type']) |
| 3774 | 3847 | ? sanitize_text_field($input['type']) |
| 3775 | 3848 | : (string) get_post_meta($template_id, 'pagelayer_template_type', true); |
| 3776 | 3849 | |
| @@ -3815,8 +3888,12 @@ | ||
| 3815 | 3888 | if (!$template_id || get_post_type($template_id) !== 'pagelayer-template') { |
| 3816 | 3889 | return new \WP_Error('invalid_template', __('Template not found.', 'pagelayer')); |
| 3817 | 3890 | } |
| 3818 | 3891 | |
| 3892 | + if (!current_user_can('edit_theme_options') || !current_user_can('delete_post', $template_id)) { | |
| 3893 | + return new \WP_Error('forbidden', __('You are not allowed to delete this template.', 'pagelayer'), array('status' => 403)); | |
| 3894 | + } | |
| 3895 | + | |
| 3819 | 3896 | $res = wp_delete_post($template_id, true); |
| 3820 | 3897 | return array('success' => (bool)$res); |
| 3821 | 3898 | } |
| 3822 | 3899 | |
| @@ -3952,8 +4029,12 @@ | ||
| 3952 | 4029 | ); |
| 3953 | 4030 | } |
| 3954 | 4031 | |
| 3955 | 4032 | public static function execute_create_menu($input) { |
| 4033 | + if (!current_user_can('manage_options') && !current_user_can('edit_theme_options')) { | |
| 4034 | + return new \WP_Error('forbidden', __('You are not allowed to manage navigation menus.', 'pagelayer'), array('status' => 403)); | |
| 4035 | + } | |
| 4036 | + | |
| 3956 | 4037 | $name = isset($input['name']) ? sanitize_text_field($input['name']) : ''; |
| 3957 | 4038 | if ($name === '') { |
| 3958 | 4039 | return new \WP_Error('missing_name', __('A menu name is required.', 'pagelayer')); |
| 3959 | 4040 | } |
| @@ -4176,8 +4257,12 @@ | ||
| 4176 | 4257 | return self::serialize_layout_to_blocks(array($node)); |
| 4177 | 4258 | } |
| 4178 | 4259 | |
| 4179 | 4260 | public static function execute_delete_menu($input) { |
| 4261 | + if (!current_user_can('manage_options') && !current_user_can('edit_theme_options')) { | |
| 4262 | + return new \WP_Error('forbidden', __('You are not allowed to delete navigation menus.', 'pagelayer'), array('status' => 403)); | |
| 4263 | + } | |
| 4264 | + | |
| 4180 | 4265 | $menu = self::resolve_menu(isset($input['menu']) ? sanitize_text_field($input['menu']) : ''); |
| 4181 | 4266 | if (!$menu) { |
| 4182 | 4267 | return new \WP_Error('invalid_menu', __('Navigation menu not found.', 'pagelayer')); |
| 4183 | 4268 | } |
| @@ -4204,8 +4289,16 @@ | ||
| 4204 | 4289 | if (!$post_id || get_post_type($post_id) !== 'page') { |
| 4205 | 4290 | return new \WP_Error('invalid_page', __('Page not found.', 'pagelayer')); |
| 4206 | 4291 | } |
| 4207 | 4292 | |
| 4293 | + if (!current_user_can('edit_post', $post_id)) { | |
| 4294 | + return new \WP_Error('forbidden', __('You are not allowed to edit this page.', 'pagelayer'), array('status' => 403)); | |
| 4295 | + } | |
| 4296 | + | |
| 4297 | + if (isset($input['status']) && $input['status'] === 'publish' && !current_user_can('publish_pages')) { | |
| 4298 | + return new \WP_Error('forbidden', __('You are not allowed to publish pages.', 'pagelayer'), array('status' => 403)); | |
| 4299 | + } | |
| 4300 | + | |
| 4208 | 4301 | if (isset($input['title'])) { |
| 4209 | 4302 | wp_update_post(array('ID' => $post_id, 'post_title' => sanitize_text_field($input['title']))); |
| 4210 | 4303 | } |
| 4211 | 4304 | if (isset($input['status'])) { |
| @@ -4306,12 +4399,18 @@ | ||
| 4306 | 4399 | } |
| 4307 | 4400 | |
| 4308 | 4401 | public static function execute_publish_page($input) { |
| 4309 | 4402 | $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; |
| 4310 | - if (!$post_id || !get_post($post_id)) { | |
| 4403 | + $post = get_post($post_id); | |
| 4404 | + if (!$post_id || !$post) { | |
| 4311 | 4405 | return new \WP_Error('invalid_post', __('Post or page not found.', 'pagelayer')); |
| 4312 | 4406 | } |
| 4313 | 4407 | |
| 4408 | + $cap = ($post->post_type === 'page') ? 'publish_pages' : 'publish_posts'; | |
| 4409 | + if (!current_user_can('edit_post', $post_id) || !current_user_can($cap)) { | |
| 4410 | + return new \WP_Error('forbidden', __('You are not allowed to publish this item.', 'pagelayer'), array('status' => 403)); | |
| 4411 | + } | |
| 4412 | + | |
| 4314 | 4413 | wp_update_post(array('ID' => $post_id, 'post_status' => 'publish')); |
| 4315 | 4414 | return array('success' => true, 'url' => get_permalink($post_id)); |
| 4316 | 4415 | } |
| 4317 | 4416 | |
| @@ -4321,8 +4420,17 @@ | ||
| 4321 | 4420 | if (!$post) { |
| 4322 | 4421 | return new \WP_Error('invalid_post', __('Post or page not found.', 'pagelayer')); |
| 4323 | 4422 | } |
| 4324 | 4423 | |
| 4424 | + if (!current_user_can('edit_post', $post_id)) { | |
| 4425 | + return new \WP_Error('forbidden', __('You are not allowed to duplicate this item.', 'pagelayer'), array('status' => 403)); | |
| 4426 | + } | |
| 4427 | + | |
| 4428 | + $cap = ($post->post_type === 'page') ? 'edit_pages' : 'edit_posts'; | |
| 4429 | + if (!current_user_can($cap)) { | |
| 4430 | + return new \WP_Error('forbidden', __('You are not allowed to create this item.', 'pagelayer'), array('status' => 403)); | |
| 4431 | + } | |
| 4432 | + | |
| 4325 | 4433 | $title = isset($input['title']) ? sanitize_text_field($input['title']) : $post->post_title . ' (Copy)'; |
| 4326 | 4434 | |
| 4327 | 4435 | $new_id = wp_insert_post(array( |
| 4328 | 4436 | 'post_title' => $title, |
| @@ -4372,8 +4480,12 @@ | ||
| 4372 | 4480 | if (!$post_id || get_post_type($post_id) !== 'page') { |
| 4373 | 4481 | return new \WP_Error('invalid_page', __('Page not found.', 'pagelayer')); |
| 4374 | 4482 | } |
| 4375 | 4483 | |
| 4484 | + if (!current_user_can('delete_post', $post_id)) { | |
| 4485 | + return new \WP_Error('forbidden', __('You are not allowed to delete this page.', 'pagelayer'), array('status' => 403)); | |
| 4486 | + } | |
| 4487 | + | |
| 4376 | 4488 | $res = wp_delete_post($post_id, $force); |
| 4377 | 4489 | return array('success' => (bool)$res); |
| 4378 | 4490 | } |
| 4379 | 4491 | |
| @@ -5158,8 +5270,16 @@ | ||
| 5158 | 5270 | if (!$post || $post->post_type !== 'post') { |
| 5159 | 5271 | return new \WP_Error('invalid_post', __('Blog post not found.', 'pagelayer')); |
| 5160 | 5272 | } |
| 5161 | 5273 | |
| 5274 | + if (!current_user_can('edit_post', $post_id)) { | |
| 5275 | + return new \WP_Error('forbidden', __('You are not allowed to edit this post.', 'pagelayer'), array('status' => 403)); | |
| 5276 | + } | |
| 5277 | + | |
| 5278 | + if (isset($input['status']) && $input['status'] === 'publish' && !current_user_can('publish_posts')) { | |
| 5279 | + return new \WP_Error('forbidden', __('You are not allowed to publish posts.', 'pagelayer'), array('status' => 403)); | |
| 5280 | + } | |
| 5281 | + | |
| 5162 | 5282 | if (isset($input['title'])) { |
| 5163 | 5283 | wp_update_post(array('ID' => $post_id, 'post_title' => sanitize_text_field($input['title']))); |
| 5164 | 5284 | } |
| 5165 | 5285 | if (isset($input['status'])) { |
| @@ -5291,8 +5411,12 @@ | ||
| 5291 | 5411 | if (!$post_id || get_post_type($post_id) !== 'post') { |
| 5292 | 5412 | return new \WP_Error('invalid_post', __('Blog post not found.', 'pagelayer')); |
| 5293 | 5413 | } |
| 5294 | 5414 | |
| 5415 | + if (!current_user_can('delete_post', $post_id)) { | |
| 5416 | + return new \WP_Error('forbidden', __('You are not allowed to delete this post.', 'pagelayer'), array('status' => 403)); | |
| 5417 | + } | |
| 5418 | + | |
| 5295 | 5419 | $res = wp_delete_post($post_id, $force); |
| 5296 | 5420 | return array('success' => (bool)$res); |
| 5297 | 5421 | } |
| 5298 | 5422 | |
| @@ -5305,8 +5429,24 @@ | ||
| 5305 | 5429 | $title = isset($input['title']) ? sanitize_text_field($input['title']) : ''; |
| 5306 | 5430 | $post_type = isset($input['post_type']) ? sanitize_text_field($input['post_type']) : 'page'; |
| 5307 | 5431 | $status = isset($input['status']) ? sanitize_text_field($input['status']) : 'publish'; |
| 5308 | 5432 | |
| 5433 | + if ($post_type === 'page') { | |
| 5434 | + if (!current_user_can('edit_pages')) { | |
| 5435 | + return new \WP_Error('forbidden', __('You are not allowed to create pages.', 'pagelayer'), array('status' => 403)); | |
| 5436 | + } | |
| 5437 | + if ($status === 'publish' && !current_user_can('publish_pages')) { | |
| 5438 | + $status = 'draft'; | |
| 5439 | + } | |
| 5440 | + } else { | |
| 5441 | + if (!current_user_can('edit_posts')) { | |
| 5442 | + return new \WP_Error('forbidden', __('You are not allowed to create posts.', 'pagelayer'), array('status' => 403)); | |
| 5443 | + } | |
| 5444 | + if ($status === 'publish' && !current_user_can('publish_posts')) { | |
| 5445 | + $status = 'draft'; | |
| 5446 | + } | |
| 5447 | + } | |
| 5448 | + | |
| 5309 | 5449 | if (empty($title)) { |
| 5310 | 5450 | return new \WP_Error('missing_title', __('A title is required.', 'pagelayer')); |
| 5311 | 5451 | } |
| 5312 | 5452 | |
| @@ -5395,13 +5535,13 @@ | ||
| 5395 | 5535 | set_post_thumbnail($post_id, $img_id); |
| 5396 | 5536 | } |
| 5397 | 5537 | } |
| 5398 | 5538 | |
| 5399 | - if (!empty($input['is_homepage'])) { | |
| 5539 | + if (!empty($input['is_homepage']) && current_user_can('manage_options')) { | |
| 5400 | 5540 | update_option('show_on_front', 'page'); |
| 5401 | 5541 | update_option('page_on_front', $post_id); |
| 5402 | 5542 | } |
| 5403 | - if (!empty($input['is_posts_page'])) { | |
| 5543 | + if (!empty($input['is_posts_page']) && current_user_can('manage_options')) { | |
| 5404 | 5544 | update_option('page_for_posts', $post_id); |
| 5405 | 5545 | } |
| 5406 | 5546 | |
| 5407 | 5547 | return array( |
| @@ -5753,8 +5893,11 @@ | ||
| 5753 | 5893 | $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; |
| 5754 | 5894 | if (!$post_id || !get_post($post_id)) { |
| 5755 | 5895 | return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); |
| 5756 | 5896 | } |
| 5897 | + if (!current_user_can('edit_post', $post_id)) { | |
| 5898 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 5899 | + } | |
| 5757 | 5900 | |
| 5758 | 5901 | if (!isset($input['pagelayer_data']) || !is_array($input['pagelayer_data'])) { |
| 5759 | 5902 | return new \WP_Error('missing_pagelayer_data', __('pagelayer_data is required.', 'pagelayer')); |
| 5760 | 5903 | } |
| @@ -5803,8 +5946,11 @@ | ||
| 5803 | 5946 | $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; |
| 5804 | 5947 | if (!$post_id || !get_post($post_id)) { |
| 5805 | 5948 | return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); |
| 5806 | 5949 | } |
| 5950 | + if (!current_user_can('edit_post', $post_id)) { | |
| 5951 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 5952 | + } | |
| 5807 | 5953 | |
| 5808 | 5954 | if (isset($input['pagelayer_data']) && is_array($input['pagelayer_data'])) { |
| 5809 | 5955 | $inline_css = self::inline_css_gate($input['pagelayer_data']); |
| 5810 | 5956 | if (is_wp_error($inline_css)) { |
| @@ -5836,8 +5982,11 @@ | ||
| 5836 | 5982 | $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; |
| 5837 | 5983 | if (!$post_id || !get_post($post_id)) { |
| 5838 | 5984 | return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); |
| 5839 | 5985 | } |
| 5986 | + if (!current_user_can('edit_post', $post_id)) { | |
| 5987 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 5988 | + } | |
| 5840 | 5989 | |
| 5841 | 5990 | $styles = isset($input['styles']) && is_array($input['styles']) ? $input['styles'] : array(); |
| 5842 | 5991 | if (empty($styles)) { |
| 5843 | 5992 | return new \WP_Error('no_styles', __('No style changes provided.', 'pagelayer')); |
| @@ -5982,9 +6131,17 @@ | ||
| 5982 | 6131 | ); |
| 5983 | 6132 | } |
| 5984 | 6133 | |
| 5985 | 6134 | public static function execute_find_elements($input) { |
| 5986 | - $post_id = (int) $input['post_id']; | |
| 6135 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6136 | + $post = get_post($post_id); | |
| 6137 | + if (!$post) { | |
| 6138 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6139 | + } | |
| 6140 | + if ('publish' !== $post->post_status && !current_user_can('edit_post', $post_id)) { | |
| 6141 | + return new \WP_Error('unauthorized', __('You do not have permission to view this post.', 'pagelayer')); | |
| 6142 | + } | |
| 6143 | + | |
| 5987 | 6144 | $data = get_post_meta($post_id, 'pagelayer-data', true); |
| 5988 | 6145 | if (!is_array($data)) { |
| 5989 | 6146 | return array('elements' => array()); |
| 5990 | 6147 | } |
| @@ -6252,9 +6409,17 @@ | ||
| 6252 | 6409 | return $id === null ? $ref : $id; |
| 6253 | 6410 | } |
| 6254 | 6411 | |
| 6255 | 6412 | public static function execute_navigator($input) { |
| 6256 | - $post_id = (int) $input['post_id']; | |
| 6413 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6414 | + $post = get_post($post_id); | |
| 6415 | + if (!$post) { | |
| 6416 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6417 | + } | |
| 6418 | + if ('publish' !== $post->post_status && !current_user_can('edit_post', $post_id)) { | |
| 6419 | + return new \WP_Error('unauthorized', __('You do not have permission to view this post.', 'pagelayer')); | |
| 6420 | + } | |
| 6421 | + | |
| 6257 | 6422 | $data = get_post_meta($post_id, 'pagelayer-data', true); |
| 6258 | 6423 | if (!is_array($data)) { |
| 6259 | 6424 | return array('outline' => array()); |
| 6260 | 6425 | } |
| @@ -6266,9 +6431,16 @@ | ||
| 6266 | 6431 | ); |
| 6267 | 6432 | } |
| 6268 | 6433 | |
| 6269 | 6434 | public static function execute_update_element($input) { |
| 6270 | - $post_id = (int) $input['post_id']; | |
| 6435 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6436 | + if (!$post_id || !get_post($post_id)) { | |
| 6437 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6438 | + } | |
| 6439 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6440 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6441 | + } | |
| 6442 | + | |
| 6271 | 6443 | $element_id = self::resolve_element_ref($post_id, sanitize_text_field($input['element_id'])); |
| 6272 | 6444 | $data = get_post_meta($post_id, 'pagelayer-data', true); |
| 6273 | 6445 | if (!is_array($data)) { |
| 6274 | 6446 | return new \WP_Error('no_data', __('Page has no Pagelayer data.', 'pagelayer')); |
| @@ -6345,9 +6517,16 @@ | ||
| 6345 | 6517 | return array('success' => true); |
| 6346 | 6518 | } |
| 6347 | 6519 | |
| 6348 | 6520 | public static function execute_add_element($input) { |
| 6349 | - $post_id = (int) $input['post_id']; | |
| 6521 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6522 | + if (!$post_id || !get_post($post_id)) { | |
| 6523 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6524 | + } | |
| 6525 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6526 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6527 | + } | |
| 6528 | + | |
| 6350 | 6529 | $parent_id = isset($input['parent_id']) ? self::resolve_element_ref($post_id, sanitize_text_field($input['parent_id'])) : ''; |
| 6351 | 6530 | |
| 6352 | 6531 | $inline_css_found = array(); |
| 6353 | 6532 | self::scrub_node_inline_css($input['element'], $inline_css_found); |
| @@ -6418,9 +6597,16 @@ | ||
| 6418 | 6597 | return new \WP_Error('parent_not_found', __('Parent element not found.', 'pagelayer')); |
| 6419 | 6598 | } |
| 6420 | 6599 | |
| 6421 | 6600 | public static function execute_delete_element($input) { |
| 6422 | - $post_id = (int) $input['post_id']; | |
| 6601 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6602 | + if (!$post_id || !get_post($post_id)) { | |
| 6603 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6604 | + } | |
| 6605 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6606 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6607 | + } | |
| 6608 | + | |
| 6423 | 6609 | $element_id = self::resolve_element_ref($post_id, sanitize_text_field($input['element_id'])); |
| 6424 | 6610 | $data = get_post_meta($post_id, 'pagelayer-data', true); |
| 6425 | 6611 | if (!is_array($data)) { |
| 6426 | 6612 | return new \WP_Error('no_data', __('Page has no Pagelayer data.', 'pagelayer')); |
| @@ -6470,9 +6656,16 @@ | ||
| 6470 | 6656 | return new \WP_Error('not_found', __('Element not found.', 'pagelayer')); |
| 6471 | 6657 | } |
| 6472 | 6658 | |
| 6473 | 6659 | public static function execute_move_element($input) { |
| 6474 | - $post_id = (int) $input['post_id']; | |
| 6660 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6661 | + if (!$post_id || !get_post($post_id)) { | |
| 6662 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6663 | + } | |
| 6664 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6665 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6666 | + } | |
| 6667 | + | |
| 6475 | 6668 | $element_id = self::resolve_element_ref($post_id, sanitize_text_field($input['element_id'])); |
| 6476 | 6669 | $parent_id = isset($input['parent_id']) ? self::resolve_element_ref($post_id, sanitize_text_field($input['parent_id'])) : ''; |
| 6477 | 6670 | $index = isset($input['index']) ? (int) $input['index'] : -1; |
| 6478 | 6671 | |
| @@ -6567,9 +6760,16 @@ | ||
| 6567 | 6760 | return new \WP_Error('parent_not_found', __('Target parent not found.', 'pagelayer')); |
| 6568 | 6761 | } |
| 6569 | 6762 | |
| 6570 | 6763 | public static function execute_duplicate_element($input) { |
| 6571 | - $post_id = (int) $input['post_id']; | |
| 6764 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6765 | + if (!$post_id || !get_post($post_id)) { | |
| 6766 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6767 | + } | |
| 6768 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6769 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6770 | + } | |
| 6771 | + | |
| 6572 | 6772 | $element_id = self::resolve_element_ref($post_id, sanitize_text_field($input['element_id'])); |
| 6573 | 6773 | $data = get_post_meta($post_id, 'pagelayer-data', true); |
| 6574 | 6774 | if (!is_array($data)) { |
| 6575 | 6775 | return new \WP_Error('no_data', __('Page has no Pagelayer data.', 'pagelayer')); |
| @@ -6640,15 +6840,18 @@ | ||
| 6640 | 6840 | return new \WP_Error('not_found', __('Element to duplicate not found.', 'pagelayer')); |
| 6641 | 6841 | } |
| 6642 | 6842 | |
| 6643 | 6843 | public static function execute_begin_transaction($input) { |
| 6644 | - $post_id = (int) $input['post_id']; | |
| 6645 | - $data = get_post_meta($post_id, 'pagelayer-data', true); | |
| 6844 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6646 | 6845 | $post = get_post($post_id); |
| 6647 | 6846 | if (!$post) { |
| 6648 | 6847 | return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); |
| 6649 | 6848 | } |
| 6849 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6850 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6851 | + } | |
| 6650 | 6852 | |
| 6853 | + $data = get_post_meta($post_id, 'pagelayer-data', true); | |
| 6651 | 6854 | $backup = array( |
| 6652 | 6855 | 'data' => $data, |
| 6653 | 6856 | 'content' => $post->post_content |
| 6654 | 6857 | ); |
| @@ -6656,15 +6859,29 @@ | ||
| 6656 | 6859 | return array('success' => true); |
| 6657 | 6860 | } |
| 6658 | 6861 | |
| 6659 | 6862 | public static function execute_commit_transaction($input) { |
| 6660 | - $post_id = (int) $input['post_id']; | |
| 6863 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6864 | + if (!$post_id || !get_post($post_id)) { | |
| 6865 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6866 | + } | |
| 6867 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6868 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6869 | + } | |
| 6870 | + | |
| 6661 | 6871 | delete_option('pagelayer_tx_backup_' . $post_id); |
| 6662 | 6872 | return array('success' => true); |
| 6663 | 6873 | } |
| 6664 | 6874 | |
| 6665 | 6875 | public static function execute_rollback_transaction($input) { |
| 6666 | - $post_id = (int) $input['post_id']; | |
| 6876 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6877 | + if (!$post_id || !get_post($post_id)) { | |
| 6878 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6879 | + } | |
| 6880 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6881 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6882 | + } | |
| 6883 | + | |
| 6667 | 6884 | $backup = get_option('pagelayer_tx_backup_' . $post_id); |
| 6668 | 6885 | if (!$backup) { |
| 6669 | 6886 | return new \WP_Error('no_backup', __('No active transaction to rollback.', 'pagelayer')); |
| 6670 | 6887 | } |
| @@ -6678,10 +6895,21 @@ | ||
| 6678 | 6895 | return array('success' => true); |
| 6679 | 6896 | } |
| 6680 | 6897 | |
| 6681 | 6898 | public static function execute_save_template($input) { |
| 6899 | + if (!current_user_can('edit_theme_options') && !current_user_can('manage_options')) { | |
| 6900 | + return new \WP_Error('unauthorized', __('You do not have permission to save templates.', 'pagelayer')); | |
| 6901 | + } | |
| 6902 | + | |
| 6682 | 6903 | $template_name = sanitize_text_field($input['name']); |
| 6683 | - $post_id = (int) $input['post_id']; | |
| 6904 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6905 | + if (!$post_id || !get_post($post_id)) { | |
| 6906 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6907 | + } | |
| 6908 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6909 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6910 | + } | |
| 6911 | + | |
| 6684 | 6912 | $element_id = isset($input['element_id']) ? self::resolve_element_ref($post_id, sanitize_text_field($input['element_id'])) : ''; |
| 6685 | 6913 | |
| 6686 | 6914 | $data = get_post_meta($post_id, 'pagelayer-data', true); |
| 6687 | 6915 | if (!is_array($data)) { |
| @@ -6723,10 +6951,17 @@ | ||
| 6723 | 6951 | return array('templates' => array_keys($library)); |
| 6724 | 6952 | } |
| 6725 | 6953 | |
| 6726 | 6954 | public static function execute_insert_template($input) { |
| 6955 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6956 | + if (!$post_id || !get_post($post_id)) { | |
| 6957 | + return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); | |
| 6958 | + } | |
| 6959 | + if (!current_user_can('edit_post', $post_id)) { | |
| 6960 | + return new \WP_Error('unauthorized', __('You do not have permission to edit this post.', 'pagelayer')); | |
| 6961 | + } | |
| 6962 | + | |
| 6727 | 6963 | $template_name = sanitize_text_field($input['name']); |
| 6728 | - $post_id = (int) $input['post_id']; | |
| 6729 | 6964 | $parent_id = isset($input['parent_id']) ? sanitize_text_field($input['parent_id']) : ''; |
| 6730 | 6965 | $index = isset($input['index']) ? (int) $input['index'] : -1; |
| 6731 | 6966 | |
| 6732 | 6967 | $library = get_option('pagelayer_template_library', array()); |
| @@ -6803,9 +7038,17 @@ | ||
| 6803 | 7038 | return new \WP_Error('parent_not_found', __('Parent not found.', 'pagelayer')); |
| 6804 | 7039 | } |
| 6805 | 7040 | |
| 6806 | 7041 | public static function execute_upload_media($input) { |
| 7042 | + if (!current_user_can('upload_files')) { | |
| 7043 | + return new \WP_Error('unauthorized', __('You do not have permission to upload files.', 'pagelayer')); | |
| 7044 | + } | |
| 7045 | + | |
| 6807 | 7046 | $url = esc_url_raw($input['url']); |
| 7047 | + if (!wp_http_validate_url($url)) { | |
| 7048 | + return new \WP_Error('invalid_url', __('Invalid media URL provided.', 'pagelayer')); | |
| 7049 | + } | |
| 7050 | + | |
| 6808 | 7051 | $desc = isset($input['alt_text']) ? sanitize_text_field($input['alt_text']) : ''; |
| 6809 | 7052 | |
| 6810 | 7053 | require_once(ABSPATH . 'wp-admin/includes/image.php'); |
| 6811 | 7054 | require_once(ABSPATH . 'wp-admin/includes/file.php'); |
| @@ -6837,12 +7080,15 @@ | ||
| 6837 | 7080 | ); |
| 6838 | 7081 | } |
| 6839 | 7082 | |
| 6840 | 7083 | public static function execute_get_preview($input) { |
| 6841 | - $post_id = (int) $input['post_id']; | |
| 7084 | + $post_id = isset($input['post_id']) ? (int) $input['post_id'] : 0; | |
| 6842 | 7085 | $post = get_post($post_id); |
| 6843 | 7086 | if (!$post) { |
| 6844 | 7087 | return new \WP_Error('invalid_post', __('Post not found.', 'pagelayer')); |
| 7088 | + } | |
| 7089 | + if ('publish' !== $post->post_status && !current_user_can('edit_post', $post_id)) { | |
| 7090 | + return new \WP_Error('unauthorized', __('You do not have permission to preview this post.', 'pagelayer')); | |
| 6845 | 7091 | } |
| 6846 | 7092 | $url = ('publish' === $post->post_status) |
| 6847 | 7093 | ? get_permalink($post_id) |
| 6848 | 7094 | : get_preview_post_link($post_id); |