How to really exclude the home page in the WordPress wp_page_menu() function

I’m posting this little tip for my future self as much as for anyone else.

The problem: It seems that in recent years WordPress has all-but-deprecated the wp_page_menu() function in favor of the wp_nav_menu() function, but the latter is primarily intended for Custom Menus, and there are some cases where Custom Menus aren’t what you want.

I’m working on a couple of sites right now that have a lot of pages, and I don’t want their editors to have to go over to Appearance > Menus every time they add or move a page. I’m relying on the CMS Tree Page View plugin on these sites to allow the clients to leverage the built in Parent / Order settings on pages to manage their primary navigation, rather than managing a separate Custom Menu.

Since I’m not using a Custom Menu, I don’t want to use wp_nav_menu() at all; I pretty much have to use wp_page_menu(). Which is fine. Except for the fact that the site designs do not call for having Home in the navigation.

No problem, right? After all, I can just add 'show_home' => false to the arguments passed to the function, and it will remove the home page.

But it doesn’t.

I’m not entirely sure what’s going on in the WordPress core here, but I suspect that show_home only works if you’re using the old school default configuration of WordPress that calls for the home page being the main “posts” page. And who does that anymore? If you’ve created a custom home page, even if you’ve configured WordPress properly to display it as the main page, this function doesn’t care. (Do functions “care” about anything? But I digress…)

Remember also that there’s an exclude argument, to which you can pass a comma-delimited string of post IDs to exclude. That works, but… ugh. You mean I really have to hardcode the post ID of the home page right into my header.php file?

Of course not. Use this: get_option('page_on_front') and it will automatically find the home page. This is great if you want to be able to reuse a theme or custom function on multiple sites or just not commit the transgression of hardcoding something that really doesn’t need to be hardcoded.

Here’s the complete code sample. Note there’s no need to bother with show_home at all.

wp_page_menu(array(
    'exclude' => get_option('page_on_front'),
));

Edge of what?

Let’s talk about Internet Explorer for a minute. Approaching two decades into a career as a web developer — cripes! how is that even possible? — I have spent a big chunk of my life hating Internet Explorer.

There was a time when I didn’t hate it. For several years, Internet Explorer was the best web browser for the Mac. (Yes, really!) But right around the time Apple released Safari and Microsoft decided to pull the plug on the Mac version of IE, everything started to go sour.

In the early 2000s, when Windows XP was released, and Internet Explorer 6 along with it, Microsoft dominated the tech world. Especially the business tech world. And with the web standards movement in its infancy, Microsoft could pretty much do whatever they wanted with the browser. Internet Explorer 5, 5.5 and 6 each introduced new, Microsoft-only technologies (VBScript, ActiveX, .NET, etc.) that became deeply entrenched in the business world, where countless corporate developers created indispensable internal web applications that were not only dependent on Internet Explorer, but specifically on quirks of version 6 (or 7) of IE. It’s a big reason why there are still office computers running Windows XP and IE 6 or 7. Because even as bad as IE 8 is, it was the beginning of Microsoft’s acknowledgment of the changing times and reluctant move towards web standards.

Long story short, I don’t just hate IE because it’s from Microsoft, or because it’s fun to bash on. Contrary to the impression I sometimes give, I don’t hate Microsoft, and as much as I love to crank, I’d prefer a world where I didn’t have things to crank about. I hate Internet Explorer because it has made my job harder, for most of the time that I’ve been doing this work.

So, it probably goes without saying that I took the announcement of the death of Internet Explorer as good news. Of course, Microsoft has to make its own browser. Uh… just… ‘cuz. Of course. So with IE going away, Microsoft has announced “Edge”, their new browser.

Meet the new browser, same as the old browser

This morning Brand New posted the new logo/icon for Edge. At least, I think it’s a new logo. For a new browser.

edge
Source: Brand New

What is this? No, seriously. What. Is. This.

This logo fails for me on several levels. First, and most obviously, it evokes Internet Explorer. Why would Microsoft want to do that? They’re killing IE for good reason. Why create an immediate association between it and their new browser?

I think this new logo fails both conceptually and in its execution. It’s just plain ugly. But more than that, the slice/swoosh thing doesn’t work. In the old logo, it was part of the “ring” around the “planet” that the perfect circle “e” represented. A bit hackneyed conceptually, but at least it was a consistent concept. But by using the “e” from Microsoft’s new humanist corporate font (I think) — which, taken on its own, is kind of an ugly shape anyway — I think, you lose the “planet” concept. And the rest of the ring outside of the “e” is gone too. So all you have left is this weird “e” with a slice missing, which makes absolutely no sense. The only explanation for the slice is as a deliberate evocation of the old Internet Explorer logo, which again it seems they should want to distance themselves from.

I like the new blue color. That’s about the only good thing it has going for it.

So far I have not tried the preview release of Microsoft Edge. Frankly, as a web developer, I am not enthusiastic about having to support another new browser, and I’m not confident that Microsoft is going to make a very good new browser, even though IE 9 through 11 were pretty decent. All I have to go by, at this point, is this logo. And what it tells me is that Edge is just a crappy knockoff of an already crappy browser. No thanks.

Postscript: I just noticed that exactly 6 years ago today I wrote a blog post that also discusses Internet Explorer. Even then — SIX YEARS AGO — IE 8 was out and I was already cranking about IE 6 as an old and outdated browser.

Some thoughts on the target market for the Apple Watch Edition

Thoughts on the Apple Watch have been simmering on my mental back burner since it was announced in September, and I followed along with the announcement yesterday to see what new things we might learn about the product — most significantly, the prices for the higher-end models. All we knew up to this point was that they “start” at $349, and that’s for the lowest-end, anodized aluminum Apple Watch “Sport”, with a “fluoroelastomer” (a.k.a. rubber) band. (Side note: It seems Apple is taking great pains not to call the band “rubber” in their marketing, yet I noticed Christy Turlington Burns, in her appearance on stage yesterday, referred to it — quite dismissively, no less — as “rubber”, and I think I saw Tim Cook flinch just a bit.)

The stainless steel “regular” Apple Watch and the gold Apple Watch “Edition”, with fancier bands, would clearly cost more… much more. I was frankly surprised the starting price for the steel Apple Watch is only $200 more than the Sport, and I was not at all surprised that the Edition starts at $10,000. But the fact that it does start at $10,000 got me thinking more about why. I have some ideas, which I will explore here, but first some more general thoughts on the Apple Watch.

Not my taste

First off, I personally am not really in the market for an Apple Watch at all. I find it interesting, but a) I don’t really want to wear something on my wrist, and b) I’m not interested in this until the second or third iteration. But if I were to buy an Apple Watch, there is absolutely no question that I would get the Sport. I wouldn’t even consider either other option, and price is only a small factor in that. I just don’t like shiny objects. Perhaps it’s my stubborn proletarianism lashing out, but I find wearing shiny items like a highly polished watch (in either stainless steel or gold) to be an ostentatious display of… something. Not my personality.

I especially dislike gold. I can tolerate the mild “champagne” gold color Apple has introduced on the iPhone, iPad and now MacBook, though I don’t personally want them. But pure, shiny gold is something I associate closely with Donald Trump’s raging ego. (Sorry… very specific and most probably unfair, but it is what it is. Again, just my personal taste.)

Clearly I am not the kind of person who desires high-end luxury goods. I appreciate high quality. That’s why I like Apple’s products in the first place. But there is definitely a level of luxury to which I cannot, and emphatically do not, aspire. So be it. Despite my own tastes, I do think I have some idea why Apple has created the Edition version of its watch, to whom it’s targeted, and why it costs so much.

So whose taste is it?

Most people who can’t afford it think $10,000 is an insane price to pay for a watch, and they especially cannot comprehend how someone would want to spend that much on a high-tech watch that will become obsolete in a few years. I’ve read many comparisons with Rolex and other high-end mechanical watches that are priced in that range, which are typically bought to last a lifetime, if not to become a family heirloom passed down through the generations.

This cannot be exactly the audience Apple is targeting, either. Because there’s a range of wealth where someone can afford a $10,000 watch, but it’s still a substantial investment for them — the kind of thing you buy once, ever. And then there’s the range of wealth where someone can easily drop $10,000 on a whim and not think twice about having to do so again in a few years.

This is the target market for the Apple Watch Edition. It’s extremely small. But it exists.

So the question then becomes, why is Apple targeting this market at all?

Watches are different from phones and other gadgets in a significant way that Tim Cook touched on yesterday. But he didn’t fully probe that difference and why it might make sense for Apple to create a “disposable” product with a 5-figure price tag. Watches are something you wear, a fashion accessory in a way that other objects that you merely hold or carry in your pocket are not. They are an extension of who you are, at least for some people, and quite likely for this exact small niche market I’m talking about.

In short, I believe the subset of people for whom the Apple Watch Edition is made are, in general, willing to carry the same smartphone as “the masses”… but they are not willing to wear a similarly proletarian watch.

These people are important customers to Apple. Not just because they spend a lot of money on Apple’s products, but also because they tend to be high profile people whom Apple wants the public to see carrying, using, and now wearing Apple’s products. And the only way to get them to wear an Apple Watch is to make one that is as pretentious “discriminating” as their other tastes.

But why so exclusive?

Now there is one point Tim Cook mentioned that I interpreted in a different way from other analyses I’ve seen of yesterday’s announcement. Tim Cook said something to the effect that Apple Watch Edition will be available only in extremely limited supplies, and only in “select retail stores”. Most of what I’ve read suggests this means that only some of the Apple Stores will be carrying Apple Watch Edition. Personally, I don’t think any Apple Stores will be carrying it. I think it will only be sold in exclusive luxury jewelry and watch stores. Rodeo Drive kind of places. (I guess… what the hell do I know? I’m sure there’s some ultra-luxury shopping venue I’ve never even heard of, and the people who shop there look down their noses at lowly Rodeo Drive.)

The point is, I think Apple is going out of their way to make Apple Watch Edition so absurdly ultra-exclusive because there’s a very small, very wealthy, and very important niche market for a product this extreme, but for whom it needs to be this extreme, solely as a differentiator. For everybody else who might even consider such a product — everybody — they will be satisfied by either the Apple Watch Sport or the Apple Watch.

The only difference between the regular Apple Watch and the Edition is the metal and the strap. The electronics are identical. The amount of extra R&D Apple had to put into creating the Edition can probably pay for itself, even if the Edition sells just a few hundred units. So, from that perspective, why not create it? But if you do, the only way to make it worthwhile is to go all out, if not completely overboard, with the exclusivity and perceived “luxury” of the object.

I think that’s exactly what Apple has done.

Overriding WooCommerce (or any WordPress plugin) functions: a semi-solution

Here’s a question I’ve spent the past few days dealing with based on an issue a client had. How can you override a function in the WooCommerce core? The simple and dismissive answer is, you can’t. I tried a lot of different possible solutions. Eventually you may just be able to track down a solution that works in your specific case, but there’s no general rule that works universally. In this post I am going to discuss the specific problem that was the impetus for this journey, explore some of the challenges the situation presents, and describe the ugly but marginally tolerable solution that worked in this one narrow case.

What’s the problem?

Why would you ever want to override existing WooCommerce functionality? Well, maybe I need to start with a simpler question. What is WooCommerce? It’s a plugin for WordPress that adds extensive e-commerce capabilities. It’s hugely popular and, for the most part, really good. But it’s not perfect, and it’s not right for every possible scenario.

We have a client with a huge number of products, and a huge number of variants of those products. Sizes, colors, styles, etc. And as it happens, this client doesn’t give all of those variants their own SKU. But they do give some of them a SKU, and sometimes multiple variants share the same SKU.

Don’t get all principled about the definition of a SKU. They’re totally arbitrary and different businesses will use them in different ways. It’s not my job to police my clients’ SKU conventions, just to give them a way to use them within WooCommerce.

So now, here’s the problem. The client had been able to give multiple variants the same SKU. But a couple months back, we ran an available WooCommerce update, and it “broke” this “feature” for them. As it happens it was really fixing this bug in the system that allowed variants with duplicate SKUs. At least that was the argument in favor of the change. Even though I agree with coenjacobs who wrote: “I can think of use cases where this might be expected behaviour. Variations don’t necessarily require a product to be completely different, therefore duplicate SKUs might be required.”

Yes, I can think of use cases like that… my client’s website! Unfortunately there’s no turning back, so we need to find a solution.

The solution, on the surface, is extremely simple. Inside the WooCommerce plugin there’s a deeply buried file called includes/admin/meta-boxes/class-wc-meta-box-product-data.php that has this bit of code at line 1547:

$unique_sku = wc_product_has_unique_sku( $variation_id, $new_sku );

That code happens inside a method that saves variant data. It’s calling a function that checks that the SKU on the variant is unique. This block of code was apparently added in version 2.1.12 of WooCommerce. All you really need to do to “fix” the problem and restore the old functionality that allowed duplicates is to comment out that line of code, and replace it with this:

$unique_sku = true;

(OK, technically it would probably be better to write another function that makes sure that the SKU is unique in the system except within variants of this particular product, but that’s a topic for another day… or at least a passing mention again at the end of this post.)

I put “fix” in quotation marks up above because, trust me, you’re not fixing anything by editing the core files of a plugin directly. You’re just setting yourself up for future problems. Next time there’s an update, and a well-meaning person runs it — say, oh, I don’t know… you — your changes will get wiped out. You need a way to make your changes somewhere else. You need to be able to override this functionality without changing the plugin itself. This is WordPress 101, people. Maybe you need a refresher course. (It’s all ball bearings nowadays!)

What makes this so difficult?

As it happens, there are a number of exacerbating factors that make fixing this particular problem in a safe way incredibly challenging.

1) PHP doesn’t let you override functions that have already been declared. If it did, I could very easily just redefine wc_product_has_unique_sku() in my own way, a way that does mostly what it currently does, but with the added allowance for duplicate SKUs for variants of the same product.

One potential solution I tried pursuing, even though I knew it wouldn’t work, was to “redefine” the function inside an if (function_exists()) conditional. This is a fairly common feature of WordPress and some plugins that is called making functions “pluggable”. The idea is that if you declare your function inside that conditional, and it’s already been declared somewhere else earlier, no problem. Files inside your theme are loaded first, so this allows you to write your own versions of pluggable functions and override the defaults.

But this “pluggable” conditional has to be in the core files in order to work. I can’t put that conditional into my functions.php file, because either it loads after the plugin (it doesn’t, but bear with me for sake of argument) and doesn’t do anything, or (as it actually happens) it loads before the plugin, declares the function, and then when the plugin loads PHP throws a fatal error.

Like I said, I knew this wouldn’t work, for the exact reasons described above. But I tried it anyway. Guess what. It didn’t work. Fatal error. Moving on…

2) WordPress offers “hooks” to allow you to override functionality, but only if that functionality uses a hook. Not every function call in WordPress, or in plugins, uses hooks. A lot of them do, and that’s great. But the call to wc_product_has_unique_sku() at line 1547 in the save_variations() method of the WC_Meta_Box_Product_Data class, found inside that aforementioned class-wc-meta-box-product-data.php file, does not. At least not directly. So the only way this solution will work (hint, hint) is if you can find a place farther up the logic chain that does.

3) The one way PHP does let you override functions is through a PECL extension that is probably not installed on your web server. If I were hosting this client’s site myself, on my own server, I’d just install the necessary PECL extension so the override_function() function was available to me. But I’m not, so I can’t.

4) As designed, WooCommerce only allows you to override files inside its templates subdirectory. You may see a lot out there about solving all of these kinds of problems I’m describing by simply creating a woocommerce directory inside your theme and replicating files from WooCommerce there. Yeah, you can do that, and it works great. As long as the file you’re trying to override is in the templates directory of the main WooCommerce plugin. Anything else is untouchable. Trust me. I tried.

But… you said you found a solution, right?

Yes. I did.

Well… what is it?

You’re not going to like it.

A solution is a solution. C’mon, just tell me!

OK, fine. I found a solution that is ugly and messy, and only works in my specific case. And it’s not at all the kind of ideal “replace this function with my own” solution I wanted.

I did a manual backtrace-of-sorts on the functionality surrounding line 1547 in class-wc-meta-box-product-data.php to see if I could find an action, somewhere, that I could hook into. And I did. But it isn’t pretty.

You see, like I said, line 1547, the one and only line in the entire body of WooCommerce that I want to change — I don’t even want to edit the wc_product_has_unique_sku() function because it’s necessary elsewhere; I just want to not run it right here — is deeply nestled inside a delightfully complex method called save_variations() that weighs in at 263 lines of code. What’s worse, this method is only called in one place in the entirety of WooCommerce, and that’s in the save() method within the same file. Guess what… the save() method is 434 lines of code. Yikes.

Next up, I needed to find any places in the WooCommerce code base where that method appears. And as it happens it only appears in one place… and it’s an add_action() call! At last, we have a hook!

In the file includes/admin/class-wc-admin-meta-boxes.php, at line 50, we have the following:

add_action( ‘woocommerce_process_product_meta’, ‘WC_Meta_Box_Product_Data::save’, 10, 2 );

That’s it. One static instance of the method, inside an action. That’s something we can use, but… oh, no. Does that really mean… but wait, you’re sure there’s… uh… oh.

Yes, we can override this method by creating our own class that extends WC_Meta_Box_Product_Data, and then removing this action and replacing it with another one that runs the save() method inside our class instead. And we can change anything we want inside that save() class. And it will work. I tried it.

But it means we have to replicate all of the rest of that code inside our custom class. We don’t have to replace any of the methods of the parent class that we’re not changing. But we have to replace both the save_variations() method and the save() method that calls it, because at line 1442, there’s… this:

self::save_variations( $post_id, $post );

That self means that if we just change save_variations() in our class, it’ll never run. We need to bring over the entirety of both of those methods into our class — all combined 697 lines of code — just to replace that one line.

That said… it does work. And it’s safe from future WooCommerce plugin updates. As long as nothing else in those two methods gets changed.

Practically speaking, it means remembering before every WooCommerce update to do a diff on class-wc-admin-meta-boxes.php between the new version and the version you’re currently running, then making sure any changes that appear in those two methods get copied over into your child class.

Is this really any better than editing the core and just remembering to redo your change every time there’s an update? I think you could argue that it’s not, but I’ll still say it is, because at least your code won’t disappear when the update gets run. Something still might break… but if this file hasn’t been changed, it won’t. Editing the core file directly guarantees your fix will break when an update runs, and it’s much harder to get your code back. (Hope you kept a backup.)

Let’s put it all together

Now that you’ve seen what we need to do, let’s do it. This goes into your functions.php file in your theme, or, as I have it, into a separate file that gets include()’d into your functions.php file.

<?php

class My_WC_Meta_Box_Product_Data extends WC_Meta_Box_Product_Data {

  public static function save( $post_id, $post ) {
    // Yes, put the ENTIRE WC_Meta_Box_Product_Data::save() method in here, unchanged.
  }

  public static function save_variations( $post_id, $post ) {
    // Again, the ENTIRE WC_Meta_Box_Product_Data::save_variations() method goes in here,
    // unchanged EXCEPT for this line, which you comment out and replace with the next:
    // $unique_sku = wc_product_has_unique_sku( $variation_id, $new_sku );
    $unique_sku = true;
    // Everything else that came after in the original method goes here too.
  }
}

remove_action('woocommerce_process_product_meta', 'WC_Meta_Box_Product_Data::save', 10);
add_action('woocommerce_process_product_meta', 'My_WC_Meta_Box_Product_Data::save', 10);

?>

Well, that’s profoundly unsatisfying

I agree. This is not the solution I was looking for. I’m sure it’s not the solution you’re looking for. But for now at least it seems like the best only way.

What I would really like as a solution requires changes to the WooCommerce core:

1) This would really be great: a checkbox option in the WooCommerce settings to allow duplicate SKUs for variants. I built my own CMS and used it for almost every project for years before fully embracing WordPress as a general purpose CMS. Eventually, it got to a point where some clients were asking for functionality changes that might conflict with ways other clients were using the system. Any time I introduced a significant functionality change, even if it was fixing a “bug” that some clients might assume was a “feature”, I always set it up as a configurable option in the system, with the default setting being the way it already was.

2) Not as great, but this would still help: all WooCommerce functions being made “pluggable”. If only the declaration of the wc_product_has_unique_sku() function inside wc-product-functions.php were wrapped inside an if (function_exists()) conditional, I would be able to write my own version of that function that allowed duplicate variant SKUs. In fact that would be a much more powerful solution than what I’ve done, because I could write it to make sure the variants’ SKUs were only duplicates within the same product. I haven’t tested it thoroughly yet, but I’m pretty sure my actual solution will do nothing to prevent duplicate SKUs for variants of different products, as well as the same product.

Here’s hoping someone with commit rights on the WooCommerce project sees these suggestions and acts on them. In the meantime, I’ll have to live with my convoluted solution. I hope this helps shed some light on ways you might be able to find your own way around this sticky problem.

Micro-Prog

Last night I learned about the Free Music Archive’s microSong challenge — create a song 15 seconds or shorter — and I knew immediately that I wanted to participate.

But not just participate. I wanted to record a prog rock epic suite that was 15 seconds long. Today I took the challenge. My song has been submitted, but as of this writing it’s not yet up on the site. However, that doesn’t stop me from sharing it here, because the contest requires that each piece of music be released under Creative Commons Zero license. So, here it is! Do whatever you want with it! No one can stop you!

The piece of music I created is called The Fall of the Village of the Infinitesimal Forest at the Hands of the Royal Aggressor [An Epic in Seven Parts]. In fact it’s not just seven parts, it’s seven measures.

In those seven measures there are 6 time signature changes (6/8, 7/8, two measures of 2/4, 7/8, 9/8 and 5/16). The song features a recorder trio (yes, those are real recorders, feebly played by me); MIDI drums, electric piano, B3 organ, Mellotron and analog synth; and real electric bass, two electric guitars and an acoustic guitar.

Here are the titles of the seven parts (and their time signatures):

I. In Which the Villagers Engage in Their Melancholy Dance [6/8]
II. Theme of the Royal Aggressor [7/8]
III. The Battle of the Infinitesimal Forest [2/4]
IV. A Hero Awakens in the Gloaming [2/4]
V. In Which the Hero Mourns His Fallen Love [7/8]
VI. Theme of the Royal Aggressor [Reprise] [9/8]
VII. In Which the Lone Survivor Surveys the Ruins [5/16]

And… here’s the music: