diff --git a/boilerplates/torque-plugin/README.md b/boilerplates/torque-plugin/README.md index 551d1a66..b8bcc53f 100644 --- a/boilerplates/torque-plugin/README.md +++ b/boilerplates/torque-plugin/README.md @@ -19,7 +19,7 @@ 4. Rename all files in this directory: {torque-plugin}-etc-class.php => {}-etc-class.php -5. Add '' to cli/lib/workspaces.sh +5. Add '' to cli/lib/workspaces.sh 6. Open new terminal, and in **project** root, run: diff --git a/cli/lib/workspaces.sh b/cli/lib/workspaces.sh index 02247ad5..651b6454 100644 --- a/cli/lib/workspaces.sh +++ b/cli/lib/workspaces.sh @@ -17,4 +17,5 @@ declare -a workspaces=( "torque-us-states" "torque-residences" "torque-services" + "torque-contact-form" ) diff --git a/plugins/torque-contact-form/.babelrc b/plugins/torque-contact-form/.babelrc new file mode 100644 index 00000000..481a0aa8 --- /dev/null +++ b/plugins/torque-contact-form/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": ["react", "env"], + "plugins": [ + "transform-runtime" + ] +} diff --git a/plugins/torque-contact-form/README.md b/plugins/torque-contact-form/README.md new file mode 100644 index 00000000..f31a7d38 --- /dev/null +++ b/plugins/torque-contact-form/README.md @@ -0,0 +1,23 @@ +# Torque Contact Form + +Adds a shortcode for displaying a contact form on pages. The child theme will decide the form fields for now. + +## Filters + + + +*Filter* | *Function* | *Value Type* +--- | --- | --- +`'torque_contact_form_fields_filter'` | Allows the child theme control over the form fields, giving the ability to add new ones or hide defaults | Array + + + +# Changelog + +## [1.0.0] 09/26/2018 + +### Added + +- Plugin config +- All form classes - email, field factory, template and root class +- Shortcode with tinyMCE diff --git a/plugins/torque-contact-form/package.json b/plugins/torque-contact-form/package.json new file mode 100644 index 00000000..a35f6399 --- /dev/null +++ b/plugins/torque-contact-form/package.json @@ -0,0 +1,31 @@ +{ + "name": "torque-contact-form", + "version": "1.0.0", + "author": "Torque", + "license": "ISC", + "scripts": { + "start": "webpack --mode=development --watch", + "build": "webpack --mode=production" + }, + "dependencies": {}, + "devDependencies": { + "autoprefixer": "^9.0.2", + "babel-cli": "^6.26.0", + "babel-core": "^6.26.3", + "babel-loader": "^7.1.5", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-env": "^1.7.0", + "babel-preset-react": "^6.24.1", + "copy-webpack-plugin": "^4.5.2", + "css-loader": "^1.0.0", + "extract-text-webpack-plugin": "^4.0.0-alpha.0", + "file-loader": "^1.1.11", + "node-sass": "^4.9.2", + "postcss-loader": "^2.1.6", + "sass-loader": "^7.1.0", + "style-loader": "^0.21.0", + "url-loader": "^1.0.1", + "webpack": "^4.16.3", + "webpack-cli": "^3.1.0" + } +} diff --git a/plugins/torque-contact-form/src/autoload.php b/plugins/torque-contact-form/src/autoload.php new file mode 100644 index 00000000..4da7fa2e --- /dev/null +++ b/plugins/torque-contact-form/src/autoload.php @@ -0,0 +1,29 @@ + diff --git a/plugins/torque-contact-form/src/form/form-contact-email.php b/plugins/torque-contact-form/src/form/form-contact-email.php new file mode 100644 index 00000000..3ddf7c9a --- /dev/null +++ b/plugins/torque-contact-form/src/form/form-contact-email.php @@ -0,0 +1,53 @@ +recipient = $recipient; + $this->fields = $fields; + } + + public function send() { + // allow html content + add_filter( 'wp_mail_content_type', array($this, 'set_html_content_type')); + + $message = $this->create_message(); + $subject = 'New Message'; + $headers = array('Content-Type: text/html; charset=UTF-8'); + + // send mail + wp_mail( $this->recipient, $subject, $message, $headers ); + + // remove html filter to avoid conflicts + remove_filter( 'wp_mail_content_type', array($this, 'set_html_content_type')); + } + + /** + * We need this as a separate function so we can remove the filter too + */ + public function set_html_content_type() { + return 'text/html'; + } + + private function create_message() { + ob_start(); + ?> + +

New Message

+ + fields as $id => $options) { + ?> +

:

+ diff --git a/plugins/torque-contact-form/src/form/form-contact-field.php b/plugins/torque-contact-form/src/form/form-contact-field.php new file mode 100644 index 00000000..e7837e8b --- /dev/null +++ b/plugins/torque-contact-form/src/form/form-contact-field.php @@ -0,0 +1,124 @@ + + + + + + + + + +
+ $label) { + ?> +
+ > + +
+ +
+ + +
+ + +
+ + diff --git a/plugins/torque-contact-form/src/form/form-contact-template.php b/plugins/torque-contact-form/src/form/form-contact-template.php new file mode 100644 index 00000000..6d36a694 --- /dev/null +++ b/plugins/torque-contact-form/src/form/form-contact-template.php @@ -0,0 +1,95 @@ +fields = $fields; + + if ( $message ) { + $this->message = $message; + } + } + + public function create_markup() { + ob_start(); + ?> + +
+ + the_message(); ?> + +
+ + the_nonce(); ?> + + + + + the_fields(); ?> + + + +
+ +
+ + get_the_message(); + } + + private function get_the_message() { + ob_start(); + + if (isset($this->message) && $this->message) { + + $success_class = ! $this->message['success'] ? 'error' : ''; + + ?> + +
+ message['message']; ?> +
+ + get_the_nonce(); + } + + private function get_the_nonce() { + return wp_nonce_field( Torque_Contact_Form_Form::$NONCE_NAME ); + } + + private function the_fields() { + + foreach ($this->fields as $id => $options) { + + if ( $options['type'] && in_array( $options['type'], Torque_Contact_Form_Field_Factory::$supported_field_types ) ) { + + echo Torque_Contact_Form_Field_Factory::create_new($id, $options); + + } + } + } +} + +?> diff --git a/plugins/torque-contact-form/src/form/form-contact.php b/plugins/torque-contact-form/src/form/form-contact.php new file mode 100644 index 00000000..32adbf94 --- /dev/null +++ b/plugins/torque-contact-form/src/form/form-contact.php @@ -0,0 +1,112 @@ +recipient = $recipient; + + // allow child theme to hide (or add) fields + $this->fields = apply_filters( self::$FIELDS_FILTER_HANDLE, array( + 'tq-name' => array( + 'name' => 'Name', + 'type' => 'text' + ), + 'tq-email' => array( + 'name' => 'Email', + 'type' => 'email' + ), + 'tq-state' => array( + 'name' => 'State', + 'type' => 'text' + ), + 'tq-zip' => array( + 'name' => 'Zip Code', + 'type' => 'text' + ), + 'tq-phone' => array( + 'name' => 'Phone', + 'type' => 'tel' + ), + /* + Example radio buttons option + + 'tq-resident-investor' => array( + 'name' => 'I am a:', + 'type' => 'radio', + 'options' => array( + 'resident' => 'Resident', + 'investor' => 'Investor' + ) + ), + */ + 'tq-message' => array( + 'name' => 'Message', + 'type' => 'textarea' + ), + )); + } + + public function get_form_markup() { + $message = $this->maybe_handle_submit(); + + $template = new Torque_Contact_Form_Form_Template( $this->fields, $message ); + return $template->create_markup(); + } + + public function maybe_handle_submit() { + $message = null; + + if (isset($_POST[self::$HIDDEN_FIELD_NAME])) { + // form was submitted + try { + foreach ($this->fields as $field_id => $field_options) { + if ( ! isset( $_POST[$field_id] ) ) { + throw new Exception('All form fields are required'); + } + } + + if ( + ! isset($_POST['_wpnonce']) || + ! wp_verify_nonce( $_POST['_wpnonce'], self::$NONCE_NAME ) + ) { + // couldnt verify nonce + throw new Exception('Form failed validation'); + } + + // form is validated - send email + $email = new Torque_Contact_Form_Email($this->recipient, $this->fields); + $email->send(); + + $message = array( + 'success' => true, + 'message' => 'Message sent successfully!' + ); + + } catch (Exception $e) { + + $message = array( + 'success' => false, + 'message' => $e->getMessage() !== '' ? $e->getMessage() : 'Something went wrong' + ); + } + } + + return $message; + } +} + +?> diff --git a/plugins/torque-contact-form/src/index.js b/plugins/torque-contact-form/src/index.js new file mode 100644 index 00000000..e69de29b diff --git a/plugins/torque-contact-form/src/shortcode/torque-contact-form-shortcode-class.php b/plugins/torque-contact-form/src/shortcode/torque-contact-form-shortcode-class.php new file mode 100644 index 00000000..f1b51e2f --- /dev/null +++ b/plugins/torque-contact-form/src/shortcode/torque-contact-form-shortcode-class.php @@ -0,0 +1,79 @@ +expected_args = array( + 'recipient_email' => '', + ); + + add_shortcode( self::$SHORTCODE_SLUG , array( $this, 'shortcode_handler') ); + + add_action( 'load-post.php' , array( + Torque_Contact_Form_TinyMCE::get_inst(), + 'init' ), + 20 ); + add_action( 'load-post-new.php', array( + Torque_Contact_Form_TinyMCE::get_inst(), + 'init' ), + 20 ); + } + + /** + * The callback for the shortcode, should output some markup to be displayed. + * + * @param array $atts Attributes found when parsing shortcode + * @param string $content Children found inside enclosing shortcode tags + * @return string + */ + public function shortcode_handler( $atts, $content ) { + $this->atts = $this->setup_atts( $atts ); + $this->content = $content; + + return $this->get_markup(); + } + + /** + * Combing the atts found when parsing the shortcode with our default atts + * + * @param array $atts Attributes found when parsing shortcode + * @return array Attributes combined with our defaults + */ + private function setup_atts($atts) { + return shortcode_atts( array_merge( $this->expected_args, + // these are your arguments that do not show up in the front end. + array( + '' => '' + ) ), $atts, self::$SHORTCODE_SLUG ); + } + + + /** + * Using the atts and content saved to the instance, + * we should return some markup here that the shortcoded will be returned as. + * + * @return string + */ + private function get_markup() { + $form = new Torque_Contact_Form_Form( $this->atts['recipient_email'] ); + return $form->get_form_markup(); + } +} + +?> diff --git a/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-class.php b/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-class.php new file mode 100644 index 00000000..ab37e29d --- /dev/null +++ b/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-class.php @@ -0,0 +1,58 @@ + + + id ) + || get_current_screen()->base != 'post' ) + return; + include_once 'torque-contact-form-tinymce-editor.html'; + } +} + +?> diff --git a/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-editor.html b/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-editor.html new file mode 100644 index 00000000..4fc30dec --- /dev/null +++ b/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-editor.html @@ -0,0 +1,8 @@ + diff --git a/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-plugin.js b/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-plugin.js new file mode 100644 index 00000000..e012a425 --- /dev/null +++ b/plugins/torque-contact-form/src/shortcode/torque-contact-form-tinymce-plugin.js @@ -0,0 +1,87 @@ +(function($) { + $(document).ready(function() { + // holds our row shortcode name + var shortcode_string = "torque_contact_form"; + + wp.media = wp.media || {}; + wp.mce = wp.mce || {}; + + // our torque_contact_form template + wp.mce.torque_contact_form = { + // holds data from our shortcode + shortcode_data: {}, + + // set our template + // @see editor-torque-contact-form-template.html + template: wp.media.template("editor-torque-contact-form-template"), + + // gets called everytime the shortcode is + // loaded in the Visual tab of the WYSISWYG + getContent: function() { + // build options + var options = { ...this.shortcode.attrs.named }; + // insert template into editor + return this.template(options); + }, + + // get called when clicking on the edit + // button on the shortcode's UI in the + // Visual tab of the WYSIWYG editor + edit: function(data) { + // build options + var shortcode_data = wp.shortcode.next(shortcode_string, data); + var values = shortcode_data.shortcode.attrs.named; + wp.mce.torque_contact_form.popupwindow(tinyMCE.activeEditor, values); + }, + + popupwindow: function(editor, values, onsubmit_callback) { + values = values || []; + if (typeof onsubmit_callback !== "function") { + onsubmit_callback = function(e) { + // Insert content when the window form is submitted (this also replaces during edit, handy!) + var _attr = {}; + + if (e.data.recipient_email) { + _attr.recipient_email = e.data.recipient_email; + } + + var args = { + tag: shortcode_string, + type: "closed", + content: "", + attrs: _attr + }; + editor.insertContent(wp.shortcode.string(args)); + }; + } + var formBody = [ + { + type: "textbox", + name: "recipient_email", + label: "Recipient Email", + value: values.recipient_email + } + ]; + + editor.windowManager.open({ + title: "Torque Contact Form", + body: formBody, + onsubmit: onsubmit_callback + }); + } + }; // torque_contact_form + + // register the tinymce view template + wp.mce.views.register(shortcode_string, wp.mce.torque_contact_form); + }); + + tinymce.PluginManager.add("torque_contact_form", function(editor) { + editor.addButton("torque_contact_form_button", { + text: "Torque Contact Form", + icon: false, + onclick: function() { + wp.mce.torque_contact_form.popupwindow(editor); + } + }); + }); +})(jQuery); diff --git a/plugins/torque-contact-form/src/torque-contact-form.php b/plugins/torque-contact-form/src/torque-contact-form.php new file mode 100644 index 00000000..ff4a0cf0 --- /dev/null +++ b/plugins/torque-contact-form/src/torque-contact-form.php @@ -0,0 +1,85 @@ + diff --git a/plugins/torque-contact-form/webpack.config.js b/plugins/torque-contact-form/webpack.config.js new file mode 100644 index 00000000..d22721c1 --- /dev/null +++ b/plugins/torque-contact-form/webpack.config.js @@ -0,0 +1,82 @@ +const projectConfig = require('../../config') +const path = require('path') +const webpack = require('webpack') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') + +const srcDir = path.join(__dirname, 'src') +const buildDir = path.join( + projectConfig.root, + 'wp-content/plugins/torque-contact-form' +) + +const config = { + context: srcDir, + + entry: { + main: ['./index.js'], + }, + + output: { + path: path.join(buildDir, './bundles'), + publicPath: '/', + filename: 'bundle.js', + }, + + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + { + test: projectConfig.webpackDefaults.css.test, + exclude: /node_modules/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader?sourceMap', + use: projectConfig.webpackDefaults.css.loaders, + }), + }, + { + test: projectConfig.webpackDefaults.scssModules.test, + exclude: /node_modules/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader?sourceMap', + // resolve-url-loader may be chained before sass-loader if necessary + use: projectConfig.webpackDefaults.scssModules.loaders, + }), + }, + { + test: projectConfig.webpackDefaults.images.test, + exclude: /node_modules/, + use: projectConfig.webpackDefaults.images.loaders, + }, + { + test: projectConfig.webpackDefaults.fonts.test, + exclude: /node_modules/, + use: projectConfig.webpackDefaults.fonts.loaders, + }, + ], + }, + + plugins: [ + new ExtractTextPlugin({ + filename: 'main.css', + publicPath: '/', + allChunks: true, + ignoreOrder: true, + }), + new CopyWebpackPlugin([ + { from: path.join(srcDir, 'shortcode/*.js'), to: buildDir }, + { from: path.join(srcDir, 'shortcode/*.html'), to: buildDir }, + { from: path.join(srcDir, '**/*.php'), to: buildDir }, + ]), + ], + + devtool: 'source-map', +} + +module.exports = config diff --git a/themes/torque/src/includes/utilities/torque-utilities.php b/themes/torque/src/includes/utilities/torque-utilities.php index 799575a5..216fb1c7 100755 --- a/themes/torque/src/includes/utilities/torque-utilities.php +++ b/themes/torque/src/includes/utilities/torque-utilities.php @@ -90,4 +90,18 @@ public static function get_category_id( $cat_name ){ $term = get_term_by( 'name', $cat_name, 'category' ); return $term->term_id; } + + public static function array_swap_assoc($key1, $key2, $array) { + $newArray = array (); + foreach ($array as $key => $value) { + if ($key == $key1) { + $newArray[$key2] = $array[$key2]; + } elseif ($key == $key2) { + $newArray[$key1] = $array[$key1]; + } else { + $newArray[$key] = $value; + } + } + return $newArray; + } }