Merge pull request #1221 from codecombat/master

Master into production
This commit is contained in:
Michael Schmatz 2014-06-24 11:30:28 -07:00
commit 22b7e52f98
19 changed files with 88 additions and 25 deletions

View file

@ -1,8 +1,8 @@
module.exports = initializeGoogle = ->
onGPlusLoaded = ->
window.onGPlusLoaded = ->
Backbone.Mediator.publish "gapi-loaded"
return
signinCallback = (authResult) ->
window.signinCallback = (authResult) ->
Backbone.Mediator.publish "gplus-logged-in", authResult if authResult["access_token"]
return
(->

View file

@ -288,10 +288,13 @@ module.exports = class Camera extends CocoClass
boundTarget: (pos, zoom) ->
# Given an {x, y} in Surface coordinates, return one that will keep our viewport on the Surface.
return pos unless @bounds
y = pos.y
if thang = pos.sprite?.thang
y = @worldToSurface(x: thang.pos.x, y: thang.pos.y).y # ignore z
marginX = (@canvasWidth / zoom / 2)
marginY = (@canvasHeight / zoom / 2)
x = Math.min(Math.max(marginX + @bounds.x, pos.x + @offset.x), @bounds.x + @bounds.width - marginX)
y = Math.min(Math.max(marginY + @bounds.y, pos.y + @offset.y), @bounds.y + @bounds.height - marginY)
y = Math.min(Math.max(marginY + @bounds.y, y + @offset.y), @bounds.y + @bounds.height - marginY)
{x: x, y: y}
updateViewports: (target) ->

View file

@ -64,7 +64,7 @@ class CocoModel extends Backbone.Model
@backedUp = {}
schema: -> return @constructor.schema
getValidationErrors: ->
errors = tv4.validateMultiple(@attributes, @constructor.schema or {}).errors
return errors if errors?.length
@ -76,7 +76,7 @@ class CocoModel extends Backbone.Model
for error in errors
console.debug "\t", error.dataPath, ":", error.message
return errors
save: (attrs, options) ->
options ?= {}
options.headers ?= {}
@ -97,19 +97,19 @@ class CocoModel extends Backbone.Model
noty text: "#{errorMessage}: #{res.status} #{res.statusText}", layout: 'topCenter', type: 'error', killer: false, timeout: 10000
@trigger "save", @
return super attrs, options
patch: (options) ->
return false unless @_revertAttributes
options ?= {}
options.patch = true
attrs = {_id: @id}
keys = []
for key in _.keys @attributes
unless _.isEqual @attributes[key], @_revertAttributes[key]
attrs[key] = @attributes[key]
keys.push key
return unless keys.length
console.debug 'Patching', @get('name') or @, keys
@save(attrs, options)
@ -207,7 +207,10 @@ class CocoModel extends Backbone.Model
applyDelta: (delta) ->
newAttributes = $.extend(true, {}, @attributes)
jsondiffpatch.patch newAttributes, delta
try
jsondiffpatch.patch newAttributes, delta
catch error
console.error "Error applying delta", delta, "to attributes", newAttributes, error
@set newAttributes
getExpandedDelta: ->

View file

@ -122,7 +122,7 @@ module.exports = class SuperModel extends Backbone.Model
return @progress is 1.0 or not @denom
addModelResource: (modelOrCollection, name, fetchOptions, value=1) ->
modelOrCollection.saveBackups = @shouldSaveBackups(modelOrCollection)
modelOrCollection.saveBackups = modelOrCollection.saveBackups or @shouldSaveBackups(modelOrCollection)
@checkName(name)
res = new ModelResource(modelOrCollection, name, fetchOptions, value)
@storeResource(res, value)

View file

@ -49,6 +49,9 @@
tr
cursor: pointer
tr.expired
opacity: 0.5
code
background-color: rgb(220, 220, 220)
color: #555

View file

@ -1,5 +1,21 @@
#docs-modal .modal-dialog
width: 800px
.tab-content
padding-top: 20px
li:not(.active) a[data-toggle="tab"]
cursor: pointer
img
display: block
margin: 0 auto
em
display: block
margin: 0 auto
text-align: center
hr
border-color: #5c5c5c
width: 80%

View file

@ -93,7 +93,9 @@ block content
for candidate, index in area.candidates
- var profile = candidate.get('jobProfile');
- var authorized = candidate.id; // If we have the id, then we are authorized.
tr(data-candidate-id=candidate.id, id=candidate.id)
- var profileAge = (new Date() - new Date(profile.updated)) / 86400 / 1000;
- var expired = profileAge > 2 * 30.4;
tr(data-candidate-id=candidate.id, id=candidate.id, class=expired ? "expired" : "")
td
if authorized
img(src=candidate.getPhotoURL(50), alt=profile.name, title=profile.name, height=50)
@ -115,7 +117,7 @@ block content
code= skill
span
td= profile.experience
td(data-profile-age=(new Date() - new Date(profile.updated)) / 86400 / 1000)= moment(profile.updated).fromNow()
td(data-profile-age=profileAge)= moment(profile.updated).fromNow()
if me.isAdmin()
td= remarks[candidate.id] ? remarks[candidate.id].get('contactName') : ''
if me.isAdmin() && area.id == 'inactive-candidates'

View file

@ -22,7 +22,11 @@ block content
p
strong Tournament ended!
a(href="#winners") Behold the winners
| . Thanks for playing!
| . Thanks for playing! You can
strong still play
| Greed and all of our other
a(href="/play/ladder") multiplayer arenas
| .
p
| Want to commiserate? Head over to
a(href="http://discourse.codecombat.com/") the forum

View file

@ -1,7 +1,7 @@
ul#primary-goals-list
div.goals-status
strong(data-i18n="play_level.goals") Goals
span.spl.spr :
span.spr :
span(data-i18n="play_level.success").secret.goal-status.success Success!
span(data-i18n="play_level.incomplete").secret.goal-status.incomplete Incomplete
span(data-i18n="play_level.timed_out").secret.goal-status.timed-out Ran out of time

View file

@ -22,6 +22,7 @@ adminContacts = [
{id: "5162fab9c92b4c751e000274", name: "Scott"}
{id: "51eb2714fa058cb20d0006ef", name: "Michael"}
{id: "51538fdb812dd9af02000001", name: "George"}
{id: "52a57252a89409700d0000d9", name: "Ignore"}
]
module.exports = class ProfileView extends View
@ -104,6 +105,7 @@ module.exports = class ProfileView extends View
@linkedinLoaded = true
if @waitingForLinkedIn
@renderLinkedInButton()
@authorizedWithLinkedIn = IN?.User?.isAuthorized()
renderLinkedInButton: =>
IN?.parse()

View file

@ -81,7 +81,7 @@ module.exports = class ArticleEditView extends View
@preview.focus() if window.focus
@preview.onload = => @pushChangesToPreview()
return false
openSaveModal: ->
@openModalView(new SaveVersionModal({model: @article}))
@ -101,6 +101,7 @@ module.exports = class ArticleEditView extends View
@disableModalInProgress(modal)
res.success =>
@article.clearBackup()
modal.modal('hide')
url = "/editor/article/#{newArticle.get('slug') or newArticle.id}"
document.location.href = url

View file

@ -76,7 +76,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
else if @level.isPublished() and not newModel.isPublished()
newModel.publish() # Publish any LevelComponents that weren't published yet
formsToSave.push form
for model in modelsToSave
if errors = model.getValidationErrors()
messages = ("\t #{error.dataPath}: #{error.message}" for error in errors)
@ -96,6 +96,8 @@ module.exports = class LevelSaveView extends SaveVersionModal
forms.applyErrorsToForm($(form), JSON.parse(res.responseText))
res.success =>
modelsToSave = _.without modelsToSave, newModel
oldModel = _.find @supermodel.models, (m) -> m.get('original') is newModel.get('original')
oldModel.clearBackup() # Otherwise looking at old versions is confusing.
unless modelsToSave.length
url = "/editor/level/#{@level.get('slug') or @level.id}"
document.location.href = url

View file

@ -66,7 +66,7 @@ module.exports = class ThangTypeEditView extends View
raw = ("raw:#{name}" for name in raw)
main = _.keys(@thangType.get('actions') or {})
main.concat(raw)
afterRender: ->
super()
return unless @supermodel.finished()
@ -326,7 +326,9 @@ module.exports = class ThangTypeEditView extends View
image = @currentSprite.imageObject.image
portraitSource = imageToPortrait image
# bit of a hacky way to get that portrait
success = -> document.location.href = url
success = =>
@thangType.clearBackup()
document.location.href = url
newThangType.uploadGenericPortrait success, portraitSource
clearRawData: ->
@ -419,4 +421,4 @@ imageToPortrait = (img) ->
scaleY = 100 / img.height
ctx.scale scaleX, scaleY
ctx.drawImage img, 0, 0
canvas.toDataURL("image/png")
canvas.toDataURL("image/png")

View file

@ -14,9 +14,9 @@ module.exports = class GoldView extends View
super options
@teamGold = {}
@teamGoldEarned = {}
@shownOnce = false
onGoldChanged: (e) ->
@$el.show()
return if @teamGold[e.team] is e.gold and @teamGoldEarned[e.team] is e.goldEarned
@teamGold[e.team] = e.gold
@teamGoldEarned[e.team] = e.goldEarned
@ -30,9 +30,11 @@ module.exports = class GoldView extends View
text += " (#{e.goldEarned})"
goldEl.text text
@updateTitle()
@$el.show()
@shownOnce = true
updateTitle: ->
@$el.attr 'title', ("Team '#{team}' has #{gold} now of #{@teamGoldEarned[team]} gold earned." for team, gold of @teamGold).join ' '
onSetLetterbox: (e) ->
@$el.toggle not e.on
@$el.toggle not e.on if @shownOnce

View file

@ -8,6 +8,7 @@ utils = require 'lib/utils'
module.exports = class DocsModal extends View
template: template
id: 'docs-modal'
plain: true
shortcuts:
'enter': 'hide'

View file

@ -180,8 +180,8 @@ module.exports = class SpellView extends View
return (owner is 'this' or owner is 'more') and (not doc.owner? or doc.owner is 'this')
console.log 'could not find doc for', prop, 'from', e.allDocs['__' + prop], 'for', owner, 'of', e.propGroups unless doc
doc ?= prop
if doc.snippets?
entry =
if doc.snippets?[@spell.language]
entry =
content: doc.snippets[@spell.language].code
name: doc.name
tabTrigger: doc.snippets[@spell.language].tab

View file

@ -3,6 +3,7 @@ fs = require 'fs'
request = require 'request'
mongoose = require('mongoose')
errors = require '../commons/errors'
config = require '../../server_config'
module.exports.setup = (app) ->
app.all '/file*', (req, res) ->
@ -130,6 +131,8 @@ checkExistence = (options, req, res, force, done) ->
errors.conflict(res, {canForce:userCanEditFile(req.user, file)})
done(true)
else if file
fullPath = "/file/#{options.metadata.path}/#{options.filename}"
clearCloudFlareCacheForFile(fullPath)
q = { _id: file._id }
q.root = 'media'
Grid.gfs.remove q, (err) ->
@ -172,3 +175,20 @@ createPostOptions = (req) ->
options.metadata.description = req.body.description if req.body.description?
options
clearCloudFlareCacheForFile = (path='/file') ->
unless config.cloudflare.token
console.log 'skipping clearing cloud cache, not configured'
return
request = require 'request'
r = request.post 'https://www.cloudflare.com/api_json.html', (err, httpResponse, body) ->
if (err)
console.error('CloudFlare file cache clear failed:', body)
form = r.form()
form.append 'tkn', config.cloudflare.token
form.append 'email', 'scott@codecombat.com'
form.append 'z', 'codecombat.com'
form.append 'a', 'zone_file_purge'
form.append 'url', "http://codecombat.com#{path}"

View file

@ -308,9 +308,9 @@ UserHandler = class UserHandler extends Handler
getCandidates: (req, res) ->
authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions'))
since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString()
months = if req.user.isAdmin() then 12 else 2
since = (new Date((new Date()) - months * 30.4 * 86400 * 1000)).toISOString()
query = {'jobProfile.updated': {$gt: since}}
#query.jobProfileApproved = true unless req.user.isAdmin() # We split into featured and other now.
query['jobProfile.active'] = true unless req.user.isAdmin()
selection = 'jobProfile jobProfileApproved photoURL'
selection += ' email name' if authorized

View file

@ -4,6 +4,8 @@ config.unittest = process.argv.indexOf("--unittest") > -1
config.port = process.env.COCO_PORT or process.env.COCO_NODE_PORT or 3000
config.ssl_port = process.env.COCO_SSL_PORT or process.env.COCO_SSL_NODE_PORT or 3443
config.cloudflare =
token: process.env.COCO_CLOUDFLARE_API_KEY or ''
config.mongo =
port: process.env.COCO_MONGO_PORT or 27017