├── assets ├── images │ ├── logo.png │ ├── star.png │ ├── logo-alt.png │ ├── neve-logo.png │ ├── placeholder.jpg │ ├── count-animation.png │ ├── neve-upsell-img.png │ ├── guide │ │ ├── welcome-ai.png │ │ ├── welcome-css.png │ │ ├── welcome-pro.png │ │ ├── welcome-finish.png │ │ ├── welcome-logo.png │ │ └── welcome-section.png │ ├── typing-animation.png │ ├── black-friday-banner.png │ ├── dashboard-feedback.png │ └── live-search-thumbnail.png ├── icons │ ├── map-dark.png │ ├── map-night.png │ ├── map-retro.png │ ├── map-silver.png │ ├── map-aubergine.png │ ├── map-standard.png │ ├── featured.svg │ ├── acf.svg │ ├── meta.svg │ ├── author.svg │ ├── user.svg │ └── woo.svg ├── leaflet │ ├── images │ │ ├── layers.png │ │ ├── layers-2x.png │ │ ├── marker-icon.png │ │ ├── marker-icon-2x.png │ │ └── marker-shadow.png │ └── leaflet-gesture-handling.min.css ├── fontawesome │ ├── webfonts │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.ttf │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ └── fa-solid-900.woff2 │ └── LICENSE.txt └── glide │ ├── glide.core.min.css │ └── glide.theme.min.css ├── .browserslistrc ├── docs ├── blocks │ ├── images │ │ ├── save-code.png │ │ ├── save-error.png │ │ ├── save-backend.png │ │ ├── save-frontend.png │ │ ├── save-error-inspector.png │ │ ├── simple-block-react.png │ │ ├── styled-simple-react.png │ │ └── dynamic-simple-block-react.png │ ├── index.md │ └── deprecation.md ├── images │ ├── copy-paste-flow.png │ ├── sticky-triggers-1.png │ ├── template-library.png │ ├── copy-paste-local-storage.png │ └── local-by-flywheel-overview.png ├── knowledge-stash.md ├── index.md ├── adding-patterns.md ├── blocks.md ├── uniit-and-e2e-testing.md ├── otter-family-plugins.md ├── woocommerce-extensions.md ├── live-search.md ├── form-block-workflow.md └── project-structure.md ├── plugins ├── blocks-animation │ ├── assets │ │ └── images │ │ │ └── welcome-notice.png │ ├── readme.txt │ ├── readme.md │ └── blocks-animation.php ├── otter-pro │ └── inc │ │ ├── css │ │ └── blocks │ │ │ ├── class-modal-css.php │ │ │ ├── class-form-file-css.php │ │ │ ├── class-review-comparison-css.php │ │ │ ├── class-business-hours-item-css.php │ │ │ ├── class-form-stripe-field-css.php │ │ │ └── class-business-hours-css.php │ │ ├── render │ │ ├── woocommerce │ │ │ ├── tpl │ │ │ │ └── content-single-product.php │ │ │ ├── class-product-meta-block.php │ │ │ ├── class-product-price-block.php │ │ │ ├── class-product-title-block.php │ │ │ ├── class-product-upsells-block.php │ │ │ ├── class-product-add-to-cart-block.php │ │ │ ├── class-product-short-description-block.php │ │ │ ├── class-product-related-products-block.php │ │ │ ├── class-product-stock-block.php │ │ │ ├── class-product-rating-block.php │ │ │ ├── class-product-tabs-block.php │ │ │ └── class-product-images-block.php │ │ ├── class-add-to-cart-button-block.php │ │ ├── class-form-hidden-block.php │ │ ├── class-form-file-block.php │ │ └── class-form-stripe-block.php │ │ ├── plugins │ │ ├── class-options-settings.php │ │ ├── class-stripe-pro-features.php │ │ ├── class-review-woo-integration.php │ │ ├── class-posts-acf-integration.php │ │ └── class-fonts-module.php │ │ └── server │ │ └── class-posts-acf-server.php ├── blocks-css │ ├── readme.txt │ ├── readme.md │ └── blocks-css.php └── blocks-export-import │ ├── readme.txt │ ├── blocks-export-import.php │ └── readme.md ├── .editorconfig ├── inc ├── render │ ├── class-form-nonce-block.php │ ├── class-about-author-block.php │ ├── class-leaflet-map-block.php │ ├── class-google-map-block.php │ ├── amp │ │ ├── class-slider-block.php │ │ └── class-lottie.block.php │ └── class-masonry-variant.php ├── css │ └── blocks │ │ ├── class-form-multiple-choice-css.php │ │ ├── class-form-textarea-css.php │ │ ├── class-icon-list-item-css.php │ │ ├── class-form-input-css.php │ │ ├── class-google-map-css.php │ │ ├── class-leaflet-map-css.php │ │ ├── class-timeline-item-css.php │ │ ├── class-sharing-icons-css.php │ │ ├── class-slider-css.php │ │ ├── class-core-image-plugin-css.php │ │ ├── class-progress-bar-css.php │ │ └── class-timeline-css.php ├── integrations │ └── interfaces │ │ └── interface-form-subscribe-service.php ├── Tracker.php ├── class-blocks-export-import.php ├── patterns │ ├── large-quote.php │ ├── call-to-action-1.php │ ├── hero-area-with-button.php │ ├── call-to-action-5.php │ ├── call-to-action-4.php │ ├── testimonial-with-inline-image.php │ ├── call-to-action-3.php │ └── image-and-text-over-dark-background.php └── server │ └── class-fse-onboarding-server.php ├── phpcs.xml.dist ├── phpstan.neon ├── tsconfig.json ├── .wp-env.override.json ├── development.php ├── CONTRIBUTING.md ├── webpack.config.pro.js └── webpack.config.plugins.js /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/star.png -------------------------------------------------------------------------------- /assets/icons/map-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/icons/map-dark.png -------------------------------------------------------------------------------- /assets/icons/map-night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/icons/map-night.png -------------------------------------------------------------------------------- /assets/icons/map-retro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/icons/map-retro.png -------------------------------------------------------------------------------- /assets/icons/map-silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/icons/map-silver.png -------------------------------------------------------------------------------- /assets/images/logo-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/logo-alt.png -------------------------------------------------------------------------------- /assets/images/neve-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/neve-logo.png -------------------------------------------------------------------------------- /assets/icons/map-aubergine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/icons/map-aubergine.png -------------------------------------------------------------------------------- /assets/icons/map-standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/icons/map-standard.png -------------------------------------------------------------------------------- /assets/images/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/placeholder.jpg -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | # Browsers that we support 2 | 3 | last 1 version 4 | > 1% 5 | not ie > 0 6 | not ie_mob > 0 7 | not dead -------------------------------------------------------------------------------- /assets/images/count-animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/count-animation.png -------------------------------------------------------------------------------- /assets/images/neve-upsell-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/neve-upsell-img.png -------------------------------------------------------------------------------- /assets/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/leaflet/images/layers.png -------------------------------------------------------------------------------- /docs/blocks/images/save-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/save-code.png -------------------------------------------------------------------------------- /docs/blocks/images/save-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/save-error.png -------------------------------------------------------------------------------- /docs/images/copy-paste-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/images/copy-paste-flow.png -------------------------------------------------------------------------------- /docs/images/sticky-triggers-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/images/sticky-triggers-1.png -------------------------------------------------------------------------------- /docs/images/template-library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/images/template-library.png -------------------------------------------------------------------------------- /assets/images/guide/welcome-ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/guide/welcome-ai.png -------------------------------------------------------------------------------- /assets/images/guide/welcome-css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/guide/welcome-css.png -------------------------------------------------------------------------------- /assets/images/guide/welcome-pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/guide/welcome-pro.png -------------------------------------------------------------------------------- /assets/images/typing-animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/typing-animation.png -------------------------------------------------------------------------------- /assets/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /docs/blocks/images/save-backend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/save-backend.png -------------------------------------------------------------------------------- /assets/images/black-friday-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/black-friday-banner.png -------------------------------------------------------------------------------- /assets/images/dashboard-feedback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/dashboard-feedback.png -------------------------------------------------------------------------------- /assets/images/guide/welcome-finish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/guide/welcome-finish.png -------------------------------------------------------------------------------- /assets/images/guide/welcome-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/guide/welcome-logo.png -------------------------------------------------------------------------------- /assets/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /docs/blocks/images/save-frontend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/save-frontend.png -------------------------------------------------------------------------------- /assets/images/guide/welcome-section.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/guide/welcome-section.png -------------------------------------------------------------------------------- /assets/images/live-search-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/images/live-search-thumbnail.png -------------------------------------------------------------------------------- /assets/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /assets/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /docs/images/copy-paste-local-storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/images/copy-paste-local-storage.png -------------------------------------------------------------------------------- /docs/blocks/images/save-error-inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/save-error-inspector.png -------------------------------------------------------------------------------- /docs/blocks/images/simple-block-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/simple-block-react.png -------------------------------------------------------------------------------- /docs/blocks/images/styled-simple-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/styled-simple-react.png -------------------------------------------------------------------------------- /docs/images/local-by-flywheel-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/images/local-by-flywheel-overview.png -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /assets/fontawesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/assets/fontawesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /docs/blocks/images/dynamic-simple-block-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/docs/blocks/images/dynamic-simple-block-react.png -------------------------------------------------------------------------------- /plugins/blocks-animation/assets/images/welcome-notice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeinwp/otter-blocks/HEAD/plugins/blocks-animation/assets/images/welcome-notice.png -------------------------------------------------------------------------------- /docs/blocks/index.md: -------------------------------------------------------------------------------- 1 | ## Creating Blocks for Otter in Gutenberg 2 | 3 | ### Chapters 4 | - [Your first block](first-block.md) 5 | - [Editor - attributes & edit function](editor.md) 6 | - [Style your block](styling.md) 7 | - [Save](save.md) 8 | - [Style with PHP](style-with-php.md) 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/css/blocks/class-modal-css.php: -------------------------------------------------------------------------------- 1 | A collection of useful information that helps with the knowledge necessary to work with the project. 4 | 5 | | Link | Description | 6 | | :- | :- | 7 | | [WordPress Data Series: Overview and Introduction](https://unfoldingneurons.com/2020/wordpress-data-series-overview-and-introduction) | Great series on exploring Gutenberg data store| 8 | | [4 reasons your z-index isn’t working (and how to fix it)](https://www.freecodecamp.org/news/4-reasons-your-z-index-isnt-working-and-how-to-fix-it-coder-coder-6bc05f103e6c/) | A page which explain some quirk for `z-index` property. `z-index` does not always work on how you expect. When fixing issue related to Modal component, keep in mind the context of `z-index`. | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/woocommerce/tpl/content-single-product.php: -------------------------------------------------------------------------------- 1 | 20 |
> 21 | 22 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /assets/glide/glide.core.min.css: -------------------------------------------------------------------------------- 1 | .glide{position:relative;width:100%;box-sizing:border-box}.glide *{box-sizing:inherit}.glide__track{overflow:hidden}.glide__slides{position:relative;width:100%;list-style:none;backface-visibility:hidden;transform-style:preserve-3d;touch-action:pan-Y;overflow:hidden;padding:0;white-space:nowrap;display:flex;flex-wrap:nowrap;will-change:transform}.glide__slides--dragging{user-select:none}.glide__slide{width:100%;height:100%;flex-shrink:0;white-space:normal;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}.glide__slide a{user-select:none;-webkit-user-drag:none;-moz-user-select:none;-ms-user-select:none}.glide__arrows{-webkit-touch-callout:none;user-select:none}.glide__bullets{-webkit-touch-callout:none;user-select:none}.glide--rtl{direction:rtl} 2 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/woocommerce/class-product-meta-block.php: -------------------------------------------------------------------------------- 1 | '; 26 | if ( isset( $attributes['formId'] ) ) { 27 | $output .= wp_nonce_field( 'form-verification', $attributes['formId'] . '_nonce_field', true, false ); 28 | } else { 29 | $output .= wp_nonce_field( 'form-verification', '_nonce_field', true, false ); 30 | } 31 | $output .= ''; 32 | $output .= ''; 33 | return $output; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /plugins/blocks-css/readme.txt: -------------------------------------------------------------------------------- 1 | === Blocks CSS: CSS Editor for Gutenberg Blocks === 2 | Contributors: themeisle, hardeepasrani 3 | Tags: gutenberg, block, css, css editor, blocks css 4 | Requires at least: 6.2 5 | Tested up to: 6.9 6 | Requires PHP: 5.4 7 | Stable tag: 3.1.4 8 | License: GPLv3 9 | License URI: https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | Blocks CSS allows you add custom CSS to your Blocks straight from the Block Editor (Gutenberg). 12 | 13 | == Description == 14 | 15 | Blocks CSS allows you add custom CSS to your Blocks straight from the Block Editor (Gutenberg). 16 | 17 | It adds a syntax-highlighted CSS Editor where you can add additional CSS to your Gutenberg Blocks to style them the way you want. 18 | 19 | All the code and sources for this plugin are publicly available as part of https://github.com/Codeinwp/otter-blocks. 20 | 21 | == Screenshots == 22 | 23 | 1. CSS Editor 24 | 2. CSS Editor 25 | 26 | == Changelog == 27 | 28 | You can check the changelog [here.](https://github.com/Codeinwp/otter-blocks/blob/master/CHANGELOG.md) 29 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/woocommerce/class-product-rating-block.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | ThemeIsle ruleset 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | vendor/* 28 | assets/* 29 | build/* 30 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/css/blocks/class-form-file-css.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--label-color', 41 | 'value' => 'labelColor', 42 | ), 43 | ), 44 | ) 45 | ); 46 | 47 | $style = $css->generate(); 48 | 49 | return $style; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/woocommerce/class-product-tabs-block.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--label-color', 41 | 'value' => 'labelColor', 42 | ), 43 | ), 44 | ) 45 | ); 46 | 47 | $style = $css->generate(); 48 | 49 | return $style; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/woocommerce/class-product-images-block.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--label-color', 41 | 'value' => 'labelColor', 42 | ), 43 | array( 44 | 'property' => '--input-width', 45 | 'value' => 'inputWidth', 46 | ), 47 | ), 48 | ) 49 | ); 50 | 51 | $style = $css->generate(); 52 | 53 | return $style; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /inc/css/blocks/class-icon-list-item-css.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'properties' => array( 40 | array( 41 | 'property' => '--content-color', 42 | 'value' => 'contentColor', 43 | ), 44 | array( 45 | 'property' => '--icon-color', 46 | 'value' => 'iconColor', 47 | ), 48 | ), 49 | ) 50 | ); 51 | 52 | $style = $css->generate(); 53 | 54 | return $style; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /inc/css/blocks/class-form-input-css.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--label-color', 41 | 'value' => 'labelColor', 42 | ), 43 | array( 44 | 'property' => '--input-width', 45 | 'value' => 'inputWidth', 46 | 'unit' => '%', 47 | ), 48 | ), 49 | ) 50 | ); 51 | 52 | $style = $css->generate(); 53 | 54 | return $style; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /assets/icons/featured.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Gutenberg Blocks 2 | 3 | Otter Blocks 4 | - [Environment installation](environment.md) 5 | - [Handbook](handbook.md) 6 | - [Otter Family Plugins](otter-family-plugins.md) 7 | - [Project Structure](project-structure.md) 8 | - [Creating a new block](creating-blocks.md) 9 | - [Dynamic CSS with PHP](dynamic-css-with-php.md) 10 | - [Adding Patterns](adding-patterns.md) 11 | - [Form Workflow](form-block-workflow.md) 12 | - [Dynamic Conditions and Content](dynamic-conditions-and-content.md) 13 | - [Sticky Block](sticky-blocks.md) 14 | - [Copy/Paste Block Style](copy-paste.md) 15 | - [WooCommerce Extensions](woocommerce-extensions.md) 16 | - [Live Search](live-search.md) 17 | - [Testing Cheatsheet](testing-cheatsheet.md) 18 | - [Testing Checklist](testing-checklist.md) 19 | - [Unit Testing & E2E](uniit-and-e2e-testing.md) 20 | - [Pull Request Best Practices](pull-request-best-practices.md) 21 | - [Coding Best Practices](coding-best-practices.md) 22 | - [Knowledge Stash](knowledge-stash.md) 23 | 24 | Other 25 | 26 | - [Creating Blocks for Gutenberg](blocks/index.md) 27 | - [Blocks Basics](blocks.md) 28 | - [Registration](blocks.md#registration) 29 | - [Server-side Rendering](blocks.md#server-side-rendering) 30 | - [Custom CSS & Google Fonts](blocks.md#custom-css--google-fonts) 31 | -------------------------------------------------------------------------------- /assets/glide/glide.theme.min.css: -------------------------------------------------------------------------------- 1 | .glide__arrow{position:absolute;display:block;top:50%;z-index:2;color:white;text-transform:uppercase;padding:9px 12px;background-color:transparent;border:2px solid rgba(255,255,255,0.5);border-radius:4px;box-shadow:0 0.25em 0.5em 0 rgba(0,0,0,0.1);text-shadow:0 0.25em 0.5em rgba(0,0,0,0.1);opacity:1;cursor:pointer;transition:opacity 150ms ease, border 300ms ease-in-out;transform:translateY(-50%);line-height:1}.glide__arrow:focus{outline:none}.glide__arrow:hover{border-color:white}.glide__arrow--left{left:2em}.glide__arrow--right{right:2em}.glide__arrow--disabled{opacity:0.33}.glide__bullets{position:absolute;z-index:2;bottom:2em;left:50%;display:inline-flex;list-style:none;transform:translateX(-50%)}.glide__bullet{background-color:rgba(255,255,255,0.5);width:9px;height:9px;padding:0;border-radius:50%;border:2px solid transparent;transition:all 300ms ease-in-out;cursor:pointer;line-height:0;box-shadow:0 0.25em 0.5em 0 rgba(0,0,0,0.1);margin:0 0.25em}.glide__bullet:focus{outline:none}.glide__bullet:hover,.glide__bullet:focus{border:2px solid white;background-color:rgba(255,255,255,0.5)}.glide__bullet--active{background-color:white}.glide--swipeable{cursor:grab;cursor:-moz-grab;cursor:-webkit-grab}.glide--dragging{cursor:grabbing;cursor:-moz-grabbing;cursor:-webkit-grabbing} 2 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/css/blocks/class-review-comparison-css.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'selector' => ' .o-review-comparison_buttons a', 40 | 'properties' => array( 41 | array( 42 | 'property' => 'color', 43 | 'value' => 'buttonText', 44 | ), 45 | array( 46 | 'property' => 'background-color', 47 | 'value' => 'buttonColor', 48 | ), 49 | ), 50 | ) 51 | ); 52 | 53 | $style = $css->generate(); 54 | 55 | return $style; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 6 3 | paths: 4 | - %currentWorkingDirectory%/inc 5 | - %currentWorkingDirectory%/plugins/otter-pro 6 | - %currentWorkingDirectory%/plugins/blocks-css/blocks-css.php 7 | - %currentWorkingDirectory%/plugins/blocks-export-import/blocks-export-import.php 8 | - %currentWorkingDirectory%/plugins/blocks-animation/blocks-animation.php 9 | scanDirectories: 10 | - %currentWorkingDirectory%/vendor/stripe/stripe-php 11 | bootstrapFiles: 12 | - %currentWorkingDirectory%/vendor/php-stubs/wordpress-stubs/wordpress-stubs.php 13 | - %currentWorkingDirectory%/vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php 14 | - %currentWorkingDirectory%/vendor/php-stubs/acf-pro-stubs/acf-pro-stubs.php 15 | - %currentWorkingDirectory%/vendor/wptt/webfont-loader/wptt-webfont-loader.php 16 | - %currentWorkingDirectory%/tests/php/static-analysis-stubs/otter.php 17 | - %currentWorkingDirectory%/tests/php/static-analysis-stubs/learndash.php 18 | dynamicConstantNames: 19 | - COOKIEPATH 20 | - COOKIE_DOMAIN 21 | - OTTER_BLOCKS_PRO_SUPPORT 22 | - OTTER_BLOCKS_SHOW_NOTICES 23 | ignoreErrors: 24 | - 25 | identifiers: 26 | - include.fileNotFound 27 | - requireOnce.fileNotFound 28 | includes: 29 | - %currentWorkingDirectory%/vendor/szepeviktor/phpstan-wordpress/extension.neon 30 | - %currentWorkingDirectory%/phpstan-baseline.neon 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, 4 | "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, 5 | "allowJs": true /* Allow javascript files to be compiled. */, 6 | "checkJs": false /* Report errors in .js files. */, 7 | "outDir": "./build" /* Redirect output structure to the directory. */, 8 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, 9 | "strict": true /* Enable all strict type-checking options. */, 10 | "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ 11 | "resolveJsonModule": true, 12 | "jsx": "preserve", 13 | "noUncheckedIndexedAccess": true, 14 | "lib": [ "dom", "esnext" ], 15 | "moduleResolution": "node", 16 | "allowSyntheticDefaultImports": true, 17 | "esModuleInterop": false, 18 | "declaration": true, 19 | "declarationMap": true, 20 | "isolatedModules": true, 21 | "typeRoots": ["./node_modules/@types"] 22 | }, 23 | "include": ["src/**/*" ], 24 | "exclude": [ 25 | "node_modules/**/*", 26 | "build", 27 | "dist", 28 | "assets", 29 | "artifact" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /docs/adding-patterns.md: -------------------------------------------------------------------------------- 1 | # Patterns 2 | 3 | Patterns are big part of Gutenberg ecosystem. In the old days, plugin developer made their own mechanism to add them, but things are now more organized. 4 | 5 | Adding a new pattern for Otter is straight forward. 6 | 7 | - All the patterns are located in `./src/patterns` folder. 8 | - Every pattern is just an array with keys described by the [Gutenberg documentation](https://developer.wordpress.org/block-editor/developers/block-api/block-patterns/). 9 | - After creating a file for it, register it on `./inc/patterns.php` file in `$block_patterns` array. 10 | 11 | ## Mentions 12 | 13 | - The pattern name should be unique. 14 | - Do not use specific theme CSS vars as values for attributes. Like `"color": "var(--neve-custom-color)"`. This will make the pattern unusable on other themes. Patterns should be theme agnostic. Exceptions can be made, but only if it is intended. 15 | - Pay attention to image links. The images should the accessible from internet. If use the image that are in a private network, they can not used by users. 16 | - Always test the pattern. There is chance that you might have doing a small modification without thinking it will affect the code. 17 | - Pay attention to the blocks that you use. You accidentally might use a block that is not available in Otter or Gutenberg. Of course, you can add any block, but if it is external, it must intended. 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/blocks.md: -------------------------------------------------------------------------------- 1 | # Blocks 2 | 3 | Blocks are the fundamental element of the editor. They are the primary way in which plugins and themes can register their own functionality and extend the capabilities of the editor. 4 | 5 | ## Registration 6 | 7 | For each block, you create a new folder in the `blocks` directory. If the block is a structural block, like Testimonial, Service or Pricing, then you can keep them in `blocks/structural` folder instead. 8 | 9 | You need to have an index.js file for the block registration, and rest of the registration process is simple. You can find more info on Block API on [Gutenberg Handbook](https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/). 10 | 11 | For styles, you can have two files: 12 | 13 | - style.scss 14 | - editor.scss 15 | 16 | Styles that are put in `style.scss` will only be loaded on the front-end, while `editor.scss` styles will only be loaded on the backend. 17 | 18 | ## Custom CSS & Google Fonts 19 | 20 | If your block needs to add any dynamic CSS that can't be added inline, such as pseudo-elements or media queries, you can use `cycle_through_blocks` method of [`GutenbergBlocks`](https://github.com/Codeinwp/gutenberg-blocks/blob/master/class-gutenberg-blocks.php) class. 21 | 22 | Similarly, if your block loads Google Fonts then you can use `get_google_fonts` method. Your Google Fonts attributes should be `fontFamily` and `fontVariant`. -------------------------------------------------------------------------------- /plugins/blocks-animation/readme.md: -------------------------------------------------------------------------------- 1 | # Blocks Animation: CSS Animations for Gutenberg Blocks # 2 | **Contributors:** [themeisle](https://profiles.wordpress.org/themeisle/), [hardeepasrani](https://profiles.wordpress.org/hardeepasrani/), [mariamunteanu1](https://profiles.wordpress.org/mariamunteanu1/) 3 | **Tags:** gutenberg, block, block editor, editor, animation, animations, animate, styles, block animations 4 | **Requires at least:** 6.2 5 | **Tested up to:** 6.9 6 | **Requires PHP:** 5.4 7 | **Stable tag:** 3.1.4 8 | **License:** GPLv3 9 | **License URI:** https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | Blocks Animation allows you to add CSS Animations to all of your Gutenberg blocks in the most elegant way. 12 | 13 | ## Description ## 14 | 15 | 16 | Blocks Animation allows you to add CSS Animations to all of your Gutenberg blocks in the most elegant way. 17 | 18 | The UI for Blocks Animation feels so native and intuitive, you won't even notice it's installed. Just install, and you will see animation settings in all the blocks, right in the Block Settings Sidebar. 19 | 20 | All the code and sources for this plugin are publicly available as part of https://github.com/Codeinwp/otter-blocks. 21 | 22 | ## Screenshots ## 23 | 24 | 1. Block Animations 25 | 26 | 27 | ## Changelog ## 28 | 29 | You can check the changelog [here.](https://github.com/Codeinwp/otter-blocks/blob/master/CHANGELOG.md) 30 | -------------------------------------------------------------------------------- /inc/css/blocks/class-google-map-css.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--height', 41 | 'value' => 'height', 42 | 'format' => function ( $value, $attrs ) { 43 | return is_numeric( $value ) ? $value . 'px' : $value; 44 | }, 45 | ), 46 | array( 47 | 'property' => '--height-tablet', 48 | 'value' => 'heightTablet', 49 | ), 50 | array( 51 | 'property' => '--height-mobile', 52 | 'value' => 'heightMobile', 53 | ), 54 | ), 55 | ) 56 | ); 57 | 58 | return $css->generate(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /plugins/blocks-export-import/readme.txt: -------------------------------------------------------------------------------- 1 | === Blocks Export Import === 2 | Contributors: themeisle, hardeepasrani 3 | Tags: gutenberg, block, blocks, export, import, exporter, importer, block exporter, block export, block import, block importer 4 | Requires at least: 6.2 5 | Tested up to: 6.9 6 | Requires PHP: 5.4 7 | Stable tag: 3.1.4 8 | License: GPLv3 9 | License URI: https://www.gnu.org/licenses/gpl-3.0.en.html 10 | 11 | Blocks Export Import allows to Export and Import blocks as JSON in Gutenberg Block Editor. 12 | 13 | == Description == 14 | 15 | Blocks Export Import plugin allows you to import and export blocks as JSON in Gutenberg Block Editor. 16 | 17 | All the code and sources for this plugin are publicly available as part of https://github.com/Codeinwp/otter-blocks. 18 | 19 | == Installation == 20 | Activating this plugin is just like any other plugin. If you’ve uploaded the plugin package to your server already, skip to step 5 below: 21 | 22 | 1. Install using the WordPress built-in Plugin installer, or Extract the zip file and drop the contents in the wp-content/plugins/ directory of your WordPress installation. 23 | 2. Activate the plugin through the ‘Plugins’ menu in WordPress. 24 | 3. Go to Gutenberg editor and play around with the block. 25 | 26 | == Screenshots == 27 | 28 | 1. Export Blocks. 29 | 2. Import Blocks 30 | 31 | 32 | == Changelog == 33 | 34 | You can check the changelog [here.](https://github.com/Codeinwp/otter-blocks/blob/master/CHANGELOG.md) 35 | -------------------------------------------------------------------------------- /.wp-env.override.json: -------------------------------------------------------------------------------- 1 | { 2 | "core": null, 3 | "plugins": [ 4 | "." 5 | ], 6 | "config": { 7 | "WP_DEBUG": true, 8 | "WP_DEBUG_DISPLAY": true, 9 | "FS_METHOD": "direct", 10 | "WP_DEFAULT_THEME": "twentytwentythree" 11 | }, 12 | "phpVersion": "7.4", 13 | "env": { 14 | "development": { 15 | "themes": [ "./test/emptytheme" ], 16 | "mappings": { 17 | "wp-content/themes/raft": "https://downloads.wordpress.org/theme/raft.zip" 18 | } 19 | }, 20 | "tests": { 21 | "config": { 22 | "WP_DEBUG": false, 23 | "WP_DEBUG_DISPLAY": false, 24 | "ENABLE_OTTER_PRO_DEV": true 25 | }, 26 | "plugins": [ 27 | "." 28 | ], 29 | "themes": [ "./test/emptytheme" ], 30 | "mappings": { 31 | "wp-content/mu-plugins": "./packages/e2e-tests/mu-plugins", 32 | "wp-content/plugins/gutenberg-test-plugins": "./packages/e2e-tests/plugins", 33 | "wp-content/themes/gutenberg-test-themes": "./test/gutenberg-test-themes", 34 | "wp-content/themes/gutenberg-test-themes/twentytwentyone": "https://downloads.wordpress.org/theme/twentytwentyone.zip", 35 | "wp-content/themes/gutenberg-test-themes/twentytwentythree": "https://downloads.wordpress.org/theme/twentytwentythree.zip", 36 | "wp-content/themes/raft": "https://downloads.wordpress.org/theme/raft.zip" 37 | } 38 | } 39 | }, 40 | "lifecycleScripts": { 41 | "afterStart": "bash bin/e2e-tests.sh" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /inc/render/class-about-author-block.php: -------------------------------------------------------------------------------- 1 | ', 27 | esc_url( get_author_posts_url( intval( get_the_author_meta( 'ID' ) ) ) ), 28 | esc_attr( get_avatar_url( get_the_author_meta( 'ID' ), array( 'size' => 130 ) ) ) 29 | ); 30 | 31 | $title_markup = sprintf( 32 | '

%1$s

', 33 | esc_html( get_the_author_meta( 'display_name' ) ) 34 | ); 35 | 36 | $content_markup = ''; 37 | if ( ! empty( get_the_author_meta( 'description' ) ) ) { 38 | $content_markup = sprintf( 39 | '

%1$s

', 40 | esc_html( wp_strip_all_tags( get_the_author_meta( 'description' ) ) ) 41 | ); 42 | } 43 | 44 | return sprintf( 45 | '
%2$s
%3$s%4$s
', 46 | get_block_wrapper_attributes(), 47 | $img_markup, 48 | $title_markup, 49 | $content_markup 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /plugins/blocks-export-import/blocks-export-import.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--height', 41 | 'value' => 'height', 42 | 'format' => function ( $value, $attrs ) { 43 | 44 | // Check if the value is a number. 45 | if ( is_numeric( $value ) ) { 46 | $suffix = substr( $value, -2 ); 47 | if ( 'px' !== $suffix ) { 48 | return $value . 'px'; 49 | } 50 | } 51 | 52 | return $value; 53 | }, 54 | ), 55 | array( 56 | 'property' => '--height-tablet', 57 | 'value' => 'heightTablet', 58 | ), 59 | array( 60 | 'property' => '--height-mobile', 61 | 'value' => 'heightMobile', 62 | ), 63 | ), 64 | ) 65 | ); 66 | 67 | return $css->generate(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /inc/integrations/interfaces/interface-form-subscribe-service.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'properties' => array( 40 | array( 41 | 'property' => 'background-color', 42 | 'value' => 'backgroundColor', 43 | ), 44 | ), 45 | ) 46 | ); 47 | 48 | $css->add_item( 49 | array( 50 | 'selector' => ' .otter-business-hour-item__label', 51 | 'properties' => array( 52 | array( 53 | 'property' => 'color', 54 | 'value' => 'labelColor', 55 | ), 56 | ), 57 | ) 58 | ); 59 | 60 | $css->add_item( 61 | array( 62 | 'selector' => ' .otter-business-hour-item__time', 63 | 'properties' => array( 64 | array( 65 | 'property' => 'color', 66 | 'value' => 'timeColor', 67 | ), 68 | ), 69 | ) 70 | ); 71 | 72 | $style = $css->generate(); 73 | 74 | return $style; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /inc/Tracker.php: -------------------------------------------------------------------------------- 1 | $events Data to track. 26 | * @param array $options Options. 27 | * @return void 28 | */ 29 | public static function track( $events, $options = array() ) { 30 | 31 | if ( ! self::has_consent() && ( ! isset( $options['hasConsent'] ) || ! $options['hasConsent'] ) ) { 32 | return; 33 | } 34 | 35 | try { 36 | $payload = array(); 37 | 38 | $license = apply_filters( 'product_otter_license_key', 'free' ); 39 | 40 | if ( 'free' !== $license ) { 41 | $license = wp_hash( $license ); 42 | } 43 | 44 | foreach ( $events as $event ) { 45 | $payload[] = array( 46 | 'slug' => 'otter', 47 | 'site' => get_site_url(), 48 | 'license' => $license, 49 | 'data' => $event, 50 | ); 51 | } 52 | 53 | $args = array( 54 | 'headers' => array( 55 | 'Content-Type' => 'application/json', 56 | ), 57 | 'body' => wp_json_encode( $payload ), 58 | ); 59 | 60 | wp_remote_post( self::$track_url, $args ); 61 | } finally { 62 | return; 63 | } 64 | } 65 | 66 | /** 67 | * Check if the user has consented to tracking. 68 | * 69 | * @return bool 70 | */ 71 | public static function has_consent() { 72 | return (bool) get_option( 'otter_blocks_logger_flag', false ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/css/blocks/class-form-stripe-field-css.php: -------------------------------------------------------------------------------- 1 | add_item( 37 | array( 38 | 'properties' => array( 39 | array( 40 | 'property' => '--label-color', 41 | 'value' => 'labelColor', 42 | ), 43 | array( 44 | 'property' => '--stripe-border-color', 45 | 'value' => 'borderColor', 46 | ), 47 | array( 48 | 'property' => '--stripe-border-radius', 49 | 'value' => 'borderRadius', 50 | 'format' => function ( $value ) { 51 | return CSS_Utility::render_box( $value ); 52 | }, 53 | ), 54 | array( 55 | 'property' => '--stripe-border-width', 56 | 'value' => 'borderWidth', 57 | 'format' => function ( $value ) { 58 | return CSS_Utility::render_box( $value ); 59 | }, 60 | ), 61 | ), 62 | ) 63 | ); 64 | 65 | $style = $css->generate(); 66 | 67 | return $style; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /docs/uniit-and-e2e-testing.md: -------------------------------------------------------------------------------- 1 | # Automated Testing 2 | 3 | #### [Testing overview from Gutenberg.](https://developer.wordpress.org/block-editor/contributors/code/testing-overview/) 4 | 5 | ## Unit Testing 6 | 7 | Unit testing will be used to test functions (with [Jest](https://jestjs.io/docs/getting-started)) and React components (with [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)). 8 | 9 | The test will pe places in `tests/unit` 10 | 11 | They can be run with the command `npm run test:unit`. 12 | 13 | For debugging the test, use `npm run test:unit:debug` 14 | 15 | ## E2E Testing 16 | 17 | E2E are used to test the code on a running WordPress instance. They are very helpful for testing component that depends on the WP ecosystem like block, hooks (e.g.: `blockInit`). 18 | 19 | [Docs for Gutenberg utils](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-e2e-test-utils/) 20 | 21 | :warning: Most the code from Gutenberg is written in Puppeter, but they are migrating to Playwright which make the feature to not a very stable situation like Unit testing. [Source](https://make.wordpress.org/core/2022/03/23/migrating-wordpress-e2e-tests-to-playwright/) 22 | 23 | - [Puppeter Docs](https://pptr.dev) 24 | - [Playwright Docs](https://playwright.dev/docs/intro) and [Gutenberg Repo](https://github.com/WordPress/gutenberg/tree/trunk/test/e2e) 25 | 26 | You will need to use Docker for creating the WP Instace via `npm run wp-env start` command. 27 | 28 | The tests can be run with the command `npm run test:e2e'. 29 | 30 | For interactive mode (a Chromium instance will be created to watch), use `npm run test:e2e:interactive' 31 | 32 | For debugging the test, use `npm run test:e2e:debug` 33 | 34 | :warning: The e2e is a very slow test. Expect minutes of running. 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /development.php: -------------------------------------------------------------------------------- 1 | ', '', $message ); 35 | } 36 | ); 37 | 38 | add_filter( 'otter_pro_hide_license_field', '__return_true' ); 39 | 40 | \ThemeIsle\OtterPro\Main::instance(); 41 | 42 | if ( class_exists( '\ThemeIsle\OtterPro\Plugins\License' ) && ! ThemeIsle\OtterPro\Plugins\License::has_active_license() ) { 43 | add_action( 'init', function() { 44 | $license_file = dirname( __FILE__ ) . '/license.json'; 45 | 46 | global $wp_filesystem; 47 | 48 | if ( ! is_file( $license_file ) ) { 49 | return false; 50 | } 51 | 52 | require_once ABSPATH . '/wp-admin/includes/file.php'; 53 | 54 | \WP_Filesystem(); 55 | 56 | $content = json_decode( $wp_filesystem->get_contents( $license_file ) ); 57 | 58 | if ( ! is_object( $content ) ) { 59 | return false; 60 | } 61 | 62 | if ( ! isset( $content->key ) ) { 63 | return false; 64 | } 65 | 66 | $license_key = $content->key; 67 | 68 | apply_filters( 'themeisle_sdk_license_process_otter', $license_key, 'activate' ); 69 | } ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/class-add-to-cart-button-block.php: -------------------------------------------------------------------------------- 1 | $product->add_to_cart_description(), 37 | 'data-quantity' => '1', 38 | 'data-product_id' => $product->get_id(), 39 | 'data-product_sku' => $product->get_sku(), 40 | 'rel' => 'nofollow', 41 | 'class' => 'wp-block-button__link wp-element-button add_to_cart_button', 42 | ); 43 | 44 | if ( 45 | $product->supports( 'ajax_add_to_cart' ) && 46 | $product->is_purchasable() && 47 | ( $product->is_in_stock() || $product->backorders_allowed() ) 48 | ) { 49 | $attrs['class'] .= ' ajax_add_to_cart'; 50 | } 51 | 52 | $button = sprintf( 53 | '%s', 54 | esc_url( $product->add_to_cart_url() ), 55 | wc_implode_html_attributes( $attrs ), 56 | esc_html( isset( $attributes['label'] ) ? $attributes['label'] : $product->add_to_cart_text() ) 57 | ); 58 | 59 | return sprintf( 60 | '
%2$s
', 61 | $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => 'wp-block-button' ) ), 62 | $button 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/class-form-hidden-block.php: -------------------------------------------------------------------------------- 1 | $id, 41 | ) 42 | ); 43 | 44 | $output = '
'; 45 | 46 | $output .= ''; 47 | 48 | $output .= ''; 53 | 54 | $output .= '
'; 55 | 56 | 57 | return $output; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /assets/icons/acf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/otter-family-plugins.md: -------------------------------------------------------------------------------- 1 | # The big four 2 | 3 | As you can see, there are four other plugins besides Otter that are part of the family. They are: 4 | - Block Animation: allow you to add animation to any block. 5 | - Blocks CSS: allow you to add custom CSS to any block. 6 | - Block Export & Import: allow you to export and import blocks as JSON files. 7 | 8 | ## Block Animation 9 | 10 | This plugin offer the option to the user to animate the block via: 11 | - `animate.css` library ([link](https://animate.style)): we just add this classes to `className` attribute of the block. 12 | - Typing (text only). We use a format tag (`o-anim-typing`) to mark the text that should be animated. We use a custom script to animate the text. 13 | - Counting: Same as Typing, but with the tag `o-anim-counting`. 14 | 15 | When it come to upgrading, the `animate.css` library is sometime troublesome. We need to check if the new version is compatible with the old one. If not, we need to update the code to make it compatible. 16 | 17 | For Typing and Counting, we have total control over the code (since their are made in-house). So, it is easier to upgrade. 18 | 19 | ## Blocks CSS 20 | 21 | This plugin allow the user to add custom CSS to any block. The CSS is added to the block via a `style` tag. This is handy for edge cases where the user need to enhance a block with an option that is not available in Inspector. 22 | 23 | Also, it allow us to provide quick hacks to the user until we fix the issue with styling in a Block. 24 | 25 | ## Block Export & Import 26 | 27 | This is a utility plugin that allow the user to export and import blocks as JSON files. It is useful for the user to backup their blocks and for us to debug issues. Sometime we need to test a block with a specific configuration. This plugin allow us to do the sharing more easily. 28 | 29 | For importing, the trick is simple: deserialize the JSON file and replace the block with the new one. For exporting, we need to serialize the block and save it to a JSON file. -------------------------------------------------------------------------------- /docs/woocommerce-extensions.md: -------------------------------------------------------------------------------- 1 | # WooCommerce Extensions 2 | 3 | In WordPress, WooCommerce is the most popular plugin for e-commerce. In a simple view of a plugin developer: people with business that have a high chance to pay for a premium plugin. The market is so big that we can't ignore it. Otter can not a respectable plugin, without WooCommerce integration. 4 | 5 | The available WooCommerce extensions are: 6 | - Add to cart Block 7 | - Product Review with Woo Sync 8 | - Live Search (show product information in search results) 9 | - Small Showcase Blocks (title, price, rating, stock, etc.) 10 | 11 | Woo Commerce own plugin offer a lot of features for Gutenberg, and is mostly redundant to re-invent the wheel for most of them. So winning strategy is to allow Otter users to integrate WooCommerce with Otter Blocks. You use Product Review at the beginning then migrate to WooCommerce, no need to redo things, you can just sync it. The feeling is more like a 'handy feature` than a full blow WooCommerce extension plugin. 12 | 13 | ## Structure 14 | 15 | | Feature | Location | 16 | | :-- | :-- | 17 | | Frontend Render Add to Cart | `./plugins/otter-pro/inc/render/class-add-to-cart-button-block.php` | 18 | | Frontend Render Small Blocks | `./plugins/otter-pro/inc/render/woocommerce` | 19 | | Editor Add to Cart | `./src/pro/blocks/add-to-cart-button` | 20 | | Editor Small Blocks | `./src/pro/woocommerce` | 21 | 22 | For Live Search, learn more [here](live-search.md). 23 | 24 | ## Mentions 25 | 26 | - All of those feature require WooCommerce to be installed and activated. If WooCommerce is not installed, the block will be disabled. The features are built on top of WooCommerce API, so it is not possible to use them without WooCommerce. 27 | - When developing a new feature, you should always check if WooCommerce is installed and activated. If not, you should disable the feature. 28 | - Have stability always in mind. An e-commerce website is extremely important for the owner. Crashing the website is like hiding the owner's wallet. -------------------------------------------------------------------------------- /docs/live-search.md: -------------------------------------------------------------------------------- 1 | # Live Search 2 | 3 | Live Search plugin is an enhancement to default Search block. It unlock new style and the ability to preview search results. 4 | 5 | ## Structure 6 | 7 | | Feature | Location | 8 | | :-- | :-- | 9 | | Editor Block Extension Upsell | `./src/blocks/plugins/live-search` | 10 | | Editor Block Extension | `./src/pro/plugins/live-search` | 11 | | Frontend | `./src/blocks/frontend/live-search` | 12 | | Backend | `./plugins/otter-pro/inc/plugins/class-live-search` | 13 | 14 | 15 | ## How it works 16 | 17 | The normal search will only show the results when you press the Submit button, and the result will be a page with a list of posts. Live Search will show the results as you type, and the result will be shown as a dropdown with list of posts, pages, products, and other custom post types. 18 | 19 | Every time you type a new character, the search will be triggered with [this function](https://github.com/Codeinwp/otter-blocks/blob/4d9eafabaec64c02d0e522d78f08404e56a80396/src/blocks/frontend/live-search/index.ts#L59-L79). The request will be processed [here](https://github.com/Codeinwp/otter-blocks/blob/4d9eafabaec64c02d0e522d78f08404e56a80396/plugins/otter-pro/inc/server/class-live-search-server.php#L131-L171) by leveraging WordPress `WP_Query`. 20 | 21 | In a nutshell, this is just fancy interface that makes the classic Search Block to feel more modern. The inspiration point was the [search bar for MDN Web Docs](https://developer.mozilla.org/en-US/) [#1135](https://github.com/Codeinwp/otter-blocks/issues/1135). 22 | 23 | The main selling point of this plugin is showing useful information to the end user. This information is mostly from metadata, like: date, author, product price, category, etc. Users to run e-commerce websites will find this plugin very useful. 24 | 25 | When you are developing a new feature for it have those in mind: 26 | - Monitor the request time, it should be as fast as possible. 27 | - Watch the requests number. You don't want to overload the server. 28 | - Pay attention to error handling when integrating with other plugins. 29 | -------------------------------------------------------------------------------- /inc/css/blocks/class-timeline-item-css.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'properties' => array( 40 | array( 41 | 'property' => '--o-timeline-cnt-bg', 42 | 'value' => 'containerBackgroundColor', 43 | ), 44 | array( 45 | 'property' => '--o-timeline-cnt-br-c', 46 | 'value' => 'containerBorderColor', 47 | ), 48 | array( 49 | 'property' => '--o-timeline-i-color', 50 | 'value' => 'iconColor', 51 | ), 52 | array( 53 | 'property' => '--o-timeline-cnt-br-w', 54 | 'value' => 'containerBorder', 55 | 'format' => function ( $value, $attrs ) { 56 | return CSS_Utility::box_values( 57 | $value, 58 | array( 59 | 'left' => '8px', 60 | 'right' => '8px', 61 | 'top' => '8px', 62 | 'bottom' => '8px', 63 | ) 64 | ); 65 | }, 66 | ), 67 | array( 68 | 'property' => '--o-timeline-cnt-br-r', 69 | 'value' => 'containerRadius', 70 | 'format' => function ( $value, $attrs ) { 71 | return CSS_Utility::box_values( 72 | $value, 73 | array( 74 | 'left' => '8px', 75 | 'right' => '8px', 76 | 'top' => '8px', 77 | 'bottom' => '8px', 78 | ) 79 | ); 80 | }, 81 | ), 82 | ), 83 | ) 84 | ); 85 | 86 | $style = $css->generate(); 87 | 88 | return $style; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | 3 | More details about the setup can be found in [docs/environment-installation.md](docs/environment-installation.md). 4 | 5 | This projects requires you to have Node.js (with npm) and Composer. 6 | 7 | - You can run `npm ci` & `composer install` to install dependencies. 8 | - Once done, you can run `npm run build` to generate build files. 9 | - You can also use `npm run start` to generate dev build if you are working on the files. 10 | 11 | The project also ships with a `docker-compose.yml` file, you can run `docker compose up -d` to bring the instance up. 12 | 13 | ## Project Structure 14 | 15 | This repo is codebase for five different plugins, which are: 16 | 17 | - Otter Blocks 18 | - Otter Pro 19 | - Blocks Animation 20 | - Blocks CSS 21 | - Blocks Export Import 22 | 23 | Blocks Animation, CSS & Export Import are also shipped as part of Otter Blocks. Codebase of each sister-plugin of Otter, which is plugin-specific, can be found in `/plugins` folder of the repo, while the code which is shared in both Otter & sister-plugins is kept inside `/inc` folder. 24 | 25 | You can take a look at `/bin/dist.sh` file to see how each plugin is generated. Each sister-plugin has its own `.distignore` and `.wordpress-org` file/directory that needs to be maintained with latest changes. 26 | 27 | And finally, `blocks.json` file defines the build path of block assets. 28 | 29 | ## Compatibility 30 | 31 | Any change you make, you should test with at least last 2 major versions of WordPress. 32 | 33 | ## Releasing 34 | 35 | This repository uses conventional [changelog commit](https://github.com/Codeinwp/conventional-changelog-simple-preset) messages to trigger release 36 | 37 | How to release a new version: 38 | 39 | - Clone the master branch 40 | - Do your changes 41 | - Send a PR to master and merge it using the following subject message 42 | - `release: ` - for patch release 43 | - `release(minor): ` - for minor release 44 | - `release(major): ` - for major release 45 | The release notes will inherit the body of the commit message which triggered the release. For more details check the [simple-preset](https://github.com/Codeinwp/conventional-changelog-simple-preset) that we use. 46 | -------------------------------------------------------------------------------- /inc/css/blocks/class-sharing-icons-css.php: -------------------------------------------------------------------------------- 1 | $attrs ) { 38 | $css->add_item( 39 | array( 40 | 'selector' => ' .is-' . $icon, 41 | 'properties' => array( 42 | array( 43 | 'property' => '--icon-bg-color', 44 | 'value' => $icon, 45 | 'format' => function ( $value, $attrs ) { 46 | return $value['backgroundColor']; 47 | }, 48 | 'condition' => function ( $attrs ) use ( $icon ) { 49 | return isset( $attrs[ $icon ]['backgroundColor'] ); 50 | }, 51 | ), 52 | array( 53 | 'property' => '--text-color', 54 | 'value' => $icon, 55 | 'format' => function ( $value, $attrs ) { 56 | return $value['textColor']; 57 | }, 58 | 'condition' => function ( $attrs ) use ( $icon ) { 59 | return isset( $attrs[ $icon ]['textColor'] ); 60 | }, 61 | ), 62 | ), 63 | ) 64 | ); 65 | } 66 | 67 | $css->add_item( 68 | array( 69 | 'properties' => array( 70 | array( 71 | 'property' => '--icons-gap', 72 | 'value' => 'gap', 73 | 'unit' => 'px', 74 | ), 75 | array( 76 | 'property' => '--border-radius', 77 | 'value' => 'borderRadius', 78 | 'unit' => 'px', 79 | ), 80 | ), 81 | ) 82 | ); 83 | 84 | return $css->generate(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /inc/css/blocks/class-slider-css.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'properties' => array( 40 | array( 41 | 'property' => '--height', 42 | 'value' => 'height', 43 | 'format' => function ( $value, $attrs ) { 44 | return is_numeric( $value ) ? $value . 'px' : $value; 45 | }, 46 | ), 47 | array( 48 | 'property' => '--height-tablet', 49 | 'value' => 'heightTablet', 50 | ), 51 | array( 52 | 'property' => '--height-mobile', 53 | 'value' => 'heightMobile', 54 | ), 55 | array( 56 | 'property' => '--width', 57 | 'value' => 'width', 58 | ), 59 | array( 60 | 'property' => '--arrows-color', 61 | 'value' => 'arrowsColor', 62 | ), 63 | array( 64 | 'property' => '--arrows-background-color', 65 | 'value' => 'arrowsBackgroundColor', 66 | ), 67 | array( 68 | 'property' => '--pagination-color', 69 | 'value' => 'paginationColor', 70 | ), 71 | array( 72 | 'property' => '--pagination-active-color', 73 | 'value' => 'paginationActiveColor', 74 | ), 75 | array( 76 | 'property' => '--border-color', 77 | 'value' => 'borderColor', 78 | ), 79 | array( 80 | 'property' => '--border-width', 81 | 'value' => 'borderWidth', 82 | ), 83 | array( 84 | 'property' => '--border-radius', 85 | 'value' => 'borderRadius', 86 | ), 87 | ), 88 | ) 89 | ); 90 | 91 | $style = $css->generate(); 92 | 93 | return $style; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /docs/form-block-workflow.md: -------------------------------------------------------------------------------- 1 | # Form Block Workflow 2 | 3 | A guide about how Form workflow works. 4 | 5 | ## The scope 6 | 7 | Allowing the end user to send some data to the website owner. Data can be: text, number, email, date, files, etc. The can be sent to a third party service or to the website owner. It can go via email or internal storage. 8 | 9 | When doing this we need to consider the following: 10 | - Bots. We need to prevent bots from sending data. 11 | - Security. We need to prevent malicious data from being sent. Data sanitization, validation, etc. For files we have to consider the numbers, the size and the type. 12 | - 3rd party services. We need to consider the API of the service and how to send the data. 13 | 14 | ## How it works from the Submit click to the data being sent 15 | 16 | When the user clicks on the Submit button, the following happens: 17 | - The data is validated with JS and collected. Then sended to the server via `wp-json/form/frontend` endpoint (definition in `./inc/server/class-form-server.php`). 18 | - The data is validated with PHP via `otter_form_validate_form` filter hook. If the data is invalid, we check if it was sended by a bot with `otter_form_anti_spam_validation` filter hook. 19 | - If all ok, we apply some data preparation `otter_form_data_preparation`. This will add or change the data from the `$form_data` variable. 20 | - If everything goes well, we get the provider (the service that will receive the data) and run with the current data request of `$form_data`. 21 | - At the end we do a `otter_form_after_submit` to trigger extra actions. (deleting files, sending data to 3rd party services, auto-responder, etc.) 22 | 23 | You will see in the server a lot of error handling. This is because we need to be sure that the data is sent to the user. If something goes wrong, we need to inform the user or the admin (in critical cases). For the server part, the PHP utility files are in `./inc/integrations/`. 24 | 25 | ## Where are the options for the form? 26 | 27 | As you know, you can not trust the request that come to the server. It might be malicious. When we process a request, we pull the options of the form that send the request with the `get_option` function (the data is saved in WordPress options and you can see the definition in `./inc/plugins/class-options-settings.php` ) and check if request respect the options. If not, we return an error. 28 | 29 | If you add a new feature and need to save in options, make sure to change the definition from `./inc/plugins/class-options-settings.php`. 30 | 31 | -------------------------------------------------------------------------------- /inc/render/class-leaflet-map-block.php: -------------------------------------------------------------------------------- 1 | '; 29 | $output .= ' '; 30 | $output .= ''; 31 | 32 | return $output; 33 | } 34 | 35 | // Set the ID and the class name. 36 | $id = isset( $attributes['id'] ) ? esc_attr( $attributes['id'] ) : 'wp-block-themeisle-blocks-map-' . wp_rand( 10, 100 ); 37 | $class = ''; 38 | $style = ''; 39 | 40 | if ( isset( $attributes['height'] ) ) { 41 | $style .= 'height:' . esc_attr( $attributes['height'] . 'px;' ); 42 | } 43 | 44 | if ( isset( $attributes['align'] ) ) { 45 | $class .= 'align' . esc_attr( $attributes['align'] ); 46 | } 47 | 48 | $wrapper_attributes = get_block_wrapper_attributes( 49 | array( 50 | 'id' => $id, 51 | 'class' => $class, 52 | 'style' => $style, 53 | ) 54 | ); 55 | 56 | // Load the attributes in the page and make a placeholder to render the map. 57 | $output = '
' . "\n"; 58 | $output .= '' . "\n"; 64 | 65 | return $output; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /assets/icons/meta.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /inc/render/class-google-map-block.php: -------------------------------------------------------------------------------- 1 | '; 35 | $output .= ' '; 36 | $output .= ''; 37 | 38 | return $output; 39 | } 40 | 41 | $id = isset( $attributes['id'] ) ? esc_attr( $attributes['id'] ) : 'wp-block-themeisle-blocks-google-map-' . wp_rand( 10, 100 ); 42 | $class = ''; 43 | $style = ''; 44 | 45 | if ( isset( $attributes['align'] ) ) { 46 | $class .= 'align' . esc_attr( $attributes['align'] ); 47 | } 48 | 49 | if ( isset( $attributes['height'] ) ) { 50 | $style .= 'height:' . esc_attr( is_numeric( $attributes['height'] ) ? $attributes['height'] . 'px' : $attributes['height'] ); 51 | } 52 | 53 | $wrapper_attributes = get_block_wrapper_attributes( 54 | array( 55 | 'class' => $class, 56 | 'style' => $style, 57 | ) 58 | ); 59 | 60 | $output = '
' . "\n"; 61 | $output .= '' . "\n"; 67 | 68 | return $output; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /webpack.config.pro.js: -------------------------------------------------------------------------------- 1 | const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); 2 | const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); 3 | const NODE_ENV = process.env.NODE_ENV || 'development'; 4 | const ANALYZER = 'true' === process.env.NODE_ANALYZER ? true : false; 5 | const path = require( 'path' ); 6 | const FileManagerPlugin = require( 'filemanager-webpack-plugin' ); 7 | const blocks = require( './blocks.json' ); 8 | 9 | defaultConfig.plugins.splice( 1, 1 ); // We need to remove Core's Copy Files plugin. 10 | 11 | const blockFilesPro = Object.keys( blocks ).filter( block => blocks[ block ].block !== undefined && true === blocks[ block ]?.isPro ) 12 | .map( block => { 13 | return { 14 | source: `src/${ blocks[ block ].block }`, 15 | destination: `build/pro/${ block }/` 16 | }; 17 | }); 18 | 19 | const blockFoldersPro = Object.keys( blocks ).filter( block => true === blocks[ block ]?.isPro ).map( block => `build/pro/${ block }` ); 20 | 21 | const changeTextDomain = textdomain => { 22 | return { 23 | test: /\.(j|t)sx?$/, 24 | exclude: /node_modules/, 25 | use: [ 26 | { 27 | loader: require.resolve( 'babel-loader' ), 28 | options: { 29 | cacheDirectory: 30 | process.env.BABEL_CACHE_DIRECTORY || true, 31 | babelrc: false, 32 | configFile: false, 33 | presets: [ 34 | require.resolve( 35 | '@wordpress/babel-preset-default' 36 | ) 37 | ], 38 | plugins: [ 39 | [ '@automattic/babel-plugin-replace-textdomain', { textdomain }] 40 | ] 41 | } 42 | } 43 | ] 44 | }; 45 | }; 46 | 47 | module.exports = [ 48 | { 49 | 50 | // OTTER PRO 51 | ...defaultConfig, 52 | stats: 'minimal', 53 | devtool: 'development' === NODE_ENV ? 'eval-source-map' : undefined, 54 | mode: NODE_ENV, 55 | entry: { 56 | blocks: [ 57 | './src/pro/blocks/index.js', 58 | './src/pro/plugins/index.js' 59 | ], 60 | dashboard: './src/pro/dashboard/index.js', 61 | woocommerce: './src/pro/woocommerce/index.js' 62 | }, 63 | output: { 64 | path: path.resolve( __dirname, './build/pro' ), 65 | filename: '[name].js', 66 | chunkFilename: 'chunk-[name].js' 67 | }, 68 | module: { 69 | rules: [ 70 | changeTextDomain('otter-pro'), 71 | ...defaultConfig.module.rules 72 | ] 73 | }, 74 | plugins: [ 75 | ...defaultConfig.plugins, 76 | new FileManagerPlugin({ 77 | events: { 78 | onEnd: { 79 | mkdir: blockFoldersPro, 80 | copy: blockFilesPro 81 | } 82 | }, 83 | runOnceInWatchMode: false, 84 | runTasksInSeries: true 85 | }), 86 | new BundleAnalyzerPlugin({ 87 | analyzerMode: 'disabled', 88 | generateStatsFile: ANALYZER 89 | }) 90 | ] 91 | } 92 | ]; 93 | -------------------------------------------------------------------------------- /plugins/blocks-css/blocks-css.php: -------------------------------------------------------------------------------- 1 | 'free', 78 | ), 79 | tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/otter-bf', 'bfcm', 'blocks-css' ) ) 80 | ); 81 | 82 | $configs[ BLOCKS_CSS_PRODUCT_SLUG ] = $config; 83 | 84 | return $configs; 85 | } 86 | ); 87 | -------------------------------------------------------------------------------- /inc/class-blocks-export-import.php: -------------------------------------------------------------------------------- 1 | init(); 68 | } 69 | 70 | return self::$instance; 71 | } 72 | 73 | /** 74 | * Throw error on object clone 75 | * 76 | * The whole idea of the singleton design pattern is that there is a single 77 | * object therefore, we don't want the object to be cloned. 78 | * 79 | * @access public 80 | * @since 1.0.0 81 | * @return void 82 | */ 83 | public function __clone() { 84 | // Cloning instances of the class is forbidden. 85 | _doing_it_wrong( __FUNCTION__, 'Cheatin’ huh?', '1.0.0' ); 86 | } 87 | 88 | /** 89 | * Disable unserializing of the class 90 | * 91 | * @access public 92 | * @since 1.0.0 93 | * @return void 94 | */ 95 | public function __wakeup() { 96 | // Unserializing instances of the class is forbidden. 97 | _doing_it_wrong( __FUNCTION__, 'Cheatin’ huh?', '1.0.0' ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /inc/patterns/large-quote.php: -------------------------------------------------------------------------------- 1 | __( 'Large Quote', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'text' ), 11 | 'content' => '

"...The greatest victory is that which requires no battle...."

Sun Tzu, The Art of War

', 12 | ); 13 | -------------------------------------------------------------------------------- /inc/css/blocks/class-core-image-plugin-css.php: -------------------------------------------------------------------------------- 1 | /m'; 47 | preg_match_all( $re, $block['innerHTML'], $matches, PREG_SET_ORDER, 0 ); 48 | 49 | if ( empty( $matches ) || empty( $matches[0][1] ) ) { 50 | return $style; 51 | } 52 | 53 | $id = $matches[0][1]; 54 | 55 | $css->set_id( $id ); 56 | 57 | $css->add_item( 58 | array( 59 | 'selector' => ' img', 60 | 'properties' => array( 61 | array( 62 | 'property' => 'box-shadow', 63 | 'pattern' => 'horizontal vertical blur color', 64 | 'pattern_values' => array( 65 | 'horizontal' => array( 66 | 'value' => 'boxShadowHorizontal', 67 | 'unit' => 'px', 68 | 'default' => 0, 69 | ), 70 | 'vertical' => array( 71 | 'value' => 'boxShadowVertical', 72 | 'unit' => 'px', 73 | 'default' => 0, 74 | ), 75 | 'blur' => array( 76 | 'value' => 'boxShadowBlur', 77 | 'unit' => 'px', 78 | 'default' => 5, 79 | ), 80 | 'color' => array( 81 | 'value' => 'boxShadowColor', 82 | 'default' => '#000', 83 | 'format' => function ( $value, $attrs ) { 84 | $opacity = ( isset( $attrs['boxShadowColorOpacity'] ) ? $attrs['boxShadowColorOpacity'] : 50 ); 85 | return ( strpos( $value, '#' ) !== false && $opacity < 100 ) ? Base_CSS::hex2rgba( $value, $opacity / 100 ) : $value; 86 | }, 87 | ), 88 | ), 89 | 'condition' => function ( $attrs ) { 90 | return isset( $attrs['boxShadow'] ); 91 | }, 92 | ), 93 | ), 94 | ) 95 | ); 96 | 97 | $style = $css->generate(); 98 | 99 | return $style; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /docs/blocks/deprecation.md: -------------------------------------------------------------------------------- 1 | > We recommend to read this article before: https://developer.wordpress.org/block-editor/reference-guides/block-api/block-deprecation/ 2 | 3 | ## [When the big change is comming](https://www.youtube.com/watch?v=enuOArEfqGo) 4 | 5 | In the previous chapters we removed the `color` property so that we can add with PHP. Let's add it back and make the migration scheme. **Remember to save the current block (Update or Save draft)**. 6 | 7 | ```jsx 8 | registerBlockType( 'themeisle-blocks/tutorial-2', { 9 | title: __( 'My first Block - Tutorial 2' ), 10 | description: __( 'Small Example 2' ), 11 | icon: 'universal-access-alt', 12 | category: 'themeisle-blocks', 13 | keywords: [ 'tutorial' ], 14 | attributes: { 15 | text: { 16 | type: 'string', 17 | default: 'Hello' 18 | }, 19 | color: { 20 | type: 'string', 21 | default: 'green' 22 | } 23 | }, 24 | edit: ( props ) => { 25 | console.log( props ); 26 | const [ wordsNum, setWordsNum ] = useState( 0 ); 27 | const onTextChange = ( value ) => props.setAttributes({ text: value.target.value }); 28 | 29 | useEffect( () => { 30 | setWordsNum( props.attributes?.text?.split( ' ' ).length || 0 ); 31 | }, [ props.attributes.text ]); 32 | 33 | return ( 34 |
35 | 36 |

The text is: {props.attributes.text}

37 |

Words Number: {wordsNum}

38 |
39 | ); 40 | }, 41 | save: ( props ) => { 42 | return ( 43 |
44 |

The text is: {props.attributes.text}

45 |
46 | ); 47 | }, 48 | deprecated: [ { 49 | attributes: { 50 | text: { 51 | type: 'string', 52 | default: 'Hello' 53 | }, 54 | color: { 55 | type: 'string', 56 | default: 'green' 57 | } 58 | }, 59 | save: ( props ) => { 60 | return ( 61 |
62 |

The text is: {props.attributes.text}

63 |
64 | ); 65 | } 66 | } ] 67 | }); 68 | ``` 69 | 70 | If we refesh the page, the transition will be made and no error will appear. 71 | 72 | ```html 73 | 74 |

The text is: Hello

75 | 76 | ``` 77 | 78 | Blocks for reference: 79 | 80 | - Section Block: `src/blocks/section/column/deprecated.js` | `src/blocks/section/columns/deprecated.js` 81 | - Posts: `src/blocks/posts/deprecated.js` 82 | - Font Awesome Icons: `src/blocks/font-awesome-icons/deprecated.js` 83 | - Advanced Heading: `src/blocks/advanced-heading/deprecated.js` 84 | 85 | We do not have many blocks with the `deprecated` feature; we try as much as possible to avoid this. 86 | 87 | 88 | -------------------------------------------------------------------------------- /plugins/blocks-animation/blocks-animation.php: -------------------------------------------------------------------------------- 1 | 'free', 82 | ), 83 | tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/otter-bf', 'bfcm', 'blocks-animation' ) ) 84 | ); 85 | 86 | $configs[ BLOCKS_ANIMATION_PRODUCT_SLUG ] = $config; 87 | 88 | return $configs; 89 | } 90 | ); 91 | -------------------------------------------------------------------------------- /assets/icons/author.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /inc/render/amp/class-slider-block.php: -------------------------------------------------------------------------------- 1 | loadHTML( $block['innerHTML'] ); 41 | $id = $block['attrs']['id']; 42 | $images = $dom->getElementsByTagName( 'figure' ); 43 | $block_content = ''; 44 | 45 | foreach ( $images as $image ) { 46 | $block_content .= $html5->saveHTML( $image ); 47 | } 48 | 49 | $block_content .= ''; 50 | 51 | return $block_content; 52 | } 53 | 54 | return $block_content; 55 | } 56 | 57 | /** 58 | * The instance method for the static class. 59 | * Defines and returns the instance of the static class. 60 | * 61 | * @static 62 | * @since 1.7.1 63 | * @access public 64 | * @return Slider_Block 65 | */ 66 | public static function instance() { 67 | if ( is_null( self::$instance ) ) { 68 | self::$instance = new self(); 69 | self::$instance->init(); 70 | } 71 | 72 | return self::$instance; 73 | } 74 | 75 | /** 76 | * Throw error on object clone 77 | * 78 | * The whole idea of the singleton design pattern is that there is a single 79 | * object therefore, we don't want the object to be cloned. 80 | * 81 | * @access public 82 | * @since 1.7.1 83 | * @return void 84 | */ 85 | public function __clone() { 86 | // Cloning instances of the class is forbidden. 87 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 88 | } 89 | 90 | /** 91 | * Disable unserializing of the class 92 | * 93 | * @access public 94 | * @since 1.7.1 95 | * @return void 96 | */ 97 | public function __wakeup() { 98 | // Unserializing instances of the class is forbidden. 99 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /inc/render/amp/class-lottie.block.php: -------------------------------------------------------------------------------- 1 | '; 49 | return $block_content; 50 | } 51 | 52 | return $block_content; 53 | } 54 | 55 | /** 56 | * The instance method for the static class. 57 | * Defines and returns the instance of the static class. 58 | * 59 | * @static 60 | * @since 1.7.1 61 | * @access public 62 | * @return Lottie_Block 63 | */ 64 | public static function instance() { 65 | if ( is_null( self::$instance ) ) { 66 | self::$instance = new self(); 67 | self::$instance->init(); 68 | } 69 | 70 | return self::$instance; 71 | } 72 | 73 | /** 74 | * Throw error on object clone 75 | * 76 | * The whole idea of the singleton design pattern is that there is a single 77 | * object therefore, we don't want the object to be cloned. 78 | * 79 | * @access public 80 | * @since 1.7.1 81 | * @return void 82 | */ 83 | public function __clone() { 84 | // Cloning instances of the class is forbidden. 85 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 86 | } 87 | 88 | /** 89 | * Disable unserializing of the class 90 | * 91 | * @access public 92 | * @since 1.7.1 93 | * @return void 94 | */ 95 | public function __wakeup() { 96 | // Unserializing instances of the class is forbidden. 97 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /webpack.config.plugins.js: -------------------------------------------------------------------------------- 1 | const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); 2 | const FileManagerPlugin = require( 'filemanager-webpack-plugin' ); 3 | const NODE_ENV = process.env.NODE_ENV || 'development'; 4 | const path = require( 'path' ); 5 | 6 | defaultConfig.plugins.splice( 1, 1 ); // We need to remove Core's Copy Files plugin. 7 | 8 | const changeTextDomain = textdomain => { 9 | return { 10 | test: /\.(j|t)sx?$/, 11 | exclude: /node_modules/, 12 | use: [ 13 | { 14 | loader: require.resolve( 'babel-loader' ), 15 | options: { 16 | cacheDirectory: 17 | process.env.BABEL_CACHE_DIRECTORY || true, 18 | babelrc: false, 19 | configFile: false, 20 | presets: [ 21 | require.resolve( 22 | '@wordpress/babel-preset-default' 23 | ) 24 | ], 25 | plugins: [ 26 | [ '@automattic/babel-plugin-replace-textdomain', { textdomain }] 27 | ] 28 | } 29 | } 30 | ] 31 | }; 32 | }; 33 | 34 | const plugins = { 35 | plugins: [ 36 | ...defaultConfig.plugins, 37 | new FileManagerPlugin({ 38 | events: { 39 | onEnd: { 40 | delete: [ 41 | 'build/animation/blocks/', 42 | 'build/animation/pro/', 43 | 'build/css/blocks/', 44 | 'build/css/pro/', 45 | 'build/export-import/blocks/', 46 | 'build/export-import/pro/' 47 | ] 48 | } 49 | }, 50 | runOnceInWatchMode: false, 51 | runTasksInSeries: true 52 | }) 53 | ] 54 | }; 55 | 56 | module.exports = [ 57 | { 58 | 59 | // ANIMATION 60 | ...defaultConfig, 61 | stats: 'minimal', 62 | mode: NODE_ENV, 63 | entry: { 64 | index: './src/animation/index.js', 65 | frontend: './src/animation/frontend.js', 66 | 'anim-count': './src/animation/frontend/count/index.js', 67 | 'anim-typing': './src/animation/frontend/typing/index.js', 68 | 'welcome-notice': './src/animation/welcome-notice/index.js' 69 | }, 70 | output: { 71 | path: path.resolve( __dirname, './build/animation' ) 72 | }, 73 | module: { 74 | rules: [ 75 | changeTextDomain('blocks-animation'), 76 | ...defaultConfig.module.rules 77 | ] 78 | }, 79 | ...plugins 80 | }, 81 | { 82 | 83 | // CSS 84 | ...defaultConfig, 85 | stats: 'minimal', 86 | mode: NODE_ENV, 87 | entry: { 88 | index: './src/css/index.js' 89 | }, 90 | output: { 91 | path: path.resolve( __dirname, './build/css' ) 92 | }, 93 | module: { 94 | rules: [ 95 | changeTextDomain('blocks-css'), 96 | ...defaultConfig.module.rules 97 | ] 98 | }, 99 | ...plugins 100 | }, 101 | { 102 | 103 | // Export Import 104 | ...defaultConfig, 105 | stats: 'minimal', 106 | mode: NODE_ENV, 107 | entry: { 108 | index: './src/export-import/index.js' 109 | }, 110 | output: { 111 | path: path.resolve( __dirname, './build/export-import' ) 112 | }, 113 | module: { 114 | rules: [ 115 | changeTextDomain('blocks-export-import'), 116 | ...defaultConfig.module.rules 117 | ] 118 | }, 119 | ...plugins 120 | } 121 | ]; 122 | -------------------------------------------------------------------------------- /inc/css/blocks/class-progress-bar-css.php: -------------------------------------------------------------------------------- 1 | add_item( 40 | array( 41 | 'properties' => array( 42 | array( 43 | 'property' => '--percentage', 44 | 'value' => 'percentage', 45 | 'unit' => '%', 46 | ), 47 | array( 48 | 'property' => '--title-color', 49 | 'value' => 'titleColor', 50 | ), 51 | array( 52 | 'property' => '--percentage-color', 53 | 'value' => 'percentageColor', 54 | 'condition' => function ( $attrs ) { 55 | return ! isset( $attrs['percentagePosition'] ); 56 | }, 57 | ), 58 | array( 59 | 'property' => '--percentage-color-outer', 60 | 'value' => 'percentageColor', 61 | 'condition' => function ( $attrs ) { 62 | return isset( $attrs['percentagePosition'] ) && 'outer' === $attrs['percentagePosition']; 63 | }, 64 | ), 65 | array( 66 | 'property' => '--percentage-color-tooltip', 67 | 'value' => 'percentageColor', 68 | 'condition' => function ( $attrs ) { 69 | return isset( $attrs['percentagePosition'] ) && 'tooltip' === $attrs['percentagePosition']; 70 | }, 71 | ), 72 | array( 73 | 'property' => '--percentage-color-append', 74 | 'value' => 'percentageColor', 75 | 'condition' => function ( $attrs ) { 76 | return isset( $attrs['percentagePosition'] ) && 'append' === $attrs['percentagePosition']; 77 | }, 78 | ), 79 | array( 80 | 'property' => '--background-color', 81 | 'value' => 'backgroundColor', 82 | ), 83 | array( 84 | 'property' => '--border-radius', 85 | 'value' => 'borderRadius', 86 | 'unit' => 'px', 87 | ), 88 | array( 89 | 'property' => '--height', 90 | 'value' => 'height', 91 | 'unit' => 'px', 92 | ), 93 | array( 94 | 'property' => '--bar-background', 95 | 'value' => 'barBackgroundColor', 96 | ), 97 | array( 98 | 'property' => '--title-font-size', 99 | 'value' => 'titleFontSize', 100 | ), 101 | ), 102 | ) 103 | ); 104 | 105 | $style = $css->generate(); 106 | 107 | return $style; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /inc/css/blocks/class-timeline-css.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'properties' => array( 40 | array( 41 | 'property' => '--o-timeline-cnt-bg', 42 | 'value' => 'containerBackgroundColor', 43 | ), 44 | array( 45 | 'property' => '--o-timeline-cnt-br-c', 46 | 'value' => 'containerBorderColor', 47 | ), 48 | array( 49 | 'property' => '--o-timeline-i-font-size', 50 | 'value' => 'iconSize', 51 | ), 52 | array( 53 | 'property' => '--o-timeline-i-color', 54 | 'value' => 'iconColor', 55 | ), 56 | array( 57 | 'property' => '--o-timeline-v-color', 58 | 'value' => 'verticalLineColor', 59 | ), 60 | array( 61 | 'property' => '--o-timeline-v-width', 62 | 'value' => 'verticalLineWidth', 63 | ), 64 | array( 65 | 'property' => '--o-timeline-cnt-br-w', 66 | 'value' => 'containerBorder', 67 | 'format' => function ( $value, $attrs ) { 68 | return CSS_Utility::box_values( 69 | $value, 70 | array( 71 | 'left' => '8px', 72 | 'right' => '8px', 73 | 'top' => '8px', 74 | 'bottom' => '8px', 75 | ) 76 | ); 77 | }, 78 | ), 79 | array( 80 | 'property' => '--o-timeline-cnt-br-r', 81 | 'value' => 'containerRadius', 82 | 'format' => function ( $value, $attrs ) { 83 | return CSS_Utility::box_values( 84 | $value, 85 | array( 86 | 'left' => '8px', 87 | 'right' => '8px', 88 | 'top' => '8px', 89 | 'bottom' => '8px', 90 | ) 91 | ); 92 | }, 93 | ), 94 | array( 95 | 'property' => '--o-timeline-cnt-pd', 96 | 'value' => 'containerPadding', 97 | 'format' => function ( $value, $attrs ) { 98 | return CSS_Utility::box_values( 99 | $value, 100 | array( 101 | 'left' => '20px', 102 | 'right' => '20px', 103 | 'top' => '20px', 104 | 'bottom' => '20px', 105 | ) 106 | ); 107 | }, 108 | ), 109 | ), 110 | ) 111 | ); 112 | 113 | $style = $css->generate(); 114 | 115 | return $style; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /inc/patterns/call-to-action-1.php: -------------------------------------------------------------------------------- 1 | __( 'Call to Action', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'call-to-action' ), 11 | 'content' => '

A call to action section

A Call to action section

', // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation 12 | ); 13 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/class-form-file-block.php: -------------------------------------------------------------------------------- 1 | 1 ); 37 | $allowed_files = isset( $attributes['allowedFileTypes'] ) ? implode( ',', $attributes['allowedFileTypes'] ) : ''; 38 | 39 | $wrapper_attributes = get_block_wrapper_attributes( 40 | array( 41 | 'id' => $id, 42 | ) 43 | ); 44 | 45 | $output = '
'; 46 | $mapped_name = isset( $attributes['mappedName'] ) ? esc_attr( $attributes['mappedName'] ) : 'field-' . $id; 47 | 48 | $output .= ''; 49 | 50 | $output .= ''; 59 | 60 | $output .= '' 61 | . $help_text 62 | . ''; 63 | 64 | $output .= '
'; 65 | return $output; 66 | } 67 | 68 | /** 69 | * Render the required sign. 70 | * 71 | * @param bool $is_required The required status of the field. 72 | * @return string 73 | */ 74 | public function render_required_sign( $is_required ) { 75 | return $is_required ? '*' : ''; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/plugins/class-options-settings.php: -------------------------------------------------------------------------------- 1 | 'boolean', 41 | 'description' => __( 'Inherit license from Neve Pro.', 'otter-pro' ), 42 | 'show_in_rest' => true, 43 | 'default' => false, 44 | ) 45 | ); 46 | 47 | register_setting( 48 | 'themeisle_blocks_settings', 49 | 'otter_offload_fonts', 50 | array( 51 | 'type' => 'boolean', 52 | 'description' => __( 'Store Google Fonts Offline.', 'otter-pro' ), 53 | 'show_in_rest' => true, 54 | 'default' => true === boolval( get_option( 'nv_pro_enable_local_fonts', false ) ) ? true : false, 55 | ) 56 | ); 57 | 58 | register_setting( 59 | 'themeisle_blocks_settings', 60 | 'otter_iphub_api_key', 61 | array( 62 | 'type' => 'string', 63 | 'description' => __( 'IPHub API Key.', 'otter-pro' ), 64 | 'sanitize_callback' => 'sanitize_text_field', 65 | 'show_in_rest' => true, 66 | 'default' => '', 67 | ) 68 | ); 69 | } 70 | 71 | /** 72 | * The instance method for the static class. 73 | * Defines and returns the instance of the static class. 74 | * 75 | * @static 76 | * @since 1.2.0 77 | * @access public 78 | * @return Options_Settings 79 | */ 80 | public static function instance() { 81 | if ( is_null( self::$instance ) ) { 82 | self::$instance = new self(); 83 | self::$instance->init(); 84 | } 85 | 86 | return self::$instance; 87 | } 88 | 89 | /** 90 | * Throw error on object clone 91 | * 92 | * The whole idea of the singleton design pattern is that there is a single 93 | * object therefore, we don't want the object to be cloned. 94 | * 95 | * @access public 96 | * @since 1.2.0 97 | * @return void 98 | */ 99 | public function __clone() { 100 | // Cloning instances of the class is forbidden. 101 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 102 | } 103 | 104 | /** 105 | * Disable unserializing of the class 106 | * 107 | * @access public 108 | * @since 1.2.0 109 | * @return void 110 | */ 111 | public function __wakeup() { 112 | // Unserializing instances of the class is forbidden. 113 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /assets/icons/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/render/class-form-stripe-block.php: -------------------------------------------------------------------------------- 1 | create_request( 'product', $attributes['product'] ); 41 | 42 | if ( is_wp_error( $product ) ) { 43 | return sprintf( 44 | '
%2$s
', 45 | get_block_wrapper_attributes(), 46 | __( 'An error occurred! Could not retrieve product information!', 'otter-pro' ) . $this->format_error( $product ) 47 | ); 48 | } 49 | 50 | $details_markup = ''; 51 | 52 | if ( 0 < count( $product['images'] ) ) { 53 | $details_markup .= '' . $product['description'] . ''; 54 | } 55 | 56 | $price = $stripe->create_request( 'price', $attributes['price'] ); 57 | 58 | if ( is_wp_error( $price ) ) { 59 | return sprintf( 60 | '
%2$s
', 61 | get_block_wrapper_attributes(), 62 | __( 'An error occurred! Could not retrieve the price of the product!', 'otter-pro' ) . $this->format_error( $price ) 63 | ); 64 | } 65 | 66 | $currency = Review_Block::get_currency( $price['currency'] ); 67 | $amount = number_format( $price['unit_amount'] / 100, 2, '.', ' ' ); 68 | 69 | $details_markup .= '
'; 70 | $details_markup .= '

' . $product['name'] . '

'; 71 | $details_markup .= '
' . $currency . $amount . '
'; 72 | $details_markup .= '
'; 73 | 74 | $html_attributes = 'id="' . $attributes['id'] . '" ' . 75 | ( isset( $attributes['mappedName'] ) ? ( ' name="' . $attributes['mappedName'] . '"' ) : '' ) . 76 | ( isset( $attributes['fieldOptionName'] ) ? ( ' data-field-option-name="' . $attributes['fieldOptionName'] . '"' ) : '' ); 77 | 78 | return sprintf( 79 | '
%2$s
', 80 | get_block_wrapper_attributes() . $html_attributes, 81 | $details_markup 82 | ); 83 | } 84 | 85 | /** 86 | * Format the error message. 87 | * 88 | * @param \WP_Error $error The error. 89 | * @return string 90 | */ 91 | private function format_error( $error ) { 92 | return defined( 'WP_DEBUG' ) && WP_DEBUG ? ( 93 | '' . __( 'Error message: ', 'otter-pro' ) . ' ' . $error->get_error_message() . '' 94 | ) : ''; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /inc/render/class-masonry-variant.php: -------------------------------------------------------------------------------- 1 | .otter-masonry .blocks-gallery-grid .blocks-gallery-item img{ width:100% }'; 66 | 67 | $block_content = $style . '
' . $block_content . '
'; 68 | 69 | return $block_content; 70 | } 71 | 72 | return $block_content; 73 | } 74 | 75 | /** 76 | * The instance method for the static class. 77 | * Defines and returns the instance of the static class. 78 | * 79 | * @static 80 | * @since 1.7.1 81 | * @access public 82 | * @return Masonry_Variant 83 | */ 84 | public static function instance() { 85 | if ( is_null( self::$instance ) ) { 86 | self::$instance = new self(); 87 | self::$instance->init(); 88 | } 89 | 90 | return self::$instance; 91 | } 92 | 93 | /** 94 | * Throw error on object clone 95 | * 96 | * The whole idea of the singleton design pattern is that there is a single 97 | * object therefore, we don't want the object to be cloned. 98 | * 99 | * @access public 100 | * @since 1.7.1 101 | * @return void 102 | */ 103 | public function __clone() { 104 | // Cloning instances of the class is forbidden. 105 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 106 | } 107 | 108 | /** 109 | * Disable unserializing of the class 110 | * 111 | * @access public 112 | * @since 1.7.1 113 | * @return void 114 | */ 115 | public function __wakeup() { 116 | // Unserializing instances of the class is forbidden. 117 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/plugins/class-stripe-pro-features.php: -------------------------------------------------------------------------------- 1 | get_session_email( $session_id ); 57 | if ( ! $email ) { 58 | return; 59 | } 60 | 61 | $to = $email; 62 | $headers[] = 'Content-Type: text/html'; 63 | $headers[] = 'From: ' . get_bloginfo( 'name', 'display' ); 64 | $subject = $attributes['autoresponder']['subject']; 65 | $body = $attributes['autoresponder']['body']; 66 | 67 | // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail 68 | if ( wp_mail( $to, $subject, $body, $headers ) ) { 69 | set_transient( $transient_key, true, WEEK_IN_SECONDS ); 70 | } 71 | } 72 | 73 | /** 74 | * The instance method for the static class. 75 | * Defines and returns the instance of the static class. 76 | * 77 | * @static 78 | * @since 1.7.1 79 | * @access public 80 | * @return Stripe_Pro_Features 81 | */ 82 | public static function instance() { 83 | if ( is_null( self::$instance ) ) { 84 | self::$instance = new self(); 85 | self::$instance->init(); 86 | } 87 | 88 | return self::$instance; 89 | } 90 | 91 | /** 92 | * Throw error on object clone 93 | * 94 | * The whole idea of the singleton design pattern is that there is a single 95 | * object therefore, we don't want the object to be cloned. 96 | * 97 | * @access public 98 | * @since 1.7.1 99 | * @return void 100 | */ 101 | public function __clone() { 102 | // Cloning instances of the class is forbidden. 103 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 104 | } 105 | 106 | /** 107 | * Disable unserializing of the class 108 | * 109 | * @access public 110 | * @since 1.7.1 111 | * @return void 112 | */ 113 | public function __wakeup() { 114 | // Unserializing instances of the class is forbidden. 115 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /inc/patterns/hero-area-with-button.php: -------------------------------------------------------------------------------- 1 | __( 'Hero Area with Button', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'featured', 'columns', 'header' ), 11 | 'content' => '

Pumpkins & Penguins

Sed ut perspiciatis unde omnis iste natus

', 12 | ); 13 | -------------------------------------------------------------------------------- /inc/server/class-fse-onboarding-server.php: -------------------------------------------------------------------------------- 1 | namespace . $this->version; 50 | 51 | register_rest_route( 52 | $namespace, 53 | '/onboarding/templates', 54 | array( 55 | array( 56 | 'methods' => \WP_REST_Server::READABLE, 57 | 'callback' => array( $this, 'get_templates' ), 58 | 'permission_callback' => function () { 59 | return current_user_can( 'manage_options' ); 60 | }, 61 | ), 62 | ) 63 | ); 64 | } 65 | 66 | /** 67 | * List Templates. 68 | * 69 | * @param \WP_REST_Request $request The request. 70 | * 71 | * @return \WP_REST_Response 72 | * @access public 73 | */ 74 | public function get_templates( \WP_REST_Request $request ) { 75 | $fse_onboarding = FSE_Onboarding::instance(); 76 | 77 | $templates = $fse_onboarding->get_templates(); 78 | 79 | if ( ! $templates ) { 80 | return rest_ensure_response( 81 | array( 82 | 'success' => false, 83 | 'data' => array( 84 | 'message' => __( 'Missing templates', 'otter-blocks' ), 85 | ), 86 | ) 87 | ); 88 | } 89 | 90 | return rest_ensure_response( 91 | array( 92 | 'success' => true, 93 | 'data' => $templates, 94 | ) 95 | ); 96 | } 97 | 98 | /** 99 | * The instance method for the static class. 100 | * Defines and returns the instance of the static class. 101 | * 102 | * @static 103 | * @since 1.7.0 104 | * @access public 105 | * @return FSE_Onboarding_Server 106 | */ 107 | public static function instance() { 108 | if ( is_null( self::$instance ) ) { 109 | self::$instance = new self(); 110 | self::$instance->init(); 111 | } 112 | 113 | return self::$instance; 114 | } 115 | 116 | /** 117 | * Throw error on object clone 118 | * 119 | * The whole idea of the singleton design pattern is that there is a single 120 | * object therefore, we don't want the object to be cloned. 121 | * 122 | * @access public 123 | * @since 1.7.0 124 | * @return void 125 | */ 126 | public function __clone() { 127 | // Cloning instances of the class is forbidden. 128 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 129 | } 130 | 131 | /** 132 | * Disable unserializing of the class 133 | * 134 | * @access public 135 | * @since 1.7.0 136 | * @return void 137 | */ 138 | public function __wakeup() { 139 | // Unserializing instances of the class is forbidden. 140 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/plugins/class-review-woo-integration.php: -------------------------------------------------------------------------------- 1 | get_name(); 49 | $attributes['description'] = $product->get_short_description(); 50 | $attributes['price'] = $product->get_regular_price() ? $product->get_regular_price() : $product->get_price(); 51 | $attributes['currency'] = get_woocommerce_currency(); 52 | 53 | if ( ! empty( $product->get_sale_price() ) && $attributes['price'] !== $product->get_sale_price() ) { 54 | $attributes['discounted'] = $product->get_sale_price(); 55 | } 56 | 57 | $attributes['image'] = array( 58 | 'id' => $product->get_image_id(), 59 | 'url' => wp_get_attachment_image_url( intval( $product->get_image_id() ), '' ), 60 | 'alt' => get_post_meta( intval( $product->get_image_id() ), '_wp_attachment_image_alt', true ), 61 | ); 62 | 63 | $attributes['links'] = array( 64 | array( 65 | 'label' => __( 'Buy Now', 'otter-pro' ), 66 | 'href' => method_exists( $product, 'get_product_url' ) ? $product->get_product_url() : $product->get_permalink(), 67 | 'isSponsored' => method_exists( $product, 'get_product_url' ), 68 | ), 69 | ); 70 | 71 | return $attributes; 72 | } 73 | 74 | /** 75 | * The instance method for the static class. 76 | * Defines and returns the instance of the static class. 77 | * 78 | * @static 79 | * @since 2.0.1 80 | * @access public 81 | * @return Review_Woo_Integration 82 | */ 83 | public static function instance() { 84 | if ( is_null( self::$instance ) ) { 85 | self::$instance = new self(); 86 | self::$instance->init(); 87 | } 88 | 89 | return self::$instance; 90 | } 91 | 92 | /** 93 | * Throw error on object clone 94 | * 95 | * The whole idea of the singleton design pattern is that there is a single 96 | * object therefore, we don't want the object to be cloned. 97 | * 98 | * @access public 99 | * @since 2.0.1 100 | * @return void 101 | */ 102 | public function __clone() { 103 | // Cloning instances of the class is forbidden. 104 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 105 | } 106 | 107 | /** 108 | * Disable unserializing of the class 109 | * 110 | * @access public 111 | * @since 2.0.1 112 | * @return void 113 | */ 114 | public function __wakeup() { 115 | // Unserializing instances of the class is forbidden. 116 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /inc/patterns/call-to-action-5.php: -------------------------------------------------------------------------------- 1 | __( 'Call to Action', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'call-to-action' ), 11 | 'content' => '

Follow me on Social!

Synergestic actionables. Organic growth deep dive but circle back or but what\'s the real problem we\'re trying to solve here?

', // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation 12 | ); 13 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/plugins/class-posts-acf-integration.php: -------------------------------------------------------------------------------- 1 | '; 62 | if ( isset( $field['prepend'] ) ) { 63 | $data .= esc_html( $field['prepend'] ); 64 | } 65 | 66 | if ( isset( $field['value'] ) ) { 67 | $data .= esc_html( $field['value'] ); 68 | } elseif ( isset( $field['default_value'] ) ) { 69 | $data .= esc_html( $field['default_value'] ); 70 | } 71 | 72 | if ( isset( $field['append'] ) ) { 73 | $data .= esc_html( $field['append'] ); 74 | } 75 | $data .= ''; 76 | } 77 | } 78 | 79 | return $data; 80 | } 81 | 82 | /** 83 | * The instance method for the static class. 84 | * Defines and returns the instance of the static class. 85 | * 86 | * @static 87 | * @since 2.0.1 88 | * @access public 89 | * @return Posts_ACF_Integration 90 | */ 91 | public static function instance() { 92 | if ( is_null( self::$instance ) ) { 93 | self::$instance = new self(); 94 | self::$instance->init(); 95 | } 96 | 97 | return self::$instance; 98 | } 99 | 100 | /** 101 | * Throw error on object clone 102 | * 103 | * The whole idea of the singleton design pattern is that there is a single 104 | * object therefore, we don't want the object to be cloned. 105 | * 106 | * @access public 107 | * @since 2.0.1 108 | * @return void 109 | */ 110 | public function __clone() { 111 | // Cloning instances of the class is forbidden. 112 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 113 | } 114 | 115 | /** 116 | * Disable unserializing of the class 117 | * 118 | * @access public 119 | * @since 2.0.1 120 | * @return void 121 | */ 122 | public function __wakeup() { 123 | // Unserializing instances of the class is forbidden. 124 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /inc/patterns/call-to-action-4.php: -------------------------------------------------------------------------------- 1 | __( 'Call to Action', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'call-to-action' ), 11 | 'content' => '

Schedule a free consulting call with a representative

', // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation 12 | ); 13 | -------------------------------------------------------------------------------- /inc/patterns/testimonial-with-inline-image.php: -------------------------------------------------------------------------------- 1 | __( 'Testimonial with Inline Image', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'testimonials' ), 11 | 'content' => '

"...Sed ut perspiciatis unde omnis natus error sit voluptatem accusantium doloremque..."

Jason Doe

', 12 | ); 13 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/css/blocks/class-business-hours-css.php: -------------------------------------------------------------------------------- 1 | add_item( 38 | array( 39 | 'properties' => array( 40 | array( 41 | 'property' => 'background-color', 42 | 'value' => 'backgroundColor', 43 | ), 44 | array( 45 | 'property' => 'border-radius', 46 | 'value' => 'borderRadius', 47 | 'unit' => 'px', 48 | ), 49 | array( 50 | 'property' => 'border-width', 51 | 'value' => 'borderWidth', 52 | 'unit' => 'px', 53 | ), 54 | array( 55 | 'property' => 'border-color', 56 | 'value' => 'borderColor', 57 | ), 58 | array( 59 | 'property' => 'border-style', 60 | 'default' => 'solid', 61 | 'condition' => function ( $attrs ) { 62 | return isset( $attrs['borderWidth'] ) && ! empty( $attrs['borderWidth'] ); 63 | }, 64 | ), 65 | ), 66 | ) 67 | ); 68 | 69 | $css->add_item( 70 | array( 71 | 'selector' => ' .otter-business-hour__container .otter-business-hour__title', 72 | 'properties' => array( 73 | array( 74 | 'property' => 'text-align', 75 | 'value' => 'titleAlignment', 76 | ), 77 | array( 78 | 'property' => 'font-size', 79 | 'value' => 'titleFontSize', 80 | 'unit' => 'px', 81 | ), 82 | array( 83 | 'property' => 'color', 84 | 'value' => 'titleColor', 85 | ), 86 | ), 87 | ) 88 | ); 89 | 90 | $css->add_item( 91 | array( 92 | 'selector' => ' .otter-business-hour__container .otter-business-hour__content .wp-block-themeisle-blocks-business-hours-item', 93 | 'properties' => array( 94 | array( 95 | 'property' => 'padding-top', 96 | 'value' => 'gap', 97 | 'unit' => 'px', 98 | ), 99 | array( 100 | 'property' => 'padding-bottom', 101 | 'value' => 'gap', 102 | 'unit' => 'px', 103 | ), 104 | ), 105 | ) 106 | ); 107 | 108 | $css->add_item( 109 | array( 110 | 'selector' => ' .otter-business-hour__container .otter-business-hour__content .wp-block-themeisle-blocks-business-hours-item .otter-business-hour-item__label', 111 | 'properties' => array( 112 | array( 113 | 'property' => 'font-size', 114 | 'value' => 'itemsFontSize', 115 | 'unit' => 'px', 116 | ), 117 | ), 118 | ) 119 | ); 120 | 121 | $css->add_item( 122 | array( 123 | 'selector' => ' .otter-business-hour__container .otter-business-hour__content .wp-block-themeisle-blocks-business-hours-item .otter-business-hour-item__time', 124 | 'properties' => array( 125 | array( 126 | 'property' => 'font-size', 127 | 'value' => 'itemsFontSize', 128 | 'unit' => 'px', 129 | ), 130 | ), 131 | ) 132 | ); 133 | 134 | $style = $css->generate(); 135 | 136 | return $style; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /inc/patterns/call-to-action-3.php: -------------------------------------------------------------------------------- 1 | __( 'Call to Action', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'call-to-action' ), 11 | 'content' => '

Download my eBook for Free

Follow me on social and get exclusive recipes and deals!

', // phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation 12 | ); 13 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/plugins/class-fonts-module.php: -------------------------------------------------------------------------------- 1 | init(); 96 | } 97 | 98 | return self::$instance; 99 | } 100 | 101 | /** 102 | * Throw error on object clone 103 | * 104 | * The whole idea of the singleton design pattern is that there is a single 105 | * object therefore, we don't want the object to be cloned. 106 | * 107 | * @access public 108 | * @since 2.0.5 109 | * @return void 110 | */ 111 | public function __clone() { 112 | // Cloning instances of the class is forbidden. 113 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 114 | } 115 | 116 | /** 117 | * Disable unserializing of the class 118 | * 119 | * @access public 120 | * @since 2.0.5 121 | * @return void 122 | */ 123 | public function __wakeup() { 124 | // Unserializing instances of the class is forbidden. 125 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /docs/project-structure.md: -------------------------------------------------------------------------------- 1 | # The Everlasting Otter 2 | 3 | As time goes by, the project grows and evolves. The codebase becomes more complex. It's important to keep the project organized and easy to understand for everyone. 4 | 5 | ## Project structure 6 | 7 | - `docs/` contains the documentation of the project 8 | - `src/` contains the source code of the project (mainly JS & SCSS files) 9 | - `blocks/` the JS & SCSS files for Otter Blocks plugin 10 | - `components/` contains reusable components 11 | - `blocks/` blocks definition and functionality 12 | - `frontend/` script that add functionality for the end user. E.g.: opening tabs in accordion, sending form to the backend for Form Block. 13 | - `plugins/` global features: Global Defaults, Sticky Blocks, Copy & Paste Styles, Dynamic Content & Conditions 14 | - `helpers/` utility functions: Add an ID to the block, Google Fonts loader. 15 | - `test` contains the test files for the blocks 16 | - `css/` contains the CSS files for Custom CSS plugin 17 | - `animation/` contains the JS & SCSS files for Animation plugin 18 | - `dashboard/` contains the JS & SCSS files for Otter Dashboard (in WP: Tools > Otter ) 19 | - `export-import/` contains the JS & SCSS files for Export/Import Block plugin 20 | - `pro/` contains the JS & SCSS files for Otter Pro plugin 21 | - `blocks/` Pro blocks source files 22 | - `components/` reusable components 23 | - `dashboard/` dashboard extension with Pro features 24 | - `helpers/` utility functions 25 | - `plugins/` Pro features for Blocks: Dynamic Content & Conditions, Sticky options, Countdown options, Live Search, etc. 26 | - `woocommerce/` WooCommerce features and extensions 27 | - `inc/` contains the PHP files for all plugins 28 | - `css/` CSS dynamic generator for Blocks. It's used to generate the CSS for the blocks based on the user settings. 29 | - `integration/` Form Block utilities 30 | - `patterns/` contains the patterns for the Pattern Library 31 | - `plugins/` plugins functionality: Dynamic Content & Conditions, Stripe, WordPress Options definitions for Rest API, etc. 32 | - `render/` render classes for dynamic blocks (e.g.: Form File Field, Google/Leaflet Map, Plugin Card, Stripe Checkout, etc.) 33 | - `server/` WP REST API endpoints: Form Block, Dynamic Content & Conditions, Stripe, etc. 34 | - `plugins/` contains PHP files for other plugins 35 | - `blocks-css/` Custom CSS plugin 36 | - `blocks-animation/` Animation plugin 37 | - `blocks-export-import/` Export/Import Block plugin 38 | - `otter-pro/` Otter Pro plugin 39 | - `css/` CSS dynamic generator for Pro blocks 40 | - `plugins/` Pro features for Blocks: Dynamic Content & Conditions, Sticky options, Countdown options, Live Search, etc. 41 | - `render/` render classes for dynamic blocks: WooCommerce 42 | - `server/` WP REST API endpoints: Live Search 43 | 44 | ## Tips on navigation 45 | 46 | If you are working on Form block: `./src/blocks/blocks/form/`, `./inc/integrations/` and `./inc/server/class-form-server.php` are the main hot spots. 47 | 48 | Dealing with the CSS generation? `./inc/css/` are the main files. 49 | 50 | PHP loading related issues: `./inc/class-registration.php` is the main file. 51 | 52 | Add PHP functionality only for Otter Pro: `./plugins/otter-pro/` is the main folder. 53 | 54 | JS is not working on frontend for a block: `./src/blocks/frontend/` 55 | 56 | Add a new tab in Global Default interface for a block: `./src/blocks/plugins/options/global-defaults/controls` 57 | 58 | Add a new options in WordPress Options Settings: `./inc/plugins/class-options-settings.php` 59 | 60 | When you make a PHP file and don't know where to hook it up (make it visible to others) look at how similar file do it. API Endpoint? `./inc/server/`. CSS Generator? `./inc/css/`. Dynamic block rendering? `./inc/render/`. -------------------------------------------------------------------------------- /assets/icons/woo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /inc/patterns/image-and-text-over-dark-background.php: -------------------------------------------------------------------------------- 1 | __( 'Image and Text over Dark Background', 'otter-blocks' ), 10 | 'categories' => array( 'otter-blocks', 'featured', 'columns' ), 11 | 'content' => '

Overline

Section with image

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta.

', 12 | ); 13 | -------------------------------------------------------------------------------- /plugins/otter-pro/inc/server/class-posts-acf-server.php: -------------------------------------------------------------------------------- 1 | namespace . $this->version; 50 | 51 | register_rest_route( 52 | $namespace, 53 | '/acf-fields', 54 | array( 55 | array( 56 | 'methods' => \WP_REST_Server::READABLE, 57 | 'callback' => array( $this, 'get_acf_fields' ), 58 | 'permission_callback' => function () { 59 | return current_user_can( 'edit_posts' ); 60 | }, 61 | ), 62 | ) 63 | ); 64 | } 65 | 66 | /** 67 | * Get the ACF data about custom meta fields. 68 | * 69 | * @param mixed $request Rest request. 70 | * @since 1.7.6 71 | * @return mixed|\WP_REST_Response 72 | */ 73 | public function get_acf_fields( $request ) { 74 | $return = array( 75 | 'success' => false, 76 | ); 77 | 78 | if ( ! ( function_exists( 'acf_get_field_groups' ) && function_exists( 'acf_get_fields' ) ) ) { 79 | $return['error'] = esc_html__( 'ACF is not installed!', 'otter-pro' ); 80 | $return['eror_code'] = 1; 81 | return rest_ensure_response( $return ); 82 | } 83 | 84 | $return['groups'] = array(); 85 | $groups = acf_get_field_groups(); 86 | 87 | foreach ( $groups as $group ) { 88 | $group_data = array( 89 | 'data' => $group, 90 | 'fields' => array(), 91 | ); 92 | 93 | $fields = acf_get_fields( $group ); 94 | 95 | foreach ( $fields as $field ) { 96 | array_push( $group_data['fields'], $field ); 97 | } 98 | array_push( $return['groups'], $group_data ); 99 | } 100 | 101 | $return['success'] = true; 102 | return rest_ensure_response( $return ); 103 | } 104 | 105 | /** 106 | * The instance method for the static class. 107 | * Defines and returns the instance of the static class. 108 | * 109 | * @static 110 | * @since 1.0.0 111 | * @access public 112 | * @return Posts_ACF_Server 113 | */ 114 | public static function instance() { 115 | if ( is_null( self::$instance ) ) { 116 | self::$instance = new self(); 117 | self::$instance->init(); 118 | } 119 | 120 | return self::$instance; 121 | } 122 | 123 | /** 124 | * Throw error on object clone 125 | * 126 | * The whole idea of the singleton design pattern is that there is a single 127 | * object therefore, we don't want the object to be cloned. 128 | * 129 | * @access public 130 | * @since 1.0.0 131 | * @return void 132 | */ 133 | public function __clone() { 134 | // Cloning instances of the class is forbidden. 135 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 136 | } 137 | 138 | /** 139 | * Disable unserializing of the class 140 | * 141 | * @access public 142 | * @since 1.0.0 143 | * @return void 144 | */ 145 | public function __wakeup() { 146 | // Unserializing instances of the class is forbidden. 147 | _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-pro' ), '1.0.0' ); 148 | } 149 | } 150 | --------------------------------------------------------------------------------