Public
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
New Commentator

Can we bulk edit labels on Idea Exchange threads?

We're changing the labels on our Ideas Exchange, and would like to find a way to bulk edit the already existing threads to the new labels.

For example, there are a lot of threads that have the label "Company." This label will be changed to "Companies." Is there a way to edit all of the threads that have the "Company" labels to be "Companies"? 

thanks!

9 Replies 9
Highlighted
Khoros Staff

You can edit labels in Community Admin>Features>Labels>Edit Labels.

Changes made here will update all usage of the label across the community. It can take up to 10 minutes to see all the UI update to reflect this change.

This feature is only available at the community level and can not be used to make changes on a specific node only.

Highlighted

Hi Pam!

Thanks for that! Do you know if there's a way to bulk edit the labels that are on the already existing threads?

For example, there are a bunch of threads with the label "dog". If I change the label "dog" to "cat" in the settings, is there a way to edit all the threads that had the "dog" label to now have the "cat" label?

Thanks!

Highlighted

Changing the label in admin should change the usage of label everywhere on the community. If you change dog to cat, then it will be updated in all existing threads that use it. 

Hope this helps.

Highlighted
Honored Contributor

@JoeMayall  if you want to change labels just in a specific idea exchange (or forum, or tkb, or blog) you do that using APIs.  I have a script I use quite often for bulk changes to labels in just one node. 

--
Community manager in the Micro Focus Community. My computer always used to beat me at chess, but it is no match for me now I changed the competition to kick boxing.
Highlighted

Hi! Is there anywhere I can learn more about this?

Highlighted
Occasional Contributor

@kgroneman 

Can you send me that script? That would be very nice!

Highlighted
Honored Contributor

Message me your email address and I'll send you the script or I've included the code at the bottom of this post.  FYI since the 20.5 update, the script has had problems running and I've got a support case open on it since it is a Khoros created script.  They're looking into it.   If you wanna try it, here you go go. 

The script requires Ruby 1,9+ and Curl be installed on your machine.

1. Make sure restapi.enableRefererCheck set to false on the community instance you want to run the script against. Without this check being disabled, you can't authenticate the script using cookie from another session. This can be requested through support temporarily (until the next restart of the instance) by explicitly requesting VAR change (in memory) and not config change. Config change would make the setting disabled permanently or until the config is updated again.

2. Login to your community with an Admin account

3. Copy the session ID from the 'LiSESSIONID' cookie

4. Run reports and/or browse to the topics you want to add or remove labels (or obtain the topic IDs and labels using other means)

5. Prepare the CSV file with the topic ID, action and label - each row will contain one community root (topic) message ID, the action type and the label, separated by commas. For example:

      1604938,ADD,label1
      1609722,REMOVE,label2

6. Create a command line with this type of format.  I keep them in a text file.   The benefit of doing that is that if something goes wrong, you can easily switch things around and reverse the script to undo any changes you make:  

***Sample Command line:

-------always the same---| URL OF THE COMMUNITY----------|----| Lithium Session ID cookie: LiSESSIONID | Input File(and dir) | Output log File and Directory

7. Run the script against the instance from CMD like these examples formatted like the above:

Copy this code to a whatever.rb file:

# encoding: UTF-8

require 'rexml/document'
require 'logger'
require 'time'

# Ruby script for adding and removing labels inside messages
# Requirements: Ruby, Curl, restapi.enableRefererCheck set to false on the Lithium instance (in order to authenticate with another session ID)
# Usage: ruby update-labels.rb -h <hostname> -s <session ID> <CSV-filename> [> <logfile>]
# Tested with ruby 1.9.3p551(centos),2.2.6p396(win) and curl 7.39.0(centos),7.50.0(win)
# Author: Lithium migration team, Boyana Dobrinska (boyanna.dobrinska@lithium.com)

$log = Logger.new(STDOUT)

#function to output log data on both screen and file if redirected by command line
def echo(output_text, fileonly=0)
	if $stdout.tty? && (fileonly==1) then return end
	$log.info output_text
	if !$stdout.tty? && (fileonly!=1)	then
		$stderr.puts output_text
	end
end

$hostname = "" #full instance hostname URL eg. http://migration3.stage.lithium.com/
$sessionid = "" #Lithium cookie session ID
$filename = "" #CSV filename with the source, target node movement pairs

#checking command line agruments
ARGV.each do|a|
	if $hostname == "?" then
		$hostname = a
		next
	end
	if $sessionid == "?" then
		$sessionid = a
		next
	end
	if a == "-h" then
		$hostname = "?"
	elsif a == "-s" then
		$sessionid = "?"
	else
		$filename = a.encode('utf-8','windows-1252')
	end
end

if $filename.empty? || $hostname.length<4 || $sessionid.length<4 then
	$stderr.puts "Usage: ruby #{$PROGRAM_NAME} -h <hostname> -s <session ID> <CSV-filename> [> <logfile>]"
	$stderr.puts "Example: ruby #{$PROGRAM_NAME} -h http://migration3.stage.lithium.com/ -s E5F00635A34824446A2854A9D6B1D117 update-labels-test.csv > todays-test.log"
	exit
	end

#creating the rest urls from the hostname url
REST_URL = "#{$hostname}#{$hostname[-1]!="/" ? "/" : ""}restapi/vc" #ADDING THE REST PART TO THE BASE URL

count_actions = 0
count_errors = 0
start_time = Time.now

begin

echo "STARTING THE PROCESS"
echo "*** VERBOSE MODE *** NO CHANGES WILL BE WRITTEN TO THE COMMUNITY" if $verbose == 1
echo "   INSTANCE: #{$hostname}"
echo "  SESSIONID: #{$sessionid}"
echo "SOURCE FILE: #{$filename}"

#check file exists
if !File.exist?($filename) then
	echo "WARN: FILE '#{$filename}' DOESN'T EXIST."
	count_errors += 1
	exit
end

#check for authenticated admin
restcall="curl -s -S --retry 15 -k " \
+"	--cookie \"LiSESSIONID=#{$sessionid}\" " \
+" #{REST_URL}/authentication/sessions/current/user/roles"
#puts restcall
curl = %x[#{restcall}]
#puts curl
if $?.exitstatus !=0 then
	echo "WARN: HTTP communication failed for rest call: #{restcall}. Exit status: #{$?.exitstatus}."
	count_errors += 1
	exit
end
begin
	doc = REXML::Document.new(curl)
rescue REXML::ParseException => msg
	echo "WARN: XML Parsing Failed: #{msg.message} (Line:#{msg.backtrace.inspect.match(/(.rb:)(\d+):/)[2]})"
	echo "WARN: XML for parsing:\n#{curl}"
	count_errors += 1
	exit
end
if doc.elements["response"].nil? || (doc.elements["response"].attributes["status"] != "success") then
	echo "WARN: REST call failed."
	echo "WARN: REST response:\n#{curl}"
	count_errors += 1
	exit	
end
if !/<name type="string">Administrator<\/name>/.match(curl)
	echo "WARN: The user doesn't have the Administrator role. Exiting."
	count_errors += 1
	exit	
end

echo "Authentication success."

REGPAT = /^\s*([0-9]+?)\s*,\s*(ADD|REMOVE)\s*,\s*([^,]+)/i #regular expression to match a proper CSV file record

count_lines = 0

#reading the csv
file = File.new($filename, "r:UTF-8")
while (line = file.gets)
	count_lines+=1
	
	if match = line.match(REGPAT) then
		topicid, action, label = match.captures
	else
		echo "WARN: LINE NOT MATCHING REGEXP: line #{count_lines} content: #{line}"
		break if count_lines > 1 #only one bad line is allowed, usually the last line in the file may be empty
		next
	end

	topicid = topicid.strip
	action = action.strip.downcase
	label = label.strip
	echo "line: #{count_lines}, topic '#{topicid}': #{action} '#{label}'"

	#check the message
	restcall="curl -s -S --retry 15 -k " \
	+"	--cookie \"LiSESSIONID=#{$sessionid}\" "\
	+"	#{REST_URL}/messages/id/#{topicid}"
	#echo restcall
	curl = %x[#{restcall}]
	#echo curl
	if $?.exitstatus !=0 then
		echo "WARN: HTTP communication failed for message #{rootmsg}. Rest call: #{restcall}. Exit status: #{$?.exitstatus}."
		count_errors += 1
		next #change to exit if you need hard stop on such error
	end
	
	begin
		doc = REXML::Document.new(curl)
	rescue REXML::ParseException => msg
		echo "WARN: XML Parsing Failed: #{msg.message} (Line:#{msg.backtrace.inspect.match(/(.rb:)(\d+):/)[2]})"
		echo "WARN: XML for parsing:\n#{curl}"
		count_errors += 1
		next #change to exit if you need hard stop on such error
	end
	if doc.elements["response"].nil? || (doc.elements["response"].attributes["status"] != "success") then
		echo "WARN: REST call failed."
		echo "WARN: REST response:\n#{curl}"
		count_errors += 1
		next #change to exit if you need hard stop on such error
	end

	author = doc.elements["response/message/author"].attributes["href"].to_s.strip
			
	labels, newlabels = ""
	labelfound = false

	doc.elements.each("response/message/labels/label") do |labelitem|
		labelfound = true if labelitem.elements["text"].text == label
		labels += (", " + labelitem.elements["text"].text)
	end
	labels[0..1] = "" if labels[0] = ","

	#echo labels

	case action
	when 'remove'
		if not labelfound then
			echo "WARN: Label '#{label}' not found inside the existing '#{labels}'."
			count_errors += 1
			next #change to exit if you need hard stop on such error
		end
		newlabels = labels.gsub(label,"")
	when 'add'
		if labelfound then
			echo "WARN: Label '#{label}' already present."
			count_errors += 1
			next #change to exit if you need hard stop on such error
		end
		newlabels = labels + (labels == "" ? "" : ",") + label
	else
		echo "WARN: Unknown action type. This should never happen."
		exit
	end
	
	#echo newlabels
	
	#updating the message
	restcall="curl -s -S --retry 15 -k " \
	+"	#{REST_URL}/messages/id/#{topicid}/edit "\
	+"	--cookie \"LiSESSIONID=#{$sessionid}\" "\
	+"  --form \"restapi.response_style=view\" "\
	+ (author != "" ? "	--form \"credentials.identity_user=#{author}\" " : "") \
	+"	--form \"label.labels=#{newlabels.gsub(/(["`$])/, '\\\\\1')}\" "
	
	#echo restcall
	curl = %x[#{restcall}]
	#echo curl
	if $?.exitstatus !=0 then
		echo "WARN: HTTP communication failed for message #{rootmsg}. Rest call: #{restcall}. Exit status: #{$?.exitstatus}."
		count_errors += 1
		next #change to exit if you need hard stop on such error
	end
	
	begin
		doc = REXML::Document.new(curl)
	rescue REXML::ParseException => msg
		echo "WARN: XML Parsing Failed: #{msg.message} (Line:#{msg.backtrace.inspect.match(/(.rb:)(\d+):/)[2]})"
		echo "WARN: XML for parsing:\n#{curl}"
		count_errors += 1
		next #change to exit if you need hard stop on such error
	end
	if doc.elements["response"].nil? || (doc.elements["response"].attributes["status"] != "success") then
		echo "WARN: REST call failed."
		echo "WARN: REST response:\n#{curl}"
		count_errors += 1
		next #change to exit if you need hard stop on such error
	end
	
	echo "Updated #{doc.elements["response/message"].attributes["view_href"].to_s.strip}."
	count_actions += 1
	
end
file.close

echo "TOTAL OF #{count_actions} ACTIONS PERFORMED, #{count_errors} ERRORS ENCOUNTERED."
echo "THE PROCESS TOOK #{Time.at(Time.now - start_time).gmtime.strftime('%R:%S')} HOURS."
echo "PROCESS COMPLETED."

end

 

 

--
Community manager in the Micro Focus Community. My computer always used to beat me at chess, but it is no match for me now I changed the competition to kick boxing.
Highlighted

@kgroneman Curious - What problems you having with it? We just built a similar script that best I can tell accomplishes the same thing, but upon running it, it nukes our cover photos/authors on the article, completely randomly.  (Been waiting a month myself on Support, thus my curiosity).

We also have a bug open about the Admin->Edit labels not always working, so we're currently in a bind of having no options available to us 😞

Highlighted
Honored Contributor

@StanGromer  the problem is that when I run it, sometimes it works, but most of the time it will throw strange errors on specific posts, like I don't have permission or the post doesn't exist, etc.  The errors don't make sense.   I've not seen it do what you describe. Just 30 minutes ago I was on a call with Khoros and ran a test while they did some logging.   Waiting for the results. 

--
Community manager in the Micro Focus Community. My computer always used to beat me at chess, but it is no match for me now I changed the competition to kick boxing.