{"id":2605,"date":"2018-10-31T08:54:26","date_gmt":"2018-10-31T08:54:26","guid":{"rendered":"https:\/\/2018.seattle.wordcamp.org\/?p=2605"},"modified":"2018-10-31T02:56:08","modified_gmt":"2018-10-31T02:56:08","slug":"voice-text-capabilities-wordpress","status":"publish","type":"post","link":"https:\/\/seattle.wordcamp.org\/2018\/voice-text-capabilities-wordpress\/","title":{"rendered":"Adding Voice and Text Capabilities to WordPress"},"content":{"rendered":"<p><em><span style=\"font-weight: 400\">by Peter B. MacIntyre, ZCE\u00a0\u00a0<\/span><\/em><\/p>\n<p style=\"text-align: center\"><strong>DISCLAIMER: We have not tested this code.<\/strong><\/p>\n<p><span style=\"font-weight: 400\">Recently I came across <\/span><a href=\"https:\/\/www.ringcentral.com\" rel=\"nofollow\"><span style=\"font-weight: 400\">RingCentral<\/span><\/a><span style=\"font-weight: 400\">\u00a0<em>(<span style=\"color: #339966\">one of our sponsors<\/span>),\u00a0<\/em>and their way cool suite of communications products. \u00a0With RingCentral, companies are able to take their traditional phone system into the cloud (letting them have multiple numbers ring, make calls from their office phone, laptop, or cell phone, and letting them configure their phone system through an online interface).<\/span><\/p>\n<p><span style=\"font-weight: 400\">But beyond the traditional phone system, RingCentral also provides numerous APIs and widgets via the <\/span><a href=\"https:\/\/developers.ringcentral.com\/\"><span style=\"font-weight: 400\">Developer program<\/span><\/a><span style=\"font-weight: 400\"> &#8211; letting their customers <\/span><a href=\"https:\/\/developers.ringcentral.com\/api-products\/voice\"><span style=\"font-weight: 400\">make phone calls<\/span><\/a><span style=\"font-weight: 400\">, <\/span><a href=\"https:\/\/developers.ringcentral.com\/api-products\/sms\"><span style=\"font-weight: 400\">send text messages<\/span><\/a><span style=\"font-weight: 400\">, build out <\/span><a href=\"https:\/\/developers.ringcentral.com\/api-products\/team-messaging\"><span style=\"font-weight: 400\">team messaging<\/span><\/a><span style=\"font-weight: 400\"> and interactions, <\/span><a href=\"https:\/\/developers.ringcentral.com\/api-products\/meetings\"><span style=\"font-weight: 400\">schedule meetings<\/span><\/a><span style=\"font-weight: 400\">, and even <\/span><a href=\"https:\/\/developers.ringcentral.com\/api-products\/fax\"><span style=\"font-weight: 400\">send faxes<\/span><\/a><span style=\"font-weight: 400\"> (if that\u2019s still a thing) all online programmatically.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Seeing all of these capabilities made me start to wonder, what could I as a WordPress developer do with them? \u00a0And how could these features help my customers?<\/span><span style=\"font-weight: 400\">Naturally, that meant it was time to build a WordPress plugin. \u00a0Specifically one that would let users subscribe to be notified via text message when new blogs were posted, but also that would let administrators make phone calls and send SMS messages from within the WordPress admin.<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Jumping into the Code<\/span><\/h2>\n<p><span style=\"font-weight: 400\">To get started, I created a basic WordPress plugin skeleton. Create a folder under the wp-content\/plugins folder structure and call it the same name as your plugin \u201c\/ringcentral\u201d in our case. Then have a PHP file named the same as the folder within that folder: \u201cringcentral.php\u201d in our case. Also, in that folder you should have a file called \u201cuninstall.php\u201d that will be used if the plugin is to be removed at any time. This will have any commands in it that will allow the plugin to remove itself from the WordPress environment and also tidy up after itself in the database if that is required. The overall folder structure should look like this including folders for all the supporting cast of characters (technology) that you may use over time. The \u201cincludes\u201d folder is where you would place the majority of your plugin\u2019s PHP code.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Within that \u201cringcentral.php\u201d PHP file you should have the basic comment header that describes the plugin and any licensing that goes along with it, typically the GPL2 license is used. Here is a generic example that would be in this path \u00a0\u00a0\u00a0wp-content\/plugins\/my-own-plugin\/my-own-plugin.php:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/*<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Plugin Name: My Own Plugin<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Plugin URI: http:\/\/paladin-bs.com\/mage\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Description: My Own Plugin Admin System<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Author: Peter MacIntyre<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Version: 0.25<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Author URI: \u00a0http:\/\/paladin-bs.com\/peter-macintyre\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> Details URI: http:\/\/paladin-bs.com<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> License: \u00a0\u00a0\u00a0\u00a0GPL2<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> License URI: https:\/\/www.gnu.org\/licenses\/gpl-2.0.html<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">My Own Plugin is free software: you can redistribute it and\/or modify<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">it under the terms of the GNU General Public License as published by<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">the Free Software Foundation, either version 2 of the License, or<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">any later version.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">My Own Plugin is distributed in the hope that it will be useful,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">but WITHOUT ANY WARRANTY; without even the implied warranty of<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">GNU General Public License for more details.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">See License URI for full details.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">*\/<\/span><\/p>\n<p><span style=\"font-weight: 400\">After this typical heading, you would start your code. Keeping in mind that usually you are creating a plugin with an admin menu component, you would add in a basic menu structure with code like this:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* ===================== *\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* Make top level menu \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0*\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* ===================== *\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">function My_Own_Plugin(){<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0add_menu_page(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;My_Own_Plugin Admin Management Page&#8217;, \u00a0\/\/ Page &amp; tab title<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;My Own Plugin&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Menu title<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;manage_options&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Capability option<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;MOP_Admin&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Menu slug<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;MOP_config_page&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ menu destination function call<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;dashicons-admin-multisite&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/\/ use a dashicon available in WordPress<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ https:\/\/developer.wordpress.org\/resource\/dashicons<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"color: #339966\"><span style=\"font-weight: 400\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span> <span style=\"font-weight: 400\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ plugin_dir_url(__FILE__) . &#8216;images\/My_Own_Plugin_icon.jpg&#8217;, <\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span> <span style=\"font-weight: 400\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ or add your own custom menu icon<\/span><\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a080 ); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ menu position level <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0add_submenu_page(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;MOP_Admin&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ parent slug<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;My_Own_Plugin System Configuration Page&#8217;, \/\/ page title<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;System Config&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ menu title &#8211; can be different than parent<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;manage_options&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ options<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;MOP_Admin&#8217; ); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ menu slug to match top level (go to the same link)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0add_submenu_page(<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;MOP_Admin&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ parent menu slug<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;Do Something Cool Page&#8217;, \u00a0\/\/ page title<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;Do Something Cool&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ menu title<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;manage_options&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ capability<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;DSC_upl&#8217;, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ menu slug<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;DSC_html&#8217; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ callable function<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);<\/span><\/p>\n<p><span style=\"font-weight: 400\">Then calling the add_action hook to run the My_Own_Plugin menu building function, and sub-menu functions.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">add_action(&#8216;admin_menu&#8217;, &#8216;My_Own_Plugin&#8217;); \u00a0\/\/ call add action func on menu building function above<\/span><\/p>\n<p><span style=\"font-weight: 400\">Done right this will create a new plugin entry on the inactive tab of the admin plugin section. Activate the plugin and you should see a new menu item structure that should look like this.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2607\" data-permalink=\"https:\/\/seattle.wordcamp.org\/2018\/voice-text-capabilities-wordpress\/ringcentral-2\/\" data-orig-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral.png\" data-orig-size=\"360,126\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"RingCentral\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral-300x105.png\" data-large-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral.png\" class=\" wp-image-2607 alignleft\" src=\"https:\/\/2018.seattle.wordcamp.org\/files\/2018\/10\/RingCentral-300x105.png\" alt=\"\" width=\"360\" height=\"126\" srcset=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral-300x105.png 300w, https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral.png 360w\" sizes=\"auto, (max-width: 360px) 100vw, 360px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2><span style=\"font-weight: 400\">Adding the Virtual Phone<\/span><\/h2>\n<p><span style=\"font-weight: 400\">One of the nicer elements of RingCentral Developers is that they offer a <\/span><a href=\"https:\/\/developers.ringcentral.com\/embeddable-voice.html\"><span style=\"font-weight: 400\">copy and paste widget<\/span><\/a><span style=\"font-weight: 400\">. \u00a0This meant that I could add the virtual phone to the admin with the following HTML snippet:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"color: #339966\"><span style=\"font-weight: 400\">&lt;<\/span><span style=\"font-weight: 400\">script<\/span> <span style=\"font-weight: 400\">src<\/span><span style=\"font-weight: 400\">= <\/span><span style=\"font-weight: 400\">&#8220;https:\/\/ringcentral.github.io\/ringcentral-embeddable-voice\/adapter.js&#8221;<\/span><span style=\"font-weight: 400\">&gt;&lt;<\/span><span style=\"font-weight: 400\">\/script<\/span><span style=\"font-weight: 400\">&gt;<\/span><\/span><\/p>\n<p><span style=\"font-weight: 400\">However, I wanted to go a little bit further, giving the administrator the ability to not only use the API to send SMS messages, but also be able to turn different features of my plugin on or off. \u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400\">To do this, I built a basic page for the WordPress admin area and jazzed it up with a logo or two. I added a checkbox to turn on or off the embedded phone tool and kept that value in a database table I created.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2608\" data-permalink=\"https:\/\/seattle.wordcamp.org\/2018\/voice-text-capabilities-wordpress\/ringcentral2\/\" data-orig-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2.png\" data-orig-size=\"997,397\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"RingCentral2\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2-300x119.png\" data-large-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2.png\" class=\"alignleft wp-image-2608 size-full\" src=\"https:\/\/2018.seattle.wordcamp.org\/files\/2018\/10\/RingCentral2.png\" alt=\"\" width=\"997\" height=\"397\" srcset=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2.png 997w, https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2-300x119.png 300w, https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2-768x306.png 768w, https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral2-500x199.png 500w\" sizes=\"auto, (max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">A quick tangent: be sure to write your SQL code with security and stability in mind. By this I mean that you should be using <\/span><a style=\"font-size: 1rem\" href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wpdb\/prepare\/\">SQL prepared statements<\/a><span style=\"font-weight: 400\"> as one means of defense, it will save you a lot of headaches. Also on the stability point, when creating a plugin that will change and improve over time be sure to write your SQL table alterations (in an activation subroutine) with the <\/span><a style=\"font-size: 1rem\" href=\"https:\/\/developer.wordpress.org\/reference\/functions\/dbdelta\/\">dbDelta<\/a><span style=\"font-weight: 400\"> WordPress function; you\u2019ll be glad that you did.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Next I wrote a simple \u201cadmin_footer\u201d action in the plugin that would call a function to interrogate the DB value and show or hide the phone app accordingly.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* ============================================= *\/ <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* Add action for the ringcentral Embedded Phone app toggle \u00a0\u00a0*\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* ============================================= *\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">add_action(&#8216;admin_footer&#8217;, &#8216;ringcentral_embed_phone&#8217;);<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* ====================================== *\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* Add custom footer action \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0*\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* This toggles the ringcentral Embedded Phone app \u00a0*\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">\/* ======================================= *\/<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">function ringcentral_embed_phone() {<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0global $wpdb; \u00a0\u00a0\u00a0<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0$result_rc = $wpdb-&gt;get_row( $wpdb-&gt;prepare(&#8220;SELECT `embedded_phone` <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FROM `ringcentral_control`<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0WHERE `ringcentral_control_id` = %d&#8221;, 1)<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0); \u00a0<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0if ($result_rc-&gt;embedded_phone == 1) { ?&gt;<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"color: #339966\"><span style=\"font-weight: 400\"> \u00a0\u00a0\u00a0<\/span> <span style=\"font-weight: 400\"><a href=\"https:\/\/ringcentral.github.io\/ringcentral-embeddable-voice\/adapter.js\">https:\/\/ringcentral.github.io\/ringcentral-embeddable-voice\/adapter.js<\/a><\/span><\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0&lt;?php } <\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">}<\/span><\/p>\n<p><span style=\"font-weight: 400\">The neat thing about the \u201cadmin_footer\u201d action call is that it inserts my defined function call in every page of the admin interface thus allowing the phone app to be accessible anywhere within the WordPress admin area.<\/span><\/p>\n<p><span style=\"font-weight: 400\">And just like that I now had my virtual phone in WordPress!<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2609\" data-permalink=\"https:\/\/seattle.wordcamp.org\/2018\/voice-text-capabilities-wordpress\/ringcentral3\/\" data-orig-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral3.png\" data-orig-size=\"308,543\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"RingCentral3\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral3-170x300.png\" data-large-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral3.png\" class=\"size-medium wp-image-2609 alignleft\" src=\"https:\/\/2018.seattle.wordcamp.org\/files\/2018\/10\/RingCentral3-170x300.png\" alt=\"\" width=\"170\" height=\"300\" srcset=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral3-170x300.png 170w, https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral3.png 308w\" sizes=\"auto, (max-width: 170px) 100vw, 170px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2><span style=\"font-weight: 400\">Adding in SMS Notifications<\/span><\/h2>\n<p><span style=\"font-weight: 400\">While the virtual phone let me manually send SMS messages and make phone calls from the admin, I wanted to let my readers (and my customer\u2019s readers) get SMS notifications about new blog posts automatically.<\/span><\/p>\n<p><span style=\"font-weight: 400\">To do this, I built a widget interface asking site visitors if they want to sign up for news feed notifications (blog announcements) when new posts are published on the website.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2610\" data-permalink=\"https:\/\/seattle.wordcamp.org\/2018\/voice-text-capabilities-wordpress\/ringcentral4\/\" data-orig-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral4.png\" data-orig-size=\"327,427\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"RingCentral4\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral4-230x300.png\" data-large-file=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral4.png\" class=\"size-medium wp-image-2610 alignleft\" src=\"https:\/\/2018.seattle.wordcamp.org\/files\/2018\/10\/RingCentral4-230x300.png\" alt=\"\" width=\"230\" height=\"300\" srcset=\"https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral4-230x300.png 230w, https:\/\/seattle.wordcamp.org\/2018\/files\/2018\/10\/RingCentral4.png 327w\" sizes=\"auto, (max-width: 230px) 100vw, 230px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">When a user signed up, they would then be added to the database, and receive an email notification asking them to confirm their email address, an SMS notification asking them to confirm their phone number, or both.<\/span><\/p>\n<p><span style=\"font-weight: 400\">To send an email notification, I simply used the pre-existing wp_mail function, but to send SMS messages I would need to call the RingCentral API.<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Adding in the RingCentral API<\/span><\/h3>\n<p><span style=\"font-weight: 400\">Thankfully, RingCentral provides a full <\/span><a href=\"https:\/\/github.com\/ringcentral\/ringcentral-php\"><span style=\"font-weight: 400\">PHP SDK<\/span><\/a><span style=\"font-weight: 400\"> that lets you take advantage of all their API calls, without having to understand cURL, adding attachments (for MMS or fax), or boundaries (don\u2019t even ask). \u00a0You can get the RingCentral SDK using <\/span><a href=\"https:\/\/getcomposer.org\/\"><span style=\"font-weight: 400\">Composer<\/span><\/a><span style=\"font-weight: 400\"> by running the following command:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">php composer.phar require ringcentral\/ringcentral-php<\/span><\/p>\n<p><span style=\"font-weight: 400\">And then including the RingCentral SDK with the vendor autoload file:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"color: #339966\"><span style=\"font-weight: 400\">require<\/span><span style=\"font-weight: 400\">(<\/span><span style=\"font-weight: 400\">&#8216;vendor\/autoload.php&#8217;<\/span><span style=\"font-weight: 400\">);<\/span><\/span><\/p>\n<p><span style=\"font-weight: 400\">Now calling the RingCentral PHP SDK class is as simple as:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">$sdk = ringcentral_sdk() ;<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Sending the Text Message<\/span><\/h2>\n<p><span style=\"font-weight: 400\">Now was the time for the moment of truth. \u00a0I built a function using RingCentral\u2019s SDK to send text messages, wrapping it in a try\/catch, like so:<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">try {<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0$apiResponse = $sdk-&gt;platform()-&gt;post(&#8216;\/account\/~\/extension\/~\/sms&#8217;,<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0array(&#8216;from&#8217; =&gt; array(&#8216;phoneNumber&#8217; =&gt; &#8216;$config[\u2018ringcentral_number\u2019]&#8217;),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;to&#8217; =&gt; array( array(&#8216;phoneNumber&#8217; =&gt; $mobile_contact[&#8216;mobile&#8217;]) ),<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8216;text&#8217; =&gt; $message ) );<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0} catch (\\RingCentral\\SDK\\Http\\ApiException $e) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0$apiResponse = $e-&gt;apiResponse();<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0$return_message = &#8220;There was an error broadcasting to the mobile list. =&gt; &#8221; . $apiResponse;<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0}<\/span><\/p>\n<p><span style=\"font-weight: 400\">In the case of this function, I would pass in $sdk and $config as globals, and pass the $mobile_contact and $message as parameters, letting me reuse the function for each subscriber within a loop.<\/span><\/p>\n<p><span style=\"font-weight: 400\">And needless to say &#8211; it worked! \u00a0Well sort of\u2026 my friends in the US and Canada received the text without any problem. \u00a0But it turns out, to protect users RingCentral requires you to enable international SMS messages for the API (as domestic text messages are free, but international texts usually cost more due to carrier fees). \u00a0This means to use RingCentral SMS internationally, you\u2019ll need to enable this on your RingCentral account (and also be very conscious of how many international messages you\u2019re sending).<\/span><\/p>\n<p><span style=\"font-weight: 400\">One solution I came up with was to limit the SMS subscriptions to North American numbers only, while letting everyone receive email notifications. \u00a0This was as simple as implementing a quick regex to validate the phone number and the country code.<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">if (!preg_match(\u2018\/1[0-9]{9}\/\u2019) {<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\"> \u00a0\u00a0\u00a0echo \u2018Only US Numbers are supported at this time.\u2019;<\/span><\/p>\n<p style=\"padding-left: 30px\"><span style=\"font-weight: 400;color: #339966\">}<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Next Steps<\/span><\/h2>\n<p><span style=\"font-weight: 400\">With RingCentral\u2019s platform, the sky\u2019s the limit &#8211; as you have the ability to not only create SMS notifications, but start video meetings and build in real-time, rich messaging that can be accessed from within your app, via the RingCentral App, and on the go.<\/span><\/p>\n<p><span style=\"font-weight: 400\">The same goes for this plugin. \u00a0While it now supports the virtual phone widget and SMS notifications, I\u2019m excited to see what else we can add to it, and how we can take advantage of real-time communication tools inside of WordPress.<\/span><\/p>\n<p><span style=\"font-weight: 400\">If you happen to be at either WordCamp Seattle, or RingCentral\u2019s <\/span><a href=\"https:\/\/connectcentral.ringcentral.com\/\"><span style=\"font-weight: 400\">ConnectCentral conference<\/span><\/a><span style=\"font-weight: 400\"> in San Francisco I\u2019d love to talk to you more about how I built the plugin, it\u2019s current status, and what the future holds. \u00a0And, of course, if you\u2019re looking for a WordPress developer &#8211; <\/span><a href=\"https:\/\/paladin-bs.com\"><span style=\"font-weight: 400\">Paladin<\/span><\/a><span style=\"font-weight: 400\"> is open for business!<\/span><\/p>\n<p><span style=\"font-weight: 400\">You can also get more information about this plugin, including when it is officially released at <\/span><a href=\"https:\/\/paladin-bs.com\/contact\/\"><span style=\"font-weight: 400\">https:\/\/paladin-bs.com\/contact\/<\/span><\/a><span style=\"font-weight: 400\"> (please use \u201cRC API Plugin\u201d as the subject).<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">=====================<\/span><\/p>\n<p><span style=\"font-weight: 400\">Peter MacIntyre has over 28 years of experience in IT, primarily in PHP. Author of &#8220;<\/span><a href=\"https:\/\/www.phparch.com\/books\/building-exceptional-websites-with-wordpress-thesis\/\"><span style=\"font-weight: 400\">Building Exceptional Sites with WordPress &amp; Thesis<\/span><\/a><span style=\"font-weight: 400\">&#8221; and &#8220;<\/span><a href=\"https:\/\/www.amazon.com\/PHP-Good-Parts-Delivering-Best\/dp\/0596804377\"><span style=\"font-weight: 400\">PHP: The Good Parts<\/span><\/a><span style=\"font-weight: 400\">&#8220;; co-author: Programming PHP-3rd Ed &amp; Pro PHP Programming, among others. Peter has spoken at PHP Central Europe (Warsaw, Poland); PHP[World] 2016 (Washington, DC); ZendCon 2016 (Las Vegas), NortheastPHP (Boston), CA-World (New Orleans); CA-TechniCon (K\u00f6ln, Germany); and CA-Expo (Melbourne, Australia). \u00a0Peter is a past co-chair and co-founder for the Northeast PHP Developer&#8217;s Conference, and is Zend certified for both PHP 5.3 and 4.0.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>by Peter B. MacIntyre, ZCE\u00a0\u00a0 DISCLAIMER: We have not tested this code. Recently I came across RingCentral\u00a0(one of our sponsors),\u00a0and their way cool suite of communications products. \u00a0With RingCentral, companies are able to take their traditional phone system into the cloud (letting them have multiple numbers ring, make calls from their office phone, laptop, or &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/seattle.wordcamp.org\/2018\/voice-text-capabilities-wordpress\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Adding Voice and Text Capabilities to WordPress&#8221;<\/span><\/a><\/p>\n","protected":false},"author":15618397,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1246044],"tags":[216,14],"class_list":["post-2605","post","type-post","status-publish","format-standard","hentry","category-meet-the-ground-crew","tag-plugins","tag-wordpress"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9ygka-G1","_links":{"self":[{"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/posts\/2605","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/users\/15618397"}],"replies":[{"embeddable":true,"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/comments?post=2605"}],"version-history":[{"count":5,"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/posts\/2605\/revisions"}],"predecessor-version":[{"id":2614,"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/posts\/2605\/revisions\/2614"}],"wp:attachment":[{"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/media?parent=2605"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/categories?post=2605"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/seattle.wordcamp.org\/2018\/wp-json\/wp\/v2\/tags?post=2605"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}