SalesLogix v8.0 Activity Customizations (JavaScript)

You may have heard that SalesLogix version 8 provides a new way to customize the Activities dialog and listviews. This new way is really powerful, uses Dojo, and is a plug-in architecture. This means you can now add your own tabs, controls, and do so without modifying the out-of-the-box files.

In the training department we are working on a Master’s Series course showing how to customize the activity dialog, listview (mainview in list mode), and add the activity list when added to your custom entity. How cool is that?

Well to get started let’s look at the files you need to create. Inside of your portal/supportfiles/jscript/

Create a folder called

Training

and within that folder four (five in our case since we have a new tab) files named

main.js
ActivityService.js
ActivityEditor.js
ActivityManager
ActivityEditorProjectsTab.js

Then we have to make a change to base.master.

~Line 355:

paths: {
'Sage': '../../../jscript/Sage',
'Training': '../../../jscript/Training'
},


~Line 577:

"Sage/TaskPane/ActivityTaskPaneActions",
"Training/main"
],

Now build and deploy.

You really shouldn’t notice any differences except perhaps a few console errors when viewing the activitydialog and a new folder and files deployed.

Get going.

How to insert child entities with a nested parent using SData

The question is how can we insert child entities like contact while associating that new contact to an account and address?

The solution is pretty straight-forward once you know the few steps.

Here are the steps without code:
1. Get a template of the new entity (contacts/$template)
2. Get the payload for the related account (accounts(‘accountidhere’))
3. Place the account payload into the contact’s template payload
4. Get the address’s template (addresses/$template)
5. update the contents of the address’s tempalte
6. place the address into the new contact’s payload
7. .create the contact request.

Here is the code:
https://github.com/jasonhuber/SData_CreateContact_AttachAccount/blob/master/InsertNewContactExistingAccountviaSData/Form1.cs

1. Get a template of the new entity (contacts/$template)

//_service is already declared
// SDataTemplateResourceRequest needs .Core so I have a using.
SDataTemplateResourceRequest req = new SDataTemplateResourceRequest(_service);
req.ResourceKind = "contacts";
//AtomEntry needs Sage.SData.Client.Atom so I added a using
AtomEntry entry = req.Read();
var contact = entry.GetSDataPayload();

2. Get the payload for the related account (accounts(‘accountidhere’))

GetEntityPayload("accounts", "AA2EK0013031");
*snip*
private SDataPayload GetEntityPayload(string entitytypename, string entityid)
{
Sage.SData.Client.Core.SDataSingleResourceRequest request = new
Sage.SData.Client.Core.SDataSingleResourceRequest(_service);

request.ResourceKind = entitytypename;

request.ResourceSelector = string.Format("'{0}'", entityid);
// Read the feed from the server

//I did this as a seperate so that I could tell when the read comes back
Sage.SData.Client.Atom.AtomEntry entry = request.Read();
//and clear the please wait message.
//first get the payload out for the entry
return entry.GetSDataPayload();

}


3. Place the account payload into the contact’s template payload

contact.Values["Account"] = GetEntityPayload("accounts", "AA2EK0013031");

4. Get the address’s template (addresses/$template)

GetEntityTemplate("addresses");

*snip*

private SDataPayload GetEntityTemplate(string entitytypename)
{
// SDataTemplateResourceRequest needs .Core so I have a using.
SDataTemplateResourceRequest req = new SDataTemplateResourceRequest(_service);
req.ResourceKind = entitytypename;
//AtomEntry needs Sage.SData.Client.Atom so I added a using
AtomEntry entry = req.Read();
return entry.GetSDataPayload();

}

5. update the contents of the address’s template

SDataPayload address = GetEntityTemplate("addresses");

address.Values["Description"] = "SalesLogix/Act Office";
address.Values["Address1"] = "8800 n. Gainey Center Drive";
address.Values["City"] = "Scottsdale";
address.Values["State"] = "AZ";

6. place the address into the new contact’s payload

contact.Values["Address"] = address;

7. .create the contact request.

Sage.SData.Client.Core.SDataSingleResourceRequest rcu = new Sage.SData.Client.Core.SDataSingleResourceRequest(_service);

rcu.ResourceKind = "contacts";
rcu.Entry = entry;
try
{
AtomEntry result = rcu.Create();

That is it. Basically you have to set the account and address entries in the payload for the contact to full payloads from either a template for a new address or an entry in the case of the existing account.

Updating Nested Entities in SalesLogix v8 using the C# client libraries for SData

The question of how to update nested entities came up last week between another SalesLogix partner and I. This partner was trying to update a parent entity when the main payload she was receiving or using was from the child entity.

For this example we can use Ticket and Account. A Ticket is a child of Account. A Ticket has an AccountId field which is related to Account via a relationship called Account. I could refer to this as Ticket.Account. Account also has a relationship back to Ticket by Account.Tickets (plural), but we really do not need that right now.

So if I have a Ticket or a bunch of Ticket and I want to update some value on the account how can I do it?

The code to pull a Ticket (All code is really here: https://github.com/jasonhuber/SDataUpdateNestedEntities/blob/master/SdataNestedTest/Form1.cs is:


Sage.SData.Client.Core.SDataSingleResourceRequest request = new
Sage.SData.Client.Core.SDataSingleResourceRequest(_service);
request.ResourceKind = "tickets";
//**Be sure to change this query!
request.ResourceSelector = "'tDEMOA000002'";
//I did this as a separate so that I could tell when the read comes back
Sage.SData.Client.Atom.AtomEntry entry = request.Read();

You needed to assume I had an _service variable already setup. I do.

That gives me a Ticket. A lot like what you would see if you went to this URI.

(not linked on purpose)

http://localhost:3333/sdata/slx/dynamic/-/tickets(‘tDEMOA000002′)?format=json

That is the ticket entry and payload.

The account isn’t included.

So we really want this URI:

(not linked on purpose)

http://localhost:3333/sdata/slx/dynamic/-/tickets(‘tDEMOA000002′)?include=Account&format=json

That will give us the Ticket entry and the Account entry within the ticket. Be sure to check it out.

Our code then becomes:


Sage.SData.Client.Core.SDataSingleResourceRequest request = new
Sage.SData.Client.Core.SDataSingleResourceRequest(_service);

request.ResourceKind = "tickets";
//**Be sure to change this query!
//****new*****//
request.QueryValues.Add("include", "Account");
request.ResourceSelector = "'tDEMOA000002'";
// Read the feed from the server
//I did this as a separate so that I could tell when the read comes back
Sage.SData.Client.Atom.AtomEntry entry = request.Read();

So then we can pull the Ticket payload and the Account payload out, update them, put the payloads back into the entry and .update:


//pull the payloads out
ticket = entry.GetSDataPayload();
account = (SDataPayload)ticket.Values["Account"];

//update the account payload as needed
account.Values["UserField1"] = "Sam";

//put everything back..
//account back into ticket
ticket.Values["Account"] = account;
//ticket back into the entry
entry.SetSDataPayload(ticket);
//entry back into the request
request.Entry = entry;
request.Update();

Hopefully this makes sense. You should be able to do this for as many nested entities as you like.

Sage SalesLogix Mobile 2.0 running against Sage SalesLogix 7.5.4

*This is currently unsupported officially by Sage. Consider 2.0 Beta for 8.0 at this time and 2.0 for 7.5.4 possibly not supported*

Sage SalesLogix 2.0 is due to be released with Version 8 of Sage SalesLogix. Typically paired with the web installation, but at Sage Summit there was a lot of talk about running Mobile along side the Lan installation of Sage SalesLogix. As long as the SData portal is deployed you can do either or both!

Sage SalesLogix 2.0 has a bunch of changes, but the big ones I have on my list are:

  • Recurring activities fix (when you complete a recurring activity you are either warned and not allowed (2.0 against 7.5.4) or it works as expected (2.0 against 8.0).
  • Activities details updates – awesome (8.0 only)
  • Field Level Security (8.0 only)
  • New Dojo backend/framework

You might have noticed that the major features of 2.0 are really version 2.0 for 8.0 only. While that is true, the main benefit of getting Mobile 2.0 coded against 7.5.4 right now for your customers is upgrading. If you write customizations for Mobile 1.2 against 7.5.4 you will need to “rewrite” them for 2.0.

The good news is Jeff’s team is doing a great job of documenting this experience.

In fact the upgrade from 1.2 to 2.0 process is detailed here:
1.2 to 2.0 GIST

The process to get a development 2.0 mobile system up is easy and just like 1.2 with one addition for 7.5.4.

First create a virtual directory. Usually under your SalesLogix website in IIS.
This way we do not have to worry about Cross Origin Requests and all that.

I called mine slxmobile2 and it points to a new directory: C:\inetpub\wwwroot\slxmobile2

I grabbed the 2.0 branch of this repo:
https://github.com/Sage/argos-sdk/tree/2.0

Follow that link and look for the zip button. Download that.

Extract the contents. There will be a folder named argos-sdk that you need to drill into.
Find the folder containing the build folder.
Copy the build folder and everything around it (the contents of the folder containing the build folder) and paste it to
C:\inetpub\wwwroot\slxmobile2\argos-sdk (this folder needs to be created at this point too)

Now download this
https://github.com/SageSalesLogix/argos-saleslogix/tree/2.0
Extract the contents. There will be a folder named argos-saleslogix that you need to drill into.
Find the folder containing the build folder.
Copy the build folder and everything around it (the contents of the folder containing the build folder) and paste it to
C:\inetpub\wwwroot\slxmobile2\products\argos-saleslogix (these folders need to be created at this point too)

If you are on version 8 (or pointing to an 8.0 SData endpoint you can skip the for 7.5.4 steps)

For 7.5.4
Download this
https://github.com/SageSalesLogix/argos-saleslogix-20_for_754
Extract the contents. There will be a folder named argos-saleslogix-20_for_754 that you need to drill into.
Find the folder containing the build folder.
Copy the build folder and everything around it (the contents of the folder containing the build folder) and paste it to
C:\inetpub\wwwroot\slxmobile2\products\argos-saleslogix-20_for_754 (these folders need to be created at this point too)

still for 7.5.4 you need to open
C:\inetpub\wwwroot\slxmobile2\products\argos-saleslogix-20_for_754\index-dev-20_for_754.html

Scroll to about line 58:
https://github.com/SageSalesLogix/argos-saleslogix-20_for_754/blob/master/index-dev-20_for_754.html#L58 and grab the leading comma and the two lines:


,
{ name: 'Mobile/BackCompat', location: '../argos-saleslogix-20_for_754/src' },
{ name: 'configuration/backcompat', location: '../argos-saleslogix-20_for_754/configuration' }

(these create the dojo.require (like an include in C#) for the 7.5.4 bits.)

Copy those and paste in the same location here:
C:\Inetpub\wwwroot\slxmobiledale\products\argos-saleslogix\index-dev.html

Finding the same location:

https://github.com/SageSalesLogix/argos-saleslogix/blob/2.0/index-dev.html#L58

and pasting them carefully (make sure the comma ends up after that last entry and all that).

End of for 7.5.4 specific steps

Now everyone needs to update their connection to SData here:
C:\Inetpub\wwwroot\slxmobiledale\products\argos-saleslogix\configuration\development.js

Changing this line at least:

https://github.com/SageSalesLogix/argos-saleslogix/blob/2.0/configuration/development.js#L11

I just changed to 50.x.x.x to localhost:3333

Now you can browse to
http://localhost:3333/slxmobiledale/products/argos-saleslogix/index-dev.html

And you should be all set. If you see a blank screen you can open firebug or the developer tools (F12 or Cntrl+shift+i) and look for any errors.

Mobile Views – how can we create a different look for a list view?

What are we trying to accomplish?

Typically we a lit layout in mobile where each lineitem from the result of our query is typically shown one item at a time and is actually an ListItem or LI tag.

Well what if you wanted to show it in more of a tabular format? You could not get a header row out of the contentView you typically use. You need to control the layout a bit more.

I ran into this situation prepping for Summit and got some guidance from the developers.

First take a look here:
https://github.com/Sage/argos-sdk/blob/1.2/src/List.js#L120

You will see line 120 where the attachment points and views are defined.

Take a look at the viewTemplate on line 133″

viewTemplate: new Simplate([
'<div id="{%= $.id %}" title="{%= $.titleText %}" class="list {%= $.cls %}" {% if ($.resourceKind) { %}data-resource-kind="{%= $.resourceKind %}"{% } %}>',
'{%! $.searchTemplate %}',
'<a href="#" class="android-6059-fix">fix for android issue #6059</a>',
'{%! $.emptySelectionTemplate %}',
'<ul class="list-content"></ul>',
'{%! $.moreTemplate %}',
'</div>'
]),

Specifically
'<ul class="list-content"></ul>',

This line gets “filled” with whatever your template has in here:
contentTemplate: new Simplate([
'<h3>{%: $.$descriptor %}</h3>',
'<h4>{%: $.$key %}</h4>'
]),

So inside your list view definition you should have something like this:

I threw a bit more in here while testing:

Mobile.Training.MyList.List = Ext.extend(Sage.Platform.Mobile.List, {
//Templates
viewTemplate: new Simplate([
'<div id="{%= $.id %}" title="{%= $.titleText %}" class="list {%= $.cls %}" {% if ($.resourceKind) { %}data-resource-kind="{%= $.resourceKind %}"{% } %}>',
'{%! $.searchTemplate %}',
'<a href="#" class="android-6059-fix">fix for android issue #6059</a>',
'{%! $.emptySelectionTemplate %}',
'<div><div style="float:left; width:40%; font-weight:bold;">Column1</div>',
'<div style="float:left; width:15%; font-weight:bold;">Column2</div>',
'<div style="width:40%;float:left; font-weight:bold;">Column3</div></div>',
'<div class="list-content"></div>',
'{%! $.moreTemplate %}',
'</div>'
]),
itemTemplate: new Simplate([
'<tr data-action="activateEntry" data-key="{%= $.$key %}" data-descriptor="{%: $.$descriptor %}">',
'<div data-action="selectEntry" class="list-item-selector"></div>',
'{%! $$.contentTemplate %}',
'</tr>'
]),
noDataTemplate: new Simplate([
'<div class="no-data">',
'<h3>{%= $.noDataText %}</h3>',
'</div>'
]),
contentTemplate: new Simplate([
'<div><div style="float:left; width:40%;">{%: $.Fieldfromfeed1 %}</div>',
'<div style="float:left; width:15%;">{%: $.Fieldfromfeed2 %}</div>',
'<div style="float:left; width:40%;">{%: $.Fieldfromfeed3 %} </div></div><br />'
]),
loadingTemplate: new Simplate([
'<div class="list-loading-indicator"><div>{%= $.loadingText %}</div></div>'
]),
//Localization
titleText: 'My Title',

I was originally going to use a table tag, but anything related to tables (table, tr, td, thead, tbody) seemed to simply get removed before the site was rendered. Divs are better anyway, but you can see my CSS needs a little work. The point remains. You can do just about anything you want in the view.

I have a dialog insert form. How can I default the parent entity?

Let’s say we have a new entity called widgets. These widgets can be tied to contacts and/or accounts.

The widgets have a few fields price, quantity, date, and color.

The widgets have a relationship to contacts and accounts too.

You can add widgets from the contact’s mainview and from the account’s mainview since there is a more tab (quickform) for each. To add a widget you press the little green plus and the addeditwidget quickform appears.

This addeditwidget quickform is found in AA under the widget entity (not under contact or account).

When you press add widget from a contact (say John Abbott) you want John Abbot and Abbott ltd. to show on the insert widget form that shows as a dialog right?

In order to do this you need to set the widget.contact and widget.account.

Well you need to know that when that dialog “pops up” you can insert some code.

C# code specifically. You can tell the current entity in a load action of the form by typing:

Sage.Entity.Interfaces.IWidget widget = this.BindingSource.Current as Sage.Entity.Interfaces.IWidget;

Now you can get the parent of that widget by saying:

this.GetParentEntity();

But this addeditwidget form is loaded from both the contact and account. What if you have a contact? What if it is an account? You can set both the contact and account if the parent is a contact. You can only set the account if the parent is an account.

So you check:


object parent = this.GetParentEntity();
if(parent is Sage.Entity.Interfaces.IAccount)
{
//we have an account, set the account
widget.Account = (Sage.Entity.Interfaces.IAccount)parent;
//assuming the widget has a relationship called Account
}
else if(parent is Sage.Entity.Interfaces.IContact)
{
//we have a Contact, set the account and contact
Sage.Entity.Interfaces.IContact contact = (Sage.Entity.Interfaces.IContact)parent;
widget.Account = contact.Account;
widget.Contact = contact;
//assuming the widget has a relationship called Account and one called Contact
}
else
{
//well this is an awkward else isn't it?
}

The context for each control will be set for you. Enjoy!

32 bit mode on a 64 bit machine? Why?

The problem

If you install SalesLogix on a 64 bit machine (Windows server of course), you are told that you need to change your application pool mode to “classic.” This instruction is on page 93 of the current implementation guide. It seems that this would be a huge hindrance to performance for your website. That isn’t the case with this particular setting. The Managed Pipeline mode tells IIS how to handles requests. By default IIS 7 will use a single pipeline for all code and this pipeline is tightly integrated with ASP.NET, which I assume means better/faster. This mode does not have anything to do with memory instead it adds the ability for IIS to run some legacy modules.

What about the Allow 32 bit applications setting? Same deal here, we have some modules that will not run in 64 bit mode like the provider and groupviewer. So does this mean in 32 bit mode my website is only getting 2 gigs of Ram? yes it does but not really.

Good practice?

Each application pool is assigned that 2 gigs of memory. You can place SData, SlxClient, and processhost in their own app pools. The same goes for any other virtual directory. In their own app pool each virtual directory will get its own 2 gigs of memory. Not too bad, but you can always up that to 4 gigs with a registry setting.

An easy solution? Well it isn’t necessarily a solution, but an easy way to get your website some more memory. You need to match these application pool settings in Application Architect since AA will by default use the same app pool for all portals.

I bet the fix is to get those modules rewritten or modified so they are not 32 bit or classic mode dependent.

But wait there is more

According to Stuart Carnie who ran a dumpbin on w3wp.exe on a 64 bit machine w3wp is large address aware and does get 4 gigs be default.

I think we have a good solution worked out. Is this how you do it? Is my explanation off somewhere? I am not an expert in IIS by any means, so please add your comments!

HQL DataSource – sometimes the simple answer

I recently had a situation where I wanted to show a “more tab” with a datagrid that would show data that had a secondary relationship to the entity on which it was placed.

The idea is on the User details I wanted to show the tasks assigned to that user through the project. In this scenario the project is assigned to the user. The task user is not the correct user.

So the relationship looks like this:

Users -> Projects -> tasks

Users has a .projects, but I cannot get the tasks to show using a traditional datagrid so I used a very simple HQL Query.

Add the HQL Data source to the page and the query can be as simple as (from) “Projects p inner join Tasks t”

The relationship is already defined, so we do not need to SQL “on p.projectid = t.projectid”

Now we can set our columns to whatever we want to show in the grid, add the grid and we are on our way.

Simple.

New Sage SalesLogix Mobile Web versions? Where to download

A great product!

I have been working a lot with the New Sage SalesLogix Mobile product. We have a new master’s series class, many developer subscription videos, a production release setup which our internal PSG team developed, and now a v1.1 class. This was a lot of work, but man is it fun to work on such a sleek, great looking, and fast product. We have talked about the background of mobile before but we now really have two main versions: 1.0 and 1.1.

Versions
Version 1.0 of the New Sage SalesLogix Mobile Web works with 7.5.3, but really needs HF3 to be fully functional.

Version 1.1 of the New Sage SalesLogix Mobile Web works with 7.5.4. Absolutely needs 7.5.4 since some additional SData endpoints were added to support v. 1.1.

Everything I did in version 1.0 worked in version 1.1. No problems. The one catch is I also tried this on a 7.5.3 installation and got an error right after login. I just could not figure it out. I saw the end point and emailed a developer to see what was up. Turns out the above requirements stand. I needed 7.5.4 to run the newest downloads from github.

So where do I download v1.0 of Sage SalesLogix Mobile Web for my development environment?

Both versions of the New Sage SalesLogix Mobile Mobile Web can be downloaded from github.com here:
https://github.com/SageScottsdalePlatform/argos-saleslogix/branches

The trick is to show all branches. It is a link in the sub menu near the middle top to the left.

You need to do this for argos-saleslogix as well as argos-sdk. The above link is for argos-saleslogix. Here is the link for argos-sdk:
https://github.com/SageScottsdalePlatform/argos-sdk/branches

For version 1.0 you are looking for the download labeled: v1.0-rc1

What silence means to a trainer.

We have all been there either as a learner or as a trainer — a question is asked by the trainer and the response is nothing but dead air. No response at all. How the trainer interprets this is one of the most important things they can consider as it pertains to the absorption of the material.

What are some possible causes of complete silence?

  • Learners are checking their email or are otherwise distracted
  • Learners do not understand the material

In either case the trainer should recognize that an engaged student will respond. At least in a group of 6 or more learners you are likely to have at least one learner that will respond. So if you get no response it is the responsibility of the trainer to identify why there was no response and address it.

The first issue is easier to address. You can ask for a response via chat to a simple question. Most learners that are partially paying attention will perk up and join back in and answer your chat. Something like “hey guys let me know via chat what version you are on.” You should get a good number of responses and you can move on.

If the subject is new for the learners you should be more concerned with their grasp of the material. If there is no response and you are confident some of the learners are actively listening then you need to re-explain yourself. Ask the learners to explain what you just said in their own words. This can be very valuable. Other students will hear the explanation in the words of another and you will get a chance to asses the learners grasp of the material.

At Sage we are trying to educate the developers, administrators and users of our products. It makes good business sense to provide the best training we can since the people we train sell or use the product. Better educated users are happier with the product they have purchased. Partners who sell our products are served by training by quickly learning the newest customization techniques so that they can modify the application for their customer’s needs. Developers and administrators have a interest in learning the material as best they can so that they are productive at their jobs.

So why the silence? We are considering it here at Sage and coming up with ways to ensure we get the message out.

If you attend training we set you up to absorb the material in a few ways. First you get the book. It is a detailed, step-by-step guide showing you how to perform the tasks for each exercise. Second we give you a working SalesLogix environment that is prepped for the class you are attending. Usually this is simply a base machine with SalesLogix installed. You get to try it out with your own hands. Then we give you videos showing you someone else completing the tasks and walking you through each step. In the videos we might take a different route than the book to show you what is possible.

So you can be as silent as you like while taking advantage of the various learning tools. We are ok with that.

One note — what we show you in the class is the required learning to get you started in the material. There is always more to learn and someone else to learn from. We can learn from you just as you will learn from us. So stay in touch with training. Ideas, problems, and suggestions are welcome.