iraira
iraira

Reputation: 325

Silverstripe 3.2: How to make a custom action button in the CMS to create a new Dataobject and populate it from another one

I'm searching for a way to create a custom action button which allows me to make a new DataObject with pre-filled content from another DataObject. As a simple example: When I have an email and click the "answer"-button in my email-client, I get a new window with pre-filled content from the email before. I need exactly this functionality for my button. This button should appear next to each DataObject in the GridField.

So I know how to make a button and add it to my GridField (--> https://docs.silverstripe.org/en/3.2/developer_guides/forms/how_tos/create_a_gridfield_actionprovider/) and I know how to go to a new DataObject:

Controller::curr()->redirect($gridField->Link('item/new'));

I also found out that there is a duplicate function for DataObjects:

public function duplicate($doWrite = true) {
        $className = $this->class;
        $clone = new $className( $this->toMap(), false, $this->model );
        $clone->ID = 0;

        $clone->invokeWithExtensions('onBeforeDuplicate', $this, $doWrite);
        if($doWrite) {
            $clone->write();
            $this->duplicateManyManyRelations($this, $clone);
        }
        $clone->invokeWithExtensions('onAfterDuplicate', $this, $doWrite);

        return $clone;
    }

Perhaps it's easier than I think but at the moment I just don't get how to rewrite this to get what I need. Can somebody give me a hint?

Upvotes: 1

Views: 1454

Answers (1)

csy_dot_io
csy_dot_io

Reputation: 1199

That's for sure not the cleanest solution but I think it should do the trick.

At first let's create the custom gridfield action. Here we will save all accessible records in a session and add a query string to the url so that we'll know which object we want to "clone"

public function getColumnContent($gridField, $record, $columnName) {
    if(!$record->canEdit()) return;

    $field = GridField_FormAction::create(
        $gridField,
        'clone'.$record->ID,
        'Clone',
        'clone',
        array('RecordID' => $record->ID)
    );

    $values = Session::get('ClonedData');
    $data = $record->data()->toMap();

    if($arr = $values) {
        $arr[$record->ID] = $data;
    } else {
        $arr = array(
            $record->ID => $data
        );
    }

    Session::set('ClonedData', $arr);

    return $field->Field();
}

public function getActions($gridField) {
    return array('clone');
}

public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
    if($actionName == 'clone') {
        $id = $arguments['RecordID'];
        Controller::curr()->redirect($gridField->Link("item/new/?cloneID=$id"));
    }
}

after adding this new component to our gridfield,

$gridField->getConfig()->addComponent(new GridFieldCustomAction());

we'll need to bring the data into the new form. To do so, add this code directly above "return $fields" on your getCMSFields function so it will be executed every time we'll open this kind of object.

$values = Session::get('ClonedData');

if($values) {
  Session::clear('ClonedData');
  $json = json_encode($values);
  $fields->push(LiteralField::create('ClonedData', "<div id='cloned-data' style='display:none;'>$json</div>"));
}

At the end we need to bring the content back into the fields. We'll do that with a little bit of javascript so at first you need to create a new script.js file and include it in the ss backend (or just use an existing one).

(function($) {
  $('#cloned-data').entwine({
    onmatch: function() {
      var data = JSON.parse($(this).text()),
          id = getParameterByName('cloneID');

      if(id && data) {
        var obj = data[id];

        if(obj) {
          $.each(obj, function(i, val) {
            $('[name=' + i + ']').val(val);
          });
        }
      }
    }
  });

  // http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript#answer-901144
  function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  }
})(jQuery);

And that's it ... quite tricky. Hope it will solve your problem.

Upvotes: 1

Related Questions