ClassicPress Plugin Development: Semantic Versioning 2.0

ClassicPress PluginsThis post is part of the ClassicPress Plugin Development series in which I am going to look at both best practice for developing plugins and how I approach some requirements as well as some of the functions I commonly use.

When developing plugins for ClassicPress, you need to develop using semantic versioning (or semver) as this will be a requirement of the ClassicPress Directory.

Semantic Versioning is a versioning scheme for using meaningful version numbers which can be used to avoid incompatibilities.

semver version numbering works using three segments:

  1. MAJOR version when you make breaking changes where users will need to take some action during an upgrade.
  2. MINOR version when you add functionality in a backwards compatible manner which users can simply apply without taking any other action.
  3. PATCH version when you make backwards compatible bug fixes which, like MINOR versions users can apply without taking any other action.

One key point to note when using semver, is that when a versioned release has been made, nothing in that release can be changed; a new version with incremented number must be released instead.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format; pre-release versions often have a MAJOR version number of 0. When a plugin is ready for release to production it should be released as version 1.0.0.

Continue reading “ClassicPress Plugin Development: Semantic Versioning 2.0”

VBA Snippets: Execute URL or Application

MicrosoftThis post is part of the series on VBA Snippets.

it is possible to execute a URL or application in VBA using the Windows Shell Execute API function. In this snippet I am executing a URL, but this could be an application.

Before you can call ShellExecute in code you need to add the following line to the declarations at the top of the mdoule:

Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

The following VBA command will execute the supplied URL (or application); the highlighted variable contains the URL:

iResult = ShellExecute(0, "open", sReportURL, vbNullString, vbNullString, vbNormalFocus)

You can then use the iResult for error handling.

VBA Snippets: Sleep

MicrosoftThis post is part of the series on VBA Snippets.

The following VBA snippets can be used to set a pause (sleep) in the code.

The first one needs to be in the declarations at the top of the module:

Private Declare Sub Sleep Lib "kernel32" (ByVal milliseconds As Long)

The second is used where you want the code to pause (my recent use was to pause for five seconds after an error and before retrying the action):

Sleep (1000 * 5)

The highlighted section is the time; 1000 milliseconds multiplied by 5 to give me 5 seconds.

VBA Snippets: Series Index

MicrosoftWe’ve recently taken on a new client for support of Microsoft Dynamics GP who has a number of modified forms and reports which have been extended with VBA code to add additional functionality. I have done a reasonable amount of VBA and VB6 in the past, but that was sometime ago and I’ve found myself searching online for examples on how to do some things when they’ve asked for further modifications, so I’ve decided that I’ll post snippets of VBA code here so I can easily find how to do things.

I’ve already been doing similar posts with Network Shell Snippets and PowerShell Snippets. The series index will automatically update as posts go live, but if you’re reading a syndicated version, you’ll need to check back to the original post

VBA Snippets
Sleep
Execute URL or Application
Adding an SQL ODBC Connection in Microsoft Dynamics GP
Select Records from Microsoft Dynamics ODBC Connection
Open a File for Appending

ClassicPress Plugin Development: Coding Paradigms

ClassicPress PluginsThis post is part of the ClassicPress Plugin Development series.

There are two code paradigms that can be used when developing for ClassicPress:

  • Procedural Programming
  • Object Oriented Programming

There has been many a flame war fought over which is better and which is worse, but in all honesty I believe that both can have their place and there is no one true way of coding.

I was going to cover the differences between the two types, but when I was researching for good definitions I came across an article by Tom McFarlin linking to articles he and Stephen Harris wrote on the coding paradigms which can be used with WordPress, and therefore ClassicPress, plugins.

I am not that old, but my programming career (short as it was and now a few years in the past after I moved into being a consultant) tended to be with older languages such as Pick Databasic and Visual Basic; I also self-taught myself Lua and later PHP.

In all of the programming/scripting languages I have worked with professionally, to any great extent, the code has been procedural; I’m not saying all of these languages are only procedural, just that virtually all of the code I worked with was written as procedural. As such I am far happier in a procedural world and so I have used this for writing my plugins. At least where I can; plugins with widgets are the exception as ClassicPress require the widget elements to be object orientated.

How you choose to develop for ClassicPress is exactly that; your choice. The examples I will be posted will, except when talking about widgets, be procedural code.

Click to show/hide the ClassicPress Plugin Development Series Index

ClassicPress Plugin Development: Series Index

ClassicPress PluginsI started developing plugins for WordPress almost eight years ago and moved to ClassicPress just over two years ago; I now have 47 plugins available for ClassicPress.

I am creating this series to show how certain aspects of development for ClassicPress plugins are done, partly to benefit others who take up developing for ClassicPress but also as a quick reference for myself including detailed examples.

This series index will automatically update to show new posts as they are published:

ClassicPress Plugin Development
Coding Paradigms
Semantic Versioning 2.0
Coding Standards
To Use Namepsaces or Not
Using Namespaces
Structure of a Plugin
Format of a Plugin readme.txt
Format of a Plugin Header
Add an index.php to Every Folder
Develop for Translation
Develop for Accessibility
Create a Plugin Update Server Using Code Potent's Update Manager
Integrating Code Potent's Update Manager into a Plugin
Create a Plugin Update Endpoint Using Code Potent's Update Manager
Create a Custom Image Path and URL for Code Potent's Update Manager
Loading Front-End Stylesheets
Loading Back-End Stylesheets
Loading Front-End Scripts
Loading Back-End Scripts
Best Practice for Loading Styles and Scripts
Create a Plugin Action Link
Add Plugin Options Page to Settings Main Menu
Add Plugin Options Page to Security Main Menu
Create Custom Top Level Menu
Create Submenu on Custom Top Level Menu
Create Custom Post Type
Create Submenu for a Custom Post Type
Rename First Sublevel Menu of a Custom Top Level Menu
Load and Save Options
Load Options with Defaults
Load Multilevel Options with Defaults
Get Options Page Title
Create an Options Page
Save an Options Page

Simple Audit for Microsoft Dynamics GP: Conclusion

Microsoft Dynamics GPThis post is part of a series on creating a simple audit for Microsoft Dynamics GP.

As I’ve shown over the last few posts, it is relatively easy to create a simple audit on a table in Dynamics GP, but this approach is not very scalable and requires someone to manually create the SQL triggers. For a client on a shoestring budget and who wanted to audit one table, this approach sufficed.

However, for a larger client who would want to audit more tables in more than one company and who would want to have non-technical users maintaining the audited information, I would recommend a solution like Assure Suite from Fastpath which I have implemented with a umber of clients previously.

What brought this approach to mind was a client had an issue with some incorrect data and we could not determine who or what was changing some data, so I amended this custom audit for the tables we needed to record for that issue. In this instance, there was no need for a full audit solution as it will only be in place temporarily while investigating a specific issue.

Simple Audit for Microsoft Dynamics GP: SQL View for Reporting

Microsoft Dynamics GPThis post is part of a series on creating a simple audit for Microsoft Dynamics GP.

Once the audit table and triggers have been deployed, any changes made through the audit will be recorded and available for reviewing later to see who has been making changes and, more significantly, what was changed.

The easiest way of making this available to the client was to create a SmartList for them using SmartList Designer to select data from the new custom audit table. SmartList Designer can see either Dexterity tables or SQL views, but not custom SQL tables, I created a SQL view on the custom audit table, joining it to the Users Master (SY01400) table in the DYNAMICS table to get the username:

/*
Created by Ian Grieve of azurecurve | Ramblings of an IT Professional (http://www.azurecurve.co.uk) This code is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0 Int). */
CREATE VIEW uv_AZRCRV_Audit AS SELECT ['Audit'].RecordType AS 'Record Type' ,['Audit'].RecordID AS 'Record ID' ,['Audit'].UpdateType AS 'Update Type' ,['Audit'].USERID AS 'User ID' ,['User Master'].Username AS 'Username' ,FORMAT(['Audit'].ChangeDateTime, 'yyyy-MM-dd') AS 'Change Date' ,FORMAT(['Audit'].ChangeDateTime, 'HH:ss') AS 'Change Time' ,['Audit'].OldData AS 'Old Data' ,['Audit'].NewData AS 'New Data' FROM ut_AZRCRV_Audit ['Audit'] LEFT JOIN DYNAMICS..SY01400 AS ['User Master'] ON ['User Master'].USERID = ['Audit'].USERID GO GRANT SELECT ON uv_AZRCRV_Audit TO DYNGRP GO

Simple Audit for Microsoft Dynamics GP: Create Triggers

Microsoft Dynamics GPThis post is part of a series on creating a simple audit for Microsoft Dynamics GP.

With the table created to store the audited information, the second step is to create the required triggers on the Address Electronic Funds Transfer Master (SY06000) table. For an Vendor EFT audit there are three triggers required:

  1. INSERT
  2. UPDATE
  3. DELETE

These triggers will record all new Vendor EFT information added as well as that which is amended or deleted. The client for which this audit was created only dealt with vendors in the UK and only ever set three fields in the EFT Bank window:

  1. Bank Name
  2. EFT Bank Code
  3. EFT Bank Account

Additional fields can be added to the audit if other fields need to be stored.

As the Address Electronic Funds Transfer Master (SY06000) table holds EFT Bank information for customers as well as vendors, the Record ID has been set to include the Series. Strictly speaking, this was not necessary as the client did not store bank details for their customers in Dynamics GP as none were making direct debit payments.

The audited data is being trimmed and cast as varchar so the extra whitespace held by Dynamics GP due to the columns being chars are removed.

The first trigger creates the trigger which runs when data is inserted:

/*
Created by Ian Grieve of azurecurve | Ramblings of an IT Professional (http://www.azurecurve.co.uk) This code is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0 Int). */
CREATE TRIGGER utr_AZRCRV_SY06000_AuditInsert ON SY06000 AFTER INSERT AS INSERT INTO ut_AZRCRV_Audit --VALUES SELECT 'Vendor EFT' ,CAST(i.SERIES AS VARCHAR(100)) + ' | ' + CAST(RTRIM(i.CustomerVendor_ID) AS VARCHAR(100)) + ' | ' + CAST(RTRIM(i.ADRSCODE) AS VARCHAR(100)) ,'Insert' ,SYSTEM_USER ,GETDATE_USER() ,'' ,'BANKNAME = ' + CAST(RTRIM(i.BANKNAME) AS VARCHAR(100)) + ' | ' + 'EFTBankCode = ' + CAST(RTRIM(i.EFTBankCode) AS VARCHAR(100)) + ' | ' + 'EFTBankAcct = ' + CAST(RTRIM(i.EFTBankAcct) AS VARCHAR(100)) FROM inserted i GO

Continue reading “Simple Audit for Microsoft Dynamics GP: Create Triggers”

Simple Audit for Microsoft Dynamics GP: Create Table

Microsoft Dynamics GPThis post is part of a series on creating a simple audit for Microsoft Dynamics GP.

The first step in creating the audit is to create a table in the company database to hold the audited information. From the user point of view there was five pieces of information required:

  1. Key for the vendor EFT being amended
  2. User ID
  3. Date/Time
  4. Old Data
  5. New Data

To make reporting easier and to add an element of future proofing, I also added two other pieces of information:

  1. Record Type to record the type of information being audit; in this case Vendor EFT.
  2. UpdateType to explicitly record whether the change was an INSERT, UPDATE or DELETE

The following SQL will create a table with the table with the seven columns mentioned above:

/*
Created by Ian Grieve of azurecurve | Ramblings of an IT Professional (http://www.azurecurve.co.uk) This code is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0 Int). */
CREATE TABLE ut_AZRCRV_Audit ( RecordType VARCHAR(100) ,RecordID VARCHAR(100) ,UpdateType VARCHAR(10) ,USERID VARCHAR(150) ,ChangeDateTime DATETIME ,OldData NVARCHAR(MAX) ,NewData NVARCHAR(MAX) ) GO