Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 6622)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 6626)
@@ -45,5 +45,5 @@
 from waeup.sirp.browser.resources import datepicker, tabs, datatable
 from waeup.sirp.browser.viewlets import (
-    ManageActionButton, PrimaryNavTab, LeftSidebarLink
+    ManageActionButton, PrimaryNavTab, LeftSidebarLink, AddActionButton,
     )
 from waeup.sirp.image.browser.widget import (
@@ -67,4 +67,5 @@
     Student,
     )
+from waeup.sirp.students.catalog import search
 
 class StudentsTab(PrimaryNavTab):
@@ -84,6 +85,6 @@
         return self.view.application_url('students')
 
-class StudentsContainerPage(WAeUPDisplayFormPage):
-    """The standard view for regular student containers.
+class StudentsContainerPage(WAeUPPage):
+    """The standard view for student containers.
     """
     grok.context(IStudentsContainer)
@@ -92,7 +93,4 @@
     grok.template('studentscontainerpage')
 
-    form_fields = grok.AutoFields(IStudentsContainer)
-    #form_fields['description'].custom_widget = ReSTDisplayWidget
-
     @property
     def title(self):
@@ -103,5 +101,25 @@
         return self.title
 
-class StudentssContainerManageActionButton(ManageActionButton):
+    def update(self, *args, **kw):
+        datatable.need()
+        form = self.request.form
+        self.hitlist = []
+        if 'searchterm' in form and form['searchterm']:
+            self.searchterm = form['searchterm']
+            self.searchtype = form['searchtype']
+        elif 'old_searchterm' in form:
+            self.searchterm = form['old_searchterm']
+            self.searchtype = form['old_searchtype']
+        else:
+            if 'search' in form:
+                self.flash('Empty search string.')
+            return
+        self.hitlist = search(query=self.searchterm,
+            searchtype=self.searchtype, view=self)
+        if not self.hitlist:
+            self.flash('No student found.')
+        return
+
+class StudentsContainerManageActionButton(ManageActionButton):
     grok.order(1)
     grok.context(IStudentsContainer)
@@ -110,13 +128,12 @@
     text = 'Manage students'
 
-class StudentsContainerManagePage(WAeUPDisplayFormPage):
-    """The standard view for regular student containers.
+
+class StudentsContainerManagePage(WAeUPPage):
+    """The manage page for student containers.
     """
     grok.context(IStudentsContainer)
     grok.name('manage')
     grok.require('waeup.manageStudents')
-    grok.template('studentscontainerpage')
-
-    form_fields = grok.AutoFields(IStudentsContainer)
+    grok.template('studentscontainermanagepage')
 
     @property
@@ -128,8 +145,46 @@
         return self.title
 
-    @grok.action('Add student', validator=NullValidator)
-    def addStudent(self, **data):
-        self.redirect(self.url(self.context, 'addstudent'))
+    def update(self, *args, **kw):
+        datatable.need()
+        form = self.request.form
+        self.hitlist = []
+        if 'searchterm' in form and form['searchterm']:
+            self.searchterm = form['searchterm']
+            self.searchtype = form['searchtype']
+        elif 'old_searchterm' in form:
+            self.searchterm = form['old_searchterm']
+            self.searchtype = form['old_searchtype']
+        else:
+            if 'search' in form:
+                self.flash('Empty search string.')
+            return
+        #import pdb; pdb.set_trace()
+        if not 'entries' in form:
+            self.hitlist = search(query=self.searchterm,
+                searchtype=self.searchtype, view=self)
+            if not self.hitlist:
+                self.flash('No student found.')
+            return
+        entries = form['entries']
+        if isinstance(entries, basestring):
+            entries = [entries]
+        deleted = []
+        for entry in entries:
+            if 'remove' in form:
+                del self.context[entry]
+                deleted.append(entry)
+        self.hitlist = search(query=self.searchterm,
+            searchtype=self.searchtype, view=self)
+        if len(deleted):
+            self.flash('Successfully removed: %s' % ', '.join(deleted))
         return
+
+class StudentsContainerAddActionButton(AddActionButton):
+    grok.order(1)
+    grok.context(IStudentsContainer)
+    grok.view(StudentsContainerManagePage)
+    grok.require('waeup.manageStudents')
+    text = 'Add student'
+    target = 'addstudent'
 
 class StudentAddFormPage(WAeUPAddFormPage):
@@ -155,4 +210,5 @@
             self.flash('The student id chosen already exists.')
             return
+        self.flash('Student record created.')
         self.redirect(self.url(self.context[student_id], 'index'))
         return
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/studentscontainermanagepage.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/studentscontainermanagepage.pt	(revision 6626)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/studentscontainermanagepage.pt	(revision 6626)
@@ -0,0 +1,44 @@
+<h2 tal:content="view/label">Title</h2>
+<form method="POST">
+
+<br />
+
+  <input type="submit" name="search" value="Search" />
+  for students
+
+  <select name="searchtype">
+    <option value="student_id">with id</option>
+    <option value="name">with name</option>
+  </select>
+
+  <input type="text" name="searchterm" />
+
+  <p>&nbsp;</p>
+  <div tal:condition="view/hitlist">
+    <h3>Search Results</h3>
+    <input type="hidden" name="old_searchterm"
+         tal:attributes="value view/searchterm" />
+    <input type="hidden" name="old_searchtype"
+         tal:attributes="value view/searchtype" />
+    <table class="display dataTableManage">
+      <thead>
+    	<tr>
+    	  <th>&nbsp;</th>
+    	  <th>Student Id</th><th>Name</th>
+    	</tr>
+      </thead>
+      <tbody>
+    	<tr tal:repeat="item view/hitlist">
+    	  <td><input type="checkbox" name="entries"
+    	             tal:attributes="value item/student_id" /></td>
+    	  <td tal:content="item/student_id">A123456</td>
+    	  <td tal:content="item/name">Bob</td>
+    	</tr>
+      </tbody>
+    </table>
+    <div class="actionButtons">
+        <input type="submit" name="remove" value="Remove selected" />
+    </div>
+
+  </div>
+</form>
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/studentscontainerpage.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/studentscontainerpage.pt	(revision 6622)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/studentscontainerpage.pt	(revision 6626)
@@ -1,27 +1,37 @@
 <h2 tal:content="view/label">Title</h2>
+<form method="POST">
 
-<form method="POST">
-<table class="zebra" tal:condition="layout/isAuthenticated">
-  <tbody>
-    <tal:block repeat="widget view/widgets">
-      <tr>
-        <td class="fieldname">
-          <tal:block replace="python:widget.label"/>:
-        </td>
-        <td class="field">
-          <tal:block tal:replace="structure widget" />
-        </td>
-      </tr>
-    </tal:block>
-      <tr>
-        <td>
-            <div class="actionButtons" tal:condition="view/availableActions">
-                <span tal:repeat="action view/actions" tal:omit-tag="">
-                    <input tal:replace="structure action/render"/>
-                </span>
-            </div>
-        </td>
-      </tr>
-  </tbody>
-</table>
+<br />
+
+  <input type="submit" name="search" value="Search" />
+  for students
+
+  <select name="searchtype">
+    <option value="student_id">with id</option>
+    <option value="name">with name</option>
+  </select>
+
+  <input type="text" name="searchterm" />
+
+  <p>&nbsp;</p>
+  <div tal:condition="view/hitlist">
+    <h3>Search Results</h3>
+    <input type="hidden" name="old_searchterm"
+         tal:attributes="value view/searchterm" />
+    <input type="hidden" name="old_searchtype"
+         tal:attributes="value view/searchtype" />
+    <table class="display dataTableManage">
+      <thead>
+    	<tr>
+    	  <th>Student Id</th><th>Name</th>
+    	</tr>
+      </thead>
+      <tbody>
+    	<tr tal:repeat="item view/hitlist">
+    	  <td tal:content="item/student_id">A123456</td>
+    	  <td tal:content="item/name">Bob</td>
+    	</tr>
+      </tbody>
+    </table>
+  </div>
 </form>
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/catalog.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/catalog.py	(revision 6626)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/catalog.py	(revision 6626)
@@ -0,0 +1,50 @@
+"""Cataloging and searching components for students.
+"""
+import grok
+from grok import index
+from hurry.query import Eq, Text
+from hurry.query.query import Query
+from zope.index.text.parsetree import ParseError
+from waeup.sirp.interfaces import IUniversity, IQueryResultItem
+from waeup.sirp.students.interfaces import IStudent
+
+class StudentIndexes(grok.Indexes):
+    """A catalog for students.
+    """
+    grok.site(IUniversity)
+    grok.name('students_catalog')
+    grok.context(IStudent)
+
+    student_id = index.Field(attribute='student_id')
+    name = index.Text(attribute='name')
+    #history = index.Text(attribute='history')
+    #state = index.Field(attribute='state')
+
+class StudentQueryResultItem(object):
+    grok.implements(IQueryResultItem)
+
+    title = u'Student Query Item'
+    description = u'Some students found in a search'
+
+    def __init__(self, context, view):
+        self.context = context
+        self.url = view.url(context)
+        self.student_id = context.student_id
+        self.name = context.name
+        #self.history = context.history
+        #self.state = context.state
+
+def search(query=None, searchtype=None, view=None):
+    hitlist = []
+    #if not query:
+    #    view.flash('Empty search string.')
+    #    return
+    if searchtype in ('history','name'):
+        results = Query().searchResults(
+            Text(('students_catalog', searchtype), query))
+    else:
+        results = Query().searchResults(
+            Eq(('students_catalog', searchtype), query))
+    for result in results:
+        hitlist.append(StudentQueryResultItem(result, view=view))
+    return hitlist
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py	(revision 6622)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py	(revision 6626)
@@ -73,5 +73,5 @@
         self.container_path = 'http://localhost/app/students'
         self.manage_container_path = self.container_path + '/@@manage'
-        self.add_student_path = self.container_path + '/@@add'
+        self.add_student_path = self.container_path + '/addstudent'
 
         # Populate university
@@ -121,11 +121,9 @@
         return
 
-    def test_add_students(self):
-        # Managers can add students
+    def test_add_search_delete_students(self):
+        # Managers can add search and remove students
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
-        self.add_student_path = self.container_path + '/addstudent'
-        self.container_manage_path = self.container_path + '/@@manage'
-        self.browser.open(self.container_manage_path)
-        self.browser.getControl("Add student").click()
+        self.browser.open(self.manage_container_path)
+        self.browser.getLink("Add student").click()
         self.assertEqual(self.browser.headers['Status'], '200 Ok')
         self.assertEqual(self.browser.url, self.add_student_path)
@@ -133,2 +131,37 @@
         self.browser.getControl(name="form.name").value = 'Bob Tester'
         self.browser.getControl("Create student record").click()
+        self.assertTrue('Student record created' in self.browser.contents)
+
+        self.browser.open(self.container_path)
+        self.browser.getControl("Search").click()
+        self.assertTrue('Empty search string' in self.browser.contents)
+        self.browser.getControl(name="searchtype").value = ['student_id']
+        self.browser.getControl(name="searchterm").value = 'A123456'
+        self.browser.getControl("Search").click()
+        self.assertTrue('Bob Tester' in self.browser.contents)
+        # The old searchterm will be used again
+        self.browser.getControl("Search").click()
+        self.assertTrue('Bob Tester' in self.browser.contents)
+
+        self.browser.open(self.manage_container_path)
+        self.browser.getControl("Search").click()
+        self.assertTrue('Empty search string' in self.browser.contents)
+        self.browser.getControl(name="searchtype").value = ['name']
+        self.browser.getControl(name="searchterm").value = 'Bob Tester'
+        self.browser.getControl("Search").click()
+        self.assertTrue('A123456' in self.browser.contents)
+        ctrl = self.browser.getControl(name='entries')
+        ctrl.getControl(value='A123456').selected = True
+        self.browser.getControl("Remove selected", index=0).click()
+        self.assertTrue('Successfully removed' in self.browser.contents)
+        self.browser.getControl(name="searchtype").value = ['student_id']
+        self.browser.getControl(name="searchterm").value = 'A123456'
+        self.browser.getControl("Search").click()
+        self.assertTrue('No student found' in self.browser.contents)
+
+        self.browser.open(self.container_path)
+        self.browser.getControl(name="searchtype").value = ['student_id']
+        self.browser.getControl(name="searchterm").value = 'A123456'
+        self.browser.getControl("Search").click()
+        self.assertTrue('No student found' in self.browser.contents)
+
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_catalog.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_catalog.py	(revision 6626)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_catalog.py	(revision 6626)
@@ -0,0 +1,88 @@
+## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+import shutil
+import tempfile
+import unittest
+from zope.app.testing.functional import (
+    FunctionalTestCase, FunctionalTestSetup, getRootFolder)
+from zope.catalog.interfaces import ICatalog
+from zope.component import queryUtility, getUtility
+from zope.component.hooks import setSite, clearSite
+from zope.interface.verify import verifyClass, verifyObject
+from hurry.query.interfaces import IQuery
+from hurry.query import Eq
+from waeup.sirp.app import University
+from waeup.sirp.interfaces import IQueryResultItem
+from waeup.sirp.testing import FunctionalLayer
+from waeup.sirp.students.students import Student
+#from waeup.sirp.students.workflow import INITIALIZED, USED, DISABLED
+from waeup.sirp.students.catalog import StudentQueryResultItem, search
+
+class CatalogTestSetup(FunctionalTestCase):
+    # A setup for testing students catalog related stuff.
+    #
+    # sets up a site with some student already created.
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CatalogTestSetup, self).setUp()
+
+        # Prepopulate ZODB
+        app = University()
+        self.dc_root = tempfile.mkdtemp()
+        app['datacenter'].setStoragePath(self.dc_root)
+
+        # Prepopulate the ZODB...
+        self.getRootFolder()['app'] = app
+        self.app = self.getRootFolder()['app']
+        setSite(self.app)
+
+        # Create students
+        student = Student()
+        student.name = u'Bob Tester'
+        student.student_id = u'A123456'
+        self.app['students'][student.student_id] = student
+        return
+
+    def tearDown(self):
+        shutil.rmtree(self.dc_root)
+        super(CatalogTestSetup, self).tearDown()
+        return
+
+class StudentCatalogTests(CatalogTestSetup):
+
+    layer = FunctionalLayer
+
+    def test_get_catalog(self):
+        # We can get an students catalog if we wish
+        cat = queryUtility(ICatalog, name='students_catalog')
+        assert cat is not None
+
+    def test_search_by_id(self):
+        # We can find a certain student id
+        cat = queryUtility(ICatalog, name='students_catalog')
+        results = cat.searchResults(student_id=('A123456', 'A123456'))
+        results = [x for x in results] # Turn results generator into list
+        assert len(results) == 1
+        assert results[0] is self.app['students']['A123456']
+
+    def test_search_by_name(self):
+        # We can find a certain name
+        cat = queryUtility(ICatalog, name='students_catalog')
+        results = cat.searchResults(name='Bob Tester')
+        results = [x for x in results] # Turn results generator into list
+        assert len(results) == 1
+        assert results[0] is self.app['students']['A123456']
