You are on page 1of 101

2011 RightNow Technologies. All rights reserved. RightNow and RightNow logo are trademarks of RightNow Technologies Inc.

All
other trademarks are the property of their respective owners. No part of this document may be reproduced, stored in or introduced
into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise),
or for any purpose, without the express written permission of RightNow Technologies.

RightNow
Integration and Customization
for Developers
Student Guide



Copyright 2012, Oracle and / or its affiliates. All rights reserved.


Table of Contents












Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 1

This course is an introduction to integration and customization for developers


working with RightNow CX and the Agent Desktop. It introduces the core knowledge
which developers need to begin integrating RightNow CX with other systems. It also
offers an introduction to developing Add-Ins that can be used to customize, integrate,
automate, and extend the Agent Desktop.

After completing this course, you will be able to:


Access the basic functionality of the Agent Desktop.
Write queries using ROQL and the Connect Common Object Model.
Write C# code using the Connect Web Services for SOAP API to query and
manipulate objects from the RightNow database.
Use chaining and batching techniques to improve performance in
applications that use Connect Web Services for SOAP.
Execute non-CRUD, SOAP-based operations.
Develop various kinds of Add-Ins to customize, integrate, automate, and
extend the Agent Desktop.

To get the full benefit from this course, you will need to have experience with:
Object-oriented programming, preferably NET application development with
C#.
Relational database concepts.



Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 2

There are many resources available to assist you while you are working with this
course and related material. Registration may be required to view some of these
resources. These resources include:

RightNow Developer Community:
http://community.rightnow.com/developers.php
o This area contains full developer documentation and code samples,
discussion forums, a developer blog, and conference presentations.
RightNow CX Users Guide:
o Downloadable PDF from http://www.rightnow.com under
Community > Customers > Documentation
Other training and tutorials: http://www.rightnow.com/services-
training.php
RightNow Developer Conference: http://www.rightnow.com

The exercises in the course are based on the following scenario.



You have recently been hired as a developer by Smartech, a subsidiary of Global
Solutions. Smartech uses RightNow CX to support its multi-channel customer
service interactions. Among your duties will be to write applications that help
integrate data from the RightNow database with other enterprise data systems at
Smartech, so you will need to learn about the object model used by RightNow, as
well as the tools that are available for accessing and manipulating data in the
database.

Another of your duties will be to respond to requests to enhance or customize the
Agent Desktop with new functionality. In some cases, the users will request specific
controls and behaviors, and in others they might express their need and ask you to
determine how best to meet it.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 3

RightNow CX, which stands for Customer Experience, is a suite of applications that
empowers enterprises to engage directly with their customers through Social, Web,
and Contact Center experiences. RightNow CX is provided as a hosted service in the
cloud consisting of a knowledge base and at least one interface that is used to access
it. Each interface has its own unique URL.

RightNow CX empowers customers to get help from a self-service web site and/or
support from live agents who use an application called the Agent Desktop to handle
customer issues. Although the full suite of RightNow CX products includes much
more, such as a social experience, this course will be concerned only with the contact
center and the integrated database on which it depends.

Customer service agents and administrators can access a RightNow site via the Agent
Desktop. That requires downloading and installing the RightNow CX Agent Desktop,
which provisions a local machine with the needed components to accessing a
particular site.

Customers of an enterprise that uses RightNow can use a CustomerPortal web site to
interact with the RightNow knowledge base to access self-help information or to
contact a customer service agent for help.

Developers can write code to interact with a knowledge base via RightNow APIs, one
of which allows customization of the Agent Desktop.

This course begins with a look at the Agent Desktop to familiarize you with the most
common entities in RightNow CX.

You have been assigned your own RightNow knowledge base, with two interfaces, to
use for this course. The second interface has the same name as the first, followed by
"_2". A second interface could be used for training, development, or staging, for
example. The second interface accesses the same database, but can have a different
appearance.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 4


Site URL (end-user): http://___________________.rnowtraining.com
Site URL (admin/launch):
http://___________________.rnowtraining.com
/cgi-bin/__________________.cfg/php/admin
/launch.php
URL for site WSDL
http://_________.rnowtraining.com/cgi-
bin/_________.cfg/services/soap?wsdl
Admin Username: cxadmin
Admin Password: cxadmin


To install the client application that accesses a RightNow site, use the Site URL for
admin/launch, listed above. When you point your browser to this URL, a screen will
open with a button saying Install RightNow CX. Click this button and wait for the
brief installation.

After the installation completes, a window will open that allows you to enter your
username and password. For this course, both the username and password will be
"cxadmin". Check Remember Me, and click Login to open the Agent Desktop for
your site.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 5


You have been given a link to use to install the Agent Desktop, and told to set it up
on your own workstation.

In this activity, you will install the Agent Desktop and log in to your site.

1 Type the URL for your site into the address bar of Internet Explorer.
<sitename>.rnowtraining.com/cgi-
bin/<sitename>.cfg/php/admin/launch.php
2 Click the Install RightNow CX button. This begins a download to
your workstation, which may take several minutes.
3 In the Login window, type your username and password.
4 Click the Login button.

To access the same site in the future, you can launch the Agent Desktop by selecting
Start menu > All Programs > RightNow > RightNow (your site name). You
can create a shortcut to it, or pin it to your taskbar.

The RightNow console is divided into three main areas:
Navigation Pane: Left panel area that contains a set of buttons to navigate
through the Agent Desktop and to access information about various record
types stored in the RightNow database. These buttons can be expanded to
show the functionality they provide.
Ribbon: Top toolbar containing the actions available to the object in focus.
Content Pane: Main panel that changes based on the record type,
component type, configuration activity, or report being accessed.

The configuration of the content pane and ribbon specific to a particular record type
is known as the Workspace for that record type.

A contact center agent uses the RightNow Agent Desktop to access information to
help customers, such as records for:
Contacts: An individual with a customer record in the database. Contact
records can be added using the Agent Desktop or on the Customer Portal
pages.
Incidents: Questions or requests for help submitted by a contact through
any means, such as email, a chat session, etc. Incidents can also be added by
agents when they work with customers by phone or email.
Answers: Information in the database (known as the knowledge base) that
provides solutions to commonly asked questions. Agents can access answers
through the Agent Desktop to help resolve incidents.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 6

Agents can use the Agent Dsktop manually to:


View and create objects such as Contacts, Incidents and Answers
Customize Objects and Workspaces
Create profiles and staff accounts
Run and view Reports

A simplified typical example of how a customer and agent might interact using
RightNow Customer Portal and the Agent Desktop, respectively, would include the
following:
A Contact (customer) visits the RightNow web portal (Customer Portal)
The Contact searches for Answers (FAQs) relevant to his problem
If no answer is found, the Contact logs in and posts a question, which
creates a Thread on an Incident
A customer service Agent who is logged into Agent Desktop
Retrieves the Incident from a queue
Searches again for Answers or uses other resources
Replies to the Contact, creating another Thread on the same Incident

The underlined words in the scenario above represent real-world entities that are
stored as primary objects in the RightNow database.

By default, the Agent Desktop lets an agent search for a particular Contact by
selecting the Contacts button, then clicking on the Contact Search link, and then
optionally applying a filter.

Individual contacts can be selected from the results of a Contact Search. Clicking on
the contact will open a Contact Workspace in which details can be viewed or edited.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 7


Answers are record types that are similar to FAQs in that they consist of question-
and-answer pairs. Each Answer includes:
Summary: Brief description of content, typically in statement format.
Question: Full description of the issue, including all pertinent information.
Answer: Full description of the resolution to the issue.
Products/Categories: The products or product categories to which the
Answer applies

The RightNow SmartAssistant is a feature that uses database statistics and artificial
intelligence algorithms to find other Answers that might be relevant to the Answer
being viewed.

Your supervisor suggests that you start to learn about RightNow CX by working
with the Agent Desktop from the point of view of a contact center agent, to see how
objects such as Contact, Incident, and Answer are used and manipulated. You will
view lists of objects as well as individual objects, and you will both edit existing
objects and create new objects.

In this activity, you will access and view a list of Contacts and open a single Contact
for editing.

1 From the Navigation pane, click Contacts.
2 Double-click Contact Search.
3 In the Search dialog that opens, leave Select All selected, and click
Search. Notice that you could also choose to filter your search in
various ways.
4 The list of Contacts is displayed.
5 To see details of an individual Contact, double-click to open the record.

In this activity, you will create a new Contact.



1 From the Contact Search tab, click New on the ribbon to open a
blank Contact record. Required fields are in red.
2 Type in your own first and last name.
3 Click Save & Close on the ribbon.
4 Select the Contact Search tab.
5 Click Refresh from the ribbon.
6 Locate your name, now shown as a Contact in the list.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 8

In this activity, you will view an Incident and related Answers.



1 From the Navigation pane, click Incidents.
2 Double-click Incidents and leave Search All selected in the dialog,
but notice that the list is being filtered for Unresolved Incidents only.
3 Click Search to open the Incident list. Click Subject to sort by that
column.
4 Double-click to open the first Incident from the list.
5 Click the Status menu, then select Unresolved.
6 Go to the Messages tab. Click the SmartAssistant button to view
recommended Answers. Close the dialog.

In this activity, you will create a new answer that explains how to reset a password.

1 Go to Answers > Answers Default > Search.
2 Click New and type Summary: Password Reset
3 Set Answer options.
Status Public
Language English
Access Level Everyone
Answer Type HTML
4 Complete the Content tab:
In the Keywords box, type password.
Click the Question sub-tab and type: How do I reset my password?
Click the Answer sub-tab and type: Click the Forgot Password link
on the login page.
Click the Quick Preview sub-tab to preview your work.
5 Click the Products/Categories tab and select General and Technical
Support.
6 Click Save.

The Agent Desktop can be custom-configured in various ways. Some of the areas in
which administrators can configure the desktop include:
Customizable menus: Adding new items to certain menus of choices, such
as Incident Status.
Custom fields: Adding new data fields to existing objects.
Custom workspace: Adding new items to be displayed in a custom
workspace.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 9



Like many enterprises, Smartech wants to customize RightNow, so you have been
assigned to implement these customizations:
Add a new value to the menu which shows the status of an incident.
Add a new field to the database record for Incidents, to store whether the
indicident is considered urgent or not.
Add a customized workspace to the Agent Desktop that will display the
contents of the new field when an Incident is viewed.

In this activity, you will configure RightNow to add a new incident status value to a
menu.

1 Go to Configuration > Application Appearance >
Customizable Menus.
2 Expand System Menus and select Incident Statuses.
3 Click New and type the Label: Researching.
4 Assign Status: Unresolved to have this status marked as open.
5 Click Save & Close.
6 Select the Incidents tab, then click on the New icon to open a new
incident record.
7 Select Status to see the new value within the list.

In this activity, you will create a custom field on the Incidents table.

1 Select Configuration > Database > Custom Fields > Incident.
2 Click New to create a new Incident custom field.
3 Type Name: Urgent?.
4 Select Data Type: Yes/No.
5 Type the Column Name: urgent.

This is the value used in the underlying database, so it must start with
a letter, contain letters and underscores only, and cannot contain
spaces.
6 Click Save & Close, then click Yes.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 10

In this activity, you will configure a custom workspace to display the custom field.

1 Select Configuration > Application Appearance >
Workspaces/Workflows > Standard.
2 Right-click Incident and select Copy.
3 Type Name: Custom Agent Workspace, then click OK.
4 Select the Workspaces and Workflows folder, and open Custom
Agent Workspace.
5 Click on the Insert Field tab.
6 Scroll down to the Urgent? field and drag and drop it onto the
workspace.
7 Click Save & Close.

Profiles are similar to what are called roles in some other applications. They are used
to set permissions for a category or group of users. As a developer who will use web
services to access the RightNow database, your profile must have the Public SOAP
API profile bit enabled.

Staff Accounts provide individual logins for users. Each account is assigned to a
profile.

In order to write applications that access objects in Smartechs RightNow database,
you will need to use a staff account that has a profile with the appropriate
permissions.

In this activity, you will create a personal staff account.



1 Go to Configuration > Staff Management > Staff Accounts by
Profile.
2 Click New on the ribbon.
3 Type in a User Name (case and space-sensitive) of your choice.
4 Fill in the required fields, which are printed in red with asterisks.
In the Profile field, click the magnifying glass search icon
and select CX All-2.
In the Group field, click the search icon, then click New
Group. Type Name: Developers then click OK.
5 Under Password, uncheck Password Expires. Click Change
Password and type an acceptable password.
6 Click Save & Close.
7 Exit the Agent Desktop, then re-open it and sign in with the new
account information.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 11

RightNow CX offers hundreds of standard reports in the system. You can add run-
time filters to standard reports, or you can create customized reports to meet your
needs.

Your supervisor has suggested that you learn what kinds of reports are already
available by default in RightNow CX, because later, you will be expected to run
some of these reports in code.

In this activity, you will open an existing report, change a run-time filter, and view
the output.

1 Go to Analytics > Reports Explorer.
2 Click Find from the ribbon, then select Find using: Name.
3 Select Match for the operator.
4 Type Agent into the text box and click Find.
5 Double-click Agent Activity found in the right pane of the Reports
Explorer to run the report.
6 You can select filters in the Search dialog. Make the Date Range start
value 01/01/2010.
7 Click Search and view the results.

A dashboard is a combination of individual reports shown from a single report
location. Dashboards can be customized with a choice of reports and layouts.

You would like to have a dashboard available for your own use. As a developer, the
reports you're interested in are under Site Administration. You want to include:
Custom Fields
o Common > Site Administration > Custom Fields
Products
o Common > Site Administration > Customizable Menus
Public API Meters
o Common > Site Administration > Public API
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 12

In this activity, you will create a Dashboard, including the following reports: Custom
Fields and Products.

1 Go to Analytics > Reports Explorer.
2 Click New Dashboard.
3 In the left panel, expand Common > Administration > Custom
Fields.
4 Drag and drop the Custom Fields report into the upper panel.
5 Expand Site Administration > Customizable Menus, and drag
and drop Products into the lower panel.
6 Click Save from the Quick Access area above the main ribbon. Save in
the My Reports folder and type Name: Developer Dashboard. Click
OK.
7 To run the dashboard, navigate to Reports Explorer > My Reports,
then double-click Developer Dashboard.

Custom fields are useful for adding data that is logically associated with an existing
object. For more complex data structure needs, a custom object can be defined,
which creates an entirely new database table, rather than just a custom column in an
existing table.

Custom objects typically represent entities in a particular customers business, such
as a product warranty. Custom objects can be displayed in the Agent Desktop and
added to analytics reports or other areas.

Custom objects are created using the Object Designer. The Object Designer is a
graphic tool that can be used by trained site administrators.

It is generally best to create a new Custom Object (CO) in a test site and test it
extensively before deploying it to your production environment.

Custom objects are stored in packages which also scope the objects.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 13


To use the Object Designer, Custom Objects must be enabled on your site, and the
user must have the Object Designer permission enabled in his or her profile. This can
be done by a site administrator.

Smartech provides training classes on how to work with Smartech products. You
have been asked you to create a Custom Object named TrainingClass to hold the
information about a class: a class number and class name. You will also create a
custom workspace for the new object.

In this activity, you will create a new Custom Object and a workspace for it.

1 Select Configuration > Database > Object Designer.
2 Click New and select Package.
3 Name the package Classes.
4 Select the Classes package and click New > Object.
5 Type Name: TrainingClass.
6 Click Fields in the ribbon, then select Add New Field.
7 Select Integer.
8 Type field Name: ClassNumber, avoiding spaces.
9 Click the Add New Field button again.
10 Select Text.
11 Type field Name: ClassName, avoiding spaces.
12 Type Length of Field: 50.
13 Click Save, then Deploy.
14 Type in your email address, then check Deploy Immediately. In a
real scenario, you could schedule deployment for a time when the
potential performance hit would be least problematic.
15 Click Deploy, then confirm Yes.
16 Wait until the deployment finishes.
17 Select Configuration > Application Appearance >
Workspaces/Workflows > Standard.
18 On the ribbon, click New Workspace.
19 Select TrainingClass Multi-Edit.
20 From the Fields group on the ribbon, drag each of the two fields onto
the blank area of the new workspace.
21 Click on the Preview button to see how the new workspace will look to
the agent user.
22 Click the Save & Close icon above the ribbon.
23 Name the new workspace Training Class.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 14


The RightNow Application button provides access or shortcuts to additional areas of
support or information. When this button is selected, the menu is divided into two
panels.

The left panel contains contextual options based on the object in focus, such as New
Incident and Save Incident when an incident report or record is open. Below these
options are a group of global options that will always appear regardless of the
selection: Community, Links, Help, and Add-In Logging. These additional items have
sub-menus available within each item.

The right panel contains shortcuts for adding objects to the database, such as
incidents or answers, based on your permissions in the system.

The items listed in this menu are configurable. In addition, you can write code to
customize the menu, as you will learn later in the course.

Another small help button with a question mark icon is located in the upper right
corner of the screen. This button provides help that is context-sensitive.

RightNow Customer Portal is the web site interface that customers access for
customer support. Customer Portal consists of a standard set of files that can be
customized, and it is integrated with RightNow Service so customers can query the
knowledge base for answers, ask questions, provide feedback, manage their customer
account, and request chat sessions.

To learn more about how entities in the RightNow database are being used through
the Right Now CX suite, you have decided to explore the Customer Portal from the
point of view of a customer who is visiting the support pages of a company that
uses RightNow.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 15


In this activity, you will explore and interact with the Customer Portal support pages
of your site.

1 Open the end-user pages of your training site in a browser:
http://<sitename>.rnowtraining.com.
2 Navigate your end-user pages to:
Search for an answer by typing password reset into the initial
screen search text box. View the result.
Use the Your Account tab to register for an account.
Use the Ask A Question tab to submit a question about a
topic of your choice. Notice the Urgent? item on this page.
Use the Agent Desktop to find your question (Incident), and
that it identifies you as the Contact.

What are some of the basic entity objects in RightNow CX and how are they used?


Give some examples of configurations that can be made to the Agent Desktop by a
CX Administrator, without requiring coding.


Why might an administrator need to create a new Staff Account? What profile bits
must be enabled for a developer account?


Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 16


How do you run and view a basic Analytics report in the Agent Desktop?


Why might you create a Custom Object?


What tool is used to design and create a Custom Object?






Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 17

RightNow Connect is a set of tools for developers. Connect includes:


Connect Web Services for SOAP API
Connect Desktop Add-In Framework
Connect for PHP API
Connect Knowledge Foundation API
Other older APIs

This class will focus on the first two of these. Another course available from
RightNow covers Connect for PHP.

Connect Web Services for SOAP can be used to manipulate data in RightNow and to
integrate RightNow with other systems or vice versa. Example integrations could
include upkeep on a master data store, or synchronizing data across multiple sites.
Using Connect Web Services for SOAP, you can import data into applications where
it can be manipulated by that app, or provide data for business processes like billing
or marketing. Connect Web Services for SOAP can also be used to provide data to the
Agent Desktop from the RightNow knowledge base or other applications.

Connect Desktop Add-In Framework is a framework for building custom .NET
components, controls, and applications that can be added to the Agent Desktop to
provide integration, automation, or extensions.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 18


The Connect Web Services for SOAP API is based on SOAP (Simple Object Access
Protocol) and WSDL (Web Services Description Language). These are open
standards, not proprietary, and are very widely used, making it easier to integrate
RightNow with other systems that also support SOAP and WSDL. Also, many
programming tools support SOAP with wizards and utilities that make it quick and
easy to create or use SOAP-based web services. That includes Microsofts Visual
Studio which will be used in this course.

Using open standards technology also makes it easier to implement RightNow within
a Service Oriented Architecture (SOA) in which a range of disparate systems must
communicate with each other, either directly or through an Enterprise Service Bus
(ESB).

All of the APIs which are part of RightNow Connect use the RightNow Connect
Common Object Model (CCOM). Developers can learn the object model once, and
use it throughout RightNow CX.

Earlier APIs, which have now been deprecated in favor of Connect, did not support
Custom Objects. One of the biggest reasons for moving to Connect was to provide
support for Custom Objects.

Finally, Connect has been designed for backward compatibility. When customers
choose to move to a newer version of Connect, their integrations are less likely to
break or need maintenance.




Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 19

Object data , such as data from contacts, incidents, or answers, is kept in databases
hosted on RightNow servers.

Database objects can be viewed and manipulated programmatically using classes
based on the RightNow Connect Common Object Model. The RightNow Connect
Common Object Model, or CCOM, defines a set of objects that include the entities
(record types) represented in the Knowledge Base and used in the Agent Desktop.

The RightNow Connect Common Object Model (CCOM) is a standard intended for
use across all of the RightNow public APIs, although some older APIs do not support
it.

The CCOM provides several advantages:
It allows developers to learn a single, logical-class hierarchy that can be used
to access and manipulate data in both the Agent Desktop and Customer
Portal, as well as to interact with external applications.
It works with various technologies. It is used with PHP to access the
Customer Portal, and with SOAP Web services to interact with the Agent
Desktop or external applications. CCOM objects can also be used with the
Desktop Add-In Framework to automate, extend, or integrate the Agent
Desktop.

Code that uses CCOM objects indirectly accesses data from the underlying database
using RightNow APIs to handle the conversion. This shields custom code against
changes that are made to the database.

The CCOM defines a set of objects that includes primary objects, which have a unique
ID assigned to them by the RightNow Knowledge Base server, and support direct
Create/Read/Update/Delete (CRUD) operations. All primary objects inherit from a
base class RNObject.

Primary objects may contain primitive data types directly or may contain sub-
objects.

Sub-objects, in general, cannot be directly manipulated with CRUD operations but
instead are manipulated through operations on the primary object in which they are
embedded. Sub-objects can contain sub-objects of their own.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 20

The RightNow Connect Common Object Model specifies a number of primary


objects, including, among others:
Answer
Contact
Incident
Organization
Task
Account
SiteInterface

Most of these represent entities in the real world domain. SiteInterface provides
access to details about the current site interface, including name and language.

Examples of sub-objects include:
Address
PersonName
Phone
Email

To access individual data items (field values) in an object, CCOM uses a dot notation,
meaning that the primary object is given first, followed by a dot, followed by any sub-
object(s), followed by a dot, then followed by the actual name of the attribute.

For example, the Contact object has a sub-object Name with two fields: First and
Last. To access the last name, use: Contact.Name.Last

Here are examples of how some of the parts of a Contact object would be accessed in
dot notation:
Contact
Contact.Title
Contact.Name.First
Contact.Name.Last
Contact.CreatedTime
Contact.Emails
Contact.Emails[0].Address
Contact.Emails[0].AddressType.Name
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 21


The Connect Common Object Model includes many objects of type NamedID. That
type has two parts: an ID, and an associated name. ID is itself an object, with an
attribute named ID that is a long integer. ID is a unique ID value assigned by the
server when a new object is stored. The name part of the NamedID is a string.

Some examples of NamedID types in RightNow CX are:
StateOrProvince
Country
Email AddressType
Language
Incident Queue

A list is a wrapper for a collection of sub-objects, such as EmailList


PhoneList
AnswerLinkList
NoteList
ThreadList

Additional documentation of the Connect Common Object Model is available in the


Developer Community on the RightNow website. Under the documentation section,
open the documentation for Connect Web Services for SOAP, and expand Web
Service API > Object Model > Object Model Overview > Primary Objects.

Using this documentation, you can drill down from a primary object to reach the
primitive data it contains.

You will have to register to access the site. Registration is free.




Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 22

For Read operations, the Connect APIs use a special query language named ROQL
(pronounced Rock-well), which stands for RightNow Object Query Language.

ROQL is related to SQL (Standard Query Language), and to OQL (Object Query
Language), which is a specialized version of SQL.

There are two types of queries in ROQL. The syntax for a tabular query type is similar
to SQL:

SELECT primaryObject.field,... FROM primaryObject
WHERE condition

However, what follows FROM in the query is the singular name of a primary object,
not the name of a table.



To query a NamedID type, you can query the ID and/or the Name of the object. For
example, in the following query

SELECT Contact.Address.City,
Contact.Address.StateOrProvince.Name,
Contact.Address.StateOrProvince.ID

City, which is not a Named ID type but a string primitive, can be queried directly, but
StateOrProvince requires that either Name or ID be specified.

To query a List type, use the name of the list type without an index. What is returned
will be an automatic iteration of all values.

SELECT Contact.Emails.Address FROM Contact . . .

The ROQL statement below will show each phone type and the associated phone
number, placing a colon between them.

SELECT Contact.Phones.PhoneType.Name,': ',
Contact.Phones.Number

To select only one item from a list of NamedID types such as Emails or Phones, use
the WHERE clause to select item you want, either by ID or name.

SELECT Contact.Emails.Address FROM Contact
WHERE Contact.Emails.AddressType.Name =
'Email - Primary'
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 23

ROQL is executed by calling a method in one of the Connect APIs. Code to call the
API can be written in C#, Java, or PHP.

The API call is executed by a client object of type RightNowSyncPortClient. The client
object has an embedded sub-object named UserName which has fields for the
UserName and Password whose privileges will be used to execute the query.

RightNowSyncPortClient _client = new RightNowSyncPortClient();
_client.ClientCredentials.UserName.UserName = "cxadmin";
_client.ClientCredentials.UserName.Password = "cxadmin";


A method named QueryCSV is used to execute a ROQL tabular query. It takes seven
parameters:
A Client Information Header
The query itself in a variable or as a double-quoted string literal
An integer for the maximum number of rows to return, which cannot exceed
10,000
A delimiter string for returned values, which is typically ","
A Boolean for whether to return results as raw data, that is, as an array of
bytes.
A Boolean for whether or not MTOM should be disabled, which is typically
false. MTOM stands for Message Transmission Optimization Mechanism,
which is a way of sending binary data via SOAP without encoding it as text,
and is therefore more efficient.
A variable to hold an array of binary bytes. This is a required parameter, but
it won't be populated unless the first Boolean variable listed above is set to
true. This is an out parameter which must be labeled as such by preceding it
with the word out. Out parameters do not pass a value into a method, but
will be given a value within the method body which will be returned to that
variable when the method exits.

A ClientInfoHeader object has a single attribute, AppID, which is a string that will
identify this assembly when it runs on the RightNow server. It is not required to be
absolutely unique, but should be specific enough to make it unlikely that it will be
duplicated by another application. Set up a ClientInformationHeader as follows:

ClientInfoHeader cIH = new ClientInfoHeader();
cIH.AppID = "String identifier";
or
ClientInfoHeader cIH = new ClientInfoHeader{AppID =
"SomeString"};
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 24


Here is an example of calling the QueryCSV method, within a try..catch block. As
in SQL, string literals are single-quoted. In this example, the byte array will not be
populated when the method call returns.

byte[] output = null;
try
{
CSVTableSet tableSet = _client.QueryCSV(cIH, "SELECT
Contact.Name.Last, Contact.Name.First,
Contact.Address.Street FROM Contact WHERE
Contact.Address.City='Singapore' ", 10000, ",", false,
false, out output);
}
catch (Exception ex){ . . . }

The results of a tabular query are returned in an array of tables. When only a single
ROQL query is included in the call, then the results will be placed in the first and
only table in that array.

Within that table, a collection Rows holds an array of strings, with each string
holding the data values returned from one record in CSV (comma-separated values)
format.

CSVTable t = tableSet.CSVTables[0])
System.Console.WriteLine(t.Columns); // column names
String[] rows = t.Rows;
foreach (String data in rows)
{
System.Console.WriteLine(data);
}

In this example of calling the QueryCSV method, the return value of the method will
not be assigned to an array of tables. Instead, the byte array will automatically have
been populated within the method body itself.

byte[] output = null;
try
{
_client.QueryCSV(cIH, "SELECT Contact.Name.Last,
Contact.Name.First, Contact.Address.Street FROM Contact WHERE
Contact.Address.City='Singapore' ", 10000, ",", true, true,
out output);
}
catch (Exception ex){ . . .}
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 25


The output from the previous query will be array of bytes that can be looped through.

char[] res = Encoding.UTF8.GetChars(output);

foreach (char c in res)
{
Console.Write(c);
}

Using Web Services for SOAP requires having II_CONNECT_ENABLED set for
the relevant site. In addition, the user credential supplied with the client must be for
an account in which the Public SOAP profile permission has been set.

To create a simple executable that can retrieve data with ROQL and Web Services for
SOAP, use a Console Application project type. To access the WSDL, add a Service
Reference to the relevant RightNow site:

http://sitename.rnowtraining.com/cgi-bin/
sitename.cfg/services/soap?wsdl

Then add using statements for the namespaces for the Service Reference and for the
namespace System.ServiceModel.

using ConsoleApplication1.ServiceReference1;
using System.ServiceModel;

You need to retrieve data from the RightNow database that will be logged for
various purposes. You start by writing ROQL queries to display the desired data on
the screen, so that you can verify your logic and syntax. Later, you could write the
data to a logfile or make it available to other systems for integration.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 26

In this activity, you will use documentation to write applications that use ROQL CSV
queries to select and display data.
The three possible email addresses for the contact whose last name is
"Crown"
The city and state for the contact whose last name is "Barnes"
The name of the queue to which incident 110602-000003 has been assigned

A ROQL Object Query returns entire objects. Only one type of object can be selected
for in a single query, and the returned objects will be held in an array. The code can
then manipulate the objects and use their properties. The syntax of an object query is

SELECT Contact FROM Contact
WHERE Contact.Address.City='Singapore

What follows SELECT is a whole object, without individual fields specified. In the
WHERE clause, dot notation is used to find a value within a sub-object.

The QueryObjects method is used to make a ROQL object query. It requires four
parameters: a client information header, a ROQL object query, an object template
which defines what level of data will be returned for each object, and a row
maximum.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 27


An object template provides a template for what should be populated by the server
when it returns the objects requested.

By default, only top-level sub-objects will be populated. No data will be returned for
nested sub-objects. So, for example, the values for street and city would be
populated, but not the ID or Name properties for StateOrProvince, if you used the
simple template below:
Contact contactTemplate = new Contact();
RNObject[] objectTemplates = new RNObject[]{contactTemplate

Alternatively, to have the server populate sub-objects, instantiate them in the object
template, as shown below:
Contact contactTemplate = new Contact();
contactTemplate.Address = new Address();
contactTemplate.Address.StateOrProvince = new NamedID();
RNObject[] objectTemplates = new RNObject[]{contactTemplate};

Like the tabular query, an object query is executed within a try..catch block.

try
{
QueryResultData[] queryObjects =
_client.QueryObjects(clientInfoHeader, "SELECT Contact
FROM Contact WHERE Contact.Address.City = 'Singapore' ",
objectTemplates, 10000);
}
catch (Exception ex)
{
. . .
}

The output from an object query is an array of type RNObject, which is the parent
object of all other primary object types. Iterate through that array to obtain
individual objects, casting each object back to the correct type, and then use the
properties and methods of the object as needed.

RNObject[] rnObjects=queryObjects[0].RNObjectsResult;
foreach (RNObject obj in rnObjects)
{
Contact cont = (Contact)obj;
System.Console.WriteLine(cont.Name.First + " " +
cont.Name.Last + " ID: " + cont.ID.id);
}

Note that in ROQL, ID alone returns an ID number for a NamedID type. Later in
this material, you will see that in C# code, ID.id must be used.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 28


You want to retrieve whole objects and manipulate them in code, instead of
retrieving individual attributes.

In this activity, you will re-write the same queries you wrote earlier in QueryCSV, this
time using QueryObjects.

A ROQL query is syntactically similar to SQL, with important differences.

SQL:
SELECT first_name FROM contacts
WHERE city=Singapore

ROQL QueryObject:
SELECT Contact FROM Contact
WHERE Contact.Address.City='Singapore'

ROQL QueryCSV:
SELECT Contact.Name.First FROM Contact
WHERE Contact.Address.City= 'Singapore'

Many elements of SQL are not available in ROQL, including joins and nested queries.
Boolean are 0/1, rather than TRUE/FALSE, and while it is possible to use the
asterisk wildcard, RightNow does not recommend it, in part because the order of
columns returned is not guaranteed. Instead, request a whole object.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 29


SQL makes its queries or changes by directly accessing tables in the database. ROQL
does not access the database directly. The API takes a query written in ROQL and
transforms it into appropriate SQL. Then the SQL commands are used to access the
database tables directly. This intermediary between the developer-generated ROQL
and the SQL that accesses the database itself means that the code developers can
write is limited to what is available in ROQL, This provides some degree of data
insulation and protection for data integrity.

The intermediate step of the API translation also makes it possible to make changes
to the underlying database without necessarily affecting the client that uses ROQL. In
many cases, the API can absorb these changes, taking the same ROQL call and
transforming it to match the new database schema. This also helps ensure backward
compatibility, so ROQL queries written for earlier releases will still operate correctly
in later releases.

ROQL also uses the Connect Common Object Model, and like the CCOM, it will be
used throughout the RightNow Connect APIs so a developer can learn ROQL once
and then use it with all RightNow languages and APIs.

Beginning with the November, 2011, release, ROQL supports the GROUP BY,
HAVING, and ORDER BY (ASC/DESC) clauses, so the following query

SELECT Organization.Name, COUNT(Contacts.ID)
FROM Organization
GROUP BY Organization.Name
HAVING COUNT(Contacts.ID)>=1
ORDER BY COUNT(Contacts.ID) DESC

returns all organizations that have at least one contact, grouped by the organization
name so there is only one line for each organization, and listed in descending order
by number of contacts.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 30

Operators that can be used in ROQL are similar to those in SQL. The equals, not
equals, IN/NOT IN, BETWEEN and greater/lesser than operators can be used with
numeric or alphabetic characters. Unlike SQL, the equals operator is case-sensitive.
However, this may change in later releases.

The LIKE operator performs a match comparison with a string using wildcards. The
two wildcards available are _ (underscore) for a single character and % (percent) for
zero or more characters. The string containing the wildcards must be in single
quotes. LIKE matches in ROQL are case insensitive. The LIKE operator cannot be
used with LOB (Large Object) fields such as BLOBs and CLOBs.

IS NULL and IS NOT NULL are also available.

LIKE is case-insensitive, but = (equals) is case-sensitive. So, if the database holds an
object with the last name of "Bell", these comparisons return 1 for true:

WHERE Contact.Name.Last = 'Bell'
WHERE Contact.Name.Last LIKE 'bell'
WHERE Contact.Name.Last LIKE 'BELL'

and this returns 0 for false:

WHERE Contact.Name.Last = 'BELL'

Beginning with the November, 2011, release, RightNow CX supports the string
functions UPPER and LOWER. Like all functions in ROQL, these cannot be used
against a field value in the WHERE clause.

The logical operators AND, OR, and NOT are supported.

Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 31


Aggregate functions are available in tabular (CSV) queries only.

Prior to November, 2011, RightNow CX included only three functions: COUNT and
MAX/MIN. In the November, 2011, release, three more aggregate functions were
added: SUM, AVG, and STDDEV for standard deviation.

DateTime values use ISO 8601 format: YYYY-MM-DDThh:mm:ss



The T character separates the date from the time. The time value can be followed by a
Z to indicate UTC, or by a plus or minus sign and the number of hours the local time
zone is earlier or later than UTC. The entire value is single-quoted.

'2011-08-28T13:10:00-05:00' 1:30pm EST 8-28-11
'2011-08-28T18:10:00Z' same as above, UTC
'2010-01-01T00:00:00Z' midnight, Jan 1, 2010,UTC

ROQL makes four functions available to work with DateTime objects:


sysdate()
o Current date and time
date_ add(date, units, interval, round)
o Adds units number of intervals to date
o Possible interval values: seconds, minutes, hours, days, months,
weeks, years
o round is Boolean
date_diff(dateA, dateB)
o Returns seconds
date_trunc(date, interval)
o date to nearest interval

In ROQL, as in SQL, an alias placed after the name of an object in the FROM clause
can be used in the SELECT and/or WHERE clauses to shorten the dot notation,
purely for convenience.

SELECT Contact FROM Contact C WHERE C.Address.City='Singapore'
SELECT C.ID FROM Contact C WHERE Contact.Address.City= 'Rome'
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 32

When custom objects are used in ROQL, the object name in the FROM clause is
scoped by the package name with a dot.

SELECT ID
FROM CO.prodreg

Custom fields in ROQL are accessed by name, not ID. The field name must be
prefixed by CustomFields.c. Although technically custom fields are sub-objects in
the CCOM, they are treated as scalars in queries.

SELECT CustomFields.c.age, Name
FROM Contact
WHERE CustomFields.c.age IS NOT NULL

In certain situations, it may be preferable to instantiate a RightNowSyncPortClient


only as needed, rather than in a class constructor. This might be the case if, for
example, the code included conditional logic so that a client might or might not ever
be used in a particular execution. However, in these situations, it is also important to
avoid having a client instantiated multiple times when the section of code that needs
the client is executed multiple times.

This method, which instantiates a client only if it has not already been instantiated,
can be called as needed instead of having the client created automatically in the
constructor.

RightNowSyncPortClient GetClient() {
if (_client == null) {
_client = new RightNowSyncPortClient {
ClientCredentials = {
UserName = {
UserName = "cxadmin",
Password = "cxadmin"
}
}
};
}
return _client;
}
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 33


You have been assigned to work on the Smartech data integration project
writing ROQL commands to select data for integration. This will require
using more complex operators and functions.

In this activity, you will write queries with WHERE clauses using operators and
functions.
All Organizations whose name includes the word "Pharmaceuticals" sorted
by name
The total number of organizations in the database
All the subjects of all Incidents created in 2010
If you get an error here, read the error message carefully. To fix it,
edit app.config to increase the values in maxReceivedMessageSize
and maxBufferSize
The IDs of the 'prodreg' custom objects created during March, 2010
All the text in all the threads for incidents where the organization is
"Intertrade Malls"


ROQL does not support regular SQL-type joins, but it provides something similar
using pre-defined relationships that allow you to link objects either as:
Parent to Child: a primary key value in the parent table will link to one or
more child rows.
Child to Parent: a foreign key in the child will link to exactly one primary
key in the parent.
SELECT <Parent Object>.<Parent to Child
Relationship Name>.<Property of Child Object> FROM <Parent
Object>
SELECT <Child Object>.<Child to Parent Relationship Name> FROM
<Child Object>
The ROQL documentation contains a list of all relationships that have been defined
for primary objects. These are the only joins that can be made for primary objects.

Relationship Queries can be used to return fields from a CSV query, or objects from
an object query, or functions with a CSV query:

SELECT Contact.PrimaryContactIncidents.ID FROM Contact
SELECT Contact.PrimaryContactIncidents . . .
SELECT count(Contact.PrimaryContactIncidents.ID) . . .

The relationship "join" is being made in the SELECT clause.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 34


In a relationship query, ROQL performs a left outer join by default, which means that
the rows from the first table in the join (the leftmost table) will be returned if there is
a match in the right-hand table. But a left outer join also will return a row if there is
no value (NULL) in the right-hand table. That is, it returns all but explicit non-
matches.

If you do not want to return rows that match to NULL, then add a check for IS NOT
NULL to the WHERE clause in the right-hand table.

SELECT C.PrimaryContactIncidents.CreatedTime
FROM Contact C
WHERE C.ID = 99 AND C.PrimaryContactIncidents.CreatedTime IS
NOT NULL

SELECT I.Category, C.ID, C.Name.Last, C.Name.First,
C.Address.City, C.Address.StateOrProvince.Name
FROM Incident I, Incident.ParentOrganization.Contacts C
WHERE Incident.ID = 11

Starting with the November, 2011, release, you can put a relationship string in the
FROM clause, comma-separated from the primary object. An alias for the relationship
string can then be used in the SELECT clause to save typing and make the query
easier to read. A left outer join will be performed, but can be restricted as explained
earlier.

You need to retrieve data stored in more than one primary object.

In this activity, you will write appropriate ROQL relationship queries.


The time when all incidents were created for the Contact whose Contact ID
is 99.
The last name of the primary contact for incident 812.

Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 35


The LIMIT keyword is followed by a number that limits the number of rows returned
by a SELECT query.

SELECT Contact.Name.First FROM Contact LIMIT 100

The number after the keyword OFFSET tells how many records from the record set
generated on the server will be skipped before records are returned to the client.
LIMIT can be used without OFFSET, but OFFSET cannot be used without LIMIT.

SELECT Contact.Name.First FROM Contact LIMIT 100 OFFSET 20

If the offset value is greater than the total number of records that meet the select
criteria, no records will be returned, and no exception or error message will be
generated.

Paging is an object that is returned with each QueryObjects query to indicate if more
results were generated than were returned. This would happen if a LIMIT had been
set that was lower than the results generated, or if the results generated were greater
than the 10,000 maximum limit.

while . . .
{ . . .
"SELECT FROM WHERE LIMIT OFFSET " + offset;

. . .

if (queryObjects[0].Paging.ReturnedCount > 0)
{
offset += queryObjects[0].Paging.ReturnedCount;
}
}

Paging can be used in a loop with OFFSET to obtain any addition results that were
not returned in the previous iteration.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 36


You need to return and display results in groups of 3, instead of all at once.

In this activity, you will modify existing code in ControllingOutput_Starter so that it


prints out results in groups of three.

If you have multiple database queries, you can make your code more efficient by
combining them into a single call. This requires only one network round trip and one
database connection to return results from all the queries at once.

To combine queries, put multiple SELECT statements into the same string,
separating them with semicolons. The queries must all be of the same type, either
object queries or CSV queries.

SELECT Contact FROM Contact
WHERE Contact.Address.City= 'Singapore' ;
SELECT SalesProduct FROM SalesProduct WHERE . . .

There is a limit of 100 ROQL statements per request, and an overall limit of 10,000
rows returned from all statements combined.


When you execute multiple SELECT statements in the same QueryObjects call, the
results are returned into separate elements of the array of QueryResultData. The
output from the first SELECT statement, which will be an array of RNObjects, will be
put into the first element of the QueryResultData array. The output from the other
SELECT statements will then go into the next elements of the array, in order.

QueryObjects operates polymorphically. It can return data of any sub-type of
RNObject Contacts, Answers, etc. Therefore, the return data is of the parent type,
RNObject, and the developer must cast each object back to the correct sub-type.

Multiple statements in a QueryCSV method require no special handling, since they
are all of type string, but the result of each individual query will be placed into a
separate table in the array of type CSVTable.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 37

QueryResultData[] queryObjects= _client.QueryObjects(


clientInfoHeader, "SELECT Contact FROM Contact WHERE
Contact.Address.City= 'Singapore' ; SELECT SalesProduct
FROM SalesProduct", objectTemplates, 10000);
RNObject[] rnContactObjects = queryObjects[0].RNObjectsResult;
foreach (RNObject obj in rnContactObjects)
{
Contact cont = (Contact)obj;
System.Console.WriteLine(" "+cont.Name.First);
}
RNObject[] rnSalesProductObjects =
queryObjects[1].RNObjectsResult;
foreach (RNObject obj in rnSalesProductObjects)
{
SalesProduct sp = (SalesProduct)obj;
System.Console.WriteLine(" " + sp.Name);
}

CSVTableSet tableSet = _client.QueryCSV(clientInfoHeader,


"SELECT C.Name.First FROM Contact C WHERE C.Address.City =
'Singapore'; SELECT SalesProduct.Name FROM SalesProduct",
10000,",",false,false,out output);
CSVTable[] tables = tableSet.CSVTables;
foreach (CSVTable t in tables)
{
Console.WriteLine(Environment.NewLine + t.Name+":");
String[] rows = t.Rows;
foreach (String data in rows)
{
System.Console.WriteLine(" " + data);
}
}
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 38


Occasionally the server may blacklist queries which are running too slowly or whose
syntax suggests they will run slowly. In addition, if a query runs too slowly on first
execution, the server may allow it to run the first time, but blacklist it for subsequent
executions.

Blacklisted queries will return a message in the thrown exception, such as:
Poor performing query aborting
Poor performing query blocked
Poor performing query too many rows examined
Poor performing query too much time taken

In general, blacklisted queries must be re-written.

You have written an application that uses a ROQL query. For better performance
and to reduce bandwidth needs, you want to re-write it to use multiple queries.

In this activity, you will use multiple queries to re-write a query in existing code in
MultipleQueries_Starter.


Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 39

Special queries can be used to get contextual information about the current instance
of CX. The available queries are:
curLanguage()
curLanguageName()
curInterface()
curInterfaceName()
curAdminUser()
curAdminUserName()

In each of the pairs above, the first query returns an ID number, and the second
returns the string name associated with that ID.

The example below shows using QueryCSV to find the name of the current
language/culture of the interface. Notice that the query specifies that a maximum of
one row will be returned. Also notice that parentheses must be used after the
function name.

CSVTableSet tableSet = Program._client.QueryCSV
(clientInfoHeader, "SELECT curLanguageName()", 1);
Console.WriteLine("Current language/culture setting is " +
tableSet.CSVTables[0].Rows[0]);
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 40

What are the advantages of using RightNow Connect?




What is the purpose of the RightNow Connect Common Object Model?


What is a primary object? A sub-object?


What is a ClientInfoHeader?


What is the difference between Object Queries and CSV Queries?


Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 41


What are some of the ways in which ROQL differs from SQL?


What is a relationship query?


How do you iterate through result sets when more results are generated by a query
than are returned to the client? (How do you know there are more?)


How can you get around blacklisting?





Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 42

Connect Web Services for SOAP provides methods for the four basic CRUD
operations:
Create
Read
Update
Delete

These operations can be combined so that multiple operations can be sent to the
server for execution in a single call:
Bulk: Applies the same operation to objects of different types.
Batch: Sends mixed operations to be executed sequentially.
Chain: Passes the output from one operation as input to a subsequent
operation.

There are also special operations, such as running a report, that can be executed by
a call to a SOAP web service.

RNObject[] = _client.Create
(ClientInfoHeader, RNObject[], CreateProcessingOptions);

To create a new object, you will call the ROQL Create method from a
RightNowSyncPortClient, passing it three arguments:
A ClientInfoHeader.
An array of the parent type, RNObject, that contains instances of any child
types to be created. Create operates polymorphically on any object type.
An instance of the CreateProcessingOptions class with Boolean
properties for the following:
o SuppressExternalEvents: Indicates whether external events
should be fired when the object(s) are created. An external event is
code that runs when fired by an event or a business rule..
o SuppressRules: Specifies whether business rules associated with
the object should be applied when it is created.

The return value from the Create operation is an array of IDs of newly-created
objects. As in ROQL, these are IDs of the parent type, RNObject, but can be cast to
the sub-types if needed.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 43

PersonName name = new PersonName();


name.First ="Dan";
name.Last = "Mains";
Email email = new Email();
email.action = ActionEnum.add;
email.actionSpecified = true;
email.Address = "dmains@abc.com.invalid";
email.AddressType = new NamedID();
email.AddressType.ID = new ID();
email.AddressType.ID.id =0;
email.AddressType.ID.idSpecified = true;
email.Invalid = false;
email.InvalidSpecified = true;
Email[] emails = new Email[];
emails[0] = email;

The documentation indicates which values are required for any CRUD operation.
For example, a new Contact can be instantiated without initializing any of its fields,
or some/all of the fields can be initialized with values, but a contact's ID is required
on Update or Destroy.

However, normally before creating a new Contact, you will populate many of the
fields, and instantiate and initialize many of the sub-objects which the Contact will
have. The sample code above instantiates a new PersonName type and set its First
and Last fields.

An email address in a Contact is kept in the Emails field which is an array of Email
objects. In the code above, Emails is instantiated and initialized with a single Email
object. That Email is itself instantiated and initialized with values including an
action to be performed, in this case adding the email, and a Boolean to say whether
or not an action has been specified. Finally, you must create an array of emails and
assign this email to that array.

In the code above, there are also values for Address and AddressType, which itself is
a NamedID so it must be instantiated before it can be initialized with an ID value.
Then set a property to indicate that the ID has been specified and assign a value
stating the email is still valid (not invalid) and that a value has been specified.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 44


The CRUD operations on List data types are handled separately from the containing
object. When an object is updated, for example, an individual list item might be
added, deleted, or updated. To indicate the action to take, an action attribute is set to
a value from the ActionEnum enumeration (Add/Update/Remove/None), and then
actionSpecified is set to true, to show that it has been set.

email.action = ActionEnum.add;
email.actionSpecified = true;

In most cases, you will need to provide an object ID to an operation. This is not true
on create operations, because the server assigns those IDs. An ID object has a
Boolean property for idSpecified. If you are specifying an id, you will also have to set
the idSpecified property to true. If you fail to do that, you may get an error message
that refers to a very large negative integer value as the ID. An error message like that
should suggest to you to check that you have set idSpecified to true.

There are only a handful of situations in which idSpecified is set to false. Those will
be covered later in this material .

Contact contact = new Contact();


contact.Name = name;
contact.Emails = emails;

This code instantiates a new contact and sets Name and Emails properties to the
name and email array objects created earlier under the section "Creating Sub-Objects
for a New Object".
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 45

Contact[] contacts = new Contact[1];


contacts[0] = contact;
CreateProcessingOptions po = new CreateProcessingOptions();
po.SuppressExternalEvents = true;
po.SuppressRules = true;

This code instantiates an array of contacts and a CreateProcessingOptions object
with properties that determine whether or not rules and external events will be
suppressed.

Choosing whether or not to run rules and external events is an individual decision
made based on what rules and external events have been set up on a specific
installation and whether it is desirable to run them when a new object is created in
code in this particular application.

By default, duplicate emails are not permitted, so an attempt to create a new object
with a specified email address that already exists in the database will throw an
exception:

Cannot save/create: Contact: (null)
While performing create on Contact

RNObject[] returnValues = _client.Create(clientInfoHeader,


contacts, po);
contact = (Contact) returnValues[0];
Console.WriteLine (contact.ID.id);

This part of the code calls the Create method, passing in three arguments: the
clientInfoHeader, the array of objects to create, and the processing options. The call
returns an array of IDs of the new RNObjects. In this case, only one object was in
the array to be created, so only one object is returned, and it is the first object in the
array.

What was created was a Contact object, but since the call returns an array of the
parent type, RNObject, it must be cast back to a Contact before accessing its
properties. The only properties returned are the unique ID assigned by the CX
server, along with (read-only) created and updated times.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 46

Contact contact = new Contact {


Name = new PersonName {First="Dan", Last="Mains"},
Emails = new Email[] {
new Email {
action = ActionEnum.add,
actionSpecified = true,
Address = "dmains@abc.com.invalid",
AddressType = new NamedID {
ID = new ID {id=0,idSpecified=true}
},
Invalid = false, InvalidSpecified=true
}
}
};

The code above has the same effect as the earlier code example, but is more
compact. It instantiates a new contact, and initializes it at the same time with a
value for name and email.

RNObject[] returnValues = _client.Create(


new ClientInfoHeader { AppID = "DTM" },
new RNObject[] { contact },
new CreateProcessingOptions
{
SuppressExternalEvents = true,
SuppressRules = true
}
);
contact = (Contact) returnValues[0];
Console.WriteLine (contact.ID.id);

This example of calling the Create method embeds instantiation of the
ClientInfoHeader, the array of RNObjects, and CreateProcessingOptions to create
shorter and more compact code.

You need to be able to add new contacts to the database in code, rather than by
hand.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 47

In this activity, you will create a new Contact with attributes of your choice. Open
the Agent Desktop and use it to verify that your new object exists.

RNObject[] rnObjects = _client.Get(cIH, objects, options);



The Get operation implements the CRUD Read functionality.

To get an object, its ID must be supplied, so an array of RNObject types with IDs
set is passed to the Get method, and results are returned to another array of
RNObject, just as in the Create operation.

By default, only top-level objects are returned. If you need data from sub-objects,
request it by instantiating empty sub-object(s) and attaching them to the object(s) in
the array being sent to the Get method. This behavior is referred to in the
documentation as "specify-to-get".
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 48

Contact contact = new Contact();


ID contactID = new ID();
contactID.id = 1;
contactID.idSpecified = true;
contact.ID = contactID;
Note[] notes = new Note[1];
contact.Notes = notes;

RNObject[] objects = new RNObject[] { contact };
GetProcessingOptions options = new GetProcessingOptions();
options.FetchAllNames = false;

try
{
ClientInfoHeader cIH = new ClientInfoHeader();
clientInfoHeader.AppID = "Basic Get";
RNObject[] rnObjects = _client.Get(cIH, objects, options);

contact = (Contact)rnObjects[0];
Console.WriteLine("Contact First Name: " +
contact.Name.First +" Last Name: " +
contact.Name.Last);

if (contact.Notes != null)
{
foreach (Note note in contact.Notes)
{
Console.WriteLine("Note: " + note.Text);
}
}
}

Notes is an array of type Note, a text object that holds extended comments.

GetProcessingOptions, unlike CreateProcessingOptions, has a single field,
FetchAllNames, which, if set to true, would fetch the names of all NamedID types. If
set to false, only the long integer IDs are returned, making the query more efficient.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 49


A colleague has asked you to help him debug his Windows application, which uses
a Get operation. It is not printing out all values.

In this activity, you will debug existing code in GettingAnObject_Starter that uses a
Get operation. Uncomment lines so that the full address should print out,
determine why that is not happening, and fix it. Since not all records in the
demonstration database have full addresses, check with your instructor or use your
Agent Desktop to find which records can be used for testing.

To update an object:
Obtain the ID(s) of the object(s) to be updated
Copy ID(s) to a new instance of the object type.
On the copy, set a value for any fields to be updated.
Send new object(s) back to server, which will update only the fields you
have populated.

Certain fields that are not visible on update and attempting to set them will cause
the update to fail. See documentation for details.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 50

Before this code is executed, you must have the ID of the object to be updated, in
this case an Organization. You might obtain the ID through a ROQL query or a Get
operation. That ID will be set into a new instance of the Organization type, as in line
four of the code snippet below (in place of <id>), and then any fields that are to be
updated must be set with their new values. In the code below, we are setting only the
Login field, so that is the only field that will be changed on the database.

Organization organization = new Organization();
ID orgID = new ID();
// this is the ID of the object to be changed
orgID.id = <id of object to be updated>;
orgID.idSpecified = true;
organization.ID = orgID;
organization.Login = "RNOW";
RNObject[] objects = new RNObject[] { organization};
UpdateProcessingOptions options = new
UpdateProcessingOptions();
options.SuppressExternalEvents =false;
options.SuppressRules=false;
try {
_client.Update(new ClientInfoHeader {"testing"},
objects, options);
Console.WriteLine(objects[0].Login);
}
catch (Exception e) {. . . }

Notice that the return from the Update operation is not being assigned to a variable,
because the return from an Update operation has no useful data, not even a Boolean
to indicate success or failure. If the operation does not throw an exception, you can
assume success.

Of course you muse always execute this SOAP method, and all others, within a try
.. catch block.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 51

contact.Emails = new Email[1];


contact.Emails[0] = new Email();
contact.Emails[0].Address = "addr_new@sample.com.invalid";
contact.Emails[0].AddressType = new NamedID
{ ID = new ID { id = 0, idSpecified = true } };
contact.Emails[0].action = ActionEnum.update;
contact.Emails[0].actionSpecified = true;
contact.Emails[0].Invalid = false;
contact.Emails[0].InvalidSpecified = true;

For a list type, such as Emails or Notes, in an object to be updated, instantiate the
array that holds the list, and instantiate any individual items in it that need to be
updated.

For each of the fields to be updated, set the required attributes. Check the
documentation for attributes required on update for any individual data type. For
example, an Email requires an AddressType on update. Set any values that need to
be updated, added, or deleted, and use ActionEnum to specify which of those
actions is to be performed with the update.

For example, it would be possible to update a primary object by deleting one of its
emails while changing another and adding a third. The CRUD operations on a list
item are independent of the CRUD operation on its containing type.

In this example, Invalid is also specified. Invalid may or may not need to be
updated, but it is good practice to set it in case the previous address had been set to
invalid. If so, your update could be successful and hold a new email, but the email
still would not be available for use because it would be marked invalid.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 52

contact.Address = new Address();


contact.Address.StateOrProvince = new NamedID();
contact.Address.StateOrProvince.Name = "TX";

The code above shows how to use NamedID when updating or creating an object. In
the example, if you need to set a value for the state in a contact's address. you can
set the Name field of the StateOrProvince NamedID. The server will then look up
the corresponding ID and assign it to the record so the developer doesn't have to
know the ID code for the state of Texas.

This approach is easier and makes your code more self-documenting, but it does
have an impact on performance because an ID is always required by the database
itself. That means there will be an extra lookup is needed to get the ID from the
name. To avoid that performance penalty, you can get name values for a NamedID
type by using the GetValuesForNamedID method, shown later in this course, and
then use the ID instead of the name.

You need to update a single record that includes NamedIDs and Lists.

In this activity, you will write an app to change the email for the new contact you
created earlier to another email of your choice.

There are three ways multiple operations can be combined into a single server
request in order to reduce network traffic and improve performance:
Bulk operations
o Mixed object CRUD capabilities
o Create a contact, incident & organization in a single request
Batch operations
o Mixed operation capabilities
o Create, update, and delete a contact in a single request
Chaining operations
o Output of one operation is input to another operation
o Create a new contact and chain the contact id to a new incident,
setting the incident contact, in a single request
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 53

All CRUD operations are polymorphic, so any CRUD operation can accept as input
any primary object that is a sub-type of RNObject. In addition, multiple objects of
multiple types can be supplied at the same time to the same operation. For example,
a Contact, an Incident, and an Organization can all be supplied to a single
invocation of the create method to create all three at once. This is referred to as bulk
processing.

All items supplied to a CRUD operation are treated as one transaction. Therefore, if
any one object in the request cannot be processed, the entire request is rolled back
and none of the data is committed.

There is a hard limit of 1000 objects that may be supplied in a single CRUD request.
However, due to operational constraints, it is possible that supplying the maximum
objects may result in an error. The number of items that may safely be sent in a
single CRUD operation request will need to be weighed against the object
complexity and size of data. In cases where too much data is supplied in a single
request the server may fail to complete the transaction and the client will receive an
unexpected exception.

The steps required to destroy an object (to delete it from the database) are:
Create an instance of the primary object to destroy.
Set its ID property. No other fields need to be set.
Call the Destroy method, sending it the instance.

The destroy operation provides no return value. It deletes the primary object and all
related sub-objects. In some cases, it may also delete related primary objects. For
example, deleting an Organization deletes related Contacts, and deleting a Contact
deletes its Incidents. However, if an Account is deleted, then any Incidents that refer
to that Account in their AssignedAccount field will have that field set to null, but the
entire Incident will not be deleted.

For more information on how and when related objects or fields are changed on a
delete of another object, see the RightNow documentation.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 54

Contact c = new Contact();


c.ID = new ID( id = <id>, idSpecified=true);
Organization o = new Organization();
o.ID = new ID(id = <id>, idspecified=true);
RNObject[] objects = new RNObject[2];
objects[0] = c;
objects[1] = o;

DestroyProcessingOptions options = new
DestroyProcessingOptions();
options.SuppressExternalEvents = true;
options.SuppressRules = true;

try
{
_client.Destroy(cIH, objects, options);
}

The code above destroys two different types of objects in a single call.

Your company, Smart Technologies, uses an application that creates two objects.
As currently written, the application makes two separate calls to the database: one
to create a Contact and one to create an Organization.

You have been asked to improve the performance of the app, and reduce the
network traffic it generates, by combining the two calls into one bulk call.

In this activity, you will modify CreatingObjectsInBulk_Starter to perform both


Create operations in bulk together.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 55

Batching refers to including multiple operations of different CRUD types in a single


call. One call, for example, could include a Get, a Create, and an Update.

Transactional boundaries can be set within batched operations. All items in the
transaction will fail if any one item fails, and no more items in that transaction will
be processed, but items in any subsequent transactions may still be processed, if the
failure was not a fatal error.

There is a maximum of 1000 BatchRequestItem objects permitted per call, and 100
transactions. However, each BatchRequestItem can be bulk. Though the limit for
bulk is 1000 and the limit for batch is 1000, there is still a hard limit overall of
10,000, so you cannot use the maximum for both at the same time.

The steps required to implement batch processing are:


Create as many BatchRequestItems as necessary.
o Each item will hold a Message type appropriate for the CRUD
operation, such as a CreateMsg or a GetMsg
o Each Message will hold
An array of RNObjects of the appropriate type
An instance of ProcessingOptions
Populate an array with the items.
Set any transaction boundaries in the array as desired.
Send the request to process array to server.
Process returned items according to the BatchResonseItem type for each
one
Handle errors, if any, in the results returned.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 56


The code below is a method to create and return a BatchRequestItem. Each
BatchRequestItem refers to a specific type of operation on a specific type of object. In
this case, the Get operation on an Incident item is specified by using a GetMsg. There
are also Msg objects for other operations, such as CreateMsg. GetMsg is the message
that will be sent to the server, and it has as attributes the same elements that would
be in a regular non-batched call to the Get operation: an array of RNObjects and an
object to hold processing options. It does not need a ClientInfoHeader because it
runs using the ClientInfoHeader of the complete batch operation.

When the GetMsg has been initialized with options and an array of objects, it is
assigned to the Item property of the BatchRequestItem.

public BatchRequestItem GetIncident(long id_in )
{
BatchRequestItem getItem = new BatchRequestItem();
GetMsg getMsg = new GetMsg();
GetProcessingOptions getOptions =
new GetProcessingOptions();
getOptions.FetchAllNames = false;
getMsg.ProcessingOptions = getOptions;
Incident incident = new Incident{
new ID {id=id_in, idSpecified=true }
};
getMsg.RNObjects = new RNObject[] { incident };
getItem.Item = getMsg;
return getItem;
}
. . .
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 57


BatchRequestItem[] requestItems = new BatchRequestItem[5];
requestItems[0] = p.GetIncident(5);
requestItems[1] = p.CreateContact("Sam");
requestItems[2] = p.GetIncident(3);
requestItems[2].CommitAfter = true;
requestItems[2].CommitAfterSpecified = true;
requestItems[3] = p.CreateContact("Dave");
requestItems[4] = p.GetIncident(7);
. . .

After creating as many BatchRequestItems as needed, put them into an array of
BatchRequestItems. If you want to divide your batch into transactions, you can set an
attribute called CommitAfter on elements in the array. A commit after attribute
marks the end of a transaction. When setting CommitAfter, CommitAfterSpecified
must also be set to true. There is also an implicit CommitAfter at the end of the array.
The array in this example shows mixed operation types, which is typical of batching.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 58


To send the array of BatchRequestItem for processing, call the Batch operation and
provide a ClientInfoHeader and the array that holds the items. Set processing
options are not needed in this call, because they are set on each individual request
item.

The return value is an array of BatchResponseItems. They are generic so each will
need to be cast back to the original type as shown in the code below.

The sample shows GetResponseMsg, which corresponds to the response obtained
from a GetMsg request. Extract the actual object from the response and obtain its ID
or data type. On a Get operation, you can cast the object back to its type in order to
obtain other attribute values. On a Create operation, only the ID will be populated
and available to you in the response message. Nothing useful is returned in Destroy
or Update responses.

BatchResponseItem[] batchRes =
_client.Batch(clientInfoHeader, requestItems);
foreach (BatchResponseItem resp in batchRes)
{
if (resp.Item is GetResponseMsg)
{
GetResponseMsg getResp = (GetResponseMsg)resp.Item;
//casting to response type
RNObject[] getObjects = GetResp.RNObjectsResult;
foreach (RNObject o in getObjects)
{
Console.WriteLine("Retrieved: " + o.ID.id);
Console.WriteLine("of type " + o.GetType());
}
}

. . . // process other response types
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 59


if (resp.Item is RequestErrorFaultType)
{
RequestErrorFaultType fault =
(RequestErrorFaultType)resp.Item;
Console.WriteLine("Fault message: " +
fault.exceptionMessage);
}

As part of handling responses, you should include a check to see if the response item
is of type RequestErrorFaultType. A RequestErrorFault is returned by the server
when there is a validation failure or a data-related error in the request.

There are two other fault types: ServerErrorFaultType and
UnexpectedErrorFaultType. Both of these are relatively rare. You could include
checks for them, or simply allow them to be caught by a catch statement. A list of all
the possible exception messages which the fault provides is available in the
documentation.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 60


ROQL CSV queries can be included in a batch. ROQL has its own message type,
QueryMsg, and its own response type, QueryCSVResponseMsg.

BatchRequestItem csvItm = new BatchRequestItem();
QueryMsg qMsg = new QueryMsg
{
Query = "SELECT . . . FROM . . . WHERE . . .",
PageSize = 10000,
PageSizeSpecified = true
};
csvItm.Item = qMsg;
ClientInfoHeader ciH = new ClientInfoHeader();
cIH.AppID = ". . .";
BatchRequestItem[] reqIts=new BatchRequestItem[1];
reqIts[0] = csvItm;

BatchResponseItem[] batchRes = _client.Batch(cIH, reqIts);
foreach (BatchResponseItem r in batchRes)
{
if (r.Item is QueryCSVResponseMsg)
{
QueryCSVResponseMsg rM=(QueryCSVResponseMsg)r.Item;
foreach (string s in rM.CSVTableSet.CSVTables[0].Rows)
{
// process returned data in string s
}
}
if (r.Item is RequestErrorFaultType)
{
. . .
}
. . .
}
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 61


Create Batch Request Items
o Include usual operation parameters, except for the ClientInformationHeader
Populate array with items
Set transaction boundaries in array
Send request to process array to server
o Includes ClientInformationHeader
Iterate through results and process
o Check each BatchResponseItem type and process accordingly
o Provide fault handling

Smartech has an existing application that uses batching to Get Incidents and Create
Contacts. It processes each item individually. You have been assigned to add to that
app the ability to delete Contacts.

In this activity, you will modify the existing application Batch_Starter appropriately
by adding a method that returns a BatchRequestItem for destroying items.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 62

Using a single request to create a new Contact, then a new Incident related to the
Contact can be problematic. Creating a new Incident requires a Primary Contact
object, but the ID of the new Contact is not available until both operations have
completed and the server call returns.

Chaining solves this problem by passing the output of one operation as input to the
next operation.

Chaining is only supported on CRUD operations, and as of the November, 2011,
release, cannot span transaction boundaries.

To implement chaining, you must:


Instantiate a ChainSourceID.
Set its variableName attribute.
Instantiate a ChainDestinationID.
Set its variableName attribute to the same value as the ChainSourceID.

All primary and foreign key fields are of type ID. To chain the ID of a newly created
object, a specialized ChainSourceID (derived from ID) is passed to the create
request. It defines a variableName attribute which is used as a unique variable name
to bind that newly created ID value to that variable name for the scope of the SOAP
request. This object will function somewhat like a server-side variable.

For any subsequent objects that need access to the newly-created ID, instantiate a
ChainDestinationID, set the variableName attribute to the same name, and assign
that object to the ID field. The public SOAP service will handle placing the value that
is assigned to the ID into any ChainSourceIDs and ChainDestinationIDs as needed.

Since the service will not handle forward references, provide the source ID before
assigning the destination ID.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 63


_chainContactID = new ChainSourceID();
_chainContactID.idSpecified = false;
_chainContactID.variableName = "SharedID";
newContact.ID = _chainContactID;
. . .
IncidentContact primaryContact = new IncidentContact();
NamedID primContactNamedID = new NamedID();
ChainDestinationID chainDestID = new ChainDestinationID();
chainDestID.idSpecified = false;
chainDestID.variableName = _chainContactID.variableName;
primContactNamedID.ID = chainDestID;
primaryContact.Contact = primContactNamedID;
newIncident.PrimaryContact = primaryContact;

Prior to the start of this code snippet, a Contact object named newContact was
instantiated. Now a static class variable of ChainSourceID type (_chainContactID)
can be assigned to its ID property. You must indicate that the ID of the
ChainSourceID has not been specified (line 8 above), then give it a string label such
as "SharedID".

An Incident must have a primary contact. In this case, the Incident's
PrimaryContact field has a ChainDestinationID type in the contacts NamedID.ID
field. Set idSpecified to false, and assign to the DestinationID's variableName
property the variable name or the string literal from the _chainContactID which will
receive its value when the Contact is instantiated.

After the connection between the two objects has been set up in this way, the objects
can be created one after the other in a batch or bulk operation and the ID assigned
to the one will automatically be provided to the other.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 64


Beginning with the November, 2011, release, chaining can be used between ROQL
queries that are being processed in batch. For example, one query can be used to
retrieve an object's ID, based on some other known property of the object. Then a
subsequent query in the same batch can use that ID to retrieve data from other
related objects.

The mechanism for this differs from the mechanism used in other CRUD operations
explained earlier.

First query:

SELECT ID as '@ContactID'
FROM Contact
WHERE Login = 'badams'

Second query:

SELECT . . . FROM Contact WHERE ID = @ContactID

The chain source is a variable of your choice, preceded by the @ symbol and enclosed
in single quotes. In the SELECT clause of the first query, the value to be assigned to
the chain source is followed by as and then by the variable. The value being assigned
to the chain source must be numeric.

In the next ROQL query, the variable name, with the @ but without the single quotes,
can be used like a number, as in the example above.

You have been assigned to debug an application that uses batching to create a new
Contact and a new Incident for which the new Contact is the primary contact.
When you run the existing app, you realize that the Incident was not created
because it did not have a primary contact ID. You decide that chaining is what is
needed here.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 65

In this activity, you will re-write Chaining_Starter to use chaining.


Custom Objects cannot be defined in code. They are defined in the Agent Desktop
using Object Designer. However, Custom Objects can be instantiated, updated, or
destroyed in code.

Create an instance of the GenericObject type.
Set the ObjectType property of the Generic Object to the Namespace
(package) + TypeName (name) of the Custom Object.
Set the GenericFields property of the Generic Object to an array of type
GenericField.
Standard code is available to create GenericFields



GenericObject go = new GenericObject();
RNObjectType objType = new RNObjectType
{ Namespace = "Classes",
TypeName = "TrainingClass" };
go.ObjectType = objType;
GenericField[] gfs = new GenericField[]
{
CreateGenericField("ClassNumber",
ItemsChoiceType.IntegerValue, 2),
CreateGenericField("ClassName",
ItemsChoiceType.StringValue,
"Advanced Smartech")
};
go.GenericFields = gfs;
try
{
RNObject[] results = _client.Create(
_clientInfoHeader,
new RNObject[] { go },
createProcessingOptions
);
}
catch { . . .
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 66


private GenericField CreateGenericField(string Name,
ItemsChoiceType itemsChoiceType, object Value)
{
GenericField gf = new GenericField();
gf.name = Name;
gf.DataValue = new DataValue();
gf.DataValue.ItemsElementName =
new ItemsChoiceType[] { itemsChoiceType };
gf.DataValue.Items = new object[] { Value };
return gf;
}

To create a new instance of an existing Custom Object, start by instantiating a
GenericObject which will hold the custom object data. Provide the namespace, which
is the same as the package or folder in the Object Designer.

Set up an array with an element for each field in the custom object. The
CreateGenericField method shown above handles creating the fields. It is user-
written, not part of the .NET or RightNow frameworks.

Assign the array of generic fields to the generic object, and finally, call the Create
method.

The CreateGenericField method is standardized code so it can be copied as is into
applications. The method takes three arguments: the name of the new field, its data
type, and its value. Each argument is placed in matching attributes of the generic
field object. Note that the data value attribute can hold an array of objects.

Earlier you used the Agent Desktop to create a Custom Object named
"TrainingClass". Now you will manipulate that Custom Object in code.

In this activity, you will write an application to instantiate and populate an instance
of TrainingClass, and then to retrieve and display the data from the object just
created.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 67


Connect Web Services for SOAP also includes operations to handle operations that
are not basic CRUD functions:
Running an Analytics Report
Getting values for a NamedID type

In order to run an analytics report in code, create an Analytics Report object, and set
the report id or name, then, optionally, attach an AnalyticsReportFilter.

Limit and Start are the third and fourth arguments to the operation, and they show,
respectively, how many rows to return and which row to start from. The last four
arguments are the same as those for QueryCSV.

CSVTableSet set = _client.RunAnalyticsReport(ClientInfoHeader,
ReportObject, 10000, 0, ",", false, false, out output);
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 68

AnalyticsReport analyticsReport = new AnalyticsReport();


int limit = 100;
int start = 0;

ID reportID = new ID();
reportID.id = 13029;
reportID.idSpecified=true;
analyticsReport.ID = reportID;
AnalyticsReportFilter filter = new AnalyticsReportFilter();
filter.Name = "Opportunity Id";
analyticsReport.Filters = new AnalyticsReportFilter[] { filter
};

ClientInfoHeader cIH = new ClientInfoHeader
{ AppID ="My_Rpt"};
Byte[] output = null/
CSVTableSet set = new CSVTableSet();
set=_client.RunAnalyticsReport(cIH, analyticsReport, limit,
start,",", false, false, out output);

String[] rows = set.CSVTables[0].Rows;
foreach (string data in rows)
{
Console.WriteLine(data);
}

The call to RunAnalyticsReport returns a CSVTableSet, which is processed like the
CSVTableSet that is the return value from a QueryCSV operation.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 69


The code below will get the names of all Analytics Reports that start with a given
letter, along with the names of all columns in that report, to help you locate the
report you want.

string q = "SELECT AnalyticsReport
FROM AnalyticsReport
WHERE name LIKE '" + first_letters + "%'";
QueryResultData[] queryObjects = _client.QueryObjects(cIH, q,
objectTemplates, 10000);
foreach (RNObject obj in rnObjects)
{
AnalyticsReport ar = (AnalyticsReport)obj;
Console.WriteLine(ar.Name);
AnalyticsReportColumn[] cols = ar.Columns;
Console.WriteLine("Columns ");
foreach (AnalyticsReportColumn c in cols)
{
Console.WriteLine(" " + c.Heading);
}
}

You need to be able to run a report that will show each staff account and profile.

In this activity, you will determine what report you should use, and run it to produce
the results required by the scenario.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 70

NamedID[] namedIDs = _client.GetValuesForNamedID (


new ClientInfoHeader { AppID = "NamedIDs" },
null, "Contact.Emails.EmailList.AddressType" );
foreach (NamedID namedID in namedIDs)
{
Console.WriteLine(namedID.ID.id + "\t" + namedID.Name);
}

The NamedID type is used throughout RightNow CX. The code shown above will
retrieve matching pairs of names and IDs for a NamedID type.

The second parameter to the method is a package name, which is used if you are
getting values for a custom menu. Otherwise it can be null.

There are several other non-CRUD operations available in Connect Web Services for
SOAP. More information on these is available in the on-line documentation.
Managing file attachments
Resetting a Contact password
Sending mailings to Contacts
Executing a marketing flow

You need to be able to run a report that will show each staff account and profile.

In this activity, you will write code to get the IDs and names required.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 71


What are the three arguments to CRUD operations methods?


What are the advantages/disadvantages of providing the name instead of the ID of a
NamedID type?


What is the difference between bulk operations and batch operations?


Why would you need to chain operations?


What are some of the non-CRUD operations available in the API?






Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 72

Add-Ins are components written by developers that can be used to:


Integrate external data into the agent desktop.
Automate the desktop.
Create application extensions.

Add-In components can be created and placed in many different locations on the
Agent Desktop. An Add-In can be placed directly onto a workspace, or on various
parts of the navigation and control system of the Agent Desktop.

For example, the Application Menu can have new buttons, the Status Bar can provide
short messages, the Navigation Pane can have new controls, or buttons can be added
to the ribbon.

A fellow developer has already created an Add-In and it has been compiled. You
have been asked to upload it to the site, making it available to all users, and to test it
after uploading it.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 73


In this activity, you will upload an existing compiled Add-In DLL to the RightNow
server for your site.

1 From the Agent Desktop, go to Configuration > Site Configuration >
Add-In Manager.
2 Click New and browse for MyAddIn.dll (located in the
"\Starter\Uploading an Add-In\ClassLibrary1\bin\Release" folder)
and click Open.
3 Click the Profile Access button on the ribbon and select CX All 2 for
profile, and All for the interfaces.
4 Click Save & Close.
5 Close and re-open the Agent Desktop.
6 Locate the button in the Home navigation set and click to test that it
changes the color of its section.


The RightNow Connect Desktop Add-In Framework makes it easier for developers to
write add-ins such as custom controls or components.

An Add-In is compiled into a Dynamic Link Library (DLL).

The RightNow Connect Desktop Add-In Framework leverages the .NET Add-In
Framework, which is in the System.AddIn namespace.

The Add-In Framework is designed to create code that will be backwardly compatible
in future releases of CX.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 74


Developer mode allows a developer to place an Add-In assembly and related files in
the following directory:
%USERPROFILE%\RightNowDev\AddIns

The local instance of the Agent Desktop loads Add-Ins from that folder so they can be
tested without affecting any users other than the local user. The Add-In can be
uploaded to the server when it is fully developed.

To use Developer Mode you must:
Turn on Developer Mode for your profile.
o Configuration > Profiles > your profile > AddIns Tab
o Check Developer Access

RightNow provides a set of Visual Studio project templates for Add-Ins. After
installing the templates, you can select a specific template to create a new project for
a specific add-in type.


The RightNow Add-In wizard makes it easy to create new projects with the Add-In
templates. To use the wizard:
Open a new project based on one of the Add-In templates.
If multiple interfaces are available, select an interface for Add-In.

The wizard will:
Add the standard references needed to create an Add-In.
Provide a skeleton implementation that compiles and runs, but has no
functionality in response to events.
Place the compiled assembly (DLL) in an appropriately named folder within
the Add-Ins folder used by the developer mode.

The Agent Desktop must be re-opened in order to load the new Add-In.

You have been assigned to create several new Add-Ins within a short timeline and
you want to use the Add-In templates with the wizard to create them quickly and
easily. You have decided to start learning how to use the templates by downloading
and installing them from the RightNow Developer Community, then creating a
simple, empty project to see how much the template and wizard will do for you.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 75


In this activity, you will follow online instructions to download and install the Add-In
templates from the RightNow Developer Community, and then to create a basic
application based on the Global Ribbon template.

The template for a Global Ribbon button contains three classes:
RibbonTab
RibbonGroup
RibbonButton

Each of these implement global interfaces which in turn are derived from base
interfaces. Default implementations are provided for you, so the template will
compile and can be displayed in the Agent Desktop. You can then change the
implementations as needed.

In the case of the RibbonButton class, you will need to provide your own
implementation of the Click() method which is called when the button is clicked. You
may also want to change the image on the button, by changing the image which is
returned by the Image16 property. You will also want to change the Text property
which is displayed on the button, below the image.

You can change any other button properties you wish, and similarly change at least
the Text property of the RibbonGroup and RibbonTab classes.

If you want to have additional tabs, buttons, or groups, you can copy-and-paste the
appropriate class, remembering to rename both the class and the AddIn attribute
which is located inside square brackets just above the class declaration. Each button
has a property to show which group it belongs to, and each group has a property to
show which header it belongs to.

When you have made your changes, re-open the Agent Desktop so that it will read in
the new AddIn locally, in Developer Mode. Then you can test your AddIn and upload
it to the server when you are ready to make it available to other users.

Below is code that could be used to start the Notepad application from the Click
method of a Global Ribbon button. The second line shows opening a specific file in
Notepad.

private void Click () {
System.Diagnostics.Process.Start
(@"c:\windows\system32\notepad.exe");
System.Diagnostics.Process.Start
(@"c:\windows\system32\notepad.exe", "test.txt");
}
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 76



You have been assigned to implement a recommendation to provide agents with
access to the built-in calculator in Windows directly from the Agent Desktop, so
they will not have to go outside the Agent Desktop to the Start menu to use the
calculator.

In this activity you will provide a button on the Agent Desktop global ribbon to open
the Windows calculator.

The template provides code for a button. When that button is selected, a sub-menu
appears that displays one or more clickable menu items organized under one or more
headers.

You will provide text for the button and any headers or menu items. You will also
implement click methods for the main button and any clickable menu item buttons.
Optionally, you can place an image on the button.

The AddIn can contain multiple buttons, and multiple headers/menu items for each
button. Each header and menu item has properties that can be set to identify which
button or header it belongs to.

Your users have requested a button to pop up a message with the phone number of
the security department on it, in case of emergencies. They would like a red phone
icon on the button.

In this activity, you will use the templates and wizard write code to provide the
button the users have asked for.

Note that if you have created an Add-In, placed it in the AddIns folder, and opened
the Agent Desktop to test it, you will not be able to make changes to the code and re-
build the DLL unless the Agent Desktop is closed. As long as the Agent Desktop it
open, the DLL in the AddIns folder is in use and locked, so it cannot be overwritten
by a new build of the code in Visual Studio. The build will fail with an error message
related to the post-build events. Those events were set up by the Add-In wizard and
can be seen by selecting Project > Properties > Build Events.

Close the Agent Desktop so that you can execute the build command.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 77


A NavigationSection add in includes two classes. One is NavigationSection, which
inherits from Windows Panel and holds one or more other controls which are
displayed in the section. The second class is a NavigationSectionFactory, which
generates and returns an instance of the NavigationSection itself. This method is
called by the Agent Desktop to get the section to place in the navigation set.

Within the NavigationSection are two regions. One is labeled IAddInControl
members, where controls and handlers are placed. The other is labeled
InavigationSection2, and it is where you will set a title for the navigations section.

In the region labeled IAddInControl Members, you will declare whatever controls are
wanted. In the GetControl method, set their properties appropriately, and then add
them to the panel itself. The return value from the GetControl value is this , which
returns the panel itself when it is called by the Agent Desktop.

If any controls you have placed on the panel trigger events, declare and implement
handlers for those events as separate methods.

Your users (contact center agents who use the Agent Desktop) would like to have a
calendar available right on the Agent Desktop in a general location where it can
easily be accessed. You decide to place the calendar in a Navigation Section.

In this activity, you will write and the add-in needed in the scenario.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 78


Unlike executable applications, Add-Ins are DLLs, which means that they cannot be
run directly. Without special extra steps, you will not be able to use the Visual Studio
debugging tools such as setting breakpoints, viewing variables, and stepping through
code.

To set up debugging, first make sure that Visual Studio has been configured to create
debug files when the project builds. From the main menu, select Build >
Configuration Manager and then select Debug as the active configuration.

Then select Project > ProjectName Properties. From the tabs on the left, select
Debug. In the Start Action section, select Start external program, and click the
ellipsis button on the right to browse for the executable that starts the RightNow
desktop client. Browse to the executable below and click Open to place it in the
textbox.

C:\Users\%userprofile%\AppData\Local\Apps\2.0\NumberLetterCombo\Numb
erLetterCombo\righNumberLetterCombo\RightNow.CX.exe

The specific "number-letter" string can vary, so if necessary, you can do a search for
RightNow.CX.exe to find the path.

In the Start Options section, type in the following, using your site name:

auto=true uname=cxadmin pword=cxadmin dbname=sitename_no_domain
launch=sitename.domain/cgi-bin/sitename.cfg

The new settings instruct Visual Studio to start the Agent Desktop application
whenever there is an attempt to run this project. You can then set breakpoints in the
code in Visual Studio and run the DLL just like you would run a regular executable.
The Agent Desktop will open with the Add-In activated. If you trigger a breakpoint as
you use the Agent Desktop, Visual Studio will come to the front and allow you to
inspect variable values, step through code, and use any of the other debugging
features

You need to set up debugging to see how one of your application is working.

In this activity, you will set up an existing application for debugging and run it in
debug mode.

If debugger seems to be skipping breakpoints, try deleting all compiled assemblies in
the solution folder and in the AddIns folder, and then re-build.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 79


The framework provides interfaces for the following system events so that code can
capture and react to them:
Login / Logout
Console Open / Close

The code below shows an Add-In that reacts to a login event, using the context object
which is passed to the Initialize event. It has information on which user and profile is
currently logged in, the value of many of the permissions for that profile, and the
language of the current interface.

[AddIn("Login Event Add-In", Version = "1.0.0.0")]
public class LoginEventAddIn : IEventLogin
{
public bool Initialize(IGlobalContext context)
{
MessageBox.Show("You are logging in as " + context.Login);
return true;
}
}

An Event Add-In template is available with skeleton code for handling the four
supported events:
Login
Logout
ConsoleOpen
ConsoleClose

Add-Ins have their own logging system that can be used to automatically track add-
ins as they are distributed, activated, and used. The logs produced are not persisted
by default, they are per-session only, but can be saved to a text file.

Add-ins can also create log entries using the Global Context object which is passed
into the Initialize method.

context.LogMessage("Add In showed value " + val + " at " +
System.DateTime.Now);

The behavior of add-in logging in versions earlier than November, 2011, was slightly
different. Consult documentation for details.

Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 80


To enable logging from components, go to the Application Menu and select View
Current Log File. Check to select the type of Add-In activity you want to track. To
enable logging of messages generated by code in an Add-In, select a log level such as
All under Component Logger. The log messages can be seen under the In Memory
Log Messages tab, but they are in-memory only and will not be persisted after logout.
A button in the lower left will allow you to save the log messages as a text file.

An Automation Context object can be used to:
Create/delete/edit record
Open/close/focus editor
Run report
React to CurrentEditorTabChanged event

A reference to the AutomationContext can be obtained from the GlobalContext.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 81

To use the AutomationContext object, create a class-level variable to hold the


context. Then, in the Initialize method, which is part of the base, assign to the class-
level object the global context object which is automatically passed into the Initialize
method.

private static IGlobalContext _globalContext;
. . .
public bool Initialize(IGlobalContext GlobalContext)
{
_globalContext = GlobalContext;
return true;
}

The class variable _globalContext can now be used to access the AutomationContext
functionality, for example in an event handler as shown below.

Opening a record workspace with AutomationContext:



public void Click() {
long val = 1040;
_globalContext.AutomationContext.
EditWorkspaceRecord(RightNow.AddIns.Common.
WorkspaceRecordType.Contact, val);
}

Running a report with AutomationContext:

public void Click() {
System.Collections.Generic.IList<IReportFilter> filters = new
System.Collections.Generic.List<IReportFilter>();
//next five lines are optional; leave out to fetch all rows
IReportFilter filter =
AddInViewsDataFactory.Create<IReportFilter>();
filter.Expression="incidents.search_thread"; //table.field
filter.Operator = 1; // 1 is the operator for "equals"
filter.Value = "phone";
filters.Add(filter);
_globalContext.AutomationContext. RunReport(13029, filters);
}

To call the RunReport method, a report number and a list of IReportFilters is
required. Create the filters list as shown, and add filters or leave empty by
eliminating text in bold.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 82


Supervisors at Smart Technologies frequently need to consult analytics reports on
Agent Logins. They have asked for a button that could quickly run the report from
wherever they happen to be within the Agent Desktop interface, without having to
open Analytics and drill down.

In this activity, you will first modify code so that the button implements the
requirements of the scenario above.

Then you will add another button to open a specific record number.

A workspace ribbon button is a button that a developer creates and makes


functional. A RightNow administrator then places that button on a customized
workspace. Using a template, you can create a Workspace Ribbon button that needs
only an implementation for its Click event.

An object instance of a class that implements IRecordContext provides information
on whichever record is current. It has a method GetWorkspaceRecord that returns an
object that implements the interface for one of the defined CX record types, such as
Contact or Answer. The properties of that object can be used to obtain specific pieces
of data about the current record. The argument to the GetWorkspaceRecord method
is one of the workspace record types.

A method named TriggerNamedEvent can be used to fire a rule associated with that
workspace
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 83

IContact c = (IContact) RecordContext.GetWorkspaceRecord


(RightNow.AddIns.Common.WorkspaceRecordType. Contact);
string custName = c.NameFirst + " " + c.NameLast;

To use the IRecordContext:
Obtain a RecordContext, which is by definition of type IRecordContext,
from the Add-In constructor where it is automatically passed in when the
Add-In is instantiated.
Use it to call GetWorkspaceRecord, passing in an appropriate record type
for the given workspace. For example, to use this Add-In to work with
Contacts pass in a WorkspaceRecordType of Contact, as shown above.
That method returns an object that implements the IWorkspaceRecord
interface.
Cast it to the interface needed to access the properties of the appropriate
object.

Note that you are working with an interface, not an instantiated object, so you must
use property names that are slightly different, for example, c.NameFirst instead of
c.Name.First.

You have been asked to provide a button which can be placed on a Task workspace
to display the date on which the task currently displayed was last edited.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 84

In this activity, you will implement the requirements of the scenario above, first by
writing the Add-In and compiling the code so that the Add-In will be activated in the
workspace in Developer mode. Then close and re-open the Agent Desktop to load
the Add-In. Follow the steps below to create a custom workspace, edit the ribbon to
add a button, and edit your profile to assign the workspace to it.

1 Open Configuration > Application Appearance >
Workspaces/Workflows.
2 Click New Workspace.
3 In the dialog, click Task under Standard Types.
4 Click the arrow to expand the Fields group, select Task ID, and drag
it onto the top section of the new workspace.
5 From the Workspace Properties group, click on Ribbon.
6 In the dialog, click Edit Tab to edit the Home tab.
7 In the Editing Tab, Actions group, click Edit Group.
8 All existing buttons on the ribbon in the Actions group are shown.
Click Add Button.
9 Check the box to add the custom button, Check Task Update
Status, from the list. Click OK.
10 Preview the customized ribbon. Click OK.
11 On the Quick Access toolbar, click Save & Close. Type Name: My
Task Workspace, and place it in the \Workspaces and Workflows
folder. Click OK.
12 Assign this new workspace to your profile. Go to Configuration >
Staff Management > Profiles.
13 Double-click to open the CX All profile. Under the
Workspaces/Workflows tab, scroll down to Task. Click the
search icon at the far right of the Task row.
14 In the dialog, select My Task Workspace and click OK.
15 Click Save & Close.
16 Now test your button. From the Navigation Pane, select Tasks.
17 Double-click Recently Modified Tasks.
18 Double-click a task of your choice to open the Tasks workspace to see
your button on the modified ribbon. Click the button to display the
date on which the task you selected was last modified.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 85

Navigation Items are similar to the Navigation Section from the first exercise in that
they can be used to customize a Navigation Set. However, Navigation Items are
simple links rather than full sections.

To add a Navigation Item to a Navigation Set, click the Customize List link on the
Navigation Pane. Select the Navigation Item from the left-hand side under
Components > Add-Ins. Click Add, then OK. The link will now be visible in the
Navigation Pane.

Navigation Item Add-Ins implement the INavigationItem interface which has an


Activate method that is called when the Navigation Item link is clicked. Place any
desired behavior in the Activate method.

Calls to the Web Service for SOAP API can be made from an Add-In, but they will
have to include the additional code below to generate the client side bindings.

BasicHttpBinding binding = new
BasicHttpBinding(BasicHttpSecurityMode.
TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType =
BasicHttpMessageCredentialType.UserName;
EndpointAddress endPointAddr = new EndpointAddress
("https://<sitename>/cgi-bin/<sitename>.cfg/services/soap");
_client = new RightNowSyncPortClient(binding, endPointAddr);
_client.ClientCredentials.UserName.UserName = "cxadmin";
_client.ClientCredentials.UserName.Password = "cxadmin";
BindingElementCollection elements =
_client.Endpoint.Binding.CreateBindingElements();
elements.Find<SecurityBindingElement>().
IncludeTimestamp=false;
_client.Endpoint.Binding = new CustomBinding(elements);
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 86


You need a Navigation Link that uses a SOAP call to retrieve an Incident record and
display it in the Incident workspace.

In this activity you will develop a Navigation Item add-in to meet the requirements
of the scenario. When the item is clicked, you will display in the Incident workspace
data retrieved by the SOAP call about a particular incident. Remember this creates a
Navigation Item that you will need to manually add to your navigation set.

The documentation for the Desktop AddIn Framework (located in the developer
community on rightnow.com) includes a link to download sample code for several
different types of add-ins.

To use the samples, download and unzip the file. All the Add-Ins are in a single DLL.
Open the Solution file and replace the Reference to RightNow.AddIns.AddInViews
with the correct path on your own system.

You can now study the working code samples for various add-in types. To see how
the add-ins work in the Agent Desktop, compile the code. When you next open the
Agent Desktop, all the various Add-In types in the sample code will be activated.


A Workspace Add-In is a customized user control that is placed on a customized
workspace and given appropriate functionality for that workspace.

Templates are available in Visual Studio for both the Workspace Add-In, which is a
RightNow template, and for the User Control, which is a standard Visual Studio
template.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 87


A basic user control includes an event, in this case, btnClicked, and a delegate for a
handler for that event, in this case, ClickHandler.

public partial class UserControl1 : UserControl {
public delegate void ClickHandler(string data);
public event ClickHandler btnClicked;

public UserControl1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e){
if (btnClicked != null) {
btnClicked(textBox1.Text);
}
}
}

The button1_Click method provides a trigger for the event. Code in the Workspace
Add-In will implement a handler for the btnClicked event and attach it to the
btnClicked event.
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 88


To place the custom User Control in a Workspace Add-In, declare the control at the
global level as static:

private static UserControl1 _usrCtrl;

In the GetControl method, instantiate the control and attach a handler to its Click
method, then return the user control.

public Control GetControl()
{
_usrCtrl = new UserControl1();
_usrCtrl.btnClicked += new
UserControl1.ClickHandler(usrCtrl_btnClicked);
return _usrCtrl;
}

Implement the desired behaviors in the handler. Here you could use a
RecordContext type to access the properties of the current record, as described in an
earlier section.

void usrCtrl_btnClicked(string data)
{
. . .
}

The Boolean variable inDesignMode can be used to suppress behaviors that are
inappropriate in design mode. Use this code in the constructor:

public WorkspaceAddIn(bool inDesignMode, IRecordContext
RecordContext)
{
if (! inDesignMode)
_recordContext = RecordContext;
}
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 89



There are several other types of Add-Ins not included in this material, but they are
covered in the documentation and templates are available for them.
Dashboard Add-In: Holds report to add to dashboard.
Status Bar Add-In: Displays rotating text in status bar.
Content Pane Add-In: Like workspaces, but launched and controlled by
an Add-In rather than directly from the Agent Desktop.
Report Command Add-In: Puts a link on each row of a report.
Extension Bar Add-In: Dockable toolbar hidden unless items are placed
on it; often used for CTI (computer-telephony integration) or for universal
queuing.

Set up add-ins
o In your profile, enable Developer Mode
o On your workstation, install Add-In Wizard and Templates
Start new project in Visual Studio by opening a template
Customize the template
o Test locally
Upload add-in to server using Site Configuration > Add-In Manager
Enable the new add-in for your profile only, and for the interface you are using
o Test on server
Enable the new add-in for other profiles/interfaces
Continued on next page


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 90

To see what Add-Ins are currently active and inactive, click the Application Button in
the upper left and select RightNow CX Options > Add-Ins.

What are some of the ways you can use Add-Ins?




What is the advantage of using Developer Mode?


How do you load an add-in to the server?


Why would you want to use the add-in templates and the template wizard?






Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 91

Smartech needs to integrate RightNow with another data processing system they
have. The data in the other system is made available through a web service that
returns groups of new records that need to be loaded into RightNow CX. Your
assignment is to develop the application that will call the service and integrate the
data it provides into the RightNow database.

In this activity, you will run a web service on localhost that will simulate the web
service provided by the system you are going to integrate with.

Write an application that will access that service and retrieve whatever data it
provides, which will include one or more Contacts, each of which will have an
associated Incident.

For each Contact/Incident pair:
If the contact does not already exist, create it and add it to the database.
Associate the incident with the new or existing contact.
Provide error handling.

The users and their supervisors think that it would be helpful to put a button in the
extension bar area that would open a web browser to http://www.merriam-
webster.com/dictionary.

Create and test this functionality. To open a particular site in a browser, you can use
syntax similar to that used to open an external application, such as opening
Notebook with a particular text file loaded.


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 92

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using ConsoleApplication1.ServiceReference1;
6 using System.ServiceModel;
7 using System.IO;
8
9 namespace ConsoleApplication1
10 {
11 class Program
12 {
13 private static RightNowSyncPortClient _client;
14
15 static void Main(string[] args)
16 {
17 Program program = new Program();
18 program.QueryContact();
19 }
20
21 public Program()
22 {
23 _client = new RightNowSyncPortClient();
24 _client.ClientCredentials.UserName.UserName = "xxx";
25 _client.ClientCredentials.UserName.Password = "yyy";
26
27 }
28 void QueryContact()
29 {
30 ClientInfoHeader clientInfoHeader = new ClientInfoHeader();


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 93

31 clientInfoHeader.AppID = "Identifier goes here";
32
33 try
34 {
35 byte [] output = null;
36 CSVTableSet tableSet = _client.QueryCSV(clientInfoHeader,
37 "SELECT Contact.Name.Last, Contact.ID FROM Contact WHERE Contact.ID < 7",
38 10000, ",", false, false, out output););
39 CSVTable[] tables = tableSet.CSVTables;
40
41 foreach (CSVTable t in tables)
42 {
43 System.Console.WriteLine("Name: " + t.Name);
44 System.Console.WriteLine("Columns: " + t.Columns);
45 String[] rows = t.Rows;
46
47 foreach (String data in rows)
48 {
49 System.Console.WriteLine("Row Data: " + data + Environment.NewLine);
50 }
51 }
52 }
53 catch (Exception ex)
54 {
55 Console.WriteLine(ex.Message);
56 }
57 PressAnyKey();
58
59 }
60
61 void PressAnyKey()
62 {
63 System.Console.WriteLine(Environment.NewLine + "Press any key to continue");
64 Console.ReadKey();
65 System.Console.WriteLine(Environment.NewLine);
66 }
67 }
68 }



Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 94



Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 95


1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using ConsoleApplication1.ServiceReference1;
6 using System.ServiceModel;
7
8
9 namespace ConsoleApplication1
10 {
11 class Program
12 {
13 private static RightNowSyncPortClient _client;
14
15 static void Main(string[] args)
16 {
17 Program program = new Program();
18 program.QueryContact();
19 }
20
21 public Program()
22 {
23 _client = new RightNowSyncPortClient();
24 _client.ClientCredentials.UserName.UserName = "xxx";
25 _client.ClientCredentials.UserName.Password = "yyy";
26
27 }
28 void QueryContact()
29 {
30 ClientInfoHeader clientInfoHeader = new ClientInfoHeader();
31 clientInfoHeader.AppID = "Identifier goes here";
32 Contact contactTemplate = new Contact();


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 96

33 RNObject[] objectTemplates = new RNObject[] { contactTemplate };
34
35
36 try
37 {
38 QueryResultData[] queryObjects = _client.QueryObjects(clientInfoHeader,
39 "SELECT Contact FROM Contact WHERE Contact.Address.City='Singapore'",
40 objectTemplates, 10000);
41 RNObject[] rnObjects = queryObjects[0].RNObjectsResult;
42
43 foreach (RNObject obj in rnObjects)
44 {
45 Contact cont = (Contact)obj;
46 Console.WriteLine(cont.Name.Last + ", " + cont.Name.First);
47 }
48 }
49 catch (Exception ex)
50 {
51 Console.WriteLine(ex.Message);
52 }
53 }
54 }
55 }


Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 97



Copyright 2012, Oracle and / or its affiliates. All rights reserved.
Page 98

You might also like