Merge branch 'master' into feature/defaults
This commit is contained in:
commit
570380c2a4
20 changed files with 221 additions and 57 deletions
app
Router.coffee
lib
styles
docs
editor/level
templates
views
server
server_config.coffee
|
@ -47,7 +47,8 @@ module.exports = class CocoRouter extends Backbone.Router
|
||||||
|
|
||||||
'db/*path': 'routeToServer'
|
'db/*path': 'routeToServer'
|
||||||
'demo(/*subpath)': go('DemoView')
|
'demo(/*subpath)': go('DemoView')
|
||||||
'docs/components': go('docs/ComponentDocumentationView')
|
'docs/components': go('docs/ComponentsDocumentationView')
|
||||||
|
'docs/systems': go('docs/SystemsDocumentationView')
|
||||||
|
|
||||||
'editor': go('CommunityView')
|
'editor': go('CommunityView')
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ class AudioPlayer extends CocoClass
|
||||||
@soundsToPlayWhenLoaded[name] = volume
|
@soundsToPlayWhenLoaded[name] = volume
|
||||||
|
|
||||||
playSound: (name, volume=1, delay=0, pos=null) ->
|
playSound: (name, volume=1, delay=0, pos=null) ->
|
||||||
|
return console.error 'Trying to play empty sound?' unless name
|
||||||
audioOptions = {volume: (me.get('volume') ? 1) * volume, delay: delay}
|
audioOptions = {volume: (me.get('volume') ? 1) * volume, delay: delay}
|
||||||
filename = if _.string.startsWith(name, '/file/') then name else '/file/' + name
|
filename = if _.string.startsWith(name, '/file/') then name else '/file/' + name
|
||||||
unless (filename of cache) and createjs.Sound.loadComplete filename
|
unless (filename of cache) and createjs.Sound.loadComplete filename
|
||||||
|
|
27
app/styles/docs/components-documentation-view.sass
Normal file
27
app/styles/docs/components-documentation-view.sass
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#components-documentation-view
|
||||||
|
background-color: #e4cf8c
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
#toggle-all-component-code
|
||||||
|
margin: 10px
|
||||||
|
|
||||||
|
.container, .row
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.index-column, .documentation-column
|
||||||
|
overflow-x: hidden
|
||||||
|
|
||||||
|
> ul
|
||||||
|
padding: 0px 20px 20px 20px
|
||||||
|
|
||||||
|
.doc-name
|
||||||
|
color: rgb(139, 69, 19)
|
||||||
|
|
||||||
|
.index-column
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
.documentation-column
|
||||||
|
width: 75%
|
||||||
|
|
||||||
|
.special-list, .doc-description, .code-block
|
||||||
|
list-style-type: none
|
|
@ -1,31 +0,0 @@
|
||||||
#docs-components-view
|
|
||||||
color: saddlebrown
|
|
||||||
|
|
||||||
.row
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.index-column, .documentation-column
|
|
||||||
overflow-x: hidden
|
|
||||||
min-height: 600px
|
|
||||||
|
|
||||||
> ul
|
|
||||||
padding: 0px 20px 20px 20px
|
|
||||||
|
|
||||||
.doc-name
|
|
||||||
color: rgb(139, 69, 19)
|
|
||||||
|
|
||||||
.index-column
|
|
||||||
width: 25%
|
|
||||||
|
|
||||||
.documentation-column
|
|
||||||
width: 75%
|
|
||||||
|
|
||||||
.specialList
|
|
||||||
list-style-type: none
|
|
||||||
|
|
||||||
.doc-description
|
|
||||||
list-style-type: none
|
|
||||||
|
|
||||||
.codeBlock
|
|
||||||
list-style-type: none
|
|
27
app/styles/docs/systems-documentation-view.sass
Normal file
27
app/styles/docs/systems-documentation-view.sass
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#systems-documentation-view
|
||||||
|
background-color: #e4cf8c
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
#toggle-all-system-code
|
||||||
|
margin: 10px
|
||||||
|
|
||||||
|
.container, .row
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.index-column, .documentation-column
|
||||||
|
overflow-x: hidden
|
||||||
|
|
||||||
|
> ul
|
||||||
|
padding: 0px 20px 20px 20px
|
||||||
|
|
||||||
|
.doc-name
|
||||||
|
color: rgb(139, 69, 19)
|
||||||
|
|
||||||
|
.index-column
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
.documentation-column
|
||||||
|
width: 75%
|
||||||
|
|
||||||
|
.special-list, .doc-description, .code-block
|
||||||
|
list-style-type: none
|
5
app/styles/editor/level/documentation_tab.sass
Normal file
5
app/styles/editor/level/documentation_tab.sass
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#editor-level-documentation
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.tab-content
|
||||||
|
height: 100%
|
|
@ -1,13 +1,14 @@
|
||||||
//extends /templates/base
|
.container
|
||||||
|
.pull-right
|
||||||
|
button.btn.btn-primary#toggle-all-component-code Toggle all code
|
||||||
|
.clearfix
|
||||||
|
|
||||||
|
|
||||||
block content
|
|
||||||
.row
|
.row
|
||||||
.col-xs-3.index-column.nano
|
.col-xs-3.index-column.nano
|
||||||
ul.nav.nav-list.list-group.nano-content
|
ul.nav.nav-list.list-group.nano-content
|
||||||
for component in components
|
for component in components
|
||||||
a.doc-name(href="##{component.get('name')}")
|
a.doc-name(href="##{component.get('name')}")
|
||||||
li.list-group-item= component.get('name')
|
li.list-group-item= component.get('system') + '.' + component.get('name')
|
||||||
ul
|
ul
|
||||||
// .list-group for different layout
|
// .list-group for different layout
|
||||||
each doc in component.attributes.propertyDocumentation
|
each doc in component.attributes.propertyDocumentation
|
||||||
|
@ -19,24 +20,26 @@ block content
|
||||||
for component in components
|
for component in components
|
||||||
div(id="#{component.get('name')}" class="panel panel-defalt")
|
div(id="#{component.get('name')}" class="panel panel-defalt")
|
||||||
div(class="panel-heading")
|
div(class="panel-heading")
|
||||||
| #{component.get('name')}
|
strong= component.get('system') + '.' + component.get('name')
|
||||||
div(class="panel-body")
|
div(class="panel-body")
|
||||||
| #{component.get('description')}
|
| #{component.get('description')}
|
||||||
ul
|
ul
|
||||||
each doc in component.attributes.propertyDocumentation
|
each doc in component.attributes.propertyDocumentation
|
||||||
li.list-group-item(id="#{component.get('name')}#{doc.name}")
|
li.list-group-item(id="#{component.get('name')}#{doc.name}")
|
||||||
| #{doc.name}
|
| #{doc.name}
|
||||||
ul.specialList
|
ul.special-list
|
||||||
if doc.description[codeLanguage]
|
if doc.description[codeLanguage]
|
||||||
li!=marked(doc.description[codeLanguage])
|
li!=marked(doc.description[codeLanguage])
|
||||||
else
|
else
|
||||||
li!=marked(doc.description)
|
li!=marked(doc.description)
|
||||||
li.panel-heading
|
li.panel-heading
|
||||||
a.codeBlock(data-toggle="collapse" data-parent="##{component.get('name')}" href="##{component.get('name')}Code")
|
a.code-block(data-toggle="collapse" data-parent="##{component.get('name')}" href="##{component.get('name')}Code")
|
||||||
| Code
|
| Code
|
||||||
div(id="#{component.get('name')}Code" class="panel-collapse collapse")
|
div(id="#{component.get('name')}Code" class="panel-collapse collapse")
|
||||||
div.panel-body
|
div.panel-body
|
||||||
pre
|
pre
|
||||||
| #{component.attributes.code}
|
| #{component.attributes.code}
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
.clearfix
|
||||||
|
.clearfix
|
31
app/templates/docs/systems-documentation-view.jade
Normal file
31
app/templates/docs/systems-documentation-view.jade
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
.container
|
||||||
|
.pull-right
|
||||||
|
button.btn.btn-primary#toggle-all-system-code Toggle all code
|
||||||
|
.clearfix
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col-xs-3.index-column.nano
|
||||||
|
ul.nav.nav-list.list-group.nano-content
|
||||||
|
for system in systems
|
||||||
|
a.doc-name(href="##{system.get('name')}")
|
||||||
|
li.list-group-item= system.get('name')
|
||||||
|
.col-xs-9.documentation-column.nano
|
||||||
|
ul.nano-content
|
||||||
|
for system in systems
|
||||||
|
div(id="#{system.get('name')}" class="panel panel-defalt")
|
||||||
|
div(class="panel-heading")
|
||||||
|
| #{system.get('name')}
|
||||||
|
div(class="panel-body")
|
||||||
|
| #{system.get('description')}
|
||||||
|
ul
|
||||||
|
li.panel-heading
|
||||||
|
a.code-block(data-toggle="collapse" data-parent="##{system.get('name')}" href="##{system.get('name')}Code")
|
||||||
|
| Code
|
||||||
|
div(id="#{system.get('name')}Code" class="panel-collapse collapse")
|
||||||
|
div.panel-body
|
||||||
|
pre
|
||||||
|
| #{system.attributes.code}
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
.clearfix
|
||||||
|
.clearfix
|
|
@ -36,7 +36,7 @@ block header
|
||||||
li
|
li
|
||||||
a(href="#related-achievements-view", data-toggle="tab") Achievements
|
a(href="#related-achievements-view", data-toggle="tab") Achievements
|
||||||
li
|
li
|
||||||
a(href="#docs-components-view", data-toggle="tab", data-i18n="editor.level_tab_docs") Documentation
|
a(href="#editor-level-documentation", data-toggle="tab", data-i18n="editor.level_tab_docs") Documentation
|
||||||
.navbar-header
|
.navbar-header
|
||||||
span.navbar-brand #{level.attributes.name}
|
span.navbar-brand #{level.attributes.name}
|
||||||
|
|
||||||
|
@ -125,7 +125,15 @@ block outer_content
|
||||||
|
|
||||||
div.tab-pane#related-achievements-view
|
div.tab-pane#related-achievements-view
|
||||||
|
|
||||||
div.tab-pane#docs-components-view
|
div.tab-pane#editor-level-documentation
|
||||||
|
ul.nav.nav-pills.nav-justified
|
||||||
|
li
|
||||||
|
a(href="#components-documentation-view", data-toggle="pill", data-i18n="resources.components") Components
|
||||||
|
li
|
||||||
|
a(href="#systems-documentation-view", data-toggle="pill", data-i18n="resources.systems") Systems
|
||||||
|
div.tab-content
|
||||||
|
div.tab-pane#components-documentation-view
|
||||||
|
div.tab-pane#systems-documentation-view
|
||||||
|
|
||||||
div#error-view
|
div#error-view
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
#RootView = require 'views/kinds/RootView'
|
|
||||||
CocoView = require 'views/kinds/CocoView'
|
CocoView = require 'views/kinds/CocoView'
|
||||||
template = require 'templates/docs/components'
|
template = require 'templates/docs/components-documentation-view'
|
||||||
CocoCollection = require 'collections/CocoCollection'
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
LevelComponent = require 'models/LevelComponent'
|
LevelComponent = require 'models/LevelComponent'
|
||||||
|
|
||||||
class ComponentDocsCollection extends CocoCollection
|
class ComponentDocsCollection extends CocoCollection
|
||||||
url: '/db/level.component?project=name,description,dependencies,propertyDocumentation,code'
|
url: '/db/level.component?project=system,name,description,dependencies,propertyDocumentation,code'
|
||||||
model: LevelComponent
|
model: LevelComponent
|
||||||
|
comparator: 'system'
|
||||||
|
|
||||||
module.exports = class ComponentDocumentationView extends CocoView
|
module.exports = class ComponentsDocumentationView extends CocoView
|
||||||
id: 'docs-components-view'
|
id: 'components-documentation-view'
|
||||||
template: template
|
template: template
|
||||||
className: 'tab-pane'
|
className: 'tab-pane'
|
||||||
|
collapsed: true
|
||||||
|
|
||||||
|
events:
|
||||||
|
'click #toggle-all-component-code': 'onToggleAllCode'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super(options)
|
super(options)
|
||||||
|
@ -24,3 +28,8 @@ module.exports = class ComponentDocumentationView extends CocoView
|
||||||
c.marked = marked
|
c.marked = marked
|
||||||
c.codeLanguage = me.get('aceConfig')?.language ? 'javascript'
|
c.codeLanguage = me.get('aceConfig')?.language ? 'javascript'
|
||||||
c
|
c
|
||||||
|
|
||||||
|
onToggleAllCode: (e) ->
|
||||||
|
@collapsed = not @collapsed
|
||||||
|
@$el.find('.collapse').collapse(if @collapsed then 'hide' else 'show')
|
||||||
|
@$el.find('#toggle-all-component-code').toggleClass 'active', not @collapsed
|
35
app/views/docs/SystemsDocumentationView.coffee
Normal file
35
app/views/docs/SystemsDocumentationView.coffee
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
CocoView = require 'views/kinds/CocoView'
|
||||||
|
template = require 'templates/docs/systems-documentation-view'
|
||||||
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
|
LevelSystem = require 'models/LevelSystem'
|
||||||
|
|
||||||
|
class SystemDocsCollection extends CocoCollection
|
||||||
|
url: '/db/level.system?project=name,description,code'
|
||||||
|
model: LevelSystem
|
||||||
|
comparator: 'name'
|
||||||
|
|
||||||
|
module.exports = class SystemsDocumentationView extends CocoView
|
||||||
|
id: 'systems-documentation-view'
|
||||||
|
template: template
|
||||||
|
className: 'tab-pane'
|
||||||
|
collapsed: true
|
||||||
|
|
||||||
|
events:
|
||||||
|
'click #toggle-all-system-code': 'onToggleAllCode'
|
||||||
|
|
||||||
|
constructor: (options) ->
|
||||||
|
super(options)
|
||||||
|
@systemDocs = new SystemDocsCollection()
|
||||||
|
@supermodel.loadCollection @systemDocs, 'systems'
|
||||||
|
|
||||||
|
getRenderData: ->
|
||||||
|
c = super()
|
||||||
|
c.systems = @systemDocs.models
|
||||||
|
c.marked = marked
|
||||||
|
c.codeLanguage = me.get('aceConfig')?.language ? 'javascript'
|
||||||
|
c
|
||||||
|
|
||||||
|
onToggleAllCode: (e) ->
|
||||||
|
@collapsed = not @collapsed
|
||||||
|
@$el.find('.collapse').collapse(if @collapsed then 'hide' else 'show')
|
||||||
|
@$el.find('#toggle-all-system-code').toggleClass 'active', not @collapsed
|
|
@ -17,7 +17,8 @@ SaveVersionModal = require 'views/modal/SaveVersionModal'
|
||||||
PatchesView = require 'views/editor/PatchesView'
|
PatchesView = require 'views/editor/PatchesView'
|
||||||
RelatedAchievementsView = require 'views/editor/level/RelatedAchievementsView'
|
RelatedAchievementsView = require 'views/editor/level/RelatedAchievementsView'
|
||||||
VersionHistoryView = require './modals/LevelVersionsModal'
|
VersionHistoryView = require './modals/LevelVersionsModal'
|
||||||
ComponentDocsView = require 'views/docs/ComponentDocumentationView'
|
ComponentsDocumentationView = require 'views/docs/ComponentsDocumentationView'
|
||||||
|
SystemsDocumentationView = require 'views/docs/SystemsDocumentationView'
|
||||||
|
|
||||||
module.exports = class LevelEditView extends RootView
|
module.exports = class LevelEditView extends RootView
|
||||||
id: 'editor-level-view'
|
id: 'editor-level-view'
|
||||||
|
@ -41,6 +42,7 @@ module.exports = class LevelEditView extends RootView
|
||||||
'click #level-patch-button': 'startPatchingLevel'
|
'click #level-patch-button': 'startPatchingLevel'
|
||||||
'click #level-watch-button': 'toggleWatchLevel'
|
'click #level-watch-button': 'toggleWatchLevel'
|
||||||
'click #pop-level-i18n-button': -> @level.populateI18N()
|
'click #pop-level-i18n-button': -> @level.populateI18N()
|
||||||
|
'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab'
|
||||||
'mouseup .nav-tabs > li a': 'toggleTab'
|
'mouseup .nav-tabs > li a': 'toggleTab'
|
||||||
|
|
||||||
constructor: (options, @levelID) ->
|
constructor: (options, @levelID) ->
|
||||||
|
@ -79,7 +81,8 @@ module.exports = class LevelEditView extends RootView
|
||||||
@insertSubView new ComponentsTabView supermodel: @supermodel
|
@insertSubView new ComponentsTabView supermodel: @supermodel
|
||||||
@insertSubView new SystemsTabView supermodel: @supermodel
|
@insertSubView new SystemsTabView supermodel: @supermodel
|
||||||
@insertSubView new RelatedAchievementsView supermodel: @supermodel, level: @level
|
@insertSubView new RelatedAchievementsView supermodel: @supermodel, level: @level
|
||||||
@insertSubView new ComponentDocsView # Don't give it the supermodel, it'll pollute it!
|
@insertSubView new ComponentsDocumentationView # Don't give it the supermodel, it'll pollute it!
|
||||||
|
@insertSubView new SystemsDocumentationView # Don't give it the supermodel, it'll pollute it!
|
||||||
|
|
||||||
Backbone.Mediator.publish 'editor:level-loaded', level: @level
|
Backbone.Mediator.publish 'editor:level-loaded', level: @level
|
||||||
@showReadOnly() if me.get('anonymous')
|
@showReadOnly() if me.get('anonymous')
|
||||||
|
@ -164,3 +167,9 @@ module.exports = class LevelEditView extends RootView
|
||||||
li.parent().find('li').hide()
|
li.parent().find('li').hide()
|
||||||
li.show()
|
li.show()
|
||||||
console.log li.hasClass('active')
|
console.log li.hasClass('active')
|
||||||
|
|
||||||
|
onClickDocumentationTab: (e) ->
|
||||||
|
# It's either too late at night or something is going on with Bootstrap nested tabs, so we do the click instead of using .active.
|
||||||
|
return if @initializedDocs
|
||||||
|
@initializedDocs = true
|
||||||
|
@$el.find('a[href="#components-documentation-view"]').click()
|
||||||
|
|
|
@ -113,7 +113,7 @@ module.exports = class ScriptsTabView extends CocoView
|
||||||
|
|
||||||
onThangsEdited: (e) ->
|
onThangsEdited: (e) ->
|
||||||
# Update in-place so existing Treema nodes refer to the same array.
|
# Update in-place so existing Treema nodes refer to the same array.
|
||||||
@thangIDs.splice(0, @thangIDs.length, @getThangIDs()...)
|
@thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...)
|
||||||
|
|
||||||
|
|
||||||
class ScriptsNode extends TreemaArrayNode
|
class ScriptsNode extends TreemaArrayNode
|
||||||
|
|
|
@ -59,7 +59,7 @@ module.exports = class SettingsTabView extends CocoView
|
||||||
|
|
||||||
onThangsEdited: (e) ->
|
onThangsEdited: (e) ->
|
||||||
# Update in-place so existing Treema nodes refer to the same array.
|
# Update in-place so existing Treema nodes refer to the same array.
|
||||||
@thangIDs.splice(0, @thangIDs.length, @getThangIDs()...)
|
@thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...)
|
||||||
|
|
||||||
|
|
||||||
class SettingsNode extends TreemaObjectNode
|
class SettingsNode extends TreemaObjectNode
|
||||||
|
|
|
@ -313,7 +313,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
createEssentialComponents: (defaultComponents) ->
|
createEssentialComponents: (defaultComponents) ->
|
||||||
physicalConfig = {pos: {x: 10, y: 10, z: 1}}
|
physicalConfig = {pos: {x: 10, y: 10, z: 1}}
|
||||||
if physicalOriginal = _.find(defaultComponents ? [], original: componentOriginals['physics.Physical'])
|
if physicalOriginal = _.find(defaultComponents ? [], original: componentOriginals['physics.Physical'])
|
||||||
physicalConfig.pos.z = physicalOriginal.config.pos.z # Get the z right
|
physicalConfig.pos.z = physicalOriginal.config?.pos?.z ? 1 # Get the z right
|
||||||
console.log physicalOriginal, defaultComponents, componentOriginals['physics.Physical'], physicalConfig
|
console.log physicalOriginal, defaultComponents, componentOriginals['physics.Physical'], physicalConfig
|
||||||
[
|
[
|
||||||
{original: componentOriginals['existence.Exists'], majorVersion: 0, config: {}}
|
{original: componentOriginals['existence.Exists'], majorVersion: 0, config: {}}
|
||||||
|
|
|
@ -6,6 +6,7 @@ log = require 'winston'
|
||||||
Patch = require '../patches/Patch'
|
Patch = require '../patches/Patch'
|
||||||
User = require '../users/User'
|
User = require '../users/User'
|
||||||
sendwithus = require '../sendwithus'
|
sendwithus = require '../sendwithus'
|
||||||
|
hipchat = require '../hipchat'
|
||||||
|
|
||||||
PROJECT = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
PROJECT = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
||||||
FETCH_LIMIT = 300
|
FETCH_LIMIT = 300
|
||||||
|
@ -370,6 +371,8 @@ module.exports = class Handler
|
||||||
parentDocument.makeNewMajorVersion(updatedObject, done)
|
parentDocument.makeNewMajorVersion(updatedObject, done)
|
||||||
|
|
||||||
notifyWatchersOfChange: (editor, changedDocument, editPath) ->
|
notifyWatchersOfChange: (editor, changedDocument, editPath) ->
|
||||||
|
docLink = "http://codecombat.com#{editPath}"
|
||||||
|
@sendChangedHipChatMessage creator: editor, target: changedDocument, docLink: docLink
|
||||||
watchers = changedDocument.get('watchers') or []
|
watchers = changedDocument.get('watchers') or []
|
||||||
watchers = (w for w in watchers when not w.equals(editor.get('_id')))
|
watchers = (w for w in watchers when not w.equals(editor.get('_id')))
|
||||||
return unless watchers.length
|
return unless watchers.length
|
||||||
|
@ -390,9 +393,20 @@ module.exports = class Handler
|
||||||
commit_message: changedDocument.get('commitMessage')
|
commit_message: changedDocument.get('commitMessage')
|
||||||
sendwithus.api.send context, (err, result) ->
|
sendwithus.api.send context, (err, result) ->
|
||||||
|
|
||||||
|
sendChangedHipChatMessage: (options) ->
|
||||||
|
message = "#{options.creator.get('name')} saved a change to <a href=\"#{options.docLink}\">#{options.target.get('name')}</a>: #{options.target.get('commitMessage')}"
|
||||||
|
hipchat.sendHipChatMessage message
|
||||||
|
|
||||||
makeNewInstance: (req) ->
|
makeNewInstance: (req) ->
|
||||||
model = new @modelClass({})
|
model = new @modelClass({})
|
||||||
model.set 'watchers', [req.user.get('_id')] if @modelClass.schema.is_patchable
|
if @modelClass.schema.is_patchable
|
||||||
|
watchers = [req.user.get('_id')]
|
||||||
|
if req.user.isAdmin() # https://github.com/codecombat/codecombat/issues/1105
|
||||||
|
nick = mongoose.Types.ObjectId('512ef4805a67a8c507000001')
|
||||||
|
scott = mongoose.Types.ObjectId('5162fab9c92b4c751e000274')
|
||||||
|
watchers.push nick unless _.find watchers, (id) -> id.equals nick
|
||||||
|
watchers.push scott unless _.find watchers, (id) -> id.equals scott
|
||||||
|
model.set 'watchers', watchers
|
||||||
model
|
model
|
||||||
|
|
||||||
validateDocumentInput: (input) ->
|
validateDocumentInput: (input) ->
|
||||||
|
|
|
@ -22,5 +22,5 @@ module.exports.addDelightedUser = addDelightedUser = (user) ->
|
||||||
gender: user.get('gender')
|
gender: user.get('gender')
|
||||||
lastLevel: user.get('lastLevel')
|
lastLevel: user.get('lastLevel')
|
||||||
request.post {uri: "https://#{key}:@api.delightedapp.com/v1/people.json", form: form}, (err, res, body) ->
|
request.post {uri: "https://#{key}:@api.delightedapp.com/v1/people.json", form: form}, (err, res, body) ->
|
||||||
return log.error 'Error sending Delighted request:', err or body if err or /error/.test body
|
return log.error 'Error sending Delighted request:', err or body if err or /error/i.test body
|
||||||
#log.info "Got DelightedApp response: #{body}"
|
#log.info "Got DelightedApp response: #{body}"
|
||||||
|
|
16
server/hipchat.coffee
Normal file
16
server/hipchat.coffee
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
config = require '../server_config'
|
||||||
|
request = require 'request'
|
||||||
|
log = require 'winston'
|
||||||
|
|
||||||
|
module.exports.sendHipChatMessage = sendHipChatMessage = (message) ->
|
||||||
|
return unless key = config.hipchatAPIKey
|
||||||
|
roomID = 254598
|
||||||
|
form =
|
||||||
|
color: 'yellow'
|
||||||
|
notify: false
|
||||||
|
message: message
|
||||||
|
messageFormat: 'html'
|
||||||
|
url = "https://api.hipchat.com/v2/room/#{roomID}/notification?auth_token=#{key}"
|
||||||
|
request.post {uri: url, json: form}, (err, res, body) ->
|
||||||
|
return log.error 'Error sending HipChat patch request:', err or body if err or /error/i.test body
|
||||||
|
#log.info "Got HipChat patch response:", body
|
|
@ -6,6 +6,7 @@ schema = require '../../app/schemas/models/patch'
|
||||||
mongoose = require 'mongoose'
|
mongoose = require 'mongoose'
|
||||||
log = require 'winston'
|
log = require 'winston'
|
||||||
sendwithus = require '../sendwithus'
|
sendwithus = require '../sendwithus'
|
||||||
|
hipchat = require '../hipchat'
|
||||||
|
|
||||||
PatchHandler = class PatchHandler extends Handler
|
PatchHandler = class PatchHandler extends Handler
|
||||||
modelClass: Patch
|
modelClass: Patch
|
||||||
|
@ -75,14 +76,16 @@ PatchHandler = class PatchHandler extends Handler
|
||||||
onPostSuccess: (req, doc) ->
|
onPostSuccess: (req, doc) ->
|
||||||
log.error 'Error sending patch created: could not find the loaded target on the patch object.' unless doc.targetLoaded
|
log.error 'Error sending patch created: could not find the loaded target on the patch object.' unless doc.targetLoaded
|
||||||
return unless doc.targetLoaded
|
return unless doc.targetLoaded
|
||||||
|
docLink = "http://codecombat.com#{req.headers['x-current-path']}"
|
||||||
|
@sendPatchCreatedHipChatMessage creator: req.user, patch: doc, target: doc.targetLoaded, docLink: docLink
|
||||||
watchers = doc.targetLoaded.get('watchers') or []
|
watchers = doc.targetLoaded.get('watchers') or []
|
||||||
watchers = (w for w in watchers when not w.equals(req.user.get('_id')))
|
watchers = (w for w in watchers when not w.equals(req.user.get('_id')))
|
||||||
return unless watchers?.length
|
return unless watchers?.length
|
||||||
User.find({_id: {$in: watchers}}).select({email: 1, name: 1}).exec (err, watchers) =>
|
User.find({_id: {$in: watchers}}).select({email: 1, name: 1}).exec (err, watchers) =>
|
||||||
for watcher in watchers
|
for watcher in watchers
|
||||||
@sendPatchCreatedEmail req.user, watcher, doc, doc.targetLoaded, req.headers['x-current-path']
|
@sendPatchCreatedEmail req.user, watcher, doc, doc.targetLoaded, docLink
|
||||||
|
|
||||||
sendPatchCreatedEmail: (patchCreator, watcher, patch, target, editPath) ->
|
sendPatchCreatedEmail: (patchCreator, watcher, patch, target, docLink) ->
|
||||||
# return if watcher._id is patchCreator._id
|
# return if watcher._id is patchCreator._id
|
||||||
context =
|
context =
|
||||||
email_id: sendwithus.templates.patch_created
|
email_id: sendwithus.templates.patch_created
|
||||||
|
@ -92,8 +95,12 @@ PatchHandler = class PatchHandler extends Handler
|
||||||
email_data:
|
email_data:
|
||||||
doc_name: target.get('name') or '???'
|
doc_name: target.get('name') or '???'
|
||||||
submitter_name: patchCreator.get('name') or '???'
|
submitter_name: patchCreator.get('name') or '???'
|
||||||
doc_link: "http://codecombat.com#{editPath}"
|
doc_link: docLink
|
||||||
commit_message: patch.get('commitMessage')
|
commit_message: patch.get('commitMessage')
|
||||||
sendwithus.api.send context, (err, result) ->
|
sendwithus.api.send context, (err, result) ->
|
||||||
|
|
||||||
|
sendPatchCreatedHipChatMessage: (options) ->
|
||||||
|
message = "#{options.creator.get('name')} submitted a patch to <a href=\"#{options.docLink}\">#{options.target.get('name')}</a>: #{options.patch.get('commitMessage')}"
|
||||||
|
hipchat.sendHipChatMessage message
|
||||||
|
|
||||||
module.exports = new PatchHandler()
|
module.exports = new PatchHandler()
|
||||||
|
|
|
@ -41,6 +41,8 @@ config.mail =
|
||||||
cronHandlerPublicIP: process.env.COCO_CRON_PUBLIC_IP or ''
|
cronHandlerPublicIP: process.env.COCO_CRON_PUBLIC_IP or ''
|
||||||
cronHandlerPrivateIP: process.env.COCO_CRON_PRIVATE_IP or ''
|
cronHandlerPrivateIP: process.env.COCO_CRON_PRIVATE_IP or ''
|
||||||
|
|
||||||
|
config.hipchatAPIKey = process.env.COCO_HIPCHAT_API_KEY or ''
|
||||||
|
|
||||||
config.queue =
|
config.queue =
|
||||||
accessKeyId: process.env.COCO_AWS_ACCESS_KEY_ID or ''
|
accessKeyId: process.env.COCO_AWS_ACCESS_KEY_ID or ''
|
||||||
secretAccessKey: process.env.COCO_AWS_SECRET_ACCESS_KEY or ''
|
secretAccessKey: process.env.COCO_AWS_SECRET_ACCESS_KEY or ''
|
||||||
|
|
Reference in a new issue