Changeset 9217 for main/waeup.kofa/trunk/src/waeup/kofa/browser
- Timestamp:
- 21 Sep 2012, 11:21:05 (12 years ago)
- Location:
- main/waeup.kofa/trunk/src/waeup/kofa/browser
- Files:
-
- 9 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.kofa/trunk/src/waeup/kofa/browser/__init__.py
r7819 r9217 1 1 import os 2 2 3 from waeup.kofa.browser.layout import (4 KofaPage, KofaForm, KofaLayout, KofaDisplayFormPage, KofaEditFormPage,5 KofaAddFormPage, NullValidator)6 from waeup.kofa.browser.pages import ContactAdminForm3 #from waeup.kofa.browser.layout import ( 4 # KofaPage, KofaForm, KofaLayout, KofaDisplayFormPage, KofaEditFormPage, 5 # KofaAddFormPage, NullValidator) 6 #from waeup.kofa.browser.pages import ContactAdminForm 7 7 8 8 IMAGE_PATH = os.path.join( -
main/waeup.kofa/trunk/src/waeup/kofa/browser/breadcrumbs.py
r7819 r9217 26 26 IConfigurationContainer, ISessionConfiguration) 27 27 from waeup.kofa.interfaces import MessageFactory as _ 28 from waeup.kofa.browser import interfaces 29 from waeup.kofa.browser.interfaces import (IBreadcrumb, 30 IBreadcrumbIgnorable, IBreadcrumbContainer) 28 from waeup.kofa.browser.interfaces import ( 29 IBreadcrumb, IBreadcrumbIgnorable, IBreadcrumbContainer, IKofaObject, 30 IUniversity, IFacultiesContainer, IUsersContainer, IDataCenter, IFaculty, 31 IDepartment, ICourse, ICertificate, ICoursesContainer, ICertificateCourse, 32 ICertificatesContainer, 33 ) 31 34 32 35 class Breadcrumb(grok.Adapter): … … 34 37 """ 35 38 grok.provides(IBreadcrumb) 36 grok.context( interfaces.IKofaObject)39 grok.context(IKofaObject) 37 40 grok.name('index') 38 41 … … 87 90 """A breadcrumb for university index pages. 88 91 """ 89 grok.context( interfaces.IUniversity)92 grok.context(IUniversity) 90 93 title = _(u'Home') 91 94 parent = None … … 100 103 itself is bound to. 101 104 """ 102 grok.context( interfaces.IUniversity)105 grok.context(IUniversity) 103 106 grok.name('manage') 104 107 title = _(u'Portal Settings') … … 113 116 """A breadcrumb for faculty containers. 114 117 """ 115 grok.context( interfaces.IFacultiesContainer)118 grok.context(IFacultiesContainer) 116 119 title = _(u'Academics') 117 120 … … 119 122 """A breadcrumb for administration areas of University instances. 120 123 """ 121 grok.context( interfaces.IUniversity)124 grok.context(IUniversity) 122 125 grok.name('administration') 123 126 title = _(u'Administration') … … 145 148 """A breadcrumb for user containers. 146 149 """ 147 grok.context( interfaces.IUsersContainer)150 grok.context(IUsersContainer) 148 151 title = _(u'Portal Users') 149 152 parent_viewname = 'administration' … … 152 155 """A breadcrumb for data centers. 153 156 """ 154 grok.context( interfaces.IDataCenter)157 grok.context(IDataCenter) 155 158 title = _(u'Data Center') 156 159 parent_viewname = 'administration' … … 159 162 """A breadcrumb for faculties. 160 163 """ 161 grok.context( interfaces.IFaculty)164 grok.context(IFaculty) 162 165 163 166 @property … … 168 171 """A breadcrumb for departments. 169 172 """ 170 grok.context( interfaces.IDepartment)173 grok.context(IDepartment) 171 174 172 175 class CourseBreadcrumb(FacultyBreadcrumb): 173 176 """A breadcrumb for courses. 174 177 """ 175 grok.context( interfaces.ICourse)178 grok.context(ICourse) 176 179 177 180 class CertificateBreadcrumb(FacultyBreadcrumb): 178 181 """A breadcrumb for certificates. 179 182 """ 180 grok.context( interfaces.ICertificate)183 grok.context(ICertificate) 181 184 182 185 class CoursesContainerBreadcrumb(Breadcrumb): 183 186 """ We don't want course container breadcrumbs. 184 187 """ 185 grok.context( interfaces.ICoursesContainer)188 grok.context(ICoursesContainer) 186 189 grok.implements(IBreadcrumbIgnorable) 187 190 … … 189 192 """ We don't want course container breadcrumbs. 190 193 """ 191 grok.context( interfaces.ICertificatesContainer)194 grok.context(ICertificatesContainer) 192 195 grok.implements(IBreadcrumbIgnorable) 193 196 … … 195 198 """ We don't want course container breadcrumbs. 196 199 """ 197 grok.context( interfaces.ICertificateCourse)200 grok.context(ICertificateCourse) 198 201 @property 199 202 def title(self): -
main/waeup.kofa/trunk/src/waeup/kofa/browser/captcha.py
r8127 r9217 28 28 from zope.interface import Interface 29 29 from zope.publisher.interfaces.http import IHTTPRequest 30 from waeup.kofa.browser import KofaPage, resources 30 from waeup.kofa.browser import resources 31 from waeup.kofa.browser.layout import KofaPage 31 32 from waeup.kofa.browser.interfaces import ( 32 33 ICaptchaRequest, ICaptchaResponse, ICaptcha, ICaptchaConfig, -
main/waeup.kofa/trunk/src/waeup/kofa/browser/interfaces.py
r8257 r9217 21 21 from zope.interface import Interface, Attribute 22 22 from waeup.kofa.interfaces import ( 23 IKofaObject, IUniversity, IUsersContainer, IDataCenter) 23 IKofaObject, IUniversity, IUsersContainer, IDataCenter, validate_email) 24 from waeup.kofa.interfaces import MessageFactory as _ 24 25 from waeup.kofa.university.interfaces import ( 25 26 IFacultiesContainer, IFaculty, IFacultyAdd, IDepartment, IDepartmentAdd, … … 196 197 If no `title` is given, nothing will be rendered. 197 198 """ 199 200 class IChangePassword(IKofaObject): 201 """Interface needed for change pasword page. 202 203 """ 204 identifier = schema.TextLine( 205 title = _(u'Unique Identifier'), 206 description = _( 207 u'User Name, Student or Applicant Id, Matriculation or ' 208 u'Registration Number'), 209 required = True, 210 readonly = False, 211 ) 212 213 email = schema.ASCIILine( 214 title = _(u'Email Address'), 215 required = True, 216 constraint=validate_email, 217 ) 218 219 class IStudentNavigationBase(IKofaObject): 220 """Objects that provide student navigation (whatever it is) should 221 implement this interface. 222 """ 223 student = Attribute('''Some student object that has a ''' 224 ''' `display_fullname` attribute.''') 225 226 class IApplicantBase(IKofaObject): 227 """Some Applicant. 228 """ 229 display_fullname = Attribute('''Fullname.''') -
main/waeup.kofa/trunk/src/waeup/kofa/browser/layout.py
r9088 r9217 36 36 from waeup.kofa.interfaces import MessageFactory as _ 37 37 from waeup.kofa.utils.helpers import to_timezone 38 from waeup.kofa.browser.interfaces import ITheme 38 from waeup.kofa.browser.interfaces import ( 39 ITheme, IStudentNavigationBase, IApplicantBase) 39 40 from waeup.kofa.browser.theming import get_all_themes, KofaThemeBase 40 from waeup.kofa.students.interfaces import IStudentNavigation41 from waeup.kofa.applicants.interfaces import IApplicant42 41 from waeup.kofa.authentication import get_principal_role_manager 43 42 … … 288 287 """Return the student name. 289 288 """ 290 if IStudentNavigation .providedBy(self.context):289 if IStudentNavigationBase.providedBy(self.context): 291 290 return self.context.student.display_fullname 292 291 return … … 295 294 """Return the applicant name. 296 295 """ 297 if IApplicant.providedBy(self.context): 296 if IApplicantBase.providedBy(self.context): 297 # XXX: shouldn't that be `display_fullname???` 298 298 return self.context.fullname 299 299 return -
main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py
r9172 r9217 18 18 """ Viewing components for Kofa objects. 19 19 """ 20 import copy21 20 import csv 22 21 import grok … … 24 23 import re 25 24 import sys 26 import time27 import re28 25 from urllib import urlencode 29 26 from zope import schema … … 35 32 getUtilitiesFor, 36 33 ) 37 #from zope.component.interfaces import Invalid38 34 from zope.event import notify 39 from zope.securitypolicy.interfaces import ( 40 IPrincipalRoleManager, IPrincipalRoleMap) 35 from zope.securitypolicy.interfaces import IPrincipalRoleManager 41 36 from zope.session.interfaces import ISession 42 37 from zope.password.interfaces import IPasswordManager 43 from waeup.kofa.browser import (38 from waeup.kofa.browser.layout import ( 44 39 KofaPage, KofaForm, KofaEditFormPage, KofaAddFormPage, 45 40 KofaDisplayFormPage, NullValidator) … … 48 43 IDepartment, IDepartmentAdd, ICourse, ICourseAdd, ICertificate, 49 44 ICertificateAdd, ICertificateCourse, ICertificateCourseAdd, 50 ICaptchaManager )45 ICaptchaManager, IChangePassword) 51 46 from waeup.kofa.browser.layout import jsaction, action, UtilityView 52 from waeup.kofa.browser.resources import warning, datepicker, tabs, datatable 47 from waeup.kofa.browser.resources import ( 48 warning, tabs, datatable) 53 49 from waeup.kofa.interfaces import MessageFactory as _ 54 50 from waeup.kofa.interfaces import( … … 56 52 IKofaXMLImporter, IKofaXMLExporter, IBatchProcessor, 57 53 ILocalRolesAssignable, DuplicationError, IConfigurationContainer, 58 ISessionConfiguration, ISessionConfigurationAdd, 59 IPasswordValidator, IContactForm, IKofaUtils, ICSVExporter, 60 IChangePassword) 54 ISessionConfiguration, ISessionConfigurationAdd, IJobManager, 55 IPasswordValidator, IContactForm, IKofaUtils, ICSVExporter,) 61 56 from waeup.kofa.permissions import ( 62 57 get_users_with_local_roles, get_all_roles, get_all_users) 58 63 59 from waeup.kofa.students.catalog import search as searchstudents 64 60 from waeup.kofa.university.catalog import search 65 61 from waeup.kofa.university.vocabularies import course_levels 66 62 from waeup.kofa.authentication import LocalRoleSetEvent 67 #from waeup.kofa.widgets.restwidget import ReSTDisplayWidget68 63 from waeup.kofa.widgets.htmlwidget import HTMLDisplayWidget 69 from waeup.kofa.authentication import get_principal_role_manager70 64 from waeup.kofa.utils.helpers import get_user_account 71 65 from waeup.kofa.mandates.mandate import PasswordMandate … … 797 791 def delFiles(self, **data): 798 792 form = self.request.form 799 logger = self.context.logger800 793 if form.has_key('val_id'): 801 794 child_id = form['val_id'] … … 1196 1189 return False 1197 1190 1198 @property1199 def nextstep(self):1200 return self.url(self.context, '@@import4')1201 1202 1191 def update(self, headerfield=None, back2=None, cancel=None, proceed=None): 1203 1192 datatable.need() … … 1390 1379 label = _('Download portal data as CSV file') 1391 1380 pnav = 0 1392 export_button = _(u'Download') 1381 export_button = _(u'Create CSV file') 1382 _running_exports = None 1393 1383 1394 1384 def getExporters(self): … … 1398 1388 return sorted(title_name_tuples) 1399 1389 1400 def update(self, export=None, exporter=None): 1401 if None in (export, exporter): 1402 return 1403 self.redirect( 1404 self.url(self.context, 'export.csv') + '?exporter=%s' % exporter) 1390 def job_finished(self, status): 1391 return status == 'completed' 1392 1393 def getRunningExports(self): 1394 """Returns running exports as list of tuples. 1395 1396 Only exports triggered by the current user (identified by 1397 principal.id) are returned. 1398 1399 Each tuple has the form (<STATUS>, <STATUS_TITLE>, <EXPORTER_NAME>). 1400 1401 ``STATUS``: 1402 the status as machine readable string (something like 1403 ``'completed'``) 1404 1405 ``STATUS_TITLE``: 1406 status of export as translated string. 1407 1408 ``EXPORTER_NAME``: 1409 string representing the exporter title used when triggering 1410 the export job. 1411 """ 1412 if self._running_exports is None: 1413 self._running_exports = self._getRunningExports() 1414 return self._running_exports 1415 1416 def _getRunningExports(self): 1417 result = self.context.get_export_jobs_status(self.user_id) 1418 return result 1419 1420 def update(self, export=None, start_export=None, exporter=None, 1421 discard=None, download=None): 1422 self.user_id = self.request.principal.id 1423 if discard: 1424 myjobs = self.context.get_running_export_jobs(self.user_id) 1425 for entry in myjobs: 1426 self.context.delete_export_entry(entry) 1427 self.flash(_('Discarded export result')) 1428 return 1429 if download: 1430 myjobs = self.context.get_running_export_jobs(self.user_id) 1431 if not len(myjobs): 1432 self.flash(_('This export was already discarded.')) 1433 return 1434 job_id = myjobs[0][0] 1435 self.redirect( 1436 self.url(self.context, 'export.csv') + '?job_id=%s' % job_id) 1437 return 1438 if None in (start_export, exporter): 1439 return 1440 job_id = self.context.start_export_job( 1441 exporter, self.request.principal.id) 1442 self.redirect(self.url(self.context, 'export')) 1405 1443 return 1406 1444 … … 1410 1448 grok.require('waeup.manageDataCenter') 1411 1449 1412 def render(self, exporter=None): 1413 if exporter is None: 1414 return 1415 ob_class = self.__implemented__.__name__.replace('waeup.kofa.','') 1416 self.context.logger.info( 1417 '%s - exported: %s' % (ob_class, exporter)) 1418 exporter = getUtility(ICSVExporter, name=exporter) 1419 csv_data = exporter.export_all(grok.getSite()) 1420 #csv_data.seek(0) 1450 def render(self, exporter=None, job_id=None): 1451 1452 manager = getUtility(IJobManager) 1453 job = manager.get(job_id) 1454 if job is None: 1455 return 1456 if hasattr(job.result, 'traceback'): 1457 # XXX: Some error happened. Do something more approriate here... 1458 return 1459 path = job.result 1460 if not os.path.exists(path): 1461 # XXX: Do something more appropriate here... 1462 return 1463 result = open(path, 'rb').read() 1464 acronym = grok.getSite()['configuration'].acronym.replace(' ','') 1465 filename = "%s_%s" % (acronym, os.path.basename(path)) 1421 1466 self.response.setHeader( 1422 1467 'Content-Type', 'text/csv; charset=UTF-8') 1423 acronym = grok.getSite()['configuration'].acronym.replace(' ','')1424 filename = "%s%s.csv" % (acronym, exporter.title.title().replace(' ',''))1425 1468 self.response.setHeader( 1426 'Content-Disposition:', 'attachment; filename="%s' % filename) 1427 return csv_data 1469 'Content-Disposition', 'attachment; filename="%s' % filename) 1470 # remove job and running_exports entry from context 1471 self.context.delete_export_entry( 1472 self.context.entry_from_job_id(job_id)) 1473 return result 1428 1474 1429 1475 class ExportXMLPage(grok.View): -
main/waeup.kofa/trunk/src/waeup/kofa/browser/resources.py
r8126 r9217 302 302 waeup_kofa, 'recaptcha_white.js' 303 303 ) 304 305 #: A page reloader (reloads after 3 secs) 306 page_reloader = ResourceInclusion( 307 waeup_kofa, 'page_reloader.js', depends=[jquery]) 308 309 #: Disables page reloader (which is triggered by a JavaScript timer). 310 page_not_reloader = ResourceInclusion( 311 waeup_kofa, 'page_not_reloader.js', depends=[jquery]) 312 313 loadbar = ResourceInclusion( 314 waeup_kofa, 'loadbar.js', depends=[jquery]) -
main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/datacenterexportpage.pt
r7974 r9217 1 1 <div i18n:domain="waeup.kofa" class="row"> 2 <div class="span4 columns">3 2 <div class="span8 columns"> 4 <p i18n:translate="">Here you can download parts of portal data.</p> 5 <p i18n:translate=""> 6 Please pick the type of objects you want to export from the 7 selection below. 8 </p> 9 </div> 3 <div class="span8 columns"> 4 <p i18n:translate=""> 5 Here you can create CSV files from parts of portal data. 6 </p> 7 <p i18n:translate=""> 8 Please pick the type of objects you want to export from the 9 selection below. 10 </p> 11 <p i18n:translate=""> 12 The file will be generated and then be made available for you 13 to download. You must download or discard any existing export 14 file before creating a new one. 15 </p> 16 </div> 10 17 11 <form method="POST">12 <fieldset>18 <form method="POST"> 19 <fieldset> 13 20 <div class="clearfix"> 14 <label for="exporter">Data Type:</label> 15 <div class="input"> 16 <select name="exporter"> 17 <span tal:repeat="items view/getExporters" tal:omit-tag=""> 18 <option 19 tal:define="name python: items[1]; title python: items[0]" 20 tal:attributes="value name"> 21 <span tal:replace="title">TITLE</span> 22 </option> 21 <label for="running_exports" 22 tal:condition="view/getRunningExports">Running Export:</label> 23 <div class="input span6"> 24 <div tal:repeat="items view/getRunningExports"> 25 <div> 26 Data type: 27 <span tal:content="python: items[2]">exporter</span><br /> 28 Status: 29 <span tal:content="python: items[1]">status</span><br /> 30 <br /><br /> 31 </div> 32 <div> 33 <span tal:condition="not: python: view.job_finished(items[0])"> 34 <img src="" 35 tal:attributes="src static/ajax-loader.gif" 36 alt="Loading..." 37 class="spinner" /> 38 <input type="submit" name="reload" value="Reload" 39 class="btn primary" /> 40 </span> 41 <div> 42 <span tal:condition="python: view.job_finished(items[0])"> 43 <input type="submit" name="download" value="Download" 44 class="btn primary" /> 45 <input type="submit" name="discard" value="Discard" 46 class="btn secondary" /> 47 </span> 48 </div> 49 </div> 50 </div> 51 52 <div> 53 <tal:loading_bar content="structure provider:loadingbar" /> 54 </div> 55 </div> 56 57 <div class="clearfix" 58 tal:condition="not: view/getRunningExports"> 59 <label for="exporter">Data Type:</label> 60 <div class="input"> 61 <select name="exporter"> 62 <span tal:repeat="items view/getExporters" tal:omit-tag=""> 63 <option 64 tal:define="name python: items[1]; title python: items[0]" 65 tal:attributes="value name"> 66 <span tal:replace="title">TITLE</span> 67 </option> 68 </span> 69 </select> 70 <span class="help-inline" i18n:translate=""> 71 Type of objects to export 23 72 </span> 24 </select> 25 <span class="help-inline" i18n:translate=""> 26 Type of objects to export 27 </span> 73 </div> 28 74 </div> 29 </div> 30 <div class="input"> 31 <input i18n:translate="" type="submit" class="btn primary" 32 name="export" tal:attributes="value view/export_button" /> 75 <div class="input" 76 tal:condition="not: view/getRunningExports"> 77 <input i18n:translate="" type="submit" class="btn primary" 78 name="start_export" 79 tal:attributes="value view/export_button" /> 80 </div> 33 81 </div> 34 82 </fieldset> -
main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_browser.py
r9034 r9217 21 21 import shutil 22 22 import tempfile 23 import pytz24 from datetime import datetime, timedelta25 from StringIO import StringIO26 23 import os 27 import grok 28 from zope.event import notify 29 from zope.component import createObject, queryUtility 24 from zc.async.testing import wait_for_result 25 from zope.component import createObject, getUtility 30 26 from zope.component.hooks import setSite, clearSite 31 from zope.catalog.interfaces import ICatalog32 27 from zope.security.interfaces import Unauthorized 33 from zope.securitypolicy.interfaces import IPrincipalRoleManager34 28 from zope.testbrowser.testing import Browser 35 from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState36 29 from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase 37 30 from waeup.kofa.app import University 31 from waeup.kofa.interfaces import IJobManager 32 from waeup.kofa.tests.test_async import FunctionalAsyncTestCase 38 33 from waeup.kofa.university.faculty import Faculty 39 34 from waeup.kofa.university.department import Department … … 106 101 shutil.rmtree(self.dc_root) 107 102 108 109 103 class DataCenterUITests(UniversitySetup): 110 104 # Tests for DataCenter class views and pages … … 149 143 return 150 144 151 def test_export(self): 145 146 class DataCenterUIExportTests(UniversitySetup, FunctionalAsyncTestCase): 147 # Tests for DataCenter class views and pages 148 149 layer = FunctionalLayer 150 151 def wait_for_export_job_completed(self): 152 # helper function waiting until the current export job is completed 153 manager = getUtility(IJobManager) 154 job_id = self.app['datacenter'].running_exports[0][0] 155 job = manager.get(job_id) 156 wait_for_result(job) 157 return job_id 158 159 def stored_in_datacenter(self, job_id): 160 # tell whether job_id is stored in datacenter's running jobs list 161 for entry in list(self.app['datacenter'].running_exports): 162 if entry[0] == job_id: 163 return True 164 return False 165 166 def test_export_start(self): 167 # we can trigger export file creation 152 168 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 153 169 self.browser.open(self.datacenter_path) … … 156 172 self.browser.getLink("Export data").click() 157 173 self.browser.getControl(name="exporter").value = ['faculties'] 174 self.browser.getControl("Create CSV file").click() 175 self.assertEqual(self.browser.headers['Status'], '200 Ok') 176 return 177 178 def test_export_download(self): 179 # we can download a generated export result 180 self.test_export_start() 181 # while the export file is created, we get a reload button 182 # (or a loading bar if javascript is enabled)... 183 self.browser.getControl("Reload").click() 184 # ...which is displayed as long as the job is not finished. 185 # When the job is finished and we reload the page... 186 job_id = self.wait_for_export_job_completed() 187 try: 188 self.browser.getControl("Reload").click() 189 except LookupError: 190 # if the job completed very fast, we will get the download 191 # link immediately 192 pass 193 # ...we can download the result 158 194 self.browser.getControl("Download").click() 159 self.assertEqual(self.browser.headers['Status'], '200 Ok') 160 self.assertEqual(self.browser.headers['Content-Type'], 195 self.assertEqual(self.browser.headers['content-type'], 161 196 'text/csv; charset=UTF-8') 162 self.assert True ('WAeUP.KofaFaculties.csv' in163 self.browser.headers['content-disposition'])197 self.assertEqual(self.browser.headers['content-disposition'], 198 'attachment; filename="WAeUP.Kofa_faculties.csv') 164 199 self.assertEqual(self.browser.contents, 165 200 'code,title,title_prefix,users_with_local_roles\r\n' 166 201 'fac1,Unnamed Faculty,faculty,[]\r\n') 167 logfile = os.path.join( 168 self.app['datacenter'].storage, 'logs', 'datacenter.log') 169 logcontent = open(logfile).read() 170 self.assertTrue('zope.mgr - browser.pages.ExportCSVView - ' 171 'exported: faculties' in logcontent) 202 203 # after download, the job and the result file are removed 204 manager = getUtility(IJobManager) 205 result = manager.get(job_id) 206 self.assertEqual(result, None) 207 self.assertEqual(self.stored_in_datacenter(job_id), False) 208 #logfile = os.path.join( 209 # self.app['datacenter'].storage, 'logs', 'datacenter.log') 210 #logcontent = open(logfile).read() 211 #self.assertTrue('zope.mgr - browser.pages.ExportCSVView - ' 212 # 'exported: faculties' in logcontent) 213 return 214 215 def test_export_discard(self): 216 # we can discard a generated export result 217 self.test_export_start() 218 self.wait_for_export_job_completed() 219 self.browser.open(self.datacenter_path + '/@@export') 220 self.browser.getControl("Discard").click() 221 self.assertTrue('Discarded export result' in self.browser.contents) 172 222 return 173 223
Note: See TracChangeset for help on using the changeset viewer.