Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 16609)
@@ -2924,5 +2924,6 @@
 
 class StudentFilesUploadPage(KofaPage):
-    """ View to upload files by student
+    """ View to upload passport picture by student. The class name
+    is historical. The page is only used for the student's portrait.
     """
     grok.context(IStudent)
@@ -2940,4 +2941,24 @@
             return
         super(StudentFilesUploadPage, self).update()
+        return
+
+class StudentSignatureUploadPage(KofaPage):
+    """ View to upload scanned signature by student. Not active
+    in base package.
+    """
+    grok.context(IStudent)
+    grok.name('change_signature')
+    grok.require('waeup.uploadStudentFile')
+    grok.template('filesuploadpage')
+    label = _('Upload signature')
+    pnav = 4
+
+    def update(self):
+        SIGNATURE_CHANGE_STATES = getUtility(IStudentsUtils).SIGNATURE_CHANGE_STATES
+        if self.context.student.state not in SIGNATURE_CHANGE_STATES:
+            emit_lock_message(self,
+                _('This form is locked. You are in the wrong state.'))
+            return
+        super(StudentSignatureUploadPage, self).update()
         return
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/basemanagepage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/basemanagepage.pt	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/basemanagepage.pt	(revision 16609)
@@ -10,5 +10,5 @@
     <li>
       <a href="#tab2" data-toggle="tab">
-        <span i18n:translate="">Passport Picture</span>
+        <span i18n:translate="">Files</span>
       </a>
     </li>
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/imagedisplay.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/imagedisplay.pt	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/imagedisplay.pt	(revision 16609)
@@ -1,3 +1,3 @@
-<tr>
+<tr tal:condition="viewlet/file_exists">
   <td class="fieldname">
         <span tal:replace="viewlet/label">FILENAME</span>:
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/imageupload.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/imageupload.pt	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/imageupload.pt	(revision 16609)
@@ -4,13 +4,14 @@
   </td>
   <td>
-    <img align="middle" height="125px"
-         tal:attributes="src python: view.url(context, viewlet.download_name)" />
-    &nbsp;
-    <input type="submit" tal:condition="viewlet/file_exists"
-     tal:attributes="id python:('delete_%s' % viewlet.input_name);
-        name python:('delete_%s' % viewlet.input_name)"
-     value="Delete" class="btn btn-danger" />
-    <br /><br />
-
+    <tal:file_exists condition="viewlet/file_exists">
+      <img align="middle" height="125px"
+           tal:attributes="src python: view.url(context, viewlet.download_name)" />
+      &nbsp;
+      <input type="submit"
+       tal:attributes="id python:('delete_%s' % viewlet.input_name);
+          name python:('delete_%s' % viewlet.input_name)"
+       value="Delete" class="btn btn-danger" />
+      <br /><br />
+    </tal:file_exists>
 		<div class="input-group">
 			<div class="input-group-btn">
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/fileviewlets.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/fileviewlets.py	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/fileviewlets.py	(revision 16609)
@@ -37,5 +37,5 @@
     StudentBaseDisplayFormPage, StudentBaseManageFormPage,
     StudentClearanceDisplayFormPage, StudentClearanceManageFormPage,
-    ExportPDFClearanceSlip, StudentFilesUploadPage)
+    ExportPDFClearanceSlip, StudentFilesUploadPage, StudentSignatureUploadPage)
 
 grok.context(IKofaObject) # Make IKofaObject the default context
@@ -83,5 +83,5 @@
 
 
-# File viewlets for student base page
+# File viewlets for student base and clearance page
 
 class PassportDisplay(StudentFileDisplay):
@@ -115,8 +115,45 @@
 
 
+class SignatureDisplay(StudentFileDisplay):
+    """Signature display viewlet.
+    """
+    grok.order(2)
+    grok.context(IStudent)
+    grok.view(StudentBaseDisplayFormPage)
+    grok.require('waeup.viewStudent')
+    grok.template('imagedisplay')
+    label = _(u'Scanned Signature')
+    download_name = u'signature.jpg'
+
+
+class SignatureUploadManage(StudentFileUpload):
+    """Signature upload viewlet for officers.
+    """
+    grok.order(2)
+    grok.context(IStudent)
+    grok.view(StudentBaseManageFormPage)
+    grok.require('waeup.manageStudent')
+    grok.template('imageupload')
+    label = _(u'Scanned Signature (jpg only)')
+    download_name = u'signature.jpg'
+    tab_redirect = '#tab2'
+
+    @property
+    def mus(self):
+        kofa_utils = getUtility(IKofaUtils)
+        return kofa_utils.MAX_PASSPORT_SIZE
+
+
 class PassportUploadEdit(PassportUploadManage):
     """Passport upload viewlet for students.
     """
     grok.view(StudentFilesUploadPage)
+    grok.require('waeup.uploadStudentFile')
+
+
+class SignatureUploadEdit(SignatureUploadManage):
+    """Signature upload viewlet for students.
+    """
+    grok.view(StudentSignatureUploadPage)
     grok.require('waeup.uploadStudentFile')
 
@@ -152,4 +189,10 @@
     grok.context(IStudent)
 
+class Signature(StudentImage):
+    """Renders jpeg signature.
+    """
+    grok.name('signature.jpg')
+    download_name = u'signature.jpg'
+    grok.context(IStudent)
 
 class ApplicationSlipImage(StudentImage):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 16609)
@@ -681,4 +681,35 @@
         # Only the Application Slip does not exist
         self.assertFalse('Application Slip' in self.browser.contents)
+        return
+
+    def test_upload_files(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.manage_student_path)
+        self.browser.getControl(name="form.firstname").value = 'John'
+        self.browser.getControl(name="form.lastname").value = 'Tester'
+        self.browser.getControl(name="form.reg_number").value = '345'
+        self.browser.getControl(name="password").value = 'secret'
+        self.browser.getControl(name="control_password").value = 'secret'
+        self.browser.getControl("Save").click()
+        self.assertMatches('...Form has been saved...',
+                           self.browser.contents)
+        ctrl = self.browser.getControl(name='passportuploadmanage')
+        file_obj = open(SAMPLE_IMAGE, 'rb')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
+        self.browser.getControl(
+            name='upload_passportuploadmanage').click()
+        self.assertTrue(
+            'src="http://localhost/app/students/K1000000/passport.jpg"'
+            in self.browser.contents)
+        ctrl = self.browser.getControl(name='signatureuploadmanage')
+        file_obj = open(SAMPLE_IMAGE, 'rb')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(file_obj, filename='my_signature.jpg')
+        self.browser.getControl(
+            name='upload_signatureuploadmanage').click()
+        self.assertTrue(
+            'src="http://localhost/app/students/K1000000/signature.jpg"'
+            in self.browser.contents)
         return
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 16609)
@@ -1242,4 +1242,8 @@
     PORTRAIT_CHANGE_STATES = (ADMITTED,)
 
+    #: A tuple containing the names of registration states in which changing of
+    #: scanned signatures is allowed.
+    SIGNATURE_CHANGE_STATES = ()
+
     #: A tuple containing all exporter names referring to students or
     #: subobjects thereof.
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py	(revision 16608)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/viewlets.py	(revision 16609)
@@ -623,4 +623,21 @@
         return self.view.url(self.view.context, self.target)
 
+class StudentSignatureActionButton(ManageActionButton):
+    grok.order(4)
+    grok.context(IStudent)
+    grok.view(StudentBaseDisplayFormPage)
+    grok.require('waeup.handleStudent')
+    icon = 'actionicon_signature.png'
+    text = _('Change signature')
+    target = 'change_signature'
+
+    @property
+    def target_url(self):
+        SIGNATURE_CHANGE_STATES = getUtility(
+            IStudentsUtils).SIGNATURE_CHANGE_STATES
+        if self.context.state not in SIGNATURE_CHANGE_STATES:
+            return ''
+        return self.view.url(self.view.context, self.target)
+
 
 class StudentClearanceStartActionButton(ManageActionButton):
