Actually, the real bane of the web designer is that I can’t even post this directly on social media like Facebook or Twitter because they automatically convert images to JPEG and compress the hell out of them!
Actually, the real bane of the web designer is that I can’t even post this directly on social media like Facebook or Twitter because they automatically convert images to JPEG and compress the hell out of them!
The first sign that something was wrong was when I tried to create a new page on the client’s site. The blue Publish button I normally see was replaced with Submit for Review. What the…? That’s what WordPress users with the lowly Contributor role usually see. But I’m an Administrator — the most mighty role known to the world of (single-site) WordPress. (Yes, multi-site installations also confer the fearsome title of Super Admin upon a select few.)
Worse still, if I tried to click Submit for Review, it wouldn’t actually save!
Other problems abounded — I tried to create a new user with Administrator privileges, just to see if my own user account was corrupt. Couldn’t save that, either.
I had Debug Bar installed, and I noticed it was giving an error:
WARNING: wp-admin/includes/post.php:641 - Creating default object from empty value
get_default_post_to_edit
Well, that’s not good. Googling the error didn’t lead to anything immediately helpful, besides this comment that led me to explore the database structure in phpMyAdmin for any problems.
Yes, there were problems. Many of the tables, including wp_options
, wp_posts
, wp_postmeta
and wp_users
were missing their primary keys. A bit more digging into the WordPress core showed that, for complex reasons (i.e. I don’t totally get it), without primary keys on these tables, WordPress can’t determine the post type of a new post, and if it can’t determine the post type, it can’t determine the user’s capabilities with regard to that post type, which all comes back to…
WARNING: wp-admin/includes/post.php:641 - Creating default object from empty value
get_default_post_to_edit
Googling on the matter of WordPress tables missing their primary keys (or, perhaps more pertinently, their auto-increments), led me to a solution!!
Fixing WordPress indexes, foreign keys and auto_increment fields
Well, a partial solution. Because the database I was working with was not damaged in exactly the same way as the one the OP was working with, I couldn’t use the sample code directly. I had to go through the database and manually create a few primary keys, delete a bunch of auto-draft posts that all had an ID of 0
, etc. Then I had to skip a few lines of the OP’s SQL code because they referred to tables that hadn’t lost their keys in my case, for whatever reason. But this is the… key… to solving the problem.
Now then, how did the database get this way? Well, the site lives on a fairly creaky old Fatcow (ugh, that name) shared hosting account, running an old version of MySQL and an almost unrecognizably ancient version of phpMyAdmin. We were undertaking major content changes on the site, so I copied it over to my own sleek, modern staging server running the latest and greatest of everything. The idea was that we’d get all of our changes in place just the way we wanted on the staging server, rather than mess up the live site for 2-3 weeks, and when we were done, we’d just copy everything back over.
Slick. Right? Sure, if both servers are running reasonably identical software versions. Which of course is never the case. Ever.
Apparently when I copied the site back to Fatcow, due to the older MySQL (or possibly phpMyAdmin) version, certain things like the primary keys and auto-increments — and, I’d be willing to bet, but I’m not sure it matters, the collation as well — got lost along the way.
I’m working on a site right now that has a fixed, full-bleed (i.e. background-size: cover
) background image on the <body>
. The content flows over it, mostly obscuring it completely, but the background is occasionally revealed in the spaces between content blocks. Some blocks have a semi-transparent background so you can see the fixed background as if through frosted glass.
Here’s the CSS:
body {
background: rgb(255,255,255) url('../images/ui/body_bg.jpg') center center no-repeat fixed;
background-size: cover;
}
It’s a cool effect, but it really, really does not want to play nicely on mobile. Various odd things happen on both Android and iOS, and they are completely different.
Quick side note: Yes, the background image is a JPEG. Normally I only use PNG or SVG images in UI elements, but I had good reason to use JPEG here: even though it’s only two colors (with some in-between colors due to antialiasing), the pattern in the background is incredibly complex, and a JPEG version of the file is about 1/5 the size of the PNG. And since it’s an illustration, I tried making an SVG version first, but the pattern is so large that the SVG was about 2 MB! So JPEG it is… which may be a factor in the issue I’m having on Android, but I haven’t tested a PNG version of the image to verify that.
I’m an iPhone user, so I mainly test responsive sites on iOS. I do own an Android phone (a Motorola Moto E, which I highly recommend as a cheap-but-decent Android phone for testing), but I generally only break it out during the final round of browser testing prior to launching a site.
The issues with background images on iOS are well-known to most web developers. iOS has a number of rather arbitrary seeming limitations imposed upon the Mobile Safari browsing experience, generally for one of three reasons: 1) performance, 2) touch interface usability, 3) the whims of the ghost of Steve Jobs. In the case of background images, background-attachment
is not supported. I’m not really sure how this would impact either (1) or (2) — although I think with the early underpowered iPhone generations, it did impact performance — so I think we’re dealing mostly with (3) here. At any rate, because you can’t have an attached background on iOS, I added this in my media queries:
@media screen and (max-width: 782px) {
body {
/* For background handling on iOS */
background-attachment: scroll; background-repeat: repeat;
}}
Another quick side note: Why is my phone break point at 782 pixels, you ask? Because that’s where WordPress has its break point for the admin interface. I’m not exactly sure why the WP team chose that number, but why fight it?
Besides the background attachment, there’s also the issue that background-size: cover
on a phone is going to make the background image huuuuuuuuuge because it’s scaling it to fit the height of the page content, not the screen size. I initially solved that with background-size: 100%;
, since we’re now allowing the background to repeat. As you’ll see, however, that led to problems on Android, so I ended up scrapping it.
Yes, Android has problems. Don’t even get me started! But I wasn’t prepared for this.
I opened the page in Android, and, although the background image was displaying as I expected in terms of size and attachment, it looked… awful. The original source image I am working with is a generous 2400 x 1857 pixels, enough to look reasonably sharp on most displays, even at high resolution. And it looks great on my Mac, great on my iPhone. But on the Android phone it was splotchy and low-res looking… like it had been reduced to 200 pixels and then upscaled (which is maybe what Android is doing, somehow… and here is where I’m wondering if the image being a JPEG is a factor, but that’s just a stab in the dark).
I tried a number of possible solutions, the most obvious being to set exact pixel dimensions for the image. I tried 1200 x 929, basically a “x2” size for high-res devices. Still looked like crap. I even tried setting it to 2400 x 1857, the actual dimensions of the image, and it looked like crap… and I don’t mean pixel-doubled, which is what it actually should be; I mean the same splotchy weirdness I had been seeing at other sizes.
And then I discovered David Chua’s solution:
html {
/* For background image scaling on Android */
height:100%; min-height:100%;
}
Yet another quick side note: Here I am not placing this inside a media query. We don’t want to only fix this issue on phone screens. Granted, the iOS solution above needs to work on iPads, too… something I haven’t really solved here. I’m workin’ on it!
This change for Android worked perfectly! By this point I had, temporarily at least, removed the iOS workarounds I mentioned above, so on Android the background image was not only perfectly scaled to the browser window, looking sharp and clean, but it was even fixed-position, just like on desktop!
But… the image was back to being huuuuuuuuuge on iOS. Apparently this html
trick for Android does absolutely nothing on iOS, so you’re left trying to find another solution that won’t simultaneously break Android.
It’s not perfect, but I found that if I put both of these tricks together, everything works… the only thing we lose is the fixed-position treatment that Android allows but iOS does not. But the background looks great on both platforms and most importantly, behaves consistently on both.
Here’s the complete code I’m rolling with, for now:
html {
/* For background image scaling on Android */
height:100%; min-height:100%;
}@media screen and (max-width: 782px) {
body {
/* For background handling on iOS */
background-attachment: scroll; background-repeat: repeat;
}}
As noted above, this doesn’t really address iPads. A simple solution would be to change the media query to @media screen and (max-width: 1024px)
, but a) that doesn’t account for the larger iPad Pro and b) it also means a desktop display will lose the proper background effect if the window is smaller than that size. I don’t really have a solution; an adaptive treatment using either server-side or JavaScript-based browser detection would be a consideration, but I don’t really like resorting to that sort of thing for something as basic as this.
It doesn’t help that I recently gave my iPad to my daughter so I don’t currently have a tablet of any kind for testing. That’s about to change as I have a newly ordered Kindle Fire arriving today, but of course that’s not going to give me the answer for an iPad. I can try Responsive Design Mode in desktop Safari, but that’s not always a perfect representation of the quirks of an actual mobile device.
Still… this combined solution for phones is an improvement over the default behavior in both cases.
Whew… that title was almost as long as the variable name I’m about to throw out in a code example.
I spent well over an hour beating my head against the wall on this problem today before narrowing it down to a Gravity Forms issue. The scenario: I have a site that is loading iframes from a different subdomain. As is common in this situation, I wanted to be able to adjust the height of the iframe with JavaScript, to match the height of the page within the iframe and prevent internal scrollbars.
The solution to that problem is readily available on teh interwebz, with the addition of a bit of extra JavaScript to allow cross-site scripting: both the containing page and the contained page need to specify the same document.domain
so browsers will let them talk to each other.
Not long after we put this in place, my client informed me that none of their AJAX-based Gravity Forms were working. The spinner would just spin indefinitely, even if (usually) the form actually did submit properly. It didn’t take me long to narrow the problem down to a JavaScript error pertaining to cross-site scripting. I found that AJAX and document.domain
don’t mix. Or at least that seemed to be the issue.
But that’s where I hit a wall. No one else seemed to be describing the exact problem I was having. Most solutions involved adding a Access-Control-Allow-Origin
header, but that didn’t do anything for me.
Eventually I realized that was because the problem wasn’t with the AJAX, per se. It was the fact that Gravity Forms adds its own hidden iframe where it works some secret mojo on AJAX submissions. And that iframe needed to have document.domain
added to it, just like my site and the other subdomain I was loading in iframes did.
So the question then was, is there a Gravity Forms hook to modify its iframe output? Fortunately, the answer is yes.
The gform_ajax_iframe_content
filter pretty much does what it says on the tin. Add a filter to insert the necessary JavaScript, and you’re good. The only thing I don’t get about this is the name given to its lone input parameter. I mean, really? (Actually… I do think I understand it, but I don’t understand it.)
Anyway… here’s what you need to make this work. Just replace example.com
with the correct domain name. And if you’re running on a version of PHP before 5.3, you won’t be able to use an anonymous function. But you’re not running an old version of PHP, are you?
add_filter('gform_ajax_iframe_content', function($doctype_html_html_head_meta_charset_utf_8_head_body_class_gf_ajax_postback_form_string_body_html) {
echo "<script>document.domain = 'example.com';</script>\n";
return $doctype_html_html_head_meta_charset_utf_8_head_body_class_gf_ajax_postback_form_string_body_html;
});
Update (April 12, 2023): If you’re looking to use my RSS Enhancements for The Events Calendar plugin in conjunction with MailChimp in 2023, please note that MailChimp has changed up how they handle RSS feeds in campaigns. A user of the plugin provided some insight on the changes over in the WordPress Support Forums.
This one’s a doozy. I have a client who is using The Events Calendar, and they want to automate a weekly email blast listing that week’s events, using MailChimp.
The Events Calendar automatically generates an RSS feed of future events, inserting the event’s date and time in the RSS <pubDate>
field. And MailChimp offers an RSS Campaign feature that can be scheduled to automatically send out emails with content pulled in from an RSS feed.
So far so good. But there were a few things the client wanted that were missing:
To make this happen, I had to first get the RSS feed to actually contain the right data. Then I had to modify the MailChimp campaign to display the information.
The problem in both cases surrounded documentation. RSS, though it’s still widely used, is definitely languishing if not dead. The spec is well-defined, but there’s not a lot of good information about how you can customize the WordPress RSS feed, and even less about how to customize The Event Calendar’s version. What info I could find was generally outdated or flat-out wrong — like the example in the official WordPress documentation (the old documentation, to be fair) that has at least three major errors in it. (I’m not even going to bother to explain them. Just trust that it’s wrong and you shouldn’t use it.)
Now that I’ve put in the hours of trial and error and futile Googling, I’ll save you the trouble and summarize my successful end result.
It took a surprising amount of effort to figure out how this is done, although in the end it’s a very small amount of code. Part of the problem was that I was not aware of the posts_per_rss
query parameter, and therefore I wasted a lot of time trying to figure out why posts_per_page
wasn’t working. Maybe that’s just my dumb mistake. I hope so.
I also spent a bunch of time trying to get a meta_query
working before I realized that The Events Calendar adds an end_date
query parameter which makes it super-easy to define a date-based endpoint for the query.
You need both of these. Depending on how full your calendar is, the default posts_per_rss
value of 10 is possibly not enough to cover a full week. I decided to change it to 100. If this client ever has a week with more than 100 events in it, we’ll be in trouble… probably in more ways than one.
Here’s the modification you need. Put this in your functions.php
file or wherever you feel is appropriate:
// Modify feed query
function my_rss_pre_get_posts($query) {
if ($query->is_feed() && $query->tribe_is_event_query) {
// Change number of posts retrieved on events feed
$query->set(‘posts_per_rss’, 100);
// Add restriction to only show events within one week
$query->set(‘end_date’, date(‘Y-m-d H:i:s’, mktime(23, 59, 59, date(‘n’), date(‘j’) + 7, date(‘Y’))));
}
return $query;
}
add_action(‘pre_get_posts’,’my_rss_pre_get_posts’,99);
What’s happening here? The if
conditional is critical, since pre_get_posts
runs on… oh… every database query. This makes sure it’s only running on a query to retrieve the RSS feed and, specifically, The Events Calendar’s events query.
We’re changing posts_per_rss
to an arbitrarily large value — the maximum number of events we can possibly anticipate having within the date range we’re about to set.
The change to end_date
(it’s actually empty by default) sets a maximum event end date to retrieve. My mktime
function call is setting the date to 11:59:59 pm on the date one week from the current date. You can just change the 7
to another number to set the query to that many days in the future. There are a lot of other fun manipulations you can make to mktime
. Check out the official PHP documentation if you’re unfamiliar with it.
Every add_action()
call can include priority as the third input parameter. Sometimes it doesn’t matter and you can leave it blank, but in this case it does matter. I’m not sure what the minimum value is that would work, but I found 99
does, so I stuck with that.
RSS is XML, so it has a syntax similar to HTML, but with its own specific tags. (And with XML’s much stricter validation requirements.) WordPress uses RSS 2.0. This can get you into trouble later with the MailChimp integration, because MailChimp’s RSS Merge Tags documentation gives an example of the RSS 1.5 <media:content>
tag for inserting images, but you’ll actually need to use the <enclosure>
tag… which MailChimp also mentions, but not in conjunction with images. Still with me?
All right, so the first thing we’re going to need to modify in the RSS output is the images. And don’t believe that official WordPress documentation I mentioned earlier. It. Is. Wrong. My way works.
The next thing we want to do, and we’ll roll it into the same function (because I want to contain the madness), is to add in the event’s location. There’s no RSS tag to account for something like this. You could add it to the <description>
tag, although I found that since the WordPress rss2_item
hook seems to be directly outputting RSS XML as it goes, I didn’t track down a way to modify any of the output, just add to it.
There’s another standard RSS tag that WordPress doesn’t use — or at least doesn’t seem to use — the <source>
tag. This is supposed to be used to provide a link and title of an external reference for the item, but I’m going to take the liberty of misusing it to pass along the location name instead. In my particular case I’m not using it as a link; I just need the text of the location name. But the url
attribute is required, so I just stuck the event’s URL in there. (I also added a conditional so this is only inserted on events, not on other post types. But for images I figured it would be a nice bonus to add the featured image across all post types on the site. You may want to add your own conditionals to limit this.)
Here we go:
function my_rss_modify_item() {
global $post;
// Add featured image
$uploads = wp_upload_dir();
if (has_post_thumbnail($post->ID)) {
$thumbnail = wp_get_attachment_image_src(get_post_thumbnail_id($post->ID), ‘thumbnail’);
$image = $thumbnail[0];
$ext = pathinfo($image, PATHINFO_EXTENSION);
$mime = ($ext == ‘jpg’) ? ‘image/jpeg’ : ‘image/’ . $ext;
$path = $uploads[‘basedir’] . substr($image, (strpos($image, ‘/uploads’) + strlen(‘/uploads’)));
$size = filesize($path);
echo ‘<enclosure url=”‘ . esc_url($image) . ‘” length=”‘ . intval($size) . ‘” type=”‘ . esc_attr($mime) . ‘” />’ . “\n”;
}
// Add event location (fudged into the <source> tag)
if ($post->post_type == ‘tribe_events’) {
if ($location = strip_tags(tribe_get_venue($post->ID))) {
echo ‘<source url=”‘ . get_permalink($post->ID) . ‘”>’ . $location . ‘</source>’;
}
}
}
add_action(‘rss2_item’,’my_rss_modify_item’);
You might be able to find a more efficient way of obtaining the $path
value… to be honest I was getting a bit fatigued by this point in the process! But it works. You really only need that value anyway in order to fill in the length
attribute, and apparently that value doesn’t even need to be correct, it just needs to be there for the XML to validate. So maybe you can try leaving it out entirely.
OK… I’m not going to tell you how to set up an RSS Campaign in MailChimp. I already linked to their docs. But I will tell you how to customize the template to include these nice new features you’ve added to your RSS feed.
Edit the campaign, and once you’re in the Campaign Builder, place an RSS Items block, then click on it to open the editor on the right side. Set the dropdown to Custom, which will reveal a WYSIWYG editor full of a bunch of special tags that dynamically insert RSS content into the layout. For the most part you can edit everything here… except for the image. You’re going to have to insert one of these tags into the src
attribute of the HTML <img>
tag. That requires going into the raw code view, which you can access by clicking the <> button in the WYSIWYG editor’s toolbar.
A few key tags:
*|RSSITEM:ENCLOSURE_URL|*
This is your code for the URL of the image. Yes, it has to be put into the src
attribute of the <img>
tag directly. There’s not a way that I could find to get MailChimp to recognize an <enclosure>
as being an image and display it inline.
*|RSSITEM:SOURCE_TITLE|*
This will display the location name, if you added it to the <source>
tag.
*|RSSITEM:DATE:F j - g:i a|*
I just though I’d point this out: you can customize the way MailChimp shows an event date by inserting a colon and a standard PHP date format into the *|RSSITEM:DATE|*
tag. Nice!
If you’re interested in a nice layout with the featured image left aligned and the event info next to it, here’s something you can work with. Paste this in its entirety into the WYSIWYG editor’s raw code view in place of whatever you have in there now. Yes, inline CSS… welcome to HTML email!
*|RSSITEMS:|*
<div style=”clear: both; padding-bottom: 1em;”>
<img src=”*|RSSITEM:ENCLOSURE_URL|*” style=”display: block; float: left; padding-right: 1em; width: 100px; height: 100px;” />
<h2 class=”mc-toc-title” style=”text-align: left;”><a href=”*|RSSITEM:URL|*” target=”_blank”>*|RSSITEM:TITLE|*</a></h2><div style=”text-align: left;”><strong>*|RSSITEM:DATE:F j – g:i a|*</strong><br />
*|RSSITEM:SOURCE_TITLE|*</div><div style=”clear: both; content: ”; display: table;”> </div>
</div>
*|END:RSSITEMS|*
Update! I have encapsulated this functionality, along with some configuration options, into a plugin. You can download it from the WordPress Plugin Directory.