new file mode 100644
--- /dev/null
+++ b/reviewboard/reviewboard/static/rb/js/diffviewer/models/diffReviewableModel_mozreview.js
@@ -0,0 +1,103 @@
+/*
+ * Provides state and utility functions for loading and reviewing diffs.
+ */
+RB.DiffReviewable = RB.AbstractReviewable.extend({
+ defaults: _.defaults({
+ fileIndex: null,
+ fileDiffID: null,
+ interFileDiffID: null,
+ revision: null,
+ interdiffRevision: null
+ }, RB.AbstractReviewable.prototype.defaults),
+
+ commentBlockModel: RB.DiffCommentBlock,
+ defaultCommentBlockFields: ['fileDiffID', 'interFileDiffID'],
+
+ /*
+ * Adds comment blocks for the serialized comment blocks passed to the
+ * reviewable.
+ */
+ loadSerializedCommentBlock: function(serializedCommentBlock) {
+ this.createCommentBlock({
+ reviewRequest: this.get('reviewRequest'),
+ review: this.get('review'),
+ fileDiffID: this.get('fileDiffID'),
+ interFileDiffID: this.get('interFileDiffID'),
+ beginLineNum: serializedCommentBlock.linenum,
+ endLineNum: serializedCommentBlock.linenum +
+ serializedCommentBlock.num_lines - 1,
+ serializedComments: serializedCommentBlock.comments || []
+ });
+ },
+
+ /*
+ * Returns the rendered diff for a file.
+ *
+ * The rendered file will be fetched from the server and eventually
+ * returned as the argument to the success callback.
+ */
+ getRenderedDiff: function(callbacks, context) {
+ this._fetchFragment({
+ url: this._buildRenderedDiffURL() +
+ '?index=' + this.get('fileIndex') + '&' + TEMPLATE_SERIAL,
+ noActivityIndicator: true
+ }, callbacks, context);
+ },
+
+ /*
+ * Returns a rendered fragment of a diff.
+ *
+ * The fragment will be fetched from the server and eventually returned
+ * as the argument to the success callback.
+ */
+ getRenderedDiffFragment: function(options, callbacks, context) {
+ console.assert(options.chunkIndex !== undefined,
+ 'chunkIndex must be provided');
+
+ this._fetchFragment({
+ url: this._buildRenderedDiffURL() + 'chunk/' +
+ options.chunkIndex + '/',
+ data: {
+ 'index': this.get('fileIndex'),
+ 'lines-of-context': options.linesOfContext
+ }
+ }, callbacks, context);
+ },
+
+ /*
+ * Fetches the diff fragment from the server.
+ *
+ * This is used internally by getRenderedDiff and getRenderedDiffFragment
+ * to do all the actual fetching and calling of callbacks.
+ */
+ _fetchFragment: function(options, callbacks, context) {
+ RB.apiCall(_.defaults(
+ {
+ type: 'GET',
+ dataType: 'html'
+ },
+ options,
+ _.bindCallbacks(callbacks, context)
+ ));
+ },
+
+ /*
+ * Builds a URL that forms the base of a diff fragment fetch.
+ */
+ _buildRenderedDiffURL: function() {
+ var revisionStr,
+ interdiffRevision = this.get('interdiffRevision'),
+ interFileDiffID = this.get('interFileDiffID');
+
+ revisionStr = this.get('revision');
+
+ if (interdiffRevision) {
+ revisionStr += '-' + interdiffRevision;
+ }
+
+ return this.get('reviewRequest').get('reviewURL') + 'diff/' +
+ revisionStr + '/fragment/' + this.get('fileDiffID') +
+ (interFileDiffID ? '-' + interFileDiffID : '') +
+ '/';
+ }
+});
new file mode 100644
--- /dev/null
+++ b/reviewboard/reviewboard/static/rb/js/resources/models/baseCommentModel_mozreview.js
@@ -0,0 +1,185 @@
+/*
+ * The base model for a comment.
+ *
+ * This provides all the common properties, serialization, deserialization,
+ * validation, and other functionality of comments. It's meant to be
+ * subclassed by more specific implementations.
+ */
+RB.BaseComment = RB.BaseResource.extend({
+ defaults: function() {
+ return _.defaults({
+ /*
+ * The text format type to request for text in all responses.
+ */
+ forceTextType: null,
+
+ /*
+ * A string containing a comma-separated list of text types to
+ * include in the payload.
+ */
+ includeTextTypes: null,
+
+ /* Whether or not an issue is opened. */
+ issueOpened: null,
+
+ /*
+ * The current state of the issue.
+ *
+ * This must be one of STATE_DROPPED, STATE_OPEN, or
+ * STATE_RESOLVED.
+ */
+ issueStatus: null,
+
+ /*
+ * Markdown-formatted text fields, if the caller fetches or posts
+ * with include-text-types=markdown.
+ */
+ markdownTextFields: {},
+
+ /*
+ * Raw text fields, if the caller fetches or posts with
+ * include-text-types=raw.
+ */
+ rawTextFields: {},
+
+ /* Whether the comment is saved in rich-text (Markdown) format. */
+ richText: null,
+
+ /* The text entered for the comment. */
+ text: ''
+ }, RB.BaseResource.prototype.defaults());
+ },
+
+ extraQueryArgs: function() {
+ var textTypes = 'raw';
+
+ if (RB.UserSession.instance.get('defaultUseRichText')) {
+ textTypes += ',markdown';
+ }
+
+ return {
+ 'force-text-type': 'html',
+ 'include-text-types': textTypes
+ };
+ },
+
+ supportsExtraData: true,
+
+ attrToJsonMap: {
+ forceTextType: 'force_text_type',
+ includeTextTypes: 'include_text_types',
+ issueOpened: 'issue_opened',
+ issueStatus: 'issue_status',
+ richText: 'text_type'
+ },
+
+ serializedAttrs: [
+ 'forceTextType',
+ 'includeTextTypes',
+ 'issueOpened',
+ 'issueStatus',
+ 'richText',
+ 'text'
+ ],
+
+ deserializedAttrs: [
+ 'issueOpened',
+ 'issueStatus',
+ 'text',
+ 'html'
+ ],
+
+ serializers: {
+ forceTextType: RB.JSONSerializers.onlyIfValue,
+ includeTextTypes: RB.JSONSerializers.onlyIfValue,
+ richText: RB.JSONSerializers.textType,
+
+ issueStatus: function(value) {
+ var parentObject;
+
+ if (this.get('loaded')) {
+ parentObject = this.get('parentObject');
+
+ if (parentObject.get('public')) {
+ return value;
+ }
+ }
+
+ return undefined;
+ }
+ },
+
+ /*
+ * Destroys the comment if and only if the text is empty.
+ *
+ * This works just like destroy(), and will in fact call destroy()
+ * with all provided arguments, but only if there's some actual
+ * text in the comment.
+ */
+ destroyIfEmpty: function(options, context) {
+ if (!this.get('text')) {
+ this.destroy(options, context);
+ }
+ },
+
+ /*
+ * Deserializes comment data from an API payload.
+ *
+ * This must be overloaded by subclasses, and the parent version called.
+ */
+ parseResourceData: function(rsp) {
+ var rawTextFields = rsp.raw_text_fields || rsp,
+ data = RB.BaseResource.prototype.parseResourceData.call(this, rsp);
+
+ data.richText = (rawTextFields.text_type === 'markdown');
+
+ if (rsp.raw_text_fields) {
+ data.rawTextFields = {
+ text: rsp.raw_text_fields.text
+ };
+ }
+
+ if (rsp.markdown_text_fields) {
+ data.markdownTextFields = {
+ text: rsp.markdown_text_fields.text
+ };
+ }
+
+ if (rsp.html_text_fields) {
+ data.html = rsp.html_text_fields.text;
+ }
+
+ return data;
+ },
+
+ /*
+ * Performs validation on the attributes of the model.
+ *
+ * By default, this validates the issueStatus field. It can be
+ * overridden to provide additional validation, but the parent
+ * function must be called.
+ */
+ validate: function(attrs) {
+ if (_.has(attrs, 'parentObject') && !attrs.parentObject) {
+ return RB.BaseResource.strings.UNSET_PARENT_OBJECT;
+ }
+
+ if (attrs.issueStatus &&
+ attrs.issueStatus !== RB.BaseComment.STATE_DROPPED &&
+ attrs.issueStatus !== RB.BaseComment.STATE_OPEN &&
+ attrs.issueStatus !== RB.BaseComment.STATE_RESOLVED) {
+ return RB.BaseComment.strings.INVALID_ISSUE_STATUS;
+ }
+
+ return RB.BaseResource.prototype.validate.apply(this, arguments);
+ }
+}, {
+ STATE_DROPPED: 'dropped',
+ STATE_OPEN: 'open',
+ STATE_RESOLVED: 'resolved',
+
+ strings: {
+ INVALID_ISSUE_STATUS: 'issueStatus must be one of STATE_DROPPED, ' +
+ 'STATE_OPEN, or STATE_RESOLVED'
+ }
+});
new file mode 100644
--- /dev/null
+++ b/reviewboard/reviewboard/static/rb/js/resources/models/diffCommentModel_mozreview.js
@@ -0,0 +1,148 @@
+(function() {
+
+
+var parentProto = RB.BaseComment.prototype;
+
+
+/*
+ * Provides commenting functionality for diffs.
+ *
+ * A DiffComment represents a comment on a range of lines on either a
+ * FileDiff or an interdiff consisting of two FileDiffs.
+ */
+RB.DiffComment = RB.BaseComment.extend({
+ defaults: function() {
+ return _.defaults({
+ /* The first line number in the range (0-indexed). */
+ beginLineNum: 0,
+
+ /* The last line number in the range (0-indexed). */
+ endLineNum: 0,
+
+ /* The FileDiff the comment applies to. */
+ fileDiff: null,
+
+ /* The ID of the FileDiff the comment is on. */
+ fileDiffID: null,
+
+ /* The optional FileDiff at the end of an interdiff range. */
+ interFileDiff: null,
+
+ /*
+ * The ID of the optional FileDiff specifying the end of an
+ * interdiff range.
+ */
+ interFileDiffID: null
+ }, parentProto.defaults());
+ },
+
+ rspNamespace: 'diff_comment',
+ expandedFields: ['filediff', 'interfilediff'],
+
+ attrToJsonMap: _.defaults({
+ fileDiffID: 'filediff_id',
+ beginLineNum: 'first_line',
+ interFileDiffID: 'interfilediff_id',
+ numLines: 'num_lines'
+ }, parentProto.attrToJsonMap),
+
+ serializedAttrs: [
+ 'beginLineNum',
+ 'numLines',
+ 'fileDiffID',
+ 'interFileDiffID'
+ ].concat(parentProto.serializedAttrs),
+
+ deserializedAttrs: [
+ 'beginLineNum',
+ 'endLineNum'
+ ].concat(parentProto.deserializedAttrs),
+
+ serializers: _.defaults({
+ fileDiffID: RB.JSONSerializers.onlyIfUnloaded,
+ interFileDiffID: RB.JSONSerializers.onlyIfUnloadedAndValue,
+
+ numLines: function() {
+ return this.getNumLines();
+ }
+ }, parentProto.serializers),
+
+ /*
+ * Returns the total number of lines the comment spans.
+ */
+ getNumLines: function() {
+ return this.get('endLineNum') - this.get('beginLineNum') + 1;
+ },
+
+ /*
+ * Deserializes comment data from an API payload.
+ */
+ parseResourceData: function(rsp) {
+ var result = parentProto.parseResourceData.call(this, rsp);
+
+ result.endLineNum = rsp.num_lines + result.beginLineNum - 1;
+
+ result.fileDiff = new RB.FileDiff(rsp.filediff, {
+ parse: true
+ });
+
+ if (rsp.interfilediff) {
+ result.interFileDiff = new RB.FileDiff(rsp.interfilediff, {
+ parse: true
+ });
+ }
+
+ return result;
+ },
+
+ /*
+ * Performs validation on the attributes of the model.
+ *
+ * This will check the range of line numbers to make sure they're
+ * a valid ordered range, along with the default comment validation.
+ */
+ validate: function(attrs, options) {
+ var strings = RB.DiffComment.strings,
+ hasBeginLineNum,
+ hasEndLineNum;
+
+ /*
+ * XXX: Existing diff comments won't have the "fileDiffID" attribute
+ * populated when we load the object from the API. Since we don't do
+ * anything that needs that attribute unless we're trying to create a
+ * new diff comment, only check it if isNew().
+ */
+ if (this.isNew() && _.has(attrs, 'fileDiffID') && !attrs.fileDiffID) {
+ return strings.INVALID_FILEDIFF_ID;
+ }
+
+ hasBeginLineNum = _.has(attrs, 'beginLineNum');
+
+ if (hasBeginLineNum && attrs.beginLineNum < 0) {
+ return strings.BEGINLINENUM_GTE_0;
+ }
+
+ hasEndLineNum = _.has(attrs, 'endLineNum');
+
+ if (hasEndLineNum && attrs.endLineNum < 0) {
+ return strings.ENDLINENUM_GTE_0;
+ }
+
+ if (hasBeginLineNum && hasEndLineNum &&
+ attrs.beginLineNum > attrs.endLineNum) {
+ return strings.BEGINLINENUM_LTE_ENDLINENUM;
+ }
+
+ return parentProto.validate.call(this, attrs, options);
+ }
+}, {
+ strings: {
+ INVALID_FILEDIFF_ID: 'fileDiffID must be a valid ID',
+ BEGINLINENUM_GTE_0: 'beginLineNum must be >= 0',
+ ENDLINENUM_GTE_0: 'endLineNum must be >= 0',
+ BEGINLINENUM_LTE_ENDLINENUM: 'beginLineNum must be <= endLineNum'
+ }
+});
+
+
+})();
new file mode 100644
--- /dev/null
+++ b/reviewboard/reviewboard/static/rb/js/resources/models/draftReviewModel_mozreview.js
@@ -0,0 +1,47 @@
+/*
+ * Draft reviews.
+ *
+ * Draft reviews are more complicated than most objects. A draft may already
+ * exist on the server, in which case we need to be able to get its ID. A
+ * special resource exists at /reviews/draft/ which will redirect to the
+ * existing draft if one exists, and return 404 if not.
+ */
+RB.DraftReview = RB.Review.extend(_.extend({
+ /*
+ * Publishes the review.
+ *
+ * Before publish, the "publishing" event will be triggered.
+ *
+ * After the publish has succeeded, the "published" event will be
+ * triggered.
+ */
+ publish: function(options, context) {
+ options = options || {};
+
+ this.trigger('publishing');
+
+ this.ready({
+ ready: function() {
+ this.set('public', true);
+ this.save({
+ attrs: options.attrs,
+ success: function() {
+ this.trigger('published');
+
+ if (_.isFunction(options.success)) {
+ options.success.call(context);
+ }
+ },
+ error: function(model, xhr) {
+ model.trigger('publishError', xhr.errorText);
+
+ if (_.isFunction(options.error)) {
+ options.error.call(context, model, xhr);
+ }
+ }
+ }, this);
+ },
+ error: error
+ }, this);
+ }
+}, RB.DraftResourceModelMixin));
new file mode 100644
--- /dev/null
+++ b/reviewboard/reviewboard/static/rb/js/resources/models/reviewRequestModel_mozreview.js
@@ -0,0 +1,427 @@
+/*
+ * A review request.
+ *
+ * ReviewRequest is the starting point for much of the resource API. Through
+ * it, the caller can create drafts, diffs, file attachments, and screenshots.
+ *
+ * Fields on a ReviewRequest are set by accessing the ReviewRequest.draft
+ * object. Through there, fields can be set like any other model and then
+ * saved.
+ *
+ * A review request can be closed by using the close() function, reopened
+ * through reopen(), or even permanently destroyed by calling destroy().
+ */
+RB.ReviewRequest = RB.BaseResource.extend({
+ defaults: function() {
+ return _.defaults({
+ approved: false,
+ approvalFailure: null,
+ branch: null,
+ bugTrackerURL: null,
+ bugsClosed: null,
+ commitID: null,
+ closeDescription: null,
+ closeDescriptionRichText: false,
+ dependsOn: [],
+ description: null,
+ descriptionRichText: false,
+ draftReview: null,
+ lastUpdated: null,
+ localSitePrefix: null,
+ 'public': null,
+ repository: null,
+ reviewURL: null,
+ state: null,
+ summary: null,
+ targetGroups: [],
+ targetPeople: [],
+ testingDone: null,
+ testingDoneRichText: false
+ }, RB.BaseResource.prototype.defaults());
+ },
+
+ rspNamespace: 'review_request',
+
+ extraQueryArgs: {
+ 'force-text-type': 'html',
+ 'include-text-types': 'raw'
+ },
+
+ attrToJsonMap: {
+ approvalFailure: 'approval_failure',
+ bugsClosed: 'bugs_closed',
+ closeDescription: 'close_description',
+ closeDescriptionRichText: 'close_description_text_type',
+ dependsOn: 'depends_on',
+ descriptionRichText: 'description_text_type',
+ lastUpdated: 'last_updated',
+ reviewURL: 'url',
+ targetGroups: 'target_groups',
+ targetPeople: 'target_people',
+ testingDone: 'testing_done',
+ testingDoneRichText: 'testing_done_text_type'
+ },
+
+ deserializedAttrs: [
+ 'approved',
+ 'approvalFailure',
+ 'branch',
+ 'bugsClosed',
+ 'closeDescription',
+ 'dependsOn',
+ 'description',
+ 'lastUpdated',
+ 'public',
+ 'reviewURL',
+ 'summary',
+ 'targetGroups',
+ 'targetPeople',
+ 'testingDone'
+ ],
+
+ initialize: function(attrs, options) {
+ options = options || {};
+
+ RB.BaseResource.prototype.initialize.call(this, attrs, options);
+
+ this.reviews = new Backbone.Collection([], {
+ model: RB.Review
+ });
+
+ this.draft = new RB.DraftReviewRequest(_.defaults({
+ parentObject: this,
+ branch: this.get('branch'),
+ bugsClosed: this.get('bugsClosed'),
+ dependsOn: this.get('dependsOn'),
+ description: this.get('description'),
+ descriptionRichText: this.get('descriptionRichText'),
+ summary: this.get('summary'),
+ targetGroups: this.get('targetGroups'),
+ targetPeople: this.get('targetPeople'),
+ testingDone: this.get('testingDone'),
+ testingDoneRichText: this.get('testingDoneRichText')
+ }, options.extraDraftAttrs));
+ },
+
+ url: function() {
+ var url = SITE_ROOT + (this.get('localSitePrefix') || '') +
+ 'api/review-requests/';
+
+ if (!this.isNew()) {
+ url += this.id + '/';
+ }
+
+ return url;
+ },
+
+ /*
+ * Creates the review request from an existing commit.
+ *
+ * This can only be used for new ReviewRequest instances, and requires
+ * a commitID option.
+ */
+ createFromCommit: function(options, context) {
+ console.assert(options.commitID);
+ console.assert(this.isNew());
+
+ this.set('commitID', options.commitID);
+ this.save(
+ _.extend({
+ createFromCommit: true
+ }, options),
+ context);
+ },
+
+ /*
+ * Creates a Diff object for this review request.
+ */
+ createDiff: function() {
+ return new RB.Diff({
+ parentObject: this
+ });
+ },
+
+ /*
+ * Creates a Review object for this review request.
+ *
+ * If an ID is specified, the Review object will reference that ID.
+ * Otherwise, it is considered a draft review, and will either return
+ * the existing one (if the draftReview attribute is set), or create
+ * a new one (and set the attribute).
+ */
+ createReview: function(reviewID) {
+ var review;
+
+ if (reviewID === undefined) {
+ review = this.get('draftReview');
+
+ if (review === null) {
+ review = new RB.DraftReview({
+ parentObject: this
+ });
+
+ this.set('draftReview', review);
+ }
+
+ return review;
+ } else {
+ review = this.reviews.get(reviewID);
+
+ if (!review) {
+ review = new RB.Review({
+ parentObject: this,
+ id: reviewID
+ });
+ this.reviews.add(review);
+ }
+ }
+
+ return review;
+ },
+
+ /*
+ * Creates a Screenshot object for this review request.
+ */
+ createScreenshot: function(screenshotID) {
+ return new RB.Screenshot({
+ parentObject: this,
+ id: screenshotID
+ });
+ },
+
+ /*
+ * Creates a FileAttachment object for this review request.
+ */
+ createFileAttachment: function(attributes) {
+ return new RB.FileAttachment(_.defaults({
+ parentObject: this
+ }, attributes));
+ },
+
+ /*
+ * Marks a review request as starred or unstarred.
+ */
+ setStarred: function(starred, options, context) {
+ var watched = RB.UserSession.instance.watchedReviewRequests;
+
+ if (starred) {
+ watched.addImmediately(this, options, context);
+ } else {
+ watched.removeImmediately(this, options, context);
+ }
+ },
+
+ /*
+ * Closes the review request.
+ *
+ * A 'type' option must be provided, which must match one of the
+ * close types (ReviewRequest.CLOSE_DISCARDED or
+ * ReviewRequest.CLOSE_SUBMITTED).
+ *
+ * An optional description can be set by passing a 'description' option.
+ */
+ close: function(options, context) {
+ var data = {},
+ changingState,
+ saveOptions;
+
+ console.assert(options);
+
+ if (options.type === RB.ReviewRequest.CLOSE_DISCARDED) {
+ data.status = 'discarded';
+ } else if (options.type === RB.ReviewRequest.CLOSE_SUBMITTED) {
+ data.status = 'submitted';
+ } else {
+ if (_.isFunction(options.error)) {
+ options.error.call(this, {
+ errorText: 'Invalid close type'
+ });
+ }
+
+ return;
+ }
+
+ if (options.description !== undefined) {
+ data.close_description = options.description;
+ }
+
+ if (options.richText !== undefined) {
+ data.close_description_text_type =
+ (options.richText ? 'markdown' : 'plain');
+ }
+
+ if (options.postData !== undefined) {
+ _.extend(data, options.postData);
+ }
+
+ changingState = (options.type !== this.get('state'));
+
+ saveOptions = _.defaults({
+ data: data,
+
+ success: _.bind(function() {
+ if (changingState) {
+ this.trigger('closed');
+ }
+
+ this.markUpdated(this.get('lastUpdated'));
+
+ if (_.isFunction(options.success)) {
+ options.success.call(context);
+ }
+ }, this)
+ }, options);
+
+ delete saveOptions.type;
+ delete saveOptions.description;
+
+ this.save(saveOptions, context);
+ },
+
+ /*
+ * Reopens the review request.
+ */
+ reopen: function(options, context) {
+ options = options || {};
+
+ this.save(
+ _.defaults({
+ data: {
+ status: 'pending'
+ },
+
+ success: _.bind(function() {
+ this.trigger('reopened');
+ this.markUpdated(this.get('lastUpdated'));
+
+ if (_.isFunction(options.success)) {
+ options.success.call(context);
+ }
+ }, this)
+ }, options),
+ context);
+ },
+
+ /*
+ * Marks the review request as having been updated at the given timestamp.
+ *
+ * This should be used when an action will trigger an update to the
+ * review request's Last Updated timestamp, but where we don't want
+ * a notification later on. The local copy of the timestamp can be
+ * bumped to mark it as up-to-date.
+ */
+ markUpdated: function(timestamp) {
+ this._lastUpdateTimestamp = timestamp;
+ },
+
+ /*
+ * Begins checking for server-side updates to the review request.
+ *
+ * This takes a type of update to check for, and the last known
+ * updated timestamp.
+ *
+ * The 'updated' event will be triggered when there's a new update.
+ */
+ beginCheckForUpdates: function(type, lastUpdateTimestamp) {
+ this._checkUpdatesType = type;
+ this._lastUpdateTimestamp = lastUpdateTimestamp;
+
+ this.ready({
+ ready: function() {
+ setTimeout(_.bind(this._checkForUpdates, this),
+ RB.ReviewRequest.CHECK_UPDATES_MSECS);
+ }
+ }, this);
+ },
+
+ /*
+ * Checks for updates.
+ *
+ * This is called periodically after an initial call to
+ * beginCheckForUpdates. It will see if there's a new update yet on the
+ * server, and if there is, trigger the 'updated' event.
+ */
+ _checkForUpdates: function() {
+ RB.apiCall({
+ type: 'GET',
+ prefix: this.get('sitePrefix'),
+ noActivityIndicator: true,
+ url: this.get('links').last_update.href,
+ success: _.bind(function(rsp) {
+ var lastUpdate = rsp.last_update;
+ if ((this._checkUpdatesType === undefined ||
+ this._checkUpdatesType === lastUpdate.type) &&
+ this._lastUpdateTimestamp !== lastUpdate.timestamp) {
+ this.trigger('updated', lastUpdate);
+ }
+
+ this._lastUpdateTimestamp = lastUpdate.timestamp;
+
+ setTimeout(_.bind(this._checkForUpdates, this),
+ RB.ReviewRequest.CHECK_UPDATES_MSECS);
+ }, this)
+ });
+ },
+
+ /*
+ * Serialize for sending to the server.
+ */
+ toJSON: function(options) {
+ var commitID = this.get('commitID'),
+ repository = this.get('repository'),
+ result = {};
+
+ options = options || {};
+
+ if (this.isNew()) {
+ if (commitID) {
+ result.commit_id = commitID;
+
+ if (options.createFromCommit) {
+ result.create_from_commit_id = true;
+ }
+ }
+
+ if (repository) {
+ result.repository = repository;
+ }
+
+ return result;
+ } else {
+ return _super(this).toJSON.apply(this, arguments);
+ }
+ },
+
+ /*
+ * Deserialize the response from the server.
+ */
+ parseResourceData: function(rsp) {
+ var state = {
+ pending: RB.ReviewRequest.PENDING,
+ discarded: RB.ReviewRequest.CLOSE_DISCARDED,
+ submitted: RB.ReviewRequest.CLOSE_SUBMITTED
+ }[rsp.status],
+ rawTextFields = rsp.raw_text_fields || rsp,
+ data = RB.BaseResource.prototype.parseResourceData.call(this, rsp);
+
+ data.state = state;
+ data.closeDescriptionRichText =
+ (rawTextFields.close_description_text_type === 'markdown');
+ data.descriptionRichText =
+ (rawTextFields.description_text_type === 'markdown');
+ data.testingDoneRichText =
+ (rawTextFields.testing_done_text_type === 'markdown');
+
+ return data;
+ }
+}, {
+ CHECK_UPDATES_MSECS: 5 * 60 * 1000, // Every 5 minutes
+
+ CLOSE_DISCARDED: 1,
+ CLOSE_SUBMITTED: 2,
+ PENDING: 3,
+
+ VISIBILITY_VISIBLE: 1,
+ VISIBILITY_ARCHIVED: 2,
+ VISIBILITY_MUTED: 3
+});
--- a/reviewboard/reviewboard/staticbundles.py
+++ b/reviewboard/reviewboard/staticbundles.py
@@ -121,32 +121,32 @@ PIPELINE_JS = dict({
'rb/js/resources/models/baseResourceModel.js',
'rb/js/resources/models/apiTokenModel.js',
'rb/js/resources/models/repositoryBranchModel.js',
'rb/js/resources/models/repositoryCommitModel.js',
'rb/js/resources/models/draftResourceChildModelMixin.js',
'rb/js/resources/models/draftResourceModelMixin.js',
'rb/js/resources/models/draftReviewRequestModel.js',
'rb/js/resources/models/reviewModel.js',
- 'rb/js/resources/models/draftReviewModel.js',
- 'rb/js/resources/models/baseCommentModel.js',
+ 'rb/js/resources/models/draftReviewModel_mozreview.js',
+ 'rb/js/resources/models/baseCommentModel_mozreview.js',
'rb/js/resources/models/baseCommentReplyModel.js',
'rb/js/resources/models/defaultReviewerModel.js',
- 'rb/js/resources/models/diffCommentModel.js',
+ 'rb/js/resources/models/diffCommentModel_mozreview.js',
'rb/js/resources/models/diffCommentReplyModel.js',
'rb/js/resources/models/diffModel.js',
'rb/js/resources/models/fileAttachmentModel.js',
'rb/js/resources/models/fileAttachmentCommentModel.js',
'rb/js/resources/models/fileAttachmentCommentReplyModel.js',
'rb/js/resources/models/fileDiffModel.js',
'rb/js/resources/models/draftFileAttachmentModel.js',
'rb/js/resources/models/repositoryModel.js',
'rb/js/resources/models/reviewGroupModel.js',
'rb/js/resources/models/reviewReplyModel.js',
- 'rb/js/resources/models/reviewRequestModel.js',
+ 'rb/js/resources/models/reviewRequestModel_mozreview.js',
'rb/js/resources/models/screenshotModel.js',
'rb/js/resources/models/screenshotCommentModel.js',
'rb/js/resources/models/screenshotCommentReplyModel.js',
'rb/js/resources/models/validateDiffModel.js',
'rb/js/resources/collections/resourceCollection.js',
'rb/js/resources/collections/repositoryBranchesCollection.js',
'rb/js/resources/collections/repositoryCommitsCollection.js',
'rb/js/ui/views/dialogView.js',
@@ -237,17 +237,17 @@ PIPELINE_JS = dict({
'rb/js/views/textBasedReviewableView.js',
'rb/js/views/textCommentRowSelector.js',
'rb/js/views/markdownReviewableView.js',
'rb/js/views/uploadDiffView.js',
'rb/js/views/updateDiffView.js',
'rb/js/diffviewer/models/diffCommentBlockModel.js',
'rb/js/diffviewer/models/diffCommentsHintModel.js',
'rb/js/diffviewer/models/diffFileModel.js',
- 'rb/js/diffviewer/models/diffReviewableModel.js',
+ 'rb/js/diffviewer/models/diffReviewableModel_mozreview.js',
'rb/js/diffviewer/models/diffRevisionModel.js',
'rb/js/diffviewer/models/paginationModel.js',
'rb/js/diffviewer/collections/diffFileCollection.js',
'rb/js/diffviewer/views/chunkHighlighterView.js',
'rb/js/diffviewer/views/diffCommentBlockView.js',
'rb/js/diffviewer/views/diffCommentsHintView.js',
'rb/js/diffviewer/views/diffComplexityIconView.js',
'rb/js/diffviewer/views/diffFileIndexView.js',