Category: Uncategorized

  • how to react

    Steps to create your first React Project

    • Start with design of UI
    • Break the design into components
    • Design state for the components

    As for our assignment, we already have the design of what we want to make, here it is

    Next is to find out the components from the design

    We can even break down the components more!!


    We will start working on the actual coding and state design tommorow!

    React Life Cycle

    The react component goes through 3 phases,

    • Mounting
    • Updating
    • Unmounting

    In mounting, the component is being added for the first time, the componentDidMount hook can be used here.

    The render is the most common method, it is compulsory to have in our component, it will render the component.

    componentDidMount is called as soon as the component is mounted and ready, we can use the useState here to update the component

    componentDidUpdate : This method is invoked when the componet updates,

    componentWillUnmount : This method is called just before the component will be unmounted, we can make use of it to cleanup or cancel any api calls.

    The catch here is that all these methods are only available in class components, what about functional components?

    we can use lifecycle methods in functional components with the help of the useEffect() hook.

    useEffect( () => {
    
    }, [])

    the useEffect has two arguments, a callback function and dependency array

    By using this we can create functionality similar to the class component lifecycle methods componentDidMount, componentDidUpdate and componentDidUnmount

    If we pass empty dependency array, it will only be called when component is mounted

    useEffect(() => {
    			console.log("componentDidMount");
    			
    		}, []);
    

    This will only be called once.

    If we want to useEffect for component update, in dependency array we can give the state variable on which change we want to run the hook.

    useEffect(() => {
    			console.log("componentDidUpdate");
    			
    		}, [ctr]);
    

    Event Handling

    Event handling in react is similar to that of html but the names of the event are camelcase

    So, onclick event becomes onClick in react

    <button onClick={handleIncrement}>Increment</button>

    I had created this input field and explicitly put the value of it, after doing that it stopped being editable, why? , if we set the value of the input we must also provide an onChange event handler which will update the input’s value.

    import React, { useState } from "react";
    
    function Task(props) {
      const [task, setTask] = useState(props.value);
      const inputOnChange = (e) => {
        setTask(e.target.value);
      };
      return (
        <>
          <input
            type="text"
            disabled={props.disabled}
            className="list-group-item"
            onChange={inputOnChange}
            value={task}
          ></input>
          <p></p>
        </>
      );
    }
    
    export default Task;
    
    

    Conditional Rendering

    We can use conditional to render the components or part of component conditionaly.

    For example, if input field has some error then only we will show the error message else it will be hidden, this can be achieved by code below

    function Task(props) {
      const [task, setTask] = useState({value: props.value});
    	const inputOnChange = (e) => {
    		let newTask = { ...task };
    		newTask.value = e.target.value;
    		if (newTask.value.length < 5) {
    			newTask.error = true;
    		}
        setTask(newTask);
      };
      return (
    	  <>
    		  
          <input
            type="text"
            disabled={props.disabled}
            className="list-group-item"
            onChange={inputOnChange}
            value={task.value}
    		  ></input>
    		{task.error &&  <p>Enter text more than 5 characters</p>  }
        </>
      );
    }
    
    

    Here is how the output will look:-

    [googleapps domain=”drive” dir=”file/d/1lNlNL7lzr4hPbsf9R5SBV2bJjLqAq_rG/preview” query=”” width=”640″ height=”480″ /]

    List of references refered today:

    • https://programmingwithmosh.com/javascript/react-lifecycle-methods/
    • https://react.dev/reference/react/useState
    • https://overreacted.io/a-complete-guide-to-useeffect/
    • https://babeljs.io/repl
    • https://react.dev/reference/react/StrictMode#fixing-bugs-found-by-double-rendering-in-development
    • https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

  • react and more..

    • Sanitization in JS
    • The Customizer JS API
    • React introduction

    How to sanitize text in js to be used in innerHTML?

    We can use wp-sanitize.js functions to sanitize the strings to be used to append to HTML elements.

    We can use stripTags function to remove tags from the string, and safely update the text of element.

    Example use case of this can be in our customizer.js where we are using postMessage transport for live preview. Instead of directly adding the user input to innerHTML, we can sanitize it first.

    Here is the output of stripTags


    Customizer JS API

    For each object of customizer in php, there is corresponding object in js, and we can use it to change the customizer.

    Here are some things I tried from console

    • Move a control to another section
    • activate/deactivate a section
    • Change order of sections (by changing priority)
    • To focus on a specific section or control
    wp.customize.control('blogname').section('colors'))

    This will move the blogname control into colors section.

    wp.customize.section('colors').deactivate()

    This will hide the colors section.

    wp.customize.section('colors').deactivate({duration : 1000})

    we can even set the time for the animation is shows when hiding the section.

    For reordering the sections, we can change their priority.

    wp.customize.panel('widgets').priority(10);

    This will bring the widgets panel at the top!

    When we use selective refresh, we get a pencil icon on that specific control and when we click it, the control is opened directly in the customizer.

    This is achieved using the focus method, we can try it by focusing a section or panel

    wp.customize.control('blogname').focus()

    React

    React is js library using which we can create user interfaces.

    Components are building blocks in react, that we can use to define a UI element, like an table component. Components can be exchanged data using props.

    Concepts in React :-

    Virtual DOM

    Virtual DOM is virtual representation of the actual DOM, it exists in the memory and used to update the actual DOM

    When we render our react component for the first time, Virtual DOM tree is generated, it is similar to the JSX structure we write for our component.

    When we make changes to the component state or props, re-render is triggered but react does not update the actual DOM, instead it created new virtual DOM and compared it with the old one.

    Then only the changes which are required are updated in the actual DOM

    JSX

    JSX is Javascript XML, it is extension of js that is used with react.

    Using JSX we can write HTML like code for our component

    Setup Dev Environment

    I am using vscode for coding, I have installed prettier, eslint extensions.

    For starting with react, we can use create-react-app that sets up the environment for us and we can get to building the components.

    npx create-react-app todo

    I created this very simple functional component

    function App() {
      return (
       &lt;h1&gt;Hello From Pratik!&lt;/h1&gt;
      );
    }
    
    export default App;
    
    

    References I refered Today:-

    • https://create-react-app.dev/
    • https://react.dev/learn/writing-markup-with-jsx
    • https://developer.wordpress.org/themes/customize-api/the-customizer-javascript-api/
    • https://wpseek.com/source/wp/latest/nav.html?wp-includes/js/wp-sanitize.js.source.html
  • day 50

    • Hashing passwords
    • Comments template
    • Compiler vs Transpiler
    • HMR

    How WordPress Stores Passwords?

    WordPress uses hashing and salting to save the passwords in the database, When we enter the password at auth it is again hashed and salted using the same algorithm used before and then compared with the one in DB

    WordPress provides function wp_hash_password(), it takes a string password as input and returns the hashed value.

    $my_pass = 'very-secret-password';
    wp_hash_password( $my_pass );
    

    WordPress has a function to check the password : wp_check_password()

    Loading the comments template

    The function comments_template(); can be used to load the comments template, but where to put the comments.php?

    We can put it anywhere, just have to give the file path as a parameter to the function, by default it will check for comments.php in the theme root.

     comments_template('/template/comments.php');

    Compiler Vs Transpiler

    A compiler is a tool used to translate high-level language code to low-level language code, like from cpp to machine code

    Transpiler translates the code from one high-level language to another high-level language

    So the Babel tool must be a transpiler, then why on the official site do they say it is a compiler and not a transpiler? 🤨


    Setting the Hot Module Replacement in WebPack dev server

    First, in the configuration, we have to enable the HMR or do we?

    The HMR is enabled by default for the latest versions of webpack dev server, but if it is not we can enable using this configuration

    const path = require('path');
    module.exports = {
    	entry: {
    		index: './src/index.js',
    	},
    	devServer: {
    		static: './dist',
    	    hot: true,
    	  },
    	output: {
    		path: path.resolve(__dirname, 'dist') ,
    		filename: '[name].js',
    	},
    
    	mode : 'development',
    	
    }
    
    

    then run the server using

     % npx webpack-dev-server --hot

    If it is still reloading and HMR is not working, you may need to put below code at the top of your entry point.

    if (module.hot) {
    	module.hot.accept();
    }
    

    References that I refered to learn today:-


    • https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
    • https://www.anthonysteele.co.uk/TheSingleReturnLaw.html
    • https://youmightnotneedjquery.com/
    • https://wordpress.tv/2017/06/22/alain-schlesser-demystifying-the-wordpress-bootstrap-process/

  • Ui, Ux and more

    • HTML Best practises
    • CSS Best Practises
    • BEM

    Semantic Elements

    Semantic elements are used so both browser and developer know their meaning.

    • <article>
    • <aside>
    • <footer>
    • <header>
    • <main>
    • <nav>
    • <section>

    Instead of using generic div containers, we should use these semantic elements for specific purposes

    Like the <header> element can be used for displaying header, it just makes the HTML more readable.

    Don’t place block-level element within inline elements

    &lt;span&gt;
        &lt;p&gt;
        &lt;/p&gt;
    &lt;span&gt;
    
    

    Here the <p> is block element which is inside <span> which is inline element, we should not put it this way.

    • The block-level element cannot be nested inside an inline element.
    • The inline element can be nested inside a block-level element.
    • The inline and the block-level element can be nested inside the block-level element.

    BEM (Block Element Modifier)

    This is a standard of naming the css classes, so they are more readable and maintinable.

    In BEM we name classes in this format

    block__element--modifier
    

    We should not use inline style, here are the reasons why.

    • The inline styles have the highest specificity which makes it very hard to change the styles.
    • The inline styles are sent from the HTML itsel, how’s the browser going to cache it.
    • The HTML file will be large if we add the style in the HTML itself.
    • Separation of concerns, HTML is for markup not styles, use .css file instead

    Specificity is something that we should always keep in mind, we should always avoid overly specific selector.


    Selective Refresh

    We can add selective refresh to our widget, when using postMessage, we are writing code in js also in php for rendering the specific control or option, but using selective refresh we can write that in our php code only.

    $wp_customize->selective_refresh->add_partial(
    			'display_navigation',
    			array(
    				'selector'        => '.navigation.post-navigation',
    				'render_callback' => function () {
    
    					if ( get_theme_mod( 'display_navigation' ) ) {
    						the_post_navigation();
    					}
    				},
    			)
    		);
    
    
  • Solving problems and more..

    • Conditional loading of customizer
    • Using relation in widget for querying posts

    The customizer panel, sections and controllers we have created should only be visible when we are customizing the page related to our custom CPTs rt-movie and rt-person.

    This is where the conditionals in wordpress can be used.

    We can check if the current page is a single post or an archive page for a specific post type and if it is then only load the customizer options for our theme. (Basically, early return if the page being customized is not related to a movie or person.)

    $is_movie_page  = is_singular( 'rt-movie' ) || is_post_type_archive( 'rt-movie' );
    		$is_person_page = is_singular( 'rt-person' ) || is_post_type_archive( 'rt-person' );
    
    		if ( ! ( $is_movie_page ||  $is_person_page ) ) {
    			return;
    		}
    

    But, this does not work inside the customize object, it does not have access to the global environment of WordPress site and this always results in false!

    What to do now? well, there is an argument called active callback that we can pass when registering our panel.

    We can use the conditionals there!

    // Add the Panel for Theme.
    		$wp_customize->add_panel(
    			'screen-time',
    			array(
    				'title'    => __( 'Screen Time', 'screen-time' ),
    				'priority' => 10,
    				'active_callback' => array($this, 'active_callback'),
    			)
    		);
    
    /**
    	 * To be active or not to be active.
    	 */
    	function active_callback()  {
    		$is_movie_page  = is_singular( 'rt-movie' ) || is_post_type_archive( 'rt-movie' );
    		$is_person_page = is_singular( 'rt-person' ) || is_post_type_archive( 'rt-person' );
    
    		return $is_movie_page || $is_person_page;
    		
    	}
    

    In our related movies and person widget, we are using a taxonomy as a relation, querying the posts according to that taxonomy to display in the widget.

    For this, we have to first get the name of the taxonomy, and the terms related to that taxonomy and create a tax query for that.

    $query = new \WP_Query(
    			array(
    				'post_type'      => 'rt-movie',
    				'posts_per_page' => $instance['count'],
    				'tax_query'      => array( //phpcs:ignore
    					array(
    						'taxonomy' => $instance['relation'],
    						'field'    => 'slug',
    						'terms'    => $relation_terms,
    					),
    				),
    			)
    		);
    

    The flow to get terms is simple, first, get the id of the current post, then get the terms of that post for the relation chosen in the widget setting.

    Use the terms in the query!

    $current_person_id = get_the_ID();
    		$terms             = get_the_terms( $current_person_id, $instance['relation'] );
    		if ( ! $terms ) {
    			return;
    		}
    		$relation_terms = array_map(
    			function( $term ) {
    				return $term->slug;
    
    			},
    			$terms
    		);
    
    
    

    Webpack

    Webpack is a javascript module that is used to package various modules and dependencies together so they can be loaded efficiently on browsers.

    So, using Webpack we can basically combine all the js files or any other asset files in a bundle.


    References visited today:

    • https://codex.wordpress.org/Theme_Customization_API
    • https://code.tutsplus.com/digging-into-the-theme-customizer-overview–wp-27158t
    • https://webpack.js.org/concepts/
    • http://ottopress.com/2012/how-to-leverage-the-theme-customizer-in-your-own-themes/
  • day 47

    Theme Security

    • Validation
    • Escaping
    • Sanitization
    • Nonces
    Validation

    Validation is checking the input against some rules, an example would be an email address. We want the email address entered to be a valid email, not something gibberish text or numbers.

    The client-side validation I think is for the user to let them know that the info they have input is not valid.

    From a security perspective, the client-side validation can be overridden easily by just sending the data programmatically instead of inputting from a form.

    That is when the server-side validation comes into the picture, server-side validation validates the data after receiving the request and before probably saving it to the database.


    Escaping

    Some characters are not printable because they have special meaning, the escaping is the process of printing those as it is.

    Example a post data stored in the database may have HTML markup, if we directly echo it then the browser will treat it as markup and render it.

    Escaping will change it so that the HTML is printed as it is like a normal string.

    • esc_sql(): Escapes data for use in a MySQL query.
    • ec_url(): Checks and cleans a URL.
    • esc_url_raw(): Sanitizes a URL for database or redirect usage.
    • esc_js(): Escapes text strings for echoing in JS.
    • esc_html(): Escaping for HTML blocks.
    • esc_attr(): Escaping for HTML attributes.
    • esc_textarea(): Escaping for textarea values.
    • esc_xml(): Escaping for XML blocks.

    Sanitizing

    Sanitizing basically means cleaning, sanitizing is used to remove illegal characters from the data.

    As a best practice we should never trust anything the user has entered and should sanitize it.

    Nonces

    Nonce stand for Number used Once

    So it is a number that is used only once, but the wordpress nonces are not true nonces in that sense, they can be used more than once.

    In wordpress, the nonces are valid for up to 24 hours.

    For validating the nonce, they use the tick value, which is used when creating the nonce value.

    $token = wp_get_session_token();
    		$i     = wp_nonce_tick( $action );
    
    		return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    
    $token = wp_get_session_token();
    		$i     = wp_nonce_tick( $action );
    
    		// Nonce generated 0-12 hours ago.
    		$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    		if ( hash_equals( $expected, $nonce ) ) {
    			return 1;
    		}
    
    		// Nonce generated 12-24 hours ago.
    		$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    		if ( hash_equals( $expected, $nonce ) ) {
    			return 2;
    		
    

    The DAY_IN_SECONDS value is used for nonce expiration, which is 86400 seconds.

    The formular used to calculate the tick number is:

     ceil( time() / ( $nonce_life / 2 ) );

    So, if current time (POSIX) is 1694632761 then

    $tick = ceil (1694632761 / (86400/2));

    which results in :- 39228


  • customizer


    The customizer in wordpress provides user feature to live preview the changes while editing the theme options.

    Say the user want to see how the new header image will look it can be viewed in the customizer, user does not need to publish the changes.

    The customizer has following objects

    • Panels
    • Sections
    • Settings
    • Controls

    Settings:-

    • We can add two types of settings, theme_mod and option

    Theme Mod:

    The theme mod are primarily used for storing customization data realated to the specific theme,

    Each theme can have its own theme mods, they are not shared across other themes.

    Theme Option:

    The options will be stored directly in the wp_options table, and will be applied regardless of which theme is activated.

    We should almost always use the theme mod over options settings for our theme.

    Control:-

    Controls are the objects that display the UI for a setting

    It is must that each control must be associated with a setting. That setting will be used to save the data entered by user in the control.

    Section:-

    Sections are containers for controls, we can use them to organize related controls of our theme.

    There are many in built customizer sections, here’s the list of core sections:-

    TitleIDPriority (Order)
    Site Title & Taglinetitle_tagline20
    Colorscolors40
    Header Imageheader_image60
    Background Imagebackground_image80
    Menus (Panel)nav_menus100
    Widgets (Panel)widgets110
    Static Front Pagestatic_front_page120
    default160
    Additional CSScustom_css200
    source

    Or, we can add our own sections if the settings we are adding do not match with the built in sections.

    Here is the combined code that create a panel, in that it adds a section and for that section it created a control in it.

    Things to remember:-

    • A Panel must have at least one section in it
    • A section must have at least one control in it
    • There must be a setting associated with a control
    function add_panel( $wp_customize ) {
    	$wp_customize->add_panel(
    		'pratiks-panel',
    		array(
    			'capability'     => 'edit_theme_options',
    			'theme_supports' => '',
    			'title'          => 'Pratiks Panel',
    			'description'    => 'Pratiks Panel',
    			'priority'       => 10,
    		)
    	);
    
    	$wp_customize->add_section(
    		'pratiks-section',
    		array(
    			'title' => 'Pratiks Section',
    			'panel' => 'pratiks-panel',
    		)
    	);
    
    	$wp_customize->add_setting(
    		'test',
    		array(
    			'type'       => 'theme_mod',
    			'capability' => 'edit_theme_options',
    
    		)
    	);
    	$wp_customize->add_control(
    		'test',
    		array(
    			'label'   => __( 'Thoughts' ),
    			'type'    => 'textarea',
    			'section' => 'pratiks-section',
    		)
    	);
    }
    
    
    
    add_action( 'customize_register', 'add_panel' );
    
    

  • day 45

    • Adding capability to user (not role)

    To add capability directly to user, we can use the add_cap() method in the WP_User class

    $user_id = get_current_user_id();
    $user = new WP_User( $user_id );
    $user->add_cap( 'edit_posts' );
    
    
    • Fields in usermeta table

    The usermeta table consists of key value pairs of meta information about the user. IT contains following fields

    • first name
    • last name
    • nickname
    • show_admin_bar_front
    • wp_capabilities
    • wp_user_levels
    The Header API

    The wordpress plugins and themes are identified by the header comment used in index.php and style.css respectively.

    It contains information like the version number, uri. We can make use of this info internally to perform update on the custom db tables we have created.

    For that we can get the version number of our plugin using:-

    require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
    
    $data = get_plugin_data(WP_CONTENT_DIR . '/plugins/movie-library/movie-library.php');
    var_dump($data['Version']);
    die();
    
    The wp HTTP API:

    The HTTP api wrapper methods can be used to send requests like GET, POST and HEAD. But what if we want to send request of another method?, for that we can use the wp_remote_request() method

    $response = wp_remote_request( 
    	'https://dev-bkp.local/wp-json/wp/v2/',
    	array( 'method' => 'PUT' ));
    

    Other methods in the API

    • wp_remote_get()
    • wp_remote_post()
    • wp_remote_head()
    Disabling wp cron

    The WP Cron system is not true cron, it needs a request to be made by the user so that the cron jobs are run. What if there are some critical jobs that needed to be run and we got no requests?, for that what we can do is disable the default behaviour and use of our system cron to run the cron jobs.

    We can use command to send request directly to the cron.php and make the cron jobs run instead of relying on external requests.

    For that first we have to define the constant to disable deafult cron behaviour

    define('DISABLE_WP_CRON', true);
    

    Then we can use crontab e and add new entry, we can use a tool like CURL to make request to the cron.php of our site

    */05 * * * * curl http://bkp-dev.local/wp-cron.php?doing_wp_cron

    This will run every 5 minutes.

  • Creating custom tables and more…

    Do we really need to create custom tables ?

    WordPress has it’s own schema covering most of the use cases for our plugins but sometimes, it is not the right solution to use the inbuilt tables only.

    Maybe we want to store data very unrelated to what the wordPress already does.

    Here is how to create custom table, for that we are going to use `dbDelta()` function that wordpress uses itself when upgrading.

    Let’s start by writing the SQL query

    global $wpdb;
    		$table_name = $wpdb->prefix . 'rt-person_meta';
    
    		$charset_collate = $wpdb->get_charset_collate();
    
    		$sql = "CREATE TABLE `$table_name` (
    			meta_id bigint(20) NOT NULL AUTO_INCREMENT,  
    			`rt-person_id` bigint(20) NOT NULL DEFAULT '0',  
    			meta_key varchar(255) DEFAULT NULL,  
    			meta_value longtext,  
    			PRIMARY KEY (meta_id),  
    			KEY `rt-person_id` (`rt-person_id`),  
    			KEY meta_key (meta_key)
    			) $charset_collate";
    

    Now, call the function to create this table

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    		dbDelta( $sql );
    

    As we are using this as a meta table we have to let the wpdb know about our meta table for that we can use

    $table        = 'rt-person_meta';
    		$wpdb->$table = $wpdb->prefix . 'rt-person_meta';
    

    Ok, done with this, now we can use the already existing meta functions, just pass the post_type as our own like rt-movie or something.

    It will then check for the entry of wp_rt-moviemeta in the wpdb, and if there is a table it will use that.

    Will the meta query work though?

    • I printed the query after creating the tables and it was somehow using the wp_postmeta table instead of our custom
    • I had specified the post_type but it was taking it as just post

    Why does it not work?

    I don’t really know for sure, but this could be the reason, so meta query uses the class WP_Meta_Query to generate the sql that will be added to the main sql to query the database accordingly.


    Adding new role

    We can have many use cases to add a new role to maange only specific post types and not all the others!, for that we can create new roles, custom capabilities and assign them,

    To create a new role we can use the function add_role()

    add_role(
    			'movie-manager',
    			__( 'Movie Manager', 'movie-library' ),
    			$capabilities
    		);
    

    We should add this in plugin activation hook, as it should be called only once, We can also remove the role using function remove_role()

    remove_role( 'movie-manager' );
    

    We will add this in the plugin deactivation hook.


  • invalidating transient cache..

    In our assignment task we are creating a widget that queries the db, gets the movies and shows them as a list.

    We are using transients to store the query results, so we don’t have to run another complex query on the db to get the data.

    The expiration is set to 4 hours currently. What if new movies get added to db or existing movies meta data is updated.

    We are using the meta query to get the posts by rating and release date.

    This is the query I am using

    $args   = array(
    			'post_type' => 'rt-movie',
    			// Using meta_key is necessary because the rating is stored as a meta data of movie post type.
    			'meta_key'  => 'rt-movie-meta-basic-rating', // phpcs:ignore WordPress.DB.SlowDBQuery
    			'orderby'   => 'meta_value_num',
    			'order'     => 'DESC',
    		);
    		$query  = new WP_Query( $args );
    		$movies = $query->posts;
    

    And using the code to save the results in the transients, but if it is already saved then don’t run the query just return the results.

    $movies = get_transient( 'mlb_top_rated_movies' );
    		if ( $movies ) {
    			return $movies;
    		}
    		....
    		$query  = new WP_Query( $args );
    		$movies = $query->posts;
    
    		set_transient( 'mlb_top_rated_movies', $movies, HOUR_IN_SECONDS * 4 );
    		return $movies;
    

    Now, what happens if new movie is added or existing movies rating get’s decreased. We should not store the stale data and fetch the fresh data.

    For that, I got this hook that we can use which will fire when post meta is updated.

    		add_action('update_postmeta', array($this, 'delete_transients_on_meta_update') , 10, 4);
    
    

    Now we can use this to check if the post meta is updated then we can just delete the transients instead of updating them with new data.

    Out code will then add the data in the transients again.

    /**
    	 * Delete transients on meta update.
    	 */
    	public function delete_transients_on_meta_update($meta_id, $object_id, $meta_key, $meta_value ){
    
    		if('rt-movie' !== get_post_type($object_id)){
    			return;
    		}
    
    		if('rt-movie-meta-basic-rating' === $meta_key){
    
    			delete_transient('mlb_top_rated_movies');
    		}
    
    	}
    
    
    

    Note : I am still working on how to also hook into save_post and invalidate the transients when new movie is added.


    User Roles and Capabilities

    There are below rules by default in wordpress

    • Super Admin
    • Administrator
    • Editor
    • Author
    • Contributor
    • Subscriber

    We can add our own roles too. For that we can use functions:-

    • add_role()
    • remove_role()

    We can assign capabilities to this roles or remove them using

    • add_cap()
    • remove_cap()