Example #1
0
 def __init__(self, block_types, **kwargs):
     super().__init__(**kwargs)
     if isinstance(block_types, Block):
         self.stream_block = block_types
     elif isinstance(block_types, type):
         self.stream_block = block_types(required=not self.blank)
     else:
         self.stream_block = StreamBlock(block_types, required=not self.blank)
Example #2
0
    def test_that_image_caption_should_be_displayed_when_provided(self):
        caption_text = "Test caption"
        body_block = StreamBlock([("image", CaptionedImageBlock())])
        block_value = StructValue(CaptionedImageBlock,
                                  [("image", ImageFactory()),
                                   ("caption", caption_text)])
        body = StreamValue(body_block, [("image", block_value)])
        blog_article_page = self._create_blog_article_page(body=body)

        response = self.client.get(path=blog_article_page.get_absolute_url())

        self.assertContains(response, caption_text)
Example #3
0
    def test_that_article_intro_should_strip_markdown_syntax(self):
        paragraph_string = "###Lorem ipsum `dolor` sit amet, **consectetur** adipiscing [elit](http://www.google.com). \r\n* Nullam *laoreet* venenatis enim, non luctus nisi finibus ut.\r\n> Mauris porta eleifend massa, nec maximus lacus luctus a. Aenean libero felis, placerat non malesuada a, maximus id erat. Nulla ut"
        expected_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam laoreet venenatis enim, non luctus nisi finibus ut. Mauris porta eleifend massa, nec maximus lacus luctus a. Aenean libero felis, placerat non malesuada a, maximus id erat. Nulla ut"

        body_block = StreamBlock([(ArticleBodyBlockNames.MARKDOWN.value,
                                   MarkdownBlock())])
        body = StreamValue(
            body_block,
            [(ArticleBodyBlockNames.MARKDOWN.value, paragraph_string)])
        blog_article = self._create_blog_article_page(body=body)

        self.assertEqual(blog_article.intro, expected_string + INTRO_ELLIPSIS)
Example #4
0
class SectionBlock(StructBlock):
    special_class = CharBlock(required=False)

    rows = StreamBlock([
        ("row", RowBlock()),
        ("spacer", SpacerBlock()),
    ])

    class Meta:
        template = "blocks/section.html"
        icon = "doc-empty"
        label = "Section"
class ModalBlock(BaseStructBlock):
    title = CharBlock()
    body = StreamBlock([('description', RichTextBlock(required=False)),
                        ('document_block', DocumentBlock(required=False)),
                        ('embed_block', EmbedBlock(required=False)),
                        ('image_block', ImageBlock(required=False)),
                        ('map_block', MapBlock(required=False)),
                        ('table_block', TableBlock(required=False))])

    class Meta:
        icon = 'no-view'
        template = 'core/blocks/modal_block.html'
Example #6
0
 def test_that_existing_article_should_not_be_given_duplicated_article_in_recommendations(
         self):
     other_article = self._create_blog_article_page()
     articles_block = StreamBlock([("page", PageChooserBlock())])
     recommended_articles = StreamValue(articles_block,
                                        [("page", other_article)])
     article = self._create_blog_article_page(
         recommended_articles=recommended_articles)
     article.recommended_articles.stream_data.append(
         ("page", other_article))
     with self.assertRaises(ValidationError):
         article.full_clean()
Example #7
0
def highlight_streamfield():
    """Return a streamfield which only allows one highlight block."""
    return StreamField(
        StreamBlock(
            [
                ('highlight', HighlightBlock()),
            ],
            max_num=1,
            required=False,
        ),
        blank=True,
    )
Example #8
0
class TerminalBlock(blocks.StructBlock):

    content = StreamBlock(
        [("line", TerminalLineBlock()),
         ("multiline", TerminalMultiLineBlock())],
        required=True,
    )

    class Meta:
        template = "streams/terminal_block.html"
        icon = "fa-terminal"
        label = "Terminal Display"
Example #9
0
class MediaGalleryBlock(StructBlock):
    """
    """
    title = TextBlock()
    description = TextBlock()
    gallery = StreamBlock([
        ('image_item',MediaGalleryImageItem()),
        ('video_item', MediaGalleryVideoItem())
    ])

    class Meta:
        template = "includes/blocks/media_gallery.html"
Example #10
0
    def test_that_article_intro_should_support_markdown_blocks(self):
        paragraph_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam laoreet venenatis enim, non luctus nisi finibus ut. Mauris porta eleifend massa, nec maximus lacus luctus a. Aenean libero felis, placerat non malesuada a, maximus id erat. Nulla ut purus elementum, auctor orci eget, facilisis est."
        expected_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam laoreet venenatis enim, non luctus nisi finibus ut. Mauris porta eleifend massa, nec maximus lacus luctus a. Aenean libero felis, placerat non malesuada a, maximus id erat. Nulla ut"

        body_block = StreamBlock([(ArticleBodyBlockNames.MARKDOWN.value,
                                   MarkdownBlock())])
        body = StreamValue(
            body_block,
            [(ArticleBodyBlockNames.MARKDOWN.value, paragraph_string)])
        blog_article = self._create_blog_article_page(body=body)

        self.assertEqual(blog_article.intro, expected_string + INTRO_ELLIPSIS)
class Nav(StructBlock):
    custom_class = CharBlock(required=False)
    nav_items = StreamBlock([
        ('nav_item', NavItem()),
        ('login_link', LoginNavItem()),
        ('dashboard_link', DashboardLinkNavItem()),
        ('logout_link', LogoutNavItem()),
        ('create_account_link', CreateAccountNavItem()),
    ])

    class Meta:
        icon = "fa-bars"
        template = "blocks/bootstrap/nav.html"
Example #12
0
class PlaceBlock(StructBlock):
    title = TextBlock()
    subtitle = TextBlock(required=False)
    image = ImageChooserBlock(required=False)
    body = RichTextBlock(icon='fa-paragraph',
                         template='blocks/paragraph_block.html',
                         features=RICHTEXT_FEATURES_NO_FOOTNOTES)
    side_section_title = TextBlock(required=False)
    links = StreamBlock([('link', LinkBlock())], required=False)

    class Meta():
        icon = 'fa-globe'
        template = 'blocks/place_block.html'
Example #13
0
class HeroSectionBlock(SectionBlock):
    heading = CharBlock(required=False,
                        max_length=100,
                        label='Hero title',
                        default='We are heroes')
    description = TextBlock(
        required=False,
        max_length=400,
        label='Hero subtitle',
        default=
        'The thing we do is better than any other similar thing and this hero panel will convince you of that, just by having a glorious background image.'
    )
    image = ImageChooserBlock(required=False, label='Hero image')
    content = StreamBlock(
        [('button',
          StructBlock(
              [
                  ('text',
                   CharBlock(required=False, max_length=80, label='Label')),
                  ('url', URLBlock(required=False, label='URL')),
              ],
              required=False,
              label='Call to action',
              help_text='A "call-to-action" button, like "Sign Up Now!"')),
         ('video', EmbedBlock(required=False, label='Video')),
         ('quote',
          StructBlock(
              [
                  ('text', TextBlock()),
                  ('author', CharBlock(required=False)),
              ],
              required=False,
              label='Quote',
              help_text
              ='An inspiring quotation, optionally attributed to someone'))],
        required=False,
        block_counts={
            'button': {
                'max_num': 1
            },
            'video': {
                'max_num': 1
            },
            'quote': {
                'max_num': 1
            }
        })

    class Meta:
        icon = 'placeholder'
        label = 'Hero Section'
Example #14
0
def update_information_panel(panel):
    (rendition_mobile,
     rendition_desktop) = get_renditions(panel.value['image_meta'])

    panel.value['image'] = StructValue(BackgroundImageBlock, [
        ('image', panel.value['image']),
        ('meta_mobile_rendition',
         rendition_mobile if panel.value['mobile_use_renditions'] else 'none'),
        ('meta_desktop_rendition', rendition_desktop
         if panel.value['desktop_use_renditions'] else 'none'),
    ])

    panel.value['meta_layout'] = 'desktop-image-left-mobile-image-top'

    if panel.value['meta_variant'] not in [
            'light_background', 'dark_background'
    ]:
        variant = {
            'mobile-image-top-text-right':
            'desktop-image-left-mobile-image-top',
            'mobile-image-right': 'desktop-image-right-mobile-image-right',
        }
        panel.value['meta_layout'] = variant[panel.value['meta_variant']]
        panel.value['meta_variant'] = 'light_background'

    cta_stream_block = StreamBlock([('simple_menu_item', SimpleCtaLinkBlock())
                                    ])

    ctas = []
    for item in panel.value['cta']:
        ctas.append(('simple_menu_item',
                     StructValue(SimpleCtaLinkBlock, [
                         ('link_text', item.value['link_text']),
                         ('link_external', item.value['link_external']),
                         ('link_page', item.value['link_page']),
                         ('link_id', item.value['link_id']),
                         ('meta_cta_variant', 'button'),
                     ])))

    panel.value['ctas'] = StreamValue(cta_stream_block, ctas)

    panel.value['panel_id'] = panel.value.pop('shelf_id')

    keys_to_remove = [
        'image_meta', 'mobile_use_renditions', 'desktop_use_renditions', 'cta'
    ]
    for key in keys_to_remove:
        del (panel.value[key])

    panel.block = StandardInformationPanel()
    return panel
class ExamplePage(Page):
    content = StreamField([
        ('image', ImageChooserBlock()),
        ('text', RichTextBlock()),
        ('docs',
         StreamBlock([
             ('doc', DocumentChooserBlock()),
             ('page', PageChooserBlock()),
         ])),
    ])

    content_panels = Page.content_panels + [
        StreamFieldPanel('content'),
    ]
Example #16
0
class Offer(models.Model):
    company_name = models.CharField(
        max_length=255,
        blank=False,
        help_text='Display name of the company offering the deal'
    )

    company_logo = models.ForeignKey(
        MatteImage,
        blank=True,
        null=True,
        help_text='Companies logo displayed next to the offer',
        on_delete=models.SET_NULL
    )

    company_website = models.URLField(blank=True, default='')

    deal_tag = models.CharField(
        max_length=255,
        blank=False,
        help_text='The deal itself, "40%", "By one get one free", etc'
    )

    is_featured = models.BooleanField(default=False)

    category = models.ForeignKey(OfferCategory, blank=False, null=False, on_delete=models.CASCADE)

    main = StreamField(
        StreamBlock([
            components.text.to_pair(),
            components.image.to_pair(),
            components.callout.to_pair(),
        ], required=False),
        blank=True,
        help_text='Any additional information about this deal'
    )

    def __str__(self):
        return f'{self.deal_tag} @ {self.company_name}'

    panels = [
        FieldPanel('deal_tag', classname='full title'),
        FieldPanel('company_name', classname='full title'),
        FieldPanel('company_website'),
        FieldPanel('category'),
        FieldPanel('is_featured'),
        ImageChooserPanel('company_logo'),
        StreamFieldPanel('main'),
    ]
Example #17
0
class ProfileSlice(blocks.StructBlock):
    title = blocks.CharBlock(required=True)
    description = blocks.TextBlock(required=True)

    menu_name = blocks.CharBlock(required=True)
    background_color = blocks.CharBlock(required=True, max_length=7)

    body = StreamBlock([
        components.text.to_pair(),
        components.internal_link.to_pair(),
        components.external_link.to_pair(),
    ])

    image = components.image.block()
    profile_link = blocks.URLBlock(required=False)
Example #18
0
 def block_definition(local_blocks):
     return LinkCategorySerializer.block_name, blocks.StructBlock(
         local_blocks=[
             ('name',
              blocks.TextBlock(max_length=50,
                               required=True,
                               help_text=_('Name of the category'))),
             ('stream',
              StreamBlock(local_blocks,
                          label=_('A nested content of a category'))),
             ('icon',
              blocks.ChoiceBlock(choices=[('none', _('None'))] + icons,
                                 default=['none'],
                                 label=_('The icon'))),
         ])
Example #19
0
def navigation(blank=False):
    """Return a stream of different module types."""
    required = not blank
    return StreamField(
        StreamBlock(
            [
                ('type_a', TypeA()),
                ('type_b', TypeB()),
                ('type_c', TypeC()),
            ],
            max_num=1,
            required=required,
        ),
        blank=blank
    )
Example #20
0
class TwoColumnBlock(StructBlock):
    background = ChoiceBlock(choices=COLOUR_CHOICES, default="orange")
    left_column = StreamBlock([
        ('heading', CharBlock(classname="full title")),
        ('paragraph', RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('embedded_video', EmbedBlock()),
    ],
                              icon='arrow-left',
                              label='Left column content')

    right_column = StreamBlock([
        ('heading', CharBlock(classname="full title")),
        ('paragraph', RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('embedded_video', EmbedBlock()),
    ],
                               icon='arrow-right',
                               label='Right column content')

    class Meta:
        template = 'blocks/two_column_block.html'
        icon = 'placeholder'
        label = 'Two Columns'
Example #21
0
class ColumnBlock(StructBlock):
    """
    Define the blocks that all columns will utilize
    """
    background = ImageChooserBlock(
        required=False,
        help_text="This will set the background image of the row.",
        group="Container")
    width = IntegerBlock(
        max_value=12,
        min_value=1,
        default=12,
        blank=True,
        required=False,
        help_text="Select the width of the column, max of 12.",
        group="Container")

    padding = ChoiceBlock(
        choices=[
            ('', 'Select a padding size'),
            ('none', 'None'),
            ('small', 'Small'),
            ('medium', 'Medium'),
            ('large', 'Large'),
        ],
        blank=True,
        required=False,
        help_text=
        "Select how much top and bottom padding you would like on the row.",
        group="Container")

    content = StreamBlock([
        ('heading', HeadingBlock()),
        ('paragraph',
         RichTextBlock(icon="fa-paragraph",
                       template="blocks/paragraph_block.html")),
        ('carousel', Carousel()),
        ('image', ImageBlock()),
        ('banner', BannerBlock()),
        ('quote', BlockQuote()),
        ('heading', HeadingBlock()),
    ],
                          help_text="Add content to column.")

    class Meta:
        icon = "fa-columns"
        template = "blocks/column.html"
        closed = True
Example #22
0
    def test_that_deleting_recommended_articles_should_not_raise_any_errors(
            self):
        articles = [self._create_blog_article_page() for _ in range(3)]
        articles_block = StreamBlock([("page", PageChooserBlock())])
        recommended_articles = StreamValue(articles_block,
                                           [("page", article)
                                            for article in articles])
        main_article = self._create_blog_article_page(
            recommended_articles=recommended_articles)

        for article in articles:
            article.delete()
        main_article.refresh_from_db()

        self.assertEqual(BlogArticlePage.objects.all().count(), 1)
        self.assertEqual(len(main_article.recommended_articles), 0)
Example #23
0
class DocumentBoxSectionBlock(StructBlock):
    """
    A block for holding multiple document boxes.
    """
    section_heading = TextBlock(required=False)
    section_sub_heading = RichTextBlock(required=False, features=RICHTEXT_FEATURES_NO_FOOTNOTES)
    document_box_heading = CharBlock(icon="title", required=False)
    document_boxes = StreamBlock([
        ('document_box', DocumentBoxBlock()),
    ], required=False)
    alt = BooleanBlock(default=True, help_text="White background if checked", required=False)


    class Meta:
        icon = 'doc-full-inverse'
        template = 'blocks/documentboxsection_block.html'
Example #24
0
class ImageBlock(StructBlock):
    image = ImageChooserBlock()
    description = CharBlock(required=False)
    is_circle = BooleanBlock(label="Show as a circle?", required=False)
    is_full_width = BooleanBlock(required=False)
    content = StreamBlock([
        ('testimonial',
         TestimonialBlock(target_model='pages.Testimonial',
                          template='blocks/testimonial_block.html')),
        ('rich_text', RichTextBlock()),
    ],
                          required=False)

    class Meta:
        label = 'Image'
        template = 'blocks/image_block.html'
        icon = 'fa-image'
Example #25
0
class VideoProjectPage(CreationBase):
    template = "blog/creative_hub/post.html"
    subpage_types = []
    parent_page_type = [
        "blog.CreativeHub",
    ]

    video = StreamField(
        StreamBlock([("youtube", blocks.YouTubeBlock())], min_num=1,
                    max_num=1))

    content_panels = CreationBase.content_panels + [
        StreamFieldPanel("video"),
    ]

    def creation(self):
        return self.video
Example #26
0
 def setUp(self):
     self._set_default_blog_index_page_as_new_root_page_child()
     self.blog_index_page = self._get_newest_blog_index_page()
     header_block = StreamBlock([(ArticleBodyBlockNames.HEADER.value,
                                  CharBlock())])
     body = StreamValue(
         header_block,
         [
             (ArticleBodyBlockNames.HEADER.value, "Header 1"),
             (ArticleBodyBlockNames.HEADER.value, "Header 2"),
             (ArticleBodyBlockNames.HEADER.value, "Header 3"),
         ],
     )
     self.blog_article_page = self._create_blog_article_page(
         blog_index_page=self.blog_index_page,
         body=body,
         table_of_contents=True)
Example #27
0
def get_or_create_banner(banner_values):
    try:
        return Banner.objects.get(shelf_id=banner_values.shelf_id)
    except Banner.DoesNotExist:
        contenttype = ContentType.objects.get(app_label='dctcmsbase',
                                              model='banner')
        expanded_stream_block = ExpandedStreamBlock([('background_image',
                                                      BackgroundImageBlock())])
        cta_stream_block = StreamBlock([('cta', SimpleCtaLinkBlock())])

        attributes = StreamValue(
            expanded_stream_block,
            [('background_image',
              StructValue(BackgroundImageBlock, [
                  ('image', banner_values.background_image),
                  ('meta_variant',
                   'gradient' if banner_values.meta_gradient else 'none'),
                  ('meta_mobile_rendition', 'none'),
                  ('meta_desktop_rendition', 'none'),
                  ('meta_image_display', 'cover'),
              ]))],
        )

        ctas = StreamValue(
            cta_stream_block,
            [('cta',
              StructValue(SimpleCtaLinkBlock, [
                  ('link_text', banner_values.cta_text),
                  ('link_external', banner_values.cta_link),
                  ('link_page', banner_values.cta_page),
                  ('link_id', ''),
                  ('meta_cta_variant', 'button'),
              ]))],
        )

        banner = Banner.objects.create(
            shelf_id=banner_values.shelf_id,
            heading=banner_values.heading,
            body=banner_values.body,
            tracking_group=banner_values.tracking_group,
            content_type=contenttype,
            attributes=attributes,
            ctas=ctas,
        )
    return banner
Example #28
0
class BasicContentPage(Page):
    content = StreamField(StreamBlock([
        components.text.to_pair(),
        components.callout.to_pair(),
        components.alert.to_pair(),
        components.inset.to_pair(),
        components.image.to_pair(),
        components.button_group_links.to_pair(),
    ]),
                          verbose_name='Main Content',
                          null=True,
                          blank=True)

    content_panels = Page.content_panels + [
        StreamFieldPanel('content'),
    ]

    type_fields = ('content', )
Example #29
0
class ReferencePage(Page):
    main = StreamField(StreamBlock([
        components.text.to_pair(),
        components.callout.to_pair(),
        components.image.to_pair(),
        components.button_group_links.to_pair(),
    ]),
                       verbose_name='Main Content',
                       null=True,
                       blank=True)

    content_panels = [
        FieldPanel('title'),
        StreamFieldPanel('main'),
    ]

    def topic(self):
        return self.get_parent().specific
Example #30
0
class ColumnBlock(StructBlock):
    class_name = "column"
    body = StreamBlock([
        ("title", TitleBlock(required=False)),
        ("text", RichTextBlock(required=False)),
        ("image", ImageChooserBlock(required=False)),
        ("link", LinkBlock(required=False)),
    ])

    class Meta:
        icon = "cogs"
        label = "Column"
        template = "blocks/column.html"

    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context=parent_context)
        context["class_name"] = self.class_name
        return context
 def _create_blog_article_page(
     self,
     blog_index_page=None,
     title="Simple Article Title",
     page_title="Simple Article Title",
     date=datetime.now(),
     body=None,
     author=None,
     read_time=7,
     table_of_contents=False,
     recommended_articles=None,
     views=0,
     cover_photo=None,
     article_photo=None,
     is_main_article=False,
 ):
     if body is None:
         block = StreamBlock([(ArticleBodyBlockNames.MARKDOWN.value,
                               MarkdownBlock())])
         body = StreamValue(
             block,
             [(ArticleBodyBlockNames.MARKDOWN.value, "Hello, World")])
     if author is None:
         author = BossFactory()
     blog_article_page = BlogArticlePage(
         title=title,
         page_title=page_title,
         date=date,
         body=body,
         author=author,
         read_time=read_time,
         table_of_contents=table_of_contents,
         recommended_articles=recommended_articles,
         views=views,
         cover_photo=cover_photo,
         article_photo=article_photo,
         is_main_article=is_main_article,
     )
     if blog_index_page is None:
         blog_index_page = self._get_newest_blog_index_page()
     blog_index_page.add_child(instance=blog_article_page)
     blog_article_page.save()
     return blog_article_page
Example #32
0
class StreamField(models.Field):
    def __init__(self, block_types, **kwargs):
        super().__init__(**kwargs)
        if isinstance(block_types, Block):
            self.stream_block = block_types
        elif isinstance(block_types, type):
            self.stream_block = block_types(required=not self.blank)
        else:
            self.stream_block = StreamBlock(block_types, required=not self.blank)

    def get_internal_type(self):
        return 'TextField'

    def get_panel(self):
        from wagtail.admin.edit_handlers import StreamFieldPanel
        return StreamFieldPanel

    def deconstruct(self):
        name, path, _, kwargs = super().deconstruct()
        block_types = self.stream_block.child_blocks.items()
        args = [block_types]
        return name, path, args, kwargs

    def to_python(self, value):
        if value is None or value == '':
            return StreamValue(self.stream_block, [])
        elif isinstance(value, StreamValue):
            return value
        elif isinstance(value, str):
            try:
                unpacked_value = json.loads(value)
            except ValueError:
                # value is not valid JSON; most likely, this field was previously a
                # rich text field before being migrated to StreamField, and the data
                # was left intact in the migration. Return an empty stream instead
                # (but keep the raw text available as an attribute, so that it can be
                # used to migrate that data to StreamField)
                return StreamValue(self.stream_block, [], raw_text=value)

            if unpacked_value is None:
                # we get here if value is the literal string 'null'. This should probably
                # never happen if the rest of the (de)serialization code is working properly,
                # but better to handle it just in case...
                return StreamValue(self.stream_block, [])

            return self.stream_block.to_python(unpacked_value)
        else:
            # See if it looks like the standard non-smart representation of a
            # StreamField value: a list of (block_name, value) tuples
            try:
                [None for (x, y) in value]
            except (TypeError, ValueError):
                # Give up trying to make sense of the value
                raise TypeError("Cannot handle %r (type %r) as a value of StreamField" % (value, type(value)))

            # Test succeeded, so return as a StreamValue-ified version of that value
            return StreamValue(self.stream_block, value)

    def get_prep_value(self, value):
        if isinstance(value, StreamValue) and not(value) and value.raw_text is not None:
            # An empty StreamValue with a nonempty raw_text attribute should have that
            # raw_text attribute written back to the db. (This is probably only useful
            # for reverse migrations that convert StreamField data back into plain text
            # fields.)
            return value.raw_text
        else:
            return json.dumps(self.stream_block.get_prep_value(value), cls=DjangoJSONEncoder)

    def from_db_value(self, value, expression, connection, context):
        return self.to_python(value)

    def formfield(self, **kwargs):
        """
        Override formfield to use a plain forms.Field so that we do no transformation on the value
        (as distinct from the usual fallback of forms.CharField, which transforms it into a string).
        """
        defaults = {'form_class': BlockField, 'block': self.stream_block}
        defaults.update(kwargs)
        return super().formfield(**defaults)

    def value_to_string(self, obj):
        value = self.value_from_object(obj)
        return self.get_prep_value(value)

    def get_searchable_content(self, value):
        return self.stream_block.get_searchable_content(value)

    def check(self, **kwargs):
        errors = super().check(**kwargs)
        errors.extend(self.stream_block.check(field=self, **kwargs))
        return errors

    def contribute_to_class(self, cls, name, **kwargs):
        super().contribute_to_class(cls, name, **kwargs)

        # Add Creator descriptor to allow the field to be set from a list or a
        # JSON string.
        setattr(cls, self.name, Creator(self))