├── .gitignore ├── custom-elementor-source.php ├── includes └── source.php └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | phpcs.xml 3 | phpunit.xml 4 | Thumbs.db 5 | wp-cli.local.yml 6 | node_modules/ 7 | *.sql 8 | *.tar.gz 9 | *.zip 10 | -------------------------------------------------------------------------------- /custom-elementor-source.php: -------------------------------------------------------------------------------- 1 | _registered_sources[ $id ] ); 26 | }; 27 | 28 | $unregister_source->call( \Elementor\Plugin::instance()->templates_manager, 'remote'); 29 | \Elementor\Plugin::instance()->templates_manager->register_source( 'Elementor\TemplateLibrary\Source_Custom' ); 30 | }, 15 ); 31 | -------------------------------------------------------------------------------- /includes/source.php: -------------------------------------------------------------------------------- 1 | prepare_template( $template_data ); 103 | } 104 | } 105 | 106 | return $templates; 107 | } 108 | 109 | /** 110 | * Get templates data. 111 | * 112 | * Retrieve the templates data from a remote server. 113 | * 114 | * @access public 115 | * @static 116 | * 117 | * @param bool $force_update Optional. Whether to force the data update or 118 | * not. Default is false. 119 | * 120 | * @return array The templates data. 121 | */ 122 | public static function get_library_data( $force_update = false ) { 123 | self::get_info_data( $force_update ); 124 | 125 | $library_data = get_option( self::LIBRARY_OPTION_KEY ); 126 | 127 | if ( empty( $library_data ) ) { 128 | return []; 129 | } 130 | 131 | return $library_data; 132 | } 133 | 134 | /** 135 | * Get info data. 136 | * 137 | * This function notifies the user of upgrade notices, new templates and contributors. 138 | * 139 | * @access private 140 | * @static 141 | * 142 | * @param bool $force_update Optional. Whether to force the data retrieval or 143 | * not. Default is false. 144 | * 145 | * @return array|false Info data, or false. 146 | */ 147 | private static function get_info_data( $force_update = false ) { 148 | 149 | $elementor_update_timestamp = get_option( '_transient_timeout_elementor_remote_info_api_data_' . ELEMENTOR_VERSION ); 150 | $update_timestamp = get_transient( self::TIMESTAMP_CACHE_KEY ); 151 | 152 | if ( $force_update || ! $update_timestamp || $update_timestamp != $elementor_update_timestamp ) { 153 | $timeout = ( $force_update ) ? 25 : 8; 154 | 155 | $response = wp_remote_get( self::$api_info_url, [ 156 | 'timeout' => $timeout, 157 | 'body' => [ 158 | // Which API version is used. 159 | 'api_version' => ELEMENTOR_VERSION, 160 | // Which language to return. 161 | 'site_lang' => get_bloginfo( 'language' ), 162 | ], 163 | ] ); 164 | 165 | if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { 166 | set_transient( self::TIMESTAMP_CACHE_KEY, [], 2 * HOUR_IN_SECONDS ); 167 | 168 | return false; 169 | } 170 | 171 | $info_data = json_decode( wp_remote_retrieve_body( $response ), true ); 172 | 173 | if ( empty( $info_data ) || ! is_array( $info_data ) ) { 174 | set_transient( self::TIMESTAMP_CACHE_KEY, [], 2 * HOUR_IN_SECONDS ); 175 | 176 | return false; 177 | } 178 | 179 | if ( isset( $info_data['library'] ) ) { 180 | update_option( self::LIBRARY_OPTION_KEY, $info_data['library'], 'no' ); 181 | } 182 | 183 | set_transient( self::TIMESTAMP_CACHE_KEY, $elementor_update_timestamp, 12 * HOUR_IN_SECONDS ); 184 | } 185 | 186 | return $info_data; 187 | } 188 | 189 | /** 190 | * Get remote template. 191 | * 192 | * Retrieve a single remote template from Elementor.com servers. 193 | * 194 | * @access public 195 | * 196 | * @param int $template_id The template ID. 197 | * 198 | * @return array Remote template. 199 | */ 200 | public function get_item( $template_id ) { 201 | $templates = $this->get_items(); 202 | 203 | return $templates[ $template_id ]; 204 | } 205 | 206 | /** 207 | * Save remote template. 208 | * 209 | * Remote template from Elementor.com servers cannot be saved on the 210 | * database as they are retrieved from remote servers. 211 | * 212 | * @access public 213 | * 214 | * @param array $template_data Remote template data. 215 | * 216 | * @return \WP_Error 217 | */ 218 | public function save_item( $template_data ) { 219 | return new \WP_Error( 'invalid_request', 'Cannot save template to a remote source' ); 220 | } 221 | 222 | /** 223 | * Update remote template. 224 | * 225 | * Remote template from Elementor.com servers cannot be updated on the 226 | * database as they are retrieved from remote servers. 227 | * 228 | * @access public 229 | * 230 | * @param array $new_data New template data. 231 | * 232 | * @return \WP_Error 233 | */ 234 | public function update_item( $new_data ) { 235 | return new \WP_Error( 'invalid_request', 'Cannot update template to a remote source' ); 236 | } 237 | 238 | /** 239 | * Delete remote template. 240 | * 241 | * Remote template from Elementor.com servers cannot be deleted from the 242 | * database as they are retrieved from remote servers. 243 | * 244 | * @access public 245 | * 246 | * @param int $template_id The template ID. 247 | * 248 | * @return \WP_Error 249 | */ 250 | public function delete_template( $template_id ) { 251 | return new \WP_Error( 'invalid_request', 'Cannot delete template from a remote source' ); 252 | } 253 | 254 | /** 255 | * Export remote template. 256 | * 257 | * Remote template from Elementor.com servers cannot be exported from the 258 | * database as they are retrieved from remote servers. 259 | * 260 | * @access public 261 | * 262 | * @param int $template_id The template ID. 263 | * 264 | * @return \WP_Error 265 | */ 266 | public function export_template( $template_id ) { 267 | return new \WP_Error( 'invalid_request', 'Cannot export template from a remote source' ); 268 | } 269 | 270 | /** 271 | * Get remote template data. 272 | * 273 | * Retrieve the data of a single remote template from Elementor.com servers. 274 | * 275 | * @access public 276 | * 277 | * @param array $args Custom template arguments. 278 | * @param string $context Optional. The context. Default is `display`. 279 | * 280 | * @return array Remote Template data. 281 | */ 282 | public function get_data( array $args, $context = 'display' ) { 283 | $data = self::get_template_content( $args['template_id'] ); 284 | 285 | if ( is_wp_error( $data ) ) { 286 | return $data; 287 | } 288 | 289 | $data['content'] = $this->replace_elements_ids( $data['content'] ); 290 | $data['content'] = $this->process_export_import_content( $data['content'], 'on_import' ); 291 | 292 | $post_id = $args['editor_post_id']; 293 | $document = Plugin::$instance->documents->get( $post_id ); 294 | if ( $document ) { 295 | $data['content'] = $document->get_elements_raw_data( $data['content'], true ); 296 | } 297 | 298 | return $data; 299 | } 300 | 301 | /** 302 | * Get template content. 303 | * 304 | * Retrieve the templates content received from a remote server. 305 | * 306 | * @access public 307 | * @static 308 | * 309 | * @param int $template_id The template ID. 310 | * 311 | * @return array The template content. 312 | */ 313 | public static function get_template_content( $template_id ) { 314 | $url = sprintf( self::$api_get_template_content_url, $template_id ); 315 | 316 | $body_args = [ 317 | // Which API version is used. 318 | 'api_version' => ELEMENTOR_VERSION, 319 | // Which language to return. 320 | 'site_lang' => get_bloginfo( 'language' ), 321 | ]; 322 | 323 | /** 324 | * API: Template body args. 325 | * 326 | * Filters the body arguments send with the GET request when fetching the content. 327 | * 328 | * @param array $body_args Body arguments. 329 | */ 330 | $body_args = apply_filters( 'elementor/api/get_templates/body_args', $body_args ); 331 | 332 | $response = wp_remote_get( $url, [ 333 | 'timeout' => 40, 334 | 'body' => $body_args, 335 | ] ); 336 | 337 | if ( is_wp_error( $response ) ) { 338 | return $response; 339 | } 340 | 341 | $response_code = (int) wp_remote_retrieve_response_code( $response ); 342 | 343 | if ( 200 !== $response_code ) { 344 | return new \WP_Error( 'response_code_error', sprintf( 'The request returned with a status code of %s.', $response_code ) ); 345 | } 346 | 347 | $template_content = json_decode( wp_remote_retrieve_body( $response ), true ); 348 | 349 | if ( isset( $template_content['error'] ) ) { 350 | return new \WP_Error( 'response_error', $template_content['error'] ); 351 | } 352 | 353 | if ( empty( $template_content['data'] ) && empty( $template_content['content'] ) ) { 354 | return new \WP_Error( 'template_data_error', 'An invalid data was returned.' ); 355 | } 356 | 357 | return $template_content; 358 | } 359 | 360 | /** 361 | * @access private 362 | */ 363 | private function prepare_template( array $template_data ) { 364 | $favorite_templates = $this->get_user_meta( 'favorites' ); 365 | 366 | return [ 367 | 'template_id' => $template_data['id'], 368 | 'source' => $this->get_id(), 369 | 'type' => $template_data['type'], 370 | 'subtype' => $template_data['subtype'], 371 | 'title' => $template_data['title'], 372 | 'thumbnail' => $template_data['thumbnail'], 373 | 'date' => $template_data['tmpl_created'], 374 | 'author' => $template_data['author'], 375 | 'tags' => json_decode( $template_data['tags'] ), 376 | 'isPro' => ( '1' === $template_data['is_pro'] ), 377 | 'popularityIndex' => (int) $template_data['popularity_index'], 378 | 'trendIndex' => (int) $template_data['trend_index'], 379 | 'hasPageSettings' => ( '1' === $template_data['has_page_settings'] ), 380 | 'url' => $template_data['url'], 381 | 'favorite' => ! empty( $favorite_templates[ $template_data['id'] ] ), 382 | ]; 383 | } 384 | } 385 | 386 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # custom-elementor-source 2 | 3 | This plugin is an example of overriding Elementor template library as detail [here](https://dinhtungdu.github.io/create-your-own-elementor-template-library/). This plugin is never meant to use in the production. 4 | 5 | ## Installation 6 | 1. Clone this repository or download it as a zip file. 7 | 2. Extract and move the plugin folder into the `wp-content/plugins` folder. 8 | 3. Edit `includes/source.php` file and replace the following constants' value with yours: 9 | * `$api_info_url`: The URL that returns the information of your library. 10 | * `$api_get_template_content_url`: The URL that returns the detail of specific template id (Notice the `%d`). 11 | 4. Activate the plugin. 12 | 5. Manually sync the library using Elementor > Tools. 13 | 6. Create/edit a post and see the new remote library. 14 | 15 | ## Notes 16 | * This plugin is just an example of how we override Elementor library. You should put the source class in your plugin (recommended) or your theme. 17 | --------------------------------------------------------------------------------