Forum Discussion

isxtn's avatar
isxtn
Advisor
2 years ago

As far as anyone knows is there a way to transfer posts, etc., from PROD to STAGE

Has anyone come up with a script to do this? Querying Production and Posting to stage with either curl or the API?

It would just be nice to be able to populate more and more realistic content on the Stage instance.

Or even just to post articles programmatically?

There's the REST API

 

https://developer.khoros.com/khoroscommunitydevdocs/reference/rest

 

But I don't see where the body of the messagePostBody goes. 

Example: POST (Create):

 

<#assign subject = http.request.parameters.name.get("subject", "") />
<#if subject?length gt 0>
  <#-- Build the requestBody parameter and assign it to a variable-->
	<#assign messagePostBody = {
	  "type": "message",
	  "subject": subject,
	  "board": { "type": "board", "id": "Otis" }
	} />

  <#-- Make your REST call -->
	<#assign resp = rest("2.0", "/messages", "POST", messagePostBody) />
	${apiv2.toJson(resp)}
<#else>
 {
   "status": "error",
   "message": "no subject parameter passed."
 }
</#if>

 

Do I just add a "body" member property to that messagePostBody with whatever the body of the message is? Or is there some esoteric way of doing it?

For the example given, PUT:

 

<#assign messagePostBody = {
 "type": "message",
 "id": "34",
 "subject": "How do I post a REST API message?"
} />

 

There's a subject, a type, an id, but no body. How do I add body content?  

Or do I have to use cURL as noted here in "recipes"? Does one need to authenticate using cURL? Can it be done within components? A lot of this seems vague. There's not a lot of context I can find. Most recipes have instructions for what to do with the ingredients ...

 

https://developer.khoros.com/khoroscommunitydevdocs/recipes/create-a-new-message-with-products

 

Do we run this in Freemarker? How? Do we create a component? Do we use authentication? How? In a terminal? With a fox? In a box? With green eggs and ham? 😉

This would be very helpful information. These "recipes" assume a lot of knowledge about how to use cURL within the Khoros Community that isn't provided. I would love to run this in a component. But there's no information nearby that explains that. Please clarify? A lot? Complete examples in the context of the Community product would make all the difference. 

 

curl --request POST \
     --url https://community-domain/api/2.0/messages \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/json'
 {
    "data": {
        "type": "message",
        "board": {
            "id": "runningShoes"
        },
        "subject": "Alternative to Nike Revolution Running Shoes",
        "body": "I have the Nike Revolution Shoes and got the Reebok Protonium shoes, but it feels very stiff while running - not much comfort/cushion while running and uncomfortable. I originally switched to Saucony because it was very comfortable and lightweight. suggestions for Saucony running shoes for women? I run only about 10mi/wk. <li-image id=\"336i254DB2339\"/>",
        "tags": {
            "items": [
                {
                    "text": "trail running"
                },
                {
                    "text": "running shoes"
                }
            ]
        },
        "labels": {
            "items": [
                {
                    "id": "RunningShoes",
                    "text": "Running Shoes"
                },
                {
                    "id": "Suggestions",
                    "text": "Suggestions"
                }
            ]
        },
        "products": {
            "items": [
                {
                    "id": "54"
                },
                {
                    "id": "56"
                }
            ],
            "list_item_type": "product"
        }
    }
}

 

Thanks, just rather unclear on the documentation around this.

  • I think maybe we're misunderstanding each other a little. When you say embed the cURL call, cURL is how you would make an API call from the command line. You wouldn't use cURL inside a Khoros component. You would use restBuilder or rest or restadmin to make the same API call with freemarker. Or if you were using JavaScript, you would use fetch maybe or XMLHttpRequest. So, I think when you say "cURL call" you are referring to the same thing I mean when I say "API call".

    The examples I shared are making those calls embedded in a component.

    I will take the cURL example from here Create message (khoros.com) and rewrite it using restBuilder so you can see what I mean.

    So here is an example with cURL from the link above.

     

    curl -L -X POST 'https://[COMMUNITY-DOMAIN]/api/2.0/messages/' \
    -H 'Content-Type: application/json' \
    -H 'Li-Api-Session-Key: [SESSION-KEY]' \
    --data-raw '{
        "data": {
            "type": "message",
            "board": {
                "id": "runningShoes"
            },
            "subject": "Alternative to Saucony Ride 10 GTX",
            "body": "I have the Saucony Ride 9 and got the Saucony Ride 10 GTX, but it feels very stiff while running - not much comfort/cushion while running and uncomfortable. I originally switched to Saucony because it was very comfortable and lightweight. suggestions for Saucony running shoes for women? I run only about 10mi/wk. <li-image id=\"336i254DB2339\"/>",
            "tags": {
                "items": [
                    {
                        "text": "trail running"
                    },
                    {
                        "text": "running shoes"
                    }
                ]
            },
            "labels": {
                "items": [
                    {
                        "id": "womensRunningShoes",
                        "text": "Womens Running Shoes"
                    },
                    {
                        "id": "recommendations",
                        "text": "Recommendations"
                    }
                ]
            },
            "products": {
                "items": [
                    {
                        "id": "sauconyRide10gtx"
                    },
                    {
                        "id": "sauconyFreedomIso"
                    }
                ],
                "list_item_type": "product"
            }
        }
    }'

     

     

    Here is my (untested) attempt to rewrite with restBuilder which you could include in a component.

     

    <#assign messagePostCall = restBuilder()
       .method("POST")
       .path("/messages")
       .version("v2")
       .header("Content-Type","application/json")
       .header("Li-Api-Session-Key","[SESSION-KEY]")
       .body({
            "type": "message",
            "board": {
                "id": "runningShoes"
            },
            "subject": "Alternative to Saucony Ride 10 GTX",
            "body": "I have the Saucony Ride 9 and got the Saucony Ride 10 GTX, but it feels very stiff while running - not much comfort/cushion while running and uncomfortable. I originally switched to Saucony because it was very comfortable and lightweight. suggestions for Saucony running shoes for women? I run only about 10mi/wk. <li-image id=\"336i254DB2339\"/>",
            "tags": {
                "items": [
                    {
                        "text": "trail running"
                    },
                    {
                        "text": "running shoes"
                    }
                ]
            },
            "labels": {
                "items": [
                    {
                        "id": "womensRunningShoes",
                        "text": "Womens Running Shoes"
                    },
                    {
                        "id": "recommendations",
                        "text": "Recommendations"
                    }
                ]
            },
            "products": {
                "items": [
                    {
                        "id": "sauconyRide10gtx"
                    },
                    {
                        "id": "sauconyFreedomIso"
                    }
                ],
                "list_item_type": "product"
            }
        }) />
    
    <#assign resp = messagePostCall.call() />

     

     

    So, to compare the two:

    • First line from cURL lists "POST" as the method which goes in "method" on restbuilder.
    • Also from the first line of the cURL example is the URL 'https://[COMMUNITY-DOMAIN]/api/2.0/messages/'. Because you are inside a Khoros component you don't need to include the community domain. You also don't need to include api/2.0 as that is covered by the ".version" in restBuilder. So, all you need from the URL is the /messages which goes in the "path" on restBuilder.
    • Next the content type and session key would go under .header in restBuilder.
    • Lastly the "data" from cURL would be the .body in restBuilder.

    Some additional notes. You don't actually need the .version("v2") here because v2 is the default. You really only need it if you are using API v1.

    Also, because you are in a component, you don't really need a session key. If you are logged in Khoros knows who you are. I included it just so you could see everything from the cURL example.

    Finally, if you want to make this call with admin permissions, you would add ".admin(true)" to the restBuilder.

     

    Complete Example:

    As mentioned in a previous post I was working on a component to create an abuse report. This is very similar to posting a regular message and I can share it here. This has two parts. First, a custom endpoint created in studio which is where I use restBuilder to make the API call to create the abuse report. Second, a custom compnent which I added to the "Content Archived Page". This component calls the custom endpoint I made when a button is clicked and passes along the message id.

    Endpoint Code

    <#assign messageId = http.request.parameters.name.get("id", "") />
    <#assign path = "messages/" + messageId + "/abuse_reports" />
    
    <#if config.getString("phase", "prod") == "stage">
        <#assign body = "***UNARCHIVE REQUEST***  https://community.stage.ptc.com/t5/contentarchivals/viewpage/message-uid/" + messageId />
    <#else>
        <#assign body = "***UNARCHIVE REQUEST***  https://community.ptc.com/t5/contentarchivals/viewpage/message-uid/" + messageId />
    </#if>
    
    <#assign unarchiveCall = restBuilder()
       .method("POST")
       .path("${path}")
       .body({
            "data":{
                "type":"abuse_report",
                "body":"${body}"
            }
        })
       .admin(true) />
    <#assign resp = unarchiveCall.call() />
    
    {
        "http_code":"${resp.http_code}",
        "status" : "${resp.status}"
    }

     

    Component Code

    <div id="unarchive-button-container-ak">
        <span class="lia-button lia-button-primary" id="unarchive-button-ak">Request Unarchive</span>
    </div>
    
    <div id="result-message-ak"></div>
    
    <style>
        div#result-message-ak {
            margin-top: 20px;
        }
    
        #result-message-ak p {
            padding: 10px;
            font-size: 16px;
            text-align: center;
        }
    
        div#unarchive-button-container-ak {
            width: 194px;
            margin-left: auto;
            margin-right: auto;
        }
    </style>
    
    <@liaAddScript>
        ;(function($) {
    
            const unarchiveButton = document.getElementById('unarchive-button-ak');
    
            unarchiveButton.addEventListener("click", () => {
                const loc = window.location.pathname;
                const locArray = loc.split("/");
                const messageID = locArray[locArray.length - 1];
                console.log(messageID);
                const resultMessage = document.getElementById('result-message-ak');
    
                const url = '/sejnu66972/plugins/custom/ptc/ptc1/unarchive?id=' + messageID
    
                async function unarchiveRequest(url) {
                    const response = await fetch(url, {
                        method: "POST",
                        headers: {
                            "Content-Type": "application/json"
                        }
                    })
    
                    const result = await response.json();
    
                    console.log(result);
    
                    if (result.status == "success") {
                        resultMessage.innerHTML = "<p>Your request was successfully sent. You should hear from a moderator within 24 business hours.</p>"
                    } else {
                        resultMessage.innerHTML = "<p>There was a problem sending your request. Please try again later or contact the community team directly at community@ptc.com</p>"
                    }
                }
    
                unarchiveRequest(url);
            })
    
        })(LITHIUM.jQuery);
    </@liaAddScript>

     

    Sorry for the wall of text. Again, hopefully that's not too much info. Also, sorry there are no comments in my code let me know if you have any questions.

13 Replies

  • isxtn's avatar
    isxtn
    Advisor
    2 years ago

    Oh, I see. By creating it in Community, you bypass the SSO. Clever. 

    What is the PTC in 

    Andrew Kenefick
    PTC

    ?

    I assume you've been working on the Community a long time. A wealth of knowledge. Thank you.

  • Akenefick's avatar
    Akenefick
    Genius
    2 years ago

    PTC makes engineering and manufacturing software. Digital Transforms Physical | PTC I'm the operations manager for their support community. I've been working on the community for about 2 and a half years. When I started, I was also not an experienced developer, so it was an uphill battle for me, but I've learned a lot and got a lot of help here on the developer board either asking questions or reading other people's questions and answers.

  • isxtn's avatar
    isxtn
    Advisor
    2 years ago

    Nice. That's a good learning curve! I've been working with Khoros Community for a while but there are so many esoteric bits of knowledge that are either not documented or documented poorly (or that I can't remember or where to find it) that I wind up asking a lot of questions on here. Great resource. I'm really impressed with the level of knowledge on this you've achieved after just a couple of years! Especially not being a developer!