def create_subobjects(self, context, total=0, types=None):
        request = self.request
        amount = int(request.get('amount', 3))
        if types is None:
            base = aq_base(context)
            if IBaseContent.providedBy(base):
                types = []
                if hasattr(base, 'constrainTypesMode') and base.constrainTypesMode:
                    types = context.locallyAllowedTypes
            elif IDexterityContent.providedBy(base):
                fti = getUtility(IDexterityFTI, name=context.portal_type)
                types = fti.filter_content_types and fti.allowed_content_types
                if not types:
                    msg = _('Either restrict the addable types in this folder or ' \
                            'provide a type argument.')
                    addStatusMessage(request, msg)
                    return total
            else:
                msg = _("The context doesn't provide IBaseContent or "
                        "IDexterityContent. It might be a Plone Site object, "
                        "but either way, I haven't gotten around to dealing with "
                        "it. Why don't you jump in and help?")
                addStatusMessage(request, msg)
                return total

        for portal_type in types:
            if portal_type in ['File', 'Image', 'Folder']:
                continue
                
            for n in range(0, amount):
                obj = self.create_object(context, portal_type)
                total += 1
                if request.get('recurse'):
                    total = self.create_subobjects(obj, total=total, types=None)
        return total
Пример #2
0
def create_subobjects(root, context, data, total=0):
    amount = int(data.get('amount', 3))
    types = data.get('portal_type')
    if types is None:
        base = aq_base(context)
        if IBaseContent.providedBy(base):
            types = []
            if hasattr(base, 'constrainTypesMode') and base.constrainTypesMode:
                types = context.locallyAllowedTypes
        elif IDexterityContent.providedBy(base):
            fti = component.getUtility(IDexterityFTI, name=context.portal_type)
            types = fti.filter_content_types and fti.allowed_content_types
            if not types:
                msg = _('Either restrict the addable types in this folder or ' \
                        'provide a type argument.')
                addStatusMessage(context.request, msg)
                return total
        else:
            msg = _("The context doesn't provide IBaseContent or "
                    "IDexterityContent. It might be a Plone Site object, "
                    "but either way, I haven't gotten around to dealing with "
                    "it. Why don't you jump in and help?")
            addStatusMessage(context.request, msg)
            return total

    recurse = False
    if data.get('recurse', None) not in [None, '0', 'False', False]:
        depth = 0
        node = context
        while IUUID(node) != IUUID(root):
            depth += 1
            node = node.aq_parent

        if depth < data.get('recursion_depth'):
            recurse = True

    for portal_type in types:
        for n in range(0, amount):
            obj = create_object(context, portal_type, data)
            total += 1

            if not IObjectManager.providedBy(obj):
                continue

            if recurse:
                if shasattr(obj, 'getLocallyAllowedTypes'):
                    data['portal_type'] = \
                            list(obj.getLocallyAllowedTypes())
                elif shasattr(obj, 'allowedContentTypes'):
                    data['portal_type'] = \
                            [t.id for t in obj.allowedContentTypes()]
            
                total = create_subobjects(root, obj, data, total)
    return total
    def create_subobjects(self, context, total=0, types=None):
        request = self.request
        amount = int(request.get('amount', 3))
        if types is None:
            base = aq_base(context)
            if IBaseContent.providedBy(base):
                types = []
                if hasattr(base,
                           'constrainTypesMode') and base.constrainTypesMode:
                    types = context.locallyAllowedTypes
            elif IDexterityContent.providedBy(base):
                fti = getUtility(IDexterityFTI, name=context.portal_type)
                types = fti.filter_content_types and fti.allowed_content_types
                if not types:
                    msg = _('Either restrict the addable types in this folder or ' \
                            'provide a type argument.')
                    addStatusMessage(request, msg)
                    return total
            else:
                msg = _(
                    "The context doesn't provide IBaseContent or "
                    "IDexterityContent. It might be a Plone Site object, "
                    "but either way, I haven't gotten around to dealing with "
                    "it. Why don't you jump in and help?")
                addStatusMessage(request, msg)
                return total

        for portal_type in types:
            if portal_type in ['File', 'Image', 'Folder']:
                continue

            for n in range(0, amount):
                obj = self.create_object(context, portal_type)
                total += 1
                if request.get('recurse'):
                    total = self.create_subobjects(obj,
                                                   total=total,
                                                   types=None)
        return total
Пример #4
0
    def __call__(self, **kw):
        """ """
        mdata = api.portal.get_tool('portal_memberdata')
        regtool = api.portal.get_tool('portal_registration')
        portal_props = api.portal.get_tool('portal_properties')
        props = portal_props.site_properties
        use_email_as_login = props.getProperty('use_email_as_login')
        basedir = os.path.abspath(os.path.dirname(__file__))
        datadir = os.path.join(basedir, '../dummydata')
        file = open(datadir + '/memberdata.csv')
        reader = csv.reader(file)
        row_num = 0
        for row in reader:
            if row_num == 0:
                # We will use the headers in the first row as variable names to
                # store the user's details in portal_memberdata.
                dummy_fields = row
            else:
                properties = {}
                for field in dummy_fields:
                    # Since we don't know what properties might be in
                    # portal_memberdata, for example postal_code or zipcode or
                    # zip_code, we make give each header a list of possible values
                    # separated by spaces.
                    fields = field.split(' ')
                    for f in fields:
                        if hasattr(mdata, f):
                            properties[f] = row[dummy_fields.index(field)]

                fullname = row[0] + ' ' + row[1]

                if use_email_as_login:
                    username = properties['email']
                else:
                    username = self.sanitize(fullname.lower().replace(
                        ' ', '-'))
                properties['username'] = username
                properties['fullname'] = fullname
                try:
                    # addMember() returns MemberData object
                    member = regtool.addMember(username,
                                               'secret',
                                               properties=properties)
                except ValueError, e:
                    # Give user visual feedback what went wrong
                    IStatusMessage(self.request).add(
                        _(u"Could not create the users. %s" % username) +
                        unicode(e), "error")
                    continue
                else:
                    log.info('Registered dummy user: %s' % fullname)
Пример #5
0
    def create(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = '\n'.join([
                error.error.__str__() for error in errors])
            return

        context = aq_inner(self.context)
        total = create_subobjects(context, context, data, 0)
        addStatusMessage(
            self.request,
            _(u'Successfully created %d dummy objects.') % total,
            type='info')
        self.request.response.redirect(self.context.REQUEST.get('URL'))
Пример #6
0
    def create(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = '\n'.join([
                error.error.__str__() for error in errors])
            return

        context = aq_inner(self.context)
        total = create_subobjects(context, context, data, 0)
        addStatusMessage(
            self.request,
            _(u'Successfully created %d dummy objects.') % total,
            type='info')
        self.request.response.redirect(self.context.REQUEST.get('URL'))
Пример #7
0
    def __call__(self, **kw):
        """ """
        mdata = api.portal.get_tool('portal_memberdata')
        regtool = api.portal.get_tool('portal_registration')
        portal_props = api.portal.get_tool('portal_properties')
        props = portal_props.site_properties
        use_email_as_login = props.getProperty('use_email_as_login')
        basedir = os.path.abspath(os.path.dirname(__file__))
        datadir = os.path.join(basedir, '../dummydata')
        file = open(datadir+'/memberdata.csv')
        reader = csv.reader(file)
        row_num = 0
        for row in reader:
            if row_num == 0:
                # We will use the headers in the first row as variable names to
                # store the user's details in portal_memberdata.
                dummy_fields = row
            else:
                properties = {}
                for field in dummy_fields:
                    # Since we don't know what properties might be in
                    # portal_memberdata, for example postal_code or zipcode or
                    # zip_code, we make give each header a list of possible values
                    # separated by spaces.
                    fields = field.split(' ')
                    for f in fields:
                        if hasattr(mdata, f):
                            properties[f] = row[dummy_fields.index(field)]

                fullname = row[0] + ' ' + row[1]

                if use_email_as_login:
                    username = properties['email']
                else:
                    username = self.sanitize(fullname.lower().replace(' ', '-'))
                properties['username'] = username
                properties['fullname'] = fullname
                try:
                    # addMember() returns MemberData object
                    member = regtool.addMember(username, 'secret', properties=properties)
                except ValueError, e:
                    # Give user visual feedback what went wrong
                    IStatusMessage(self.request).add(_(u"Could not create the users. %s" % username) + unicode(e), "error")
                    continue
                else:
                    log.info('Registered dummy user: %s' % fullname)
Пример #8
0
    def __call__(self, **kw):
        """ 
        type: string - The portal_type of the content type to create
        amount: integer - The amount of objects to create

        ul: bool - Add unordered lists.
        ol: bool - Add numbered lists.
        dl: bool - Add description lists.
        bq: bool - Add blockquotes.
        code: bool - Add code samples.
        link: bool - Add links.
        prude: bool - Prude version.
        headers: bool - Add headers.
        allcaps: bool - Use ALL CAPS.
        decorate: bool - Add bold, italic and marked text.

        publish: bool - Should the objects be published

        recurse: bool - Should objects be created recursively?

        parnum: integer - 
            The number of paragraphs to generate. (NOT USED)

        length: short, medium, long, verylong - 
            The average length of a paragraph (NOT USED)
        """
        request = self.request
        context = aq_inner(self.context)

        types = self.request.get('type')
        if isinstance(types, str):
            types = [types]

        # There are some formatting options that we want enabled by default. If
        # the user didn't specify them in the URL, we add them here.
        for key, default in OPTIONS.items():
            if not request.has_key(key):
                request.set(key, default)

        total = create_subobjects(context, request, 0, types)
        addStatusMessage(request, _('%d objects successfully created' % total))
        return request.RESPONSE.redirect('/'.join(context.getPhysicalPath()))
Пример #9
0
    def __call__(self, **kw):
        """
        type: string - The portal_type of the content type to create
        amount: integer - The amount of objects to create

        ul: bool - Add unordered lists.
        ol: bool - Add numbered lists.
        dl: bool - Add description lists.
        bq: bool - Add blockquotes.
        code: bool - Add code samples.
        link: bool - Add links.
        prude: bool - Prude version.
        headers: bool - Add headers.
        allcaps: bool - Use ALL CAPS.
        decorate: bool - Add bold, italic and marked text.

        publish: bool - Should the objects be published

        recurse: bool - Should objects be created recursively?

        parnum: integer -
            The number of paragraphs to generate. (NOT USED)

        length: short, medium, long, verylong -
            The average length of a paragraph (NOT USED)
        """
        request = self.request
        context = aq_inner(self.context)

        types = self.request.get('type')
        if isinstance(types, str):
            types = [types]

        # There are some formatting options that we want enabled by default. If
        # the user didn't specify them in the URL, we add them here.
        for key, default in OPTIONS.items():
            if not request.has_key(key):
                request.set(key, default)

        total = create_subobjects(context, request, 0, types)
        addStatusMessage(request, _('%d objects successfully created' % total))
        return request.RESPONSE.redirect('/'.join(context.getPhysicalPath()))
Пример #10
0
    def __call__(self, **kw):
        """ 
        type: string - The portal_type of the content type to create
        amount: integer - The amount of objects to create

        ul: bool - Add unordered lists.
        ol: bool - Add numbered lists.
        dl: bool - Add description lists.
        bq: bool - Add blockquotes.
        code: bool - Add code samples.
        link: bool - Add links.
        prude: bool - Prude version.
        headers: bool - Add headers.
        allcaps: bool - Use ALL CAPS.
        decorate: bool - Add bold, italic and marked text.

        publish: bool - Should the objects be published

        recurse: bool - Should objects be created recursively?

        parnum: integer - 
            The number of paragraphs to generate. (NOT USED)

        length: short, medium, long, verylong - 
            The average length of a paragraph (NOT USED)
        """
        request = self.request
        context = aq_inner(self.context)

        types = self.request.get('type')
        if isinstance(types, str):
            types = [types]

        total = self.create_subobjects(context, 0, types)
        addStatusMessage(request, _('%d objects successfully created' % total))
        return request.RESPONSE.redirect('/'.join(context.getPhysicalPath()))
Пример #11
0
    def __call__(self, **kw):
        """ 
        type: string - The portal_type of the content type to create
        amount: integer - The amount of objects to create

        ul: bool - Add unordered lists.
        ol: bool - Add numbered lists.
        dl: bool - Add description lists.
        bq: bool - Add blockquotes.
        code: bool - Add code samples.
        link: bool - Add links.
        prude: bool - Prude version.
        headers: bool - Add headers.
        allcaps: bool - Use ALL CAPS.
        decorate: bool - Add bold, italic and marked text.

        publish: bool - Should the objects be published

        recurse: bool - Should objects be created recursively?

        parnum: integer - 
            The number of paragraphs to generate. (NOT USED)

        length: short, medium, long, verylong - 
            The average length of a paragraph (NOT USED)
        """
        request = self.request
        context = aq_inner(self.context)

        types = self.request.get('type')
        if isinstance(types, str):
            types = [types]

        total = self.create_subobjects(context, 0, types)
        addStatusMessage(request, _('%d objects successfully created' % total))
        return request.RESPONSE.redirect('/'.join(context.getPhysicalPath()))
Пример #12
0
                fullname = row[0] + ' ' + row[1]
                username = self.sanitize(fullname.lower().replace(' ', '-'))
                properties['username'] = username
                properties['fullname'] = fullname
                try:
                    # addMember() returns MemberData object
                    member = regtool.addMember(username, 'secret', properties=properties)
                except ValueError, e:
                    # Give user visual feedback what went wrong
                    IStatusMessage(self.request).add(_(u"Could not create the users. %s" % username) + unicode(e), "error")
                    continue
                else:
                    log.info('Registered dummy user: %s' % fullname)
            row_num += 1

        IStatusMessage(self.request).add(_(u"Succesfully created %d users." % (row_num-1)), "info")
        return self.request.RESPONSE.redirect('/'.join(self.context.getPhysicalPath()))

    def sanitize(self, str):
        for code, ascii in [('\xc3\xbc', 'ue'),
                            ('\xc3\xb6', 'oe'),
                            ('\xc3\xa4', 'ae'),
                            ('\xc3\xa7', 'c'),
                            ('\xc3\xa8', 'e'),
                            ('\xc3\xa9', 'e'),
                            ('\xc3\xab', 'e'),
                            ('\xc3\xaf', 'i'),
                            ('\xc5\x9e', 'S'),
                            ('\xc5\x9f', 'e'),
                            ]:
            str = str.replace(code, ascii)
Пример #13
0
                fullname = row[0] + ' ' + row[1] 
                username = self.sanitize(fullname.lower().replace(' ', '-'))
                properties['username'] = username 
                properties['fullname'] = fullname
                try:
                    # addMember() returns MemberData object
                    member = regtool.addMember(username, 'secret', properties=properties)
                except ValueError, e:
                    # Give user visual feedback what went wrong
                    IStatusMessage(self.request).add(_(u"Could not create the users. %s" % username) + unicode(e), "error") 
                    continue
                else:
                    log.info('Registered dummy user: %s' % fullname)
            row_num += 1

        IStatusMessage(self.request).add(_(u"Succesfully created %d users." % (row_num-1)), "info") 
        return self.request.RESPONSE.redirect('/'.join(self.context.getPhysicalPath()))

    def sanitize(self, str):
        for code, ascii in [('\xc3\xbc', 'ue'), 
                            ('\xc3\xb6', 'oe'),
                            ('\xc3\xa4', 'ae'), 
                            ('\xc3\xa7', 'c'),
                            ('\xc3\xa8', 'e'), 
                            ('\xc3\xa9', 'e'),
                            ('\xc3\xab', 'e'), 
                            ('\xc3\xaf', 'i'),
                            ('\xc5\x9e', 'S'), 
                            ('\xc5\x9f', 'e'),
                            ]:
            str = str.replace(code, ascii)
Пример #14
0
from collective.loremipsum.utils import create_subobjects
from plone.z3cform.fieldsets.extensible import ExtensibleForm
from plone.z3cform.layout import FormWrapper
from z3c.form import button
from z3c.form import field
from z3c.form import form
from z3c.form.browser.checkbox import CheckBoxFieldWidget
from zope import interface
from zope import schema
import logging


log = logging.getLogger(__name__)

formatting_vocabulary = schema.vocabulary.SimpleVocabulary.fromItems([
    (_(u"Add unordered lists <ul>"), 'ul'),
    (_(u"Add numbered lists <ol>"), 'ol'),
    (_(u"Add description lists <dl>"), 'dl'),
    (_(u"Add blockquotes <bq>"), 'bq'),
    (_(u"Add code samples <code>"), 'code'),
    (_(u"Add links <link>"), 'link'),
    (_(u"Prude version (removes legitimate latin words like 'sex')"), 'prude'),
    (_(u"Add headers"), 'headers'),
    (_(u"Use ALL CAPS"), 'allcaps'),
    (_(u"Add bold, italic and marked text"), 'decorate'),
])


class IPopulateFormSchema(interface.Interface):
    """ """
    portal_type = schema.List(
Пример #15
0
class IPopulateFormButtons(interface.Interface):
    """ """
    create = button.Button(title=_(u"Create the dummy content"))
Пример #16
0
class IPopulateFormSchema(interface.Interface):
    """ """
    portal_type = schema.List(
        title=_(u"Item Type"),
        description=_(u"Choose the types of objects you'd like to "
                      u"create dummies of. The column on the right contains "
                      u"the types that will be created, which is by default "
                      u"all the types allowed in this folder."),
        value_type=schema.Choice(
            vocabulary="plone.app.vocabularies.ReallyUserFriendlyTypes"),
    )

    amount = schema.Int(
        title=_(u"Amount"),
        description=_(u"Choose the amount of objects you'd like to be "
                      u"created for each type chosen above. "
                      u"The default is 3."),
        default=3,
    )

    recurse = schema.Bool(
        title=_(u"Should objects be created recursively?"),
        description=_(
            u'description_recurse',
            default=u"If the objects added are containers, then new "
                    u"objects will be created inside them and so forth. "
                    u"The types of objects created inside a container are "
                    u"determined by the allowable types inside that "
                    u"container."),
    )

    recursion_depth = schema.Int(
        title=_(u"Recursion Depth"),
        description=_(
            u"If objects are created recursively, how many levels"
            u" deep should they be created?"),
        required=True,
        default=3,
    )

    recurse_same_ptypes = schema.Bool(
        title=_(u"Use the same portal types selected above?"),
        description=_(
            u'recurse_same_ptypes',
            default=u"Generate sub-objects using only content types declared "
                    u"into 'Item Type'."),
        default=True,
    )

    generate_images = schema.Bool(
        title=_(u"Generate fake images' content?"),
        description=_(
            u"Check this box to get random fake images "
            u"for all the items with a field named 'image'. ARCHETYPES ONLY"),
        default=False,
        required=False,
    )

    generate_images_service = schema.Choice(
        title=_(u"Image service"),
        description=_(u"Choose which service to use to generate images"),
        vocabulary="collective.loremipsum.fakeimagegetters",
        default=DEFAULT_IMAGE_GETTER,
        required=False,
    )

    generate_images_params = schema.TextLine(
        title=_(u"Image parameters"),
        description=_(
            u"Column ';' separated parameters for the image service. "
            u"Check respective websites for available parameters. "
            u"Default 'text' is the title of the generated object."),
        default=u"size=300x200; category=sports;",
        required=False,
    )

    type_in_title = schema.Bool(
        title=_(u"Include the type name in the title"),
        description=_(
            u"It's sometimes difficult to determine the type of "
            u"an object populated with lorem ipsum text. This option "
            u"helps  by including the type of object in its title."),
        required=False,
    )

    subjects = schema.Text(
        title=_(u"Subjects"),
        description=_(
            u"Line separated subjects. If none provided a default set will be "
            u"used."),
        required=False,
    )

    publish = schema.Bool(
        title=_(u"Publish objects"),
        description=_(u"Should newly created objects be published?"),
        required=False,
    )

    commit = schema.Bool(
        title=_(u"Commit between objects"),
        description=_(
            u"Check this box if the transaction should be "
            u"committed each time an object is created. This will take more "
            u"time but might be necessary, for example if you have default "
            u"values that depend on previous objects being catalogued."),
        required=False,
    )

    formatting = schema.List(
        title=_(u"Rich Text Formatting"),
        description=_(
            u"Choose from the formatting options for "
            u"the lorem ipsum dummy text. This only "
            u"applies to RichText fields."),
        default=['ul', 'ol', 'dl', 'bq', 'code',
                 'link', 'headers', 'decorate'],
        required=False,
        value_type=schema.Choice(
            vocabulary=formatting_vocabulary),
    )
Пример #17
0
                    # addMember() returns MemberData object
                    member = regtool.addMember(username,
                                               'secret',
                                               properties=properties)
                except ValueError, e:
                    # Give user visual feedback what went wrong
                    IStatusMessage(self.request).add(
                        _(u"Could not create the users. %s" % username) +
                        unicode(e), "error")
                    continue
                else:
                    log.info('Registered dummy user: %s' % fullname)
            row_num += 1

        IStatusMessage(self.request).add(
            _(u"Succesfully created %d users." % (row_num - 1)), "info")
        return self.request.RESPONSE.redirect('/'.join(
            self.context.getPhysicalPath()))

    def sanitize(self, str):
        for code, ascii in [
            ('\xc3\xbc', 'ue'),
            ('\xc3\xb6', 'oe'),
            ('\xc3\xa4', 'ae'),
            ('\xc3\xa7', 'c'),
            ('\xc3\xa8', 'e'),
            ('\xc3\xa9', 'e'),
            ('\xc3\xab', 'e'),
            ('\xc3\xaf', 'i'),
            ('\xc5\x9e', 'S'),
            ('\xc5\x9f', 'e'),
Пример #18
0
def create_subobjects(root, context, data, total=0):
    amount = int(data.get('amount', 3))
    types = data.get('portal_type')
    request = getRequest()

    depth = 0
    node = context
    if not IPloneSiteRoot.providedBy(root):
        while IUUID(node) != IUUID(root):
            depth += 1
            node = node.aq_parent
    else:
        while not IPloneSiteRoot.providedBy(node):
            depth += 1
            node = node.aq_parent

    if types is None or depth > 0:
        base = aq_base(context)
        if IBaseContent.providedBy(base):
            types = []
            if hasattr(base, 'constrainTypesMode') and base.constrainTypesMode:
                types = context.locallyAllowedTypes
        elif IDexterityContent.providedBy(base):
            fti = component.getUtility(IDexterityFTI, name=context.portal_type)
            types = fti.filter_content_types and fti.allowed_content_types
            if not types:
                msg = _('Either restrict the addable types in this folder '
                        'or provide a type argument.')
                addStatusMessage(request, msg)
                return total
        else:
            msg = _("The context doesn't provide IBaseContent or "
                    "IDexterityContent. It might be a Plone Site object, "
                    "but either way, I haven't gotten around to dealing with "
                    "it. Why don't you jump in and help?")
            addStatusMessage(request, msg)
            return total

    recurse = False
    if data.get('recurse', None) not in [None, '0', 'False', False] and \
            depth < data.get('recursion_depth'):
        recurse = True

    for portal_type in types:
        for n in range(0, amount):
            obj = create_object(context, portal_type, data)
            total += 1

            if not IObjectManager.providedBy(obj):
                continue

            if recurse:
                if not data.get('recurse_same_ptypes', False):
                    if shasattr(obj, 'getLocallyAllowedTypes'):
                        data['portal_type'] = \
                            list(obj.getLocallyAllowedTypes())
                    elif shasattr(obj, 'allowedContentTypes'):
                        data['portal_type'] = \
                            [t.id for t in obj.allowedContentTypes()]

                total = create_subobjects(root, obj, data, total)
    return total
Пример #19
0
from collective.loremipsum.utils import create_subobjects
from plone.z3cform.fieldsets.extensible import ExtensibleForm
from plone.z3cform.layout import FormWrapper
from z3c.form import button
from z3c.form import field
from z3c.form import form
from z3c.form.browser.checkbox import CheckBoxFieldWidget
from zope import interface
from zope import schema
import logging


log = logging.getLogger(__name__)

formatting_vocabulary = schema.vocabulary.SimpleVocabulary.fromItems([
    (_(u"Add unordered lists <ul>"), 'ul'),
    (_(u"Add numbered lists <ol>"), 'ol'),
    (_(u"Add description lists <dl>"), 'dl'),
    (_(u"Add blockquotes <bq>"), 'bq'),
    (_(u"Add code samples <code>"), 'code'),
    (_(u"Add links <link>"), 'link'),
    (_(u"Prude version (removes legitimate latin words like 'sex')"), 'prude'),
    (_(u"Add headers"), 'headers'),
    (_(u"Use ALL CAPS"), 'allcaps'),
    (_(u"Add bold, italic and marked text"), 'decorate'),
])


class IPopulateFormSchema(interface.Interface):
    """ """
    portal_type = schema.List(