Moving to a new community (part 2)

One laborious but also very worthy thing I can do to support Berg Builds is make time to review the work folks here create with their DoOO. This is especially true of student work, I feel. Even though the web is, in ways, a very public medium, there is the needle-in-the-haystack nature of the web that makes it really hard to break out and to get noticed. Taking the time to recognize the effort, creativity, and skill that folks apply to their domains is gratifying, energizing, and fun for all of us. From a teaching and learning perspective, it’s also a critically important way to underscore how iterative and reflective practices help us remember, synthesize, find insight, and make connections across disciplines and among courses. In short, spending time with the thoughts and ideas happening on my campus is part of my work as an educator, and I really must find the time and attention necessary to do this well. But it’s a challenge.

When I first started building a community hub here, our Domains community was small – perhaps 200-300 websites. Now, it’s upwards of 1,800 with an increasing share of students managing two, three, or more WordPress sites on their domains. My initial community site didn’t paginate, didn’t have a keyword search feature, and didn’t prune sites or fix broken links.

But Reclaim Hosting, and specifically Taylor Jadin, offer a community hub template built with WordPress and some choice plugins that can potentially get my community site up and running again. I’m really excited about this.

But again, how can I review sites and decide what to include? How can I even know what sites are out there? I want to share two simple files — two little bits of code — that make the initial work of selecting sites for inclusion a bit easier for me and hopefully may for others, too.

First, I want to talk about the muhlenberginstalls.json file. A few years back, Tim Owens wrote a script that pulls Installatron data into a JSON file and plants this file on our main Berg Builds server every night. This file, what it can collect and how it might be used, could be another excellent topic for future Reclaim Community chats. For instance, at present I’m only collecting WordPress installations, but with minor adjustments, I could pull in Scalar, Omeka, and Grav installations, too. But back to JSON….

JSON, for those not familiar with it, stands for JavaScript Object Notation. JSON is a standard for building a structured data file (technically a semi-structured data file) a bit similar to XML, but built for easy use by JavaScript. It can be read by both humans and computers, like HTML. It’s nicely compact (meaning the files are small compared to, say, spreadsheet file formats, and they tend to load quickly in browsers). As a semi-structured data file, it is hierarchical. And after spending a little bit of time with a JSON file, one can see how some data elements are parents, some are children, and some are siblings. The ability to parse this structured data by moving up and down (or maybe in and out) of a branching scheme, allows a JSON file to preload (before a web page is fully rendered in a browser). Then JavaScript can do stuff when a site’s visitor interacts with a page. It’s AWESOME and POWERFUL, and occasionally it drives you mad, like all magic.

The isotope library uses JavaScript, a simplifier called jQuery, and JSON files to more easily build sortable navigation on websites. The Post Grid is a WordPress plugin that brings Isotope into WordPress and allows folks to configure WordPress posts as things to be grouped and sorted by categories. The Post Grid also allows folks to build navigation to shuffle WordPress posts based on one or more categories. This is often called faceted search and retrieval, because we can organize results by more than one quality or characteristic. In a future post, I’ll take a closer look at Isotope and The Post Grid plugin, and how they work in a community site built with the Reclaim Hosting template.

I’m going to now show one way to parse that nightly report, muhlenberginstalls.json, not with JavaScript but with some good old-fashioned PHP. The two files I display below help me to generate a very simple list of all the WordPress sites at Muhlenberg so I can methodically work through them and see which ones meet a basic rubric I devised for inclusion in our community site. This rubric came out of discussions with my colleagues in Digital Learning and colleagues at other DoOO institutions. When I presented on Muhlenberg’s community hub at Domains 19, the ethical decisions around who gets included, who gets excluded, and why this matters was a big part of what I talked about that day. It’s still one of the more problematic aspects of a community site, and will likely end up in yet another future post in this series.

But a list, sorted alphabetically is perhaps a bit less of a problem and is at least a place to begin. So for now, take a quick look at this:


*  Making a quick list of all              *
*  WP sites retrieved from our             *
*  muhlenberginstalls.json file.           *
*                                          *

$sites = file_get_contents("/somewhere/muhlenberginstalls.json");   //substitute your path and .json file
$sites_json_data = json_decode($sites, true); //the PHP function to decode JSON. It's partner is json_encode

$stuff = $sites_json_data["data"]; //only working with those data at the highest branch of the tree
$stuffCount = count($stuff); //get a count. It's useful for looping and such.

//if you want to display other data within the json file, parse the tree and set variables like these below

for ($x = 0; $x < $stuffCount; $x++) {       //starting a loop
    $url = $stuff[$x]["url"];                //the WordPress site's URL
    $title = $stuff[$x]["cf-sitetitle"];     //the WordPress site title
    $tagline = $stuff[$x]["cf-sitetagline"]; //the WordPress site tagline
    echo "<tr>\n";
    echo "<td><a href='".$url."' target='_blank'>".$url."</a></td>\n";
    echo "<td>".$title."</td>\n";
    echo "<td>".$tagline."</td>\n";
    echo "</tr>\n";
}                                             //ending the loop


This file is called and it does not live in the /public_html/ (aka the /www/ directory) on my community site. Here’s why: the muhlenberginstalls.json file contains some stuff I wouldn’t want prying eyes to see. For instance, it contains WordPress database IDs that could aid a troublemaker with SQL mucking about or other sorts of mean stuff. For this reason, I keep both the muhlenberginstalls.json AND this file someplace that is not accessible via http or https. In other words, this file and my JSON report aren’t served out like web pages to the whole world. These files shouldn’t be crawled by bots, and shouldn’t show up if someone asks for a listing of all directory contents on a website. If this seems confusing, please don’t fret. If you have questions, please reach out to me and I can try to explain it better, or we can look stuff up together. For now, just please know to be a bit careful with building this list, and that care starts with ensuring your installs.json report AND this file live somewhere other than your directories accessible to your web server.

So what the heck am I looking at?

This PHP include file (note the .inc file extension, that stands for “include this”) will be called by a file that lives in my /public_html/ directory, called list.php. I’ll talk about it in a second. But this present file,, DECODES the muhlenberginstalls.json file and gets it ready for PHP to parse. This file also gets a count of all the objects in the file, and I use this number to start a simple loop. While the loop is running, I parse the JSON file and extract each:

  • URL for a site
  • Title of the WordPress site
  • Tagline of the WordPress site

There are tons of other things I could grab from the JSON file. If you’re interested in exploring what else we can do with it, please reach out to me or to the folks at Reclaim Hosting. For now, we just want a simple page that lists ALL the WordPress sites, their Titles and Taglines, and makes it easy to start clicking and evaluating sites.

You may also notice some small fragments of HTML in my include file. These are there because I’m building the insides of a table to display all my results. I chose to do this here because I’m building a loop. Ideally, this is a PERFECT application of JSON, JavaScript, and jQuery. If this were something for the public to use, and not simply a tool for my administration of Berg Builds, I’d build it differently. But the truth is, I don’t really want this JSON file preloaded anywhere, again because it has stuff I don’t want the whole world to see.

OK, now let’s look at the companion file, list.php which lives in my /public_html/ directory:

<title>this is a list of all the WordPress sites on Berg Builds today</title>

<table border=\"1\">

require("/someplace/");  //here I call my include file

echo "<br />";
echo "Today there are <strong>". $stuffCount . "</strong> sites listed below<br />\n"; //$stuffCount lives outside of the for loop, so I can call it here like this.
echo "<br />";


Two files, one purpose

Here, you’ll recognize an embarrassingly simple HTML file whose only purpose is to start a <table> and then import (in this case using the PHP function, require) that file we’ve looked at above. It also gives me a count of how many WordPress sites are listed in my muhlenberginstalls.json file, just for my own reference. The innards of the table I started in inside my for loop are now displayed. And at the end, I close the table, and close up the HTML file.

Now, importantly, list.php *does* live somewhere in the /public_html/ directory of my domain. It’s called from a browser, with a URL. In fact, it lives in a subdirectory, /public_html/list/ or Because this is an administrative tool and not a public website, I decided to place a password on it the directory. I did so mostly because I don’t want this page crawled by indexing web bots, but also because I wouldn’t want to draw the wrong kind of attention to, say, those “Hello, World!” sites students are just starting to figure out. There is power in collocating ALL the sites at Muhlenberg, and so I put a simple .htaccess password on the directory where my list lives. Here’s how I do that:

Password protecting a directory with cPanel

If you’re not interested in setting a password, or if you already know how it’s done feel free to skip ahead.

From your cPanel dashboard, search for “Directory Privacy”. Or you can scroll and search for this icon:

This is a screenshot of the Directory Privacy icon in cPanel
A screenshot of the Directory Privacy icon in cPanel

Directory Privacy will open with a view of all the directories within your /home/ directory. To set a password within a directory that contains a subdomain, look for that folder. In my case, I’ve created a subdirectory within my /public_html/ directory called “list”. When you find where you’d like to place a password, click the EDIT button.

a screen capture of the Directory Privacy utility in cPanel. This screenshot depicts file folders and the “Edit” button beside them.

Once you click “EDIT” you are given a few form fields to complete. These forms change depending on whether it’s your first time setting up a directory password, or if you’re changing one that is already set. Unfortunately, this is a poorly designed form and I’ve gotten myself confused a couple of times using it. On the first pass, it looks like this:

screen capture of the Directory Privacy utility in cPanel.   This image depicts the form to complete when setting up a password for a directory.
The Directory Privacy utility in cPanel, when setting up a password the first time.

Once you have given a name to a protected directory, you will be presented with a different form, like this:

screen capture of the Directory Privacy utility in cPanel.  This depicts the second form shown, after a name has been given to the protected directory

On the second pass, only click the SAVE button beneath the Create User form. To change a user’s password, retype the Username exactly, and provide a New Password and confirm that password.

Now, back to list.php

Once you’ve placed your file somewhere within your /home/ directory outside those areas accessible via http, you’ll need to update your list.php file. Change the require() statement to reference the file all the way from /home/ . Next, when you’ve protected your directory and you’ve copied the list.php file, you can call up the list and you should have a useful table of hyperlinked URLs, titles, and taglines. Mine looks like this:

A screen capture of my generated list showing site URL, Title, and Tagline.  Names and URLs have been blurred.
A few of the 1,869 sites displayed in my hyperlinked list

With this simple list, I can easily access all the WordPress sites on Berg Builds. If I wanted to add more information, or if I wanted to build something that tracks my review progress, I have the foundational PHP to start creating that tool.

But here is where I hope I might make some modifications to the community site template built by Taylor. If this parsing of a muhlenberginstalls.json file can generate a simple table in HTML, it could also generate an SQL insert or update statement. instead of generating a dynamic .php page, it could instead write a series of SQL statements that could place all of this information into a WordPress database. In other words, I think the community template could not only help me build a public presentation, but might also help me with my evaluation workflows, and help me keep track of which sites I’ve reviewed. And even better, with a little more work, I think the installs.json file could also help me cull WordPress sites that have been removed from Berg Builds, and could then drop out of my community site!

All of this needs to be explored, carefully tested, and the like. But the germ of the idea is here. And thanks to Taylor, this is something that might be easier to build, use, and share.

Featured Image: “Jungle Jim” by Alan Levine. CC 0.

Leave a Reply

Your email address will not be published. Required fields are marked *