Sale!
Backend Frontend Template Pro: the WordPres Plugin Template

Backend Frontend Template Pro: the WordPress Plugin Template

125,84

The WordPress plugin template you need to create advanced plugins quickly, simple and professional. Menus, settings, automated listings, shortcodes, AJAX, and much more.

Save time and focus on developing your idea.

➜ 35% for Cyber Week!, Don't wait for the offer to run out!

Description

Backend Frontend Template Pro is a template and library with which to develop plugins for WordPress easily and quickly. You won't have to reinvent the wheel to get a WordPress plugin created, just develop your idea.

Get started now, it's not necessary to know all the WordPress plugin developments documentation because you won't need to know WordPress functions like add_menu_page, add_submenu_page, add_settings_section, add_settings_field, register_setting among others. Nor will it be necessary to reinvent the wheel by modifying the class WP_List_Table to be able to have a listing in WordPress because WP does not provide a direct way to have a listing in a plugin. It will also not be necessary to investigate how to make an AJAX form on the client side, how to do translations, how to use the WP settings system,… This WordPress plugin template makes it easy and explains these points step by step, unlike a simple WordPress boilerplate that only generates doubts and will make you waste too many hours

With this template/library you can develop your plugin right now just by following the installation, making the plugin yours, checking the content, and seeing the examples only of what you need

Download conditions

  • No limit on the number of downloads
  • Lifetime License
  • From your download section you will be able to download the latest current version of the product

Reviews

There are no reviews yet.

Only logged in customers who have purchased this product may leave a review.

Free version

If you are not sure how much time BFT Pro will save you, you can download the base version now from this link, GitHub or from the WordPress plugin repository (soon) and start accelerating the development of your plugin now, and when you need more you can purchase Backend Frontend Template Pro: the WordPres Plugin Template and update libraries
BFT BFT Pro
Menu system Yes Yes
Log manipulation Yes Yes
Error handling Yes Yes
Internalization Yes Yes
Frontend system by shortcode Yes Yes
Menu with nestable children No Yes
System of settings/data stored by WP No Yes
Ipunts system, including images and files processed by WP No Yes
Sample database No Yes
Menu of installation and desistalación of the database of the plugin No Yes
Automated data manipulation of the plugin database No Yes
Automated listings and forms through the plugin database No Yes
Paginated listing by MySQL/Maria DB query No Yes
Direct listing by array No Yes
Manual form No Yes
Download system for private files No Yes
Iframe system No Yes
PDF by iframe system No Yes
Shortcode frontend system with AJAX No Yes
Frontend form system by shortcode with AJAX response No Yes

Documentation

The plugin contains all the documentation with examples, and it can also be accessed better thanks to the nested menu system, but here you will find the basic documentation in case you need to consult it

Installation

Disable Backend Frontend Template (non-Pro version)

Download the code and copy the folder “backend-frontend-template-pro” in your WordPress instalation/wp-content/plugins

Make the plugin yours

  1. Uninstall the sample database in Database / Installation/(De)install database explanation / Install/(De)install menu. You can reinstall the sample database later
  2. Change the folder and files of wp-content/plugins/backend-frontend-template-pro with the id of your plugin (like ‘your-plugin’)
  3. Delete the files or change their extension:
    1. wp-content/plugins/backend-frontend-template-pro/backend-frontend-template-pro.php
    2. wp-content/plugins/backend-frontend-template-pro/includes/class-bft.php
  4. Change the extension of the files to PHP:
    1. wp-content/plugins/backend-frontend-template-pro/your-plugin.txt
    2. wp-content/plugins/backend-frontend-template-pro/includes/class-your-plugin.txt
  5. Uncomment: “//$this->admin_pages_main_name = $this->plugin_title; on wp-content/plugins/backend-frontend-template-pro/admin/class-your-plugin-admin.php
  6. Find and replace all the file names with ‘your-plugin’ like ‘class-your-plugin.php’ ' to your plugin id, example: ‘class-a-plugin-name.php’.
  7. On wp-content/plugins/backend-frontend-template-pro/languages replace the file names with 'bft-internationalization’ to 'your-plugin', example: 'a-plugin-name-es_ES.mo’
  8. Go to search and replace of your editor, active 'match case’ and replace these strings:
    1. 'bft-internationalization’ to your-plugin
    2. '$plugin_slug = “bft_pro”‘ to $plugin_slug = “your_plugin”
    3. 'bft_shortcode’ to your_plugin_shortcode
    4. 'bft-shortcode’ to your-plugin-shortcode
  9. Go to search and replace of your editor, active “match case” and replace the words with the names and IDs of your plugin:
    1. Description of Your Plugin
    2. https://yourfuturewebsite/your-plugin/
    3. https://yourfuturewebsite
    4. Plugin Author
    5. pluginauthor@email
    6. Your Plugin
    7. your_plugin
    8. Your_Plugin
    9. your-plugin
    10. YOUR_PLUGIN
  10. Replace the icon for you own on wp-content/plugins/backend-frontend-template-pro/admin/img/icon-16px.png

Content

  1. BFT Pro folders

    1. /admin -> administration folder
    2. /includes -> global folder, for administration and public files
    3. /languages -> translation files, these binaries are created with programs such as Poedit or EazyPo
    4. /private -> for sensitive data that only a download script can send the file to the user
    5. /admin -> public folder
  2. BFT Pro sub Folders

    1. /css -> CSS files
    2. /img -> images
    3. /js -> JavaScript files
    4. /lib -> library, the internal BFT files are stored here
    5. /partials -> frontend files
  3. Top BFT Pro Files (using the original filenames)

    1. /includes/class-your-plugin.txt -> main class of the plugin
    2. /includes/class-your-plugin-activator.php -> controls when the plugin is activated
    3. /includes/class-your-plugin-deactivator.php -> controls when the plugin is disabled
    4. /includes/class-your-plugin-cronjobs.php -> control the cronjobs of the plugin
    5. /includes/class-your-plugin-install-upgrade-deinstall-database.php -> install and remove the plugin database
    6. /includes/class-your-plugin-functions-admin-public.php -> class with functions for admin and public classes, it's an extension of the BFT admin-public class
    7. /admin/class-your-plugin-admin.php -> class for the admin section, it's an extension of the BFT admin-public class, your admin-public class and BFT admin class
    8. /public/class-your-plugin-public.php -> class for the public section, it's an extension of the BFT admin-public class, your admin-public class and the BFT public class

Pro menu

Edit your menu in the variable $this->admin_pages of the file admin/class-your-plugin-admin.php

Designing a BFT menu looks like this:

	$this->admin_pages = [
		"hello_world" => [
			"page_title" => $this->__("Hello world page"),
			"menu_title" => $this->__("Hello world"),
			"file" => "your-plugin-admin-display-hello-world.php",
		],
		"blank_page" => [
			"page_title" => $this->__("Blank page"),
			"menu_title" => $this->__("Blank page"),
			"file" => "bft-admin-display-blank-page-with-title.php",
		],	
	];	

BFT menu can have child pages, example:

	$this->admin_pages = [
		"hello_world" => [
			"page_title" => $this->__("Hello world page"),
			"menu_title" => $this->__("Hello world"),
			"file" => "your-plugin-admin-display-hello-world.php",
			"children" => [
				"blank_page" => [
					"page_title" => $this->__("Blank page"),
					"menu_title" => $this->__("Blank page"),
					"file" => "bft-admin-display-blank-page-with-title.php",
				],	
			]
		],
	];

Note: the array data is expanded by the function $this->admin_pages_prepare(), if you make a $this->debug_log_write($this->admin_pages) on a page: you can see the atual status of the array in the WordPress log

Explaining the BFT menu:

  1. Automatic parameters added to the array

    • id: the array key
    • is_child: isset and true if child
    • page_parent: isset with the key of the parent if it's a child
    • ids_required_get_data: the GET data found within the ids 'ids_required’
    • ids_optional_get_data: the GET data found within the ids 'ids_optional’
    • ids_aux_required_get_data: the GET data found within the ids 'ids_aux_required’
    • ids_aux_optional_get_data: the GET data found within the ids 'ids_aux_optional’
    • ids_all: 'ids_required', 'ids_optional', 'ids_aux_required', 'ids_aux_optional’ all combined, this order is the priority for maintain the data
    • ids_all_get_data: the GET data found within the ids 'ids_all’, uses the ‘key_final’ as ‘key’
    • ids_all_get_data_excluded_zeros: ids_all_get_data sin los ids con get data == ‘0’
    • ids_all_get_url: ids_all_get_data transformed on a URL GET string ‘&key=data’
    • ids_all_get_data_excluded_zeros: ids_all_get_url sin los ids con get data == ‘0’
    • ids_all_inverse: ids_all on inverse order, ex:
      	$page_data["ids_all"] = [
      		"key_initial" =>"key_final"
      	]
      	->
      	$page_data["ids_all_inverse"] = [
      		"key_final" =>"key_initial"
      	]
      
    • ids_required_all: 'ids_required', ‘ids_aux_required’ all combined, this order is the priority for maintain the data
    • ids_required_all_get_data: the GET data found within the ids 'ids_required_all', uses the ‘key_final’ as ‘key’
    • ids_required_all_get_data_excluded_zeros: ids_required_all_get_data without the ids with get data == ‘0’
    • ids_optional_all: 'ids_optional', 'ids_aux_optional’ all combined, this order is the priority for maintain the data
    • ids_optional_all_get_data: the GET data found within the ids 'ids_optional_all', uses the ‘key_final’ as ‘key’
    • ids_principal_all: 'ids_required', ‘ids_optional’ all combined, this order is the priority for maintain the data
    • ids_principal_all_get_data: the GET data found within the ids 'ids_optional_all', uses the ‘key_final’ as ‘key’
    • ids_aux_all: 'ids_aux_required', 'ids_aux_optional’ all combined, this order is the priority for maintain the data
    • ids_aux_all_get_data: the GET data found within the ids 'ids_aux_all', uses the ‘key_final’ as ‘key’
  2. Parametters with default data if missing

    • page_title: page title, by default: $this->admin_pages_page_title_default
    • menu_title: page tab title, by default: $this->admin_pages_page_title_default
    • menu_slug: page slug, by default: page key. The slug menu will be changed to: $this->admin_pages_slug_name_prefix.”_”.menu_slug because it's needed a unique page name among the plugins
    • tab_show: if false does not display the page tab, even if the page is selected, by default: true
    • tabs_new_or_edit_on_url: true it adds '&action=edit’ o '&action=new’ in the tab link. New if there are an id detected on the GET data, defaults false
    • tabs_show_children: if false doesn't show the first line children tabs of a page, default True
    • function: the function for when a page is displayed, by default: $this->admin_pages_function_default
    • function_load: loads the function before a page is displayed,by default: $this->admin_pages_function_load_default
    • file: the admin/partials file that will be displayed, default: $this->admin_pages_file_default (If the file starts with 'bft-‘ the file will be loaded of the folder admin/lib/BFT/partials
    • error_throw_what_do, it's used on error_throw, options:
      • show_error: shows the error (default option)
      • show_error_and_die: show the error and stop the execution
      • go_to_parent: go to the parent page and anotes on the GET data the error (only works if $triggered_on_function_load = true, because in a normal WordPress function it will cause the error: 'Cannot modify header information – headers already sent')
    • error_throw_file_change: change the file option if error_throw_what_do is triggered, by default: false
    • capability, by default: 'manage_options', WordPress capabilities: https://wordpress.org/support/article/roles-and-capabilities/
    • IDs: they are data arrays with 'name on the GET data’ => 'id internal to use', default empty array
      On the GET data it's needed to put a unique id, example: 'book_id', but on the page/function maybe it's needed like 'id', then use: 'book_id’ => 'id’ Options:

      • ids_required (throw an error if GET id not found,)
      • ids_optional
      • ids_aux_required (throw an error if GET id not found,, for pages with secondary data)
      • ids_aux_optional (for pages with secondary data)
    • Example:
      	"ids_optional" => [
      		"key_initial" => "key_final",
      		"'key_get'" => "'key_needed'",
      		"book_id" => "id",
      	],
      
    • Note: if it's a children page there may be automatic ids: if a parent have an id or id aux that it's not found on the children page, then the children has the id like an id optional on ids_optional
  3. Ready-to-use available functions (you can create whatever function you need)

    • admin_menu_page_display: displays the selected page in 'File’
    • admin_menu_page_table_display_first_menu_child: displays a table with the data of the first child and the form stored in $this->admin_forms[]
    • admin_menu_page_table_display: displays a table with the data of the displayed page
    • admin_menu_admin_form_page_display: displays a custom automated form stored in $this->admin_forms[]
  4. Functions load available out of the box (you can create whatever function you need)

    • redirect_to_first_page_child: redirects to the first child page of the current page (executes admin_permission_check_and_ids_required_check_function_load first)
    • admin_permission_check_and_ids_required_check_function_load: checks the admin permissions and checks the required and optional ids specified in the URL, principal and aux ids type checked, prepared for 'function_load', 'go_to_parent’ used if needed
    • ids_required_check_function_load: checks required and optional ids for the page specified in the URL, principal and aux ids type checked, prepared for 'function_load', 'go_to_parent’ used if needed
    • download_file_private: download private file
    • custom_form_set_do_by_post: send a form for automated data manipulation
  5. Optional parameters

    • admin_page_settings_id: for select the key to use in $this->admin_settings[], prepared for use with the function $this->settings_section_form($this->admin_pages_data_get(“admin_page_settings_id”)), that function is used in “file” => “bft-admin-display-settings.php”,
    • iframe_url: the full URL of the inside iframe, for use with “file” => “bft-admin-display-iframe.php”
    • iframe_admin_page_slug: the slug of the inside iframe, the iframe slug will be changed to: $this->admin_pages_slug_name_prefix.”_”.menu_slug, for use with “file” => “bft-admin-display-iframe.php”
    • file_aux_folder_includes: for use a second full path with the variable $file_aux_url (parameter 'file’ generates the variable $file_url )
    • new_text: the text of the new entry button, it can be added on the parent or on the child, the priority is on the child data, for use on the function admin_menu_page_table_display_first_menu_child() () and file bft-admin-display-list-table.php
      it creates the variable $button_add and it will be finally used in $this->html_button_action
      if new_text is boolean true it will be used the text: $this->__(“New”)
    • admin_forms_aux_id: for page with automatic data, used in the function: admin_pages_data_title_from_admin_forms_aux()
      Displays the automatic name of the form (column_title_name data) before the data like this: Automatic name | Title
      For the auto name is used the ids_required and ids_optional (only the first ID found), but only is used the key_initial (the key_get)
    • children: array with the children pages. A children page can has descent, the maximum levels are 5 counting the parent page. There is a limit because it is not recursive, that kills the performance of the server
    • page_copy_of: copy the data of a page. Only copy the data not found on the page, neither copy id, is_child, page_parent, menu_slug and children”)?>
  6. Your own parameters

    • You can create your own parameters, later on you can access to the info on a function or on a page with:
      $variable_name = $this->admin_pages_data_get(“parametter_name”);And if you want you can retrieve the data of a certain page with $page_name, and retrieve all the array data with $key = false
      $variable_name = $this->admin_pages_data_get($key = false, $page_name = NULL)
    • You can set your own parameters by code with:
      $this->admin_pages_data_set($key, $data, $page_name = NULL)
  7. How to start

    • Use the examples of the beginning, the majority of the options are not needed initially
    • Except all the 'id_required'/'id_optional’ part, that's important, but only for child pages. You can see detailed examples on other pages where it is needed, because the idea is to have a page listed with all the entries, and then see a single item on the children, example:
      	$this->admin_pages = [
      		"books" => [
      			"page_title" => $this->__("List with all the books"),
      			"menu_title" => $this->__("Books"),
      			"function" => "admin_menu_page_table_display_first_menu_child",
      			"file" => "bft-admin-display-list-table.php",
      			"children" => [
      				"book" => [
      					"ids_required" => [
      						"book_id" => "id",
      					],
      					"admin_forms_id" => "books",
      					"page_title" => $this->__("Book selected"),
      					"menu_title" => $this->__("Book"),
      					"function" => "admin_menu_admin_form_page_display",
      					"file" => "bft-admin-display-form.php",
      					"function_load" => "custom_form_set_do_by_post",
      				],	
      			]
      		],
      	];
      

      In this quick example there is a page that shows all the books: admin.php?page=your_plugin_books , and each item/book links to admin.php?page=your_plugin_book&book_id=[id of the book]

      And because 'book’ has 'ids_required', the page automatically throws error if a required GET data is not found, book_id in this case

      A complex example of nested tabs:

      his is a part of the menu used on the BFT default menu, more info on database -> Automated data manipulation

      	$this->admin_pages = [
      		"database" => [
      				"menu_title" => $this->__("Database"),
      				"page_title" => $this->__("Database"),
      				"file" => "bft-admin-display-menu-system.php",
      				"function_load" => "redirect_to_first_page_child",
      				"children" => [
      					"install_deinstall_upgrade_database_explication" => [...],	
      					"automated_data_manipulation_explication" => [
      						"menu_title" => $this->__("Automated data manipulation"),
      						"page_title" => $this->__("Data managed by BFT"),
      						"file" => "bft-admin-display-automated-data-manipulation-system.php",
      						"children" => [
      							"automated_data_manipulation_courses" => [...],
      							"automated_data_manipulation_students" => [...],
      							"automated_data_manipulation_teachers" => [
      								"menu_title" => $this->__("Teachers"),
      								"page_title" => $this->__("List of teachers"),
      								"function" => "admin_menu_page_table_display_first_menu_child",
      								"file" => "bft-admin-display-list-table.php",
      								"children" => [
      									"automated_data_manipulation_teacher" => [
      										"ids_required" => [
      											"teacher_id" => "id",
      										],
      										"admin_forms_id" => "teachers",
      										"tabs_new_or_edit_on_url" => true,
      										"menu_title" => $this->__("Teacher"),
      										"page_title" => $this->__("Teacher form"),
      										"function" => "admin_menu_admin_form_page_display",
      										"file" => "bft-admin-display-form.php",
      										"function_load" => "custom_form_set_do_by_post",
      										"new_text" => $this->__("New teacher"),
      									],
      									"automated_data_manipulation_teacher_notes" => [
      										"ids_required" => [
      											"teacher_id" => "teacher_id",
      										],
      										"ids_aux_required" => [
      											"teacher_id" => "id",
      										],
      										"admin_forms_aux_id" => "teachers",
      										"menu_title" => $this->__("Teacher notes"),
      										"page_title" => $this->__("List of notes"),
      										"function" => "admin_menu_page_table_display_first_menu_child",
      										"file" => "bft-admin-display-list-table.php",
      										"children" => [
      											"automated_data_manipulation_teacher_note" => [
      												"ids_required" => [
      													"note_id" => "id",
      													"teacher_id" => "teacher_id",
      												],
      												"admin_forms_id" => "teachers_notes",
      												"ids_aux_required" => [
      													"teacher_id" => "id",
      												],
      												"admin_forms_aux_id" => "teachers",
      												"tabs_new_or_edit_on_url" => true,
      												"menu_title" => $this->__("Teacher note"),
      												"page_title" => $this->__("Teacher note form"),
      												"function" => "admin_menu_admin_form_page_display",
      												"file" => "bft-admin-display-form.php",
      												"function_load" => "custom_form_set_do_by_post",
      												"new_text" => $this->__("New note"),
      											],
      										],
      									],
      								],
      							],
      						],
      					],
      				],
      			],
      		],
      	];
      

      That generates this menu:

      GET data: admin.php?page=bft_pro_automated_data_manipulation_explication

      Backend Frontend Template Pro: nested menu test 1

      GET data: admin.php?page=bft_pro_automated_data_manipulation_teachersn

      Backend Frontend Template Pro: nested menu test 2, list of teachers

      GET data: admin.php?page=bft_pro_automated_data_manipulation_teacher&action=new&teacher_id=0

      Backend Frontend Template Pro: nested menu test 3, creating teacher

      GET data: admin.php?page=bft_pro_automated_data_manipulation_teacher&action=edit&teacher_id=1

      Backend Frontend Template Pro: nested menu test 4, created teacher

      GET data: admin.php?page=bft_pro_automated_data_manipulation_teacher_notes&teacher_id=1

      Backend Frontend Template Pro: nested menu test 5, notes list of the created teacher

      GET data: admin.php?page=bft_pro_automated_data_manipulation_teacher_note&action=edit&note_id=1&teacher_id=1

      Backend Frontend Template Pro: nested menu test 6, new note of the created teacher

Settings/data system stored via WP

Wordpress allows you to store data with its adjustment system, thanks to BFT Pro you can create settings pages with its data fields:

Edit your WordPress settings pages on the variable $this->admin_settings of the file admin/class-your-plugin-admin.php

On each page we describe the fields, and several pages can repeat the same fields

Designing BFT Settings looks like this:

	$this->admin_settings = [
		"general" => [
			"title" =>  $this->__("Test settings"),
			"fields" => [
				"text_test" => [
					"title" => $this->__("Text input"),
				],
				"number_test" => [
					"title" => $this->__("Number input"),
					"args" => [
						"type" => "number",
					],
				],
			],
		],
	];

Explaining the BFT menu:

  1. Settings pages

    1. Automatic parameters added to the array

      • id: the array key
      • id_wordpress: $this->admin_pages_settings_prefix.”_”.array key
      • page: id_wordpress.$this->admin_pages_settings_page_suffix
    2. Parametters with default data if missing

      • id: the array key
      • function, by default: $this->settings_section_function_default
      • fields, by default: settings_section_function_default
      • title, by default: $this->admin_pages_settings_page_title_default
  2. Page field

    1. Automatic parameters added on the field array part

      • id: the array key
      • id_wordpress: $this->plugin_slug.”_”.the array key
    2. Parametters with default data if missing

      • title, by default: array key
      • function, by default: settings_section_form_field()
      • args, array where we define the data type, if it's multiple, the options for the select, etc. Default: “type” => “text”
        	"args" => [
        		"type" => "text"
        	],
        
    3. Input types

      • text: input text
        	"text_test" => [
        		"title" => $this->__("Text input"),
        	],
        
      • textarea: input textarea, rows and cols are optionals
        	"textarea_test" => [
        		"title" => $this->__("Textarea input"),
        		"args" => [
        			"type" => "textarea",
        			"rows" => 10,
        			"cols" => 50,
        		],
        	],
        
      • number: input number, optional args: min, max and step. By defecation for step: 1, alternative: 'decimal’ or 'decimals', this option calculates the necessary steps
        	"number_test" => [
        		"title" => $this->__("Number input"),
        		"args" => [
        			"type" => "number",
        			"min" => 1,
        			"max" => 99999,
        			"step" => 1,
        		],
        	],
        
      • select: input select, args must have 'options', it stores string wiht checked options, ex: 'en,es’ , optional args: search (defaults false), multiple (defaults false)
        	"select_test" => [
        		"title" => $this->__("Select input"),
        		"args" => [
        			"type" => "select",
        			"search" => true,
        			"options" => [
        				"zh" => $this->__("Chinesse"),
        				"en" => $this->__("English"),
        				"es" => $this->__("Spanish"),
        			],
        		],
        	],
        	"select_multiple_test" => [
        		"title" => $this->__("Select multiple input"),
        		"args" => [
        			"type" => "select",
        			"multiple" => true,
        			"search" => true,
        			"options" => [
        				"zh" => $this->__("Chinesse"),
        				"en" => $this->__("English"),
        				"es" => $this->__("Spanish"),
        			],
        		],
        	],
        
      • date: input text with a calendar, the date will be MySQL style '2023-01-01’
        	"date_test" => [
        		"title" => $this->__("Date input"),
        		"args" => [
        			"type" => "date",
        		],
        	],
        
      • checkbox, single and multiple, in multiple is needed 'options’ on args
        	"checkbox_test" => [
        		"title" => $this->__("Checkbox input"),
        		"args" => [
        			"type" => "checkbox",
        			"multiple" => false,
        		],
        	],
        	"checkbox_multiple_test" => [
        		"title" => $this->__("Checkbox multiple input"),
        		"args" => [
        			"type" => "checkbox",
        			"multiple" => true,
        			"options" => [
        				"zh" => $this->__("Chinesse"),
        				"en" => $this->__("English"),
        				"es" => $this->__("Spanish"),
        			],
        		],
        	],
        
      • radio: radio buttons, stores string wiht checked options, ex: 'en,es’, it requires 'options’
        	"radio_test" => [
        		"title" => $this->__("Radio input"),
        		"args" => [
        			"type" => "radio",
        			"multiple" => true,
        			"options" => [
        				"zh" => $this->__("Chinesse"),
        				"en" => $this->__("English"),
        				"es" => $this->__("Spanish"),
        			],
        		],
        	],
        
      • image: store a image on the WordPress Media Library, the data saved on settings is the WordPress ID of the file. Once selected shows the image
        	"image_test" => [
        		"title" => $this->__("Image input"),
        		"args" => [
        			"type" => "image",
        		],
        	],
        
      • file: store a file on the WordPress Media Library, the data saved on settings is the WordPress ID of the file. Once selected shows a download button
        	"file_test" => [
        		"title" => $this->__("File input"),
        		"args" => [
        			"type" => "file",
        		],
        	],
        
      • empty: a commented input, it prints ”
        	"empty_test" => [
        		"title" => $this->__("Input commented"),
        		"args" => [
        			"type" => "empty",
        		],
        	],
        
  3. How the fields are stored

    • On the array a field ID is a simple sentence, ex: 'languages', 'currency', etc., but BFT stores it as: $this->plugin_slug.”_”.”field_id”, then it will be: 'your_plugin_languages', 'your_plugin_currency', etc.
    • That's because WordPress doesn't isolate the settings by plugins, the plugin itself needs to isolate and secure the settings
    • Knowing this, there are two access styles for updating and deleting data by code:
      1. BFT Style

        • $a_variable = $this->option_field_get(“field_id”)
        • $this->update_option_field(“field_id”, “new string value”)
        • $this->delete_option_field(“field_id”)
      2. WordPress style

        • $a_variable = get_option(“plugin_slug”.”_”.”field_id”);
        • update_option(“plugin_slug”.”_”.”field_id”, “new string value”)
        • delete_option(“plugin_slug”.”_”.”field_id”);

      Note: use 'update’ to create a new entry

An example with all the input types:

	$this->admin_settings = [
		"input_types_test" => [
			"title" =>  $this->__("Test settings"),
			"fields" => [
				"text_test" => [
					"title" => $this->__("Text input"),
				],
				"textarea_test" => [
					"title" => $this->__("Textarea input"),
					"args" => [
						"type" => "textarea",
						"rows" => 10,
						"cols" => 50,
					],
				],
				"number_test" => [
					"title" => $this->__("Number input"),
					"args" => [
						"type" => "number",
						"min" => 1,
						"max" => 99999,
						"step" => 1,
					],
				],
				"select_test" => [
					"title" => $this->__("Select input"),
					"args" => [
						"type" => "select",
						"search" => true,
						"options" => [
							"zh" => $this->__("Chinesse"),
							"en" => $this->__("English"),
							"es" => $this->__("Spanish"),
						],
					],
				],
				"select_multiple_test" => [
					"title" => $this->__("Select multiple input"),
					"args" => [
						"type" => "select",
						"multiple" => true,
						"search" => true,
						"options" => [
							"zh" => $this->__("Chinesse"),
							"en" => $this->__("English"),
							"es" => $this->__("Spanish"),
						],
					],
				],
				"date_test" => [
					"title" => $this->__("Date input"),
					"args" => [
						"type" => "date",
					],
				],
				"checkbox_test" => [
					"title" => $this->__("Checkbox input"),
					"args" => [
						"type" => "checkbox",
						"multiple" => false,
					],
				],
				"checkbox_multiple_test" => [
					"title" => $this->__("Checkbox multiple input"),
					"args" => [
						"type" => "checkbox",
						"multiple" => true,
						"options" => [
							"zh" => $this->__("Chinesse"),
							"en" => $this->__("English"),
							"es" => $this->__("Spanish"),
						],
					],
				],
				"radio_test" => [
					"title" => $this->__("Radio input"),
					"args" => [
						"type" => "radio",
						"multiple" => true,
						"options" => [
							"zh" => $this->__("Chinesse"),
							"en" => $this->__("English"),
							"es" => $this->__("Spanish"),
						],
					],
				],
				"image_test" => [
					"title" => $this->__("Image input"),
					"args" => [
						"type" => "image",
					],
				],
				"file_test" => [
					"title" => $this->__("File input"),
					"args" => [
						"type" => "file",
					],
				],
				"empty_test" => [
					"title" => $this->__("Input commented"),
					"args" => [
						"type" => "empty",
					],
				],
			],
		],
	];

Backend Frontend Template Pro: general settings

Backend Frontend Template Pro: Input test 1

Backend Frontend Template Pro: Input test 2

Sample database

BFT Pro installs this database for testing purposes:

Backend Frontend Template Pro: relational diagram from the example database

Notes about the database:

  • Courses has an internalization table
  • Courses and students have a many-to-many relationship
  • Courses have a soft foreign key, teacher_id can be NULL, a course can exist without a teacher
  • Teachers notes has a hard foreig key, teacher_id cannot be NULL, a note it can't exist without a teacher

Inside of Backend Frontend Template Pro you will find the queries used to install the database

 

Backend Frontend Template Pro: uninstalling the database

Automated data manipulation of the plugin database

  1. Required fields on the database table

    For display and manipulate the data automatically we will need a few dedicated fields in a table

    1. Status, enum type, field name decided in $this->database_status_column_name, for the enum options use the stored data on $this->database_status_option_active_name and $this->database_status_option_bin_name
    2. Created time, datetime type, field name decided in $this->database_datetime_created_name
    3. Modified time, datetime type, field name decided in $this->database_datetime_modified_name
    4. Removed time (moved to bin), datetime type, field name decided in $this->database_datetime_removed_name

    Plus the table can have an internationalization table (i18n), that table will have a foreign_key pointing to the ID of the main table

    An example of a table with an internalization table:

    			CREATE TABLE `wp_bft_pro_courses`  (
    				`id` int(10) NOT NULL AUTO_INCREMENT,
    				`status` enum('active','bin') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'active',
    				`datetime_created` datetime NULL DEFAULT NULL,
    				`datetime_modified` datetime NULL DEFAULT NULL,
    				`datetime_removed` datetime NULL DEFAULT NULL,
    				`hours` int(4) NOT NULL DEFAULT 0,
    				`order` int(10) NOT NULL DEFAULT 0,
    				PRIMARY KEY (`id`) USING BTREE
    			) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
    
    
    			CREATE TABLE `wp_bft_pro_courses_i18n`  (
    				`id` int(10) NOT NULL AUTO_INCREMENT,
    				`course_id` int(10) NOT NULL COMMENT 'Foreign Key',
    				`language` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
    				`datetime_created` datetime NULL DEFAULT NULL,
    				`datetime_modified` datetime NULL DEFAULT NULL,
    				`name_i18n` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
    				`image` int(10) NOT NULL,
    				PRIMARY KEY (`course_id`, `language`) USING BTREE,
    				UNIQUE INDEX `uk_wp_bft_pro_courses_i18n_id`(`id`) USING BTREE,
    				UNIQUE INDEX `uk_wp_bft_pro_courses_i18n_id_primary_keys`(`course_id`, `language`) USING BTREE,
    				CONSTRAINT `fk_wp_bft_pro_courses_i18n_course_id` FOREIGN KEY (`course_id`) REFERENCES `wp_bft_pro_courses` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
    			) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Internationalization of course names' ROW_FORMAT = Dynamic;
    

    Note: don't use 'wp_', a WordPress installation maybe use other prefix, use instead $wpdb->prefix, example: CREATE TABLE `”.$wpdb->prefix.”your_plugin_table`

    Warning: ‘name’ can't be used for a column a i18n table. WordPress clean the data automatically of all 'name' appearances’ and WordPress doesn't expect an array of data (it's an array because it's the text in each language)

    On the many to many relationships is needed the created time and modified time

    			//Estudiantes no descritos en esta página de ejemplo, la clave foránea no puede funcionar si copias la query			CREATE TABLE `wp_bft_pro_students_courses`  (
    				`id` int(10) NOT NULL AUTO_INCREMENT,
    				`student_id` int(10) NOT NULL COMMENT 'Foreign Key',
    				`course_id` int(10) NOT NULL COMMENT 'Foreign Key',
    				`datetime_created` datetime NULL DEFAULT NULL,
    				`datetime_modified` datetime NULL DEFAULT NULL,
    				PRIMARY KEY (`student_id`, `course_id`) USING BTREE,
    				UNIQUE INDEX `uq_wp_bft_pro_students_courses_id`(`id`) USING BTREE,
    				UNIQUE INDEX `uq_wp_bft_pro_students_courses_student_id_course_id`(`student_id`, `course_id`) USING BTREE,
    				INDEX `fk_wp_bft_pro_students_courses_course_id`(`course_id`) USING BTREE,
    				CONSTRAINT `fk_wp_bft_pro_students_courses_course_id` FOREIGN KEY (`course_id`) REFERENCES `wp_bft_pro_courses` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    				CONSTRAINT `fk_wp_bft_pro_students_courses_student_id` FOREIGN KEY (`student_id`) REFERENCES `wp_bft_pro_students` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
    			) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Relationship table' ROW_FORMAT = Dynamic;
    
  2. $this->admin_forms

    The forms are described in $this->admin_forms of the file admin/class-your-plugin-admin.php, a form it's the union of a table and its internationalization

    Designing a BFT form looks like this:

    		$this->admin_forms = [
    			"courses" => [
    				"table" => $wpdb->prefix.$this->plugin_slug."_"."courses",
    				"column_key" => "id",
    				"column_title_name" => "name_i18n",
    				"i18n_foreign_key" => "course_id",
    				"columns" => [
    					$this->database_status_column_name => [
    						"label" => $this->database_status_column_text,
    						"type" => "select",
    						"options" => $this->database_status_options,
    					],
    					"id" => [
    						"label" => $this->__("Nº"),
    						"placeholder" => "",
    						"type" => "text",
    						"display_table" => true,
    						"readonly" => true,
    					],
    					$this->database_datetime_created_name => [
    						"label" => $this->database_datetime_created_text,
    						"placeholder" => "",
    						"type" => "datetime",
    						"display_table" => false,
    						"readonly" => true,
    					],
    					$this->database_datetime_modified_name => [
    						"label" => $this->database_datetime_modified_text,
    						"placeholder" => "",
    						"type" => "datetime",
    						"display_table" => false,
    						"readonly" => true,
    						"only_in_active" => true,
    					],
    					$this->database_datetime_removed_name => [
    						"label" => $this->database_datetime_removed_text,
    						"placeholder" => "",
    						"type" => "datetime",
    						"display_table" => true,
    						"readonly" => true,
    						"only_in_removed" => true,
    					],
    					"hours" => [
    						"label" => $this->__("Total hours of the course"),
    						"placeholder" => $this->__("Hours"),
    						"type" => "number",
    						"i18n" => false,
    						"readonly" => true,
    						"display_table" => true,
    					],
    					"name_i18n" => [
    						"label" => $this->__("Course name"),
    						"placeholder" => $this->__("Name"),
    						"type" => "text",
    						"i18n" => true,
    						"display_table" => true,
    					],
    					"image" => [
    						"label" => $this->__("Course logo"),
    						"placeholder" => $this->__("Image"),
    						"type" => "image",
    						"i18n" => true,
    						"display_table" => true,
    					],
    					"order" => [
    						"label" => $this->__("Order"),
    						"placeholder" => $this->__("Order, lowest number first"),
    						"type" => "text",
    						"i18n" => false,
    						"display_table" => true,
    					],
    					"teacher_id" => [
    						"label" => $this->__("Teacher"),
    						"placeholder" => "",
    						"type" => "select",
    						"foreign_key" => "teachers",
    						"display_table" => true,
    						"value_empty_is_null" => true,
    						"foreign_key_null_text" => $this->__("Teacher not selected"),
    					],
    					"students_courses" => [
    						"label" => $this->__("Enrolled students"),
    						"placeholder" => $this->__("Students"),
    						"type" => "many_to_many_relationship",
    						//Display table doesn't work for now with many_to_many_relationship
    						"display_table" => false,
    						"search" => true,
    					],
    				],
    			],
    		];
    

    Explaining the BFT menu:

    1. Form parameters

      1. key: internal form id
      2. table: table name
      3. column_key: key name ('id’ usually)
      4. column_title_name: field where it's stored the row name (the name of a user, rhe name of a book, etc.)
      5. i18n_foreign_key: foreign key on the internationalization table
      6. columns: list of fields
    2. Optional field parameters

      1. label: text to show on the admin. Default: “”
      2. placeholder: placeholder if the data type can have placeholder
      3. display_table: displays the field in the list table. Note: display_table doesn't work with fields many_to_many_relationship
      4. i18n: if the data of the field is stored in a internationalization table
      5. only_in_active: display the field only if the data is active
      6. only_in_removed: display the field only if the data is on the bin
      7. readonly: readonly field, for the key column for example
      8. value_from_get: catch the data from the GET data on the URL if there aren't data stored of this field. For a dependant data, example: teachers' Notes, a new note needs to know the teacher id of the note
      9. value_empty_is_null: an empty data will be transformed to NULL (the NULL data always is printed like value=”)
      10. foreign_key: it stores a foreign key
      11. foreign_key_null_text: text to display when NULL is saved in a foreign key with optional NULL
      12. type: the BFT input options plus the 'many_to_many_relationship' option, is a select to link the relationship. 'text’ by default
        • many_to_many_relationship: uses the data saved in $this->admin_forms_many_to_many_relationships
          	"type" => "many_to_many_relationship",
          	"search" => true,
          	//Display table doesn't work for now with many_to_many_relationship
          	"display_table" => false,
          
        • text: input text
          	"type" => "select",
          
        • textarea: input textarea, rows and cols are optionals
          	"type" => "textarea",
          	"rows" => 10,
          	"cols" => 50,
          
        • number: input number, optional args: min, max and step. Default of step: 1, alternative: 'decimal’ or 'decimals', this option calculates the required steps
          	"type" => "number",
          	"min" => 1,
          	"max" => 99999,
          	"step" => 1,
          
        • select: input select, args must have 'options', it stores string wiht checked options, ex: 'en,es’, optional args: search (false by default), multiple (false by default)
          	"type" => "select",
          	"search" => true,
          	"options" => [
          		"zh" => $this->__("Chinesse"),
          		"en" => $this->__("English"),
          		"es" => $this->__("Spanish"),
          	],
          
          	"type" => "select",
          	"multiple" => true,
          	"search" => true,
          	"options" => [
          		"zh" => $this->__("Chinesse"),
          		"en" => $this->__("English"),
          		"es" => $this->__("Spanish"),
          	],
          
        • date: input text with a calendar, the date will be in MySQL style '2023-01-01’
          	"type" => "date",
          
        • checkbox, single and multiple, in multiple is needed 'options’ on args
          	"type" => "checkbox",
          	"multiple" => false,
          
          		"type" => "checkbox",
          		"multiple" => true,
          		"options" => [
          			"zh" => $this->__("Chinesse"),
          			"en" => $this->__("English"),
          			"es" => $this->__("Spanish"),
          		],
          
        • radio: radio buttons, it stores string wiht checked options, ex: 'en, es’, it requires 'options’
          	"type" => "radio",
          	"multiple" => true,
          	"options" => [
          		"zh" => $this->__("Chinesse"),
          		"en" => $this->__("English"),
          		"es" => $this->__("Spanish"),
          	],
          
        • image: store is needed and select and image of the WordPress Media Library, the data saved is the ID of the file. Once selected shows the image
          	"type" => "image",
          
        • file: the same as the image type, but don't try to display an image, only shows a download button
          	"type" => "file",
          
        • empty: a commented input, it prints '<!– id=”'.$id_setting.'” empty –>’
          	"type" => "empty",
          
  3. $this->admin_forms_many_to_many_relationships

    This form describes a many to many relationship between two tables

    1. Form parameters

      1. key: internal id of the many to many relationship
      2. table: table name
      3. forms: array of the two relationship form
    1. Parameter of the two related fields

      1. key: form ID
      2. value: foreign key used in the table

    Example of the array:

    	$this->admin_forms_many_to_many_relationships = [
    		"students_courses" => [
    			"table" => $wpdb->prefix.$this->plugin_slug."_"."students_courses",
    			"forms" => [
    				"courses" => "course_id",
    				"students" => "student_id",
    			],
    		],
    	];
    

    The system will work with the above data:

    		$this->admin_forms = [
    			"courses" => [
    				[...]
    				"columns" => [
    					[...]
    					"students_courses" => [
    						"label" => $this->__("Enrolled students"),
    						"placeholder" => $this->__("Students"),
    						"type" => "many_to_many_relationship",
    						//Display table doesn't work for now with many_to_many_relationship
    						"display_table" => false,
    						"search" => true,
    					],
    				],
    			],
    		];
    
  4. $this->admin_pages

    BFT needs to know what function and function_load to use on the page. The system also needs to know how to read the GET data from the URL

    There are listing pages and form pages. It's posible to nest listing pages that use the data of the previous form, for example: teacher's notes, first select a teacher (or create a new one), then you go to that teacher's notes.

    The explanation of $this->admin_pages is in the 'Menu' tab, there is a complete example of use

      1. Display a listing

    For display a list it's needed a child with all the data, plus it's needed:

      • “function” => “admin_menu_page_table_display_first_menu_child”,
      • “file” => “bft-admin-display-list-table.php”,
      1. Display a form

    A form is usually a child page of a listing, the form needs:

      • “ids_required” => [“get_data” => “id_data”],
        	"ids_required" => [
        		"teacher_id" => "id",
        	],
        
      • “admin_forms_id” => “form_key”,
      • “function” => “admin_menu_admin_form_page_display”,
      • “file” => “bft-admin-display-form.php”,
      • “function_load” => “custom_form_set_do_by_post”,
      • “tabs_new_or_edit_on_url” => true,
      • “new_text” => “New entry text”,

    An example of a listing and form:

    	$this->admin_pages = [
    		"automated_data_manipulation_courses" => [
    			"menu_title" => $this->__("Courses"),
    			"page_title" => $this->__("List of courses"),
    			"function" => "admin_menu_page_table_display_first_menu_child",
    			"file" => "bft-admin-display-list-table.php",
    			"children" => [
    				"automated_data_manipulation_course" => [
    					"ids_required" => [
    						"courses_id" => "id",
    					],
    					"admin_forms_id" => "courses",
    					"tabs_new_or_edit_on_url" => true,
    					"menu_title" => $this->__("Course"),
    					"page_title" => $this->__("Course form"),
    					"function" => "admin_menu_admin_form_page_display",
    					"file" => "bft-admin-display-form.php",
    					"function_load" => "custom_form_set_do_by_post",
    					"new_text" => $this->__("New course"),
    				],
    			],
    		],
    	];
    
      1. Nest a form

    To nest a BFT form it's needed always to specify the external GET data, Don't put 'id’ on the foreign id

        1. Display a listing

          • “ids_required” => [“get_data” => “id_data”], is the same as the parent form
            	"ids_required" => [
            		"teacher_id" => "teacher_id",
            	],
            
          • “ids_aux_required” => [“get_data” => “id_data”], optional, works with ids_aux_required
          • “admin_forms_aux_id” => “form_key”, optional
          • “function” => “admin_menu_page_table_display_first_menu_child”,
          • “file” => “bft-admin-display-list-table.php”,
        1. Display a form

    The main difference is that it has two ids: the main id, and the foreig id. Do not put 'id’ in the foreign id. Plus it can show column_title_name

        • “ids_required” => [“get_data” => “id_data”],
          	"ids_required" => [
          		"note_id" => "id",
          		"teacher_id" => "teacher_id",
          	],
          
        • “admin_forms_id” => “form_key”,
        • “ids_aux_required” => [“get_data” => “id_data”], optional, works with ids_aux_required
        • “admin_forms_aux_id” => “form_key”, optional
        • “function” => “admin_menu_admin_form_page_display”,
        • “file” => “bft-admin-display-form.php”,
        • “function_load” => “custom_form_set_do_by_post”,
        • “tabs_new_or_edit_on_url” => true,
        • “new_text” => “New entry text”,

    An example of a nested form:

    	$this->admin_pages = [
    		"automated_data_manipulation_teachers" => [
    			"menu_title" => $this->__("Teachers"),
    			"page_title" => $this->__("List of teachers"),
    			"function" => "admin_menu_page_table_display_first_menu_child",
    			"file" => "bft-admin-display-list-table.php",
    			"children" => [
    				"automated_data_manipulation_teacher" => [
    					"ids_required" => [
    						"teacher_id" => "id",
    					],
    					"admin_forms_id" => "teachers",
    					"tabs_new_or_edit_on_url" => true,
    					"menu_title" => $this->__("Teacher"),
    					"page_title" => $this->__("Teacher form"),
    					"function" => "admin_menu_admin_form_page_display",
    					"file" => "bft-admin-display-form.php",
    					"function_load" => "custom_form_set_do_by_post",
    					"new_text" => $this->__("New teacher"),
    				],
    				"automated_data_manipulation_teacher_notes" => [
    					"ids_required" => [
    						"teacher_id" => "teacher_id",
    					],
    					"ids_aux_required" => [
    						"teacher_id" => "id",
    					],
    					"admin_forms_aux_id" => "teachers",
    					"menu_title" => $this->__("Teacher notes"),
    					"page_title" => $this->__("List of notes"),
    					"function" => "admin_menu_page_table_display_first_menu_child",
    					"file" => "bft-admin-display-list-table.php",
    					"children" => [
    						"automated_data_manipulation_teacher_note" => [
    							"ids_required" => [
    								"note_id" => "id",
    								"teacher_id" => "teacher_id",
    							],
    							"admin_forms_id" => "teachers_notes",
    							"ids_aux_required" => [
    								"teacher_id" => "id",
    							],
    							"admin_forms_aux_id" => "teachers",
    							"tabs_new_or_edit_on_url" => true,
    							"menu_title" => $this->__("Teacher note"),
    							"page_title" => $this->__("Teacher note form"),
    							"function" => "admin_menu_admin_form_page_display",
    							"file" => "bft-admin-display-form.php",
    							"function_load" => "custom_form_set_do_by_post",
    							"new_text" => $this->__("New note"),
    						],
    					],
    				],
    			],
    		],
    	];
    

Backend Frontend Template Pro: automatic data manipulation 1

Backend Frontend Template Pro: automatic data manipulation 2

In the “Menu Pro” section there is an example of nested forms, if you have not seen that section it is recommended to view it

Manual data manipulation: inside a function

  1. WordPress functions

    WordPress functions For database manipulation WordPress provide the class $wpdb, call it inside a function with global $wpdb

    Some data manipulation functions of $wpdb are:

    • $wpdb->prefix: the WordPress database prefix, all tables will have this prefix
    • $wpdb->query($query): do a direct query
    • $wpdb->insert($table_name, $array_data): insert a new entry (the function sanitizes the inputs)
    • $data = $wpdb->insert_id: last inserted ID
    • $wpdb->delete($table_name, $array_search_data): delete entries that match the array search data (the function sanitizes the inputs)
    • $wpdb->update($table_name, $array_data, $array_search_data): update the data in the entries that match the array data (the function sanitizes the inputs)
    • $data = $wpdb->get_results($query, “ARRAY_A” ): returns a two-dimensional array with all the data

    All the info of this class can be found here

  2. Backend Frontend Template functions

    BFT provides additional functions for the programmers that prefer this database access style

    • $this->wpdb_get_results_array($query): returns a two-dimensional array with all the data
    • $this->wpdb_get_results_with_index($query): Returns a two-dimensional array with all the data, the index of each row will be the data in the first column
    • $this->wpdb_get_results_one_data_per_row($query): returns a one-dimensional array, returns only the first column in each row
    • $this->wpdb_get_results_index_and_data_per_row($query): returns a one-dimensional array, the first column data will be the index, the second the data
    • $this->wpdb_get_result_one_data($query): returns a string, returns only the first column of the first row
    • $this->wpdb_insert_update_on_duplicate_key($table, $data, $multi_row = false, $modified_value = NULL, $data_for_update = array()): insert or update multiple data, more explanation below (the function sanitizes the inputs)
    • $this->wpdb_insert_update_on_duplicate_key_delete_others($table, $data, $column_where_delete, $value_where_delete): insert or update multiple data, then delete the non updated rows, more information below (the function sanitizes the inputs)

    Additional explanations

    • wpdb_insert_update_on_duplicate_key

      $this->wpdb_insert_update_on_duplicate_key($table, $data, $multi_row = false, $modified_value = NULL, $data_for_update = array())

      This function can insert or update one or more rows, when updating the data can use extra data or only use the data used to insert

      • $table: table name
      • $data: mono-dimensional array (an insert) or two-dimensional (several inserts), data can have true NULL or string 'NULL’
      • $multi_row: true if multiple inserts are required
      • $modified_value: modification datetime, it will use the column $this->database_datetime_modified_name, if NULL it will use the current time
      • $data_for_update: the data used for the update case, if the array is empty the update will use the $data info
    • wpdb_insert_update_on_duplicate_key_delete_others

      $this->wpdb_insert_update_on_duplicate_key_delete_others($table, $data, $column_where_delete, $value_where_delete)

      This function uses wpdb_insert_update_on_duplicate_key, then it does a delete of the rows not updated

      • $table: table name
      • $data: mono-dimensional array (an insert) or two-dimensional (several inserts), data can have true NULL or string 'NULL’
      • $column_where_delete: the column to check (id, foreign key, etc.)
      • $value_where_delete: the ID to check in column_where_delete

      This function is equivalent to deleting all data and inserting the new data, the problem is database abuse, with this the database updates all the data (maybe just update $this->database_datetime_modified_name), and then delete the not updated data. Less UPDATE/INSERTS, less DELETES

    NOTE: due to how WordPress works the BFT functions are not accesible from create/update/delete database in class-your-plugin-install-upgrade-deinstall-database.php only use the official WordPress function

  3. Data sanitization

    For input sanitization WordPress provide several sanitization functions in the class $wpdb

    Some sanitation functions of $wpdb are:

    • $wpdb->sanitize_key: sanitize an ID
    • $wpdb->sanitize_text_field: sanitize a string
    • $wpdb->sanitize_textarea_field: sanitize a multiple string
    • $wpdb->prepare: sanitize a full query

    All sanitation information can be found here

    For the use of $wpdb->prepare($query, $args) Check the information

Manual data manipulation: paginated listing by query

A paginated listing per query is possible by pointing the page to a custom function, and use inside the function $this->display_table_query_custom() and finally display the page with $this->admin_menu_page_display()

It is also necessary to use the file “file” => “bft-admin-display-list-table.php”

$this->display_table_query_custom()

	display_table_query_custom(
		$query_select_inside,
		$query_from_inside,
		$query_where_inside,
		$group_inside,
		$ids,
		$columns_tables_dont_search,
		$search_concat,
		$column_key,
		$columns_tables,
		$columns_labels,
		$column_action_add,
		$status_system = false,
		$write_log_query = false
	)
  1. $query_select_inside

    SELECT part of the query, without the SELECT tag

  2. $query_from_inside

    FROM part of the query, without the FROM tag

  3. $query_where_inside

    WHERE part of the query, without the WHERE tag

  4. $group_inside

    GROUP BY part of the query, without the FROM tag

  5. $ids

    Ids to search for, the indexes will be the column key

    It can be an empty array, an array with manual data, or an automatic array with the ids_required_get_data thanks to $this->ids_required_and_optional_check_and_get()

    			$ids = [
    				"teacher_id" => "1",
    			];
    
    			$ids = $this->ids_required_and_optional_check_and_get (
    				$ids_principal_aux_type = "principal",
    				$ids_require_optional_type = "both",
    				$read_all_get_data = false
    			);
    
  6. $columns_tables_dont_search

    Don't search the search string in these columns. The indexes will be the column key

    			$columns_tables_dont_search = [
    				"column_1" => "table",
    				"column_2" => "table",
    			];	
    
  7. $search_concat

    Two-dimensional array, first dimension is the final column, the second dimension is the columns of CONCAT, for search the string on merged columns

    Ex:

    			search_string: "Lorem Ipsum"
    			row_1 column_1: "Lorem"
    			row_1 column_2: "Ipsum"
    
    			$search_concat = [
    				[
    					"column_1" => "table",
    					"column_2" => "table",
    				],
    			];	
    
  8. $column_key

    Column key of the query

  9. $columns_tables

    The tables of the columns, with indexes

    			$columns_tables_dont_search = [
    				"column_1" => "table",
    				"column_2" => "table",
    			];	
    
  10. $columns_labels

    The labels of the columns, with indexes of the columns

    			$columns_labels = [
    				"id" => $this->__("Nº"),
    				"column_2" => $this->__("Label of the column"),
    				"column_3" => "Label without internalization",
    			];
    
  11. $column_action_add

    Add or not add a link

    • column_action_key: the column key for the link, false if a link is not needed
    • column_action_edit_slug: the slug of the page
    			$column_action_add = [
    				"column_action_key" => "course_id",
    				"column_action_edit_slug" => $this->admin_pages_slug_name_prefix."_"."slug_of_the_page",
    			];
    
    			$column_action_add = [
    				"column_action_key" => false,
    				"column_action_edit_slug" => "",
    			];
    
  12. $status_system

    The table uses the column $this->database_status_column_name

  13. $write_log_query

    It writes to the registry the base query

A complete example of $this->display_table_query_custom() used in a function

	public function manual_data_manipulation_listing_by_query_example() {
		global $wpdb;

		$this->admin_permission_check();

		$language_admin = $this->language_admin_get();
		$ids = array();

		
		//Add or not add a "to go element", inside the array: "column_action_key"  with the future get data with the id, "column_action_edit_slug" the target slug for the edit action
		$column_action_add = [
			"column_action_key" => "course_id", //The future get data name with the id
			"column_action_edit_slug" => $this->admin_pages_slug_name_prefix."_automated_data_manipulation_course", //Target slug for the edit action
		];

		/*
		SELECT
			wp_bft_pro_courses.id,
			wp_bft_pro_courses_i18n.name_i18n AS course
		FROM
			wp_bft_pro_courses
			LEFT JOIN	wp_bft_pro_courses_i18n
				ON	wp_bft_pro_courses.id = wp_bft_pro_courses_i18n.course_id
				AND wp_bft_pro_courses_i18n.language = 'en';
		WHERE
			teacher_id is not null
		*/

		//SELECT part of the query, whitout the SELECT tag
		$query_select_inside = "
			".$wpdb->prefix.$this->plugin_slug."_"."courses.id,
			".$wpdb->prefix.$this->plugin_slug."_"."courses_i18n.name_i18n AS name";
			
		//FROM part of the query, whitout the FROM tag
		$query_from_inside = "
			".$wpdb->prefix.$this->plugin_slug."_"."courses
			LEFT JOIN	".$wpdb->prefix.$this->plugin_slug."_"."courses_i18n
				ON	".$wpdb->prefix.$this->plugin_slug."_"."courses.id = ".$wpdb->prefix.$this->plugin_slug."_"."courses_i18n.course_id
				AND ".$wpdb->prefix.$this->plugin_slug."_"."courses_i18n.`".$this->database_i18n_column_language."` = '$language_admin'";

		//WHERE part of the query, whitout the WHERE tag
		$query_where_inside = "
			teacher_id IS NOT NULL";

		//GROUP BY part of the query, whitout the FROM tag
		$group_inside = "";

		//Column key of the query
		$column_key = "id";

		//The tables of the columns, with indexes of the tables
		$columns_tables = [
			"id" => $wpdb->prefix.$this->plugin_slug."_"."courses",
			"name" => $wpdb->prefix.$this->plugin_slug."_"."courses_i18n",
		];

		//Don't search the search string on this columns. The indexes will be the column key
		$columns_tables_dont_search = array();

		//Two dimensional array, first dimension is the final colum, the second dimension are the colums of the CONCAT ("column" => "table"), for search  the string on the final CONCAT data
		$search_concat = [];

		//The labels of the columns, with indexes of the columns
		$columns_labels = [
			"id" => $this->__("Nº"),
			"name" => $this->__("Course"),
		];

		$status_system = false;
		$write_log_query = false;
		$args = $this->display_table_query_custom($query_select_inside, $query_from_inside, $query_where_inside, $group_inside, $ids, $columns_tables_dont_search, $search_concat, $column_key, $columns_tables, $columns_labels, $column_action_add, $status_system, $write_log_query);

		$args["ids"] = $ids;
		$this->admin_menu_page_display($args);
	}

Backend Frontend Template Pro: listing by query

Manual data manipulation: listing by Array

A direct listing by array is possible by sending all the needed data to $this->admin_menu_page_display()

It's also necessary to use “file” => “bft-admin-display-list-table.php”

  1. Array data

    1. Required data

      1. data

        Array with each row of data, without id. Within each row, each column of data has the column keys

      2. columns

        Array with the keys and names of the columns

      	$display_table_data = [
      			"data" => [
      				[
      					"id" => "1",
      					"name" => "Lorem Ipsum Name",
      				],
      				[
      					"id" => "2",
      					"name" => "Dolor Sit Name",
      				],
      			],
      			"columns" => [
      				"id" => "Nº",
      				"name" => "Name",
      			],
      		];	
      
    2. Optional data

      1. $column_action_add

        Add or not add a link

        • column_action_key: the column key for the link, false if a link is not needed
        • column_action_edit_slug: the slug of the page
        					$column_action_add = [
        						"column_action_key" => "course_id",
        						"column_action_edit_slug" => $this->admin_pages_slug_name_prefix."_"."slug_of_the_page",
        					];
        
        					$column_action_add = [
        						"column_action_key" => false,
        						"column_action_edit_slug" => "",
        					];
        
      2. get_extra_all_rows

        Array with data to add in the GET data of the links

        					"get_extra_all_rows" => [
        						"example_get_data" => "Lorem_Ipsum",
        					],
        
        					GET resultant: [...]?page=[...]&example_get_data=Lorem_Ipsum
        
      3. items_per_page

        The items for a page, for data not paginated it's not needed this data

      4. items_column_key

        The key column

      5. items_count

        Total item count, for data not paginated it's not needed this data

      6. items_count_with_search

        Total item count with search, for data without search it's not needed this data

      7. search_text

        The search label, for data without search it's not needed this data

      8. page_slug

        The actual slug page

      9. ids

        Ids used on this page

  2. Sending the data

    For display a table the function admin_menu_page_display() needs “display_table” => true” and “display_table_data” with the listing data

    			$args = [
    				"ids" => $ids,
    				"display_table" => true,
    				"display_table_data" => $display_table_data,
    			];
    
    			$this->admin_menu_page_display($args);
    

A complete example of a custom listing by array data used in a function

 

	public function manual_data_manipulation_listing_by_array_example() {
		$this->admin_permission_check();

		$ids = $this->ids_required_and_optional_check_and_get($ids_principal_aux_type = "principal", $ids_require_optional_type = "both", $read_all_get_data = false);
		//$ids = array();

		/*
		$display_table_data = [
			"data" => [
				[
					"id" => "1",
					"name" => "Lorem Ipsum Name",
				],
			],
			"columns" => [
				"id" => "Nº",
                "name" => "Name",
			],
			"column_action_add" => [
				"column_action_key" => false,
                "column_action_edit_slug" => "",
			],
			"get_extra_all_rows" => false,
			"items_per_page" => 999999,
			"items_column_key" => "id",
			"items_count" => "1",
			"items_count_with_search" => "1",
			"search_text" => false,
			"page_slug" => $this->plugin_slug."_manual_data_manipulation_listing_by_array_example",
			"ids" => [],
		];
		
		$display_table_data = [
			"data" => [
				[
					"id" => "1",
					"name" => "Lorem Ipsum Name",
				],
			],
			"columns" => [
				"id" => "Nº",
                "name" => "Name",
			],
			"column_action_add" => [
				"column_action_key" => "course_id",
                "column_action_edit_slug" => $this->admin_pages_slug_name_prefix."_automated_data_manipulation_course",
			],
			"get_extra_all_rows" => [
				"example_get_data" => "Lorem Ipsum",
			],
			"items_per_page" => 999999,
			"items_column_key" => "id",
			"items_count" => "1",
			"items_count_with_search" => "1",
			"search_text" => false,
			"page_slug" => $this->plugin_slug."_manual_data_manipulation_listing_by_array_example",
			"ids" => [],
		];
		*/

		$display_table_data = [
			"data" => [
				[
					"id" => "1",
					"name" => "Lorem Ipsum Name",
				],
			],
			"columns" => [
				"id" => "Nº",
				"name" => "Name",
			],
		];

		$args = [
			"ids" => $ids,
			"display_table" => true,
			"display_table_data" => $display_table_data,
		];

		$this->admin_menu_page_display($args);
	}

Backend Frontend Template Pro: listing by Array

Manual data manipulation: forms

A manual form is possible by pointing the page to a custom function for show the form and a custom function_load for store the changes on the form

You can use the page bft-admin-display-form.php to display the form or create your own page

  1. Custom function

    Get the data your own way and send it to the function $this->admin_menu_page_display(), for example:

    	$args = [
    		"title" => $this->admin_pages_data_get("page_title"),
    		"id" => $id,
    		"ids" => $ids,
    		"form_data" => [
    			"page_name" => $this->page_name,
    			"data" => $data,
    			"column_key" => $column_key,
    		],
    	];
    
    	$this->admin_menu_page_display($args);
    

    If the page uses bft-admin-display-form.php the data will be formed in this way:

    	$data = [
    		"column_name_1" => [
    			"label" => "",
    			"placeholder" => "",
    			"type" => text,
    			"id" => column_name_1,
    			"value" => "",
    		],
    		"column_name_2" => [
    			"label" => "",
    			"placeholder" => "",
    			"type" => text,
    			"id" => column_name_2,
    			"value" => "",
    		],
    		"column_group_name_1" => [
    			"label" => "",
    			"values" => [
    				"column_name_3" => [
    					"label" => "",
    					"placeholder" => "",
    					"type" => text,
    					"id" => column_name_3,
    					"value" => "",
    				],
    				"column_name_4" => [
    					"label" => "",
    					"placeholder" => "",
    					"type" => text,
    					"id" => column_name_4,
    					"value" => "",
    				],
    			],
    		],
    	];
    
  2. Custom function_load

    This function is executed first, POST data sent from the browser or not, break the function if post data not sent or some error was found, example:

    	if ("POST" !== $_SERVER['REQUEST_METHOD']) {
    		return false;
    	}
    

    At the end of the function use redirect if needed. To redirect can be used wp_redirect() or $this->custom_form_redirect()

    1. wp_redirect($url)

      This function of WordPress needs the full URL, that's why BFT offers better alternatives:

      • $this->admin_page_url_get()

        Returns the currently displayed page without additional GET data, only the GET data of the page

        			https://yourfuturewebsite/wp-admin/admin.php?page=bft_pro_manual_data_manipulation_form_explanation
        
      • $this->gets_for_admin_page($id)

        Returns the GET data needed for the form of the page. Ex: if a page uses the form 'courses', send it the id '1'’ and it will retrieve:

        			&course_id=1
        
    2. $this->custom_form_redirect($id = false)

      This BFT function does a redirect to the full URL with the ID needed for the form and adds &action=new or &action=edit depending on whether ID has been provided

  3. Full example

    	$this->admin_pages = [
    		"manual_data_manipulation_form_simple_example_teacher" => [
    				/*"ids_required" => [
    					"teacher_id" => "id",
    				],*/
    				"admin_forms_id" => "teachers",
    				"columns" => [
    					"id",
    					"name",
    					"surname",
    					"id_card",
    				],
    				"menu_title" => $this->__("Example 1"),
    				"page_title" => $this->__("Teacher")." 1,
    				"function" => "manual_data_manipulation_form_example_for_two_pages_get",
    				"file" => "bft-admin-display-form.php",
    				"function_load" => "manual_data_manipulation_form_example_for_two_pages_set",
    			],
    		];
    
    	public function manual_data_manipulation_form_example_for_two_pages_get() {
    		$this->admin_permission_check();
    
    		global $wpdb;
    
    		$ids = $this->ids_required_and_optional_check_and_get($ids_principal_aux_type = "principal", $ids_require_optional_type = "both", $read_all_get_data = false);
    		//$ids will be empty on the example, adding the id
    		$ids["id"] = "1";
    
    		//teachers or students
    		$admin_forms_id = $this->admin_pages_data_get_admin_forms_id();
    
    		/*
    		Remember: on $this->admin_pages you can store all the data you want and retrieve it with $this->admin_pages_data_get();
    
    		Teachers:
    		"columns" => [
    			"id",
    			"name",
    			"surname",
    			"id_card",
    		],
    
    		Students:
    		"columns" => [
    			"id",
    			"name",
    			"surname",
    		],
    		*/
    		$columns_array = $this->admin_pages_data_get( "columns");
    
    		//$language_admin = $this->language_admin_get();
    
    		if (0 == count($ids)
    			||	false == $admin_forms_id
    			||	!isset($this->admin_forms[$admin_forms_id])
    			||	false == $columns_array) {
    			$error_message = $this->__("Error message");
    			$this->error_throw ($error_message, $error_throw_what_do_use_this = "show_error_and_die");
    		}
    		$id = sanitize_key(array_values($ids)[0]);
    
    
    		$query = "
    			SELECT
    				".implode(", ", $columns_array)."
    			FROM
    				".$this->admin_forms[$admin_forms_id]["table"]."
    			WHERE
    				id = '$id'";
    
    
    		$data_database = $this->wpdb_get_result_one_row($query);
    
    		if (0 == count($data_database)) {
    			$error_message = $this->__("Error message");
    			$this->error_throw ($error_message, $error_throw_what_do_use_this = "show_error_and_die");
    		}
     
    
    		
    		$data = array();
    		foreach ($data_database AS $data_database_key => $data_database_value) {
    			if (isset($this->admin_forms[$admin_forms_id]["columns"][$data_database_key])) {
    				$data[$data_database_key] = $this->admin_forms[$admin_forms_id]["columns"][$data_database_key];
    				$data[$data_database_key]["value"] = $data_database_value;
    			}
    		}
    
    		
    		$args = [
    			"title" => $this->admin_pages_data_get("page_title"),
    			"id" => $id,
    			"ids" => $ids,
    			"form_data" => [
    				"page_name" => $this->page_name,
    				"data" => $data,
    				"column_key" => $this->admin_forms[$admin_forms_id]["column_key"],
    			],
    		];
    
    		$this->admin_menu_page_display($args);
    	}
    
    	public function manual_data_manipulation_form_example_for_two_pages_set() {
    		$this->admin_permission_check();
    
    		$redirect_page = false;
    		$request_method = "POST";
    
    		if ($request_method !== $_SERVER['REQUEST_METHOD']) {
    			//Post data not found
    			return false;
    		}
    		
    
    		$data_raw = $_POST;
    
    		if (	!isset($data_raw["_wpnonce"])
    			||	!wp_verify_nonce($data_raw["_wpnonce"], $this->plugin_slug."_form")) {
    			//Nonce not verified
    			return false;
    		}
    
    		//For security check
    		$this->ids_required_and_optional_check_and_get($ids_principal_aux_type = "principal", $ids_require_optional_type = "both", $read_all_get_data = false);
    
    
    		//teachers or students
    		$admin_forms_id = $this->admin_pages_data_get_admin_forms_id();
    
    		/*
    		Remember: on $this->admin_pages you can store all the data you want and retrieve it with $this->admin_pages_data_get();
    
    		Teachers:
    		"columns" => [
    			"id",
    			"name",
    			"surname",
    			"id_card",
    		],
    
    		Students:
    		"columns" => [
    			"id",
    			"name",
    			"surname",
    		],
    		*/
    		$columns_array = $this->admin_pages_data_get( "columns");
    
    		if (	false == $admin_forms_id
    			||	!isset($this->admin_forms[$admin_forms_id])
    			||	false == $columns_array) {
    			return false;
    		}
    
    
    
    		$data_row = array();
    
    		foreach ($columns_array AS $column) {
    			if (!isset($data_raw[$column])) {
    				return false;
    			}
    
    			$data_row[$column] = $data_raw[$column];
    		}
    
    
    		$this->wpdb_insert_update_on_duplicate_key($this->admin_forms[$admin_forms_id]["table"], $data_row, $multi_row = false);
    
    		/*
    		Query executed:
    
    		INSERT INTO
    			wp_bft_pro_teachers
    			(
    				datetime_modified,
    				id,
    				name,
    				surname,
    				id_card,
    				datetime_created
    			)
    		VALUES
    			(
    				'2023-01-01 00:00:01',
    				'1',
    				'Teacher 1 edited',
    				'surname 1',
    				'1111',
    				'2023-01-01 00:00:01'
    			)
    		ON DUPLICATE KEY UPDATE
    			datetime_modified = '2023-01-01 00:00:01',
    			id = '1',
    			name = 'Teacher 1 edited',
    			surname = 'surname 1',
    			id_card = '1111'
    
    		Obviously the duplicate key will be executed on this case for the "id" column
    
    		Note the automated columns:
    		$this->database_datetime_created_name (datetime_created)
    		$this->database_datetime_modified_name (datetime_modified)
    		*/
    
    		if (true === $redirect_page) {
    			//ids not specified in the example page, this is for a complete case
    			$this->custom_form_redirect($data_row["id"]);
    		}
    		else {
    			return true;
    		}
    	}
    

Backend Frontend Template Pro: manual form example

Internationalization

Prepare the plugin for future translations

  1. Preparing the text

    For specify a text that maybe needs translation, WordPress provides the functions:

    • __(‘string’, 'translation domain/plugin id'): for direct translation
    • _e(‘string’, 'translation domain/plugin id'): for direct translation and display the translated text
    • esc_html_(‘string’, 'translation domain/plugin id') for translation and escape HTML characters
    • esc_html_e(‘string’, 'translation domain/plugin id') for translation, escape HTML characters and display the resulting text

    For more functions look in the WordPress documentation: link here

    With that, WordPress will use a translation file if it exists and has that sentence, or an external plugin will be able to translate your plugin into the visitor's language

    BFT also provides the option of use its intermediary functions to avoid having to put the domain in every string prepared for a future translation . With ALL the WordPress translation functions

    For that, call the function through ‘$this’ and don't put the translation domain

    • $this->__(‘string’): for direct translation
    • $this->_e(‘string’): for direct translation and display the translated text
    • $this->esc_html_(‘string’) for translation and escape HTML characters
    • $this->esc_html_e(‘string’) for translation, escape HTML characters and display the resulting text

    NOTE: the functions allow the domain field, if you put a domain then that domain will be use

  2. Preparing the translation files

    The translation files are allocated in plugin_folder/languages, BFT automatically will set WordPress to search translations on that folder

    The language files are:

    • .pot: Portable Object Template, the master file with all the strings
    • .po: Portable Object, the file with the strings translated to one language
    • .mo: Portable Object, Machine Object, the compiled data from the .po file, WordPress will use this file

    Steps to translate a plugin:

    1. Create a new .pot file of your plugin
    2. Update/merge the original .pot with the new .pot file
    3. Delete the old .pot files and rename the new merged file if needed
    4. Prepare the .po language file
      1. If it's a new translation language: duplicate the .pot file and change the name and extension to the designed language, like bft-pro – copy.pot to bft-pro-es.po or bft-pro-es_ES.po
      2. If it's a existing translation language: update/merge the file with the new .pot file
    5. Translate the sentences of the .po file
    6. Create the .mo file from the .po file

    To create and combine the files and translate the sentences you can use programs such as Poedit or EazyPo

The WordPress log with BFT

The log in WordPress is activated in wp-config.php, change:

  • define( ‘WP_DEBUG’, true );
  • define( ‘WP_DEBUG_LOG’, true );

Now you can check the log in wp-content/debug.log

For printing to the log you can use the WordPress function error_log($string_or_number), but with Backend Frontend Template you can use: $this->debug_log_write($whatever)

$this->debug_log_write() it's a better option because it shows:

  • ‘NULL’ if is a NULL variable
  • ‘TRUE’ and 'FALSE'’ if it's a boolean
  • print_r() whether it is an array or an object

Now you can print in the log the variable you want

In addition, BFT offers an alternative name for debug_log_write: $this->write_log()

Function visibility

A quick summary for what visibility to use on the functions of your plugin:

  • Private

    Don't use private functions, BFT use inheritance on the classes and a private function can't inheritance

  • Protected

    Ideal for the internal functions for security reasons, only your classes can use these functions

  • Public

    Some functions need to be public due to how WordPress works:

    • Functions called via $this->admin_pages -> an_admin_page -> 'function_load’ data
    • Functions called via $this->admin_pages -> an_admin_page -> 'function’ data
    • Functions called via install, upgrade or uninstall, upgrade or uninstall
    • Functions called via shortcodes
    • Functions called via AJAX responses

Secure the functions

  1. Function load

    The 'function_load’ option of the menu is the function that the page executes before sending the HTML headers

    By default all pages execute admin_permission_check_and_ids_required_check_function_load(), the executed function can be changed on
    class-your-plugin-admin -> $this->admin_pages_function_load_default = “admin_permission_check_and_ids_required_check_function_load”

    The function admin_permission_check_and_ids_required_check_function_load() checks if the admin capabilities are correct and if the id required data is not missing. In this function it works the 'go_to_parent’ option of the menu

    This function can be called at the beginning of a custom function_load to check all before save changes

  2. Function

    The 'function’ option of the menu is the main function that the page executes

    By default all pages execute admin_permission_check_and_ids_required_and_optional_check_page_display(), the executed function can be changed on
    class-your-plugin-admin -> $this->admin_pages_function_default = “admin_permission_check_and_ids_required_and_optional_check_page_display”

    The function admin_permission_check_and_ids_required_and_optional_check_page_display() checks if the admin capabilities are correct and if the id required data is not missing

    On a custom function there are functions for checking the access and to retrieve the ids:

    • $this->admin_permission_check(): check the admin permissions and throw an error if needed. Recommended for use at the beginning of the function
    • $this->ids_required_check(): check the ids required permissions and throw an error if needed
    • $this->ids_required_and_optional_check_and_get ($ids_principal_aux_type = “principal”, $ids_require_optional_type = “both”, $read_all_get_data = true, $return_type = “array”, $error_message = NULL, $die_always_if_required_missing = true, $triggered_on_function_load = false): check the ids and return the data

    Additional explanations

    • ids_required_and_optional_check_and_get

      $this->ids_required_and_optional_check_and_get ($ids_principal_aux_type = “principal”, $ids_require_optional_type = “both”, $read_all_get_data = true, $return_type = “array”, $error_message = NULL, $die_always_if_required_missing = true, $triggered_on_function_load = false)

      This is an alternative method to retrieve manually the data with admin_pages_data_get()

      	$data = $this->admin_pages_data_get("ids_required_get_data");
      	$data = $this->admin_pages_data_get("ids_principal_all_get_data");
      	$data = $this->admin_pages_data_get("ids_aux_required_get_data");
      	$data = $this->admin_pages_data_get("ids_aux_all_get_data");
      	$data = $this->admin_pages_data_get("ids_required_all_get_data");
      	$data = $this->admin_pages_data_get("ids_all_get_data");
      

      But this method check if there are ids requiered missing, also can add GET data not define on the ids

      Function variables:

      • $ids_principal_aux_type: What ids to receive. Options: 'principal', 'aux’ and 'both', default 'principal'. Principal -> “ids_required', aux -> 'ids_aux_required', both -> 'ids_required_all’ (ids_required and ids_aux_required)
      • $ids_require_optional_type: Select if only principal ids or add the optionals. Options: 'require’ and 'both', default 'require'. If both: Principal -> 'ids_principal_all_get_data’ (ids_required and ids_optional), aux -> 'ids_aux_all_get_data’ (ids_aux_required and ids_aux_optional), both -> 'ids_all_get_data’ (ids_required, ids_optional, ids_aux_required and ids_aux_optional)
      • $read_all_get_data: Read all GET data from the URL and add additional options, default True. 'page’ GET data will not be added, the GET data key needs to be more than 2 characters
      • $return_type: 'array’ (default option), 'array_always’ (don't return false, instead it returns an empty array if error found), 'data’ (first data if multiple ids)
      • $error_message: default text: Missing required ID’
      • $die_always_if_required_missing: If true adds 'show_error_and_die’ in a error throw. Default true
      • $triggered_on_function_load: If triggered on a function_load, select false in a main function. Default false

      Example of use:

      	$ids = $this->ids_required_and_optional_check_and_get ($ids_principal_aux_type = 'principal', $ids_require_optional_type = 'required', $read_all_get_data = false);
      	$ids_aux = $this->ids_required_and_optional_check_and_get ($ids_principal_aux_type = 'aux', $ids_require_optional_type = 'required', $read_all_get_data = false);

Manage and display errors

  1. Show an error

    Backend Frontend Template can easily display errors, and does not repeat the same error on the same load. Also: the title of the plugin will be added to the message

    • $this->error_show ($error_message = “”) displays an error message. If $error_message = “” it shows “Error detected”
    • Adding error_message in the GET URL, the error message can be triggered with the functions $this->admin_permission_check() or $this->error_throw()
  2. Throw an error

    BFT can throw errors with
    $this->error_throw ($error_message = “”, $error_throw_what_do_use_this = NULL, $error_throw_file_change_use_this = NULL, $triggered_on_function_load = false, $page_id = NULL)

    • $error_message: error to send to $this->error_show(), but first it will show the 'error_message’ stored in the URL
    • $error_throw_what_do_use_this: to use this data instead of $this->admin_pages_data_get(“error_throw_what_do”), options: show_error, show_error_and_die, go_to_parent
    • $error_throw_file_change_use_this: NULL by default, it use this data instead of $this->admin_pages_data_get(“error_throw_file_change”), for change the file displayed if error triggered
    • $triggered_on_function_load: false by default, 'go_to_parent’ 'go_to_parent' only works if true == $triggered_on_function_load because it's needed do the redirect before sending the headers
    • $page_id: the key/page name, if null it's the visualized page
  3. Examples

    • This page show an error with:
      		$error_message = $this->__("This is an error test");
      		$this->error_show ($error_message);
      
    • The children page throws an error and return to this page with:
      		$this->admin_pages = [
      			"errors_manage" => [
      				"menu_title" => $this->__("Errors"),
      				"page_title" => $this->__("Manage and display errors"),
      				"file" => "bft-admin-display-errors-manage-show-pro.php",
      				"children" => [
      					"throw_error_and_return_to_parent" => [
      						"menu_title" => $this->__("Throw error and return to parent"),
      						"page_title" => $this->__("Throw error and return to parent"),
      						"ids_required" => [
      							"nonexistent_id" => "nonexistent_id",
      						],
      						"error_throw_what_do" => "go_to_parent",
      					],
      				]
      			],	
      		];
      

      Result that gives the test to enter a child page that gives error:

      Backend Frontend Template Pro: showing child page error that redirects to parent page

Download private files

The folder private is a private folder thanks to his .htaccess

	Deny from all

Therefore a simple link will not work, but a BFT download works (Examples available inside of Backend Frontend Pro: the WordPress Plugin Template)

For download it's needed a page where to send the data, this page uses the “function_load” => “download_file_private”

Plus note that download_file_private() search the 'file' get data’ on the URL, that's why it's needed to filter the URL calls without 'file' data: “ids_required” => [“file” => “file”]

Remember:

  • The menu don't show a tab with GET data missing
  • WordPress admin menu can't filter a existing page, don't use it on a parent page:Backend Frontend Template Pro: problem with the download page if it is hosted as a parent page

 

This example uses this download page:

	"download_explanation" => [
		[...]
		"children" => [
			"download_file_private" => [
				"menu_title" => $this->__("Download private file"),
				"page_title" => $this->__("Download private file"),
				"menu_slug" => "download",
				"function_load" => "download_file_private",
				"file" => "bft-admin-display-blank-page.php",
				"ids_required" => [
					"file" => "file",
				],
				"error_throw_what_do" => "go_to_parent",
			],
		]
	],	

Iframe on an admin page

There are two types of iframes, both use the page bft-admin-display-iframe.php, but you can make your own page:

	<?php
	if (isset($iframe_admin_page_slug) && false != $iframe_admin_page_slug)  {
	?>
		<iframe src="" width="100%" height="700">
		</iframe>
	<?php
	}

	if (isset($iframe_url) && false != $iframe_url)  {
	?>
		<iframe src="" width="100%" height="700">
		</iframe>
	<?php
	}
	?>

There are two types of iframes:

  1. Iframe by slug

    This method requires two pages: One is the main page with the iframe, the other is the iframe itself

    1. Main page

      The main page especifies the slug of the iframe page with “iframe_admin_page_slug” => “iframe page” and the display iframe page with “file” => “bft-admin-display-iframe.php”

      	"iframe_admin_page_slug_test" => [
      		"menu_title" => $this->__("Iframe by slug"),
      		"page_title" => $this->__("Iframe by slug").': "iframe_admin_page_slug" => "iframe_test"',
      		"file" => "bft-admin-display-iframe.php",
      		"iframe_admin_page_slug" => "iframe_test",
      	],
      
    2. Iframe page

      The iframe page will call a function_load with a die, a function_load runs before displaying the WordPress Admin Menu. With “tab_show” => false we ignore the page on the BFT tab menu

      	"iframe_test" => [
      		"tab_show" => false,
      		"file" => "bft-admin-display-iframe-test.php",
      		"function_load" => "function_load_page_display",
      	],
      
  2. Iframe by URL

    This method only needs the URL of the iframe: “iframe_url” => admin_url() and the display iframe page “file” => “bft-admin-display-iframe.php”

    	"iframe_url_test" => [
    		"menu_title" => $this->__("URL iframe"),
    		"page_title" => $this->__("URL iframe").": ".admin_url(),
    		"file" => "bft-admin-display-iframe.php",
    		"iframe_url" => admin_url()."admin.php?page=".$this->plugin_slug."_iframe_test",
    	],
    

    Note: admin_url() points to the WordPress Admin Menu, it's used on this example because normally an external iframe will not work

  3. Display PDF on ifame

    With all this we can show a private PDF via slug iframe with “private_file_dir” => “file on private folder” and “function_load” => “show_pdf_private”

    	"iframe_admin_page_slug_test_pdf" => [
    		"menu_title" => $this->__("PDF example"),
    		"page_title" => $this->__("Iframe by slug - PDF example").': "private_file_dir" => "hello_world.pdf" | "function_load" => "show_pdf_private"',
    		"file" => "bft-admin-display-iframe.php",
    		"iframe_admin_page_slug" => "iframe_test_pdf",
    		"private_file_dir" => "hello_world.pdf",
    	],
    	"iframe_test_pdf" => [
    		"tab_show" => false,
    		"function_load" => "show_pdf_private",
    	],
    

Backend Frontend Template Pro: iframe on an admin page

Backend Frontend Template Pro: displaying PDF by iframe on an admin page

Language functions

Backend Frontend Template offers several functions about languages:

  1. $this->languages_codes_names_get()

    Returns a language list

    	$languages_codes_names = [
    		'ab' => $this->__('Abkhazian'),
    		'aa' => $this->__('Afar'),
    		'af' => $this->__('Afrikaans'),
    		'ak' => $this->__('Akan'),
    		'sq' => $this->__('Albanian'),
    		'am' => $this->__('Amharic'),
    		[...]
    
  2. $this->languages_get($country_code)

    Returns the data saved in the $this- setting>option_field_get(“languages”)

  3. $this->language_admin_get($country_code)

    Returns the data saved in the $this- setting>option_field_get(“language_admin”) if exists in $this->option_field_get(“languages”)

    • If languages is empty it will set the languages 'en’ and 'es’
    • If language_admin is empty or not found in the languages, it will set the first language stored in languages

Country functions

Backend Frontend Template offers several functions about countries:

  1. $this->countries_codes_names_get()

    Returns a country list

    	$countries_codes_names = [
    		'AF'=> $this->__('Afghanistan'),
    		'AX'=> $this->__('Aland Islands'),
    		'AL'=> $this->__('Albania'),
    		'DZ'=> $this->__('Algeria'),
    		'AS'=> $this->__('American Samoa'),
    		'AD'=> $this->__('Andorra'),	
    		[...]
    
  2. $this->country_code_name_get($country_code)

    Returns the country name via country code

Currency functions

Backend Frontend Template offers several functions about currencies:

  1. $this->currencies_array_get()

    Returns to a currency list with all data, including the numer of currency on the ISO 4217 standard 4217

    	$currencies_name_and_symbol = [
    		'ARS' => [
    			'id'   => 'ARS',
    			'name'   => 'Argentina Peso',
    			'symbol' => '$',
    			'code' => '032',
    		],
    		'AWG' => [
    			'id'   => 'AWG',
    			'name'   => 'Aruba Guilder',
    			'symbol' => 'ƒ',
    			'code' => '533',
    		],
    		[...]
    
  2. $this->currencies_selector_get()

    Returns a currency list

    	$currencies_name_and_symbol = [
    		'ALL' => 'L - Albania Lek',
    		'AFN' => '؋ Afghanistan Afghani',
    		'ARS' => '$ Argentina Peso',
    		'AWG' => 'ƒ Aruba Guilder',
    		[...]
    
  3. $this->currency_symbol_get($currency_id)

    Returns the currency symbol through the currency code

  4. $this->currency_code_get($currency_id)

    Returns a the ISO 4217 number 4217 through the currency id

Shortcode system

It's easy to create and manage shortcodes with BFT:

  1. Defining a shortcode

    The shortcodes on BFT are defined on public -> class-your-plugin-admin -> shortcodes_init_plugin()

    The structure of a shortcode is:

    	add_shortcode("shortcode-name", array($this, "shortcode_function_name"));
    
  2. Defining a function

    The structure of a shortcode function is:

    	public function shortcode_function_name ( $atts = [], $content = null, $tag = '' ) {
    	}
    

    The variables of the function are:

    • $atts: array with all data specified on the shortcode
    • $content: the content within the two tags, if the shortcode uses a closing tag
    • $tag: the shortcode tag
  3. Shortcodes uses examples

    A shortcode without data in $atts and $content

    	[bft-shortcode-test]
    

    Shortcode with data on $atts and $content

    	[bft-shortcode-test atts_data_1="Lorem ipsum" atts_data_2="Dolor sit amet"]Datos del contenido[/bft-shortcode-test]
    
  4. Full example

    	public function shortcodes_init_plugin() {
    		add_shortcode("bft-shortcode-test", array($this, "bft_shortcode_test"));
    	}
    
    	public function bft_shortcode_test( $atts = [], $content = null, $tag = '' ) {
    
    		$html_aux = "";
    
    		if (isset($atts["aditional_text"])) {
    			$html_aux .= "<h4>".esc_html($atts["aditional_text"])."</h4>";
    		}
    
    		if (!is_null($content)) {
    			$html_aux .= "<p>".esc_html($content)."<p>";
    		}
    
    		ob_start();
    		require plugin_dir_path( dirname( __FILE__ ) ) . "public/partials/your-plugin-shortcode-test.php";
    		$html = ob_get_clean(); 
    
    		return $html;
    	}
    
  5. Try it for yourself

    Create a page, insert a shortcode block and put:

    		[bft-shortcode-test]
    

    Or:

    		[bft-shortcode-test aditional_text="Esto es un texto adicional"]El texto dentro de las etiquetas[/bft-shortcode-test]
    

Backend Frontend Template Pro: simple shortcode example, editing in WordPress admin

Backend Frontend Template Pro: simple shortcode example, showing the results in the WordPress frontend

Shortcode system with AJAX

It's easy to add AJAX on your shortcodes:

  1. Initial shortcode

    1. Explanation

      The shortcode will not change,The only change is on the partial page, that page will have the JavaScript AJAX data

      You can use your own method, but BFT uses jQuery, the functions provided by $this->bft_ajax_functions() are designed for be printed inside of a jQuery script

      $this->bft_ajax_functions() provides JavaScript functions to show error on the designed container and erase the HTML data on the main container:

      • bft_pro_error_wordpress_show(response_array, id_container_error, id_container_success = “”)
      • bft_pro_error_conection_show(jqXHR, textStatus, errorThrown, id_container_error, id_container_success = “”)

      For the WordPress part, it's necessary:

      • AJAX URL, it's provided by Backend Frontend Template with the variable bft_pro_ajax_url
      • Action data, from a form or created by code:
        	var form_data = [];
        	var data = {};
        	data["name"] = 'action';
        	data["value"] = "bft_shortcode_ajax_test_response";
        	form_data.push(data);
        
    2. Full example
      	<div id="bft_shortcode_ajax_test_response_container"></div>
      	<div id="bft_shortcode_ajax_test_response_error_container"></div>
      
      	<!--
      	<form method="post" action="#" name="bft_shortcode_ajax_test_response" class="bft_shortcode_ajax_test_response" id="bft_shortcode_ajax_test_response">
      		<input type="hidden" name="action" value="bft_shortcode_ajax_test_response">   
      	</form>
      	-->
      
      	<script>
      		(function( $ ) {
      		"use strict";
      
      		function bft_shortcode_ajax_test_function () {
      			var form_data = [];
      			var data = {};
      			data["name"] = "action";
      			data["value"] = "bft_shortcode_ajax_test_response";
      			form_data.push(data);
      
      			//var form_data = jQuery("#bft_shortcode_ajax_test_response").serializeArray();
      
      			jQuery.ajax({
      				url : your_plugin_ajax_url,
      				type : "post",
      				data : form_data,
      				success : function( response ) {
      					console.log("<?=$this->__("AJAX successful")?>")
      					console.log(response);
      					if (typeof response.success !== "undefined" && false === false) {
      						console.log("<?=$this->__("AJAX failed")?>");
      						<?=$this->plugin_slug?>_error_wordpress_show(response, "bft_shortcode_ajax_test_response_error_container", "bft_shortcode_ajax_test_response_container");
      					}
      					else {
      						$("#bft_shortcode_ajax_test_response_container").html(response.data.html);
      					}
      				},
      				fail : function( jqXHR, textStatus, errorThrown) {
      					console.log("<?=$this->__("AJAX failed")?>");
      					<?=$this->plugin_slug?>_error_conection_show(jqXHR, textStatus, errorThrown, "bft_shortcode_ajax_test_response_error_container", "bft_shortcode_ajax_test_response_container");
      				}
      			});
      		}
      
      		bft_shortcode_ajax_test_function();
      
      		<?=$this->bft_ajax_functions()?>
      
      		})( jQuery );
      
      	</script>
      
  2. AJAX response

    1. Defining a WordPress AJAX Action

      The AJAX responses are defined on includes -> class-your-plugin -> define_public_hooks()

      The structure of a AJAX response is double, WordPress separates the logged ussers of non logged users:

      	//AJAX response for logged users
      	$this->loader->add_action( 'wp_ajax_ajax_response_function_name', $plugin_public, 'ajax_response_function_name' );
      	
      	//AJAX response for non logged users
      	$this->loader->add_action( 'wp_ajax_nopriv_ajax_response_function_name', $plugin_public, 'ajax_response_function_name' );
      
    2. Defining a function

      This function doesn't has variables, all pass data will be POST data

      For preparing the return data, BFT uses this structure:

      	$data = array();
      	$data["html"] = $html;
      
      	$return = [
      		"error" => [],
      		"data" => $data,
      	];
      

      For sending the data, the function will use the WordPress functions wp_send_json() and wp_die()

      	wp_send_json($return);
      	wp_die();
      
    3. Returning an error

      Backend Frontend Template provides the function $this->error_send_json(“Error text”)

      $this->error_send_json() will return the JSON and will stop the execution of the code

    4. Full example
      	private function define_public_hooks() {
      		//bft_shortcode_ajax_test_response
      		$this->loader->add_action( 'wp_ajax_bft_shortcode_ajax_test_response', $plugin_public, 'bft_shortcode_ajax_test_response' );
      		$this->loader->add_action( 'wp_ajax_nopriv_bft_shortcode_ajax_test_response', $plugin_public, 'bft_shortcode_ajax_test_response' );
      	}
      
      	public function bft_shortcode_ajax_test_response( ) {
      
      		ob_start();
      		require plugin_dir_path( dirname( __FILE__ ) ) . "public/partials/your-plugin-shortcode-ajax-test-response.php";
      		$html = ob_get_clean();
      
      		$data = array();
      		$data["html"] = $html;
      
      		$return = [
      			"error" => [],
      			"data" => $data,
      		];
      
      		wp_send_json($return);
      		wp_die();
      
      		return $html;
      	}
      
  3. Try it for yourself

    Create a page, insert a shortcode block and put

    		[bft-shortcode-ajax-test]
    

Backend Frontend Template Pro: shortcode example with AJAX, editing in WordPress admin

Backend Frontend Template Pro: shortcode example with AJAX, showing the results in the WordPress frontend

Shortcode system with an AJAX response form

From an AJAX response to a form there are only a few steps to consider:

  1. Initial shortcode

    1. Explanation

      On the HTML data will be a form with the 'action', data, the data will be read with jQuery(“”).serializeArray()

      	var form_data = jQuery("#bft_shortcode_form_test_response").serializeArray();
      

      Also, the form data will have a nonce if the user is logged, for additional security

      	$script_push_nonce = "";
      	if(is_user_logged_in()) {
      		$script_push_nonce = 'form_data.push( { "name" : "'.$this->plugin_slug.'_security", "value" : '.$this->plugin_slug.'_ajax_nonce } );';
      	}
      
    2. Full example
      	public function bft_shortcode_form_test( $atts = [], $content = null, $tag = '' ) {
      
      		$script_push_nonce = "";
      		if(is_user_logged_in()) {
      			$script_push_nonce = 'form_data.push( { "name" : "'.$this->plugin_slug.'_security", "value" : '.$this->plugin_slug.'_ajax_nonce } );';
      		}
      
      		ob_start();
      		require plugin_dir_path( dirname( __FILE__ ) ) . "public/partials/bft-shortcode-form-test-static.php";
      		$html = ob_get_clean(); 
      
      		return $html;
      	}
      

      HTML

      	<form method="post" action="#" name="bft_shortcode_form_test_response" class="bft_shortcode_form_test_response" id="bft_shortcode_form_test_response">
      		<input type="hidden" name="action" value="bft_shortcode_form_test_response">
      		<div>
      			<label><?=$this->__("Name")?></label> <input type="text" name="bft_shortcode_form_test_response_name" id="bft_shortcode_form_test_response_name" value="">   
      		</div>
      		<div>
      			<label><?=$this->__("Surname")?></label> <input type="text" name="bft_shortcode_form_test_response_surname" id="bft_shortcode_form_test_response_surname" value="">   
      		</div>
      		<div>
      			<input type="submit" value="<?=$this->__("Test form")?>" id="bft_shortcode_form_test_response_submit">
      		</div>
      	</form>
      
      	<div id="bft_shortcode_form_test_response_container"></div>
      	<div id="bft_shortcode_form_test_response_error_container"></div>
      
      	<script>
      		(function( $ ) {
      		"use strict";
      
      		$(document).on("submit", ".bft_shortcode_form_test_response", function(e) {
      			e.preventDefault();
      
      			var form_data = jQuery("#bft_shortcode_form_test_response").serializeArray();
      			<?=$script_push_nonce?>
      
      			$("#bft_shortcode_form_test_response_container").html("");
      			$("#bft_shortcode_form_test_response_error_container").text("");
      
      			jQuery.ajax({
      				url : your_plugin_ajax_url,
      				type : "post",
      				data : form_data,
      				success : function( response ) {
      					console.log("<?=$this->__("AJAX successful")?>")
      					if (typeof response.success !== "undefined" && false === false) {
      						console.log("<?=$this->__("AJAX failed")?>");
      						<?=$this->plugin_slug?>_error_wordpress_show(response, "bft_shortcode_form_test_response_error_container", "bft_shortcode_form_test_response_container");
      					}
      					else {
      						$("#bft_shortcode_form_test_response_container").html(response.data.html);
      					}
      				},
      				fail : function( jqXHR, textStatus, errorThrown) {
      					console.log("<?=$this->__("AJAX failed")?>");
      					<?=$this->plugin_slug?>_error_conection_show(jqXHR, textStatus, errorThrown, "bft_shortcode_form_test_response_error_container", "bft_shortcode_form_test_response_container");
      				}
      			});
      		});
      
      		<?=$this->bft_ajax_functions()?>
      
      		})( jQuery );   
      
      	</script>
      
  2. AJAX response

    1. Explanation

      An AJAX form will check the nonce for logged users before use the data with check_ajax_referer(), if the nonce fails the execution will stop

      		if(is_user_logged_in()) {
      			check_ajax_referer($this->plugin_slug."_form_public_nonce", $this->plugin_slug."_security" );
      		}
      		unset($_POST["action"]);
      		unset($_POST[$this->plugin_slug."_security"]);
      
    2. Full example
      		private function define_public_hooks() {
      			//bft_shortcode_form_test_response
      			$this->loader->add_action( 'wp_ajax_bft_shortcode_form_test_response', $plugin_public, 'bft_shortcode_form_test_response' );
      			$this->loader->add_action( 'wp_ajax_nopriv_bft_shortcode_form_test_response', $plugin_public, 'bft_shortcode_form_test_response' );
      		}
      
      		public function bft_shortcode_form_test_response( ) {
      
      			if(is_user_logged_in()) {
      				check_ajax_referer($this->plugin_slug."_form_public_nonce", $this->plugin_slug."_security" );
      			}
      			unset($_POST["action"]);
      			unset($_POST[$this->plugin_slug."_security"]);
      
      			if (	!isset($_POST["bft_shortcode_form_test_response_name"])
      				||	!isset($_POST["bft_shortcode_form_test_response_surname"])) {
      				$this->error_send_json($this->__("An error of validation ocurred, contact the admin of the site"));
      			}
      
      
      			ob_start();
      			require plugin_dir_path( dirname( __FILE__ ) ) . "public/partials/bft-shortcode-form-test-response.php";
      			$html = ob_get_clean();
      
      			$data = array();
      			$data["html"] = $html;
      
      			$return = [
      				"error" => [],
      				"data" => $data,
      			];
      
      			wp_send_json($return);
      			wp_die();
      
      			return $html;
      		}
      
  3. Try it for yourself

    Create a page, insert a shortcode block and put

    		[bft-shortcode-form-test]
    

Backend Frontend Template Pro: shortcode example that generates a form with AJAX response, editing in WordPress admin

Backend Frontend Template Pro: shortcode example that generates a form with AJAX response, showing the results in the WordPress frontend

Your first edited page

Try to modify admin/partials/your-plugin-admin-display-hello-world.php and transform his Lorem Ipsum into your own Hello World

Changelog

1.1.0 | 2023-10-02

Changed

  • Example images are now loaded from moisesbarrachina.online to make the plugin lighter
  • Improved explanation and readability of texts

Corrected

  • Fixed typos

1.0.0 | 2023-09-14

Added

  • Menu system with children
  • Settings system
  • Ipunts system:text, textarea, number, select, select multiple, date, checkbox, checkbox multiple, radio, image, file and commented input
  • Database example
  • Install and uninstall the plugin database
  • Automated data manipulation
  • Manual data manipulation
  • Paginated listing by query
  • Direct listing by array
  • Manual form
  • Log manipulation
  • Error manipulation
  • Internalization
  • Download system for private files
  • Iframe system
  • PDF by iframe system
  • Frontend system by shortcode
  • AJAX system by frontend
  • Internalization into Spanish

Blog sobre Backend Frontend Template Pro

Erratum

Erratum

$wpdb->sanitize_key, $wpdb->sanitize_text_field y $wpdb->sanitize_textarea_field no existen!

read more

We use our own and third-party cookies to improve our services and to offer you a better experience (for example, Displaying personalized advertising) by analyzing your browsing habits (for example, pages visited). You can accept all cookies by pressing the "Accept" button. To configure them, obtain more information or refuse its use, Click HERE

The cookie settings on this website are configured to "allow cookies" and offer you the best possible browsing experience. If you continue to use this website without changing your cookie settings or click "Accept", you will be giving your consent to this.

Close