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

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

Add getVerdictsDict method to SIRPUtils which is used to setup the new VerdictSource?.

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