Templates for custom rich text fields

79 views
Skip to first unread message

Greg McGuffey

unread,
Sep 26, 2016, 2:06:22 PM9/26/16
to DevTargetProcess
I need to setup one or more mashups to load templates for custom rich text fields. This would have the same functionality as the Entity Templates mashup, but for specific custom fields that are rich text.

I hoping it is just a matter of setting up the right listener. I think I need to replace 'description' with something else in this line:

addBusListener('description', 'afterRender', function(e, renderData) {

So, as an example, I have a custom field Major Deliverables, which has an ID of 111 and a field name CustomField9. I tried CustomField9 and just tried print to console, but it didn't like that.

I'm very new to mashup and could use some guidance.

Thanks!

tsar...@targetprocess.com

unread,
Sep 27, 2016, 1:16:09 PM9/27/16
to DevTargetProcess
Hello!

That's quite simple to achieve :).

Use the following snippet:

tau.mashups
   
.addDependency('Underscore')
   
.addDependency('tau/configurator')
   
.addMashup(function(_, configurator) {
       
'use strict';

       
// Use pair 'rich text custom field name (as set when you create the one)': 'custom field content'.
       
var templates = {
           
'Major Deliverables': 'As a <i>major deliverables</i>...',
           
'Minor Deliverables': 'As a <i>minor deliverables</i>...',
           
// Put an HTML-formatted template into quotes
           
'My rich custom field name': 'My template'
       
};

       
var reg = configurator.getBusRegistry();

       
var addBusListener = function(busName, eventName, listener) {
            reg
.on('create', function(e, data) {
               
var bus = data.bus;
               
if (bus.name === busName) {
                    bus
.once(eventName, listener);
               
}
           
});

            reg
.on('destroy', function(e, data) {
               
var bus = data.bus;
               
if (bus.name === busName) {
                    bus
.removeListener(eventName, listener);
               
}
           
});
       
};

       
// Interrupt rich text custom field bus. Options.ready to ensure our placeholder is set after Tp's one.
        addBusListener
('customField.richtext', 'afterRender + options.ready', function(e, renderData, unused) {
           
var $el = renderData.element;

           
var $description = $el.find('.ui-description__inner');
           
var data = renderData.data;
           
var value = renderData.data.value;

           
if ($description.length && !value) {
               
var templateName = data.name;
               
var template = templates[templateName];

               
if (template) {
                    $description
.attr('data-placeholder', '');
                    $description
.append('<div>' + template + '</div>');
               
}
           
}
       
});

       
// Interrupt rich text custom field bus.
        addBusListener
('customField.richtext', 'afterRender:last + $editor.ready', function(e, renderData, $editor) {
           
var data = renderData.data;
           
var value = renderData.data.rawDescription;

           
var templateName = data.name;
           
var template = templates[templateName];
           
           
if (!value && template && $editor.data('ui-richeditorMarkdown')) {
                $editor
.richeditorMarkdown('setText', template);
           
}
       
});
   
});

Hope, this helps!

Greg McGuffey

unread,
Sep 27, 2016, 2:01:12 PM9/27/16
to DevTargetProcess, tsar...@targetprocess.com
Thanks so much!

One more question: how can I differentiate between custom fields on different entities. E.g. I have a custom field named "Risks" on UserStory and TeamIteration and I want different templates for each entity.

Dmitry Tsarevich

unread,
Sep 27, 2016, 3:54:53 PM9/27/16
to DevTargetProcess, tsar...@targetprocess.com
My bad :).

Try this updated snippet:

tau.mashups
    .addDependency('Underscore')
    .addDependency('tau/configurator')
    .addMashup(function(_, configurator) {
        'use strict';

        var templates = {
            // 'Entity type' : { custom fields configs }. 
            'UserStory': {
                // Use pair 'rich text custom field name (as set when you create the one)': 'custom field content'.
                'Major Deliverables': 'As a <i>major deliverables</i>...',
                // Put an HTML-formatted template into quotes.
                'Risks': 'User story risks...'
            },
            'Bug': {
                'Risks': 'Bug risks...',
                'My rich custom field name': 'My template'
            },
            'Feature': null,
            'Task': null,
            'Request': null,
            'TestCase': null
        };

        var reg = configurator.getBusRegistry();

        var addBusListener = function(busName, eventName, listener) {
            reg.on('create', function(e, data) {
                var bus = data.bus;
                if (bus.name === busName) {
                    bus.once(eventName, listener);
                }
            });

            reg.on('destroy', function(e, data) {
                var bus = data.bus;
                if (bus.name === busName) {
                    bus.removeListener(eventName, listener);
                }
            });
        };

        var getEntityCustomFieldTemplates = function(context) {
            var entityTypeName = context.entity.entityType.name.toLowerCase();
            var term = _.find(context.getTerms(), function(v) {
                return (v.wordKey || v.name).toLowerCase().replace(' ', '') === entityTypeName;
            });
            var termValue = term ? term.value.toLowerCase() : null;

            var customFieldTemplates = _.find(templates, function(v, k) {
                return k.toLowerCase() === entityTypeName || k.toLowerCase() === termValue;
            });
            return customFieldTemplates;
        };

        var getTemplate = function(context, templateName) {
            var customFieldTemplates = getEntityCustomFieldTemplates(context);
            return customFieldTemplates[templateName];
        };

        // Interrupt rich text custom field bus. Options.ready to ensure our placeholder is set after Tp's one.
        addBusListener('customField.richtext', 'afterRender + options.ready', function(e, renderData, unused) {
            var $el = renderData.element;

            var $description = $el.find('.ui-description__inner');
            var data = renderData.data;
            var value = renderData.data.value;

            if ($description.length && !value) {
                var templateName = data.name;
                var template = getTemplate(renderData.view.config.context, templateName);

                if (template) {
                    $description.attr('data-placeholder', '');
                    $description.append('<div>' + template + '</div>');
                }
            }
        });

        // Interrupt rich text custom field bus. 
        addBusListener('customField.richtext', 'afterRender:last + $editor.ready', function(e, renderData, $editor) {
            var data = renderData.data;
            var value = renderData.data.rawDescription;

            var templateName = data.name;
            var template = getTemplate(renderData.view.config.context, templateName);
            
            if (!value && template && $editor.data('ui-richeditorMarkdown')) {
                $editor.richeditorMarkdown('setText', template);
            }
        });
    });

tsar...@targetprocess.com

unread,
Jan 25, 2017, 11:53:25 AM1/25/17
to DevTargetProcess, tsar...@targetprocess.com
Now with TP 3.10.9 mashup needs an update. Correct version is:

tau.mashups
   
.addDependency('Underscore')
   
.addDependency('tau/configurator')
   
.addMashup(function(_, configurator) {
       
'use strict';



       
var templates = {

           
// 'Entity type' : { custom fields configs }.
           
'UserStory': {

               
// Use pair 'rich text custom field name (as set when you create the one)': 'custom field content'.

               
'Major Deliverables': 'As a <i>major deliverables</i>...',

               
// Put an HTML-formatted template into quotes.
               
'Risks': 'User story risks...'
           
},
           
'Bug': {
               
'Risks': 'Bug risks...',

               
'My rich custom field name': 'My template'

           
},
           
'Feature': null,
           
'Task': null,
           
'Request': null,
           
'TestCase': null
       
};



       
var reg = configurator.getBusRegistry();



       
var addBusListener = function(busName, eventName, listener) {
            reg
.on('create', function(e, data) {
               
var bus = data.bus;
               
if (bus.name === busName) {
                    bus
.once(eventName, listener);
               
}
           
});


            reg
.on('destroy', function(e, data) {
               
var bus = data.bus;
               
if (bus.name === busName) {
                    bus
.removeListener(eventName, listener);
               
}
           
});
       
};



       
var getEntityCustomFieldTemplates = function(context) {

           
var entityTypeName = context.entity.entityType.name.toLowerCase();
           
var term = _.find(context.getTerms(), function(v) {
               
return (v.wordKey || v.name).toLowerCase().replace(' ', '') === entityTypeName;
           
});
           
var termValue = term ? term.value.toLowerCase() : null;


           
var customFieldTemplates = _.find(templates, function(v, k) {
               
return k.toLowerCase() === entityTypeName || k.toLowerCase() === termValue;
           
});
           
return customFieldTemplates;
       
};


       
var getTemplate = function(context, templateName) {
           
var customFieldTemplates = getEntityCustomFieldTemplates(context);
           
return customFieldTemplates[templateName];
       
};



       
// Interrupt rich text custom field bus. previewBus.ready to ensure our placeholder is set after Tp's one.
        addBusListener
('customField.richtext', 'afterRender + previewBus.ready', function(e, renderData, unused) {

           
var $el = renderData.element;


           
var $description = $el.find('.ui-description__inner');
           
var data = renderData.data;
           
var value = renderData.data.value;


           
if ($description.length && !value) {
               
var templateName = data.name;

               
var template = getTemplate(renderData.view.config.context, templateName);



               
if (template) {
                    $description
.attr('data-placeholder', '');
                    $description
.append('<div>' + template + '</div>');
               
}
           
}
       
});


       
// Interrupt rich text custom field bus.
        addBusListener
('customField.richtext', 'afterRender:last + $editor.ready', function(e, renderData, $editor) {
           
var data = renderData.data;
           
var value = renderData.data.rawDescription;


           
var templateName = data.name;

           
var template = getTemplate(renderData.view.config.context, templateName);

           
           
if (!value && template && $editor.data('ui-richeditorMarkdown')) {
                $editor
.richeditorMarkdown('setText', template);
           
}
       
});
   
});

Sorry for broken one.

Greg McGuffey

unread,
Feb 1, 2017, 12:19:30 PM2/1/17
to devtarge...@googlegroups.com

Thank you for the update!

--
You received this message because you are subscribed to a topic in the Google Groups "DevTargetProcess" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/devtargetprocess/LMVJisJyTTM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to devtargetproce...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Greg McGuffey

unread,
Feb 1, 2017, 1:02:16 PM2/1/17
to devtarge...@googlegroups.com

I just noticed that this is now by entity, very nice!

 

This does prompt a few questions:

1.       Can I do custom entities for iterations or team iterations?

2.       And if we customize the name of an entity, do we use the TP standard name or the custom name? I.e. if we rename bugs to defects, which do we use?

3.       It appears that if two processes use the same name for custom fields in each process, the same template is used, correct? (don’t see a problem, just want to validate I’m understanding it correctly)

Thanks!

Greg McGuffey

unread,
Feb 1, 2017, 7:13:39 PM2/1/17
to devtarge...@googlegroups.com

The code provided doesn’t work at all. I was not able to figure out why or a work around.

 

It just does nothing….

tsar...@targetprocess.com

unread,
Feb 2, 2017, 8:11:21 AM2/2/17
to DevTargetProcess
Hello!


The code provided doesn’t work at all. I was not able to figure out why or a work around.
 
It just does nothing….

What is the TP version you use?
Just tested version 3.10.9.32425 and new mashup works well.

Looks like you've configured the mashup incorrectly.

1.       Can I do custom entities for iterations or team iterations?


If I understood you correctly, you can apply mashup for iterations (Sprint) and team iterations (Team Sprint).


2.       And if we customize the name of an entity, do we use the TP standard name or the custom name? I.e. if we rename bugs to defects, which do we use?


You should use TP standard name. For example if you rename Bug to Defect, Bug should be used to apply templates for Bug & Defect.

We can customize mashup to allow select & apply rich text field templates for distinct custom names, if needed.

 

3.       It appears that if two processes use the same name for custom fields in each process, the same template is used, correct? (don’t see a problem, just want to validate I’m understanding it correctly) 

Yes, it is. The same template will be used when two processes have the same name for rich text custom fields.

Greg McGuffey

unread,
Feb 2, 2017, 11:30:50 AM2/2/17
to devtarge...@googlegroups.com

Thanks for answering the questions. Much appreciated.

 

We are using TP OnDemand, so latest version: 3.10.9.32425

 

And after a good night’s sleep, it now works!

 

One last (probably not the last) question: the two bus listeners are needed to handle HTML and markdown versions?

Greg McGuffey

unread,
Feb 2, 2017, 7:00:23 PM2/2/17
to devtarge...@googlegroups.com

So, something weird is going on. I’ve been having issues with the mashup:

·         No custom fields show up on a form

·         Boards refuse to load (i.e. they just are blank with the progress spinner spinning.

 

I added custom fields to match the sample you provided and it seems to work OK.

 

However, any changes start breaking stuff.

 

Steps:

1.       Open two tabs: one to do mashup editing, one to test

2.       Open a user story in test tab. Note custom fields (TP.mashup.custom.fields.shown.png)

3.       Open mashup editor in other one

4.       Took your sample code and made that my mashup.

5.       Set all entity templates to null (no templates defined).

6.       Enabled mashup.

7.       Save it

8.       Refresh testing tab.

9.       Open a user story

10.   NO CUSTOM fields. (TP.mashup.missing.custom.fields.png)

11.   Disable mashup and save

12.   Rerfresh testing tab

13.   Custom fields are back

TP.mashup.missing.custom.fields.png
TP.mashups.custom.fields.shown.png

tsar...@targetprocess.com

unread,
Feb 3, 2017, 5:22:12 AM2/3/17
to DevTargetProcess
One last (probably not the last) question: the two bus listeners are needed to handle HTML and markdown versions?
 
Looks like rich text custom fields always use HTML editor, so there is no need to add second bus listener or smth.

So, something weird is going on.

I though you will just comment or delete unneeded entity templates.

In case you want to null unused entity templates, the mashup should be updated. See below:

tau.mashups
   
.addDependency('Underscore')
   
.addDependency('tau/configurator')
   
.addMashup(function(_, configurator) {
       
'use strict';

       
var templates = {
           
// 'Entity type' : { custom fields configs }.

           
'UserStory': null,

           
'Bug': {
               
'Risks': 'Bug risks...',
               
'My rich custom field name': 'My template'
           
},

           
'Sprint': {
               
'Annotation': 'Annotation...'
           
},
           
'Team Sprint': {
               
'Annotation': 'Annotation...'

           
},
           
'Feature': null,
           
'Task': null,
           
'Request': null,
           
'TestCase': null
       
};
       
var reg = configurator.getBusRegistry();

       
var addBusListener = function(busName, eventName, listener) {
            reg
.on('create', function(e, data) {
               
var bus = data.bus;
               
if (bus.name === busName) {
                    bus
.once(eventName, listener);
               
}
           
});

            reg
.on('destroy', function(e, data) {
               
var bus = data.bus;
               
if (bus.name === busName) {
                    bus
.removeListener(eventName, listener);
               
}
           
});
       
};

       
var getEntityCustomFieldTemplates = function(context) {
           
var entityTypeName = context.entity.entityType.name.toLowerCase();
           
var term = _.find(context.getTerms(), function(v) {
               
return (v.wordKey || v.name).toLowerCase().replace(' ', '') === entityTypeName;
           
});
           
var termValue = term ? term.value.toLowerCase() : null;
            var customFieldTemplates = _.find(templates || {}, function(v, k) {

               
return k.toLowerCase() === entityTypeName || k.toLowerCase() === termValue;
           
});
           
return customFieldTemplates;
       
};

       
var getTemplate = function(context, templateName) {
           
var customFieldTemplates = getEntityCustomFieldTemplates(context);

           
return customFieldTemplates && customFieldTemplates[templateName];

       
};

       
// Interrupt rich text custom field bus. previewBus.ready to ensure our placeholder is set after Tp's one.
        addBusListener
('customField.richtext', 'afterRender + previewBus.ready', function(e, renderData, unused) {
           
var $el = renderData.element;
           
var $description = $el.find('.ui-description__inner');
           
var data = renderData.data;
           
var value = renderData.data.value;

           
if ($description.length && !value) {
               
var templateName = data.name;
               
var template = getTemplate(renderData.view.config.context, templateName);

               
if (template) {
                    $description
.attr('data-placeholder', '');
                    $description
.append('<div>' + template + '</div>');
               
}
           
}
       
});

       
// Interrupt rich text custom field bus.
        addBusListener
('customField.richtext', 'afterRender:last + $editor.ready', function(e, renderData, $editor) {
           
var data = renderData.data;
           
var value = renderData.data.rawDescription;
           
var templateName = data.name;
           
var template = getTemplate(renderData.view.config.context, templateName);
           
           
if (!value && template && $editor.data('ui-richeditorMarkdown')) {
                $editor
.richeditorMarkdown('setText', template);
           
}
       
});
   
});

Greg McGuffey

unread,
Feb 3, 2017, 11:28:26 AM2/3/17
to devtarge...@googlegroups.com

That really helped. There was one more line that needed to be added to the getCustomFieldTemplates function. The one that worked is below:

 

        var getEntityCustomFieldTemplates = function(context) {

            var entityTypeName = context.entity.entityType.name.toLowerCase();

            var term = _.find(context.getTerms(), function(v) {

                return (v.wordKey || v.name).toLowerCase().replace(' ', '') === entityTypeName;

            });

            var termValue = term ? term.value.toLowerCase() : null;

            var customFieldTemplates = _.find(templates || {}, function(v, k) {

                return k.toLowerCase() === entityTypeName || k.toLowerCase() === termValue;

            });

 

           // this line added so the getTemplate has a list.

            customFieldTemplates = customFieldTemplates || {};

 

            return customFieldTemplates;

        };

 

With that line, all is well in the universe.

 

Thanks again!

 

 

From: devtarge...@googlegroups.com [mailto:devtarge...@googlegroups.com] On Behalf Of tsar...@targetprocess.com


Sent: Friday, February 03, 2017 3:22 AM
To: DevTargetProcess

Reply all
Reply to author
Forward
0 new messages