Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot copy / paste areablock elements #559

Closed
solverat opened this issue Apr 20, 2016 · 13 comments
Closed

Cannot copy / paste areablock elements #559

solverat opened this issue Apr 20, 2016 · 13 comments
Labels
Milestone

Comments

@solverat
Copy link
Contributor

1.) create area element (named "test") with a areablock:

<?= $this->areablock("myAreablock", [
    "allowed" => ["wysiwyg"],
    ]]);
?>

2.) place "test" in content two times.
3.) add a wysiwyg element in first area.
4.) try to copy wysiwyg element and paste into second "test" area element.

result
element has been copied but lost its content.

bildschirmfoto 2016-04-20 um 18 18 15

i debugged the pimcore/document/tags/areablock.js:

copyToClipboard(): This method searches after a myAreablockcontent71 element but only finds myAreablockcontent77_1. I think there's a problem in Model\Document\Tag::buildTagName(). Probably the method creates wrong numeration as soon elements are nested.

@solverat
Copy link
Contributor Author

solverat commented Apr 21, 2016

UPDATE

  • 1.) Allow copy & paste elements in areablocks in every depth.
  • 2.) Allow copy & paste content between areabricks

Ok, found it. In Model\Document\Tag::buildTagName() i removed all the numerations except the last one - because that's the new level.

replace

if (\Zend_Registry::isRegistered("pimcore_tag_block_current")) {
    $blocks = \Zend_Registry::get("pimcore_tag_block_current");
    $numeration = \Zend_Registry::get("pimcore_tag_block_numeration");
    if (is_array($blocks) and count($blocks) > 0) {
        if ($type == "block") {
            $tmpBlocks = $blocks;
            $tmpNumeration = $numeration;
            array_pop($tmpBlocks);
            array_pop($tmpNumeration);
            $tmpName = $name;
            if (is_array($tmpBlocks)) {
                $tmpName = $name . implode("_", $tmpBlocks) . implode("_", $tmpNumeration);
            }
            if ($blocks[count($blocks) - 1] == $tmpName) {
                array_pop($blocks);
                array_pop($numeration);
            }
        }
        $name = $name . implode("_", $blocks) . implode("_", $numeration);
    }
}

with

if (\Zend_Registry::isRegistered("pimcore_tag_block_current")) {
        $blocks = \Zend_Registry::get("pimcore_tag_block_current");

        $numeration = \Zend_Registry::get("pimcore_tag_block_numeration");
        if (is_array($blocks) and count($blocks) > 0) {
            if ($type == "block") {
                $tmpBlocks = $blocks;
                $tmpNumeration = $numeration;
                array_pop($tmpBlocks);
                array_pop($tmpNumeration);

                $tmpName = $name;
                if (is_array($tmpBlocks)) {
                    $tmpName = $name . implode("_", $tmpBlocks) . implode("_", $tmpNumeration);
                }

                if ($blocks[count($blocks) - 1] == $tmpName) {
                    array_pop($blocks);
                    array_pop($numeration);
                }
            }

            if( count( $numeration ) > 1 ) {
                $numeration = array_pop( $numeration );
                $blocks = array_pop( $blocks );
            } else {
                $numeration = $numeration[0];
                $blocks = $blocks[0];
            }

            $name = $name . $blocks . $numeration;

        }
    }

But this fix comes with a "big downside": Every placed content inside nested areablock elements will be lost, because the reference to the "documents_elements" table does not exist anymore.

maybe someone can check that?

@solverat
Copy link
Contributor Author

after several hours of debugging i finally found a solution, i think - and it's pretty easy though... i just updated my previous answer.

i would be great if someone can check this out.

@dpfaffenbauer
Copy link
Contributor

i can confirm the problem @solverat has and his fix-suggestion works.

@brusch brusch added the Bug label Apr 25, 2016
@solverat
Copy link
Contributor Author

bump. any progress on this? i think the fix isn't to hard to implement but also important. Although the integration into a seamless update process seems to be an issue, right? i tried to find another way without changing the given structure but i think that's impossible: the php save event creates a different style than javascript on clientside.

our support team is crying for salvation, can't you hear it? :)

@brusch
Copy link
Member

brusch commented Jun 7, 2016

Hmm, the suggest fix is no option. This will completely break the backward compatibility and I don't see any possible migration path.

@solverat
Copy link
Contributor Author

solverat commented Jun 20, 2016

i just tried the last three days and nights to find a solution but i failed (i have to admit, i'm a little bit frustrated). It seems to be impossible to find a continuous way. whatever i tried, at least one of my goals couldn't be done:

  • copy a element from a nested area into root
  • copy a element from root into a nested area
  • copy a nested element into a nested area
  • copy a root element with a arbitrary amount of child areas to root
  • copy a nested element with a arbitrary amount of child areas into another nested area

Because the "child area" elements aren't real children, just some elements with a merged id of the nested parents, it's very difficult to update their ids.

what i tried to do:

extend the editableConfiguration with a new field "tagData"

  • in pimcore/models/Document/Tag/*
$options = [
    "options" => $this->getOptions(),
    "data" => $data,
    "name" => $this->getName(),
    "id" => "pimcore_editable_" . $this->getName(),
    "type" => $this->getType(),
    "inherited" => $this->getInherited(),
    "tagData" => $this->getTagStructureData()
];
$options = @\Zend_Json::encode($options, false, ['enableJsonExprFinder' => true]);

if ($this->editmode) {
    echo '
        <script type="text/javascript">
            editableConfigurations.push(' . $options . ');
        </script>
        <div id="pimcore_editable_' . $this->getName() . '" class="pimcore_editable pimcore_tag_' . $this->getType() . '">
    ';
}

update Pimcore\Model\Document\Tag.php
outsource blocks and numeration to buildTagNameStructure so we can use it in the javascript tag

protected $tagStructureData = array();

public function setTagStructureData($data)
{
        $this->tagStructureData = $data;
}

public function getTagStructureData()
{
       return $this->tagStructureData;
}

/**
 * @param $type
 * @param $name
 * @param null $document
 * @return string
 * @throws \Exception
 * @throws \Zend_Exception
 */
public static function buildTagName($type, $name, $document = null)
{
    if (!preg_match("@^[ -~]+$@", $name)) {
        throw new \Exception("Only ASCII characters are allowed as the name for an editable, your name was: " . $name);
    }

    // check for persona content
    if ($document && $document instanceof Document\Page && $document->getUsePersona()) {
        $name = $document->getPersonaElementName($name);
    }

    $tagStructure = self::buildTagNameStructure($type, $name);

    if (count($tagStructure['blocks']) > 0) {

        $blocks = $tagStructure['blocks'];
        $numeration = $tagStructure['numeration'];

        $name = $name . implode("_", $blocks) . implode("_", $numeration);
        var_dump($name);
    }


    if (strlen($name) > 750) {
        throw new \Exception("Composite name is longer than 750 characters - use shorter names for your editables or reduce amount of nesting levels. Name is: " . $name);
    }

    return $name;
}

public static function buildTagNameStructure($type, $name)
{
    $blocks = array();
    $numeration = array();

    // @todo add document-id to registry key | for example for embeded snippets
    // set suffixes if the tag is inside a block
    if (\Zend_Registry::isRegistered("pimcore_tag_block_current")) {
        $blocks = \Zend_Registry::get("pimcore_tag_block_current");
        $numeration = \Zend_Registry::get("pimcore_tag_block_numeration");
        if (is_array($blocks) and count($blocks) > 0) {
            if ($type == "block") {
                $tmpBlocks = $blocks;
                $tmpNumeration = $numeration;
                array_pop($tmpBlocks);
                array_pop($tmpNumeration);

                $tmpName = $name;
                if (is_array($tmpBlocks)) {
                    $tmpName = $name . implode("_", $tmpBlocks) . implode("_", $tmpNumeration);
                }

                if ($blocks[count($blocks) - 1] == $tmpName) {
                    array_pop($blocks);
                    array_pop($numeration);
                }
            }
        }
    }

    return array('blocks' => $blocks, 'numeration' => $numeration);
}

update startup.js

  • pass tagData to tag clas
function getEditable(config) {
    var id = config.id;
    var type = config.type;
    var name = config.name;
    var options = config.options;
    var data = config.data;
    var tagData = config.tagData ? config.tagData : [];
    var inherited = false;
    if(typeof config["inherited"] != "undefined") {
        inherited = config["inherited"];
    }
    if(in_array(name,editableNames)) {
        pimcore.helpers.showNotification("ERROR", "Duplicate editable name: " + name, "error");
    }
    editableNames.push(name);

    var tag = new pimcore.document.tags[type](id, name, options, data, inherited, tagData);
    tag.setInherited(inherited);

    return tag;
}

update pimcore/document/tags/areablock.js

  • when coping data to clipboard, i tried to define the parent element for each area.
  • with that code it's possible to copy & paste areas with nested areas without any problems. but it's not possible to paste a nested area into another nested area.
copyToClipboard: function (element) {
    var ea;
    var areaIdentifier = {name: this.getName(), key: parseInt(element.getAttribute("key")), tagData : this.tagData};
    var item = {
        identifier: areaIdentifier,
        type: element.getAttribute("type"),
        values: {}
    };

    // check which editables are inside this area and get the data
    for (var i = 0; i < editables.length; i++) {
        try {
            ea = editables[i];

            var blockJoin = areaIdentifier["tagData"]["blocks"].length > 0 ? areaIdentifier["tagData"]["blocks"].join('_') + '_' : '';
            var key = areaIdentifier["tagData"]["numeration"].length > 0 ? areaIdentifier["tagData"]["numeration"].join('_') : areaIdentifier['key'];

            if (ea.getName().indexOf( blockJoin + areaIdentifier["name"] + key ) > 0 && ea.getName() && !ea.getInherited()) {
                item.values[ea.getName()] = {};
                item.values[ea.getName()].parent = false;
                item.values[ea.getName()].data = ea.getValue();
                item.values[ea.getName()].type = ea.getType();
                item.values[ea.getName()].tagData = ea.tagData;
            }


        } catch (e) { }
    }

    //now define each parent
    Ext.Object.each(item.values, function(name, data) {

        if( data["tagData"] && data["tagData"]["blocks"] && data["tagData"]["blocks"].length > 0 ) {

            var currentElementBlocks = data["tagData"]["blocks"];

            var posParentStr = currentElementBlocks[currentElementBlocks.length-1];

            //this element does not have any parents.
            if(currentElementBlocks.length === 1) {
                return;
            }

            Ext.Object.each(item.values, function(subName, subData) {

                if( subData["tagData"] && subData["tagData"]["blocks"] && subData["tagData"]["blocks"].length > 0 ) {

                    var subBlocks = subData["tagData"]["blocks"].slice(0);
                    var subPosParentStr = subBlocks[subBlocks.length-1];

                    //console.log(subName, posParentStr);

                    if( subName === posParentStr )
                    {
                        item.values[ name ].parent = {name : subName, blocks : subBlocks, numeration : subData.tagData.numeration};
                    }

                }
            });

        }

    });

    pimcore.globalmanager.add("areablock_clipboard", item);

},

optionsClickhandler: function (element, btn, e) {
    var menu = new Ext.menu.Menu();

    if(element != false) {
        menu.add(new Ext.menu.Item({
            text: t('copy'),
            iconCls: "pimcore_icon_copy",
            handler: function (item) {
                item.parentMenu.destroy();
                this.copyToClipboard(element);
            }.bind(this)
        }));

        menu.add(new Ext.menu.Item({
            text: t('cut'),
            iconCls: "pimcore_icon_cut",
            handler: function (item) {
                item.parentMenu.destroy();
                this.copyToClipboard(element);
                this.removeBlock(element);
            }.bind(this)
        }));
    }

    if(pimcore.globalmanager.exists("areablock_clipboard")) {
        menu.add(new Ext.menu.Item({
            text: t('paste'),
            iconCls: "pimcore_icon_paste",
            handler: function (item) {
                item.parentMenu.destroy();

                var item = pimcore.globalmanager.get("areablock_clipboard");

                var areaIdentifier = {
                    name: this.getName(),
                    key: (this.getNextKey()+1),
                    tagData : this.tagData
                };

                var fromAreaTagDataBlocks = item["identifier"]["tagData"]['blocks'],
                    fromAreaTagDataNumeration = item["identifier"]["tagData"]['numeration'];

                var fromAreaName = item["identifier"]["name"],
                    fromAreaKey = item["identifier"]["key"];

                var toAreaTagDataBlocks = areaIdentifier["tagData"]['blocks'],
                    toAreaTagDataNumeration = areaIdentifier["tagData"]['numeration'];

                var toAreaName = areaIdentifier["name"],
                    toAreaKey = areaIdentifier["key"];

                console.log('from', fromAreaTagDataBlocks, fromAreaTagDataNumeration, fromAreaName, fromAreaKey);
                console.log('to', toAreaTagDataBlocks, toAreaTagDataNumeration, toAreaName, toAreaKey);

                // push the data as an object compatible to the pimcore.document.tag interface to the rest of
                // available editables so that they get read by pimcore.document.edit.getValues()
                Ext.iterate(item.values, function (key, value) {

                    editables.push({
                        getName: function () {

                            var currentElementTagData = value["tagData"];

                            var realName = key.replace(currentElementTagData["blocks"].join('_') + currentElementTagData["numeration"].join('_'), '');

                            console.log('realName', realName);
                            console.log('elementKey', key);
                            console.log('elementParent', value["parent"]);
                            console.log('elementTagData', currentElementTagData);

                            var subPosItemStr = key;

                            var f1 = fromAreaName + fromAreaKey + fromAreaKey;
                            var f2 = fromAreaName + fromAreaKey;
                            var f3 = fromAreaName;

                            var toAreaNameWithPrefix = toAreaName;

                            if(toAreaTagDataBlocks.length > 0 && fromAreaTagDataBlocks.join('_') !== toAreaTagDataBlocks.join('_')) {

                                toAreaNameWithPrefix = toAreaTagDataBlocks.join('_') + '_' + toAreaName;
                            }

                            subPosItemStr = subPosItemStr.replace( new RegExp(f1, 'g'), toAreaNameWithPrefix + toAreaKey + toAreaKey);
                            subPosItemStr = subPosItemStr.replace( new RegExp(f2, 'g'), toAreaNameWithPrefix + toAreaKey);
                            subPosItemStr = subPosItemStr.replace( new RegExp(f3, 'g'), toAreaNameWithPrefix);

                            if(fromAreaTagDataBlocks.length > 0) {

                                subPosItemStr = subPosItemStr.replace( new RegExp(fromAreaTagDataBlocks.join('_'), 'g'), (toAreaTagDataBlocks.length>0 ? toAreaTagDataBlocks.join('_') : '') );
                                subPosItemStr = subPosItemStr.replace( new RegExp(fromAreaTagDataNumeration.join('_'), 'g'), (toAreaTagDataNumeration.length>0 ? toAreaTagDataNumeration.join('_') : '') );

                            }

                            var newSlug = subPosItemStr;

                            if( value["parent"] !== false && value["parent"]["numeration"].length > 0) {

                                var parentNumeration = value["parent"]["numeration"].slice(0);

                                if( parentNumeration.length > 1 ) {

                                    var oldParentNumerationStr = parentNumeration.join("_");

                                    parentNumeration[0] = toAreaKey;
                                    var newParentNumerationStr = parentNumeration.join("_");

                                    console.log("replace parent: ", newSlug, oldParentNumerationStr, newParentNumerationStr);
                                    newSlug = newSlug.replace(new RegExp(oldParentNumerationStr, 'g'), newParentNumerationStr);

                                }

                            } else {

                                newSlug = newSlug.replace(new RegExp(fromAreaKey +'$'), toAreaKey );

                            }

                            //cleanup
                            newSlug = newSlug.replace( new RegExp('__', 'g'), '');
                            newSlug = newSlug.replace(/_$/, "");

                            var newKey = newSlug;

                            console.warn(newKey);

                            return newKey;
                        },
                        getValue: function () {
                            return value["data"];
                        },
                        getInherited: function () {
                            return false;
                        },
                        getType: function () {
                            return value["type"];
                        }
                    });

                });

                this.addBlock(element, item.type);
            }.bind(this)
        }));
    }

    if(menu.items && menu.items.getCount()) {
        menu.showAt(e.getXY());
    }

    e.stopEvent();
}

@solverat
Copy link
Contributor Author

second thought:
it would be nice to have a parent row in documents_elements. with that in mind the hierarchical structure will allow endless nested area elements. and maybe this issue would be easier to solve. :)

@emonect
Copy link

emonect commented Mar 11, 2017

The company i am working for is now also evaluating pimcore as our alternative CMS and we have come across the same issue with copy&pasting brick elements across different areablocks: Content will not be copied.
Is there a solution in reach? Not being able to copy/paste bricks is somehow a dealbreaker.
I agree that it would be much better to have a child/parent relationship included into the documents_elements table structure. We use something very similar in our current CMS. And i also agree that backwards compatibility shouldn't be the main focus here since this really is a big issue and should somehow be fixed.
We came across this problem trying to create brick-containers which are areablocks themself which results in nested areablock:
http://stackoverflow.com/questions/42723624/copy-paste-brick-elements-between-different-areablock-elements

@brusch brusch added this to the 5.0.0 milestone Mar 24, 2017
@alesak
Copy link
Contributor

alesak commented Apr 5, 2017

I have tried different approach with md5 hash ids for elements which was also rejected as not way to go but I still think it will be good way to go. It is backward compatible and it can automatically update document_elements table on element update. My solution just needs extend document_elements table with one column with element hierarchy inside. It also solve this issue. I have stopped working on it when partially working #876 was just rejected without further communication. I will be more than happy to finish it but need to know it will be considered as the way to go...

@solverat
Copy link
Contributor Author

solverat commented Apr 5, 2017

hey @alesak the coreteam added the 5.0.0 milestone to this issue. meaning they're working on a solution. because there is a feature implemention stop on 4.x i think you should stop with investing more time on this "build-in" feature. but maybe you could finish it as patch/plugin somehow? I'm sure that a lot of pimcore 4.x users (count me in) are interested to check out your solution. :)

@alesak
Copy link
Contributor

alesak commented Apr 6, 2017

Hi @solverat I will definitely pause my effort on this for now unless there will be some communication from core team @brusch . Any solution needs to be backward but more importantly forward compatible with new solution which pimcore team hopefully comes with.... Anyway I consider this as a BUG and not a feature so as long as there is planed 4.5.1 version it should be considered... I will probably try to patch 4.x & 3.x branch when solution comes to 5.x branch from core team as we have many instances running previous versions of pimcore . Hopefully this new solution will bring unlimited brick inheritance which is crucial for me...

@solverat
Copy link
Contributor Author

solverat commented Apr 6, 2017

you're right - it's a bug, not a feature. :)

@brusch brusch changed the title cannot copy / paste areablock elements Cannot copy / paste areablock elements Apr 27, 2017
@brusch
Copy link
Member

brusch commented Jun 9, 2017

fixed in v5

@brusch brusch closed this as completed Jun 9, 2017
maff added a commit that referenced this issue Jan 25, 2018
C/p support was removed in 007e770
as we decided to only support it for the new naming scheme. This
re-introduces c/p for the legacy scheme, with the same errors and
implications as outlined in #559.

Upgrade to the nested naming scheme is still highly recommended.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants