Telerik blogs

This project uses the Telerik UI PHP wrapper to create a reusable WordPress plugin.

The PHP wrapper from Progress Telerik was something I have wanted to use for some time. It is inexpensive if you purchase just that item. And the commercial license allows me to create a resaleable product for WordPress.

The customer who required a solution shall remain nameless, although I should state that he is also a friend. His brief was simple.

He wanted a way of recording people’s visits to his site. Specifically, recording activity for visits to blog and image items. One thing that he wanted to be sure of was the place that the link to follow was clicked and how many visits. Zero through many have been made over time.

He specifically did not want “personalization” of the click-through link, which is akin to using a tracking pixel. I.e., he wants less intrusion into the person who clicks the link. This “personalization” of the tracking software is possible for those wishing to capture statistics based more accurately on those clicking the originating link.

I decided to use the Telerik UI for PHP wrapper because it fit with the LAMP (or in my case WIMP) setup of WordPress. Incidentally LAMP is a technology descriptor and equals Linux Apache MySql PHP. WIMP equals Windows IIS MySql PHP. The commonalities between LAMP and WIMP are MySql and PHP, both of which port to Windows from their more native Linux. Note the Windows server setup for WIMP happens to be a Virtual Machine (VM) on my computer. Hyper-V hosts the VM and I share the folder C:\inetpub\wwwroot from the VM, and I use Visual Studio Code to modify the files within the WordPress folder directly.

I then zip the plugin folder containing the plugin source and Telerik assets for distribution to upload to WordPress. It creates a 30 Mb file. However, I pass all the Telerik assets in case later on they are needed for another specified enhancement. Perhaps I am being lazy in not stripping out most of the Telerik controls that are not being used, but I do not think I am. It means I do not have to do this every time I upgrade Telerik on this plugin. Time is precious.

I believe that the continued prevalence of PHP in the world might be achieved because of the widespread usage of WordPress. WordPress is very widely used because of its excellent configurability and the “free” distribution of this program. The number of third-party developers who work on the WordPress product is immense—thus ensuring its continued popularity.

This does not take away from Progress Sitefinity, as it is more structured and “safer” in its delivery of successful solutions. Sitefinity is also a product that I enjoy working with. However, I need to find customers who would like me to design a product around this platform which is a Windows-based offering. It is a little bit like comparing Apples and Lemons. I would enjoy using the two products for different applications. I am, for instance, more comfortable with .NET than PHP. That is not to say that I am not still learning in both environments. There, it seems, is always room to learn in the world of computing.

There are many articles on the internet where you might find descriptions on how to set up and create WordPress plugins. I shall not mention any here, as it is important that your research is your own.

I discovered several things when making this plugin—not my first, however one of my more “destined to be longer lived” plugins—that they are not hard to create. The WordPress engine has many established “hooks” and features around the way it should be used to operate the plugin.

So let us jump into the code.

JIU project file tree

Here is the “tree” which displays the files that I have created for this project. I did not expand the CSS and JS directories because that would create distractions. I shall explain these later. Also jReadUrl.php is a red herring and is to be deleted in a later version of the program. I was trying out a technique and it was not needed after I created the file, so is now defunct.

You can see that the jMain.php file is at the lowest level in the program, that is because it is the Wordpress entrypoint.

jMain.php

We are looking at the Plugin “entry point” into WordPress. It is called “jMain.php”.

Note the comment header. This is necessary for the plugin to be recognized by the WordPress engine as such. The comment is part of the program.

My understanding of WordPress (which is not complete due to the size and complexity of all that is WordPress) is that it “fires” this routine whenever a page is loaded, along with the same routine for each of the plugins in the program. Therefore, it is a lightweight routine and completes quickly.

I believe that the comment header at the top of the routine establishes the plugin to be a plugin, and the WordPress program registers it in the list of plugins to check as pages are loaded.

This routine has one of the main functionalities of the coding “experiment” I had to make as a proof of concept. It needed to record the “hits” of the incoming URL from places such as a MailChimp newsletter link through. Once I had established that this was going to work, I found that this routine was firing twice. So, I needed to build in a check so that if a “hit” was recorded within 60s of the current hit, I would ignore the attempt.

It seems to hold together so I am pleased to say, “It works!” … David said. So, the main premise of the entire attempt is solved, and we have a solution. I did not know, for sure, if it would work when I embarked on this journey—however, am pleased to say it has worked so the solution is sound, as well as found.

Note the require_once line which relates to jFunctions.php … this is loaded and is described in the next segment.

You shall notice that there is no sign of Telerik … That is coming. 🙂

<?php
/*
Plugin Name: JADER Incoming Url
Plugin Uri: https:///www.dar-jader.com
Description: This plugin interrogates incoming Url's to perform actions within the wordpress program.
Author: David A. Robertson
*/
// This part of the program is executed when Wordpress loads
// It is actually loaded twice - Since we do not want the program to record two "hits" we prevent
// The program recording a hit if one has occurred with same query string in last 60s.
require( $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php' );
// Here, we fix up the functions that the plugin uses.
require_once(plugin_dir_path(__FILE__) . 'Includes/jFunctions.php');
// Here, we look for the jiu_QueryId querystring parameter. We only continue with recording the "hit" if 
// the parameter exists and the contents are numeric.
if (isset( $_GET['jiu_QueryId'] ) && is_numeric($_GET['jiu_QueryId']))
{
    $nJADERIncomingUrlId = intval( $_GET['jiu_QueryId'] );
    if (get_post( $nJADERIncomingUrlId, 'OBJECT', null ) != null)
    {
        // The following chunk of code is designed to make sure that a "hit" count is not performed if 
        // one has been made in last 60s.
        $dDatetime1 = date("Y-m-d H:i:s");
        $dTimestamp = strtotime($dDatetime1);
        $dTime1 = $dTimestamp + (60 * 60);
        $dTime2 = $dTime1 - 60;
        $dDatetime2 = date("Y-m-d H:i:s", $dTime2);
        $aDateQuery = array('after' => $dDatetime2);
        $aCommentArgs = array( 'post_id' => $nJADERIncomingUrlId, 'date_query' => $aDateQuery );
        $oGetComments = get_comments( $aCommentArgs );
        // We only continue, here, if no other comment exists that has been posted in the last 60s.
        if ($oGetComments == null || $oGetComments == 0)
        {
            // Here, we create a comment on the post which is the "hit" data. The comment datetime is the "hit" date
            // We store the incoming Url for statistical purposes. That is the only data we need.
            // The Url is disseminated later for statistical analysis and export to Excel and pdf as reporting.
            $sProtocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";  
            $sCurrent_Url = $sProtocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 
            $aCommentArgs = array( 'comment_post_ID' => $nJADERIncomingUrlId, 'comment_content' => $sCurrent_Url, 'comment_date' => $dDatetime1 );
            wp_insert_comment( $aCommentArgs );
        }
    }
}
?>

Includes/jFunctions.php

This program creates the “hooks” into WordPress so that the engine triggers parts of my code when appropriate to do so.

  1. The first item on the list is to create the “jiu_URLSettings” category if it does not exist. The program requires that the category exists when adding “hits” and categorizing the configuration information which is stored as WordPress posts. The function wp_create_category is described here.

  2. The second item on the list is to include the jEnqueue.php in the execution of this file. The file is included below, and an explanation follows.

  3. We next “fix up” the menu in the WordPress dashboard. The menu option is called “JADER Incoming Url.” The routine used to do this (add_menu_page) is part of the WordPress program and is documented here. The WordPress routine add_action is used to add the action to the “use” queue so WordPress knows what to do when their program draws the menu. See this routine here.

  4. The functions jSaveUrlECB and jSaveUrlICB are a pair of routines that save information as a post. They are called from jMainPage.php (below). AJAX is used to call these functions, which is why the add action for wp_ajax wp_ajax_nopriv_ are set below so when the WordPress AJAX call is made they are in the list of objects that may be called.

  5. The following functions are also to do with AJAX calls made in the jMainPage.php file.

  6. Note: This technique is down to the way WordPress works and functions quite well. The reason we use this technique is so that if WordPress or PHP is ever updated, then the technique continues to work whether the AJAX call methodology is updated or not. A kind of future proofing.

<?php
/*
 * Add my new menu to the Admin Control Panel
 * Add actions for the code in jMainPage.php
 */
if (get_cat_ID("jiu_URLSettings") == 0)
{
    wp_create_category("jiu_URLSettings");
}
require_once plugin_dir_path(__FILE__) . 'jEnqueue.php';
function j_Add_My_Admin_Link()
{
    add_menu_page(
        'JADER Incoming Url', // Title of the page
        'JADER Incoming Url', // Text to show on the menu link
        'manage_options', // Capability requirement to see the link
        plugin_dir_path(__FILE__) . 'jMainPage.php' // The 'slug' - file to display when clicking the link
    );
}
add_action( 'admin_menu', 'j_Add_My_Admin_Link' );
function jSaveUrlECB()
{
    if (isset( $_POST['ID'] ))
    {
        if (is_numeric($_POST['ID']))
        {
            $nId = intval( $_POST['ID'] );
            $oPost = get_post( $nId );
            if ($oPost != null)
            {
                $oPostArray = array('ID' => $nId, 'post_name' => $_POST['post_name'], 'post_title' => $_POST['post_title'], 'post_content' => $_POST['post_content']);
                wp_update_post( $oPostArray );
            }
        }
    }
    wp_die();
}
add_action('wp_ajax_jSaveUrlECB', 'jSaveUrlECB' );
add_action('wp_ajax_nopriv_jSaveUrlECB', 'jSaveUrlECB' );
function jSaveUrlICB()
{
    if (isset($_POST['post_name']))
    {
        $nCategoryId = get_cat_ID("jiu_URLSettings");
        $oPostArray = array( 'post_name' => $_POST['post_name'], 'post_title' => $_POST['post_title'], 'post_content' => $_POST['post_content'], 'post_category' => [ $nCategoryId ], 'post_status' => 'private' );
        $oWPError = wp_insert_post($oPostArray, true, false);
    }
    wp_die();
}
add_action( 'wp_ajax_jSaveUrlICB', 'jSaveUrlICB' );
add_action( 'wp_ajax_nopriv_jSaveUrlICB', 'jSaveUrlICB' );
function jReadUrlCB()
{
    $nCategoryId = get_cat_ID('jiu_URLSettings');
    $oArgs = array( 'numberposts' => -1, 'post_category' => [ $nCategoryId ], 'post_status' => 'private' );
    $oPosts = get_posts( $oArgs );
    header('Content-Type: application/json');
    echo wp_json_encode( $oPosts );
    wp_die();
}
add_action( 'wp_ajax_jReadUrlCB', 'jReadUrlCB' );
add_action( 'wp_ajax_nopriv_jReadUrlCB', 'jReadUrlCB' );
function jReadUrl2CB()
{
    $nCategoryId = get_cat_ID('jiu_URLSettings');
    $oArgs = array( 'numberposts' => -1, 'post_category' => [ $nCategoryId ], 'post_status' => 'private' );
    $oPosts = get_posts( $oArgs );
    header('Content-Type: application/json');
    echo wp_json_encode( $oPosts );
    wp_die();
}
add_action( 'wp_ajax_jReadUrl2CB', 'jReadUrl2CB' );
add_action( 'wp_ajax_nopriv_jReadUrl2CB', 'jReadUrl2CB' );
function jReadStatisticsCB()
{
    $sRequest_body = file_get_contents('php://input');
    $oOutput = explode("=", $sRequest_body);
    $nId = intval($oOutput[1]);
    $oPost = get_post( $nId );
    if ($oPost != null)
    {
        $oArgs = array('post_id' => $nId);
        $oComments = get_comments($oArgs);
        $oComments2 = array();
        $oFieldOutputs = explode(",", $oPost->post_content);
        foreach ( $oComments as $oComment ) {
            $sUrl_components = parse_url($oComment->comment_content);
            parse_str($sUrl_components['query'], $aParams);
            $oComments2[] = array('ID' => $nId, 'post_name' => $oPost->post_name, 'comment_date' => $oComment->comment_date, 'comment_content' => $oComment->comment_content, "jiu_A" => $aParams["jiu_A"], "jiu_B" => $aParams["jiu_B"], "jiu_C" => $aParams["jiu_C"], "jiu_D" => $aParams["jiu_D"], "jiu_E" => $aParams["jiu_E"], "jiu_F" => $aParams["jiu_F"], "jiu_G" => $aParams["jiu_G"], "jiu_H" => $aParams["jiu_H"]);
        }
        header('Content-Type: application/json');
        echo wp_json_encode( $oComments2 );
    }
    wp_die();
}
add_action( 'wp_ajax_jReadStatisticsCB', 'jReadStatisticsCB' );
add_action( 'wp_ajax_nopriv_jReadStatisticsCB', 'jReadStatisticsCB' );
?>

Include/jEnqueue.php

This include file adds files that you would normally put into the head tag of a website.

You shall notice JS and CSS files which contain parts of the program that affect program flow and display. Specifically, note the kendo files—they are used to display the grids by the PHP wrapper used in the jMainPage.php program.

<?php
    function jiu_scripts()
    {
    //css
        wp_enqueue_style( 'jiucss' ,  plugins_url('assets/css/jiu.css', __FILE__ ));
        wp_enqueue_style( 'jiukendocommon' , plugins_url('assets/css/kendo/kendo.common.min.css', __FILE__ ));
        wp_enqueue_style( 'jiukendoblueopal' , plugins_url('assets/css/kendo/kendo.' . ($_COOKIE['WP_Kendo_Theme'] != '' ? $_COOKIE['WP_Kendo_Theme'] : 'blueopal') . '.min.css', __FILE__ ));
        wp_enqueue_style( 'jiukendomobile' , plugins_url('assets/css/kendo/kendo.mobile.all.min.css', __FILE__ ));
    //JQuery
        wp_enqueue_script( 'jquery' );
        wp_enqueue_script( 'jiujs' , plugins_url('assets/js/jiu.js', __FILE__ ));
        wp_enqueue_script( 'jiuall' , plugins_url('assets/js/kendo/kendo.all.min.js', __FILE__ ));
        wp_enqueue_script( 'jiutimezone' , plugins_url('assets/js/kendo/kendo.timezones.min.js', __FILE__ ));
        wp_enqueue_script( 'jiuculture' , plugins_url('assets/js/kendo/cultures/kendo.culture.en-GB.min.js', __FILE__ ));
        wp_enqueue_script( 'jiumessages' , plugins_url('assets/js/kendo/messages/kendo.messages.en-GB.min.js', __FILE__ ));
        wp_enqueue_script( 'jiumobile' , plugins_url('assets/js/kendo/kendo.mobile.min.js', __FILE__ ));
        wp_enqueue_script( 'jiuweb' , plugins_url('assets/js/kendo/kendo.web.min.js', __FILE__ ));
        wp_enqueue_script( 'jszip' , plugins_url('assets/js/kendo/jszip.min.js', __FILE__ ));
        
    }
    add_action( 'admin_enqueue_scripts', 'jiu_scripts', 1000 );
?>

Include/jMainPage.php

  1. This file maintains the tab group for the WordPress Dashboard menu item. Please see the images showing the different tabs below the code snippet.

  2. The two require_once lines at the top of the file are loading two PHP files which relate to the kendo wrapper. These two files must be present for the PHP expansions to occur.

  3. Some HTML sets up output to the Dashboard menu item in the right pane of the WordPress site.

  4. One thing I noticed with using the Telerik Kendo UI Wrapper was that the setting up of the grid tends to be a bit harder than Razor syntax in a cshtml. This might simply be that it is not yet a natural extension of my knowledge or programming abilities. It is making me think, though, which is why I built this program.

  5. A lot of the methods are self-explanatory. Or if you delve into the Telerik Demo Docs you shall achieve understanding in concordance.

<?php
    require_once plugin_dir_path(__FILE__) . 'assets/lib/Kendo/Autoload.php';
    require_once plugin_dir_path(__FILE__) . 'assets/lib/DataSourceResult.php';
    $sProtocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";  
    $sCurrent_Url = $sProtocol . $_SERVER['HTTP_HOST']; 
?>
<div class="wrap">
    <h1>JADER Incoming Url Plugin Admin</h1>
    <?php
        $oTabStrip = new \Kendo\UI\TabStrip('AdminTabStrip');
        $oGeneralSettings = new \Kendo\UI\TabStripItem();
        $oGeneralSettings->text("General Settings");
        $oGeneralSettings->startContent();
    ?>
        <p>
            Skin:&nbsp;
            <?php
                $oSkinDropDownList = new \Kendo\UI\DropDownList('SkinDropDownList');
                $oSkinDropDownList->value(($_COOKIE['WP_Kendo_Theme'] != '' ? $_COOKIE['WP_Kendo_Theme'] : '-1'))
                    ->change('SkinDropDownListChange')
                    ->dataTextField('text')
                    ->dataValueField('value')
                    ->dataSource(array(
                        array('text' => 'Please select a skin...', 'value' => '-1'),
                        array('text' => 'Black', 'value' => 'black'),
                        array('text' => 'Blue Opal', 'value' => 'blueopal'),
                        array('text' => 'Bootstrap', 'value' => 'bootstrap'),
                        array('text' => 'Default', 'value' => 'default'),
                        array('text' => 'Fiori', 'value' => 'Fiori'),
                        array('text' => 'Flat', 'value' => 'Flat'),
                        array('text' => 'High Contrast', 'value' => 'highcontrast'),
                        array('text' => 'Material', 'value' => 'material'),
                        array('text' => 'moonlight', 'value' => 'moonlight'),
                        array('text' => 'Nova', 'value' => 'nova'),
                        array('text' => 'Office 365', 'value' => 'office365'),
                        array('text' => 'Silver', 'value' => 'silver'),
                        array('text' => 'Uniform', 'value' => 'uniform')
                    ))
                    ->attr('style', 'width: 36%');
                echo $oSkinDropDownList->render();
            ?>
        </p>
    <?php
        $oGeneralSettings->endContent();
        $oUrlSettings = new \Kendo\UI\TabStripItem();
        $oUrlSettings->text("Url Settings")->startContent();
        $oPageable = new Kendo\UI\GridPageable();
        $oPageable->refresh(true)
                  ->pageSizes(true)
                  ->buttonCount(5);
        $oGridUrlSettings = new \Kendo\UI\Grid('UrlSettingsGrid');
        $oTransport = new \Kendo\Data\DataSourceTransport();
        $oRead = new \Kendo\Data\DataSourceTransportRead();
        $oRead->url(admin_url('admin-ajax.php?action=jReadUrlCB'))
             ->contentType('application/json')
             ->dataType("json")
             ->type("POST");
        $oTransport->read($oRead)->parameterMap('function(data) {
            return kendo.stringify(data);
        }');
        $oModel = new \Kendo\Data\DataSourceSchemaModel();
        $oIDField = new \Kendo\Data\DataSourceSchemaModelField('ID');
        $oIDField->type('number');
        $oNameField = new \Kendo\Data\DataSourceSchemaModelField('post_name');
        $oNameField->type('string');
        $oTitleField = new \Kendo\Data\DataSourceSchemaModelField('post_title');
        $oTitleField->type('string');
        $oContentField = new \Kendo\Data\DataSourceSchemaModelField('post_content');
        $oContentField->type('string');
        $oModel->addField($oIDField)
               ->addField($oNameField)
               ->addField($oTitleField)
               ->addField($oContentField);
        $oSchema = new \Kendo\Data\DataSourceSchema();
        $oSchema->model($oModel);
        $oDataSource = new \Kendo\Data\DataSource();
        $oDataSource->transport($oTransport)
                   ->data('data')
                   ->pageSize(10)
                   ->schema($oSchema)
                   ->serverFiltering(false)
                   ->serverSorting(false)
                   ->serverGrouping(false)
                   ->serverPaging(false);
        $oViewId = new \Kendo\UI\GridColumn();
        $oViewId->template("#= ID #")->title('ID')->width(85);
        $oPostNameColumn = new \Kendo\UI\GridColumn();
        $oPostNameColumn->field("post_name")->title('Url Name')->width(300);
        $oPostTitleColumn = new \Kendo\UI\GridColumn();
        $oPostTitleColumn->field("post_title")->title('Url Title')->width(300);
        $oPostUrlColumn = new \Kendo\UI\GridColumn();
        $oPostUrlColumn->field("post_content")->title('Url Parameter Information')->width(300);
        $oUrlSettingsCommand = new \Kendo\UI\GridColumn();
        $oUrlSettingsCommand->addCommandItem("edit")
                ->title("&nbsp;")
                ->width(80);
        $oColumnMenu = new \Kendo\UI\GridColumnMenu();
        $oColumnMenu->filterable(true);
        $oGridUrlSettings->addColumn($oUrlSettingsCommand, $oViewId, $oPostNameColumn, $oPostTitleColumn, $oPostUrlColumn)->dataSource($oDataSource)->columnMenu($oColumnMenu)->height(400)->editable('popup')->pageable($oPageable)->sortable(true)->navigatable(true)->resizable(true)->reorderable(true)->groupable(false)->filterable(true)->addToolbarItem(new \Kendo\UI\GridToolbarItem('excel'), new \Kendo\UI\GridToolbarItem('pdf'), new \Kendo\UI\GridToolbarItem('create'), new \Kendo\UI\GridToolbarItem('search'))->cancel('UrlSettingsGridCancel')->save('UrlSettingsGridSave');
        echo $oGridUrlSettings->render();
    ?>
    <?php
        $oTransport3 = new \Kendo\Data\DataSourceTransport();
        $oRead3 = new \Kendo\Data\DataSourceTransportRead();
        $oRead3->url(admin_url('admin-ajax.php?action=jReadUrl2CB'))
             ->contentType('application/json')
             ->dataType("json")
             ->type("POST");
        $oTransport3->read($oRead3)->parameterMap('function(data) {
            return kendo.stringify(data);
        }');
        $oModel3 = new \Kendo\Data\DataSourceSchemaModel();
        $oIDField3 = new \Kendo\Data\DataSourceSchemaModelField('ID');
        $oIDField3->type('number');
        $oNameField3 = new \Kendo\Data\DataSourceSchemaModelField('post_name');
        $oNameField3->type('string');
        $oModel3->addField($oIDField3)
               ->addField($oNameField3);
        $oSchema3 = new \Kendo\Data\DataSourceSchema();
        $oSchema3->model($oModel3);
        $oDataSource3 = new \Kendo\Data\DataSource();
        $oDataSource3->transport($oTransport3)
                   ->data('data')
                   ->schema($oSchema3);
        $oUrlSettings->endContent();
        $oStatistics = new \Kendo\UI\TabStripItem();
        $oStatistics->text("Statstics")->startContent();
        $oUrlSetting = new \Kendo\UI\DropDownList('UrlSettingDropDownList');
        $oUrlSetting
              ->dataTextField('post_name')
              ->dataValueField('ID')
              ->dataSource($oDataSource3)
              ->attr('style', 'width: 90%');
        echo $oUrlSetting->render();
        $oUrlSettingPopulate = new \Kendo\UI\Button('UrlSettingButton');
        $oUrlSettingPopulate->content("Populate");
        $oUrlSettingPopulate->click(new \Kendo\JavaScriptFunction('function() { nId = jQuery("#UrlSettingDropDownList").data("kendoDropDownList").value(); jQuery("#StatisticsGrid").data("kendoGrid").dataSource.read(); }'));
        echo $oUrlSettingPopulate->render();
        $oGridStatistics = new \Kendo\UI\Grid('StatisticsGrid');
        $oTransport2 = new \Kendo\Data\DataSourceTransport();
        $oRead2 = new \Kendo\Data\DataSourceTransportRead();
        $oRead2->url(admin_url('admin-ajax.php?action=jReadStatisticsCB'))
             ->contentType('application/json')
             ->dataType("json")
             ->type("POST");
        $oRead2->data(new \Kendo\JavaScriptFunction('function() { return { nId: nId }; }'));
        $oTransport2->read($oRead2);
        $oModel2 = new \Kendo\Data\DataSourceSchemaModel();
        $oIDField2 = new \Kendo\Data\DataSourceSchemaModelField('ID');
        $oIDField2->type('number');
        $oNameField2 = new \Kendo\Data\DataSourceSchemaModelField('post_name');
        $oNameField2->type('string');
        $oDate2 = new \Kendo\Data\DataSourceSchemaModelField('comment_date');
        $oDate2->type('date');
        $oContent2 = new \Kendo\Data\DataSourceSchemaModelField('comment_content');
        $oContent2->type('string');
        $oA2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_A');
        $oA2->type('string');
        $oB2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_B');
        $oB2->type('string');
        $oC2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_C');
        $oC2->type('string');
        $oD2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_D');
        $oD2->type('string');
        $oE2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_E');
        $oE2->type('string');
        $oF2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_F');
        $oF2->type('string');
        $oG2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_G');
        $oG2->type('string');
        $oH2 = new \Kendo\Data\DataSourceSchemaModelField('jiu_H');
        $oH2->type('string');
        $oModel2->addField($oIDField2)
               ->addField($oNameField2)
               ->addField($oDate2)
               ->addField($oContent2)
               ->addField($oA2)
               ->addField($oB2)
               ->addField($oB2)
               ->addField($oC2)
               ->addField($oD2)
               ->addField($oE2)
               ->addField($oF2)
               ->addField($oG2)
               ->addField($oH2);
        $oSchema2 = new \Kendo\Data\DataSourceSchema();
        $oSchema2->model($oModel2);
        $oDataSource2 = new \Kendo\Data\DataSource();
        $oDataSource2->transport($oTransport2)
                   ->data('data')
                   ->pageSize(10)
                   ->schema($oSchema2)
                   ->serverFiltering(false)
                   ->serverSorting(false)
                   ->serverGrouping(false)
                   ->serverPaging(false);
        $oViewId2 = new \Kendo\UI\GridColumn();
        $oViewId2->template("#= ID #")->title('ID')->width(85);
        $oPostNameColumn2 = new \Kendo\UI\GridColumn();
        $oPostNameColumn2->field('post_name')->title('Name')->width(300);
        $oCommentDate2 = new \Kendo\UI\GridColumn();
        $oCommentDate2->field('comment_date')->title('Date')->width(200);
        $oCommentContent2 = new \Kendo\UI\GridColumn();
        $oCommentContent2->field('comment_content')->title('Content')->width(400);
        $oA = new \Kendo\UI\GridColumn();
        $oA->field('jiu_A')->title('A')->width(200);
        $oB = new \Kendo\UI\GridColumn();
        $oB->field('jiu_B')->title('B')->width(200);
        $oC = new \Kendo\UI\GridColumn();
        $oC->field('jiu_C')->title('C')->width(200);
        $oD = new \Kendo\UI\GridColumn();
        $oD->field('jiu_D')->title('D')->width(200);
        $oE = new \Kendo\UI\GridColumn();
        $oE->field('jiu_E')->title('E')->width(200);
        $oF = new \Kendo\UI\GridColumn();
        $oF->field('jiu_F')->title('F')->width(200);
        $oG = new \Kendo\UI\GridColumn();
        $oG->field('jiu_G')->title('G')->width(200);
        $oH = new \Kendo\UI\GridColumn();
        $oH->field('jiu_H')->title('H')->width(200);
        $oColumnMenu2 = new \Kendo\UI\GridColumnMenu();
        $oColumnMenu2->filterable(true);
        $oGridStatistics->addColumn($oViewId2, $oPostNameColumn2, $oCommentDate2, $oCommentContent2, $oA, $oB, $oC, $oD, $oE, $oF, $oG, $oH)->autoBind(false)->dataSource($oDataSource2)->columnMenu($oColumnMenu2)->height(400)->pageable($oPageable)->sortable(true)->navigatable(true)->resizable(true)->reorderable(true)->groupable(true)->filterable(true)->addToolbarItem(new \Kendo\UI\GridToolbarItem('excel'), new \Kendo\UI\GridToolbarItem('pdf'), new \Kendo\UI\GridToolbarItem('search'));
        echo $oGridStatistics->render();
    ?>
    <?php
        $oStatistics->endContent();
        $oHelp = new \Kendo\UI\TabStripItem();
        $oHelp->text("Help")->startContent();
    ?>
    <p>The product you are evaluating starts in demonstration mode. You have 30 days from date of install to evaluate the product.</p>
    <p>Part of the running of the software checks for license status.</p>
    <p>The software shall not run completely if you have not paid for usage after the 30 days.</p>
    <p>It is the statistical area of the software that shall not function correctly. The program continues to collect hits otherwise.</p>
    <p>We appreciate you using the software, and thank you for your patronage.</p>
    <p>Kind Regards</p>
    <p><a href="mailto:david@dar-jader.com">David Robertson</a></p>
    <p>Please email me your feedback</p>
    <?php
        $oHelp->endContent();
        $oTabStrip->addItem($oGeneralSettings, $oUrlSettings, $oStatistics, $oHelp);
        // set animation
        $oAnimation = new \Kendo\UI\TabStripAnimation();
        $oOpenAnimation = new \Kendo\UI\TabStripAnimationOpen();
        $oOpenAnimation->effects("fadeIn");
        $oAnimation->open($oOpenAnimation);
        $oTabStrip->animation($oAnimation);
    
        echo $oTabStrip->render();
    ?>    
</div>
<script type="text/javascript">
    var nId;
    function SkinDropDownListChange(e)
    {
        if (e.sender.value() == "-1")
        {
            jiu_SetCookie("WP_Kendo_Theme", "blueopal");
        }
        else
        {
            jiu_SetCookie("WP_Kendo_Theme", e.sender.value());
        }
        jiu_ReloadPage();
    }
    
    jQuery(document).ajaxStart(function () {
        setTimeout(function () {
//            kendo.ui.progress(jQuery(document.body), true);
        }, 0);
    });
    jQuery(document).ajaxStop(function () {
        setTimeout(function () {
//            kendo.ui.progress(jQuery(document.body), false);
        }, 0);
    });
    jQuery(document).ajaxError(function(event,xhr,options,exc)
    {
//        alert("An unspecified ajax error has occurred. Status: " + xhr.status + ", Message: " + xhr.statusText);
    });
    jQuery(document).ready(function () {
        var sSkin = jiu_GetCookie("WP_Kendo_Theme");
        if (sSkin == null) {
            jiu_SetCookie("WP_Kendo_Theme", "blueopal");
            jiu_ReloadPage();
        }
    });
    function UrlSettingsGridCancel(e)
    {
        jQuery("#UrlSettingsGrid").data("kendoGrid").dataSource.read();
    }
    function UrlSettingsGridSave(e)
    {
        var oData = {};
        oData.ID = e.model.ID;
        oData.post_name = e.model.post_name;
        oData.post_title = e.model.post_title;
        oData.post_content = e.model.post_content;
        if (e.model.ID != 0)
        {
            oData.action = "jSaveUrlECB"; // Edit
        }
        else
        {
            oData.action = "jSaveUrlICB"; // Insert
        }
        jQuery.ajax({
            url : ajaxurl,
            data : oData,
            method : 'POST',
            success: function(oData) {
                jQuery("#UrlSettingsGrid").data("kendoGrid").dataSource.read();
            } 
        });
    }
</script>

Plugin General Settings set skin to high contrast

Plugin URL Settings has a list of url names, titles, and parameter info

Plugin statistics shows date, content, A, B

Plugin help shows the message about demo mode

Conclusion

I would like to thank you for taking time to absorb this blog. I am very willing to take comments on this item and shall answer all questions as soon as I can.

I think I deserve a chocolate biscuit. 🙂 I have eaten them in advance of finishing. 🙂

Wishing you well.
David


About the Author

David Robertson

David Robertson is a software developer/computer scientist, with degrees and many years’ experience working and writing in the field. He also enjoys ethos and philosophical discussions about computing—where he sees it going and the journey so far. He believes in balance within the tech industry and supports it at every juncture, supporting and advocating for good mental health as a way forward for reasonable and reasoned programming.

 

Related Posts

Comments

Comments are disabled in preview mode.