Networking – how to solve the “find the utilization for a TCP sender that is continuously sending” problem

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class.

You have a TCP sender that is continuously sending a 1,096-byte segment. If a TCP receiver advertises a window size of 8,551 bytes, and with a link transmission rate 35 Mbps an end-to-end propagation delay of 33.3 ms, what is the utilization?

(Assume no errors, no processing or queueing delay, and ACKs transmit instantly. Also assume the sender will not transmit a non-full segment.)

Suppose they want the answer as a percentage rounded to one decimal place.

This one breaks down into a series of simple steps:

Step 1: Calculate how many segments the pipeline holds

formula: 
segments = floor(pipeline size / segment size)
8551 / 1096 = 7.802 
= 7 segments

Remember to round down! There are no partial segments in this pipeline.

Step 2: Calculate how long it takes to send one segment

This uses the L/R formula, which is:

formula: 
time to send one segment = length of segment / transmission rate

(Don’t forget to use the same units for both. I convert everything to bits for consistency.)

(1096 * 8) / (35 x 10^6) = 0.250514 time to send one segment

Step 3: Calculate the Round Trip Time (RTT)

formula: 
RTT = propagation delay * 2 
33.3 * 2 = 66.6 round trip time

Step 4: Calculate the delay per packet

formula: 
delay per packet = RTT + time to send one
66.6 + 0.250514 = 66.850514 delay per packet

Step 5: Calculate the utilization!

formula: 
utilization = (segments * time to send one) / delay per packet
(7 * 0.250514) / 66.850514 = 0.026316

Move the decimal to places to the right to get the percentage.

= 2.6% utilization

Networking – how to solve the “effective delay” problem

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class.

Given an effective delay of 99ms when network usage is 76%, what is the effective delay when network usage is 53%?

Suppose they want the answer in milliseconds rounded to 1 decimal place.

You might’ve seen a variation of this problem where the delay is given in ms and the network usage is 0%. If you’re given a delay with a network that’s in-use, you have to first figure out what the the delay would be if the network congestion was 0%.

Let’s call that value x and solve for it.

We will use the same D = Do/(1-U) formula we would use if we already knew the delay for a zero-congestion network, but we will put our 99 ms delay where D goes and our unknown “x” value for Do.

Do / (1 - U) = D
x / (1- 0.76) = 99ms 
x / (0.24) = 99

Multiply both sides by 0.24 to turn that into a value for x:

x = 0.24 * 99
x = 23.76 <-- network delay in ms when congestion is at 0%

Now that we know the delay in ms when congestion is at 0%, we can plug it into the D = Do/(1-U) formula again.

D = 23.76 / (1 - .53) 
D = 50.6 ms

It’s not a difficult problem but my class’s materials only covered the “easy” version so I wanted to demonstrate the “missing steps” for anyone else who might be wondering what to do when they give you the delay for a partially-congested network instead.

Networking – how to calculate the size of MSS for a “mythical set of protocols”

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class.

Imagine a mythical set of protocols with the following details:

  • Maximum Link-Layer data frame: 1,331 bytes
  • Network-Layer header size: 29 bytes
  • Transport-Layer header size: 34 bytes

What is the size, in bytes, of the MSS?

Answer:

1331 - 29 - 34 = 1268

You just subtract the header sizes from the data frame. Easy.

You probably could’ve guessed that, but if you’re in CS372 and you’re reading this then it might be because you weren’t 100% sure wanted to check your work but the class materials didn’t really cover it. And that’s the thing that irked me about this problem: the class just threw it at us as a surprise on the weekly (graded but open-book) exercises in week 6. Then you go a-Googlin’ and end up somewhere like chegg.com who wants $15 to sell you a solution! That’s just silly.

Now you know: calculating the maximum segment size is just a simple subtraction problem.

Networking – how to solve the “non-persistent vs persistent HTTP” problem

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class.

A client’s browser sends an HTTP request to a website. The website responds with a handshake and sets up a TCP connection. The connection setup takes 2.5 ms, including the RTT. The browser then sends the request for the website’s index file. The index file references 2 additional images, which are to be requested/downloaded by the client’s browser. How much longer would non-persistent HTTP take than persistent HTTP?

Suppose they want the answer in milliseconds rounded to 2 decimal places.

You can get fancy with writing out each and every request (and this is what they’ll show in the worksheet), but there’s a faster way, especially if the number of referenced objects is more than a few.

Step 1: Count the total number things you’re getting, ie: 1 index file + 3 images = 3. That’s your N value.

index.html + image1.jpg + image2.jpg = 3 things total

Step 2: For non-persistent HTTP, your answer will be the result of N x 2.

3 x 2 = 6 connections needed for non-persistent HTTP

(That’s because for each and every item you request, you also need to request a TCP connection.)

  1. TCP request
  2. index.htm request
  3. TCP request
  4. image1.jpg request
  5. TCP request
  6. image2.jpg request

Step 3: For persistent HTTP, do N+1

3 + 1 = 4 connections needed for persistent HTTP

(That’s because you only need to connect once, and then request all the things you want.)

  1. TCP request
  2. index.htm request
  3. image1.jpg request
  4. image2.jpg request

Step 4: Now you have:

6 connection requests for non-persistent HTTP
4 connection requests for persistent HTTP

Step 5: Find the difference.

6 - 4 = 2

Non-persistent HTTP needs 2 more connections than persistent.

Step 6: Multiply that difference by how long each connection takes to set up.

2 x 2.5 milliseconds = 5 milliseconds. 

Double-check your rounding and units! You might be asked to give this in seconds instead of milliseconds.

In summary

  • Sum the things (index file and images) to get N
  • Non-persistent HTTP = N x 2
  • Persistent HTTP = N + 1
  • Find the difference
  • Multiply the difference by how long a round trip takes

Networking – how to solve the “fileX and fileY” problem

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class. 

You’re given a link with a maximum transmission rate of 70.5 Mbps. Two computers, X and Y, want to transmit starting at the same time. Computer X sends File X (18 MiB) and computer Y sends File Y (130 KiB), both starting at time t = 0.

  • Statistical multiplexing is used, with details as follows
    • Packet Payload Size = 1000 Bytes
    • Packet Header Size = 24 Bytes (overhead)
  • Ignore Processing and Queueing delays
  • Assume partial packets (packets consisting of less than 1000 Bytes of data) are padded so that they are the same size as full packets.
  • Assume continuous alternating-packet transmission.
  • Computer X gets the transmission medium first.

At what time (t = ?) would File X finish transmitting?

Suppose they want the answer in seconds (hah – not milliseconds like usual!) and they want it rounded to two decimal places.

The “twist” of this problem is that File X and File Y take turns being sent in little chunks of 1000 bytes (plus 24 bytes of overhead).

First a piece of X goes, then a piece of Y, then back to X, etc.

Y is smaller so you run out of Y before you run out of X, but fortunately for us, this problem wants to know when X is done being sent. So really what we’re looking for is how long it takes to send both X and Y.

Here’s a drawing I made to visualize the problem (obviously, X and Y are both way more than just 10 and 5 packets in our real problem).

Diagram depicting X1, Y1, X2, Y2, X3, Y3, X4, Y4, X5, Y5, X6, X7, X8, X9, X10 interwoven with each other. Each block represents a packet.

Step 1: Figure out how many packets have to be sent, total

Our packet size (1000 bytes) and our overhead size (24 Bytes) are given in bytes, so I’m going to turn the two filesizes into Bytes instead of bits for this one. (We can always turn it into bits later if needed.)

File X = 18 MiB // turn to bytes by multiplying by 1024 twice
= 18 * 1024 * 1024 
= 18874368 bytes
File Y = 130 KiB // turn to bytes by multiplying by 1024 once 
= 130 * 1024
= 133120 bytes

Now X and Y are in the same units (bytes) and we can use that to figure out how many packets of 1000 bytes each one would be divided into.

File X = 18874368 Bytes / 1000 = 18874.368 packets

Our result isn’t a whole number. What should we do with the “partial packet”? Well, the problem tells us that any partial packets should be padded up to a full size. In practice, this just means we round up to the next nearest whole number. 18874.368 becomes 18875 packets for File X.

Now do the same for File Y:

File Y = 133120 Bytes / 1000 = 133.12 
Round up to get 134 packets for File Y

(Save these numbers for later, we’ll come back to them.)

Step 2: Calculate the time needed to send one packet plus its overhead

Add the packet size and the overhead size (both must be in the same units; here, that’s bytes but your variation of this problem may differ). Since transmission rate is in bits, let’s also multiply the packet size + overhead size by 8 to put it in bits, and multiply the transmission rate by 10^6 to put it in bits also. That looks like…

L/R, where R's units is what the outcome of this calculation will be 
= (1000 Bytes + 24 Bytes * 8) / (70.5 Mbps x 10^6)
= (8192) / (70.5 x 10^6)
= 0.0001161985816 seconds // same units as R (bits per second) 
= 0.11619858156 msec // multiply by 1000 to get milliseconds

We now know that it takes 0.11619858156 milliseconds to send one packet (and its overhead). Save this number for later.

Step 3: Calculate the time it takes to send all packets

This step is easy: we know how many packets we have between File X and File Y, and we know how long it takes to send one of them, so let’s figure out how long it takes to send all of them.

18875 packets + 134 packets = 19009 packets to send
19009 packets x 0.11619868156 msec = 2208.820738 milliseconds to send all 

Step 4: Convert to seconds and round to 2 decimal places to get the final answer

This variation of the problem wants the solution in seconds, so divide our result from Step 3 by 1000 to get 2.208820738 seconds and round it to two decimal places to get 2.21 seconds, which is when time at which File X will be done sending.

What if it wanted to know when the smaller file was done sending?

Well, let’s look at our diagram again. Here we can see that for every packet of Y sent, a packet of X has already been sent.

For every packet of Y that is sent, one from X precedes it. In other words, to send all 5 packets of Y we must also send 5 packets of X.

We know Y is made up of 134 packets, so that many packets of X will also be sent. That means for our final answer all we have to do is figure out how long it’ll take to send 134*2 packets.

134 * 2 = 268 // all of Y + same amount of X
268 packets x 0.11619868156 msec = 31.14124666 msec to send File Y

Convert that answer to seconds and there we have it: 0.03 seconds have elapsed when File Y (the smaller file) is done sending.

Networking – how to solve the “routers in a sequence” problem

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class. Yeah, I’m still cranky that this class makes no effort to teach this stuff. They just send you a-Googlin’. Welcome, Googlers…

Suppose there are 3 routers in sequence between Host A and Host B, all of which use store-and-forward routing. What is the total end-to-end delay for a packet originating from Host A with destination Host B, under the following conditions.

  • Each of the link transmission rates are 5.1 Mbps
  • The total distance from Host A to Host B along its path of transmission is 175.1 km
  • The speed of propagation through the transmission medium is is 2.7 x 108 m/s
  • The packet size is 2 KiB

Remember that you must also uplink from Host A to the first router. Suppose this one wants the answer in milliseconds rounded to 1 decimal place.

Step 1: Figure out the transmission delay for one link

The “twist” of this problem is that there are multiple transmission delays to account for. Every “hop” incurs a transmission delay.

We can use the L/R formula with the known packet size (2 KiB and known transmission rate (5.1 Mbps) to calculate the transmission delay for one node to another. Node here means host or router. Be sure to convert both into bits.

(2 KiB * 8192) / (5.1 Mbps * 10^6) 
= 16,384/5,100,000 = 0.00321254... seconds

Remember that the units R is in determines the outcome, so this result is in seconds. Multiply it by 1000 to turn it into milliseconds and save this result for later.

0.00321254 x 1000 = 3.21254901... milliseconds

Step 2: Figure out the entire path’s transmission delay

Recall that our three routers and two hosts basically look like this:

[A]–[1]–[2]–[3]–[B]

We know the transmission delay (the time it takes for a bit to be placed on the transmission medium) at one host or router, and we know how many routers there are total, so we can figure out how many “transmission delays” our bits will go through by multiplying the total number of routers plus one (to account for being placed on the transmission medium by A) by the transmission delay we calculated in step 1.

(R + 1) * 3.21254901  // R is the number of routers
= (3 + 1) * 3.21254901 // add 1 to account for initial link
= 4 * 3.21254901 
= 12.85019604 milliseconds

This number represents the total amount of transmission delay experienced by bits traversing this multi-router path.

Step 3: Figure out the propagation delay

Recall that our distance is 175.1 km and our speed of propagation is 2.7 x 108 m/s and the propagation delay formula is Tprop = d/s. Note that km has to be turned into meters and the result will be in seconds.

= (175.1 * 1000) / (2.7 x 108 m/s) = 0.0006485185... seconds 
0.0006485185... seconds x 1000 = 0.64851 milliseconds

Step 4: Add it all up

 3.21254901 + 12.85019604 + 0.64851 = 16.71125505 milliseconds

And that’s it, you’re done! Hope you found this helpful!

Networking – how to solve the “VOIP problem”

In this post: A detailed, step-by-step guide demonstrating the steps I use to solve a problem similar to one encountered in my CS 372 Intro to Networking class.

The “VOIP problem”

You’re given a diagram (you won’t need it) and some values in the form of…

  • Host A converts analog to digital at a = 40 Kbps
  • Link transmission rate R = 4.2 Mbps
  • Host A groups data into packets of length L = 51 bytes
  • Distance to travel d = 900 km
  • Propagation speed s = 2.5 x 108 m/s
  • Host A sends each packet to Host B as soon as it gathers a whole packet.
  • Host B converts back from digital to analog as soon as it receives a whole packet.

The question asks, How much time elapses from when the first bit starts to be created until the conversion back to analog begins?

Let’s say they want the answer in milliseconds rounded to two decimal places (note: this varies – sometimes the answer has to be in seconds, sometimes they want it rounded to one decimal place – double check your instance of the problem!)

Step 1: Figure out how long it takes to make a single packet

“When the first bit starts to be created” means “when this imaginary machine starts building the very first packet”.

We know how big a packet is: 50 bytes.

We know the rate at which it produces packets: 40 Kbps.

This is the classic L/R (length over rate) you’ve hopefully seen if you’re keeping up with the course materials. We can use L/R here, but we have to turn 51 bytes into bits (multiply it by 8) and we have to turn 40 Kbps into bps (multiply it by 1,000).

(51 * 8) / (40 * 1,000) = (408 / 40,000) = 0.0102 seconds

We get 0.0102 seconds because the units that R is in determines the outcome. R is in bits per second (bps), so that’s how we know the units we’re looking at here is seconds.

We need this in milliseconds, because we’re going to put everything in milliseconds so that we’re working in the units the question expects. Multiply the seconds answer by 1000 to get milliseconds, like so:

0.0102 x 1000 = 10.2 milliseconds

That’s the time it takes to make one single, complete, ready-to-transmit packet. Save this value for later, we will come back to it in step 4.

Step 2: Figure out the transmission delay

Recall from the course materials that transmission delay is the time it takes to place every bit onto the transmission medium. Transmission delay is not the time it takes to actually send it!

If you like car analogies, consider transmission delay the time it takes to get your car onto the highway. Your car is being placed on the transmission medium, and the time it takes to do that is the transmission delay. (Again, this is not the time it takes to travel the length of the highway! It’s the time it takes to get your car onto the highway.)

We will use the L/R formula again for this step. This time L is the length of the packet (51 bytes) and R is the transmission rate (4.2Mbps). We need both of them in bits, so multiply 51 (bytes) by 8 to get bits, and multiply 3.8 by 1,000,000 to get bps.

(51 * 8) / (3.8 x 1,000,000) = (408 / 3,800,000) = 0.00010737 seconds

Again, the units of R determine what units the result is in, which in this case is seconds (because we had bits per second for our R).

To turn it into milliseconds, multiply by 1000:

0.00010737 x 1000 = 0.10736842 milliseconds

So, what do we have so far? We have the time it takes to make one packet and the time it takes to transmit that packet, both in milliseconds. There is one missing piece: propagation delay.

Step 3: Figure out the propagation delay

Propagation delay is the time it takes a bit to travel the given distance (900 km) at the given speed (2.5 x 108 m/s). The formula for propagation delay is d/s (distance over speed).

To go back to our car analogy, the propagation delay is the time it takes the car to zoom down the highway to its destination.

Like L/R, the d/s result will be in the same units as the bottom number (so, seconds). However, before we proceed, note that the distance we were given is in kilometers and the speed is given in meters per second. We need to convert kilometers to meters when we do our calculation, so multiply that 900 by 1,000.

(900 km * 1,000 / 2.5 x 108) = 0.0036 seconds

Remember to convert that answer to milliseconds:

0.0036 x 1000 = 3.6 milliseconds

Step 4: Add the previous three answers together

Time to generate packet + time to place it on the transmission medium + time for it to propagate = our answer

10.2 msec + 0.10736842 msec + 3.6 msec = 13.90736842 milliseconds

Step 5: Round to the requested number of decimal places

Since this problem wants the answer rounded to two decimal places, 13.90736842 becomes 13.91

I hope this breakdown of the “VOIP problem” helps someone else!

I have to admit I’m a bit salty this class had us sit through all those lectures, read all those book chapters, and do all those worksheets and couldn’t once be bothered to expose us to even a watered-down or partial version of it before throwing it as us on a graded, timed quiz.

Fortunately, once you’ve mastered solving this problem, a lot of the other “math problems” that CS 372 throws at you in week 1 and week 2 will feel like variations on it.

Building a WordPress “product box” plugin, part 4 – adding the image

Part 1 – Initial idea, design, and first-pass implementation
Part 2 – Building the product box that gets displayed in posts
Part 3 – Refactoring the codebase into classes, views, and separate files
Part 4 – Adding image uploading, shared plugin options, and uninstallation

Day 9: Image upload and display

Next up: adding the Image Upload / Select feature to the plugin. When the user creates a new Product Box, they should be able to pick an image for it (not just paste in a URL, that’s too janky for this fancy plugin). Furthermore, the user also needs to be able to edit that image choice when editing an existing Product Box.

WordPress already has a media management page/popup.

That’s this thing:

Adding WordPress’s built-in Media Uploader to my plugin was quick and painless.

I just needed to hook into this feature from my plugin’s page. I used this tutorial as a starting point.

I didn’t use quite the same structure as this tutorial recommends. I wanted all of my form HTML in the same view files, not echoed in by a php function elsewhere in the codebase, but I did need to add an admin.js file and enqueue it as shown here, in my ‘init’ action. The tutorial left this step out so I’ve included it here:

add_action( 'init', function() {
    include dirname( __FILE__ ) . '/includes/class-amazin-product-box-admin-menu.php';
    include dirname( __FILE__ ) . '/includes/class-amazin-product-box-list-table.php';
    include dirname( __FILE__ ) . '/includes/class-form-handler.php';
    include dirname( __FILE__ ) . '/includes/amazin-product-box-functions.php';

    // WordPress image upload library
    wp_enqueue_media();
    $jsurl = plugin_dir_url(__FILE__) . 'admin.js';
    wp_enqueue_script('admin', $jsurl, array( 'jquery' ), 1.1, true);
   
    ...

The images are stored in my post content object by their ID (look at the very end – “215” is the image’s ID).

{"productName":"The Rainbow","productTagline":"It\'s got all the colors of the spectrum","productDescription":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce pulvinar, leo at cursus finibus, lectus massa tincidunt nulla, vitae lobortis lectus orci vitae nunc. Aenean a elit mollis, iaculis felis sed, consectetur velit. ","productUrl":"http://wow.com","productButtonText":"See the rainbow on Amazon.com","productImage":"215"}

I used wp_get_attachment_url() to turn the ID into the full path to the image. This is how it looks in the html that renders the actual product box in the post:

<img src="<?php echo wp_get_attachment_url( $content['productImage'] ) ?>"/>

Here’s my New Product Box page as the user first sees it:

And here’s my Edit Product Box page displaying an image the user already chose for a product box:

And here it is in a post!

Smaller images are centered; larger ones fill the frame as shown in this screenshot. Huge ones are stopped from overflowing the box and get scaled to fit. For pleasing results, square images are recommended.

This merged pull request shows the complete image uploading code (plus some bonus CSS styling and tagging to make it all look a bit better).

Day 10: Making the plugin’s settings page

I thought it’d be cool if all the product boxes shared a “headline” phrase, such as “We recommend” or “Our choice”. The user should edit in one place instead of on a per-box basis (though maybe a per-box override could be added, too, in the future).

WordPress calls this sort of thing an “option” and makes it very easy to set/get them. (This tutorial was helpful for identifying which hooks to use.)

Unlike everything else so far, the settings are not saved as posts. They’re saved as options in the _options table. Since all plugins (and WordPress itself) saves their plugins here, it’s important that the option have a unique name. Here’s my new option along with the string I gave it:

I wanted my plugin options to be adjustable from the plugin page (rather than a separate settings/options page elsewhere in the dashboard like some plugins do) so I simply added another form below the list table:

Here’s the form HTML (you can find this in view/product-box-list.php)

<form method="post" action="options.php">
  <?php settings_fields( 'amazin_product_box_options_group' ); ?>
    <h3>Product box settings</h3>
    <p>These settings are shared by all product boxes on your site.</p>
    <table>
      <tr valign="top">
        <th scope="row">
          <label for="amazin_product_box_option_headline">Product Box Headline</label>
         </th>
         <td>
           <input type="text" id="amazin_product_box_option_headline" name="amazin_product_box_option_headline" value="<?php echo get_option('amazin_product_box_option_headline'); ?>" />
            <br/>
             <span class="description"><?php _e('Examples: "We recommend", "Our pick", "A Sitename Favorite", etc.', 'apb' ); ?></span>
          </td>
        </tr>
    </table>
  <?php  submit_button(); ?>
</form>

I also had to add it as an option and register the settings in amazin-product-box-plugin.php:

add_action( 'init', function() {
  //other init code here
  add_option( 'amazin_product_box_option_headline', 'We recommend');
  register_setting( 'amazin_product_box_options_group', 'amazin_product_box_option_headline', 'amazin_product_box_callback' );

And finally, I modified the HTML that displays the actual in-post product box to echo out the setting value instead of a hard-coded string (also in amazin-product-box-plugin.php):

...
<p class="amazin-product-box-recommend-text"><?php echo get_option('amazin_product_box_option_headline'); ?></p>
...

Here’s the product box displaying the text “Our choice” (it used to say “We recommend”) after I made the change in the plugin’s settings.

The complete code that adds plugin-specific options can be found in this merged pull request.

[Bug fixes and improvements] Adding a welcome banner, hiding search, completing the shortcode display, and fixing “sort by name”

Before moving onto a new feature I took a moment to make a few more improvements. There is now a “Welcome” banner at the top of the plugin page, the shortcodes now display in full for each table row, I hid the search bar, and I fixed sorting by name (it used to not work at all because it was trying to run the query on ‘name’ instead of ‘post_title’).

You can see all of that code here in this commit.

Day 11: Adding the uninstall script

First, I read the official guide on deactivating vs. uninstalling plugins. I figured if there was one place I really didn’t want to just fly blind, this was it.

My uninstall script needed to do the following:

  • Delete the custom posts of type “amazin_product_box” from the _posts table
  • Delete the plugin’s setting (“amazin_product_box_option_headline”) from _options

I went the route of making a standalone .php script. The example from WordPress’s developer guide shows dropping a table but that’s a bit extreme for my use, I just needed to delete the posts of type “amazin_product_box”.

Here’s what I did (it’s basically the example from WP’s own site with my own plugin’s names for things instead):

uninstall.php

<?php
// if uninstall.php is not called by WordPress, die
if (!defined('WP_UNINSTALL_PLUGIN')) {
    die;
}

$option_name = 'amazin_product_box_option_headline';

delete_option($option_name);

// for site options in Multisite
delete_site_option($option_name);

// drop a custom database table
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->prefix}posts WHERE post_type='amazin_product_box'");
?>

With the uninstall script in place, I was able to deactivate and uninstall my plugin. I verified that the custom posts were gone from the _posts database and the headline option was gone from _options and from WordPress’s plugins directory on my server. Everything looked good.

The shortcode gets left behind in the posts, but I don’t think there’s anything I can do about that:

Finally, I reinstalled my plugin. None of the previously-created product boxes were present, which I expected since they were gone from the db, and I was able to create a brand new product box and stick it in a post.

Sweet – back in business.

Phew. I was dreading this part but the uninstall feature turned out to be the easiest, most “got it right on the first try” step of the whole project.

Here’s the commit that adds the uninstall script.

Day 12: Testing, bug fixes, and trying it out on a real website

The last thing I did was install the plugin on one of my actual sites and try it out as if I were an actual user.

I found a few bugs (including one that made the button not actually go anywhere, yikes) and had a few ideas for improving it, so I spent this last day on fixing those things.

  • [Bug fix] – Editing a product box with an existing image now retains the existing saved image
  • [Improvement] – Image upload help text added to Edit and New forms
  • [Improvement] – Added a “plugin action link” that goes directly to Amazin’ Product Box management page via the plugin’s entry in the plugins page
  • [Bug fix] – Button actually goes to the user’s link now
  • [Improvement] – Added a setting for whether the button should open the link in a new tab or stay in the same tab
  • [Improvement] – Product Box is now narrower than the post (I think it looks nicer that way)

You can see these fixes and improvements in this merge. I also did a small “security” pass in which I added code to prevent direct script access and removed an unused file. You can see that commit here.

Final thoughts

Here it is: my first WordPress plugin, just over 2 weeks after I started the project – looking exactly how I’d hoped.

I started blogging about product photography (and later smart home technology) about 5 years ago. I wanted to make my own custom plugins but I was just a baby programmer at the time and I got overwhelmed by terminology and just generally having no clue how to put something like it together.

Even now, after having worked professionally as a full-stack web dev for a few years and having completed most of a computer science degree, I was still a little intimidated by this project. Working in something new is always a bit uncomfortable at first.

Fortunately, this project wasn’t nearly as hard as I’d feared, and while I’m sure there’s something I got wrong with my first attempt at a plugin, I was only a little bit scared to deploy it on one of my live sites. :D

If you read this entire dev journal, thanks for following along – and if you see anything I could’ve done better, don’t hesitate to leave a comment or create a pull request on the project repo.

Resources

For future reference, here are some guides I found helpful for WP plugin development:

Building a WordPress “product box” plugin, part 3 – refactoring

Part 1 – Initial idea, design, and first-pass implementation
Part 2 – Building the product box that gets displayed in posts
Part 3 – Refactoring the codebase into classes, views, and separate files
Part 4 – Adding image uploading, shared plugin options, and uninstallation

Day 8: Refactoring the plugin’s management page with WP_List_Table

I did some soul-searching and realized that while I was happy with what I’d built so far in Part 1 and Part 2, it wasn’t very ‘standardized’. In other words, I had finally learned enough to want to scrap it all and start over.

I’m glad I did – and while it took some effort, it was well worth it in the end.

In which I keep what I’ve learned so far but throw out the code and start over.

Using WP List Table for a standard-looking admin table

I noticed many plugins (such as Contact Form 7 and TablePress ) seemed to be using a standardized table design that looks like WP’s own default for managing posts, with Edit and Delete links on hover, a checkbox for each row, and bulk actions.

I dug around a bit and found the WP_List_Table class. It’s not an official developer API, but it’s stable and widely used for the creation of admin tables in plugins.

Reading about WP List Table led me to a few example codebases (this one is my favorite), a detailed guide, and even a generator.

(I recommend reading all of the aforementioned links before attempting to work with WP List Table, even if through a generator or boilerplate code.)

I used the tareq.co generators to get started. (Definitely watch the video to see how all the parts fit together and how to name the files, using the generators alone is not enough to piece it together).

The generator did not include delete so I implemented that myself.

Implementing Delete (both single and bulk delete) in the WP List Table

I got stuck on implementing “Delete” (both the singular variety and the bulk kind) for a little while, mostly because I was trying to refresh the page myself after calling my delete method. Naively, I tried solutions like wp_redirect() right after calling my delete function, but that didn’t work – the page would reload but the stale, already-deleted row(s) were still in the table until the page was refreshed a second time or I would get a “headers already sent” error.

Ultimately, the solution was not to call any kind of redirect or refresh action but to simply call process_bulk_actions() before getting $items (in my code, that’s apb_get_all_product_boxes()) in the prepare_items() method in list-table.php.

The prepare_items() method (starting at about line 190) is called any time the list page is rendered, and the list page is automatically reloaded and rendered when clicking the Delete link or submitting the bulk action on the form. With that in mind, I didn’t need to reload the page manually – I just needed to make sure the deleted items were processed and removed from the db before retrieving the latest list of items.

For more on how I implemented Delete after using Tareq’s generated files as a starting point, see my comment on the generator’s repo here.

Here’s the merged pull request for this refactor.

And here’s how it all looks now:

Final thoughts on this refactor

It took a few days and it felt like backwards progress for a while, but I’m glad I did it – now my plugin looks more like “professional” plugins, both in-page and in the codebase, and I learned a lot in the process. I’d rather do it right than hold onto prototype-grade code.

Building it myself from scratch was still an important learning step for me. At this point, I feel like I might actually finish this thing because all that’s left is… implementing the image box! (And uninstalling, and finding and fixing bugs, and trying it out on a live site…)

Continue on to part 4!

Building a WordPress “product box” plugin, part 2 – shortcodes and frontend

Part 1 – Initial idea, design, and first-pass implementation
Part 2 – Building the product box that gets displayed in posts
Part 3 – Refactoring the codebase into classes, views, and separate files
Part 4 – Adding image uploading, shared plugin options, and uninstallation

Now it’s time to render the Product Box in a post and give it some attractive styling.

Day 6: Embedding the Product Box in a post using a Shortcode

With the management page built (or at least built enough), the next thing I wanted to do was see a product box in a post. I knew I wanted to do this with a WordPress shortcode, so I started by reading the Shortcode API and a Shortcode tutorial and used it to whip up the most basic Shortcode-driven bit of code in the history of WP plugins:

function amazin_product_box_shortcode( $atts ) {
    $a = shortcode_atts( array(
        'id' => 'id'
        ), $atts );
    return "Hello from Amazin Product Box for post ID " . $a['id'] . '!';
}
add_shortcode( 'amazin-product-box', 'amazin_product_box_shortcode' );

I have the new Gutenberg (blocks) version of WP so I used the “Shortcode” block to put my plugin’s Shortcode into the post:

And here it is in the published post! (It’s the last line, the one that begins with “Hello”.)

Sweet – it’s in the post!

The next step was to make it HTML, not just a string. I encountered a few problems along the way, so I documented them and their fixes here to help explain the code a bit better.

I now have a product box in a post displaying real data from the database.

The code for this step can be found here.

Tying up a few loose ends: classes on the HTML tags, Shortcodes in the management table

In preparation for styling the box, I added classes to all the HTML elements. The plugin will have some default styles, but will also expose the CSS for the user to edit to their liking (and save it somewhere).

Here are the classes in their own commit (yeah, it’s a tiny commit :) )

I also went back to the admin page and made it so Shortcodes display here without rendering as product boxes inside the table. That commit is here.

Day 7: Styling the Product Box

The next thing I did was add a stylesheet to the project, enqueue it, and add a bunch of styles to make the default Product Box look better. Here it is in my post, now styled:

Here is the commit that adds the box’s styling.

I also added the “We Recommend” text at the top, which is hard-coded for now but will ultimately be something the user can edit in the plugin’s settings (so that the user only has to change it in one place if they want it to say something different, like “Our Pick”, or hide it entirely) and fixed a problem with loading the posts for editing (it was the same “NULL fix” I used for displaying them in the post).

I considered letting the user modify the Amazin’ Product Box’s CSS via the plugin, but realized that 1. this would be a ton of work to develop, debug, and support, likely involving sanitizing the user’s CSS input and saving it to a file in the plugin directory, retrieving it, using it (or parts of it) based on the user’s settings and 2. this functionality is basically already present in WordPress’s own Appearance > Customize tab.

As a test, I restyled the button using WP’s custom CSS area. Anyone who wants to change the look of an Amazin’ Product Box should be able to safely do so via their theme or child theme or WP’s Customize using this same technique.

The photo area is just a placeholder for now, I’ll come back to that in a bit. For now, move on to Part 3 where I refactor everything I’ve built so far to be more “standardized”.