mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Built diplomat-specific views for editing levels, components, achievements and thang types.
This commit is contained in:
parent
2dbdacd958
commit
cea04d27ad
37 changed files with 627 additions and 28 deletions
|
@ -72,6 +72,12 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
|
||||
'github/*path': 'routeToServer'
|
||||
|
||||
'i18n': go('i18n/I18NHomeView')
|
||||
'i18n/thang/:handle': go('i18n/I18NEditThangTypeView')
|
||||
'i18n/component/:handle': go('i18n/I18NEditComponentView')
|
||||
'i18n/level/:handle': go('i18n/I18NEditLevelView')
|
||||
'i18n/achievement/:handle': go('i18n/I18NEditAchievementView')
|
||||
|
||||
'legal': go('LegalView')
|
||||
|
||||
'multiplayer': go('MultiplayerView')
|
||||
|
|
|
@ -175,3 +175,7 @@ prunePath = (delta, path) ->
|
|||
prunePath delta[path[0]], path.slice(1) unless delta[path[0]] is undefined
|
||||
keys = (k for k in _.keys(delta[path[0]]) when k isnt '_t')
|
||||
delete delta[path[0]] if keys.length is 0
|
||||
|
||||
module.exports.DOC_SKIP_PATHS = [
|
||||
'_id','version', 'commitMessage', 'parent', 'created',
|
||||
'slug', 'index', '__v', 'patches', 'creator', 'js', 'watchers']
|
|
@ -536,6 +536,7 @@
|
|||
achievement_query_misc: "Key achievement off of miscellanea"
|
||||
achievement_query_goals: "Key achievement off of level goals"
|
||||
level_completion: "Level Completion"
|
||||
pop_i18n: "Populate I18N"
|
||||
|
||||
article:
|
||||
edit_btn_preview: "Preview"
|
||||
|
|
|
@ -233,7 +233,7 @@ class CocoModel extends Backbone.Model
|
|||
|
||||
getDelta: ->
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
differ.diff @_revertAttributes, @attributes
|
||||
differ.diff(_.omit(@_revertAttributes, deltasLib.DOC_SKIP_PATHS), _.omit(@attributes, deltasLib.DOC_SKIP_PATHS))
|
||||
|
||||
getDeltaWith: (otherModel) ->
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
|
@ -272,9 +272,11 @@ class CocoModel extends Backbone.Model
|
|||
sum = 0
|
||||
data ?= $.extend true, {}, @attributes
|
||||
schema ?= @schema() or {}
|
||||
addedI18N = false
|
||||
if schema.properties?.i18n and _.isPlainObject(data) and not data.i18n?
|
||||
data.i18n = {'-':'-'} # mongoose doesn't work with empty objects
|
||||
sum += 1
|
||||
addedI18N = true
|
||||
|
||||
if _.isPlainObject data
|
||||
for key, value of data
|
||||
|
@ -287,6 +289,7 @@ class CocoModel extends Backbone.Model
|
|||
if schema.items and _.isArray data
|
||||
sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data
|
||||
|
||||
@set('i18n', data.i18n) if addedI18N and not path # need special case for root i18n
|
||||
@updateI18NCoverage()
|
||||
sum
|
||||
|
||||
|
@ -343,10 +346,8 @@ class CocoModel extends Backbone.Model
|
|||
|
||||
updateI18NCoverage: ->
|
||||
i18nObjects = @findI18NObjects()
|
||||
console.log 'i18n objects', i18nObjects
|
||||
return unless i18nObjects.length
|
||||
langCodeArrays = (_.keys(i18n) for i18n in i18nObjects)
|
||||
console.log 'lang code arrays', langCodeArrays
|
||||
window.codes = langCodeArrays
|
||||
@set('i18nCoverage', _.intersection(langCodeArrays...))
|
||||
|
||||
findI18NObjects: (data, results) ->
|
||||
|
|
|
@ -78,10 +78,7 @@ _.extend AchievementSchema.properties,
|
|||
default: {kind: 'linear', parameters: {}}
|
||||
required: ['kind', 'parameters']
|
||||
additionalProperties: false
|
||||
i18n: c.object
|
||||
format: 'i18n'
|
||||
props: ['name', 'description']
|
||||
description: 'Help translate this achievement'
|
||||
i18n: {type: 'object', format: 'i18n', props: ['name', 'description'], description: 'Help translate this achievement'}
|
||||
rewards: c.RewardSchema 'awarded by this achievement'
|
||||
|
||||
|
||||
|
@ -93,5 +90,6 @@ _.extend AchievementSchema, # Let's have these on the bottom
|
|||
AchievementSchema.definitions = {}
|
||||
AchievementSchema.definitions['mongoQueryOperator'] = MongoQueryOperatorSchema
|
||||
AchievementSchema.definitions['mongoFindQuery'] = MongoFindQuerySchema
|
||||
c.extendTranslationCoverageProperties AchievementSchema
|
||||
|
||||
module.exports = AchievementSchema
|
||||
|
|
|
@ -23,6 +23,12 @@ PropertyDocumentationSchema = c.object {
|
|||
required: ['name', 'type', 'description']
|
||||
},
|
||||
name: {type: 'string', title: 'Name', description: 'Name of the property.'}
|
||||
i18n: { type: 'object', format: 'i18n', props: ['description', 'context'], description: 'Help translate this property'}
|
||||
context: {
|
||||
type: 'object'
|
||||
title: 'Example template context'
|
||||
additionalProperties: { type: 'string' }
|
||||
}
|
||||
codeLanguages: c.array {title: 'Specific Code Languages', description: 'If present, then only the languages specified will show this documentation. Leave unset for language-independent documentation.', format: 'code-languages-array'}, c.shortString(title: 'Code Language', description: 'A specific code language to show this documentation for.', format: 'code-language')
|
||||
# not actual JS types, just whatever they describe...
|
||||
type: c.shortString(title: 'Type', description: 'Intended type of the property.')
|
||||
|
@ -84,6 +90,7 @@ PropertyDocumentationSchema = c.object {
|
|||
}
|
||||
{title: 'Description', type: 'string', description: 'Description of the return value.', maxLength: 1000}
|
||||
]
|
||||
i18n: { type: 'object', format: 'i18n', props: ['description'], description: 'Help translate this return value'}
|
||||
|
||||
DependencySchema = c.object {
|
||||
title: 'Component Dependency'
|
||||
|
@ -155,5 +162,6 @@ c.extendSearchableProperties LevelComponentSchema
|
|||
c.extendVersionedProperties LevelComponentSchema, 'level.component'
|
||||
c.extendPermissionsProperties LevelComponentSchema, 'level.component'
|
||||
c.extendPatchableProperties LevelComponentSchema
|
||||
c.extendTranslationCoverageProperties LevelComponentSchema
|
||||
|
||||
module.exports = LevelComponentSchema
|
||||
|
|
|
@ -116,6 +116,7 @@ _.extend ThangTypeSchema.properties,
|
|||
rotationType: {title: 'Rotation', type: 'string', enum: ['isometric', 'fixed', 'free']}
|
||||
matchWorldDimensions: {title: 'Match World Dimensions', type: 'boolean'}
|
||||
shadow: {title: 'Shadow Diameter', type: 'number', format: 'meters', description: 'Shadow diameter in meters'}
|
||||
description: { type:'string', format: 'markdown', title: 'Description' }
|
||||
layerPriority:
|
||||
title: 'Layer Priority'
|
||||
type: 'integer'
|
||||
|
@ -144,6 +145,7 @@ _.extend ThangTypeSchema.properties,
|
|||
type: 'number'
|
||||
description: 'Snap to this many meters in the y-direction.'
|
||||
components: c.array {title: 'Components', description: 'Thangs are configured by changing the Components attached to them.', uniqueItems: true, format: 'thang-components-array'}, ThangComponentSchema # TODO: uniqueness should be based on 'original', not whole thing
|
||||
i18n: {type: 'object', format: 'i18n', props: ['name', 'description'], description: 'Help translate this ThangType\'s name and description.'}
|
||||
|
||||
ThangTypeSchema.required = []
|
||||
|
||||
|
@ -158,5 +160,6 @@ c.extendBasicProperties ThangTypeSchema, 'thang.type'
|
|||
c.extendSearchableProperties ThangTypeSchema
|
||||
c.extendVersionedProperties ThangTypeSchema, 'thang.type'
|
||||
c.extendPatchableProperties ThangTypeSchema
|
||||
c.extendTranslationCoverageProperties ThangTypeSchema
|
||||
|
||||
module.exports = ThangTypeSchema
|
||||
|
|
|
@ -166,6 +166,7 @@ me.FunctionArgumentSchema = me.object {
|
|||
required: ['name', 'type', 'example', 'description']
|
||||
},
|
||||
name: {type: 'string', pattern: me.identifierPattern, title: 'Name', description: 'Name of the function argument.'}
|
||||
i18n: { type: 'object', format: 'i18n', props: ['description'], description: 'Help translate this argument'}
|
||||
# not actual JS types, just whatever they describe...
|
||||
type: me.shortString(title: 'Type', description: 'Intended type of the argument.')
|
||||
example:
|
||||
|
|
12
app/styles/i18n/i18n-edit-model-view.sass
Normal file
12
app/styles/i18n/i18n-edit-model-view.sass
Normal file
|
@ -0,0 +1,12 @@
|
|||
.i18n-edit-model-view
|
||||
#patch-submit
|
||||
margin-top: 5px
|
||||
|
||||
td
|
||||
width: 40%
|
||||
|
||||
.outer-content
|
||||
padding: 10px
|
||||
|
||||
select
|
||||
margin-bottom: 10px
|
|
@ -14,6 +14,7 @@ block content
|
|||
button.achievement-tool-button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-button Recalculate
|
||||
button.achievement-tool-button(data-i18n="common.delete", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#delete-button Delete
|
||||
button.achievement-tool-button(data-i18n="common.save", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#save-button Save
|
||||
button.achievement-tool-button(data-i18n="editor.pop_i18n").btn.btn-primary#populate-i18n-button Populate I18N
|
||||
|
||||
h3(data-i18n="achievement.edit_achievement_title") Edit Achievement
|
||||
span
|
||||
|
|
|
@ -34,7 +34,9 @@ nav.navbar.navbar-default(role='navigation')
|
|||
if !me.get('anonymous')
|
||||
li#create-new-component-button
|
||||
a(data-i18n="editor.level_component_b_new") Create New Component
|
||||
|
||||
li
|
||||
a(data-i18n="editor.pop_i18n")#pop-component-i18n-button Populate i18n
|
||||
|
||||
li.divider
|
||||
li.dropdown-header Info
|
||||
|
||||
|
|
|
@ -56,6 +56,8 @@ block header
|
|||
a(data-i18n="common.fork")#fork-start-button Fork
|
||||
li(class=anonymous ? "disabled": "")
|
||||
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert
|
||||
li(class=anonymous ? "disabled": "")
|
||||
a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n
|
||||
li.divider
|
||||
li.dropdown-header Info
|
||||
li#history-button
|
||||
|
|
71
app/templates/i18n/i18n-edit-model-view.jade
Normal file
71
app/templates/i18n/i18n-edit-model-view.jade
Normal file
|
@ -0,0 +1,71 @@
|
|||
extends /templates/base
|
||||
|
||||
block header
|
||||
if model.loading
|
||||
nav.navbar.navbar-default(role='navigation')
|
||||
.container-fluid
|
||||
ul.nav.navbar-nav
|
||||
li
|
||||
a(href="/i18n")
|
||||
span.glyphicon-home.glyphicon
|
||||
|
||||
else
|
||||
nav.navbar.navbar-default(role='navigation')
|
||||
ul.nav.navbar-nav
|
||||
li
|
||||
a(href="/i18n")
|
||||
span.glyphicon-home.glyphicon
|
||||
|
||||
.navbar-header
|
||||
span.navbar-brand #{model.get('name')}
|
||||
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li
|
||||
button.btn.btn-info.btn-sm.pull-right#patch-submit(disabled=model.hasLocalChanges() ? null : 'disabled', value=model.id) Submit Changes
|
||||
|
||||
li.dropdown
|
||||
|
||||
a(data-toggle='dropdown')
|
||||
span.glyphicon-chevron-down.glyphicon
|
||||
|
||||
ul.dropdown-menu
|
||||
li.dropdown-header Actions
|
||||
|
||||
li(class=anonymous ? "disabled": "")
|
||||
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert
|
||||
|
||||
li.divider
|
||||
li.dropdown-header Info
|
||||
li#history-button
|
||||
a(href='#', data-i18n="general.version_history") Version History
|
||||
li.divider
|
||||
li.dropdown-header Help
|
||||
li
|
||||
a(href='https://github.com/codecombat/codecombat/wiki', data-i18n="editor.wiki", target="_blank") Wiki
|
||||
li
|
||||
a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat", target="_blank") Live Chat
|
||||
li
|
||||
a(href='http://discourse.codecombat.com/category/diplomat', data-i18n="nav.forum", target="_blank") Forum
|
||||
li
|
||||
a(data-toggle="coco-modal", data-target="modal/ContactModal", data-i18n="nav.contact") Email
|
||||
|
||||
block outer_content
|
||||
.outer-content
|
||||
|
||||
select.form-control#language-select
|
||||
|
||||
table.table
|
||||
for row in translationList
|
||||
tr(data-format=row.format || '')
|
||||
th= row.title
|
||||
td.english-value-row
|
||||
div= row.enValue
|
||||
td.to-value-row
|
||||
if row.format === 'markdown'
|
||||
div(data-index=row.index.toString())= row.toValue
|
||||
else
|
||||
input.input-sm.form-control.translation-input(data-index=row.index.toString(), value=row.toValue)
|
||||
|
||||
div#error-view
|
||||
.clearfix
|
||||
block footer
|
20
app/templates/i18n/i18n-home-view.jade
Normal file
20
app/templates/i18n/i18n-home-view.jade
Normal file
|
@ -0,0 +1,20 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
table.table.table-condensed
|
||||
tr
|
||||
th
|
||||
select#language-select.form-control.input-sm
|
||||
|
||||
th Type
|
||||
th Specifically Covered
|
||||
th Generally Covered
|
||||
|
||||
for model in collection.models
|
||||
tr
|
||||
td
|
||||
a(href=model.i18nURLBase+model.get('slug'))= model.get('name')
|
||||
td= model.constructor.className
|
||||
td(class=model.specificallyCovered ? 'success' : 'danger')= model.specificallyCovered ? 'Yes' : 'No'
|
||||
td(class=model.generallyCovered ? 'success' : 'danger')= model.generallyCovered ? 'Yes' : 'No'
|
||||
|
|
@ -93,14 +93,14 @@ module.exports = class DeltaView extends CocoView
|
|||
treemaOptions = { schema: deltaData.schema or {}, readOnly: true }
|
||||
|
||||
if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
|
||||
options = _.defaults {data: deltaData.left}, treemaOptions
|
||||
options = _.defaults {data: _.merge({}, deltaData.left)}, treemaOptions
|
||||
try
|
||||
TreemaNode.make(leftEl, options).build()
|
||||
catch error
|
||||
console.error "Couldn't show left details Treema for", deltaData.left, treemaOptions
|
||||
|
||||
if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
|
||||
options = _.defaults {data: deltaData.right}, treemaOptions
|
||||
options = _.defaults {data: _.merge({}, deltaData.right)}, treemaOptions
|
||||
try
|
||||
TreemaNode.make(rightEl, options).build()
|
||||
catch error
|
||||
|
|
|
@ -2,15 +2,13 @@ ModalView = require 'views/kinds/ModalView'
|
|||
template = require 'templates/editor/patch_modal'
|
||||
DeltaView = require 'views/editor/DeltaView'
|
||||
auth = require 'lib/auth'
|
||||
deltasLib = require 'lib/deltas'
|
||||
|
||||
module.exports = class PatchModal extends ModalView
|
||||
id: 'patch-modal'
|
||||
template: template
|
||||
plain: true
|
||||
modalWidthPercent: 60
|
||||
@DOC_SKIP_PATHS = [
|
||||
'_id','version', 'commitMessage', 'parent', 'created',
|
||||
'slug', 'index', '__v', 'patches', 'creator', 'js', 'watchers']
|
||||
|
||||
events:
|
||||
'click #withdraw-button': 'withdrawPatch'
|
||||
|
@ -54,7 +52,7 @@ module.exports = class PatchModal extends ModalView
|
|||
|
||||
afterRender: ->
|
||||
return super() unless @supermodel.finished() and @deltaWorked
|
||||
@deltaView = new DeltaView({model:@pendingModel, headModel:@headModel, skipPaths: PatchModal.DOC_SKIP_PATHS})
|
||||
@deltaView = new DeltaView({model:@pendingModel, headModel:@headModel, skipPaths: deltasLib.DOC_SKIP_PATHS})
|
||||
changeEl = @$el.find('.changes-stub')
|
||||
@insertSubView(@deltaView, changeEl)
|
||||
super()
|
||||
|
|
|
@ -16,7 +16,9 @@ module.exports = class AchievementEditView extends RootView
|
|||
'click #recalculate-button': 'confirmRecalculation'
|
||||
'click #recalculate-all-button': 'confirmAllRecalculation'
|
||||
'click #delete-button': 'confirmDeletion'
|
||||
|
||||
'click #populate-i18n-button': -> @achievement.populateI18N()
|
||||
|
||||
|
||||
constructor: (options, @achievementID) ->
|
||||
super options
|
||||
@achievement = new Achievement(_id: @achievementID)
|
||||
|
|
|
@ -20,6 +20,7 @@ module.exports = class LevelComponentEditView extends CocoView
|
|||
'click #component-history-button': 'showVersionHistory'
|
||||
'click #patch-component-button': 'startPatchingComponent'
|
||||
'click #component-watch-button': 'toggleWatchComponent'
|
||||
'click #pop-component-i18n-button': -> @levelComponent.populateI18N()
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
|
|
@ -5,6 +5,7 @@ LevelComponent = require 'models/LevelComponent'
|
|||
LevelSystem = require 'models/LevelSystem'
|
||||
DeltaView = require 'views/editor/DeltaView'
|
||||
PatchModal = require 'views/editor/PatchModal'
|
||||
deltasLib = require 'lib/deltas'
|
||||
|
||||
module.exports = class SaveLevelModal extends SaveVersionModal
|
||||
template: template
|
||||
|
@ -40,7 +41,7 @@ module.exports = class SaveLevelModal extends SaveVersionModal
|
|||
for changeEl, i in changeEls
|
||||
model = models[i]
|
||||
try
|
||||
deltaView = new DeltaView({model: model, skipPaths: PatchModal.DOC_SKIP_PATHS})
|
||||
deltaView = new DeltaView({model: model, skipPaths: deltasLib.DOC_SKIP_PATHS})
|
||||
@insertSubView(deltaView, $(changeEl))
|
||||
catch e
|
||||
console.error 'Couldn\'t create delta view:', e
|
||||
|
|
|
@ -45,6 +45,7 @@ module.exports = class ThangTypeEditView extends RootView
|
|||
'click .play-with-level-button': 'onPlayLevel'
|
||||
'click .play-with-level-parent': 'onPlayLevelSelect'
|
||||
'keyup .play-with-level-input': 'onPlayLevelKeyUp'
|
||||
'click #pop-level-i18n-button': 'onPopulateLevelI18N'
|
||||
|
||||
subscriptions:
|
||||
'editor:thang-type-color-groups-changed': 'onColorGroupsChanged'
|
||||
|
@ -352,7 +353,8 @@ module.exports = class ThangTypeEditView extends RootView
|
|||
saveNewThangType: (e) ->
|
||||
newThangType = if e.major then @thangType.cloneNewMajorVersion() else @thangType.cloneNewMinorVersion()
|
||||
newThangType.set('commitMessage', e.commitMessage)
|
||||
|
||||
newThangType.updateI18NCoverage() if newThangType.get('i18nCoverage')
|
||||
|
||||
res = newThangType.save()
|
||||
return unless res
|
||||
modal = $('#save-version-modal')
|
||||
|
@ -455,6 +457,10 @@ module.exports = class ThangTypeEditView extends RootView
|
|||
showVersionHistory: (e) ->
|
||||
@openModalView new ThangTypeVersionsModal thangType: @thangType, @thangTypeID
|
||||
|
||||
onPopulateLevelI18N: ->
|
||||
@thangType.populateI18N()
|
||||
_.delay((-> document.location.reload()), 500)
|
||||
|
||||
openSaveModal: ->
|
||||
@openModalView new SaveVersionModal model: @thangType
|
||||
|
||||
|
|
16
app/views/i18n/I18NEditAchievementView.coffee
Normal file
16
app/views/i18n/I18NEditAchievementView.coffee
Normal file
|
@ -0,0 +1,16 @@
|
|||
I18NEditModelView = require './I18NEditModelView'
|
||||
Achievement = require 'models/Achievement'
|
||||
|
||||
module.exports = class I18NEditAchievementView extends I18NEditModelView
|
||||
id: "i18n-edit-component-view"
|
||||
modelClass: Achievement
|
||||
|
||||
buildTranslationList: ->
|
||||
lang = @selectedLanguage
|
||||
|
||||
# name, description
|
||||
if i18n = @model.get('i18n')
|
||||
if name = @model.get('name')
|
||||
@wrapRow "Achievement name", ['name'], name, i18n[lang]?.name, []
|
||||
if description = @model.get('description')
|
||||
@wrapRow "Achievement description", ['description'], description, i18n[lang]?.description, []
|
44
app/views/i18n/I18NEditComponentView.coffee
Normal file
44
app/views/i18n/I18NEditComponentView.coffee
Normal file
|
@ -0,0 +1,44 @@
|
|||
I18NEditModelView = require './I18NEditModelView'
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
|
||||
module.exports = class I18NEditComponentView extends I18NEditModelView
|
||||
id: "i18n-edit-component-view"
|
||||
modelClass: LevelComponent
|
||||
|
||||
buildTranslationList: ->
|
||||
lang = @selectedLanguage
|
||||
|
||||
propDocs = @model.get('propertyDocumentation')
|
||||
|
||||
for propDoc, propDocIndex in propDocs
|
||||
|
||||
#- Component property descriptions
|
||||
if i18n = propDoc.i18n
|
||||
path = ["propertyDocumentation", propDocIndex]
|
||||
if _.isObject propDoc.description
|
||||
for progLang, description of propDoc
|
||||
@wrapRow "#{propDoc.name} description (#{progLang})", [progLang, 'description'], description, i18n[lang]?[progLang]?.description, path, 'markdown'
|
||||
else if _.isString propDoc.description
|
||||
@wrapRow "#{propDoc.name} description", ['description'], propDoc.description, i18n[lang]?.description, path, 'markdown'
|
||||
|
||||
#- Component return value descriptions
|
||||
if i18n = propDoc.returns?.i18n
|
||||
path = ["propertyDocumentation", propDocIndex, "returns"]
|
||||
d = propDoc.returns.description
|
||||
if _.isObject d
|
||||
for progLang, description of d
|
||||
@wrapRow "#{propDoc.name} return val (#{progLang})", [progLang, 'description'], description, i18n[lang]?[progLang]?.description, path, 'markdown'
|
||||
else if _.isString d
|
||||
@wrapRow "#{propDoc.name} return val", ['description'], d, i18n[lang]?.description, path, 'markdown'
|
||||
|
||||
#- Component argument descriptions
|
||||
if propDoc.args
|
||||
for argDoc, argIndex in propDoc.args
|
||||
if i18n = argDoc.i18n
|
||||
path = ["propertyDocumentation", propDocIndex, 'args', argIndex]
|
||||
if _.isObject argDoc.description
|
||||
for progLang, description of argDoc
|
||||
@wrapRow "#{propDoc.name} arg description #{argDoc.name} (#{progLang})", [progLang, 'description'], description, i18n[lang]?[progLang]?.description, path, 'markdown'
|
||||
else if _.isString argDoc.description
|
||||
@wrapRow "#{propDoc.name} arg description #{argDoc.name}", ['description'], argDoc.description, i18n[lang]?.description, path, 'markdown'
|
||||
|
48
app/views/i18n/I18NEditLevelView.coffee
Normal file
48
app/views/i18n/I18NEditLevelView.coffee
Normal file
|
@ -0,0 +1,48 @@
|
|||
I18NEditModelView = require './I18NEditModelView'
|
||||
Level = require 'models/Level'
|
||||
|
||||
module.exports = class I18NEditLevelView extends I18NEditModelView
|
||||
id: "i18n-edit-level-view"
|
||||
modelClass: Level
|
||||
|
||||
buildTranslationList: ->
|
||||
lang = @selectedLanguage
|
||||
|
||||
# name, description
|
||||
if i18n = @model.get('i18n')
|
||||
if name = @model.get('name')
|
||||
@wrapRow "Level name", ['name'], name, i18n[lang]?.name, []
|
||||
if description = @model.get('description')
|
||||
@wrapRow "Level description", ['description'], description, i18n[lang]?.description, []
|
||||
|
||||
# goals
|
||||
for goal, index in @model.get('goals') ? []
|
||||
if i18n = goal.i18n
|
||||
@wrapRow "Goal name", ['name'], goal.name, i18n[lang]?.name, ['goals', index]
|
||||
|
||||
# documentation
|
||||
for doc, index in @model.get('documentation')?.specificArticles ? []
|
||||
if i18n = doc.i18n
|
||||
@wrapRow "Guide article name", ['name'], doc.name, i18n[lang]?.name, ['documentation', 'specificArticles', index]
|
||||
@wrapRow "'#{doc.name}' description", ['description'], doc.description, i18n[lang]?.description, ['documentation', 'specificArticles', index], 'markdown'
|
||||
|
||||
# sprite dialogues
|
||||
for script, scriptIndex in @model.get('scripts') ? []
|
||||
for noteGroup, noteGroupIndex in script.noteChain ? []
|
||||
for spriteCommand, spriteCommandIndex in noteGroup.sprites ? []
|
||||
pathPrefix = ['scripts', scriptIndex, 'noteChain', noteGroupIndex, 'sprites', spriteCommandIndex, 'say']
|
||||
|
||||
if i18n = spriteCommand.say?.i18n
|
||||
if spriteCommand.say.text
|
||||
@wrapRow "Sprite text", ['text'], spriteCommand.say.text, i18n[lang]?.text, pathPrefix, 'markdown'
|
||||
if spriteCommand.say.blurb
|
||||
@wrapRow "Sprite blurb", ['blurb'], spriteCommand.say.blurb, i18n[lang]?.blurb, pathPrefix
|
||||
|
||||
for response, responseIndex in spriteCommand.say?.responses ? []
|
||||
if i18n = response.i18n
|
||||
@wrapRow "Response button", ['text'], response.text, i18n[lang]?.text, pathPrefix.concat(['responses', responseIndex])
|
||||
|
||||
# victory modal
|
||||
if i18n = @model.get('victory')?.i18n
|
||||
@wrapRow "Victory text", ['body'], @model.get('victory').body, i18n[lang]?.body, ['victory'], 'markdown'
|
||||
|
159
app/views/i18n/I18NEditModelView.coffee
Normal file
159
app/views/i18n/I18NEditModelView.coffee
Normal file
|
@ -0,0 +1,159 @@
|
|||
RootView = require 'views/kinds/RootView'
|
||||
locale = require 'locale/locale'
|
||||
Patch = require 'models/Patch'
|
||||
template = require 'templates/i18n/i18n-edit-model-view'
|
||||
deltasLib = require 'lib/deltas'
|
||||
|
||||
module.exports = class I18NEditModelView extends RootView
|
||||
className: 'editor i18n-edit-model-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'change .translation-input': 'onInputChanged'
|
||||
'change #language-select': 'onLanguageSelectChanged'
|
||||
'click #patch-submit': 'onSubmitPatch'
|
||||
|
||||
constructor: (options, @modelHandle) ->
|
||||
super(options)
|
||||
@model = new @modelClass(_id: @modelHandle)
|
||||
@model = @supermodel.loadModel(@model, 'model').model
|
||||
@model.saveBackups = true
|
||||
@selectedLanguage = me.get('preferredLanguage', true)
|
||||
|
||||
showLoading: ($el) ->
|
||||
$el ?= @$el.find('.outer-content')
|
||||
super($el)
|
||||
|
||||
onLoaded: ->
|
||||
super()
|
||||
@model.markToRevert() unless @model.hasLocalChanges()
|
||||
|
||||
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()
|
||||
|
||||
@hush = true
|
||||
$select = @$el.find('#language-select').empty()
|
||||
@addLanguagesToSelect($select, @selectedLanguage)
|
||||
@hush = false
|
||||
editors = []
|
||||
|
||||
@$el.find('tr[data-format="markdown"]').each((index, el) =>
|
||||
englishEditor = ace.edit(enEl=$(el).find('.english-value-row div')[0])
|
||||
englishEditor.el = enEl
|
||||
englishEditor.setReadOnly(true)
|
||||
toEditor = ace.edit(toEl=$(el).find('.to-value-row div')[0])
|
||||
toEditor.el = toEl
|
||||
toEditor.on 'change', @onEditorChange
|
||||
editors = editors.concat([englishEditor, 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)
|
||||
|
||||
onLanguageSelectChanged: (e) ->
|
||||
return if @hush
|
||||
@selectedLanguage = $(e.target).val()
|
||||
@render()
|
||||
|
||||
onSubmitPatch: (e) ->
|
||||
|
||||
delta = @model.getDelta()
|
||||
flattened = deltasLib.flattenDelta(delta)
|
||||
save = _.all(flattened, (delta) ->
|
||||
return _.isArray(delta.o) and delta.o.length is 1 and 'i18n' in delta.dataPath
|
||||
)
|
||||
|
||||
if save
|
||||
modelToSave = @model.cloneNewMinorVersion()
|
||||
modelToSave.updateI18NCoverage() if modelToSave.get('i18nCoverage')
|
||||
|
||||
else
|
||||
modelToSave = new Patch()
|
||||
modelToSave.set 'delta', @model.getDelta()
|
||||
modelToSave.set 'target', {
|
||||
'collection': _.string.underscored @model.constructor.className
|
||||
'id': @model.id
|
||||
}
|
||||
|
||||
if @modelClass.schema.properties.commitMessage
|
||||
commitMessage = "Diplomat submission for lang #{@selectedLanguage}: #{flattened.length} change(s)."
|
||||
modelToSave.set 'commitMessage', commitMessage
|
||||
|
||||
errors = modelToSave.validate()
|
||||
button = $(e.target)
|
||||
button.attr('disabled', 'disabled')
|
||||
return button.text('Failed to Submit Changes') if errors
|
||||
res = modelToSave.save()
|
||||
return button.text('Failed to Submit Changes') unless res
|
||||
res.error => button.text('Error Submitting Changes')
|
||||
res.success => button.text('Submit Changes')
|
15
app/views/i18n/I18NEditThangTypeView.coffee
Normal file
15
app/views/i18n/I18NEditThangTypeView.coffee
Normal file
|
@ -0,0 +1,15 @@
|
|||
I18NEditModelView = require './I18NEditModelView'
|
||||
ThangType = require 'models/ThangType'
|
||||
|
||||
module.exports = class ThangTypeI18NView extends I18NEditModelView
|
||||
id: "thang-type-i18n-view"
|
||||
modelClass: ThangType
|
||||
|
||||
buildTranslationList: ->
|
||||
lang = @selectedLanguage
|
||||
@model.markToRevert() unless @model.hasLocalChanges()
|
||||
i18n = @model.get('i18n')
|
||||
if i18n
|
||||
name = @model.get('name')
|
||||
@wrapRow('Name', ['name'], name, i18n[lang]?.name, [])
|
||||
@wrapRow('Description', ['description'], @model.get('description'), i18n[lang]?.description, [], 'markdown')
|
87
app/views/i18n/I18NHomeView.coffee
Normal file
87
app/views/i18n/I18NHomeView.coffee
Normal file
|
@ -0,0 +1,87 @@
|
|||
RootView = require 'views/kinds/RootView'
|
||||
template = require 'templates/i18n/i18n-home-view'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
ThangType = require 'models/ThangType'
|
||||
Level = require 'models/Level'
|
||||
Achievement = require 'models/Achievement'
|
||||
|
||||
languages = _.keys(require 'locale/locale').sort()
|
||||
PAGE_SIZE = 100
|
||||
|
||||
module.exports = class I18NHomeView extends RootView
|
||||
id: "i18n-home-view"
|
||||
template: template
|
||||
|
||||
events:
|
||||
'change #language-select': 'onLanguageSelectChanged'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@selectedLanguage = me.get('preferredLanguage', true)
|
||||
|
||||
#-
|
||||
@aggregateModels = new Backbone.Collection()
|
||||
project = ['name', 'components.original', 'i18nCoverage', 'slug']
|
||||
|
||||
@thangTypes = new CocoCollection([], { url: '/db/thang.type?view=i18n-coverage', project: project, model: ThangType })
|
||||
@components = new CocoCollection([], { url: '/db/level.component?view=i18n-coverage', project: project, model: LevelComponent })
|
||||
@levels = new CocoCollection([], { url: '/db/level?view=i18n-coverage', project: project, model: Level })
|
||||
@achievements = new CocoCollection([], { url: '/db/achievement?view=i18n-coverage', project: project, model: Achievement })
|
||||
|
||||
for c in [@thangTypes, @components, @levels, @achievements]
|
||||
c.skip = 0
|
||||
c.fetch({data: {skip: 0, limit: PAGE_SIZE}, cache:false})
|
||||
@supermodel.loadCollection(c, 'documents')
|
||||
@listenTo c, 'sync', @onCollectionSynced
|
||||
|
||||
|
||||
onCollectionSynced: (collection) ->
|
||||
for model in collection.models
|
||||
model.i18nURLBase = switch model.constructor.className
|
||||
when "ThangType" then "/i18n/thang/"
|
||||
when "LevelComponent" then "/i18n/component/"
|
||||
when "Achievement" then "/i18n/achievement/"
|
||||
when "Level" then "/i18n/level/"
|
||||
getMore = collection.models.length is PAGE_SIZE
|
||||
@aggregateModels.add(collection.models)
|
||||
@render()
|
||||
|
||||
if getMore
|
||||
collection.skip += PAGE_SIZE
|
||||
collection.fetch({data: {skip: collection.skip, limit: PAGE_SIZE}})
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
@updateCoverage()
|
||||
c.languages = languages
|
||||
c.selectedLanguage = @selectedLanguage
|
||||
c.collection = @aggregateModels
|
||||
c
|
||||
|
||||
updateCoverage: ->
|
||||
selectedBase = @selectedLanguage[..2]
|
||||
relatedLanguages = (l for l in languages when l.startsWith(selectedBase) and l isnt @selectedLanguage)
|
||||
for model in @aggregateModels.models
|
||||
@updateCoverageForModel(model, relatedLanguages)
|
||||
model.generallyCovered = true if @selectedLanguage.startsWith 'en'
|
||||
|
||||
updateCoverageForModel: (model, relatedLanguages) ->
|
||||
model.specificallyCovered = true
|
||||
model.generallyCovered = true
|
||||
coverage = model.get('i18nCoverage')
|
||||
|
||||
if @selectedLanguage not in coverage
|
||||
model.specificallyCovered = false
|
||||
if not _.any((l in coverage for l in relatedLanguages))
|
||||
model.generallyCovered = false
|
||||
return
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@addLanguagesToSelect(@$el.find('#language-select'), @selectedLanguage)
|
||||
|
||||
onLanguageSelectChanged: (e) ->
|
||||
@selectedLanguage = $(e.target).val()
|
||||
@render()
|
|
@ -106,15 +106,20 @@ module.exports = class RootView extends CocoView
|
|||
$select.parent().find('.options, .trigger').remove()
|
||||
$select.unwrap().removeClass('fancified')
|
||||
preferred = me.get('preferredLanguage', true)
|
||||
@addLanguagesToSelect($select, preferred)
|
||||
$select.fancySelect().parent().find('.trigger').addClass('header-font')
|
||||
$('body').attr('lang', preferred)
|
||||
|
||||
addLanguagesToSelect: ($select, initialVal) ->
|
||||
initialVal ?= me.get('preferredLanguage', true)
|
||||
codes = _.keys(locale)
|
||||
genericCodes = _.filter codes, (code) ->
|
||||
_.find(codes, (code2) ->
|
||||
code2 isnt code and code2.split('-')[0] is code)
|
||||
for code, localeInfo of locale when not (code in genericCodes) or code is preferred
|
||||
for code, localeInfo of locale when not (code in genericCodes) or code is initialVal
|
||||
$select.append(
|
||||
$('<option></option>').val(code).text(localeInfo.nativeDescription))
|
||||
$select.val(preferred).fancySelect().parent().find('.trigger').addClass('header-font')
|
||||
$('body').attr('lang', preferred)
|
||||
$select.val(initialVal)
|
||||
|
||||
onLanguageChanged: ->
|
||||
newLang = $('.language-dropdown').val()
|
||||
|
|
|
@ -4,6 +4,7 @@ DeltaView = require 'views/editor/DeltaView'
|
|||
PatchModal = require 'views/editor/PatchModal'
|
||||
nameLoader = require 'lib/NameLoader'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
deltasLib = require 'lib/deltas'
|
||||
|
||||
class VersionsViewCollection extends CocoCollection
|
||||
url: ''
|
||||
|
@ -55,7 +56,7 @@ module.exports = class VersionsModal extends ModalView
|
|||
@deltaView = new DeltaView({
|
||||
model: earlierVersion
|
||||
comparisonModel: laterVersion
|
||||
skipPaths: PatchModal.DOC_SKIP_PATHS
|
||||
skipPaths: deltasLib.DOC_SKIP_PATHS
|
||||
loadModels: true
|
||||
})
|
||||
@insertSubView(@deltaView, deltaEl)
|
||||
|
|
|
@ -68,7 +68,8 @@
|
|||
"node-gyp": "~0.13.0",
|
||||
"aether": "~0.2.39",
|
||||
"JASON": "~0.1.3",
|
||||
"JQDeferred": "~2.1.0"
|
||||
"JQDeferred": "~2.1.0",
|
||||
"jsondiffpatch": "0.1.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jade": "0.33.x",
|
||||
|
|
|
@ -64,6 +64,7 @@ AchievementSchema.post 'save', -> @constructor.loadAchievements()
|
|||
|
||||
AchievementSchema.plugin(plugins.NamedPlugin)
|
||||
AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']})
|
||||
AchievementSchema.plugin plugins.TranslationCoveragePlugin
|
||||
|
||||
module.exports = Achievement = mongoose.model('Achievement', AchievementSchema, 'achievements')
|
||||
|
||||
|
|
|
@ -5,13 +5,37 @@ class AchievementHandler extends Handler
|
|||
modelClass: Achievement
|
||||
|
||||
# Used to determine which properties requests may edit
|
||||
editableProperties: ['name', 'query', 'worth', 'collection', 'description', 'userField', 'proportionalTo', 'icon', 'function', 'related', 'difficulty', 'category', 'rewards']
|
||||
editableProperties: [
|
||||
'name'
|
||||
'query'
|
||||
'worth'
|
||||
'collection'
|
||||
'description'
|
||||
'userField'
|
||||
'proportionalTo'
|
||||
'icon'
|
||||
'function'
|
||||
'related'
|
||||
'difficulty'
|
||||
'category'
|
||||
'rewards'
|
||||
'i18n'
|
||||
'i18nCoverage'
|
||||
]
|
||||
|
||||
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||
jsonSchema = require '../../app/schemas/models/achievement.coffee'
|
||||
|
||||
|
||||
hasAccess: (req) ->
|
||||
req.method is 'GET' or req.user?.isAdmin()
|
||||
req.method in ['GET', 'PUT'] or req.user?.isAdmin()
|
||||
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
method = (method or req.method).toLowerCase()
|
||||
return true if method is 'get'
|
||||
return true if req.user?.isAdmin()
|
||||
return true if method is 'put' and @isJustFillingTranslations(req, document)
|
||||
return
|
||||
|
||||
get: (req, res) ->
|
||||
# /db/achievement?related=<ID>
|
||||
|
|
|
@ -7,6 +7,7 @@ Patch = require '../patches/Patch'
|
|||
User = require '../users/User'
|
||||
sendwithus = require '../sendwithus'
|
||||
hipchat = require '../hipchat'
|
||||
deltasLib = require '../../app/lib/deltas'
|
||||
|
||||
PROJECT = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
||||
FETCH_LIMIT = 300
|
||||
|
@ -32,9 +33,22 @@ module.exports = class Handler
|
|||
hasAccess: (req) -> true
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
return true if req.user?.isAdmin()
|
||||
|
||||
if @modelClass.schema.uses_coco_translation_coverage and (method or req.method).toLowerCase() is 'put'
|
||||
return true if @isJustFillingTranslations(req, document)
|
||||
|
||||
if @modelClass.schema.uses_coco_permissions
|
||||
return document.hasPermissionsForMethod?(req.user, method or req.method)
|
||||
return true
|
||||
|
||||
isJustFillingTranslations: (req, document) ->
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
omissions = ['original'].concat(deltasLib.DOC_SKIP_PATHS)
|
||||
delta = differ.diff(_.omit(document.toObject(), omissions), _.omit(req.body, omissions))
|
||||
flattened = deltasLib.flattenDelta(delta)
|
||||
_.all(flattened, (delta) ->
|
||||
# sometimes coverage gets moved around... allow other changes to happen to i18nCoverage
|
||||
return _.isArray(delta.o) and (('i18n' in delta.dataPath and delta.o.length is 1) or 'i18nCoverage' in delta.dataPath))
|
||||
|
||||
formatEntity: (req, document) -> document?.toObject()
|
||||
getEditableProperties: (req, document) ->
|
||||
|
@ -87,6 +101,27 @@ module.exports = class Handler
|
|||
get: (req, res) ->
|
||||
@sendForbiddenError(res) if not @hasAccess(req)
|
||||
|
||||
if @modelClass.schema.uses_coco_translation_coverage and req.query.view is 'i18n-coverage'
|
||||
# TODO: generalize view, project, limit and skip query parameters
|
||||
projection = {}
|
||||
if req.query.project
|
||||
projection[field] = 1 for field in req.query.project.split(',')
|
||||
query = {slug: {$exists: true}, i18nCoverage: {$exists: true}}
|
||||
q = @modelClass.find(query, projection)
|
||||
|
||||
skip = parseInt(req.query.skip)
|
||||
if skip? and skip < 1000000
|
||||
q.skip(skip)
|
||||
|
||||
limit = parseInt(req.query.limit)
|
||||
if limit? and limit < 1000
|
||||
q.limit(limit)
|
||||
|
||||
q.exec (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
documents = (@formatEntity(req, doc) for doc in documents)
|
||||
@sendSuccess(res, documents)
|
||||
|
||||
specialParameters = ['term', 'project', 'conditions']
|
||||
|
||||
# If the model uses coco search it's probably a text search
|
||||
|
|
|
@ -12,6 +12,7 @@ LevelComponentSchema.plugin plugins.PermissionsPlugin
|
|||
LevelComponentSchema.plugin plugins.VersionedPlugin
|
||||
LevelComponentSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'searchStrings', 'description']}
|
||||
LevelComponentSchema.plugin plugins.PatchablePlugin
|
||||
LevelComponentSchema.plugin plugins.TranslationCoveragePlugin
|
||||
LevelComponentSchema.pre('save', (next) ->
|
||||
name = @get('name')
|
||||
strings = _.str.humanize(name).toLowerCase().split(' ')
|
||||
|
|
|
@ -14,6 +14,7 @@ LevelComponentHandler = class LevelComponentHandler extends Handler
|
|||
'propertyDocumentation'
|
||||
'configSchema'
|
||||
'name'
|
||||
'i18nCoverage'
|
||||
]
|
||||
|
||||
getEditableProperties: (req, document) ->
|
||||
|
|
|
@ -28,6 +28,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
'banner'
|
||||
'employerDescription'
|
||||
'terrain'
|
||||
'i18nCoverage'
|
||||
]
|
||||
|
||||
postEditableProperties: ['name']
|
||||
|
|
|
@ -36,13 +36,22 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
|||
'featureImage'
|
||||
'spriteType'
|
||||
'i18nCoverage'
|
||||
'i18n'
|
||||
'description'
|
||||
]
|
||||
|
||||
hasAccess: (req) ->
|
||||
req.method is 'GET' or req.user?.isAdmin()
|
||||
req.method in ['GET', 'PUT'] or req.user?.isAdmin()
|
||||
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
method = (method or req.method).toLowerCase()
|
||||
return true if method is 'get'
|
||||
return true if req.user?.isAdmin()
|
||||
return true if method is 'put' and @isJustFillingTranslations(req, document)
|
||||
return
|
||||
|
||||
get: (req, res) ->
|
||||
if req.query.view in ['items', 'heroes']
|
||||
if req.query.view in ['items', 'heroes', 'i18n-coverage']
|
||||
projection = {}
|
||||
if req.query.project
|
||||
projection[field] = 1 for field in req.query.project.split(',')
|
||||
|
@ -52,7 +61,19 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
|||
else if req.query.view is 'heroes'
|
||||
query.kind = 'Unit'
|
||||
query.original = {$in: _.values heroes} # TODO: replace with some sort of ThangType property later
|
||||
ThangType.find(query, projection).exec (err, documents) =>
|
||||
else if req.query.view is 'i18n-coverage'
|
||||
query.i18nCoverage = {$exists: true}
|
||||
|
||||
q = ThangType.find(query, projection)
|
||||
skip = parseInt(req.query.skip)
|
||||
if skip? and skip < 1000000
|
||||
q.skip(skip)
|
||||
|
||||
limit = parseInt(req.query.limit)
|
||||
if limit? and limit < 1000
|
||||
q.limit(limit)
|
||||
|
||||
q.exec (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
documents = (@formatEntity(req, doc) for doc in documents)
|
||||
@sendSuccess(res, documents)
|
||||
|
|
|
@ -14,6 +14,7 @@ config = require './server_config'
|
|||
auth = require './server/routes/auth'
|
||||
UserHandler = require './server/users/user_handler'
|
||||
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
||||
global.jsondiffpatch = require 'jsondiffpatch'
|
||||
|
||||
productionLogging = (tokens, req, res) ->
|
||||
status = res.statusCode
|
||||
|
|
Loading…
Reference in a new issue