Showing posts with label IntrNet. Show all posts
Showing posts with label IntrNet. Show all posts

Sunday, February 24, 2013

Rich Text Data Entry and HTML5

On occasion I've had requests on projects where the user wanted a way to enter some text and be able to mark the text as bold or italic. I've been able to convince the users that entering some markup is the way to go. They typically go along after some convincing.

The contenteditable attribute, available in HTML5 makes such editing easier. You can try it out yourself below:
  1. The text between the lines below is tagged with that attribute
  2. Feel free to edit it:
    1. Add line feeds (i.e., hit the Enter key)
    2. Highlight text and use CTRL B, or I or U
  3. The click the button to see the HTML text with your changes.
This text can be edited by the user. Feel free to edit it. You can even hit the enter key for a line feed. You might want to do this at the beginning of every sentence. You can also highlight some text and use CTRL-B, CTRL-I, CTRL-U to make the text bold or italic or underlined.


So how did I do that? All I had to do is wrap the text in a DIV tag with the contenteditable attribute enabled, i.e.,

<div contenteditable="true" id="cmnts">
The text I want the user to be able to edit.
</div>

The DIV tag has an ID attribute so I can get the HMTL text using the innerHTML attribute.

Now your next question, is how can I upload the value to the server since the text is not in a form field (e.g., an INPUT or TEXTAREA tag)? All I need to do is to create a hidden form field in my form, e.g.,

<input type="hidden" name="userText" id="userText">

and then in my form tag I add an onSubmit attribute to assign the HTML text to the form field, e.g.,

onSubmit="document.getElementById('userText').value =
          document.getElementByID('cmnts').innerHTML;"

On the SAS Server side (e.g., the SAS/IntrNet® Application Dispatcher or the Stored Process Server), like for any other form field, a macro variable (userText) contains the HTML text that can be saved or used however the applications sees fit.

This is very much a Work In Progress and as I continue my research and experimentation with HTML5, hopefully I will find an easy to implement facility for a more complete Rich Text Editor. But for now, the contenteditable attribute is a pretty good alternative IMO.

Tuesday, January 22, 2013

Error Handling in Utility Macros

Dealing with errors is always problematic. It can be particularly challenging in utility macros. Does every macro need to check for every possible error? Or should a developer assume some level of error checking has been done by the calling application, program or macro?

My view is that assuming a certain level of error checking by the calling application, program or macro is reasonable; but that one should still do a reasonable level of defensive programming.

For example, consider the generateOptionTag macro that I have mentioned in a number of posts. It creates a select tag from data in a SAS data set. But suppose the data set, or the variables for the coded value, or the label for the select tag, don't exist?

The minimalist defensive solution is to just exit the macro without generating the select tag. But the user is left with a user interface page that is incomplete/wrong. And since the user, in this case, is almost certainly not a developer, determining exactly what additional messages or diagnostics are appropriate can be challenging.

The approach that I like is to make sure the user knows that an error happened and, at the same time, give them enough information so they can alert the right folks.

Both the Stored Process Server and SAS/IntrNet Application Server programs will display a message to the user if an error is encountered. But since defensive programming has prevented a SAS error, the issue is how can we force that message without generating a SAS error? Reviewing the SAS knowledge base article Tips to remove the Show SAS log button if a SAS® Stored Process executes with an error suggests a simple solution: set the value of the SYSCC macro variable to a value larger that 4.

But we still need to make it easy for the user to find the error. That is where Using NOTE, WARNING, ERROR in Your Program's Generated Messages can help.  You can produce an error message that provides some details and since it will be highlighted in the SAS Log, it is easy to find.

So here is a typical snippet of code that illustrates this technique:

%let syscc=256;
%put ERROR: The specified data set, &data, is not available.;
%return;

The %return statement stops the currently executing macro and returns control to the calling macro or program.

And in a SAS Server Page utility macro that is generating a user interface, you can also generate a mailto hyperlink to make it easy for the user to report the error. Something like:

Email the <a href="mailto:developer-email?Subject=Error Message">developer</a> about the error.

can make it easy for the user to report the error.

Monday, January 14, 2013

Remembering User Choices

In my Chapter 2 of my ebook (SAS® Server Pages: Generating Dynamic Content) I show several examples of reporting functionality that use Stored Processes and SAS Server Pages on the SAS Portal where the user's choices are saved. As shown in the example screen shot, upon logging into the Portal and then selecting the reporting tab, the user's last report choice is remembered and submitted. The box on the left is a select tag (generated by the generateOptionTag utility macro) that uses the size option so multiple choices are displayed. This select tag is populated using the parms.report_list_view data set. The variable Report_Key is the value of the select tag and Report_Description is the label. Thus, remembering the user's choice is simply a matter of saving the value of Report_Key on the server.

The following simple data step saves the user's report choice so that it is available later in the same browser session; as well as the next time they log in (even if it is days later).

data profiles.&_metauser._report;
  Report_Key=&Report_Key;
run;

This code is included in the report driver macro so that whenever the user makes a choice, it is saved. The Stored Process Server provided macro variable _metauser is used to identify the user (note that if the SAS/IntrNet Application Dispatcher is being used, _rmtuser would be used instead of _metauser).

Using the following utility macro in the SAS Server Page that generates the UI allows the page to remember the user's last choice.:

%macro getReportKey;
 %local dsid rc;
 %let dsid=%sysfunc(open(profiles.&_metauser._report));
 %if &dsid=0 %then %return; /*no saved choices dataset*/
 %let rc=%sysfunc(fetch(&dsid));
 %if &rc=0 %then %sysfunc(getvarn(&dsid,1)); /* data set has a saved choice */
 %let dsid=%sysfunc(close(&dsid));
%mend getReportKey;

And then you only need to include code like what is shown below in the SAS Server Page:

%let Report_Key = %getReportKey;
.
.
.
%generateOptionTag
        (data = parms.report_list_view
        ,var = _report_key
        ,name = report_key
        ,selected = &report_key
        ,label = report_description
        ,otherOptions = size=30 onchange="submit();"
        )

That is really all there is to it.

Needless to say there are lots of variations and extensions of this technique:
  • This example has a dedicated data library (profiles) that has one data set for each user. A database or SAS/SHARE could be used so that the data is stored in a single data set with a row for each user.
  • Multiple data sets can be saved for each user if different kinds of data, or multiple values, need to be saved (in a future blog posting I will discuss the example in my book that uses multiple checkboxes, instead of a select tag). And, of course, there are any number of alternative data structures that could be used.
  • If the user id (_metauser) has special characters in it (e.g., /, \ or @), some siimple macro logic could be used to map it to a unique value that is a valid SAS name.
  • The macro could be paramaterized (I kept it simple for this example).
I have also used this technique with Web Report Studio using an Information Map as discussed in this example so that the user's choices are remembered for Web Report Studio reports. This can be particularly helpful when there are multiple reports and users don't want to have to reselect the report parameters for each report.