Move an article from one instance to another.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-30-2023 12:39 PM
Is there a quick method to move an article from one instance to another?
Our use case is to keep all the bookmark links/
attachments, images and links to other articles in-tacked and directing to the new instance as we are trying to refresh the content of some articles.
Example we have on production Article A version 23
On our training instance we have Article A but version 10 (so it is outdated)
We want copy the production Article A version 23 over keeping all bookmark links
attachments, images and links to other articles in tacked and directing to the new instance
Thanks
Juliet
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-30-2023 12:43 PM
I think there are some limitations to this functionally...but I don't think they will affect you in your use case, since your intent is to move the article downward to a subproduction instance. You can export as xml from production and import as xml to training (subprod):
Export and import XML files (servicenow.com)
Export/Import XML of a KB Article containing images does not show the images in the target instance ...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Hi,
The second link is broken. Is there some other document on the same topic i can refer to?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago
Second link works for me. Here is the article copy/pasted:
Description
KB Articles having images attached and included in the page content, when XML-exported, will contain the image reference to the original attachment in the source instance. As a consequence, when imported in another instance, the target KB Article will not show the expected embedded image, but only an attachment. The source image html field does not reflect the new attachment sys_id being created as the new target.
Steps to Reproduce
1. Verify the source instance has Knowledge Management Advanced installed.
2. Choose a non published KB Article.
3. Insert an image in the HTML content field, selecting the image type as "Attachment".
4. Save the record.
5. Click on "View Article" and observe the image is correctly displayed and shown as an attachment.
6. Click "Inspect" on the added image and take note of the src value (i.e. src="sys_attachment.do?sys_id=<sys_id>"), showing the sys_id of the sys_attachment record. Take note of that sys_id as well.
7. Publish the Knowledge Article.
8. Checkout the KB Article.
9. A new record is created with a different sys_id for the same KB number, as a consequence two new attachment records (two new sys_ids) are created in the sys_attachment table to refer to the new KB record.
10. The image source reference field in the kb_knowledge is still pointing to the old sys_attachment record (check this repeating step 6).
11. Publish the KB article again to make it available.
12. Export to XML the Published version of the KB article. The exported XML will have the image src reference pointing to the old sys_attachment record.
13. Import the XML into a target instance. The imported article is showing the attachments but cannot display the image as this is pointing to a non-existing sys_attachment record in the target instance.
Workaround
After carefully considering the severity and frequency of the issue, and the cost and risk of attempting a fix, it has been decided to not address this issue in any current or near future releases. We do not make these decisions lightly, and we apologize for any inconvenience.
Related Problem: PRB1311273
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 weeks ago - last edited 2 weeks ago
Hello @Manas4
You need to ensure the Knowledge Base under which KB articles will be copied from source instance and production / destination instance have same sys_id as well as the basic authentication user is available in both the instances (if not using OAuth).
You can use below working code:
try {
// source and destination KB Article Knowledgebase value
var knowledgeBaseSysID = gs.getProperty('knowlegebase.name'); //You can also hardcode the Knowledge base sys_id here or create a system property in the sys_properties table
var sourceFilterShortDesc = 'Dummy'; // <-- KB Article filter condition based upon Short Description. Please change as needed.
// If you ONLY want exact matches, leave this false.
var allowContainsSimilarity = false;
//1. Retrieve all Source Instance's KB articles based upon encoded query which needs to be copied to destination instance
var kbArticlesList = [];
var kbGR = new GlideRecord('kb_knowledge');
var enc = 'kb_knowledge_base=' + knowledgeBaseSysID + '^active=true^workflow_state=published';
if (sourceFilterShortDesc) {
enc = 'short_descriptionLIKE' + sourceFilterShortDesc + '^' + enc;
}
kbGR.addEncodedQuery(enc);
kbGR.query();
while (kbGR.next()) {
kbArticlesList.push({
short_description: kbGR.getValue('short_description'),
text: kbGR.getValue('text'),
kb_knowledge_base: kbGR.getValue('kb_knowledge_base'),
workflow_state: 'published',
original_sys_id: kbGR.getValue('sys_id') // used for attachment copy
});
}
//2. Duplicate Article Creation Avoidance: Fetch target instance similar KB Articles
var targetShortDescSet = {}; //
var getKBRequest = new sn_ws.RESTMessageV2('Copy KB Articles To Prod', 'GET KB Article Prod'); //Outbound Rest Message with GET HTTP Method https://prod.service-now.com/api/now/table/kb_knowledge
getKBRequest.setStringParameter('knowledge_base', knowledgeBaseSysID);
var getKBResponse = getKBRequest.execute();
var getStatus = getKBResponse.getStatusCode();
var body = getKBResponse.getBody();
// Only treat as "not found" when the call is successful however if article body is empty, then no result. If the call fails, stop to avoid duplicate creation.
var getObj = {};
if (getStatus === 200 && body) {
try {
getObj = JSON.parse(body) || {};
} catch (e) {
gs.error('Target GET response JSON parse failed: ' + e.message);
getObj = {};
}
} else if (getStatus === 200) {
// 200 but empty article body, so treating it as no result found
getObj = {};
} else {
gs.error('Target GET failed. Status=' + getStatus + ' Body=' + body);
throw new Error('Target GET failed. Aborting to avoid duplicate creation.');
}
var targetResults = (getObj && Array.isArray(getObj.result)) ? getObj.result : [];
// Build lookup set of target short_descriptions
for (var t = 0; t < targetResults.length; t++) {
var tsd = targetResults[t] && targetResults[t].short_description;
if (tsd) {
var tsdKey = ('' + tsd).replace(/\s+/g, ' ').trim().toLowerCase();
targetShortDescSet[tsdKey] = true;
}
}
// 3. Identify "missing" KB articles on target ---
var missingList = [];
for (var i = 0; i < kbArticlesList.length; i++) {
var srcSdRaw = kbArticlesList[i].short_description;
var srcSd = ('' + (srcSdRaw || '')).replace(/\s+/g, ' ').trim().toLowerCase();
var found = false;
if (!allowContainsSimilarity) {
found = !!targetShortDescSet[srcSd];
} else {
if (targetShortDescSet[srcSd]) {
found = true;
} else {
for (var k in targetShortDescSet) {
if (k && k.indexOf(srcSd) > -1) {
found = true;
break;
}
}
}
}
if (!found)
missingList.push(kbArticlesList[i]);
}
// If target returned ZERO KBs for this base, this will create everything.
gs.info('Source articles found: ' + kbArticlesList.length +
', Target articles found: ' + targetResults.length +
', Missing to create: ' + missingList.length);
// 4. Create missing KBs + copy attachments + fix attachment sys_id references
for (var j = 0; j < missingList.length; j++) {
var newArticle = missingList[j];
var createPayload = {
short_description: newArticle.short_description,
text: newArticle.text,
kb_knowledge_base: newArticle.kb_knowledge_base,
workflow_state: newArticle.workflow_state
};
// Create article via REST API in target
var createKBRequest = new sn_ws.RESTMessageV2('Copy KB Articles To Prod','POST Create KB Article Prod'); //Outbound Rest Message POST HTTP Method https://prod.service-now.com/api/now/table/kb_knowledge
createKBRequest.setRequestHeader('Accept', 'application/json');
createKBRequest.setRequestHeader('Content-Type', 'application/json');
createKBRequest.setRequestBody(JSON.stringify(createPayload));
var createKBResponse = createKBRequest.execute();
var createBody = createKBResponse.getBody();
var createStatus = createKBResponse.getStatusCode();
if ((createStatus == 201 || createStatus == 200) && createBody) {
var parsedCreate = {};
try {
parsedCreate = JSON.parse(createBody);
} catch (pe) {
gs.error('KB created but response JSON parse failed. Body=' + createBody);
continue;
}
var newKBID = parsedCreate && parsedCreate.result && parsedCreate.result.sys_id;
if (!newKBID) {
gs.error('KB created but sys_id missing. Body=' + createBody);
continue;
}
// Copy attachments from original article to new article
var attachmentGR = new GlideRecord('sys_attachment');
attachmentGR.addQuery('table_name', 'kb_knowledge');
attachmentGR.addQuery('table_sys_id', newArticle.original_sys_id);
attachmentGR.query();
var oldToNewAttachmentMap = {};
var newText = newArticle.text || '';
while (attachmentGR.next()) {
var fileName = attachmentGR.getValue('file_name');
var oldSysId = attachmentGR.getValue('sys_id');
try {
var uploadRequest = new sn_ws.RESTMessageV2('Copy KB Articles To Prod','Upload Article Attachment Prod'); // Outbound Rest Message POST HTTP Method https://production.service-now.com/api/now/attachment/file
uploadRequest.setQueryParameter('table_name', 'kb_knowledge');
uploadRequest.setQueryParameter('table_sys_id', newKBID);
uploadRequest.setQueryParameter('file_name', fileName);
uploadRequest.setRequestHeader('Accept', 'application/json');
uploadRequest.setRequestHeader('Content-Type', attachmentGR.getValue('content_type'));
uploadRequest.setRequestBodyFromAttachment(attachmentGR.getValue('sys_id'));
var uploadResponse = uploadRequest.execute();
var uploadStatus = uploadResponse.getStatusCode();
if (uploadStatus == 201) {
var uploadObj = {};
try {
uploadObj = JSON.parse(uploadResponse.getBody());
} catch (upe) {
gs.error('Upload succeeded but response JSON parse failed. Body=' + uploadResponse.getBody());
uploadObj = {};
}
var newSysId = uploadObj && uploadObj.result && uploadObj.result.sys_id;
if (newSysId) {
oldToNewAttachmentMap[oldSysId] = newSysId;
gs.info('Uploaded attachment: ' + fileName + ' | New sys_id: ' + newSysId);
} else {
gs.error('Upload succeeded but sys_id missing. Body=' + uploadResponse.getBody());
}
} else {
gs.error('Upload failed: ' + fileName + ' | Status=' + uploadStatus +
' | Body=' + uploadResponse.getBody());
}
} catch (ex1) {
gs.error('Attachment upload exception for ' + fileName + ': ' + ex1.message);
}
}
// Replace old attachment sys_ids in the HTML text with new ones
for (var oldId in oldToNewAttachmentMap) {
var newId = oldToNewAttachmentMap[oldId];
newText = newText.replace(new RegExp(oldId, 'g'), newId);
}
// Update the KB text in target with corrected attachment references
try {
var updateTextRequest = new sn_ws.RESTMessageV2('Copy KB Articles To Prod','Update Article Prod'); // Outbound Rest Message PUT HTTP Method https://production.service-now.com/api/now/table/kb_knowledge/${sys_id}
updateTextRequest.setRequestHeader('Accept', 'application/json');
updateTextRequest.setRequestHeader('Content-Type', 'application/json');
updateTextRequest.setStringParameterNoEscape('sys_id', newKBID);
updateTextRequest.setRequestBody(JSON.stringify({
text: newText
}));
var updateResp = updateTextRequest.execute();
var updateStatus = updateResp.getStatusCode();
if (updateStatus === 200) {
gs.info('Article created and updated successfully in target (KB: ' + newKBID + ')');
} else {
gs.error('Failed to update article text (KB: ' + newKBID + ') | Status=' +
updateStatus + ' | Body=' + updateResp.getBody());
}
} catch (ex2) {
gs.error('Exception while updating KB text (KB: ' + newKBID + '): ' + ex2.message);
}
} else {
gs.error('KB creation failed for "' + newArticle.short_description + '" | Status=' +
createStatus + ' | Body=' + createBody);
}
}
} catch (ex) {
gs.error('Script failed: ' + ex.message);
}
Hope that helps!
