├── .distignore ├── .github └── workflows │ ├── release.yml │ ├── trigger-bundle-plugins-update.yml │ └── update-from-extensions.yml ├── .gitignore ├── README.md ├── app ├── SettingsContext.js ├── code.js ├── components │ ├── Sidebar.js │ └── Upgrade.js ├── controls │ ├── Checkbox.js │ ├── CheckboxList.js │ ├── Control.js │ ├── Fontawesome.js │ ├── Icon.js │ ├── Input.js │ ├── Select.js │ ├── Slug.js │ ├── Textarea.js │ ├── Toggle.js │ ├── Tooltip.js │ └── logo.svg ├── post-type │ ├── App.js │ ├── MainTabs.js │ ├── Result.js │ └── constants │ │ ├── Data.js │ │ ├── DefaultSettings.js │ │ └── PhpCode.js └── taxonomy │ ├── App.js │ ├── MainTabs.js │ ├── Result.js │ └── constants │ ├── Data.js │ ├── DefaultSettings.js │ └── PhpCode.js ├── assets ├── build │ ├── post-type.asset.php │ ├── post-type.js │ ├── taxonomy.asset.php │ └── taxonomy.js ├── edit.js ├── fontawesome │ ├── css │ │ └── all.min.css │ └── webfonts │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff2 │ │ ├── fa-v4compatibility.ttf │ │ └── fa-v4compatibility.woff2 ├── import.css ├── import.js ├── migrate.js ├── order.css ├── order.js ├── style.css └── style.scss ├── composer.json ├── composer.lock ├── mb-custom-post-type.php ├── package.json ├── pnpm-lock.yaml ├── readme.txt ├── src ├── Ajax.php ├── Edit.php ├── Export.php ├── Import.php ├── Integrations │ ├── Polylang │ │ ├── Manager.php │ │ ├── PostType.php │ │ └── Taxonomy.php │ └── WPML │ │ ├── Manager.php │ │ ├── PostType.php │ │ └── Taxonomy.php ├── Migration.php ├── Order.php ├── PostListTable.php ├── PostTypeRegister.php ├── Register.php ├── TaxonomyRegister.php └── Warning.php ├── webpack.config.js └── wpml-config.xml /.distignore: -------------------------------------------------------------------------------- 1 | /.git 2 | /.github 3 | /node_modules 4 | 5 | .DS_Store 6 | Thumbs.db 7 | 8 | app 9 | assets/*.scss 10 | .distignore 11 | .gitignore 12 | *.json 13 | *.lock 14 | pnpm-lock.yaml 15 | README.md 16 | webpack.config.js 17 | vendor/meta-box/support/.git -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | workflow_dispatch: 4 | branch: 5 | - master 6 | push: 7 | tags: 8 | - "*" 9 | jobs: 10 | call-workflow: 11 | uses: wpmetabox/meta-box/.github/workflows/wordpressorg.yml@master 12 | secrets: 13 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} 14 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }} 15 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 16 | -------------------------------------------------------------------------------- /.github/workflows/trigger-bundle-plugins-update.yml: -------------------------------------------------------------------------------- 1 | name: Trigger update on bundle plugins 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: 6 | - master 7 | push: 8 | tags: 9 | - "*" 10 | 11 | jobs: 12 | call-workflow: 13 | uses: wpmetabox/meta-box/.github/workflows/trigger-bundle-plugins-update.yml@master 14 | secrets: 15 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/update-from-extensions.yml: -------------------------------------------------------------------------------- 1 | name: Auto update extensions 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | call-workflow: 8 | uses: wpmetabox/meta-box/.github/workflows/update-from-extensions.yml@master 9 | secrets: 10 | SSH_KEY: ${{ secrets.SSH_KEY }} 11 | SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | assets/build/*.map 3 | node_modules 4 | vendor 5 | *.LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **MB Custom Post Types & Custom Taxonomies** helps you to create and manage custom post types and custom taxonomies easily in WordPress by providing an easy-to-use interface in the admin area. 2 | 3 | The plugin allows you to handle all post type's arguments and taxonomy's arguments such as menu labels, admin bar label, exclude from search, disable archive page, etc. just in minutes. You don't need to write custom PHP code to register custom post types anymore (using function `register_post_type()` and `register_taxonomy()`). 4 | 5 | > **Meta Box Lite**
6 | > We recommend using [Meta Box Lite](https://metabox.io/lite/), a feature-rich free UI version of Meta Box that provides UI and all free features for managing custom fields and dynamic content on WordPress, including post types, taxonomies, custom fields, and relationships. 7 | 8 | Using **MB Custom Post Types & Custom Taxonomies**, you will be able to craft the WordPress admin and turn it into a professional Content Management Systems. 9 | 10 | 11 | 12 | ### Features 13 | 14 | * Supports all arguments for creating custom post types (like `register_post_type()`) 15 | * Supports all arguments for creating custom taxonomies (like `register_taxonomy()`) 16 | * Supports **live editing mode**, which auto fill in all necessary labels for you! 17 | * Clean code 18 | * Registered custom post types can be exported/imported using default WordPress functionality (no more plugins!) 19 | 20 | ### Plugin Links 21 | 22 | - [Project Page](https://metabox.io/plugins/custom-post-type/) 23 | - [Github Repo](https://github.com/rilwis/mb-custom-post-type/) 24 | 25 | This plugin is a free extension of [Meta Box](https://metabox.io) plugin, which is a powerful, professional solution to create custom meta boxes and custom fields for WordPress websites. Using **MB Custom Post Types & Custom Taxonomies** in combination with [other extensions](https://metabox.io/plugins/) will help you manage any content types in WordPress easily and make your website more professional. 26 | 27 | ## Installation 28 | 29 | You need to install [Meta Box](https://metabox.io) plugin first 30 | 31 | - Go to **Plugins | Add New** and search for **Meta Box** 32 | - Click **Install Now** button to install the plugin 33 | - After installing, click **Activate Plugin** to activate the plugin 34 | 35 | Install **MB Custom Post Types & Custom Taxonomies** extension 36 | 37 | - Go to **Plugins | Add New** and search for **MB Custom Post Types & Custom Taxonomies** 38 | - Click **Install Now** button to install the plugin 39 | - After installing, click **Activate Plugin** to activate the plugin 40 | -------------------------------------------------------------------------------- /app/SettingsContext.js: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from '@wordpress/element'; 2 | 3 | export const SettingsContext = createContext(); 4 | 5 | const removeHtml = text => text.replace( /<.*?>/g, '' ).replace( /(<|>)/g, '' ); 6 | 7 | export const SettingsProvider = ( { children, value } ) => { 8 | const [ settings, setSettings ] = useState( value ); 9 | const updateSettings = data => { 10 | // Remove HTML for labels. 11 | if ( data.hasOwnProperty( 'labels' ) ) { 12 | let labels = data.labels; 13 | 14 | // Fix labels is [] when empty. 15 | if ( typeof labels !== 'object' || Array.isArray( labels ) || labels === null ) { 16 | labels = {}; 17 | } 18 | 19 | Object.keys( labels ).forEach( key => labels[ key ] = removeHtml( labels[ key ] ) ); 20 | data.labels = labels; 21 | } 22 | 23 | setSettings( prev => ( { ...prev, ...data } ) ); 24 | }; 25 | 26 | return { children }; 27 | }; -------------------------------------------------------------------------------- /app/code.js: -------------------------------------------------------------------------------- 1 | import dotProp from 'dot-prop'; 2 | 3 | const maxKeyLength = object => Math.max.apply( null, Object.keys( object ).map( key => key.length ) ); 4 | const spaces = ( settings, key ) => ' '.repeat( maxKeyLength( settings ) - key.length ); 5 | const checkText = ( settings, key ) => { 6 | let value = dotProp.get( settings, key, '' ).replace(/\\/g, '\\\\'); 7 | value = value.replace(/\'/g, '\\\''); 8 | return value; 9 | }; 10 | const text = ( settings, key ) => `'${ key }'${ spaces( settings, key ) } => '${ checkText( settings, key ) }'`; 11 | const translatableText = ( settings, key ) => `'${ key }'${ spaces( settings, key ) } => esc_html__( '${ checkText( settings, key ) }', '${ settings.text_domain }' )`; 12 | const checkboxList = ( settings, key, defaultValue ) => `'${ key }'${ spaces( settings, key ) } => ${ dotProp.get( settings, key, [] ).length ? `['${ dotProp.get( settings, key, [] ).join( "', '" ) }']` : defaultValue }`; 13 | const general = ( settings, key ) => { 14 | let value = dotProp.get( settings, key ); 15 | if ( [ '', undefined ].includes( value ) ) { 16 | value = "''"; 17 | } 18 | return `'${ key }'${ spaces( settings, key ) } => ${ value }`; 19 | }; 20 | 21 | const labels = settings => { 22 | const { labels } = settings; 23 | 24 | let keys = Object.keys( labels ); 25 | labels.text_domain = dotProp.get( settings, 'text_domain', 'your-textdomain' ); // Add text domain to run the `text` function above. 26 | 27 | return keys.map( key => translatableText( labels, key ) ).join( ",\n\t\t" ); 28 | }; 29 | 30 | export { spaces, text, translatableText, checkboxList, general, labels }; 31 | -------------------------------------------------------------------------------- /app/components/Sidebar.js: -------------------------------------------------------------------------------- 1 | import { Panel, PanelBody, PanelRow } from '@wordpress/components'; 2 | import { __ } from "@wordpress/i18n"; 3 | 4 | const Sidebar = () => ( 5 | 6 | 7 | 8 |

{ MBCPT.status }

9 |

{ MBCPT.published }

10 | { MBCPT.modifiedtime &&

{ MBCPT.modifiedtime }

} 11 |

{ MBCPT.author }

12 |

13 |
14 |
15 | 16 | 17 |

{ __( 'Like this plugin? Check out our other WordPress plugins:', 'mb-custom-post-type' ) }

18 |

Slim SEO - { __( 'A fast, lightweight and full-featured SEO plugin for WordPress with minimal configuration.', 'mb-custom-post-type' ) }

19 |

Slim SEO Schema - { __( 'An advanced, powerful and flexible plugin to add schemas to WordPress', 'mb-custom-post-type' ) }

20 |

Slim SEO Link Manager - { __( 'Build internal link easier in WordPress with real-time reports.', 'mb-custom-post-type' ) }

21 |
22 |
23 | 24 | 25 |

{ __( 'If you like this plugin, please write a review on WordPress.org to help us spread the word. We really appreciate that!', 'mb-custom-post-type' ) }

26 |

{ __( 'Write a review', 'mb-custom-post-type' ) } →

27 |
28 |
29 |
30 | ); 31 | 32 | export default Sidebar; -------------------------------------------------------------------------------- /app/components/Upgrade.js: -------------------------------------------------------------------------------- 1 | import { Button } from '@wordpress/components'; 2 | import { __ } from '@wordpress/i18n'; 3 | import { Icon, arrowRight, check } from '@wordpress/icons'; 4 | 5 | const Upgrade = () => MBCPT.upgrade && 6 |
7 |

8 | { __( 'Get Meta Box Premium', 'mb-custom-post-type' ) } 9 | 10 |

11 |

{ __( 'Get the powerful framework to build custom fields & custom data for WordPress with the expert support.', 'mb-custom-post-type' ) }

12 |
13 | 20 | 27 |
28 | 29 |
; 30 | 31 | export default Upgrade; -------------------------------------------------------------------------------- /app/controls/Checkbox.js: -------------------------------------------------------------------------------- 1 | import { ToggleControl } from '@wordpress/components'; 2 | import Tooltip from './Tooltip'; 3 | 4 | const Checkbox = ( { label, name, description = '', update, checked, required = false, tooltip = '' } ) => ( 5 |
6 | 11 |
12 | update( name, value ) } /> 13 |
14 |
15 | ); 16 | 17 | export default Checkbox; -------------------------------------------------------------------------------- /app/controls/CheckboxList.js: -------------------------------------------------------------------------------- 1 | import { ToggleControl } from '@wordpress/components'; 2 | import { useContext } from '@wordpress/element'; 3 | import dotProp from 'dot-prop'; 4 | import { SettingsContext } from '../SettingsContext'; 5 | 6 | const CheckboxList = ( { name, options, description } ) => { 7 | const { settings, updateSettings } = useContext( SettingsContext ); 8 | const saved = dotProp.get( settings, name, [] ); 9 | 10 | const update = ( value, checked ) => { 11 | let newSaved = [ ...saved ]; 12 | if ( checked ) { 13 | newSaved.push( value ); 14 | } else { 15 | newSaved = newSaved.filter( option => option !== value ); 16 | } 17 | updateSettings( { [ name ]: newSaved } ); 18 | }; 19 | 20 | return ( 21 |
22 |
23 | { description &&

{ description }

} 24 | { 25 | Object.entries( options ).map( ( [ value, label ] ) => ( 26 | update( value, checked ) } /> 27 | ) ) 28 | } 29 |
30 |
31 | ); 32 | }; 33 | 34 | export default CheckboxList; -------------------------------------------------------------------------------- /app/controls/Control.js: -------------------------------------------------------------------------------- 1 | import { useContext } from '@wordpress/element'; 2 | import dotProp from 'dot-prop'; 3 | import slugify from 'slugify'; 4 | import { SettingsContext } from '../SettingsContext'; 5 | import Checkbox from './Checkbox'; 6 | import Fontawesome from './Fontawesome'; 7 | import Icon from './Icon'; 8 | import Input from './Input'; 9 | import Select from './Select'; 10 | import Slug from './Slug'; 11 | import Textarea from './Textarea'; 12 | import Toggle from './Toggle'; 13 | 14 | const ucfirst = str => str.length ? str[ 0 ].toUpperCase() + str.slice( 1 ) : str; 15 | const normalizeBool = value => { 16 | if ( 'true' === value ) { 17 | value = true; 18 | } else if ( 'false' === value ) { 19 | value = false; 20 | } 21 | return value; 22 | }; 23 | 24 | const Control = ( { field, autoFills = [] } ) => { 25 | const { settings, updateSettings } = useContext( SettingsContext ); 26 | 27 | const isDisplay = field => { 28 | const { dependency } = field; 29 | if ( !dependency ) { 30 | return true; 31 | } 32 | const dep = dependency.match( /([^:]+):([^:\s]+)/ ); 33 | const depName = dep[ 1 ]; 34 | const depValue = normalizeBool( dep[ 2 ] ); 35 | const currentDepValue = dotProp.get( settings, depName ); 36 | 37 | return depValue === currentDepValue; 38 | }; 39 | 40 | const autofill = ( newSettings, name, value ) => { 41 | const placeholder = name.replace( 'labels.', '' ); 42 | autoFills.forEach( f => { 43 | let newValue; 44 | 45 | if ( 'slug' === f.name ) { 46 | // Only generate slug when it's not manually changed. 47 | if ( newSettings._slug_changed ) { 48 | return; 49 | } 50 | newValue = slugify( value, { lower: true } ); 51 | } else { 52 | newValue = ucfirst( f.default 53 | .replace( `%${ placeholder }%`, value ) 54 | .replace( `%${ placeholder }_lowercase%`, value.toLowerCase() ) 55 | ); 56 | } 57 | 58 | dotProp.set( newSettings, f.name, newValue ); 59 | } ); 60 | 61 | return newSettings; 62 | }; 63 | 64 | const update = e => { 65 | const name = e.target.name; 66 | let value = 'checkbox' === e.target.type ? dotProp.get( e.target, 'checked', false ) : e.target.value; 67 | value = normalizeBool( value ); 68 | value = name === 'menu_position' ? parseFloat( value ) || '' : value; 69 | 70 | let newSettings = { ...settings }; 71 | dotProp.set( newSettings, name, value ); 72 | autofill( newSettings, name, value ); 73 | 74 | updateSettings( newSettings ); 75 | }; 76 | 77 | const updateCheckbox = ( name, value ) => { 78 | let newSettings = { ...settings }; 79 | dotProp.set( newSettings, name, value ); 80 | 81 | updateSettings( newSettings ); 82 | }; 83 | 84 | const _value = dotProp.get( settings, field.name, field.default || '' ); 85 | if ( !isDisplay( field ) ) { 86 | return ''; 87 | } 88 | switch ( field.type ) { 89 | case 'text': 90 | return ; 91 | case 'textarea': 92 | return