Remodeling the base theme

0 %
Manoj Kumar
Freelance Web Developer
"One Is All, All Is One"
  • City:
    Chennai
  • Country:
    India
  • Age:
    27
Lifetime score board
Shows
Music
Games
Current Interest Levels
Web Development
Cricket
Chess
Fitness
Movies & TV Series
Cooking
Trading
Cybersecurity
  • Ambivert
  • Nocturnal
  • Gym Junkie
  • Tech Enthusiast
  • Teetotalism

Remodeling the base theme as a WordPress developer

August 27, 2022

At first glance, it might look like the site is entirely built on the Arter theme but there is more to it then just editing the placeholder content. I will list them based on certain features so that you have an idea or get a forward path while customizing your favorite theme. This post is suitable for beginners to advanced developers.

I have to give it to the authors, this was one of my favorite selections in WordPress history because I wanted exactly something like this. There are other themes that are similar as well. Having said that, the functionality is not extremely customizable from the developer’s point of view. Every theme is limited to its theme options and you need to think out of the box and have some experience coding to make it the way you want it.

Before you make changes to the theme, make sure you are Creating a child theme and copying all the files that you need to overwrite. I was working on some websites and some developers directly edited the theme. It could have been handy if they had saved the changes through a version control but they did not. It becomes complex to know which file was changed and also it is difficult to update the theme since all your changes would be gone and you will have to add those changes again.

There is an alternative way to do this: Using Code Snippets plugin but you need to make sure you are editing the theme using filters and actions.

The below changes involve WordPress classic/Elementor, shortcodes, admin AJAX, and the Gutenberg editor for the blocks so you get the idea for both the modern and classic WordPress development world. I am a fan of both although Gutenberg lets me develop in the ReactJS ecosystem.

Plan the UI that is similar to the WordPress theme that you use. I saw many sites where they would be using elements that are not matching with their current theme and that ruins the experience and of course not pleasing to the eyes.

1. The Scoreboard

Let’s consider you want to edit the default PHP value fields with your own data and design, then this section can be good for you.

Frontend
Backend

Earlier, there used to be a circular score that would indicate a number, which is proportionate to a goal. For example, 50 out of 100 but I have a lifetime score that is limitless. Since it is a scoreboard, something like a LED number would fit. I was exploring lots of free and premium fonts. Finally, I end up on a clean looking Digital 7 font. The even better part is that it is free for personal use.

If you are having trouble finding where this circular progress levels are available in your filesystem, you can Inspect Element in your browser, copy the class/ID and search in the folder through VScode. Another way is to install: Query Monitor which would list out the template files and parts that a page is using. It is very powerful not just for finding the template files.

Query Monitor

As you can see in the above tabs, it reveals almost all of the essential development needs for WordPress. For me, it is a must-have tool. I use it to debug values: Logging variables. I love it because I come from the console.log JS background.

Another issue with the values was that it was static and not dynamic. I rewrote the code so that it takes the count value of the WP options table value of each – Movies, Music & Games. In this way, I need not go and manually update the number of items.

So my edited code looks like this:

child-theme-folder/template-parts/info-bar.php
<!-- Lifetime score -->
<div class="lifetime-score p-30-15">
   <!-- skill -->
   <div class="art-lang-skills-item">
      <div class="score movies"><a href="<?php echo site_url('movies-and-tv-series'); ?>"> <?php echo get_option("moviesCount", "486"); ?></a></div>
      <!-- title -->
      <h6>Shows</h6>
   </div>
   <!-- skill end -->
   <!-- skill -->
   <div class="art-lang-skills-item">
      <div class="score music">
         <a href="<?php echo site_url('music-station'); ?>"> <?php echo get_option("musicCount", "177"); ?></a>        
      </div>
      <!-- title -->
      <h6>Music</h6>
   </div>
   <!-- skill end -->
   <!-- skill -->
   <div class="art-lang-skills-item">
      <div class="score games">
         <a href="<?php echo site_url('games-library'); ?>"> <?php echo get_option("gamesCount", "60"); ?></a>         
      </div>
      <!-- title -->
      <h6>Games</h6>
   </div>
   <!-- skill end -->
</div>
<!-- language skills end -->

The values might seem hardcoded but they are default values for get_option. These values are dynamically set in another file that you will learn through in another section of this post. You will also need a bit of CSS to make things look fine.

child-theme-folder/style.css

/* Include new fonts */

@font-face {
  font-family: "Digital 7";
  src: url(fonts/digital-7/digital-7.ttf);
}

/* Lifetime score */

.art-info-bar-frame .lifetime-score {
  display: flex;
  justify-content: space-between;
  padding: 1em 0 2em 0;
}

.lifetime-score .score {
  font-family: "Digital 7", sans-serif;
  font-size: 2em;
  text-align: center;
  color: var(--primary-color);
}

.lifetime-score .score a {
  color: var(--primary-color);
}

The final result is what you see on the left sidebar. The values are automatically updated when the JSON source is updated in section 6.

2. Tooltips

This section will help you include one of the many JS libraries that are available in Openbase using WordPress way of inclusion. 

The default tooltip from the theme was not very customizable or just that I was too lazy to look into the theme default code to find a workaround.

Tippy library came handy for all the tooltips. I just enqueued them through the functions.php file. You may want to look into wp_enqueue_script and wp_enqueue_style which will lead you to one of the ways of including external JS.

child-theme-folder/functions.php
/**
 * Embed custom scripts.
 */

function mk_child_scripts() {
	wp_enqueue_script( 'mk-scripts', get_stylesheet_directory_uri() . '/script.js', array('jquery'), '1.0.0', true);
	wp_enqueue_script( 'tippy-core', 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.5/umd/popper.min.js', null, '2.0.0', true);
	wp_enqueue_script( 'tippy', 'https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy-bundle.umd.js', null, '6.0.0', true);
}

add_action( 'wp_enqueue_scripts',  'mk_child_scripts');

I left the tooltip color to default as it looked good to match the theme instead of going full-blown green. It solved the majority of my issues since if you look at the recreational pages, I could just write 2-3 lines of code for all the dynamic comments for a recreational card instead of having a separate page for each of them that would be in my blocks JS file.

block-plugin-folder/src/frontend.js

filteredResults.forEach((music, index) {
   let thoughts = music.snippet.comments;

   musicdiv.innerHTML = `<div id="thoughts-${index}" class="thought"> </div>`;
   // All the additional element initialization
   tippy(`#thoughts-${index}`, {
      content: thoughts,
   });
});

The tooltip version on the music and games page looks better than the modal opener of the movies page.

3. Realtime light/dark mode

Let’s say there is a server-side functionality that you would want users to make use of on the client side. We will talk about the use of AJAX in plugins.

The theme had an option to set the light and dark mode from the theme options but that is dependent on the site admin’s preference and not the visitor’s preference. I always prefer the dark mode but there are certain users who don’t like that and I respect their view.

I chose to store the light-mode and dark-mode preferences in LocalStorage. I had the CSS files for both the light and dark versions in the Arter theme by default. So I combined those CSS properties in a file and added prefixed .dark-mode and .light-mode to the selectors.

For eg: 

.art-btn {
  color: #20202a;
  background: var(--primary-color);
}

/* Converted to prefixed selector */

.light-mode .art-btn {
  color: #20202a;
  background: var(--primary-color);
}

I manually had to change for almost 100+ lines of code because there was no common pattern as well as there were multiple nested properties. There might have been better ways to solve this but this is what I felt at that moment.

The functionality code part goes in my script.js file:

child-theme-folder/script.js

/* Implement Dark and light mode functionality */

const themeSwitcherEl = document.querySelector(".theme-switcher .icon");

// Set dark mode to be the default
if (localStorage.getItem("light-dark-mode") === null) {
  localStorage.setItem("light-dark-mode", "dark");
}

themeSwitcherEl.addEventListener("click", (e) => {
  dropSound.play();

  if (!themeSwitcherEl.classList.contains("clicked")) {
    themeSwitcherEl.classList.add("clicked", "disabled");
  }
  setTimeout(() => {
    if (themeSwitcherEl.classList.contains("light-mode-active")) {
      document.body.classList.remove("light-mode");
      document.body.classList.add("dark-mode");
      themeSwitcherEl.classList.remove("light-mode-active");
      localStorage.setItem("light-dark-mode", "dark");
    } else {
      document.body.classList.add("light-mode");
      document.body.classList.remove("dark-mode");
      themeSwitcherEl.classList.add("light-mode-active");
      localStorage.setItem("light-dark-mode", "light");
    }

    $.ajax({
      type: "POST",
      url: "/wp-admin/admin-ajax.php",
      datatype: "html",
      data: {
        action: "site_mode_update",
        site_mode: localStorage.getItem("light-dark-mode"),
      },
      success: function (response) {},
      error: function (response) {},
    });
  }, 500);
  setTimeout(() => {
    themeSwitcherEl.classList.remove("clicked", "disabled");
  }, 3000);
});

// Store preference in Browser's local storage

if (window.localStorage.getItem("light-dark-mode") == "light") {
  document.body.classList.add("light-mode");
  document.body.classList.remove("dark-mode");
  themeSwitcherEl.classList.add("light-mode-active");
} else {
  document.body.classList.remove("light-mode");
  document.body.classList.add("dark-mode");
}

Notice that I later added the AJAX code because I have different preloader images that I added using Advanced Custom Fields based on the light/dark mode and having two images with CSS hiding trick was causing layout shifts. So I need to manipulate the database option along with LocalStorage value.

child-theme-folder/functions.php

function update_site_mode() {
  $site_mode = $_POST['site_mode'];
  update_option('site_mode', $site_mode);
  die();
}

add_action('wp_ajax_nopriv_site_mode_update', 'update_site_mode');
add_action('wp_ajax_site_mode_update', 'update_site_mode');

Note that your action should match the add_action hook suffix. I cannot explain each line of code but it is all there in the documentation of AJAX in viewer facing side.

The below table row is updated every time you click on the theme switcher on the right side. It does not require any page reload or submission to do that.

Adminer: wp_options

4. Typewriter effect

There will be scenarios where it would be tiresome to even edit the smallest of the codes because it is integrated with a module. In that situation, you might want to replace it completely with your own solution so that you can expand it without limitations. It is also handy when you are going to use the same library for other sections of the site.

I really liked the default typewriting effect in the header section. But it was tied to an Elementor module and you cannot change the entire text but only certain portions of the text.

I had to import an external library: TypewriterJS that served the purpose for the header as well as some of the other sections like the next one.

It is common to use the load event handler but it could have been perfect if I had used the preloader hidden event as I have for the random interest levels.

child-theme-folder/script.js
window.addEventListener("load", () => {
   let headerTypeWriter = document.querySelector("#header-cyberpunk .art-code");

   if (headerTypeWriter) {
      let typewriter = new Typewriter(headerTypeWriter, {
         autoStart: true,
         wrapperClassName: "console-writer",
         delay: 50,
         onCreateTextNode: customNodeCreator,
         loop: true,
      });

      typewriter
         .pauseFor(1000)
         .typeString(
            `<code> WordPress, ReactJS & JS ecosystem </code>`
         )
         .start()
         .pauseFor(1000)
         .deleteAll();

      typewriter
         .pauseFor(1000)
         .typeString(
            `<manage> Micro to major tasks </manage>`
         )
         .start()
         .pauseFor(1000)
         .deleteAll();

      typewriter
         .pauseFor(1000)
         .typeString(
            `<consult> Adapt to ever changing tech stack </consult>`
         )
         .start()
         .pauseFor(1000)
         .deleteAll();

      typewriter
         .pauseFor(1000)
         .typeString(
            `<automate> Automate using multiple external tools </automate>`
         )
         .start()
         .pauseFor(1000)
         .deleteAll();
   }
});

5. Contact form

I felt that contact forms were being repeated so I wanted to try out a different style. Like using a code editor UI for contact. This was created as a classic WordPress plugin so that I can use the code as the shortcode anywhere on the site. I don’t think I would need a captcha either to filter out the spam.

I have integrated some libraries for it:

ShepherdJS – For guiding the user through the input fields. I was finding it hard for the custom code initially but somehow hacked into the documentation. Here is the essential part of the code:

plugin-folder/js/script.js

// Tour functions

let innerDetails = {
   name: "",
   email: "",
   subject: "",
   message: "",
};

let tour = new Shepherd.Tour({
   defaultStepOptions: {
      cancelIcon: {
         enabled: false,
      },
      classes: "about-tour",
      scrollTo: {
         behavior: "smooth",
         block: "center"
      },
      tourName: "About tour",
      keyboardNavigation: true,
      exitOnEsc: true,
   },
});

tour.addStep({
   title: "",
   text: `Hey! I was missing some code. I hope it is to be filled by you. Can you help me out? Let's start with your name.`,
   attachTo: {
      element: "#contact-js .get-name",
      on: "right",
   },
   buttons: [{
      action() {
         let nameEl = document.querySelector(".shepherd-target");
         let nameValue = nameEl.value;
         let lastModal = document.querySelector(".shepherd-element:last-child");

         if (nameValue !== "") {
            innerDetails.name = nameValue;

            guestValue = nameValue;
            consoleMessage("success", `${guestValue.split(" ")[0]} Logged in`);
            document.querySelector(".get-email").disabled = false;
            return this.next();
         } else {
            clicked++;
            if (clicked <= 3) {
               consoleMessage("error", "Name is empty");
            } else if (clicked > 3) {
               setTimeout(() => {
                  clicked = 0;
               }, 5000);
            }
            setTimeout(() => {
               lastModal
                  .querySelector(".shepherd-content")
                  .classList.remove("animate__animated", "animate__headShake");
            }, 500);
         }
      },
      text: "Next",
   }, ],
   id: "creating",
});

let clicked = 0;

tour.addStep({
   title: "",
   text: `I would need your address so that I can reply back.`,
   attachTo: {
      element: "#contact-js .get-email",
      on: "right",
   },
   buttons: [{
         action() {
            document.querySelector(".get-email").disabled = true;
            return this.back();
         },
         classes: "shepherd-button-secondary",
         text: "Back",
      },
      {
         action() {
            let emailEl = document.querySelector(".shepherd-target");
            let emailValue = emailEl.value;
            let lastModal = document.querySelector(".shepherd-element:last-child");

            if (emailValue !== "") {
               innerDetails.email = emailValue;
               document.querySelector(".get-subject").disabled = false;
               return this.next();
            } else {
               clicked++;
               if (clicked <= 3) {
                  consoleMessage("error", "Email is empty");
               } else if (clicked > 3) {
                  setTimeout(() => {
                     clicked = 0;
                  }, 5000);
               }
               setTimeout(() => {}, 500);
            }
         },
         text: "Next",
      },
   ],
   id: "creating",
});

tour.addStep({
   title: "",
   text: `Let me know why you want to contact me. Do you have a project in mind? Do you want to recommend me movies, music or games? Do you want to meet up? Contact me even if you just want to say Hi!`,
   attachTo: {
      element: "#contact-js .get-subject",
      on: "right",
   },
   buttons: [{
         action() {
            document.querySelector(".get-subject").disabled = true;
            return this.back();
         },
         classes: "shepherd-button-secondary",
         text: "Back",
      },
      {
         action() {
            let subjectEl = document.querySelector(".shepherd-target");
            let subjectValue = subjectEl.value;

            if (subjectValue !== "") {
               innerDetails.subject = subjectValue;
               document.querySelector(".get-message").disabled = false;
               return this.next();
            } else {
               clicked++;
               if (clicked <= 3) {
                  consoleMessage("error", "Subject is empty");
               } else if (clicked > 3) {
                  setTimeout(() => {
                     clicked = 0;
                  }, 5000);
               }
               setTimeout(() => {}, 500);
            }
         },
         text: "Next",
      },
   ],
   id: "creating",
});

tour.addStep({
   title: "",
   text: `Now the actual message that you want to share. I am always up for chat for interesting topics, projects, or even an engaging personal date at my place or your place.`,
   attachTo: {
      element: "#contact-js .get-message",
      on: "right",
   },
   buttons: [{
         action() {
            document.querySelector(".get-message").disabled = true;
            return this.back();
         },
         classes: "shepherd-button-secondary",
         text: "Back",
      },
      {
         action() {
            let messageEl = document.querySelector(".shepherd-target");
            let messageValue = messageEl.value;

            if (messageValue !== "") {
               innerDetails.message = messageValue;
               sendText();
               return this.next();
            } else {
               clicked++;

               if (clicked <= 3) {
                  consoleMessage("error", "Message field is empty");
               } else if (clicked > 3) {
                  setTimeout(() => {
                     clicked = 0;
                  }, 5000);
               }
               setTimeout(() => {}, 500);
            }
         },
         text: "Complete & Send",
      },
   ],
   id: "creating",
});

tour.start();

TypewriterJS – Of course, it is another use case for the typewriter effect which would have not been possible with the default theme options. For example, I could make use of it in creating success and error messages in the console.

plugin-folder/js/script.js

let typewriter = new Typewriter(app, {
   autoStart: true,
   wrapperClassName: "console-writer",
   delay: 5,
   onCreateTextNode: customNodeCreator,
});

const errorMessage = (errorText) => {
   typewriter
      .pauseFor(500)
      .typeString(`<div class="power-shell error">${errorText}</div>`)
      .start();
};

const successMessage = (successText) => {
   typewriter
      .pauseFor(500)
      .typeString(`<div class="power-shell success">${successText}</div>`)
      .start();
};

const consoleMessage = (status, text) => {
   if (status == "error") {
      errorMessage(text);
      keepScrolling();
      pathAddress();
   } else if (status == "success") {
      successMessage(text);
      keepScrolling();
      pathAddress();
   }
};

Telegram API – When the user completes the form, it goes into my Telegram ID. Thanks to some random YouTube tutorial that lead me to do that. Earlier, this code was visible on the client side, it is open for abuse since API keys are exposed. Then I learned about Admin AJAX as mentioned in the above section to implement it in a way that the API keys are hidden.

plugin-folder/js/script.js

const sendText = () => {
  let $ = jQuery;
  consoleMessage("success", "Processing your message...");
  $.ajax({
    type: "POST",
    url: "/wp-admin/admin-ajax.php",
    datatype: "html",
    data: {
      action: "manojsending_mail",
      name: innerDetails.name,
      message: innerDetails.message,
      subject: innerDetails.subject,
      contact: innerDetails.email,
    },
    success: function (response) {
      consoleMessage(
        "success",
        "I got your message through Telegram and Email. Please wait for my reply."
      );
    },
    error: function (response) {
      consoleMessage(
        "error",
        "I did not receive your message. Please use me@manojkumar.online."
      );
    },
  });
};

wp_mail – I was trying to use an external JS mail library when I found a very easy inbuilt function from WordPress. This function and wp_remote_request, which is a WordPress substitute for JS fetch/axios, helped me with sending messages to my mail and Telegram ID. You will have to make use of this: Telegram bots API to replace the dummy URL.

child-theme-folder/functions.php

function send_mail_contact() {
    $contactNumber = get_option('contact_visitor_number', 0);

    $to = "me@manojkumar.online";
    $subject = $_POST['subject'];
    $contact = $_POST['contact'];
    $name = $_POST['name'];
    $message = $_POST['message'];

    if( wp_mail($to, $subject, $message) ){
        $contactNumber++;
        update_option('contact_visitor_number', $contactNumber);

        $emailBody = "Visitor No: " . $contactNumber . "\n\nName: ". $name . "\n\nMessage:\n\n" . $message . "\n\nContact: " . $contact;

        // Send to mail
        wp_mail($to, $subject, $emailBody);

        // Send to Telegram
        $telegramRequest = wp_remote_request('https://telegramURL');
      
    } else {
        echo "mail not sent";
    }

    die(); // never forget to die() your AJAX reuqests

}

I could finally use this in a form of a shortcode. There is a lot more code but I just added the essential parts above and below.

plugin-folder-name/plugin-file.php

function code_editor_contact_shortcode() {
    ob_start();
    include_once( plugin_dir_path( __FILE__ ) . 'templates/code-editor-contact.php' );
    return ob_get_clean();
}

add_shortcode( "code-editor-contact", "code_editor_contact_shortcode" );

6. External API fetching

So far we have seen classic WordPress development but what if you want to use the power of ReactJS? You need to make use of the Gutenberg editor. I was reluctant to learn about ReactJS but when I found out that WordPress and ReactJS are merging, it was great to hear and I loved it because I was mostly using only JavaScript and a little bit of PHP.

Earlier I would be manually creating block plugins but now thanks to WordPress contributors we need not manually set it up. I use npx @wordpress/create-block block-plugin-name

As you can see my website has ways to show the movies I have watched, music that I have listened to, and games that I have played. But I have not compiled them manually from the WordPress post dashboard. The list compiled comes from YouTube, IMDB, and TGDB databases that I created. There may be ways to do that through WordPress plugins but I didn’t want to limit myself to a plugin.

Youtube playlist:

There is a separate API to fetch data from the YouTube playlists that you have compiled. You can refer to it here: YouTube playlist items

Executing the playlist item API after filling the part as ‘snippet’ and with the playlist ID from the URL will give you a set of JSON data. You will have to make use of the page tokens if the playlist is greater than 50 items. I copied the default JSON data and I inserted my own keys so that I can write my comments and other info on each video and alter the titles for some.

For example,

Youtube playlist API data

will be manually converted to this:

music-list.json sample
"snippet": {
   "publishedAt": "2022-08-26T15:16:46Z",
   "channelId": "UCE5sm_unYNjCeywj2S1EAwQ",
   "title": "Batman: The Animated Series (Main Title)",
   "description": "Provided to YouTube by Watertower Music\n\nBatman: The Animated Series (Main Title) · Danny Elfman\n\nBatman: The Animated Series, Vol. 1 (Original Soundtrack from the Warner Bros. Television Series)\n\n℗ 2014 Warner Bros. Entertainment\n\nWriter: Danny Elfman\n\nAuto-generated by YouTube.",
   "adjustedTitle": "Batman: The Animated Series (Main Title)",
   "comments": "Apt for a Batman into.",
   "endorphinMeter": 90,
   "type": "Theme",
   "language": "English",
   "thumbnails": {
      "default": {
         "url": "https://i.ytimg.com/vi/LLFtgDd16Wg/default.jpg",
         "width": 120,
         "height": 90
      },
      "medium": {
         "url": "https://i.ytimg.com/vi/LLFtgDd16Wg/mqdefault.jpg",
         "width": 320,
         "height": 180
      },
      "high": {
         "url": "https://i.ytimg.com/vi/LLFtgDd16Wg/hqdefault.jpg",
         "width": 480,
         "height": 360
      },
      "standard": {
         "url": "https://i.ytimg.com/vi/LLFtgDd16Wg/sddefault.jpg",
         "width": 640,
         "height": 480
      },
      "maxres": {
         "url": "https://i.ytimg.com/vi/LLFtgDd16Wg/maxresdefault.jpg",
         "width": 1280,
         "height": 720
      }
   },

Now, this is where I used Gutenberg to build my blocks. An example of my edit function would be like this:

block-plugin-folder/src/edit.js

import {
   useEffect
} from "@wordpress/element";

export default function Edit(props) {
   const {
      attributes,
      setAttributes
   } = props;
   useEffect(() => {
      let list = [];

      function fetchData() {
         fetch(
               "path/to/music-list.json"
            )
            .then((res) => res.json())
            .then((body) => {
               list = {
                  ...body
               };
               setAttributes({
                  list: list.items
               });
               return list;
            });
      }

      fetchData();
   }, []);
   return ( <
      p {
         ...useBlockProps()
      } > {
         __("Youtube Playlist – hello from the editor!", "youtube-playlist")
      } <
      /p>
   );
}

The above code would save JSON to the list attribute which you would have set in index.js. Now, you can use the render_callback in your plugin PHP file.

block-plugin-folder/block-plugin-file.php

function youtube_playlist_block_init() {
	register_block_type( __DIR__ . '/build', array('render_callback' => 'render_youtube_function') );
}
add_action( 'init', 'youtube_playlist_block_init' );

function render_youtube_function($attributes) {
	if(!is_admin()) {
		wp_enqueue_script("youtube-playlist-frontend", plugin_dir_url(__FILE__) . "/build/frontend.js" );
		wp_enqueue_style("youtube-playlist-frontend", plugin_dir_url(__FILE__) . "/build/frontend.css");
	}

	$musicCount = count($attributes['list']);

	update_option("musicCount", $musicCount);

	ob_start(); ?>
	// Rest of the HTML and return with ob_get_clean()
}

Since you now have the JSON data in the front end, no one is going to stop you from using it the way you want. Make sure you add the build commands for the custom files in your package.json configuration.

This logic would be similar to videos as well but just that data would vary and the classes and text would change.

IMDB & TGDB API

Now, IMDB API is not public and they provide commercial APIs which are costly. So I had to import my movies list into CSV and then convert them to JSON. Same logic as the music data that I would manually edit to add my own comments. I had to manually download images for each of the movies and upload them through WordPress. I know I should have used a web scraper. But I thought before spending too much time on it as I was new to scraping, I would just do it manually plus anyway since I was manually editing JSON, it was just normal. TGDB data has almost the same process.

The end:

There were some more trivial edits similar to this. For example, manually adding custom icons through JavaScript and PHP since the font selection is limited in the admin panel options. Another one is the randomly changing interest levels which are set using Math.random() which deviates between a range of 1-10 from the set value in the theme options panel. There are much more like that I did not list here.

I hope this serves as a good guide for you when you are trying to edit your WordPress site from a developer’s point of view instead of just relying on page builders, plugins, and theme options which are good for most purposes but still limited.

Posted in Design, WordPress
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x