mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-02 20:07:25 -05:00
7bab895dee
Previously, when diplomats submit translations, the system would try to figure out whether it should be a 'patch' or a 'change', and then would either create a patch for an admin or artisan to review and accept or reject, or would apply the changes immediately and they would be live. This was done as a compromise between getting translations live quickly, but also preventing already-translated text from getting overwritten without oversight. But having the client handle this added logical complexity. So this makes all diplomats submit patches, no matter what. The server is then in charge of deciding if it should auto-accept the patch or not. Either way, a patch is created. There was also much refactoring. This commit includes: * Update jsondiffpatch so changes within array items are handled correctly * Refactor posting patches to use the new auto-accepting logic, and out of Patch model * Refactor POST /db/patch/:handle/status so that it doesn't rely on handlers * Refactor patch stat handling to ensure auto-accepted patches are counted * Refactor User.incrementStat to use mongodb update commands, to avoid race conditions * Refactor Patch tests
193 lines
5.8 KiB
CoffeeScript
193 lines
5.8 KiB
CoffeeScript
RootView = require 'views/core/RootView'
|
|
locale = require 'locale/locale'
|
|
Patch = require 'models/Patch'
|
|
Patches = require 'collections/Patches'
|
|
PatchModal = require 'views/editor/PatchModal'
|
|
template = require 'templates/i18n/i18n-edit-model-view'
|
|
deltasLib = require 'core/deltas'
|
|
|
|
###
|
|
This view is the superclass for all views which Diplomats use to submit translations
|
|
for database documents. They all work mostly the same, except they each set their
|
|
`@modelClass` which is a patchable Backbone model class, and they use `@wrapRow()`
|
|
to dynamically specify which properties are being translated.
|
|
###
|
|
|
|
UNSAVED_CHANGES_MESSAGE = 'You have unsaved changes! Really discard them?'
|
|
|
|
module.exports = class I18NEditModelView extends RootView
|
|
className: 'editor i18n-edit-model-view'
|
|
template: template
|
|
|
|
events:
|
|
'input .translation-input': 'onInputChanged'
|
|
'change #language-select': 'onLanguageSelectChanged'
|
|
'click #patch-submit': 'onSubmitPatch'
|
|
'click .open-patch-link': 'onClickOpenPatchLink'
|
|
|
|
constructor: (options, @modelHandle) ->
|
|
super(options)
|
|
|
|
@model = new @modelClass(_id: @modelHandle)
|
|
@supermodel.trackRequest(@model.fetch())
|
|
@patches = new Patches()
|
|
@listenTo @patches, 'change', -> @renderSelectors('#patches-col')
|
|
@patches.comparator = '_id'
|
|
@supermodel.trackRequest(@patches.fetchMineFor(@model))
|
|
|
|
@selectedLanguage = me.get('preferredLanguage', true)
|
|
@madeChanges = false
|
|
|
|
showLoading: ($el) ->
|
|
$el ?= @$el.find('.outer-content')
|
|
super($el)
|
|
|
|
onLoaded: ->
|
|
super()
|
|
@originalModel = @model.clone()
|
|
|
|
getRenderData: ->
|
|
c = super()
|
|
|
|
c.model = @model
|
|
c.selectedLanguage = @selectedLanguage
|
|
|
|
@translationList = []
|
|
if @supermodel.finished() then @buildTranslationList() else []
|
|
result.index = index for result, index in @translationList
|
|
c.translationList = @translationList
|
|
|
|
c
|
|
|
|
afterRender: ->
|
|
super()
|
|
|
|
@ignoreLanguageSelectChanges = true
|
|
$select = @$el.find('#language-select').empty()
|
|
@addLanguagesToSelect($select, @selectedLanguage)
|
|
@$el.find('option[value="en-US"]').remove()
|
|
@ignoreLanguageSelectChanges = false
|
|
editors = []
|
|
|
|
@$el.find('tr[data-format="markdown"]').each((index, el) =>
|
|
foundEnEl = enEl=$(el).find('.english-value-row div')[0]
|
|
if foundEnEl?
|
|
englishEditor = ace.edit(foundEnEl)
|
|
englishEditor.el = enEl
|
|
englishEditor.setReadOnly(true)
|
|
editors.push englishEditor
|
|
foundToEl = toEl=$(el).find('.to-value-row div')[0]
|
|
if foundToEl?
|
|
toEditor = ace.edit(foundToEl)
|
|
toEditor.el = toEl
|
|
toEditor.on 'change', @onEditorChange
|
|
editors.push toEditor
|
|
)
|
|
|
|
for editor in editors
|
|
session = editor.getSession()
|
|
session.setTabSize 2
|
|
session.setMode 'ace/mode/markdown'
|
|
session.setNewLineMode = 'unix'
|
|
session.setUseSoftTabs true
|
|
editor.setOptions({ maxLines: Infinity })
|
|
|
|
onEditorChange: (event, editor) =>
|
|
return if @destroyed
|
|
index = $(editor.el).data('index')
|
|
rowInfo = @translationList[index]
|
|
value = editor.getValue()
|
|
@onTranslationChanged(rowInfo, value)
|
|
|
|
wrapRow: (title, key, enValue, toValue, path, format) ->
|
|
@translationList.push {
|
|
title: title,
|
|
key: key,
|
|
enValue: enValue,
|
|
toValue: toValue or '',
|
|
path: path
|
|
format: format
|
|
}
|
|
|
|
buildTranslationList: -> [] # overwrite
|
|
|
|
onInputChanged: (e) ->
|
|
index = $(e.target).data('index')
|
|
rowInfo = @translationList[index]
|
|
value = $(e.target).val()
|
|
@onTranslationChanged(rowInfo, value)
|
|
|
|
onTranslationChanged: (rowInfo, value) ->
|
|
#- Navigate down to where the translation will live
|
|
base = @model.attributes
|
|
|
|
for seg in rowInfo.path
|
|
base = base[seg]
|
|
|
|
base = base.i18n
|
|
|
|
base[@selectedLanguage] ?= {}
|
|
base = base[@selectedLanguage]
|
|
|
|
if rowInfo.key.length > 1
|
|
for seg in rowInfo.key[..-2]
|
|
base[seg] ?= {}
|
|
base = base[seg]
|
|
|
|
#- Set the data in a non-kosher way
|
|
base[rowInfo.key[rowInfo.key.length-1]] = value
|
|
@model.saveBackup()
|
|
|
|
#- Enable patch submit button
|
|
@$el.find('#patch-submit').attr('disabled', null)
|
|
@madeChanges = true
|
|
|
|
onLanguageSelectChanged: (e) ->
|
|
return if @ignoreLanguageSelectChanges
|
|
if @madeChanges
|
|
return unless confirm(UNSAVED_CHANGES_MESSAGE)
|
|
@selectedLanguage = $(e.target).val()
|
|
if @selectedLanguage
|
|
me.set('preferredLanguage', @selectedLanguage)
|
|
me.patch()
|
|
@madeChanges = false
|
|
@model.set(@originalModel.clone().attributes)
|
|
@render()
|
|
|
|
onClickOpenPatchLink: (e) ->
|
|
patchID = $(e.currentTarget).data('patch-id')
|
|
patch = @patches.get(patchID)
|
|
modal = new PatchModal(patch, @model)
|
|
@openModalView(modal)
|
|
|
|
onLeaveMessage: ->
|
|
if @madeChanges
|
|
return UNSAVED_CHANGES_MESSAGE
|
|
|
|
onSubmitPatch: (e) ->
|
|
delta = @originalModel.getDeltaWith(@model)
|
|
flattened = deltasLib.flattenDelta(delta)
|
|
collection = _.string.underscored @model.constructor.className
|
|
patch = new Patch({
|
|
delta
|
|
target: { collection, 'id': @model.id }
|
|
commitMessage: "Diplomat submission for lang #{@selectedLanguage}: #{flattened.length} change(s)."
|
|
})
|
|
errors = patch.validate()
|
|
button = $(e.target)
|
|
button.attr('disabled', 'disabled')
|
|
return button.text('No changes submitted, did not save patch.') unless delta
|
|
return button.text('Failed to Submit Changes') if errors
|
|
res = patch.save(null, { url: _.result(@model, 'url') + '/patch' })
|
|
return button.text('Failed to Submit Changes') unless res
|
|
button.text('Submitting...')
|
|
Promise.resolve(res)
|
|
.then =>
|
|
@madeChanges = false
|
|
@patches.add(patch)
|
|
@renderSelectors('#patches-col')
|
|
button.text('Submit Changes')
|
|
.catch =>
|
|
button.text('Error Submitting Changes')
|
|
@$el.find('#patch-submit').attr('disabled', null)
|
|
|