SAFE-Chris-Code-Samples
Thank you for your interest in my code samples. I greatly appreciate you taking the time to study my code. I will be glad to answer any further questions you may have. Please expand the code samples below to review the snippets. In addition, my cover page summarizes my work history and past clients. At any time, you can click the blue button above to return to my cover page. Every project is different. The sample code below, server architecture, database designs, and other decisions are project specific and may not apply to other projects. I made these particular decisions to coordinate into a specific set of budget, scope, and other constraints. Below is an outline of a real world code task for a previous project. To demonstrate my work, I will outline setting the goal, identifying a known issue, and how I solved this issue. Some code has been hidden to protect private authentication keys. Also these code snippets are only a small part of a much larger platform. Note: My non-standard naming conventions are derived from over two decades of C# coding. Earlier versions of PHP did not support types, and I was trained in type based languages. Therefore, sometimes I may use a lowercase prefix on some variables as a reminder of their types. However, I can easily adapt to any standard naming convention that a new coding team requires.
Sample 1 – Goal – Image Upload via WordPress API
Voice.club – Sample WebApp for Writing and Language – Design Goals
Design Goals – Primary Focus – WebApp via WordPress
This code task shown below was a small part of a large WebApp for Writers and Language. The sample code below is one task of a writing contest submission module allowing image uploads. The module should work from a desktop or mobile web browser. Our goal was to build a Word Press reusable block allowing a mobile phone browser to take pictures, then allow the user to upload directly to an image server. I decided to use the WordPress API to transfer and store the image. The design involved two WordPress sites, one which represented the WebApp, and the second which was used for the image server. By offloading images to a remote server, the primary WordPress site becomes more like a smart-client streamlined WebApp.
CODE SAMPLE 1.1 – PHP – HTML – Building the basic UI
Building the Block – PHP Class Object and HTML
Remember, our goal was a Word Press reusable block allowing a mobile phone browser to take pictures, then allow the user to upload directly to an image server. The code below builds the first step in the process, creating the block’s main PHP object class, and building the UI Page based on various permissions and business rules.Chris-Code-Sample-1.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
//sc_media_upload.php | |
// | |
//2020.10.17.CT-TODO: - List latest 5 or 10 items from the MediaServer | |
// - Create New Function to List Media | |
// - Use this new function to check and see if media is uploaded correctly | |
//WP API DOC: | |
// https://developer.wordpress.org/rest-api/reference/media/ | |
// https://developer.wordpress.org/rest-api/reference/media/#list-media | |
//2021.01.24.CT: -Session-Bug-Fix: | |
// If you want more info, check the following URL | |
// https://www.xspdf.com/resolution/53892058.html | |
// | |
add_shortcode('sc_media_upload','MediaInit'); | |
// | |
class cMedia { | |
public $img_upload_button; | |
public $s_url_ajax; | |
public $convert_Api_Key; | |
//2019.09.16.CT:DONE | |
public $s_url_CloudConvertAPI; | |
public $WPAPIUser; | |
public $WPAPIPassword; | |
public $WPAPIservice_url; | |
//2019.10.05.CT:DONE | |
public $aFileTypesAllowed; | |
public $aFileTypesAllowedStr; | |
public $aFileTypesConverted; | |
public $aFileTypesAllowedJS; | |
//2019.10.27.CT:DONE | |
public $sFilename; | |
//2021.01.25.CT:DONE | |
public $sURLNextPage; | |
public $sNextPageLinkText; | |
public $bCloudConvertEnabled; | |
public function __construct () { | |
$this->img_upload_button = plugin_dir_url( __FILE__ ).'sa-upload-button.png'; | |
$this->s_url_ajax = admin_url('admin-ajax.php'); | |
$this->convert_Api_Key = "*****"; | |
$this->s_url_CloudConvertAPI = "https://api.cloudconvert.com/v1/convert"; | |
$this->WPAPIUser = "*****"; | |
$this->WPAPIPassword = "*****"; | |
$this->WPAPIservice_url = "*****/wp-json/wp/v2/media"; | |
//2019.10.05.CT:DONE | |
// TODO:Future: Possible Values: jpg, png, mp4, mp3 | |
$this->aFileTypesAllowed=array("jpg","JPG","png","PNG","jpeg","JPEG","mp3","MP3","gif","GIF"); | |
$this->aFileTypesConverted=array("jpg","JPG","png","PNG","jpeg","JPEG","gif","GIF"); | |
$this->aFileTypesAllowedJS = implode(",", $this->aFileTypesAllowed); | |
$this->initAllowedTypes(); | |
//2019.10.27.CT:DONE | |
$this->sFilename="WPAPI"; | |
} | |
public function initAllowedTypes() { | |
$types = ''; | |
foreach($this->aFileTypesAllowed as $type) { | |
if($types != '') | |
$types .= ','; | |
$types .= '.'.$type; | |
} | |
$this->aFileTypesAllowedStr = $types; | |
} | |
} | |
//Main Shortcode Function - MediaInit: | |
// | |
function MediaInit($atts) { | |
extract(shortcode_atts(array( | |
'media_category' => 'Default', | |
'media_form_id' => '0', | |
'media_tag' => '2021-XXX-01-XXX', | |
'next_page_url_slug' => 'TODO-Next-Form-Page', | |
'next_page_link_text' => 'DONE-Click Here to Continue ->', | |
'cloud_convert_enabled' => '0', | |
), $atts)); | |
//2021.01.21.CT - Need a Taxanomy to Prefix the media filename: | |
//2021.01.25.CT - MediaSubmitKey - Identifies Media to Form Entry | |
// | |
// sMediaSubmitKey = FormID-UserId-DateTimeKey | |
// DateTimeKey = https://www.php.net/manual/en/datetime.formats.compound.php | |
// DateTimeKey = https://stackoverflow.com/questions/20822821/what-is-a-unix-timestamp-and-why-use-it | |
// DateTimeKey = Unix Timestamp "@" "-"? [0-9]+ "@1215282385" | |
// | |
$sMediaSubmitKey = $media_form_id."-".get_current_user_id()."-".strtotime("now"); | |
// | |
$sMediaFileNamePrefix = | |
$media_category | |
."-KEY-".$sMediaSubmitKey | |
."-TAG-".$media_tag."-FILE-"; | |
//2021.01.21.CT - Current User Is Admin: | |
$bCurrentUserIsAdmin = 0; | |
// | |
if(current_user_can('administrator')) { | |
$bCurrentUserIsAdmin = 1; | |
} | |
else { | |
$bCurrentUserIsAdmin = 0; | |
} | |
$oMedia = new cMedia(); | |
$oMedia->sFilename = $oMedia->sFilename.'-'.$sMediaFileNamePrefix; | |
//2021.01.29.CT: | |
$oMedia->bCloudConvertEnabled = cloud_convert_enabled; | |
//2021.01.29.CT:TODO - Pseudocode | |
//Add this code to Guzzle: | |
// IF ($oMedia->bCloudConvertEnabled = 1) | |
// Convert File via CloudConvert | |
// ELSE IF ($oMedia->bCloudConvertEnabled = 0) | |
// Do NOT ConvertFile (Do not send to CloudConvertAPI) | |
// Upload Directly to SoftArt MediaServer -> WPAPIservice_url | |
// END IF | |
//2021.01.25.CT:TODO: continue_form_url_slug | |
//ROOT = https://www.voice.club/ | |
//sURLNextPage = ROOT + next_page_url_slug | |
// | |
$sURLRoot = "https://www.voice.club/"; | |
$sURLParam_sMediaSubmitKey = "?MediaSubmitKey=".$sMediaSubmitKey; | |
// | |
$oMedia->sURLNextPage = $sURLRoot | |
.$next_page_url_slug | |
.$sURLParam_sMediaSubmitKey; | |
$oMedia->sNextPageLinkText = $next_page_link_text; | |
// | |
media_Init_Global_JS_Vars( | |
$oMedia->s_url_ajax, | |
$oMedia->aFileTypesAllowedStr, | |
$bCurrentUserIsAdmin); | |
upload_init($oMedia); | |
$oMedia_json = json_encode($oMedia); | |
$_SESSION['oMedia'] = $oMedia_json; | |
} | |
function media_Init_Global_JS_Vars( | |
$sURL_Admin_Ajax, | |
$aFileTypesAllowedJS, | |
$bCurrentUserIsAdmin) { | |
// | |
$sHTML = <<<HTML | |
<script type="text/javascript" > | |
var sa_ajax_url = "$sURL_Admin_Ajax"; | |
var types_str = "$aFileTypesAllowedJS"; | |
var sa_media_upload_file_types = types_str.split(","); | |
var upload_Image_width_Allowed = 500; | |
var upload_Image_height_Allowed = 500; | |
var bCurrentUserIsAdmin = $bCurrentUserIsAdmin; | |
</script> | |
HTML; | |
// | |
return $sHTML; | |
} | |
//2021.01.12.CT: htmlAdminOnly - Only Admin user can see this HTML: | |
function htmlAdminOnly_GetImageByURL() { | |
$sHTML = <<<HTML | |
<div> | |
<input id="image_path" | |
type="text" | |
placeholder="Enter Image URL" | |
style="width:80%;"> | |
<input id="post_image" | |
type="button" | |
class="wpbf-button" | |
name="post_image" | |
value="Upload Image"> | |
</div> | |
HTML; | |
if(current_user_can('administrator')) { | |
return $sHTML; | |
} | |
else { | |
return ""; | |
} | |
// | |
} | |
//2021.01.12.CT: htmlAdminOnly - Only Admin user can see this HTML: | |
function htmlAdminOnly_GetMedia() { | |
$sHTML = <<<HTML | |
<div> | |
<input id="getMedia" | |
type="button" | |
class="wpbf-button" | |
name="getMedia" | |
value="get media"> | |
</input> | |
<select id="resultNum" | |
name="resultNum" | |
style="width:100px;"> | |
<option value="1">1</option> | |
<option value="5">5</option> | |
<option value="10">10</option> | |
<option value="15">15</option> | |
</select> | |
<div id='media' | |
style="display:none; padding-top:20px;"> | |
</div> | |
</div> | |
HTML; | |
if(current_user_can('administrator')) { | |
return $sHTML; | |
} | |
else { | |
return ""; | |
} | |
// | |
} | |
function upload_init($oMedia){ | |
// | |
$htmlAdminOnly_GetImageByURL = htmlAdminOnly_GetImageByURL(); | |
$htmlAdminOnly_GetMedia = htmlAdminOnly_GetMedia(); | |
// | |
$sHTML = <<<HTML | |
<!-- 2020.03.31.CT - Text Below Does Not Align --> | |
<!-- <h3>Select File and Upload:</h3> --> | |
<form method="post" enctype="multipart/form-data" action=""> | |
<div class="image-upload"> | |
<label for="file-input"> | |
<!-- 2021.01.26.CT-TODO: Replace URL Below with STAFF Image URL --> | |
<img id="imageUploadButton" | |
src="*****/sa-upload-button.png" /> | |
</label> | |
<input id='fileToUpload' | |
type='file' | |
name='fileToUpload' | |
accept='$oMedia->aFileTypesAllowedStr'> | |
</input> | |
<div> | |
<!-- <b>Allowed File Types: $oMedia->aFileTypesAllowedStr </b> --> | |
<!-- <b>Allowed File Types: jpg, png, mp3, mp4</b> --> | |
</div> | |
$htmlAdminOnly_GetImageByURL | |
</div> | |
</form> | |
<div id="progressDivId" | |
style="display:none; | |
padding-top:20px;" > | |
<div id="bar" | |
class="line stripesLoader" | |
style="background-position:0%; background-color:green"> | |
</div> | |
</div> | |
<div id='msg' | |
style="display:none; padding-top:20px;"> | |
</div> | |
<hr> | |
$htmlAdminOnly_GetMedia | |
HTML; | |
// | |
return $sHTML; | |
} |
CODE SAMPLE 1.2 – JavaScript – Building the JS Tier – Axios
This task requires only a very simple JavaScript UI code, so in this case I chose to use plain vanilla code. Actually the bulk of the work in this task happens at the server tier. Remember, our goal was a Word Press reusable block allowing a mobile phone browser to take pictures, then allow the user to upload directly to an image server. I generally use either plain JS, sometimes jQuery, and for more complex user interfaces, I may use React JS combined with Kendo React Widgets.
Chris-Code-Sample-1.2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//sc_media_upload.js | |
window.onload = function() { | |
var fileToUpload = saQuery("fileToUpload"); | |
// | |
fileToUpload.onchange = function(e) { | |
e.preventDefault(); | |
sendData(); | |
} | |
//2019.08.08.CT: | |
saQuery("imageUploadButton").addEventListener("click", function() | |
{ | |
saQuery("fileToUpload").click(); | |
}); | |
//2021.01.30.CT: | |
if(bCurrentUserIsAdmin) { | |
saQuery("post_image").addEventListener("click", function(e) { | |
e.preventDefault(); | |
sendImageUrl(); | |
}); | |
saQuery("getMedia").addEventListener("click", function(e) { | |
e.preventDefault(); | |
var resultNum = saQuery("resultNum").value; | |
getMedia(resultNum); | |
}); | |
} | |
} | |
function bImageSizeValid(nImageWidth, nImageHeight ) | |
{ | |
//ORIGINAL: (upload_Image_width_Allowed < img.width || upload_Image_height_Allowed < img.width) | |
// (upload_Image_width_Allowed < nImageWidth || upload_Image_height_Allowed < nImageHeight); | |
// | |
let bValid = false; | |
bValid = ((upload_Image_width_Allowed == nImageWidth) && (upload_Image_height_Allowed == nImageHeight)); | |
console.log("voice-bImageSizeValid="+bValid); | |
return bValid; | |
} | |
function htmlImageSizeInvalidMessage(nImageWidth, nImageHeight ) | |
{ | |
let sHTML = "<font color='red'>Invalid Size: Your image size is " | |
+ nImageWidth + " x " | |
+ nImageHeight + " but we require " | |
+ upload_Image_width_Allowed+" x " | |
+ upload_Image_height_Allowed+" size image. </font>"; | |
return sHTML; | |
} | |
function sendData() { | |
//2019.08.08.CT : $ajax_url -> $oMedia->s_url_ajax | |
var file_data = saQuery("fileToUpload").files[0]; | |
var form_data = new FormData(); | |
form_data.append('fileToUpload', file_data); | |
form_data.append('action', 'postWPAPIMedia'); | |
var file_ext = file_data.name.substr(file_data.name.lastIndexOf('.')+1,file_data.name.length); | |
if (saQuery("fileToUpload").value == "") { | |
saQuery("msg").style.display = "block"; | |
saQuery("msg").innerHTML = "<div class='error'>Choose a file to upload.</div>"; | |
} | |
else if(sa_media_upload_file_types.indexOf("."+file_ext) == -1) { | |
saQuery("msg").innerHTML = "<font color='red'>This file type ("+file_ext+") is not supported. </font>"; | |
saQuery("msg").style.display = "block"; | |
} | |
else { | |
if (saQuery("fileToUpload").files[0].type.indexOf("image") == -1) { | |
uploadFile(form_data); | |
} | |
else { | |
let img = new Image(); | |
img.onload = () => { | |
//if(upload_Image_width_Allowed < img.width || upload_Image_height_Allowed < img.height) | |
//2021.01.10.CT: | |
if (!bImageSizeValid(img.width,img.height)) { | |
saQuery("msg").innerHTML = htmlImageSizeInvalidMessage(img.width,img.height); | |
saQuery("msg").style.display = "block"; | |
} | |
else { | |
uploadFile(form_data); | |
} | |
} | |
img.src = window.URL.createObjectURL(file_data); | |
} | |
} | |
} | |
function uploadFile(form_data) { | |
saQuery("msg").style.display = "none"; | |
var percentValue = '0%'; | |
saQuery("bar").style.backgroundPosition = percentValue; | |
saQuery("progressDivId").style.display = "block"; | |
// | |
axios.post(sa_ajax_url, form_data, { | |
onUploadProgress: function (progressEvent) { | |
const totalLength = progressEvent.lengthComputable ? progressEvent.total : progressEvent.target.getResponseHeader('content-length') || progressEvent.target.getResponseHeader('x-decompressed-content-length'); | |
if (totalLength !== null) { | |
var perc = ( progressEvent.loaded / totalLength ) * 100; | |
perc = Math.round(perc); | |
var percentValue = perc + '%'; | |
saQuery("bar").style.backgroundPosition = percentValue; | |
} | |
} | |
}) | |
.then(function (res) { | |
saQuery("fileToUpload").value = ""; | |
if(bCurrentUserIsAdmin) { | |
saQuery("image_path").value = ""; | |
} | |
saQuery("progressDivId").style.display = "none"; | |
saQuery("msg").innerHTML = res.data; | |
saQuery("msg").style.display = "block"; | |
}); | |
} | |
function sendImageUrl(){ | |
var image_path = saQuery("image_path").value; | |
if (!image_path.trim()) { | |
saQuery("msg").style.display = "block"; | |
saQuery("msg").innerHTML = "<div class='error'>Please enter Image url.</div>"; | |
} | |
else { | |
var form_data = new FormData(); | |
form_data.append('image_path', image_path); | |
form_data.append('action', 'postWPAPIMedia'); | |
let img = new Image(); | |
img.onload = () => { | |
//if(upload_Image_width_Allowed < img.width || upload_Image_height_Allowed < img.height) { | |
//2021.01.10.CT: | |
if (!bImageSizeValid(img.width,img.height)) { | |
saQuery("msg").innerHTML = htmlImageSizeInvalidMessage(img.width,img.height); | |
saQuery("msg").style.display = "block"; | |
} else { | |
uploadFile(form_data); | |
} | |
} | |
img.onerror = img.onabort = () => { | |
//it's not image | |
uploadFile(form_data); | |
} | |
img.src = image_path; | |
} | |
} | |
function getMedia(resultNum) { | |
axios.get(sa_ajax_url, { | |
params: { | |
action: 'getWPAPIMedia', | |
resultNum: resultNum | |
} | |
}) | |
.then(function (res) { | |
saQuery("media").innerHTML = res.data; | |
saQuery("media").style.display = "block"; | |
}); | |
} |
CODE SAMPLE 1.3 – Guzzle – CloudConvert.com API + WordPres API – Integrate
The industry standard PHP Guzzle HTTP client library was used for connecting external processes. One issue we found was after uploading from the mobile web browser, WordPress would store and display the image “sideways”. Research found this was a known issue with WordPress, so our fix was to use an image rotation API provided by Cloud Convert. Our final code now offers an optional attribute that allows each image to be rotated and possibly resized. Finally after optional rotation, and resizing, the image will be uploaded to an external media server via the WordPress API. After we implemented the Cloud Convert API rotation fix, now all images appear normal with their correct rotation orientation intact.
Chris-Code-Sample-1.3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: sa-wp-guzzle | |
Description: Voice.club - Guzzle - HTTP Layer | |
Version: 2020.07.27 | |
Author: SoftArt | |
*/ | |
//2020.07.27.CT: Composer Lib + sa-wp-guzzle - Production Install: | |
//------ | |
//COMPOSER - Install -> SSH + SUDO access via PuTTY or CodeAnywhere SSH: | |
// STEP #1: https://getcomposer.org/download/ | |
// STEP #2: | |
// SoftArt (CodeAnywhere):$ composer require guzzlehttp/guzzle:^7.0 | |
// Voice.club (production):$ php composer.phar require guzzlehttp/guzzle:^7.0 | |
// STEP #3: Activate Plugin: sa-wp-guzzle | |
// | |
// http://docs.guzzlephp.org/en/latest/quickstart.html | |
//------ | |
// Load Composer | |
require 'vendor/autoload.php'; | |
use GuzzleHttp\Client; | |
use GuzzleHttp\Psr7\Request; | |
//NOTE: SoftArt API - Global URL + Token: | |
// $_SESSION["sa_api_token"] | |
// $_SESSION["sa_api_url"] | |
//Guzzle - Set Global $client (for this plugin): | |
// | |
function GuzzleClient() { | |
$client = new Client([ | |
// Base URI is used with relative requests | |
'base_uri' => $_SESSION["sa_api_url"], | |
// You can set any number of default request options. | |
'timeout' => 2.0, | |
]); | |
return $client; | |
} | |
////////// | |
// BEGIN: Guzzle - WordPress Connect to SoftArtAPI --> Get MS-SQL Data | |
// Connecting WordPress to an External Microsoft SQL Server DB | |
////////// | |
////////// | |
// BEGIN: Guzzle - MediaUpload | |
// NOTE: Process Uploaded Image using 2 distinct external APIs | |
// 1. CloudConvert.com - API - to convert, resize, and roate image | |
// 2. WordPress API - Transfer and Host Final Converted Image | |
////////// | |
//2020.11.21.CT:DONE: Move saveFile --> Plugin --> sa_wp_guzzle: | |
function sa_guzzle_postWPAPIMedia() { | |
session_start(); | |
//NOTE: Required to Deserialize Class Object from Session | |
$oMedia_json= $_SESSION['oMedia']; | |
$oMedia = json_decode($oMedia_json); | |
$ext; | |
$img_name; | |
$img_path; | |
$post; | |
if(isset($_FILES['fileToUpload'])) { | |
$img_name = $_FILES["fileToUpload"]["name"]; | |
$img_path = $_FILES["fileToUpload"]["tmp_name"]; | |
$ext = pathinfo($_FILES['fileToUpload']['name'], PATHINFO_EXTENSION); | |
$new_name = str_replace($ext,"converted.".$ext,$img_name); | |
//2020.12.06.CT:CloudConvert API Params: Upload File from PC (local file) | |
$post =[ | |
[ | |
'name' => 'apikey', | |
'contents' => $oMedia->convert_Api_Key | |
], | |
[ | |
'name' => 'inputformat', | |
'contents' => $ext | |
], | |
[ | |
'name' => 'outputformat', | |
'contents' => $ext | |
], | |
[ | |
'name' => 'input', | |
'contents' => 'upload' | |
], | |
[ | |
'name' => 'filename', | |
'contents' => $new_name | |
], | |
[ | |
'name' => 'converteroptions[auto_orient]', | |
'contents' => 'true' | |
], | |
[ | |
'name' => 'converteroptions[resize]', | |
'contents' => '500' | |
], | |
[ | |
'name' => 'wait', | |
'contents' => 'true' | |
], | |
[ | |
'name' => 'download', | |
'contents' => 'false' | |
], | |
[ | |
'name' => 'file', | |
'contents' => fopen($img_path, 'r'), | |
'filename' => $img_name | |
] | |
]; | |
} | |
else if(isset($_POST['image_path'])) { | |
$img_path = $_POST['image_path']; | |
$ext = pathinfo($img_path, PATHINFO_EXTENSION); | |
$img_name = pathinfo($img_path, PATHINFO_FILENAME); | |
$new_name = str_replace($ext,"converted.".$ext,$img_name); | |
//2020.12.06.CT:CloudConvert API Params: Upload File from URL | |
$post =[ | |
[ | |
'name' => 'apikey', | |
'contents' => $oMedia->convert_Api_Key | |
], | |
[ | |
'name' => 'inputformat', | |
'contents' => $ext | |
], | |
[ | |
'name' => 'outputformat', | |
'contents' => $ext | |
], | |
[ | |
'name' => 'input', | |
'contents' => 'download' | |
], | |
[ | |
'name' => 'converteroptions[auto_orient]', | |
'contents' => 'true' | |
], | |
[ | |
'name' => 'converteroptions[resize]', | |
'contents' => '500' | |
], | |
[ | |
'name' => 'wait', | |
'contents' => 'true' | |
], | |
[ | |
'name' => 'download', | |
'contents' => 'false' | |
], | |
[ | |
'name' => 'file', | |
'contents' => $img_path | |
] | |
]; | |
} | |
else { | |
exit(); | |
} | |
if (in_array($ext, $oMedia->aFileTypesAllowed)) { | |
if ($oMedia->bCloudConvertEnabled == 1 && in_array($ext, $oMedia->aFileTypesConverted)) { | |
//2020.12.06.CT:Guzzle: | |
$headers = ['Connection' => 'keep-alive', | |
'cache-control'=> 'no-cache']; | |
$client = new Client([ | |
'base_uri' => $oMedia->s_url_CloudConvertAPI | |
]); | |
$response = $client->request('POST', | |
$oMedia->s_url_CloudConvertAPI, | |
['multipart' => $post, 'headers' => $headers]); | |
$sBody = $response->getBody()->getContents(); | |
$json = json_decode($sBody); | |
// | |
saveFile("http:".$json->output->url, $oMedia->sFilename."-".$json->output->filename, | |
$oMedia->WPAPIservice_url, $oMedia->WPAPIUser, $oMedia->WPAPIPassword, | |
$oMedia->sURLNextPage,$oMedia->sNextPageLinkText); | |
} | |
else { | |
saveFile($img_path, $oMedia->sFilename."-".$img_name, | |
$oMedia->WPAPIservice_url, $oMedia->WPAPIUser, $oMedia->WPAPIPassword, | |
$oMedia->sURLNextPage,$oMedia->sNextPageLinkText); | |
} | |
} | |
else { | |
echo '<pre id="msg">'; | |
echo "<font color='red'>This file type (".$ext.") is not supported. </font>"; | |
echo '</pre>'; | |
} | |
exit(); | |
} | |
add_action('wp_ajax_postWPAPIMedia', 'sa_guzzle_postWPAPIMedia'); | |
add_action( 'wp_ajax_nopriv_postWPAPIMedia', 'sa_guzzle_postWPAPIMedia'); | |
function saveFile($img_path, $img_name, | |
$service_url, $_APIUser, $_APIPassword, | |
$sURLNextPage,$sNextPageLinkText) | |
{ | |
//$curl = curl_init($service_url); | |
$data = file_get_contents($img_path); | |
if($data === false) | |
{ | |
echo '<pre id="msg"><font color="red"> | |
Error - Contact Admin - Failed to Access the File. | |
</font></pre>'; | |
} | |
else | |
{ | |
$headers = ['cache-control'=> 'no-cache', | |
'content-disposition'=> 'attachment; filename='.$img_name]; | |
$client = new Client([ | |
'base_uri' => $service_url | |
]); | |
$response = $client->request('POST', $service_url, | |
['body' => $data | |
, 'headers' => $headers | |
, 'auth' => [ | |
$_APIUser, | |
$_APIPassword | |
]]); | |
$sBody = $response->getBody()->getContents(); | |
$json = json_decode($sBody); | |
//2020.03.31.CT: Clean Up Messages for Production: | |
echo '<pre id="msg">'; | |
if(!empty($json)) { | |
if(!empty($json->data->status) && $json->data->status != 200) | |
echo "<font color='red'>Error - Contact Admin</br>".$json->message."</font>"; | |
else { | |
//2020.03.31.CT:DONE: Pass in Shortcode PARAM - PageToRedirectOnSuccess | |
$sURL = $sURLNextPage; | |
echo "<h2><a href='".$sURL."'>$sNextPageLinkText</a></h2>"; | |
} | |
} | |
else { | |
echo "<font color='red'>Error - Contact Admin</font>"; | |
} | |
echo '</pre>'; | |
} | |
} | |
function sa_guzzle_getWPAPIMedia() { | |
$resultNum = $_GET['resultNum']; | |
if(is_null($resultNum)) { | |
$resultNum = 1; | |
} | |
$client = new Client([ | |
'base_uri' => 'https://*****/wp-json/wp/v2', | |
'timeout' => 2.0, | |
]); | |
$dataArray = array("per_page"=>$resultNum); | |
$data = http_build_query($dataArray); | |
$headers = ['Accept' => 'application/json', | |
'cache-control'=> 'no-cache']; | |
try { | |
$response = $client->request('GET', | |
'https://*****/wp-json/wp/v2/media?'.$data, | |
[ | |
'auth' => [ | |
'*****', | |
'*****' | |
], | |
'headers' => $headers | |
]); | |
$sBody = $response->getBody()->getContents(); | |
$json = json_decode($sBody); | |
} | |
catch ( Exception $e ) { | |
$json = null; | |
} | |
echo '<pre >'; | |
if(!empty($json)) | |
{ | |
foreach($json as $key=>$item) | |
{ | |
if($item->media_type == "image") | |
echo '<img src="'.$item->source_url | |
.'" width="140" height="140" style="padding:10px;"/>'; | |
else if($item->media_type == "file") | |
echo '<audio controls><source src="' | |
.$item->source_url.'" type="' | |
.$item->mime_type.'"></audio>'; | |
if(($key+1)%5 == 0) | |
echo '</br>'; | |
} | |
} | |
else { | |
echo "<font color='red'>Error - Contact Admin</font>"; | |
} | |
echo '</pre>'; | |
exit(); | |
} | |
add_action('wp_ajax_getWPAPIMedia', 'sa_guzzle_getWPAPIMedia'); | |
add_action( 'wp_ajax_nopriv_getWPAPIMedia', 'sa_guzzle_getWPAPIMedia'); | |
//--------------------------- | |
//2020.11.21.CT:END: | |
////////// | |
// END: Guzzle - MediaUpload | |
// WP-API + CloudConvert API | |
////////// |
Sample 2- Kendo ReactJS Grid in WordPress
CODE SAMPLE 2.1 – KendoReact Grid in WordPress
GOAL – Embed KendoReact Grid in WordPress
Our goal was to build a ReactJS component which instantiated a Kendo React Data Grid. The next step was to fill this grid with Vocabulary list data from MS-SQL. The last step was to convert this into a Gutenberg reusable code block.KendoReact Grid Docs:
https://www.telerik.com/kendo-react-ui/components/grid
Chris-Code-Sample-2.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from "react"; | |
const Component = React.Component; | |
import axios from 'axios'; | |
// ES2015 module syntax | |
import { Button } from '@progress/kendo-react-buttons'; | |
import { Grid, GridColumn as Column } from '@progress/kendo-react-grid'; | |
import { orderBy } from '@progress/kendo-data-query'; | |
import { filterBy } from '@progress/kendo-data-query'; | |
import EditForm from './cVocabForm.jsx'; | |
const EditCommandCell = props => { | |
return ( | |
<td> | |
<button | |
className="k-button k-primary" | |
onClick={() => props.enterEdit(props.dataItem)} | |
> | |
Edit | |
</button> | |
</td> | |
); | |
}; | |
//DOCS: | |
/* | |
2020.09.05.CT:TODO - Edit + Update Grid Row in External Pop-Up Form: | |
https://www.telerik.com/kendo-react-ui/components/grid/editing/editing-external-form | |
https://wordpress.stackexchange.com/questions/282163/wordpress-ajax-with-axios/284423 | |
*/ | |
export default class Vocab extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
sort: [ | |
{ field: 'Term', dir: 'asc' } | |
], | |
filter: { | |
logic: "and", | |
filters: [] | |
}, | |
name: '', | |
vocab: '', | |
openForm: false, | |
editItem: {} | |
}; | |
this.GetVocabData = this.GetVocabData.bind(this); | |
} | |
enterEdit = item => { | |
this.setState({ | |
openForm: true, | |
editItem: item | |
}); | |
}; | |
MyEditCommandCell = props => ( | |
<EditCommandCell {...props} enterEdit={this.enterEdit} /> | |
); | |
handleSubmit = (event) => { | |
this.setState({ | |
vocab: this.state.vocab.map(item => { | |
if (event.ID === item.ID) { | |
item = { ...event }; | |
} | |
return item; | |
}), | |
openForm: false | |
}); | |
} | |
handleCancelEdit = () => { | |
this.setState({ openForm: false }) | |
} | |
GetVocabData() { | |
event.preventDefault(); | |
var params = new URLSearchParams(); | |
params.append('action', 'sa_api_getVoiceVocab'); | |
let sURL_Site = '/'; | |
let sURL_Ajax = sURL_Site+"wp-admin/admin-ajax.php"; | |
axios.post(sURL_Ajax, params) | |
.then(res => { | |
console.log(res); | |
this.dataRecieved(res.data); | |
}); | |
//2020.03.23.CT-TODO: res.data --> Fill data into the grid below: | |
} | |
componentDidMount() { | |
var params = new URLSearchParams(); | |
params.append('action', 'sa_api_getVoiceVocab'); | |
let sURL_Site = '/'; | |
let sURL_Ajax = sURL_Site+"wp-admin/admin-ajax.php"; | |
axios.post(sURL_Ajax, params) | |
.then(res => { | |
console.log(res); | |
//this.setState({ vocab: res.data}); | |
this.dataRecieved(res.data); | |
}); | |
} | |
dataRecieved(items) { | |
this.setState({ | |
...this.state, | |
vocab: items | |
}); | |
} | |
render() { | |
return ( | |
<React.Fragment> | |
<form onSubmit={this.GetVocabData}> | |
<Button primary={true} | |
type="submit"> | |
Get Vocab Data | |
</Button> | |
</form> | |
<hr></hr> | |
<Grid | |
style={{ height: '300px' }} | |
data={orderBy(filterBy([...this.state.vocab], this.state.filter), this.state.sort)} | |
sortable={true} | |
sort={this.state.sort} | |
onSortChange={(e) => { | |
this.setState({ | |
sort: e.sort | |
}); | |
}} | |
filterable | |
filter={this.state.filter} | |
onFilterChange={(e) => { | |
this.setState({ | |
filter: e.filter | |
}); | |
}} | |
> | |
<Column field="ID" title="ID" width="70px" sortable={true} filterable={false}/> | |
<Column field="Term" title="Term" width="120px" sortable={true}/> | |
<Column field="LexicalClass" title="LexicalClass" width="140px" sortable={true} filterable={false}/> | |
<Column field="Definition" title="Definition" sortable={false} filterable={false}/> | |
<Column field="Sentence" title="Sentence" sortable={false} filterable={false}/> | |
<Column cell={this.MyEditCommandCell} sortable={false} filterable={false} /> | |
</Grid> | |
{this.state.openForm && | |
<EditForm | |
cancelEdit={this.handleCancelEdit} | |
onSubmit={this.handleSubmit} | |
item={this.state.editItem} | |
/> | |
} | |
</React.Fragment> | |
) | |
} | |
} |