source: main/waeup.sirp/trunk/src/waeup/sirp/interfaces.py @ 7609

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

Allow apostrophe in email addresses.

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