PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / settings / REST / Settings.php

Settings.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at modules/settings/REST/Settings.php

207 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Modules\Settings\REST;
4
5 use Templately\Admin\API\BaseAPI;
6 use Templately\Modules\ElementorKitSettings\Utils\KitApplier;
7 use Templately\Utils\Helper;
8 use Templately\Utils\Response\FatalGuard;
9
10 use WP_REST_Request;
11
12 class Settings extends BaseAPI {
13 private $endpoint = 'settings';
14
15
16
17 /**
18 * @param $request WP_REST_Request for getting all route request in time.
19 *
20 * @return WP_Error|boolean
21 */
22 public function permission_check( WP_REST_Request $request ) {
23 $this->request = $request;
24 if(!current_user_can('manage_options')){
25 return false;
26 }
27 return true;
28 }
29
30 public function register_routes() {
31 $this->post( $this->endpoint . '/update', [$this, 'update_settings'] );
32 }
33
34 public function update_settings() {
35 // A PHP fatal raised by a builder further down this method (see contain())
36 // otherwise reaches the browser as WP core's "critical error" page, which the
37 // settings screen cannot parse. Armed so it comes back as the 043 envelope.
38 FatalGuard::arm( 'settings/update' );
39
40 $_settings = $this->request->get_param('settings');
41 $platform = $_settings['platform'] ?? '';
42 $siteTitle = $_settings['siteTitle'] ?? '';
43 $siteTagline = $_settings['siteTagline'] ?? '';
44 $customCSS = $_settings['customCSS'] ?? '';
45
46 $failed = [];
47
48 // Written one at a time, each contained. `blogname` / `blogdescription` are the
49 // two options Elementor hooks (it syncs both into the active kit), so on a site
50 // with a broken kit the FIRST write used to throw and take the whole save with
51 // it — the tagline, the custom CSS and every colour/typography assignment below
52 // were silently never written. See contain().
53 foreach ( [
54 'blogname' => $siteTitle,
55 'blogdescription' => $siteTagline,
56 'templately_custom_css' => $customCSS,
57 ] as $key => $value ) {
58 if ( ! $this->contain( "option:{$key}", function () use ( $key, $value ) {
59 update_option( $key, $value );
60 } ) ) {
61 $failed[] = $key;
62 }
63 }
64
65 if ( ! $this->contain( 'logo', [ $this, 'update_logo' ] ) ) {
66 $failed[] = 'logo';
67 }
68
69 if($platform === 'elementor'){
70 if(class_exists('Elementor\Plugin')){
71 if ( ! $this->contain( 'elementor-kit', function () use ( $_settings ) {
72 KitApplier::apply($_settings);
73 } ) ) {
74 $failed[] = 'kit';
75 }
76 }
77 }
78 else if($platform === 'gutenberg'){
79 if ( ! $this->contain( 'gutenberg-settings', function () use ( $_settings ) {
80 $this->update_gutenberg_settings($_settings);
81 } ) ) {
82 $failed[] = 'global-styles';
83 }
84 }
85
86 $response = [ 'message' => 'Settings updated successfully' ];
87
88 // Named so the client can tell the user WHICH parts did not stick. An empty
89 // list is omitted, so the shape is unchanged for the ordinary save.
90 if ( ! empty( $failed ) ) {
91 $response['failed'] = $failed;
92 }
93
94 return $this->success( $response );
95 }
96
97 /**
98 * Run one settings write, contained.
99 *
100 * Every write below reaches third-party code: `update_option()` fires that option's
101 * hooks (Elementor listens on `update_option_blogname`/`update_option_blogdescription`
102 * and writes the value into the ACTIVE KIT), and the kit/global-style writers call
103 * into the builder directly. On a site whose Elementor kit post is gone — a bad
104 * restore, a deleted kit, a half-finished migration — that path throws
105 * `Exception( 'Invalid post.' )` from Elementor's page settings manager: `Kit`
106 * documents for a missing post carry an empty `WP_Post`, so `save_settings()` asks
107 * `ajax_before_save_settings()` to validate post ID `0`.
108 *
109 * Uncontained, an exception in a REST callback is an uncaught PHP fatal — WP does not
110 * catch these during dispatch, and `RestEnvelope` runs on `rest_post_dispatch`, which
111 * never sees it. The user got a raw 500 with nothing saved, from the very first write.
112 *
113 * Each write is therefore independent: a failure is logged with its section name,
114 * reported in the response's `failed` list, and the remaining sections still run.
115 * `\Throwable` rather than `Exception` because the same code raises PHP `\Error` too
116 * (an add-on referencing a class Elementor has since removed).
117 *
118 * @param string $section Log/label for the write ("option:blogname", "logo", …).
119 * @param callable $write The write itself.
120 *
121 * @return bool True when it completed without raising.
122 */
123 private function contain( $section, callable $write ): bool {
124 try {
125 $write();
126 return true;
127 } catch ( \Throwable $e ) {
128 Helper::log(
129 sprintf( 'Settings update failed for "%s": %s', $section, $e->getMessage() ),
130 'settings',
131 'error'
132 );
133
134 return false;
135 }
136 }
137
138 public function update_logo() {
139 $_settings = $this->request->get_param('settings');
140 $logo = $_settings['logoImage'] ?? [];
141
142 if(!empty($logo['id'])){
143 set_theme_mod( 'custom_logo', $logo['id'] );
144 update_option( 'site_logo', $logo['id'] );
145
146 // Update Elementor kit if Elementor is enabled
147 if(class_exists('Elementor\Plugin')){
148 $kit = \Elementor\Plugin::$instance->kits_manager->get_active_kit();
149 // `get_active_kit()` NEVER returns falsy — a site with no valid kit gets
150 // an empty Kit instance (post id 0) whose update_settings() throws
151 // `Invalid post.`. get_id() is the check that actually means "real kit".
152 if($kit && $kit->get_id()){
153 $settings = $kit->get_settings();
154 $settings['site_logo'] = $logo;
155 $kit->update_settings($settings);
156 }
157 }
158 }
159 else{
160 remove_theme_mod( 'custom_logo' );
161 delete_option( 'site_logo' );
162
163 // Remove logo from Elementor kit if Elementor is enabled
164 if(class_exists('Elementor\Plugin')){
165 $kit = \Elementor\Plugin::$instance->kits_manager->get_active_kit();
166 if($kit && $kit->get_id()){ // see the note on the branch above
167 $settings = $kit->get_settings();
168 unset($settings['site_logo']);
169 $kit->update_settings($settings);
170 }
171 }
172 }
173
174 return true;
175 }
176
177 private function update_gutenberg_settings($_settings){
178 $colors = $_settings['colors'] ?? [];
179 $typography = $_settings['typography'] ?? [];
180 $settings = get_option('eb_global_styles', []);
181 $settings = is_array($settings) ? $settings: [];
182 $settings = array_map(function($item) { return json_decode($item, true); }, $settings);
183
184
185 if(!empty($colors)){
186 if (isset($settings['global_colors'])) {
187 foreach ($settings['global_colors'] as $key => $color) {
188 $settings['global_colors'][$key]['color'] = $colors[$color['var']] ?? $color['color'];
189 }
190 }
191
192 if (isset($settings['custom_colors'])) {
193 foreach ($settings['custom_colors'] as $key => $color) {
194 $settings['custom_colors'][$key]['color'] = $colors[$color['var']] ?? $color['color'];
195 }
196 }
197 }
198
199 if(!empty($typography)){
200 $settings["global_typography"] = $typography;
201 }
202
203 $settings = array_map('json_encode', $settings);
204 update_option('eb_global_styles', $settings);
205 }
206 }
207