Merge org app email changes and access fix.
diff --git a/app/melange/logic/organization.py b/app/melange/logic/organization.py
index bba00f3..b080c26 100644
--- a/app/melange/logic/organization.py
+++ b/app/melange/logic/organization.py
@@ -149,13 +149,16 @@
 
 
 @ndb.transactional
-def setStatus(organization, program, site, new_status, recipients=None):
+def setStatus(organization, program, site, program_messages,
+              new_status, recipients=None):
   """Sets status of the specified organization.
 
   Args:
     organization: Organization entity.
     program: Program entity to which organization is assigned.
     site: Site entity.
+    program_messages: ProgramMessages entity that holds the message
+          templates provided by the program admins.
     new_status: New status of the organization. Must be one of
       org_model.Status constants.
     recipients: List of one or more recipients for the notification email.
@@ -172,11 +175,13 @@
       if new_status == org_model.Status.ACCEPTED:
         notification_context = (
             notifications.OrganizationAcceptedContextProvider()
-                .getContext(recipients, organization, program, site))
+                .getContext(recipients, organization, program,
+                            site, program_messages))
       elif new_status == org_model.Status.REJECTED:
         notification_context = (
             notifications.OrganizationRejectedContextProvider()
-                .getContext(recipients, organization, program, site))
+                .getContext(recipients, organization, program,
+                            site, program_messages))
 
       sub_txn = mailer.getSpawnMailTaskTxn(
           notification_context, parent=organization)
diff --git a/app/melange/mapreduce/apply_org_admission_decisions.py b/app/melange/mapreduce/apply_org_admission_decisions.py
index 0c9ef13..298fd77 100644
--- a/app/melange/mapreduce/apply_org_admission_decisions.py
+++ b/app/melange/mapreduce/apply_org_admission_decisions.py
@@ -21,6 +21,7 @@
 from mapreduce import context as mapreduce_context
 
 from melange.logic import organization as org_logic
+from melange.logic import profile as profile_logic
 from melange.models import organization as org_model
 
 from soc.logic import site as site_logic
@@ -45,6 +46,19 @@
   # TODO(daniel): add email recipients, i.e. organization admins
   site = site_logic.singleton()
 
+  org_admins = profile_logic.getOrgAdmins(organization.key)
+  recipients = [org_admin.contact.email for org_admin in org_admins]
+
+  # We are "prefetching" the ProgramMessages entity here instead of fetching
+  # it where it is required i.e. when the message templates are required
+  # to build the email message body. We do this because we perform the
+  # operation of fetching the ProgramMessages entity if it exists or create
+  # it if it doesn't in a Appengine regular "db" transation whereas rest
+  # of the updating of organization entities happen within an ndb transaction
+  # because Organization model is an ndb model and such cross API nested
+  # transactions are incompatible in Appengine.
+  program_messages = program.getProgramMessages()
+
   @ndb.transactional
   def updateOrganizationStatus():
     """Transactionally updates organization status."""
@@ -52,9 +66,11 @@
     if organization.program.to_old_key() == program_key:
       if organization.status == org_model.Status.PRE_ACCEPTED:
         org_logic.setStatus(
-            organization, program, site, org_model.Status.ACCEPTED)
+            organization, program, site, program_messages,
+            org_model.Status.ACCEPTED, recipients)
       elif organization.status == org_model.Status.PRE_REJECTED:
         org_logic.setStatus(
-            organization, program, site, org_model.Status.REJECTED)
+            organization, program, site, program_messages,
+            org_model.Status.REJECTED, recipients)
 
   updateOrganizationStatus()
diff --git a/app/soc/logic/helper/notifications.py b/app/soc/logic/helper/notifications.py
index 681d5e5..bf71b75 100644
--- a/app/soc/logic/helper/notifications.py
+++ b/app/soc/logic/helper/notifications.py
@@ -14,6 +14,7 @@
 
 """Helper functions for sending out notifications."""
 
+from django import template
 from django.template import loader
 from django.utils.translation import ugettext
 
@@ -39,10 +40,10 @@
     'New Google Summer of Code Connection')
 
 DEF_ACCEPTED_ORG = ugettext(
-    '[%s] Your organization application has been accepted.')
+    '[%(org)s] Your organization application has been accepted.')
 
 DEF_REJECTED_ORG = ugettext(
-    '[%s] Your organization application has been rejected.')
+    '[%(org)s] Your organization application has been rejected.')
 
 DEF_MENTOR_WELCOME_MAIL_SUBJECT = ugettext('Welcome to %s')
 
@@ -62,16 +63,12 @@
 DEF_NEW_ANONYMOUS_CONNECTION_NOTIFICATION_TEMPLATE = \
     'modules/gsoc/notification/anonymous_connection.html'
 
-DEF_ACCEPTED_ORG_TEMPLATE = \
-    'soc/notification/org_accepted.html'
-
-DEF_REJECTED_ORG_TEMPLATE = \
-    'soc/notification/org_rejected.html'
-
 DEF_MENTOR_WELCOME_MAIL_TEMPLATE = \
     'soc/notification/mentor_welcome_mail.html'
 
-def getContext(site, program, receivers, message_properties, subject, template):
+
+def _getContextCommon(site, program, receivers, message_properties,
+                      subject, body):
   """Sends out a notification to the specified user.
 
   Args:
@@ -80,7 +77,7 @@
     receivers: Email addresses to which the notification should be sent.
     message_properties: Message properties.
     subject: Subject of notification email.
-    template: Template used for generating notification.
+    body: Email body to be sent as notification.
   Returns:
     A dictionary containing the context for a message to be sent to one
     or more recipients.
@@ -88,8 +85,6 @@
   message_properties['sender_name'] = 'The %s Team' % site.site_name
   message_properties['program_name'] = program.name
 
-  body = loader.render_to_string(template, dictionary=message_properties)
-
   # TODO(nathaniel): "to" can be a list of email addresses or a single
   # email address? Is that right? The documentation of mailer.getMailContext
   # affords no answer.
@@ -103,6 +98,53 @@
   return mailer.getMailContext(to, subject, body, bcc=bcc)
 
 
+def getContextFromTemplateString(site, program, receivers,
+                                 message_properties, subject,
+                                 template_string):
+  """Sends out a notification to the specified user using the template
+  string to construct the notification body.
+
+  Args:
+    site: Site entity.
+    program: Program entity to which the notification applies.
+    receivers: Email addresses to which the notification should be sent.
+    message_properties: Message properties.
+    subject: Subject of notification email.
+    template_string: Template used for generating notification.
+  Returns:
+    A dictionary containing the context for a message to be sent to one
+    or more recipients.
+  """
+  template_inst = template.Template(template_string)
+  context_instance = template.Context(message_properties)
+  body = template_inst.render(context_instance)
+
+  return _getContextCommon(site, program, receivers, message_properties,
+                           subject, body)
+
+
+def getContext(site, program, receivers, message_properties,
+               subject, template):
+  """Sends out a notification to the specified user by using the template
+  file to construct the notification body.
+
+  Args:
+    site: Site entity.
+    program: Program entity to which the notification applies.
+    receivers: Email addresses to which the notification should be sent.
+    message_properties: Message properties.
+    subject: Subject of notification email.
+    template: Template used for generating notification.
+  Returns:
+    A dictionary containing the context for a message to be sent to one
+    or more recipients.
+  """
+  body = loader.render_to_string(template, dictionary=message_properties)
+
+  return _getContextCommon(site, program, receivers, message_properties,
+                           subject, body)
+
+
 def getDefaultContext(request_data, emails, subject, extra_context=None):
   """Generate a dictionary with a default context.
 
@@ -319,7 +361,7 @@
   is accepted into a program.
   """
 
-  def getContext(self, emails, organization, program, site):
+  def getContext(self, emails, organization, program, site, program_messages):
     """Provides notification context of an email to send out when the specified
     organization is accepted into the program.
 
@@ -328,18 +370,19 @@
       organization: Organization entity.
       program: Program entity.
       site: Site entity.
+      program_messages: ProgramMessages entity that holds the message
+          templates provided by the program admins.
     """
-    # TODO(daniel): replace with a dynamic mail content
-    # program_messages = program.getProgramMessages()
-    # template = program_messages.accepted_orgs_msg
-    template = DEF_ACCEPTED_ORG_TEMPLATE
-    subject = DEF_ACCEPTED_ORG % organization.name
+    subject = DEF_ACCEPTED_ORG % {
+        'org': organization.name,
+        }
 
     # TODO(daniel): consult what properties are needed.
     message_properties = {}
 
-    return getContext(
-        site, program, emails, message_properties, subject, template)
+    return getContextFromTemplateString(
+        site, program, emails, message_properties, subject,
+        program_messages.accepted_orgs_msg)
 
 
 class OrganizationRejectedContextProvider(object):
@@ -347,7 +390,7 @@
   is rejected from a program.
   """
 
-  def getContext(self, emails, organization, program, site):
+  def getContext(self, emails, organization, program, site, program_messages):
     """Provides notification context of an email to send out when the specified
     organization is rejected from the program.
 
@@ -356,18 +399,19 @@
       organization: Organization entity.
       program: Program entity.
       site: Site entity.
+      program_messages: ProgramMessages entity that holds the message
+          templates provided by the program admins.
     """
-    # TODO(daniel): replace with a dynamic mail content
-    # program_messages = program.getProgramMessages()
-    # template = program_messages.rejected_orgs_msg
-    template = DEF_REJECTED_ORG_TEMPLATE
-    subject = DEF_REJECTED_ORG % organization.name
+    subject = DEF_REJECTED_ORG % {
+        'org': organization.name,
+        }
 
     # TODO(daniel): consult what properties are needed.
     message_properties = {}
 
-    return getContext(
-        site, program, emails, message_properties, subject, template)
+    return getContextFromTemplateString(
+        site, program, emails, message_properties, subject,
+        program_messages.rejected_orgs_msg)
 
 
 def orgAppContext(data, record, new_status, apply_url):
diff --git a/app/soc/templates/soc/notification/org_accepted.html b/app/soc/templates/soc/notification/org_accepted.html
deleted file mode 100644
index c042bcf..0000000
--- a/app/soc/templates/soc/notification/org_accepted.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "soc/notification/base.html" %}
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-{% block content %}
-Your Organization Application for "{{ org }}" in {{ program_name }} has been accepted.<br />
-Please click <a href="{{ url }}">here</a> to fill in the necessary information and create your Organization.
-{% endblock %}
-
-{% block unsubscribe %}
-{% endblock unsubscribe %}
diff --git a/app/soc/templates/soc/notification/org_rejected.html b/app/soc/templates/soc/notification/org_rejected.html
deleted file mode 100644
index defd98d..0000000
--- a/app/soc/templates/soc/notification/org_rejected.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "soc/mail/base.html" %}
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-{% block content %}
-Thank you for submitting "{{ org_app_name }}" organization application to {{ program_name }}.
-Unfortunately, we were unable to accept your organization's application at this time.
-We received many more applications for the program than we are able to accommodate,
-and we would encourage you to reapply for future instances of the program.
-{% endblock %}
-
-{% block signature %}
-  Best regards, <br />{{ sender_name }}
-{% endblock %}
-
-{% block unsubscribe %}
-{% endblock unsubscribe %}
diff --git a/app/summerofcode/views/org_app.py b/app/summerofcode/views/org_app.py
index 2c4a183..d9a54c6 100644
--- a/app/summerofcode/views/org_app.py
+++ b/app/summerofcode/views/org_app.py
@@ -1110,7 +1110,8 @@
             message='Missing or invalid new status in POST data.')
       else:
         organization = org_key.get()
-        org_logic.setStatus(organization, data.program, data.site, new_status)
+        org_logic.setStatus(organization, data.program, data.site,
+                            data.program.getProgramMessages(), new_status)
         return http.HttpResponse()
 
 
diff --git a/tests/app/melange/logic/test_organization.py b/tests/app/melange/logic/test_organization.py
index 8cc6394..b146504 100644
--- a/tests/app/melange/logic/test_organization.py
+++ b/tests/app/melange/logic/test_organization.py
@@ -322,8 +322,8 @@
   def testAcceptOrganization(self):
     """Tests that organization is successfully accepted."""
     org = org_logic.setStatus(
-        self.org, self.program, self.site, org_model.Status.ACCEPTED,
-        recipients=[TEST_EMAIL])
+        self.org, self.program, self.site, self.program.getProgramMessages(),
+        org_model.Status.ACCEPTED, recipients=[TEST_EMAIL])
 
     self.assertEqual(org.status, org_model.Status.ACCEPTED)
     self.assertEmailSent()
@@ -331,8 +331,8 @@
   def testRejectOrganization(self):
     """Tests that organization is successfully rejected."""
     org = org_logic.setStatus(
-        self.org, self.program, self.site, org_model.Status.REJECTED,
-        recipients=[TEST_EMAIL])
+        self.org, self.program, self.site, self.program.getProgramMessages(),
+        org_model.Status.REJECTED, recipients=[TEST_EMAIL])
 
     self.assertEqual(org.status, org_model.Status.REJECTED)
     self.assertEmailSent()
@@ -340,8 +340,8 @@
   def testPreAcceptOrganization(self):
     """Tests that organization is successfully pre-accepted."""
     org = org_logic.setStatus(
-        self.org, self.program, self.site, org_model.Status.PRE_ACCEPTED,
-        recipients=[TEST_EMAIL])
+        self.org, self.program, self.site, self.program.getProgramMessages(),
+        org_model.Status.PRE_ACCEPTED, recipients=[TEST_EMAIL])
 
     self.assertEqual(org.status, org_model.Status.PRE_ACCEPTED)
 
@@ -350,8 +350,8 @@
   def testPreRejectOrganization(self):
     """Tests that organization is successfully pre-accepted."""
     org = org_logic.setStatus(
-        self.org, self.program, self.site, org_model.Status.PRE_REJECTED,
-        recipients=[TEST_EMAIL])
+        self.org, self.program, self.site, self.program.getProgramMessages(),
+        org_model.Status.PRE_REJECTED, recipients=[TEST_EMAIL])
 
     self.assertEqual(org.status, org_model.Status.PRE_REJECTED)
 
diff --git a/tests/app/melange/mapreduce/test_apply_org_admission_decisions.py b/tests/app/melange/mapreduce/test_apply_org_admission_decisions.py
index 0d7a563..524f78d 100644
--- a/tests/app/melange/mapreduce/test_apply_org_admission_decisions.py
+++ b/tests/app/melange/mapreduce/test_apply_org_admission_decisions.py
@@ -19,8 +19,10 @@
 from melange.models import organization as org_model
 
 from tests import org_utils
+from tests import profile_utils
 from tests import test_utils
 
+from soc.logic.helper import notifications
 from soc.models import program as program_model
 from soc.mapreduce.helper import control as mapreduce_control
 from soc.modules.seeder.logic.seeder import logic as seeder_logic
@@ -49,6 +51,14 @@
           status=statuses[i % len(statuses)])
       seeded_orgs[i % len(statuses)].append(org.key)
 
+    self.admins = []
+    for i in range(int(1.5 * len(seeded_orgs))):
+      admin = profile_utils.seedNDBProfile(
+          self.program.key(), admin_for=seeded_orgs[i % len(seeded_orgs)])
+      self.admins.append(admin)
+
+    self.program_messages = self.program.getProgramMessages()
+
     self.pre_accepted_orgs = seeded_orgs[0]
     self.pre_rejected_orgs = seeded_orgs[1]
     self.applying_orgs = seeded_orgs[2]
@@ -80,6 +90,20 @@
       org = org_key.get()
       self.assertEqual(org.status, org_model.Status.APPLYING)
 
+    for org_key in self.pre_accepted_orgs:
+      org = org_key.get()
+      subject = notifications.DEF_ACCEPTED_ORG % {
+          'org': org.name,
+          }
+      self.assertEmailSent(cc=org.contact.email, subject=subject)
+
+    for org_key in self.pre_rejected_orgs:
+      org = org_key.get()
+      subject = notifications.DEF_REJECTED_ORG % {
+          'org': org.name,
+          }
+      self.assertEmailSent(cc=org.contact.email, subject=subject)
+
   def testOrgsForAnotherProgram(self):
     """Tests that status of organizations for another program is untouched."""
     # seed another program
diff --git a/tests/program_utils.py b/tests/program_utils.py
index c744fcb..19d1e88 100644
--- a/tests/program_utils.py
+++ b/tests/program_utils.py
@@ -256,10 +256,7 @@
   """
   properties = {'parent': program_key}
   properties.update(kwargs)
-  program_messages = models.program_messages_model(**properties)
-  program_messages.put()
-
-  return program_messages
+  return seeder_logic.seed(models.program_messages_model, properties)
 
 
 def seedGSoCProgramMessages(program_key=None, **kwargs):