Comments

  • Andrew Rowse

    You don't push a dynamic field value to an AddIn; instead you use the Add-In framework's Record Context to pull the workspace data. This assumes that your workspace field is for an actual database object and not a faux field added through the the workspace designer. If it's not a database field, you can't get the field value. 

    An article with examples of accessing the record context: http://cxdeveloper.com/article/working-workspace-records

    You also use this record context to watch for field changes. Change events are exposed for most fields and can be subscribed to. Alternatively, workspace rules can watch for changes and fire an Add-In action on an Add-In instance.

    As for converting ID's to labels, you have two potential options:

    1. Use the OptList method from the Add-In Framework

    2. Use the SOAP API getValuesForNamedID() method to pre-fetch (and cache for the users session) a mapping of status id's to labels.

  • Andrew Rowse

    Have you looked at Oracle Integration Cloud. It probably would do what you want and is "inside" of the Oracle hosted infrastructure. I'm guessing that you are using CPM's to trigger the syncing of data. A word of warning; design for failure where the CPM either won't trigger, fail or be delayed for hours. Expect this to happen and incorporate it into your design. For us, this means running something on a schedule to poll for changes within a window that have not been synced (i.e. middleware). This is good practice regardless of the current fragility of CPMs.

    I don't know if Oracle Pro Services still can and will do engagements where they will use the restricted methods. You'd have to check with someone in PS or your sales rep about the feasibility. How it used to work was that the customization/integration/whatever was written by an Oracle Pro Services engineer. The design and code were reviewed by an architectural committee and an exception was granted to use the restricted methods. If approved, the method(s) were whitelisted for that site and specific files.

    IMHO, you are better off accepting middleware. Oracle Integration Cloud probably can do what you need. If you have more organizational flexibility, AWS and Azure have some really AWESOME serverless options (Azure Logic Apps + Functions and AWS Lambda + Step Flow).

  • Andrew Rowse

    There is no other way for you to do it yourself. I've been down this same path and hairpinning is the only option when it is available. Not being able to fetch fattach data from a script is one of my top 5 OSvC peeves. Oracle has worked so *** hard to block it yet most customers eventually find that they need to do it and go down the same path you are.

    There are restricted internal functions that can do this, but they are blacklisted from being used in any customer written scripts. Oracle Pro Services used to be able to whitelist usage of these functions, but this involved a PS engagement.

    What are you trying to do? There is likely a better architectural option or another approach. And at a certain point, you are better off leveraging some middleware.

  • Andrew Rowse

    It's likely blocked on the PCI pod because of default firewall rules. Have you submitted an SR to Oracle and asked that traffic be allowed? They will do this (usually) upon request.

  • Andrew Rowse

    SOAP API will fetch data, but it's not the right choice if you are trying to execute your orchestration logic via Connect PHP. It is possible to do this in connect php (with a few caveats and gotchas depending on if the PHP runtime is Customer Portal, Custom Script, CPM or other). If your execution of the orchestration process is outside of OSvC, such as from OIC or another orchestration tool, then the SOAP (or REST) API route is the correct answer and you should read no further.

    To do a file-grab from Connect PHP....

    Caveat 1: You have to authenticate as an agent to the Connect PHP API. Otherwise this won't work. This is often not ideal as there are not super secure ways to pass Agent Session ID or credentials to a script.

    Caveat 2: There is a "/tmp" folder available that the data can be downloaded to then read again using file_put_contents and file_get_contents php functions. I'm not 100% sure how this will behave when running from an async CPM.

    Caveat 3: Memory considerations for the process

    The code would look something like this (disclaimer... likely not 100% functional and needs some tweaks):

    <?php
    
    \load_curl();
    
    //Authenticate to Connect PHP with Agent Session or Agent Credentials.
    require_once (DOCROOT . '/include/services/AgentAuthenticator.phph');
    AgentAuthenticator::authenticateSessionID($_GET['agent_session']);
    
    foreach($incident->FileAttachments as $attachment)
    {
        /* @var $attachment \RightNow\Connect\v1_3\FileAttachment */
        $url = $attachment->getAdminURL();
        if(!$url) throw new \Exception("Couldn't find file attachment on Ticket");
    
        $ch = \curl_init();
        \curl_setopt($ch, CURLOPT_URL, $url);
        \curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
        \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $body = \curl_exec($ch);
        $curl_errno= curl_error($ch);
        \curl_close($ch);
    
        //Process the body
        if($body)
        {
            $filename = \sprintf("\tmp\tmp-%s", $attachment->ID);
            \file_put_contents($filename, $body);
            
            //Do Sync... via CURL
            
            //Clean up tmp file
            \unlink($filename);
    
        }
        else 
        {
            throw new \Exception("Error retrieving File Attachment: " . $curl_errno);
        }    
    }
    
  • Andrew Rowse

    You need to query via the object model (not the lower-level DB layout).

    Try something like this:

    SELECT count(ServiceSettings.SLAInstances.ID) 
    FROM Contact 
    WHERE ID = 1 
      AND ServiceSettings.SLAInstances.SLASet = 1 
      AND ServiceSettings.SLAInstances.StateOfSLA.LookupName IN ('Active', 'Inactive');
    

    This would give you a count of Active or Inactive (but not disabled) SLA's for the Contact (ID = 1) where the SLA Definition with ID = 1. It's also possible to query using the SLA name, but you seem to have the ID in your code.

    To get an idea of what's inside of the SLA Instances area of the object model, run this Query:

    DESCRIBE Contact.ServiceSettings.SLAInstances.SLAInstanceList;
    
  • Andrew Rowse
    <?php 
        $var = \RightNow\Utils\Config::getMessage(CUSTOM_MSG_PROMOTIONS_5);
        $varjs = \RightNow\Utils\Config::getMessageJS(CUSTOM_MSG_PROMOTIONS_5);
    ​?>
    
  • Andrew Rowse

    For anyone running across this thread, there is a new capability in the November 2016 release that I think solves this problem.

    A new method named "SendIncidentResponse" has been added to the SOAP API that allows for the programatic sending of Incident Responses.

    See: http://documentation.custhelp.com/euf/assets/devdocs/november2016/Connect_Web_Services_for_SOAP/Content/Web%20Service%20API/Operations/SendIncidentResponse.htm

    I'm not seeing a similar feature added to Connect PHP, but maybe this will be coming in the future (hint, hint).

  • Andrew Rowse

    Having IDE based code completion for ConnectPHP can really help with the learning curve. My company has a free tool out there than can generate PHPDocs for a site's ConnectPHP API that has pretty complete coverage. If you use an IDE that supports PHPDocs, such as NetBeans (and I think Eclipse), this can speed things up significantly.

    New version of the toolkit is available here: http://cxdeveloper.binpress.com/product/cxdev-toolbox/3941

    Details on the PHP Stub generator here: http://cxdeveloper.com/article/cxdev-toolbox-php-stub-generator

  • Andrew Rowse

    This answer is probably far too late for Nagesh's needs, but here is an example of an Incident update event notification. Note: YES, you can subscribe to Incident events. The documentation omits this but it is available for create, update and destroy operations.

    I was able to fetch this by registering an Event subscription and pointing it to Runscope. If you haven't used it, Runscope can act as a logging proxy where you can send requests (and optionally forward them to another system) and then access all of the traffic logs. It's a tool that can do so many things.

    <soapenv:Envelope
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:v1="urn:messages.ws.rightnow.com/v1_3"
      xmlns:v11="urn:base.ws.rightnow.com/v1_3">
      <soapenv:Header>
        <v1:ClientInfoHeader soapenv:mustUnderstand="0">
          <v1:AppID>Oracle Service Event Subscription</v1:AppID>
        </v1:ClientInfoHeader>
        <wsse:Security soapenv:mustUnderstand="1"
          xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
          xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
          <wsse:UsernameToken wsu:Id="UsernameToken-7ff92677f395298b">
            <wsse:Username>XXXXXXXXXXXX</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXXXXXXX</wsse:Password>
            <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">XXXXXXXXXXXXXXXXX</wsse:Nonce>
            <wsu:Created>2017-02-06T23:56:13Z</wsu:Created>
          </wsse:UsernameToken>
          <wsu:Timestamp wsu:Id="TS-7ff92677f395298b">
            <wsu:Created>2017-02-06T23:56:13Z</wsu:Created>
            <wsu:Expires>2017-02-07T00:01:13Z</wsu:Expires>
          </wsu:Timestamp>
        </wsse:Security>
      </soapenv:Header>
      <soapenv:Body>
        <v1:SubscriptionNotification>
          <v1:SubscriptionName>
            <v11:ID id="10001"/>
            <v11:Name>XXXXXXXXXXXXXXXXx</v11:Name>
          </v1:SubscriptionName>
          <v1:Timestamp>2017-02-06T23:56:13Z</v1:Timestamp>
          <v1:EventType>UPDATE</v1:EventType>
          <n0:TriggeringObject
            xmlns:n0="urn:messages.ws.rightnow.com/v1_3"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="n1:Incident"
            xmlns:n2="urn:base.ws.rightnow.com/v1_3"
            xmlns:n3="urn:generic.ws.rightnow.com/v1_3"
            xmlns:n1="urn:objects.ws.rightnow.com/v1_3">
            <n2:ID id="2376987"></n2:ID>
            <n2:LookupName>170206-000000</n2:LookupName>
            <n2:CreatedTime>2017-02-06T23:09:15.000Z</n2:CreatedTime>
            <n2:UpdatedTime>2017-02-06T23:56:13.000Z</n2:UpdatedTime>
            <n1:AssignedTo>
              <n1:Account>
                <n2:ID id="2821014"></n2:ID>
                <n2:Name>XXXXXXXXXX</n2:Name>
              </n1:Account>
              <n1:StaffGroup>
                <n2:ID id="100132"></n2:ID>
                <n2:Name>Consultant</n2:Name>
              </n1:StaffGroup>
            </n1:AssignedTo>
            <n1:CreatedByAccount>
              <n2:ID id="2821014"></n2:ID>
              <n2:Name>XXXXXXXXXXX</n2:Name>
            </n1:CreatedByAccount>
            <n1:CustomFields xsi:type="n3:GenericObject">
              <n3:ObjectType>
                <n3:Namespace></n3:Namespace>
                <n3:TypeName>IncidentCustomFields</n3:TypeName>
              </n3:ObjectType>
              <n3:GenericFields name="CO" dataType="OBJECT">
                <n3:DataValue>
                  <n3:ObjectValue xsi:type="n3:GenericObject">
                    <n3:ObjectType>
                      <n3:Namespace></n3:Namespace>
                      <n3:TypeName>IncidentCustomFieldsCO</n3:TypeName>
                    </n3:ObjectType>
                    <n3:GenericFields name="EntitlementID" dataType="NAMED_ID">
                      <n3:DataValue xsi:nil="true"></n3:DataValue>
                    </n3:GenericFields>
                  </n3:ObjectValue>
                </n3:DataValue>
              </n3:GenericFields>
              <n3:GenericFields name="c" dataType="OBJECT">
                <n3:DataValue>
                  <n3:ObjectValue xsi:type="n3:GenericObject">
                    <n3:ObjectType>
                      <n3:Namespace></n3:Namespace>
                      <n3:TypeName>IncidentCustomFieldsc</n3:TypeName>
                    </n3:ObjectType>
                    <n3:GenericFields name="contact_consumer" dataType="NAMED_ID">
                      <n3:DataValue>
                        <n3:NamedIDValue>
                          <n2:ID id="185"></n2:ID>
                          <n2:Name>Phone</n2:Name>
                        </n3:NamedIDValue>
                      </n3:DataValue>
                    </n3:GenericFields>
                    <n3:GenericFields name="product_desc" dataType="STRING">
                      <n3:DataValue xsi:nil="true"></n3:DataValue>
                    </n3:GenericFields>
                    <n3:GenericFields name="serial_num" dataType="STRING">
                      <n3:DataValue xsi:nil="true"></n3:DataValue>
                    </n3:GenericFields>
                    <n3:GenericFields name="date_problem" dataType="DATE">
                      <n3:DataValue xsi:nil="true"></n3:DataValue>
                    </n3:GenericFields>
                    <n3:GenericFields name="referred_by" dataType="NAMED_ID">
                      <n3:DataValue xsi:nil="true"></n3:DataValue>
                    </n3:GenericFields>
                    <n3:GenericFields name="answer_display" dataType="BOOLEAN">
                      <n3:DataValue>
                        <n3:BooleanValue>true</n3:BooleanValue>
                      </n3:DataValue>
                    </n3:GenericFields>
                    <n3:GenericFields name="int_status" dataType="NAMED_ID">
                      <n3:DataValue>
                        <n3:NamedIDValue>
                          <n2:ID id="200"></n2:ID>
                          <n2:Name>To Be Processed</n2:Name>
                        </n3:NamedIDValue>
                      </n3:DataValue>
                    </n3:GenericFields>
                  </n3:ObjectValue>
                </n3:DataValue>
              </n3:GenericFields>
            </n1:CustomFields>
            <n1:InitialResponseDueTime>2017-02-08T17:09:00.000Z</n1:InitialResponseDueTime>
            <n1:Interface>
              <n2:ID id="1"></n2:ID>
              <n2:Name>XXXXXXXXXX</n2:Name>
            </n1:Interface>
            <n1:Language>
              <n2:ID id="1"></n2:ID>
              <n2:Name>en_US</n2:Name>
            </n1:Language>
            <n1:Mailbox>
              <n2:ID id="4"></n2:ID>
              <n2:Name>XXXXXXXXXX@custhelp.com</n2:Name>
            </n1:Mailbox>
            <n1:MilestoneInstances>
              <n1:IncidentMilestoneInstanceList>
                <n1:ResolutionDueTime>2017-02-08T17:09:00.000Z</n1:ResolutionDueTime>
                <n1:Milestone>
                  <n2:ID id="1"></n2:ID>
                  <n2:Name>Resolution Due</n2:Name>
                </n1:Milestone>
              </n1:IncidentMilestoneInstanceList>
            </n1:MilestoneInstances>
            <n1:PrimaryContact>
              <n1:Contact>
                <n2:ID id="3509258"></n2:ID>
                <n2:Name>XXXXXXXXX</n2:Name>
              </n1:Contact>
            </n1:PrimaryContact>
            <n1:Product>
              <n2:ID id="829"></n2:ID>
              <n2:Name>Other</n2:Name>
            </n1:Product>
            <n1:ReferenceNumber>170206-000000</n1:ReferenceNumber>
            <n1:ResponseEmailAddressType>
              <n2:ID id="0"></n2:ID>
              <n2:Name>Email - Primary</n2:Name>
            </n1:ResponseEmailAddressType>
            <n1:Source>
              <n2:ID id="1001"></n2:ID>
              <n2:Name>Case Editor</n2:Name>
              <n2:Parents xsi:type="n2:NamedReadOnlyID">
                <n2:ID id="32002"></n2:ID>
                <n2:Name>CX Console</n2:Name>
              </n2:Parents>
            </n1:Source>
            <n1:StatusWithType>
              <n1:Status>
                <n2:ID id="1"></n2:ID>
                <n2:Name>Open</n2:Name>
              </n1:Status>
              <n1:StatusType>
                <n2:ID id="1"></n2:ID>
                <n2:Name>Unresolved</n2:Name>
              </n1:StatusType>
            </n1:StatusWithType>
            <n1:Subject>Example of Event Sub Output</n1:Subject>
          </n0:TriggeringObject>
        </v1:SubscriptionNotification>
      </soapenv:Body>
    </soapenv:Envelope>
    
  • Andrew Rowse

    It's been a while since this original post, but I have an additional tip for anyone running across this post.

    The line endings used in the XML definition files are VERY important. They must be CRLF line endings otherwise OSC will throw the error that Jared saw.

    It is common for us to put our exports into a code repository, in our case git. In particular, git will change line endings to be compatible with your host system unless a .gitattributes file is in present that instructs git to use a specific line ending for certain file extensions.

  • Andrew Rowse

    Actually, to expand upon my last posting, the issue doesn't seem to the be ChatLaunchButton v1.3 widget. The problem still persists when using the v1.4 widget with the CP 3.3 framework. There likely is some other issue where widget YUI dependency of inherited parent widgets is not working except in Reference mode. 

    We are not going to dig any further and just simply create a blank widget that loads anim-scroll and add it to the Chat Launch page.

     

  • Andrew Rowse

    The issue is in the Chat Launch button pre v1.4 version of the widget. The Chat Launch button will call this.Y.Anim function whenever the Error container is not visible on the page. That is why you will sometimes see this issue (in a popup) and not when viewed as a full webpage. If the page is big enough to have the Error div container visible, the this.Y.Anim function is never called and the page works as normal. If instead you scroll really far down the page to ensure the Error div is offscreen, you will experience the issue.

    I think this is a bug in the Chat Launch button's info.yaml file that Oracle fixed with version 1.4, but I'm not 100% sure yet. I know that version 1.3 in our page doesn't work and has a suspect info.yaml file while viewing the page in Reference Mode (which uses the latest version of widgets, in our case v1.4 for Chat Launch Button) works as expected.

    Typically a info.yaml file will list any non-core YUI modules that are needed for a widget to function. The FormSubmit widget explicitly specifies the "anim-scroll" module.

    For example:

    version: "1.0"
    requires:
      framework: ["3.2", "3.3"]
      jsModule: [standard, mobile]
      yui: [anim-scroll]
    

    While the ChatLaunchButton widget extends the FormSubmit widget, it does not explicitly specify "anim-scroll" as a dependency. I would expect that any parent-widget dependencies would implicitly be included, but it seems that this ins't the case. 

    Chat Launch v1.3 YAML

    version: "1.3.1"
    requires:
      framework: ["3.3"]
      jsModule: [standard, mobile]
    

    In summary, the fix for this should be either to start using the v1.4.x ChatLaunchButton OR if you are using a customized Chat Launch Button, include "anim-scroll" in your info.yaml file.

     

  • Andrew Rowse

    In order to correct an error in my last post, the hash algorithm is actually SHA1 with some specific settings. I looked a little further and it turns out the Agent Console performs the XML generation and hashing, and not something done on the server. NOTE: The Console uses .NET with DLL's, for those out there who know why that is important.

    I was successful last week with modifying, signing and re-importing a Guided Assistance export.

    In summary, it can be done and all of the info on how is accessible.

  • Andrew Rowse

    At the bottom of most exported XML configuration file from the console is a "Signature". This signature is a calculated hash of the exported document's contents. 

    For example, here is a signature at the end of a Guided Assistance export.

    </CallUrlNode></ResponseNode></QuestionNode></RootNode>
    <!--dzQS/iueTBqxMw1sdyq3EOzjdQuULfjz-->
    

    When you go to import a file, RightNow will take the contents of the document and re-calculate the hash. If the calculated hash doesn't match the hash declared at the end of the file, you will see the "not a valid file exported" error.

    The hash was a proprietary algorithm that has never been published and can't really be re-produced. I'll call this algorithm "MSU32", which will be entertaining to at least one person still at Oracle. It looks like Reports might use a slightly different format, but the concept is the same. 

    On my site, the Report export signature looks like this:

    </analytics_core>
    Version: 
    vBdrCfXnOgg1FBna/jji4wKeSkRQz1vEBWVr
    

    While this signature prevents someone from importing an incomplete, incorrect or corrupt file, it prevents an experienced user from doing a find/replace or other XML modification by hand. My specific use case is with a Guided Assistance where I need to do a find and replace on some content... which brings me to the forums today.

    As for why your unzipped Analytics report won't import; my guesses would be

    1. You didn't actually do a full unzip and just used the winzip (or whatever tool you are using) preview ability to select the .xml file)
    2. The unzip modified the file in some way that made the XML signature not valid
    3. The export was created from a previous version of RightNow and you've subsequently upgraded to a version where the signature validation was changed by Oracle

    I do know that while I was at RightNow PS I had a command line utility that did allow me (as a company employee) to re-create that hash; it might be worth reaching out to Oracle Customer Care to see if they can help. You'll probably have to get to a VERY high support rep, or they will just say "no" and encourage you to buy Oracle Pro Service hours.

    It's probably just faster to re-create the Report :P