Skip to content

Turn user references into links to that user

Jason Barnabe edited this page Jun 26, 2014 · 2 revisions

This transformer will turn user references into a link to that user's page with an anchor text of the user's name. For example, assume a user exists with ID 123 and name Billy. The text:

I like user 123.

will turn into:

I like <a href="/users/show/123">Billy</a>.

def has_ancestor(node, ancestor_node_name)
	until node.nil?
		return true if node.name == ancestor_node_name
		node = node.parent
	end
	return false
end

def replace_text_with_link(node, original_text, link_text, url)
	# the text itself becomes a link
	link = Nokogiri::XML::Node.new('a', node.document)
	link['href'] = url
	link.add_child(Nokogiri::XML::Text.new(link_text, node.document))
	return replace_text_with_node(node, original_text, link)
end

linkify_users = lambda do |env|
	node = env[:node]
	return unless node.text?
	return if has_ancestor(node, 'a')
	return if has_ancestor(node, 'pre')
	user_pattern = /(\s|^|\()(user ([0-9]+))/i
	user_reference = node.text.match(user_pattern)
	return if user_reference.nil?
	index = 0
	# in normal cases we will only handle one reference per call, but we actually need to loop in case there are invalid references
	until user_reference.nil?
		original_text = user_reference[2]
		user_id = user_reference[3].to_i
		index = index + node.text[index, node.text.length].index(original_text)
		begin
			user = User.find(user_id)
			resulting_nodes = replace_text_with_link(node, original_text, user.name, url_for(:controller => "users", :action => "show", :id => user.id))
			# the current node will not contain any more references as all subsequent text will be in the newly created nodes.
			# not required in Sanitize 3
			# sanitize the new nodes ourselves; they won't be picked up otherwise.
#			resulting_nodes.delete(node)
#			resulting_nodes.each do |new_node|
#				Sanitize.clean_node!(new_node, env[:config])
#			end
			return
		rescue ActiveRecord::RecordNotFound => ex
			# not a valid reference, move on to the next
		end
		index = index + original_text.length
		user_reference = node.text[index, node.text.length].match(user_pattern)
	end
end