Index: /main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- /main/waeup.kofa/trunk/CHANGES.txt	(revision 14933)
+++ /main/waeup.kofa/trunk/CHANGES.txt	(revision 14934)
@@ -4,4 +4,9 @@
 1.6.dev0 (unreleased)
 =======================
+
+* Do no longer hide the 'Create students' buttons but switch the portal to
+  maintenance mode to ensure that nobody can enter the portal during
+  record creation except for user 'admin'. This guarantees that record
+  creation is only started once.
 
 * Replace `gpa_boundaries` utility attribute by `GPABoundaries` method
Index: /main/waeup.kofa/trunk/docs/source/userdocs/applicants/create_students.rst
===================================================================
--- /main/waeup.kofa/trunk/docs/source/userdocs/applicants/create_students.rst	(revision 14933)
+++ /main/waeup.kofa/trunk/docs/source/userdocs/applicants/create_students.rst	(revision 14934)
@@ -25,6 +25,5 @@
   applicants will be skipped without further notice.
 
-3. Last but not least, the portal manager with user id ``admin``
-   does see a link button:
+3. Last but not least, the portal managers do see a link button:
 
   |create_students_button|
@@ -32,25 +31,25 @@
   on the `ApplicantsContainerPage` and on the `ApplicantsRootPage`
   which creates student records from applicants inside a single
-  applicants container or even portal-wide respectively.
+  applicants container or even portal-wide respectively. A warning
+  message appears before the process is started. After start the
+  portal is immediately switched into maintenance mode to ensure that
+  nobody can enter the portal during record creation except for user
+  'admin'. Maintenance mode is switched off as soon as the process has
+  finished.
 
-  The reader may wonder: Why only user ``admin``?
+.. note::
 
   Batch creation of thousands or even tenthousands of student records
   is very computation intensive and takes quite a long time, even with
   powerful computers. Experience has shown that the portal must be
-  disconnected from the Internet in order to avoid database write
-  conflict errors, which cannot be resolved if also students access
-  the portal. Usually conflict errrors are not serious. Unresolved
-  conflicts just lead to an error message and the database remains
-  unchanged. Unfortunataly, student creation does not only alter the
-  object database. As we will see below, also folders and files in the
-  filesystem are added. These filesystem changes are not being
-  reverted after unresolved conflicts, a fact which can cause serious
-  problems.
-
-  So the answer to the question is trivial: We expect that the admin
-  user knows what s/he is doing and is careful enough to start the
-  creation process in maintenance mode, which means when the portal is
-  disconnected from a public network.
+  switched to maintenance mode (or even disconnected from the Internet)
+  in order to avoid database write conflict errors, which cannot be
+  resolved if also students access the portal. Usually conflict
+  errrors are not serious. Unresolved conflicts just lead to an error
+  message and the database remains unchanged. Unfortunataly, student
+  creation does not only alter the object database. As we will see
+  below, also folders and files in the filesystem are added. These
+  filesystem changes are not being reverted after unresolved conflicts,
+  a fact which can cause serious problems.
 
 
Index: /main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py	(revision 14933)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py	(revision 14934)
@@ -24,5 +24,5 @@
 from urllib import urlencode
 from datetime import datetime, date
-from time import time
+from time import time, sleep
 from zope.event import notify
 from zope.component import getUtility, queryUtility, createObject, getAdapter
@@ -680,5 +680,5 @@
 class CreateAllStudentsPage(UtilityView, grok.View):
     """Create all student objects from applicant data
-    in the root container or in a specific  applicants container only.
+    in the root container or in a specific applicants container only.
     Only PortalManagers can do this.
     """
@@ -688,4 +688,10 @@
 
     def update(self):
+        grok.getSite()['configuration'].maintmode_enabled_by = u'admin'
+        transaction.commit()
+        # Wait 10 seconds for all transactions to be finished.
+        # Do not wait in tests.
+        if not self.request.principal.id == 'zope.mgr':
+            sleep(10)
         cat = getUtility(ICatalog, name='applicants_catalog')
         results = list(cat.searchResults(state=(ADMITTED, ADMITTED)))
@@ -706,4 +712,5 @@
                 applicants_root.logger.info(
                     '%s - %s - %s' % (ob_class, result.applicant_id, msg))
+        grok.getSite()['configuration'].maintmode_enabled_by = None
         if len(created):
             self.flash(_('${a} students successfully created.',
Index: /main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_applicantcopier.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_applicantcopier.py	(revision 14933)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_applicantcopier.py	(revision 14934)
@@ -26,4 +26,5 @@
 from zope.component import getUtility
 from zope.i18n import translate
+from zope.securitypolicy.interfaces import IPrincipalRoleManager
 from waeup.kofa.testing import FunctionalLayer
 from waeup.kofa.interfaces import IExtFileStore, IFileStoreNameChooser
@@ -42,5 +43,4 @@
 
     def prepare_applicant(self):
-        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.browser.open(self.manage_path)
         self.fill_correct_values()
@@ -56,4 +56,5 @@
 
     def test_copier(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.prepare_applicant()
         storage = getUtility(IExtFileStore)
@@ -140,4 +141,5 @@
 
     def test_batch_copying(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.prepare_applicant()
         IWorkflowState(self.applicant).setState('admitted')
@@ -155,4 +157,5 @@
         logfile = os.path.join(
             self.app['datacenter'].storage, 'logs', 'applicants.log')
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.prepare_applicant()
         self.browser.open(self.container_path + '/createallstudents')
@@ -182,4 +185,5 @@
         logfile = os.path.join(
             self.app['datacenter'].storage, 'logs', 'applicants.log')
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
         self.prepare_applicant()
         self.browser.open(self.manage_path)
@@ -238,2 +242,27 @@
         logcontent = open(logfile).read()
         self.assertTrue('IOError: Application Slip could not be created.' in logcontent)
+
+    def disabled_test_delay(self):
+        # Create portalmanager manager
+        self.app['users'].addUser('mrportalmanager', 'mrportalmanagersecret')
+        self.app['users']['mrportalmanager'].email = 'mrportalmanager@foo.ng'
+        self.app['users']['mrportalmanager'].title = 'Carlos Portales'
+        prmglobal = IPrincipalRoleManager(self.app)
+        prmglobal.assignRoleToPrincipal(
+            'waeup.PortalManager', 'mrportalmanager')
+        # Login as portal manager
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = 'mrportalmanager'
+        self.browser.getControl(name="form.password").value = 'mrportalmanagersecret'
+        self.browser.getControl("Login").click()
+        self.prepare_applicant()
+        self.browser.open(self.manage_path)
+        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
+        self.browser.getControl(name="form.course_admitted").value = ['CERT1']
+        self.browser.getControl("Save").click()
+        IWorkflowState(self.applicant).setState('admitted')
+        notify(grok.ObjectModifiedEvent(self.applicant))
+        start = datetime.now()
+        self.browser.open(self.root_path + '/createallstudents')
+        self.assertTrue('1 students successfully created' in self.browser.contents)
+        self.assertTrue((datetime.now() - start).seconds >= 10)
Index: /main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py	(revision 14933)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py	(revision 14934)
@@ -167,4 +167,13 @@
     grok.order(4)
 
+creation_warning = _(
+    "'The creation process may take a considerably long time "
+    "depending on the number of student records to be created "
+    "(appr. 0.5s per student). "
+    "All users will be logged out and the portal will be in "
+    "maintenance mode during record creation. You have to login again after "
+    "creation process has finished. "
+    "Do you really want to start the process now?'")
+
 class ApplicantsRootCreateStudentsActionButton(ManageActionButton):
     grok.order(3)
@@ -177,8 +186,6 @@
 
     @property
-    def target_url(self):
-        if self.target and self.request.principal.id == 'admin':
-            return self.view.url(self.view.context, self.target)
-        return
+    def onclick(self):
+        return "return window.confirm(%s);" % creation_warning
 
 class ApplicantsContainerCreateStudentsActionButton(ManageActionButton):
@@ -192,8 +199,6 @@
 
     @property
-    def target_url(self):
-        if self.target and self.request.principal.id == 'admin':
-            return self.view.url(self.view.context, self.target)
-        return
+    def onclick(self):
+        return "return window.confirm(%s);" % creation_warning
 
 class ApplicantViewActionButton(ManageActionButton):
