source: main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py @ 8472

Last change on this file since 8472 was 8394, checked in by uli, 13 years ago
  • Make IDataCenter a bit more useful.
  • Cut overlong lines.
  • pyflakes.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.0 KB
Line 
1## $Id: interfaces.py 8394 2012-05-09 13:39:02Z uli $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import os
19import re
20import codecs
21import zope.i18nmessageid
22from datetime import datetime
23from hurry.file.interfaces import IFileRetrieval
24from hurry.workflow.interfaces import IWorkflowInfo
25from zc.sourcefactory.basic import BasicSourceFactory
26from zope import schema
27from zope.pluggableauth.interfaces import IPrincipalInfo
28from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
29from zope.component import getUtility
30from zope.component.interfaces import IObjectEvent
31from zope.container.interfaces import INameChooser
32from zope.interface import Interface, Attribute
33from zope.schema.interfaces import IObject
34from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
35from waeup.kofa.schema import PhoneNumber
36
37_ = MessageFactory = zope.i18nmessageid.MessageFactory('waeup.kofa')
38
39DELETION_MARKER = 'XXX'
40IGNORE_MARKER = '<IGNORE>'
41
42CREATED = 'created'
43ADMITTED = 'admitted'
44CLEARANCE = 'clearance started'
45REQUESTED = 'clearance requested'
46CLEARED = 'cleared'
47PAID = 'school fee paid'
48RETURNING = 'returning'
49REGISTERED = 'courses registered'
50VALIDATED = 'courses validated'
51
52#default_rest_frontpage = u'' + codecs.open(os.path.join(
53#        os.path.dirname(__file__), 'frontpage.rst'),
54#        encoding='utf-8', mode='rb').read()
55
56default_html_frontpage = u'' + codecs.open(os.path.join(
57        os.path.dirname(__file__), 'frontpage.html'),
58        encoding='utf-8', mode='rb').read()
59
60def SimpleKofaVocabulary(*terms):
61    """A well-buildt vocabulary provides terms with a value, token and
62       title for each term
63    """
64    return SimpleVocabulary([
65            SimpleTerm(value, value, title) for title, value in terms])
66
67def year_range():
68    curr_year = datetime.now().year
69    return range(curr_year - 4, curr_year + 5)
70
71def academic_sessions():
72    curr_year = datetime.now().year
73    year_range = range(curr_year - 10, curr_year + 2)
74    return [('%s/%s' % (year,year+1), year) for year in year_range]
75
76academic_sessions_vocab = SimpleKofaVocabulary(*academic_sessions())
77
78registration_states_vocab = SimpleKofaVocabulary(
79    (_('created'), CREATED),
80    (_('admitted'), ADMITTED),
81    (_('clearance started'), CLEARANCE),
82    (_('clearance requested'), REQUESTED),
83    (_('cleared'), CLEARED),
84    (_('school fee paid'), PAID),
85    (_('returning'), RETURNING),
86    (_('courses registered'), REGISTERED),
87    (_('courses validated'), VALIDATED),
88    )
89
90class SubjectSource(BasicSourceFactory):
91    """A source for school subjects used in exam documentation.
92    """
93    def getValues(self):
94        subjects_dict = getUtility(IKofaUtils).EXAM_SUBJECTS_DICT
95        return sorted(subjects_dict.keys())
96
97    def getTitle(self, value):
98        subjects_dict = getUtility(IKofaUtils).EXAM_SUBJECTS_DICT
99        return "%s:" % subjects_dict[value]
100
101class GradeSource(BasicSourceFactory):
102    """A source for exam grades.
103    """
104    def getValues(self):
105        for entry in getUtility(IKofaUtils).EXAM_GRADES:
106            yield entry[0]
107
108    def getTitle(self, value):
109        return dict(getUtility(IKofaUtils).EXAM_GRADES)[value]
110
111# Define a validation method for email addresses
112class NotAnEmailAddress(schema.ValidationError):
113    __doc__ = u"Invalid email address"
114
115check_email = re.compile(
116    r"[a-zA-Z0-9._%-']+@([a-zA-Z0-9-]+.)*[a-zA-Z]{2,4}").match
117
118def validate_email(value):
119    if not check_email(value):
120        raise NotAnEmailAddress(value)
121    return True
122
123# Define a validation method for international phone numbers
124class InvalidPhoneNumber(schema.ValidationError):
125    __doc__ = u"Invalid phone number"
126
127# represent format +NNN-NNNN-NNNN
128RE_INT_PHONE = re.compile(r"^\+?\d+\-\d+\-[\d\-]+$")
129
130def validate_phone(value):
131    if not RE_INT_PHONE.match(value):
132        raise InvalidPhoneNumber(value)
133    return True
134
135class FatalCSVError(Exception):
136    """Some row could not be processed.
137    """
138    pass
139
140class DuplicationError(Exception):
141    """An exception that can be raised when duplicates are found.
142
143    When raising :exc:`DuplicationError` you can, beside the usual
144    message, specify a list of objects which are duplicates. These
145    values can be used by catching code to print something helpful or
146    similar.
147    """
148    def __init__(self, msg, entries=[]):
149        self.msg = msg
150        self.entries = entries
151
152    def __str__(self):
153        return '%r' % self.msg
154
155class RoleSource(BasicSourceFactory):
156    """A source for site roles.
157    """
158    def getValues(self):
159        # late import: in interfaces we should not import local modules
160        from waeup.kofa.permissions import get_waeup_role_names
161        return get_waeup_role_names()
162
163    def getTitle(self, value):
164        # late import: in interfaces we should not import local modules
165        from waeup.kofa.permissions import get_all_roles
166        roles = dict(get_all_roles())
167        if value in roles.keys():
168            title = roles[value].title
169            if '.' in title:
170                title = title.split('.', 2)[1]
171        return title
172
173class CaptchaSource(BasicSourceFactory):
174    """A source for captchas.
175    """
176    def getValues(self):
177        captchas = ['No captcha', 'Testing captcha', 'ReCaptcha']
178        try:
179            # we have to 'try' because IConfiguration can only handle
180            # interfaces from w.k.interface.
181            from waeup.kofa.browser.interfaces import ICaptchaManager
182        except:
183            return captchas
184        return sorted(getUtility(ICaptchaManager).getAvailCaptchas().keys())
185
186    def getTitle(self, value):
187        return value
188
189class IResultEntry(Interface):
190    """A school grade entry.
191    """
192    subject = schema.Choice(
193        title = _(u'Subject'),
194        source = SubjectSource(),
195        )
196    grade = schema.Choice(
197        title = _(u'Grade'),
198        source = GradeSource(),
199        )
200
201class IResultEntryField(IObject):
202    """A zope.schema-like field for usage in interfaces.
203
204    Marker interface to distuingish result entries from ordinary
205    object fields. Needed for registration of widgets.
206    """
207
208class IKofaUtils(Interface):
209    """A collection of methods which are subject to customization.
210    """
211
212    PORTAL_LANGUAGE = Attribute("Dict of global language setting")
213    PREFERRED_LANGUAGES_DICT = Attribute("Dict of preferred languages")
214    EXAM_SUBJECTS_DICT = Attribute("Dict of examination subjects")
215    EXAM_GRADES_DICT = Attribute("Dict of examination grades")
216    INST_TYPES_DICT = Attribute("Dict if institution types")
217    STUDY_MODES_DICT = Attribute("Dict of study modes")
218    APP_CATS_DICT = Attribute("Dict of application categories")
219    SEMESTER_DICT = Attribute("Dict of semesters or trimesters")
220    INT_PHONE_PREFIXES = Attribute(
221        "Dict of international phone number prefixes")
222
223    def sendContactForm(
224          from_name,from_addr,rcpt_name,rcpt_addr,
225          from_username,usertype,portal,body,subject):
226        """Send an email with data provided by forms.
227        """
228
229    def fullname(firstname,lastname,middlename):
230        """Full name constructor.
231        """
232
233    def sendCredentials(user, password, login_url, msg):
234        """Send credentials as email.
235
236        Input is the applicant for which credentials are sent and the
237        password.
238
239        Returns True or False to indicate successful operation.
240        """
241
242    def genPassword(length, chars):
243        """Generate a random password.
244        """
245
246class IKofaObject(Interface):
247    """A Kofa object.
248
249    This is merely a marker interface.
250    """
251
252class IUniversity(IKofaObject):
253    """Representation of a university.
254    """
255
256
257class IKofaContainer(IKofaObject):
258    """A container for Kofa objects.
259    """
260
261class IKofaContained(IKofaObject):
262    """An item contained in an IKofaContainer.
263    """
264
265class ICSVExporter(Interface):
266    """A CSV file exporter for objects.
267    """
268    fields = Attribute("""List of fieldnames in resulting CSV""")
269
270    title = schema.TextLine(
271        title = u'Title',
272        description = u'Description to be displayed in selections.',
273        )
274    def mangle_value(value, name, obj):
275        """Mangle `value` extracted from `obj` or suobjects thereof.
276
277        This is called by export before actually writing to the result
278        file.
279        """
280
281    def export(iterable, filepath=None):
282        """Export iterables as rows in a CSV file.
283
284        If `filepath` is not given, a string with the data should be
285        returned.
286
287        What kind of iterables are acceptable depends on the specific
288        exporter implementation.
289        """
290
291    def export_all(site, filapath=None):
292        """Export all items in `site` as CSV file.
293
294        if `filepath` is not given, a string with the data should be
295        returned.
296        """
297
298class IKofaExporter(Interface):
299    """An exporter for objects.
300    """
301    def export(obj, filepath=None):
302        """Export by pickling.
303
304        Returns a file-like object containing a representation of `obj`.
305
306        This is done using `pickle`. If `filepath` is ``None``, a
307        `cStringIO` object is returned, that contains the saved data.
308        """
309
310class IKofaXMLExporter(Interface):
311    """An XML exporter for objects.
312    """
313    def export(obj, filepath=None):
314        """Export as XML.
315
316        Returns an XML representation of `obj`.
317
318        If `filepath` is ``None``, a StringIO` object is returned,
319        that contains the transformed data.
320        """
321
322class IKofaXMLImporter(Interface):
323    """An XML import for objects.
324    """
325    def doImport(filepath):
326        """Create Python object from XML.
327
328        Returns a Python object.
329        """
330
331class IBatchProcessor(Interface):
332    """A batch processor that handles mass-operations.
333    """
334    name = schema.TextLine(
335        title = _(u'Processor name')
336        )
337
338    def doImport(path, headerfields, mode='create', user='Unknown',
339                 logger=None, ignore_empty=True):
340        """Read data from ``path`` and update connected object.
341
342        `headerfields` is a list of headerfields as read from the file
343        to import.
344
345        `mode` gives the import mode to use (``'create'``,
346        ``'update'``, or ``'remove'``.
347
348        `user` is a string describing the user performing the
349        import. Normally fetched from current principal.
350
351        `logger` is the logger to use during import.
352
353        `ignore_emtpy` in update mode ignores empty fields if true.
354        """
355
356class IContactForm(IKofaObject):
357    """A contact form.
358    """
359
360    email_from = schema.ASCIILine(
361        title = _(u'Email Address:'),
362        default = None,
363        required = True,
364        constraint=validate_email,
365        )
366
367    email_to = schema.ASCIILine(
368        title = _(u'Email to:'),
369        default = None,
370        required = True,
371        constraint=validate_email,
372        )
373
374    subject = schema.TextLine(
375        title = _(u'Subject:'),
376        required = True,)
377
378    fullname = schema.TextLine(
379        title = _(u'Full Name:'),
380        required = True,)
381
382    body = schema.Text(
383        title = _(u'Text:'),
384        required = True,)
385
386class IKofaPrincipalInfo(IPrincipalInfo):
387    """Infos about principals that are users of Kofa Kofa.
388    """
389    email = Attribute("The email address of a user")
390    phone = Attribute("The phone number of a user")
391
392
393class IKofaPrincipal(IPrincipal):
394    """A principle for Kofa Kofa.
395
396    This interface extends zope.security.interfaces.IPrincipal and
397    requires also an `id` and other attributes defined there.
398    """
399
400    email = schema.TextLine(
401        title = _(u'Email Address'),
402        description = u'',
403        required=False,)
404
405    phone = PhoneNumber(
406        title = _(u'Phone'),
407        description = u'',
408        required=False,)
409
410class IUserAccount(IKofaObject):
411    """A user account.
412    """
413    name = schema.TextLine(
414        title = _(u'User Id'),
415        description = u'Login name of user',
416        required = True,)
417
418    title = schema.TextLine(
419        title = _(u'Full Name'),
420        required = False,)
421
422    description = schema.Text(
423        title = _(u'Description/Notice'),
424        required = False,)
425
426    email = schema.ASCIILine(
427        title = _(u'Email Address'),
428        default = None,
429        required = True,
430        constraint=validate_email,
431        )
432
433    phone = PhoneNumber(
434        title = _(u'Phone'),
435        default = None,
436        required = False,
437        )
438
439    roles = schema.List(
440        title = _(u'Portal roles'),
441        value_type = schema.Choice(source=RoleSource()),
442        required = False,
443        )
444
445class IPasswordValidator(Interface):
446    """A password validator utility.
447    """
448
449    def validate_password(password, password_repeat):
450        """Validates a password by comparing it with
451        control password and checking some other requirements.
452        """
453
454
455class IUsersContainer(IKofaObject):
456    """A container for users (principals).
457
458    These users are used for authentication purposes.
459    """
460
461    def addUser(name, password, title=None, description=None):
462        """Add a user.
463        """
464
465    def delUser(name):
466        """Delete a user if it exists.
467        """
468
469class ILocalRolesAssignable(Interface):
470    """The local roles assignable to an object.
471    """
472    def __call__():
473        """Returns a list of dicts.
474
475        Each dict contains a ``name`` referring to the role assignable
476        for the specified object and a `title` to describe the range
477        of users to which this role can be assigned.
478        """
479
480class IConfigurationContainer(IKofaObject):
481    """A container for session configuration objects.
482    """
483
484    name = schema.TextLine(
485        title = _(u'Name of University'),
486        default = _(u'Sample University'),
487        required = True,
488        )
489
490    acronym = schema.TextLine(
491        title = _(u'Abbreviated Title of University'),
492        default = u'WAeUP.Kofa',
493        required = True,
494        )
495
496    skin = schema.Choice(
497        title = _(u'Skin'),
498        default = u'gray waeup theme',
499        vocabulary = 'waeup.kofa.browser.theming.ThemesVocabulary',
500        required = True,
501        )
502
503    frontpage = schema.Text(
504        title = _(u'Content in HTML format'),
505        required = False,
506        default = default_html_frontpage,
507        )
508
509    frontpage_dict = schema.Dict(
510        title = u'Content as language dictionary with values in html format',
511        required = False,
512        default = {},
513        )
514
515    accommodation_session = schema.Choice(
516        title = _(u'Accommodation Booking Session'),
517        source = academic_sessions_vocab,
518        default = datetime.now().year,
519        required = False,
520        readonly = False,
521        )
522
523    accommodation_states = schema.List(
524        title = _(u'Allowed States for Accommodation Booking'),
525        value_type = schema.Choice(
526            vocabulary = registration_states_vocab,
527            ),
528        default = [],
529        )
530
531    name_admin = schema.TextLine(
532        title = _(u'Name of Administrator'),
533        default = u'Administrator',
534        required = True,
535        )
536
537    email_admin = schema.ASCIILine(
538        title = _(u'Email Address of Administrator'),
539        default = 'contact@waeup.org',
540        required = True,
541        constraint=validate_email,
542        )
543
544    email_subject = schema.TextLine(
545        title = _(u'Subject of Email to Administrator'),
546        default = _(u'Kofa Contact'),
547        required = True,
548        )
549
550    smtp_mailer = schema.Choice(
551        title = _(u'SMTP mailer to use when sending mail'),
552        vocabulary = 'Mail Delivery Names',
553        default = 'No email service',
554        required = True,
555        )
556
557    captcha = schema.Choice(
558        title = _(u'Captcha used for public registration pages'),
559        source = CaptchaSource(),
560        default = u'No captcha',
561        required = True,
562        )
563
564    carry_over = schema.Bool(
565        title = _(u'Carry-over Course Registration'),
566        default = False,
567        )
568
569class ISessionConfiguration(IKofaObject):
570    """A session configuration object.
571    """
572
573    academic_session = schema.Choice(
574        title = _(u'Academic Session'),
575        source = academic_sessions_vocab,
576        default = None,
577        required = True,
578        readonly = True,
579        )
580
581    application_fee = schema.Float(
582        title = _(u'Application Fee'),
583        default = 0.0,
584        required = False,
585        )
586
587    clearance_fee = schema.Float(
588        title = _(u'Clearance Fee'),
589        default = 0.0,
590        required = False,
591        )
592
593    booking_fee = schema.Float(
594        title = _(u'Bed Booking Fee'),
595        default = 0.0,
596        required = False,
597        )
598
599    def getSessionString():
600        """Returns the session string from the vocabulary.
601        """
602
603
604class ISessionConfigurationAdd(ISessionConfiguration):
605    """A session configuration object in add mode.
606    """
607
608    academic_session = schema.Choice(
609        title = _(u'Academic Session'),
610        source = academic_sessions_vocab,
611        default = None,
612        required = True,
613        readonly = False,
614        )
615
616ISessionConfigurationAdd['academic_session'].order =  ISessionConfiguration[
617    'academic_session'].order
618
619class IDataCenter(IKofaObject):
620    """A data center.
621
622    A data center manages files (uploads, downloads, etc.).
623
624    Beside providing the bare paths needed to keep files, it also
625    provides some helpers to put results of batch processing into
626    well-defined final locations (with well-defined filenames).
627
628    The main use-case is managing of site-related files, i.e. files
629    for import, export etc.
630
631    DataCenters are _not_ meant as storages for object-specific files
632    like passport photographs and similar.
633
634    It is up to the datacenter implementation how to organize data
635    (paths) inside its storage path.
636    """
637    storage = schema.Bytes(
638        title = u'Path to directory where everything is kept.'
639        )
640
641    deleted_path = schema.Bytes(
642        title = u'Path were data about deleted objects should be stored.'
643        )
644
645    def getFiles(sort='name'):
646        """Get a list of files stored in `storage` sorted by basename.
647        """
648    def setStoragePath(path, move=False, overwrite=False):
649        """Set the path where to store files.
650
651        If `move` is True, move over files from the current location
652        to the new one.
653
654        If `overwrite` is also True, overwrite any already existing
655        files of same name in target location.
656
657        Triggers a DataCenterStorageMovedEvent.
658        """
659
660    def distProcessedFiles(successful, source_path, finished_file,
661                           pending_file, mode='create', move_orig=True):
662        """Distribute processed files over final locations.
663        """
664
665
666class IDataCenterFile(Interface):
667    """A data center file.
668    """
669
670    name = schema.TextLine(
671        title = u'Filename')
672
673    size = schema.TextLine(
674        title = u'Human readable file size')
675
676    uploaddate = schema.TextLine(
677        title = u'Human readable upload datetime')
678
679    lines = schema.Int(
680        title = u'Number of lines in file')
681
682    def getDate():
683        """Get creation timestamp from file in human readable form.
684        """
685
686    def getSize():
687        """Get human readable size of file.
688        """
689
690    def getLinesNumber():
691        """Get number of lines of file.
692        """
693
694class IDataCenterStorageMovedEvent(IObjectEvent):
695    """Emitted, when the storage of a datacenter changes.
696    """
697
698class IObjectUpgradeEvent(IObjectEvent):
699    """Can be fired, when an object shall be upgraded.
700    """
701
702class ILocalRoleSetEvent(IObjectEvent):
703    """A local role was granted/revoked for a principal on an object.
704    """
705    role_id = Attribute(
706        "The role id that was set.")
707    principal_id = Attribute(
708        "The principal id for which the role was granted/revoked.")
709    granted = Attribute(
710        "Boolean. If false, then the role was revoked.")
711
712class IQueryResultItem(Interface):
713    """An item in a search result.
714    """
715    url = schema.TextLine(
716        title = u'URL that links to the found item')
717    title = schema.TextLine(
718        title = u'Title displayed in search results.')
719    description = schema.Text(
720        title = u'Longer description of the item found.')
721
722class IKofaPluggable(Interface):
723    """A component that might be plugged into a Kofa Kofa app.
724
725    Components implementing this interface are referred to as
726    'plugins'. They are normally called when a new
727    :class:`waeup.kofa.app.University` instance is created.
728
729    Plugins can setup and update parts of the central site without the
730    site object (normally a :class:`waeup.kofa.app.University` object)
731    needing to know about that parts. The site simply collects all
732    available plugins, calls them and the plugins care for their
733    respective subarea like the applicants area or the datacenter
734    area.
735
736    Currently we have no mechanism to define an order of plugins. A
737    plugin should therefore make no assumptions about the state of the
738    site or other plugins being run before and instead do appropriate
739    checks if necessary.
740
741    Updates can be triggered for instance by the respective form in
742    the site configuration. You normally do updates when the
743    underlying software changed.
744    """
745    def setup(site, name, logger):
746        """Create an instance of the plugin.
747
748        The method is meant to be called by the central app (site)
749        when it is created.
750
751        `site`:
752           The site that requests a setup.
753
754        `name`:
755           The name under which the plugin was registered (utility name).
756
757        `logger`:
758           A standard Python logger for the plugins use.
759        """
760
761    def update(site, name, logger):
762        """Method to update an already existing plugin.
763
764        This might be called by a site when something serious
765        changes. It is a poor-man replacement for Zope generations
766        (but probably more comprehensive and better understandable).
767
768        `site`:
769           The site that requests an update.
770
771        `name`:
772           The name under which the plugin was registered (utility name).
773
774        `logger`:
775           A standard Python logger for the plugins use.
776        """
777
778class IAuthPluginUtility(Interface):
779    """A component that cares for authentication setup at site creation.
780
781    Utilities providing this interface are looked up when a Pluggable
782    Authentication Utility (PAU) for any
783    :class:`waeup.kofa.app.University` instance is created and put
784    into ZODB.
785
786    The setup-code then calls the `register` method of the utility and
787    expects a modified (or unmodified) version of the PAU back.
788
789    This allows to define any authentication setup modifications by
790    submodules or third-party modules/packages.
791    """
792
793    def register(pau):
794        """Register any plugins wanted to be in the PAU.
795        """
796
797    def unregister(pau):
798        """Unregister any plugins not wanted to be in the PAU.
799        """
800
801class IObjectConverter(Interface):
802    """Object converters are available as simple adapters, adapting
803       interfaces (not regular instances).
804
805    """
806
807    def fromStringDict(self, data_dict, context, form_fields=None):
808        """Convert values in `data_dict`.
809
810        Converts data in `data_dict` into real values based on
811        `context` and `form_fields`.
812
813        `data_dict` is a mapping (dict) from field names to values
814        represented as strings.
815
816        The fields (keys) to convert can be given in optional
817        `form_fields`. If given, form_fields should be an instance of
818        :class:`zope.formlib.form.Fields`. Suitable instances are for
819        example created by :class:`grok.AutoFields`.
820
821        If no `form_fields` are given, a default is computed from the
822        associated interface.
823
824        The `context` can be an existing object (implementing the
825        associated interface) or a factory name. If it is a string, we
826        try to create an object using
827        :func:`zope.component.createObject`.
828
829        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
830        <DATA_DICT>)`` where
831
832        ``<FIELD_ERRORS>``
833           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
834           error that happened when validating the input data in
835           `data_dict`
836
837        ``<INVARIANT_ERRORS>``
838           is a list of invariant errors concerning several fields
839
840        ``<DATA_DICT>``
841           is a dict with the values from input dict converted.
842
843        If errors happen, i.e. the error lists are not empty, always
844        an empty ``<DATA_DICT>`` is returned.
845
846        If ``<DATA_DICT>` is non-empty, there were no errors.
847        """
848
849class IFieldConverter(Interface):
850    def request_data(name, value, schema_field, prefix='', mode='create'):
851        """Create a dict with key-value mapping as created by a request.
852
853        `name` and `value` are expected to be parsed from CSV or a
854        similar input and represent an attribute to be set to a
855        representation of value.
856
857        `mode` gives the mode of import.
858
859        :meth:`update_request_data` is then requested to turn this
860        name and value into vars as they would be sent by a regular
861        form submit. This means we do not create the real values to be
862        set but we only define the values that would be sent in a
863        browser request to request the creation of those values.
864
865        The returned dict should contain names and values of a faked
866        browser request for the given `schema_field`.
867
868        Field converters are normally registered as adapters to some
869        specific zope.schema field.
870        """
871
872class IObjectHistory(Interface):
873
874    messages = schema.List(
875        title = u'List of messages stored',
876        required = True,
877        )
878
879    def addMessage(message):
880        """Add a message.
881        """
882
883class IKofaWorkflowInfo(IWorkflowInfo):
884    """A :class:`hurry.workflow.workflow.WorkflowInfo` with additional
885       methods for convenience.
886    """
887    def getManualTransitions():
888        """Get allowed manual transitions.
889
890        Get a sorted list of tuples containing the `transition_id` and
891        `title` of each allowed transition.
892        """
893
894class ISiteLoggers(Interface):
895
896    loggers = Attribute("A list or generator of registered KofaLoggers")
897
898    def register(name, filename=None, site=None, **options):
899        """Register a logger `name` which logs to `filename`.
900
901        If `filename` is not given, logfile will be `name` with
902        ``.log`` as filename extension.
903        """
904
905    def unregister(name):
906        """Unregister a once registered logger.
907        """
908
909class ILogger(Interface):
910    """A logger cares for setup, update and restarting of a Python logger.
911    """
912
913    logger = Attribute("""A :class:`logging.Logger` instance""")
914
915
916    def __init__(name, filename=None, site=None, **options):
917        """Create a Kofa logger instance.
918        """
919
920    def setup():
921        """Create a Python :class:`logging.Logger` instance.
922
923        The created logger is based on the params given by constructor.
924        """
925
926    def update(**options):
927        """Update the logger.
928
929        Updates the logger respecting modified `options` and changed
930        paths.
931        """
932
933class ILoggerCollector(Interface):
934
935    def getLoggers(site):
936        """Return all loggers registered for `site`.
937        """
938
939    def registerLogger(site, logging_component):
940        """Register a logging component residing in `site`.
941        """
942
943    def unregisterLogger(site, logging_component):
944        """Unregister a logger.
945        """
946
947#
948# External File Storage and relatives
949#
950class IFileStoreNameChooser(INameChooser):
951    """See zope.container.interfaces.INameChooser for base methods.
952    """
953    def checkName(name, attr=None):
954        """Check whether an object name is valid.
955
956        Raises a user error if the name is not valid.
957        """
958
959    def chooseName(name, attr=None):
960        """Choose a unique valid file id for the object.
961
962        The given name may be taken into account when choosing the
963        name (file id).
964
965        chooseName is expected to always choose a valid file id (that
966        would pass the checkName test) and never raise an error.
967
968        If `attr` is not ``None`` it might been taken into account as
969        well when generating the file id. Usual behaviour is to
970        interpret `attr` as a hint for what type of file for a given
971        context should be stored if there are several types
972        possible. For instance for a certain student some file could
973        be the connected passport photograph or some certificate scan
974        or whatever. Each of them has to be stored in a different
975        location so setting `attr` to a sensible value should give
976        different file ids returned.
977        """
978
979class IExtFileStore(IFileRetrieval):
980    """A file storage that stores files in filesystem (not as blobs).
981    """
982    root = schema.TextLine(
983        title = u'Root path of file store.',
984        )
985
986    def getFile(file_id):
987        """Get raw file data stored under file with `file_id`.
988
989        Returns a file descriptor open for reading or ``None`` if the
990        file cannot be found.
991        """
992
993    def getFileByContext(context, attr=None):
994        """Get raw file data stored for the given context.
995
996        Returns a file descriptor open for reading or ``None`` if no
997        such file can be found.
998
999        Both, `context` and `attr` might be used to find (`context`)
1000        and feed (`attr`) an appropriate file name chooser.
1001
1002        This is a convenience method.
1003        """
1004
1005    def deleteFile(file_id):
1006        """Delete file stored under `file_id`.
1007
1008        Remove file from filestore so, that it is not available
1009        anymore on next call to getFile for the same file_id.
1010
1011        Should not complain if no such file exists.
1012        """
1013
1014    def deleteFileByContext(context, attr=None):
1015        """Delete file for given `context` and `attr`.
1016
1017        Both, `context` and `attr` might be used to find (`context`)
1018        and feed (`attr`) an appropriate file name chooser.
1019
1020        This is a convenience method.
1021        """
1022
1023    def createFile(filename, f):
1024        """Create file given by f with filename `filename`
1025
1026        Returns a hurry.file.File-based object.
1027        """
1028
1029class IFileStoreHandler(Interface):
1030    """Filestore handlers handle specific files for file stores.
1031
1032    If a file to store/get provides a specific filename, a file store
1033    looks up special handlers for that type of file.
1034
1035    """
1036    def pathFromFileID(store, root, filename):
1037        """Turn file id into path to store.
1038
1039        Returned path should be absolute.
1040        """
1041
1042    def createFile(store, root, filename, file_id, file):
1043        """Return some hurry.file based on `store` and `file_id`.
1044
1045        Some kind of callback method called by file stores to create
1046        file objects from file_id.
1047
1048        Returns a tuple ``(raw_file, path, file_like_obj)`` where the
1049        ``file_like_obj`` should be a HurryFile, a KofaImageFile or
1050        similar. ``raw_file`` is the (maybe changed) input file and
1051        ``path`` the relative internal path to store the file at.
1052
1053        Please make sure the ``raw_file`` is opened for reading and
1054        the file descriptor set at position 0 when returned.
1055
1056        This method also gets the raw input file object that is about
1057        to be stored and is expected to raise any exceptions if some
1058        kind of validation or similar fails.
1059        """
1060
1061class IPDF(Interface):
1062    """A PDF representation of some context.
1063    """
1064
1065    def __call__(view=None, note=None):
1066        """Create a bytestream representing a PDF from context.
1067
1068        If `view` is passed in additional infos might be rendered into
1069        the document.
1070
1071        `note` is optional HTML rendered at bottom of the created
1072        PDF. Please consider the limited reportlab support for HTML,
1073        but using font-tags and friends you certainly can get the
1074        desired look.
1075        """
1076
1077class IMailService(Interface):
1078    """A mail service.
1079    """
1080
1081    def __call__():
1082        """Get the default mail delivery.
1083        """
1084
1085from zope.configuration.fields import Path
1086class IDataCenterConfig(Interface):
1087    path = Path(
1088        title = u'Path',
1089        description = u"Directory where the datacenter should store "
1090                      u"files by default (adjustable in web UI).",
1091        required = True,
1092        )
1093
1094class IChangePassword(IKofaObject):
1095    """Interface needed for change pasword page.
1096
1097    """
1098    identifier = schema.TextLine(
1099        title = _(u'Unique Identifier'),
1100        description = _(
1101            u'User Name, Student or Applicant Id, Matriculation or '
1102            u'Registration Number'),
1103        required = True,
1104        readonly = False,
1105        )
1106
1107    email = schema.ASCIILine(
1108        title = _(u'Email Address'),
1109        required = True,
1110        constraint=validate_email,
1111        )
Note: See TracBrowser for help on using the repository browser.