
Adding Structured Data to Your JEvents Calendar Entries
Search engine optimization (SEO) is an important part of any website’s design. Structured data is an important part of a basic SEO program, but unfortunately, many Joomla extensions are well behind the state of the art when it comes to implementing structured data, even though Joomla itself has made great strides in recent releases.
Calendar plugins are an important part of most web sites, and Schema.org implements a very rich Event tag should be implemented by all calendar plugins. Many do not implement it, including the JEvents extension.
The article that follows describes how to use the custom template feature in JEvents to add structured data to your calendar.
HTML Implementation of Event Tag Structured Data for JEvents Event Detail
Generally, the JavaScript based tags are the easiest way to implement structured data, but in most editor implementations, script tags get stripped out for security reasons. You can generally enable scripting but this presents some security issues for general users, and may require you to purchase a paid version of a plugin in order to get granular authorizations that allow admins to use scripting but prevent general users from doing so.
Because of the difficulties in script authorization, I have chosen an implementation that does not use scripting, but instead uses HTML microdata. It is harder to read and is more cumbersome but does not require special permissions management.
To add the structured data, go to the “Custom Layouts” section and select the “Event Detail Page” entry. Add the highlighted tags to you entry and publish the page.
<div class="event-wrapper" itemscope="itemscope" itemtype="http://schema.org/Event">
<p>{{Ical Button:ICALBUTTON}} {{Edit Button:EDITBUTTON}}</p>
<div class="contentpaneopen jev_evdt">
<div class="jev_evdt_header">
<div class="jev_evdt_title">
<div class="event-title" itemprop="name">{{Title:TITLE}}</div>
</div>
</div>
<div class="jev_eventdetails_body"><!--
<div class="jev_evdt_summary">{{Repeat Summary:REPEATSUMMARY}} {{Previous/Next Links:PREVIOUSNEXT}}</div>
-->
<div class="jev_evdt_summary">
<div class="event-date" itemprop="startDate">{{Start Date:STARTDATE}} {{Start Time:STARTTIME}}</div>
to {{Start Time:ENDTIME}}</div>
<div class="jev_evdt_hits">{{Hits:HITS}}</div>
<div class="jev_evdt_contact"><span class="contact">{{Contact Label:CONTACT_LABEL}}</span>{{Contact:CONTACT}}</div>
<div class="jev_evdt_location">
<div class="event-venue" itemscope="itemscope" itemtype="http://schema.org/Place" itemprop="location">
<div class="address" itemscope="itemscope" itemtype="http://schema.org/PostalAddress" itemprop="address">{{Location:LOCATION}}</div>
</div>
</div>
<br class="jev_evdt_extrainf" /> </div>
<div class="jev_evdt_desc"><span class="description"><span itemprop="description">{{Description:DESCRIPTION}}</span></span></div>
<div class="jev_evdt_extrainf">{{Extra Info:EXTRAINFO}}</div>
<div> </div>
<!--
<div>{{End Time:ENDTIME}}{{Repeat Summary:REPEATSUMMARY}}</div>
</div>
<div class="jev_evdt_creator"><span class="creator">{{Creator:CREATOR_LABEL}} </span>{{Creator:CREATOR}}</div>
<div class="jev_evdt_location"><span class="location">{{Location Label:LOCATION_LABEL}}</span>{{Location:LOCATION}}</div>
--></div>
</div>
Testing Your Structured Data Markup
To test your structured data markup, use the Google Structured Data Testing Tool
Adding Event-specific Structured Data
JEvents (at least the free version) does not allow you to specify offers or many of the other Event tags that Google recommends. You add them as a part of the event detail with some additional work, as shown in the code listed in Figures 2, 3 and 4.
<div itemscope="itemscope" itemtype="http://schema.org/AggregateOffer" itemprop="offers">This is a free event (<span itemprop="priceCurrency">$</span><span itemprop="price">0.00</span>), but the room for this session only has seats for about <span itemprop="availability">20</span> people, so please RSVP on the <span itemprop="url"><a href="https://www.meetup.com/joomladallas/events/236093658/">https://www.meetup.com/joomladallas/events/236093658/</a></span>.</div>
<p><span itemscope="itemscope" itemtype="http://schema.org/Person" itemprop="performer"><span itemprop="name">Bruce Moore</span></span>, <span itemscope="itemscope" itemtype="http://schema.org/Person" itemprop="performer"><span itemprop="name">Jeff Morris</span></span>, and <span itemscope="itemscope" itemtype="http://schema.org/Person" itemprop="performer"><span itemprop="name">Wilma Howell</span></span> will speak at this event.</p>
<figure><figcaption>Bruce Moore speaking at the North Dallas Joomla User Group Meetup</figcaption> <img src="/images/figures/bruce_moore_speaking_at_joomla_meetup.jpg" alt="North Dallas Joomla Meetup" itemprop="image" /></figure>
<p>For event details, see the <a href="https://www.meetup.com/joomladallas/events/236093658/">Joomla User Group Meetup page</a>.</p>
To see how this is interpreted by Google, look at the results from the Structured Data Testing Tool.
- Details
- Written by Bruce Moore
- Hits: 2550
UseR! 2017 Recap
I recently returned from a week at the UseR! 2017 conference in Brussels, which was a great opportunity to catch up on the latest trends in the R world. This conference was noticeably different from the 2015 Aalborg conference in the demographics of the audience; in prior conferences, the attendees were overwhelmingly either PhD faculty or PhD candidates but at this conference many if not the majority were consultants and practitioners from industry. There is a lot to cover, so I’ll split things into a few categories:
- Trends
- Natural Language Processing
- A Tidal Wave of Mapping
- Shiny Stuff
- Docker Was Common
- Tidyverse
- Mixed Integer Programming
- Parallel Processing
- Making Web Sites Accessible to the Blind


- Details
- Written by Bruce Moore
- Hits: 2849
What Can You Do with an Insecure Laptop, Phone, Tablet or Music Player?
The short answer is not much. The long answer is “it depends.’ We have looked at resurrecting old devices that no longer receive security updates, given the likelihood of having to check devices in luggage and potential damage or theft. We now plan to travel with some old devices, but with some restrictions on how we use them. The article that follows discusses the security and usability issues for several devices that we have resurected.
MacBook circa Late 2009
Our old MacBook has not received security updates for at least two years and as such has not been used. The battery is no longer good either. Since it is essentially valueless, getting it stolen would not be a problem except for the potential data security issues. The battery was dead, but I found a reputable third-party replacement for $44. I backed up the disk with Clonezilla, and installed the current version of Ubuntu 16.04. The only thing that took some tinkering was copying the firmware for the iSight video camera and installing it. During the install, I enabled home directory encryption.
With about two hours of work, the laptop is secure and works surprisingly well for normal office and browsing tasks. The battery life is only two to three hours, but with some research and tweeking, that could probably be improved. I was pleasantly surprised that I was even able to get Skype working.
Android Tablets and Phones
LineageOS is an alternative Android Firmware available for some phones and tablets. Unfortunately, it takes some research using the LineageOS CVE Tracker to figure out whether or not the firmware for your particular device is up to date on CVE security patches; some of the devices are up to date, but the CVE Tracker has not been updated.
This can be a good alternative, but probably is not a good idea for a device with critical information, as a build problem with permissions could leave you with a very insecure device.
Using Devices that Cannot Be Secured
Using old iPhones, iPods and Android devices that cannot be brought to current security patch levels is problematic, since a key logger other malware could be installed without your knowledge. You could lose access to email and other passwords. Because many services allow you to reset a password via email, you could lose control of critical financial and social media accounts.
What You Cannot Do
Here are things you cannot safely do:
- Do not use your primary Apple ID and/or Google ID with contacts and email. You can get a second ID that you do not use for contacts or email and use this. Through the Apple Family Sharing plan, you may even be able to get access to some apps. You CANNOT use this device for email.
- Do not use an account where an attacker could get access to credit card information by logging in to a subscription account.
- Do not log in to web sites.
- Do not connect the device to your home Wi-Fi network.
- Do not install Google Authenticator or another authentication program.
What You Can Do
There are two principles to using an insecure device:
- Do not use it for anything where compromise would be a problem. Nothing of value should be present on the device. No important passwords.
- Minimize the exposure of the device.
These two principles lead to a few simple practices:
- Turn off Wi-Fi outside your home network, or better, just turn it off altogether.
- Turn off Bluetooth.
- Turn off location services, since this may turn on Wi-Fi and/or Bluetooth.
- Use an Apple ID or Google ID that is not used for ANYTHING that needs to be secure.
- Add music and ebooks only via USB connections.
- Update content only through USB connections.
There are some things that you can do using an insecure device, but they are quite limited:
- You can use it as an e-reader.
- You can use it as a music player.
- You can use it to read newspaper apps if you sync via iTunes, as long as the log-in account uses a unique password and the customer service portal does not expose your credit card information.
- Details
- Written by Bruce Moore
- Hits: 2045

Health Insurance for Small Consulting Firms
Before quitting my job to start this business, I spoke with several self-employed people to get their advice. Without exception, their first question was “how are you going to get health insurance?” I told them that Kristin planned to keep working and that we would get health insurance through her employer until the Affordable Care Act (ACA) kicked in and we could get insurance on the private market. Like almost everyone over the age of 40, I have pre-existing conditions that would have prevented me from getting health insurance prior to the passage of the ACA; I am healthy enough to ride a bicycle across the United States, but not healthy enough to get health insurance prior to the ACA.
For all of the problems that the federal exchange has had, the exchange makes it possible (if not necessarily easy) to compare insurance prices in different locations. To compare costs for different locations, go to Healthcare.gov under the “Get Answers” section for “Where can I find 2017 Marketplace plans and prices?”
Figure 1 shows a summary of the costs and availability of plans for a zip code.

If you collect information from several zip codes, you can start to compare insurance costs as shown in Table 1. Bronze plans are expected to cover 60% of expected costs, Silver 70%, Gold 80% and Platinum 90% (See Minnesota Rate Release Packet).
The only conclusion one can draw from the small data set shown in Table 1 and Figure 2 is that price and number of policies available are related. The number of policies available is largely a proxy for the number of insurers in the market. Whether competition is the cause for lower insurance prices, or more competition is the effect or less expensive and more readily available delivery cannot be determined from this data set.
Table 1. Comparison of Average Health Insurance Costs and Availability on Healthcare.gov. Retrieved July 13, 2017. Costs for people who are near 65 would be about 1.5-2 times higher while costs for those near 18 would be about 1/2 the costs shown here. | |||||||
Zip Code | City | State | Bronze | Silver | Gold | Platinum | Wind.Location |
---|---|---|---|---|---|---|---|
2906 | Providence | Rhode Island | NA ~ $NA | NA ~ $NA | NA ~ $NA | Industrial | |
23173 | Richmond | Virginia | 14 ~ $264 | 15 ~ $308 | 3 ~ $406 | Industrial | |
29342 | Gaffney | South Carolina | 5 ~ $336 | 15 ~ $382 | 5 ~ $472 | Small City | |
43612 | Toledo | Ohio | 19 ~ $275 | 34 ~ $303 | 9 ~ $361 | Industrial | |
48109 | Ann Arbor | Michigan | 23 ~ $280 | 30 ~ $362 | 6 ~ $466 | 1 ~ $506 | Industrial |
48236 | Detroit | Michigan | 32 ~ $239 | 41 ~ $312 | 9 ~ $388 | 1 ~ $474 | Industrial |
48501 | Flint | Michigan | 26 ~ $239 | 35 ~ $311 | 7 ~ $401 | 1 ~ $477 | Industrial |
49770 | Petosky | Michigan | 13 ~ $249 | 22 ~ $317 | 5 ~ $447 | 1 ~ $510 | Small City |
55082 | Stillwater | Minnesota | NA ~ $NA | NA ~ $NA | NA ~ $NA | Downwind | |
55374 | Rogers | Minnesota | NA ~ $NA | NA ~ $NA | NA ~ $NA | Upwind | |
55447 | Minneapolis | Minnesota | NA ~ $NA | NA ~ $NA | NA ~ $NA | Industrial | |
59808 | Missoula | Montana | 10 ~ $381 | 8 ~ $474 | 6 ~ $593 | Small City | |
59937 | Whitefish | Michigan | 5 ~ $429 | 4 ~ $514 | 3 ~ $610 | Small City | |
60613 | Chicago | Illinois | 11 ~ $333 | 21 ~ $353 | 6 ~ $454 | Industrial | |
61820 | Champaign | Illinois | 4 ~ $396 | 4 ~ $489 | 1 ~ $579 | Small City | |
75019 | Coppell | Texas | 8 ~ $328 | 18 ~ $359 | 6 ~ $461 | Downwind | |
75167 | Waxahachie | Texas | 5 ~ $361 | 5 ~ $466 | 3 ~ $560 | Upwind | |
76201 | Denton | Texas | 7 ~ $342 | 15 ~ $377 | 4 ~ $527 | Downwind | |
77840 | College Station | Texas | 5 ~ $352 | 5 ~ $453 | 5 ~ $545 | Small City | |
97035 | Portland | Oregon | 14 ~ $279 | 14 ~ $347 | 9 ~ $415 | Industrial |
## Warning: Removed 12 rows containing non-finite values (stat_smooth).
## Warning: Removed 12 rows containing missing values (geom_point).
In this small sample of locations, the average cost of a bronze plan varies from $239/month in Flint, Michigan to $429/month in Whitefish, Montana, a 79% difference in price and a 5.2x difference in number of plans from which to choose. The number of plans available also varies significantly. Some states, including Minnesota and Rhode Island, have set up their own exchanges which can sometimes be much more useful if not as aesthetically pleasing as the Healthcare.gov site. The figures on these two sites are oriented to annual total costs including deductibles and though much more useful, would take a lot of work to get into the format on the healthcare.gov web site, and thus have been omitted.
It is clear that some states are better places to work if you depend upon the private market for health insurance. Texas is not the worst place for a private health insurance market, but it is far from the best. Although it is difficult to make direct comparisons, states that implemented their own exchanges seem to have more vibrant markets than states that did not. The difference in healthcare costs are significant enough to potentially outweigh higher tax rates in other states.
Health Insurance Costs and Air Quality
In identifying a place to live, air quality should always be a consideration, but how does air quality effect the cost of health insurance? By joining insurance cost data with air pollution data, we can attempt to answer that question. One would expect costs to decline as air pollution increases. Figure 3 shows an unexpected pattern; costs first decline as the number of unhealthy days for sensitive individuals increases, and then costs begin to increase. The most likely explanation is that air pollution is related to the size of a city, and that costs decline with the economies of scale associated with a city before the poor air quality of cities begins to overwhelm the economies of scale in medical care delivery. This data set is not large enough to do regressions to prove that hypothesis.
Because many rural locations do not have reliable air quality reporting, the an “adjusted number of good air quality days” has been calculated as the (number of good days)/(number days reported)*366.
Figure 4 shows the cost versus the number of days with good air quality, and the cost benefit of good air quality is weak, but reasonably clear for this data set up to about 270 days of good air quality. Beyond 270, it appears that costs increase, but this may be drive more by urban vs. rural cost inefficiencies than by air quality. Generally speaking, only rural locations get more than 300 good air days per year. The urban vs. rural aspect is clear for the Platinum policies which are only present for the state of Michigan in this data set. There is a clear increasing trend with Adjusted Number of Good air days, which corresponds to decreasing city size.
The clear implication is that one should select a suburb on the upwind side of a medium-sized city.
Upwind or Downwind
There appear to be two patterns in this small and admittedly skewed dataset:
- There are economies of scale that lower health insurance costs in or near large cities.
These two patterns imply that health insurance costs are lowest upwind of a major city. Figure 5 shows a plot of costs vs. Adjusted Number of Good Air Quality Days–the same as Figure 4, but this time the color coding is based upon the classification of a location as “upwind”, “industrial”,“downwind” or “small city”. There really is not enough data on upwind costs, but it is clear for this data set that for small cities, costs decline as air quality improves beyond about 270 days.
Because the costs in the state of Michigan are significantly lower than the other states in this analysis, all Michigan data is removed in Figure 6; the trend for cost decreases with increasing air quality still appears to hold for small cities, but not for industrial cities. The small size of this data set make firm conclusions impossible, but this looks like it would be an interesting area for a larger analysis.
Shop for Policies
When talking with other consultants, I have been surprised that I have found several who are paying higher premiums than we are even though the ages of their family members would indicate that they should be paying lower premiums. They chose plans with low deductibles and higher premiums, but in most cases, the combined premium and deductible were higher for their plan than for our “high deductible” plan. In most circumstances, it does not make sense to get this type of plan.
High deductibles may be completely foreign coming from the traditional salaried market world, but high deductible plans make sense in the independent consultant world. Do not be worried that you may not get any reimbursements–the goal is not to maximize what you get back from the insurance company–the goal is to minimize annual costs to protect your family from a health and financial catastrophe.
There is a 100% chance that you will pay all of the premiums, but some chance less than 100% that you will pay all of the deductible. With a high premium/low deductible policy, you only get the money back if you require a lot of healthcare services. With a low premium/high deductible plan, you avoid expenses altogether if you do not require healthcare services.
Because you have almost no information on expected expenses, a “minimize maximum regret”decision strategy is the only one where you have the information to make a choice, and low premium/high deductible policies almost invariably have the lowest combined premium and deductible annual cost. In this respect, most of the state-run exchanges are much easier for shopping because they tend to expose the combined premium and deductible cost directly while on the federal exchange you have to go in and look up the deductible directly.
Destabilizing the Affordable Care Act Insurance Markets by Removing Risk Corridors
Part of the reason premiums have increased and some insurers have left some markets is that the actions of the Republican party have destabilized the ACA insurance markets. In 2015, Marco Rubio and others managed to insert into a spending bill language to remove one of the key financing provisions of the Affordable Care Act. The language removed a “risk corridor” financing provision intended to help insurance companies during the first years of the start-up of the ACA. Because no insurance company had any significant data on the expected costs of the newly insured, the premium price setting process involved a lot more guessing than for setting premiums in mature markets. The risk corridor payments were intended to reduce losses should initial premium estimates prove wrong. When the risk corridors were removed, only unusually large and well capitalized companies could afford the risk of guessing wrong on the premium pricing. The result of the Republican action to remove this was entirely predictable: smaller companies exited markets where the risk of mis-pricing was too high, and with less competition, larger companies raised prices.
Destabilizing the Insurance Markets by Threatening to Stop Low-income Subsidy Payments
The threats by the Trump administration to withhold subsidy payments to insurance companies for low-income policy holders has caused some insurers to pull out of markets, further destabilizing them.
Destabilizing the Insurance Markets by Planning to Repeal the Affordable Care Act
Current efforts to repeal the ACA are further destabilizing markets; what insurer would invest in building physician networks, call centers, and other infrastructure for a line of business that could disappear completely at the whim of Congress? The opaque nature of the policy debate guarantees that everyone–including insurance companies and their actuaries–will be surprised by whatever does (or does not) come out of the Republican caucuses. In the insurance industry, surprise is a very, very bad thing; the only thing way to protect against surprises is to raise premiums.
What Should Independent Consultants in IT Industry Do?
The effect of the destabilization of the private insurance marketplace has gotten the attention of just about every independent consultant that I know; everyone has started talking about the possibility of closing shop and going back into the salaried workforce for the sole reason of maintaining access to health insurance. If this were to happen, the influx of workers into the salaried market would cause wages to stagnate or even decline; which would benefit no one.
For businesses that do not require a local presence, it is worth investigating the health insurance costs in other locations. The health insurance availability and cost savings could be substantial.
- Details
- Written by Bruce Moore
- Hits: 2322
Setting up Google Authenticator for Joomla
The recent threat of a laptop travel ban pushed me to bite the bullet and set up two factor authentication on all of the web sites that I manage. Since traveling with a laptop may be problematic in the near future, I may have to log in to various sites from public terminals in order to do various administrative tasks. This is just asking to have both the user ID and password compromised. Fortunately Joomla has had two factor authentication built in for about two years. I tried to set it up once before, but had some problems and abandoned the effort. It turns out that there were two very simple problems that stopped me. I will not repeat the whole process as it is well documented on the Joomla website, but I will add two key steps that you should do first.
Preparing to Set up Two Factor Authentication for Joomla
Before you start the standard instructions, you should do three things:
- Make sure that your web server clock is synchronized with the standard NTP time that is used for cellphones.
- Make sure that your cell phone (or whatever device you are using for Google Authenticator) is synchronized with NTP time servers.
- If you have Akeeba Admin Tools installed, make sure that
Components->Admin Tools->WAF->Configure WAF->Joomla Feature Hardening->Disable editing backend users' properties
is set to “No.” This is only necessary when you are setting up two factor authentication for backend users. If you do not do this, you will get a 403 Access Forbidden error when you try to set up two factor authentication for backend users.
If the server time and device time are not synchronized, the six-digit code that Google Authenticator provides will never match what Joomla expects. On Android devices there is a button that will allow you to synchronize the time in the Google Authenticator app, but the iPhone app does not have this feature. You may want to put JavaScript on your website (as shown below) to show the two times to help you diagnose this type of problem.
Setting up Two Factor Authentication for Joomla
The Joomla website has a great article, Two Factor Authentication, that describes how to enable two factor authentication; I won't repeat the instructions here.
When you are finished with the set up for your backend users, remember to turn the Akeeba Admin Tools feature hardening back on.
Make sure to copy the ten one-time keys to a safe place in case your phone is lost or stolen.
Add JavaScript to Show Both Server and Browser Time
To help diagnose time synchronization problems, you can add the following JavaScript to a custom HTML module:
<!-- From http://www.webdeveloper.com/forum/showthread.php?228309-Getting-server-date-time-with-no-server-side-script -->
Server Time is
<script language="javascript">
var xmlHttp;
function srvTime(){try{xmlHttp=new XMLHttpRequest();}
catch(err1){try{xmlHttp=new ActiveXObject('Msxml2.XMLHTTP');}
catch(err2){try{xmlHttp=new ActiveXObject('Microsoft.XMLHTTP');}
catch(eerr3){alert("AJAX not supported");}}}
xmlHttp.open('HEAD',window.location.href.toString(),false);
xmlHttp.setRequestHeader("Content-Type","text/html");
xmlHttp.send('');
return xmlHttp.getResponseHeader("Date");}
var st=srvTime();
var date=new Date(st);
document.write(date);
</script>
Browser time is <script language="javascript">var today=new Date();document.write(today);</script>
When you or your users are having log-in problems, having an easy way to rule out server to device synchronization problems can simplify problem diagnosis tremendously.
- Details
- Written by Bruce Moore
- Hits: 1953