├── .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 | VIDEO
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 | { __( 'Status', 'mb-custom-post-type' ) } { MBCPT.status }
9 | { __( 'Published', 'mb-custom-post-type' ) } { MBCPT.published }
10 | { MBCPT.modifiedtime && { __( 'Last modified', 'mb-custom-post-type' ) } { MBCPT.modifiedtime }
}
11 | { __( 'Author', 'mb-custom-post-type' ) } { MBCPT.author }
12 | { __( 'Move to trash', 'mb-custom-post-type' ) }
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 |
14 | { __( '50+ custom field types', 'mb-custom-post-type' ) }
15 | { __( 'Building relationships between posts, terms & users', 'mb-custom-post-type' ) }
16 | { __( 'Submiting posts from frontend', 'mb-custom-post-type' ) }
17 | { __( 'Creating custom Gutenberg blocks', 'mb-custom-post-type' ) }
18 | { __( 'Creating user profile forms', 'mb-custom-post-type' ) }
19 |
20 |
21 | { __( 'Creating custom settings pages', 'mb-custom-post-type' ) }
22 | { __( 'Creating Customizer panels & sections', 'mb-custom-post-type' ) }
23 | { __( 'Saving data in custom tables', 'mb-custom-post-type' ) }
24 | { __( 'Integrations with all page builders', 'mb-custom-post-type' ) }
25 | { __( 'Life-time deals available', 'mb-custom-post-type' ) }
26 |
27 |
28 |
{ __( 'Upgrade now', 'mb-custom-post-type' ) }
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 |
7 | { label }
8 | { required && * }
9 | { tooltip && }
10 |
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 ;
93 | case 'toggle':
94 | return ;
95 | case 'checkbox':
96 | return ;
97 | case 'icon':
98 | return ;
99 | case 'fontawesome':
100 | return ;
101 | case 'select':
102 | return ;
103 | case 'slug':
104 | return ;
105 | }
106 | };
107 |
108 | export default Control;
109 |
--------------------------------------------------------------------------------
/app/controls/Fontawesome.js:
--------------------------------------------------------------------------------
1 | import Tooltip from './Tooltip';
2 | import { RawHTML } from '@wordpress/element';
3 |
4 | const Fontawesome = ( { label, name, update, value, required = false, tooltip = '', description = '' } ) => {
5 | return (
6 |
7 |
8 | { label }
9 | { required && * }
10 | { tooltip && }
11 |
12 |
13 |
14 |
15 |
16 |
17 | { description &&
{ description }
}
18 |
19 |
20 | );
21 | };
22 |
23 | export default Fontawesome;
--------------------------------------------------------------------------------
/app/controls/Icon.js:
--------------------------------------------------------------------------------
1 | import { useState } from "@wordpress/element";
2 | import Tooltip from './Tooltip';
3 | import { __ } from "@wordpress/i18n";
4 |
5 | const getIconLabel = icon => {
6 | let label = icon.replace( /-/g, ' ' ).trim();
7 |
8 | const startsText = [ 'admin', 'controls', 'editor', 'format', 'image', 'media', 'welcome' ];
9 | startsText.forEach( text => {
10 | if ( label.startsWith( text ) ) {
11 | label = label.replace( text, '' );
12 | }
13 | } );
14 |
15 | const endsText = [ 'alt', 'alt2', 'alt3' ];
16 | endsText.forEach( text => {
17 | if ( label.endsWith( text ) ) {
18 | label = label.replace( text, `(${ text })` );
19 | }
20 | } );
21 |
22 | label = label.trim();
23 | const specialText = {
24 | businessman: 'business man',
25 | aligncenter: 'align center',
26 | alignleft: 'align left',
27 | alignright: 'align right',
28 | customchar: 'custom character',
29 | distractionfree: 'distraction free',
30 | removeformatting: 'remove formatting',
31 | strikethrough: 'strike through',
32 | skipback: 'skip back',
33 | skipforward: 'skip forward',
34 | leftright: 'left right',
35 | screenoptions: 'screen options',
36 | };
37 | label = specialText[ label ] || label;
38 |
39 | return label.trim().toLowerCase();
40 | };
41 |
42 | const Icon = ( { label, name, update, value, required = false, tooltip = '' } ) => {
43 | const [ query, setQuery ] = useState( "" );
44 | let data = MBCPT.icons.map( icon => [ icon, getIconLabel( icon ) ] )
45 | .filter( item => query === '' || item[ 1 ].includes( query.toLowerCase() ) );
46 |
47 | return (
48 |
49 |
50 | { label }
51 | { required && * }
52 | { tooltip && }
53 |
54 |
55 |
56 |
57 | setQuery( event.target.value ) } />
58 |
59 |
60 | {
61 | data.map( ( [ icon, label ] ) => (
62 |
63 |
64 |
65 |
66 |
67 | { label }
68 |
69 | ) )
70 | }
71 |
72 |
73 |
74 | );
75 | };
76 |
77 | export default Icon;
--------------------------------------------------------------------------------
/app/controls/Input.js:
--------------------------------------------------------------------------------
1 | import { RawHTML } from '@wordpress/element';
2 | import Tooltip from './Tooltip';
3 |
4 | const Input = ( { label, name, value, update, tooltip = '', description = '', required = false, placeholder = '', datalist = [] } ) => (
5 |
6 |
7 | { label }
8 | { required && * }
9 | { tooltip && }
10 |
11 |
12 |
21 |
22 | { description &&
{ description }
}
23 | { datalist.length > 0 &&
24 | { datalist.map( item => ) }
25 | }
26 |
27 |
28 | );
29 |
30 | export default Input;
--------------------------------------------------------------------------------
/app/controls/Select.js:
--------------------------------------------------------------------------------
1 | import Tooltip from './Tooltip';
2 |
3 | const Select = ( { label, name, update, description = '', options, value, required = false, tooltip = '' } ) => (
4 |
5 |
6 | { label }
7 | { required && * }
8 | { tooltip && }
9 |
10 |
11 |
12 | { options.map( option => { option.label } ) }
13 |
14 | { description &&
{ description }
}
15 |
16 |
17 | );
18 |
19 | export default Select;
--------------------------------------------------------------------------------
/app/controls/Slug.js:
--------------------------------------------------------------------------------
1 | import { RawHTML, useEffect } from '@wordpress/element';
2 | import { __ } from '@wordpress/i18n';
3 | import Tooltip from './Tooltip';
4 |
5 | const Slug = ( {
6 | label,
7 | name,
8 | value,
9 | update,
10 | tooltip = '',
11 | description = '',
12 | required = false,
13 | limit = 20,
14 | settings,
15 | updateSettings
16 | } ) => {
17 | const isReservedTerm = MBCPT.reservedTerms.includes( value );
18 | const isTooLong = value.length > limit;
19 | const error = isReservedTerm
20 | ? __( 'ERROR: the slug must not be one of WordPress reserved terms ', 'mb-custom-post-type' )
21 | : isTooLong
22 | ? sprintf( __( 'ERROR: the slug must not exceed %d characters.', 'mb-custom-post-type' ), limit )
23 | : '';
24 |
25 | useEffect( () => {
26 | document.querySelector( '.mb-cpt-publish' ).disabled = !!error;
27 | document.querySelector( '.mb-cpt-draft' ).disabled = !!error;
28 | }, [ value ] );
29 |
30 | const setSlugChanged = () => {
31 | const newSettings = { ...settings };
32 | newSettings._slug_changed = true;
33 | updateSettings( newSettings );
34 | };
35 |
36 | return (
37 |
38 |
39 | { label }
40 | { required && * }
41 | { tooltip && }
42 |
43 |
44 |
53 | { description &&
{ description }
}
54 | { error &&
{ error } }
55 |
56 |
57 | );
58 | };
59 |
60 | export default Slug;
--------------------------------------------------------------------------------
/app/controls/Textarea.js:
--------------------------------------------------------------------------------
1 | import Tooltip from './Tooltip';
2 |
3 | const Textarea = ( { label, name, placeholder, value, update, description = '', required = false, tooltip = '' } ) => (
4 |
5 |
6 | { label }
7 | { required && * }
8 | { tooltip && }
9 |
10 |
11 |
12 | { description &&
{ description }
}
13 |
14 |
15 | );
16 |
17 | export default Textarea;
--------------------------------------------------------------------------------
/app/controls/Toggle.js:
--------------------------------------------------------------------------------
1 | import { ToggleControl } from '@wordpress/components';
2 |
3 | const Toggle = ( { label, name, description, update, checked } ) => (
4 |
5 |
6 | update( name, value ) } />
7 |
8 |
9 | );
10 |
11 | export default Toggle;
--------------------------------------------------------------------------------
/app/controls/Tooltip.js:
--------------------------------------------------------------------------------
1 | const { Tooltip: T, Dashicon } = wp.components;
2 |
3 | const Tooltip = ( { content } ) => (
4 |
5 |
6 |
7 | );
8 | export default Tooltip;
--------------------------------------------------------------------------------
/app/controls/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/post-type/App.js:
--------------------------------------------------------------------------------
1 | import { render } from "@wordpress/element";
2 | import { SettingsProvider } from '../SettingsContext';
3 | import DefaultSettings from './constants/DefaultSettings';
4 | import MainTabs from './MainTabs';
5 |
6 | const App = () =>
7 |
8 | ;
9 |
10 | const container = document.getElementById( 'poststuff' );
11 | container.classList.add( 'mb-cpt' );
12 | container.id = 'mb-cpt-app';
13 |
14 | // Use React 17 to make the rendering synchronous to make sure WordPress's JS (like detecting #submitdiv or .wp-header-end)
15 | // runs after the app is rendered.
16 | render( , container );
17 |
--------------------------------------------------------------------------------
/app/post-type/MainTabs.js:
--------------------------------------------------------------------------------
1 | import { Button, Flex, TabPanel, Tooltip } from '@wordpress/components';
2 | import { useContext, useReducer } from "@wordpress/element";
3 | import { __ } from '@wordpress/i18n';
4 | import { drawerRight } from '@wordpress/icons';
5 | import { SettingsContext } from '../SettingsContext';
6 | import Sidebar from '../components/Sidebar';
7 | import Upgrade from '../components/Upgrade';
8 | import CheckboxList from '../controls/CheckboxList';
9 | import Control from '../controls/Control';
10 | import { ReactComponent as Logo } from '../controls/logo.svg';
11 | import Result from './Result';
12 | import { AdvancedControls, BasicControls, CodeControls, FeatureControls, LabelControls, SupportControls } from './constants/Data';
13 |
14 | const tabs = [
15 | {
16 | name: 'general',
17 | title: __( 'General', 'mb-custom-post-type' ),
18 | },
19 | {
20 | name: 'labels',
21 | title: __( 'Labels', 'mb-custom-post-type' ),
22 | },
23 | {
24 | name: 'advanced',
25 | title: __( 'Advanced', 'mb-custom-post-type' ),
26 | },
27 | {
28 | name: 'supports',
29 | title: __( 'Supports', 'mb-custom-post-type' ),
30 | },
31 | {
32 | name: 'taxonomies',
33 | title: __( 'Taxonomies', 'mb-custom-post-type' ),
34 | },
35 | {
36 | name: 'features',
37 | title: __( 'Features', 'mb-custom-post-type' ),
38 | },
39 | {
40 | name: 'code',
41 | title: __( 'Get PHP Code', 'mb-custom-post-type' ),
42 | className: 'mb-cpt-code button button-small'
43 | }
44 | ];
45 |
46 | let autoFills = [ ...LabelControls, ...BasicControls ];
47 | autoFills.push( { name: 'label', default: '%name%', updateFrom: 'labels.name' } );
48 |
49 | const panels = {
50 | general: BasicControls.map( ( field, key ) => f.updateFrom === field.name ) } /> ),
51 | labels: LabelControls.map( ( field, key ) => ),
52 | advanced: AdvancedControls.map( ( field, key ) => ),
53 | supports: ,
54 | taxonomies: ,
55 | features: FeatureControls.map( ( field, key ) => ),
56 | code: (
57 | <>
58 | { CodeControls.map( ( field, key ) => ) }
59 |
60 | >
61 | )
62 | };
63 |
64 | const MainTabs = () => {
65 | const { settings } = useContext( SettingsContext );
66 | const [ sidebarOpen, toggleSidebar ] = useReducer( open => !open, true );
67 |
68 | return <>
69 |
70 |
71 |
72 |
73 |
74 | { MBCPT.action === 'add' ? __( 'Add Post Type', 'mb-custom-post-type' ) : __( 'Edit Post Type', 'mb-custom-post-type' ) }
75 | { MBCPT.action !== 'add' && { __( 'Add New', 'mb-custom-post-type' ) } }
76 |
77 |
78 |
88 |
98 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | { tab => panels[ tab.name ] }
116 |
117 |
118 |
119 |
120 |
121 | { sidebarOpen && }
122 |
123 |
124 |
125 |
126 |
127 | >;
128 | };
129 |
130 | export default MainTabs;
--------------------------------------------------------------------------------
/app/post-type/Result.js:
--------------------------------------------------------------------------------
1 | import { ClipboardButton } from '@wordpress/components';
2 | import { withState } from '@wordpress/compose';
3 | import { useContext } from '@wordpress/element';
4 | import { __ } from '@wordpress/i18n';
5 | import { UnControlled as CodeMirror } from 'react-codemirror2';
6 | import { SettingsContext } from '../SettingsContext';
7 | import PhpCode from './constants/PhpCode';
8 |
9 | const Result = () => {
10 | const { settings } = useContext( SettingsContext );
11 | const Button = withState( {
12 | hasCopied: false,
13 | } )( ( { hasCopied, setState } ) => (
14 | setState( { hasCopied: true } ) } onFinishCopy={ () => setState( { hasCopied: false } ) }>
15 | { hasCopied ? __( 'Copied!', 'meta-box-builder' ) : __( 'Copy', 'meta-box-builder' ) }
16 |
17 | ) );
18 |
19 | return (
20 |
21 |
{ __( 'Copy and paste the following code into your theme\'s functions.php file.', 'mb-custom-post-type' ) }
22 |
23 |
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | export default Result;
--------------------------------------------------------------------------------
/app/post-type/constants/Data.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 |
3 | export const BasicControls = [
4 | {
5 | type: 'text',
6 | name: 'labels.name',
7 | label: __( 'Plural name', 'mb-custom-post-type' ),
8 | required: true,
9 | tooltip: __( 'General name for the post type, usually plural', 'mb-custom-post-type' ),
10 | },
11 | {
12 | type: 'text',
13 | name: 'labels.singular_name',
14 | label: __( 'Singular name', 'mb-custom-post-type' ),
15 | required: true,
16 | tooltip: __( 'Name for one object of this post type', 'mb-custom-post-type' ),
17 | },
18 | {
19 | type: 'slug',
20 | name: 'slug',
21 | label: __( 'Slug', 'mb-custom-post-type' ),
22 | required: true,
23 | updateFrom: 'labels.singular_name',
24 | tooltip: __( 'Post type key. Must not exceed 20 characters and may only contain lowercase alphanumeric characters, dashes, and underscores', 'mb-custom-post-type' ),
25 | },
26 | ];
27 |
28 | export const CodeControls = [
29 | {
30 | type: 'text',
31 | name: 'function_name',
32 | label: __( 'Function name', 'mb-custom-post-type' ),
33 | tooltip: __( 'Your function name that registers the post type', 'mb-custom-post-type' ),
34 | },
35 | {
36 | type: 'text',
37 | name: 'text_domain',
38 | label: __( 'Text domain', 'mb-custom-post-type' ),
39 | tooltip: __( 'Required for multilingual website. Used in the exported code only.', 'mb-custom-post-type' ),
40 | },
41 | ];
42 |
43 | export const LabelControls = [
44 | // Name
45 | // Singular name
46 | {
47 | type: 'text',
48 | name: 'labels.add_new',
49 | label: __( 'Add new', 'mb-custom-post-type' ),
50 | default: __( 'Add New', 'mb-custom-post-type' ),
51 | tooltip: __( 'Label for adding a new singular item', 'mb-custom-post-type' ),
52 | },
53 | {
54 | type: 'text',
55 | name: 'labels.add_new_item',
56 | label: __( 'Add new item', 'mb-custom-post-type' ),
57 | default: __( 'Add New %singular_name%', 'mb-custom-post-type' ),
58 | updateFrom: 'labels.singular_name',
59 | tooltip: __( 'Label for adding a new singular item', 'mb-custom-post-type' ),
60 | },
61 | {
62 | type: 'text',
63 | name: 'labels.edit_item',
64 | label: __( 'Edit item', 'mb-custom-post-type' ),
65 | default: __( 'Edit %singular_name%', 'mb-custom-post-type' ),
66 | updateFrom: 'labels.singular_name',
67 | tooltip: __( 'Label for editing a singular item', 'mb-custom-post-type' ),
68 | },
69 | {
70 | type: 'text',
71 | name: 'labels.new_item',
72 | label: __( 'New item', 'mb-custom-post-type' ),
73 | default: __( 'New %singular_name%', 'mb-custom-post-type' ),
74 | updateFrom: 'labels.singular_name',
75 | tooltip: __( 'Label for the new item page title', 'mb-custom-post-type' ),
76 | },
77 | {
78 | type: 'text',
79 | name: 'labels.view_item',
80 | label: __( 'View item', 'mb-custom-post-type' ),
81 | default: __( 'View %singular_name%', 'mb-custom-post-type' ),
82 | updateFrom: 'labels.singular_name',
83 | tooltip: __( 'Label for viewing a singular item', 'mb-custom-post-type' ),
84 | },
85 | {
86 | type: 'text',
87 | name: 'labels.view_items',
88 | label: __( 'View items', 'mb-custom-post-type' ),
89 | default: __( 'View %name%', 'mb-custom-post-type' ),
90 | updateFrom: 'labels.name',
91 | tooltip: __( 'Label for viewing post type archives', 'mb-custom-post-type' ),
92 | },
93 | {
94 | type: 'text',
95 | name: 'labels.search_items',
96 | label: __( 'Search items', 'mb-custom-post-type' ),
97 | default: __( 'Search %name%', 'mb-custom-post-type' ),
98 | updateFrom: 'labels.name',
99 | tooltip: __( 'Label for searching items', 'mb-custom-post-type' ),
100 | },
101 | {
102 | type: 'text',
103 | name: 'labels.not_found',
104 | label: __( 'Not found', 'mb-custom-post-type' ),
105 | default: __( 'No %name_lowercase% found.', 'mb-custom-post-type' ),
106 | updateFrom: 'labels.name',
107 | tooltip: __( 'Label used when no items are found', 'mb-custom-post-type' ),
108 | },
109 | {
110 | type: 'text',
111 | name: 'labels.not_found_in_trash',
112 | label: __( 'Not found in Trash', 'mb-custom-post-type' ),
113 | default: __( 'No %name_lowercase% found in Trash.', 'mb-custom-post-type' ),
114 | updateFrom: 'labels.name',
115 | tooltip: __( 'Label used when no items are in the Trash', 'mb-custom-post-type' ),
116 | },
117 | {
118 | type: 'text',
119 | name: 'labels.parent_item_colon',
120 | label: __( 'Parent items', 'mb-custom-post-type' ),
121 | default: __( 'Parent %singular_name%:', 'mb-custom-post-type' ),
122 | updateFrom: 'labels.singular_name',
123 | tooltip: __( 'Label used to prefix parents of hierarchical items. Not used on non-hierarchical post types', 'mb-custom-post-type' ),
124 | },
125 | {
126 | type: 'text',
127 | name: 'labels.all_items',
128 | label: __( 'All items', 'mb-custom-post-type' ),
129 | default: __( 'All %name%', 'mb-custom-post-type' ),
130 | updateFrom: 'labels.name',
131 | tooltip: __( 'Label to signify all items in a submenu link', 'mb-custom-post-type' ),
132 | },
133 | {
134 | type: 'text',
135 | name: 'labels.archives',
136 | label: __( 'Nav menus archives', 'mb-custom-post-type' ),
137 | default: __( '%singular_name% Archives', 'mb-custom-post-type' ),
138 | updateFrom: 'labels.singular_name',
139 | tooltip: __( 'Label for archives in nav menus', 'mb-custom-post-type' ),
140 | },
141 | {
142 | type: 'text',
143 | name: 'labels.attributes',
144 | label: __( 'Attributes meta box', 'mb-custom-post-type' ),
145 | default: __( '%singular_name% Attributes', 'mb-custom-post-type' ),
146 | updateFrom: 'labels.singular_name',
147 | tooltip: __( 'Label for the attributes meta box', 'mb-custom-post-type' ),
148 | },
149 | {
150 | type: 'text',
151 | name: 'labels.insert_into_item',
152 | label: __( 'Media frame button', 'mb-custom-post-type' ),
153 | default: __( 'Insert into %singular_name_lowercase%', 'mb-custom-post-type' ),
154 | updateFrom: 'labels.singular_name',
155 | tooltip: __( 'Label for the media frame button', 'mb-custom-post-type' ),
156 | },
157 | {
158 | type: 'text',
159 | name: 'labels.uploaded_to_this_item',
160 | label: __( 'Media frame filter', 'mb-custom-post-type' ),
161 | default: __( 'Uploaded to this %singular_name_lowercase%', 'mb-custom-post-type' ),
162 | updateFrom: 'labels.singular_name',
163 | tooltip: __( 'Label for the media frame filter', 'mb-custom-post-type' ),
164 | },
165 | {
166 | type: 'text',
167 | name: 'labels.featured_image',
168 | label: __( 'Featured image meta box', 'mb-custom-post-type' ),
169 | default: __( 'Featured image', 'mb-custom-post-type' ),
170 | tooltip: __( 'Label for the featured image meta box title', 'mb-custom-post-type' ),
171 | },
172 | {
173 | type: 'text',
174 | name: 'labels.set_featured_image',
175 | label: __( 'Setting the featured image', 'mb-custom-post-type' ),
176 | default: __( 'Set featured image', 'mb-custom-post-type' ),
177 | tooltip: __( 'Label for setting the featured image', 'mb-custom-post-type' ),
178 | },
179 | {
180 | type: 'text',
181 | name: 'labels.remove_featured_image',
182 | label: __( 'Removing the featured image', 'mb-custom-post-type' ),
183 | default: __( 'Remove featured image', 'mb-custom-post-type' ),
184 | tooltip: __( 'Label for removing the featured image', 'mb-custom-post-type' ),
185 | },
186 | {
187 | type: 'text',
188 | name: 'labels.use_featured_image',
189 | label: __( 'Used as featured image', 'mb-custom-post-type' ),
190 | default: __( 'Use as featured image', 'mb-custom-post-type' ),
191 | tooltip: __( 'Label in the media frame for using a featured image', 'mb-custom-post-type' ),
192 | },
193 | {
194 | type: 'text',
195 | name: 'labels.menu_name',
196 | label: __( 'Menu name', 'mb-custom-post-type' ),
197 | default: __( '%name%', 'mb-custom-post-type' ),
198 | updateFrom: 'labels.name',
199 | tooltip: __( 'Label for the menu name', 'mb-custom-post-type' ),
200 | },
201 | {
202 | type: 'text',
203 | name: 'labels.filter_items_list',
204 | label: __( 'Table filter hidden heading', 'mb-custom-post-type' ),
205 | default: __( 'Filter %name_lowercase% list', 'mb-custom-post-type' ),
206 | updateFrom: 'labels.name',
207 | tooltip: __( 'Label for the table views hidden heading', 'mb-custom-post-type' ),
208 | },
209 | {
210 | type: 'text',
211 | name: 'labels.filter_by_date',
212 | label: __( 'Table date filter hidden heading', 'mb-custom-post-type' ),
213 | default: __( 'Filter by date', 'mb-custom-post-type' ),
214 | tooltip: __( 'Label for the date filter in list tables', 'mb-custom-post-type' ),
215 | },
216 | {
217 | type: 'text',
218 | name: 'labels.items_list_navigation',
219 | label: __( 'Table pagination hidden heading', 'mb-custom-post-type' ),
220 | default: __( '%name% list navigation', 'mb-custom-post-type' ),
221 | updateFrom: 'labels.name',
222 | tooltip: __( 'Label for the table pagination hidden heading', 'mb-custom-post-type' ),
223 | },
224 | {
225 | type: 'text',
226 | name: 'labels.items_list',
227 | label: __( 'Table hidden heading', 'mb-custom-post-type' ),
228 | default: __( '%name% list', 'mb-custom-post-type' ),
229 | updateFrom: 'labels.name',
230 | tooltip: __( 'Label for the table hidden heading', 'mb-custom-post-type' ),
231 | },
232 | {
233 | type: 'text',
234 | name: 'labels.item_published',
235 | label: __( 'Item published', 'mb-custom-post-type' ),
236 | default: __( '%singular_name% published.', 'mb-custom-post-type' ),
237 | updateFrom: 'labels.singular_name',
238 | tooltip: __( 'Label used when an item is published', 'mb-custom-post-type' ),
239 | },
240 | {
241 | type: 'text',
242 | name: 'labels.item_published_privately',
243 | label: __( 'Item published privately', 'mb-custom-post-type' ),
244 | default: __( '%singular_name% published privately.', 'mb-custom-post-type' ),
245 | updateFrom: 'labels.singular_name',
246 | tooltip: __( 'Label used when an item is published with private visibility', 'mb-custom-post-type' ),
247 | },
248 | {
249 | type: 'text',
250 | name: 'labels.item_reverted_to_draft',
251 | label: __( 'Item switched to draft', 'mb-custom-post-type' ),
252 | default: __( '%singular_name% reverted to draft.', 'mb-custom-post-type' ),
253 | updateFrom: 'labels.singular_name',
254 | tooltip: __( 'Label used when an item is switched to a draft', 'mb-custom-post-type' ),
255 | },
256 | {
257 | type: 'text',
258 | name: 'labels.item_scheduled',
259 | label: __( 'Item scheduled', 'mb-custom-post-type' ),
260 | default: __( '%singular_name% scheduled.', 'mb-custom-post-type' ),
261 | updateFrom: 'labels.singular_name',
262 | tooltip: __( 'Label used when an item is scheduled for publishing', 'mb-custom-post-type' ),
263 | },
264 | {
265 | type: 'text',
266 | name: 'labels.item_updated',
267 | label: __( 'Item updated', 'mb-custom-post-type' ),
268 | default: __( '%singular_name% updated.', 'mb-custom-post-type' ),
269 | updateFrom: 'labels.singular_name',
270 | tooltip: __( 'Label used when an item is updated', 'mb-custom-post-type' ),
271 | },
272 | ];
273 |
274 | export const SupportControls = {
275 | title: __( 'Title', 'mb-custom-post-type' ),
276 | editor: __( 'Editor', 'mb-custom-post-type' ),
277 | excerpt: __( 'Excerpt', 'mb-custom-post-type' ),
278 | author: __( 'Author', 'mb-custom-post-type' ),
279 | thumbnail: __( 'Thumbnail', 'mb-custom-post-type' ),
280 | trackbacks: __( 'Trackbacks', 'mb-custom-post-type' ),
281 | 'custom-fields': __( 'Custom fields', 'mb-custom-post-type' ),
282 | comments: __( 'Comments', 'mb-custom-post-type' ),
283 | revisions: __( 'Revisions', 'mb-custom-post-type' ),
284 | 'page-attributes': __( 'Page attributes', 'mb-custom-post-type' ),
285 | 'post-formats': __( 'Post formats', 'mb-custom-post-type' ),
286 | };
287 |
288 | const CapabilityControls = [
289 | { value: 'post', label: __( 'Post', 'mb-custom-post-type' ) },
290 | { value: 'page', label: __( 'Page', 'mb-custom-post-type' ) },
291 | { value: 'custom', label: __( 'Custom', 'mb-custom-post-type' ) },
292 | ];
293 |
294 | export const AdvancedControls = [
295 | {
296 | type: 'textarea',
297 | name: 'description',
298 | label: __( 'Description', 'mb-custom-post-type' ),
299 | placeholder: __( 'A short descriptive summary of what the post type is', 'mb-custom-post-type' ),
300 | tooltip: __( 'A short descriptive summary of what the post type is', 'mb-custom-post-type' ),
301 | },
302 | {
303 | type: 'checkbox',
304 | name: 'public',
305 | label: __( 'Public', 'mb-custom-post-type' ),
306 | description: __( 'Controls how the type is visible to authors and readers.', 'mb-custom-post-type' ),
307 | tooltip: __( 'Whether a post type is intended for use publicly either via the admin interface or by front-end users', 'mb-custom-post-type' ),
308 | },
309 | {
310 | type: 'checkbox',
311 | name: 'hierarchical',
312 | label: __( 'Hierarchical', 'mb-custom-post-type' ),
313 | description: __( 'Whether the post type is hierarchical.', 'mb-custom-post-type' ),
314 | tooltip: __( 'Whether the post type is hierarchical (e.g. page)', 'mb-custom-post-type' ),
315 | },
316 | {
317 | type: 'checkbox',
318 | name: 'exclude_from_search',
319 | label: __( 'Exclude from search', 'mb-custom-post-type' ),
320 | description: __( 'Whether to exclude posts with this post type from frontend search results.', 'mb-custom-post-type' ),
321 | tooltip: __( 'Whether to exclude posts with this post type from front end search results', 'mb-custom-post-type' ),
322 | },
323 | {
324 | type: 'checkbox',
325 | name: 'publicly_queryable',
326 | label: __( 'Publicly queryable', 'mb-custom-post-type' ),
327 | description: __( 'Whether queries can be performed on the frontend.', 'mb-custom-post-type' ),
328 | tooltip: __( 'Whether queries can be performed on the front end for the post type as part of parse_request()', 'mb-custom-post-type' ),
329 | },
330 | {
331 | type: 'checkbox',
332 | name: 'show_ui',
333 | label: __( 'Show UI', 'mb-custom-post-type' ),
334 | description: __( 'Whether to generate a default UI for managing this post type in the admin.', 'mb-custom-post-type' ),
335 | tooltip: __( 'Whether to generate and allow a UI for managing this post type in the admin', 'mb-custom-post-type' ),
336 | },
337 | {
338 | type: 'select',
339 | name: 'show_in_menu',
340 | label: __( 'Show in menu', 'mb-custom-post-type' ),
341 | description: __( 'Where to show the post type in the admin menu. Show UI must be enabled.', 'mb-custom-post-type' ),
342 | options: MBCPT.show_in_menu_options,
343 | tooltip: __( 'Where to show the post type in the admin menu', 'mb-custom-post-type' ),
344 | },
345 | {
346 | type: 'select',
347 | name: 'menu_position',
348 | label: __( 'Menu position after', 'mb-custom-post-type' ),
349 | options: MBCPT.menu_position_options,
350 | dependency: 'show_in_menu:true',
351 | tooltip: __( 'The position in the menu order the post type should appear', 'mb-custom-post-type' ),
352 | },
353 | {
354 | type: 'select',
355 | name: 'icon_type',
356 | label: __( 'Icon type', 'mb-custom-post-type' ),
357 | options: MBCPT.icon_type,
358 | dependency: 'show_in_menu:true',
359 | },
360 | {
361 | type: 'icon',
362 | name: 'icon',
363 | label: __( 'Icon', 'mb-custom-post-type' ),
364 | dependency: 'icon_type:dashicons',
365 | tooltip: __( 'The icon to be used for the admin menu (Dashicons)', 'mb-custom-post-type' ),
366 | },
367 | {
368 | type: 'text',
369 | name: 'icon_svg',
370 | label: __( 'Icon SVG', 'mb-custom-post-type' ),
371 | dependency: 'icon_type:svg',
372 | description: __( 'Must be in base64 encoded format.', 'mb-custom-post-type' ),
373 | },
374 | {
375 | type: 'text',
376 | name: 'icon_custom',
377 | label: __( 'Icon URL', 'mb-custom-post-type' ),
378 | dependency: 'icon_type:custom',
379 | },
380 | {
381 | type: 'fontawesome',
382 | name: 'font_awesome',
383 | label: __( 'Icon', 'mb-custom-post-type' ),
384 | tooltip: __( 'The icon to be used for the admin menu (FontAwesome)', 'mb-custom-post-type' ),
385 | description: __( 'Enter FontAwesome icon class here. Supports FontAwesome free version only.', 'mb-custom-post-type' ),
386 | dependency: 'icon_type:font_awesome',
387 | },
388 | {
389 | type: 'checkbox',
390 | name: 'show_in_nav_menus',
391 | label: __( 'Show in nav menus', 'mb-custom-post-type' ),
392 | description: __( 'Whether post type is available for selection in navigation menus.', 'mb-custom-post-type' ),
393 | tooltip: __( 'Makes this post type available for selection in navigation menus', 'mb-custom-post-type' ),
394 | },
395 | {
396 | type: 'checkbox',
397 | name: 'show_in_admin_bar',
398 | label: __( 'Show in admin bar', 'mb-custom-post-type' ),
399 | description: __( 'Whether to make this post type available in the WordPress admin bar.', 'mb-custom-post-type' ),
400 | tooltip: __( 'Makes this post type available via the admin bar', 'mb-custom-post-type' ),
401 | },
402 | {
403 | type: 'checkbox',
404 | name: 'show_in_rest',
405 | label: __( 'Show in REST', 'mb-custom-post-type' ),
406 | description: __( 'Whether to expose this post type in the REST API. Must be true to enable the Gutenberg editor.', 'mb-custom-post-type' ),
407 | tooltip: __( 'Whether to include the post type in the REST API. Set this to true for the post type to be available in the block editor', 'mb-custom-post-type' ),
408 | },
409 | {
410 | type: 'text',
411 | name: 'rest_base',
412 | label: __( 'REST API base slug', 'mb-custom-post-type' ),
413 | description: __( 'Leave empty to use the post type slug.', 'mb-custom-post-type' ),
414 | placeholder: __( 'Slug to use in REST API URL', 'mb-custom-post-type' ),
415 | tooltip: __( 'Custom base URL of REST API route', 'mb-custom-post-type' ),
416 | },
417 | {
418 | type: 'select',
419 | name: 'capability_type',
420 | label: __( 'Capability type', 'mb-custom-post-type' ),
421 | description: __( 'If select custom capability, make sure to add capabilities to admin or other roles to add or edit posts of this type (using a plugin like Members or User Role Editor).', 'mb-custom-post-type' ),
422 | options: CapabilityControls,
423 | default: 'post',
424 | tooltip: __( 'The string to use to build the read, edit, and delete capabilities', 'mb-custom-post-type' ),
425 | },
426 | // map_meta_cap
427 | // supports
428 | // taxonomies
429 | {
430 | type: 'checkbox',
431 | name: 'has_archive',
432 | label: __( 'Has archive', 'mb-custom-post-type' ),
433 | description: __( 'Enables post type archives.', 'mb-custom-post-type' ),
434 | tooltip: __( 'Whether there should be post type archives. Will generate the proper rewrite rules if the rewrite settings is enabled', 'mb-custom-post-type' ),
435 | },
436 | {
437 | type: 'text',
438 | name: 'archive_slug',
439 | label: __( 'Custom archive slug', 'mb-custom-post-type' ),
440 | description: __( 'Default is the post type slug.', 'mb-custom-post-type' ),
441 | tooltip: __( 'The custom archive slug', 'mb-custom-post-type' ),
442 | },
443 | {
444 | type: 'text',
445 | name: 'rewrite.slug',
446 | label: __( 'Custom rewrite slug', 'mb-custom-post-type' ),
447 | description: __( 'Leave empty to use the post type slug.', 'mb-custom-post-type' ),
448 | tooltip: __( 'Customize the permastruct slug', 'mb-custom-post-type' ),
449 | },
450 | {
451 | type: 'checkbox',
452 | name: 'rewrite.with_front',
453 | label: __( 'Prepended permalink structure', 'mb-custom-post-type' ),
454 | description: __( 'Example: if your permalink structure is /blog/, then your links will be: false -> /news/, true -> /blog/news/.', 'mb-custom-post-type' ),
455 | tooltip: __( 'Whether the custom permastruct should be prepended', 'mb-custom-post-type' ),
456 | },
457 | {
458 | type: 'checkbox',
459 | name: 'query_var',
460 | label: __( 'Query var', 'mb-custom-post-type' ),
461 | description: __( 'Enables request the post via URL: example.com/?post_type=slug', 'mb-custom-post-type' ),
462 | tooltip: __( 'Sets the custom query var key for this post type', 'mb-custom-post-type' ),
463 | },
464 | {
465 | type: 'checkbox',
466 | name: 'can_export',
467 | label: __( 'Can export', 'mb-custom-post-type' ),
468 | description: __( 'Can this post type be exported', 'mb-custom-post-type' ),
469 | tooltip: __( 'Whether to allow this post type to be exported', 'mb-custom-post-type' ),
470 | },
471 | {
472 | type: 'checkbox',
473 | name: 'delete_with_user',
474 | label: __( 'Delete with user', 'mb-custom-post-type' ),
475 | description: __( 'Whether to move posts to Trash when deleting a user', 'mb-custom-post-type' ),
476 | tooltip: __( 'Whether to delete posts of this type when deleting a user', 'mb-custom-post-type' ),
477 | },
478 | ];
479 |
480 | export const FeatureControls = [
481 | {
482 | type: 'toggle',
483 | name: 'order',
484 | label: __( 'Re-Order Posts', 'mb-custom-post-type' ),
485 | description: __( 'Order posts of this post type using a drag and drop interface.', 'mb-custom-post-type' ),
486 | },
487 | ];
488 |
--------------------------------------------------------------------------------
/app/post-type/constants/DefaultSettings.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 |
3 | const DefaultSettings = {
4 | // Custom attributes.
5 | slug : '',
6 | function_name: 'your_prefix_register_post_type',
7 | text_domain : 'your-textdomain',
8 |
9 | label : '',
10 | labels: {
11 | name : '',
12 | singular_name : '',
13 | add_new : __( 'Add New', 'mb-custom-post-type' ),
14 | add_new_item : '',
15 | edit_item : '',
16 | new_item : '',
17 | view_item : '',
18 | view_items : '',
19 | search_items : '',
20 | not_found : '',
21 | not_found_in_trash : '',
22 | parent_item_colon : '',
23 | all_items : '',
24 | archives : '',
25 | attributes : '',
26 | insert_into_item : '',
27 | uploaded_to_this_item : '',
28 | featured_image : __( 'Featured image', 'mb-custom-post-type' ),
29 | set_featured_image : __( 'Set featured image', 'mb-custom-post-type' ),
30 | remove_featured_image : __( 'Remove featured image', 'mb-custom-post-type' ),
31 | use_featured_image : __( 'Use as featured image', 'mb-custom-post-type' ),
32 | menu_name : '',
33 | filter_items_list : '',
34 | filter_by_date : '',
35 | items_list_navigation : '',
36 | items_list : '',
37 | item_published : '',
38 | item_published_privately: '',
39 | item_reverted_to_draft : '',
40 | item_scheduled : '',
41 | item_updated : '',
42 | },
43 | description : '',
44 | public : true,
45 | hierarchical : false,
46 | exclude_from_search: false,
47 | publicly_queryable : true,
48 | show_ui : true,
49 | show_in_menu : true,
50 | show_in_nav_menus : true,
51 | show_in_admin_bar : true,
52 | show_in_rest : true,
53 | rest_base : '',
54 | menu_position : '',
55 | icon_type : 'dashicons',
56 | icon : 'dashicons-admin-generic',
57 | capability_type : 'post',
58 | supports : ['title', 'editor', 'thumbnail'],
59 | taxonomies : [],
60 | has_archive : true,
61 | archive_slug : '', // Custom attribute.
62 | rewrite : {
63 | slug : '',
64 | with_front: false,
65 | },
66 | query_var : true,
67 | can_export : true,
68 | delete_with_user: true,
69 | }
70 |
71 | export default DefaultSettings;
72 |
--------------------------------------------------------------------------------
/app/post-type/constants/PhpCode.js:
--------------------------------------------------------------------------------
1 | import { checkboxList, general, labels, spaces, text, translatableText } from '../../code';
2 |
3 | const advanced = settings => {
4 | const ignore = [ 'slug', 'function_name', 'text_domain', 'label', 'labels', 'description', 'rest_base', 'show_in_menu', 'menu_icon', 'menu_position', 'capability_type', 'has_archive', 'archive_slug', 'rewrite', 'supports', 'taxonomies', 'icon_type', 'icon', 'icon_svg', 'icon_custom', 'font_awesome' ];
5 |
6 | let keys = Object.keys( settings ).filter( key => !ignore.includes( key ) );
7 | return keys.map( key => general( settings, key ) ).join( ",\n\t\t" );
8 | };
9 |
10 | const showInMenu = settings => {
11 | let value = settings.show_in_menu;
12 | value = [ true, false ].includes( value ) ? value : `'${ value }'`;
13 | let code = `'show_in_menu'${ spaces( settings, 'show_in_menu' ) } => ${ value },`;
14 | if ( value === true ) {
15 | code += `\n\t\t${ general( settings, 'menu_position' ) },`;
16 | }
17 | return code;
18 | };
19 |
20 | const menu_icon = settings => {
21 | let value_type = settings.icon_type ? `'${ settings.icon_type }'` : settings.icon_type;
22 | let value = settings.icon ? `'${ settings.icon }'` : settings.icon;
23 |
24 | if ( value_type == `'dashicons'` ) {
25 | value = settings.icon ? `'${ settings.icon }'` : settings.icon;
26 | } else if ( value_type == `'svg'` ) {
27 | value = settings.icon_svg ? `'${ settings.icon_svg }'` : settings.icon_svg;
28 | } else if ( value_type == `'custom'` ) {
29 | value = settings.icon_custom ? `'${ settings.icon_custom }'` : settings.icon_custom;
30 | } else if ( value_type == `'font_awesome'` ) {
31 | value = settings.font_awesome ? `'${ settings.font_awesome }'` : settings.font_awesome;
32 | }
33 |
34 | return `'menu_icon'${ spaces( settings, 'menu_icon' ) } => ${ value }`;
35 |
36 | };
37 |
38 | const archive = settings => {
39 | let value = settings.archive_slug ? `'${ settings.archive_slug }'` : settings.has_archive;
40 | return `'has_archive'${ spaces( settings, 'has_archive' ) } => ${ value }`;
41 | };
42 |
43 | const rewrite = settings => {
44 | let value = [];
45 | if ( settings.rewrite.slug ) {
46 | value.push( text( settings.rewrite, 'slug' ) );
47 | }
48 | value.push( general( settings.rewrite, 'with_front' ) );
49 |
50 | return `'rewrite'${ spaces( settings, 'rewrite' ) } => [
51 | ${ value.join( ",\n\t\t\t" ) },
52 | ]`;
53 | };
54 |
55 | const PhpCode = settings => {
56 | return ` $labels,
65 | ${ text( settings, 'description' ) },
66 | ${ advanced( settings ) },
67 | ${ archive( settings ) },
68 | ${ text( settings, 'rest_base' ) },
69 | ${ showInMenu( settings ) }
70 | ${ menu_icon( settings ) },
71 | ${ text( settings, 'capability_type' ) },
72 | ${ checkboxList( settings, 'supports', false ) },
73 | ${ checkboxList( settings, 'taxonomies', '[]' ) },
74 | ${ rewrite( settings ) },
75 | ];
76 |
77 | register_post_type( '${ settings.slug.replace(/\\/g, '\\\\').replace(/\'/g, '\\\'') }', $args );
78 | }`;
79 | };
80 |
81 | export default PhpCode;
82 |
--------------------------------------------------------------------------------
/app/taxonomy/App.js:
--------------------------------------------------------------------------------
1 | import { render } from "@wordpress/element";
2 | import { SettingsProvider } from '../SettingsContext';
3 | import DefaultSettings from './constants/DefaultSettings';
4 | import MainTabs from './MainTabs';
5 |
6 | const App = () =>
7 |
8 | ;
9 |
10 | const container = document.getElementById( 'poststuff' );
11 | container.classList.add( 'mb-cpt' );
12 | container.id = 'mb-cpt-app';
13 |
14 | // Use React 17 to make the rendering synchronous to make sure WordPress's JS (like detecting #submitdiv or .wp-header-end)
15 | // runs after the app is rendered.
16 | render( , container );
17 |
--------------------------------------------------------------------------------
/app/taxonomy/MainTabs.js:
--------------------------------------------------------------------------------
1 | import { Button, Flex, TabPanel, Tooltip } from '@wordpress/components';
2 | import { useContext, useReducer } from "@wordpress/element";
3 | import { __ } from '@wordpress/i18n';
4 | import { drawerRight } from "@wordpress/icons";
5 | import { SettingsContext } from '../SettingsContext';
6 | import Sidebar from '../components/Sidebar';
7 | import Upgrade from '../components/Upgrade';
8 | import CheckboxList from '../controls/CheckboxList';
9 | import Control from '../controls/Control';
10 | import { ReactComponent as Logo } from '../controls/logo.svg';
11 | import Result from './Result';
12 | import { AdvancedControls, BasicControls, CodeControls, LabelControls, PermissionsControls } from './constants/Data';
13 |
14 | const tabs = [
15 | {
16 | name: 'general',
17 | title: __( 'General', 'mb-custom-post-type' ),
18 | },
19 | {
20 | name: 'labels',
21 | title: __( 'Labels', 'mb-custom-post-type' ),
22 | },
23 | {
24 | name: 'advanced',
25 | title: __( 'Advanced', 'mb-custom-post-type' ),
26 | },
27 | {
28 | name: 'types',
29 | title: __( 'Post Types', 'mb-custom-post-type' ),
30 | },
31 | {
32 | name: 'permissions',
33 | title: __( 'Permissions', 'mb-custom-post-type' ),
34 | },
35 | {
36 | name: 'code',
37 | title: __( 'Get PHP Code', 'mb-custom-post-type' ),
38 | className: 'mb-cpt-code button button-small'
39 | }
40 | ];
41 |
42 | let autoFills = [ ...LabelControls, ...BasicControls ];
43 | autoFills.push( { name: 'label', default: '%name%', updateFrom: 'labels.name' } );
44 |
45 | const panels = {
46 | general: BasicControls.map( ( field, key ) => f.updateFrom === field.name ) } /> ),
47 | labels: LabelControls.map( ( field, key ) => ),
48 | advanced: AdvancedControls.map( ( field, key ) => ),
49 | permissions: PermissionsControls.map( ( field, key ) => ),
50 | types: ,
51 | code: (
52 | <>
53 | { CodeControls.map( ( field, key ) => ) }
54 |
55 | >
56 | )
57 | };
58 |
59 | const MainTabs = () => {
60 | const { settings } = useContext( SettingsContext );
61 | const [ sidebarOpen, toggleSidebar ] = useReducer( open => !open, true );
62 |
63 | return <>
64 |
65 |
66 |
67 |
68 |
69 | { MBCPT.action === 'add' ? __( 'Add Taxonomies', 'mb-custom-post-type' ) : __( 'Edit Taxonomies', 'mb-custom-post-type' ) }
70 | { MBCPT.action !== 'add' && { __( 'Add New', 'mb-custom-post-type' ) } }
71 |
72 |
73 |
83 |
93 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | { tab => panels[ tab.name ] }
111 |
112 |
113 |
114 |
115 |
116 | { sidebarOpen && }
117 |
118 |
119 |
120 |
121 |
122 | >;
123 | };
124 |
125 | export default MainTabs;
--------------------------------------------------------------------------------
/app/taxonomy/Result.js:
--------------------------------------------------------------------------------
1 | import { ClipboardButton } from '@wordpress/components';
2 | import { withState } from '@wordpress/compose';
3 | import { useContext } from '@wordpress/element';
4 | import { __ } from '@wordpress/i18n';
5 | import { UnControlled as CodeMirror } from 'react-codemirror2';
6 | import { SettingsContext } from '../SettingsContext';
7 | import PhpCode from './constants/PhpCode';
8 |
9 | const Result = () => {
10 | const { settings } = useContext( SettingsContext );
11 | const Button = withState( {
12 | hasCopied: false,
13 | } )( ( { hasCopied, setState } ) => (
14 | setState( { hasCopied: true } ) } onFinishCopy={ () => setState( { hasCopied: false } ) }>
15 | { hasCopied ? __( 'Copied!', 'meta-box-builder' ) : __( 'Copy', 'meta-box-builder' ) }
16 |
17 | ) );
18 |
19 | return (
20 |
21 |
{ __( 'Copy and paste the following code into your theme\'s functions.php file.', 'mb-custom-post-type' ) }
22 |
23 |
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | export default Result;
--------------------------------------------------------------------------------
/app/taxonomy/constants/Data.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 |
3 | export const BasicControls = [
4 | {
5 | type: 'text',
6 | name: 'labels.name',
7 | label: __( 'Plural name', 'mb-custom-post-type' ),
8 | required: true,
9 | tooltip: __( 'General name for the taxonomy, usually plural', 'mb-custom-post-type' ),
10 | },
11 | {
12 | type: 'text',
13 | name: 'labels.singular_name',
14 | label: __( 'Singular name', 'mb-custom-post-type' ),
15 | required: true,
16 | tooltip: __( 'Name for one object of this taxonomy', 'mb-custom-post-type' ),
17 | },
18 | {
19 | type: 'slug',
20 | name: 'slug',
21 | label: __( 'Slug', 'mb-custom-post-type' ),
22 | required: true,
23 | updateFrom: 'labels.singular_name',
24 | tooltip: __( 'Taxonomy key, must not exceed 32 characters and may only contain lowercase alphanumeric characters, dashes, and underscores', 'mb-custom-post-type' ),
25 | limit: 32,
26 | },
27 | ];
28 |
29 | export const CodeControls = [
30 | {
31 | type: 'text',
32 | name: 'function_name',
33 | label: __( 'Function name', 'mb-custom-post-type' ),
34 | tooltip: __( 'Your function name that registers the taxonomy', 'mb-custom-post-type' ),
35 | },
36 | {
37 | type: 'text',
38 | name: 'text_domain',
39 | label: __( 'Text domain', 'mb-custom-post-type' ),
40 | tooltip: __( 'Required for multilingual website. Used in the exported code only.', 'mb-custom-post-type' ),
41 | },
42 | ];
43 |
44 | export const LabelControls = [
45 | // name
46 | // singular_name
47 | {
48 | type: 'text',
49 | name: 'labels.search_items',
50 | label: __( 'Search items', 'mb-custom-post-type' ),
51 | default: __( 'Search %name%', 'mb-custom-post-type' ),
52 | updateFrom: 'labels.name',
53 | tooltip: __( 'Label for searching items', 'mb-custom-post-type' ),
54 | },
55 | {
56 | type: 'text',
57 | name: 'labels.popular_items',
58 | label: __( 'Popular items', 'mb-custom-post-type' ),
59 | default: __( 'Popular %name%', 'mb-custom-post-type' ),
60 | updateFrom: 'labels.name',
61 | tooltip: __( 'Label for most popular items, only used for non-hierarchical taxonomies', 'mb-custom-post-type' ),
62 | },
63 | {
64 | type: 'text',
65 | name: 'labels.all_items',
66 | label: __( 'All items', 'mb-custom-post-type' ),
67 | default: __( 'All %name%', 'mb-custom-post-type' ),
68 | updateFrom: 'labels.name',
69 | tooltip: __( 'Label to signify all items in a submenu link', 'mb-custom-post-type' ),
70 | },
71 | {
72 | type: 'text',
73 | name: 'labels.parent_item',
74 | label: __( 'Parent item', 'mb-custom-post-type' ),
75 | default: __( 'Parent %singular_name%', 'mb-custom-post-type' ),
76 | updateFrom: 'labels.singular_name',
77 | tooltip: __( 'Label for parent item, only used for hierarchical taxonomies', 'mb-custom-post-type' ),
78 | },
79 | {
80 | type: 'text',
81 | name: 'labels.parent_item_colon',
82 | label: __( 'Parent item colon', 'mb-custom-post-type' ),
83 | default: __( 'Parent %singular_name%:', 'mb-custom-post-type' ),
84 | updateFrom: 'labels.singular_name',
85 | tooltip: __( 'The same as parent item, but with colon : in the end', 'mb-custom-post-type' ),
86 | },
87 | {
88 | type: 'text',
89 | name: 'labels.edit_item',
90 | label: __( 'Edit item', 'mb-custom-post-type' ),
91 | default: __( 'Edit %singular_name%', 'mb-custom-post-type' ),
92 | updateFrom: 'labels.singular_name',
93 | tooltip: __( 'Label for adding a new singular item', 'mb-custom-post-type' ),
94 | },
95 | {
96 | type: 'text',
97 | name: 'labels.view_item',
98 | label: __( 'View item', 'mb-custom-post-type' ),
99 | default: __( 'View %singular_name%', 'mb-custom-post-type' ),
100 | updateFrom: 'labels.singular_name',
101 | tooltip: __( 'Label for viewing a singular item', 'mb-custom-post-type' ),
102 | },
103 | {
104 | type: 'text',
105 | name: 'labels.update_item',
106 | label: __( 'Update item', 'mb-custom-post-type' ),
107 | default: __( 'Update %singular_name%', 'mb-custom-post-type' ),
108 | updateFrom: 'labels.singular_name',
109 | tooltip: __( 'Label for updating a singular item', 'mb-custom-post-type' ),
110 | },
111 | {
112 | type: 'text',
113 | name: 'labels.add_new_item',
114 | label: __( 'Add new item', 'mb-custom-post-type' ),
115 | default: __( 'Add New %singular_name%', 'mb-custom-post-type' ),
116 | updateFrom: 'labels.singular_name',
117 | tooltip: __( 'Label for adding a new singular item', 'mb-custom-post-type' ),
118 | },
119 | {
120 | type: 'text',
121 | name: 'labels.new_item_name',
122 | label: __( 'New item name', 'mb-custom-post-type' ),
123 | default: __( 'New %singular_name% Name', 'mb-custom-post-type' ),
124 | updateFrom: 'labels.singular_name',
125 | tooltip: __( 'Label for new item name', 'mb-custom-post-type' ),
126 | },
127 | {
128 | type: 'text',
129 | name: 'labels.separate_items_with_commas',
130 | label: __( 'Separate items with commas', 'mb-custom-post-type' ),
131 | default: __( 'Separate %name_lowercase% with commas', 'mb-custom-post-type' ),
132 | updateFrom: 'labels.name',
133 | tooltip: __( 'This label is only used for non-hierarchical taxonomies. Default "Separate tags with commas", used in the meta box', 'mb-custom-post-type' ),
134 | },
135 | {
136 | type: 'text',
137 | name: 'labels.add_or_remove_items',
138 | label: __( 'Add or remove items', 'mb-custom-post-type' ),
139 | default: __( 'Add or remove %name_lowercase%', 'mb-custom-post-type' ),
140 | updateFrom: 'labels.name',
141 | tooltip: __( 'This label is only used for non-hierarchical taxonomies. Default "Add or remove tags", used in the meta box when JavaScript is disabled', 'mb-custom-post-type' ),
142 | },
143 | {
144 | type: 'text',
145 | name: 'labels.choose_from_most_used',
146 | label: __( 'Choose from most used', 'mb-custom-post-type' ),
147 | default: __( 'Choose most used %name_lowercase%', 'mb-custom-post-type' ),
148 | updateFrom: 'labels.name',
149 | tooltip: __( 'This label is only used on non-hierarchical taxonomies. Default "Choose from the most used tags", used in the meta box', 'mb-custom-post-type' ),
150 | },
151 | {
152 | type: 'text',
153 | name: 'labels.not_found',
154 | label: __( 'Not found', 'mb-custom-post-type' ),
155 | default: __( 'No %name_lowercase% found.', 'mb-custom-post-type' ),
156 | updateFrom: 'labels.name',
157 | tooltip: __( 'Label used in the meta box and taxonomy list table', 'mb-custom-post-type' ),
158 | },
159 | {
160 | type: 'text',
161 | name: 'labels.no_terms',
162 | label: __( 'No terms', 'mb-custom-post-type' ),
163 | default: __( 'No %name_lowercase%', 'mb-custom-post-type' ),
164 | updateFrom: 'labels.name',
165 | tooltip: __( 'Label used in the posts and media list tables', 'mb-custom-post-type' ),
166 | },
167 | {
168 | type: 'text',
169 | name: 'labels.filter_by_item',
170 | label: __( 'Filter by', 'mb-custom-post-type' ),
171 | default: __( 'Filter by %singular_name_lowercase%', 'mb-custom-post-type' ),
172 | updateFrom: 'labels.singular_name',
173 | tooltip: __( 'This label is only used for hierarchical taxonomies, used in the posts list table', 'mb-custom-post-type' ),
174 | },
175 | {
176 | type: 'text',
177 | name: 'labels.items_list_navigation',
178 | label: __( 'Table pagination hidden heading', 'mb-custom-post-type' ),
179 | default: __( '%name% list pagination', 'mb-custom-post-type' ),
180 | updateFrom: 'labels.name',
181 | tooltip: __( 'Label for the table pagination hidden heading', 'mb-custom-post-type' ),
182 | },
183 | {
184 | type: 'text',
185 | name: 'labels.items_list',
186 | label: __( 'Table hidden heading', 'mb-custom-post-type' ),
187 | default: __( '%name% list', 'mb-custom-post-type' ),
188 | updateFrom: 'labels.name',
189 | tooltip: __( 'Label for the table hidden heading', 'mb-custom-post-type' ),
190 | },
191 | {
192 | type: 'text',
193 | name: 'labels.most_used',
194 | label: __( 'Most used tab', 'mb-custom-post-type' ),
195 | default: __( 'Most Used', 'mb-custom-post-type' ),
196 | updateFrom: 'labels.name',
197 | tooltip: __( 'Title for the Most Used tab', 'mb-custom-post-type' ),
198 | },
199 | {
200 | type: 'text',
201 | name: 'labels.back_to_items',
202 | label: __( 'Back to items', 'mb-custom-post-type' ),
203 | default: __( '← Go to %name%', 'mb-custom-post-type' ),
204 | updateFrom: 'labels.name',
205 | tooltip: __( 'Label displayed after a term has been updated', 'mb-custom-post-type' ),
206 | },
207 | {
208 | type: 'text',
209 | name: 'labels.menu_name',
210 | label: __( 'Menu name', 'mb-custom-post-type' ),
211 | default: __( '%name%', 'mb-custom-post-type' ),
212 | updateFrom: 'labels.name',
213 | tooltip: __( 'Label for the tab in the admin menu', 'mb-custom-post-type' ),
214 | },
215 | ];
216 |
217 | export const AdvancedControls = [
218 | {
219 | type: 'textarea',
220 | name: 'description',
221 | label: __( 'Description', 'mb-custom-post-type' ),
222 | description: __( 'A short descriptive summary of what the taxonomy is for.', 'mb-custom-post-type' ),
223 | tooltip: __( 'A short descriptive summary of what the taxonomy is for', 'mb-custom-post-type' ),
224 | },
225 | {
226 | type: 'checkbox',
227 | name: 'public',
228 | label: __( 'Public', 'mb-custom-post-type' ),
229 | description: __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users.', 'mb-custom-post-type' ),
230 | tooltip: __( 'Whether a taxonomy is intended for use publicly either via the admin interface or by front-end users', 'mb-custom-post-type' ),
231 | },
232 | {
233 | type: 'checkbox',
234 | name: 'publicly_queryable',
235 | label: __( 'Public queryable', 'mb-custom-post-type' ),
236 | description: __( 'Whether the taxonomy is publicly queryable.', 'mb-custom-post-type' ),
237 | tooltip: __( 'Whether the taxonomy is publicly queryable', 'mb-custom-post-type' ),
238 | },
239 | {
240 | type: 'checkbox',
241 | name: 'hierarchical',
242 | label: __( 'Hierarchical', 'mb-custom-post-type' ),
243 | description: __( 'Whether the taxonomy is hierarchical.', 'mb-custom-post-type' ),
244 | tooltip: __( 'Whether the taxonomy is hierarchical', 'mb-custom-post-type' ),
245 | },
246 | {
247 | type: 'checkbox',
248 | name: 'show_ui',
249 | label: __( 'Show UI', 'mb-custom-post-type' ),
250 | description: __( 'Whether to generate and allow a UI for managing terms in this taxonomy in the admin.', 'mb-custom-post-type' ),
251 | tooltip: __( 'Whether to generate and allow a UI for managing terms in this taxonomy in the admin', 'mb-custom-post-type' ),
252 | },
253 | {
254 | type: 'checkbox',
255 | name: 'show_in_menu',
256 | label: __( 'Show in menu', 'mb-custom-post-type' ),
257 | description: __( 'Whether to show the taxonomy in the admin menu.', 'mb-custom-post-type' ),
258 | tooltip: __( 'Whether to show the taxonomy in the admin menu. If true, the taxonomy is shown as a submenu of the object type menu. If false, no menu is shown. The show UI settings must be true', 'mb-custom-post-type' ),
259 | },
260 | {
261 | type: 'checkbox',
262 | name: 'show_in_nav_menus',
263 | label: __( 'Show in nav menus', 'mb-custom-post-type' ),
264 | description: __( 'Makes this taxonomy available for selection in navigation menus.', 'mb-custom-post-type' ),
265 | tooltip: __( 'Makes this taxonomy available for selection in navigation menus', 'mb-custom-post-type' ),
266 | },
267 | {
268 | type: 'checkbox',
269 | name: 'meta_box_cb',
270 | label: __( 'Show on edit page', 'mb-custom-post-type' ),
271 | description: __( 'Whether to show the taxonomy on the edit page.', 'mb-custom-post-type' ),
272 | tooltip: __( 'Whether to show the taxonomy meta box on the edit page', 'mb-custom-post-type' ),
273 | },
274 | {
275 | type: 'checkbox',
276 | name: 'show_in_rest',
277 | label: __( 'Show in REST', 'mb-custom-post-type' ),
278 | description: __( 'Whether to include the taxonomy in the REST API. Must be true to enable the Gutenberg editor.', 'mb-custom-post-type' ),
279 | tooltip: __( 'Whether to include the taxonomy in the REST API. Set this to true for the taxonomy to be available in the block editor', 'mb-custom-post-type' ),
280 | },
281 | {
282 | type: 'text',
283 | name: 'rest_base',
284 | label: __( 'REST API base slug', 'mb-custom-post-type' ),
285 | description: __( 'The base URL of the REST API route. Leave empty to use the taxonomy slug.', 'mb-custom-post-type' ),
286 | tooltip: __( 'To change the base URL of REST API route', 'mb-custom-post-type' ),
287 | },
288 | {
289 | type: 'checkbox',
290 | name: 'show_tagcloud',
291 | label: __( 'Show tag cloud', 'mb-custom-post-type' ),
292 | description: __( 'Whether to list the taxonomy in the Tag Cloud Widget controls.', 'mb-custom-post-type' ),
293 | tooltip: __( 'Whether to list the taxonomy in the Tag Cloud Widget controls', 'mb-custom-post-type' ),
294 | },
295 | {
296 | type: 'checkbox',
297 | name: 'show_in_quick_edit',
298 | label: __( 'Show in quick edit', 'mb-custom-post-type' ),
299 | description: __( 'Whether to show the taxonomy in the quick/bulk edit panel.', 'mb-custom-post-type' ),
300 | tooltip: __( 'Whether to show the taxonomy in the quick/bulk edit panel', 'mb-custom-post-type' ),
301 | },
302 | {
303 | type: 'checkbox',
304 | name: 'show_admin_column',
305 | label: __( 'Show admin column', 'mb-custom-post-type' ),
306 | description: __( 'Whether to display a column for the taxonomy on its post type listing screens.', 'mb-custom-post-type' ),
307 | tooltip: __( 'Whether to display a column for the taxonomy on its post type listing screens', 'mb-custom-post-type' ),
308 | },
309 | {
310 | type: 'text',
311 | name: 'rewrite.slug',
312 | label: __( 'Custom rewrite slug', 'mb-custom-post-type' ),
313 | description: __( 'Leave empty to use the taxonomy slug.', 'mb-custom-post-type' ),
314 | tooltip: __( 'Customize the permastruct slug', 'mb-custom-post-type' ),
315 | },
316 | {
317 | type: 'checkbox',
318 | name: 'rewrite.with_front',
319 | label: __( 'Prepended permalink structure', 'mb-custom-post-type' ),
320 | description: __( 'Example: if your permalink structure is /blog/, then your links will be: false -> /news/, true -> /blog/news/.', 'mb-custom-post-type' ),
321 | tooltip: __( 'Should the permastruct be prepended', 'mb-custom-post-type' ),
322 | },
323 | {
324 | type: 'checkbox',
325 | name: 'rewrite.hierarchical',
326 | label: __( 'Hierarchical URL', 'mb-custom-post-type' ),
327 | description: __( 'Either hierarchical rewrite tag or not', 'mb-custom-post-type' ),
328 | tooltip: __( 'Either hierarchical rewrite tag or not', 'mb-custom-post-type' ),
329 | },
330 | {
331 | type: 'checkbox',
332 | name: 'query_var',
333 | label: __( 'Query var', 'mb-custom-post-type' ),
334 | description: __( 'Uncheck to disable the query var, check to use the taxonomy\'s "name" as query var.', 'mb-custom-post-type' ),
335 | tooltip: __( 'Sets the query var key (taxonomy slug) for this taxonomy', 'mb-custom-post-type' ),
336 | },
337 | {
338 | type: 'checkbox',
339 | name: 'sort',
340 | label: __( 'Sort', 'mb-custom-post-type' ),
341 | description: __( 'Whether this taxonomy should remember the order in which terms are added to objects.', 'mb-custom-post-type' ),
342 | tooltip: __( 'Whether terms in this taxonomy should be sorted', 'mb-custom-post-type' ),
343 | },
344 | ];
345 |
346 | export const PermissionsControls = [
347 | {
348 | type: 'text',
349 | name: 'capabilities.manage_terms',
350 | placeholder: 'manage_categories',
351 | label: __( 'Manage terms', 'mb-custom-post-type' ),
352 | description: __( 'The capability required for managing terms.', 'mb-custom-post-type' ),
353 | tooltip: __( 'The capability required for managing terms.', 'mb-custom-post-type' ),
354 | datalist: MBCPT.allCapabilities,
355 | },
356 | {
357 | type: 'text',
358 | name: 'capabilities.edit_terms',
359 | placeholder: 'manage_categories',
360 | label: __( 'Edit terms', 'mb-custom-post-type' ),
361 | description: __( 'The capability required for editing terms.', 'mb-custom-post-type' ),
362 | tooltip: __( 'The capability required for editing terms.', 'mb-custom-post-type' ),
363 | datalist: MBCPT.allCapabilities,
364 | },
365 | {
366 | type: 'text',
367 | name: 'capabilities.delete_terms',
368 | placeholder: 'manage_categories',
369 | label: __( 'Delete terms', 'mb-custom-post-type' ),
370 | description: __( 'The capability required for deleting terms.', 'mb-custom-post-type' ),
371 | tooltip: __( 'The capability required for deleting terms.', 'mb-custom-post-type' ),
372 | datalist: MBCPT.allCapabilities,
373 | },
374 | {
375 | type: 'text',
376 | name: 'capabilities.assign_terms',
377 | placeholder: 'edit_posts',
378 | label: __( 'Assign terms', 'mb-custom-post-type' ),
379 | description: __( 'The capability required for assigning terms.', 'mb-custom-post-type' ),
380 | tooltip: __( 'The capability required for assigning terms.', 'mb-custom-post-type' ),
381 | datalist: MBCPT.allCapabilities,
382 | },
383 | ];
--------------------------------------------------------------------------------
/app/taxonomy/constants/DefaultSettings.js:
--------------------------------------------------------------------------------
1 | const DefaultSettings = {
2 | slug : '',
3 | types : ['post'],
4 | function_name: 'your_prefix_register_taxonomy',
5 | text_domain : 'your-textdomain',
6 |
7 | labels : {
8 | name : '',
9 | singular_name : '',
10 | menu_name : '',
11 | search_items : '',
12 | popular_items : '',
13 | all_items : '',
14 | parent_item : '',
15 | parent_item_colon : '',
16 | edit_item : '',
17 | view_item : '',
18 | update_item : '',
19 | add_new_item : '',
20 | new_item_name : '',
21 | separate_items_with_commas: '',
22 | add_or_remove_items : '',
23 | choose_from_most_used : '',
24 | not_found : '',
25 | no_terms : '',
26 | filter_by_item : '',
27 | items_list_navigation : '',
28 | items_list : '',
29 | most_used : '',
30 | back_to_items : '',
31 | },
32 | description : '',
33 | public : true,
34 | publicly_queryable: true,
35 | hierarchical : false,
36 | show_ui : true,
37 | show_in_menu : true,
38 | show_in_nav_menus : true,
39 | meta_box_cb : 'post_tags_meta_box',
40 | show_in_rest : true,
41 | rest_base : '',
42 | show_tagcloud : true,
43 | show_in_quick_edit: true,
44 | show_admin_column : false,
45 | rewrite : {
46 | slug : '',
47 | with_front : false,
48 | hierarchical: false,
49 | },
50 | query_var : true,
51 | sort : false,
52 | capabilities: {
53 | manage_terms: 'manage_categories',
54 | edit_terms : 'manage_categories',
55 | delete_terms: 'manage_categories',
56 | assign_terms: 'edit_posts',
57 | }
58 | };
59 |
60 | export default DefaultSettings;
--------------------------------------------------------------------------------
/app/taxonomy/constants/PhpCode.js:
--------------------------------------------------------------------------------
1 | import dotProp from 'dot-prop';
2 | import { general, labels, spaces, text, translatableText } from '../../code';
3 |
4 | const types = settings => {
5 | let values = dotProp.get( settings, 'types', [] );
6 | if ( !Array.isArray( values ) ) {
7 | values = [ values ];
8 | }
9 | return `${ values.length ? `['${ values.join( "', '" ) }']` : '[]' }`;
10 | };
11 |
12 | const advanced = settings => {
13 | const ignore = [ 'slug', 'types', 'function_name', 'text_domain', 'label', 'labels', 'description', 'rest_base', 'rewrite', 'meta_box_cb' ];
14 |
15 | let keys = Object.keys( settings ).filter( key => !ignore.includes( key ) );
16 | return keys.map( key => general( settings, key ) ).join( ",\n\t\t" );
17 | };
18 |
19 | const rewrite = settings => {
20 | let value = [];
21 | if ( settings.rewrite.slug ) {
22 | value.push( text( settings.rewrite, 'slug' ) );
23 | }
24 | value.push( general( settings.rewrite, 'with_front' ) );
25 | value.push( general( settings.rewrite, 'hierarchical' ) );
26 |
27 | return `'rewrite'${ spaces( settings, 'rewrite' ) } => [
28 | ${ value.join( ",\n\t\t\t" ) },
29 | ]`;
30 | };
31 |
32 | const meta_box_cb = settings => {
33 | let value = settings.meta_box_cb ? `'${ settings.meta_box_cb }'` : settings.meta_box_cb;
34 |
35 | if ( value ) {
36 | value = settings.hierarchical ? `'post_categories_meta_box'` : `'post_tags_meta_box'`; ;
37 | }
38 |
39 |
40 | return `'meta_box_cb'${ spaces( settings, 'meta_box_cb' ) } => ${ value }`;
41 | };
42 |
43 | const PhpCode = settings => {
44 | return ` $labels,
53 | ${ text( settings, 'description' ) },
54 | ${ advanced( settings ) },
55 | ${ meta_box_cb( settings ) },
56 | ${ text( settings, 'rest_base' ) },
57 | ${ rewrite( settings ) },
58 | ];
59 | register_taxonomy( '${ settings.slug.replace(/\\/g, '\\\\').replace(/\'/g, '\\\'') }', ${ types( settings, 'types' ) }, $args );
60 | }`;
61 | };
62 |
63 | export default PhpCode;
--------------------------------------------------------------------------------
/assets/build/post-type.asset.php:
--------------------------------------------------------------------------------
1 | array('react', 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '9839978b20b789442416');
2 |
--------------------------------------------------------------------------------
/assets/build/taxonomy.asset.php:
--------------------------------------------------------------------------------
1 | array('react', 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => '4d477d21b61f40f43fef');
2 |
--------------------------------------------------------------------------------
/assets/edit.js:
--------------------------------------------------------------------------------
1 | {
2 | // Empty container for React.
3 | // Must empty to avoid problems of detecting #submitdiv in wp-admin/js/post.js:701 that prevents submitting the form.
4 | document.querySelector( '#poststuff' ).innerHTML = '';
5 |
6 | // Remove .wp-header-end element to properly show notices.
7 | document.querySelector( '.wp-header-end' ).remove();
8 |
9 | const form = document.querySelector( '#post' );
10 |
11 | // Force form to validate to force users to enter required fields.
12 | // Use setTimeout because this attribute is dynamically added.
13 | setTimeout( () => {
14 | form.removeAttribute( 'novalidate' );
15 | }, 100 );
16 |
17 | // Set post status when clicking submit buttons.
18 | form.addEventListener( 'submit', e => {
19 | const submitButton = e.submitter;
20 | const status = submitButton.dataset.status;
21 | const originalStatus = form.querySelector( '#original_post_status' ).value;
22 | if ( originalStatus !== status ) {
23 | form.querySelector( '[name="messages"]' ).setAttribute( 'name', MBCPT.status !== 'publish' ? 'publish' : 'save' );
24 | }
25 | if ( originalStatus === 'auto-draft' && status === 'draft' ) {
26 | form.querySelector( '[name="messages"]' ).setAttribute( 'name', 'save' );
27 | }
28 |
29 | submitButton.disabled = true;
30 | submitButton.setAttribute( 'value', MBCPT.saving );
31 | form.querySelector( '[name="post_status"]' ).setAttribute( 'value', status );
32 | } );
33 |
34 | }
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-brands-400.ttf
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-brands-400.woff2
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-v4compatibility.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-v4compatibility.ttf
--------------------------------------------------------------------------------
/assets/fontawesome/webfonts/fa-v4compatibility.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wpmetabox/mb-custom-post-type/a099c585bad429ac83d26411890d0fb796fa018c/assets/fontawesome/webfonts/fa-v4compatibility.woff2
--------------------------------------------------------------------------------
/assets/import.css:
--------------------------------------------------------------------------------
1 | /* Import form */
2 | .mbcpt-import-form {
3 | display: none;
4 | padding: 50px 0;
5 | }
6 | .mbcpt-import-form p {
7 | color: #555d66;
8 | font-size: 18px;
9 | font-style: normal;
10 | margin: 0;
11 | padding: 0;
12 | text-align: center;
13 | }
14 | .mbcpt-import-form form {
15 | background: #fafafa;
16 | border: 1px solid #e5e5e5;
17 | padding: 30px;
18 | margin: 30px auto;
19 | max-width: 380px;
20 | }
--------------------------------------------------------------------------------
/assets/import.js:
--------------------------------------------------------------------------------
1 | jQuery( function ( $ ) {
2 | // Add "Export" option to the Bulk Actions dropdowns.
3 | $( '' )
4 | .text( MBCPT.export )
5 | .appendTo( 'select[name="action"], select[name="action2"]' );
6 |
7 | // Toggle upload form.
8 | var $form = $( $( '#mbcpt-import-form' ).html() ).insertAfter( '.wp-header-end' );
9 | var $toggle = $( '' )
10 | .text( MBCPT.import )
11 | .insertAfter( '.page-title-action' );
12 |
13 | $toggle.on( 'click', function( e ) {
14 | e.preventDefault();
15 | $form.toggle();
16 | } );
17 |
18 | // Enable submit button when selecting a file.
19 | var $input = $form.find( 'input[type="file"]' ),
20 | $submit = $form.find( 'input[type="submit"]' );
21 |
22 | $input.on( 'change', function() {
23 | $submit.prop( 'disabled', ! $input.val() );
24 | } );
25 | } );
--------------------------------------------------------------------------------
/assets/migrate.js:
--------------------------------------------------------------------------------
1 | ( function ( document, i18n ) {
2 | 'use strict';
3 |
4 | const status = document.querySelector( '#migrate-status' );
5 |
6 | document.querySelector( '#migrate-button' ).addEventListener( 'click', async () => {
7 | printMessage( i18n.start );
8 |
9 | printMessage( i18n.migratingPostTypes );
10 | await migrate_post_types();
11 |
12 | printMessage( i18n.migratingTaxonomies );
13 | await migrate_taxonomies();
14 |
15 | printMessage( i18n.done );
16 | document.querySelector( '#migrate-links' ).removeAttribute( 'style' );
17 | } );
18 |
19 | async function migrate_post_types() {
20 | await get( `${ ajaxurl }?action=mbcpt_migrate_post_types` );
21 | }
22 |
23 | async function migrate_taxonomies() {
24 | await get( `${ ajaxurl }?action=mbcpt_migrate_taxonomies` );
25 | }
26 |
27 | async function get( url ) {
28 | const response = await fetch( url );
29 | const json = await response.json();
30 | if ( !response.ok ) {
31 | throw Error( json.data );
32 | }
33 | return json;
34 | }
35 |
36 | const printMessage = text => status.innerHTML += `${ text }
`;
37 | } )( document, MbCpt );
38 |
--------------------------------------------------------------------------------
/assets/order.css:
--------------------------------------------------------------------------------
1 | .wp-list-table .column-mbcpt_order {
2 | width: 20px;
3 | padding-top: 10px;
4 | padding-right: 0;
5 | padding-left: 4px;
6 | }
7 |
8 | .column-mbcpt_order svg {
9 | display: none;
10 | width: 18px;
11 | height: 18px;
12 | cursor: grab;
13 | fill: #aaa;
14 | }
15 |
16 | tr:hover .column-mbcpt_order svg {
17 | display: inline-block;
18 | }
19 |
20 | .column-mbcpt_order+.check-column.check-column {
21 | padding-left: 0;
22 | }
23 |
24 | .column-mbcpt_order+.check-column>input[type="checkbox"] {
25 | margin-left: 0;
26 | }
27 |
28 | .column-mbcpt_order+.check-column label:hover,
29 | .column-mbcpt_order+.check-column input:hover+label {
30 | background: none;
31 | }
--------------------------------------------------------------------------------
/assets/order.js:
--------------------------------------------------------------------------------
1 | ( function ( $ ) {
2 | $( 'table.posts #the-list' ).sortable( {
3 | 'items': 'tr',
4 | 'axis': 'y',
5 | 'handle': '.mbcpt_order',
6 | 'update': function () {
7 | $.post( ajaxurl, {
8 | action: 'mbcpt_update_menu_order',
9 | order: $( '#the-list' ).sortable( 'serialize' ),
10 | security: MBCPT.security
11 | } );
12 | }
13 | } );
14 | } )( jQuery );
15 |
--------------------------------------------------------------------------------
/assets/style.css:
--------------------------------------------------------------------------------
1 | .update-nag{display:none}.components-tooltip{max-width:400px}#mb-cpt-upgrade .dashicons{color:#46b450}#mb-cpt{margin-top:0}.mb-cpt{--color-gray: #999;--color-light: #e0e0e0;--color-red: #e53e3e;--color-pink: #ffebe8;--color: var(--wp-admin-theme-color, #2271b1)}.mb-cpt input[type=text],.mb-cpt input[type=number],.mb-cpt textarea,.mb-cpt select{display:block;width:100%;max-width:none}.mb-cpt svg{width:16px;height:16px;fill:var(--color-gray)}.mb-cpt pre{padding:0}.mb-cpt label{cursor:pointer}.mb-cpt .alert-error{color:var(--color-red);background:var(--color-pink)}.mb-cpt-required{color:var(--color-red)}.mb-cpt-loading{margin-left:12px;font-style:italic}.mb-cpt-tooltip-icon svg{width:14px;height:14px;fill:var(--color-light);margin-left:6px;top:2px;position:relative}.mb-cpt-tooltip-icon .dashicon{color:var(--color-light);font-size:14px;position:relative;top:2px}.mb-cpt-field{margin-bottom:24px;display:flex;justify-content:space-between}.mb-cpt-label{width:25%}.mb-cpt-input{flex:1}.mb-cpt-description{font-style:italic;margin-top:4px;color:var(--color-gray)}.mb-cpt-error{font-style:italic;color:var(--color-red)}.mb-cpt-icon-selected{display:flex}.mb-cpt-icon-selected span{width:36px;height:36px;padding:4px;box-sizing:border-box;font-size:24px;border-radius:4px;border:1px solid #8c8f94;margin-right:10px;display:grid;place-items:center}.mb-cpt-icon-selected .icon-fontawesome{position:relative}.mb-cpt-icon-selected .icon-fontawesome:before{font-size:18px}.mb-cpt-items{display:grid;grid-template-columns:repeat(auto-fit, 65px);gap:10px 15px;max-height:25em;overflow:auto;padding:1px;margin-top:15px}.mb-cpt-item{display:flex;flex-direction:column;justify-content:start;align-items:center;text-align:center}.mb-cpt-item:hover .mb-cpt-icon{box-shadow:0 0 0 1px rgba(0,0,0,.3)}.mb-cpt-item:has(input:checked):hover .mb-cpt-icon{box-shadow:none}.mb-cpt-item__text{color:var(--color-gray);font-size:11px;line-height:1.1;margin-top:4px}.mb-cpt-icon{border-radius:4px}.mb-cpt-icon .dashicons{width:36px;height:36px;padding:4px;box-sizing:border-box;font-size:24px;border-radius:4px}.mb-cpt-icon input{display:none}.mb-cpt-icon input:checked~.dashicons{color:#fff;background:var(--color)}.mb-cpt-result code{font-size:12px}.mb-cpt-result .react-codemirror2{border:1px solid var(--color-light);margin-bottom:24px}.mb-cpt-result__body{position:relative}.mb-cpt-result .button{position:absolute;top:0;right:0;border-color:var(--color-light);border-radius:0 0 0 4px;z-index:9;height:30px}.mb-cpt-header{background:#fff;position:sticky;top:32px;z-index:9;border-bottom:1px solid var(--color-light)}.mb-cpt-header h1{font-size:20px;padding:0}.logo svg{width:48px;height:48px;padding:6px;display:block}.mb-cpt .page-title-action{top:unset}.mb-cpt-header .components-popover__content{background:#000;box-shadow:none;width:max-content}.mb-cpt-action{padding-right:12px}.mb-cpt-action svg{width:auto;height:auto;fill:currentColor}.mb-cpt-body{position:relative}.mb-cpt-body .mb-cpt-code{margin-left:auto;margin-right:12px;height:auto}.mb-cpt-body .mb-cpt-code.is-active{box-shadow:none}.mb-cpt-body .components-tab-panel__tabs{border-bottom:1px solid var(--color-light);align-items:center;flex-wrap:wrap;padding-left:24px;margin-bottom:24px;gap:32px}.mb-cpt-body .components-tab-panel__tabs-item{padding:0}.mb-cpt-body .components-tab-panel__tab-content{padding:0 24px}.mb-cpt-body .notice{border-radius:6px;border-top-color:#e0e0e0;border-bottom-color:#e0e0e0;border-right-color:#e0e0e0;padding-block:6px}.mb-cpt-body .notice-dismiss{bottom:0}.mb-cpt-content{margin:40px 16px 16px;flex:1}.mb-cpt-tabs-wrapper{max-width:900px;margin-inline:auto}.mb-cpt-tabs{background:#fff;border:1px solid #e0e0e0;border-radius:6px}.mb-cpt-sidebar{width:300px;border-top:none}.mb-cpt-sidebar .components-panel__row{display:block}.mb-cpt-sidebar .summary p{display:flex}.mb-cpt-sidebar .summary label{width:40%}.mb-cpt-sidebar .status{text-transform:capitalize}.wp-menu-image img{max-width:20px}.admin-color-blue .mb-cpt{--color: var(--wp-admin-theme-color, #e1a948)}.admin-color-coffee .mb-cpt{--color: var(--wp-admin-theme-color, #c7a589)}.admin-color-ectoplasm .mb-cpt{--color: var(--wp-admin-theme-color, #a3b745)}.admin-color-light .mb-cpt{--color: var(--wp-admin-theme-color, #04a4cc)}.admin-color-midnight .mb-cpt{--color: var(--wp-admin-theme-color, #e14d43)}.admin-color-modern .mb-cpt{--color: var(--wp-admin-theme-color, #3858e9)}.admin-color-ocean .mb-cpt{--color: var(--wp-admin-theme-color, #9ebaa0)}.admin-color-sunrise .mb-cpt{--color: var(--wp-admin-theme-color, #dd823b)}#screen-meta-links,.wrap h1.wp-heading-inline,.wrap>.page-title-action{display:none}.wrap{margin:0}#wpcontent{padding-left:0}.mb-cpt-message{position:absolute;top:15px;left:50%;transform:translateX(-50%);animation:fadein .5s;font-weight:600;color:#00a32a}.mb-cpt-upgrade{margin-top:64px;background:linear-gradient(15deg, #202e3c, #1f3749, #1d4358, #1a5168, #156279, #0f788b, #08919e, #00b0b3);color:#fff;border-radius:6px;padding:24px;text-align:center}.mb-cpt-upgrade h2{font-size:24px;color:#fff;display:flex;align-items:center;gap:8px;justify-content:center}.mb-cpt-upgrade h2 svg{fill:#fff449;width:24px;height:24px}.mb-cpt-upgrade p{font-size:16px}.mb-cpt-upgrade__features{display:flex;justify-content:space-around;text-align:left;font-size:14px}.mb-cpt-upgrade li{display:flex;align-items:center;gap:4px}.mb-cpt-upgrade li svg{background:#fff;border-radius:50%}.mb-cpt-upgrade .components-button{padding:24px;background:#fff;color:#1a5168;font-size:14px;position:relative;transition:all .2s}.mb-cpt-upgrade .components-button svg{margin-left:4px}.mb-cpt-upgrade .components-button:hover{margin-top:-2px;margin-bottom:2px}@media(max-width: 767px){.mb-cpt-field{flex-direction:column}}
2 |
--------------------------------------------------------------------------------
/assets/style.scss:
--------------------------------------------------------------------------------
1 | // Do not show update nag.
2 | .update-nag {
3 | display: none;
4 | }
5 |
6 | // Block editor components.
7 | .components-tooltip {
8 | max-width: 400px;
9 | }
10 |
11 | #mb-cpt-upgrade .dashicons {
12 | color: #46b450;
13 | }
14 |
15 | #mb-cpt {
16 | margin-top: 0;
17 | }
18 |
19 | .mb-cpt {
20 | --color-gray: #999;
21 | --color-light: #e0e0e0;
22 | --color-red: #e53e3e;
23 | --color-pink: #ffebe8;
24 | --color: var(--wp-admin-theme-color, #2271b1);
25 |
26 | // Elements
27 | input[type="text"],
28 | input[type="number"],
29 | textarea,
30 | select {
31 | display: block;
32 | width: 100%;
33 | max-width: none;
34 | }
35 |
36 | svg {
37 | width: 16px;
38 | height: 16px;
39 | fill: var(--color-gray);
40 | }
41 |
42 | pre {
43 | padding: 0;
44 | }
45 |
46 | label {
47 | cursor: pointer;
48 | }
49 |
50 | .alert-error {
51 | color: var(--color-red);
52 | background: var(--color-pink);
53 | }
54 |
55 | &-required {
56 | color: var(--color-red);
57 | }
58 |
59 | &-loading {
60 | margin-left: 12px;
61 | font-style: italic;
62 | }
63 |
64 | // Tooltip
65 | &-tooltip-icon {
66 |
67 | // WordPress < 5.6
68 | svg {
69 | width: 14px;
70 | height: 14px;
71 | fill: var(--color-light);
72 | margin-left: 6px;
73 | top: 2px;
74 | position: relative;
75 | }
76 |
77 | // WordPress 5.6+
78 | .dashicon {
79 | color: var(--color-light);
80 | font-size: 14px;
81 | position: relative;
82 | top: 2px;
83 | }
84 | }
85 |
86 | // Fields
87 | &-field {
88 | margin-bottom: 24px;
89 | display: flex;
90 | justify-content: space-between;
91 | }
92 |
93 | &-label {
94 | width: 25%;
95 | }
96 |
97 | &-input {
98 | flex: 1;
99 | }
100 |
101 | &-description {
102 | font-style: italic;
103 | margin-top: 4px;
104 | color: var(--color-gray);
105 | }
106 |
107 | &-error {
108 | font-style: italic;
109 | color: var(--color-red);
110 | }
111 |
112 | // search icon
113 | &-icon-selected {
114 | display: flex;
115 |
116 | span {
117 | width: 36px;
118 | height: 36px;
119 | padding: 4px;
120 | box-sizing: border-box;
121 | font-size: 24px;
122 | border-radius: 4px;
123 | border: 1px solid #8c8f94;
124 | margin-right: 10px;
125 | display: grid;
126 | place-items: center;
127 | }
128 |
129 | .icon-fontawesome {
130 | position: relative;
131 |
132 | &:before {
133 | font-size: 18px;
134 | }
135 | }
136 | }
137 |
138 | &-items {
139 | display: grid;
140 | grid-template-columns: repeat(auto-fit, 65px);
141 | gap: 10px 15px;
142 | max-height: 25em;
143 | overflow: auto;
144 | padding: 1px;
145 | margin-top: 15px;
146 | }
147 |
148 | &-item {
149 | display: flex;
150 | flex-direction: column;
151 | justify-content: start;
152 | align-items: center;
153 | text-align: center;
154 |
155 | &:hover .mb-cpt-icon {
156 | box-shadow: 0 0 0 1px rgba(0, 0, 0, .3);
157 | }
158 |
159 | &:has(input:checked):hover .mb-cpt-icon {
160 | box-shadow: none;
161 | }
162 |
163 | &__text {
164 | color: var(--color-gray);
165 | font-size: 11px;
166 | line-height: 1.1;
167 | margin-top: 4px;
168 | }
169 | }
170 |
171 | &-icon {
172 | border-radius: 4px;
173 |
174 | .dashicons {
175 | width: 36px;
176 | height: 36px;
177 | padding: 4px;
178 | box-sizing: border-box;
179 | font-size: 24px;
180 | border-radius: 4px;
181 | }
182 |
183 | input {
184 | display: none;
185 |
186 | &:checked~.dashicons {
187 | color: #fff;
188 | background: var(--color);
189 | }
190 | }
191 | }
192 |
193 | &-result {
194 | code {
195 | font-size: 12px;
196 | }
197 |
198 | .react-codemirror2 {
199 | border: 1px solid var(--color-light);
200 | margin-bottom: 24px;
201 | }
202 |
203 | &__body {
204 | position: relative;
205 | }
206 |
207 | .button {
208 | position: absolute;
209 | top: 0;
210 | right: 0;
211 | border-color: var(--color-light);
212 | border-radius: 0 0 0 4px;
213 | z-index: 9;
214 | height: 30px;
215 | }
216 | }
217 | }
218 |
219 | // Tabs
220 | .mb-cpt-header {
221 | background: #fff;
222 | position: sticky;
223 | top: 32px;
224 | z-index: 9;
225 | border-bottom: 1px solid var(--color-light);
226 |
227 | h1 {
228 | font-size: 20px;
229 | padding: 0;
230 | }
231 | }
232 |
233 | .logo svg {
234 | width: 48px;
235 | height: 48px;
236 | padding: 6px;
237 | display: block;
238 | }
239 |
240 | .mb-cpt .page-title-action {
241 | top: unset;
242 | }
243 |
244 | .mb-cpt-header .components-popover__content {
245 | background: #000;
246 | box-shadow: none;
247 | width: max-content;
248 | }
249 |
250 | .mb-cpt-action {
251 | padding-right: 12px;
252 |
253 | svg {
254 | width: auto;
255 | height: auto;
256 | fill: currentColor;
257 | }
258 | }
259 |
260 | .mb-cpt-body {
261 | position: relative;
262 |
263 | .mb-cpt-code {
264 | margin-left: auto;
265 | margin-right: 12px;
266 | height: auto;
267 |
268 | &.is-active {
269 | box-shadow: none;
270 | }
271 | }
272 |
273 | .components-tab-panel__tabs {
274 | border-bottom: 1px solid var(--color-light);
275 | align-items: center;
276 | flex-wrap: wrap;
277 | padding-left: 24px;
278 | margin-bottom: 24px;
279 | gap: 32px;
280 | }
281 |
282 | .components-tab-panel__tabs-item {
283 | padding: 0;
284 | }
285 |
286 | .components-tab-panel__tab-content {
287 | padding: 0 24px;
288 | }
289 |
290 | // Notices.
291 | .notice {
292 | border-radius: 6px;
293 | border-top-color: #e0e0e0;
294 | border-bottom-color: #e0e0e0;
295 | border-right-color: #e0e0e0;
296 | padding-block: 6px;
297 | }
298 |
299 | .notice-dismiss {
300 | bottom: 0;
301 | }
302 | }
303 |
304 | .mb-cpt-content {
305 | margin: 40px 16px 16px;
306 | flex: 1;
307 | }
308 |
309 | .mb-cpt-tabs-wrapper {
310 | max-width: 900px;
311 | margin-inline: auto;
312 | }
313 |
314 | .mb-cpt-tabs {
315 | background: #fff;
316 | border: 1px solid #e0e0e0;
317 | border-radius: 6px;
318 | }
319 |
320 | .mb-cpt-sidebar {
321 | width: 300px;
322 | border-top: none;
323 |
324 | .components-panel__row {
325 | display: block;
326 | }
327 |
328 | .summary {
329 | p {
330 | display: flex;
331 | }
332 |
333 | label {
334 | width: 40%;
335 | }
336 | }
337 |
338 | .status {
339 | text-transform: capitalize;
340 | }
341 | }
342 |
343 | .wp-menu-image {
344 | img {
345 | max-width: 20px;
346 | }
347 | }
348 |
349 | /* Admin color schemes */
350 | .admin-color-blue .mb-cpt {
351 | --color: var(--wp-admin-theme-color, #e1a948);
352 | }
353 |
354 | .admin-color-coffee .mb-cpt {
355 | --color: var(--wp-admin-theme-color, #c7a589);
356 | }
357 |
358 | .admin-color-ectoplasm .mb-cpt {
359 | --color: var(--wp-admin-theme-color, #a3b745);
360 | }
361 |
362 | .admin-color-light .mb-cpt {
363 | --color: var(--wp-admin-theme-color, #04a4cc);
364 | }
365 |
366 | .admin-color-midnight .mb-cpt {
367 | --color: var(--wp-admin-theme-color, #e14d43);
368 | }
369 |
370 | .admin-color-modern .mb-cpt {
371 | --color: var(--wp-admin-theme-color, #3858e9);
372 | }
373 |
374 | .admin-color-ocean .mb-cpt {
375 | --color: var(--wp-admin-theme-color, #9ebaa0);
376 | }
377 |
378 | .admin-color-sunrise .mb-cpt {
379 | --color: var(--wp-admin-theme-color, #dd823b);
380 | }
381 |
382 | #screen-meta-links,
383 | .wrap h1.wp-heading-inline,
384 | .wrap > .page-title-action {
385 | display: none;
386 | }
387 |
388 | .wrap {
389 | margin: 0;
390 | }
391 |
392 | #wpcontent {
393 | padding-left: 0;
394 | }
395 |
396 | .mb-cpt-message {
397 | position: absolute;
398 | top: 15px;
399 | left: 50%;
400 | transform: translateX(-50%);
401 | animation: fadein .5s;
402 | font-weight: 600;
403 | color: #00a32a;
404 | }
405 |
406 | .mb-cpt-upgrade {
407 | margin-top: 64px;
408 | background: linear-gradient(15deg, #202e3c, #1f3749, #1d4358, #1a5168, #156279, #0f788b, #08919e, #00b0b3);
409 | color: #fff;
410 | border-radius: 6px;
411 | padding: 24px;
412 | text-align: center;
413 |
414 | h2 {
415 | font-size: 24px;
416 | color: #fff;
417 | display: flex;
418 | align-items: center;
419 | gap: 8px;
420 | justify-content: center;
421 |
422 | svg {
423 | fill: #fff449;
424 | width: 24px;
425 | height: 24px;
426 | }
427 | }
428 |
429 | p {
430 | font-size: 16px;
431 | }
432 |
433 | &__features {
434 | display: flex;
435 | justify-content: space-around;
436 | text-align: left;
437 | font-size: 14px;
438 | }
439 |
440 | li {
441 | display: flex;
442 | align-items: center;
443 | gap: 4px;
444 |
445 | svg {
446 | background: #fff;
447 | border-radius: 50%;
448 | }
449 | }
450 |
451 | .components-button {
452 | padding: 24px;
453 | background: #fff;
454 | color: #1a5168;
455 | font-size: 14px;
456 | position: relative;
457 | transition: all .2s;
458 |
459 | svg {
460 | margin-left: 4px;
461 | }
462 |
463 | &:hover {
464 | margin-top: -2px;
465 | margin-bottom: 2px;
466 | }
467 | }
468 | }
469 |
470 | @media (max-width:767px) {
471 | .mb-cpt-field {
472 | flex-direction: column;
473 | }
474 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wpmetabox/mb-custom-post-type",
3 | "type": "wordpress-plugin",
4 | "keywords": [
5 | "wordpress",
6 | "ui",
7 | "select2",
8 | "wp-admin",
9 | "meta-box",
10 | "wordpress-plugin",
11 | "custom-field",
12 | "custom-post-type"
13 | ],
14 | "description": "Create and manage custom post types and custom taxonomies with an easy-to-use interface in WordPress.",
15 | "homepage": "https://metabox.io/plugins/custom-post-type/",
16 | "license": "GPL-2.0",
17 | "minimum-stability": "stable",
18 | "config": {
19 | "prepend-autoloader": false,
20 | "sort-packages": true
21 | },
22 | "require": {
23 | "wpmetabox/support": "dev-master"
24 | },
25 | "authors": [
26 | {
27 | "name": "Tran Ngoc Tuan Anh",
28 | "email": "anhtnt@elightup.com",
29 | "homepage": "https://metabox.io",
30 | "role": "Developer"
31 | }
32 | ],
33 | "autoload": {
34 | "psr-4": {
35 | "MBCPT\\" : "src/"
36 | }
37 | },
38 | "scripts": {
39 | "phpcbf": [
40 | "phpcbf src/ --standard=../meta-box-aio/phpcs.xml"
41 | ],
42 | "post-update-cmd": [
43 | "echo Deleting .git dirs...",
44 | "rm -rf vendor/**/**/.git"
45 | ],
46 | "post-install-cmd": [
47 | "echo Deleting .git dirs...",
48 | "rm -rf vendor/**/**/.git"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "6458f3d68afc0fa87c7bd91a89176019",
8 | "packages": [
9 | {
10 | "name": "wpmetabox/support",
11 | "version": "dev-master",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/wpmetabox/support.git",
15 | "reference": "874318ec497ce7fb918eb0629dec825210155d97"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/wpmetabox/support/zipball/874318ec497ce7fb918eb0629dec825210155d97",
20 | "reference": "874318ec497ce7fb918eb0629dec825210155d97",
21 | "shasum": ""
22 | },
23 | "default-branch": true,
24 | "type": "library",
25 | "autoload": {
26 | "psr-4": {
27 | "MetaBox\\Support\\": ""
28 | }
29 | },
30 | "notification-url": "https://packagist.org/downloads/",
31 | "license": [
32 | "GPL-2.0-or-later"
33 | ],
34 | "description": "Helpers for Meta Box plugins",
35 | "homepage": "https://metabox.io",
36 | "support": {
37 | "issues": "https://github.com/wpmetabox/support/issues",
38 | "source": "https://github.com/wpmetabox/support/tree/master"
39 | },
40 | "time": "2025-03-17T08:34:51+00:00"
41 | }
42 | ],
43 | "packages-dev": [],
44 | "aliases": [],
45 | "minimum-stability": "stable",
46 | "stability-flags": {
47 | "wpmetabox/support": 20
48 | },
49 | "prefer-stable": false,
50 | "prefer-lowest": false,
51 | "platform": {},
52 | "platform-dev": {},
53 | "plugin-api-version": "2.6.0"
54 | }
55 |
--------------------------------------------------------------------------------
/mb-custom-post-type.php:
--------------------------------------------------------------------------------
1 | .
26 | */
27 |
28 | // Prevent loading this file directly.
29 | if ( ! defined( 'ABSPATH' ) ) {
30 | return;
31 | }
32 |
33 | if ( ! function_exists( 'mb_cpt_load' ) ) {
34 | if ( file_exists( __DIR__ . '/vendor' ) ) {
35 | require __DIR__ . '/vendor/autoload.php';
36 | }
37 |
38 | add_action( 'init', 'mb_cpt_load', 0 );
39 |
40 | function mb_cpt_load() {
41 | define( 'MB_CPT_DIR', __DIR__ );
42 | define( 'MB_CPT_VER', '2.8.2' );
43 |
44 | if ( class_exists( 'RWMB_Loader' ) ) {
45 | list( , $url ) = RWMB_Loader::get_path( __DIR__ );
46 | define( 'MB_CPT_URL', $url );
47 | } else {
48 | define( 'MB_CPT_URL', plugin_dir_url( __FILE__ ) );
49 | }
50 |
51 | new MBCPT\Integrations\WPML\Manager();
52 | new MBCPT\Integrations\Polylang\Manager();
53 |
54 | new MBCPT\PostTypeRegister();
55 | new MBCPT\TaxonomyRegister();
56 | new MBCPT\Order();
57 |
58 | if ( ! is_admin() ) {
59 | return;
60 | }
61 |
62 | // Show Meta Box admin menu.
63 | add_filter( 'rwmb_admin_menu', '__return_true' );
64 |
65 | new MBCPT\Edit( 'mb-post-type' );
66 | new MBCPT\Edit( 'mb-taxonomy' );
67 | new MBCPT\Warning();
68 | new MBCPT\Import();
69 | new MBCPT\Export();
70 | new MBCPT\PostListTable();
71 |
72 | if ( defined( 'CPTUI_VERSION' ) ) {
73 | new MBCPT\Migration();
74 | new MBCPT\Ajax();
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mb-cpt",
3 | "version": "2.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "license": "GPL-2.0-or-later",
7 | "scripts": {
8 | "watch:css": "sass --no-source-map --style=compressed --watch assets/style.scss assets/style.css",
9 | "build:css": "sass --no-source-map --style=compressed assets/style.scss assets/style.css",
10 | "build:js": "wp-scripts build",
11 | "build": "npm run build:css && npm run build:js",
12 | "start": "wp-scripts start"
13 | },
14 | "dependencies": {
15 | "dot-prop": "^5.3.0",
16 | "react-codemirror2": "^7.2.1",
17 | "slugify": "^1.4.5"
18 | },
19 | "devDependencies": {
20 | "@svgr/webpack": "^8.1.0",
21 | "@wordpress/icons": "^10.7.0",
22 | "@wordpress/scripts": "^27.9.0",
23 | "sass": "^1.54.0",
24 | "url-loader": "^4.1.1"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === MB Custom Post Types & Custom Taxonomies ===
2 | Contributors: elightup, metabox, rilwis, duc88b, truongwp, barcavn2
3 | Donate link: https://metabox.io/pricing/
4 | Tags: custom post types, custom taxonomies, posts, taxonomies
5 | Requires at least: 5.9
6 | Tested up to: 6.8.1
7 | Requires PHP: 7.2
8 | Stable tag: 2.8.2
9 | License: GPLv2 or later
10 |
11 | Create and manage custom post types and custom taxonomies with an easy-to-use UI in WordPress.
12 |
13 | == Description ==
14 |
15 | **MB Custom Post Types & Custom Taxonomies** helps you to register and edit custom post types and custom taxonomies easily in WordPress by providing an easy-to-use UI in the admin area.
16 |
17 | 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()`).
18 |
19 | > **Meta Box Lite**
20 | > 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.
21 |
22 | Using **MB Custom Post Types & Custom Taxonomies**, you will be able to craft the WordPress content types and turn it into a professional CMS (Content Management Systems).
23 |
24 | ### Features
25 |
26 | * Supports all arguments for creating custom post types (like `register_post_type()`)
27 | * Supports all arguments for creating custom taxonomies (like `register_taxonomy()`)
28 | * Supports **live editing mode**, which auto fill in all necessary labels for you!
29 | * **Export to PHP Code**
30 | * Clean code
31 | * Registered custom post types can be exported/imported using default WordPress functionality (no more plugins!)
32 |
33 | ### Plugin Links
34 |
35 | - [Project Page](https://metabox.io/plugins/custom-post-type/)
36 | - [Github Repo](https://github.com/rilwis/mb-custom-post-type/)
37 |
38 | 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. Using **MB Custom Post Types & Custom Taxonomies** in combination with [other Meta Box extensions](https://metabox.io/plugins/) will help you manage any content types in WordPress easily and make your website more professional.
39 |
40 | ### You might also like
41 |
42 | If you like this plugin, you might also like our other WordPress products:
43 |
44 | - [Slim SEO](https://wpslimseo.com) - A fast, lightweight and full-featured SEO plugin for WordPress with minimal configuration.
45 | - [Slim SEO Schema](https://wpslimseo.com/products/slim-seo-schema/) - An advanced, powerful and flexible plugin to add schemas to WordPress.
46 | - [Slim SEO Link Manager](https://wpslimseo.com/products/slim-seo-link-manager/) - Build internal link easier in WordPress with real-time reports.
47 | - [GretaThemes](https://gretathemes.com) - Free and premium WordPress themes that clean, simple and just work.
48 | - [Auto Listings](https://wpautolistings.com) - A car sale and dealership plugin for WordPress.
49 |
50 | == Installation ==
51 |
52 | - Go to **Plugins | Add New** and search for **MB Custom Post Types & Custom Taxonomies**
53 | - Click **Install Now** button to install the plugin
54 | - After installing, click **Activate Plugin** to activate the plugin
55 |
56 | == Frequently Asked Questions ==
57 |
58 | == Screenshots ==
59 | 1. Edit custom post type - General tab
60 | 1. Edit custom post type - Labels tab
61 | 1. Edit custom post type - Advanced tab
62 | 1. Edit custom post type - Supports tab
63 | 1. Edit custom post type - Taxonomies tab
64 |
65 | == Changelog ==
66 |
67 | = 2.8.2 - 2025-05-21 =
68 |
69 | Fix WPML integration with some languages
70 |
71 | = 2.8.1 - 2025-04-01 =
72 |
73 | Fix: Unknown format specifier "C" in TaxonomyRegister.php
74 |
75 | = 2.8.0 - 2025-02-04 =
76 | - Add custom permissions for taxonomy
77 | - Fix saving issues on Safari
78 |
79 | = 2.7.10 - 2024-11-19 =
80 | - Improve accessibility of the switch control
81 | - Fix empty labels when changing post type supports
82 |
83 | = 2.7.9 - 2024-11-07 =
84 | - Fix error exporting post types
85 | - Fix blank plural and singular labels
86 |
87 | = 2.7.8 - 2024-11-01 =
88 | - Fix warning when a post type has no labels
89 | - Fix export json warning
90 |
91 | = 2.7.7 - 2024-10-19 =
92 | - Sanitize labels in JS and PHP
93 | - Fix Plugin Check issues
94 |
95 | = 2.7.6 - 2024-08-22 =
96 | Fix check for premium users
97 |
98 | = 2.7.5 - 2024-08-21 =
99 | Fix edge-case error when adding "add fields" links
100 |
101 | = 2.7.4 - 2024-08-19 =
102 | Fix running PHP Codesniffer when installing & autoload the plugin's main file via Composer
103 |
104 | = 2.7.3 - 2024-08-14 =
105 | - Fix menu position not working if set after Bricks
106 | - Fix limit of taxonomy slug 32
107 | - Do not auto generate slug when it's manually changed
108 |
109 | = 2.7.2 - 2024-07-22 =
110 | Fix showing excerpt in post table lists
111 |
112 | = 2.7.1 - 2024-07-17 =
113 | A link to create custom fields after a custom post type or a custom taxonomy was created. Requires [Meta Box Builder](https://metabox.io/plugins/meta-box-builder/) to create custom fields.
114 |
115 | = 2.7.0 - 2024-06-21 =
116 |
117 | **Highlights**:
118 |
119 | This new version brings a new UI/UX to the plugin, powered by block editor components. The new UI has the same layout and interaction as editing posts with the block editor, which will brings you a modern, friendly interface and a great experience.
120 |
121 | For more details, please see the [blog post](https://metabox.io/mb-custom-post-types-custom-taxonomies-update-ui-ux-improvements/).
122 |
123 | = 2.6.4 - 2024-04-11 =
124 | - Fix admin color scheme
125 | - Fix strings not localized
126 |
127 | = 2.6.3 - 2024-03-07=
128 | - Improve i18n issues
129 | - Fix null warning notices
130 |
131 | = 2.6.2 - 2023-11-18 =
132 | - Revert feature "Allow to edit WordPress's built-in post types and post types registered by other code/plugins" in 2.6.0.
133 |
134 | = 2.6.1 - 2023-11-17 =
135 | - Fix Blocksy not recognizing post types
136 |
137 | = 2.6.0 - 2023-11-16 =
138 | - Allow to edit WordPress's built-in post types and post types registered by other code/plugins
139 |
140 | = 2.5.5 - 2023-07-18 =
141 | - Fix error when Meta Box is not available
142 |
143 | = 2.5.4 - 2023-03-23 =
144 | - Fix error when not using with Meta Box
145 |
146 | = 2.5.3 - 2023-03-23 =
147 | - Fix URL when installing with Composer
148 |
149 | = 2.5.2 - 2023-02-27 =
150 | - Enqueue Font Awesome only when a post type use it (#55)
151 |
152 | = 2.5.1 - 2023-01-31 =
153 | - Fix CPT export wrong file name
154 | - Update description for FontAwesome icon select
155 |
156 | = 2.5.0 - 2023-01-13 =
157 | - Add support for Font Awesome in admin menu
158 | - Add icon label and allow to search icons by label
159 | - Fix import on Windows
160 |
161 | = 2.4.0 - 2022-12-24 =
162 | - Add import export feature for post types and taxonomies
163 |
164 | = 2.3.5 - 2022-12-13 =
165 | - Fix error with Meta Box 5.6.14 when edit post types
166 | - Fix typo
167 |
168 | = 2.3.4 - 2022-11-11 =
169 | - Fix `meta_box_cb` settings not working properly
170 |
171 | = 2.3.3 - 2022-07-27 =
172 | - Improve post type and taxonomy label capitalization
173 |
174 | = 2.3.2 - 2022-05-17 =
175 | - Fix `meta_box_cb` parameter not working property for tags and categories
176 |
177 | = 2.3.1 - 2022-01-13 =
178 | - Fix the menu icon not showing from v2.2.6
179 |
180 | = 2.3.0 - 2021-12-13 =
181 | - Add migration tool from Custom Post Type UI
182 |
183 | = 2.2.6 - 2021-10-28 =
184 | - Fix changing menu icon not working
185 |
186 | = 2.2.5 - 2021-10-20 =
187 | - Fix generated code if labels contain single quotes
188 | - Update list of Dashicons
189 |
190 | = 2.2.4 - 2021-09-16 =
191 | - Fix missing comma in the generated code.
192 |
193 | = 2.2.2 - 2021-09-10 =
194 | - Fix generated code
195 | - Restrict access to admin menu for admins only
196 |
197 | = 2.2.1 - 2021-07-26 =
198 | - Fix custom archive slug not working
199 |
200 | = 2.2.0 - 2021-07-05 =
201 | - Add a warning if permalink is default.
202 | - Add options for menu icon type, which supports SVG and custom URL.
203 |
204 | = 2.1.2 - 2021-04-29 =
205 | - Update Youtube video and documentation link in the welcome page.
206 |
207 | = 2.1.1 - 2021-04-23 =
208 | - Fix supports = false when no supports features are selected
209 | - Fix custom capabilities when set plural and singular names are similar
210 |
211 | = 2.1.0 - 2021-04-13 =
212 | - Add tooltips to all settings
213 | - Show slug in lists of post types and taxonomies
214 | - Show error and disable publish button if slug is one of WordPress reserved terms
215 | - Set full custom capabilities for post types and add guidance message for custom capabilities
216 | - Add missing labels for posts table headings
217 |
218 | = 2.0.11 - 2021-04-08 =
219 | - Fix menu position not working
220 |
221 | = 2.0.10 - 2021-03-13 =
222 | - Fix checkbox cannot be checked
223 | - Fix default value for menu_position
224 |
225 | = 2.0.9 - 2021-03-06 =
226 | - Fix number in plural name.
227 |
228 | = 2.0.8 - 2021-01-25 =
229 | - Increase priority to register post type to make the submenu Post types & Taxonomies appear first in the main Meta Box menu
230 |
231 | = 2.0.7 - 2020-12-11 =
232 | - Fix missing options for show_in_menu and menu_position
233 | - Fix label reverts to English
234 | - Fix icon styling in WordPress 5.6
235 | - Make JS text translatable after bundling with Webpack
236 |
237 | = 2.0.6 - 2020-11-05 =
238 | - Fix menu icon not showing
239 | - Improve the code using React context properly
240 |
241 | = 2.0.5 - 2020-11-04 =
242 | - Fix supports tab not rendering
243 |
244 | = 2.0.4 - 2020-11-02 =
245 | - Fix [object Object] when register a custom taxonomy.
246 |
247 | = 2.0.3 - 2020-10-07 =
248 | - Fix migrating data
249 |
250 | = 2.0.2 - 2020-10-05 =
251 | - Fix encoding characters. For users who already upgraded, please add `?mbcpt-force=1` to your website URL to fix the problem.
252 | - Fix empty page when clicking Taxonomies/Get PHP Code tab
253 |
254 | = 2.0.1 - 2020-10-01 =
255 | - Fix menu position not working
256 |
257 | = 2.0.0 - 2020-09-30 =
258 | - Rewrite the UI with React.
259 | - Update PHP code to use Composer.
260 |
261 | = 1.9.5 - 2020-07-28 =
262 | - Use WordPress's built-in clipboard script
263 |
264 | = 1.9.3 - 2020-04-16 =
265 | - Fix notice: Undefined index `meta_box_cb`
266 | - Fix warning for `supports` parameter in WordPress 5.3
267 | - Add filter for advanced settings manipulation
268 | - Improve toggle buttons
269 |
270 | = 1.9.2 - 2019-11-28 =
271 | - Fix warning for 'supports' parameter in WordPress 5.3.
272 |
273 | = 1.9.1 - 2019-09-06 =
274 | - Fix menu icon not working
275 |
276 | = 1.9.0 - 2019-08-29 =
277 | - Add support for custom archive slug
278 | - Fix style in dashboard
279 | - Do not show upgrade message for premium users
280 |
281 | = 1.8.6 - 2019-07-17 =
282 | - Hide the meta box for taxonomy if set `meta_box_cb` = false in Gutenberg.
283 |
284 | = 1.8.5 - 2019-06-27 =
285 | - Fix quotes in plural and singular names not working.
286 |
287 | = 1.8.4 - 2019-06-01 =
288 | - Update the page layout to make it more friendly.
289 | - Enabled REST API by default for taxonomies to make they work with Gutenberg.
290 | - Make the plugin safe to include in Meta Box AIO.
291 |
292 | = 1.8.3 - 2019-03-21 =
293 | - Set `'map_meta_cap' => true` for custom capabilities
294 |
295 | = 1.8.2 - 2019-03-06 =
296 | - Enabled REST API by default for post types to make they work with Gutenberg.
297 | - Added "custom" for capability type, allowing developers to set custom capabilities.
298 | - Auto truncated post type slug to 20 characters.
299 |
300 | = 1.8.1 - 2018-12-10 =
301 | - Fix typos and reformat code.
302 |
303 | = 1.8.0 - 2018-06-08 =
304 |
305 | - Used the shared menu from Meta Box to keep the admin menu clean.
306 | - Added tabs to the About page
307 | - Remove redirection after activation.
308 |
309 | = 1.7.0 - 2018-06-02 =
310 | - Added support for move the custom post type menu to a sub-menu of an existing top-level menu.
311 | - Updated some text strings for better description
312 | - Fixed button "Advanced" not working
313 | - Fixed REST API base not a text input for taxonomy
314 |
315 | = 1.6.0 - 2018-05-28 =
316 | - Changed menu position from a text field to a select field, so users just select the position they want without knowing the position number.
317 | - Hide the ads for premium users. You need to enter correct license key to hide it.
318 |
319 | = 1.5.0 =
320 | - Added support for rewrite options for taxonomies.
321 | - Added "Copy to Clipboard" for generated code.
322 |
323 | = 1.4.3 =
324 | - Fixed undefined index when registering a new taxonomy.
325 |
326 | = 1.4.2 =
327 | - Fixed error in generated code for taxonomy.
328 | - Fixed translation and logo URL.
329 |
330 | = 1.4.1 =
331 | - Added "About" page to help new users use the plugin.
332 |
333 | = 1.4 =
334 | - Added export to PHP code, so you can just copy and paste into your theme or plugin.
335 | - Added option to enable/disable "Custom Fields" meta box.
336 | - Fixed "menu_position" doesn't work.
337 |
338 | = 1.3.1 =
339 | - Added option to hide taxonomy meta box in the edit page.
340 |
341 | = 1.3 =
342 | - Added support for showing post types/taxonomies in REST API
343 |
344 | = 1.2.5 =
345 | - Custom post types not shown when edit custom taxonomy.
346 |
347 | = 1.2.4 =
348 | - Removed undefined index notice.
349 |
350 | = 1.2.3 =
351 | - Added new options for custom rewrite slug and with_front.
352 |
353 | = 1.2.2 =
354 | - Custom taxonomies now can be added to 'post', 'page', 'attachment'
355 |
356 | = 1.2.1 =
357 | - Unable to assign only category or tags to custom post type.
358 |
359 | = 1.2.0 =
360 | - Allow custom post types to have default taxonomies: category and tags.
361 |
362 | = 1.1.0 =
363 | - Integrates with [MB Custom Taxonomy](https://wordpress.org/plugins/mb-custom-taxonomy/) to create and manage custom taxonomies with similar interface.
364 |
365 | = 1.0.2 =
366 | - Added custom code to load plugin dependency for smaller footprint.
367 |
368 | = 1.0 =
369 | - First version.
370 |
371 | == Upgrade Notice ==
372 |
--------------------------------------------------------------------------------
/src/Ajax.php:
--------------------------------------------------------------------------------
1 | Arr::get( $value, 'rewrite_slug' ),
39 | 'with_front' => Arr::get( $value, 'rewrite_withfront' ),
40 | ];
41 | unset( $value['rewrite_slug'] );
42 | unset( $value['rewrite_withfront'] );
43 | unset( $value['name'] );
44 | unset( $value['menu_icon'] );
45 | unset( $value['show_in_menu_string'] );
46 | $array = [
47 | 'singular_name' => $singular,
48 | 'name' => $plural,
49 | 'menu_name' => Arr::get( $value, 'labels.menu_name', $plural ) ?: $plural,
50 | 'all_items' => Arr::get( $value, 'labels.all_items', 'All ' . $plural ) ?: 'All ' . $plural,
51 | 'view_items' => Arr::get( $value, 'labels.view_items', 'View ' . $plural ) ?: 'View ' . $plural,
52 | 'search_items' => Arr::get( $value, 'labels.search_items', 'Search ' . $plural ) ?: 'Search ' . $plural,
53 | 'not_found' => Arr::get( $value, 'labels.not_found', 'No ' . $plural . ' found' ) ?: 'No ' . $plural . ' found',
54 | 'not_found_in_trash' => Arr::get( $value, 'labels.not_found_in_trash', 'No ' . $plural . ' found in trash' ) ?: 'No ' . $plural . ' found in trash',
55 | 'add_new_item' => Arr::get( $value, 'labels.add_new_item', 'All new ' . $singular ) ?: 'All new ' . $singular,
56 | 'edit_item' => Arr::get( $value, 'labels.edit_item', 'Edit ' . $singular ) ?: 'Edit ' . $singular,
57 | 'new_item' => Arr::get( $value, 'labels.new_item', 'New ' . $singular ) ?: 'New ' . $singular,
58 | 'view_item' => Arr::get( $value, 'labels.view_item', 'View ' . $singular ) ?: 'View ' . $singular,
59 | 'add_new' => Arr::get( $value, 'labels.add_new', 'Add new' ) ?: 'Add new',
60 | 'parent_item_colon' => Arr::get( $value, 'labels.parent_item_colon', 'Parent ' . $singular ) ?: 'Parent ' . $singular,
61 | 'featured_image' => Arr::get( $value, 'labels.featured_image', 'Featured image' ) ?: 'Featured image',
62 | 'set_featured_image' => Arr::get( $value, 'labels.set_featured_image', 'Set featured image' ) ?: 'Set featured image',
63 | 'remove_featured_image' => Arr::get( $value, 'labels.remove_featured_image', 'Remove featured image' ) ?: 'Remove featured image',
64 | 'use_featured_image' => Arr::get( $value, 'labels.use_featured_image', 'Use as featured image' ) ?: 'Use as featured image',
65 | 'archives' => Arr::get( $value, 'labels.archives', $singular . ' archives' ) ?: $singular . ' archives',
66 | 'insert_into_item' => Arr::get( $value, 'labels.insert_into_item', 'Insert into ' . $singular ) ?: 'Insert into ' . $singular,
67 | 'uploaded_to_this_item' => Arr::get( $value, 'labels.uploaded_to_this_item', 'Uploaded to this ' . $singular ) ?: 'Uploaded to this ' . $singular,
68 | 'filter_items_list' => Arr::get( $value, 'labels.filter_items_list', 'Filter ' . $plural . ' list' ) ?: 'Filter ' . $plural . ' list',
69 | 'items_list_navigation' => Arr::get( $value, 'labels.items_list_navigation', $plural . ' list navigation' ) ?: $plural . ' list navigation',
70 | 'items_list' => Arr::get( $value, 'labels.items_list', $plural . ' list' ) ?: $plural . ' list',
71 | 'attributes' => Arr::get( $value, 'labels.attributes', $plural . ' attributes' ) ?: $plural . ' attributes',
72 | 'item_published' => Arr::get( $value, 'labels.item_published', $singular . ' published' ) ?: $singular . ' published',
73 | 'item_published_privately' => Arr::get( $value, 'labels.item_published_privately', $singular . ' published privately' ) ?: $singular . ' published privately',
74 | 'item_reverted_to_draft' => Arr::get( $value, 'labels.item_published_privately', $singular . ' reverted to draft' ) ?: $singular . ' reverted to draft',
75 | 'item_scheduled' => Arr::get( $value, 'labels.item_scheduled', $singular . ' scheduled' ) ?: $singular . ' scheduled',
76 | 'item_updated' => Arr::get( $value, 'labels.item_updated', $singular . ' updated' ) ?: $singular . ' updated',
77 | ];
78 | $value['labels'] = array_merge( $value['labels'], $array );
79 | $content = wp_json_encode( $value, JSON_UNESCAPED_UNICODE );
80 | $content = str_replace( '"true"', 'true', $content );
81 | wp_insert_post([
82 | 'post_content' => str_replace( '"false"', 'false', $content ),
83 | 'post_type' => 'mb-post-type',
84 | 'post_title' => $singular,
85 | 'post_status' => 'publish',
86 | ]);
87 | }
88 | delete_option( 'cptui_post_types' );
89 | wp_send_json_success();
90 | }
91 |
92 | public function migrate_taxonomies() {
93 | if ( session_status() !== PHP_SESSION_ACTIVE ) {
94 | session_start();
95 | }
96 | $data_taxoui = get_option( 'cptui_taxonomies' );
97 | if ( empty( $data_taxoui ) ) {
98 | wp_send_json_error();
99 | }
100 | foreach ( $data_taxoui as $value ) {
101 | $plural = Arr::get( $value, 'label' );
102 | $singular = Arr::get( $value, 'singular_label' );
103 | $value['slug'] = Arr::get( $value, 'name' );
104 | $value['types'] = Arr::get( $value, 'object_types' );
105 | $value['rewrite'] = [
106 | 'slug' => Arr::get( $value, 'rewrite_slug' ),
107 | 'with_front' => Arr::get( $value, 'rewrite_withfront' ),
108 | ];
109 | unset( $value['rewrite_slug'] );
110 | unset( $value['rewrite_withfront'] );
111 | unset( $value['name'] );
112 | unset( $value['object_types'] );
113 | $array = [
114 | 'singular_name' => $singular,
115 | 'name' => $plural,
116 | 'menu_name' => Arr::get( $value, 'labels.menu_name', $plural ) ?: $plural,
117 | 'search_items' => Arr::get( $value, 'labels.search_items', 'Search ' . $plural ) ?: 'Search ' . $plural,
118 | 'popular_items' => Arr::get( $value, 'labels.popular_items', 'Popular ' . $plural ) ?: 'Popular ' . $plural,
119 | 'all_items' => Arr::get( $value, 'labels.all_items', 'All ' . $plural ) ?: 'All ' . $plural,
120 | 'view_item' => Arr::get( $value, 'labels.view_item', 'View ' . $singular ) ?: 'View ' . $singular,
121 | 'parent_item' => Arr::get( $value, 'labels.parent_item', 'Parent ' . $singular ) ?: 'Parent ' . $singular,
122 | 'parent_item_colon' => Arr::get( $value, 'labels.parent_item_colon', 'Parent ' . $singular ) ?: 'Parent ' . $singular,
123 | 'edit_item' => Arr::get( $value, 'labels.edit_item', 'Edit ' . $singular ) ?: 'Edit ' . $singular,
124 | 'update_item' => Arr::get( $value, 'labels.update_item', 'Update ' . $singular ) ?: 'Update ' . $singular,
125 | 'add_new_item' => Arr::get( $value, 'labels.add_new_item', 'Add new ' . $singular ) ?: 'Add new ' . $singular,
126 | 'new_item_name' => Arr::get( $value, 'labels.new_item_name', 'New ' . $singular . ' name' ) ?: 'New ' . $singular . ' name',
127 | 'filter_by_item' => Arr::get( $value, 'labels.filter_by_item', 'Filter by ' . $singular ) ?: 'Filter by ' . $singular,
128 | 'separate_items_with_commas' => Arr::get( $value, 'labels.separate_items_with_commas', 'Separate ' . $plural . ' with commas' ) ?: 'Separate ' . $plural . ' with commas',
129 | 'add_or_remove_items' => Arr::get( $value, 'labels.add_or_remove_items', 'Add or remove ' . $plural ) ?: 'Add or remove ' . $plural,
130 | 'choose_from_most_used' => Arr::get( $value, 'labels.choose_from_most_used', 'Choose from the most used ' . $plural ) ?: 'Choose from the most used ' . $plural,
131 | 'not_found' => Arr::get( $value, 'labels.not_found', 'Not ' . $plural . ' found' ) ?: 'Not ' . $plural . ' found',
132 | 'no_terms' => Arr::get( $value, 'labels.no_terms', 'No ' . $plural ) ?: 'No ' . $plural,
133 | 'items_list_navigation' => Arr::get( $value, 'labels.items_list_navigation', $plural . ' list navigation' ) ?: $plural . ' list navigation',
134 | 'items_list' => Arr::get( $value, 'labels.items_list', $plural . ' list' ) ?: $plural . ' list',
135 | 'back_to_items' => Arr::get( $value, 'labels.back_to_items', 'Back to ' . $plural ) ?: 'Back to ' . $plural,
136 | ];
137 | $value['labels'] = array_merge( $value['labels'], $array );
138 | $content = wp_json_encode( $value, JSON_UNESCAPED_UNICODE );
139 | $content = str_replace( '"true"', 'true', $content );
140 | wp_insert_post([
141 | 'post_content' => str_replace( '"false"', 'false', $content ),
142 | 'post_type' => 'mb-taxonomy',
143 | 'post_title' => $singular,
144 | 'post_status' => 'publish',
145 | ]);
146 | }
147 | delete_option( 'cptui_taxonomies' );
148 | wp_send_json_success();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/Edit.php:
--------------------------------------------------------------------------------
1 | post_type = $post_type;
11 | add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
12 | }
13 |
14 | public function enqueue_scripts() {
15 | if ( ! $this->is_screen() ) {
16 | return;
17 | }
18 |
19 | wp_enqueue_style( $this->post_type, MB_CPT_URL . 'assets/style.css', [ 'wp-components' ], MB_CPT_VER );
20 | wp_enqueue_style( 'font-awesome', MB_CPT_URL . 'assets/fontawesome/css/all.min.css', [], '6.6.0' );
21 | wp_enqueue_style( 'wp-edit-post' );
22 |
23 | wp_enqueue_script( 'mbcpt-edit', MB_CPT_URL . 'assets/edit.js', [], filemtime( MB_CPT_DIR . '/assets/edit.js' ), true );
24 |
25 | $object = str_replace( 'mb-', '', $this->post_type );
26 | wp_enqueue_code_editor( [ 'type' => 'application/x-httpd-php' ] );
27 |
28 | $asset = require MB_CPT_DIR . "/assets/build/$object.asset.php";
29 | wp_enqueue_script( $this->post_type, MB_CPT_URL . "assets/build/$object.js", $asset['dependencies'], $asset['version'], true );
30 | wp_localize_script( $this->post_type, 'MBCPT', $this->js_vars() );
31 |
32 | $path = '';
33 | if ( defined( 'META_BOX_LITE_DIR' ) ) {
34 | $path = META_BOX_LITE_DIR . '/languages/mb-custom-post-type';
35 | }
36 | if ( defined( 'META_BOX_AIO_DIR' ) ) {
37 | $path = META_BOX_AIO_DIR . '/languages/mb-custom-post-type';
38 | }
39 | wp_set_script_translations( $this->post_type, 'mb-custom-post-type', $path );
40 | }
41 |
42 | private function js_vars(): array {
43 | $post = get_post();
44 |
45 | $vars = [
46 | 'icons' => Data::get_dashicons(),
47 | 'settings' => json_decode( $post->post_content, true ),
48 | 'reservedTerms' => $this->get_reserved_terms(),
49 | 'action' => get_current_screen()->action,
50 | 'url' => admin_url( 'edit.php?post_type=' . get_current_screen()->id ),
51 | 'add' => admin_url( 'post-new.php?post_type=' . get_current_screen()->id ),
52 | 'status' => $post->post_status,
53 | 'author' => get_the_author_meta( 'display_name', (int) $post->post_author ),
54 | 'trash' => get_delete_post_link(),
55 | 'published' => get_the_date( 'F d, Y' ) . ' ' . get_the_time( 'g:i a' ),
56 | 'modifiedtime' => get_post_modified_time( 'F d, Y g:i a', true, null, true ),
57 | 'saving' => __( 'Saving...', 'mb-custom-post-type' ),
58 | 'upgrade' => ! $this->is_premium_user(),
59 | 'allCapabilities' => $this->get_all_capabilities(),
60 | ];
61 |
62 | if ( 'mb-post-type' === get_current_screen()->id ) {
63 | $taxonomies = Data::get_taxonomies();
64 | $options = [];
65 | foreach ( $taxonomies as $slug => $taxonomy ) {
66 | $options[ $slug ] = sprintf( '%s (%s)', $taxonomy->labels->singular_name, $slug );
67 | }
68 | $vars['taxonomies'] = $options;
69 | $vars['icon_type'] = [
70 | [
71 | 'value' => 'dashicons',
72 | 'label' => esc_html__( 'Dashicons', 'mb-custom-post-type' ),
73 | ],
74 | [
75 | 'value' => 'svg',
76 | 'label' => esc_html__( 'SVG', 'mb-custom-post-type' ),
77 | ],
78 | [
79 | 'value' => 'custom',
80 | 'label' => esc_html__( 'Custom URL', 'mb-custom-post-type' ),
81 | ],
82 | [
83 | 'value' => 'font_awesome',
84 | 'label' => esc_html__( 'Font Awesome', 'mb-custom-post-type' ),
85 | ],
86 | ];
87 | $vars['menu_position_options'] = $this->get_menu_position_options();
88 | $vars['show_in_menu_options'] = $this->get_show_in_menu_options();
89 | }
90 |
91 | if ( 'mb-taxonomy' === get_current_screen()->id ) {
92 | $post_types = Data::get_post_types();
93 | $options = [];
94 | foreach ( $post_types as $slug => $post_type ) {
95 | $options[ $slug ] = sprintf( '%s (%s)', $post_type->labels->singular_name, $slug );
96 | }
97 | $vars['types'] = $options;
98 | }
99 |
100 | return $vars;
101 | }
102 |
103 | private function is_screen() {
104 | $screen = get_current_screen();
105 | return 'post' === $screen->base && $this->post_type === $screen->post_type;
106 | }
107 |
108 | private function is_premium_user(): bool {
109 | if ( ! class_exists( 'MetaBox\Updater\Option' ) || ! class_exists( 'MetaBox\Updater\Checker' ) ) {
110 | return false;
111 | }
112 | $update_option = new \MetaBox\Updater\Option();
113 | $update_checker = new \MetaBox\Updater\Checker( $update_option );
114 | return $update_checker->has_extensions();
115 | }
116 |
117 | private function get_show_in_menu_options(): array {
118 | global $menu;
119 |
120 | $options = [
121 | [
122 | 'value' => 'true',
123 | 'label' => esc_html__( 'Show as top-level menu', 'mb-custom-post-type' ),
124 | ],
125 | [
126 | 'value' => 'false',
127 | 'label' => esc_html__( 'Do not show in the admin menu', 'mb-custom-post-type' ),
128 | ],
129 | ];
130 | foreach ( $menu as $params ) {
131 | if ( ! empty( $params[0] ) && ! empty( $params[2] ) ) {
132 | $options[] = [
133 | 'value' => $params[2],
134 | // Translators: %s is the main menu label.
135 | 'label' => sprintf( __( 'Show as sub-menu of %s', 'mb-custom-post-type' ), $this->strip_span( $params[0] ) ),
136 | ];
137 | }
138 | }
139 | return $options;
140 | }
141 |
142 | private function get_menu_position_options(): array {
143 | global $menu;
144 |
145 | $positions = [
146 | [
147 | 'value' => '',
148 | 'label' => __( 'Default', 'mb-custom-post-type' ),
149 | ],
150 | ];
151 | foreach ( $menu as $position => $params ) {
152 | if ( ! empty( $params[0] ) ) {
153 | $positions[] = [
154 | 'value' => $position,
155 | 'label' => $this->strip_span( $params[0] ),
156 | ];
157 | }
158 | }
159 |
160 | return $positions;
161 | }
162 |
163 | /**
164 | * Strip span tag from HTML.
165 | *
166 | * @param string $html HTML content.
167 | *
168 | * @return string
169 | */
170 | private function strip_span( $html ): string {
171 | return preg_replace( '@.* @si', '', $html );
172 | }
173 |
174 | /**
175 | * Get reserved terms.
176 | *
177 | * @return string[]
178 | */
179 | private function get_reserved_terms(): array {
180 | return [
181 | 'action',
182 | 'attachment',
183 | 'attachment_id',
184 | 'author',
185 | 'author_name',
186 | 'calendar',
187 | 'cat',
188 | 'category',
189 | 'category__and',
190 | 'category__in',
191 | 'category__not_in',
192 | 'category_name',
193 | 'comments_per_page',
194 | 'comments_popup',
195 | 'custom',
196 | 'customize_messenger_channel',
197 | 'customized',
198 | 'cpage',
199 | 'day',
200 | 'debug',
201 | 'embed',
202 | 'error',
203 | 'exact',
204 | 'feed',
205 | 'fields',
206 | 'hour',
207 | 'link_category',
208 | 'm',
209 | 'minute',
210 | 'monthnum',
211 | 'more',
212 | 'name',
213 | 'nav_menu',
214 | 'nonce',
215 | 'nopaging',
216 | 'offset',
217 | 'order',
218 | 'orderby',
219 | 'p',
220 | 'page',
221 | 'page_id',
222 | 'paged',
223 | 'pagename',
224 | 'pb',
225 | 'perm',
226 | 'post',
227 | 'post__in',
228 | 'post__not_in',
229 | 'post_format',
230 | 'post_mime_type',
231 | 'post_status',
232 | 'post_tag',
233 | 'post_type',
234 | 'posts',
235 | 'posts_per_archive_page',
236 | 'posts_per_page',
237 | 'preview',
238 | 'robots',
239 | 's',
240 | 'search',
241 | 'second',
242 | 'sentence',
243 | 'showposts',
244 | 'static',
245 | 'status',
246 | 'subpost',
247 | 'subpost_id',
248 | 'tag',
249 | 'tag__and',
250 | 'tag__in',
251 | 'tag__not_in',
252 | 'tag_id',
253 | 'tag_slug__and',
254 | 'tag_slug__in',
255 | 'taxonomy',
256 | 'tb',
257 | 'term',
258 | 'terms',
259 | 'theme',
260 | 'title',
261 | 'type',
262 | 'types',
263 | 'w',
264 | 'withcomments',
265 | 'withoutcomments',
266 | 'year',
267 | ];
268 | }
269 |
270 | /**
271 | * Get all capabilities.
272 | *
273 | * @return string[]
274 | */
275 | private function get_all_capabilities(): array {
276 | global $wp_roles;
277 |
278 | $capabilities = [];
279 | foreach ( $wp_roles->roles as $role ) {
280 | $capabilities = array_merge( $capabilities, array_keys( $role['capabilities'] ) );
281 | }
282 |
283 | return array_values( array_unique( $capabilities ) );
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/src/Export.php:
--------------------------------------------------------------------------------
1 | post_type, [ 'mb-post-type', 'mb-taxonomy' ], true ) ) {
14 | return $actions;
15 | }
16 |
17 | $url = wp_nonce_url( add_query_arg( [
18 | 'action' => 'mbcpt-export',
19 | 'post_type' => $post->post_type,
20 | 'post[]' => $post->ID,
21 | ] ), 'bulk-posts' );
22 | $actions['export'] = '' . esc_html__( 'Export', 'mb-custom-post-type' ) . ' ';
23 |
24 | return $actions;
25 | }
26 |
27 | public function export() {
28 | $action = isset( $_REQUEST['action'] ) && 'mbcpt-export' === $_REQUEST['action'];
29 | $action2 = isset( $_REQUEST['action2'] ) && 'mbcpt-export' === $_REQUEST['action2'];
30 |
31 | if ( ( ! $action && ! $action2 ) || empty( $_REQUEST['post'] ) || empty( $_REQUEST['post_type'] ) ) {
32 | return;
33 | }
34 |
35 | check_ajax_referer( 'bulk-posts' );
36 |
37 | $post_ids = wp_parse_id_list( wp_unslash( $_REQUEST['post'] ) );
38 |
39 | $query = new WP_Query( [
40 | 'post_type' => sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ) ),
41 | 'post__in' => $post_ids,
42 | 'posts_per_page' => count( $post_ids ),
43 | 'no_found_rows' => true,
44 | 'update_post_term_cache' => false,
45 | ] );
46 |
47 | $data = [];
48 | foreach ( $query->posts as $post ) {
49 | $data[] = [
50 | 'post_title' => $post->post_title,
51 | 'settings' => json_decode( $post->post_content, true ),
52 | 'post_date' => $post->post_date,
53 | 'post_status' => $post->post_status,
54 | 'post_type' => $post->post_type,
55 | ];
56 | }
57 | $file_name = 'post-types-export';
58 | if ( $_REQUEST['post_type'] === 'mb-taxonomy' ) {
59 | $file_name = 'taxonomies-export';
60 | }
61 | if ( count( $post_ids ) === 1 ) {
62 | $data = reset( $data );
63 | $post = $query->posts[0];
64 | $file_name = $post->post_name ?: sanitize_key( $post->post_title );
65 | }
66 |
67 | $output = wp_json_encode( $data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT );
68 |
69 | header( 'Content-Type: application/octet-stream' );
70 | header( "Content-Disposition: attachment; filename=$file_name.json" );
71 | header( 'Expires: 0' );
72 | header( 'Cache-Control: must-revalidate' );
73 | header( 'Pragma: public' );
74 | header( 'Content-Length: ' . strlen( $output ) );
75 |
76 | echo wp_json_encode( $data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT );
77 | die;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Import.php:
--------------------------------------------------------------------------------
1 | id, [ 'edit-mb-post-type', 'edit-mb-taxonomy' ], true ) ) {
14 | return;
15 | }
16 |
17 | wp_enqueue_style( 'mbcpt-import', MB_CPT_URL . 'assets/import.css', [], MB_CPT_VER );
18 | wp_enqueue_script( 'mbcpt-import', MB_CPT_URL . 'assets/import.js', [ 'wp-element', 'wp-components', 'wp-i18n', 'clipboard' ], MB_CPT_VER, true );
19 |
20 | wp_localize_script( 'mbcpt-import', 'MBCPT', [
21 | 'export' => esc_html__( 'Export', 'mb-custom-post-type' ),
22 | 'import' => esc_html__( 'Import', 'mb-custom-post-type' ),
23 | ] );
24 | }
25 |
26 | public function output_js_templates() {
27 | if ( ! in_array( get_current_screen()->id, [ 'edit-mb-post-type', 'edit-mb-taxonomy' ], true ) ) {
28 | return;
29 | }
30 | ?>
31 |
32 |
33 |
34 |
35 |
46 | import_json( $data );
59 |
60 | if ( ! $result ) {
61 | // Translators: %s - go back URL.
62 | wp_die( wp_kses_post( sprintf( __( 'Invalid file data. Go back .', 'mb-custom-post-type' ), $url ) ) );
63 | }
64 |
65 | $url = add_query_arg( 'imported', 'true', $url );
66 | wp_safe_redirect( $url );
67 | die;
68 | }
69 |
70 | private function import_json( $data ) {
71 | $posts = json_decode( $data, true );
72 | if ( json_last_error() !== JSON_ERROR_NONE ) {
73 | return false;
74 | }
75 |
76 | // If import only one post.
77 | if ( isset( $posts['post_title'] ) ) {
78 | $posts = [ $posts ];
79 | }
80 |
81 | foreach ( $posts as $post ) {
82 | $post['post_content'] = wp_json_encode( $post['settings'] );
83 |
84 | $post_id = wp_insert_post( $post );
85 | if ( ! $post_id ) {
86 | wp_die( wp_kses_post( sprintf(
87 | // Translators: %s - go back URL.
88 | __( 'Cannot import the post type %1$s . Go back .', 'mb-custom-post-type' ),
89 | $post['title'],
90 | admin_url( "edit.php?post_type={$post['post_type']}" )
91 | ) ) );
92 | }
93 | if ( is_wp_error( $post_id ) ) {
94 | wp_die( wp_kses_post( implode( ' ', $post_id->get_error_messages() ) ) );
95 | }
96 | }
97 |
98 | return true;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/Integrations/Polylang/Manager.php:
--------------------------------------------------------------------------------
1 | keys = [
9 | 'name' => __( 'Label', 'mb-custom-post-type' ),
10 | 'singular_name' => __( 'Singular name', 'mb-custom-post-type' ),
11 | 'add_new' => __( 'Add new', 'mb-custom-post-type' ),
12 | 'add_new_item' => __( 'Add new item', 'mb-custom-post-type' ),
13 | 'edit_item' => __( 'Edit item', 'mb-custom-post-type' ),
14 | 'new_item' => __( 'New item', 'mb-custom-post-type' ),
15 | 'view_item' => __( 'View item', 'mb-custom-post-type' ),
16 | 'view_items' => __( 'View items', 'mb-custom-post-type' ),
17 | 'search_items' => __( 'Search items', 'mb-custom-post-type' ),
18 | 'not_found' => __( 'Not found', 'mb-custom-post-type' ),
19 | 'not_found_in_trash' => __( 'Not found in trash', 'mb-custom-post-type' ),
20 | 'parent_item_colon' => __( 'Parent item colon', 'mb-custom-post-type' ),
21 | 'all_items' => __( 'All items', 'mb-custom-post-type' ),
22 | 'archives' => __( 'Archives', 'mb-custom-post-type' ),
23 | 'attributes' => __( 'Attributes', 'mb-custom-post-type' ),
24 | 'insert_into_item' => __( 'Insert into item', 'mb-custom-post-type' ),
25 | 'uploaded_to_this_item' => __( 'Uploaded to this item', 'mb-custom-post-type' ),
26 | 'featured_image' => __( 'Featured image', 'mb-custom-post-type' ),
27 | 'set_featured_image' => __( 'Set featured image', 'mb-custom-post-type' ),
28 | 'remove_featured_image' => __( 'Remove featured image', 'mb-custom-post-type' ),
29 | 'use_featured_image' => __( 'Use as featured image', 'mb-custom-post-type' ),
30 | 'menu_name' => __( 'Menu name', 'mb-custom-post-type' ),
31 | 'filter_items_list' => __( 'Filter items list', 'mb-custom-post-type' ),
32 | 'filter_by_date' => __( 'Filter by date', 'mb-custom-post-type' ),
33 | 'items_list_navigation' => __( 'Items list navigation', 'mb-custom-post-type' ),
34 | 'items_list' => __( 'Items list', 'mb-custom-post-type' ),
35 | 'item_published' => __( 'Item published', 'mb-custom-post-type' ),
36 | 'item_published_privately' => __( 'Item published privately', 'mb-custom-post-type' ),
37 | 'item_reverted_to_draft' => __( 'Item reverted to draft', 'mb-custom-post-type' ),
38 | 'item_scheduled' => __( 'Item scheduled', 'mb-custom-post-type' ),
39 | 'item_updated' => __( 'Item updated', 'mb-custom-post-type' ),
40 | 'name_admin_bar' => __( 'Admin bar name', 'mb-custom-post-type' ),
41 | ];
42 |
43 | add_filter( 'mbcpt_post_type', [ $this, 'register_strings' ], 10 );
44 | add_filter( 'mbcpt_post_type', [ $this, 'use_translations' ], 20 );
45 | }
46 |
47 | public function register_strings( array $settings ): array {
48 | if ( empty( $settings ) || ! is_array( $settings ) ) {
49 | return $settings;
50 | }
51 |
52 | $context = $this->get_context( $settings );
53 |
54 | // Register labels.
55 | foreach ( $this->keys as $key => $label ) {
56 | if ( ! empty( $settings['labels'][ $key ] ) ) {
57 | pll_register_string( 'label_' . $key, $settings['labels'][ $key ], $context );
58 | }
59 | }
60 |
61 | // Register description.
62 | if ( ! empty( $settings['description'] ) ) {
63 | pll_register_string( 'description', $settings['description'], $context );
64 | }
65 |
66 | return $settings;
67 | }
68 |
69 | public function use_translations( array $settings ): array {
70 | // Translate labels.
71 | foreach ( $this->keys as $key => $label ) {
72 | if ( ! empty( $settings['labels'][ $key ] ) ) {
73 | $settings['labels'][ $key ] = pll__( $settings['labels'][ $key ] );
74 | }
75 | }
76 |
77 | // Translate description.
78 | if ( ! empty( $settings['description'] ) ) {
79 | $settings['description'] = pll__( $settings['description'] );
80 | }
81 |
82 | return $settings;
83 | }
84 |
85 | private function get_context( array $settings ): string {
86 | // translators: %s is the name of the post type.
87 | return sprintf( __( 'Meta Box Post Type: %s', 'mb-custom-post-type' ), $settings['labels']['name'] ?? '' );
88 | }
89 | }
--------------------------------------------------------------------------------
/src/Integrations/Polylang/Taxonomy.php:
--------------------------------------------------------------------------------
1 | keys = [
9 | 'name' => __( 'General name for the taxonomy, usually plural', 'mb-custom-post-type' ),
10 | 'singular_name' => __( 'Name for one object of this taxonomy', 'mb-custom-post-type' ),
11 | 'menu_name' => __( 'Menu name', 'mb-custom-post-type' ),
12 | 'search_items' => __( 'Search items', 'mb-custom-post-type' ),
13 | 'popular_items' => __( 'Popular items', 'mb-custom-post-type' ),
14 | 'all_items' => __( 'All items', 'mb-custom-post-type' ),
15 | 'parent_item' => __( 'Parent item', 'mb-custom-post-type' ),
16 | 'parent_item_colon' => __( 'Parent item colon', 'mb-custom-post-type' ),
17 | 'edit_item' => __( 'Edit item', 'mb-custom-post-type' ),
18 | 'view_item' => __( 'View item', 'mb-custom-post-type' ),
19 | 'update_item' => __( 'Update item', 'mb-custom-post-type' ),
20 | 'add_new_item' => __( 'Add new item', 'mb-custom-post-type' ),
21 | 'new_item_name' => __( 'New item name', 'mb-custom-post-type' ),
22 | 'separate_items_with_commas' => __( 'Separate items with commas', 'mb-custom-post-type' ),
23 | 'add_or_remove_items' => __( 'Add or remove items', 'mb-custom-post-type' ),
24 | 'choose_from_most_used' => __( 'Choose from the most used items', 'mb-custom-post-type' ),
25 | 'not_found' => __( 'No items found', 'mb-custom-post-type' ),
26 | 'no_terms' => __( 'No items', 'mb-custom-post-type' ),
27 | 'filter_by_item' => __( 'Filter by item', 'mb-custom-post-type' ),
28 | 'items_list_navigation' => __( 'Items list navigation', 'mb-custom-post-type' ),
29 | 'items_list' => __( 'Items list', 'mb-custom-post-type' ),
30 | 'most_used' => __( 'Most used', 'mb-custom-post-type' ),
31 | 'back_to_items' => __( 'Back to items', 'mb-custom-post-type' ),
32 | 'item_link' => __( 'Item link', 'mb-custom-post-type' ),
33 | 'item_link_description' => __( 'Item link description', 'mb-custom-post-type' ),
34 | 'name_field_description' => __( 'Name field description', 'mb-custom-post-type' ),
35 | 'slug_field_description' => __( 'Slug field description', 'mb-custom-post-type' ),
36 | 'parent_field_description' => __( 'Parent field description', 'mb-custom-post-type' ),
37 | 'desc_field_description' => __( 'Description field description', 'mb-custom-post-type' ),
38 | ];
39 |
40 | add_filter( 'mbcpt_taxonomy', [ $this, 'register_strings' ], 10 );
41 | add_filter( 'mbcpt_taxonomy', [ $this, 'use_translations' ], 20 );
42 | }
43 |
44 | public function register_strings( array $settings ): array {
45 | if ( empty( $settings ) || ! is_array( $settings ) ) {
46 | return $settings;
47 | }
48 |
49 | $context = $this->get_context( $settings );
50 |
51 | // Register labels.
52 | foreach ( $this->keys as $key => $label ) {
53 | if ( ! empty( $settings['labels'][ $key ] ) ) {
54 | pll_register_string( 'label_' . $key, $settings['labels'][ $key ], $context );
55 | }
56 | }
57 |
58 | // Register description.
59 | if ( ! empty( $settings['description'] ) ) {
60 | pll_register_string( 'description', $settings['description'], $context );
61 | }
62 |
63 | return $settings;
64 | }
65 |
66 | public function use_translations( array $settings ): array {
67 | // Translate labels.
68 | foreach ( $this->keys as $key => $label ) {
69 | if ( ! empty( $settings['labels'][ $key ] ) ) {
70 | $settings['labels'][ $key ] = pll__( $settings['labels'][ $key ] );
71 | }
72 | }
73 |
74 | // Translate description.
75 | if ( ! empty( $settings['description'] ) ) {
76 | $settings['description'] = pll__( $settings['description'] );
77 | }
78 |
79 | return $settings;
80 | }
81 |
82 | private function get_context( array $settings ): string {
83 | // translators: %s is the name of the taxonomy.
84 | return sprintf( __( 'Meta Box Taxonomy: %s', 'mb-custom-post-type' ), $settings['labels']['name'] ?? '' );
85 | }
86 | }
--------------------------------------------------------------------------------
/src/Integrations/WPML/Manager.php:
--------------------------------------------------------------------------------
1 | keys = [
11 | 'name' => __( 'Label', 'mb-custom-post-type' ),
12 | 'singular_name' => __( 'Singular name', 'mb-custom-post-type' ),
13 | 'add_new' => __( 'Add new', 'mb-custom-post-type' ),
14 | 'add_new_item' => __( 'Add new item', 'mb-custom-post-type' ),
15 | 'edit_item' => __( 'Edit item', 'mb-custom-post-type' ),
16 | 'new_item' => __( 'New item', 'mb-custom-post-type' ),
17 | 'view_item' => __( 'View item', 'mb-custom-post-type' ),
18 | 'view_items' => __( 'View items', 'mb-custom-post-type' ),
19 | 'search_items' => __( 'Search items', 'mb-custom-post-type' ),
20 | 'not_found' => __( 'Not found', 'mb-custom-post-type' ),
21 | 'not_found_in_trash' => __( 'Not found in trash', 'mb-custom-post-type' ),
22 | 'parent_item_colon' => __( 'Parent item colon', 'mb-custom-post-type' ),
23 | 'all_items' => __( 'All items', 'mb-custom-post-type' ),
24 | 'archives' => __( 'Archives', 'mb-custom-post-type' ),
25 | 'attributes' => __( 'Attributes', 'mb-custom-post-type' ),
26 | 'insert_into_item' => __( 'Insert into item', 'mb-custom-post-type' ),
27 | 'uploaded_to_this_item' => __( 'Uploaded to this item', 'mb-custom-post-type' ),
28 | 'featured_image' => __( 'Featured image', 'mb-custom-post-type' ),
29 | 'set_featured_image' => __( 'Set featured image', 'mb-custom-post-type' ),
30 | 'remove_featured_image' => __( 'Remove featured image', 'mb-custom-post-type' ),
31 | 'use_featured_image' => __( 'Use as featured image', 'mb-custom-post-type' ),
32 | 'menu_name' => __( 'Menu name', 'mb-custom-post-type' ),
33 | 'filter_items_list' => __( 'Filter items list', 'mb-custom-post-type' ),
34 | 'filter_by_date' => __( 'Filter by date', 'mb-custom-post-type' ),
35 | 'items_list_navigation' => __( 'Items list navigation', 'mb-custom-post-type' ),
36 | 'items_list' => __( 'Items list', 'mb-custom-post-type' ),
37 | 'item_published' => __( 'Item published', 'mb-custom-post-type' ),
38 | 'item_published_privately' => __( 'Item published privately', 'mb-custom-post-type' ),
39 | 'item_reverted_to_draft' => __( 'Item reverted to draft', 'mb-custom-post-type' ),
40 | 'item_scheduled' => __( 'Item scheduled', 'mb-custom-post-type' ),
41 | 'item_updated' => __( 'Item updated', 'mb-custom-post-type' ),
42 | 'name_admin_bar' => __( 'Admin bar name', 'mb-custom-post-type' ),
43 | ];
44 |
45 | add_action( 'save_post_mb-post-type', [ $this, 'register_package' ], 20, 2 );
46 | add_filter( 'mbcpt_post_type', [ $this, 'use_translations' ], 10, 2 );
47 | add_action( 'deleted_post_mb-post-type', [ $this, 'delete_package' ], 10, 2 );
48 | }
49 |
50 | public function register_package( int $post_id, WP_Post $post ): void {
51 | $settings = json_decode( $post->post_content, true );
52 | if ( empty( $settings ) || ! is_array( $settings ) ) {
53 | return;
54 | }
55 |
56 | $package = $this->get_package( $post );
57 |
58 | do_action( 'wpml_start_string_package_registration', $package );
59 |
60 | $this->register_strings( $settings, $post, $package );
61 |
62 | do_action( 'wpml_delete_unused_package_strings', $package );
63 | }
64 |
65 | private function register_strings( array $settings, WP_Post $post, array $package ): void {
66 | // Register labels.
67 | foreach ( $this->keys as $key => $label ) {
68 | do_action(
69 | 'wpml_register_string',
70 | $settings['labels'][ $key ] ?? '',
71 | 'label_' . $key,
72 | $package,
73 | sprintf( '%s: %s', $post->post_title, $label ),
74 | LINE
75 | );
76 | }
77 |
78 | // Register description.
79 | do_action(
80 | 'wpml_register_string',
81 | $settings['description'] ?? '',
82 | 'description',
83 | $package,
84 | sprintf( '%s: Description', $post->post_title ),
85 | LINE
86 | );
87 | }
88 |
89 | public function use_translations( array $settings, WP_Post $post ): array {
90 | $package = $this->get_package( $post );
91 |
92 | // Translate labels.
93 | foreach ( $this->keys as $key => $label ) {
94 | if ( ! empty( $settings['labels'][ $key ] ) ) {
95 | $settings['labels'][ $key ] = apply_filters( 'wpml_translate_string', $settings['labels'][ $key ], 'label_' . $key, $package );
96 | }
97 | }
98 |
99 | // Translate description.
100 | if ( ! empty( $settings['description'] ) ) {
101 | $settings['description'] = apply_filters( 'wpml_translate_string', $settings['description'], 'description', $package );
102 | }
103 |
104 | return $settings;
105 | }
106 |
107 | private function get_package( WP_Post $post ): array {
108 | return [
109 | 'kind' => 'Meta Box: Post Type',
110 | 'name' => urldecode( $post->post_name ),
111 | 'title' => $post->post_title,
112 | 'edit_link' => get_edit_post_link( $post ),
113 | ];
114 | }
115 |
116 | public function delete_package( int $post_id, WP_Post $post ) {
117 | $package = $this->get_package( $post );
118 | do_action( 'wpml_delete_package', $package['name'], $package['kind'] );
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/Integrations/WPML/Taxonomy.php:
--------------------------------------------------------------------------------
1 | keys = [
11 | 'name' => __( 'General name for the taxonomy, usually plural', 'mb-custom-post-type' ),
12 | 'singular_name' => __( 'Name for one object of this taxonomy', 'mb-custom-post-type' ),
13 | 'menu_name' => __( 'Menu name', 'mb-custom-post-type' ),
14 | 'search_items' => __( 'Search items', 'mb-custom-post-type' ),
15 | 'popular_items' => __( 'Popular items', 'mb-custom-post-type' ),
16 | 'all_items' => __( 'All items', 'mb-custom-post-type' ),
17 | 'parent_item' => __( 'Parent item', 'mb-custom-post-type' ),
18 | 'parent_item_colon' => __( 'Parent item colon', 'mb-custom-post-type' ),
19 | 'edit_item' => __( 'Edit item', 'mb-custom-post-type' ),
20 | 'view_item' => __( 'View item', 'mb-custom-post-type' ),
21 | 'update_item' => __( 'Update item', 'mb-custom-post-type' ),
22 | 'add_new_item' => __( 'Add new item', 'mb-custom-post-type' ),
23 | 'new_item_name' => __( 'New item name', 'mb-custom-post-type' ),
24 | 'separate_items_with_commas' => __( 'Separate items with commas', 'mb-custom-post-type' ),
25 | 'add_or_remove_items' => __( 'Add or remove items', 'mb-custom-post-type' ),
26 | 'choose_from_most_used' => __( 'Choose from the most used items', 'mb-custom-post-type' ),
27 | 'not_found' => __( 'No items found', 'mb-custom-post-type' ),
28 | 'no_terms' => __( 'No items', 'mb-custom-post-type' ),
29 | 'filter_by_item' => __( 'Filter by item', 'mb-custom-post-type' ),
30 | 'items_list_navigation' => __( 'Items list navigation', 'mb-custom-post-type' ),
31 | 'items_list' => __( 'Items list', 'mb-custom-post-type' ),
32 | 'most_used' => __( 'Most used', 'mb-custom-post-type' ),
33 | 'back_to_items' => __( 'Back to items', 'mb-custom-post-type' ),
34 | 'item_link' => __( 'Item link', 'mb-custom-post-type' ),
35 | 'item_link_description' => __( 'Item link description', 'mb-custom-post-type' ),
36 | 'name_field_description' => __( 'Name field description', 'mb-custom-post-type' ),
37 | 'slug_field_description' => __( 'Slug field description', 'mb-custom-post-type' ),
38 | 'parent_field_description' => __( 'Parent field description', 'mb-custom-post-type' ),
39 | 'desc_field_description' => __( 'Description field description', 'mb-custom-post-type' ),
40 | ];
41 |
42 | add_action( 'save_post_mb-taxonomy', [ $this, 'register_package' ], 20, 2 );
43 | add_filter( 'mbcpt_taxonomy', [ $this, 'use_translations' ], 10, 2 );
44 | add_action( 'deleted_post_mb-taxonomy', [ $this, 'delete_package' ], 10, 2 );
45 | }
46 |
47 | public function register_package( int $post_id, WP_Post $post ): void {
48 | $settings = json_decode( $post->post_content, true );
49 | if ( empty( $settings ) || ! is_array( $settings ) ) {
50 | return;
51 | }
52 |
53 | $package = $this->get_package( $post );
54 |
55 | do_action( 'wpml_start_string_package_registration', $package );
56 |
57 | $this->register_strings( $settings, $post );
58 |
59 | do_action( 'wpml_delete_unused_package_strings', $package );
60 | }
61 |
62 | private function register_strings( array $settings, WP_Post $post ): void {
63 | $package = $this->get_package( $post );
64 |
65 | // Register labels.
66 | foreach ( $this->keys as $key => $label ) {
67 | do_action(
68 | 'wpml_register_string',
69 | $settings['labels'][ $key ] ?? '',
70 | 'label_' . $key,
71 | $package,
72 | sprintf( '%s: %s', $post->post_title, $label ),
73 | LINE
74 | );
75 | }
76 |
77 | // Register description.
78 | do_action(
79 | 'wpml_register_string',
80 | $settings['description'],
81 | 'description',
82 | $package,
83 | sprintf( '%s: Description', $post->post_title ),
84 | LINE
85 | );
86 | }
87 |
88 | public function use_translations( array $settings, WP_Post $post ): array {
89 | $package = $this->get_package( $post );
90 |
91 | // Translate labels.
92 | foreach ( $this->keys as $key => $label ) {
93 | if ( ! empty( $settings['labels'][ $key ] ) ) {
94 | $settings['labels'][ $key ] = apply_filters( 'wpml_translate_string', $settings['labels'][ $key ], 'label_' . $key, $package );
95 | }
96 | }
97 |
98 | // Translate description.
99 | if ( ! empty( $settings['description'] ) ) {
100 | $settings['description'] = apply_filters( 'wpml_translate_string', $settings['description'], 'description', $package );
101 | }
102 |
103 | return $settings;
104 | }
105 |
106 | private function get_package( WP_Post $post ): array {
107 | return [
108 | 'kind' => 'Meta Box: Taxonomy',
109 | 'name' => urldecode( $post->post_name ),
110 | 'title' => $post->post_title,
111 | 'edit_link' => get_edit_post_link( $post ),
112 | ];
113 | }
114 |
115 | public function delete_package( int $post_id, WP_Post $post ) {
116 | $package = $this->get_package( $post );
117 | do_action( 'wpml_delete_package', $package['name'], $package['kind'] );
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Migration.php:
--------------------------------------------------------------------------------
1 | __( 'Start...', 'mb-custom-post-type' ),
26 | 'migratingPostTypes' => __( 'Migrating post types...', 'mb-custom-post-type' ),
27 | 'migratingTaxonomies' => __( 'Migrating taxonomies...', 'mb-custom-post-type' ),
28 | 'done' => __( 'Done!', 'mb-custom-post-type' ),
29 | ] );
30 | }
31 |
32 | public function render() {
33 | ?>
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
49 |
50 | base !== 'edit' || ! $this->is_enabled_ordering( $screen->post_type ) ) {
20 | return;
21 | }
22 |
23 | // Add admin columns
24 | add_filter( "manage_{$screen->post_type}_posts_columns", [ $this, 'add_admin_order_column' ] );
25 | add_action( "manage_{$screen->post_type}_posts_custom_column", [ $this, 'show_admin_order_column' ] );
26 |
27 | // Set initial orders
28 | $this->set_initial_orders( $screen->post_type );
29 |
30 | // Enqueue assets
31 | wp_enqueue_style( 'order', MB_CPT_URL . 'assets/order.css', [], MB_CPT_VER );
32 | wp_enqueue_script( 'order', MB_CPT_URL . 'assets/order.js', [ 'jquery-ui-sortable' ], MB_CPT_VER, true );
33 | wp_localize_script( 'order', 'MBCPT', [ 'security' => wp_create_nonce( 'order' ) ] );
34 | }
35 |
36 | private function set_initial_orders( string $post_type ): void {
37 | global $wpdb;
38 |
39 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Error.
40 | $result = $wpdb->get_row( $wpdb->prepare(
41 | "
42 | SELECT COUNT(*) AS total, MAX(menu_order) AS max
43 | FROM $wpdb->posts
44 | WHERE post_type = %s
45 | ",
46 | $post_type
47 | ) );
48 |
49 | if ( $result->total == 0 || $result->total == $result->max ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
50 | return;
51 | }
52 |
53 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Error.
54 | $wpdb->query( 'SET @count = 0;' );
55 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Error.
56 | $wpdb->query( $wpdb->prepare(
57 | "UPDATE $wpdb->posts as pt JOIN (
58 | SELECT ID, (@count:=@count + 1) AS `rank`
59 | FROM $wpdb->posts
60 | WHERE post_type = %s
61 | ORDER BY menu_order ASC
62 | ) as pt2
63 | ON pt.id = pt2.id
64 | SET pt.menu_order = pt2.`rank`;",
65 | $post_type
66 | ) );
67 | }
68 |
69 | public function update_menu_order(): void {
70 | check_ajax_referer( 'order', 'security' );
71 |
72 | global $wpdb;
73 |
74 | if ( empty( $_POST['order'] ) ) {
75 | return;
76 | }
77 |
78 | parse_str( wp_unslash( $_POST['order'] ), $data );// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
79 | if ( ! is_array( $data ) ) {
80 | return;
81 | }
82 |
83 | $id_arr = [];
84 | foreach ( $data as $values ) {
85 | foreach ( $values as $id ) {
86 | $id_arr[] = (int) $id;
87 | }
88 | }
89 |
90 | $menu_order_arr = [];
91 | foreach ( $id_arr as $id ) {
92 | $menu_order_arr[] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT menu_order FROM $wpdb->posts WHERE ID = %d", $id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Error.
93 | }
94 |
95 | sort( $menu_order_arr );
96 |
97 | foreach ( $data as $values ) {
98 | foreach ( $values as $position => $id ) {
99 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Error.
100 | $wpdb->update(
101 | $wpdb->posts,
102 | [ 'menu_order' => $menu_order_arr[ $position ] ],
103 | [ 'ID' => $id ],
104 | [ '%d' ],
105 | [ '%d' ]
106 | );
107 | }
108 | }
109 | }
110 |
111 | public function set_orderby_menu_order( WP_Query $query ): void {
112 | $post_type = $query->get( 'post_type' );
113 |
114 | if ( ! $post_type || ! is_string( $post_type ) || ! $this->is_enabled_ordering( $post_type ) ) {
115 | return;
116 | }
117 |
118 | if ( $query->get( 'orderby' ) ) {
119 | return;
120 | }
121 |
122 | $query->set( 'orderby', 'menu_order' );
123 | if ( ! $query->get( 'order' ) ) {
124 | $query->set( 'order', 'ASC' );
125 | }
126 | }
127 |
128 | public function add_admin_order_column( array $columns ): array {
129 | return [ 'mbcpt_order' => '' ] + $columns;
130 | }
131 |
132 | public function show_admin_order_column( string $name ): void {
133 | if ( $name === 'mbcpt_order' ) {
134 | echo ' ';
135 | }
136 | }
137 |
138 | public function order_previous_post_where( string $where ): string {
139 | global $post;
140 |
141 | if ( ! empty( $post->post_type ) && $this->is_enabled_ordering( $post->post_type ) ) {
142 | $where = preg_replace( "/p.post_date < \'[0-9\-\s\:]+\'/i", "p.menu_order > '" . $post->menu_order . "'", $where );
143 | }
144 | return $where;
145 | }
146 |
147 | public function order_previous_post_sort( string $orderby ): string {
148 | global $post;
149 |
150 | if ( ! empty( $post->post_type ) && $this->is_enabled_ordering( $post->post_type ) ) {
151 | $orderby = 'ORDER BY p.menu_order ASC LIMIT 1';
152 | }
153 | return $orderby;
154 | }
155 |
156 | public function order_next_post_where( string $where ): string {
157 | global $post;
158 |
159 | if ( ! empty( $post->post_type ) && $this->is_enabled_ordering( $post->post_type ) ) {
160 | $where = preg_replace( "/p.post_date > \'[0-9\-\s\:]+\'/i", "p.menu_order < '" . $post->menu_order . "'", $where );
161 | }
162 | return $where;
163 | }
164 |
165 | public function order_next_post_sort( string $orderby ): string {
166 | global $post;
167 |
168 | if ( ! empty( $post->post_type ) && $this->is_enabled_ordering( $post->post_type ) ) {
169 | $orderby = 'ORDER BY p.menu_order DESC LIMIT 1';
170 | }
171 | return $orderby;
172 | }
173 |
174 | private function is_enabled_ordering( string $post_type ): bool {
175 | $post_type_object = get_post_type_object( $post_type );
176 | return ! empty( $post_type_object->order );
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/PostListTable.php:
--------------------------------------------------------------------------------
1 | id, [ 'edit-mb-post-type', 'edit-mb-taxonomy' ], true ) ) {
11 | return;
12 | }
13 |
14 | $this->output_css();
15 | $this->remove_excerpt();
16 | }
17 |
18 | private function output_css() {
19 | ?>
20 |
21 | _x( 'Post Types', 'Post Type General Name', 'mb-custom-post-type' ),
14 | 'singular_name' => _x( 'Post Type', 'Post Type Singular Name', 'mb-custom-post-type' ),
15 | 'menu_name' => __( 'Post Types', 'mb-custom-post-type' ),
16 | 'name_admin_bar' => __( 'Post Type', 'mb-custom-post-type' ),
17 | 'parent_item_colon' => __( 'Parent Post Type:', 'mb-custom-post-type' ),
18 | 'all_items' => __( 'Post Types', 'mb-custom-post-type' ),
19 | 'add_new_item' => __( 'Add New Post Type', 'mb-custom-post-type' ),
20 | 'add_new' => __( 'New Post Type', 'mb-custom-post-type' ),
21 | 'new_item' => __( 'New Post Type', 'mb-custom-post-type' ),
22 | 'edit_item' => __( 'Edit Post Type', 'mb-custom-post-type' ),
23 | 'update_item' => __( 'Update Post Type', 'mb-custom-post-type' ),
24 | 'view_item' => __( 'View Post Type', 'mb-custom-post-type' ),
25 | 'search_items' => __( 'Search Post Type', 'mb-custom-post-type' ),
26 | 'not_found' => __( 'Not found', 'mb-custom-post-type' ),
27 | 'not_found_in_trash' => __( 'Not found in Trash', 'mb-custom-post-type' ),
28 | ];
29 | $args = [
30 | 'label' => __( 'Post Types', 'mb-custom-post-type' ),
31 | 'labels' => $labels,
32 | 'supports' => false,
33 | 'public' => false,
34 | 'show_ui' => true,
35 | 'show_in_menu' => defined( 'RWMB_VER' ) ? 'meta-box' : null,
36 | 'menu_icon' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNjQuMzI4IDE0OS40NDEgNTMuNDcgNDIuNjYiIHdpZHRoPSI1My40NyIgaGVpZ2h0PSI0Mi42NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNIDIwNC42NjggMTc5LjM5MSBMIDIwNS40ODggMTYwLjU1MSBMIDIwNS4zMTggMTYwLjUyMSBMIDE5My44ODggMTkyLjEwMSBMIDE4OC4xNDggMTkyLjEwMSBMIDE3Ni43NzggMTYwLjY0MSBMIDE3Ni42MDggMTYwLjY2MSBMIDE3Ny40MjggMTc5LjM5MSBMIDE3Ny40MjggMTg2LjA5MSBMIDE4MS45OTggMTg2Ljk3MSBMIDE4MS45OTggMTkyLjEwMSBMIDE2NC4zMjggMTkyLjEwMSBMIDE2NC4zMjggMTg2Ljk3MSBMIDE2OC44NjggMTg2LjA5MSBMIDE2OC44NjggMTU1LjQ4MSBMIDE2NC4zMjggMTU0LjYwMSBMIDE2NC4zMjggMTQ5LjQ0MSBMIDE2OC44NjggMTQ5LjQ0MSBMIDE4MC4wMjggMTQ5LjQ0MSBMIDE5MC44OTggMTgwLjg4MSBMIDE5MS4wNzggMTgwLjg4MSBMIDIwMi4wMzggMTQ5LjQ0MSBMIDIxNy43OTggMTQ5LjQ0MSBMIDIxNy43OTggMTU0LjYwMSBMIDIxMy4yMjggMTU1LjQ4MSBMIDIxMy4yMjggMTg2LjA5MSBMIDIxNy43OTggMTg2Ljk3MSBMIDIxNy43OTggMTkyLjEwMSBMIDIwMC4xMjggMTkyLjEwMSBMIDIwMC4xMjggMTg2Ljk3MSBMIDIwNC42NjggMTg2LjA5MSBMIDIwNC42NjggMTc5LjM5MSBaIiBzdHlsZT0iZmlsbDogcmdiKDE1OCwgMTYzLCAxNjgpOyB3aGl0ZS1zcGFjZTogcHJlOyIvPgo8L3N2Zz4=',
37 | 'can_export' => true,
38 | 'rewrite' => false,
39 | 'query_var' => false,
40 | 'menu_position' => 200,
41 | 'map_meta_cap' => true,
42 | 'capabilities' => [
43 | // Meta capabilities.
44 | 'edit_post' => 'edit_mb_post_type',
45 | 'read_post' => 'read_mb_post_type',
46 | 'delete_post' => 'delete_mb_post_type',
47 |
48 | // Primitive capabilities used outside of map_meta_cap():
49 | 'edit_posts' => 'manage_options',
50 | 'edit_others_posts' => 'manage_options',
51 | 'publish_posts' => 'manage_options',
52 | 'read_private_posts' => 'manage_options',
53 |
54 | // Primitive capabilities used within map_meta_cap():
55 | 'read' => 'read',
56 | 'delete_posts' => 'manage_options',
57 | 'delete_private_posts' => 'manage_options',
58 | 'delete_published_posts' => 'manage_options',
59 | 'delete_others_posts' => 'manage_options',
60 | 'edit_private_posts' => 'manage_options',
61 | 'edit_published_posts' => 'manage_options',
62 | 'create_posts' => 'manage_options',
63 | ],
64 | ];
65 |
66 | register_post_type( 'mb-post-type', $args );
67 |
68 | // Get all registered custom post types.
69 | $post_types = $this->get_post_types();
70 |
71 | foreach ( $post_types as $post_type => $settings ) {
72 | // Menu position can be float value. In this case, WordPress will ignore the value. We'll need to fix it later.
73 | if ( ! empty( $settings['menu_position'] ) && ! is_int( $settings['menu_position'] ) ) {
74 | $this->menu_positions[ $post_type ] = $settings;
75 | }
76 | register_post_type( $post_type, $settings );
77 | }
78 |
79 | // Fix menu position if a post type is set with float value.
80 | if ( ! empty( $this->menu_positions ) ) {
81 | add_action( 'admin_menu', [ $this, 'fix_menu_positions' ] );
82 | }
83 | }
84 |
85 | public function get_post_types() {
86 | $post_types = [];
87 |
88 | $posts = get_posts( [
89 | 'posts_per_page' => -1,
90 | 'post_status' => 'publish',
91 | 'post_type' => 'mb-post-type',
92 | 'no_found_rows' => true,
93 | 'update_post_meta_cache' => false,
94 | 'update_post_term_cache' => false,
95 | ] );
96 |
97 | foreach ( $posts as $post ) {
98 | $settings = $this->get_post_type_settings( $post );
99 | if ( empty( $settings ) ) {
100 | continue;
101 | }
102 |
103 | // Allow WPML to translate post type labels.
104 | $settings = apply_filters( 'mbcpt_post_type', $settings, $post );
105 |
106 | $post_types[ $settings['slug'] ] = $settings;
107 | }
108 |
109 | return $post_types;
110 | }
111 |
112 | public function get_post_type_settings( WP_Post $post ) {
113 | // phpcs:ignore
114 | $settings = empty( $post->post_content ) || isset( $_GET['mbcpt-force'] ) ? $this->migrate_data( $post ) : json_decode( $post->post_content, true );
115 |
116 | $this->sanitize_labels( $settings );
117 | $this->parse_archive_slug( $settings );
118 |
119 | if ( $this->has_font_awesome( $settings ) ) {
120 | $this->add_font_awesome_hooks();
121 | }
122 | $this->parse_icon( $settings );
123 |
124 | $this->parse_supports( $settings );
125 | $this->parse_capabilities( $settings );
126 |
127 | return $settings;
128 | }
129 |
130 | private function migrate_data( WP_Post $post ) {
131 | $args = [ 'labels' => [] ];
132 | $post_meta = get_post_meta( $post->ID );
133 |
134 | foreach ( $post_meta as $key => $value ) {
135 | if ( 0 !== strpos( $key, 'label_' ) && 0 !== strpos( $key, 'args_' ) ) {
136 | continue;
137 | }
138 | $this->unarray( $value, $key, [ 'args_taxonomies', 'args_supports' ] );
139 | $this->normalize_checkbox( $value );
140 |
141 | if ( 0 === strpos( $key, 'label_' ) ) {
142 | $key = str_replace( 'label_', '', $key );
143 | $args['labels'][ $key ] = $value;
144 | } else {
145 | $key = str_replace( 'args_', '', $key );
146 | $args[ $key ] = $value;
147 | }
148 | }
149 | $this->change_key( $args, 'post_type', 'slug' );
150 |
151 | // Bypass new post types.
152 | if ( isset( $_GET['mbcpt-force'] ) && empty( $args['slug'] ) ) { // phpcs:ignore
153 | return json_decode( $post->post_content, true );
154 | }
155 |
156 | // Rewrite.
157 | $rewrite = [];
158 | if ( isset( $args['rewrite_slug'] ) ) {
159 | $rewrite['slug'] = $args['rewrite_slug'];
160 | }
161 | $rewrite['with_front'] = isset( $args['rewrite_no_front'] ) ? ! $args['rewrite_no_front'] : true;
162 | $args['rewrite'] = $rewrite;
163 | unset( $args['rewrite_slug'], $args['rewrite_no_front'] );
164 |
165 | wp_update_post( [
166 | 'ID' => $post->ID,
167 | 'post_content' => wp_json_encode( $args, JSON_UNESCAPED_UNICODE ),
168 | ] );
169 | return $args;
170 | }
171 |
172 | public function updated_message( $messages ) {
173 | $post = get_post();
174 | $post_type_object = get_post_type_object( $post->post_type );
175 | $label = ucfirst( $post_type_object->labels->singular_name );
176 | $label_lower = strtolower( $label );
177 | $label = ucfirst( $label_lower );
178 | $revision = (int) filter_input( INPUT_GET, 'revision' );
179 |
180 | $add_fields_link = '';
181 | $settings = json_decode( $post->post_content, true );
182 | if ( defined( 'MBB_VER' ) && is_array( $settings ) && ! empty( $settings['slug'] ) ) {
183 | $link = add_query_arg( [
184 | 'post_type' => 'meta-box',
185 | // Translators: %s - post type singular label.
186 | 'post_title' => sprintf( __( '%s Fields', 'mb-custom-post-type' ), $post->post_title ),
187 | 'settings[object_type]' => 'post',
188 | 'settings[post_types][]' => $settings['slug'],
189 | ], admin_url( 'post-new.php' ) );
190 | $add_fields_link = '' . __( 'Add custom fields to this post type', 'mb-custom-post-type' ) . ' → ';
191 | }
192 |
193 | $message = [
194 | 0 => '', // Unused. Messages start at index 1.
195 | // translators: %s - post type singular label.
196 | 1 => sprintf( __( '%s updated.', 'mb-custom-post-type' ), $label ),
197 | 2 => __( 'Custom field updated.', 'mb-custom-post-type' ),
198 | 3 => __( 'Custom field deleted.', 'mb-custom-post-type' ),
199 | // translators: %s - post type singular label.
200 | 4 => sprintf( __( '%s updated.', 'mb-custom-post-type' ), $label ),
201 | // translators: %1$s: post type singular label, %2$s - revision title.
202 | 5 => $revision ? sprintf( __( '%1$s restored to revision from %2$s.', 'mb-custom-post-type' ), $label, wp_post_revision_title( $revision, false ) ) : false,
203 | // translators: %1$s - post type singular label, %2$s - add fields link.
204 | 6 => sprintf( __( '%1$s published. %2$s', 'mb-custom-post-type' ), $label, $add_fields_link ),
205 | // translators: %s - post type singular label.
206 | 7 => sprintf( __( '%s saved.', 'mb-custom-post-type' ), $label ),
207 | // translators: %s - post type singular label.
208 | 8 => sprintf( __( '%s submitted.', 'mb-custom-post-type' ), $label ),
209 | // translators: %1$s: post type singular label, %2$s - revision title.
210 | 9 => sprintf( __( '%1$s scheduled for: %2$s .', 'mb-custom-post-type' ), $label, date_i18n( __( 'M j, Y @ G:i', 'mb-custom-post-type' ), strtotime( $post->post_date ) ) ),
211 | // translators: %s - post type singular label.
212 | 10 => sprintf( __( '%s draft updated.', 'mb-custom-post-type' ), $label ),
213 | ];
214 |
215 | // Get all post where where post_type = mb-post-type.
216 | $post_types = get_posts( [
217 | 'posts_per_page' => -1,
218 | 'post_status' => 'any',
219 | 'post_type' => 'mb-post-type',
220 | 'no_found_rows' => true,
221 | 'update_post_meta_cache' => false,
222 | 'update_post_term_cache' => false,
223 | ] );
224 | foreach ( $post_types as $post_type ) {
225 | $settings = $this->get_post_type_settings( $post_type );
226 | $slug = Arr::get( $settings, 'slug' );
227 |
228 | $messages[ $slug ] = $message;
229 |
230 | if ( ! Arr::get( $settings, 'publicly_queryable' ) ) {
231 | continue;
232 | }
233 |
234 | $permalink = get_permalink( $post->ID );
235 |
236 | // Translators: %s - post link, %s - view post text, %s - post type label.
237 | $view_link = sprintf( ' %s .', esc_url( $permalink ), sprintf( __( 'View %s', 'mb-custom-post-type' ), $label_lower ) );
238 | $messages[ $slug ][1] .= $view_link;
239 | $messages[ $slug ][6] .= $view_link;
240 | $messages[ $slug ][9] .= $view_link;
241 |
242 | $preview_permalink = add_query_arg( 'preview', 'true', $permalink );
243 | // Translators: %s - post link, %s - preview post text, %s - post type label.
244 | $preview_link = sprintf( ' %s .', esc_url( $preview_permalink ), sprintf( __( 'Preview %s', 'mb-custom-post-type' ), $label_lower ) );
245 | $messages[ $slug ][8] .= $preview_link;
246 | $messages[ $slug ][10] .= $preview_link;
247 | }
248 |
249 | $messages['mb-post-type'] = $message;
250 | return $messages;
251 | }
252 |
253 | public function bulk_updated_messages( $bulk_messages, $bulk_counts ) {
254 | $labels = [
255 | 'mb-post-type' => [
256 | 'singular' => __( 'post type', 'mb-custom-post-type' ),
257 | 'plural' => __( 'post types', 'mb-custom-post-type' ),
258 | ],
259 | ];
260 |
261 | // Get all post where where post_type = mb-post-type.
262 | $post_types = get_posts( [
263 | 'posts_per_page' => -1,
264 | 'post_status' => 'any',
265 | 'post_type' => 'mb-post-type',
266 | 'no_found_rows' => true,
267 | 'update_post_meta_cache' => false,
268 | 'update_post_term_cache' => false,
269 | ] );
270 | foreach ( $post_types as $post_type ) {
271 | $settings = $this->get_post_type_settings( $post_type );
272 | $slug = Arr::get( $settings, 'slug' );
273 |
274 | $singular = strtolower( Arr::get( $settings, 'labels.singular_name', '' ) );
275 | $plural = strtolower( Arr::get( $settings, 'labels.name', '' ) );
276 |
277 | $bulk_messages[ $slug ] = [
278 | // Translators: %1$s - number of items, %2$s - post type label in singular or plural forms.
279 | 'updated' => sprintf( __( '%1$s %2$s updated.', 'mb-custom-post-type' ), $bulk_counts['updated'], $bulk_counts['updated'] > 1 ? $plural : $singular ),
280 | // Translators: %1$s - number of items, %2$s - post type label in singular or plural forms.
281 | 'locked' => sprintf( __( '%1$s %2$s not updated, somebody is editing.', 'mb-custom-post-type' ), $bulk_counts['locked'], $bulk_counts['locked'] > 1 ? $plural : $singular ),
282 | // Translators: %1$s - number of items, %2$s - post type label in singular or plural forms.
283 | 'deleted' => sprintf( __( '%1$s %2$s permanently deleted.', 'mb-custom-post-type' ), $bulk_counts['deleted'], $bulk_counts['deleted'] > 1 ? $plural : $singular ),
284 | // Translators: %1$s - number of items, %2$s - post type label in singular or plural forms.
285 | 'trashed' => sprintf( __( '%1$s %2$s moved to the Trash.', 'mb-custom-post-type' ), $bulk_counts['trashed'], $bulk_counts['trashed'] > 1 ? $plural : $singular ),
286 | // Translators: %1$s - number of items, %2$s - post type label in singular or plural forms.
287 | 'untrashed' => sprintf( __( '%1$s %2$s restored from the Trash.', 'mb-custom-post-type' ), $bulk_counts['untrashed'], $bulk_counts['untrashed'] > 1 ? $plural : $singular ),
288 | ];
289 | }
290 |
291 | return $bulk_messages;
292 | }
293 |
294 | private function parse_archive_slug( &$settings ) {
295 | if ( empty( Arr::get( $settings, 'has_archive' ) ) || empty( Arr::get( $settings, 'archive_slug' ) ) ) {
296 | return;
297 | }
298 | Arr::set( $settings, 'has_archive', $settings['archive_slug'] );
299 | }
300 | private function parse_capabilities( &$settings ) {
301 | if ( 'custom' !== Arr::get( $settings, 'capability_type' ) ) {
302 | return;
303 | }
304 | $plural_name = sanitize_key( Arr::get( $settings, 'labels.name' ) );
305 | $singular_name = sanitize_key( Arr::get( $settings, 'labels.singular_name' ) );
306 | if ( $plural_name === $singular_name ) {
307 | $plural_name .= 's';
308 | }
309 |
310 | Arr::set( $settings, 'capability_type', [ $singular_name, $plural_name ] );
311 | Arr::set( $settings, 'map_meta_cap', true );
312 | }
313 |
314 | private function parse_supports( &$settings ) {
315 | if ( ! empty( Arr::get( $settings, 'supports' ) ) ) {
316 | return;
317 | }
318 | Arr::set( $settings, 'supports', false );
319 | }
320 |
321 | private function parse_icon( &$settings ) {
322 | $default = Arr::get( $settings, 'menu_icon', 'dashicons-admin-generic' );
323 |
324 | $icons = [
325 | 'dashicons' => Arr::get( $settings, 'icon' ),
326 | 'svg' => Arr::get( $settings, 'icon_svg' ),
327 | 'custom' => Arr::get( $settings, 'icon_custom' ),
328 | 'font_awesome' => Arr::get( $settings, 'font_awesome' ),
329 | ];
330 | $type = Arr::get( $settings, 'icon_type', 'dashicons' );
331 | $icon = Arr::get( $icons, $type ) ?: $default;
332 | if ( $type === 'font_awesome' ) {
333 | $icon = 'dashicons-' . $icon;
334 | }
335 | Arr::set( $settings, 'menu_icon', $icon );
336 |
337 | unset( $settings['icon_type'] );
338 | unset( $settings['icon'] );
339 | unset( $settings['icon_svg'] );
340 | unset( $settings['icon_custom'] );
341 | unset( $settings['font_awesome'] );
342 | }
343 |
344 | private function has_font_awesome( $settings ) {
345 | return Arr::get( $settings, 'icon_type', 'dashicons' ) === 'font_awesome';
346 | }
347 |
348 | private function add_font_awesome_hooks() {
349 | add_action( 'admin_init', [ $this, 'enqueue_font_awesome' ] );
350 | add_action( 'admin_menu', [ $this, 'filter_class_font_awesome' ] );
351 | add_action( 'adminmenu', [ $this, 'remove_filter_class_font_awesome' ] );
352 | }
353 |
354 | public function enqueue_font_awesome(): void {
355 | wp_enqueue_style( 'font-awesome', MB_CPT_URL . 'assets/fontawesome/css/all.min.css', [], '6.6.0' );
356 | wp_add_inline_style(
357 | 'font-awesome',
358 | '.fa:before, fas, .fa-solid:before, .fab:before, .fa-brand:before, .far:before, .fa-regular:before {
359 | font-size: 16px;
360 | font-family: inherit;
361 | font-weight: inherit;
362 | }'
363 | );
364 | }
365 |
366 | public function filter_class_font_awesome() {
367 | add_filter( 'sanitize_html_class', [ $this, 'sanitize_html_class_font_awesome' ], 10, 2 );
368 | }
369 |
370 | public function remove_filter_class_font_awesome() {
371 | remove_filter( 'sanitize_html_class', [ $this, 'sanitize_html_class_font_awesome' ] );
372 | }
373 |
374 | public function sanitize_html_class_font_awesome( $classname, $fallback ) {
375 | $fa_classnames = [ 'fa', 'fas', 'fa-solid', 'fab', 'fa-brand', 'far', 'fa-regular' ];
376 | foreach ( $fa_classnames as $fa_classname ) {
377 | if ( str_contains( $fallback, $fa_classname ) ) {
378 | return str_replace( 'dashicons-', '', $fallback );
379 | }
380 | }
381 | return $classname;
382 | }
383 |
384 | public function fix_menu_positions(): void {
385 | foreach ( $this->menu_positions as $post_type => $settings ) {
386 | $this->fix_menu_position_for_post_type( $post_type, $settings );
387 | }
388 | }
389 |
390 | private function fix_menu_position_for_post_type( string $post_type, array $settings ): void {
391 | global $menu;
392 |
393 | $post_type_url = $post_type === 'post' ? 'edit.php' : "edit.php?post_type=$post_type";
394 |
395 | // Find the post type menu.
396 | foreach ( $menu as $position => $args ) {
397 | // Only process the menu of the post type.
398 | if ( $args[2] !== $post_type_url ) {
399 | continue;
400 | }
401 |
402 | // Remove the existing menu.
403 | unset( $menu[ $position ] );
404 |
405 | // Avoid the same position by adding a small number.
406 | // Same technique as in add_menu_page().
407 | $collision_avoider = (int) base_convert( substr( md5( serialize( $settings ) ), -4 ), 16, 10 ) * 0.00001; // phpcs:ignore
408 | $position = (string) ( $settings['menu_position'] + $collision_avoider );
409 |
410 | // Re-add the menu with new position.
411 | $menu[ $position ] = $args; // phpcs:ignore
412 | }
413 | }
414 | }
415 |
--------------------------------------------------------------------------------
/src/Register.php:
--------------------------------------------------------------------------------
1 | register();
9 | add_filter( 'post_updated_messages', [ $this, 'updated_message' ] );
10 | add_filter( 'bulk_post_updated_messages', [ $this, 'bulk_updated_messages' ], 10, 2 );
11 | }
12 |
13 | abstract public function register();
14 |
15 | protected function unarray( &$value, $key, $ignore = [] ) {
16 | $value = 1 === count( $value ) && ! in_array( $key, $ignore, true ) ? $value[0] : $value;
17 | }
18 |
19 | protected function normalize_checkbox( &$value ) {
20 | if ( is_numeric( $value ) && in_array( $value, [ 0, 1 ] ) ) { // phpcs:ignore
21 | $value = 1 === (int) $value;
22 | }
23 | }
24 |
25 | protected function change_key( &$arr, $from, $to ) {
26 | if ( isset( $arr[ $from ] ) ) {
27 | $arr[ $to ] = $arr[ $from ];
28 | }
29 | unset( $arr[ $from ] );
30 | }
31 |
32 |
33 | protected function sanitize_labels( &$settings ): void {
34 | $labels = Arr::get( $settings, 'labels', [] );
35 | $labels = array_map( 'sanitize_text_field', $labels );
36 | $labels = array_map( function ( $text ) {
37 | return str_replace( [ '<', '>' ], '', $text );
38 | }, $labels );
39 | Arr::set( $settings, 'labels', $labels );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/TaxonomyRegister.php:
--------------------------------------------------------------------------------
1 | meta_box_cb ) {
27 | $data_response = $response->get_data();
28 | $data_response['visibility']['show_ui'] = false;
29 | $response->set_data( $data_response );
30 | }
31 |
32 | return $response;
33 | }
34 |
35 | public function register() {
36 | // Register post type of the plugin 'mb-taxonomy'.
37 | $labels = [
38 | 'name' => _x( 'Taxonomies', 'Taxonomy General Name', 'mb-custom-post-type' ),
39 | 'singular_name' => _x( 'Taxonomy', 'Taxonomy Singular Name', 'mb-custom-post-type' ),
40 | 'menu_name' => __( 'Taxonomies', 'mb-custom-post-type' ),
41 | 'name_admin_bar' => __( 'Taxonomy', 'mb-custom-post-type' ),
42 | 'parent_item_colon' => __( 'Parent Taxonomy:', 'mb-custom-post-type' ),
43 | 'all_items' => __( 'Taxonomies', 'mb-custom-post-type' ),
44 | 'add_new_item' => __( 'Add New Taxonomy', 'mb-custom-post-type' ),
45 | 'add_new' => __( 'New Taxonomy', 'mb-custom-post-type' ),
46 | 'new_item' => __( 'New Taxonomy', 'mb-custom-post-type' ),
47 | 'edit_item' => __( 'Edit Taxonomy', 'mb-custom-post-type' ),
48 | 'update_item' => __( 'Update Taxonomy', 'mb-custom-post-type' ),
49 | 'view_item' => __( 'View Taxonomy', 'mb-custom-post-type' ),
50 | 'search_items' => __( 'Search Taxonomy', 'mb-custom-post-type' ),
51 | 'not_found' => __( 'Not found', 'mb-custom-post-type' ),
52 | 'not_found_in_trash' => __( 'Not found in Trash', 'mb-custom-post-type' ),
53 | ];
54 | $args = [
55 | 'label' => __( 'Taxonomies', 'mb-custom-post-type' ),
56 | 'labels' => $labels,
57 | 'supports' => false,
58 | 'public' => false,
59 | 'show_ui' => true,
60 | 'show_in_menu' => defined( 'RWMB_VER' ) ? 'meta-box' : 'edit.php?post_type=mb-post-type',
61 | 'menu_icon' => 'dashicons-exerpt-view',
62 | 'can_export' => true,
63 | 'rewrite' => false,
64 | 'query_var' => false,
65 | 'map_meta_cap' => true,
66 | 'capabilities' => [
67 | // Meta capabilities.
68 | 'edit_post' => 'edit_mb_taxonomy',
69 | 'read_post' => 'read_mb_taxonomy',
70 | 'delete_post' => 'delete_mb_taxonomy',
71 |
72 | // Primitive capabilities used outside of map_meta_cap():
73 | 'edit_posts' => 'manage_options',
74 | 'edit_others_posts' => 'manage_options',
75 | 'publish_posts' => 'manage_options',
76 | 'read_private_posts' => 'manage_options',
77 |
78 | // Primitive capabilities used within map_meta_cap():
79 | 'read' => 'read',
80 | 'delete_posts' => 'manage_options',
81 | 'delete_private_posts' => 'manage_options',
82 | 'delete_published_posts' => 'manage_options',
83 | 'delete_others_posts' => 'manage_options',
84 | 'edit_private_posts' => 'manage_options',
85 | 'edit_published_posts' => 'manage_options',
86 | 'create_posts' => 'manage_options',
87 | ],
88 | ];
89 |
90 | register_post_type( 'mb-taxonomy', $args );
91 |
92 | // Get all registered custom taxonomies.
93 | $taxonomies = $this->get_taxonomies();
94 | foreach ( $taxonomies as $slug => $args ) {
95 | if ( isset( $args['meta_box_cb'] ) && false !== $args['meta_box_cb'] ) {
96 | unset( $args['meta_box_cb'] );
97 | }
98 | $types = empty( $args['types'] ) ? [] : $args['types'];
99 |
100 | register_taxonomy( $slug, $types, $args );
101 | }
102 | }
103 |
104 | public function get_taxonomies() {
105 | $taxonomies = [];
106 |
107 | $posts = get_posts( [
108 | 'posts_per_page' => -1,
109 | 'post_status' => 'publish',
110 | 'post_type' => 'mb-taxonomy',
111 | 'no_found_rows' => true,
112 | 'update_post_meta_cache' => false,
113 | 'update_post_term_cache' => false,
114 | ] );
115 |
116 | foreach ( $posts as $post ) {
117 | $data = $this->get_taxonomy_data( $post );
118 | if ( empty( $data ) ) {
119 | continue;
120 | }
121 |
122 | // Allow WPML to translate taxonomy labels.
123 | $data = apply_filters( 'mbcpt_taxonomy', $data, $post );
124 |
125 | $taxonomies[ $data['slug'] ] = $data;
126 | }
127 |
128 | return $taxonomies;
129 | }
130 |
131 | public function get_taxonomy_data( WP_Post $post ) {
132 | // phpcs:ignore
133 | $settings = empty( $post->post_content ) || isset( $_GET['mbcpt-force'] ) ? $this->migrate_data( $post ) : json_decode( $post->post_content, true );
134 |
135 | $this->sanitize_labels( $settings );
136 |
137 | return $settings;
138 | }
139 |
140 | public function migrate_data( WP_Post $post ) {
141 | $args = [ 'labels' => [] ];
142 | $post_meta = get_post_meta( $post->ID );
143 |
144 | foreach ( $post_meta as $key => $value ) {
145 | if ( 0 !== strpos( $key, 'label_' ) && 0 !== strpos( $key, 'args_' ) ) {
146 | continue;
147 | }
148 | $this->unarray( $value, $key );
149 | $this->normalize_checkbox( $value );
150 |
151 | if ( 0 === strpos( $key, 'label_' ) ) {
152 | $key = str_replace( 'label_', '', $key );
153 | $args['labels'][ $key ] = $value;
154 | } else {
155 | $key = str_replace( 'args_', '', $key );
156 | $args[ $key ] = $value;
157 | }
158 | }
159 | $this->change_key( $args, 'taxonomy', 'slug' );
160 | $this->change_key( $args, 'post_types', 'types' );
161 |
162 | // Bypass new post types.
163 | if ( isset( $_GET['mbcpt-force'] ) && empty( $args['slug'] ) ) { // phpcs:ignore
164 | return json_decode( $post->post_content, true );
165 | }
166 |
167 | // Rewrite.
168 | $rewrite = [];
169 | if ( isset( $args['rewrite_slug'] ) ) {
170 | $rewrite['slug'] = $args['rewrite_slug'];
171 | }
172 | $rewrite['with_front'] = isset( $args['rewrite_no_front'] ) ? ! $args['rewrite_no_front'] : true;
173 | $rewrite['hierarchical'] = isset( $args['rewrite_hierarchical'] ) ? (bool) $args['rewrite_hierarchical'] : false;
174 | $args['rewrite'] = $rewrite;
175 | unset( $args['rewrite_slug'], $args['rewrite_no_front'], $args['rewrite_hierarchical'] );
176 |
177 | wp_update_post( [
178 | 'ID' => $post->ID,
179 | 'post_content' => wp_json_encode( $args, JSON_UNESCAPED_UNICODE ),
180 | ] );
181 | return $args;
182 | }
183 |
184 | public function updated_message( $messages ) {
185 | $post = get_post();
186 | $revision = (int) filter_input( INPUT_GET, 'revision' );
187 |
188 | $add_fields_link = '';
189 | $settings = json_decode( $post->post_content, true );
190 | if ( defined( 'MBB_VER' ) && is_array( $settings ) && ! empty( $settings['slug'] ) ) {
191 | $link = add_query_arg( [
192 | 'post_type' => 'meta-box',
193 | // Translators: %s - taxonomy singular label.
194 | 'post_title' => sprintf( __( '%s Fields', 'mb-custom-post-type' ), $post->post_title ),
195 | 'settings[object_type]' => 'term',
196 | 'settings[taxonomies][]' => $settings['slug'],
197 | ], admin_url( 'post-new.php' ) );
198 | $add_fields_link = '' . __( 'Add custom fields to this taxonomy', 'mb-custom-post-type' ) . ' → ';
199 | }
200 |
201 | $messages['mb-taxonomy'] = [
202 | 0 => '', // Unused. Messages start at index 1.
203 | 1 => __( 'Taxonomy updated.', 'mb-custom-post-type' ),
204 | 2 => __( 'Custom field updated.', 'mb-custom-post-type' ),
205 | 3 => __( 'Custom field deleted.', 'mb-custom-post-type' ),
206 | 4 => __( 'Taxonomy updated.', 'mb-custom-post-type' ),
207 | // Translators: %s - date and time of the revision.
208 | 5 => $revision ? sprintf( __( 'Taxonomy restored to revision from %s.', 'mb-custom-post-type' ), wp_post_revision_title( $revision, false ) ) : false,
209 | // Translators: %s - add fields link.
210 | 6 => sprintf( __( 'Taxonomy published. %s', 'mb-custom-post-type' ), $add_fields_link ),
211 | 7 => __( 'Taxonomy saved.', 'mb-custom-post-type' ),
212 | 8 => __( 'Taxonomy submitted.', 'mb-custom-post-type' ),
213 | // Translators: %s - date and time of the revision.
214 | 9 => sprintf( __( 'Taxonomy scheduled for: %s .', 'mb-custom-post-type' ), date_i18n( __( 'M j, Y @ G:i', 'mb-custom-post-type' ), strtotime( $post->post_date ) ) ),
215 | 10 => __( 'Taxonomy draft updated.', 'mb-custom-post-type' ),
216 | ];
217 |
218 | return $messages;
219 | }
220 |
221 | public function bulk_updated_messages( $bulk_messages, $bulk_counts ) {
222 | $bulk_messages['mb-taxonomy'] = [
223 | // Translators: %s - taxonomy label in singular and plural forms.
224 | 'updated' => sprintf( _n( '%s taxonomy updated.', '%s taxonomies updated.', $bulk_counts['updated'], 'mb-custom-post-type' ), $bulk_counts['updated'] ),
225 | // Translators: %s - taxonomy label in singular and plural forms.
226 | 'locked' => sprintf( _n( '%s taxonomy not updated, somebody is editing.', '%s taxonomies not updated, somebody is editing.', $bulk_counts['locked'], 'mb-custom-post-type' ), $bulk_counts['locked'] ),
227 | // Translators: %s - taxonomy label in singular and plural forms.
228 | 'deleted' => sprintf( _n( '%s taxonomy permanently deleted.', '%s taxonomies permanently deleted.', $bulk_counts['deleted'], 'mb-custom-post-type' ), $bulk_counts['deleted'] ),
229 | // Translators: %s - taxonomy label in singular and plural forms.
230 | 'trashed' => sprintf( _n( '%s taxonomy moved to the Trash.', '%s taxonomies moved to the Trash.', $bulk_counts['trashed'], 'mb-custom-post-type' ), $bulk_counts['trashed'] ),
231 | // Translators: %s - taxonomy label in singular and plural forms.
232 | 'untrashed' => sprintf( _n( '%s taxonomy restored from the Trash.', '%s taxonomies restored from the Trash.', $bulk_counts['untrashed'], 'mb-custom-post-type' ), $bulk_counts['untrashed'] ),
233 | ];
234 |
235 | return $bulk_messages;
236 | }
237 |
238 | /**
239 | * Get post by slug.
240 | *
241 | * @param string $slug Post slug.
242 | * @param string $post_type Post type.
243 | * @return WP_Post|null
244 | */
245 | private function get_post_by_slug( $slug, $post_type ) {
246 | $posts = get_posts( [
247 | 'name' => $slug,
248 | 'post_type' => $post_type,
249 | 'post_status' => 'publish',
250 | 'numberposts' => 1,
251 | ] );
252 |
253 | return ! empty( $posts[0] ) ? $posts[0] : null;
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/Warning.php:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
Set it here.', 'mb-custom-post-type' ), admin_url( 'options-permalink.php' ) ) ); ?>
16 |
17 |
2 |
3 | mb-post-type
4 | mb-taxonomy
5 |
6 |
--------------------------------------------------------------------------------