Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/__init__.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/__init__.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/__init__.py	(revision 7063)
@@ -3,5 +3,6 @@
 # Make this a package.
 from waeup.sirp.applicants.applicant import (
-    ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser,
+    ResultEntry, Applicant, ApplicantFactory, ApplicantImageStoreHandler,
+    get_regno_or_ac, ApplicantImageNameChooser,
     )
 from waeup.sirp.applicants.container import ApplicantsContainer
@@ -14,8 +15,10 @@
     'Applicant',
     'ApplicantFactory',
+    'ApplicantImageNameChooser',
+    'ApplicantImageStoreHandler',
     'ApplicantsContainer',
     'ApplicantsRoot',
-    'ApplicantTraverser',
     'application_exists',
     'get_applicant_data',
+    'get_regno_or_ac',
     ]
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py	(revision 7063)
@@ -20,4 +20,5 @@
 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ##
+import os
 import grok
 from grok import index
@@ -25,10 +26,20 @@
 from zope.interface import implementedBy
 from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
-from waeup.sirp.interfaces import IObjectHistory
 from waeup.sirp.app import University
+from waeup.sirp.image import WAeUPImageFile
+from waeup.sirp.imagestorage import DefaultFileStoreHandler
+from waeup.sirp.interfaces import (
+    IObjectHistory, IFileStoreHandler, IFileStoreNameChooser)
+from waeup.sirp.utils.helpers import attrs_to_fields
 from waeup.sirp.applicants.interfaces import (
-    IResultEntry, IApplicant, IApplicantEdit, default_passport_image,
+    IResultEntry, IApplicant, IApplicantEdit,
     )
-from waeup.sirp.utils.helpers import attrs_to_fields
+
+
+def get_regno_or_ac(context):
+    reg_no = getattr(context, 'reg_no', None)
+    if reg_no is None:
+        return getattr(context, 'access_code', None)
+    return reg_no
 
 class ResultEntry(grok.Context):
@@ -49,7 +60,4 @@
         return
 
-    #def getApplicantsRootLogger(self):
-    #    return grok.getSite()['applicants'].logger
-
     def loggerInfo(self, ob_class, comment=None):
         target = self.__name__
@@ -81,31 +89,4 @@
     access_code = index.Field(attribute='access_code')
 
-class ApplicantTraverser(grok.Traverser):
-    """Get image of the context applicant.
-
-    Each applicant can provide a passport photograph which will be
-    returned by this traverser if:
-
-    - we request the exact filename of the picture or
-
-    - ask for a picture named 'passport.jpg'.
-
-    If no picture was stored yet, we get a placeholder image when
-    asking for `passport.jpg`.
-
-    If none of the above applies, we return ``None``, most probably
-    resulting a :exc:`NotFound` exception.
-
-    """
-    grok.context(IApplicant)
-    def traverse(self, name):
-        passport_filename = getattr(self.context.passport, 'filename', None)
-        if name == passport_filename:
-            return self.context.passport
-        if name == 'passport.jpg':
-            if self.context.passport is not None:
-                return self.context.passport
-        return
-
 class ApplicantFactory(grok.GlobalUtility):
     """A factory for applicants.
@@ -121,2 +102,100 @@
     def getInterfaces(self):
         return implementedBy(Applicant)
+
+
+#: The file id marker for applicant passport images
+APPLICANT_IMAGE_STORE_NAME = 'img-applicant'
+
+class ApplicantImageNameChooser(grok.Adapter):
+    """A file id chooser for :class:`Applicant` objects.
+
+    `context` is an :class:`Applicant` instance.
+
+    The :class:`ApplicantImageNameChooser` can build/check file ids
+    for :class:`Applicant` objects suitable for use with
+    :class:`ExtFileStore` instances. The delivered file_id contains
+    the file id marker for :class:`Applicant` object and the
+    registration number or access code of the context applicant. Also
+    the name of the connected applicant container will be part of the
+    generated file id.
+
+    This chooser is registered as an adapter providing
+    :class:`waeup.sirp.interfaces.IFileStoreNameChooser`.
+
+    File store name choosers like this one are only convenience
+    components to ease the task of creating file ids for applicant
+    objects. You are nevertheless encouraged to use them instead of
+    manually setting up filenames for applicants.
+
+    .. seealso:: :mod:`waeup.sirp.imagestorage`
+
+    """
+    grok.context(IApplicant)
+    grok.implements(IFileStoreNameChooser)
+
+    def checkName(self, name=None):
+        """Check whether the given name is a valid file id for the context.
+
+        Returns ``True`` only if `name` equals the result of
+        :meth:`chooseName`.
+        """
+        return name == self.chooseName()
+
+    def chooseName(self, name=None):
+        """Get a valid file id for applicant context.
+
+        *Example:*
+
+        For an applicant with registration no. ``'My_reg_no_1234'``
+        and stored in an applicants container called
+        ``'mycontainer'``, this chooser would create:
+
+          ``'__img-applicant__mycontainer/My_reg_no_1234.jpg'``
+
+        meaning that the passport image of this applicant would be
+        stored in the site-wide file storage in path:
+
+          ``mycontainer/My_reg_no_1234.jpg``
+
+        If the context applicant has no parent, ``'_default'`` is used
+        as parent name.
+        """
+        parent_name = getattr(
+            getattr(self.context, '__parent__', None),
+            '__name__', '_default')
+        marked_filename = '__%s__%s/%s.jpg' % (
+            APPLICANT_IMAGE_STORE_NAME,
+            parent_name, get_regno_or_ac(self.context))
+        return marked_filename
+
+
+class ApplicantImageStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
+    """Applicant specific image handling.
+
+    This handler knows in which path in a filestore to store applicant
+    images and how to turn this kind of data into some (browsable)
+    file object.
+
+    It is called from the global file storage, when it wants to
+    get/store a file with a file id starting with
+    ``__img-applicant__`` (the marker string for applicant images).
+
+    Like each other file store handler it does not handle the files
+    really (this is done by the global file store) but only computes
+    paths and things like this.
+    """
+    grok.implements(IFileStoreHandler)
+    grok.name(APPLICANT_IMAGE_STORE_NAME)
+
+    def pathFromFileID(self, store, root, file_id):
+        """All applicants images are filed in directory ``applicants``.
+        """
+        marker, filename, basename, ext = store.extractMarker(file_id)
+        return os.path.join(root, 'applicants', filename)
+
+    def createFile(self, store, root, filename, file_id, file):
+        """Create a browsable file-like object.
+        """
+        # possible other actions: check for jpeg format
+        path = self.pathFromFileID(store, root, file_id)
+        return file, path, WAeUPImageFile(filename, file_id)
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/authentication.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/authentication.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/authentication.py	(revision 7063)
@@ -232,7 +232,7 @@
         if ac.state == 'used' and appl_ac != ac.representation:
             return None
-        if appl_ac is not None and appl_ac != ac.representation:
-            # This should never happen. It means a catalog error.
-            return None
+        # If the following fails we have a catalog error. Bad enough
+        # to pull emergency break.
+        assert appl_ac is None or appl_ac == ac.representation
         return ApplicantPrincipalInfo(accesscode)
 
@@ -256,5 +256,5 @@
     systems (regular users, officers, etc.) were set up.
     """
-    grok.provides(IAuthPluginUtility)
+    grok.implements(IAuthPluginUtility)
     grok.name('applicants_auth_setup')
 
@@ -285,4 +285,12 @@
 
     def unregister(self, pau):
+        """Unregister applicant specific authentication components from PAU.
+        """
+        pau.credentialsPlugins = tuple(
+            [x for x in list(pau.credentialsPlugins)
+             if x != 'applicant_credentials'])
+        pau.authenticatorPlugins = tuple(
+            [x for x in list(pau.authenticatorPlugins)
+             if x != 'applicants'])
         return pau
 
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py	(revision 7063)
@@ -22,4 +22,5 @@
 """UI components for basic applicants and related components.
 """
+import os
 import sys
 import grok
@@ -53,9 +54,6 @@
     ManageActionButton, PrimaryNavTab, LeftSidebarLink
     )
-from waeup.sirp.image.browser.widget import (
-    ThumbnailWidget, EncodingImageFileWidget,
-    )
-from waeup.sirp.image.image import createWAeUPImageFile
-from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
+from waeup.sirp.interfaces import (
+    IWAeUPObject, ILocalRolesAssignable, IExtFileStore, IFileStoreNameChooser)
 from waeup.sirp.permissions import get_users_with_local_roles
 from waeup.sirp.university.interfaces import ICertificate
@@ -68,5 +66,6 @@
 from waeup.sirp.applicants.interfaces import (
     IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
-    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
+    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
+    IMAGE_PATH,
     )
 from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
@@ -425,5 +424,6 @@
         # If application has ended and applicant record doesn't exist,
         # logout without marking AC as used
-        if not pin in self.context.keys() and self.context.enddate < date.today():
+        if not pin in self.context.keys() and (
+            self.context.enddate < date.today()):
             self.flash('Application has ended.')
             auth = getUtility(IAuthentication)
@@ -433,7 +433,5 @@
 
         # Mark AC as used (this also fires a pin related transition)
-        if get_access_code(pin).state == USED:
-            pass
-        else:
+        if get_access_code(pin).state != USED:
             comment = u"AC invalidated"
             # Here we know that the ac is in state initialized so we do not
@@ -514,6 +512,5 @@
             applicant_object = get_applicant_data(access_code)
             return absoluteURL(applicant_object, self.request) + self.target
-        #else:
-        #    return ''
+        return ''
 
 class AccessCodeEditLink(AccessCodeViewLink):
@@ -534,6 +531,5 @@
                 return ''
             return absoluteURL(applicant_object, self.request) + self.target
-        #else:
-        #    return ''
+        return ''
 
 class AccessCodeSlipLink(AccessCodeViewLink):
@@ -550,10 +546,12 @@
     form_fields = grok.AutoFields(IApplicant).omit(
         'locked').omit('course_admitted')
-    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
-    form_fields['passport'].custom_widget = ThumbnailWidget
     form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
     label = 'Applicant'
     grok.template('form_display')
     pnav = 3
+
+    def update(self):
+        self.passport_url = self.url(self.context, 'passport.jpg')
+        return
 
     @property
@@ -582,5 +580,4 @@
 class PDFActionButton(ManageActionButton):
     grok.context(IApplicant)
-    #grok.view(DisplayApplicant)
     grok.require('waeup.manageApplications')
     icon = 'actionicon_pdf.png'
@@ -624,9 +621,4 @@
 
     def render(self):
-        # (0,0),(-1,-1) = whole table
-        # (0,0),(0,-1) = first column
-        # (-1,0),(-1,-1) = last column
-        # (0,0),(-1,0) = first row
-        # (0,-1),(-1,-1) = last row
         SLIP_STYLE = TableStyle(
             [('VALIGN',(0,0),(-1,-1),'TOP')]
@@ -652,5 +644,4 @@
         frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
         story.append(Paragraph(self.label, style["Heading2"]))
-        #story.append(HRFlowable())
         story.append(Spacer(1, 18))
         for msg in self.context.history.messages:
@@ -658,17 +649,21 @@
             story.append(Paragraph(f_msg, style["Normal"]))
         story.append(Spacer(1, 24))
+
+        data = []
+        # insert passport photograph
+        img = getUtility(IExtFileStore).getFileByContext(self.context)
+        if img is None:
+            img = open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'rb')
+        doc_img = Image(img.name, width=4*cm, height=3*cm, kind='bound')
+        data.append(['', doc_img])
+
+        # render widget fields
         self.setUpWidgets()
-        data = []
         for widget in self.widgets:
             f_label = '<font size=12>%s</font>:' % widget.label.strip()
             f_label = Paragraph(f_label, style["Normal"])
-            if widget.name != 'form.passport':
-                f_text = '<font size=12>%s</font>' % widget()
-                f_text = Paragraph(f_text, style["Normal"])
-                data.append([f_label,f_text])
-            else:
-                filename = widget._data.file.name
-                im = Image(filename,width=4*cm, height=3*cm,kind='bound')
-                data.append([f_label,im])
+            f_text = '<font size=12>%s</font>' % widget()
+            f_text = Paragraph(f_text, style["Normal"])
+            data.append([f_label,f_text])
         f_label = '<font size=12>Admitted Course of Study:</font>'
         f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
@@ -698,4 +693,13 @@
     target = 'edit_full'
 
+def handle_img_upload(upload, context):
+    """Handle upload of applicant image.
+    """
+    store = getUtility(IExtFileStore)
+    file_id = IFileStoreNameChooser(context).chooseName()
+    store.createFile(file_id, upload)
+    upload.seek(0) # XXX: really neccessary?
+    return
+
 class EditApplicantFull(WAeUPEditFormPage):
     """A full edit view for applicant data.
@@ -705,5 +709,4 @@
     grok.require('waeup.manageApplications')
     form_fields = grok.AutoFields(IApplicant)
-    form_fields['passport'].custom_widget = EncodingImageFileWidget
     form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
     grok.template('form_edit')
@@ -718,19 +721,5 @@
         if upload:
             # We got a fresh upload
-            image = createWAeUPImageFile(upload.filename, upload)
-            # This would normally be totally illegal. We set context
-            # data without the complete form data being
-            # verified. Normally data is set in methods like `save`
-            # only, which is only called when all form data is correct
-            # (and if not, the context is not touched). With images
-            # and their uploads the problem then is, that the uploaded
-            # image won't be shown in a partially erranous form
-            # because the new uploaded image is still not part of the
-            # context obeject and the image widget cannot work around
-            # that (it has no access to the new data). For this reason
-            # we set the image here and not in 'save()' or other
-            # methods.
-            self.context.passport = image
-            upload.seek(0)
+            handle_img_upload(upload, self.context)
             self.passport_changed = True
         return
@@ -760,5 +749,6 @@
         changed_fields = self.applyData(self.context, **data)
         changed_fields = changed_fields.values()
-        fields_string = '+'.join(' + '.join(str(i) for i in b) for b in changed_fields)
+        fields_string = '+'.join(
+            ' + '.join(str(i) for i in b) for b in changed_fields)
         self.context._p_changed = True
         form = self.request.form
@@ -782,5 +772,4 @@
         'screening_score',
         )
-    form_fields['passport'].custom_widget = EncodingImageFileWidget
     form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
     grok.template('form_edit')
@@ -788,5 +777,4 @@
     title = u'Your Application Form'
 
-
     def emitLockMessage(self):
         self.flash('The requested form is locked (read-only).')
@@ -802,19 +790,5 @@
         if upload:
             # We got a fresh upload
-            image = createWAeUPImageFile(upload.filename, upload)
-            # This would normally be totally illegal. We set context
-            # data without the complete form data being
-            # verified. Normally data is set in methods like `save`
-            # only, which is only called when all form data is correct
-            # (and if not, the context is not touched). With images
-            # and their uploads the problem then is, that the uploaded
-            # image won't be shown in a partially erranous form
-            # because the new uploaded image is still not part of the
-            # context obeject and the image widget cannot work around
-            # that (it has no access to the new data). For this reason
-            # we set the image here and not in 'save()' or other
-            # methods.
-            self.context.passport = image
-            upload.seek(0)
+            handle_img_upload(upload, self.context)
             self.passport_changed = True
         super(EditApplicantStudent, self).update()
@@ -824,13 +798,8 @@
         if not self.request.form.get('confirm_passport', False):
             return 'Passport confirmation box not ticked.'
-        #if len(self.errors) > 0:
-        #    return 'Form has errors.'
         return False
 
     @grok.action('Save')
     def save(self, **data):
-        #if self.context.locked:
-        #    self.emitLockMessage()
-        #    return
         self.applyData(self.context, **data)
         self.context._p_changed = True
@@ -840,7 +809,4 @@
     @grok.action('Final Submit')
     def finalsubmit(self, **data):
-        #if self.context.locked:
-        #    self.emitLockMessage()
-        #    return
         self.applyData(self.context, **data)
         self.context._p_changed = True
@@ -868,2 +834,20 @@
     text = 'View application record'
     target = 'index'
+
+class PassportImage(grok.View):
+    """Renders the passport image for applicants.
+    """
+    grok.name('passport.jpg')
+    grok.context(IApplicant)
+    grok.require('waeup.handleApplication')
+
+    def render(self):
+        # A filename chooser turns a context into a filename suitable
+        # for file storage.
+        image = getUtility(IExtFileStore).getFileByContext(self.context)
+        self.response.setHeader(
+            'Content-Type', 'image/jpeg')
+        if image is None:
+            # show placeholder image
+            return open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'rb')
+        return image
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_display.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_display.pt	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_display.pt	(revision 7063)
@@ -13,4 +13,7 @@
 <table class="zebra">
   <tbody>
+    <tr><td></td>
+    <td><img src="" tal:attributes="src view/passport_url" /></td>
+    </tr>
     <tal:block repeat="widget view/widgets">
       <tr>
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_edit.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_edit.pt	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_edit.pt	(revision 7063)
@@ -46,4 +46,12 @@
         </tr>
       </tal:block>
+      <tr>
+        <td class="label">Photograph</td>
+        <td class="field">
+          <span class="widget">
+            <img src="passport.jpg" /><br />
+            <input type="file" name="form.passport" />
+          </span>
+      </tr>
       <tr tal:condition="view/manage_applications">
         <td class="label"><label>Application Transition:</label></td>
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py	(revision 7063)
@@ -29,11 +29,9 @@
 
 from zope import schema
-from zope.interface import Interface, Attribute, provider
+from zope.interface import Interface, Attribute
 from zope.component import getUtilitiesFor
 from zope.pluggableauth.interfaces import IPrincipalInfo
 from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
 from zc.sourcefactory.basic import BasicSourceFactory
-from waeup.sirp.image import createWAeUPImageFile
-from waeup.sirp.image.schema import ImageFile
 from waeup.sirp.interfaces import IWAeUPObject, year_range
 from waeup.sirp.university.vocabularies import application_categories
@@ -66,24 +64,4 @@
     return True
 
-@provider(schema.interfaces.IContextAwareDefaultFactory)
-def default_passport_image(context):
-    """A default value factory for ImageFile fields.
-
-    Returns some default image as WAeUPImageFile. We cannot set the
-    default directly in ImageFile fields, as, if we want to set
-    max_size or min_size as well, some utility lookups are needed
-    which are not possible during startup.
-
-    Developers which use IContextAwareDefaultFactories like this one
-    always should make sure that the delivered default meets all
-    constraints of the field that makes use of this default value
-    provider.
-    """
-    imagefile = createWAeUPImageFile(
-        'placeholder_m.jpg',
-        open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'r')
-        )
-    return imagefile
-
 class ApplicantContainerProviderSource(BasicSourceFactory):
     """A source offering all available applicants container types.
@@ -370,12 +348,12 @@
         required = False,
         )
-    passport = ImageFile(
-        title = u'Passport Photograph',
-        #default = DEFAULT_PASSPORT_IMAGE_MALE,
-        defaultFactory = default_passport_image,
-        description = u'Maximun file size is 20 kB.',
-        required = True,
-        max_size = 20480,
-        )
+    #passport = ImageFile(
+    #    title = u'Passport Photograph',
+    #    #default = DEFAULT_PASSPORT_IMAGE_MALE,
+    #    defaultFactory = default_passport_image,
+    #    description = u'Maximun file size is 20 kB.',
+    #    required = True,
+    #    max_size = 20480,
+    #    )
 
     #
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/root.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/root.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/root.py	(revision 7063)
@@ -47,10 +47,5 @@
         """
         user = get_current_principal()
-        if user is None:
-            user = 'system'
-        elif user.title == 'Applicant':
-            user = 'applicant'
-        else:
-            user = user.id
+        user = getattr(user, 'id', 'system')
         self.logger.info('%s - %s - %s - %s' % (
                 user, target, ob_class, comment))
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_applicant.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_applicant.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_applicant.py	(revision 7063)
@@ -22,26 +22,19 @@
 """Tests for applicants and related.
 """
+import grok
 import unittest
-from StringIO import StringIO
-from hurry.file.interfaces import IFileRetrieval
-from zope.component import (
-    provideAdapter, adapts, getGlobalSiteManager, provideUtility)
-from zope.component.hooks import setSite
+from zope.component import adapts, queryUtility
 from zope.component.interfaces import IFactory
 from zope.interface import verify, implements
 from zope.location.interfaces import ILocation
-from zope.publisher.base import TestRequest
-from zope.publisher.interfaces import NotFound
-from waeup.sirp.app import University
-from waeup.sirp.image import WAeUPImageFile, createWAeUPImageFile
 from waeup.sirp.image.interfaces import IWAeUPImageFile
+from waeup.sirp.imagestorage import DefaultStorage
+from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser
 from waeup.sirp.applicants import (
-    ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser,
+    ResultEntry, Applicant, ApplicantFactory, get_regno_or_ac,
+    ApplicantImageStoreHandler, ApplicantImageNameChooser,
     )
-from waeup.sirp.applicants.interfaces import (
-    IResultEntry, IApplicant,
-    )
-from waeup.sirp.imagestorage import ImageStorageFileRetrieval
-from waeup.sirp.testing import (FunctionalTestCase, FunctionalLayer)
+from waeup.sirp.applicants.interfaces import IResultEntry, IApplicant
+from waeup.sirp.testing import FunctionalTestCase, FunctionalLayer
 
 class FakeImageLocation(object):
@@ -51,4 +44,109 @@
         pass
 
+class HelperTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_get_regno_or_ac(self):
+        # we can get reg_no or access_code of an applicants if it is set
+        appl1 = Applicant()
+        appl2 = Applicant()
+        appl2.reg_no = u'foo'
+        appl3 = Applicant()
+        appl3.access_code = u'bar'
+        appl4 = Applicant()
+        appl4.reg_no = u'foo'
+        appl4.access_code = u'bar'
+        self.assertTrue(
+            get_regno_or_ac(appl1) is None)
+        self.assertEqual(
+            get_regno_or_ac(appl2), u'foo')
+        self.assertEqual(
+            get_regno_or_ac(appl3), u'bar')
+        self.assertEqual(
+            get_regno_or_ac(appl4), u'foo')
+        return
+
+    def test_image_store_handler_util_accessible(self):
+        # we can get an IFileStoreHandler utility for applicants
+        handler = queryUtility(IFileStoreHandler, name='img-applicant')
+        self.assertTrue(
+            isinstance(handler, ApplicantImageStoreHandler))
+        return
+
+    def test_image_store_handler(self):
+        store = DefaultStorage()
+        handler = queryUtility(IFileStoreHandler, name='img-applicant')
+        result1 = handler.pathFromFileID(
+            store, '/fake-root', '__img-applicant__sample.jpg')
+        result2 = handler.pathFromFileID(
+            store, '/fake-root', '__img-applicant__dir1/sample.jpg')
+        result3 = handler.pathFromFileID(
+            store, '/fake-root', '__img-applicant__dir1/dir2/sample.jpg')
+        self.assertEqual(
+            result1, '/fake-root/applicants/sample.jpg')
+        self.assertEqual(
+            result2, '/fake-root/applicants/dir1/sample.jpg')
+        self.assertEqual(
+            result3, '/fake-root/applicants/dir1/dir2/sample.jpg')
+        return
+
+class ApplicantImageNameChooserTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_iface(self):
+        # make sure we implement promised interfaces
+        obj = ApplicantImageNameChooser(None) # needs a context normally
+        verify.verifyClass(IFileStoreNameChooser, ApplicantImageNameChooser)
+        verify.verifyObject(IFileStoreNameChooser, obj)
+        return
+
+    def test_name_chooser_available(self):
+        # we can get a name chooser for applicant objects as adapter
+        appl = Applicant()
+        chooser = IFileStoreNameChooser(appl)
+        self.assertTrue(chooser is not None)
+        return
+
+    def test_name_chooser_applicant_wo_container(self):
+        # we can get an image filename for applicants not in a container
+        appl = Applicant()
+        appl.reg_no = u'MY_REG_NO'
+        chooser = IFileStoreNameChooser(appl)
+        result = chooser.chooseName()
+        # the file would be stored in a ``_default`` directory.
+        self.assertEqual(
+            result, '__img-applicant___default/MY_REG_NO.jpg')
+        return
+
+    def test_name_chooser_applicant_w_container(self):
+        appl = Applicant()
+        appl.reg_no = u'MY_REG_NO'
+        fake_container = grok.Container()
+        fake_container.__name__ = 'folder'
+        fake_container['appl'] = appl
+        appl.__parent__ = fake_container
+        chooser = IFileStoreNameChooser(appl)
+        result = chooser.chooseName()
+        self.assertEqual(
+            result, '__img-applicant__folder/MY_REG_NO.jpg')
+        return
+
+    def test_name_chooser_check_name(self):
+        # we can check file ids for applicants
+        appl = Applicant()
+        appl.reg_no = u'MY_REG_NO'
+        fake_container = grok.Container()
+        fake_container.__name__ = 'folder'
+        fake_container['appl'] = appl
+        appl.__parent__ = fake_container
+        chooser = IFileStoreNameChooser(appl)
+        result1 = chooser.checkName('foo')
+        result2 = chooser.checkName('__img-applicant__folder/MY_REG_NO.jpg')
+        self.assertEqual(result1, False)
+        self.assertEqual(result2, True)
+        return
+
 class ResultEntryTest(unittest.TestCase):
 
@@ -87,27 +185,4 @@
         return
 
-    def test_passport_no_site(self):
-        # make sure we get a real image stored in passport attr
-        image = self.applicant.passport.file.read()
-        self.assertEqual(len(image), 2059)
-        return
-
-    def test_passport_insite(self):
-        # make sure we get a real image in passport attr when inside a site.
-        # When an applicant is created 'inside a site', its passport
-        # photograph should be put inside the images folder of the
-        # site, even if it is only a placeholder.
-        self.getRootFolder()['app'] = University()
-        app = self.getRootFolder()['app']
-        setSite(app)
-        applicant = Applicant()
-        image = self.applicant.passport.file.read()
-        self.assertEqual(len(image), 2059)
-        # The image contains a file_id instead of real image-data
-        self.assertEqual(self.applicant.passport.data,
-                         u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
-        assert u'b48a1d39bbcb32e955d9ff2dea4ed0e6' in app['images'].keys()
-        return
-
 class ApplicantFactoryTest(FunctionalTestCase):
 
@@ -116,7 +191,4 @@
     def setUp(self):
         super(ApplicantFactoryTest, self).setUp()
-        # Install a IFileRetrieval utility that returns WAeUPImageFiles.
-        storage = ImageStorageFileRetrieval()
-        provideUtility(storage, IFileRetrieval)
         self.factory = ApplicantFactory()
         return
@@ -137,65 +209,2 @@
         implemented_by = self.factory.getInterfaces()
         assert implemented_by.isOrExtends(IApplicant)
-
-
-class ApplicantTraverserTest(FunctionalTestCase):
-
-    layer = FunctionalLayer
-
-    def setUp(self):
-        super(ApplicantTraverserTest, self).setUp()
-        provideAdapter(FakeImageLocation)
-        self.applicant = Applicant()
-
-        self.request = TestRequest('')
-        return
-
-    def tearDown(self):
-        gsm = getGlobalSiteManager()
-        gsm.unregisterAdapter(FakeImageLocation)
-        super(ApplicantTraverserTest, self).tearDown()
-        return
-
-    def test_traverse_wo_passport(self):
-        # Ask for some attribute not provided
-        traverser = ApplicantTraverser(
-            self.applicant, self.request
-            )
-        self.assertRaises(
-            NotFound,
-            traverser.publishTraverse, self.request, 'passport'
-            )
-        return
-
-    def test_traverse_wo_image_passport_jpg(self):
-        # Ask for applicant pic if we didn't provided one
-        # We get a placeholder.
-        traverser = ApplicantTraverser(
-            self.applicant, self.request
-            )
-        result = traverser.publishTraverse(self.request, 'passport.jpg')
-        self.assertTrue(isinstance(result, FakeImageLocation))
-        return
-
-    def test_traverse_w_image_passport_jpg(self):
-        # Ask for applicant pic that's named 'passport.jpg'
-        traverser = ApplicantTraverser(
-            self.applicant, self.request
-            )
-        self.applicant.passport = createWAeUPImageFile(
-            'nofile.jpg', StringIO('no-content'))
-        self.applicant.passport.filename = 'mypic.jpg'
-        result = traverser.publishTraverse(self.request, 'passport.jpg')
-        self.assertTrue(isinstance(result, FakeImageLocation))
-        return
-
-    def test_traverse_w_image_some_jpg(self):
-        # Ask for applicant pic by correct name
-        traverser = ApplicantTraverser(
-            self.applicant, self.request
-            )
-        self.applicant.passport = WAeUPImageFile('nofile.jpg', '')
-        self.applicant.passport.filename = 'mypic.jpg'
-        result = traverser.publishTraverse(self.request, 'mypic.jpg')
-        self.assertTrue(isinstance(result, FakeImageLocation))
-        return
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py	(revision 7063)
@@ -24,5 +24,5 @@
 import unittest
 from zope.authentication.interfaces import IAuthentication
-from zope.component import provideAdapter, getUtility
+from zope.component import provideAdapter, getUtility, queryUtility
 from zope.component.hooks import setSite
 from zope.interface import verify
@@ -35,9 +35,10 @@
 from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
 from waeup.sirp.app import University
+from waeup.sirp.interfaces import IAuthPluginUtility
 from waeup.sirp.applicants import  ApplicantsContainer, Applicant
 from waeup.sirp.applicants.authentication import (
     ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin,
     ApplicantCredentials, AuthenticatedApplicantPrincipalFactory,
-    ApplicantPrincipalInfo, ApplicantPrincipal,)
+    ApplicantPrincipalInfo, ApplicantPrincipal, ApplicantsAuthUtility)
 
 
@@ -367,14 +368,45 @@
         return
 
-def test_suite():
-    suite = unittest.TestSuite()
-    for testcase in [
-        AuthenticatorPluginTest, CredentialsPluginTest,
-        ApplicantCredentialsTest, PrincipalFactoryTest,
-        PAUSetupTest,
-        ]:
-        suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
-                testcase
-                )
-        )
-    return suite
+class FakePAU(object):
+    credentialsPlugins = ()
+    authenticatorPlugins = ()
+
+class ApplicantsAuthUtilityTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_ifaces(self):
+        # make sure we fullfill the interface promises
+        obj = ApplicantsAuthUtility()
+        verify.verifyClass(IAuthPluginUtility, ApplicantsAuthUtility)
+        verify.verifyObject(IAuthPluginUtility, obj)
+        return
+
+    def test_utility_available(self):
+        # we can get an applicant auth utility by lookup
+        util = queryUtility(IAuthPluginUtility,
+                            name='applicants_auth_setup')
+        self.assertTrue(util is not None)
+        return
+
+    def test_register(self):
+        # make sure we can register additional components
+        pau = FakePAU()
+        result = ApplicantsAuthUtility().register(pau)
+        self.assertEqual(
+            result.credentialsPlugins, ('applicant_credentials',))
+        self.assertEqual(
+            result.authenticatorPlugins, ('applicants',))
+        return
+
+    def test_unregister(self):
+        # make sure we can unregister applicant auth components
+        pau = FakePAU()
+        util = ApplicantsAuthUtility()
+        pau = util.register(pau)
+        result = util.unregister(pau)
+        self.assertEqual(
+            result.credentialsPlugins, ())
+        self.assertEqual(
+            result.authenticatorPlugins, ())
+        return
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py	(revision 7063)
@@ -28,5 +28,5 @@
 from datetime import datetime, date, timedelta
 from mechanize import LinkNotFoundError
-from zope.component import createObject
+from zope.component import createObject, getUtility
 from zope.component.hooks import setSite, clearSite
 from zope.security.interfaces import Unauthorized
@@ -37,4 +37,5 @@
 from waeup.sirp.applicants.container import ApplicantsContainer
 from waeup.sirp.applicants.applicant import Applicant
+from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser
 from waeup.sirp.university.faculty import Faculty
 from waeup.sirp.university.department import Department
@@ -350,9 +351,9 @@
         self.browser.getControl(name="SUBMIT").click()
         pin = self.pins[2]
-        appl = self.getRootFolder()['app']['applicants']['app2009']
-        appl = appl[pin]
-        passp = appl.passport
-        passp_len = len(passp.file.read())
-        self.assertEqual(passp_len, PH_LEN)
+        #appl = self.getRootFolder()['app']['applicants']['app2009']
+        #appl = appl[pin]
+        #passp = appl.passport
+        #passp_len = len(passp.file.read())
+        #self.assertEqual(passp_len, PH_LEN)
         #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg')
         image_url = "%s/%s" % (self.browser.url, 'passport.jpg')
@@ -639,12 +640,12 @@
         # There is a correct <img> link included
         self.assertTrue(
-            '<img src="placeholder_m.jpg" />' in self.browser.contents)
+            '<img src="passport.jpg" />' in self.browser.contents)
         # Browsing the link shows a real image
-        self.browser.open(self.image_url('placeholder_m.jpg'))
+        self.browser.open(self.image_url('passport.jpg'))
         self.assertEqual(
             self.browser.headers['content-type'], 'image/jpeg')
         self.assertEqual(len(self.browser.contents), PH_LEN)
 
-    def test_after_login_default_stored_in_imagestorage(self):
+    def DISABLEDtest_after_login_default_stored_in_imagestorage(self):
         # After login the applicants placeholder image is stored in
         # an imagestorage
@@ -672,27 +673,10 @@
         # There is a correct <img> link included
         self.assertTrue(
-            '<img src="placeholder_m.jpg" />' in self.browser.contents)
+            '<img src="passport.jpg" />' in self.browser.contents)
         # Browsing the link shows a real image
-        self.browser.open(self.image_url('placeholder_m.jpg'))
+        self.browser.open(self.image_url('passport.jpg'))
         self.assertEqual(
             self.browser.headers['content-type'], 'image/jpeg')
         self.assertEqual(len(self.browser.contents), PH_LEN)
-
-    def test_after_submit_default_stored_in_imagestorage(self):
-        # After submitting an applicant form the default image is
-        # correctly stored in an imagestorage
-        self.login()
-        self.browser.getControl("Save").click() # submit form
-        storage = self.app['images']
-        self.assertEqual(
-            [x for x in storage.keys()],
-            [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
-        file_id = self.applicant.passport.data
-        self.assertEqual(
-            file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
-        # The stored image can be fetched
-        fd = storage.retrieveFile(file_id)
-        file_len = len(fd.read())
-        self.assertEqual(file_len, PH_LEN)
 
     def test_uploaded_image_browsable_w_errors(self):
@@ -709,7 +693,7 @@
         # There is a correct <img> link included
         self.assertTrue(
-            '<img src="myphoto.jpg" />' in self.browser.contents)
+            '<img src="passport.jpg" />' in self.browser.contents)
         # Browsing the link shows a real image
-        self.browser.open(self.image_url('myphoto.jpg'))
+        self.browser.open(self.image_url('passport.jpg'))
         self.assertEqual(
             self.browser.headers['content-type'], 'image/jpeg')
@@ -726,15 +710,10 @@
         file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
         self.browser.getControl("Save").click() # submit form
-        storage = self.app['images']
-        self.assertTrue(
-            u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
-        # The stored image can be fetched
-        fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
-        file_len = len(fd.read())
-        self.assertEqual(file_len, 31)
-        # The image uploaded belongs to the applicant
-        file_id = self.applicant.passport.data
+        storage = getUtility(IExtFileStore)
+        file_id = IFileStoreNameChooser(self.applicant).chooseName()
+        pseudo_image.seek(0) # reset our file data source
         self.assertEqual(
-            file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
+            storage.getFile(file_id).read(), pseudo_image.read())
+        return
 
     def test_uploaded_image_browsable_wo_errors(self):
@@ -751,7 +730,7 @@
         # There is a correct <img> link included
         self.assertTrue(
-            '<img src="myphoto.jpg" />' in self.browser.contents)
+            '<img src="passport.jpg" />' in self.browser.contents)
         # Browsing the link shows a real image
-        self.browser.open(self.image_url('myphoto.jpg'))
+        self.browser.open(self.image_url('passport.jpg'))
         self.assertEqual(
             self.browser.headers['content-type'], 'image/jpeg')
@@ -769,16 +748,10 @@
         file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
         self.browser.getControl("Save").click() # submit form
-        storage = self.app['images']
-        self.assertTrue(
-            u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
+        storage = getUtility(IExtFileStore)
+        file_id = IFileStoreNameChooser(self.applicant).chooseName()
         # The stored image can be fetched
-        fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
-        #fd = storage.retrieveFile(file_id)
+        fd = storage.getFile(file_id)
         file_len = len(fd.read())
         self.assertEqual(file_len, 31)
-        # The image uploaded belongs to the applicant
-        file_id = self.applicant.passport.data
-        self.assertEqual(
-            file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
 
     def test_uploaded_images_equal(self):
@@ -797,12 +770,14 @@
         file_ctrl = ctrl.mech_control
         file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
-        passport0 = self.applicant.passport
+        file_id = IFileStoreNameChooser(self.applicant).chooseName()
+        setSite(self.app)
+        passport0 = getUtility(IExtFileStore).getFile(file_id)
         self.browser.getControl("Save").click() # submit form with changed pic
-        passport1 = self.applicant.passport
+        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
         self.browser.getControl("Save").click() # submit form w/o changes
-        passport2 = self.applicant.passport
+        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
+        self.assertTrue(passport0 is None)
         self.assertTrue(passport0 != passport1)
         self.assertTrue(passport1 == passport2)
-        self.assertTrue(passport1 is passport2)
         return
 
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_interfaces.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_interfaces.py	(revision 7063)
@@ -25,12 +25,7 @@
 import unittest
 from zc.sourcefactory.browser.source import FactoredTerms
-from zope.browser.interfaces import ITerms
-from zope.component import getMultiAdapter
-from zope.interface.verify import verifyClass, verifyObject
 from zope.publisher.browser import TestRequest
-from waeup.sirp.applicants import interfaces
-from waeup.sirp.applicants.vocabularies import (
-    APPLICATION_TYPES, application_types_vocab, GenderSource,
-    )
+from waeup.sirp.applicants.vocabularies import application_types_vocab
+from waeup.sirp.students.vocabularies import GenderSource
 
 class ApplicationCategoriesTestCase(unittest.TestCase):
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_root.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_root.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_root.py	(revision 7063)
@@ -39,6 +39,5 @@
     )
 from waeup.sirp.testing import (
-    FunctionalLayer, FunctionalTestCase, get_all_loggers, remove_new_loggers,
-    remove_logger)
+    FunctionalLayer, FunctionalTestCase, remove_logger)
 
 
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/vocabularies.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/vocabularies.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/vocabularies.py	(revision 7063)
@@ -1,13 +1,8 @@
 """Vocabularies and sources for the application section.
 """
-from datetime import datetime
 from zope.component import getUtility
 from zope.catalog.interfaces import ICatalog
-from zc.sourcefactory.basic import BasicSourceFactory
-from zc.sourcefactory.contextual import BasicContextualSourceFactory
 from waeup.sirp.interfaces import SimpleWAeUPVocabulary
-from waeup.sirp.students.lgas import LGAS
-from waeup.sirp.students.vocabularies import (
-    CertificateSource, GenderSource)
+from waeup.sirp.students.vocabularies import CertificateSource
 
 #: Types of applications we support.
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py	(revision 6915)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py	(revision 7063)
@@ -2,5 +2,4 @@
 """
 import grok
-from datetime import datetime
 from hurry.workflow.workflow import Transition, WorkflowState, NullCondition
 from hurry.workflow.interfaces import IWorkflowState, IWorkflowTransitionEvent
