diff --git a/.distignore b/.distignore
new file mode 100755
index 0000000000000000000000000000000000000000..7a767aab7b021a9d02ccfebd7846d8ce16f2f778
--- /dev/null
+++ b/.distignore
@@ -0,0 +1,36 @@
+# A set of files you probably don't want in your WordPress.org distribution
+.distignore
+.editorconfig
+.git
+.gitignore
+.gitlab-ci.yml
+.travis.yml
+.wordpress-org/*
+.DS_Store
+Thumbs.db
+behat.yml
+bitbucket-pipelines.yml
+bin
+.circleci/config.yml
+composer.json
+composer.lock
+Gruntfile.js
+package.json
+package-lock.json
+phpunit.xml
+phpunit.xml.dist
+multisite.xml
+multisite.xml.dist
+.phpcs.xml
+phpcs.xml
+.phpcs.xml.dist
+phpcs.xml.dist
+README.md
+wp-cli.local.yml
+yarn.lock
+tests
+vendor
+node_modules
+*.sql
+*.tar.gz
+*.zip
diff --git a/.editorconfig b/.editorconfig
new file mode 100755
index 0000000000000000000000000000000000000000..79207a40cb9326b8c6b8c958fa864b5345f94e68
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,22 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+# WordPress Coding Standards
+# https://make.wordpress.org/core/handbook/coding-standards/
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = tab
+indent_size = 4
+
+[{.jshintrc,*.json,*.yml}]
+indent_style = space
+indent_size = 2
+
+[{*.txt,wp-config-sample.php}]
+end_of_line = crlf
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..e6bac7e940c35c73a2d95dd587c525f4dbfcdf4f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,36 @@
+# A set of files you probably don't want in your WordPress.org distribution
+/.distignore export-ignore
+/.editorconfig export-ignore
+/.gitattributes export-ignore
+/.gitignore export-ignore
+/.gitlab-ci.yml export-ignore
+/.travis.yml export-ignore
+/.DS_Store export-ignore
+/.wordpress-org export-ignore
+/Thumbs.db export-ignore
+/behat.yml export-ignore
+/bitbucket-pipelines.yml export-ignore
+/bin export-ignore
+/.circleci/config.yml export-ignore
+/composer.json export-ignore
+/composer.lock export-ignore
+/Gruntfile.js export-ignore
+/package.json export-ignore
+/package-lock.json export-ignore
+/phpunit.xml export-ignore
+/phpunit.xml.dist export-ignore
+/multisite.xml export-ignore
+/multisite.xml.dist export-ignore
+/.phpcs.xml export-ignore
+/phpcs.xml export-ignore
+/.phpcs.xml.dist export-ignore
+/phpcs.xml.dist export-ignore
+/README.md export-ignore
+/wp-cli.local.yml export-ignore
+/yarn.lock export-ignore
+/tests export-ignore
+/vendor export-ignore
+/node_modules export-ignore
+/*.sql export-ignore
+/*.tar.gz export-ignore
+/*.zip export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000000000000000000000000000000000000..6f68bc7f65c25ceae8e856eb9ab25fa815acf0cd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+.DS_Store
+phpcs.xml
+phpunit.xml
+Thumbs.db
+wp-cli.local.yml
+node_modules/
+*.sql
+*.tar.gz
+*.zip
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100755
index 0000000000000000000000000000000000000000..1eeee56cefb802a3d77bdab5e9a75936dc251bcd
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,46 @@
+variables:
+  # Configure mysql service (https://hub.docker.com/_/mysql/)
+  MYSQL_DATABASE: wordpress_tests
+  MYSQL_ROOT_PASSWORD: mysql
+
+cache:
+  paths:
+    - $HOME/.composer
+    - /root/.composer
+
+before_script:
+  # Set up WordPress tests
+  - bash bin/install-wp-tests.sh $MYSQL_DATABASE root $MYSQL_ROOT_PASSWORD mysql latest true
+
+  # PHPUnit
+  - |
+    if [[ $(php -v) =~ "PHP 7." ]]; then
+      composer global require "phpunit/phpunit=6.1.*"
+    else
+      composer global require "phpunit/phpunit=4.8.*"
+    fi
+
+  # Install PHPCS and WPCS
+  - composer global require automattic/vipwpcs
+  - composer global require phpcompatibility/phpcompatibility-wp
+  - phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs,$HOME/.composer/vendor/automattic/vipwpcs,$HOME/.composer/vendor/phpcompatibility/php-compatibility,$HOME/.composer/vendor/phpcompatibility/phpcompatibility-paragonie,$HOME/.composer/vendor/phpcompatibility/phpcompatibility-wp
+
+PHPunit:PHP7.3:MySQL:
+  stage: test
+  image: containers.ethitter.com:443/docker/images/php:7.3
+  services:
+    - mysql:5.6
+  script:
+    - find . -type "f" -iname "*.php" | xargs -L "1" php -l
+    - phpcs -n
+    - phpunit
+  allow_failure: true
+
+PluginSVN:
+  stage: deploy
+  image: containers.ethitter.com:443/docker/images/php:7.3
+  before_script:
+    - curl -o ./bin/deploy.sh https://git-cdn.e15r.co/open-source/wp-org-plugin-deploy/raw/master/scripts/deploy.sh
+    - chmod +x ./bin/deploy.sh
+  script: ./bin/deploy.sh
+  when: on_success
diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..01cc6eea945b7d8099540f9f85f6877ba8e9f4d0
--- /dev/null
+++ b/.phpcs.xml.dist
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<ruleset name="ETH Escape HeadSpace2">
+	<description>Generally-applicable sniffs for WordPress plugins.</description>
+
+	<!-- What to scan -->
+	<file>.</file>
+	<exclude-pattern>/vendor/</exclude-pattern>
+	<exclude-pattern>/node_modules/</exclude-pattern>
+	<exclude-pattern>/tests/*</exclude-pattern>
+
+	<!-- How to scan -->
+	<!-- Usage instructions: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage -->
+	<!-- Annotated ruleset: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml -->
+	<arg value="sp"/> <!-- Show sniff and progress -->
+	<arg name="basepath" value="./"/><!-- Strip the file paths down to the relevant bit -->
+	<arg name="colors"/>
+	<arg name="extensions" value="php"/>
+	<arg name="parallel" value="8"/><!-- Enables parallel processing when available for faster results. -->
+
+	<!-- Rules: Check PHP version compatibility -->
+	<!-- https://github.com/PHPCompatibility/PHPCompatibility#sniffing-your-code-for-compatibility-with-specific-php-versions -->
+	<config name="testVersion" value="7.2-"/>
+	<!-- https://github.com/PHPCompatibility/PHPCompatibilityWP -->
+	<rule ref="PHPCompatibilityWP"/>
+
+	<!-- Rules: WordPress Coding Standards -->
+	<!-- https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards -->
+	<!-- https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/wiki/Customizable-sniff-properties -->
+	<config name="minimum_supported_wp_version" value="4.4"/>
+	<rule ref="WordPress" />
+	<rule ref="WordPressVIPMinimum" />
+	<rule ref="WordPress-VIP-Go" />
+	<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
+		<properties>
+			<!-- Value: replace the function, class, and variable prefixes used. Separate multiple prefixes with a comma. -->
+			<property name="prefixes" type="array" value="eth_escape_headspace"/>
+		</properties>
+	</rule>
+	<rule ref="WordPress.WP.I18n">
+		<properties>
+			<!-- Value: replace the text domain used. -->
+			<property name="text_domain" type="array" value="eth_escape_headspace2"/>
+		</properties>
+	</rule>
+	<rule ref="WordPress.WhiteSpace.ControlStructureSpacing">
+		<properties>
+			<property name="blank_line_check" value="true"/>
+		</properties>
+	</rule>
+</ruleset>
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100755
index 0000000000000000000000000000000000000000..9ab104ca9eb60738f040f3a2f734a43499ba572f
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,56 @@
+module.exports = function( grunt ) {
+
+	'use strict';
+
+	// Project configuration
+	grunt.initConfig( {
+
+		pkg: grunt.file.readJSON( 'package.json' ),
+
+		addtextdomain: {
+			options: {
+				textdomain: 'eth_escape_headspace2',
+			},
+			update_all_domains: {
+				options: {
+					updateDomains: true
+				},
+				src: [ '*.php', '**/*.php', '!\.git/**/*', '!bin/**/*', '!node_modules/**/*', '!tests/**/*' ]
+			}
+		},
+
+		wp_readme_to_markdown: {
+			your_target: {
+				files: {
+					'README.md': 'readme.txt'
+				}
+			},
+		},
+
+		makepot: {
+			target: {
+				options: {
+					domainPath: '/languages',
+					exclude: [ '\.git/*', 'bin/*', 'node_modules/*', 'tests/*' ],
+					mainFile: 'eth-escape-headspace.php',
+					potFilename: 'eth-escape-headspace2.pot',
+					potHeaders: {
+						poedit: true,
+						'x-poedit-keywordslist': true
+					},
+					type: 'wp-plugin',
+					updateTimestamp: true
+				}
+			}
+		},
+	} );
+
+	grunt.loadNpmTasks( 'grunt-wp-i18n' );
+	grunt.loadNpmTasks( 'grunt-wp-readme-to-markdown' );
+	grunt.registerTask( 'default', [ 'i18n','readme' ] );
+	grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] );
+	grunt.registerTask( 'readme', ['wp_readme_to_markdown'] );
+
+	grunt.util.linefeed = '\n';
+
+};
diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5ceac4b84b4d05ad6b3525cc9d8b2fd055c32554
--- /dev/null
+++ b/bin/install-wp-tests.sh
@@ -0,0 +1,155 @@
+#!/usr/bin/env bash
+
+if [ $# -lt 3 ]; then
+	echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
+	exit 1
+fi
+
+DB_NAME=$1
+DB_USER=$2
+DB_PASS=$3
+DB_HOST=${4-localhost}
+WP_VERSION=${5-latest}
+SKIP_DB_CREATE=${6-false}
+
+TMPDIR=${TMPDIR-/tmp}
+TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
+WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
+WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
+
+download() {
+    if [ `which curl` ]; then
+        curl -s "$1" > "$2";
+    elif [ `which wget` ]; then
+        wget -nv -O "$2" "$1"
+    fi
+}
+
+if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then
+	WP_BRANCH=${WP_VERSION%\-*}
+	WP_TESTS_TAG="branches/$WP_BRANCH"
+
+elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
+	WP_TESTS_TAG="branches/$WP_VERSION"
+elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
+	if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
+		# version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
+		WP_TESTS_TAG="tags/${WP_VERSION%??}"
+	else
+		WP_TESTS_TAG="tags/$WP_VERSION"
+	fi
+elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
+	WP_TESTS_TAG="trunk"
+else
+	# http serves a single offer, whereas https serves multiple. we only want one
+	download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
+	grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
+	LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
+	if [[ -z "$LATEST_VERSION" ]]; then
+		echo "Latest WordPress version could not be found"
+		exit 1
+	fi
+	WP_TESTS_TAG="tags/$LATEST_VERSION"
+fi
+set -ex
+
+install_wp() {
+
+	if [ -d $WP_CORE_DIR ]; then
+		return;
+	fi
+
+	mkdir -p $WP_CORE_DIR
+
+	if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
+		mkdir -p $TMPDIR/wordpress-nightly
+		download https://wordpress.org/nightly-builds/wordpress-latest.zip  $TMPDIR/wordpress-nightly/wordpress-nightly.zip
+		unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
+		mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
+	else
+		if [ $WP_VERSION == 'latest' ]; then
+			local ARCHIVE_NAME='latest'
+		elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
+			# https serves multiple offers, whereas http serves single.
+			download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
+			if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
+				# version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
+				LATEST_VERSION=${WP_VERSION%??}
+			else
+				# otherwise, scan the releases and get the most up to date minor version of the major release
+				local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
+				LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
+			fi
+			if [[ -z "$LATEST_VERSION" ]]; then
+				local ARCHIVE_NAME="wordpress-$WP_VERSION"
+			else
+				local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
+			fi
+		else
+			local ARCHIVE_NAME="wordpress-$WP_VERSION"
+		fi
+		download https://wordpress.org/${ARCHIVE_NAME}.tar.gz  $TMPDIR/wordpress.tar.gz
+		tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
+	fi
+
+	download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
+}
+
+install_test_suite() {
+	# portable in-place argument for both GNU sed and Mac OSX sed
+	if [[ $(uname -s) == 'Darwin' ]]; then
+		local ioption='-i.bak'
+	else
+		local ioption='-i'
+	fi
+
+	# set up testing suite if it doesn't yet exist
+	if [ ! -d $WP_TESTS_DIR ]; then
+		# set up testing suite
+		mkdir -p $WP_TESTS_DIR
+		svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
+		svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
+	fi
+
+	if [ ! -f wp-tests-config.php ]; then
+		download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
+		# remove all forward slashes in the end
+		WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
+		sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
+		sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
+		sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
+		sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
+		sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
+	fi
+
+}
+
+install_db() {
+
+	if [ ${SKIP_DB_CREATE} = "true" ]; then
+		return 0
+	fi
+
+	# parse DB_HOST for port or socket references
+	local PARTS=(${DB_HOST//\:/ })
+	local DB_HOSTNAME=${PARTS[0]};
+	local DB_SOCK_OR_PORT=${PARTS[1]};
+	local EXTRA=""
+
+	if ! [ -z $DB_HOSTNAME ] ; then
+		if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
+			EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
+		elif ! [ -z $DB_SOCK_OR_PORT ] ; then
+			EXTRA=" --socket=$DB_SOCK_OR_PORT"
+		elif ! [ -z $DB_HOSTNAME ] ; then
+			EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
+		fi
+	fi
+
+	# create database
+	mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
+}
+
+install_wp
+install_test_suite
+install_db
diff --git a/package.json b/package.json
new file mode 100755
index 0000000000000000000000000000000000000000..469e83fb0a0fa15f2cacffd96815ebb3b20ce0fc
--- /dev/null
+++ b/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "eth-escape-headspace2",
+  "version": "0.1.0",
+  "main": "Gruntfile.js",
+  "author": "Erick Hitter",
+  "devDependencies": {
+    "grunt": "~0.4.5",
+    "grunt-wp-i18n": "~0.5.0",
+    "grunt-wp-readme-to-markdown": "~1.0.0"
+  }
+}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..16a39027e72be2cf0a2656056074b6e6ed818be1
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<phpunit
+	bootstrap="tests/bootstrap.php"
+	backupGlobals="false"
+	colors="true"
+	convertErrorsToExceptions="true"
+	convertNoticesToExceptions="true"
+	convertWarningsToExceptions="true"
+	>
+	<testsuites>
+		<testsuite>
+			<directory prefix="test-" suffix=".php">./tests/</directory>
+			<exclude>./tests/test-sample.php</exclude>
+		</testsuite>
+	</testsuites>
+</phpunit>
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100755
index 0000000000000000000000000000000000000000..12067fd50a2a5bf508c78147447b3408f1992eb9
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * PHPUnit bootstrap file
+ *
+ * @package ETH_Escape_HeadSpace2
+ */
+
+$_tests_dir = getenv( 'WP_TESTS_DIR' );
+
+if ( ! $_tests_dir ) {
+	$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';
+}
+
+if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
+	echo "Could not find $_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?" . PHP_EOL; // WPCS: XSS ok.
+	exit( 1 );
+}
+
+// Give access to tests_add_filter() function.
+require_once $_tests_dir . '/includes/functions.php';
+
+/**
+ * Manually load the plugin being tested.
+ */
+function _manually_load_plugin() {
+	require dirname( dirname( __FILE__ ) ) . '/eth-escape-headspace.php';
+}
+tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
+
+// Start up the WP testing environment.
+require $_tests_dir . '/includes/bootstrap.php';
diff --git a/tests/test-sample.php b/tests/test-sample.php
new file mode 100755
index 0000000000000000000000000000000000000000..684cbf791e2c702cd296ef84c33a73e755dba8a7
--- /dev/null
+++ b/tests/test-sample.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Class SampleTest
+ *
+ * @package ETH_Escape_HeadSpace2
+ */
+
+/**
+ * Sample test case.
+ */
+class SampleTest extends WP_UnitTestCase {
+
+	/**
+	 * A single example test.
+	 */
+	public function test_sample() {
+		// Replace this with some actual testing code.
+		$this->assertTrue( true );
+	}
+}