Thursday, September 30, 2010

Modifying tab order

In AX to change tab order normally you change the order of the controls in the form.  You can set the "Skip" property on a control to Yes and when the user tabs it will skip that control.  Certain forms where there are many groups or process flow should be different than the default way require a different approach.  There is a way to specify the tab order for an entire form.

First set AutoDeclaration to Yes on all your controls.
Second override the init method and provide it an array of control IDs in the order that you want to tab.

void init()
{
    Array tabOrder = new Array(Types::Integer);
    ;
    super();

    tabOrder.value(1, Control1.id());
    tabOrder.value(2, Control2.id());
    tabOrder.value(3, Control3.id());
    tabOrder.value(4, Control4.id());
    tabOrder.value(5, Control5.id());
    tabOrder.value(6, Control6.id());
    tabOrder.value(7, Control7.id());
    tabOrder.value(8, Control8.id());
    tabOrder.value(9, Control9.id());
    tabOrder.value(10, Control10.id());
    tabOrder.value(11, Control11.id());
    tabOrder.value(12, Control12.id());

    element.tabOrder(tabOrder);
}

Sunday, September 26, 2010

Nagios: Automatically restart IIS after a CRITICAL alert

You can execute code when a service or host changes state.  These are called event handlers.

  1. Go to the Windows server and create a local user
  2. Create the following script called iisreset.sh in your Nagios plugins directory replacing the username and password in the three locations below



    #!/bin/sh
    #
    # Event handler script for restarting IIS on windows server
    #    Requires a local user on the server with the username and password below
    
    if [ $# -eq 3 ]
    then
            if [ "$1" = "CRITICAL" ]
            then
                    if [ "$2" = "HARD" ]
                    then
                            # Start the service
                            /usr/bin/net rpc service stop W3SVC -I $3 -U User%password >/dev/null
                            sleep 4
                            /usr/bin/net rpc service start W3SVC -I $3 -U User%password >/dev/null 2>&1
                            sleep 4
                            /usr/bin/net rpc service status W3SVC -I $3 -U User%password | grep 'W3SVC'
                            exit 0
                    fi
            fi
    else
            echo "Invalid number of arguments"
            exit 1
    fi
    

  3. Edit your commands.cfg file in the Nagios configuration directory



    define command {
            command_name    iisreset
            command_line    $USER1$/iisreset.sh $SERVICESTATE$ $SERVICESTATETYPE$ $HOSTADDRESS$
            }
    

  4. Add the event handler command to an existing service definition



    define service{
                    host_name                         somehost
                    service_description        HTTP
                    event_handler                  iisreset
                    ...
                    }
    

  5. The script will be called like this.  You can put this in a command line to confirm it will work. /usr/lib/nagios/plugins/iisreset.sh CRITICAL HARD 10.10.0.2
  6. Reload or start Nagios
    /etc/init.d/nagios3 reload

Saturday, September 11, 2010

Tuesday, September 7, 2010

Deleting all transactions in a company

Sometimes when creating a new playground for testing you need to delete all transactions in a company.  There is a nice utility included in AX for this purpose.  The SysDatabaseTransDelete class will delete all tables by checking the TableGroup property on the table.  All WorksheetHeader, WorksheetLine, and Transaction tables will be deleted.  There are a few exceptions so please review the class before running, and NEVER run in the live environment (I shouldn't have to say that).

Sunday, September 5, 2010

How to modify the permissions of the Admin group

After creating a new security key you will need to modify the permissions of the Admin group, but the User group permissions form does not allow it.  This small AX job will grant the Admin group permissions to the new security key.

static void grantSafetyAccess(Args _args)
{
    #Admin
    SecurityKeySet  securitySet;
    ;
    setPrefix(funcName());
 
    securitySet = new SecurityKeySet();
    securitySet.loadGroupRights(#AdminUserGroup, '');
 
    securitySet.access(securitykeynum(MyNewSecurityKey), AccessType::Delete);
 
    xAccessRightsList::saveSecurityRights(securitySet.pack(), #AdminUserGroup, '');
}

Friday, September 3, 2010

Reports being used

I was asked recently which reports were being run by users so that some of the menus could be simplified.  This SQL will list users and the AX report name for every report that has been run by anyone.  It is a good start, but it cannot get us the last date or time that they ran the report.

SELECT UserInfo.[Name], ElementName AS [Report Name], UserInfo.[Enable] AS [Active Employee]
INTO #temp
FROM SysLastValue WITH(NOLOCK)
INNER JOIN UserInfo WITH(NOLOCK)
                ON UserInfo.ID = SysLastValue.UserID
WHERE UserID NOT IN ('Admin', '')
                AND RecordType = 18 /* Report */

SELECT t.[Report Name],
                (SELECT COUNT(DISTINCT [Name])
                                FROM #temp sub
                                WHERE sub.[Active Employee] = 1
                                                AND sub.[End User] = 1
                                                AND sub.[Report Name] = t.[Report Name]) AS [# of Active Employees Who Ran The Report]
FROM #temp t
GROUP BY [Report Name]
ORDER BY [Report Name]

DROP TABLE #temp
}

This AX job returns the label and location of all reports in the menu structure.  We can then add this information with the last used information to come up with a fairly good estimation of which reports are being used.

// Lists where all reports are located in the menu structure
// Does not include reports which are called by classes, or auto printed by a scheduled job
static void listMenuStructure(Args _args)
{
    #AOT
    #Properties
    TreeNode                treeNode;
 
    void traverse(TreeNode tn)
    {
        while (tn)
        {
            switch (tn.AOTgetNodeType())
            {
                case 205: // Folder
                    traverse(tn.AOTfirstChild());
                    break;
                case 331: // Menu reference
                    if (tn.AOTgetProperty(#PropertyName))
                    {
                        treeNode = TreeNode::findNode(#MenusPath + #AOTDelimiter + any2str(tn.AOTgetProperty(#PropertyName)) + #AOTDelimiter + 'Reports');
                        if (treeNode)
                            traverse(treeNode.AOTfirstChild());
                    }
                    break;
                case 307: // Menu item
                    setPrefix(tn.treeNodePath());
                    info(tn.AOTgetProperty(#PropertyName));
                    info(tn.AOTgetProperty(#PropertyMenuitemname));
                    break;
            }
            tn = tn.AOTnextSibling();
        }
    }
    ;
    setPrefix(funcName());
    treeNode = TreeNode::findNode(#MenusPath + #AOTDelimiter + 'MainMenu');
    treeNode = treeNode.AOTfirstChild();
    traverse(treeNode);
}