Ignore:
Timestamp:
9 Nov 2011, 15:42:45 (13 years ago)
Author:
uli
Message:

Merge changes from branch ulif-extimgstore back into trunk.
Beside external image storage also waeupdocs should work again.

Location:
main/waeup.sirp/trunk
Files:
16 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.sirp/trunk

  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/__init__.py

    r6632 r7063  
    33# Make this a package.
    44from waeup.sirp.applicants.applicant import (
    5     ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser,
     5    ResultEntry, Applicant, ApplicantFactory, ApplicantImageStoreHandler,
     6    get_regno_or_ac, ApplicantImageNameChooser,
    67    )
    78from waeup.sirp.applicants.container import ApplicantsContainer
     
    1415    'Applicant',
    1516    'ApplicantFactory',
     17    'ApplicantImageNameChooser',
     18    'ApplicantImageStoreHandler',
    1619    'ApplicantsContainer',
    1720    'ApplicantsRoot',
    18     'ApplicantTraverser',
    1921    'application_exists',
    2022    'get_applicant_data',
     23    'get_regno_or_ac',
    2124    ]
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py

    r6632 r7063  
    2020## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    2121##
     22import os
    2223import grok
    2324from grok import index
     
    2526from zope.interface import implementedBy
    2627from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
    27 from waeup.sirp.interfaces import IObjectHistory
    2828from waeup.sirp.app import University
     29from waeup.sirp.image import WAeUPImageFile
     30from waeup.sirp.imagestorage import DefaultFileStoreHandler
     31from waeup.sirp.interfaces import (
     32    IObjectHistory, IFileStoreHandler, IFileStoreNameChooser)
     33from waeup.sirp.utils.helpers import attrs_to_fields
    2934from waeup.sirp.applicants.interfaces import (
    30     IResultEntry, IApplicant, IApplicantEdit, default_passport_image,
     35    IResultEntry, IApplicant, IApplicantEdit,
    3136    )
    32 from waeup.sirp.utils.helpers import attrs_to_fields
     37
     38
     39def get_regno_or_ac(context):
     40    reg_no = getattr(context, 'reg_no', None)
     41    if reg_no is None:
     42        return getattr(context, 'access_code', None)
     43    return reg_no
    3344
    3445class ResultEntry(grok.Context):
     
    4960        return
    5061
    51     #def getApplicantsRootLogger(self):
    52     #    return grok.getSite()['applicants'].logger
    53 
    5462    def loggerInfo(self, ob_class, comment=None):
    5563        target = self.__name__
     
    8189    access_code = index.Field(attribute='access_code')
    8290
    83 class ApplicantTraverser(grok.Traverser):
    84     """Get image of the context applicant.
    85 
    86     Each applicant can provide a passport photograph which will be
    87     returned by this traverser if:
    88 
    89     - we request the exact filename of the picture or
    90 
    91     - ask for a picture named 'passport.jpg'.
    92 
    93     If no picture was stored yet, we get a placeholder image when
    94     asking for `passport.jpg`.
    95 
    96     If none of the above applies, we return ``None``, most probably
    97     resulting a :exc:`NotFound` exception.
    98 
    99     """
    100     grok.context(IApplicant)
    101     def traverse(self, name):
    102         passport_filename = getattr(self.context.passport, 'filename', None)
    103         if name == passport_filename:
    104             return self.context.passport
    105         if name == 'passport.jpg':
    106             if self.context.passport is not None:
    107                 return self.context.passport
    108         return
    109 
    11091class ApplicantFactory(grok.GlobalUtility):
    11192    """A factory for applicants.
     
    121102    def getInterfaces(self):
    122103        return implementedBy(Applicant)
     104
     105
     106#: The file id marker for applicant passport images
     107APPLICANT_IMAGE_STORE_NAME = 'img-applicant'
     108
     109class ApplicantImageNameChooser(grok.Adapter):
     110    """A file id chooser for :class:`Applicant` objects.
     111
     112    `context` is an :class:`Applicant` instance.
     113
     114    The :class:`ApplicantImageNameChooser` can build/check file ids
     115    for :class:`Applicant` objects suitable for use with
     116    :class:`ExtFileStore` instances. The delivered file_id contains
     117    the file id marker for :class:`Applicant` object and the
     118    registration number or access code of the context applicant. Also
     119    the name of the connected applicant container will be part of the
     120    generated file id.
     121
     122    This chooser is registered as an adapter providing
     123    :class:`waeup.sirp.interfaces.IFileStoreNameChooser`.
     124
     125    File store name choosers like this one are only convenience
     126    components to ease the task of creating file ids for applicant
     127    objects. You are nevertheless encouraged to use them instead of
     128    manually setting up filenames for applicants.
     129
     130    .. seealso:: :mod:`waeup.sirp.imagestorage`
     131
     132    """
     133    grok.context(IApplicant)
     134    grok.implements(IFileStoreNameChooser)
     135
     136    def checkName(self, name=None):
     137        """Check whether the given name is a valid file id for the context.
     138
     139        Returns ``True`` only if `name` equals the result of
     140        :meth:`chooseName`.
     141        """
     142        return name == self.chooseName()
     143
     144    def chooseName(self, name=None):
     145        """Get a valid file id for applicant context.
     146
     147        *Example:*
     148
     149        For an applicant with registration no. ``'My_reg_no_1234'``
     150        and stored in an applicants container called
     151        ``'mycontainer'``, this chooser would create:
     152
     153          ``'__img-applicant__mycontainer/My_reg_no_1234.jpg'``
     154
     155        meaning that the passport image of this applicant would be
     156        stored in the site-wide file storage in path:
     157
     158          ``mycontainer/My_reg_no_1234.jpg``
     159
     160        If the context applicant has no parent, ``'_default'`` is used
     161        as parent name.
     162        """
     163        parent_name = getattr(
     164            getattr(self.context, '__parent__', None),
     165            '__name__', '_default')
     166        marked_filename = '__%s__%s/%s.jpg' % (
     167            APPLICANT_IMAGE_STORE_NAME,
     168            parent_name, get_regno_or_ac(self.context))
     169        return marked_filename
     170
     171
     172class ApplicantImageStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
     173    """Applicant specific image handling.
     174
     175    This handler knows in which path in a filestore to store applicant
     176    images and how to turn this kind of data into some (browsable)
     177    file object.
     178
     179    It is called from the global file storage, when it wants to
     180    get/store a file with a file id starting with
     181    ``__img-applicant__`` (the marker string for applicant images).
     182
     183    Like each other file store handler it does not handle the files
     184    really (this is done by the global file store) but only computes
     185    paths and things like this.
     186    """
     187    grok.implements(IFileStoreHandler)
     188    grok.name(APPLICANT_IMAGE_STORE_NAME)
     189
     190    def pathFromFileID(self, store, root, file_id):
     191        """All applicants images are filed in directory ``applicants``.
     192        """
     193        marker, filename, basename, ext = store.extractMarker(file_id)
     194        return os.path.join(root, 'applicants', filename)
     195
     196    def createFile(self, store, root, filename, file_id, file):
     197        """Create a browsable file-like object.
     198        """
     199        # possible other actions: check for jpeg format
     200        path = self.pathFromFileID(store, root, file_id)
     201        return file, path, WAeUPImageFile(filename, file_id)
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/authentication.py

    r6670 r7063  
    232232        if ac.state == 'used' and appl_ac != ac.representation:
    233233            return None
    234         if appl_ac is not None and appl_ac != ac.representation:
    235             # This should never happen. It means a catalog error.
    236             return None
     234        # If the following fails we have a catalog error. Bad enough
     235        # to pull emergency break.
     236        assert appl_ac is None or appl_ac == ac.representation
    237237        return ApplicantPrincipalInfo(accesscode)
    238238
     
    256256    systems (regular users, officers, etc.) were set up.
    257257    """
    258     grok.provides(IAuthPluginUtility)
     258    grok.implements(IAuthPluginUtility)
    259259    grok.name('applicants_auth_setup')
    260260
     
    285285
    286286    def unregister(self, pau):
     287        """Unregister applicant specific authentication components from PAU.
     288        """
     289        pau.credentialsPlugins = tuple(
     290            [x for x in list(pau.credentialsPlugins)
     291             if x != 'applicant_credentials'])
     292        pau.authenticatorPlugins = tuple(
     293            [x for x in list(pau.authenticatorPlugins)
     294             if x != 'applicants'])
    287295        return pau
    288296
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py

    r6816 r7063  
    2222"""UI components for basic applicants and related components.
    2323"""
     24import os
    2425import sys
    2526import grok
     
    5354    ManageActionButton, PrimaryNavTab, LeftSidebarLink
    5455    )
    55 from waeup.sirp.image.browser.widget import (
    56     ThumbnailWidget, EncodingImageFileWidget,
    57     )
    58 from waeup.sirp.image.image import createWAeUPImageFile
    59 from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
     56from waeup.sirp.interfaces import (
     57    IWAeUPObject, ILocalRolesAssignable, IExtFileStore, IFileStoreNameChooser)
    6058from waeup.sirp.permissions import get_users_with_local_roles
    6159from waeup.sirp.university.interfaces import ICertificate
     
    6866from waeup.sirp.applicants.interfaces import (
    6967    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
    70     IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
     68    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
     69    IMAGE_PATH,
    7170    )
    7271from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
     
    425424        # If application has ended and applicant record doesn't exist,
    426425        # logout without marking AC as used
    427         if not pin in self.context.keys() and self.context.enddate < date.today():
     426        if not pin in self.context.keys() and (
     427            self.context.enddate < date.today()):
    428428            self.flash('Application has ended.')
    429429            auth = getUtility(IAuthentication)
     
    433433
    434434        # Mark AC as used (this also fires a pin related transition)
    435         if get_access_code(pin).state == USED:
    436             pass
    437         else:
     435        if get_access_code(pin).state != USED:
    438436            comment = u"AC invalidated"
    439437            # Here we know that the ac is in state initialized so we do not
     
    514512            applicant_object = get_applicant_data(access_code)
    515513            return absoluteURL(applicant_object, self.request) + self.target
    516         #else:
    517         #    return ''
     514        return ''
    518515
    519516class AccessCodeEditLink(AccessCodeViewLink):
     
    534531                return ''
    535532            return absoluteURL(applicant_object, self.request) + self.target
    536         #else:
    537         #    return ''
     533        return ''
    538534
    539535class AccessCodeSlipLink(AccessCodeViewLink):
     
    550546    form_fields = grok.AutoFields(IApplicant).omit(
    551547        'locked').omit('course_admitted')
    552     #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
    553     form_fields['passport'].custom_widget = ThumbnailWidget
    554548    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
    555549    label = 'Applicant'
    556550    grok.template('form_display')
    557551    pnav = 3
     552
     553    def update(self):
     554        self.passport_url = self.url(self.context, 'passport.jpg')
     555        return
    558556
    559557    @property
     
    582580class PDFActionButton(ManageActionButton):
    583581    grok.context(IApplicant)
    584     #grok.view(DisplayApplicant)
    585582    grok.require('waeup.manageApplications')
    586583    icon = 'actionicon_pdf.png'
     
    624621
    625622    def render(self):
    626         # (0,0),(-1,-1) = whole table
    627         # (0,0),(0,-1) = first column
    628         # (-1,0),(-1,-1) = last column
    629         # (0,0),(-1,0) = first row
    630         # (0,-1),(-1,-1) = last row
    631623        SLIP_STYLE = TableStyle(
    632624            [('VALIGN',(0,0),(-1,-1),'TOP')]
     
    652644        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
    653645        story.append(Paragraph(self.label, style["Heading2"]))
    654         #story.append(HRFlowable())
    655646        story.append(Spacer(1, 18))
    656647        for msg in self.context.history.messages:
     
    658649            story.append(Paragraph(f_msg, style["Normal"]))
    659650        story.append(Spacer(1, 24))
     651
     652        data = []
     653        # insert passport photograph
     654        img = getUtility(IExtFileStore).getFileByContext(self.context)
     655        if img is None:
     656            img = open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'rb')
     657        doc_img = Image(img.name, width=4*cm, height=3*cm, kind='bound')
     658        data.append(['', doc_img])
     659
     660        # render widget fields
    660661        self.setUpWidgets()
    661         data = []
    662662        for widget in self.widgets:
    663663            f_label = '<font size=12>%s</font>:' % widget.label.strip()
    664664            f_label = Paragraph(f_label, style["Normal"])
    665             if widget.name != 'form.passport':
    666                 f_text = '<font size=12>%s</font>' % widget()
    667                 f_text = Paragraph(f_text, style["Normal"])
    668                 data.append([f_label,f_text])
    669             else:
    670                 filename = widget._data.file.name
    671                 im = Image(filename,width=4*cm, height=3*cm,kind='bound')
    672                 data.append([f_label,im])
     665            f_text = '<font size=12>%s</font>' % widget()
     666            f_text = Paragraph(f_text, style["Normal"])
     667            data.append([f_label,f_text])
    673668        f_label = '<font size=12>Admitted Course of Study:</font>'
    674669        f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
     
    698693    target = 'edit_full'
    699694
     695def handle_img_upload(upload, context):
     696    """Handle upload of applicant image.
     697    """
     698    store = getUtility(IExtFileStore)
     699    file_id = IFileStoreNameChooser(context).chooseName()
     700    store.createFile(file_id, upload)
     701    upload.seek(0) # XXX: really neccessary?
     702    return
     703
    700704class EditApplicantFull(WAeUPEditFormPage):
    701705    """A full edit view for applicant data.
     
    705709    grok.require('waeup.manageApplications')
    706710    form_fields = grok.AutoFields(IApplicant)
    707     form_fields['passport'].custom_widget = EncodingImageFileWidget
    708711    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
    709712    grok.template('form_edit')
     
    718721        if upload:
    719722            # We got a fresh upload
    720             image = createWAeUPImageFile(upload.filename, upload)
    721             # This would normally be totally illegal. We set context
    722             # data without the complete form data being
    723             # verified. Normally data is set in methods like `save`
    724             # only, which is only called when all form data is correct
    725             # (and if not, the context is not touched). With images
    726             # and their uploads the problem then is, that the uploaded
    727             # image won't be shown in a partially erranous form
    728             # because the new uploaded image is still not part of the
    729             # context obeject and the image widget cannot work around
    730             # that (it has no access to the new data). For this reason
    731             # we set the image here and not in 'save()' or other
    732             # methods.
    733             self.context.passport = image
    734             upload.seek(0)
     723            handle_img_upload(upload, self.context)
    735724            self.passport_changed = True
    736725        return
     
    760749        changed_fields = self.applyData(self.context, **data)
    761750        changed_fields = changed_fields.values()
    762         fields_string = '+'.join(' + '.join(str(i) for i in b) for b in changed_fields)
     751        fields_string = '+'.join(
     752            ' + '.join(str(i) for i in b) for b in changed_fields)
    763753        self.context._p_changed = True
    764754        form = self.request.form
     
    782772        'screening_score',
    783773        )
    784     form_fields['passport'].custom_widget = EncodingImageFileWidget
    785774    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
    786775    grok.template('form_edit')
     
    788777    title = u'Your Application Form'
    789778
    790 
    791779    def emitLockMessage(self):
    792780        self.flash('The requested form is locked (read-only).')
     
    802790        if upload:
    803791            # We got a fresh upload
    804             image = createWAeUPImageFile(upload.filename, upload)
    805             # This would normally be totally illegal. We set context
    806             # data without the complete form data being
    807             # verified. Normally data is set in methods like `save`
    808             # only, which is only called when all form data is correct
    809             # (and if not, the context is not touched). With images
    810             # and their uploads the problem then is, that the uploaded
    811             # image won't be shown in a partially erranous form
    812             # because the new uploaded image is still not part of the
    813             # context obeject and the image widget cannot work around
    814             # that (it has no access to the new data). For this reason
    815             # we set the image here and not in 'save()' or other
    816             # methods.
    817             self.context.passport = image
    818             upload.seek(0)
     792            handle_img_upload(upload, self.context)
    819793            self.passport_changed = True
    820794        super(EditApplicantStudent, self).update()
     
    824798        if not self.request.form.get('confirm_passport', False):
    825799            return 'Passport confirmation box not ticked.'
    826         #if len(self.errors) > 0:
    827         #    return 'Form has errors.'
    828800        return False
    829801
    830802    @grok.action('Save')
    831803    def save(self, **data):
    832         #if self.context.locked:
    833         #    self.emitLockMessage()
    834         #    return
    835804        self.applyData(self.context, **data)
    836805        self.context._p_changed = True
     
    840809    @grok.action('Final Submit')
    841810    def finalsubmit(self, **data):
    842         #if self.context.locked:
    843         #    self.emitLockMessage()
    844         #    return
    845811        self.applyData(self.context, **data)
    846812        self.context._p_changed = True
     
    868834    text = 'View application record'
    869835    target = 'index'
     836
     837class PassportImage(grok.View):
     838    """Renders the passport image for applicants.
     839    """
     840    grok.name('passport.jpg')
     841    grok.context(IApplicant)
     842    grok.require('waeup.handleApplication')
     843
     844    def render(self):
     845        # A filename chooser turns a context into a filename suitable
     846        # for file storage.
     847        image = getUtility(IExtFileStore).getFileByContext(self.context)
     848        self.response.setHeader(
     849            'Content-Type', 'image/jpeg')
     850        if image is None:
     851            # show placeholder image
     852            return open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'rb')
     853        return image
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_display.pt

    r6350 r7063  
    1313<table class="zebra">
    1414  <tbody>
     15    <tr><td></td>
     16    <td><img src="" tal:attributes="src view/passport_url" /></td>
     17    </tr>
    1518    <tal:block repeat="widget view/widgets">
    1619      <tr>
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/form_edit.pt

    r6756 r7063  
    4646        </tr>
    4747      </tal:block>
     48      <tr>
     49        <td class="label">Photograph</td>
     50        <td class="field">
     51          <span class="widget">
     52            <img src="passport.jpg" /><br />
     53            <input type="file" name="form.passport" />
     54          </span>
     55      </tr>
    4856      <tr tal:condition="view/manage_applications">
    4957        <td class="label"><label>Application Transition:</label></td>
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py

    r6915 r7063  
    2929
    3030from zope import schema
    31 from zope.interface import Interface, Attribute, provider
     31from zope.interface import Interface, Attribute
    3232from zope.component import getUtilitiesFor
    3333from zope.pluggableauth.interfaces import IPrincipalInfo
    3434from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
    3535from zc.sourcefactory.basic import BasicSourceFactory
    36 from waeup.sirp.image import createWAeUPImageFile
    37 from waeup.sirp.image.schema import ImageFile
    3836from waeup.sirp.interfaces import IWAeUPObject, year_range
    3937from waeup.sirp.university.vocabularies import application_categories
     
    6664    return True
    6765
    68 @provider(schema.interfaces.IContextAwareDefaultFactory)
    69 def default_passport_image(context):
    70     """A default value factory for ImageFile fields.
    71 
    72     Returns some default image as WAeUPImageFile. We cannot set the
    73     default directly in ImageFile fields, as, if we want to set
    74     max_size or min_size as well, some utility lookups are needed
    75     which are not possible during startup.
    76 
    77     Developers which use IContextAwareDefaultFactories like this one
    78     always should make sure that the delivered default meets all
    79     constraints of the field that makes use of this default value
    80     provider.
    81     """
    82     imagefile = createWAeUPImageFile(
    83         'placeholder_m.jpg',
    84         open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'r')
    85         )
    86     return imagefile
    87 
    8866class ApplicantContainerProviderSource(BasicSourceFactory):
    8967    """A source offering all available applicants container types.
     
    370348        required = False,
    371349        )
    372     passport = ImageFile(
    373         title = u'Passport Photograph',
    374         #default = DEFAULT_PASSPORT_IMAGE_MALE,
    375         defaultFactory = default_passport_image,
    376         description = u'Maximun file size is 20 kB.',
    377         required = True,
    378         max_size = 20480,
    379         )
     350    #passport = ImageFile(
     351    #    title = u'Passport Photograph',
     352    #    #default = DEFAULT_PASSPORT_IMAGE_MALE,
     353    #    defaultFactory = default_passport_image,
     354    #    description = u'Maximun file size is 20 kB.',
     355    #    required = True,
     356    #    max_size = 20480,
     357    #    )
    380358
    381359    #
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/root.py

    r6582 r7063  
    4747        """
    4848        user = get_current_principal()
    49         if user is None:
    50             user = 'system'
    51         elif user.title == 'Applicant':
    52             user = 'applicant'
    53         else:
    54             user = user.id
     49        user = getattr(user, 'id', 'system')
    5550        self.logger.info('%s - %s - %s - %s' % (
    5651                user, target, ob_class, comment))
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_applicant.py

    r6657 r7063  
    2222"""Tests for applicants and related.
    2323"""
     24import grok
    2425import unittest
    25 from StringIO import StringIO
    26 from hurry.file.interfaces import IFileRetrieval
    27 from zope.component import (
    28     provideAdapter, adapts, getGlobalSiteManager, provideUtility)
    29 from zope.component.hooks import setSite
     26from zope.component import adapts, queryUtility
    3027from zope.component.interfaces import IFactory
    3128from zope.interface import verify, implements
    3229from zope.location.interfaces import ILocation
    33 from zope.publisher.base import TestRequest
    34 from zope.publisher.interfaces import NotFound
    35 from waeup.sirp.app import University
    36 from waeup.sirp.image import WAeUPImageFile, createWAeUPImageFile
    3730from waeup.sirp.image.interfaces import IWAeUPImageFile
     31from waeup.sirp.imagestorage import DefaultStorage
     32from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser
    3833from waeup.sirp.applicants import (
    39     ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser,
     34    ResultEntry, Applicant, ApplicantFactory, get_regno_or_ac,
     35    ApplicantImageStoreHandler, ApplicantImageNameChooser,
    4036    )
    41 from waeup.sirp.applicants.interfaces import (
    42     IResultEntry, IApplicant,
    43     )
    44 from waeup.sirp.imagestorage import ImageStorageFileRetrieval
    45 from waeup.sirp.testing import (FunctionalTestCase, FunctionalLayer)
     37from waeup.sirp.applicants.interfaces import IResultEntry, IApplicant
     38from waeup.sirp.testing import FunctionalTestCase, FunctionalLayer
    4639
    4740class FakeImageLocation(object):
     
    5144        pass
    5245
     46class HelperTests(FunctionalTestCase):
     47
     48    layer = FunctionalLayer
     49
     50    def test_get_regno_or_ac(self):
     51        # we can get reg_no or access_code of an applicants if it is set
     52        appl1 = Applicant()
     53        appl2 = Applicant()
     54        appl2.reg_no = u'foo'
     55        appl3 = Applicant()
     56        appl3.access_code = u'bar'
     57        appl4 = Applicant()
     58        appl4.reg_no = u'foo'
     59        appl4.access_code = u'bar'
     60        self.assertTrue(
     61            get_regno_or_ac(appl1) is None)
     62        self.assertEqual(
     63            get_regno_or_ac(appl2), u'foo')
     64        self.assertEqual(
     65            get_regno_or_ac(appl3), u'bar')
     66        self.assertEqual(
     67            get_regno_or_ac(appl4), u'foo')
     68        return
     69
     70    def test_image_store_handler_util_accessible(self):
     71        # we can get an IFileStoreHandler utility for applicants
     72        handler = queryUtility(IFileStoreHandler, name='img-applicant')
     73        self.assertTrue(
     74            isinstance(handler, ApplicantImageStoreHandler))
     75        return
     76
     77    def test_image_store_handler(self):
     78        store = DefaultStorage()
     79        handler = queryUtility(IFileStoreHandler, name='img-applicant')
     80        result1 = handler.pathFromFileID(
     81            store, '/fake-root', '__img-applicant__sample.jpg')
     82        result2 = handler.pathFromFileID(
     83            store, '/fake-root', '__img-applicant__dir1/sample.jpg')
     84        result3 = handler.pathFromFileID(
     85            store, '/fake-root', '__img-applicant__dir1/dir2/sample.jpg')
     86        self.assertEqual(
     87            result1, '/fake-root/applicants/sample.jpg')
     88        self.assertEqual(
     89            result2, '/fake-root/applicants/dir1/sample.jpg')
     90        self.assertEqual(
     91            result3, '/fake-root/applicants/dir1/dir2/sample.jpg')
     92        return
     93
     94class ApplicantImageNameChooserTests(FunctionalTestCase):
     95
     96    layer = FunctionalLayer
     97
     98    def test_iface(self):
     99        # make sure we implement promised interfaces
     100        obj = ApplicantImageNameChooser(None) # needs a context normally
     101        verify.verifyClass(IFileStoreNameChooser, ApplicantImageNameChooser)
     102        verify.verifyObject(IFileStoreNameChooser, obj)
     103        return
     104
     105    def test_name_chooser_available(self):
     106        # we can get a name chooser for applicant objects as adapter
     107        appl = Applicant()
     108        chooser = IFileStoreNameChooser(appl)
     109        self.assertTrue(chooser is not None)
     110        return
     111
     112    def test_name_chooser_applicant_wo_container(self):
     113        # we can get an image filename for applicants not in a container
     114        appl = Applicant()
     115        appl.reg_no = u'MY_REG_NO'
     116        chooser = IFileStoreNameChooser(appl)
     117        result = chooser.chooseName()
     118        # the file would be stored in a ``_default`` directory.
     119        self.assertEqual(
     120            result, '__img-applicant___default/MY_REG_NO.jpg')
     121        return
     122
     123    def test_name_chooser_applicant_w_container(self):
     124        appl = Applicant()
     125        appl.reg_no = u'MY_REG_NO'
     126        fake_container = grok.Container()
     127        fake_container.__name__ = 'folder'
     128        fake_container['appl'] = appl
     129        appl.__parent__ = fake_container
     130        chooser = IFileStoreNameChooser(appl)
     131        result = chooser.chooseName()
     132        self.assertEqual(
     133            result, '__img-applicant__folder/MY_REG_NO.jpg')
     134        return
     135
     136    def test_name_chooser_check_name(self):
     137        # we can check file ids for applicants
     138        appl = Applicant()
     139        appl.reg_no = u'MY_REG_NO'
     140        fake_container = grok.Container()
     141        fake_container.__name__ = 'folder'
     142        fake_container['appl'] = appl
     143        appl.__parent__ = fake_container
     144        chooser = IFileStoreNameChooser(appl)
     145        result1 = chooser.checkName('foo')
     146        result2 = chooser.checkName('__img-applicant__folder/MY_REG_NO.jpg')
     147        self.assertEqual(result1, False)
     148        self.assertEqual(result2, True)
     149        return
     150
    53151class ResultEntryTest(unittest.TestCase):
    54152
     
    87185        return
    88186
    89     def test_passport_no_site(self):
    90         # make sure we get a real image stored in passport attr
    91         image = self.applicant.passport.file.read()
    92         self.assertEqual(len(image), 2059)
    93         return
    94 
    95     def test_passport_insite(self):
    96         # make sure we get a real image in passport attr when inside a site.
    97         # When an applicant is created 'inside a site', its passport
    98         # photograph should be put inside the images folder of the
    99         # site, even if it is only a placeholder.
    100         self.getRootFolder()['app'] = University()
    101         app = self.getRootFolder()['app']
    102         setSite(app)
    103         applicant = Applicant()
    104         image = self.applicant.passport.file.read()
    105         self.assertEqual(len(image), 2059)
    106         # The image contains a file_id instead of real image-data
    107         self.assertEqual(self.applicant.passport.data,
    108                          u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
    109         assert u'b48a1d39bbcb32e955d9ff2dea4ed0e6' in app['images'].keys()
    110         return
    111 
    112187class ApplicantFactoryTest(FunctionalTestCase):
    113188
     
    116191    def setUp(self):
    117192        super(ApplicantFactoryTest, self).setUp()
    118         # Install a IFileRetrieval utility that returns WAeUPImageFiles.
    119         storage = ImageStorageFileRetrieval()
    120         provideUtility(storage, IFileRetrieval)
    121193        self.factory = ApplicantFactory()
    122194        return
     
    137209        implemented_by = self.factory.getInterfaces()
    138210        assert implemented_by.isOrExtends(IApplicant)
    139 
    140 
    141 class ApplicantTraverserTest(FunctionalTestCase):
    142 
    143     layer = FunctionalLayer
    144 
    145     def setUp(self):
    146         super(ApplicantTraverserTest, self).setUp()
    147         provideAdapter(FakeImageLocation)
    148         self.applicant = Applicant()
    149 
    150         self.request = TestRequest('')
    151         return
    152 
    153     def tearDown(self):
    154         gsm = getGlobalSiteManager()
    155         gsm.unregisterAdapter(FakeImageLocation)
    156         super(ApplicantTraverserTest, self).tearDown()
    157         return
    158 
    159     def test_traverse_wo_passport(self):
    160         # Ask for some attribute not provided
    161         traverser = ApplicantTraverser(
    162             self.applicant, self.request
    163             )
    164         self.assertRaises(
    165             NotFound,
    166             traverser.publishTraverse, self.request, 'passport'
    167             )
    168         return
    169 
    170     def test_traverse_wo_image_passport_jpg(self):
    171         # Ask for applicant pic if we didn't provided one
    172         # We get a placeholder.
    173         traverser = ApplicantTraverser(
    174             self.applicant, self.request
    175             )
    176         result = traverser.publishTraverse(self.request, 'passport.jpg')
    177         self.assertTrue(isinstance(result, FakeImageLocation))
    178         return
    179 
    180     def test_traverse_w_image_passport_jpg(self):
    181         # Ask for applicant pic that's named 'passport.jpg'
    182         traverser = ApplicantTraverser(
    183             self.applicant, self.request
    184             )
    185         self.applicant.passport = createWAeUPImageFile(
    186             'nofile.jpg', StringIO('no-content'))
    187         self.applicant.passport.filename = 'mypic.jpg'
    188         result = traverser.publishTraverse(self.request, 'passport.jpg')
    189         self.assertTrue(isinstance(result, FakeImageLocation))
    190         return
    191 
    192     def test_traverse_w_image_some_jpg(self):
    193         # Ask for applicant pic by correct name
    194         traverser = ApplicantTraverser(
    195             self.applicant, self.request
    196             )
    197         self.applicant.passport = WAeUPImageFile('nofile.jpg', '')
    198         self.applicant.passport.filename = 'mypic.jpg'
    199         result = traverser.publishTraverse(self.request, 'mypic.jpg')
    200         self.assertTrue(isinstance(result, FakeImageLocation))
    201         return
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py

    r6730 r7063  
    2424import unittest
    2525from zope.authentication.interfaces import IAuthentication
    26 from zope.component import provideAdapter, getUtility
     26from zope.component import provideAdapter, getUtility, queryUtility
    2727from zope.component.hooks import setSite
    2828from zope.interface import verify
     
    3535from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
    3636from waeup.sirp.app import University
     37from waeup.sirp.interfaces import IAuthPluginUtility
    3738from waeup.sirp.applicants import  ApplicantsContainer, Applicant
    3839from waeup.sirp.applicants.authentication import (
    3940    ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin,
    4041    ApplicantCredentials, AuthenticatedApplicantPrincipalFactory,
    41     ApplicantPrincipalInfo, ApplicantPrincipal,)
     42    ApplicantPrincipalInfo, ApplicantPrincipal, ApplicantsAuthUtility)
    4243
    4344
     
    367368        return
    368369
    369 def test_suite():
    370     suite = unittest.TestSuite()
    371     for testcase in [
    372         AuthenticatorPluginTest, CredentialsPluginTest,
    373         ApplicantCredentialsTest, PrincipalFactoryTest,
    374         PAUSetupTest,
    375         ]:
    376         suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
    377                 testcase
    378                 )
    379         )
    380     return suite
     370class FakePAU(object):
     371    credentialsPlugins = ()
     372    authenticatorPlugins = ()
     373
     374class ApplicantsAuthUtilityTests(FunctionalTestCase):
     375
     376    layer = FunctionalLayer
     377
     378    def test_ifaces(self):
     379        # make sure we fullfill the interface promises
     380        obj = ApplicantsAuthUtility()
     381        verify.verifyClass(IAuthPluginUtility, ApplicantsAuthUtility)
     382        verify.verifyObject(IAuthPluginUtility, obj)
     383        return
     384
     385    def test_utility_available(self):
     386        # we can get an applicant auth utility by lookup
     387        util = queryUtility(IAuthPluginUtility,
     388                            name='applicants_auth_setup')
     389        self.assertTrue(util is not None)
     390        return
     391
     392    def test_register(self):
     393        # make sure we can register additional components
     394        pau = FakePAU()
     395        result = ApplicantsAuthUtility().register(pau)
     396        self.assertEqual(
     397            result.credentialsPlugins, ('applicant_credentials',))
     398        self.assertEqual(
     399            result.authenticatorPlugins, ('applicants',))
     400        return
     401
     402    def test_unregister(self):
     403        # make sure we can unregister applicant auth components
     404        pau = FakePAU()
     405        util = ApplicantsAuthUtility()
     406        pau = util.register(pau)
     407        result = util.unregister(pau)
     408        self.assertEqual(
     409            result.credentialsPlugins, ())
     410        self.assertEqual(
     411            result.authenticatorPlugins, ())
     412        return
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py

    r6816 r7063  
    2828from datetime import datetime, date, timedelta
    2929from mechanize import LinkNotFoundError
    30 from zope.component import createObject
     30from zope.component import createObject, getUtility
    3131from zope.component.hooks import setSite, clearSite
    3232from zope.security.interfaces import Unauthorized
     
    3737from waeup.sirp.applicants.container import ApplicantsContainer
    3838from waeup.sirp.applicants.applicant import Applicant
     39from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser
    3940from waeup.sirp.university.faculty import Faculty
    4041from waeup.sirp.university.department import Department
     
    350351        self.browser.getControl(name="SUBMIT").click()
    351352        pin = self.pins[2]
    352         appl = self.getRootFolder()['app']['applicants']['app2009']
    353         appl = appl[pin]
    354         passp = appl.passport
    355         passp_len = len(passp.file.read())
    356         self.assertEqual(passp_len, PH_LEN)
     353        #appl = self.getRootFolder()['app']['applicants']['app2009']
     354        #appl = appl[pin]
     355        #passp = appl.passport
     356        #passp_len = len(passp.file.read())
     357        #self.assertEqual(passp_len, PH_LEN)
    357358        #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg')
    358359        image_url = "%s/%s" % (self.browser.url, 'passport.jpg')
     
    639640        # There is a correct <img> link included
    640641        self.assertTrue(
    641             '<img src="placeholder_m.jpg" />' in self.browser.contents)
     642            '<img src="passport.jpg" />' in self.browser.contents)
    642643        # Browsing the link shows a real image
    643         self.browser.open(self.image_url('placeholder_m.jpg'))
     644        self.browser.open(self.image_url('passport.jpg'))
    644645        self.assertEqual(
    645646            self.browser.headers['content-type'], 'image/jpeg')
    646647        self.assertEqual(len(self.browser.contents), PH_LEN)
    647648
    648     def test_after_login_default_stored_in_imagestorage(self):
     649    def DISABLEDtest_after_login_default_stored_in_imagestorage(self):
    649650        # After login the applicants placeholder image is stored in
    650651        # an imagestorage
     
    672673        # There is a correct <img> link included
    673674        self.assertTrue(
    674             '<img src="placeholder_m.jpg" />' in self.browser.contents)
     675            '<img src="passport.jpg" />' in self.browser.contents)
    675676        # Browsing the link shows a real image
    676         self.browser.open(self.image_url('placeholder_m.jpg'))
     677        self.browser.open(self.image_url('passport.jpg'))
    677678        self.assertEqual(
    678679            self.browser.headers['content-type'], 'image/jpeg')
    679680        self.assertEqual(len(self.browser.contents), PH_LEN)
    680 
    681     def test_after_submit_default_stored_in_imagestorage(self):
    682         # After submitting an applicant form the default image is
    683         # correctly stored in an imagestorage
    684         self.login()
    685         self.browser.getControl("Save").click() # submit form
    686         storage = self.app['images']
    687         self.assertEqual(
    688             [x for x in storage.keys()],
    689             [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
    690         file_id = self.applicant.passport.data
    691         self.assertEqual(
    692             file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
    693         # The stored image can be fetched
    694         fd = storage.retrieveFile(file_id)
    695         file_len = len(fd.read())
    696         self.assertEqual(file_len, PH_LEN)
    697681
    698682    def test_uploaded_image_browsable_w_errors(self):
     
    709693        # There is a correct <img> link included
    710694        self.assertTrue(
    711             '<img src="myphoto.jpg" />' in self.browser.contents)
     695            '<img src="passport.jpg" />' in self.browser.contents)
    712696        # Browsing the link shows a real image
    713         self.browser.open(self.image_url('myphoto.jpg'))
     697        self.browser.open(self.image_url('passport.jpg'))
    714698        self.assertEqual(
    715699            self.browser.headers['content-type'], 'image/jpeg')
     
    726710        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
    727711        self.browser.getControl("Save").click() # submit form
    728         storage = self.app['images']
    729         self.assertTrue(
    730             u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
    731         # The stored image can be fetched
    732         fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
    733         file_len = len(fd.read())
    734         self.assertEqual(file_len, 31)
    735         # The image uploaded belongs to the applicant
    736         file_id = self.applicant.passport.data
     712        storage = getUtility(IExtFileStore)
     713        file_id = IFileStoreNameChooser(self.applicant).chooseName()
     714        pseudo_image.seek(0) # reset our file data source
    737715        self.assertEqual(
    738             file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
     716            storage.getFile(file_id).read(), pseudo_image.read())
     717        return
    739718
    740719    def test_uploaded_image_browsable_wo_errors(self):
     
    751730        # There is a correct <img> link included
    752731        self.assertTrue(
    753             '<img src="myphoto.jpg" />' in self.browser.contents)
     732            '<img src="passport.jpg" />' in self.browser.contents)
    754733        # Browsing the link shows a real image
    755         self.browser.open(self.image_url('myphoto.jpg'))
     734        self.browser.open(self.image_url('passport.jpg'))
    756735        self.assertEqual(
    757736            self.browser.headers['content-type'], 'image/jpeg')
     
    769748        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
    770749        self.browser.getControl("Save").click() # submit form
    771         storage = self.app['images']
    772         self.assertTrue(
    773             u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
     750        storage = getUtility(IExtFileStore)
     751        file_id = IFileStoreNameChooser(self.applicant).chooseName()
    774752        # The stored image can be fetched
    775         fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
    776         #fd = storage.retrieveFile(file_id)
     753        fd = storage.getFile(file_id)
    777754        file_len = len(fd.read())
    778755        self.assertEqual(file_len, 31)
    779         # The image uploaded belongs to the applicant
    780         file_id = self.applicant.passport.data
    781         self.assertEqual(
    782             file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
    783756
    784757    def test_uploaded_images_equal(self):
     
    797770        file_ctrl = ctrl.mech_control
    798771        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
    799         passport0 = self.applicant.passport
     772        file_id = IFileStoreNameChooser(self.applicant).chooseName()
     773        setSite(self.app)
     774        passport0 = getUtility(IExtFileStore).getFile(file_id)
    800775        self.browser.getControl("Save").click() # submit form with changed pic
    801         passport1 = self.applicant.passport
     776        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
    802777        self.browser.getControl("Save").click() # submit form w/o changes
    803         passport2 = self.applicant.passport
     778        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
     779        self.assertTrue(passport0 is None)
    804780        self.assertTrue(passport0 != passport1)
    805781        self.assertTrue(passport1 == passport2)
    806         self.assertTrue(passport1 is passport2)
    807782        return
    808783
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_interfaces.py

    r6340 r7063  
    2525import unittest
    2626from zc.sourcefactory.browser.source import FactoredTerms
    27 from zope.browser.interfaces import ITerms
    28 from zope.component import getMultiAdapter
    29 from zope.interface.verify import verifyClass, verifyObject
    3027from zope.publisher.browser import TestRequest
    31 from waeup.sirp.applicants import interfaces
    32 from waeup.sirp.applicants.vocabularies import (
    33     APPLICATION_TYPES, application_types_vocab, GenderSource,
    34     )
     28from waeup.sirp.applicants.vocabularies import application_types_vocab
     29from waeup.sirp.students.vocabularies import GenderSource
    3530
    3631class ApplicationCategoriesTestCase(unittest.TestCase):
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_root.py

    r6659 r7063  
    3939    )
    4040from waeup.sirp.testing import (
    41     FunctionalLayer, FunctionalTestCase, get_all_loggers, remove_new_loggers,
    42     remove_logger)
     41    FunctionalLayer, FunctionalTestCase, remove_logger)
    4342
    4443
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/vocabularies.py

    r6744 r7063  
    11"""Vocabularies and sources for the application section.
    22"""
    3 from datetime import datetime
    43from zope.component import getUtility
    54from zope.catalog.interfaces import ICatalog
    6 from zc.sourcefactory.basic import BasicSourceFactory
    7 from zc.sourcefactory.contextual import BasicContextualSourceFactory
    85from waeup.sirp.interfaces import SimpleWAeUPVocabulary
    9 from waeup.sirp.students.lgas import LGAS
    10 from waeup.sirp.students.vocabularies import (
    11     CertificateSource, GenderSource)
     6from waeup.sirp.students.vocabularies import CertificateSource
    127
    138#: Types of applications we support.
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py

    r6644 r7063  
    22"""
    33import grok
    4 from datetime import datetime
    54from hurry.workflow.workflow import Transition, WorkflowState, NullCondition
    65from hurry.workflow.interfaces import IWorkflowState, IWorkflowTransitionEvent
Note: See TracChangeset for help on using the changeset viewer.