plone / Products.CMFPlone

The core of the Plone content management system
https://plone.org
GNU General Public License v2.0
245 stars 188 forks source link

RelatedItems Widget upload broken if used twice #3814

Closed jensens closed 1 year ago

jensens commented 1 year ago

BUG/PROBLEM REPORT (OR OTHER COMMON ISSUE)

What I did:

I have a type with two relationfield and upload enabled on both of them.

On edit I got an error:

Uncaught (in promise) Error: Dropzone already attached.
    s Angular
    init upload.js:145
    d base.js:59
    n base.js:105
    initUploadView relateditems.js:441
    success relateditems.js:394
    jQuery 6
    renderToolbar relateditems.js:385
    formatSelection relateditems.js:559
    createChoice select2.js:3254
    addSelectedChoice select2.js:3233
    jQuery 2
    addSelectedChoice select2.js:3232
    updateSelection select2.js:3157
    initSelection select2.js:3078
    initSelection relateditems.js:649
    jQuery 6
    search utils.js:186
    initSelection relateditems.js:637
    initSelection select2.js:3076
    init select2.js:821
    select2 select2.js:3578
    jQuery 2
    select2 select2.js:3565
    initializeSelect2 select2.js:142
    init relateditems.js:678
    d base.js:59
    n base.js:105
    c base.js:40
    initPattern registry.js:119
    scan registry.js:200
    init registry.js:71
    jQuery 9
    init registry.js:64
    45057 patterns.js:73
    Webpack 5
        v
        promise callback*66500
        v
        <anonymous>
        <anonymous>
dropzone.js:427:14

full image: screenshot_2023-06-28_15:31:17_selection

What I expect to happen:

No error and one working upload UI.

What actually happened:

The first upload menu shows two times the UI:

screenshot_2023-06-28_15:28:34_selection

The second field looks fine:

screenshot_2023-06-28_15:28:45_selection

What version of Plone/ Addons I am using:

Plone 6.0.6 and a custom type with schema like:

# SNIP

@provider(IFormFieldProvider)
class ITeaserImage(model.Schema):
    """Behavior providing a referenced teaser image"""

    widget(
        "teaser_image",
        RelatedItemsFieldWidget,
        pattern_options={
            "basePath": utils.make_base_path,
            "favorites": make_image_favorites,
            "mode": "browse",
            "recentlyUsed": True,
            "recentlyUsedKey": utils.recently_used_common_key,
            "scanSelection": True,
            "selectableTypes": ["Image"],
            "upload": utils.allow_upload,
            "uploadAllowView": utils.make_allow_upload_url,
        },
    )
    teaser_image = RelationChoice(
        title=_("label_teaser_image", default="Teaser Image"),
        vocabulary="akbild.portal.vocabulary.rootcatalog",
        required=False,
    )
    fieldset(
        "images",
        label=_("Images"),
        fields=["teaser_image"],
        order=10,
    )
# SNIP

and with a subclassed field type:

# SNIP

class SliderRelatedItemsWidget(RelatedItemsWidget):
    def slides(self):
        slides = []
        try:
            related_slider_images = api.relation.get(
                source=self.context, relationship="slider_images"
            )
        except IntIdMissingError:
            return []
        for value in related_slider_images:
            obj = value.to_object
            if not obj:
                continue
            slides.append(
                {
                    "image": obj.restrictedTraverse("@@images"),
                    "title": obj.title,
                    "alt": obj.description,
                    "rights": obj.rights,
                }
            )
        return slides

@implementer(IFieldWidget)
def SliderRelatedItemsFieldWidget(field, request, extra=None):
    if extra is not None:
        request = extra
    return FieldWidget(field, SliderRelatedItemsWidget(request))

@provider(IFormFieldProvider)
class ISliderImages(model.Schema):
    """Behavior providing a referenced slider images"""

    widget(
        "slider_images",
        SliderRelatedItemsFieldWidget,
        pattern_options={
            "basePath": utils.make_base_path,
            "mode": "browse",
            "selectableTypes": ["Image"],
            "favorites": make_image_favorites,
            "upload": utils.allow_upload,
            "uploadAllowView": utils.make_allow_upload_url,
            "scanSelection": True,
            "recentlyUsed": True,
            "recentlyUsedKey": utils.recently_used_common_key,
        },
    )
    slider_images = RelationList(
        title=_("label_sliderimages", default="Slider Images"),
        value_type=RelationChoice(
            title=_("label_slidersimage", default="Slider Image"),
            vocabulary="akbild.portal.vocabulary.rootcatalog",
        ),
        required=False,
    )
    fieldset("images", fields=["slider_images"])

# SNIP

cc @thet @petschki

petschki commented 1 year ago

I use two RelatedItemsFieldWidget with upload enabled since quite some time without any issues https://github.com/collective/collective.behavior.relatedmedia/blob/main/collective/behavior/relatedmedia/behavior.py#L87 ... I quickly copied bits of your code above to my custom contenttype like this:

directives.widget(
        "teaser_image",
        RelatedItemsFieldWidget,
        pattern_options={
            "mode": "browse",
            "recentlyUsed": True,
            "scanSelection": True,
            "selectableTypes": ["Image"],
            "upload": True,
        },
    )
    teaser_image = RelationChoice(
        title=_("label_teaser_image", default="Teaser Image"),
        vocabulary="plone.app.vocabularies.Catalog",
        required=False,
    )

    directives.widget(
        "slider_images",
        RelatedItemsFieldWidget,
        pattern_options={
            "mode": "browse",
            "recentlyUsed": True,
            "scanSelection": True,
            "selectableTypes": ["Image"],
            "upload": True,
        },
    )
    slider_images = RelationList(
        title=_("label_teaser_image", default="Teaser Image"),
        value_type=RelationChoice(
            title=_("label_slidersimage", default="Slider Image"),
            vocabulary="plone.app.vocabularies.Catalog",
        ),
        required=False,
    )

result: both fields have single upload dropdown and no JS error on the site ...

Bildschirmfoto 2023-06-28 um 16 21 02 Bildschirmfoto 2023-06-28 um 16 21 11
petschki commented 1 year ago

If you append ?loglevel=DEBUG to your edit url and look at the browser console, there should be some more debugging info. This enables patternslib and mockup DEBUG logging ...

petschki commented 1 year ago

This fix just got released in plone.staticresources = 2.1.4