• Skip to primary navigation
  • Skip to main content
  • Skip to footer
  • Store
  • Support
  • Theme Documentation
  • My Account
  • Cart

9seeds

Building Custom WordPress Solutions | Plugin Development

 
  • Custom Development
  • Themes
  • Plugins
  • About
  • Contact
  • Blog

Tech Talk

Adjusting Code Increases MemberPress Value

Using MemberPress to Get that Magazine Feeling

Posted on November 6, 2017

Our customers come up with the best ways to use software sometimes. I recently had to have my eyes opened to the fact that just because we are in a digital age doesn’t mean we have to abandon all the old tools available to us.

Let’s say you want to do a magazine style site where monthly content is given to subscribers. A standard way of implementing this with membership software is to charge for a monthly subscription which gives you access to everything, Then if you cancel, you lose access to the site. In one respect this model gives people the incentive to remain a customer but in another sense, it’s not exactly fair.

  • If a customer paid for a subscription and received 3 “issues” why wouldn’t they retain access to that over time?
  • Another thing to think about is that if you are expecting your customers to pay monthly, you have to keep the prices low enough such that they don’t notice it on the bill. This is how Time gets away with it at just $2.50/mo. That’s low enough to just pay forever without worrying about it and having access to (quick google search) 93 years worth of back-issues.
  • If you want to charge a higher subscription rate you have to offer a little more and one of those things can be the fact that they get to retain access to issues they were subscribed.

So… how to implement this? Without code there really isn’t a way to accomplish this. The reality is it completely breaks the concept of a membership site to allow people access to premium content after they are no longer a member.

MemberPress developers to the rescue.

MemberPress recently implemented a rule which allows for this exact scenario. At its simplest form it states, “Even though you have access to this resource, I’m going to deny you anyway.” The way we correct this is to simply not protect the magazine issues and instead write a little snippet of code which asks, “This issue was published on March 9th, does the user attempting to access this issue have a transaction for March 9th?”

With the chunk of code below we check and make sure they have a transaction covering the time period they’re trying to access. If they do, we allow them to see what they’ve already purchased.

We love taking awesome products like MemberPress and tweaking them to provide the exact functionality our clients desire. If you have a WordPress or MemberPress customization project, please get in touch:

Get in Touch with 9seeds

add_filter('mepr-last-chance-to-block-content', 'deny_selective_access_9s');
function deny_selective_access_9s($deny,$current_post,$uri) {
    //short circuit if it's not even a magazine issue post type
    if($current_post->post_type != 'magazine-issue')
        return $deny;

    //if there isn't even a user block access. We don't want to do a lot of 
    //work looking up transactions if they aren't even logged in
    $user = new MeprUser(get_current_user_ID());
    if(!$user->ID)
        return true;

    //add a custom where to get transactions by user by date
    add_filter('mepr_transaction_get_complete_by_user_id_custom_where', 'deny_selective_access_where_9s');
    
    $num = get_all_complete_by_user_id( $user->ID,
        '', //order by
        '', //limit 
        true, //count only
        true, //include expired
        false, //include confirmations
        true //include custom where
    )
    
    //do not leave this filter lying around to confuse other parts of the memberpress software
    remove_filter('mepr_transaction_get_complete_by_user_id_custom_where', 'deny_selective_access_where_9s');

    if(!$num)
        $deny = true;

    return $deny;
}
function deny_selective_access_where_9s($where,$user_id){
    global $post,$wpdb;
      $where .= $wpdb->prepare(
        'AND %s > t.created_at  AND %s < t.expires_at ', $post->post_date,
        $post->post_date
      );
    return $where
}
Continue Reading

Todd Huish

    More by Todd Huish

    Using Memberpress to Convert Free Users to Paid

    Posted on January 10, 2017

    MemberPress is a great WordPress membership plugin that lets you create rules to protect your content and sell subscriptions to access that content. Making customized exceptions to those rules though has long been a bit challenging even for WordPress developers.
    Thanks to filters added in the latest version of MemberPress however it’s now much easier to customize MemberPress rules! We did that for a client recently.

    Having a Successful Membership Site

    A client came to us with an existing site that had a lot of content generated over time and building a sizable list of registered users. They wanted to augment their free subscription model with selling memberships to access some of that same content.

    MemberPress was built for this kind of scenario!  They installed the plugin and had already set up the following tags and MemberPress rules:

    1. Made a whole library of premium posts
    2. Tagged posts appropriately: Beginner, Intermediate, and Advanced
    3. Created 3 paid membership levels in MemberPress (Beginner for $5/mo, Intermediate for $7/mo, and Advanced for $10/mo)
    4. Set up 3 MemberPress rules to:
      • Protect all content tagged Beginner and allow Advanced, Intermediate, and Beginner memberships to access it
      • Protect all content tagged Intermediate and allow Advanced, and Intermediate memberships to access it
      • Protect all content tagged Advanced and allow Advanced memberships to access it

    With the exception of creating the content, it’s a very straightforward setup, each membership level can access all the content for their subscription level and below.

    Converting Existing Users to Paid Users

    It’s easy to direct traffic to your sales page and to get first-time site visitors to signup for something free, but what about getting all the existing free registered users to sign up for a paid membership?

    It’s also becoming common practice to build semi-permeable content gateways where a visitor can access a little content for free with or without registering but then getting locked out from viewing more content until they subscribe.

    There’s no way in MemberPress at the moment to allow limited access to some things if they don’t have a MemberPress membership of some sort though. MemberPress recently added a new hook in their WordPress plugin, however that helps with this exact scenario.

    What this hook allows is for a site owner to do is send an email to all their currently registered users and say “Hey, we have all this new premium content, you should buy it but as a long time registered user we’re going to give you unrestricted access to a premium lesson so you can try it out”.

    By setting this up in code and allowing an existing user to get access it works out better because there’s no friction to the customer. You lose a certain percentage of people for every click you make them do to get a thing so by handling it this way it just appears to work magically as far as the customer is concerned. Another way to solve this would be to make a new membership and only allow existing members to sign up for it.

    We discussed this with the client but they wanted to offer their existing base access to any single lesson they wanted. By creating a membership and then putting in a rule it would either overly give these users access to the entire library or you would have to pick a single lesson to showcase as the “free” premium one.

    Custom Code for MemberPress

    All of the non-code ideas were a compromise and so we went with the original plan and put in the following snippet for them.

    add_filter('mepr-pre-run-rule-content', 'allow_premium_access_9s' ,10,3);
    function allow_premium_access_9s( $protect, $current_post, $uri ) {
        $user = new MeprUser(get_current_user_ID());
        $prd = get_page_by_title('Premium Membership', OBJECT, MeprProduct::$cpt);
        //make sure we have a user and the premium membership lookup succeeded
        if($prd->ID && $user->ID) {
            //if the user isn't already a premium member
            if(!$user->is_already_subscribed_to($prd->ID)) {
                $promo_access = get_user_meta($user->ID,'promo_access',true);
                if(!$promo_access) {
                    //if the user does not already have promo access to a lesson
                    //allow them in to this one
                    update_user_meta($user->ID,'promo_access',array('lesson'=>$current_post->ID,'expiration'=>strtotime("+1 month")));
                    $protect = false;
                } elseif($promo_access['lesson'] == $current_post->ID && $promo_access['expiration'] > time()) {
                    //The user has previously chosen this to be their promo lesson and
                    //their special access is not expired
                    $protect = false;
                }
            }
        }
        return $protect;
    }
    

    The thing about this rule is that it allows for some sneaky special things anytime you are running a promotion. Another good example is allowing access to a protected post if there’s a special code on the URL. Put that URL code right into your Facebook ad’s target URL and have someone get to see premium content for 1 click. Then when they go off the page you let the unauthorized message do its job and upsell them to membership status.

    With the new MemberPress customizations, you can now selectively grant access to protected parts of your site without making complicated interlocking sets of rules for promotional items which sometimes live and die within days.

    Continue Reading

    Todd Huish

      More by Todd Huish

      How to change WordPress permalink structure on WP Engine

      Posted on December 20, 2016

      One of the most user friendly and powerful SEO features of WordPress has always been it’s pretty permalinks feature. Many content management systems (CMS) still to this day have urls that look like http://domain.com/something_incomprehensible/123123c1. Ugly!

      WordPress has supported pretty permalinks (human readable and ending a url with a slug resembling the post name) since version 1.0. There was one catch to those however. You needed to include a date in the format before the post slug (that’s /%year%/%monthnum%/%postname%/ for those familiar with WPs permalink formats). This resulted in URLs like this post’s URL: https://9seeds.com/2016/12/19/change-permalink-structure-wp-engine. Pretty!

      That format has always still been better for SEO than what other CMSs do with crazy random numbers that mean nothing to human or search crawlers. It’d be a little better without the dates in the way however. You could use just the post slug (that’s just /%postname%/ ) on small brochure websites with just a few dozen pages, but on sites with large numbers of posts things would slow down without dates to break things up.

      As of WordPress 3.3 there is no longer any performance hit to using just /%postname%/.  So your URLs can just look like this:  https://9seeds.com/change-permalink-structure-wp-engine/.  Prettier!

      But there’s still a catch. You can’t just go into the WordPress Permalinks settings page and change formats or incoming links to your site using the old format may 404 🙁

      There’s long been a fix for this and our friend Yoast even built a tool to make generating the needed .htaccess rule here since no one likes writing regex or coming up with htaccess rules.  That’s all well and good if you’re on a simple Apache server and want to edit an .htaccess file, but what if you’re on WP Engine? WP Engine uses a higher performance setup with an Nginx proxy in front of Apache. That means you can put those redirect rules into Nginx and they’ll be even quicker than having put them in .htaccess!  Only you can’t directly modify your nginx.conf files on WP Engine, instead WP Engine gives you a super easy to user redirect tab in their panel.

       

      Here’s how you change your permalink structure and setup the needed redirect on WP Engine.

      Step 1:

      Go to Yoast’s handy tool here and generate the needed rewrite rule. It’ll look something like this:

      RedirectMatch 301 ^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(?!page/)(.+)$ https://your-domain.com/$4

      If you’re not on WP Engine you can drop that into your .htaccess file before the WordPress section. In fact it’ll work on WP Engine, just it’s better to use the panel than to modify that file directly via FTP.

      Step 2:

      1. Go to the WP Engine Dashboard and click Redirects rules in the left sidebar

      2. Click the Redirect Rules button at the top right

      3. Set a memorable name for the Redirect.

      4. Take the orange section above, the scary looking Regular Expression that Yoast generated for you (mine is for year/month/date, your maybe be shorter if your existing permalink structure is shorter), paste that into “Source*” from the ^ to the $.

      5. Take the blue section above, the less scary url looking Regular Expression from the http to the $4 (my existing permalink has three date modifiers, hence it keeps the 4th chunk, your’s may be different if you’re using a shorter permalink structure).

      6. Click the Save button

      Step 3:

      1. Go to your WordPress dashboard and click on Permalinks under Settings in the left sidebar

      2. Change your permalink setting to %postname%

      3. Click the Save Changes button

      Step 4:

      Test!

       

      Success? Awesome let us know in the comments below!

      Continue Reading

      Jon Brown

        More by Jon Brown
        WordPress Performance

        WordPress Post Editor Performance

        Posted on August 17, 2016

        WordPress Performance sometimes seems like an endless quest, but there are sometimes little things that come along and yield great rewards.

        Front end and back end WordPress Performance auditing and optimizing is a topic I’ve been meaning to write more depth about since speaking on the topic at a few WordPress meetups, but I wanted to quickly share a performance tip on something I came across today while preparing a bug report for Yoast’s WordPress SEO plugin.

        Spotting Performance Issues in WordPress

        One of the ways we spot performance issues is to run Query Monitor on most of the sites we work on. Query Monitor is a great tool for a quick view into what’s going on on the backend on every page view and I’ll go into more in a future post.

        TL:DR; Skip to #3 at the end.

        I am no Chris Lema, which is a abstract way of saying, “I personally don’t spend much time in WordPress post editor”. However, today I was in there investigating a little visual glitch with the WordPress SEO snippet preview metabox when I also noticed that the post editor was loading very slowly. Thanks to Query Monitor I could instantly see the cause was a slow MySQL Query and it was coming from a function called meta_form().

        Now, this was on a client’s site with over 100,000 posts and with a site that size performance issues show up rather quickly. This however, does affects smaller sites as well though not to the extent it does large publisher websites like this one. Also just to be clear it is really impacted by the number of rows in the postmeta table, not the number of posts directly, but I figured saying 100,000 post made more sense that 1.8 million rows. On the big site like this one though it was causing a 15 second query. While loading the post editor is infrequent, even on a site like this one with over 100 authors, that 15s is taking up a valuable MySQL thread and certainly frustrating authors.

        On my much smaller personal blog it was still delaying page load several seconds and here on 9seeds.com (two sites with no where near as big of a database as our typical client’s site) it was causing a 7s slowdown on the post editor loading.

        WordPress Performance
        Slow meta_form() database query

        Geeky WordPress Developer Details of the slowdown

        WordPress runs a function called meta_form() which displays the custom fields meta box. It runs a somewhat intensive query on the wp_postmeta to generate that box (a box most people don’t even need anymore, but that’s a whole separate issue).

        That query looks like this:

        SELECT DISTINCT meta_key
        FROM wp_postmeta 
        WHERE meta_key NOT BETWEEN '_'
        AND '_z'  HAVING meta_key
        NOT LIKE '\\_%' 
        ORDER BY meta_key 
        LIMIT 30
        

        That’s actually the improved version of the query! When these changes were made to improve the query back in WordPress version 4.3 it seems that only half the needed changes to the database were made for existing sites. The table index was updated to 191 characters, but the field itself was not updated. That mismatch makes that query super slow on large wp_postmeta tables.

        There is a new bug report for this performance issue already. While it seems like a trivial thing to fix any time WordPress core needs to modify the existing database on millions of WordPress it’s a pretty big deal and can take a while to get incorporated.

        The good news is there are a couple fixes you can implement yourself.

        1. (not recommended) My first fix was to truncate the meta_key column to 191 characters with:

        I shouldn’t need to say this but backup your database and test this in a safe local or staging environment before doing this on your live database! Database updates are dangerous things without an “undo” button so be extra careful. If you don’t even know how/where to run this code you probably shouldn’t be doing this sort of thing. Keep reading
        ALTER TABLE wp_postmeta MODIFY meta_key varchar(191);
        

        That worked, but it’s a bit dangerous since you could have a very long meta_key and it could get truncated, so don’t do that, there are better ways.

        2. (recommended) Much easier, in WordPress 4.4 there was a new filter introduced that short-circuits that query ‘postmeta_form_keys‘ which you can provide a pre-defined array of keys to if you need.

        add_filter('postmeta_form_keys', array('my_key1, my_key2');
        

        3. (most recommended) Just ditch loading the ancient custom fields box entirely with:

        /**
         * Remove Ancient Custom Fields metabox from post editor
         * because it uses a very slow query meta_key sort query 
         * so on sites with large postmeta tables it is super slow
         * and is rarely useful anymore on any site
         */ 
        function s9_remove_post_custom_fields_metabox() {
             foreach ( get_post_types( '', 'names' ) as $post_type ) {
                 remove_meta_box( 'postcustom' , $post_type , 'normal' );    
             } 
        } 
        add_action( 'admin_menu' , 's9_remove_post_custom_fields_metabox' ); 
        

        Added to your theme or a core functionality plugin.

        Please do let me know if this helps you in the comments, and stay tuned for a deeper dive into WP site performance auditing and fixes.

        Continue Reading

        Jon Brown

          More by Jon Brown

          The tools you use, the way you want. Wrapping Help Scout in Nativefier

          Posted on July 15, 2016

          One of the things we love about WordPress is that it lets us affordably build custom functionality into business’ websites that not long ago would have required tens if not hundreds of thousands of dollars invested in custom software development costs. Which is to say we think people should be able to have software their way.

          Wrapping Slack “Making WordPress” in Nativefier

          wp-core-slack-900x900In that vein I was delighted when Jason Cosper wrote last week about using Nativefier to create a mini app for the Making WordPress Core Slack Team.

          Like so many businesses these days we use and have quickly come to rely heavily on Slack for our internal team communications. Increasingly however we’re also using it for external communications to keep in touch with specific groups, largely in the tech space but outside it as well.  I’ve heard of families starting their own Slack Team just to keep in touch.

          Personally I belong to 14 Slack Teams: 5 WordPress groups, 7 non-WordPress technology and local community groups, 2! groups dedicated to just to coffee and a few others. It’s crazy.

          One of those WordPress groups is “Making WordPress”, which is a massively huge group of over 10,000 members. It’s a vital communication channel though for everyone involved in the broader WordPress Community. Everything from Core development to organizing WordCamp events to updates to the WordPress.org website to securirty alerts gets discussed there.

          Less to it’s core mission it’s both a great way to keep in touch with friends made at WordCamps as well as a great back channel between agencies working in the WordPress space.

          Jason Cosper discovered the cause of something that had been driving me crazy the last few months with the native Slack app though.  It had become extremely unstable It was frequently crashing and taking up 100% cpu. That’s really frustrating when it’s something you rely on nearly every minute of your work day.

          It turns out a big cause of that was including “Making WordPress” and it’s 10,000+ members in the native app. Jason removed that channel and wrapped it up in it’s own desktop web app using a new tool called Nativefier. It’s much like Fluid and what we called an SSB (Site Specific Browser) apps if you ever used those back in the day.  Since removing Making WordPress from my native Slack app, like Jason, I’ve found it to be much snappier and hasn’t crashed once.

          The now isolated Nativefier app version of Making WordPress still gives me critical desktop notifications of direct messages, plus has the added benefit of staying out of my way most of the day. As you might imagine with 10,000+ members it can be a bit noisey.  It’s a win win.

          Wrapping Helpscout Making WordPress in Nativefier

          Nativefier-Help-ScoutIt occurred to me I could do the same thing with a few websites I like to keep open all the time. Starting with Help Scout which we use for tracking and responding to plugin support requests.

          Steps for Mac OS X

          Step 1:
          Install Nativefier.

          Ok, it’s a bit more complicated. You need to have Node NPM installed first, but if you’re a developer of any kind you probably already do have that. Then just at the command line:
          npm install nativefier -g

          Step 2:

          Wrap up a web page in Nativefier, I did this in ~/Applications and then moved it to /Applications.

          nativefier --name "Help Scout" "https://secure.helpscout.net"

          Step 3 (optional):

          It’ll grab the favicon by default, but his is often low res and ugly.  Replace it with a nicer icon.

          I used https://iconverticons.com/online/ to convert a Help Scout PNG I scrped from their site into the ICNS format I needed for the dekstop app.  You can grab them here for Help Scout if you want: https://iconverticons.com/icons/93a786b5f69feea3/ or create your own.

          Final Step:

          Right click on the new app and select Open. You’re going to need to right click open on it the first time because it’s not an officially signed by Apple app, but you created it so you can probably decide trust it when the pop-up warning about it being untrusted comes up.

          Now if the < 5 minutes it takes to go through those steps yourself is too much, or you don’t have NPM installed, or if your command line shy.  You can download my Nativefier wrapped up app of the Help Scout website here: Nativefier – Help Scout.

           

          I can imagine others using this for Gmail, Calendars, all sort of things…  Let us know what you’ve used it for in the comments!

          Continue Reading

          Jon Brown

            More by Jon Brown
            • Page 1
            • Page 2
            • Page 3
            • Interim pages omitted …
            • Page 5
            • Next

            Footer

            Get in Touch

            • New Project Inquiry
            • Product Support and General Inquiry
            • Store Purchase Terms and Conditions
            • Store FAQ
            • Cookie Policy
            • Privacy Policy

            Our Services

            • Custom WP Development
            • Theme Store
            • Plugin Store

            WordPress Plugins for Sale

            • Time Tracker
            • Authorize.net SIM Gateway

            WordPress Plugins for Free

            • Simple Calendar
            • WP Chargify
            • Facebook
            • Twitter
            • LinkedIn
            • WordPress
            • GitHub

            Copyright 2025 | 9seeds, LLC

            Like nearly all websites this one uses cookies too. Like most users we think consent banners like these are a dumb solution, but it's what we've got until new laws are passed. We use cookies on our website for remembering your preferences, for example if you're logged in or what is in your cart. We also use 3rd party cookies for analytics so we know what pages on the site are most popular. By clicking “Accept”, you consent to the use of ALL the cookies.
            Do not sell my personal information.
            Cookie SettingsAccept
            Manage consent

            Privacy Overview

            This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience and may even preclude you being able to login to the website.
            Necessary
            Always Enabled
            Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
            CookieDurationDescription
            __stripe_mid1 yearThis cookie is set by Stripe payment gateway. This cookie is used to enable payment on the website without storing any patment information on a server.
            __stripe_sid30 minutesThis cookie is set by Stripe payment gateway. This cookie is used to enable payment on the website without storing any patment information on a server.
            cookielawinfo-checkbox-advertisement1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Advertisement" category .
            cookielawinfo-checkbox-analytics1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Analytics" category .
            cookielawinfo-checkbox-necessary1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Necessary" category .
            cookielawinfo-checkbox-others1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to store the user consent for cookies in the category "Others".
            cookielawinfo-checkbox-performance1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to store the user consent for cookies in the category "Performance".
            Functional
            Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
            Performance
            Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
            Analytics
            Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
            CookieDurationDescription
            _ga2 yearsThe _ga cookie, installed by Google Analytics, calculates visitor, session and campaign data and also keeps track of site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognize unique visitors.
            _gid1 dayInstalled by Google Analytics, _gid cookie stores information on how visitors use a website, while also creating an analytics report of the website's performance. Some of the data that are collected include the number of visitors, their source, and the pages they visit anonymously.
            CONSENT2 yearsYouTube sets this cookie via embedded youtube-videos and registers anonymous statistical data.
            Advertisement
            Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
            CookieDurationDescription
            VISITOR_INFO1_LIVE5 months 27 daysA cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.
            YSCsessionYSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.
            yt-remote-connected-devicesneverYouTube sets this cookie to store the video preferences of the user using embedded YouTube video.
            yt-remote-device-idneverYouTube sets this cookie to store the video preferences of the user using embedded YouTube video.
            Others
            Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
            CookieDurationDescription
            cookielawinfo-checkbox-functional1 yearThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
            SAVE & ACCEPT
            Powered by CookieYes Logo