Thursday, 15 January 2009

Request Tracker scrip that involves the Category of a Custom Field

Our Request Tracker works for users in many countries, which are all assigned to areas. There is an IT administrator for each area, which should be added to the CC list for the request that is from one of the countries in his/her area.

We have created a custom field, and used the category to group the countries by areas.

We needed a scrip with an action that needs to know the category of the currently selected value of a custom field.


Condition: User Defined
Action: User Defined
Template: Global Template: Blank
Stage: TransactionCreate


Custom condition:
my $CFName = 'Field/Country';

my $CF = RT::CustomField->new( $RT::SystemUser );
$CF->LoadByNameAndQueue( Name => $CFName, Queue => $self->TicketObj->Queue );

if ( (($self->TransactionObj->Type eq "CustomField" && $self->TransactionObj->Field == $CF->id)
||
$self->TransactionObj->Type eq "Create" ) &&
($self->TicketObj->FirstCustomFieldValue('Field/Country')) )
{
return 1;
}
return 0;


Custom action preparation code:
my $CFName = 'Field/Country';
# for FirstCustomFieldValue see in Record.pm
my $fieldcountryVal = $self->TicketObj->FirstCustomFieldValue($CFName);
my $aita_email = '';
my $area = '';

#see CustomFieldValue_Overlay.pm, sub Category

my $CFObj = new RT::CustomField($RT::SystemUser);
$CFObj->Load($CFName);
my $valuesObj = $CFObj->ValuesObj();
while (my $myValue = $valuesObj->Next) {
if ($myValue->Name eq $fieldcountryVal)
{
#$RT::Logger->info($fieldcountryVal." belongs to area ".$myValue->Category);
$area = $myValue->Category;
}
}

if ($area eq "Some Area")
{
$aita_email = 'admin@somearea.ourdomain.org';
}
elsif ($area eq "Another Area")
{
$aita_email = 'admin@anotherarea.ourdomain.org';
}
#if ($aita_email ne '')
{
my ($ret, $msg) = $self->TicketObj->AddWatcher( Type => "Cc", Email => $aita_email);

if ($ret) {
#$RT::Logger->info("scrip: New watcher added to ticket");

} else {
#$RT::Logger->error("scrip: Failed to add new watcher to ticket");
}
}

return 1;

Custom action cleanup code:
return 1;

Friday, 21 November 2008

BestPractical RT: Request Tracker on CentOS 5 with PostgreSQL

see also http://www.ptitov.net/2008/07/request-tracker-installation-o.html

Here are the steps how to install Request Tracker on CentOS 5 with PostgreSQL:

Install RPM packages

yum install postgresql postgresql-server postgresql-libs
yum install httpd perl-BSD-Resource perl-FCGI perl-Apache-DBI mod_perl
wget http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
rpm -Uhv rpmforge-release*.rpm
yum install perl-Digest-HMAC perl-Apache-Session perl-Class-Container perl-Class-Data-Inheritable perl-Crypt-DES perl-Devel-StackTrace perl-Exception-Class perl-GD \
perl-GD-Graph perl-GD-Text-Util perl-Hook-LexWrap perl-HTML-Mason perl-HTTP-Server-Simple perl-HTTP-Server-Simple-Mason perl-IO-Socket-INET6 \
perl-Net-SNMP perl-Params-Validate perl-Socket6 perl-UNIVERSAL-require perl-HTML-scrubber perl-Text-WikiFormat perl-XML-RSS perl-Tree-Simple \
perl-Text-Wrapper perl-Module-Versions-Report perl-CSS-Squish \
perl-Term-ReadKey perl-Class-ReturnValue perl-Text-Quoted perl-Calendar-Simple perl-DBIx-SearchBuilder perl-Text-Autoformat \
perl-MailTools perl-Regexp-Common perl-Locale-Maketext-Lexicon perl-Locale-Maketext-Fuzzy perl-Time-HiRes perl-Time-modules perl-TimeDate \
perl-Log-Dispatch perl-Text-Template perl-HTML-Tree perl-HTML-Format \
perl-HTML-Scrubber perl-libwww-perl perl-MIME-tools perl-DBD-mysql



Configuration of Perl modules
To see if all required perl modules have been installed, run this:
/usr/bin/perl -MCPAN -e shell
make testdeps
/usr/bin/perl ./sbin/rt-test-dependencies --verbose --with-pgsql


Also, make sure you have newer versions of IO::File (1.14) and File-Temp (1.21).
Otherwise you get in trouble. More details follow here:

Fixes:
cat testemail.txt | /usr/bin/rt-mailgate --queue MyQueue --action correspond --url http://rt.mydomain.org/
Can't locate object method "seek" via package "File::Temp"


check currently installed version of File::Temp:
perl -le 'use File::Temp; print File::Temp->VERSION'

install the latest version 0.21:
cpan
# for reinitialising cpan:
o conf init
i /MIME-tools/
test D/DO/DONEILL/MIME-tools-5.425.tar.gz
i /file-temp/
install T/TJ/TJENNESS/File-Temp-0.21.tar.gz


Problem with attachements:
cat rttestmailatt.txt | /usr/bin/rt-mailgate --debug --queue myQueue --action correspond --url http://rt.mydomain.org
Connecting to http://rt.mydomain.org/REST/1.0/NoAuth/mail-gateway at /usr/bin/rt-mailgate line 102, <> line 1.
hello http://rt.mydomain.org/REST/1.0/NoAuth/mail-gateway
not ok - Failed to parse this message. Something is likely badly wrong with the message at /usr/bin/rt-mailgate line 112, <> line 1
another problem:
Can't locate object method "binmode" via package "IO::File" at /usr/lib/perl5/vendor_perl/5.8.5/MIME/Body.pm line 437.


We need version 1.14 of IO::File

perl -le 'use IO::File; print IO::File->VERSION'

requires: yum install gcc
cpan
i /IO-1.2301/
install G/GB/GBARR/IO-1.2301.tar.gz


IMPORTANT: need to reload the apache server, otherwise the new perl packages have no effect:
/etc/init.d/httpd reload

I came once across a Cpan problem:
Going to read /root/.cpan/sources/modules/03modlist.data.gz
Unrecognized character \x88 at (eval 29) line 1.

The solution was: delete 03modlist.data.gz, and just run cpan again

Install RT
Download the latest tar.gz file from http://bestpractical.com/rt/download.html
In a tmp directory, not the destination directory:
cd /home/timop
tar xvzf rt-3.6.6.tar.gz
cd rt-3.6.6
./configure --prefix=/usr --enable-layout=RH \
--with-db-host=localhost \
--with-db-dba=rt --with-db-rt-user=rt \
--with-db-database=rt --with-pgsql

Check to see if all dependancies are installed:
make testdeps | grep MISSING
make install

You then need to configure /etc/rt/RT_SiteConfig.pm
e.g. if you want to use Postgresql, it could look like this:
# {{{ Database Configuration

# Database driver beeing used. Case matters
# Valid types are "mysql", "Oracle" and "Pg"

Set($DatabaseType , 'Pg');

# The domain name of your database server
# If you're running mysql and it's on localhost,
# leave it blank for enhanced performance
Set($DatabaseHost , 'localhost');
Set($DatabaseRTHost , 'localhost');

# The port that your database server is running on. Ignored unless it's
# a positive integer. It's usually safe to leave this blank
Set($DatabasePort , '');

#The name of the database user (inside the database)
Set($DatabaseUser , 'rt');

# Password the DatabaseUser should use to access the database
Set($DatabasePassword , 'secret');

# The name of the RT's database on your database server
Set($DatabaseName , 'rt');

# If you're using Postgres and have compiled in SSL support,
# set DatabaseRequireSSL to 1 to turn on SSL communication
Set($DatabaseRequireSSL , undef);

# }}}


Configuration of PostgreSQL
vi /var/lib/pgsql/data/postgresql.conf;
For PostgreSQL 7.x: add line: tcpip_socket=true;
For PostgreSQL 8.x: listen_addresses = 'localhost'

vi /var/lib/pgsql/data/pg_hba.conf; it should contain:
local all all ident sameuser
host template1 rt 127.0.0.1/32 md5
host rt rt 127.0.0.1/32 md5
host all postgres 127.0.0.1/32 trust

/etc/init.d/postgresql start
postgresql logfile if db does not start: /var/lib/pgsql/pgstartup.log
chkconfig postgresql on
make initialize-database: problem: user rt does not exist; need to use dba postgres
/usr/bin/perl //usr/sbin/rt-setup-database --action init --dba postgres --prompt-for-dba-password

For a nightly backup of your database, you can use this command:
su - postgres -c "umask 0077; pg_dumpall | gzip > /var/lib/pgsql/data/backup.sql.gz"
To restore a database, e.g. when moving from one server to another:
su - postgres
psql template1
drop database rt;
create database rt;
\q
psql rt < /var/tmp/rt-20071210.sql

HTTPD configuration
/etc/httpd/conf.d/rt.conf should contain:
<VirtualHost *:80>
ServerName rt.mysupport.com
DocumentRoot /var/rt/html
AddDefaultCharset UTF-8

ServerAdmin Timotheus.Pokorra@mysupport.com
PerlModule Apache::DBI
PerlRequire /usr/bin/webmux.pl

<Location />
Options None
SetHandler perl-script
PerlHandler RT::Mason
<IfDefine PerlDProf>
<IfDefine PerlSmallProf>
PerlFixupHandler Apache::SmallProf
</IfDefine>
</IfDefine>
</Location>

<Location /NoAuth/images>
SetHandler default-handler
</Location>
# To use this you have to start apache with -DPerlStatus.
#
# In RHEL, add the following line to /etc/sysconfig/httpd:
#
# OPTIONS=-DPerlStatus
<IfDefine PerlStatus>
<Location /perl-status>
SetHandler perl-script
PerlHandler Apache2::Status
PerlSetVar StatusOptionsAll On
PerlSetVar StatusTerse On
PerlSetVar StatusTerseSize On
PerlSetVar StatusTerseSizeMainSummary On
PerlSetVar StatusLexInfo On
</Location>
</IfDefine>

To solve issues with "Can't change ownership of files written by interp object: Permission denied" when running Apache:
chcon -Rt httpd_sys_content_t /var/rt/mason_data/obj

chown -R apache /var/rt/mason_data/obj
chgrp -R apache /var/rt/mason_data/obj
chcon -Rt httpd_sys_content_t /var/rt/html
not: chcon -Rt httpd_sys_script_t /var/rt/html
/etc/init.d/httpd restart

To solve issues after upgrading RT, I had to do this sometimes:

rm -fr /var/rt/mason_data/obj
mkdir /var/rt/mason_data/obj
touch /var/rt/mason_data/obj/.__obj_create_marker
chmod -R a+w /var/rt/mason_data/obj
/etc/init.d/httpd restart
Configure Sendmail
The goal is to receive new requests via email.

You need to create a symbolic link, to avoid errors like "DSN: Service unavailable" or "write error: Broken pipe" in /var/log/maillog
ln -s /usr/bin/rt-mailgate /etc/smrsh
I have these lines in my /etc/aliases file:
my-support-comment: "|/etc/smrsh/rt-mailgate --queue MySupport --action comment --url http://rt.mysupport.com/"
my-support: "|/etc/smrsh/rt-mailgate --queue MySupport --action correspond --url http://rt.mysupport.com/"

Make sure that your server knows about rt.mysupport.com, you might need to add it to /etc/hosts.

You can test the delivery of emails:
echo "test" | mail my-support@rt.mysupport.com -s "test rt"
tail -f /var/log/maillog
or even more directly:
cat testemail.txt |/etc/smrsh/rt-mailgate --queue MySupport --action correspond --url http://rt.mysupport.com/
One other error I got was: "Relaying denied. IP name lookup failed" in /var/log/maillog
The problem was that emails were delivered to ticket.mycompany.com, and the rt server was at rt.mysupport.com.
I had to edit the file /etc/mail/local-host-names
and add the line ticket.mycompany.com

Tuesday, 28 October 2008

DTAUS with C#

I needed to export Lastschriften (instructions for direct debit) for our german office, and after looking for some solutions, I realised that it is not that hard to write it yourself...

Here is the result: http://download.pokorra.de/coding/Dtaus.cs
Please feel free to use the code any way you like.
I read the input data from an XML file, of course this needs to be modified to your specific situation.

A good help was this documentation of the german DTAUS bank file format:
http://www.infodrom.org/projects/dtaus/dtaus.html

Another useful tool was the JDTAUS editor, a java editor that can process and create DTAUS files:
http://www.jdtaus.de/de/
I use this to validate and test the files written with my C# program.

Monday, 20 October 2008

Building a mono 2.0 RPM for RHEL4/CentOS4

When I noticed that Novell does not offer RPMS of Mono 2.0 for RedHat Linux, I had a look at the Fedora 10 Beta and found that there is a Mono 2.0 Source RPM:
mono-2.0-6.fc10.src.rpm
Update: mono-2.0.1-12.fc10.src.rpm

Because we work with CentOS4/RedHat Enterprise Linux 4, I installed the source package on that system, and tried to build the rpms.

Building the code was fine after I deactivated a couple of dependencies.
But I ran into some problems at the file checking stage. I wondered if I can prevent the build stage to happen again and again. That way I should be able to debug the file list check etc.

Reading the man page, these parameters are interesting:
--short-circuit
--nobuild
-bi
-bl
So with rpmbuild -bl mono.spec I was able to just run the file checking stage.

The errors I got were like this:
rpm build errors: File not found: /var/tmp/mono-2.0-6-root-root/usr/bin/certmgr/usr/bin/chktrust/usr/bin/gacutil/usr/bin/gmcs/usr/bin/mcs/usr/bin/mcs1

But the files do exist in the /var/tmp/mono-2.0-6-root-root/ directory

I finally realised that the problem was caused by the Macros.
see also http://rpm.org/wiki/PackagerDocs/Macros
I had to first add curly brackets to each Macro call:
%{macro parameters}
But even then only the first line of the Macro had an effect.
This command was helpful in the files section to see that the Macros just were not resolved correctly:
%{echo: %{your_macro_here}}

In the end, I ended up adding the missing files to my own mono.spec file.
I also had a look at the last available RedHat spec file from Novell for Mono 1.9.x, and I guess there is a different rpm version for RHEL 4 and RHEL 5.

Here is my modified mono.spec file that works for RHEL4/CentOS4 for Mono 2.0:
http://download.pokorra.de/mono/mono.spec

Please download the src.rpm file from the Fedora 10 Beta, or later, and install it:
rpm -i mono-2.0-6.fc10.src.rpm
Then copy my modified mono.spec to /usr/src/redhat/SPECS, and build the packages:
rpmbuild -ba mono.spec

Alternatively, here is a zip file with the compiled RPM files for CentOS4/RHEL4:
mono-2.0-6.i386.rhel4.rpm.zip
I cannot guarantuee that it actually works, but feel free to use it for your own experiments!

Saturday, 4 October 2008

Use Progress and ODBC on CentOS with Japanese

This is related to my previous blog about using Chinese characters with Progress, .Net and Mono: Allow Chinese characters on a Client-Server application

Now the next challenge was to get Japanese characters to work.

I first converted the Progress database to shift-jis.
I set the environment variable: SQL_CLIENT_CHARSET=SHIFT-JIS

I then experimented with several locales, e.g. ja_JP.UTF8 and ja_JP (which is an alias for ja_JP.eucjp).

I used Wikipedia to get some sample words in Japanese characters, see http://en.wikipedia.org/wiki/Japanese_writing_system.

I soon realised that with some words I would get an ODBC error:
eg. with Kanji script 金魚 (Goldfish)
and with Hiragina script, きんぎょ
If I left out the last letter, it worked.

The ODBC error was:
Saving of data failed!
Reasons:
* [[ODBC]] [unixODBC][DataDirect-Technologies][ODBC 20101
driver][20101]Internal error -4 (unknown nls services error) in SQL from
subsystem NLS SERVICES function nlsCountChars called from nc_get_strlen
on . for . Save log for Progress technical support.


A helpful person from Progress Support gave me some suggestions, and I got it to work with Progress 9.1D in the end!
I had to use another locale, ja_JP.SJIS, which did not exist by default on my CentOS 4.6 (locale -a | grep JP).

According to http://kbase.redhat.com/faq/FAQ_80_1256.shtm
you can create the locale ja_JP.SJIS with this command:
localedef -f SHIFT_JIS -i ja_JP ja_JP.SJIS

This resolved the internal ODBC error.

Another issue was that I was not aware how to properly set the font in the ini file. Finally I found a hint in the Progress manuals, to check the prolang directory for the file progress.ini which is available for each language on the client side; this has a sample font. For the Japanese, this is a font with Japanese characters. I just copied it, and it worked.

Tuesday, 30 September 2008

Install Chinese or Japanese Windows XP with english XP screenshots

I sometimes have the problem that I need to install an asian Windows XP. I could always install a second XP in english in parallel in order for me to understand the screens in a language that is unknown to me.

Here are some screenshots that have helped me, hopefully they will be helpful for other people too.

I have noticed that there are more screens, e.g. Japanese XP has an extra screen for the keyboard layout. Select S for the menu, and then select "101 English keyboard layout". Otherwise you will have problems later when entering Name and Organisation. It also seems you should click on the little red dot in the keyboard selection thing at the right bottom corner when entering Name and Organisation, otherwise Tabulator key does not work, and I was not able to continue from that screen


One bit of advice: the keyboard shortcuts are usually the same, so watch out for capital Latin letters. This should help you to recognise which screen you are on and find the meaning on the english screenshots.

You can find a similar gallery here: http://techrepublic.com.com/2346-10878_11-5181.html






















Friday, 29 August 2008

Use the CSParser to process C# files

I wanted to generate code but at the same time parse the target file first to see if any changes have been manually made or with the graphical designer.


I found the following parser for c# code: http://www.codeplex.com/csparser; it seems very active at the moment, and it parsed my test cs file without any problems.



I had some issues first understanding how to look for the current line number and how to know the name of the classes and methods etc. But a look at the sample file cmc.cs helped a lot.

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using DDW.Collections;
using DDW;
using Microsoft.CSharp;

namespace test
{
class Program
{
private static void PrintErrors(IEnumerable errors)
{
foreach (Parser.Error error in errors)
{
if(error.Token.ID == TokenID.Eof && error.Line == -1)
{
Console.WriteLine(error.Message + "\nFile: " + error.FileName + "\n");
}
else
{
Console.WriteLine(error.Message + " in token " + error.Token.ID
+ "\nline: " + error.Line + ", column: " + error.Column
+ "\nin file: " + error.FileName + "\n");
}
}
}

private static CompilationUnitNode ParseUnit(string fileName, List errors)
{
Console.Write("\nParsing " + fileName);
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs, true);
Lexer l = new Lexer(sr);
TokenCollection toks = l.Lex();

Parser p = null;
CompilationUnitNode cu = null;

p = new Parser(fileName);
cu = p.Parse(toks, l.StringLiterals);

if(p.Errors.Count != 0)
{
Console.WriteLine();
PrintErrors(p.Errors);
errors.AddRange(p.Errors);
return null;
}
return cu;
}

public static void Main(string[] args)
{
string filename = "c:\\myfile.cs";
List errors = new List();
CompilationUnitNode cu = ParseUnit(filename, errors);

StringBuilder sb = new StringBuilder();
cu.ToSource(sb);
Console.WriteLine(sb.ToString());
foreach (NamespaceNode nnode in cu.Namespaces)
{
foreach (ClassNode cnode in nnode.Classes)
{
Console.WriteLine("class name: " + cnode.Name.Identifier.ToString());
foreach (FieldNode fnode in cnode.Fields)
{
foreach (QualifiedIdentifierExpression qexpr in fnode.Names)
{
sb = new StringBuilder();
qexpr.Expressions.ToSource(sb);
Console.WriteLine("Field: " + sb.ToString());
}
}
foreach (MethodNode mnode in cnode.Methods)
{
sb = new StringBuilder();
mnode.Names.ToSource(sb);
if (sb.ToString() == "InitializeComponent")
{
Console.WriteLine("Line number of InitializeComponent starting: " +
mnode.RelatedToken.Line.ToString());
int firstLine = -1;
int lastLine = -1;
foreach (StatementNode snode in mnode.StatementBlock.Statements)
{
if (firstLine == -1)
{
firstLine = snode.RelatedToken.Line;
}
lastLine = snode.RelatedToken.Line;
sb = new StringBuilder();
snode.ToSource(sb);
string line = sb.ToString();
Console.Write(snode.RelatedToken.Line.ToString() + ": " + line);

}
}
}

}
}

Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}