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

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

Carry-over course registration is optional and can now be switched on and off in the configuration.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 26.9 KB
Line 
1## $Id: interfaces.py 7664 2012-02-17 09:10:47Z 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
469    carry_over = schema.Bool(
470        title = u'Carry-over Course Registration',
471        default = False,
472        )
473
474class ISessionConfiguration(ISIRPObject):
475    """A session configuration object.
476    """
477
478    academic_session = schema.Choice(
479        title = u'Academic Session',
480        source = academic_sessions_vocab,
481        default = None,
482        required = True,
483        readonly = True,
484        )
485
486    school_fee_base = schema.Int(
487        title = u'School Fee',
488        default = 0,
489        )
490
491    surcharge_1 = schema.Int(
492        title = u'Surcharge 1',
493        default = 0,
494        )
495
496    surcharge_2 = schema.Int(
497        title = u'Surcharge 2',
498        default = 0,
499        )
500
501    surcharge_3 = schema.Int(
502        title = u'Surcharge 3',
503        default = 0,
504        )
505
506    clearance_fee = schema.Int(
507        title = u'Clearance Fee',
508        default = 0,
509        )
510
511    booking_fee = schema.Int(
512        title = u'Booking Fee',
513        default = 0,
514        )
515
516    acceptance_fee = schema.Int(
517        title = u'Acceptance Fee',
518        default = 0,
519        )
520
521    def getSessionString():
522        """Returns the session string from the vocabulary.
523        """
524
525
526class ISessionConfigurationAdd(ISessionConfiguration):
527    """A session configuration object in add mode.
528    """
529
530    academic_session = schema.Choice(
531        title = u'Academic Session',
532        source = academic_sessions_vocab,
533        default = None,
534        required = True,
535        readonly = False,
536        )
537
538ISessionConfigurationAdd['academic_session'].order =  ISessionConfiguration[
539    'academic_session'].order
540
541class IDataCenter(ISIRPObject):
542    """A data center.
543
544    TODO : declare methods, at least those needed by pages.
545    """
546    pass
547
548class IDataCenterFile(Interface):
549    """A data center file.
550    """
551
552    name = schema.TextLine(
553        title = u'Filename')
554
555    size = schema.TextLine(
556        title = u'Human readable file size')
557
558    uploaddate = schema.TextLine(
559        title = u'Human readable upload datetime')
560
561    lines = schema.Int(
562        title = u'Number of lines in file')
563
564    def getDate():
565        """Get creation timestamp from file in human readable form.
566        """
567
568    def getSize():
569        """Get human readable size of file.
570        """
571
572    def getLinesNumber():
573        """Get number of lines of file.
574        """
575
576class IDataCenterStorageMovedEvent(IObjectEvent):
577    """Emitted, when the storage of a datacenter changes.
578    """
579
580class IObjectUpgradeEvent(IObjectEvent):
581    """Can be fired, when an object shall be upgraded.
582    """
583
584class ILocalRoleSetEvent(IObjectEvent):
585    """A local role was granted/revoked for a principal on an object.
586    """
587    role_id = Attribute(
588        "The role id that was set.")
589    principal_id = Attribute(
590        "The principal id for which the role was granted/revoked.")
591    granted = Attribute(
592        "Boolean. If false, then the role was revoked.")
593
594class IQueryResultItem(Interface):
595    """An item in a search result.
596    """
597    url = schema.TextLine(
598        title = u'URL that links to the found item')
599    title = schema.TextLine(
600        title = u'Title displayed in search results.')
601    description = schema.Text(
602        title = u'Longer description of the item found.')
603
604class ISIRPPluggable(Interface):
605    """A component that might be plugged into a SIRP SIRP app.
606
607    Components implementing this interface are referred to as
608    'plugins'. They are normally called when a new
609    :class:`waeup.sirp.app.University` instance is created.
610
611    Plugins can setup and update parts of the central site without the
612    site object (normally a :class:`waeup.sirp.app.University` object)
613    needing to know about that parts. The site simply collects all
614    available plugins, calls them and the plugins care for their
615    respective subarea like the applicants area or the datacenter
616    area.
617
618    Currently we have no mechanism to define an order of plugins. A
619    plugin should therefore make no assumptions about the state of the
620    site or other plugins being run before and instead do appropriate
621    checks if necessary.
622
623    Updates can be triggered for instance by the respective form in
624    the site configuration. You normally do updates when the
625    underlying software changed.
626    """
627    def setup(site, name, logger):
628        """Create an instance of the plugin.
629
630        The method is meant to be called by the central app (site)
631        when it is created.
632
633        `site`:
634           The site that requests a setup.
635
636        `name`:
637           The name under which the plugin was registered (utility name).
638
639        `logger`:
640           A standard Python logger for the plugins use.
641        """
642
643    def update(site, name, logger):
644        """Method to update an already existing plugin.
645
646        This might be called by a site when something serious
647        changes. It is a poor-man replacement for Zope generations
648        (but probably more comprehensive and better understandable).
649
650        `site`:
651           The site that requests an update.
652
653        `name`:
654           The name under which the plugin was registered (utility name).
655
656        `logger`:
657           A standard Python logger for the plugins use.
658        """
659
660class IAuthPluginUtility(Interface):
661    """A component that cares for authentication setup at site creation.
662
663    Utilities providing this interface are looked up when a Pluggable
664    Authentication Utility (PAU) for any
665    :class:`waeup.sirp.app.University` instance is created and put
666    into ZODB.
667
668    The setup-code then calls the `register` method of the utility and
669    expects a modified (or unmodified) version of the PAU back.
670
671    This allows to define any authentication setup modifications by
672    submodules or third-party modules/packages.
673    """
674
675    def register(pau):
676        """Register any plugins wanted to be in the PAU.
677        """
678
679    def unregister(pau):
680        """Unregister any plugins not wanted to be in the PAU.
681        """
682
683class IObjectConverter(Interface):
684    """Object converters are available as simple adapters, adapting
685       interfaces (not regular instances).
686
687    """
688
689    def fromStringDict(self, data_dict, context, form_fields=None):
690        """Convert values in `data_dict`.
691
692        Converts data in `data_dict` into real values based on
693        `context` and `form_fields`.
694
695        `data_dict` is a mapping (dict) from field names to values
696        represented as strings.
697
698        The fields (keys) to convert can be given in optional
699        `form_fields`. If given, form_fields should be an instance of
700        :class:`zope.formlib.form.Fields`. Suitable instances are for
701        example created by :class:`grok.AutoFields`.
702
703        If no `form_fields` are given, a default is computed from the
704        associated interface.
705
706        The `context` can be an existing object (implementing the
707        associated interface) or a factory name. If it is a string, we
708        try to create an object using
709        :func:`zope.component.createObject`.
710
711        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
712        <DATA_DICT>)`` where
713
714        ``<FIELD_ERRORS>``
715           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
716           error that happened when validating the input data in
717           `data_dict`
718
719        ``<INVARIANT_ERRORS>``
720           is a list of invariant errors concerning several fields
721
722        ``<DATA_DICT>``
723           is a dict with the values from input dict converted.
724
725        If errors happen, i.e. the error lists are not empty, always
726        an empty ``<DATA_DICT>`` is returned.
727
728        If ``<DATA_DICT>` is non-empty, there were no errors.
729        """
730
731class IObjectHistory(Interface):
732
733    messages = schema.List(
734        title = u'List of messages stored',
735        required = True,
736        )
737
738    def addMessage(message):
739        """Add a message.
740        """
741
742class ISIRPWorkflowInfo(IWorkflowInfo):
743    """A :class:`hurry.workflow.workflow.WorkflowInfo` with additional
744       methods for convenience.
745    """
746    def getManualTransitions():
747        """Get allowed manual transitions.
748
749        Get a sorted list of tuples containing the `transition_id` and
750        `title` of each allowed transition.
751        """
752
753class ISiteLoggers(Interface):
754
755    loggers = Attribute("A list or generator of registered SIRPLoggers")
756
757    def register(name, filename=None, site=None, **options):
758        """Register a logger `name` which logs to `filename`.
759
760        If `filename` is not given, logfile will be `name` with
761        ``.log`` as filename extension.
762        """
763
764    def unregister(name):
765        """Unregister a once registered logger.
766        """
767
768class ILogger(Interface):
769    """A logger cares for setup, update and restarting of a Python logger.
770    """
771
772    logger = Attribute("""A :class:`logging.Logger` instance""")
773
774
775    def __init__(name, filename=None, site=None, **options):
776        """Create a SIRP logger instance.
777        """
778
779    def setup():
780        """Create a Python :class:`logging.Logger` instance.
781
782        The created logger is based on the params given by constructor.
783        """
784
785    def update(**options):
786        """Update the logger.
787
788        Updates the logger respecting modified `options` and changed
789        paths.
790        """
791
792class ILoggerCollector(Interface):
793
794    def getLoggers(site):
795        """Return all loggers registered for `site`.
796        """
797
798    def registerLogger(site, logging_component):
799        """Register a logging component residing in `site`.
800        """
801
802    def unregisterLogger(site, logging_component):
803        """Unregister a logger.
804        """
805
806#
807# External File Storage and relatives
808#
809class IFileStoreNameChooser(INameChooser):
810    """See zope.container.interfaces.INameChooser for base methods.
811    """
812    def checkName(name, attr=None):
813        """Check whether an object name is valid.
814
815        Raises a user error if the name is not valid.
816        """
817
818    def chooseName(name, attr=None):
819        """Choose a unique valid file id for the object.
820
821        The given name may be taken into account when choosing the
822        name (file id).
823
824        chooseName is expected to always choose a valid file id (that
825        would pass the checkName test) and never raise an error.
826
827        If `attr` is not ``None`` it might been taken into account as
828        well when generating the file id. Usual behaviour is to
829        interpret `attr` as a hint for what type of file for a given
830        context should be stored if there are several types
831        possible. For instance for a certain student some file could
832        be the connected passport photograph or some certificate scan
833        or whatever. Each of them has to be stored in a different
834        location so setting `attr` to a sensible value should give
835        different file ids returned.
836        """
837
838class IExtFileStore(IFileRetrieval):
839    """A file storage that stores files in filesystem (not as blobs).
840    """
841    root = schema.TextLine(
842        title = u'Root path of file store.',
843        )
844
845    def getFile(file_id):
846        """Get raw file data stored under file with `file_id`.
847
848        Returns a file descriptor open for reading or ``None`` if the
849        file cannot be found.
850        """
851
852    def getFileByContext(context, attr=None):
853        """Get raw file data stored for the given context.
854
855        Returns a file descriptor open for reading or ``None`` if no
856        such file can be found.
857
858        Both, `context` and `attr` might be used to find (`context`)
859        and feed (`attr`) an appropriate file name chooser.
860
861        This is a convenience method.
862        """
863
864    def deleteFile(file_id):
865        """Delete file stored under `file_id`.
866
867        Remove file from filestore so, that it is not available
868        anymore on next call to getFile for the same file_id.
869
870        Should not complain if no such file exists.
871        """
872
873    def deleteFileByContext(context, attr=None):
874        """Delete file for given `context` and `attr`.
875
876        Both, `context` and `attr` might be used to find (`context`)
877        and feed (`attr`) an appropriate file name chooser.
878
879        This is a convenience method.
880        """
881
882    def createFile(filename, f):
883        """Create file given by f with filename `filename`
884
885        Returns a hurry.file.File-based object.
886        """
887
888class IFileStoreHandler(Interface):
889    """Filestore handlers handle specific files for file stores.
890
891    If a file to store/get provides a specific filename, a file store
892    looks up special handlers for that type of file.
893
894    """
895    def pathFromFileID(store, root, filename):
896        """Turn file id into path to store.
897
898        Returned path should be absolute.
899        """
900
901    def createFile(store, root, filename, file_id, file):
902        """Return some hurry.file based on `store` and `file_id`.
903
904        Some kind of callback method called by file stores to create
905        file objects from file_id.
906
907        Returns a tuple ``(raw_file, path, file_like_obj)`` where the
908        ``file_like_obj`` should be a HurryFile, a SIRPImageFile or
909        similar. ``raw_file`` is the (maybe changed) input file and
910        ``path`` the relative internal path to store the file at.
911
912        Please make sure the ``raw_file`` is opened for reading and
913        the file descriptor set at position 0 when returned.
914
915        This method also gets the raw input file object that is about
916        to be stored and is expected to raise any exceptions if some
917        kind of validation or similar fails.
918        """
919
920class IPDF(Interface):
921    """A PDF representation of some context.
922    """
923
924    def __call__(view=None):
925        """Create a bytestream representing a PDF from context.
926
927        If `view` is passed in additional infos might be rendered into
928        the document.
929        """
930
931class IMailService(Interface):
932    """A mail service.
933    """
934
935    def __call__():
936        """Get the default mail delivery.
937        """
938
939from zope.configuration.fields import Path
940class IDataCenterConfig(Interface):
941    path = Path(
942        title = u'Path',
943        description = u"Directory where the datacenter should store"
944                     u"files by default (adjustable in web UI).",
945        required = True,
946        )
947
Note: See TracBrowser for help on using the repository browser.