Migrating a static site to WordPress CMS

In this article we will consider that you have already finalize your site on WordPress on a development server, backed up your production site and that you are ready to do the transfer. We will simply mention that once you have finalized you site on the development site don’t forget to redirect your page through .htaccess.

Redirections

Your .htaccess will look like that:

<IfModule mod_rewrite.c>
RewriteEngine On

</IfModule>

# BEGIN WordPress
# Rivit, jotka ovat "BEGIN WordPress" ja "END WordPress" välissä on luotu dynaamisesti ja niitä tulee muokata vain WordPressin filttereillä. Kaikki manuaaliset muutokset riveihin tullaan yliajamaan.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /

#My redirect

RewriteRule ^some_url.html$ https://www.example.com/some/new_url/ [L,R=301,NC]

#End redirect

RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress
#BEGIN CUSTOM

Note that the simple 301 redirect:

 Redirect 301 /oldpage.html http://www.yoursite.com/newpage.html Redirect 301 /oldpage2.html http://www.yoursite.com/folder/

as described here, won’t work as is in the WordPress .htaccess, hence the use of the previous method. Not that the URL being redirected does not use the domain name.

If you have to redirect files and folders that are hosted in a subdomain, make sure to modify the .htaccess accordingly in the affected directory.

We also recommend that you redirect your image to not lose any image traffic.

Moving WordPress

WordPress.org has a good documentation about moving wordpress:

https://wordpress.org/support/article/moving-wordpress/

In our case study, the paragraph about changing domain name and URLs is particularly of interest to us.

One thing that is not explicitly mentioned is that the column guid in the wp_posts column in the database won’t change and still refer to your development server. To solve this you might think of running an SQL command to update the gui to your new domain name:

UPDATE wp_posts SET guid = REPLACE(guid, "old[DOT]com",  "new[DOT]com");

but this operation is not recommended due to data serialization issue. I tested one of the recommended plugin in the article (better search replace) and it worked well.

Uninstall problematic plugins before migration

Note that some plugins might cause issue during the migration because they have hardcoded your previous domain (the development one). In that case you might want to uninstall them before migration. Some plugins doesn’t do proper garbage management and you might need to do some manual cleaning.

In my case, I had to uninstall Wordfence and manually clean the database and some files and folders. You will find all required information on Wordfence site. There is an advance uninstall option that is suppose to do that for you, but I was not aware of it at the time. It seems to be a separate plugin:

YITH WooCommerce Wishlist caused also issue by requiring a file that was not there (and also missing in the dev site but without causing issue). So you can also just uninstall it before backing up for migration and reinstall it once migration is complete. In my case I commented out the incriminated line which allow me to access the dashboard and updated the plugin, which solved the issue.

Minimizing down time

If your site have a large amount of pictures and you want to minimize downtime, we recommend that you upload your picture before hand on the production site ahead of the migration.

If you have followed the moving WordPress tutorial, you have done a backup of your development site and its database, changed the URLs in wp settings – general, which leads to a 404, backup your dev site and its database after the change you can upload the following folder to your production site, without interrupting production: wp-admin, wp-content, wp-includes.

Make sure you have php version compatible with wordpress on your production site.

Create a database to upload the database of your dev site (the one you did after changing the domain in the settings). Note the user name, use password, database name and host as you will need it later.

Finalize migration

Once you have finished transferring your pictures and the WordPress folders and imported your database table in the production database, you are ready to finalize the migration. Delete the folders related to the previous version of your site and upload all the WordPress files that lies at the root of your dev site.

robots.txt

Check that your site is discoverable in WP Reading Settings and double check the robots.txt and do your redirect as indicated at the beginning of the article.

Google search console

Make sure to update your site map URL in case it differs from the version prior to migration.

Analytics

And finally add your analytics tracking code to your new site.

Profit.

Get the time it takes to run a given function in PHP

To get the time in microsecond you will need to use microtime() instead of time().

You will need microtime() to return a float so set $getAsFloat to true like this:

microtime(true);

Most of the function are really fast so to get something measurable you might need to run them several thousands time within a loop:

$start=microtime(true);
for( $i=0;$i<100000;$i++){
  //the function you want to test
}
$end=microtime(true)
$total_runtime=$end-$start;

You might want to use this method to compare two functions and verify which one is the fastest. You can then compare the time it took to run each of your function and pick the smallest one.

Get the category archive name and description in a wordpress theme

If you need to access your achive title and or description, you can the code below. I used it to take the description as meta description. A better policy would be to write a custom metadesciption. But that’s a quick fix while you are working on a better solution.

    $title=get_the_archive_title();
    echo(explode('<',explode('>',$title)[1])[0]);
    if(get_the_archive_description()){
	echo '<meta name="description" content="'.preg_replace('/\<.*\>/',' ',get_the_archive_description()).'">';
    }

Add pagination to WordPress archives or search results.

There are several options to do so.

Here are two exemples, on being taken from the twentyfifteen template.

The class is to apply woocommerce styling in case you are using it.

      <div>
         <nav class="woocommerce-pagination bt_search_pagination"> <?php
          // Previous/next page navigation.

          the_posts_pagination(

              array(

                  'prev_text'          => __( 'Previous page', 'twentyfifteen' ),

                  'next_text'          => __( 'Next page', 'twentyfifteen' ),

                  'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'twentyfifteen' ) . ' </span>',

              )

          );?> 
      </nav></div>
 

A second example more compact:

     <div class="row">
          <div class="col-md-12 text-center">
              <nav class="woocommerce-pagination bt_search_pagination"><?php echo paginate_links(); ?></nav>
          </div>
      </div>

You can add this code in search.php or catregory.php

How a wordpress page is generated

https://codex.wordpress.org/Query_Overview

https://web.archive.org/web/20170524181634/http://humanshell.net/2011/08/wordpress-initialization

All http query to the webserver are send to index.php (this is not a redirect as the rule don’t use the redirect flag [R]) in the root folder. The redirect occurs in .htaccess with mod_rewrite

mod_rewrite operate by using a PCRE* regular expression

*PCRE: Perl Compatible Regular Expression

This operation retains the original URI that can be requested with $_server[‘REQUEST_URI’]

Below is the cascade of file that are succesfully called from index.php (up the 5th level but it needs to be expanded)

index.php

set the constant ‘WP_USE_THEMES’ to true and require wp-blog-header.php

wp-blog-header.php

execute wp(). This function is defined in functions.php

requires wp-load.php and template-loader.php

wp-load.php AND template-loader.php

wp-load.php requires wp-config.php

wp-load.php set the constant ‘ABSPATH’

wp-config.php requires wp-settings.php

wp-settings.php

requires:

  • version.php
  • load.php
  • class-wp-paused-extensions-storage.php
  • class-wp-fatal-error-handler.php
  • class-wp-reccovery-mode-cookie-service.php
  • class-wp-recovery-mode-key-service.php
  • class-wp-recovery-mode-link-service.php
  • class-wp-recovery-mode-email-service.php
  • class-wp-recovery-mode.php
  • error-protection.php
  • default-constant.php
  • plugin.php

and also:

  • compat.php
  • class-wp-list-util.php
  • formatting.php
  • meta.php
  • functions.php
  • class-wp-meta-query.php
  • class-wp-matchesmapregex.php
  • class-wp.php
  • class-wp-error.php
  • pomo/mo.php

and most of wordpress as well:

  • class-wp-walker.php
  • class-wp-ajax-response.php
  • capabilities.php
  • class-wp-roles.php
  • class-wp-role.php
  • class-wp-user.php
  • class-wp-query.php
  • query.php
  • class-wp-date-query.php
  • theme.php
  • class-wp-theme.php
  • template.php
  • class-wp-user-query.php
  • class-wp-session-tokens.php
  • class-wp-user-meta-sessions-tokens.php
  • class-wp-metadata-session-tokens.php
  • class-wp-metadata-lazyloader.php
  • general-template.php
  • link-template.php
  • author-template.php
  • post.php
  • class-walker-page.php
  • class-walker-page-dropdown.php
  • class-wp-post-type.php
  • class-wp-post.php
  • post-template.php
  • revision.php
  • post-formats.php
  • post-thumbnail-template.php
  • category.php
  • class-walker-category.php
  • class-walker-category-dropdown.php
  • category-template.php
  • comment.php
  • class-wp-comment.php
  • class-wp-comment-query.php
  • class-walker-comment.php
  • comment-template.php
  • rewrite.php
  • class-wp-rewrite.php
  • feed.php
  • bookmark.php
  • bookmark-template.php
  • kses.php
  • cron.php
  • deprecated.php
  • script-loader.php
  • taxonomy.php
  • class-wp-taxonomy.php
  • class-wp-term.php
  • class-wp-term-query.php
  • class-wp-tax-query.php
  • update.php
  • canonical.php
  • shortcodes.php
  • embed.php
  • class-wp-embed.php
  • class-wp-oembed.php
  • class-wp-oembed-controller.php
  • media.php
  • http.php
  • class-http.php
  • class-wp-http-streams.php
  • class-wp-http-curl.php
  • class-wp-http-proxy.php
  • class-wp-http-cookie.php
  • class-wp-http-encoding.php
  • class-wp-http-response.php
  • class-wp-http-requests-response.php
  • class-wp-http-requests-hooks.php
  • widgets.php
  • class-wp-widget.php
  • class-wp-widget-factory.php
  • nav-menu.php
  • nav-menu-template.php
  • admin-bar.php
  • rest-api.php
  • rest-api/class-wp-rest-server.php
  • rest-api/class-wp-rest-response.php
  • rest-api/class-wp-rest-request.php
  • rest-api/endpoints/class-wp-rest-controller.php
  • rest-api/endpoints/class-wp-rest-posts-controller.php
  • rest-api/endpoints/class-wp-rest-attachments-controller.php
  • rest-api/endpoints/class-wp-rest-post-types-controller.php
  • rest-api/endpoints/class-wp-rest-post-statuses-controller.php
  • rest-api/endpoints/class-wp-rest-revisions-controller.php
  • rest-api/endpoints/class-wp-rest-autosaves-controller.php
  • rest-api/endpoints/class-wp-rest-taxonomies-controller.php
  • rest-api/endpoints/class-wp-rest-terms-controller.php
  • rest-api/endpoints/class-wp-rest-users-controller.php
  • rest-api/endpoints/class-wp-rest-comments-controller.php
  • rest-api/endpoints/class-wp-rest-search-controller.php
  • rest-api/endpoints/class-wp-rest-blocks-controller.php
  • rest-api/endpoints/class-wp-rest-block-renderer-controller.php
  • rest-api/endpoints/class-wp-rest-settings-controller.php
  • rest-api/endpoints/class-wp-rest-themes-controller.php
  • rest-api/fields/class-wp-rest-meta-fields.php
  • rest-api/fields/class-wp-rest-comment-meta-fields.php
  • rest-api/fields/class-wp-rest-post-meta-fields.php
  • rest-api/fields/class-wp-rest-term-meta-fields.php
  • rest-api/fields/class-wp-rest-user-meta-fields.php
  • rest-api/search/class-wp-rest-search-handler.php
  • rest-api/search/class-wp-rest-post-search-handler.php
  • class-wp-block-type.php
  • class-wp-block-styles-registry.php
  • class-wp-block-type-registry.php
  • class-wp-block-parser.php
  • blocks.php
  • blocks/archives.php
  • blocks/block.php
  • blocks/calendar.php
  • blocks/categories.php
  • blocks/latest-comments.php
  • blocks/latest-posts.php
  • blocks/rss.php
  • blocks/search.php
  • blocks/shortcode.php
  • blocks/social-link.php
  • blocks/tag-cloud.php

Override locotranslate translation strings

If you want to override some loco translate strings, you might have noticed a warning stating that changes to the file might be lost after wordpress/plugin/template update.

Let’s see how to avoid losing all your customization during the next update.

In Loco Translate select the plugins/theme/etc that you want to customize.

Hover over the the language you want to make changes to and click on copy.

Loco Translate let you create a copy of a translation file.

Note that system, on the right end of the picture above indicates that this is a system file and prone to be overwritten during the next update.

Once you have clicked on copy you are brought to a screen to set a new language. Well, good news, it doesn’t have to be a new language at all and you can create a copy of a file for an existing language. Let’s have a look at the settings.

Loco translate “New language” screen.

1- Choose the target language, in our case this is the one of the file we copied.

2- For the location, the most sensible is the custom location. System and Author might be subject to changes from update.

3- For the templates option use the “Just copy English source strings” option and changes just what you need, the rest will fallback safely on the string in the original file. If you have already make your change directly in the original then you can choose the first option.

Once you are all set, click on start translating.

If you need to edit your custom copy. Click on “Languages” in the Loco Translate menu. Choose the languagefor which you have created the copy you want to modify. You will see all templates available under WordPress Core , Plugins and Themes for this language. Look for the name of the plugin or theme you have modified. The original file will be listed as system and your copy will be listed as custom. Click on edit.

Price Filter not working

Issue: the woocommerce price filter is not working.

Solution: The issue is likely due to a compatibility issue with your theme. To solve that, you will need to declare the theme woocommerce support and copy your page.php file content to a new file that you will call woocommerce.php. You will replace the loop by <?php woocommerce_content(); ?>.

You will also need to make sure woocommerce support is declared in your theme functions.php: add_theme_support( 'woocommerce' );

For more info on woocommerce theme development and support:

https://docs.woocommerce.com/document/woocommerce-theme-developer-handbook/
Integrate woocommerce in your theme

Add shortcode with WordPress new content editor interface Gutenberg

You might have add difficulties to and errors when trying to add shortcode to new WordPress articles and posts.

To add a short code in the new editor, hover at the top center of an unactive block and click on the circled + icon that appears at its top center .

Adding a block above another one.

You can also click on the circled + icon at the top left corner of the page.

In the search bar type: shortcode

Click on the shortcode option. A new block will be added.

The shortcode block can be found under widgets in the block type selection box.

Type in your shortcode as you would do in the classic view:

[ShortCodeTag]

Note: I did encounter an error that the page couldn’t be saved when trying to save the page with the square brackets in WordPress 5.2.4. It turned out that the page was actually saved. This issue was supposed to be fixed already since 3.9 see github repo for wordpress gutenberg.

Upload webp images to wordpress

wordpress and webp: “Sorry, this file type is not permitted for security reasons.”

Error message when uploading a webp image in wordpress media library

Certain image type like svg, jpeg2000 or webp raises an error when you try to upload them in wordpress as shown in the picture above.

We have already covered how to implement webp in wordpress but there might be some cases where you might want to upload this image manually and serve them to supporting browser via your own script if necessary (that’s not the case for svg though).

Add the following snippet of code to the functions.php file of your template. Comment out or delete the line for the mime type you are not interested in.

function add_mime_types( $mimes ) {
$mimes['webp'] = 'image/webp'; 
$mimes['svg'] = 'image/svg+xml';
$mimes['jpeg2000'] = 'image/jp2'; 
return $mimes; }
add_filter( 'upload_mimes', 'add_mime_types'  );

How to remove jquery.migrate.js in wordpress

Jquery.migrate is used to ensure compatibility with older jquery code using deprecated function.

In case your jquery is all shiny and new and you don’t use deprecated functions, then you can get the reed of jquery migrate and have one less file to load. As usual, we will do that by adding a function in the functions.php file of your theme:

function remove_jquery_migrate($scripts)
{
    if (!is_admin() && isset($scripts->registered['jquery'])) {
        $script = $scripts->registered['jquery'];
        if ($script->deps) { 
            // Check whether the script has any dependencies     
            $script->deps = array_diff($script->deps, array('jquery-migrate')); 
            }
      }
 } 
 add_action('wp_default_scripts', 'remove_jquery_migrate');