Pages

Friday 16 July 2021

Umbraco - Copy Grid content

In Umbraco 7, we had Vorto to handle multi-lingual fields.

In Umbraco 8, Variants were introduced, as well as the split screen editor where you can have 2 languages side by side, for easier translation.

My problem?

Normal text fields can be easily copied left to right by selecting them and copy/pasting the content.
What's been bugging me since the very beginning, is that it's a real hassle to copy an entire grid to a new language.

As a Content Editor in charge of translating this content, I am required to rebuild the entire grid-structure. To translate the grid in the screenshot above, I would have to :

  • add an XL row
  • add a headline component
    • copy/paste the text (or translate it on the fly)
  • add a richtext component
    • copy/paste the text (or translate it on the fly)
  • add an M-M row
  • add an image component
    • figure out which image they used, if I want to use the same one
  • add a richtext component
    • copy/paste the text (or translate it on the fly)
  • ... and so on
This gets tedious very fast. What I really want to be able to do is copy the entire grid structure from left to right and then just translate the portions of text that I need to change.

This feature was requested in October 2019. It became up-for-grabs February 2020 (meaning, anyone who felt like it, could do an attempt at implementing it and making a pull request).
April 2021 someone picked it up and started working on it. The pull request is still pending further improvements.

Disclaimer(s) about my "solution"

  • It was tested on Umbraco 8.13. It should work on older versions and will probably remain working on future versions (minor tweaks could be needed, but are unlikely since it's so isolated).
  • When upgrading to a newer version, you'll probably need to redo the modifications.
  • It is a bit hacky (modifying files in Umbraco), but works fine for all the projects I'm working on.
  • It does not take into account that custom propertyeditors might subscribe to certain events emitted by the grid when e.g. adding a new row or control.

My solution

My quick and dirty solution to have the option of copy/pasting the entire structure of the grid from left to right (or even from item A to item B), consists of adding custom code to 2 files in the Umbraco codebase.

What will happen is:
  • A button "Copy Grid" is displayed next to the "Reorder" button. When pressed, the JSON describing the entire grid is copied to localStorage.
  • A button "Paste Grid" is displayed when no rows have yet been added to the grid and will simply grab the JSON from localStorage again. 

The good news is that these files are .js and .html, so there's no compilation step necessary, making this change something a 5 year old (with advanced computer skills) could do.

\Umbraco\js\umbraco.controllers.js

We have to place are code somewhere, so find the definition of the method called "toggleSortMode" (around line 19.000) and insert the following 2 function-definitions above it.
// Start Grid Copy/Paste
var gridCopyAlias = "grid-value-copy";

$scope.copyGridModel = function copyGridModel() {
    localStorageService.set(gridCopyAlias, $scope.model.value);

    var msg = 'Grid successfully copied to clipboard.';
    notificationsService.success(msg);		
}

$scope.pasteGridModel = function pasteGridModel() {
    try {
        var modelValue = localStorageService.get(gridCopyAlias);
        if (modelValue) {
            $scope.model.value = modelValue;							
        } else {
            var msg = 'Couldn\'t find a copied Umbraco Grid. Make sure to copy an existing Grid first.';
            notificationsService.error(msg);    
        }
    } catch (err) {
        var msg = 'Something went wrong pasting the Grid.';
        notificationsService.error(msg, err);
    }
}
// End Grid Copy/Paste

Then, scroll up a bit (~250 lines) to find the definition of Angular controller "Umbraco.PropertyEditors.GridController".
'use strict';
angular.module('umbraco').controller('Umbraco.PropertyEditors.GridController', function ($scope, localizationService, gridService, umbRequestHelper, angularHelper, $element, eventsService, editorService, overlayService, $interpolate) {

and inject 2 additional services at the end:
  • notificationsService : to be able to add warnings, errors in the backoffice
  • localStorageService : something to serve as a clipboard while copy/pasting
It should now look like 
'use strict';
angular.module('umbraco').controller('Umbraco.PropertyEditors.GridController', function ($scope, localizationService, gridService, umbRequestHelper, angularHelper, $element, eventsService, editorService, overlayService, $interpolate, notificationsService, localStorageService) {

\Umbraco\Views\propertyeditors\grid\grid.html

All the way at the top, you will find the definition for the ReOrder button.
    <umb-editor-sub-header appearance="white" ng-if="showReorderButton()">
        <umb-editor-sub-header-content-right>
            <umb-button action="toggleSortMode()" button-style="link" icon="icon-navigation" label-key="{{reorderKey}}" type="button">
            </umb-button>
        </umb-editor-sub-header-content-right>
    </umb-editor-sub-header>

We will add 2 of our own buttons here, to call the methods we just added to the controller.
Just replace the entire section with the code below
    <umb-editor-sub-header appearance="white" ng-if="!showReorderButton()">
        <umb-editor-sub-header-content-right>
            <umb-button action="pasteGridModel()" button-style="link" icon="icon-arrow-down" label="Paste Grid" type="button">
            </umb-button>
        </umb-editor-sub-header-content-right>
    </umb-editor-sub-header>

    <umb-editor-sub-header appearance="white" ng-if="showReorderButton()">
        <umb-editor-sub-header-content-right>
            <umb-button action="copyGridModel()" button-style="link" icon="icon-arrow-up" label="Copy Grid" type="button">
            </umb-button>

            <umb-button action="toggleSortMode()" button-style="link" icon="icon-navigation" label-key="{{reorderKey}}" type="button">
            </umb-button>
        </umb-editor-sub-header-content-right>
    </umb-editor-sub-header>

Make sure to put these modified files in your solution, or they will be overwritten the next time you do a deploy.