Index: openacs-4/packages/contacts/contacts.info
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/contacts.info,v
diff -u -r1.91 -r1.92
--- openacs-4/packages/contacts/contacts.info	26 Jun 2007 02:53:12 -0000	1.91
+++ openacs-4/packages/contacts/contacts.info	27 Jun 2007 23:55:35 -0000	1.92
@@ -7,14 +7,14 @@
     <initial-install-p>f</initial-install-p>
     <singleton-p>f</singleton-p>
     
-    <version name="1.2b30" url="http://openacs.org/repository/download/apm/contacts-1.2b30.apm">
+    <version name="1.2b31" url="http://openacs.org/repository/download/apm/contacts-1.2b31.apm">
         <owner url="mailto:openacs@geddert.com">Matthew Geddert</owner>
         <summary>This application lets you collaboratively view, edit and categorize contacts.</summary>
         <release-date>2006-06-25</release-date>
         <description format="text/plain">Contacts is an application for managing all those people and or organization you need to keep track of. It has a complete UI for storing and categorizing contacts. Each contact can have an arbitrary number of custom attributes associated with it, including other contacts (i.e. a certain contact &quot;belongs&quot; to a certain organization). It also functions as a service contract provider for attributes related to users in your system</description>
         <maturity>0</maturity>
 
-        <provides url="contacts" version="1.2b30"/>
+        <provides url="contacts" version="1.2b31"/>
         <requires url="acs-datetime" version="4.1"/>
         <requires url="acs-events" version="0.5d3"/>
         <requires url="acs-tcl" version="5.3.0d2"/>
@@ -24,6 +24,7 @@
         <requires url="categories" version="1.0d6"/>
         <requires url="general-comments" version="4.1"/>
         <requires url="mail-tracking" version="0.6.4"/>
+        <requires url="notifications" version="5.4.0d3"/>
         <requires url="organizations" version="0.6d1"/>
         <requires url="ajaxhelper" version="0.82d"/>
 
Index: openacs-4/packages/contacts/catalog/contacts.en_US.ISO-8859-1.xml
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/catalog/contacts.en_US.ISO-8859-1.xml,v
diff -u -r1.98 -r1.99
--- openacs-4/packages/contacts/catalog/contacts.en_US.ISO-8859-1.xml	27 Jun 2007 02:53:30 -0000	1.98
+++ openacs-4/packages/contacts/catalog/contacts.en_US.ISO-8859-1.xml	27 Jun 2007 23:55:36 -0000	1.99
@@ -513,9 +513,10 @@
   <msg key="lt_zippostal_does_not_st">zip/postal does not start with -&gt;</msg>
   <msg key="lt_zippostal_starts_with">zip/postal starts with -&gt;</msg>
   <msg key="Mail">Mail</msg>
-  <msg key="Mail_recipient">Message to %recipient%</msg>
+  <msg key="Mail_group">Mail %recipient%</msg>
   <msg key="Mail_Merge">Mail Merge</msg>
   <msg key="Mail_Merge_Results">Mail Merge Results</msg>
+  <msg key="Mail_recipient">Mail %recipient%</msg>
   <msg key="Mail_through_p">Mail Through</msg>
   <msg key="Mail_to_contact">Mail to contact</msg>
   <msg key="mailing_allowed">mailing allowed</msg>
@@ -551,6 +552,8 @@
   <msg key="Normal_Searches">Normal Searches</msg>
   <msg key="Not_Configured">Not Configured</msg>
   <msg key="not_in_the_search">not in the search</msg>
+  <msg key="Notify">Notify</msg>
+  <msg key="Notify_recipient">Notify %recipient%</msg>
   <msg key="Number_of_Rows">Number of Rows</msg>
   <msg key="Object">Object:</msg>
   <msg key="Object_id">Object_id</msg>
@@ -626,6 +629,7 @@
   <msg key="Query">Query</msg>
   <msg key="READ_THESE">READ THESE</msg>
   <msg key="Recipients">Recipients</msg>
+  <msg key="Recipients_requesting_notification">%recipients% requesting notification</msg>
   <msg key="Refund">Refund</msg>
   <msg key="rel_bulk_add_confirm">Confirm deleting relationships</msg>
   <msg key="Related_contact">Related contact</msg>
Index: openacs-4/packages/contacts/lib/email-search.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/lib/email-search.tcl,v
diff -u -r1.1 -r1.2
--- openacs-4/packages/contacts/lib/email-search.tcl	25 Jun 2007 16:25:39 -0000	1.1
+++ openacs-4/packages/contacts/lib/email-search.tcl	27 Jun 2007 23:55:36 -0000	1.2
@@ -25,6 +25,18 @@
     set no_callback_p f
 }
 
+if { [contact::group::notifications_p -group_id $search_id] } {
+    set recipients_label [_ contacts.Notify]
+    # we cannot do interpolation with notifications since
+    # only one notification is generated for all recipients
+    set content_body_help_text ""
+} else {
+    set recipients_label [_ contacts.Recipients]
+    # since each message is dealt with individually we can
+    # do interpolation
+    set content_body_help_text [_ contacts.lt_remember_that_you_can]
+}
+
 # The element check_uncheck only calls a javascript function
 # to check or uncheck all recipients
 set form_elements {
@@ -34,21 +46,36 @@
     title:text(hidden),optional
     search_id:text(hidden)
     {message_type:text(hidden) {value "email"}}
-    {-section "sec1" {legendtext "[_ contacts.Recipients]"}}
+    {-section "sec1" {legendtext "$recipients_label"}}
     {recipients:text(inform)
-	{label "[_ contacts.Recipients]"} 
+	{label "$recipients_label"} 
 	{value "$recipients"}
     }
-    {cc:text(text),optional
-	{label "[_ contacts.CC]:"} 
-	{html {size 60}}
-	{help_text "[_ contacts.cc_help]"}
+}
+
+if { [contact::group::notifications_p -group_id $search_id] } {
+
+    # CC and BCC are not avalable for notifications
+    append form_elements {
+	{cc:text(hidden),optional}
+	{bcc:text(hidden),optional}
     }
-    {bcc:text(text),optional
-	{label "[_ acs-mail-lite.BCC]:"} 
-	{html {size 60}}
-	{help_text "[_ contacts.cc_help]"}
+
+} else {
+
+    append form_elements {
+	{cc:text(text),optional
+	    {label "[_ contacts.CC]:"} 
+	    {html {size 60}}
+	    {help_text "[_ contacts.cc_help]"}
+	}
+	{bcc:text(text),optional
+	    {label "[_ acs-mail-lite.BCC]:"} 
+	    {html {size 60}}
+	    {help_text "[_ contacts.cc_help]"}
+	}
     }
+
 }
 
 # Set single_email_p in the form
@@ -135,7 +162,7 @@
     {content_body:richtext(richtext),optional
 	{label "[_ contacts.Message]"}
 	{html {cols 80 rows 18}}
-	{help_text "[_ contacts.lt_remember_that_you_can]"}
+	{help_text "$content_body_help_text"}
 	{value $content_list}
     }
     {upload_file:file(file),optional
@@ -199,84 +226,7 @@
     } -on_submit {
 
 	set package_id [ad_conn package_id]
-	
-	# Make sure we get the correct users and can send an e-mail to them
-	if {[contact::group::mapped_p -group_id $search_id]} {
-	    
-	    # Make sure the user has write permission on the group
-	    permission::require_permission -object_id $search_id -privilege "write"
-	    
-	    # Get the party_ids from the group members
-	    if { [contact::group::mapped_p -group_id $search_id] } {
-		set valid_party_ids [group::get_members -group_id $search_id]
-	    }
-	} else {
-	    set valid_party_ids [contact::search::results -search_id $search_id -package_id $package_id]
-	}
-	
-	foreach party_id $valid_party_ids {
-	    if { [party::email -party_id $party_id] eq "" } {
-		# We are going to check if there is an employee relationship
-		# if there is we are going to check if the employer has an
-		# email adrres, if it does we are going to use that address
-		set employer_id [lindex [contact::util::get_employee_organization -employee_id $party_id] 0]
-		
-		if { ![empty_string_p $employer_id] } {
-		    set emp_addr [contact::email -party_id $employer_id]
-		    if { ![empty_string_p $emp_addr] } {
-			lappend party_ids $employer_id
-		    } else {
-			lappend invalid_party_ids $party_id
-		    }
-		} else {
-		    lappend invalid_party_ids $party_id
-		}
-	    } else {
-		lappend party_ids $party_id
-	    } 
-	}
 
-	# Deal with the invalid recipients
-	foreach party_id $invalid_party_ids {
-	    set contact_name   [contact::name -party_id $party_id]
-	    set contact_url    [contact::url -party_id $party_id]
-	    lappend invalid_recipients   "<a href=\"${contact_url}\">${contact_name}</a>"
-	}
-	
-	set invalid_recipients [join $invalid_recipients ", "]
-	if { [llength $invalid_recipients] > 0 } {
-	    switch $message_type {
-		letter {
-		    set error_message [_ contacts.lt_You_cannot_send_a_letter_to_invalid_recipients]
-		}
-		email {
-		    set error_message [_ contacts.lt_You_cannot_send_an_email_to_invalid_recipients]
-		}
-		default {
-		    set error_message [_ contacts.lt_You_cannot_send_a_message_to_invalid_recipients]
-		}
-	    }
-	    if { $party_ids != "" } {
-		util_user_message -html -message $error_message
-	    }
-	}
-	
-	# We get the attribute_id of the salutation attribute
-	set attribute_id [attribute::id -object_type "person" -attribute_name "salutation"]
-	    
-	# List to store know wich emails recieved the message
-	set recipients_addr [list]
-
-	set from [ad_conn user_id]
-	set from_addr [contact::email -party_id $from]
-
-	# Remove all spaces in cc and bcc
-	regsub -all " " $cc "" cc
-	regsub -all " " $bcc "" bcc
-
-	set cc_list [split $cc ";"]
-	set bcc_list [split $bcc ";"]
-
 	set mime_type [template::util::richtext::get_property format $content_body]
 	set content_body [template::util::richtext::get_property contents $content_body]
 
@@ -297,100 +247,198 @@
 		lappend file_ids $file_id
 	    }
 	}
+
+
+
 	
-	
-	# Send the mail to all parties.
-	set member_size [llength $party_ids]
-	set counter 1
-	foreach party_id $party_ids  {
 
-	    # Differentiate between person and organization
-	    if {[person::person_p -party_id $party_id]} {
-		set salutation [contact::salutation -party_id $party_id]
-		db_1row names "select first_names, last_name from persons where person_id = :party_id"
-		set name "$first_names $last_name"
+	if { [contact::group::notifications_p -group_id $search_id] } {
+
+	    permission::require_permission -object_id $search_id -privilege "write"		
+
+	    notification::new \
+		-type_id [notification::type::get_type_id -short_name contacts_group_notif] \
+		-object_id $search_id \
+		-notif_subject $subject \
+		-notif_text [ad_html_text_convert -from $mime_type -to text/plain -- $content_body] \
+		-notif_html [ad_html_text_convert -from $mime_type -to text/html -- $content_body] \
+		-file_ids $file_ids
+
+	} else {
+	
+	
+	    # Make sure we get the correct users and can send an e-mail to them
+	    if {[contact::group::mapped_p -group_id $search_id]} {
+		
+		# Make sure the user has write permission on the group
+		permission::require_permission -object_id $search_id -privilege "write"		
+		set valid_party_ids [group::get_members -group_id $search_id]
 	    } else {
-		set name [contact::name -party_id $party_id]
-		set salutation "Dear ladies and gentlemen"
-		# the following is a hot fix (nfl 2006/10/20)
-		set first_names ""
-		set last_name ""
+		set valid_party_ids [contact::search::results -search_id $search_id -package_id $package_id]
 	    }
-
 	    
-	    set date [lc_time_fmt [dt_sysdate] "%q"]
+	    set party_ids [list]
+	    set invalid_party_ids [list]
+	    set invalid_recipients [list]
+	    foreach party_id $valid_party_ids {
+		if { [party::email -party_id $party_id] eq "" } {
+		    # We are going to check if there is an employee relationship
+		    # if there is we are going to check if the employer has an
+		    # email adrres, if it does we are going to use that address
+		    set employer_id [lindex [contact::util::get_employee_organization -employee_id $party_id] 0]
+		    
+		    if { ![empty_string_p $employer_id] } {
+			set emp_addr [contact::email -party_id $employer_id]
+			if { ![empty_string_p $emp_addr] } {
+			    lappend party_ids $employer_id
+			} else {
+			    lappend invalid_party_ids $party_id
+			}
+		    } else {
+			lappend invalid_party_ids $party_id
+		    }
+		} else {
+		    lappend party_ids $party_id
+		} 
+	    }
 	    
-	    set values [list]
-	    foreach element [list first_names last_name salutation name date] {
-		lappend values [list "{$element}" [set $element]]
+	    # Deal with the invalid recipients
+	    foreach party_id $invalid_party_ids {
+		set contact_name   [contact::name -party_id $party_id]
+		set contact_url    [contact::url -party_id $party_id]
+		lappend invalid_recipients   "<a href=\"${contact_url}\">${contact_name}</a>"
 	    }
 	    
-	    set interpol_subject [contact::message::interpolate -text $subject -values $values]
-
-	    set interpol_content_body [contact::message::interpolate -text $content_body -values $values]
-	    
-	    # If we are doing mail through for tracking purposes
-	    # Set the reply_to_addr accordingly
-	    if {$mail_through_p} {
-		regsub -all {@} $from_addr {#} reply_to
-		set reply_to_addr "${reply_to}@[acs_mail_lite::address_domain]"
-	    } else {
-		set reply_to_addr $from_addr
+	    set invalid_recipients [join $invalid_recipients ", "]
+	    if { [llength $invalid_recipients] > 0 } {
+		switch $message_type {
+		    letter {
+			set error_message [_ contacts.lt_You_cannot_send_a_letter_to_invalid_recipients]
+		    }
+		    email {
+			set error_message [_ contacts.lt_You_cannot_send_an_email_to_invalid_recipients]
+		    }
+		    default {
+			set error_message [_ contacts.lt_You_cannot_send_a_message_to_invalid_recipients]
+		    }
+		}
+		if { $party_ids != "" } {
+		    util_user_message -html -message $error_message
+		}
 	    }
-
-	    ns_log Notice "Recipient: $name $party_id ( $counter / $member_size )"
-	    incr counter
-
-	    acs_mail_lite::complex_send \
-		-to_party_ids $party_id \
-		-cc_addr $cc_list \
-		-bcc_addr $bcc_list \
-		-from_addr "$from_addr" \
-		-reply_to "$reply_to_addr" \
-		-subject "$interpol_subject" \
-		-body "$interpol_content_body" \
-		-package_id $package_id \
-		-file_ids $file_ids \
-		-mime_type $mime_type \
-		-object_id $context_id \
-		-no_callback_p $no_callback_p \
-		-single_email
 	    
-	    # Link the files to all parties
-	    if {[exists_and_not_null revision_id]} {
-		application_data_link::new -this_object_id $revision_id -target_object_id $party_id
+	    # We get the attribute_id of the salutation attribute
+	    set attribute_id [attribute::id -object_type "person" -attribute_name "salutation"]
+	    
+	    # List to store know wich emails recieved the message
+	    set recipients_addr [list]
+	    
+	    set from [ad_conn user_id]
+	    set from_addr [contact::email -party_id $from]
+	    
+	    # Remove all spaces in cc and bcc
+	    regsub -all " " $cc "" cc
+	    regsub -all " " $bcc "" bcc
+	    
+	    set cc_list [split $cc ";"]
+	    set bcc_list [split $bcc ";"]
+	    
+	    # Send the mail to all parties.
+	    set member_size [llength $party_ids]
+	    set counter 1
+
+	    foreach party_id $party_ids  {
+		
+		# Differentiate between person and organization
+		if {[person::person_p -party_id $party_id]} {
+		    set salutation [contact::salutation -party_id $party_id]
+		    db_1row names "select first_names, last_name from persons where person_id = :party_id"
+		    set name "$first_names $last_name"
+		} else {
+		    set name [contact::name -party_id $party_id]
+		    set salutation "Dear ladies and gentlemen"
+		    # the following is a hot fix (nfl 2006/10/20)
+		    set first_names ""
+		    set last_name ""
+		}
+		
+		
+		set date [lc_time_fmt [dt_sysdate] "%q"]
+		
+		set values [list]
+		foreach element [list first_names last_name salutation name date] {
+		    lappend values [list "{$element}" [set $element]]
+		}
+		
+		set interpol_subject [contact::message::interpolate -text $subject -values $values]
+		
+		set interpol_content_body [contact::message::interpolate -text $content_body -values $values]
+		
+		# If we are doing mail through for tracking purposes
+		# Set the reply_to_addr accordingly
+		if { [string is true $mail_through_p] } {
+		    regsub -all {@} $from_addr {#} reply_to
+		    set reply_to_addr "${reply_to}@[acs_mail_lite::address_domain]"
+		} else {
+		    set reply_to_addr $from_addr
+		}
+		      
+		ns_log Notice "Recipient: $name $party_id ( $counter / $member_size )"
+		incr counter
+		
+		acs_mail_lite::complex_send \
+		    -to_party_ids $party_id \
+		    -cc_addr $cc_list \
+		    -bcc_addr $bcc_list \
+		    -from_addr "$from_addr" \
+		    -reply_to "$reply_to_addr" \
+		    -subject "$interpol_subject" \
+		    -body "$interpol_content_body" \
+		    -package_id $package_id \
+		    -file_ids $file_ids \
+		    -mime_type $mime_type \
+		    -object_id $context_id \
+		    -no_callback_p $no_callback_p \
+		    -single_email
+		
+		# Link the files to all parties
+		if {[exists_and_not_null revision_id]} {
+		    application_data_link::new -this_object_id $revision_id -target_object_id $party_id
+		}
+		
+		# Log the sending of the mail in contacts history
+		if { ![empty_string_p $item_id]} {
+		    
+		    contact::message::log \
+			-message_type "email" \
+			-sender_id $from \
+			-recipient_id $party_id \
+			-title $title \
+			-description $subject \
+			-content $content_body \
+			-content_format "text/plain" \
+			-item_id "$item_id"
+			
+		}
 	    }
 		
-	    # Log the sending of the mail in contacts history
-	    if { ![empty_string_p $item_id]} {
 		
-		contact::message::log \
-		    -message_type "email" \
-		    -sender_id $from \
-		    -recipient_id $party_id \
-		    -title $title \
-		    -description $subject \
-		    -content $content_body \
-		    -content_format "text/plain" \
-		    -item_id "$item_id"
 		
-	    } 
-	}
-
-	ad_returnredirect $return_url
 	
-	# Prepare the user message
-	foreach cc_addr [concat $cc_list $bcc_list] {
-	    set cc_id [party::get_by_email -email $cc_addr]
-	    if {$cc_id eq ""} {
-		lappend recipients $cc_addr
-	    } else {
-		lappend recipients "<a href=\"[contact::url -party_id $cc_id]\">[contact::name -party_id $cc_id]</a>"
+	    # Prepare the user message
+	    foreach cc_addr [concat $cc_list $bcc_list] {
+		set cc_id [party::get_by_email -email $cc_addr]
+		if {$cc_id eq ""} {
+		    lappend recipients $cc_addr
+		} else {
+		    lappend recipients "<a href=\"[contact::url -party_id $cc_id]\">[contact::name -party_id $cc_id]</a>"
+		}
 	    }
-	}
-        util_user_message -html -message "[_ contacts.Your_message_was_sent_to_-recipients-]"
+	    util_user_message -html -message "[_ contacts.Your_message_was_sent_to_-recipients-]"
 
+	}
     } -after_submit {
+	ad_returnredirect $return_url
 	ad_script_abort
     }
 
Index: openacs-4/packages/contacts/sql/postgresql/contacts-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/sql/postgresql/contacts-create.sql,v
diff -u -r1.18 -r1.19
--- openacs-4/packages/contacts/sql/postgresql/contacts-create.sql	10 Jan 2007 07:16:25 -0000	1.18
+++ openacs-4/packages/contacts/sql/postgresql/contacts-create.sql	27 Jun 2007 23:55:36 -0000	1.19
@@ -65,6 +65,8 @@
                                 constraint contact_groups_default_p_nn not null,
         user_change_p           boolean default 'f'
                                 constraint contact_groups_user_change_p_nn not null,
+        notifications_p         boolean default 'f'
+                                constraint contact_groups_notifications_p_nn not null,
         package_id              integer
                                 constraint contact_groups_package_id_fk references apm_packages(package_id)
                                 constraint contact_groups_package_id_nn not null,
@@ -233,3 +235,4 @@
 \i contacts-search-create.sql
 \i contacts-messages-create.sql
 \i contacts-list-create.sql
+\i groups-notifications-init.sql
Index: openacs-4/packages/contacts/sql/postgresql/groups-notifications-init.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/sql/postgresql/groups-notifications-init.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/contacts/sql/postgresql/groups-notifications-init.sql	27 Jun 2007 23:55:36 -0000	1.1
@@ -0,0 +1,69 @@
+-- Contact Group Notifications
+--
+-- @author Matthew Geddert (openacs@geddert.com)
+-- @creation-date 2007-06-25
+-- @cvs-id $Id: groups-notifications-init.sql,v 1.1 2007/06/27 23:55:36 matthewg Exp $
+--
+
+create function inline_0() returns integer as '
+declare
+        impl_id integer;
+        v_foo   integer;
+begin
+        -- the notification type impl
+        impl_id := acs_sc_impl__new (
+                      ''NotificationType'',
+                      ''contacts_group_notif_type'',
+                      ''contacts''
+        );
+
+        v_foo := acs_sc_impl_alias__new (
+                    ''NotificationType'',
+                    ''contacts_group_notif_type'',
+                    ''GetURL'',
+                    ''contacts::group::notification::get_url'',
+                    ''TCL''
+        );
+
+        v_foo := acs_sc_impl_alias__new (
+                    ''NotificationType'',
+                    ''contacts_group_notif_type'',
+                    ''ProcessReply'',
+                    ''contacts::group::notification::process_reply'',
+                    ''TCL''
+        );
+
+        PERFORM acs_sc_binding__new (
+                    ''NotificationType'',
+                    ''contacts_group_notif_type''
+        );
+
+        v_foo:= notification_type__new (
+	        NULL,
+                impl_id,
+                ''contacts_group_notif'',
+                ''Group Notification'',
+                ''Notifications for Groups'',
+		now(),
+                NULL,
+                NULL,
+		NULL
+        );
+
+        -- enable the various intervals and delivery methods
+        insert into notification_types_intervals
+        (type_id, interval_id)
+        select v_foo, interval_id
+        from notification_intervals where name in (''instant'',''hourly'',''daily'');
+
+        insert into notification_types_del_methods
+        (type_id, delivery_method_id)
+        select v_foo, delivery_method_id
+        from notification_delivery_methods where short_name in (''email'');
+
+	return (0);
+end;
+' language 'plpgsql';
+
+select inline_0();
+drop function inline_0();
Index: openacs-4/packages/contacts/sql/postgresql/upgrade/upgrade-1.2b30-1.2b31.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/sql/postgresql/upgrade/upgrade-1.2b30-1.2b31.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/contacts/sql/postgresql/upgrade/upgrade-1.2b30-1.2b31.sql	27 Jun 2007 23:55:36 -0000	1.1
@@ -0,0 +1,76 @@
+-- 
+-- packages/contacts/sql/postgresql/upgrade/upgrade-1.2b30-1.2b31.sql
+-- 
+-- @author Matthew Geddert (openacs@geddert.com)
+-- @creation-date 2007-06-25
+-- @arch-tag: 
+-- @cvs-id $Id: upgrade-1.2b30-1.2b31.sql,v 1.1 2007/06/27 23:55:36 matthewg Exp $
+--
+
+alter table contact_groups add column notifications_p boolean;
+alter table contact_groups alter column notifications_p set default 'f';
+update contact_groups set notifications_p = 'f';
+alter table contact_groups alter column notifications_p set not null;
+
+create function inline_0() returns integer as '
+declare
+        impl_id integer;
+        v_foo   integer;
+begin
+        -- the notification type impl
+        impl_id := acs_sc_impl__new (
+                      ''NotificationType'',
+                      ''contacts_group_notif_type'',
+                      ''contacts''
+        );
+
+        v_foo := acs_sc_impl_alias__new (
+                    ''NotificationType'',
+                    ''contacts_group_notif_type'',
+                    ''GetURL'',
+                    ''contacts::group::notification::get_url'',
+                    ''TCL''
+        );
+
+        v_foo := acs_sc_impl_alias__new (
+                    ''NotificationType'',
+                    ''contacts_group_notif_type'',
+                    ''ProcessReply'',
+                    ''contacts::group::notification::process_reply'',
+                    ''TCL''
+        );
+
+        PERFORM acs_sc_binding__new (
+                    ''NotificationType'',
+                    ''contacts_group_notif_type''
+        );
+
+        v_foo:= notification_type__new (
+	        NULL,
+                impl_id,
+                ''contacts_group_notif'',
+                ''Group Notification'',
+                ''Notifications for Groups'',
+		now(),
+                NULL,
+                NULL,
+		NULL
+        );
+
+        -- enable the various intervals and delivery methods
+        insert into notification_types_intervals
+        (type_id, interval_id)
+        select v_foo, interval_id
+        from notification_intervals where name in (''instant'',''hourly'',''daily'');
+
+        insert into notification_types_del_methods
+        (type_id, delivery_method_id)
+        select v_foo, delivery_method_id
+        from notification_delivery_methods where short_name in (''email'');
+
+	return (0);
+end;
+' language 'plpgsql';
+
+select inline_0();
+drop function inline_0();
Index: openacs-4/packages/contacts/tcl/contacts-procs-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/tcl/contacts-procs-postgresql.xql,v
diff -u -r1.28 -r1.29
--- openacs-4/packages/contacts/tcl/contacts-procs-postgresql.xql	24 Jun 2007 08:50:16 -0000	1.28
+++ openacs-4/packages/contacts/tcl/contacts-procs-postgresql.xql	27 Jun 2007 23:55:36 -0000	1.29
@@ -197,14 +197,16 @@
            CASE WHEN contact_groups.package_id is not null THEN '1' ELSE '0' END as mapped_p,
            CASE WHEN default_p THEN '1' ELSE '0' END as default_p,
            CASE WHEN user_change_p THEN '1' ELSE '0' END as user_change_p,
-           $dotlrn_community_p as dotlrn_community_p
-      from (select g.* from groups g left join application_groups ag on (ag.group_id = g.group_id) 
-		where (package_id is null or g.group_id = 1231) and group_name not like 'forumgroup_%') groups2 
-		left join ( select * from contact_groups where package_id = :package_id ) as contact_groups on ( groups2.group_id = contact_groups.group_id ), 
-		acs_objects
+           $dotlrn_community_p as dotlrn_community_p,
+           CASE WHEN contact_groups.notifications_p THEN '1' ELSE '0' END as notifications_p
+      from ( select g.*
+               from groups g left join application_groups ag on (ag.group_id = g.group_id) 
+              where package_id is null ) groups2 
+           left join ( select * from contact_groups where package_id = :package_id ) as contact_groups on ( groups2.group_id = contact_groups.group_id ), 
+           acs_objects
       $additional_from
      where groups2.group_id not in ('-1','[contacts::default_group -package_id $package_id]')
-	and groups2.group_id = acs_objects.object_id
+       and groups2.group_id = acs_objects.object_id
        and groups2.group_id not in ( select gcm.component_id from group_component_map gcm where gcm.group_id != -1 )
       $additional_where
      order by mapped_p desc, CASE WHEN contact_groups.default_p THEN '000000000' ELSE upper( $name_field ) END
@@ -254,9 +256,9 @@
 <fullquery name="contact::group::map.map_group">
   <querytext>
         insert into contact_groups
-        (group_id,default_p,package_id)
+        (group_id,default_p,notification_p,package_id)
         values
-        (:group_id,:default_p,:package_id)
+        (:group_id,:default_p,:notification_p,:package_id)
   </querytext>
 </fullquery>
 
@@ -268,6 +270,15 @@
   </querytext>
 </fullquery>
 
+<fullquery name="contact::group::notifications_p.select_notifications_p">
+  <querytext>
+	select 1 from contact_groups
+         where group_id = :group_id
+           and notifications_p
+         limit 1
+  </querytext>
+</fullquery>
+
 <fullquery name="contact::revision::new.insert_item">
   <querytext>
          insert into cr_items
Index: openacs-4/packages/contacts/tcl/contacts-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/tcl/contacts-procs.tcl,v
diff -u -r1.114 -r1.115
--- openacs-4/packages/contacts/tcl/contacts-procs.tcl	26 Jun 2007 03:52:13 -0000	1.114
+++ openacs-4/packages/contacts/tcl/contacts-procs.tcl	27 Jun 2007 23:55:36 -0000	1.115
@@ -15,7 +15,24 @@
 namespace eval contact::rels:: {}
 namespace eval contacts::person:: {}
 namespace eval contact::special_attributes {}
+namespace eval contacts::group::notification {}
 
+ad_proc -public contacts::group::notification::get_url {
+    object_id
+} {    
+    # there is not good page to send users regarding a group
+    # so we don't bring them anywhere
+    return "/notifications/manage"
+}
+
+ad_proc -public contacts::group::notification::process_reply {
+    reply_id
+} {
+
+}
+
+
+
 ad_proc -public contacts::default_group {
     {-package_id ""}
 } {
@@ -926,6 +943,7 @@
     -group_id:required
     {-package_id ""}
     {-default_p "f"}
+    {-notifications_p "f"}
 } {
     this creates a new group for use with contacts (and the permissions system)
 } {
@@ -947,6 +965,14 @@
     return [db_0or1row select_mapped_p {}]
 }
 
+ad_proc -public contact::group::notifications_p {
+    -group_id:required
+} {
+    Does this group use notifications (if one contacts instance does then all do, since the group is not bound to the contacts instance)
+} {
+    return [db_0or1row select_notifications_p {}]
+}
+
 ad_proc -public contact::group::name {
     -group_id:required
 } {
@@ -1053,6 +1079,31 @@
     }    
     group::flush_members_cache -group_id $group_id
 
+    if { [contact::group::notifications_p -group_id $group_id] && [contact::type -party_id $user_id] ne "organization" } {
+	if { [contact::type -party_id $user_id] ne "user" } {
+	    util_user_message -message "Only users can be notified. The person ($user_id) was added to the group."
+	}
+	# notifications only allows users to receive notifications.
+	# this actually makes sense, since the recipient needs
+	# a way to remove themselves from the notifications
+	# they are getting.
+	#
+	# We could potentially automatically upgrade a person
+	# to a user user if a notification request is made.
+	# to do this uncomment the following
+	# contact::person_upgrade_to_user -person_id $user_id -no_perm_check "t"
+
+	
+
+	notification::request::new \
+	    -type_id [notification::type::get_type_id -short_name contacts_group_notif] \
+	    -user_id $user_id \
+	    -object_id $group_id \
+	    -interval_id [notification::get_interval_id -name instant] \
+	    -delivery_method_id [notification::get_delivery_method_id -name email] \
+	    -format "html"
+    }
+
 }
 
 
@@ -1124,7 +1175,7 @@
     set user_id [ad_conn user_id]
     set group_list [list]
     foreach one_group [contact::groups_list -package_id $package_id -include_dotlrn_p $include_dotlrn_p] {
-	util_unlist $one_group group_id group_name member_count component_count mapped_p default_p user_change_p dotlrn_community_p
+	util_unlist $one_group group_id group_name member_count component_count mapped_p default_p user_change_p dotlrn_community_p notifications_p
 	if {$user_change_p eq ""} {
 	    set user_change_p 0
 	}
@@ -1143,11 +1194,11 @@
 	}
         if { $mapped_p || $all_p} {
 	    # we localize twice because for some reason some localized keys references another localized key
-            lappend group_list [list [lang::util::localize [lang::util::localize $group_name]] $group_id $member_count "1" $mapped_p $default_p $user_change_p $dotlrn_community_p]
+            lappend group_list [list [lang::util::localize [lang::util::localize $group_name]] $group_id $member_count "1" $mapped_p $default_p $user_change_p $dotlrn_community_p $notifications_p]
             if { $component_count > 0 && ( $expand == "all" || $expand == $group_id ) } {
                 db_foreach get_components {} {
 		    if { $mapped_p || $all_p} {
-			lappend group_list [list "$indent_with[lang::util::localize [lang::util::localize $group_name]]" $group_id $member_count "2" $mapped_p $default_p $user_change_p $dotlrn_community_p]
+			lappend group_list [list "$indent_with[lang::util::localize [lang::util::localize $group_name]]" $group_id $member_count "2" $mapped_p $default_p $user_change_p $dotlrn_community_p $notifications_p]
 		    }
 		}
             }
Index: openacs-4/packages/contacts/www/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/www/index.tcl,v
diff -u -r1.31 -r1.32
--- openacs-4/packages/contacts/www/index.tcl	27 Jun 2007 02:51:40 -0000	1.31
+++ openacs-4/packages/contacts/www/index.tcl	27 Jun 2007 23:55:36 -0000	1.32
@@ -119,11 +119,17 @@
 if { $search_id ne "" && $contacts_total_count > 0 } {
     if {[contact::group::mapped_p -group_id $search_id]} {
 	set recipient [contact::group::name -group_id $search_id]
+	set notifications_p [contact::group::notifications_p -group_id $search_id]
     } else {
 	set recipient [contact::search::title -search_id $search_id]
+	set notifications_p 0
     }
-    set label "[_ contacts.Mail_recipient]"
 
+    if { $notifications_p } {
+	set label [_ contacts.Notify_recipient]
+    } else {
+	set label [_ contacts.Mail_recipient]
+    }
     append form_elements {
 	{mail_merge_group:text(submit) {label $label} {value "1"}}
     }
Index: openacs-4/packages/contacts/www/message.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/www/message.tcl,v
diff -u -r1.40 -r1.41
--- openacs-4/packages/contacts/www/message.tcl	27 Jun 2007 02:52:33 -0000	1.40
+++ openacs-4/packages/contacts/www/message.tcl	27 Jun 2007 23:55:36 -0000	1.41
@@ -48,13 +48,17 @@
 set package_id [ad_conn package_id]
 set recipients  [list]
 
+set recipients_label [_ contacts.Recipients]
 if { $search_id ne "" } {
 
     set return_url [export_vars -base [apm_package_url_from_id $package_id] -url {search_id}]
     if {[contact::group::mapped_p -group_id $search_id]} {
 	# Make sure the user has write permission on the group
 	permission::require_permission -object_id $search_id -privilege "write"
 	lappend recipients "<a href=\"$return_url\">[contact::group::name -group_id $search_id]</a>"
+	if { [contact::group::notifications_p -group_id $search_id] } {
+	    set recipients_label [_ contacts.Notify]
+	}
     } else {
 	lappend recipients "<a href=\"$return_url\">[contact::search::title -search_id $search_id]</a>"
     }
@@ -215,7 +219,7 @@
     folder_id:text(hidden)
     object_id:text(hidden)
     context_id:text(hidden)
-    {to_name:text(inform),optional {label "[_ contacts.Recipients]"} {value $recipients}}
+    {to_name:text(inform),optional {label "$recipients_label"} {value $recipients}}
 }
 
 if { ![exists_and_not_null message_type] } {
Index: openacs-4/packages/contacts/www/admin/group-map.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/www/admin/group-map.tcl,v
diff -u -r1.5 -r1.6
--- openacs-4/packages/contacts/www/admin/group-map.tcl	21 Jun 2007 20:06:42 -0000	1.5
+++ openacs-4/packages/contacts/www/admin/group-map.tcl	27 Jun 2007 23:55:37 -0000	1.6
@@ -11,7 +11,7 @@
     {return_url "./"}
 } -validate {
     action_valid -requires {action} {
-        if { [lsearch [list map unmap makedefault] $action] < 0 } {
+        if { [lsearch [list map unmap makedefault notificationson notificationsoff] $action] < 0 } {
             ad_complain "[_ contacts.lt_the_action_supplied_i]"
         }
     }
@@ -82,6 +82,16 @@
             update contact_groups set default_p = 't' where package_id = :package_id and group_id = :group_id
         }
     }
+    notificationson {
+	db_dml notificationson {
+	    update contact_groups set notifications_p = 't' where package_id = :package_id and group_id = :group_id
+	}
+    }
+    notificationsoff {
+	db_dml notificationsoff {
+	    update contact_groups set notifications_p = 'f' where package_id = :package_id and group_id = :group_id
+	}
+    }
 }
 
 
Index: openacs-4/packages/contacts/www/admin/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/contacts/www/admin/index.tcl,v
diff -u -r1.16 -r1.17
--- openacs-4/packages/contacts/www/admin/index.tcl	25 Jun 2007 16:25:39 -0000	1.16
+++ openacs-4/packages/contacts/www/admin/index.tcl	27 Jun 2007 23:55:37 -0000	1.17
@@ -64,6 +64,19 @@
                 </if>
             }
         }
+        notifications {
+            label {Notifications}
+            display_template {
+                <if @groups.dotlrn_community_p@ false and @groups.mapped_p@ and @groups.level@ eq 1>
+                <if @groups.notifications_p@>
+                  <a href="group-map?action=notificationsoff&group_id=@groups.group_id@"><img src="/resources/acs-subsite/checkboxchecked.gif" border="0" height="13" width="13" alt="[_ contacts.True]"></a>
+                </if>
+                <else>
+                  <a href="group-map?action=notificationson&group_id=@groups.group_id@"><img src="/resources/acs-subsite/checkbox.gif" border="0" height="13" width="13" alt="[_ contacts.False]"></a>
+                </else>
+                </if>
+            }
+        }
         user_change {
             label {User Change}
             display_template {
@@ -113,18 +126,12 @@
     }
 
 
-multirow create groups group_id group_name group_url ams_person_url ams_org_url member_count level mapped_p default_p categories_url edit_url user_change_p dotlrn_community_p
+multirow create groups group_id group_name group_url ams_person_url ams_org_url member_count level mapped_p default_p categories_url edit_url user_change_p dotlrn_community_p notifications_p
 
 set return_url [ad_conn url]
 foreach group [contact::groups -indent_with "..." -expand "all" -output "all" -privilege_required "admin" -all -include_dotlrn_p "1"] {
-    set group_id [lindex $group 1]
-    set group_name [lindex $group 0]
-    set member_count [lindex $group 2]
-    set level [lindex $group 3]
-    set mapped_p [lindex $group 4]
-    set default_p [lindex $group 5]
-    set user_change_p [lindex $group 6]
-    set dotlrn_community_p [lindex $group 7]
+    util_unlist $group group_name group_id member_count level mapped_p default_p user_change_p dotlrn_community_p notifications_p
+
     set ams_person_url [ams::list::url \
                           -package_key "contacts" \
                           -object_type "person" \
@@ -144,7 +151,7 @@
     set edit_url [export_vars -base "/acs-lang/admin/edit-localized-message" {{package_key acs-translations} {locale "[ad_conn locale]"} {message_key "group_title_${group_id}"} {return_url [ad_return_url]}}]
 
     set categories_url [export_vars -base "/categories/cadmin/object-map" -url {{object_id $group_id}}]
-    multirow append groups [lindex $group 1] [lindex $group 0] "../?group_id=${group_id}" $ams_person_url $ams_org_url $member_count $level $mapped_p $default_p $categories_url $edit_url $user_change_p $dotlrn_community_p
+    multirow append groups [lindex $group 1] [lindex $group 0] "../?group_id=${group_id}" $ams_person_url $ams_org_url $member_count $level $mapped_p $default_p $categories_url $edit_url $user_change_p $dotlrn_community_p $notifications_p
 
 
 }