SmartView Workflow extension

Hi

We have workflow extension that was working fine on CS version 20.3

  var FormExtensionController = WorkItemExtensionController.extend({
  type: 101,
  sub_type: 1,

  constructor: function (attributes, options) {
  WorkItemExtensionController.prototype.constructor.apply(this, arguments);
  options || (options = {});
  this.context = attributes.context;
  },
 
  validate: function (type, sub_type) {
 
  return true;
  },
 
  execute: function (options) {

  var extensionPoint = options.extensionPoint;
  var deferred = $.Deferred();
  if (extensionPoint === WorkItemExtensionController.ExtensionPoints.AddForm) {

 
  // CUSTOM CONTENT
 
  var WRContext = new PageContext();

  var MCTileReportView = new TileReportView({
  data: {
  title: 'Workflow WebReport Content',
  id: 222222,
  },
  context: WRContext
  });
 
  WRContext.fetch();
 
  deferred.resolve({viewToShow: MCTileReportView});
  return deferred.promise();

 
  } else {

  }
  }

  });

  return FormExtensionController;
});


On 20.4 it does not work anymore, we get error "Ticket: Error getting ticket. Different user detected. Please reload the page to login again."

This seems to happen on context.fetch.

Any suggestion?

Tagged:

Comments

  • I tried using "this.context" instead of "new PageContext()" but then WebReport content is not loaded/fetched

  • There were authentication changes recently in the framework which means you have to explicitly assign the session token to the PageContext. e.g.

    var WRContext = new PageContext(),
    	connector = WRContext.getObject(ConnectorFactory);
    	
    	// Pre-authenticate the default connector session
    	connector.authenticator.updateAuthenticatedSession({
    		ticket: [LL_REPTAG_OTCSTICKET QUOTE /]
    	});
    

    The ConnectorFactory module path is 'csui/utils/contexts/factories/connector'.

    Cheers

    Ian

  • Thanks for the quick reply, Ian.

    I tried it out. Error is gone, but I still can't get the WebReport to load

    This is what I changed it

     var WRContext = new PageContext(),
      connector = WRContext.getObject(ConnectorFactory);
    
     // Pre-authenticate the default connector session
    connector.authenticator.updateAuthenticatedSession({
      ticket: options.model.connector.connection.session.ticket
     });
    

    If I understood it correctly, I just took existing ticket from main model.

  • This is the example extension with static view that works just fine

      execute: function (options) {
    
      var extensionPoint = options.extensionPoint;
      var deferred = $.Deferred();
      if (extensionPoint === WorkItemExtensionController.ExtensionPoints.AddForm) {
    
      var MyLayout = Marionette.LayoutView.extend({
      className: 'extensiontest-view',
      template: '<h4>This is the Text for ExtensionPoints Test</h4>',
    
      constructor: function MyLayout(options) {
      options || (options = {});
    
      this.context = options.context;
      Marionette.LayoutView.prototype.constructor.apply(this, arguments);
      }
      });
      var view = new MyLayout();
      deferred.resolve({viewToShow: view});
      return deferred.promise( );
      } else {
      deferred.resolve({});
      return deferred.promise();
      }
      },
    

    In version 20.3 also the WebReport View (the one I pasted first) worked.

  • Maybe @"Ferdinand Prantl" has some hint? :)
  • Ferdinand Prantl
    Ferdinand Prantl E Community Moderator

    Yes, this has been caused by a fix for security leak, that remained overseen for some time. If you create a new context, the connector that you will get using that context, will not be authenticated automatically:

    var WRContext = new PageContext();

    Such context will need an authentication to be useful for making server calls. Only the application page (or an authenticated REST call) can supply the initial authentication token. Both need to take place in back-end (OScript on CS), when a part of the script of the code is generated.

    There are two ways how to get an authenticated context. The first one that you know from the Smart UI SDK development - you get it from the parent control. The parent control passes the context in options:

    var MyView = Marionette.View.extend({
      constructor MyView(options) {
        Marionette.View.call(thi, options)
        var context = options.context
        this.context = context
        ...
      }
    

    Extensible views and controllers usually obtain the context like this and store it in the instance.

    The second way is getting the authentication token on your own, when (and only if) you generate a part of your code in OScript, as Ian described it. If your extension does not get access to an authenticated context

    If you extension point does not provide an authenticated context like this, it is worth filing a feature request for it. When your component runs on an already authenticated Smart UI page, you should not need to fetch the authentication token by your code.

  • Thanks for your reply, @Ferdinand Prantl !

    I was able to get authenticated context already with the method that @Ian_Whitfield suggested. I don't get error anymore.

    My remaining problem is that I'm not able to fetch and show WebReport view. Works if I try static view (as in documentation).

    execute: function (options) {
    
      var extensionPoint = options.extensionPoint;
      var deferred = $.Deferred();
      if (extensionPoint === WorkItemExtensionController.ExtensionPoints.AddForm) {
    
     
      // WebReport
     
       WRContext = new PageContext(),
      connector = WRContext.getObject(ConnectorFactory);
    
     // Pre-authenticate the default connector session
    connector.authenticator.updateAuthenticatedSession({
      ticket: options.model.connector.connection.session.ticket
     });
    
    
      var MCTileReportView = new TileReportView({
      data: {
      title: 'Workflow WebReport Content',
      id: 222222,
      },
      context: WRContext
      });
     
      WRContext.fetch();
      deferred.resolve({viewToShow: MCTileReportView});
    
      return deferred.promise();
    
     
      } else {
    
      }
      }
    
    

    In earlier versions these 2 lines used to work - WebReport was fetched and shown. Not anymore in version 20.4

    WRContext.fetch();
    deferred.resolve({viewToShow: MCTileReportView});
    

    Do you have any suggestion where it could be wrong?

    Uldis

  • I think I got it working like this

    MCTileReportView.on('render', function () {
      WRContext.fetch();
    });
    
    deferred.resolve({viewToShow: MCTileReportView});
    
    

    Not sure if this is 100% ok, but it works. Please let me know if there is a better way

  • Ferdinand Prantl
    Ferdinand Prantl E Community Moderator

    Your original code already fetched the data:

    var MCTileReportView = new TileReportView({
     data: {
      title: 'Workflow WebReport Content',
      id : 222222,
     },
     context: WRContext
    });
     
    // fetch the data, widget is supposed to re-render automatically
    WRContext.fetch();
    
    deferred.resolve({viewToShow: MCTileReportView});
    return deferred.promise();
    

    Fetching the data on rendering is usually dangerous, because when the new data are obtained the widget usually re-renders:

    MCTileReportView.on('render', function () {
      WRContext.fetch();
    

    There could be a problem with TileReportView, which would not process the update events in each situation. You could try debugging what happens, once the data have been fetched and why the widget would not get updated. You can also try postponing the widget rendering until the data have been fetched:

    var MCTileReportView = new TileReportView({
     data: {
      title: 'Workflow WebReport Content',
      id : 222222,
     },
     context: WRContext
    });
     
    // return the widget after the data have been fetched
    return WRContext.fetch().then(function () {
     return { viewToShow: MCTileReportView };
    });
    


  • The issues with 20.4 are probably related to the changes made to implement parameter prompting. The TileReportView now extends from the WidgetPromptWrapperView which is a Marionette 3 view and is used for all WebReport widgets that need to support prompting. It looks like there is an issue with performing the show after the fetch now as the WebReport output is lost when show is called.

    I logged the following bug for this:

    LLWR-15464: HTML WebReport widget fails to render correctly via the SDK if the view is shown after fetching the data

    Ian

  • Thanks for your answer, @Ferdinand Prantl

    The original code does not work in version 20.4. It did in 20.3. Something must have been changed since.

    I tried postponing rendering as you suggested with

     // return the widget after the data have been fetched
    return WRContext.fetch().then(function () {
     return { viewToShow: MCTileReportView };
    });
    

    Unfortunately it did not work like that.

    Could I fetch data on other event (e.g. show). Would that make a difference?

  • Thanks for update @Ian_Whitfield

    Maybe you have some workaround suggestion for version 20.4?

  • There might well be a workaround but I'd need to debug the issue in more detail first. We're in the middle of a release at the moment which means I'm not able to investigate this right now unfortunately. When things have calmed down a bit I'll try to see if there is a workaround for this and post an update here.

    Cheers

    Ian

  • @Uldis - I've looked into this now and have a fix that should be going into the next development update (21.4).

    The main problem seems to be that the new wrapper view which handles parameter prompting defers instantiation of the main content view so the model is not added to the context any more when you call new TileReportView. This means when you call WRContext.fetch() it isn't fetching the WebReport content.

    I think the simplest workaround for your specific case might be to add the model to the context manually before fetching. This should mean that the widget picks it up later when it's rendered.

    WRContext.getModel(WRTextModelFactory, {
    	attributes: {
    		id: 222222,
    		context: WRContext
    	}
    });
    
    WRContext.fetch();
    

    The RequireJS module path for WRTextModelFactory above is 'webreports/utils/contexts/factories/wrtext.model.factory'.

    Cheers

    Ian

  • Thanks @Ian_Whitfield . I will try it out