As a PHP web developer with over 13 years experience, I can say that Mautic is the best of all the free marketing automation platforms, by far.  Sugar CRM is good, for what it does, but that’s a CRM, not an MA (Marketing Automation).  Still, when you are updating Mautic, like you would WordPress, be sure to always back up your files and database!

Now, I will chronicle my latest experience updating Mautic from 2.8.3 and 2.9.1.  By clicking the update button beneath the bell icon, it take you to the update page where you have to click “Update”.  Then, the process unfolds, it seems to hang at clearing the cache on my server, but you have to logout then log back in.  Everything went fine, except when I went to “Emails” and “Focus Items” and it hung up.  In the logs, I saw the following entries:

mautic.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\Exception\InvalidFieldNameException: "An exception occurred while executing 'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_10 FROM (SELECT e0_.is_published AS is_published_0, e0_.date_added AS date_added_1, e0_.created_by AS created_by_2, e0_.created_by_user AS created_by_user_3, e0_.date_modified AS date_modified_4, e0_.modified_by AS modified_by_5, e0_.modified_by_user AS modified_by_user_6, e0_.checked_out AS checked_out_7, e0_.checked_out_by AS checked_out_by_8, e0_.checked_out_by_user AS checked_out_by_user_9, e0_.id AS id_10, e0_.name AS name_11, e0_.description AS description_12, e0_.subject AS subject_13, e0_.from_address AS from_address_14, e0_.from_name AS from_name_15, e0_.reply_to_address AS reply_to_address_16, e0_.bcc_address AS bcc_address_17, e0_.template AS template_18, e0_.content AS content_19, e0_.utm_tags AS utm_tags_20, e0_.plain_text AS plain_text_21, e0_.custom_html AS custom_html_22, e0_.email_type AS email_type_23, e0_.publish_up AS publish_up_24, e0_.publish_down AS publish_down_25, e0_.read_count AS read_count_26, e0_.sent_count AS sent_count_27, e0_.revision AS revision_28, e0_.lang AS lang_29, e0_.variant_settings AS variant_settings_30, e0_.variant_start_date AS variant_start_date_31, e0_.dynamic_content AS dynamic_content_32, e0_.variant_sent_count AS variant_sent_count_33, e0_.variant_read_count AS variant_read_count_34 FROM emails e0_ LEFT JOIN categories c1_ ON e0_.category_id = c1_.id WHERE e0_.variant_parent_id IS NULL AND e0_.translation_parent_id IS NULL ORDER BY e0_.subject DESC) dctrn_result) dctrn_table':  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'e0_.utm_tags' in 'field list'" at /home/marketingautomation/yourdirector/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php line 71 {"exception":"[object] (Doctrine\\DBAL\\Exception\\InvalidFieldNameException(code: 0): An exception occurred while executing 'SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id_10 FROM (SELECT e0_.is_published AS is_published_0, e0_.date_added AS date_added_1, e0_.created_by AS created_by_2, e0_.created_by_user AS created_by_user_3, e0_.date_modified AS date_modified_4, e0_.modified_by AS modified_by_5, e0_.modified_by_user AS modified_by_user_6, e0_.checked_out AS checked_out_7, e0_.checked_out_by AS checked_out_by_8, e0_.checked_out_by_user AS checked_out_by_user_9, e0_.id AS id_10, e0_.name AS name_11, e0_.description AS description_12, e0_.subject AS subject_13, e0_.from_address AS from_address_14, e0_.from_name AS from_name_15, e0_.reply_to_address AS reply_to_address_16, e0_.bcc_address AS bcc_address_17, e0_.template AS template_18, e0_.content AS content_19, e0_.utm_tags AS utm_tags_20, e0_.plain_text AS plain_text_21, e0_.custom_html AS custom_html_22, e0_.email_type AS email_type_23, e0_.publish_up AS publish_up_24, e0_.publish_down AS publish_down_25, e0_.read_count AS read_count_26, e0_.sent_count AS sent_count_27, e0_.revision AS revision_28, e0_.lang AS lang_29, e0_.variant_settings AS variant_settings_30, e0_.variant_start_date AS variant_start_date_31, e0_.dynamic_content AS dynamic_content_32, e0_.variant_sent_count AS variant_sent_count_33, e0_.variant_read_count AS variant_read_count_34 FROM emails e0_ LEFT JOIN categories c1_ ON e0_.category_id = c1_.id WHERE e0_.variant_parent_id IS NULL AND e0_.translation_parent_id IS NULL ORDER BY e0_.subject DESC) dctrn_result) dctrn_table':\n\nSQLSTATE[42S22]: Column not found: 1054 Unknown column 'e0_.utm_tags' in 'field list' at /home/marketingautomation/yourdirectory/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:71, Doctrine\\DBAL\\Driver\\PDOException(code: 42S22): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'e0_.utm_tags' in 'field list' at /home/marketingautomation/yourdirectory/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:106, PDOException(code: 42S22): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'e0_.utm_tags' in 'field list' at /home/marketingautomation/yourdirectory/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:104)"} []

So here’s what I did on the command line to force the Mautic update to finish:

$ app/console mautic:update:find -e prod
PHP Warning:  Module 'XCache' already loaded in Unknown on line 0
Great! You are running the current version of Mautic.
$ php app/console doctrine:migration:status
PHP Warning:  Module 'XCache' already loaded in Unknown on line 0

 == Configuration

    >> Name:                                               Mautic Migrations
    >> Database Driver:                                    pdo_mysql
    >> Database Name:                                      your_database_name
    >> Configuration Source:                               manually configured
    >> Version Table Name:                                 migrations
    >> Version Column Name:                                version
    >> Migrations Namespace:                               Mautic\Migrations
    >> Migrations Directory:                               /home/symfony/yourdirectory/app/migrations
    >> Previous Version:                                   2016-07-26 00:00:00 (20160726000000)
    >> Current Version:                                    2016-07-26 00:00:01 (20160726000001)
    >> Next Version:                                       2016-07-28 00:00:00 (20160728000000)
    >> Latest Version:                                     2017-07-25 15:50:53 (20170725155053)
    >> Executed Migrations:                                37
    >> Executed Unavailable Migrations:                    0
    >> Available Migrations:                               88
    >> New Migrations:                                     51
$ php app/console doctrine:migration:migrate
PHP Warning:  Module 'XCache' already loaded in Unknown on line 0

                    Mautic Migrations


WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)y
Migrating up to 20170725155053 from 20160726000001

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20160731000000

     -> update campaign_lead_event_log log inner join campaign_events events on log.event_id = events.id set log.metadata = 'a:0:{}' where events.type = 'email.send' and log.metadata = 'a:2:{s:6:"failed";i:1;s:6:"reason";s:50:"mautic.notification.campaign.failed.not_subscribed";}'

  ++ migrated (2.49s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20160926182807

     -> insert into companies (companyname, is_published) (SELECT DISTINCT TRIM(company), 1 from leads l left join companies c ON l.company = c.companyname where company IS NOT NULL and company <> '' and c.companyname is null)
     -> insert into companies_leads (company_id, lead_id, date_added, manually_added, manually_removed) SELECT c.id, l.id, '2017-07-30 12:30:05', 0, 0 from leads l join companies c on c.companyname = l.company ON DUPLICATE KEY UPDATE company_id = c.id;

  ++ migrated (4.85s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20161123225456

Migration 20161123225456 was executed but did not result in any SQL statements.

  ++ migrated (11.54s)

  ++ migrating 20161124145649

     -> ALTER TABLE lead_frequencyrules CHANGE `frequency_number` `frequency_number` SMALLINT DEFAULT NULL;
     -> ALTER TABLE lead_frequencyrules CHANGE `frequency_time` `frequency_time` VARCHAR(25) DEFAULT NULL;

  ++ migrated (2.38s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20170108012944

     -> ALTER TABLE push_notifications CHANGE button button LONGTEXT NULL
     -> ALTER TABLE dynamic_content_lead_data DROP FOREIGN KEY FK_515B221B55458D
     -> ALTER TABLE dynamic_content_lead_data CHANGE lead_id lead_id INT NOT NULL
     -> ALTER TABLE dynamic_content_lead_data ADD CONSTRAINT FK_515B221B55458D FOREIGN KEY (lead_id) REFERENCES leads (id) ON DELETE CASCADE

  ++ migrated (4.52s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20170216221648

     -> ALTER TABLE focus ADD editor LONGTEXT NULL
     -> ALTER TABLE focus ADD html_mode VARCHAR(255) DEFAULT NULL
     -> ALTER TABLE focus ADD html LONGTEXT NULL
     -> ALTER TABLE focus ADD utm_tags LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)';

  ++ migrated (12.38s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20170429092049

     -> ALTER TABLE emails ADD utm_tags LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)';
     -> ALTER TABLE push_notifications ADD utm_tags LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)';

  ++ migrated (4.45s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20170515222314

     -> CREATE TABLE imports (
    id INT AUTO_INCREMENT NOT NULL,
    is_published TINYINT(1) NOT NULL,
    date_added DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)',
    created_by INT DEFAULT NULL,
    created_by_user VARCHAR(255) DEFAULT NULL,
    date_modified DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)',
    modified_by INT DEFAULT NULL,
    modified_by_user VARCHAR(255) DEFAULT NULL,
    checked_out DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)',
    checked_out_by INT DEFAULT NULL,
    checked_out_by_user VARCHAR(255) DEFAULT NULL,
    dir VARCHAR(255) NOT NULL,
    file VARCHAR(255) NOT NULL,
    original_file VARCHAR(255) DEFAULT NULL,
    line_count INT NOT NULL,
    inserted_count INT NOT NULL,
    updated_count INT NOT NULL,
    ignored_count INT NOT NULL,
    priority INT NOT NULL,
    status INT NOT NULL,
    date_started DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)',
    date_ended DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)',
    object VARCHAR(255) NOT NULL,
    properties LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json_array)',
    INDEX import_object (object),
    INDEX import_status (status),
    INDEX import_priority (priority),
    PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

  ++ migrated (2.82s)

  ++ migrating 20170516000000

     -> INSERT INTO `lead_fields` (`is_published`, `label`, `alias`, `type`, `field_group`, `default_value`, `is_required`, `is_fixed`, `is_visible`, `is_short_visible`, `is_listable`, `is_publicly_updatable`, `is_unique_identifer`, `field_order`, `properties`, `object`)
VALUES (1, 'Points', 'points', 'number', 'core', '0', 0, 1, 1, 0, 0, 0, 0, 29, 'a:0:{}', 'lead');

  ++ migrated (2.09s)

  ++ migrating 20170517091309

     -> CREATE TABLE lead_event_log (
    id INT AUTO_INCREMENT NOT NULL,
    lead_id INT DEFAULT NULL,
    user_id INT DEFAULT NULL,
    user_name VARCHAR(255) DEFAULT NULL,
    bundle VARCHAR(255) DEFAULT NULL,
    object VARCHAR(255) DEFAULT NULL,
    action VARCHAR(255) DEFAULT NULL,
    object_id INT DEFAULT NULL,
    date_added DATETIME NOT NULL COMMENT '(DC2Type:datetime)',
    properties LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json_array)',
    INDEX lead_id_index (lead_id),
    INDEX lead_object_index (object, object_id),
    INDEX lead_timeline_index (bundle, object, action, object_id),
    INDEX lead_date_added_index (date_added),
    PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
     -> ALTER TABLE lead_event_log ADD CONSTRAINT FK_753AF2E55458D FOREIGN KEY (lead_id) REFERENCES leads (id) ON DELETE SET NULL;

  ++ migrated (5.34s)

  ++ migrating 20170607150241

     -> ALTER TABLE webhook_logs ADD note VARCHAR(255) DEFAULT NULL

  ++ migrated (4.9s)

  ++ migrating 20170607155015

     -> ALTER TABLE webhook_logs ADD runtime DOUBLE PRECISION DEFAULT NULL

  ++ migrated (4.03s)

  SS skipped (Reason: Schema includes this migration)

  SS skipped (Reason: Schema includes this migration)

  ++ migrating 20170628191405

     -> CREATE TABLE plugin_crm_pipedrive_owners (
  id INT AUTO_INCREMENT NOT NULL,
  email VARCHAR(255) NOT NULL,
  owner_id INT DEFAULT NULL,
  INDEX email (email),
  INDEX owner_id (owner_id),
  PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

  ++ migrated (3.61s)

  ++ migrating 20170725155053

     -> ALTER TABLE companies CHANGE score score INT(11) NULL

  ++ migrated (6.32s)

  ------------------------

  ++ finished in 71.72s
  ++ 51 migrations executed
  ++ 23 sql queries

The strange thing is, Mautic says that there has been no database schema changes, except for updates older than 2.6, but I was updating Mautic from the most recent previous verison, 2.8.3.  Oh well, something was missed in translation, can’t really complain about free marketing automation if it has a few glitches once in a while.  I’ve never updated a version of Mautic that was completely smooth but there has always been a workaround.


Recently, I updated a few instances of Mautic from 1.9 and 2.8.1 to 2.8.2 and encountered the following error message:

It didn’t take long to realize for some reason the update was redirecting to a path and adding an additional forward slash before the “/s/update/” part of the URL.  Just manually remove the extra slash in the URL and force your browser to open that address and I have confirmed the update will continue.

Before doing any upgrades with Mautic, or ANY system for that matter, to make backups of your affected directories and database and NEVER update on a live site without the ability to roll back!

Some of you may encounter an error after that, just navigate back to your /s/login/ path and you should be fine!  To set up and run marketing automation, you don’t have to be a full-fledged PHP web developer, but it sure helps!

Mautic WordPress Plug-in

Mautic is an incredible Marketing Automation tool, their free community version has very powerful capabilities that many businesses pay thousands of dollars a month for with Hub Spot, Marketo, Pardot, InfusionSoft, etc.  Mautic also has a series of free WordPress plug-ins so you can integrate Mautic with your website with a simple click of the “Install Plug-in” button.  To try them out, just go to your WordPress Plug-in manager and type “mautic” to see the latest list.

The most indispensable Mautic plug-in is “WP Mautic“, which is made by Mautic and creates an interface for you to use short codes and embed your Mautic forms directly in your website.  I can say first hand that it is very reliable and easy to use.

There is one small catch — it doesn’t say anywhere in the instructions but when you install the WP Mautic plug-in, go to its settings where it asks you for the URL of where you log in to Mautic.  Be sure not to add a trailing slash at the end of it or it won’t connect to it!

More about Mautic, the powerful, free Marketing Automation Tool

To set up Mautic and connect it to your website you actually need very little or no actual coding knowledge!  So, if you want your website to limit how often you have a pop-up form bother someone, if you want to customize page content based on a particular visitor’s tracking info and previous historical data Mautic’s already collected, or perhaps you want to throw an exit intent pop-up to just make sure before they view a certain page you give them one last chance to think about it…. You can do all this by setting up Mautic and installing the WordPress plug-in.

The list of ways to make your website more powerful, smart, intuitive and maybe even less annoying, are all made possible through this plug-in and how well you set up your Mautic account to deal with all these conditions and behaviors.

You don’t need a web developer to set up Marketing Automation?!

The fact you can get a website up and running, install and set up your own marketing automation, and get your website and marketing automation to share data and work together all without knowing any code is good news for you, bad news for web developers, right?  Well, I contend that if you want Mautic set up right, you need more than someone who has a Marketing degree, you need someone who understands systems and how to convert business logic into reliable system actions.  It can get complicated fast so you need someone with a programmer’s mentality or you will likely end up with a poorly designed system that is inefficient to work with, update, and enhance later.

Here’s a typical boilerplate campaign set up in Mautic.  By the way, now Mautic can let customers decide how they prefer to be contacted so the same campaign can send an email, SMS text OR a twitter message all through the same workflow!

Even a simple boilerplate campaign could seem overwhelming, so just because you can set all this up yourself without knowing how to write code still doesn’t mean you won’t need to hire a PHP web developer who can help you design it to work so it’s future proof and adaptable.  I often find businesses expect WordPress to do everything for them, then, they run into a brick wall where their 50 plug-ins no longer update or work well with their current theme, etc.  By this time, they’ve wasted years of time stacking an ever higher house of cards that will take more time to redo right.

The definition of insanity is to do the same thing over and over again but expect a different result.  To really enhance your website and get it to do amazing things, not just normal things, if you need a website to do more than just be a glorified business card or blog repository, you will likely be looking for a competent web developer to make it happen.  No matter how easy the tools and integrations seem to be on the surface (think one-click plug-in installs), unless you keep things really simple and need to just do simple things, beware of not utilizing someone with a programmer’s mentality who can think far ahead of your current project scope.  Beware of relying one someone who doesn’t have the proven experience setting up business systems.

Your organization needs someone who has proven their competence working with data, system design, a variety of web systems.  You need a person who has programming experience who understands efficiency, code reusability and the importance of future-proof scalability.  Using the right system designer to spearhead your marketing automation project will save your organization years of headaches and possibly hundreds of thousands of dollars of wasted money thrown at amateurish attempts experimenting with your resources on your dime to only deliver you a system that won’t meet your marketing requirements.

If you are an organization in the Jacksonville Northeast Florida area looking for open source PHP web developers with experience in WordPress, Symfony, Drupal, Magento, or API integration experts, please drop me a note, I have a list of competent people who I can recommend here in Jacksonville, Southwest Florida, and throughout the US, Canada, UK, Australia, and Germany as temporary web development consultants or remote web developers!


The good news about upgrading to Drupal 8 is that it looks a lot like Symfony, the bad news is that upgrading to Drupal 8 looks a lot like Symfony.  I guess it’s just your perspective but if you have a lot of sites steeped deeply in D7, it’s quite a transition.  However, if most of your code is a Symfony or other MVC-based framework based web dev shop without much invested in prior versions of Drupal, this transition will not only be easy, you will find your web team able to already recognize, find, and be completely familiar with all the essential organizational patterns under D8’s hood.

Red Crackle has a good list for those looking to transition from Drupal 7 to Drupal 8, just click here to read their list.

Information about migrating from Drupal 7 to Drupal 8 can be found here.

Here’s a useful video/overview of the transition and the major pros and cons.

 

 


Thanks to Jon Leigh for this snippet!  If you use Parsley form validation, a reliable form validation library, here’s an easy way to prevent double submissions of forms.  This disables the form submission button only once the form passes Parsley’s form validation.

$.listen('parsley:form:validated', function(e){
    if (e.validationResult) {
        /* Validation has passed, prevent double submissions */
        $('button[type=submit]').attr('disabled', 'disabled');
  }
});

I’ve implemented this in a few different places and it works flawlessly!  This is especially useful for mobile device users on slow Internet connections where double submissions can be more prevalent.  I hope this helps some businesses out there, it’s helped WordPress Developers and Symfony experts alike improve visitor UX while reducing complaints from people who may have been charged twice.


It was bound to happen, the very reliable Mesocolumn WordPress theme finally made an entry in a website’s fatal error logs.  I make a habit of examining website error logs, if you have a business with a web department, insist that they regularly scan and check your website error logs or subscribe to a service or has a script that can report 500 errors immediately.

Here was the error that cropped up:

[22-Apr-2016 09:27:05 UTC] PHP Fatal error: Call to undefined function get_theme_option() in /webroot/prolificfutility.com/public_html/wp-content/themes/mesocolumn-child/footer.php on line 119

500 server errors are never pleasant, it’s a fact of life if we don’t dedicate QA time to test, test and then test the sites again and again.  There is a point of diminishing returns of dedicating too much time to testing because that takes time away from web development, you know, actually creating coding solutions?!

Fixing WordPress 500 errors

This was an easy fix, because the error was easy to spot in the logs — get_theme_option function was undefined and there was a condition in the footer that called on it, so I simply wrapped the condition throwing the error first with this check:

if (function_exists('get_theme_options')) {
 // execute other code that was in the child theme here...

It’s not an ideal fix, but since I didn’t have time to investigate further, at least the page would continue loading if the get_theme_options() function was actually deprecated from the theme during a recent update.  It’s all about prioritizing your web development tasks to make sure you are optimizing your code production and in the context of other due dates, projects and considering the ROI of this task, it’s way down on the priority list.  So far down it will probably never be addressed since, truthfully, there are usually much more important and profitable web development tasks to perform.  Perhaps the next update it will fix itself, there’s no harm in doing an extra check to see if the function exists, even if they bring it back.

The more important benefit is that the page continues to load without throwing a fatal error, which is not only bad for SEO, it’s bad for the UX of every visitor and that’s always something that’s top on the priority list!