├── screenshots ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-4.png ├── screenshot-5.png ├── screenshot-3-1.png └── screenshot-3-2.png ├── languages ├── mc-acf-ft-template-fr_FR.mo └── mc-acf-ft-template-fr_FR.po ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── composer.json ├── assets ├── css │ └── mc-acf-ft-css.css └── js │ └── mc-acf-ft-template.js ├── mc-acf-flexible-template.php ├── readme.md └── includes ├── mc-acf-ft-register-cpt.php └── class-mc-acf-flexible-template.php /screenshots/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/screenshots/screenshot-1.png -------------------------------------------------------------------------------- /screenshots/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/screenshots/screenshot-2.png -------------------------------------------------------------------------------- /screenshots/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/screenshots/screenshot-4.png -------------------------------------------------------------------------------- /screenshots/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/screenshots/screenshot-5.png -------------------------------------------------------------------------------- /screenshots/screenshot-3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/screenshots/screenshot-3-1.png -------------------------------------------------------------------------------- /screenshots/screenshot-3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/screenshots/screenshot-3-2.png -------------------------------------------------------------------------------- /languages/mc-acf-ft-template-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarieComet/mc-acf-flexible-template/HEAD/languages/mc-acf-ft-template-fr_FR.mo -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Screenshots** 17 | If applicable, add screenshots to help explain your problem. 18 | 19 | **WordPress and ACF Versions** 20 | WordPress version : 21 | ACF version: 22 | ACF Pro version: 23 | ACF add-ons: 24 | 25 | **Please share an export of your ACF fields groups** 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "mariecomet/mc-acf-flexible-template", 3 | "description" : "Makes it possible to save the ACF flexible content fields as templates and to use them again.", 4 | "keywords" : ["wordpress"], 5 | "homepage" : "https://github.com/MarieComet/mc-acf-flexible-template", 6 | "license" : "GPL-2.0-or-later", 7 | "type" : "wordpress-plugin", 8 | "authors" : [ 9 | { 10 | "name" : "Marie Comet", 11 | "homepage" : "https://www.mariecomet.fr" 12 | } 13 | ], 14 | "require" : { 15 | "composer/installers" : "^1.0", 16 | "php" : ">=5.3.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /assets/css/mc-acf-ft-css.css: -------------------------------------------------------------------------------- 1 | .acf-mc-ft-wrap { 2 | text-align: right; 3 | } 4 | .acf-mc-ft-wrap .popup { 5 | display: none; 6 | padding: 1.5em; 7 | right: 0; 8 | text-align: left; 9 | font-weight: normal; 10 | width: 350px; 11 | } 12 | .acf-field .acf-mc-ft-wrap .popup label { 13 | margin-bottom: 1.5em; 14 | } 15 | button.mc-open { 16 | font-weight: normal; 17 | } 18 | button.handlediv.acf-mc-ft-close { 19 | width: 15px; 20 | height: 15px; 21 | position: absolute; 22 | right: 10px; 23 | top: 10px; 24 | } 25 | button.handlediv.acf-mc-ft-close:hover { 26 | color: #fff; 27 | } 28 | .acf-mc-ft-save-wrap { 29 | margin: 1.2em 0; 30 | } 31 | .acf-mc-ft-save-wrap h3 { 32 | text-align: right; 33 | font-weight: normal; 34 | } 35 | .acf-mc-ft-save-wrap input[type="text"].acf-mc-ft-template-name, .acf-mc-import-content select.acf-templates-select, .acf-mc-ft-save-wrap select.acf-templates-terms-select { 36 | width: 100%; 37 | vertical-align: bottom; 38 | margin-right: 0.5em; 39 | min-width: 300px; 40 | } 41 | .acf-mc-ft-save-wrap input[type="text"].acf-mc-ft-template-name, .acf-mc-save-content .select2, .acf-mc-import-content .select2 { 42 | margin-top: 0.5em; 43 | color: #383838; 44 | font-weight: normal; 45 | background-color: #fff; 46 | } 47 | .acf-mc-import-content select.acf-templates-select, .acf-mc-ft-save-wrap select.acf-templates-terms-select { 48 | min-width: 341px; 49 | } 50 | .acf-mc-ft-replace-content { 51 | margin-bottom: 15px; 52 | } 53 | .acf-mc-ft-wrap .acf-success-message { 54 | display: none; 55 | border: 2px solid #46b450; 56 | border-radius: 3px; 57 | padding: 4px 7px; 58 | margin-bottom: 10px; 59 | background-color: #fff; 60 | color: #2f353e; 61 | } 62 | .acf-flexible-content .layout.just-added.bg-green { 63 | border-top: 2px solid rgba(27, 193, 41, 0.5); 64 | } 65 | -------------------------------------------------------------------------------- /mc-acf-flexible-template.php: -------------------------------------------------------------------------------- 1 | = 0 ) { 47 | $outdated_version = false; 48 | } 49 | 50 | /* Check transient, if available display notice */ 51 | if ( get_transient( 'mc-acf-admin-notice' ) && ! class_exists('acf') ) { 52 | ?> 53 |
54 |

55 |
56 | 64 |
65 |

66 |
67 | 1` in the options of this last one (after min, max and button_label options) 22 | * Export and Import will show on your flexible fields. 23 | * You can edit your templates in admin > ACF Templates. 24 | * See screenshots. 25 | 26 | ## FAQ 27 | 28 | ### Export and import buttons don't show on my flexible field 29 | 30 | * Sometime it's necessary to turn on / off option twice. 31 | 32 | ### How to define which type of user has access to these features? 33 | 34 | * There is two filters you can use for that : 35 | `mc_ft_import_cap` and `mc_ft_save_cap` which you must pass one or more WordPress capabilities. 36 | 37 | ### How to customize labels used in admin templates? 38 | 39 | * Because Flexible fields can be very different things depending on the projects, you can use 4 filters to customize the labels used in admin, and also textdomain : 40 | `mc_ft_template_plural`, `mc_ft_template_singular` and `mc_ft_template_all_menu_label`. 41 | Examples of how to use them : 42 | ``` 43 | // singular label 44 | function my_custom_template_sing_label( $singular_name ) { 45 | $singular_name = __( 'Custom singular', 'my-text-domain' ); 46 | return $singular_name; 47 | } 48 | add_filter( 'mc_ft_template_singular', 'my_custom_template_sing_label', 10, 1 ); 49 | 50 | // plural label 51 | function my_custom_template_plur_label( $plural_name ) { 52 | $plural_name = __( 'Custom plural','my-text-domain' ); 53 | return $plural_name; 54 | } 55 | add_filter( 'mc_ft_template_plural', 'my_custom_template_plur_label', 10, 1 ); 56 | 57 | // the "all" menu label 58 | function my_custom_template_menu_label( $all_menu_label ) { 59 | $all_menu_label = __( 'Custom "all" menu label', 'my-text-domain' ); 60 | return $all_menu_label; 61 | } 62 | add_filter( 'mc_ft_template_all_menu_label', 'my_custom_template_menu_label', 10, 1 ); 63 | ``` 64 | 65 | ## Screenshots 66 | 67 | * Flexible field setting 68 | ![Field setting](https://github.com/MarieComet/MC-ACF-Flexible-Template/blob/master/screenshots/screenshot-1.png "Field setting") 69 | 70 | * Save flexible layouts as template 71 | ![Save template](https://github.com/MarieComet/MC-ACF-Flexible-Template/blob/master/screenshots/screenshot-2.png "Save template") 72 | 73 | * Import flexible template 74 | ![Import template](https://github.com/MarieComet/MC-ACF-Flexible-Template/blob/master/screenshots/screenshot-3-1.png "Import template") 75 | 76 | * Flexible template imported 77 | ![Template imported](https://github.com/MarieComet/MC-ACF-Flexible-Template/blob/master/screenshots/screenshot-3-2.png "Template imported") 78 | 79 | * Flexibles templates list 80 | ![Flexibles templates list](https://github.com/MarieComet/MC-ACF-Flexible-Template/blob/master/screenshots/screenshot-4.png "Flexibles templates list") 81 | 82 | * Edit existing template 83 | ![Edit existing template](https://github.com/MarieComet/MC-ACF-Flexible-Template/blob/master/screenshots/screenshot-5.png "Edit existing template") 84 | 85 | ### History 86 | 87 | ### 2017-12-23 1.0.1 88 | * Initial Commit 89 | 90 | ### 2018-01-04 1.0.2 91 | * Add edit templates functionnality 92 | * Add filters to edit admin labels 93 | 94 | ### 2018-07-14 1.1.0 95 | * Update for ACF 5.7.0 JS API changes 96 | * Important : require at LEAST ACF 5.7.0 97 | 98 | ### 2018-11-01 99 | * Add new feature "replace existing content" before import. Thanks @virgo79 ! 100 | 101 | ### March 13 2020 102 | * Tested with WordPress 5.3.2 and ACF Pro 5.8.7. 103 | 104 | ### October 21 2021 1.1.1 105 | * Allow html tags in acf field label (https://github.com/MarieComet/mc-acf-flexible-template/issues/23) -------------------------------------------------------------------------------- /includes/mc-acf-ft-register-cpt.php: -------------------------------------------------------------------------------- 1 | $plural_name, 23 | 'singular_name' => $singular_name, 24 | 'menu_name' => $plural_name, 25 | 'name_admin_bar' => $singular_name, 26 | 'archives' => __( 'ACF Template Archives', 'mc-acf-ft-template' ), 27 | 'attributes' => __( 'ACF Template Attributes', 'mc-acf-ft-template' ), 28 | 'parent_item_colon' => __( 'Parent Item:', 'mc-acf-ft-template' ), 29 | 'all_items' => $all_menu_label, 30 | 'add_new_item' => __( 'Add New Item', 'mc-acf-ft-template' ), 31 | 'add_new' => __( 'Add New', 'mc-acf-ft-template' ), 32 | 'new_item' => __( 'New Item', 'mc-acf-ft-template' ), 33 | 'edit_item' => __( 'Edit Item', 'mc-acf-ft-template' ), 34 | 'update_item' => __( 'Update Item', 'mc-acf-ft-template' ), 35 | 'view_item' => __( 'View Item', 'mc-acf-ft-template' ), 36 | 'view_items' => __( 'View Items', 'mc-acf-ft-template' ), 37 | 'search_items' => __( 'Search Item', 'mc-acf-ft-template' ), 38 | 'not_found' => __( 'Not found', 'mc-acf-ft-template' ), 39 | 'not_found_in_trash' => __( 'Not found in Trash', 'mc-acf-ft-template' ), 40 | 'featured_image' => __( 'Featured Image', 'mc-acf-ft-template' ), 41 | 'set_featured_image' => __( 'Set featured image', 'mc-acf-ft-template' ), 42 | 'remove_featured_image' => __( 'Remove featured image', 'mc-acf-ft-template' ), 43 | 'use_featured_image' => __( 'Use as featured image', 'mc-acf-ft-template' ), 44 | 'insert_into_item' => __( 'Insert into item', 'mc-acf-ft-template' ), 45 | 'uploaded_to_this_item' => __( 'Uploaded to this item', 'mc-acf-ft-template' ), 46 | 'items_list' => __( 'Items list', 'mc-acf-ft-template' ), 47 | 'items_list_navigation' => __( 'Items list navigation', 'mc-acf-ft-template' ), 48 | 'filter_items_list' => __( 'Filter items list', 'mc-acf-ft-template' ), 49 | ); 50 | $args = array( 51 | 'label' => __( 'ACF Template', 'mc-acf-ft-template' ), 52 | 'description' => __( 'ACF Template', 'mc-acf-ft-template' ), 53 | 'labels' => $labels, 54 | 'supports' => array( 'title', 'custom-fields' ), 55 | 'hierarchical' => false, 56 | 'public' => false, 57 | 'show_ui' => true, 58 | 'show_in_menu' => true, 59 | 'menu_position' => 80, 60 | 'menu_icon' => 'dashicons-layout', 61 | 'show_in_admin_bar' => false, 62 | 'show_in_nav_menus' => false, 63 | 'can_export' => true, 64 | 'has_archive' => false, 65 | 'exclude_from_search' => true, 66 | 'publicly_queryable' => false, 67 | 'map_meta_cap' => true, 68 | 'capability_type' => 'post', 69 | 'capabilities' => array( 70 | 'create_posts' => false, 71 | // Removes support for the "Add New" function ( use 'do_not_allow' instead of false for multisite set ups ) 72 | ), 73 | 'show_in_rest' => true, 74 | ); 75 | register_post_type( 'acf_template', $args ); 76 | 77 | } 78 | add_action( 'init', 'mc_acf_ft_register_cpt', 0 ); 79 | 80 | // Register Custom Taxonomy 81 | function mc_acf_ft_register_tax() { 82 | 83 | $labels = array( 84 | 'name' => _x( 'Categories', 'Taxonomy General Name', 'mc-acf-ft-template' ), 85 | 'singular_name' => _x( 'Category', 'Taxonomy Singular Name', 'mc-acf-ft-template' ), 86 | 'menu_name' => __( 'Categories', 'mc-acf-ft-template' ), 87 | 'all_items' => __( 'Categories', 'mc-acf-ft-template' ), 88 | 'parent_item' => __( 'Parent Item', 'mc-acf-ft-template' ), 89 | 'parent_item_colon' => __( 'Parent Item:', 'mc-acf-ft-template' ), 90 | 'new_item_name' => __( 'New Item Name', 'mc-acf-ft-template' ), 91 | 'add_new_item' => __( 'Add New Item', 'mc-acf-ft-template' ), 92 | 'edit_item' => __( 'Edit Item', 'mc-acf-ft-template' ), 93 | 'update_item' => __( 'Update Item', 'mc-acf-ft-template' ), 94 | 'view_item' => __( 'View Item', 'mc-acf-ft-template' ), 95 | 'separate_items_with_commas' => __( 'Separate items with commas', 'mc-acf-ft-template' ), 96 | 'add_or_remove_items' => __( 'Add or remove items', 'mc-acf-ft-template' ), 97 | 'choose_from_most_used' => __( 'Choose from the most used', 'mc-acf-ft-template' ), 98 | 'popular_items' => __( 'Popular Items', 'mc-acf-ft-template' ), 99 | 'search_items' => __( 'Search Items', 'mc-acf-ft-template' ), 100 | 'not_found' => __( 'Not Found', 'mc-acf-ft-template' ), 101 | 'no_terms' => __( 'No items', 'mc-acf-ft-template' ), 102 | 'items_list' => __( 'Items list', 'mc-acf-ft-template' ), 103 | 'items_list_navigation' => __( 'Items list navigation', 'mc-acf-ft-template' ), 104 | ); 105 | $args = array( 106 | 'labels' => $labels, 107 | 'hierarchical' => true, 108 | 'public' => false, 109 | 'show_ui' => true, 110 | 'show_admin_column' => true, 111 | 'show_in_nav_menus' => false, 112 | 'show_tagcloud' => false, 113 | 'rewrite' => false, 114 | 'show_in_rest' => true, 115 | ); 116 | register_taxonomy( 'acf_template_tax', array( 'acf_template' ), $args ); 117 | } 118 | add_action( 'init', 'mc_acf_ft_register_tax', 0 ); 119 | 120 | // Register custom column in admin 121 | 122 | add_filter( 'manage_acf_template_posts_columns', 'set_custom_edit_acf_template_columns' ); 123 | add_action( 'manage_acf_template_posts_custom_column' , 'custom_acf_template_column', 10, 2 ); 124 | 125 | function set_custom_edit_acf_template_columns( $columns ) { 126 | $columns['acf_template_group'] = __( 'Saved from field :', 'mc-acf-ft-template' ); 127 | return $columns; 128 | } 129 | 130 | function custom_acf_template_column( $column, $post_id ) { 131 | switch ( $column ) { 132 | 133 | case 'acf_template_group' : 134 | $layout_parent_key = get_post_meta($post_id, '_flex_layout_parent', true ); 135 | $layout_parent_obj = get_field_object($layout_parent_key); 136 | if ( $layout_parent_obj ) 137 | echo $layout_parent_obj['label']; 138 | else 139 | _e( 'Unable to get parent flexible', 'mc-acf-ft-template' ); 140 | break; 141 | } 142 | } 143 | 144 | function mc_acf_ft_rule_type( $choices ) { 145 | 146 | if ( isset( $choices['acf_template'] ) ) { 147 | unset( $choices['acf_template'] ); 148 | } 149 | return $choices; 150 | } 151 | add_filter( 'acf/location/rule_values', 'mc_acf_ft_rule_type' ); -------------------------------------------------------------------------------- /languages/mc-acf-ft-template-fr_FR.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: MC ACF Flexible Template\n" 4 | "POT-Creation-Date: 2018-01-04 09:55+0100\n" 5 | "PO-Revision-Date: 2018-01-04 09:57+0100\n" 6 | "Last-Translator: \n" 7 | "Language-Team: Marie Comet \n" 8 | "Language: fr_FR\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "X-Generator: Poedit 1.8.7\n" 13 | "X-Poedit-Basepath: ..\n" 14 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 15 | "X-Poedit-SourceCharset: UTF-8\n" 16 | "X-Poedit-KeywordsList: _e;__;_x\n" 17 | "X-Poedit-SearchPath-0: .\n" 18 | 19 | #: includes/mc-acf-ft-class.php:81 20 | msgid "Save and load templates functionality" 21 | msgstr "Fonctionnalité de sauvegarde et chargement des modèles" 22 | 23 | #: includes/mc-acf-ft-class.php:82 24 | msgid "This flexible field should display save/load functionnality ?" 25 | msgstr "" 26 | "Souhaitez-vous afficher la fonctionnalité de sauvegarde/chargement de " 27 | "modèles ?" 28 | 29 | #: includes/mc-acf-ft-class.php:147 30 | msgid "Load template" 31 | msgstr "Charger un modèle" 32 | 33 | #: includes/mc-acf-ft-class.php:151 34 | msgid "Close import modal." 35 | msgstr "Fermer la boîte de dialogue de chargement." 36 | 37 | #: includes/mc-acf-ft-class.php:172 38 | msgid "Select a template :" 39 | msgstr "Sélectionnez un modèle :" 40 | 41 | #: includes/mc-acf-ft-class.php:173 42 | msgid "Select a template" 43 | msgstr "Sélectionnez un modèle" 44 | 45 | #: includes/mc-acf-ft-class.php:205 46 | msgid "Uncategorised" 47 | msgstr "Non classé(s)" 48 | 49 | #: includes/mc-acf-ft-class.php:213 50 | msgid "Load" 51 | msgstr "Charger" 52 | 53 | #: includes/mc-acf-ft-class.php:215 54 | msgid "No template found for this flexible" 55 | msgstr "Pas de modèle trouvé pour ce champs flexible." 56 | 57 | #: includes/mc-acf-ft-class.php:238 58 | msgid "Save template" 59 | msgstr "Sauvegarder le modèle" 60 | 61 | #: includes/mc-acf-ft-class.php:242 62 | msgid "Close save modal." 63 | msgstr "Fermer la boîte de dialogue de sauvegarde." 64 | 65 | #: includes/mc-acf-ft-class.php:248 66 | msgid "Select one or more categories :" 67 | msgstr "Sélectionnez une ou plusieurs catégorie(s)" 68 | 69 | #: includes/mc-acf-ft-class.php:249 70 | msgid "Select" 71 | msgstr "Sélection" 72 | 73 | #: includes/mc-acf-ft-class.php:257 74 | msgid "Name the template :" 75 | msgstr "Nommez le modèle :" 76 | 77 | #: includes/mc-acf-ft-class.php:261 78 | msgid "Save" 79 | msgstr "Sauvegarder" 80 | 81 | #: includes/mc-acf-ft-class.php:279 includes/mc-acf-ft-class.php:402 82 | msgid "Ok!" 83 | msgstr "" 84 | 85 | #: includes/mc-acf-ft-class.php:298 86 | msgid "You can't save empty template." 87 | msgstr "Vous ne pouvez pas sauvegarder un modèle vide." 88 | 89 | #: includes/mc-acf-ft-class.php:306 90 | msgid "Template name input not found." 91 | msgstr "Le champs du nom du modèle n'a pas été trouvé." 92 | 93 | #: includes/mc-acf-ft-class.php:314 94 | msgid "Please fill the template name." 95 | msgstr "Saisissez un nom pour ce modèle." 96 | 97 | #: includes/mc-acf-ft-class.php:346 98 | msgid "Error creating post." 99 | msgstr "Erreur lors de la création du modèle." 100 | 101 | #: includes/mc-acf-ft-class.php:370 102 | msgid "Template saved, remember to save the post." 103 | msgstr "Modèle sauvegardé, pensez à enregistrer votre contenu." 104 | 105 | #: includes/mc-acf-ft-class.php:375 106 | msgid "No layouts for this flexible field." 107 | msgstr "Aucun layout trouvé pour ce champs flexible." 108 | 109 | #: includes/mc-acf-ft-class.php:421 110 | msgid "Import template select not found." 111 | msgstr "Le sélecteur de modèle n'a pas été trouvé." 112 | 113 | #: includes/mc-acf-ft-class.php:429 114 | msgid "Please select a template." 115 | msgstr "Sélectionnez un modèle." 116 | 117 | #: includes/mc-acf-ft-class.php:506 118 | msgid "Template imported, remember to save the post." 119 | msgstr "Modèle chargé, pensez à enregistrer votre contenu." 120 | 121 | #: includes/mc-acf-ft-class.php:512 122 | msgid "ACF main class not found." 123 | msgstr "La classe ACF n'a pas été trouvée." 124 | 125 | #: includes/mc-acf-ft-class.php:518 126 | msgid "You can't import empty template." 127 | msgstr "Vous ne pouvez pas charger un modèle vide." 128 | 129 | #: includes/mc-acf-ft-class.php:590 130 | msgid "Save as template :" 131 | msgstr "Sauvegarder le modèle :" 132 | 133 | #: includes/mc-acf-ft-register-cpt.php:14 134 | #: includes/mc-acf-ft-register-cpt.php:53 135 | #: includes/mc-acf-ft-register-cpt.php:54 136 | msgid "ACF Template" 137 | msgstr "Modèle ACF" 138 | 139 | #: includes/mc-acf-ft-register-cpt.php:17 140 | msgid "ACF Templates" 141 | msgstr "Modèles ACF" 142 | 143 | #: includes/mc-acf-ft-register-cpt.php:20 144 | msgid "All ACF Templates" 145 | msgstr "Tous les modèles ACF" 146 | 147 | #: includes/mc-acf-ft-register-cpt.php:28 148 | msgid "ACF Template Archives" 149 | msgstr "Archives des modèles ACF" 150 | 151 | #: includes/mc-acf-ft-register-cpt.php:29 152 | msgid "ACF Template Attributes" 153 | msgstr "Attributs des modèles ACF" 154 | 155 | #: includes/mc-acf-ft-register-cpt.php:30 156 | #: includes/mc-acf-ft-register-cpt.php:91 157 | msgid "Parent Item:" 158 | msgstr "Parent:" 159 | 160 | #: includes/mc-acf-ft-register-cpt.php:32 161 | #: includes/mc-acf-ft-register-cpt.php:93 162 | msgid "Add New Item" 163 | msgstr "Ajouter" 164 | 165 | #: includes/mc-acf-ft-register-cpt.php:33 166 | msgid "Add New" 167 | msgstr "Ajouter" 168 | 169 | #: includes/mc-acf-ft-register-cpt.php:34 170 | msgid "New Item" 171 | msgstr "Nouveau" 172 | 173 | #: includes/mc-acf-ft-register-cpt.php:35 174 | #: includes/mc-acf-ft-register-cpt.php:94 175 | msgid "Edit Item" 176 | msgstr "Modifier" 177 | 178 | #: includes/mc-acf-ft-register-cpt.php:36 179 | #: includes/mc-acf-ft-register-cpt.php:95 180 | msgid "Update Item" 181 | msgstr "Mettre à jour" 182 | 183 | #: includes/mc-acf-ft-register-cpt.php:37 184 | #: includes/mc-acf-ft-register-cpt.php:96 185 | msgid "View Item" 186 | msgstr "Voir" 187 | 188 | #: includes/mc-acf-ft-register-cpt.php:38 189 | msgid "View Items" 190 | msgstr "Voir" 191 | 192 | #: includes/mc-acf-ft-register-cpt.php:39 193 | msgid "Search Item" 194 | msgstr "" 195 | 196 | #: includes/mc-acf-ft-register-cpt.php:40 197 | msgid "Not found" 198 | msgstr "" 199 | 200 | #: includes/mc-acf-ft-register-cpt.php:41 201 | msgid "Not found in Trash" 202 | msgstr "" 203 | 204 | #: includes/mc-acf-ft-register-cpt.php:42 205 | msgid "Featured Image" 206 | msgstr "" 207 | 208 | #: includes/mc-acf-ft-register-cpt.php:43 209 | msgid "Set featured image" 210 | msgstr "" 211 | 212 | #: includes/mc-acf-ft-register-cpt.php:44 213 | msgid "Remove featured image" 214 | msgstr "" 215 | 216 | #: includes/mc-acf-ft-register-cpt.php:45 217 | msgid "Use as featured image" 218 | msgstr "" 219 | 220 | #: includes/mc-acf-ft-register-cpt.php:46 221 | msgid "Insert into item" 222 | msgstr "" 223 | 224 | #: includes/mc-acf-ft-register-cpt.php:47 225 | msgid "Uploaded to this item" 226 | msgstr "" 227 | 228 | #: includes/mc-acf-ft-register-cpt.php:48 229 | #: includes/mc-acf-ft-register-cpt.php:104 230 | msgid "Items list" 231 | msgstr "" 232 | 233 | #: includes/mc-acf-ft-register-cpt.php:49 234 | #: includes/mc-acf-ft-register-cpt.php:105 235 | msgid "Items list navigation" 236 | msgstr "" 237 | 238 | #: includes/mc-acf-ft-register-cpt.php:50 239 | msgid "Filter items list" 240 | msgstr "" 241 | 242 | #: includes/mc-acf-ft-register-cpt.php:86 243 | #: includes/mc-acf-ft-register-cpt.php:88 244 | #: includes/mc-acf-ft-register-cpt.php:89 245 | msgid "Categories" 246 | msgstr "Catégories" 247 | 248 | #: includes/mc-acf-ft-register-cpt.php:87 249 | msgid "Category" 250 | msgstr "" 251 | 252 | #: includes/mc-acf-ft-register-cpt.php:90 253 | msgid "Parent Item" 254 | msgstr "" 255 | 256 | #: includes/mc-acf-ft-register-cpt.php:92 257 | msgid "New Item Name" 258 | msgstr "" 259 | 260 | #: includes/mc-acf-ft-register-cpt.php:97 261 | msgid "Separate items with commas" 262 | msgstr "" 263 | 264 | #: includes/mc-acf-ft-register-cpt.php:98 265 | msgid "Add or remove items" 266 | msgstr "" 267 | 268 | #: includes/mc-acf-ft-register-cpt.php:99 269 | msgid "Choose from the most used" 270 | msgstr "" 271 | 272 | #: includes/mc-acf-ft-register-cpt.php:100 273 | msgid "Popular Items" 274 | msgstr "" 275 | 276 | #: includes/mc-acf-ft-register-cpt.php:101 277 | msgid "Search Items" 278 | msgstr "" 279 | 280 | #: includes/mc-acf-ft-register-cpt.php:102 281 | msgid "Not Found" 282 | msgstr "" 283 | 284 | #: includes/mc-acf-ft-register-cpt.php:103 285 | msgid "No items" 286 | msgstr "" 287 | 288 | #: includes/mc-acf-ft-register-cpt.php:128 289 | msgid "Saved from field :" 290 | msgstr "Sauvegardé depuis le champs :" 291 | 292 | #: includes/mc-acf-ft-register-cpt.php:141 293 | msgid "Unable to get parent flexible" 294 | msgstr "Impossible de récupérer le champs flexible parent." 295 | 296 | #: mc-acf-flexible-template.php:47 297 | msgid "" 298 | "MC ACF Flexible Template plugin needs \"Advanced Custom Fields Pro\" to run. " 299 | "Please download and activate it" 300 | msgstr "" 301 | "L'extension MC ACF Flexible Template requiert \"Advanced Custom Fields Pro\" " 302 | "pour fonctionner. Téléchargez et installez la." 303 | 304 | #~ msgid "All" 305 | #~ msgstr "Tous" 306 | 307 | #~ msgid "Select a category" 308 | #~ msgstr "Sélectionnez une catégorie" 309 | 310 | #~ msgid "Select one or more terms :" 311 | #~ msgstr "Sélectionnez un ou plusieurs termes :" 312 | 313 | #~ msgid "Select a term" 314 | #~ msgstr "Sélectionnez un terme" 315 | 316 | #~ msgid "All Categories" 317 | #~ msgstr "Toutes les catégories" 318 | 319 | #~ msgid "All Items" 320 | #~ msgstr "Tous les modèles" 321 | 322 | #~ msgid "Choose a template :" 323 | #~ msgstr "Sélectionnez un modèle :" 324 | 325 | #~ msgid "Original Flexible" 326 | #~ msgstr "Flexible " 327 | -------------------------------------------------------------------------------- /assets/js/mc-acf-ft-template.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function($){ 2 | 3 | // make sure acf is loaded, it should be, but just in case 4 | if (typeof acf == 'undefined') { return; } 5 | 6 | $('.mc-acf-ft-select2').select2(); 7 | 8 | var MC_ACF_Flexible_Template = new acf.Model({ 9 | 10 | events: { 11 | 'click button.mc-open': '_open_popup', 12 | 'click button.acf-mc-ft-close': '_close_popup', 13 | 'click .acf-mc-ft-import': '_import_template', 14 | 'click .acf-mc-ft-save': '_save_template', 15 | }, 16 | 17 | _open_popup: function( e, $el ) { 18 | 19 | e.preventDefault(); 20 | e.stopImmediatePropagation(); 21 | 22 | // little fix sometimes select are not initialized 23 | $( '.mc-acf-ft-select2' ).each( function ( i, obj ) { 24 | if ( !$( obj ).hasClass( 'select2-hidden-accessible' ) ) { 25 | $( obj ).select2(); 26 | } 27 | }); 28 | 29 | // close other popups 30 | $( '.popup' ).hide(); 31 | // vars 32 | var popup = $( $el.next( '.popup' ) ); 33 | 34 | if ( popup.length ) { 35 | $( popup ).show(); 36 | $( popup ).removeClass( '-close' ); 37 | $( popup ).addClass( '-open' ); 38 | } 39 | }, 40 | 41 | _close_popup: function( e, $el ) { 42 | 43 | e.preventDefault(); 44 | e.stopImmediatePropagation(); 45 | // vars 46 | 47 | var popup = $( $el.parent( '.popup' ) ); 48 | if ( popup.length ) { 49 | $( popup ).hide(); 50 | $( popup ).removeClass( '-open' ); 51 | $( popup ).addClass( '-close' ); 52 | } 53 | }, 54 | // Import template 55 | _import_template: function( e, $el ) { 56 | e.preventDefault(); 57 | 58 | var parentFlex = $el.closest( '.acf-field-flexible-content' ); 59 | 60 | // acf flexible ref 61 | var acfFlexible = acf.getField( parentFlex ); 62 | 63 | var parentValues = parentFlex.find( '.values' ); 64 | 65 | var layouts = parentValues.find( '.layout' ); 66 | 67 | var replaceCheckbox = parentFlex.find('input[name=replace_content]'); 68 | 69 | var numberLayouts = layouts.length; 70 | 71 | var error_div = parentFlex.find( '.acf-mc-ft-import-error' ); 72 | var succes_div = parentFlex.find( '.acf-mc-ft-import-success' ); 73 | 74 | $form = $( 'form#post' ); 75 | 76 | var template_select = parentFlex.find('.acf-templates-select'); 77 | var selectedTemplate = $(template_select).val(); 78 | 79 | var data = { 80 | action : 'mc_acf_import_template', 81 | acf_templates : selectedTemplate, 82 | number_layout : numberLayouts 83 | }; 84 | 85 | data = acf.prepare_for_ajax( data ); 86 | 87 | // set busy 88 | acf.validation.busy = 1; 89 | 90 | // lock form 91 | acf.validation.lockForm( $form ); 92 | 93 | var validLayout = false; 94 | 95 | $.post({ 96 | url: acf.get( 'ajaxurl' ), 97 | type: 'post', 98 | data: data, 99 | dataType: 'json', 100 | action: 'mc_acf_import_template', 101 | success: function( json ) { 102 | 103 | if( true === json.success ) { 104 | 105 | $( error_div ).hide(); 106 | 107 | if( replaceCheckbox.is(':checked') ) { 108 | 109 | layouts.each( function( key, value ) { 110 | acfFlexible.removeLayout( $(value) ); 111 | }); 112 | 113 | } 114 | 115 | var layoutsHtml = $( json.data.layouts ); 116 | 117 | // loop on layouts 118 | $.each( layoutsHtml, function( key, value ) { 119 | 120 | // bail early if validation fails 121 | if( ! acfFlexible.validateAdd() ) { 122 | 123 | validLayout = false; 124 | return false; 125 | 126 | } else { 127 | 128 | // create object for use it later 129 | var newItem = $( value ); 130 | 131 | // append to parent 132 | $( parentValues ).append( newItem ); 133 | // this action set the field and render correctly tabs, etc. 134 | acf.do_action( 'append', newItem ); 135 | // add -collapsed class, if you want all new layouts opened 136 | $( newItem ).addClass( 'just-added bg-green' ); 137 | // remove the empty div 138 | $( parentFlex ).find( '.no-value-message' ).hide(); 139 | 140 | validLayout = true; 141 | 142 | } 143 | 144 | }); 145 | 146 | if ( validLayout ) { 147 | $( succes_div ).text( json.data.message ).show(); 148 | } 149 | 150 | setTimeout(function(){ 151 | $( succes_div ).text( '' ).hide(); 152 | // reset select 153 | $( template_select ).val(null).trigger( 'change' ); 154 | // close other popups 155 | $( '.popup' ).hide(); 156 | }, 3000); 157 | 158 | } else { 159 | //console.log(json.data.message); 160 | $( succes_div ).hide(); 161 | $( error_div ).text( json.data.message ).show(); 162 | } 163 | 164 | // unlock so WP can publish form 165 | acf.validation.busy = 0; 166 | acf.validation.unlockForm( $form ); 167 | }, 168 | error: function( json ) { 169 | console.log( json ); 170 | } 171 | }); 172 | }, 173 | 174 | // Save template 175 | _save_template: function( e, $el ) { 176 | e.preventDefault(); 177 | 178 | var parentFlex = $el.closest( '.acf-field-flexible-content' ); 179 | 180 | var parentGroupKey = parentFlex.attr( 'data-key' ); 181 | 182 | var parentValues = parentFlex.find( '.values' ); 183 | 184 | var template_name_input = parentFlex.find( '.acf-mc-ft-template-name' ); 185 | var template_name = $( template_name_input ).val(); 186 | 187 | var template_terms_select = parentFlex.find( '.acf-templates-terms-select' ); 188 | var template_terms = $( template_terms_select ).val(); 189 | 190 | var error_div = parentFlex.find( '.acf-mc-ft-save-error' ); 191 | var succes_div = parentFlex.find( '.acf-mc-ft-save-success' ); 192 | 193 | $form = $( 'form#post' ); 194 | 195 | acf.doAction('validation_begin', $form); 196 | 197 | var data = acf.serialize( parentValues ); 198 | 199 | // append AJAX action 200 | data.action = 'mc_acf_ft_save_template'; 201 | data.mc_acf_template_name = template_name; 202 | 203 | if ( parentGroupKey.length ) { 204 | data.mc_acf_parent_key = parentGroupKey; 205 | } 206 | 207 | if ( template_terms ) { 208 | data.mc_acf_template_terms = template_terms; 209 | } 210 | 211 | data = acf.prepare_for_ajax( data ); 212 | 213 | // set busy 214 | acf.validation.busy = 1; 215 | 216 | // lock form 217 | acf.validation.lockForm( $form ); 218 | 219 | $.post({ 220 | url: acf.get( 'ajaxurl' ), 221 | type: 'post', 222 | data: data, 223 | dataType: 'json', 224 | action: 'mc_acf_ft_save_template', 225 | success: function( json ) { 226 | if ( true === json.success ) { 227 | $( error_div ).hide(); 228 | $( succes_div ).text( json.data.message ).show(); 229 | setTimeout(function(){ 230 | $( succes_div ).text( '' ).hide(); 231 | // reset inputs 232 | $( template_name_input ).val( '' ); 233 | $( template_terms_select ).val(null).trigger( 'change' ); 234 | // close other popups 235 | $( '.popup' ).hide(); 236 | }, 3000); 237 | } else { 238 | //console.log(json.data.message); 239 | $( succes_div ).hide(); 240 | $( error_div ).text( json.data.message ).show(); 241 | } 242 | // unlock so WP can publish form 243 | acf.validation.busy = 0; 244 | acf.validation.unlockForm( $form ); 245 | }, 246 | error: function( json ) { 247 | console.log('erreur'); 248 | } 249 | }); 250 | } 251 | }); 252 | }); 253 | -------------------------------------------------------------------------------- /includes/class-mc-acf-flexible-template.php: -------------------------------------------------------------------------------- 1 | true, 46 | 'class' => true, 47 | 'value' => true, 48 | 'name' => true, 49 | ]; 50 | $tags[ 'select' ] = [ 51 | 'style' => true, 52 | 'class' => true, 53 | 'data-placeholder' => true, 54 | 'name' => true, 55 | ]; 56 | $tags[ 'option' ] = [ 57 | 'value' => true, 58 | ]; 59 | } 60 | return $tags; 61 | } 62 | 63 | /* 64 | * mc_ft_acf_update_template 65 | * hooked on acf/save_post 66 | * Update ACF templates data 67 | * @param $post_id INT 68 | * @since 1.0.2 69 | */ 70 | public function mc_ft_acf_update_template( $post_id ) { 71 | 72 | if ( wp_is_post_revision( $post_id ) ) { 73 | return; 74 | } 75 | 76 | $post_type = get_post_type( $post_id ); 77 | 78 | if ( $post_type !== 'acf_template' ) { 79 | return; 80 | } 81 | 82 | if ( empty( $_POST['acf'] ) ) { 83 | return; 84 | } 85 | 86 | $fields = $_POST['acf']; 87 | 88 | 89 | if ( ! empty( $fields ) && is_array( $fields ) ) { 90 | foreach ( $fields as $key => $field ) { 91 | 92 | if ( ! is_serialized( $field ) ) { 93 | $field = maybe_serialize( $field ); 94 | } 95 | 96 | if ( is_string( $field ) ) { 97 | $field = wp_slash( $field ); 98 | } 99 | 100 | if ( !empty( $field ) ) { 101 | update_post_meta( $post_id, '_flex_layout_data', $field ); 102 | } 103 | } 104 | } 105 | // unset ACF post data because we don't want to add this to post_meta 106 | unset( $_POST['acf'] ); 107 | } 108 | 109 | /* 110 | * mc_ft_acf_field_groupos_add_settings 111 | * hooked on acf/render_field_settings 112 | * Add an option to flexible field to turn on/off import and export function by field 113 | * @param $field (array) 114 | * @since 1.0.1 115 | */ 116 | public function mc_ft_acf_field_groups_add_settings( $field ) { 117 | // min 118 | acf_render_field_setting( $field, array( 119 | 'label' => __('Save and load templates functionality','mc-acf-ft-template'), 120 | 'instructions' => __('This flexible field should display save/load functionnality ?', 'mc-acf-ft-template'), 121 | 'type' => 'true_false', 122 | 'name' => 'mc_acf_ft_true_false', 123 | 'ui' => 1, 124 | 'default_value' => true, 125 | ) ); 126 | } 127 | /* 128 | * mc_ft_add_filter_label 129 | * hooked on acf_get_field_label 130 | * Display the select box for import and export templates 131 | * @param $field (array) 132 | * @return $label (string) 133 | * @since 1.0.1 134 | */ 135 | public function mc_ft_add_filter_label( $label, $field ){ 136 | global $post, $pagenow, $typenow; 137 | 138 | if ( isset( $field['type'] ) 139 | && $field['type'] == 'flexible_content' 140 | && isset( $field['mc_acf_ft_true_false'] ) && $field['mc_acf_ft_true_false'] 141 | && ! in_array( $typenow, array( 'acf-field-group', 'attachment', 'acf_template' ) ) 142 | && isset( $field['key'] ) 143 | && ! empty( $field['key'] ) ) { 144 | 145 | $label .= '
'; 146 | ob_start(); 147 | // Capability for export and import 148 | $import_cap = 'edit_others_pages'; 149 | $import_cap = apply_filters( 'mc_ft_import_cap', $import_cap ); 150 | 151 | if ( current_user_can( $import_cap ) ) { 152 | $label .= $this->mc_ft_select_display( $field['key'] ); 153 | } 154 | 155 | $save_cap = 'edit_others_pages'; 156 | $save_cap = apply_filters( 'mc_ft_save_cap', $save_cap ); 157 | 158 | if ( current_user_can( $save_cap ) ) { 159 | $label .= $this->mc_ft_save_display( $field['key'] ); 160 | } 161 | $label .= ob_get_clean(); 162 | $label .= '
'; 163 | 164 | } 165 | 166 | return $label; 167 | } 168 | 169 | /* 170 | * mc_ft_select_display 171 | * get acf_template CPT list for the current field group 172 | * $field_key : the current field key group 173 | * @since 1.0.1 174 | */ 175 | public function mc_ft_select_display( $field_key ) { 176 | 177 | $templates_tax = get_terms( array( 178 | 'taxonomy' => 'acf_template_tax', 179 | 'hide_empty' => true, 180 | 'orderby' => 'name' 181 | ) ); 182 | 183 | ?> 184 | 187 | 188 | 266 | 'acf_template_tax', 278 | 'hide_empty' => false, 279 | 'orderby' => 'name' 280 | ) ); 281 | 282 | ?> 283 | 286 | 287 | 311 | true, 324 | 'valid' => 1, 325 | 'errors' => 0, 326 | 'message' => __( 'Ok!', 'mc-acf-ft-template' ) 327 | ); 328 | 329 | // default error array 330 | $error = array( 331 | 'code' => '', 332 | 'message' => '', 333 | ); 334 | 335 | // we can use the acf nonce to verify the request 336 | if ( ! wp_verify_nonce( $_POST['nonce'], 'acf_nonce' ) ) { 337 | $error = -1; 338 | wp_send_json_error( $error ); 339 | exit; 340 | } 341 | 342 | // make sure we have ACF data 343 | if ( ! isset( $_POST['acf'] ) || isset( $_POST['acf'] ) && empty( $_POST['acf'] ) ) { 344 | $error['code'] = 0; 345 | $error['message'] = __( 'You can\'t save empty template.', 'mc-acf-ft-template' ); 346 | wp_send_json_error( $error ); 347 | exit; 348 | } 349 | 350 | // make sure our template name is set 351 | if ( ! isset( $_POST['mc_acf_template_name'] ) ) { 352 | $error['code'] = -1; 353 | $error['message'] = __( 'Template name input not found.', 'mc-acf-ft-template' ); 354 | wp_send_json_error( $error ); 355 | exit; 356 | } 357 | 358 | // make sure our template name is set and not empty 359 | if ( isset( $_POST['mc_acf_template_name'] ) && empty( $_POST['mc_acf_template_name'] ) ) { 360 | $error['code'] = -2; 361 | $error['message'] = __( 'Please fill the template name.', 'mc-acf-ft-template' ); 362 | wp_send_json_error( $error ); 363 | exit; 364 | } 365 | 366 | // we have our custom field and acf post data 367 | if ( isset( $_POST['mc_acf_parent_key'] ) && ! empty( $_POST['mc_acf_parent_key'] ) ) { 368 | 369 | $template_name = $_POST['mc_acf_template_name']; 370 | 371 | // check if we have an existing template with that name 372 | $maybe_exist_template = get_page_by_title( $template_name, OBJECT, 'acf_template'); 373 | 374 | if ( $maybe_exist_template ) { 375 | $error['code'] = 0; 376 | $error['message'] = __( 'A template already exists with that name. Please enter another name.', 'mc-acf-ft-template' ); 377 | wp_send_json_error( $error ); 378 | exit; 379 | } 380 | 381 | $parent_key = $_POST['mc_acf_parent_key']; 382 | 383 | $fields = $_POST['acf'][$parent_key]; 384 | 385 | if ( ! empty( $fields ) && is_array( $fields ) ) { 386 | 387 | if ( ! is_serialized( $fields ) ) { 388 | $fields = maybe_serialize( $fields ); 389 | } 390 | 391 | // if we have some flexibles fields, save them in a CPT 392 | $post_arr = array( 393 | 'post_title' => $template_name, 394 | 'post_content' => '', 395 | 'post_status' => 'publish', 396 | 'post_author' => get_current_user_id(), 397 | 'post_type' => 'acf_template', 398 | ); 399 | 400 | $post_id = wp_insert_post( $post_arr ); 401 | 402 | if ( is_wp_error( $post_id ) ) { 403 | $error['code'] = -3; 404 | $error['message'] = __( 'Error creating post.', 'mc-acf-ft-template' ); 405 | wp_send_json_error( $error ); 406 | exit; 407 | } else { 408 | 409 | if ( is_string( $fields ) ) { 410 | $fields = wp_slash( $fields ); 411 | } 412 | 413 | if ( ! add_post_meta( $post_id, '_flex_layout_parent', $parent_key, true ) ) { 414 | update_post_meta( $post_id, '_flex_layout_parent', $parent_key ); 415 | } 416 | 417 | if ( ! add_post_meta( $post_id, '_flex_layout_data', $fields, true ) ) { 418 | update_post_meta( $post_id, '_flex_layout_data', $fields ); 419 | } 420 | 421 | if ( isset( $_POST['mc_acf_template_terms'] ) && ! empty( $_POST['mc_acf_template_terms'] ) ) { 422 | $terms_selected = $_POST['mc_acf_template_terms']; 423 | if( is_array( $terms_selected ) && ! empty( $terms_selected ) ) { 424 | wp_set_post_terms( $post_id, $terms_selected, 'acf_template_tax', true ); 425 | } 426 | } 427 | 428 | $json['message'] = __( 'Template saved, remember to save the post.', 'mc-acf-ft-template' ); 429 | } 430 | 431 | } else { 432 | $error['code'] = -4; 433 | $error['message'] = __( 'No layouts for this flexible field.', 'mc-acf-ft-template' ); 434 | wp_send_json_error( $error ); 435 | exit; 436 | } 437 | 438 | } 439 | 440 | wp_send_json_success( $json ); 441 | 442 | exit; 443 | 444 | } // end public function mc_acf_ft_save_template 445 | 446 | /* 447 | * Import template 448 | * Ajac function to import flexible template 449 | * @since 1.0.1 450 | */ 451 | public function mc_acf_import_template() { 452 | 453 | global $post; 454 | 455 | // Default json return if ok 456 | $json = array( 457 | 'success' => true, 458 | 'valid' => 1, 459 | 'errors' => 0, 460 | 'message' => __( 'Ok!', 'mc-acf-ft-template' ), 461 | ); 462 | 463 | // default error array 464 | $error = array( 465 | 'code' => '', 466 | 'message' => '', 467 | ); 468 | 469 | // we can use the acf nonce to verify the request 470 | if ( ! wp_verify_nonce( $_POST['nonce'], 'acf_nonce' ) ) { 471 | $error = -1; 472 | wp_send_json_error( $error ); 473 | exit; 474 | } 475 | 476 | // make sure our template name is set 477 | if ( ! isset( $_POST['acf_templates'] ) ) { 478 | $error['code'] = -1; 479 | $error['message'] = __( 'Import template select not found.', 'mc-acf-ft-template' ); 480 | wp_send_json_error( $error ); 481 | exit; 482 | } 483 | 484 | // make sure our template name is set and not empty 485 | if ( isset( $_POST['acf_templates'] ) && empty( $_POST['acf_templates'] ) ) { 486 | $error['code'] = -2; 487 | $error['message'] = __( 'Please select a template.', 'mc-acf-ft-template' ); 488 | wp_send_json_error( $error ); 489 | exit; 490 | } 491 | 492 | if ( isset( $_POST['acf_templates'] ) && ! empty( $_POST['acf_templates'] ) ) { 493 | 494 | // the CPT (template) selected 495 | $flex_layout_id = $_POST['acf_templates']; 496 | // layouts count in page (.values > .layout) 497 | // we can't get this values using get_row on post_id because maybe user has added layouts BEFORE import 498 | $initial_count = $_POST['number_layout']; 499 | 500 | // the flexible layout meta data saved in the template 501 | $layouts_serialized = get_post_meta( $flex_layout_id, '_flex_layout_data', true ); 502 | 503 | $layouts = maybe_unserialize( $layouts_serialized ); 504 | 505 | if ( is_array( $layouts ) && ! empty( $layouts ) ) { 506 | 507 | // the original ACF field group from which template was saved 508 | $layout_parent_key = get_post_meta( $flex_layout_id, '_flex_layout_parent', true ); 509 | 510 | // get the original field object 511 | // needed in the render_layout function 512 | $parent_object = get_field_object( $layout_parent_key, true, true ); 513 | 514 | // acf flexible main class 515 | if ( class_exists('acf_field_flexible_content') ) { 516 | $acf_flex_class = new acf_field_flexible_content(); 517 | 518 | /* 519 | * Some loops, here's the point : 520 | * When saving template, I add to post_meta ONLY the serialized values from the flexible content 521 | * (which meen only layouts, and not all the flexible parent group) for the current post. 522 | * I could save ALL the parent group object and simply output it here and avoid some loops 523 | * But flexible content field can be VERY large and complex, which meen a lot of data. 524 | * I prefer rebuild the field object here and put our layouts value in. 525 | */ 526 | // we push our saved template values in group parent object 527 | foreach ( $layouts as $i => $value ) { 528 | $parent_object['value'][] = $value; 529 | // add the name according to the group parent key, used in layout input hidden 530 | // used by ACF in render_layout function 531 | $parent_object['name'] = 'acf['.$layout_parent_key.']'; 532 | } 533 | 534 | // here we are creating a "fake" layouts array 535 | // based on the group parent object (flexible) 536 | $fake_layouts = array(); 537 | 538 | // loop on group parent layouts and push the name, used later by ACF 539 | foreach ( $parent_object['layouts'] as $k => $layout ) { 540 | $fake_layouts[ $layout['name'] ] = $layout; 541 | } 542 | 543 | $item = array(); 544 | // loop on parent object values, now contain our templates values 545 | 546 | foreach ( $parent_object['value'] as $i => $value ) { 547 | // check if layout exist in parent group now 548 | if ( array_key_exists( $value['acf_fc_layout'], $fake_layouts ) ) { 549 | // unslash values 550 | $value = wp_unslash($value); 551 | 552 | ob_start(); 553 | // render LAYOUT 554 | $acf_flex_class->render_layout( $parent_object, $fake_layouts[ $value['acf_fc_layout'] ], 555 | $initial_count, $value ); 556 | $item[] = ob_get_clean(); 557 | // increment counter 558 | $initial_count++; 559 | 560 | } 561 | } 562 | 563 | $json['layouts'] = $item; 564 | $json['message'] = __( 'Template imported, remember to save the post.', 'mc-acf-ft-template' ); 565 | if ( is_array( $item ) ) { 566 | wp_send_json_success( $json ); 567 | } 568 | } else { 569 | $error['code'] = -3; 570 | $error['message'] = __( 'ACF main class not found.', 'mc-acf-ft-template' ); 571 | wp_send_json_error( $error ); 572 | exit; 573 | } 574 | } else { 575 | $error['code'] = -4; 576 | $error['message'] = __( 'You can\'t import empty template.', 'mc-acf-ft-template' ); 577 | wp_send_json_error( $error ); 578 | exit; 579 | } 580 | } 581 | } 582 | 583 | /* 584 | * mc_acf_ft_after_title 585 | * Display our custom flexible fields in acf template CPT 586 | * $post : post object 587 | * @since 1.0.1 588 | */ 589 | public function mc_acf_ft_after_title( $post ) { 590 | if ( $post->post_type !== 'acf_template' ) { 591 | return; 592 | } 593 | 594 | $flex_layout_id = $post->ID; 595 | // the flexible layout meta data saved in the template 596 | $layouts_serialized = get_post_meta( $flex_layout_id, '_flex_layout_data', true ); 597 | $layouts = maybe_unserialize( $layouts_serialized ); 598 | 599 | // the original ACF field group from which template was saved 600 | $layout_parent_key = get_post_meta( $flex_layout_id, '_flex_layout_parent', true ); 601 | 602 | // get the original field object 603 | // needed in the render_layout function 604 | $parent_object = get_field_object( $layout_parent_key, true, true ); 605 | 606 | if ( is_array( $layouts ) && ! empty( $layouts ) ) { 607 | // acf flexible main class 608 | if ( class_exists( 'acf_field_flexible_content' ) ) { 609 | // we push our saved template values in group parent object 610 | foreach ( $layouts as $i => $value ) { 611 | 612 | // unslash values 613 | $value = wp_unslash($value); 614 | $parent_object['value'][] = $value; 615 | // add the name according to the group parent key, used in layout input hidden 616 | // used by ACF in render_layout function 617 | $parent_object['name'] = 'acf['.$layout_parent_key.']'; 618 | } 619 | // render field with values 620 | acf_render_fields( $flex_layout_id, array( $parent_object ), $el = 'div', $instruction = 'label' ); 621 | } 622 | } else { 623 | // render field without value 624 | acf_render_fields( $flex_layout_id, array( $parent_object ), $el = 'div', $instruction = 'label' ); 625 | } 626 | } 627 | 628 | public function enqueue_script() { 629 | 630 | global $post; 631 | 632 | if ( $post && 633 | isset( $post->ID ) && 634 | get_post_type( $post->ID ) === 'acf-field-group') { 635 | return; 636 | } 637 | 638 | // the handle should be changed to your own unique handle 639 | $handle = 'mc-acf-ft-template-js'; 640 | 641 | $src = MC_ACF_FT . '/assets/js/mc-acf-ft-template.js'; 642 | // make this script dependent on acf-input 643 | $depends = array( 'acf-input' ); 644 | 645 | $localize = array(); 646 | $localize['ft_label'] = __( 'Save as template :', 'mc-acf-ft-template' ); 647 | //$localized['ft_save'] = __( "Save this template", 'mc-acf-ft-template' ); 648 | 649 | wp_register_script( $handle, $src, $depends ); 650 | 651 | wp_enqueue_script( $handle ); 652 | 653 | wp_localize_script( $handle, 'mc_acf_ft', $localize ); 654 | 655 | // CSS 656 | wp_register_style( 'mc-acf-ft-css', MC_ACF_FT . 'assets/css/mc-acf-ft-css.css' ); 657 | wp_enqueue_style( 'mc-acf-ft-css' ); 658 | } // end public function enqueue_script 659 | 660 | } 661 | 662 | global $mc_acf_ft; 663 | 664 | if ( ! isset( $mc_acf_ft ) ) { 665 | 666 | $mc_acf_ft = new MC_Acf_Flexible_Template(); 667 | 668 | } 669 | } 670 | --------------------------------------------------------------------------------