Index: openacs-4/packages/acs-mail/www/doc/openacs-mail.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-mail/www/doc/openacs-mail.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-mail/www/doc/openacs-mail.html 13 Aug 2001 18:24:35 -0000 1.1 @@ -0,0 +1,482 @@ + + +
+++ ++ This document explains the structure and function of the acs-mail + package in OpenACS 4. +
+ ++ acs-mail is a package that provides basic mail services to other + OpenACS packages. +
+
++ ++ Within aD's version of ACS 4.x, there were three packages that + handled outgoing email messaging - acs-messaging, acs-mail and + acs-notifications. acs-messaging was built using the content + repository to allow the bboard package to send out email. acs-mail + came later with a little more mail functionality, including the + ability to send out multipart email messages. acs-notification was + a package that maintained a complex mail queue (the other two + packages implemented much simpler queues that place most of the + burden on the MTA). It also implemented a mail package inside + Oracle that sent the outgoing mail. Both acs-mail and acs-messaging + use ns_sendmail to send out mail, making them db-independent. +
+ ++ Our first goal was to simplify this by removing the notifications + package and replacing that functionality (basically one function) + inside acs-mail. Another goal was to have acs-mail use the content + repository so that it can take advantage of all the CR functions. +
+ ++ We haven't merged acs-messaging and acs-mail, although I think that + we should do this at some point. Currently, they provide very + similar functionality. +
+
++ ++ There are a few basic tables that the user needs to know about. The + acs_mail_bodies table contains the attributes of the mail + message. The most important column in this table is the + content_item_id attribute. This is a space for a cr_item from the + Content Repository. Note that this column is a foreign key to + acs_objects, rather than to cr_items as one would expect. This is + because the API uses this column to decide whether or not the + message is a simple message or a multipart message. If the column + contains a value which is present in acs_mail_multiparts, then the + message is a multipart message. If not, then it is a simple + message. Thus the column can't be a foreign key to cr_items since + in the case of multipart messages, it won't contain a cr_item's + item_id. Also, note that you should not use header_from and + header_to columns in this table. Instead, you'll create the mail + body as a generic mail body and then give it a from: and to: value + when you queue it. So why are they there? Good question. It may be + that they are there for incoming messages. But since that is not + implemented, I'm not sure. +
+ ++ The table acs_mail_body_headers allows you to add arbitrary + headers to your message. The column body_id in this table is a + foreign key to acs_mail_bodies +
+ ++ The table acs_mail_links contains 2 columns - a mail_link_id + and a body_id. After you create a acs_mail_body, you'll create a + mail_link and then insert the mail_link into the queue. Why the + abstraction? This was initially meant to be a point where garbage + collection could occur. So, other applications are supposed to + subclass acs_mail_links to their own use. They create a message + body and a mail_link. Once they're done, they delete the + mail_link. A scheduled process then looks for any message_bodies + that are not linked to mail_links any more. It deletes these + messages. This is not implemented yet (but would be rather easy to + do). +
+ ++ Think of acs_mail_bodies as 'message bodies' and acs_mail_links as + 'messages'. An application can send the same email to multiple + people by creating one message body but associating that message + body with multiple messages. +
+ ++ The table acs_mail_queue_messages is a table that contains + mail_links. The table acs_mail_queue_outgoing is the + outgoing queue and is a foreign key to acs_mail_queue_messages. It + also contains 2 other columns, envelope_from and envelope_to. This + is where you address the email. The table + acs_mail_queue_incoming looks just like the outgoing + queue. The plan is for the MTA to create mail bodies when incoming + messages arrive. It should then insert that mail_body into the + incoming queue. Other applications should then periodically scan + the incoming queue and grab messages that it wants (presumably + matching a pattern of some sort) and then deletes the message from + the incoming queue. +
+ ++ The table acs_mail_multiparts contains the information about + multipart messages. The column multipart_kind is either + 'alternative' or 'mixed' (are there others?). The table + acs_mail_multipart_parts then contains the individual parts. +
+ ++ The tcl procedure acs_mail_process_queue runs every 15 + minutes and sends out any messages in the outgoing queue. +
+ ++ The acs-notifications package is built on top of acs-mail and hides + the complexity from the user (if you're satisfied with creating + plain-text messages). It's described next. +
+ +
++ ++ The simplest feature of the acs-mail package is the ability to send + a notification. A notification is a plain text email that is sent + from one party to another. With ACS 4.x, this was done using the + acs-notifications package as follows: +
++ nt.post_request ( + party_from => :party_from, + party_to => :party_to, + expand_group => 'f', + subject => 'my subject', + message => 'plain text message', + max_retries => 3 + ); +++ party_from and party_to are party_id's from the parties table. If + expand_group is true and party_to is a group_id, then the procedure + will send the email to each of the group's members. If expand_group + is false, then the email will only be sent to the group email + address. +
++ The openacs version is very similar. The max_retries parameter is + not used because, as mentioned above, acs-mail's queue is very + simple and relies on the MTA. So max_retries must be zero. Here's + the openacs version: +
++ select acs_mail_nt__post_request ( + :party_from, -- p_party_from + :party_to, -- p_party_to + 'f', -- p_expand_group + :subject, -- p_subject + :message, -- p_message + 0 -- p_max_retries + ); +++ We've also overloaded the function so that you can call the + function with only the 4 essential parameters: party_from, + party_to, subject, and message. +
+ ++ Note that acs-notifications is now completely gone. So event the + oracle queries have to be converted from nt.post_request to + acs_mail_nt.post_request. acs_mail_nt.cancel_requet is also + supported. All the other nt functions are deprecated and their + replacements (if any) are described in the file + packages/acs-mail/sql/oracle/acs-mail-nt-create.sql (or the + corresponding file in the postgresql directory) +
+ +
++ ++ Creating a simple email message consists of the following steps: +
++
+ +- Create a CR item with your message
+- Create a new acs_mail_body with content_item=(item_id of your CR item)
+- Queue the message
+From PL/SQL
+++ ++begin + -- usually the content_item will already be created by your app + -- for it''s own purposes + v_item_id := content_item__new ( ... ); + v_revision_id := content_revision__new ( ... v_item_id ... ); + perform content_revision__set_live_revision( v_revision_id ); + + v_subject := ''My subject''; + v_user_id := 2222; + v_ip_addr := ''10.0.0.1''; + + -- create an acs_mail_body + v_body_id := acs_mail_body__new ( + null, -- p_body_id + null, -- body_reply_to + null, -- body_from + now(), -- body_date + null, -- header_message_id + null, -- p_header_reply_to + v_subject, -- p_header_subject + null, -- p_header_from + null, -- p_header_to + v_item_id, -- p_content_item_id + ''acs_mail_body'', -- p_object_type + now(), -- p_creation_date + v_user_id, -- p_creation_user + v_ip_addr, -- p_creation_ip + null -- p_context_id + ); + + -- put the message on the queue + v_mail_link_id := acs_mail_queue_message__new ( + null, -- p_mail_link_id + v_body_id, -- p_body_id + null, -- p_context_id + now(), -- p_creation_date + v_user_id, -- p_creation_user + v_ip_addr, -- p_creation_ip + ''acs_mail_link'' -- p_object_type + ); + + v_from_addr := ''sender@openacs.org''; + v_to_addr := ''recipient@openacs.org''; + + -- put the message on the outgoing queue + insert into acs_mail_queue_outgoing + ( message_id, envelope_from, envelope_to ) + values + ( v_mail_link_id, v_from_addr, v_to_addr )" + + return 0; +end; ++From tcl
+++ ++ The tcl procedure acs_mail_body_new allows you to create + messages from text content (-content), binary files + (-content_file) or a CR item (-content_item_id). +
++set header_subject "My subject" +set content "My message is here." +set content_type "text/plain" + +# create a mail body +set body_id [acs_mail_body_new -header_subject $header_subject \ + -content $content -content_type $content_type] + +# queue it +set sql_string "select acs_mail_queue_message.new ( + null, -- p_mail_link_id + :body_id, -- p_body_id + null, -- p_context_id + now(), -- p_creation_date + :user_id, -- p_creation_user + :ip_addr, -- p_creation_ip + 'acs_mail_link' -- p_object_type + );" + +set mail_link_id [db_string queue_message $sql_string] + +# put in in outgoing queue +set sql_string " +insert into acs_mail_queue_outgoing + ( message_id, envelope_from, envelope_to ) +values + ( :mail_link_id, :from_addr, :to_addr )" + +db_dml outgoing_queue $sql_string + ++
++ ++ The steps here are: +
++
+ +- Create a new acs_mail_multipart as either 'alternative' or + 'mixed'
+ +- Create a acs_mail_body using the multipart_id as the + content_item_id
+ +- Create content_items and add them to the multipart message +
+ +- Queue the message
+Example in tcl
++++ +# create the multipart message ('multipart/mixed') +set multipart_id [acs_mail_multipart_new -multipart_kind "mixed"] + +# create an acs_mail_body (with content_item_id = multipart_id ) +set body_id [acs_mail_body_new -header_subject "My subject" \ + -content_item_id $multipart_id ] + +# create a new text/plain item +set content_item_id [db_string create_text_item " + select content_item__new ( + 'acs-mail message $body_id-1', -- name + ... + 'text message', -- title + ... + 'text/plain', -- mime_type + ... + 'plain message content' -- text + ... + ) +"] + +acs_mail_multipart_add_content -multipart_id $multipart_id \ + -content_item_id $content_item_id + +# create a new text/html item + +set content_item_id [db_string create_html_item " + select content_item__new ( + 'acs-mail message $body_id-2', -- name + ... + 'html message', -- title + ... + 'text/html', -- mime_type + ... + 'HTML message content', -- text + ... + + ) +"] + +acs_mail_multipart_add_content -multipart_id $multipart_id \ + -content_item_id $content_item_id + +# create a new binary item +# from file that was uploaded + +set mime_type "image/jpeg" + +set content_item_id [db_string create_jpeg_item " + select content_item__new ( + 'acs-mail message $body_id-3', -- name + ... + :mime_type, -- mime_type + ... + ) +"] + +set revision_id [db_string create_jpeg_revision " + select content_revision__new ( ... ) +"] + +db_transaction { + db_dml content_add " + update cr_revisions + set content = empty_blob() + where revision_id = :revision_id + returning content into :1 +" -blob_files [list ${content_file.tmpfile}] +} + + +db_1row make_live { + select content_item__set_live_revision(:revision_id); +} + +# get the sequence number, so we can make this part an attachment +set sequence_num [acs_mail_multipart_add_content -multipart_id $multipart_id \ + -content_item_id $content_item_id] + +db_dml update_multiparts " + update acs_mail_multipart_parts + set mime_disposition='attachment; filename=\"myfile.jpg\"' + where sequence_number=:sequence_num and + multipart_id=:multipart_id" + +set sql_string "select acs_mail_queue_message.new ( + null, -- p_mail_link_id + :body_id, -- p_body_id + null, -- p_context_id + now(), -- p_creation_date + :user_id, -- p_creation_user + :ip_addr, -- p_creation_ip + 'acs_mail_link' -- p_object_type + );" + +set mail_link_id [db_string queue_message $sql_string] + +set sql_string " + insert into acs_mail_queue_outgoing + ( message_id, envelope_from, envelope_to ) + values + ( :mail_link_id, :from_addr, :to_addr )" + +db_dml outgoing_queue $sql_string + ++
+++
+- In order to schedule a message, you must call + acs_mail_queue_message__new AND then insert the message into + acs_mail_queue_outgoing table. You would think that the __new + function would do this, but it doesn't. In a way, it makes + sense. It allows you to queue one message and then to put that + message into the outgoing queue multiple times with different 'To: + addresses'.
+ +- The tcl procedure that sends out the mail + acs_mail_process_queue calls a procedure + acs_mail_body_to_output_format that sets up the parameters + to call ns_sendmail. This proc gets the from and to address from + acs_mail_bodies. acs_mail_process_queue then overwrites the + from and to address with the values from envelope_from and + envelope_to in the acs_mail_queue_outgoing table. So, if you supply + the from and to address in acs_mail_bodies, they get overwritten by + whatever is in acs_mail_queue_outgoing. (Actually it's a little + more complicated than that because acs_mail_body_to_output_format + also adds the addresses as Headers in the 5th param to ns_sendmail, + so you could get duplicate To: and From: headers. Bottom line: + Don't fill in the To: and From: fields in acs_mail_bodies. Instead, + create a unaddressed mail body and then provide the To: and From: + address in acs_mail_queue_outgoing.
+ +- Garbage collection is mentioned a lot, but not implemented.
+ +- There are some other procs available both in plsql and tcl, so + take a look at the source files.
+ +