Content Submission
This documentation explains the mechanics of submitting user-generated content (UGC) to Bazaarvoice using the Mobile SDK.
Introduction
Use the Bazaarvoice Mobile SDKs to enable Conversations functionality, such as Ratings and Reviews. The Conversations module provides an easy-to-use wrapper around the Conversations API. Contact Bazaarvoice to set up Conversations.
You will need your
apiKeyConversations
to implement and use Conversations with Mobile SDKs.
Installation instructions
With Gradle
Include the Maven Central repository and add the appropriate Bazaarvoice Mobile SDK modules to your dependencies:
dependencies {
implementation 'com.bazaarvoice.bvandroidsdk:conversations:{BV_SDK_VERSION}.+'
implementaion 'com.google.android.gms:play-services-ads:9.{YOUR_PLAY_SERVICES_VERSION}'
implementation 'com.android.support:recyclerview-v7:25.{YOUR_SUPPORT_LIBRARY_VERSION}'
}
repositories {
mavenCentral()
}
Replace the {tokens}
, including brackets, with the appropriate values. Refer to the Installation guide for {BV_SDK_VERSION}
.
Extending the application
Create a class that extends android.app.Application
, and initialize the Mobile SDK using its builder.
You can find a full list of build options in BVSDK.java
.
public class MyApplication extends Application {
@Override
public void onCreate(){
super.onCreate();
BVSDK.builder(application, BazaarEnvironment.PRODUCTION)
.logLevel(BVLogLevel.VERBOSE) // Optional: Set the log level. Default level is ERROR.
.dryRunAnalytics(false) // Optional: Set analytics to log only, not make a network request. Default is false.
.okHttpClient(okHttpClient) // Optional: Use you own okHttpClient instance
.build();
}
}
App manifest file
Add the following to your AndroidManifest.xml
file.
<!-- set the name of the class for the application entry -->
<application
android:name=".MyApplication">
</application>
Using Conversations Submission API
After you install the Mobile SDK, complete the following steps to submit user-generated content.
Step 1: Create a BVConversationsClient
Use an instance of the BVConversationsClient
to add to all of your Conversations Submission API requests. You should keep an instance of this object to handle all your Conversations requests as shown in the examples below.
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
For more information on setting up the BVSDK
instance, see Configuring the BVSDK.
For more information on supporting multiple API keys at runtime, see Multi Config
Step 2: Create an AuthenticationProvider
Choose one of the two classes that implement AuthenticationProvider
, and create an instance to add to all of your Conversations Submission API requests. You should keep an instance of this object to handle all your Conversations requests as shown in the examples below.
final AuthenticationProvider authProvider = new BVHostedAuthenticationProvider(userEmailAddress, callbackUrl); // for part 1 for BV Hosted Auth clients
final AuthenticationProvider authProvider = new BVHostedAuthenticationProvider(uas); // for part 2 for BV Hosted Auth clients
// or
final AuthenticationProvider authProvider = new SiteAuthenticationProvider(uas); // for Site Auth clients
For more information on which type you are configured for, and how to use these different AuthenticationProvider
s, see Conversations Authentication.
Step 3: View form options
For each option available in a submission form, there is a boolean flag determining whether it is a required field or not. You can discover this by iterating through the available form fields, which can be found by making a preview submission request.
These form fields may also contain extra information, such as available submission values, and keys for fields specific to your config such as context data values, etc.
Parsing form data
For more information on how the API processes Form Data please see: Submission Fundamentals: Request Form Information
final ReviewSubmissionRequest formRequest = new ReviewSubmissionRequest
.Builder(Action.Form, "productId")
.authenticationProvider(authProvider)
.build();
client.prepareCall(formRequest).loadAsync(new ConversationsSubmissionCallback<ReviewSubmissionResponse>() {
@Override
public void onSuccess(ReviewSubmissionResponse response) {
final List<FormField> formFields = response.getFormFields();
for (FormField formField : formFields) {
final String key = formField.getId(); // the key that must be used for submission
final boolean isRequired = formField.isRequired(); // Whether this field is required or optional
final String label = formField.getLabel(); // Configured label to display to user for this field
final FormInputType inputType = formField.getFormInputType();
if (inputType == FormInputType.TextAreaInput) {
final int minLength = formField.getMinLength();
final int maxLength = formField.getMaxLength();
}
if (inputType == FormInputType.SELECT) {
final List<FormFieldOption> options = formField.getFormFieldOptions();
for (FormFieldOption option : options) {
final String label = formField.getLabel(); // User facing option string
final String value = formField.getValue(); // the value that must be used for submission
}
}
}
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// Might contain failure info specific to your configuration
// that is required even for fetching a form. Otherwise missing
// form field errors do not exist for Action.Form requests
}
});
Step 4: Send submission request
- ReviewSubmission
- ProgresssiveReviewSubmission
- ReviewCommentSubmission
- QuestionSubmission
- AnswerSubmission
- FeedbackSubmission
Submitting reviews
To submit content, form the proper ConversationsSubmissionRequest
subclass, and send it through the BVConversationsClient
. All parameters to the Builder
constructors are required, and all other options may or may not be required per your config. Consult the FormData
above to answer which are required.
final ReviewSubmissionRequest previewSubmission = new ReviewSubmissionRequest
.Builder(Action.Submit, productId)
.authenticationProvider(authProvider)
.rating(4)
.title(userMadeTitle)
.reviewText(userReviewText)
.isRecommended(true)
.sendEmailAlertWhenCommented(true)
.netPromoterScore(3)
.netPromoterComment(promotionComment)
.addFreeFormTag(questionId, value)
.addPredefinedTag(questionId, tagId, value)
.addAdditionalField(key, value)
.addContextDataValueString(contextDataValueId, value)
.addRatingSlider(questionName, value)
.addRatingQuestion(questionName, value)
.addVideoUrl(youTubeLink, videoCaption) // YouTube video link if your config supports it
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(previewSubmission).loadAsync(new ConversationsSubmissionCallback<ReviewSubmissionResponse>() {
@Override
public void onSuccess(ReviewSubmissionResponse response) {
// Make sure to send with Action.SUBMIT for it to fully complete
// All good
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// How to handle described in next step
}
});
Progressive Submission
To submit content, form the proper ConversationsSubmissionRequest subclass, and send it through the BVConversationsClient. All parameters to the Builder constructors are required, and all other options may or may not be required per your config. Consult the Progressive Submission Documentation for more details about this api.
Progressive submission should be used in scenarios where clients want to display multiple review request forms for a number of items. Additionally, progressive submission supports partial or incremental submissions across different devices.
A progressive submission is a multiple request step process. The first step is to construct an ‘InitiateProgrssiveSubmitRequest’. This request will initiate a progressive Submission session for the products submitted.
final InitiateSubmitRequest initiateSubmitRequest = new InitiateSubmitRequest
.Builder(productIds, locale)
.extended(true)
.authenticationProvider(authProvider)
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(initiateSubmitRequest).loadAsync(new ConversationsSubmissionCallback<InitiateSubmitResponse>() {
@Override
public void onSuccess(InitiateSubmitResponse
response)
{ // All good }
@Override
public void onFailure(ConversationsSubmissionException exception) { // How to handle described in next step }
});
An InitiateSubmitResponse contains the submissionSessionToken
for each product. This token is used for submitting content and making updates to a review for the associated product. The next step is to collect the fields to submit a review and then construct a ‘ProgressiveSubmitReview’.
Map<String, Object> submissionFields = new HashMap<>();
submissionFields.put("rating", 4);
submissionFields.put("agreedtotermsandconditions", true);
ProgressiveSubmitRequest progressiveSubmissionRequest = new ProgressiveSubmitRequest.Builder( productId, submissionSessionToken, locale)
.submissionFields(submissionFields)
.authenticationProvider(authProvider)
.build()
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(progressiveSubmissionRequest).loadAsync(new ConversationsSubmissionCallback< ProgressiveSubmitResponse >()
{ @Override public void onSuccess(ProgressiveSubmitResponse response) \{ // All good }
@Override
public void onFailure(ConversationsSubmissionException exception)
{ // How to handle described in next step }
});
Photos
To submit a photo with a ProgressiveSubmission you will need to attach a photo’s url to the progressiveSubmission’s SubmissionFields after uploading the photo using the ‘PhotoSubmissionRequest’
Map<String, Object> submissionFields = new HashMap<>();
submissionFields.put("photourl_1", photourl_1);
submissionFields.put("photourl_2", photourl_2);
PhotoUpload
To submit content, form the proper ConversationsSubmissionRequest subclass, and send it through the BVConversationsClient. All parameters to the Builder constructors are required, and all other options may or may not be required per your config.
* Final File file = readFile("bv_sample_image.png", *this.getClass());final PhotoUploadRequest photoUploadRequest = new PhotoUploadRequest.Builder(new PhotoUpload(file, "comments", PhotoUpload.ContentType.REVIEW))
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(photoUploadRequest).loadAsync(new ConversationsSubmissionCallback< PhotoUploadResponse >() {
@Override
public void onSuccess(PhotoUploadResponse
response)
{ // All good String photoUrl = response._photo_._content_._normalUrl_ }
@Override
public void onFailure(ConversationsSubmissionException exception)
{ // How to handle described in next step }
});
Submitting review comments
To submit content, form the proper ConversationsSubmissionRequest
subclass, and send it through the BVConversationsClient
. All parameters to the Builder
constructors are required, and all other options may or may not be required per your config. Consult the FormData
above to answer which are required.
final CommentSubmissionRequest previewSubmission = new CommentSubmissionRequest
.Builder(Action.Submit, reviewId, commentText)
.authenticationProvider(authProvider)
.title(titleText)
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(previewSubmission).loadAsync(new ConversationsSubmissionCallback<CommentSubmissionResponse>() {
@Override
public void onSuccess(CommentSubmissionResponse response) {
// Make sure to send with Action.SUBMIT for it to fully complete
// All good
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// How to handle described in next step
}
});
Submitting questions
To submit content, form the proper ConversationsSubmissionRequest
subclass, and send it through the BVConversationsClient
. All parameters to the Builder
constructors are required, and all other options may or may not be required per your config. Consult the FormData
above to answer which are required.
final QuestionSubmissionRequest previewSubmission = new QuestionSubmissionRequest
.Builder(Action.Submit, productId)
.authenticationProvider(authProvider)
.questionSummary(summaryText)
.questionDetails(detailText)
.isUserAnonymous(true)
.sendEmailAlertWhenAnswered(true)
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(previewSubmission).loadAsync(new ConversationsSubmissionCallback<QuestionSubmissionResponse>() {
@Override
public void onSuccess(QuestionSubmissionResponse response) {
// Make sure to send with Action.SUBMIT for it to fully complete
// All good
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// How to handle described in next step
}
});
Submitting answers
To submit content, form the proper ConversationsSubmissionRequest
subclass, and send it through the BVConversationsClient
. All parameters to the Builder
constructors are required, and all other options may or may not be required per your config. Consult the FormData
above to answer which are required.
final AnswerSubmissionRequest previewSubmission = new AnswerSubmissionRequest
.Builder(Action.Submit, questionId, answerText)
.authenticationProvider(authProvider)
.userNickname(userNickname)
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(previewSubmission).loadAsync(new ConversationsSubmissionCallback<AnswerSubmissionResponse>() {
@Override
public void onSuccess(AnswerSubmissionResponse response) {
// Make sure to send with Action.SUBMIT for it to fully complete
// All good
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// How to handle described in next step
}
});
You cannot submit feedback for syndicated content. Make sure to check the
IsSyndicated
flag before you display your feedback UI.
Submitting feedback
To submit content, form the proper ConversationsSubmissionRequest
subclass, and send it through the BVConversationsClient
. All parameters to the Builder
constructors are required, and all other options may or may not be required per your config. Consult the FormData
above to answer which are required.
final FeedbackSubmissionRequest previewSubmission = new FeedbackSubmissionRequest
.Builder(contentId)
.authenticationProvider(authProvider)
.feedbackType(FeedbackType.HELPFULNESS) // You can also flag for inappropriateness.
.feedbackContentType(FeedbackContentType.REVIEW)
.feedbackType(FeedbackType.HELPFULNESS)
.feedbackVote(FeedbackVoteType.POSITIVE)
.reasonFlaggedText(reasonText)
.build();
final BVConversationsClient client = new BVConversationsClient.Builder(BVSDK.getInstance()).build(); // Use one instance
client.prepareCall(previewSubmission).loadAsync(new ConversationsSubmissionCallback<FeedbackSubmissionResponse>() {
@Override
public void onSuccess(FeedbackSubmissionResponse response) {
// Make sure to send with Action.SUBMIT for it to fully complete
// All good
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// How to handle described in next step
}
});
Step 5: Handle errors
For any Action.Preview
or Action.Submit
request that was not successful, you can parse out the reason programmatically from the ConversationsSubmissionException
object in the onFailure
callback.
final ReviewSubmissionRequest submitRequest = new ReviewSubmissionRequest
.Builder(Action.Submit, "productId")
.authenticationProvider(authProvider)
.build();
client.prepareCall(previewSubmissionRequest).loadAsync(new ConversationsSubmissionCallback<ReviewSubmissionResponse>() {
@Override
public void onSuccess(ReviewSubmissionResponse response) {
// Make sure to send with Action.SUBMIT for it to fully complete
// All good
}
@Override
public void onFailure(ConversationsSubmissionException exception) {
// This list of errors handles things like missing api key, etc.
final List<Error> genericErrors = exception.getErrors();
for (Error error : genericErrors) {
final ErrorCode code = error.getErrorCode();
final String message = error.getMessage();
switch (code) {
// common errors
case ERROR_BAD_REQUEST:
case ERROR_ACCESS_DENIED:
case ERROR_PARAM_INVALID_API_KEY:
case ERROR_PARAM_INVALID_LOCALE:
case ERROR_REQUEST_LIMIT_REACHED:
case ERROR_UNSUPPORTED: handleCommonError(); break;
// display specific errors
case ERROR_PARAM_INVALID_CALLBACK:
case ERROR_PARAM_INVALID_FILTER_ATTRIBUTE:
case ERROR_PARAM_INVALID_INCLUDED:
case ERROR_PARAM_INVALID_LIMIT:
case ERROR_PARAM_INVALID_OFFSET:
case ERROR_PARAM_INVALID_SEARCH_ATTRIBUTE:
case ERROR_PARAM_INVALID_SORT_ATTRIBUTE: handleDisplayError(); break;
// submission specific errors
case ERROR_DUPLICATE_SUBMISSION:
case ERROR_PARAM_INVALID_PARAMETERS:
case ERROR_PARAM_MISSING_USER_ID: handleSubmissionError(); break;
// unknown state
case ERROR_UNKNOWN:
default: handle(); break;
}
}
// This list of errors handles missing/incorrect inputs for Form Submissions
final List<FieldError> fieldErrors = exception.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
final SubmissionErrorCode code = fieldError.getErrorCode();
final String message = fieldError.getMessage();
final FormField formField = fieldError.getFormField(); // which form field has issues
switch (code) {
case ERROR_FORM_DUPLICATE:
case ERROR_FORM_DUPLICATE_NICKNAME:
case ERROR_FORM_INVALID_EMAILADDRESS:
case ERROR_FORM_INVALID_IPADDRESS:
case ERROR_FORM_INVALID_OPTION:
case ERROR_FORM_PATTERN_MISMATCH:
case ERROR_FORM_PROFANITY:
case ERROR_FORM_REJECTED:
case ERROR_FORM_REQUIRED:
case ERROR_FORM_REQUIRED_EITHER:
case ERROR_FORM_REQUIRED_NICKNAME:
case ERROR_FORM_REQUIRES_TRUE:
case ERROR_FORM_RESTRICTED:
case ERROR_FORM_STORAGE_PROVIDER_FAILED:
case ERROR_FORM_SUBMITTED_NICKNAME:
case ERROR_FORM_TOO_FEW:
case ERROR_FORM_TOO_HIGH:
case ERROR_FORM_TOO_LONG:
case ERROR_FORM_TOO_LOW:
case ERROR_FORM_TOO_SHORT:
case ERROR_FORM_UPLOAD_IO:
case ERROR_PARAM_DUPLICATE_SUBMISSION:
case ERROR_PARAM_INVALID_SUBJECT_ID:
case ERROR_PARAM_MISSING_SUBJECT_ID: handleEachSeparate(); break;
// unknown state
case ERROR_UNKNOWN:
default: handle(); break;
}
}
}
});
Generic form field options
These options are available on the builder for all Submission request objects.
requestBuilder
// User's external ID. Do not use email addresses for this value.
.userId("userId")
// User nickname display text
.userNickname("userNickname")
// Authentication provider
.authenticationProvider(authProvider)
// Arbitrary text that may be saved alongside content to indicate vehicle by which content was captured.
.campaignId("campaignId")
// Locale to display Labels, Configuration, Product Attributes and Category Attributes in.
.locale("en_US")
// User's email address.
.userEmail("[email protected]")
// User location text.
.userLocation("Austin, TX")
// Boolean indicating whether or not the user wants to be notified when their content is published.
.sendEmailAlertWhenPublished(true)
// Boolean indicating whether or not the user agreed to the terms and conditions. Required depending on the your configuration.
.agreedToTermsAndConditions(true)
Custom key-value pairs
If there is some form parameter that is not supported by the SDK, that your configuration supports, you can add it to a Submission Request as follows,
final ReviewSubmissionRequest reviewSubmitRequest = new ReviewSubmissionRequest.Builder(Action.Submit, productId)
.authenticationProvider(authProvider)
.addCustomSubmissionParameter("customKey", "customValue")
.build();
Updated 3 months ago