Showing posts with label domino. Show all posts
Showing posts with label domino. Show all posts

Friday, June 01, 2007

Ruby on Rails Domino Active Record

I have been using this class for a while that allows some ActiveRecord like features for IBM Lotus Domino databases.

An example use would be:


class Contact < DominoRecord
attr_accessor :email_address, :first_name, :last_name

validates_presence_of :first_name, :last_name, :email_address

validates_format_of :email_address, :with =>

/^\s*(?:(?:[^,@\s]+)@(?:(?:[-a-z0-9]+\.)+[a-z]{2,}\s*(,\s*|\z)))+$/i,

:message => "must be a valid email address",

:on => :create

@@database_name = "contacts.nsf" #your database here

@@server_name = 'www/projectlounge' #your server here

end


It currently uses the OLE/COM bridge to get to domino so only will work on windows at this stage. The next step would be to compile it with the NotesAPI to allow it to work on all platforms.

The model source code is here (I do not maintain this anymore but happy for others to take it on under LGPL):


require 'win32ole'

class DominoRecord



attr_accessor :form



def save

@document = @@database.CreateDocument

form = self.class.to_s.downcase

@document.ReplaceItemValue "form", form

self.instance_variables.each do |inst|

field_name = inst.sub(/[@]/, '')

field_value = self.instance_variable_get(inst)

@document.ReplaceItemValue field_name, field_value.to_s

end

@document.Save(true, false)

end



def save!;

save

end

def update_attribute; end

def new_record?; end

include ActiveRecord::Validations



@@session = nil

@@database = nil

@@database_name = nil

@@server_name = nil



def [](key)

instance_variable_get(key)

end



def self.find(*args)

options = extract_options_from_args!(args)

validate_find_options(options)



case args.first

when :first then

@document = find_initial(options)

when :all then find_every(options)

else

@document = find_from_ids(args, options)

end



record = self.new

@document.Items.each do |item|

record.send(item.Name.downcase + "=", item.Text) unless ["document", "errors"].include?(item.Name)

end

record

end



def DominoRecord.human_attribute_name(attribute_key_name)

ActiveRecord::Base.human_attribute_name(attribute_key_name)

end



def initialize(attributes=nil)

unless @@session

@@session = WIN32OLE.new('Lotus.NotesSession')

@@session.Initialize

end

@@database = @@session.GetDatabase(@@server_name,@@database_name) unless @@database



unless form == nil

fields = @@database.GetForm(form).Fields

else

fields = @@database.GetForm(self.class.to_s).Fields

end



fields.each { |field| self.class.__send__(:attr_accessor, field) }



return if attributes.nil?

attributes.each do |k, v|

send(k + "=", v)

end

end



def finalize

@@session.Finalize

end



def session

@@session

end



def database

@@database

end



def document

@document

end



def attributes=(attributes)

return if attributes.nil?

attributes.stringify_keys!

multi_parameter_attributes = []

attributes.each do |k, v|

send(k + "=", v)

end

end



def self.method_missing(method_id, *arguments)



unless @@session

@@session = WIN32OLE.new('Lotus.NotesSession')

@@session.Initialize

end

@@database = @@session.GetDatabase(@@server_name,@@database_name) unless @@database



if match = /find_by_([_a-zA-Z]\w*)/.match(method_id.to_s)



@view = @@database.GetView($1)

puts arguments

@document = @view.GetDocumentByKey(arguments[0])



record = self.new

@document.Items.each do |item|

record.send(item.Name.downcase + "=", item.Text) unless ["document", "errors"].include?(item.Name)

end

record

else

super

end

end



private

def self.find_initial(options)

#not working

end



def self.find_every(options)

#not working

end



def self.find_from_ids(ids, options)

#not working

end



def self.find_one(id, options)



if result = @@database.GetDocumentByUNID( id )

result

else

raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"

end

end



def self.find_some(ids, options)



result = find_every(options)



if result.size == ids.size

result

else

raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"

end

end





def self.extract_options_from_args!(args) #:nodoc:

args.last.is_a?(Hash) ? args.pop : {}

end



VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,

:order, :select, :readonly, :group, :from ]



def self.validate_find_options(options) #:nodoc:

options.assert_valid_keys(VALID_FIND_OPTIONS)

end

end

Monday, May 14, 2007

Allow selections not in list (for the web)

One of the neat options with drop down selections in Notes, is the ability to let people type an option that is not in the list. This helps build dynamic lists.

Here is some javascript to use to get this working on the web:


function changed(el, cmp, pmt){
if(el.options[el.selectedIndex].value==cmp) {addoption(el, pmt);}
}
function addoption(el, pmt){
var txt=prompt(pmt,'');
if(txt==null) {return;}
var o=new Option( txt, txt, false, true);
el.options[el.options.length]=o;
}


You can either put this in an included javascript file or wrap it directly inline.

Then you can call it using the onchange event for your selection field:

onchange="changed(this, 'New Group', 'Please enter new group:')"

Where "New Group" is the text you want to trigger the prompt for the text and "Please enter new group" is the prompt you want to display.

Friday, January 26, 2007

Very positive LotusSphere 2007

I am traveling back from LotusSphere 2007 and am very excited about Notes 8, Quickr (QuickPlace 8), and Lotus Connections (Ventura/Activities). I have been to a few LotusSpheres over the years and this was my first that I was not there from IBM or Iris. The positive energy around Notes 8, Quickr and Portal Express reminded me of when I did my first QuickPlace talk. The Notes developers and my old team were getting beaten up in the meet the developers lab about all the bugs and feature requests - where I was in the QuickPlace lab soaking up all the positive energy and excitement from the business partners and guests. This year, the positive energy was there for the Notes 8 client developers to enjoy.
The Ask the Developers section at the end was the most telling for me. Although there was a fair share of difficult questions, it was so positive to hear everyone preface their questions with admiration for the work and effort the development teams had put into Notes 8, Quickr and Connections.
The proof will be when the code is shipped and people start to adopt the composite application model for delivering applications. It will give IBM customers and partners a really powerful way to extend and deliver value.

Friday, November 17, 2006

How to compile NotesAPI programs without buying visual studio on Windows XP

Download and install:
1. Microsoft Visual C++ Express (GUI not needed to compile)
http://msdn.microsoft.com/vstudio/express/visualc/
2. Run Windows Update if needed

3. Microsoft Platform SDK for Windows Server 2003
http://www.microsoft.com/downloads/details.aspx?FamilyID=0baf2b35-c656-4969-ace8-e4c0c0716adb&DisplayLang=en#filelist

4. Download and extract the Lotus C API Toolkit
http://www-128.ibm.com/developerworks/lotus/downloads/toolkits.html

5. Download the nmake program and copy this to your c++ bin directory
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q132084
(may already come with c++ express)

6. Create batch files that simplify your build (put these in a directory in your path)

FILE: mk.bat
CONTENTS:
nmake /f mswin32.mak /a

FILE: setenv.bat
CONTENTS:
"D:\Program Files\Microsoft Visual Studio 8\VC\vcvars32.bat"
"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\SetEnv.bat"
set LIB=d:\notesapi\lib\mswin32;%LIB%
set INCLUDE=d:\notesapi\include;%INCLUDE%
set PATH=d:\lotus\domino;%PATH%

FILE: w32.bat
CONTENTS:
"D:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\SetEnv.Cmd" /RETAIL

7. To use the compiler
1. Start a Visual Studio 2005 Command Prompt
2. Run the commands:
a) setenv
b) w32
3. Change to the d:\notesapi\samples\basic\intro
4. run the batch file "mk"

...and you should then be able to compile NotesAPI programs.

Wednesday, May 17, 2006

Reporting Spam to SpamCop from Lotus Notes

Here is a very simple agent to report a select email or emails to spamcop's quick reporting service. You will need a spamcop account and access to quick reporting.

It uses a new r6 feature of lotusscript to get the received header information so that spamcop will know exactly where the email came from at each hop. This is important as they use this for their black lists so that the correct servers are blocked while the innocent receiving servers are not.

It is also important to list your allowed hosts with spamcop so that if it routes through some of your servers then these will not be treated at open relays.

Sub Initialize
Dim s As New notessession
Dim dc As NotesDocumentCollection
Dim db As notesdatabase
Dim doc As notesdocument

Set db = s.CurrentDatabase
Set dc = db.UnprocessedDocuments

Set doc = dc.GetFirstDocument


While Not(doc Is Nothing)

Dim text As String
text = ""
Forall t In doc.GetReceivedItemText
text = text + "Received: " + t + Chr(10)
End Forall
Forall it In doc.Items
If it.name <> "Received" Then
text = text + it.name + ": " + it.text + Chr (10)
End If
End Forall

Dim mail As notesdocument
Set mail = db.CreateDocument

mail.form = "memo"
mail.subject = "Spam to report"
mail.body = text

Call mail.Send(False, "spam@uce.gov")
Call mail.Send(False, "quick.[yoursecretkeyhere]@spam.spamcop.net")

'You can then save or just delete it here
doc.RemoveFromFolder("($junkmail)")
Call doc.Save(True, False)

Set doc = dc.GetNextDocument(doc)
Wend
End Sub

Like this article? Digg it!

Thursday, March 30, 2006

Hunting down invalid signatures

I have spent the last few days trying to hunt down an archive problem in a notes mail file. In doing this, I wrote a small lotusscript button to verify signatures that I can share here.

This could be written in the C API. However, keeping it in a button makes it easy to email out to users for them to test.

Const MAXUSERNAME =256
Declare Function NSFNoteVerifySignature Lib "nnotes.dll" Alias "NSFNoteVerifySignature" ( Byval hNote As Long, Byval null1 As Long, Byval null2 As Long, Byval retSigner As String, Byval retCertifier As String) As Integer
Declare Function NSFNoteUnsign Lib "nnotes.dll" Alias "NSFNoteUnsign" ( Byval hNote As Long) As Integer
Const NOERROR = 0
Const PKG_NSF = 512
Const ERR_NOTE_NOT_SIGNED = PKG_NSF+89 'Document is not signed
Const ERR_NOTE_INVSIG2 = PKG_NSF+91 'Document has been modified or corrupted since signed! (data)

Sub Click(Source As Button)
Dim ses As New NotesSession, irc As Integer
Dim retSigner As String, retCertifier As String
Dim doc As notesdocument, vw As NotesView
Dim db As notesdatabase
Dim dc As notesdocumentcollection
Dim fileNum%
Dim i As Integer, count As Integer

Set vw = ses.currentdatabase.views(0)
Set db = ses.currentdatabase

fileNum% = Freefile
Open "c:\temp\signatures.txt" For Output As fileNum%


Set dc = db.GetProfileDocCollection()
Set doc = dc.GetFirstDocument
count = dc.Count
i = 1
Print "Check profile Notes: " &Cstr(count)
While Not doc Is Nothing
Call fix_signature(doc, fileNum, i, count)
i = i + 1
Set doc = dc.GetNextDocument(doc)
Wend

Set dc = db.AllDocuments
Set doc = dc.GetFirstDocument
count = dc.Count
i = 1
Print "Check data Notes: " &Cstr(count)
While Not doc Is Nothing
Call fix_signature(doc, fileNum, i, count)
i = i + 1
Set doc = dc.GetNextDocument(doc)
Wend

Close fileNum%
End Sub

Sub fix_signature(doc As notesdocument, fileNum%, i, count)
Dim textline As String
retSigner = String$(MAXUSERNAME ,Chr(0))
retCertifier = String$(MAXUSERNAME ,Chr(0))

irc%= NSFNoteVerifySignature (doc.handle, 0, 0, retSigner, retCertifier)
Print Cstr(i) &"/" &Str(count)
If irc% = NOERROR Then
textline = "SIGNED: " &Cstr(doc.NoteID) &" " &Left(retSigner, Instr(retSigner,Chr(0))-1)
Write #fileNum%, textline
Elseif irc% = ERR_NOTE_NOT_SIGNED Then
textline = "NO SIGNATURE: " &Cstr(doc.NoteID)
Write #fileNum%, textline
Elseif irc% = ERR_NOTE_INVSIG2 Then
textline = "INVALID_SIGNATURE: " &Cstr(doc.NoteID)
irc% = NSFNoteUnsign(doc.handle)
If (irc% = NO_ERROR) Then
Call doc.Save(True, False)
textline = "REMOVED SIGNATURE: " &Cstr(doc.NoteID)
Else
textline = "COULD NOT REMOVE SIGNATURE: " &Cstr(doc.NoteID)
End If
Write #fileNum%, textline
Else
textline = "ERR: " &Cstr(irc%) &" " &Cstr(doc.NoteID)
Write #fileNum%, textline
End If
End Sub