# Wordpress
# Define a new type post
define('TRAINING_VIDEO_POST_TYPE', 'training_video');
// Our custom post type function
function _register_training_video() {
    register_post_type( TRAINING_VIDEO_POST_TYPE,
    // CPT Options
        array(
            'labels' => array(
                'name' => __( 'All Training Videos' ),
                'singular_name' => __( 'Training Video' )
            ),
            'public' => true,
            'has_archive' => false,
            'menu_icon' => 'dashicons-format-video',
        )
    );
}
add_action( 'init', '_register_training_video' );
# Define a page template
Create file: /page-templates/home-v5.php
<?php
/**
 * Template Name: Home
 */
get_header('v5'); // {{themse}}/header-v5.php
# Define a REST API
At functions.php
function _process_user_logout() {
    auth_logout();
    wp_redirect(web_login_url());
}
add_action ('admin_post_user_logout', '_process_user_logout');
add_action ('admin_post_nopriv_user_logout', '_process_user_logout');
At HTML
<form id="logout-form" action="<?= admin_post_url() ?>"  method="post" >
    <input type="hidden" name="action" value="user_logout" />
    <a href="javascript:void(0)" onclick="document.getElementById('logout-form').submit();" class="logout-btn">
        <i class="fas fa-sign-out-alt"></i>
    </a>
</form><!-- end login form -->
Note:
- user_logoutis an action from the param post from the Form.
- nopriv_user_logoutis used for case User has no Session in- wp-adminpage.
# Create a new Table in Database & Display in Admin
# 1/ Set up Database
function setup_db_tables() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'feedback';
    if( $wpdb->get_var("SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
        //table not in database. Create new table
        $charset_collate = $wpdb->get_charset_collate();
    
        $sql = "CREATE TABLE $table_name (
                    id mediumint(9) NOT NULL AUTO_INCREMENT,
                    lang varchar(10) NOT NULL,
                    message text NOT NULL,
                    created_at DATETIME NOT NULL,
                    PRIMARY KEY id (id)
                ) $charset_collate;";
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
    }	
}
function _theme_setup() {
    setup_db_tables();
}
add_action( 'after_setup_theme', '_theme_setup' );
$wpdb is a global variable for manipulating Database.
# 2/ Define helpers
define("ADMIN_ITEM_PER_PAGE", 50);
// Numbered Pagination
if ( !function_exists( 'wpex_pagination' ) ) {
	function wpex_pagination($total_pages) {
		
		$prev_arrow = is_rtl() ? '→' : '←';
		$next_arrow = is_rtl() ? '←' : '→';
		
		$big = 999999999; // need an unlikely integer
		if( $total_pages > 1 )  {
            if( !$current_page = get_query_var('paged') )
                $current_page = 1;
                $format = '&paged=%#%';
            // }
			echo paginate_links(array(
				'base'			=> str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
				'format'		=> $format,
				'current'		=> isset($_GET['paged']) ? (int)$_GET['paged'] : 1,
				'total' 		=> $total_pages,
				'mid_size'		=> 3,
				'type' 			=> 'list',
				'prev_text'		=> $prev_arrow,
				'next_text'		=> $next_arrow,
			 ) );
		}
	}
}
if (!function_exists('wpex_pagination_info')) {
    function wpex_pagination_info( $wpdb, $table_name ) {
        $current_page = isset($_GET['paged']) ? (int)$_GET['paged'] : 1 ;
        $offset = ADMIN_ITEM_PER_PAGE * ($current_page - 1);
        /// query get items on page 
        $sql = "SELECT * FROM {$table_name} ORDER BY created_at DESC LIMIT ${offset}," . ADMIN_ITEM_PER_PAGE;
        $rows = $wpdb->get_results( $sql  , OBJECT );
        /// count total items -> total pages
        $total = $wpdb->get_row( "SELECT count(*) AS num FROM {$table_name}")->num; 
        $total_pages = ceil($total / ADMIN_ITEM_PER_PAGE);
        return [
            $current_page, $total_pages, $rows
        ];
    }
}
# 3/ Add to sidebar Menu
function feedback_admin_page() {
    get_template_part( 'template-parts/feedback-admin');
}
function menu_feedback_submissions() {
    add_menu_page( 'Submissions', 'Feedback', 'manage_options', 'submissions-page.php', 'feedback_admin_page', 'dashicons-cloud', 25 );
}
add_action( 'admin_menu', 'menu_feedback_submissions' );
At template-parts/feedback-admin
<style>
.message-content {
  white-space: pre-line;
}
</style>
<div class="wrap">
    <h1 class="wp-heading-inline">Feedback Submissions</h1>
    <table class="wp-list-table widefat fixed striped polls">
        <thead>
        <tr>
          <th width="10%" scope="col" class="manage-column column-title column-primary"><span>ID</span></th>
          <th scope="col" class="manage-column column-title column-primary"><span>Message</span></th>
          <th width="20%" scope="col" class="manage-column column-title column-primary"><span>Created At</span></th>
        </tr>
        </thead>
        <tbody>
        <?php
        $table_name = $wpdb->prefix . 'feedback';
        list($current_page, $total_pages, $rows) = wpex_pagination_info($wpdb, $table_name);
        foreach( $rows as $row ) : ?>
          <tr>
            <td><?php echo $row->id ?></td>
            <td class="message-content"><?php echo $row->message ?></td>
            <td><?php echo $row->created_at ?></td>
          </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
    <?= wpex_pagination($total_pages); ?>
</div>
# Enqueue assets
# Frontend
function sap_enqueue_assets () {
    $dsc_path = get_template_directory_uri() . '//assets-dsc/';
    // javascript
    wp_enqueue_script('dsc-main' ,  $dsc_path . 'js/main.js', [], SAP_VERSION, true); 
    // css
    wp_enqueue_style('sap-main-dsc', get_template_directory_uri() . '/built/css/dsc_main.css', [], SAP_VERSION, 'all'); 
    // ... 
}
add_action('wp_enqueue_scripts', 'sap_enqueue_assets');
# Backend
add_action( 'admin_enqueue_scripts', function($hook) {
    if($hook === 'theme-settings_page_frontend-users') {
        wp_enqueue_script( 'my_custom_script', web_assets_url('js/admin.js'), array(), ASSETS_VERSION);
    }
});
# Add footer hook
function sap_script_footer() {
    get_template_part('page-templates/preprocess-3rd-parties');
    get_template_part('page-templates/processing-3rd-parties');
}
$priority = 100;
add_action( 'wp_footer', 'sap_script_footer', $priority );
# Update url of Taxanomy
1/ In source code
Update slug on source code.
2/ Regenerate url
Admin CMS>General>Permalink> Click save to reloads
# Update new size image
1/ In source code
/// register
add_image_size( 'product-thumbnail', 300, 300 , true);
/// usage
get_the_post_thumbnail_url( $_product, 'product-thumbnail')
2/ Regenerate by WP CLI
php wp-cli.phar  media regenerate --image_size=product-thumbnail
# Get thumbnail size
$post_thumbnail_id = get_post_thumbnail_id( $post );
$thumbnail = image_downsize( $post_thumbnail_id );
Result
Array
(
    [0] => http://pohheng.local/wp-content/uploads/2019/06/HLOVEJOURNEY0487A-300x300.jpg
    [1] => 300
    [2] => 300
    [3] => 1
)
# Roles & Capabilities
Must Install Plugin: Capability Manager Enhanced
define('VIEW_FEEDBACK_CAPABILITY', 'view_feedback');
// add capability  
function add_theme_caps() {
    $role = get_role('author');
    $role->add_cap( VIEW_FEEDBACK_CAPABILITY ); 
}
add_action( 'admin_init', 'add_theme_caps');
// inject of capability
function menu_feedback_submissions() {
    add_menu_page( 'Submissions', 'Feedback', VIEW_FEEDBACK_CAPABILITY, 'submissions-page.php', 'feedback_admin_page', 'dashicons-cloud', 25 );
}
add_action( 'admin_menu', 'menu_feedback_submissions' );
# Add Custom Menu
1/ Source code
// define name 
define('MENU_FOOTER', 'vethoi-footer-menu');
// register to wordpress
function vethoi_theme_menus()
{
    register_nav_menus(array( // Using array to specify more menus if needed
        MENU_FOOTER => 'Footer Menu',
    ));
}
add_action('init', 'vethoi_theme_menus'); 
// usage
wp_nav_menu( [
    'theme_location' => MENU_FOOTER, 
    'container_class' => 'footer-link' 
]); 
# WP CLI
# Replace options url
php wp-cli.phar  search-replace 'http://kaplan.constructdigital.net' 'http://kaplan.local'
# Regenerate Image
php wp-cli.phar  media regenerate --image_size=product-thumbnail
# Execute file
php wp-cli.phar eval-file wp-content/themes/poh-heng/migration/product_final_price.php
# WPML
WPML Coding API (opens new window)
# web_url helper
 To provide a helper with lang param
// get_site_url is a function of wordpress
function web_url($path = '', $lang = null) {
    $new_path = get_site_url(null, $path);
    if ($lang === null) {
        $lang = ICL_LANGUAGE_CODE;
    }
    return apply_filters( 'wpml_permalink', $new_path, $lang); 
}
//  if `ICL_LANGUAGE_CODE` is `vi`
//  https://example.com/login?lang=vi 
web_url('login'); 
# List languages
// `ICL_LANGUAGE_CODE` - current language
<?php $languages = apply_filters( 'wpml_active_languages', NULL); ?>
<select>
    <?php foreach($languages as $code => $lang ): ?>
        <option <?= $code === ICL_LANGUAGE_CODE ? 'selected' : '' ?> 
            title="<?= $lang['translated_name'] ?>" 
            value="<?= $lang['code'] ?>"
        >
            <?= $lang['native_name'] ?>
        </option>
    <?php endforeach; ?>
</select><!-- end language switcher -->
# Global Option
function cl_acf_set_language() {
    return acf_get_setting('default_language');
}
function get_global_option($name) {
    add_filter('acf/settings/current_language', 'cl_acf_set_language', 100);
    $option = get_field($name, 'option');
    remove_filter('acf/settings/current_language', 'cl_acf_set_language', 100);
    return $option;
}
# ACF
# Get field
1/ Get field from current Post
$value = get_field('your_field');
2/ Get field from a specific Post by Post Id
$value = get_field('your_field', $post_id);
3/ Get field from Option Page
$value = get_field('your_field', 'options');
# Register Option Page
function register_acf_options_pages() {
    acf_add_options_page(array(
        'page_title' 	=> 'Theme General Settings',
        'menu_title'	=> 'Theme Settings',
        'menu_slug' 	=> 'theme-text-settings',
        'capability'	=> 'edit_posts',
        'redirect'		=> false
    ));
}
// Hook into acf initialization.
add_action('acf/init', 'register_acf_options_pages');
# Example
title           (Text)
article_list    (Repeater)
--- article (Article)
--- title   (Text)
--- desc    (Textarea)
--- image   (Image)
<?php
$sec_data = [
    'title' => get_field('article_title'),
    'article_list' => [],
];
$articles = get_field('article_list');
if (!empty($articles)) {
    foreach($articles as $item) {
        $article = $item['article'];
        unset($item['article']);
        $row_data = $item;
        $row_data['cta_text'] = $item['cta_text'];
        if(empty($row_data['title'])) {
            $row_data['title'] = $article->post_title;
        }
       
        if(empty($row_data['desc'])){
            $row_data['desc'] = $article->post_excerpt ? $article->post_excerpt : wp_trim_words($article->post_content);
        }
        if (empty($row_data['image'])) {
            $row_data['image'] = get_the_post_thumbnail_url($article);
        } else {
            $row_data['image'] = $row_data['image']['url'];
        }
        
        if (empty($row_data['cta_url'])) {
            $row_data['cta_url'] =  get_permalink($article); 
        }
     
        $sec_data['article_list'][] = $row_data;
    }
}
# Plugins
- Classic Editor Addon: This free "Classic Editor Addon" plugin makes sure that the new block editor cannot be accidentally activated and blocks the calls to additional styles from the `` (frontend)
- Duplicator`: Migrate and backup a copy of your WordPress files and database. Duplicate and move a site from one location to another quickly.
- Advanced Custom Fields PRO: Customize WordPress with powerful, professional and intuitive fields.
- WPML Multilingual CMS: Multilingual CMS
- Yoast SEO: SEO in general
- Smush: Reduce image file sizes, improve performance and boost your SEO using the free free WPMU DEV WordPress Smush API.
- WP Security Audit Log: Wholesome auditlog
- Capability Manager Enhanced: UI for enter Roles & Capabilities
← Magic methods Crunz →