Think gadgets are the bees knees? You can create your own for Inspector. You currently have two choices: create a gadget that anyone with a UserVoice account can use, or create a gadget just for yourself. In both cases, you host the gadget on your server, and in both cases, you use the same request/response protocol.
I will first describe how to write a gadget as if you intend to let UserVoice admin use it. Then I’ll show how you can add a one-off gadget.
Creating a gadget Anyone Can Use
You want to implement a gadget and distribute it to others. Maybe you run a CRM SaaS app like pennycrm.com, or maybe you’re a 3rd party and you want to implement a gadget on their behalf. You want an admin on UserVoice.com to go here:
After they add your gadget, it should show up here:
There’s two steps to this process. First, implement an endpoint on your server according to our gadget specification. Second, tell us about it and we can work with you to add it into our UI for others to use.
The Request
Implement an endpoint that will serve the gadget HTML. It can be anything you’d like, so long as it’s SSL. For instance, you might pick:
https://pennycrm.com/gadgets/user.html
UserVoice will call this endpoint with the following parameters:
{
“email”: “sally@jones.com”, // Email address of the user
“user_id”: 123, // Same user id that you’ll find in our API
“name”: “Sally” // The current name we have on file for this person
“custom_fields”: { // Any custom fields attached to the ticket.
“Type”:”Bug Report”
}
}
In order to handle authentication and account-specific scoping, you might want to accept additional parameters that UserVoice admins supply.
You can choose anything you want, and name these parameters what you want. So you might pick account and api_token, for instance. These parameters will be merged into the default parameters, like this:
{
“email”: “sally@jones.com”, // Email address of the user
“user_id”: 123, // Same user id that you’ll find in our API
“name”: “Sally” // The current name we have on file for this person
“custom_fields”: { // Any custom fields attached to the ticket.
“Type”:”Bug Report”
},
“account”:”posterous”, // UV Admin entered this
“api_key”:”12345abcd” // UV Admin entered this, and presumably got it from your website.
}
UserVoice will then form all of this into a GET request:
For CSS, we recommend you use our CSS file. You can do whatever you want here, but obviously something that looks like it fits in is ideal. Our base gadget styles are here:
This javascript communicates via postMessage to the parent UserVoice window what the height of your gadget is and whether it has data. If you don’t include this javascript file, your gadget will not show up in UserVoice.
If your gadget has no data to display, for instance if the user can’t be found in your CRM, then the best practice is to hide the gadget completely. You can do this simply by including this snippet before you include the gadget.js file:
Here is a sample HTML document that you might render. It uses ERB and assumes you have a variable named ‘data’:
<!DOCTYPE html>
<html>
<head>
<title>Title your gadget</title>
<link href="https://cdn.uservoice.com/packages/gadget.css"
media="all" rel="stylesheet" type="text/css" />
</head>
<body>
<% if data %>
<div>Here's the data: <%= data %></div>
<% else %>
<script type="text/javascript">
window.gadgetNoData = true;
</script>
<% end %>
<script src="https://cdn.uservoice.com/packages/gadget.js"
type="text/javascript"></script>
</body>
</html>
Tell Us
Now that you’ve gotten your gadget working, you want anyone to be able to access it. Please email us at .(JavaScript must be enabled to view this email address) and we’ll work with you on getting it added into the UserVoice UI so anyone can add it!
Create a one-off gadget
Let’s say you run a website called clawstores.com. Most likely, you have a database and a secret admin area you can access to view who signed up on your site. You’d like a gadget that can pull in customer data into UserVoice. Obviously, this gadget only applies to you -- you don’t want any other UserVoice account access to it!
This situation is fully supported. You’ll want to head on over to your UserVoice account’s admin area, click Settings, and go into Integrations. Add a custom gadget:
UserVoice will then call your endpoint, including the parameters (such as email and user_id) listed above in the previous section.
It should be noted that you can use this method to test out a broader integration.
Conclusion
So, you’ve learned how to pull in data from your personal system and from popular 3rd party systems. Please let us know if you create generic gadgets that others might find useful!
If your team is anything like ours, you probably use an extreme number of online tools to get your work done. Unfortunately, most of these tools live in their own walled-off little worlds and don't talk to each other. This makes us sad. So today we're introducing a new feature called Service Hooks that allows you to push data from UserVoice into other services in real time. This can be a fantastic way of busting through those walls (Kool-Aid Man style) and integrating customer service into your company's workflow.
For UserVoice customers who want to push data into their own applications, we're also offering a generic Web Hook. This hook can push XML or JSON formatted data to any URL you specify so you can do something cool with it. I'm not saying you should set up an Arduino rig to shoot off fireworks when a customer gives you Kudos, I'm just saying you can.
In the coming weeks we'll be rolling out more Service Hooks, including some hooks for some of our favorite applications here at UserVoice.
We're also excited to announce that we're providing developers with tools to write their own Service Hooks. If you run a web application or service and you'd like your customers to be able to push their UserVoice data into your app, check out our uservoice-service-hooks repository on GitHub. Our service hooks are just simple Ruby classes that define a few user-configurable fields, provide messages for our different event types and include a method for shipping the data off to your app. Just fork that repo, write a Service Hook and some tests, and submit a pull request!
We just upgraded UserVoice, a Rails 2.3.14 application, to Ruby 1.9.3. Why?
Ruby 1.9.3 is faster (see response time difference at the end)
It has 'correct' string encoding support
We want to stay current with the Ruby ecosystem.
First of all, let me state clearly: Rails 2.3.x does NOT SUPPORT Ruby 1.9.x out of the box, unless you want to get your hands dirty with some monkey-patching.
This guide will outline some of the things you'll need to do in order to get things working.
Getting your script/server to run
Ruby changed the way that files are loaded with requires. So in order to get script/server and friends to work, you'll need to tweak them from this:
We also had to change our tests so that ruby test/unit/some_test.rb would work:
require_relative '../test_helper'
Once you get your tests going, you'll likely need to add lines like this to various files if they contain UTF-8 characters.
# encoding: utf-8
Woot! Now some of your tests might run (but they'll still likely fail).
Upgrade your gems, use mysql2
One of the first gems you'll need to upgrade is mysql -- it doesn't support encodings properly. You'll want to use the mysql2 gem. Also, if your tests indicate there's problems in any of your other gems, upgrading the gem is probably a good idea.
If you can, you should deploy all of these gem upgrades to your master branch as soon as possible, so that you can minimize the changes you experience when upgrading Ruby.
Monkey patch, ahoy!
If you weren't properly scared yet, reading these monkey patches will surely do it. If not, you are probably insane. Read on!
Patches to environment.rb:
Patches to config/initializers/rails2314_ruby193_monkey_patches.rb:
Patches to config/initializers/utf8_monkey_patches.rb:
Migrate any serialized columns
We use some YAML-serialized ActiveRecord columns in our schema. Unfortunately, if you have any UTF-8 characters in these serializations, you'll need to migrate them. Ruby 1.8 uses the syck YAML library, and Ruby 1.9.3 uses the psych library. So you'll need to manually use Syck::load on each record, and re-save it with Psych::dump.
Don't use TMail
TMail most certainly does not support Ruby 1.9.x. If you want to continue to use TMail to send your mail (this is the default in Rails 2.3.14), you'll need to monkey patch the heck out of the gem. This was actually our first approach at UserVoice, and it was 99% successful.
However, it felt a bit risky to run our ticketing system on this monkey patched gem, so we just re-wrote a few methods in ActionMailer to use the new Mail gem (included by default in Rails 3). If you send a lot of mail with more than just ascii characters, you'll likely want to do the same.
Deployment
First, make sure you backport anything possible into your application currently running on 1.8.7 and iron out the kinks with that. For instance, any gems that you upgraded are a great candidate for this. This ensures that when you upgrade to 1.9.3, you'll be minimizing risk.
We deployed 1.9.3 slowly. First, take 1 server out of your HAProxy rotation, so no web requests are going to it. Then upgrade that machine with 1.9.3 and load your git feature branch on that machine. Next, make sure it works by hitting that machine yourself (don't put your customers on it yet.) When that works, squirt a small amount of traffic from real users. If at any point you start seeing errors, take it out of rotation again and fix it.
Now, if you leave that server for a few days and it's not producing errors, go ahead and migrate the rest of your cluster.
Final thoughts
So there you have it! We decided to upgrade Ruby before Rails because it seemed more tractable (which is probably true). But it's certainly not as easy as I thought -- I might go to Rails 3 first if I had to do it again. But, UserVoice is faster now! Here's a graph of our performance increase for one of our slower, more ruby-intensive pages: as you can see, it's a roughly 50% performance increase!
As the release of Rails 4 is coming closer, many people are still considering to upgrade from Rails 2 to 3. One the most challenging issues in this upgrade is handling the issues arising from the XSS protection in Rails 3. According to OWASP, XSS was the second-largest security risk of web applications in 2010. Therefore Rails 3 defaulting to escape all custom strings is a really useful pattern. However, if your application is large and you have plenty of places you are printing HTML, you need to figure out a good process for ensuring all the strings get printed as expected after the upgrade.
Installing rails_xss in Rails 2 is a very good method to see how the XSS protection is going to change things after the upgrade to Rails 3. Depending what kind of content management system solutions, asset packaging tools or helper methods you are using, you can have problems in a plethora of different places. As a developer solving these issues in UserVoice, I would like to share my process for the XSS protected strings handling.
Typically problems that arise look like this. To solve them all, first step is to identify the different types of problems. Reserve some time for this, as there might be tens of different problem patterns, which all might not be easy to find.
The Process
Here I explain how I approached the problem and present some lessons learned during the process. This is not a definitive guide on the subject, but a rather a set of principles which I recommend to be applied during and after the rails_xss installation.
1. Install rails_xss
Install rails_xss and its dependency (erubis). After this, your Rails 2 application escapes all your “unsafe” strings by default. Explore your application and run tests to see on a general level how serious problems the default HTML escaping causes.
2. Identify the Problems
You should identify all the different types of problems and address each problem with a solution. While testing and reviewing the codebase of UserVoice, I identified many different problem patterns. I have described some of the most common ones:
2.1 Custom Helpers
For example, the return value of these kind of helpers can be marked html_safe by calling .html_safe. In this case it’s even better to use the Rails helper link_to, whose return value is html_safe by default. Additionally, you don’t have to worry about escaping the name of the user separately using the h method.
def user_link(user)
"<a href=#{user.url}>#{h(user.name)}</a>".html_safe
end
# better:
def user_link(user)
link_to(user.name, user.url)
end
2.2 Printing Conditional Strings in Views
Often you want to print a certain string depending on some condition. In these cases the resulting string must be marked html_safe. Possibly you also want to extract a commonly occurring pattern in your codebase to its own helper.
By default, unsafe + safe results in an unsafe string, which means that the link above gets unexpectedly escaped. To prevent this, ‘<hr />’ can be marked html_safe or even better, just extract it out side the ERB interpolation tag.
2.4 Printing Custom User-Defined HTML
<%= user.custom_html.html_safe %>
In these cases you need to mark the user-defined HTML html_safe. At the same time, you should also ensure that the user input is sanitized, because you do not want to allow printing arbitrary HTML in your views. A good sanitizing includes defining the set of allowed tags and denying any others. Using a good and well-tested library for sanitizing HTML is also a good idea, unless you really know what you are doing, which you most probably are not.
3. Design a Strategy
When identifying all the various cases of printing HTML, JSON and HTML entities, you might start to wonder how to cover all the cases in all your application’s views. The short is answer is that 100% coverage requires probably too much effort. However, there are many ways how you can come close to that. OWASP states about XSS vulnerability finding: “complete coverage requires a combination of manual code review and manual penetration testing, in addition to any automated approaches in use”.
3.1 Custom Automated Tool
Now that rails_xss had escaped all the output strings by default, I needed to find the places where HTML has been doubly-escaped. So I thought it wouldn’t hurt to write a utility method for matching the doubly-escaped HTML strings:
class HtmlDoubleEscapeReporter
def self.assert_sane(str)
if (str.match(/<[a-z]/) || str.match(/&(quot|rarr|larr|amp|#)/)) &&
!str.match(/looks something you do not want to print/
send_problem_report('#{str}' looks something you do not want to print")
end
return str
end
end
This basically recognizes some commonly occurring strings when HTML has been accidentally escaped twice (as usually you don’t want to print this). Depending on the application, the regular expressions might be much different but the basic idea stays the same. The idea is not to have a fully-functional problem analysis tool, but to just report about any suspicious strings which the team can check and fix if necessary. The method send_problem_report represents a method which reports the problem using e.g. Airbrake or ExceptionNotifier if it happens on a deployed application instance. It could also open a debugger using ruby-debug by calling debugger. When the debugger line is reached when running tests, you are immediately able to see the backtrace to determine if there is a problem or not. Another way is of course throwing an exception, which would print nice error in your CI system and send an exception report in testing environments. This is useful during manual testing, as testers might not notice all the problems.
Notice also the !str.match(/looks something you do not want to print/ part in the if condition. This prevents situations where the error message is used in another sanity check causing an infine loop. Whether this is possible or not, depends on your codebase, however, this occurred to me a couple of times. This check prevents that from happening.
The tricky part is, when to call this method? One solution is to hook it in all HTML printing code by alias_method_chaining simple_format, content_tag_string and link_to, which are the ones rails_xss also chained.
module ActionView
module Helpers
module TextHelper
def simple_format_with_double_escape_reporting(*args)
HtmlDoubleEscapeReporter.assert_sane(simple_format_without_double_escape_reporting(*args))
end
alias_method_chain :simple_format, :double_escape_reporting
end
module TagHelper
private
def content_tag_string_with_double_escape_reporting(*args)
HtmlDoubleEscapeReporter.assert_sane(content_tag_string_without_double_escape_reporting(*args))
end
alias_method_chain :content_tag_string, :double_escape_reporting
end
module UrlHelper
def link_to_with_double_escape_reporting(*args, &block)
HtmlDoubleEscapeReporter.assert_sane(link_to_without_double_escape_reporting(*args, &block))
end
alias_method_chain :link_to, :double_escape_reporting
end
end
end
Remember, that this kind of hooking might not work for your application, so plan and test carefully.
3.2 Manual Testing
In addition to this automated check, you most definitely want to run adequate manual regression tests on your application and involve several people in testing. Additionally, every time you find a problem, you want to grep the whole application for similar cases, as usually these problems exist in many places.
3.3 Code Review
Grepping all the code for suspicious patterns might not always be enough. Therefore you should review the code as much as possible. After seeing various problem situations, you have the best knowledge and “eye” for the problems that might still be hidden in the code. Therefore reading as much code through as possible is a highly recommendable practice in addition to automated and manual testing. Just because testing everything is often impossible.
4. Execute your Strategy
Follow your strategy and make notes on the progress. After completing all the planned steps, instruct your whole team on how to keep the application XSS safe using the new tools which you installed. This helps to keep the application both functional and secure in the future.
Today I wanted to talk about another feature we've added to the iOS
SDK that I didn't cover
before: custom stylesheets. Since you are integrating UserVoice into your app,
we thought it would be good for you to have an easy way to tweak the look and
feel to match your existing UI a bit better. To do this, we extracted most of
the colors that our UI works with into a UVStyleSheet class, and added a simple
API that allows you to substitute your own stylesheet.
To create a custom stylesheet, add a new Objective-C class to your project and
call it something like CustomUVStyleSheet. Make it a subclass of UVStyleSheet.
Then you can override any of the instance methods of UVStyleSheet to provide
your own colors. Here's a simple example:
And with that, you should be able to run your app and see custom colors
throughout the UserVoice UI! You can find a complete list of the colors you can
override in UVStyleSheet.h, with explanations of how they are used.
Our iOS SDK has been
available as a public beta for quite a while. We've gotten some great feedback
from you guys, and I've been working hard over the last few weeks to fix all of
the known bugs and layout issues. We're finally ready to make it 1.0!
At the same time we are announcing that the SDK now supports the iPad!
Landscape mode on the iPhone is now ready to use as well.
In addition to polishing the SDK itself, we have released an example iOS
app on GitHub showing
how to use it. We think this will make it a lot easier for you to get
everything set up correctly in XCode.
To get started, download the example project and pull down the SDK. Replace the
demo URL with your own UserVoice site. Log into your UserVoice account and add
an iOS app under Settings -> Channels -> iOS Apps. Copy the API key and secret
into the example codebase. That should be all you need to do to see your own
feedback forum in the example app.
To set up the SDK in your existing app, you will need to do the following things.
Run XCode 4.2 (there is an XCode 3 branch, but it is not up to date)
Have your project inside an XCode workspace
Download the SDK and drag it into your workspace
Add libUserVoice.a under the Link Binary With Libraries build phase
Set User Heading Search Paths to $(BUILD_PRODUCTS_DIR)
Set Always Search User Paths to Yes
Add -ObjC and -all_load to the Other Linker Flags setting
Drag the Include group from the SDK into your project
Create groups for any added subfolders should be selected
Uncheck any targets
You may want to rename the group something like UVHeaders
Drag the Resources group from the SDK into your project
Create groups for any added subfolders should be selected
Add to your target
You may want to rename the group something like UVResources
Once you have completed these steps, you are ready to launch the UserVoice UI
from your code. Import UserVoice.h and call one of the three methods on the
UserVoice class.
1. Standard Login: This is the most basic option, which will allow users to
either sign in, or create a UserVoice account, from inside the UserVoice UI.
This is ideal if your app does not have any information about the user.
2. SSO for local users: This will find or create a new user by passing a name,
email, and unique id. However, it will only find users that were previously
created using this method. It will not allow you to log the user in as an
existing UserVoice account. This is ideal if you only want to use UserVoice
with your iOS app.
3. UserVoice SSO: This is the most flexible option. It allows you to log the
user in using a UserVoice SSO
token. This is
ideal if you are planning to use single signon with UserVoice across multiple
platforms. We recommend you encrypt the token on your servers and pass it to
the iOS app.
Thanks to the lovely people over at AppFusions we will soon have an awesome integration into Jira 5.0. To wet your appetite for that they have put together this video
I've recently had a few people having issues with the RestSharp library for DotNet. Digging into the code for RestSharp if found that during the encoding process the values of parameters are correctly encoded but the names where being left. You can solve this by change line 161 in OAuth1Authenticator.cs from:
var encodedName = OAuthTools.UrlEncodeStrict(p.Name.ToString());
parameters.Add(new WebPair(encodedName, p.Value.ToString()));
and recompiling RestSharp. The value is encoding later in the OAuth workflow in OAuthTools.cs. I suspect there is a neater solution, which I'll have a think about before submitting a pull request. In the meantime this will get you going. The part of the spec that refers to this states:
3.4.1.3.2. Parameters Normalization
The parameters collected in Section 3.4.1.3 are normalized into a
single string as follows:
1. First, the name and value of each parameter are encoded
(Section 3.6).