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

Last change on this file since 8305 was 8260, checked in by Henrik Bettermann, 13 years ago

Rename Acceptance Fee -> Application Fee

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