mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-24 21:13:35 -04:00
Course translations fixes
* Restrict patch handling properly * Fix #3860, CS 2 description * i18nCoverage is updated when new translations are auto-accepted * Course patches are listed on PendingPatchesView properly * 'Artisan' permission allows editing course translations
This commit is contained in:
parent
d4af931e05
commit
300c81e72b
8 changed files with 77 additions and 2 deletions
app/views/admin
scripts/mongodb
server
spec/server/functional
|
@ -40,6 +40,8 @@ module.exports = class PendingPatchesView extends RootView
|
|||
"thang/#{patch.slug}"
|
||||
when 'level_system', 'level_component'
|
||||
"level/items?#{patch.target.collection}=#{patch.slug}"
|
||||
when 'course'
|
||||
"course/#{patch.slug}"
|
||||
else
|
||||
console.log "Where do we review a #{patch.target.collection} patch?"
|
||||
''
|
||||
|
|
|
@ -24,7 +24,7 @@ var courses =
|
|||
name: "Computer Science 2",
|
||||
slug: "computer-science-2",
|
||||
campaignID: ObjectId("562f88e84df18473073c74e2"),
|
||||
description: "Introduce Arguments, Variables, If Statements, and Arithmetic.",
|
||||
description: "Introduces arguments, variables, if statements, and arithmetic.",
|
||||
duration: NumberInt(5),
|
||||
free: false,
|
||||
screenshot: "/images/pages/courses/102_info.png",
|
||||
|
|
43
server/commons/i18n.coffee
Normal file
43
server/commons/i18n.coffee
Normal file
|
@ -0,0 +1,43 @@
|
|||
TreemaUtils = require '../../bower_components/treema/treema-utils.js'
|
||||
|
||||
exports.updateI18NCoverage = (doc) ->
|
||||
# TODO: Share this code between server and client (client version in CocoModel)
|
||||
langCodeArrays = []
|
||||
pathToData = {}
|
||||
|
||||
# console.log 'doc schema', doc.schema.statics.jsonSchema, doc.schema
|
||||
TreemaUtils.walk(doc.toObject(), doc.schema.statics.jsonSchema, null, (path, data, workingSchema) ->
|
||||
# Store parent data for the next block...
|
||||
if data?.i18n
|
||||
pathToData[path] = data
|
||||
|
||||
if _.str.endsWith path, 'i18n'
|
||||
i18n = data
|
||||
|
||||
# grab the parent data
|
||||
parentPath = path[0...-5]
|
||||
parentData = pathToData[parentPath]
|
||||
|
||||
# use it to determine what properties actually need to be translated
|
||||
props = workingSchema.props or []
|
||||
props = (prop for prop in props when parentData[prop])
|
||||
#unless props.length
|
||||
# console.log 'props is', props, 'path is', path, 'data is', data, 'parentData is', parentData, 'workingSchema is', workingSchema
|
||||
# langCodeArrays.push _.without _.keys(locale), 'update' # Every language has covered a path with no properties to be translated.
|
||||
# return
|
||||
|
||||
return if 'additionalProperties' of i18n # Workaround for #2630: Programmable is weird
|
||||
|
||||
# get a list of lang codes where its object has keys for every prop to be translated
|
||||
coverage = _.filter(_.keys(i18n), (langCode) ->
|
||||
translations = i18n[langCode]
|
||||
_.all((translations[prop] for prop in props))
|
||||
)
|
||||
#console.log 'got coverage', coverage, 'for', path, props, workingSchema, parentData
|
||||
langCodeArrays.push coverage
|
||||
)
|
||||
|
||||
return unless langCodeArrays.length
|
||||
# language codes that are covered for every i18n object are fully covered
|
||||
overallCoverage = _.intersection(langCodeArrays...)
|
||||
doc.set('i18nCoverage', overallCoverage)
|
|
@ -14,4 +14,10 @@ CourseHandler = class CourseHandler extends Handler
|
|||
hasAccess: (req) ->
|
||||
req.method in @allowedMethods 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() or req.user?.isArtisan()
|
||||
return
|
||||
|
||||
module.exports = new CourseHandler()
|
||||
|
|
|
@ -14,6 +14,7 @@ Patch = require '../models/Patch'
|
|||
tv4 = require('tv4').tv4
|
||||
slack = require '../slack'
|
||||
{ isJustFillingTranslations } = require '../commons/deltas'
|
||||
{ updateI18NCoverage } = require '../commons/i18n'
|
||||
|
||||
module.exports =
|
||||
|
||||
|
@ -115,6 +116,7 @@ module.exports =
|
|||
reasonNotAutoAccepted = 'Adding to existing translations.'
|
||||
else
|
||||
course.set(changedCourse)
|
||||
updateI18NCoverage(course)
|
||||
yield course.save()
|
||||
|
||||
patch = new Patch(req.body)
|
||||
|
|
|
@ -81,7 +81,9 @@ module.exports.setup = (app) ->
|
|||
|
||||
Course = require '../models/Course'
|
||||
app.get('/db/course', mw.courses.get(Course))
|
||||
app.put('/db/course/:handle', mw.auth.checkHasPermission(['admin']), mw.rest.put(Course))
|
||||
app.get('/db/course/names', mw.named.names(Course))
|
||||
app.post('/db/course/names', mw.named.names(Course))
|
||||
app.put('/db/course/:handle', mw.auth.checkHasPermission(['admin', 'artisan']), mw.rest.put(Course))
|
||||
app.get('/db/course/:handle', mw.rest.getByHandle(Course))
|
||||
app.get('/db/course/:handle/level-solutions', mw.courses.fetchLevelSolutions)
|
||||
app.get('/db/course/:handle/levels/:levelOriginal/next', mw.courses.fetchNextLevel)
|
||||
|
|
|
@ -264,6 +264,7 @@ describe 'POST /db/course/:handle/patch', ->
|
|||
course = yield Course.findById(@course.id)
|
||||
expect(course.get('i18n').de.description).toBe('German translation!')
|
||||
expect(course.get('patches')).toBeUndefined()
|
||||
expect(_.contains(course.get('i18nCoverage'),'de')).toBe(true)
|
||||
done()
|
||||
|
||||
it 'saves the changes immediately if translations are for a new langauge', utils.wrap (done) ->
|
||||
|
|
|
@ -3,6 +3,7 @@ User = require '../../../server/models/User'
|
|||
Article = require '../../../server/models/Article'
|
||||
Patch = require '../../../server/models/Patch'
|
||||
request = require '../request'
|
||||
utils = require '../utils'
|
||||
|
||||
describe '/db/patch', ->
|
||||
async = require 'async'
|
||||
|
@ -160,3 +161,21 @@ describe '/db/patch', ->
|
|||
Patch.findOne({}).exec (err, article) ->
|
||||
expect(article.get('status')).toBe 'accepted'
|
||||
done()
|
||||
|
||||
it 'only allows artisans and admins to set patch status for courses', utils.wrap (done) ->
|
||||
submitter = yield utils.initUser()
|
||||
course = yield utils.makeCourse()
|
||||
patch = new Patch({
|
||||
delta: { name: 'test' }
|
||||
target: { collection: 'course', id: course._id, original: course._id }
|
||||
creator: submitter._id
|
||||
status: 'pending'
|
||||
commitMessage: '...'
|
||||
})
|
||||
yield patch.save()
|
||||
anotherUser = yield utils.initUser()
|
||||
yield utils.loginUser(anotherUser)
|
||||
json = { status: 'rejected' }
|
||||
[res, body] = yield request.putAsync({ url: utils.getURL("/db/patch/#{patch.id}/status"), json})
|
||||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue