mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-26 05:53:39 -04:00
Merge branch 'master' of https://github.com/codecombat/codecombat
This commit is contained in:
commit
848ebf2ffe
39 changed files with 472 additions and 197 deletions
app
locale
models
schemas
styles
base.sass
editor
templates
editor
modal
views
editor
delta.coffeepatch_modal.coffeepatches_view.coffee
level
component
components_tab_view.coffeeedit.coffeesave_view.coffeesystem
systems_tab_view.coffeethangs_tab_view.coffeesystem
kinds
modal
server
test/server
|
@ -15,6 +15,9 @@
|
|||
fork: "Fork"
|
||||
play: "Play"
|
||||
retry: "Retry"
|
||||
watch: "Watch"
|
||||
unwatch: "Unwatch"
|
||||
submit_patch: "Submit Patch"
|
||||
|
||||
units:
|
||||
second: "second"
|
||||
|
|
|
@ -60,6 +60,7 @@ class CocoModel extends Backbone.Model
|
|||
return result.errors unless result.valid
|
||||
|
||||
save: (attrs, options) ->
|
||||
@set 'editPath', document.location.pathname
|
||||
options ?= {}
|
||||
success = options.success
|
||||
options.success = (resp) =>
|
||||
|
@ -68,7 +69,6 @@ class CocoModel extends Backbone.Model
|
|||
@markToRevert()
|
||||
@clearBackup()
|
||||
@trigger "save", @
|
||||
patch.setStatus 'accepted' for patch in @acceptedPatches or []
|
||||
return super attrs, options
|
||||
|
||||
fetch: ->
|
||||
|
@ -95,7 +95,6 @@ class CocoModel extends Backbone.Model
|
|||
cloneNewMinorVersion: ->
|
||||
newData = $.extend(null, {}, @attributes)
|
||||
clone = new @constructor(newData)
|
||||
clone.acceptedPatches = @acceptedPatches
|
||||
clone
|
||||
|
||||
cloneNewMajorVersion: ->
|
||||
|
@ -221,9 +220,11 @@ class CocoModel extends Backbone.Model
|
|||
delta = @getDelta()
|
||||
deltasLib.expandDelta(delta, @_revertAttributes, @schema())
|
||||
|
||||
addPatchToAcceptOnSave: (patch) ->
|
||||
@acceptedPatches ?= []
|
||||
@acceptedPatches.push patch
|
||||
@acceptedPatches = _.uniq(@acceptedPatches, false, (p) -> p.id)
|
||||
watch: (doWatch=true) ->
|
||||
$.ajax("#{@urlRoot}/#{@id}/watch", {type:'PUT', data:{on:doWatch}})
|
||||
@watching = -> doWatch
|
||||
|
||||
watching: ->
|
||||
return me.id in (@get('watchers') or [])
|
||||
|
||||
module.exports = CocoModel
|
||||
|
|
|
@ -5,4 +5,7 @@ module.exports = class PatchModel extends CocoModel
|
|||
urlRoot: "/db/patch"
|
||||
|
||||
setStatus: (status) ->
|
||||
$.ajax("/db/patch/#{@id}/status", {type:"PUT", data: {status:status}})
|
||||
PatchModel.setStatus @id, status
|
||||
|
||||
@setStatus: (id, status) ->
|
||||
$.ajax("/db/patch/#{id}/status", {type:"PUT", data: {status:status}})
|
|
@ -63,7 +63,7 @@ patchableProps = ->
|
|||
status: { enum: ['pending', 'accepted', 'rejected', 'cancelled']}
|
||||
})
|
||||
allowPatches: { type: 'boolean' }
|
||||
listeners: me.array({title:'Listeners'},
|
||||
watchers: me.array({title:'Watchers'},
|
||||
me.objectId(links: [{rel: 'extra', href: "/db/user/{($)}"}]))
|
||||
|
||||
me.extendPatchableProperties = (schema) ->
|
||||
|
|
|
@ -265,3 +265,8 @@ body[lang='ja']
|
|||
font-family: 'Glyphicons Halflings'
|
||||
src: url("/fonts/glyphicons-halflings-regular.eot")
|
||||
src: url("/fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"), url("/fonts/glyphicons-halflings-regular.woff") format("woff"), url("/fonts/glyphicons-halflings-regular.ttf") format("truetype"), url("/fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular") format("svg")
|
||||
|
||||
.spr:after
|
||||
content: " "
|
||||
.spl:before
|
||||
content: " "
|
|
@ -1,4 +1,11 @@
|
|||
#editor-level-component-edit-view
|
||||
nav
|
||||
margin-bottom: 0
|
||||
|
||||
#component-patches
|
||||
padding: 0 10px 10px
|
||||
background: white
|
||||
|
||||
.navbar-text
|
||||
float: left
|
||||
|
||||
|
@ -7,9 +14,13 @@
|
|||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
top: 50px
|
||||
top: 35px
|
||||
border: 2px solid black
|
||||
border-top: none
|
||||
|
||||
.active > a, .active > a:hover, .active > a:focus
|
||||
background-color: white !important
|
||||
.inner-editor
|
||||
position: absolute
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
top: 0px
|
|
@ -1,4 +1,6 @@
|
|||
#editor-level-components-tab-view
|
||||
h3
|
||||
margin-top: 0
|
||||
|
||||
.components-container
|
||||
position: absolute
|
||||
|
@ -7,7 +9,7 @@
|
|||
|
||||
.treema-root
|
||||
position: absolute
|
||||
top: 50px
|
||||
top: 35px
|
||||
bottom: 0
|
||||
width: 250px
|
||||
overflow: scroll
|
||||
|
@ -25,13 +27,13 @@
|
|||
|
||||
.treema-root
|
||||
position: absolute
|
||||
top: 50px
|
||||
top: 35px
|
||||
right: 0
|
||||
left: 0px
|
||||
bottom: 0
|
||||
overflow: scroll
|
||||
|
||||
#create-new-component-button
|
||||
#create-new-component-button-no-select
|
||||
position: absolute
|
||||
top: 0
|
||||
right: 0
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#editor-level-view
|
||||
&, #level-editor-top-nav
|
||||
min-width: 1024px
|
||||
|
||||
a
|
||||
font-family: helvetica, arial, sans serif
|
||||
|
||||
#top-nav
|
||||
display: none
|
||||
|
||||
position: absolute
|
||||
top: 0px
|
||||
left: 0px
|
||||
|
@ -12,22 +12,54 @@
|
|||
bottom: 0px
|
||||
|
||||
$BG: rgba(228, 207, 140, 1.0)
|
||||
$NAVBG: #2f261d
|
||||
|
||||
li.navbar-btn
|
||||
margin-right: 5px
|
||||
|
||||
#level-editor-top-nav
|
||||
.nav-tabs
|
||||
border-bottom: 0 !important
|
||||
.active > a, .active > a:hover, .active > a:focus
|
||||
background-color: $BG !important
|
||||
border-color: darken($BG, 50%)
|
||||
border-bottom: 0
|
||||
// custom navbar height rules
|
||||
.navbar-nav > li > a
|
||||
padding: 7px 8px 8px
|
||||
cursor: pointer
|
||||
&:hover
|
||||
background-color: lighten($NAVBG, 10%)
|
||||
.navbar
|
||||
min-height: 0px
|
||||
border-radius: 0
|
||||
.navbar-right // not sure why bootstrap puts a big negative margin in, but this overrides it
|
||||
margin-right: 10px
|
||||
|
||||
// custom navbar styling
|
||||
.navbar-brand
|
||||
padding-top: 7px
|
||||
padding-bottom: 7px
|
||||
color: lighten(gold, 30%)
|
||||
.navbar-header
|
||||
border-left: 2px solid lighten($NAVBG, 20%)
|
||||
border-right: 2px solid lighten($NAVBG, 20%)
|
||||
background: lighten($NAVBG, 10%)
|
||||
margin-left: 20px
|
||||
.nav-tabs
|
||||
margin-left: 5px
|
||||
border-bottom: 0 !important
|
||||
.active > a, .active > a:hover, .active > a:focus
|
||||
background-color: $BG !important
|
||||
border-color: darken($BG, 50%)
|
||||
border-bottom: 0
|
||||
a
|
||||
padding: 7px 5px !important
|
||||
.dropdown-menu a
|
||||
cursor: pointer
|
||||
&:hover
|
||||
background-color: #d3d3d3
|
||||
|
||||
.badge
|
||||
background-color: green
|
||||
|
||||
.outer-content
|
||||
background-color: $BG
|
||||
position: absolute
|
||||
top: 0
|
||||
top: 35px
|
||||
bottom: 0
|
||||
left: 0
|
||||
right: 0
|
||||
|
@ -45,12 +77,11 @@
|
|||
|
||||
#level-editor-tabs
|
||||
position: absolute
|
||||
left: 20px
|
||||
right: 20px
|
||||
top: 66px
|
||||
bottom: 20px
|
||||
left: 15px
|
||||
right: 15px
|
||||
top: 15px
|
||||
bottom: 15px
|
||||
|
||||
.treema-root
|
||||
background-color: white
|
||||
border-radius: 4px
|
||||
|
||||
border-radius: 4px
|
|
@ -7,9 +7,13 @@
|
|||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
top: 50px
|
||||
top: 35px
|
||||
border: 2px solid black
|
||||
border-top: none
|
||||
|
||||
.active > a, .active > a:hover, .active > a:focus
|
||||
background-color: white !important
|
||||
|
||||
.inner-editor
|
||||
position: absolute
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
top: 0px
|
|
@ -1,4 +1,6 @@
|
|||
#editor-level-systems-tab-view
|
||||
h3
|
||||
margin-top: 0
|
||||
|
||||
.systems-container
|
||||
position: absolute
|
||||
|
@ -7,7 +9,7 @@
|
|||
|
||||
.treema-root
|
||||
position: absolute
|
||||
top: 50px
|
||||
top: 35px
|
||||
bottom: 0
|
||||
width: 250px
|
||||
overflow: scroll
|
||||
|
@ -30,7 +32,7 @@
|
|||
|
||||
.treema-root
|
||||
position: absolute
|
||||
top: 50px
|
||||
top: 35px
|
||||
right: 0
|
||||
left: 0px
|
||||
bottom: 0
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.patches-view
|
||||
.status-buttons
|
||||
margin: 10px 0
|
||||
margin-bottom: 10px
|
||||
|
|
|
@ -1,23 +1,44 @@
|
|||
nav.navbar.navbar-default(role='navigation')
|
||||
.container-fluid
|
||||
.navbar-header
|
||||
span.navbar-brand
|
||||
span(data-i18n="editor.level_component_edit_title")
|
||||
| Edit Component
|
||||
span :
|
||||
| "#{editTitle}"
|
||||
.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav.nav-tabs
|
||||
li.active
|
||||
a(href="#component-code" data-toggle="tab" data-i18n="general.code") Code
|
||||
ul.nav.navbar-nav.nav-tabs
|
||||
li.active
|
||||
a(href="#component-code" data-toggle="tab" data-i18n="general.code")#component-code-tab Code
|
||||
li
|
||||
a(href="#component-config-schema" data-toggle="tab" data-i18n="editor.level_component_config_schema")#component-config-schema-tab Config Schema
|
||||
li
|
||||
a(href="#component-settings" data-toggle="tab" data-i18n="editor.level_component_settings")#component-settings-tab Settings
|
||||
li
|
||||
a(href="#component-patches" data-toggle="tab" data-i18n="resources.patches")#component-patches-tab Patches
|
||||
|
||||
.navbar-header
|
||||
span.navbar-brand= editTitle
|
||||
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li.dropdown
|
||||
a(data-toggle='dropdown')
|
||||
span.glyphicon-chevron-down.glyphicon
|
||||
|
||||
ul.dropdown-menu
|
||||
li.dropdown-header Actions
|
||||
li
|
||||
a(href="#component-config-schema" data-toggle="tab" data-i18n="editor.level_component_config_schema") Config Schema
|
||||
li
|
||||
a(href="#component-settings" data-toggle="tab" data-i18n="editor.level_component_settings") Settings
|
||||
ul.nav.navbar-nav.navbar-left
|
||||
li(data-i18n="general.version_history").btn.btn-primary.navbar-btn#history-button Version History
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li(data-i18n="editor.level_component_btn_new").btn.btn-primary.navbar-btn#create-new-component-button Create New Component
|
||||
a#component-watch-button
|
||||
span.watch
|
||||
span.glyphicon.glyphicon-eye-open
|
||||
span.spl Watch
|
||||
span.unwatch.secret
|
||||
span.glyphicon.glyphicon-eye-close
|
||||
span.spl Unwatch
|
||||
|
||||
li#patch-component-button
|
||||
a(data-i18n="common.submit_patch") Submit Patch
|
||||
|
||||
li#create-new-component-button
|
||||
a(data-i18n="editor.level_component_btn_new") Create New Component
|
||||
|
||||
li.divider
|
||||
li.dropdown-header Info
|
||||
|
||||
li#component-history-button
|
||||
a(data-i18n="general.version_history") Version History
|
||||
|
||||
.tab-content
|
||||
.tab-pane.active#component-code
|
||||
|
@ -26,3 +47,5 @@ nav.navbar.navbar-default(role='navigation')
|
|||
#config-schema-treema
|
||||
.tab-pane#component-settings
|
||||
#edit-component-treema
|
||||
.tab-pane#component-patches
|
||||
.patches-view
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
.edit-component-container
|
||||
if me.isAdmin()
|
||||
button(data-i18n="editor.level_component_btn_new").btn.btn-primary#create-new-component-button Create New Component
|
||||
button(data-i18n="editor.level_component_btn_new").btn.btn-primary#create-new-component-button-no-select Create New Component
|
||||
|
||||
#editor-level-component-edit-view
|
||||
|
|
|
@ -1,74 +1,90 @@
|
|||
extends /templates/base
|
||||
|
||||
block outer_content
|
||||
.outer-content
|
||||
|
||||
block header
|
||||
if level.loading
|
||||
nav.navbar.navbar-default(role='navigation')#level-editor-top-nav
|
||||
.container-fluid
|
||||
ul.nav.navbar-nav
|
||||
li
|
||||
a(href="/editor/level", data-i18n="editor.back") Back
|
||||
.navbar-header
|
||||
span.navbar-brand
|
||||
span(data-i18n="editor.level_title") Level Editor
|
||||
span :
|
||||
span.level-title #{level.attributes.name}
|
||||
.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav.nav-tabs
|
||||
|
||||
li.active
|
||||
a(href="#editor-level-thangs-tab-view", data-toggle="tab", data-i18n="editor.level_tab_thangs") Thangs
|
||||
li
|
||||
a(href="#editor-level-scripts-tab-view", data-toggle="tab", data-i18n="editor.level_tab_scripts") Scripts
|
||||
li
|
||||
a(href="#editor-level-settings-tab-view", data-toggle="tab", data-i18n="editor.level_tab_settings") Settings
|
||||
li
|
||||
a(href="#editor-level-components-tab-view", data-toggle="tab", data-i18n="editor.level_tab_components") Components
|
||||
li
|
||||
a(href="#editor-level-systems-tab-view", data-toggle="tab", data-i18n="editor.level_tab_systems") Systems
|
||||
li
|
||||
a(href="#editor-level-patches", data-toggle="tab", data-i18n="resources.patches")#patches-tab Patches
|
||||
|
||||
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary.navbar-btn#revert-button Revert
|
||||
if authorized
|
||||
li(data-i18n="common.save").btn.btn-primary.navbar-btn#commit-level-start-button Save
|
||||
else
|
||||
li(data-i18n="common.patch").btn.btn-primary.navbar-btn#commit-level-patch-button Patch
|
||||
li(data-i18n="common.fork", disabled=anonymous ? "true": undefined).btn.btn-primary.navbar-btn#fork-level-start-button Fork
|
||||
li(title="⌃↩ or ⌘↩: Play preview of current level", data-i18n="common.play")#play-button.btn.btn-inverse.banner.navbar-btn Play!
|
||||
|
||||
li.divider
|
||||
|
||||
li.dropdown
|
||||
a.dropdown-toggle(href='#', data-toggle='dropdown', data-i18n="editor.more")
|
||||
| More
|
||||
b.caret
|
||||
ul.dropdown-menu
|
||||
li#history-button
|
||||
a(href='#', data-i18n="general.version_history") Version History
|
||||
li
|
||||
a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki") Wiki
|
||||
li
|
||||
a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat") Live Chat
|
||||
li
|
||||
a(href='http://discourse.codecombat.com/category/artisan', data-i18n="nav.forum") Forum
|
||||
li
|
||||
a(data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Email
|
||||
|
||||
|
||||
|
||||
ul.dropdown-menu
|
||||
li
|
||||
span(data-i18n="editor.level_some_options").dropdown-menu-header Some Options?
|
||||
li.divider
|
||||
li
|
||||
a(data-delay="1000", href="#", data-i18n="common.delay_1_sec") 1 second
|
||||
a(data-delay="3000", href="#", data-i18n="common.delay_3_sec") 3 seconds
|
||||
a(data-delay="5000", href="#", data-i18n="common.delay_5_sec") 5 seconds
|
||||
a(data-delay="90019001", href="#", data-i18n="common.manual") Manual
|
||||
a(href="/editor/level")
|
||||
span.glyphicon-home.glyphicon
|
||||
|
||||
else
|
||||
nav.navbar.navbar-default(role='navigation')#level-editor-top-nav
|
||||
ul.nav.navbar-nav
|
||||
li
|
||||
a(href="/editor/level")
|
||||
span.glyphicon-home.glyphicon
|
||||
ul.nav.navbar-nav.nav-tabs
|
||||
|
||||
li.active
|
||||
a(href="#editor-level-thangs-tab-view", data-toggle="tab", data-i18n="editor.level_tab_thangs") Thangs
|
||||
li
|
||||
a(href="#editor-level-scripts-tab-view", data-toggle="tab", data-i18n="editor.level_tab_scripts") Scripts
|
||||
li
|
||||
a(href="#editor-level-settings-tab-view", data-toggle="tab", data-i18n="editor.level_tab_settings") Settings
|
||||
li
|
||||
a(href="#editor-level-components-tab-view", data-toggle="tab", data-i18n="editor.level_tab_components") Components
|
||||
li
|
||||
a(href="#editor-level-systems-tab-view", data-toggle="tab", data-i18n="editor.level_tab_systems") Systems
|
||||
li
|
||||
a(href="#editor-level-patches", data-toggle="tab")#patches-tab
|
||||
span(data-i18n="resources.patches").spr Patches
|
||||
- var patches = level.get('patches')
|
||||
if patches && patches.length
|
||||
span.badge= patches.length
|
||||
|
||||
.navbar-header
|
||||
span.navbar-brand #{level.attributes.name}
|
||||
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
if authorized
|
||||
li#commit-level-start-button
|
||||
a
|
||||
span.glyphicon-floppy-disk.glyphicon
|
||||
else
|
||||
li#level-patch-button
|
||||
a
|
||||
span.glyphicon-floppy-disk.glyphicon
|
||||
|
||||
li(title="⌃↩ or ⌘↩: Play preview of current level")#play-button
|
||||
a
|
||||
span.glyphicon-play.glyphicon
|
||||
li.dropdown
|
||||
a(data-toggle='dropdown')
|
||||
span.glyphicon-chevron-down.glyphicon
|
||||
ul.dropdown-menu
|
||||
li.dropdown-header Actions
|
||||
li
|
||||
a#level-watch-button
|
||||
span.watch
|
||||
span.glyphicon.glyphicon-eye-open
|
||||
span.spl Watch
|
||||
span.unwatch.secret
|
||||
span.glyphicon.glyphicon-eye-close
|
||||
span.spl Unwatch
|
||||
|
||||
li(class=anonymous ? "disabled": "")
|
||||
a(data-i18n="common.fork")#fork-level-start-button Fork
|
||||
li(class=anonymous ? "disabled": "")
|
||||
a(data-toggle="coco-modal", data-target="modal/revert", data-i18n="editor.revert")#revert-button Revert
|
||||
li.divider
|
||||
li.dropdown-header Info
|
||||
li#level-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/Artisan-Home', 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/artisan', data-i18n="nav.forum", target="_blank") Forum
|
||||
li
|
||||
a(data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Email
|
||||
|
||||
block outer_content
|
||||
.outer-content
|
||||
div.tab-content#level-editor-tabs
|
||||
div.tab-pane.active#editor-level-thangs-tab-view
|
||||
|
||||
|
|
|
@ -1,21 +1,40 @@
|
|||
nav.navbar.navbar-default(role='navigation')
|
||||
.container-fluid
|
||||
.navbar-header
|
||||
span.navbar-brand
|
||||
span(data-i18n="editor.level_system_edit_title")
|
||||
| Edit System
|
||||
span :
|
||||
| "#{editTitle}"
|
||||
.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav.nav-tabs
|
||||
li.active
|
||||
a(href="#system-code" data-toggle="tab") Code
|
||||
|
||||
ul.nav.navbar-nav.nav-tabs
|
||||
li.active
|
||||
a(href="#system-code" data-toggle="tab")#system-code-tab Code
|
||||
li
|
||||
a(href="#system-config-schema" data-toggle="tab")#system-config-schema-tab Config Schema
|
||||
li
|
||||
a(href="#system-settings" data-toggle="tab")#system-settings-tab Settings
|
||||
li
|
||||
a(href="#system-patches" data-toggle="tab" data-i18n="resources.patches")#system-patches-tab Patches
|
||||
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li.dropdown
|
||||
a(data-toggle='dropdown')
|
||||
span.glyphicon-chevron-down.glyphicon
|
||||
ul.dropdown-menu
|
||||
li.dropdown-header Actions
|
||||
li
|
||||
a(href="#system-config-schema" data-toggle="tab") Config Schema
|
||||
li
|
||||
a(href="#system-settings" data-toggle="tab") Settings
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
li(data-i18n="editor.level_system_btn_new").btn.btn-primary.navbar-btn#create-new-system-button Create New System
|
||||
a#system-watch-button
|
||||
span.watch
|
||||
span.glyphicon.glyphicon-eye-open
|
||||
span.spl Watch
|
||||
span.unwatch.secret
|
||||
span.glyphicon.glyphicon-eye-close
|
||||
span.spl Unwatch
|
||||
li#patch-system-button
|
||||
a(data-i18n="common.submit_patch") Submit Patch
|
||||
li#create-new-system
|
||||
a(data-i18n="editor.level_system_btn_new") Create New System
|
||||
li.divider
|
||||
li.dropdown-header Info
|
||||
li#system-history-button
|
||||
a(data-i18n="general.version_history") Version History
|
||||
|
||||
.navbar-header
|
||||
span.navbar-brand= editTitle
|
||||
|
||||
.tab-content
|
||||
.tab-pane.active#system-code
|
||||
|
@ -24,3 +43,5 @@ nav.navbar.navbar-default(role='navigation')
|
|||
#config-schema-treema
|
||||
.tab-pane#system-settings
|
||||
#edit-system-treema
|
||||
.tab-pane#system-patches
|
||||
.patches-view
|
|
@ -6,6 +6,7 @@ block modal-header-content
|
|||
|
||||
block modal-body-content
|
||||
.modal-body
|
||||
p= patch.get('commitMessage')
|
||||
.changes-stub
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ block modal-header-content
|
|||
|
||||
block modal-body-content
|
||||
if dataList
|
||||
table.table
|
||||
table.table.table-condensed
|
||||
tr
|
||||
th(data-i18n="general.name") Name
|
||||
th(data-i18n="general.version") Version
|
||||
|
|
|
@ -21,12 +21,12 @@ module.exports = class DeltaView extends CocoView
|
|||
if @headModel
|
||||
@headDeltas = @headModel.getExpandedDelta()
|
||||
@conflicts = deltasLib.getConflicts(@headDeltas, @expandedDeltas)
|
||||
DeltaView.deltaCounter += @expandedDeltas.length
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.deltas = @expandedDeltas
|
||||
c.counter = DeltaView.deltaCounter
|
||||
DeltaView.deltaCounter += @expandedDeltas.length
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
View = require 'views/kinds/CocoView'
|
||||
VersionHistoryView = require 'views/editor/component/versions_view'
|
||||
template = require 'templates/editor/level/component/edit'
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
VersionHistoryView = require 'views/editor/component/versions_view'
|
||||
PatchesView = require 'views/editor/patches_view'
|
||||
SaveVersionModal = require 'views/modal/save_version_modal'
|
||||
|
||||
module.exports = class LevelComponentEditView extends View
|
||||
id: "editor-level-component-edit-view"
|
||||
|
@ -10,8 +12,14 @@ module.exports = class LevelComponentEditView extends View
|
|||
|
||||
events:
|
||||
'click #done-editing-component-button': 'endEditing'
|
||||
'click #history-button': 'showVersionHistory'
|
||||
'click .nav a': (e) -> $(e.target).tab('show')
|
||||
'click #component-patches-tab': -> @patchesView.load()
|
||||
'click #component-code-tab': 'buildCodeEditor'
|
||||
'click #component-config-schema-tab': 'buildConfigSchemaTreema'
|
||||
'click #component-settings-tab': 'buildSettingsTreema'
|
||||
'click #component-history-button': 'showVersionHistory'
|
||||
'click #patch-component-button': 'startPatchingComponent'
|
||||
'click #component-watch-button': 'toggleWatchComponent'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
@ -21,6 +29,7 @@ module.exports = class LevelComponentEditView extends View
|
|||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.editTitle = "#{@levelComponent.get('system')}.#{@levelComponent.get('name')}"
|
||||
context.component = @levelComponent
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
|
@ -28,6 +37,8 @@ module.exports = class LevelComponentEditView extends View
|
|||
@buildSettingsTreema()
|
||||
@buildConfigSchemaTreema()
|
||||
@buildCodeEditor()
|
||||
@patchesView = @insertSubView(new PatchesView(@levelComponent), @$el.find('.patches-view'))
|
||||
@$el.find('#component-watch-button').find('> span').toggleClass('secret') if @levelComponent.watching()
|
||||
|
||||
buildSettingsTreema: ->
|
||||
data = _.pick @levelComponent.attributes, (value, key) => key in @editableSettings
|
||||
|
@ -40,7 +51,6 @@ module.exports = class LevelComponentEditView extends View
|
|||
schema: schema
|
||||
data: data
|
||||
callbacks: {change: @onComponentSettingsEdited}
|
||||
treemaOptions.readOnly = true unless me.isAdmin()
|
||||
@componentSettingsTreema = @$el.find('#edit-component-treema').treema treemaOptions
|
||||
@componentSettingsTreema.build()
|
||||
@componentSettingsTreema.open()
|
||||
|
@ -58,7 +68,6 @@ module.exports = class LevelComponentEditView extends View
|
|||
schema: LevelComponent.schema.properties.configSchema
|
||||
data: @levelComponent.get 'configSchema'
|
||||
callbacks: {change: @onConfigSchemaEdited}
|
||||
treemaOptions.readOnly = true unless me.isAdmin()
|
||||
@configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions
|
||||
@configSchemaTreema.build()
|
||||
@configSchemaTreema.open()
|
||||
|
@ -70,10 +79,10 @@ module.exports = class LevelComponentEditView extends View
|
|||
Backbone.Mediator.publish 'level-component-edited', levelComponent: @levelComponent
|
||||
|
||||
buildCodeEditor: ->
|
||||
editorEl = @$el.find '#component-code-editor'
|
||||
editorEl.text @levelComponent.get('code')
|
||||
@editor?.destroy()
|
||||
editorEl = $('<div></div>').text(@levelComponent.get('code')).addClass('inner-editor')
|
||||
@$el.find('#component-code-editor').empty().append(editorEl)
|
||||
@editor = ace.edit(editorEl[0])
|
||||
@editor.setReadOnly(not me.isAdmin())
|
||||
session = @editor.getSession()
|
||||
session.setMode 'ace/mode/coffee'
|
||||
session.setTabSize 2
|
||||
|
@ -90,11 +99,21 @@ module.exports = class LevelComponentEditView extends View
|
|||
Backbone.Mediator.publish 'level-component-editing-ended', levelComponent: @levelComponent
|
||||
null
|
||||
|
||||
showVersionHistory: (e) ->
|
||||
versionHistoryView = new VersionHistoryView {}, @levelComponent.id
|
||||
@openModalView versionHistoryView
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
startPatchingComponent: (e) ->
|
||||
@openModalView new SaveVersionModal({model:@levelComponent})
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
toggleWatchComponent: ->
|
||||
button = @$el.find('#component-watch-button')
|
||||
@levelComponent.watch(button.find('.watch').is(':visible'))
|
||||
button.find('> span').toggleClass('secret')
|
||||
|
||||
destroy: ->
|
||||
@editor?.destroy()
|
||||
super()
|
||||
|
||||
showVersionHistory: (e) ->
|
||||
versionHistoryView = new VersionHistoryView component:@levelComponent, @levelComponent.id
|
||||
@openModalView versionHistoryView
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
|
@ -21,6 +21,7 @@ module.exports = class ComponentsTabView extends View
|
|||
|
||||
events:
|
||||
'click #create-new-component-button': 'createNewLevelComponent'
|
||||
'click #create-new-component-button-no-select': 'createNewLevelComponent'
|
||||
|
||||
onLevelThangsChanged: (e) ->
|
||||
thangsData = e.thangsData
|
||||
|
|
|
@ -27,10 +27,18 @@ module.exports = class EditorLevelView extends View
|
|||
'click #play-button': 'onPlayLevel'
|
||||
'click #commit-level-start-button': 'startCommittingLevel'
|
||||
'click #fork-level-start-button': 'startForkingLevel'
|
||||
'click #history-button': 'showVersionHistory'
|
||||
'click #level-history-button': 'showVersionHistory'
|
||||
'click #patches-tab': -> @patchesView.load()
|
||||
'click #commit-level-patch-button': 'startPatchingLevel'
|
||||
'click #level-patch-button': 'startPatchingLevel'
|
||||
'click #level-watch-button': 'toggleWatchLevel'
|
||||
|
||||
subscriptions:
|
||||
'refresh-level-editor': 'rerenderAllViews'
|
||||
|
||||
rerenderAllViews: ->
|
||||
for view in [@thangsTab, @settingsTab, @scriptsTab, @componentsTab, @systemsTab, @patchesView]
|
||||
view.render()
|
||||
|
||||
constructor: (options, @levelID) ->
|
||||
super options
|
||||
@listenToOnce(@supermodel, 'loaded-all', @onAllLoaded)
|
||||
|
@ -93,6 +101,8 @@ module.exports = class EditorLevelView extends View
|
|||
Backbone.Mediator.publish 'level-loaded', level: @level
|
||||
@showReadOnly() if me.get('anonymous')
|
||||
@patchesView = @insertSubView(new PatchesView(@level), @$el.find('.patches-view'))
|
||||
@listenTo @patchesView, 'accepted-patch', -> setTimeout "location.reload()", 400
|
||||
@$el.find('#level-watch-button').find('> span').toggleClass('secret') if @level.watching()
|
||||
|
||||
onPlayLevel: (e) ->
|
||||
sendLevel = =>
|
||||
|
@ -124,3 +134,8 @@ module.exports = class EditorLevelView extends View
|
|||
versionHistoryView = new VersionHistoryView level:@level, @levelID
|
||||
@openModalView versionHistoryView
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
toggleWatchLevel: ->
|
||||
button = @$el.find('#level-watch-button')
|
||||
@level.watch(button.find('.watch').is(':visible'))
|
||||
button.find('> span').toggleClass('secret')
|
||||
|
|
|
@ -30,7 +30,7 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
context
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
super(false)
|
||||
changeEls = @$el.find('.changes-stub')
|
||||
models = if @lastContext.levelNeedsSave then [@level] else []
|
||||
models = models.concat @lastContext.modifiedComponents
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
View = require 'views/kinds/CocoView'
|
||||
template = require 'templates/editor/level/system/edit'
|
||||
LevelSystem = require 'models/LevelSystem'
|
||||
VersionHistoryView = require 'views/editor/system/versions_view'
|
||||
PatchesView = require 'views/editor/patches_view'
|
||||
SaveVersionModal = require 'views/modal/save_version_modal'
|
||||
|
||||
module.exports = class LevelSystemEditView extends View
|
||||
id: "editor-level-system-edit-view"
|
||||
|
@ -10,6 +13,13 @@ module.exports = class LevelSystemEditView extends View
|
|||
events:
|
||||
'click #done-editing-system-button': 'endEditing'
|
||||
'click .nav a': (e) -> $(e.target).tab('show')
|
||||
'click #system-patches-tab': -> @patchesView.load()
|
||||
'click #system-code-tab': 'buildCodeEditor'
|
||||
'click #system-config-schema-tab': 'buildConfigSchemaTreema'
|
||||
'click #system-settings-tab': 'buildSettingsTreema'
|
||||
'click #system-history-button': 'showVersionHistory'
|
||||
'click #patch-system-button': 'startPatchingSystem'
|
||||
'click #system-watch-button': 'toggleWatchSystem'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
@ -26,6 +36,7 @@ module.exports = class LevelSystemEditView extends View
|
|||
@buildSettingsTreema()
|
||||
@buildConfigSchemaTreema()
|
||||
@buildCodeEditor()
|
||||
@patchesView = @insertSubView(new PatchesView(@levelSystem), @$el.find('.patches-view'))
|
||||
|
||||
buildSettingsTreema: ->
|
||||
data = _.pick @levelSystem.attributes, (value, key) => key in @editableSettings
|
||||
|
@ -68,8 +79,9 @@ module.exports = class LevelSystemEditView extends View
|
|||
Backbone.Mediator.publish 'level-system-edited', levelSystem: @levelSystem
|
||||
|
||||
buildCodeEditor: ->
|
||||
editorEl = @$el.find '#system-code-editor'
|
||||
editorEl.text @levelSystem.get('code')
|
||||
@editor?.destroy()
|
||||
editorEl = $('<div></div>').text(@levelSystem.get('code')).addClass('inner-editor')
|
||||
@$el.find('#system-code-editor').empty().append(editorEl)
|
||||
@editor = ace.edit(editorEl[0])
|
||||
@editor.setReadOnly(not me.isAdmin())
|
||||
session = @editor.getSession()
|
||||
|
@ -88,6 +100,21 @@ module.exports = class LevelSystemEditView extends View
|
|||
Backbone.Mediator.publish 'level-system-editing-ended', levelSystem: @levelSystem
|
||||
null
|
||||
|
||||
showVersionHistory: (e) ->
|
||||
versionHistoryView = new VersionHistoryView {}, @levelSystem.id
|
||||
@openModalView versionHistoryView
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
startPatchingSystem: (e) ->
|
||||
@openModalView new SaveVersionModal({model:@levelSystem})
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
toggleWatchSystem: ->
|
||||
console.log 'toggle watch system?'
|
||||
button = @$el.find('#system-watch-button')
|
||||
@levelSystem.watch(button.find('.watch').is(':visible'))
|
||||
button.find('> span').toggleClass('secret')
|
||||
|
||||
destroy: ->
|
||||
@editor?.destroy()
|
||||
super()
|
||||
|
|
|
@ -23,6 +23,7 @@ module.exports = class SystemsTabView extends View
|
|||
events:
|
||||
'click #add-system-button': 'addLevelSystem'
|
||||
'click #create-new-system-button': 'createNewLevelSystem'
|
||||
'click #create-new-system': 'createNewLevelSystem'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
|
|
@ -255,7 +255,7 @@ module.exports = class ThangsTabView extends View
|
|||
# @thangsTreema.deselectAll()
|
||||
|
||||
selectAddThang: (e) =>
|
||||
return unless $(e.target).closest('.editor-level-thangs-tab-view').length
|
||||
return unless e? and $(e.target).closest('.editor-level-thangs-tab-view').length
|
||||
if e then target = $(e.target) else target = @$el.find('.add-thangs-palette') # pretend to click on background if no event
|
||||
return true if target.attr('id') is 'surface'
|
||||
target = target.closest('.add-thang-palette-icon')
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = class PatchModal extends ModalView
|
|||
id: "patch-modal"
|
||||
template: template
|
||||
plain: true
|
||||
modalWidthPercent: 60
|
||||
|
||||
events:
|
||||
'click #withdraw-button': 'withdrawPatch'
|
||||
|
@ -30,12 +31,15 @@ module.exports = class PatchModal extends ModalView
|
|||
c.isPatchCreator = @patch.get('creator') is auth.me.id
|
||||
c.isPatchRecipient = @targetModel.hasWriteAccess()
|
||||
c.status = @patch.get 'status'
|
||||
c.patch = @patch
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
return if @originalSource.loading
|
||||
headModel = @originalSource.clone(false)
|
||||
headModel.set(@targetModel.attributes)
|
||||
headModel = null
|
||||
if @targetModel.hasWriteAccess()
|
||||
headModel = @originalSource.clone(false)
|
||||
headModel.set(@targetModel.attributes)
|
||||
|
||||
pendingModel = @originalSource.clone(false)
|
||||
pendingModel.applyDelta(@patch.get('delta'))
|
||||
|
@ -48,7 +52,8 @@ module.exports = class PatchModal extends ModalView
|
|||
acceptPatch: ->
|
||||
delta = @deltaView.getApplicableDelta()
|
||||
@targetModel.applyDelta(delta)
|
||||
@targetModel.addPatchToAcceptOnSave(@patch)
|
||||
@patch.setStatus('accepted')
|
||||
@trigger 'accepted-patch'
|
||||
@hide()
|
||||
|
||||
rejectPatch: ->
|
||||
|
|
|
@ -44,8 +44,11 @@ module.exports = class PatchesView extends CocoView
|
|||
@$el.find(".#{@status}").addClass 'active'
|
||||
|
||||
onStatusButtonsChanged: (e) ->
|
||||
@loaded = false
|
||||
@status = $(e.target).val()
|
||||
@reloadPatches()
|
||||
|
||||
reloadPatches: ->
|
||||
@loaded = false
|
||||
@initPatches()
|
||||
@load()
|
||||
@render()
|
||||
|
@ -53,4 +56,9 @@ module.exports = class PatchesView extends CocoView
|
|||
openPatchModal: (e) ->
|
||||
patch = _.find @patches.models, {id:$(e.target).data('patch-id')}
|
||||
modal = new PatchModal(patch, @model)
|
||||
@openModalView(modal)
|
||||
@openModalView(modal)
|
||||
@listenTo modal, 'accepted-patch', -> @trigger 'accepted-patch'
|
||||
@listenTo modal, 'hide', ->
|
||||
f = => @reloadPatches()
|
||||
setTimeout(f, 400)
|
||||
@stopListening modal
|
9
app/views/editor/system/versions_view.coffee
Executable file
9
app/views/editor/system/versions_view.coffee
Executable file
|
@ -0,0 +1,9 @@
|
|||
VersionsModalView = require 'views/modal/versions_modal'
|
||||
|
||||
module.exports = class SystemVersionsView extends VersionsModalView
|
||||
id: "editor-system-versions-view"
|
||||
url: "/db/level.system/"
|
||||
page: "system"
|
||||
|
||||
constructor: (options, @ID) ->
|
||||
super options, ID, require 'models/LevelSystem'
|
|
@ -207,6 +207,7 @@ class CocoView extends Backbone.View
|
|||
# Modals
|
||||
|
||||
toggleModal: (e) ->
|
||||
return if visibleModal
|
||||
if $(e.currentTarget).prop('target') is '_blank'
|
||||
return true
|
||||
# special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value.
|
||||
|
|
|
@ -45,9 +45,11 @@ module.exports = class ModalView extends CocoView
|
|||
super($el)
|
||||
|
||||
hide: ->
|
||||
@trigger 'hide'
|
||||
@$el.removeClass('fade').modal "hide"
|
||||
|
||||
onHidden: ->
|
||||
@trigger 'hidden'
|
||||
|
||||
destroy: ->
|
||||
@hide() unless @hidden
|
||||
|
|
|
@ -8,6 +8,7 @@ module.exports = class SaveVersionModal extends ModalView
|
|||
id: 'save-version-modal'
|
||||
template: template
|
||||
plain: true
|
||||
modalWidthPercent: 60
|
||||
|
||||
events:
|
||||
'click #save-version-button': 'onClickSaveButton'
|
||||
|
@ -26,15 +27,16 @@ module.exports = class SaveVersionModal extends ModalView
|
|||
c.hasChanges = @model.hasLocalChanges()
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
afterRender: (insertDeltaView=true) ->
|
||||
super()
|
||||
@$el.find(if me.get('signedCLA') then '#accept-cla-wrapper' else '#save-version-button').hide()
|
||||
changeEl = @$el.find('.changes-stub')
|
||||
try
|
||||
deltaView = new DeltaView({model:@model})
|
||||
@insertSubView(deltaView, changeEl)
|
||||
catch e
|
||||
console.error "Couldn't create delta view:", e
|
||||
if insertDeltaView
|
||||
try
|
||||
deltaView = new DeltaView({model:@model})
|
||||
@insertSubView(deltaView, changeEl)
|
||||
catch e
|
||||
console.error "Couldn't create delta view:", e
|
||||
@$el.find('.commit-message input').attr('placeholder', $.i18n.t('general.commit_msg'))
|
||||
|
||||
onClickSaveButton: ->
|
||||
|
@ -54,6 +56,7 @@ module.exports = class SaveVersionModal extends ModalView
|
|||
}
|
||||
errors = patch.validate()
|
||||
forms.applyErrorsToForm(@$el, errors) if errors
|
||||
patch.set 'editPath', document.location.pathname
|
||||
res = patch.save()
|
||||
return unless res
|
||||
@enableModalInProgress(@$el)
|
||||
|
|
|
@ -13,6 +13,8 @@ class VersionsViewCollection extends Backbone.Collection
|
|||
module.exports = class VersionsModalView extends ModalView
|
||||
template: template
|
||||
startsLoading: true
|
||||
plain: true
|
||||
modalWidthPercent: 80
|
||||
|
||||
# needs to be overwritten by child
|
||||
id: ""
|
||||
|
|
|
@ -4,6 +4,8 @@ Grid = require 'gridfs-stream'
|
|||
errors = require './errors'
|
||||
log = require 'winston'
|
||||
Patch = require '../patches/Patch'
|
||||
User = require '../users/User'
|
||||
sendwithus = require '../sendwithus'
|
||||
|
||||
PROJECT = {original:1, name:1, version:1, description: 1, slug:1, kind: 1}
|
||||
FETCH_LIMIT = 200
|
||||
|
@ -95,7 +97,7 @@ module.exports = class Handler
|
|||
# this handler should be overwritten by subclasses
|
||||
if @modelClass.schema.is_patchable
|
||||
return @getPatchesFor(req, res, args[0]) if req.route.method is 'get' and args[1] is 'patches'
|
||||
return @setListening(req, res, args[0]) if req.route.method is 'put' and args[1] is 'listen'
|
||||
return @setWatching(req, res, args[0]) if req.route.method is 'put' and args[1] is 'watch'
|
||||
return @sendNotFoundError(res)
|
||||
|
||||
getPatchesFor: (req, res, id) ->
|
||||
|
@ -105,16 +107,16 @@ module.exports = class Handler
|
|||
patches = (patch.toObject() for patch in patches)
|
||||
@sendSuccess(res, patches)
|
||||
|
||||
setListening: (req, res, id) ->
|
||||
setWatching: (req, res, id) ->
|
||||
@getDocumentForIdOrSlug id, (err, document) =>
|
||||
return @sendUnauthorizedError(res) unless @hasAccessToDocument(req, document, 'get')
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res) unless document?
|
||||
listeners = document.get('listeners') or []
|
||||
watchers = document.get('watchers') or []
|
||||
me = req.user.get('_id')
|
||||
listeners = (l for l in listeners when not l.equals(me))
|
||||
listeners.push me if req.body.on
|
||||
document.set 'listeners', listeners
|
||||
watchers = (l for l in watchers when not l.equals(me))
|
||||
watchers.push me if req.body.on and req.body.on isnt 'false'
|
||||
document.set 'watchers', watchers
|
||||
document.save (err, document) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, @formatEntity(req, document))
|
||||
|
@ -233,6 +235,9 @@ module.exports = class Handler
|
|||
return @sendBadInputError(res, err.errors) if err?.valid is false
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, @formatEntity(req, document))
|
||||
@onPostSuccess(req, document)
|
||||
|
||||
onPostSuccess: (req, doc) ->
|
||||
|
||||
###
|
||||
TODO: think about pulling some common stuff out of postFirstVersion/postNewVersion
|
||||
|
@ -248,7 +253,6 @@ module.exports = class Handler
|
|||
document.set('original', document._id)
|
||||
document.set('creator', req.user._id)
|
||||
@saveChangesToDocument req, document, (err) =>
|
||||
console.log 'saved new version', document.toObject()
|
||||
return @sendBadInputError(res, err.errors) if err?.valid is false
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, @formatEntity(req, document))
|
||||
|
@ -291,6 +295,7 @@ module.exports = class Handler
|
|||
newDocument.save (err) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@sendSuccess(res, @formatEntity(req, newDocument))
|
||||
@notifyWatchersOfChange(req.user, newDocument, req.body.editPath) if @modelClass.schema.is_patchable
|
||||
|
||||
if major?
|
||||
parentDocument.makeNewMinorVersion(updatedObject, major, done)
|
||||
|
@ -298,8 +303,31 @@ module.exports = class Handler
|
|||
else
|
||||
parentDocument.makeNewMajorVersion(updatedObject, done)
|
||||
|
||||
notifyWatchersOfChange: (editor, changedDocument, editPath) ->
|
||||
watchers = changedDocument.get('watchers') or []
|
||||
watchers = (w for w in watchers when not w.equals(editor.get('_id')))
|
||||
return unless watchers.length
|
||||
User.find({_id:{$in:watchers}}).select({email:1, name:1}).exec (err, watchers) =>
|
||||
for watcher in watchers
|
||||
@notifyWatcherOfChange editor, watcher, changedDocument, editPath
|
||||
|
||||
notifyWatcherOfChange: (editor, watcher, changedDocument, editPath) ->
|
||||
context =
|
||||
email_id: sendwithus.templates.change_made_notify_watcher
|
||||
recipient:
|
||||
address: watcher.get('email')
|
||||
name: watcher.get('name')
|
||||
email_data:
|
||||
doc_name: changedDocument.get('name') or '???'
|
||||
submitter_name: editor.get('name') or '???'
|
||||
doc_link: if editPath then "http://codecombat.com#{editPath}" else null
|
||||
commit_message: changedDocument.get('commitMessage')
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
|
||||
makeNewInstance: (req) ->
|
||||
new @modelClass({})
|
||||
model = new @modelClass({})
|
||||
model.set 'watchers', [req.user.get('_id')] if @modelClass.schema.is_patchable
|
||||
model
|
||||
|
||||
validateDocumentInput: (input) ->
|
||||
tv4 = require('tv4').tv4
|
||||
|
|
|
@ -39,8 +39,10 @@ PatchSchema.pre 'save', (next) ->
|
|||
target.original = targetID
|
||||
|
||||
patches = document.get('patches') or []
|
||||
patches = _.clone patches
|
||||
patches.push @_id
|
||||
document.set 'patches', patches
|
||||
document.set 'patches', patches, {strict: false}
|
||||
@targetLoaded = document
|
||||
document.save (err) -> next(err)
|
||||
|
||||
module.exports = mongoose.model('patch', PatchSchema)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
Patch = require('./Patch')
|
||||
User = require '../users/User'
|
||||
Handler = require('../commons/Handler')
|
||||
schema = require '../../app/schemas/models/patch'
|
||||
{handlers} = require '../commons/mapping'
|
||||
mongoose = require('mongoose')
|
||||
log = require 'winston'
|
||||
sendwithus = require '../sendwithus'
|
||||
|
||||
PatchHandler = class PatchHandler extends Handler
|
||||
modelClass: Patch
|
||||
|
@ -50,6 +53,29 @@ PatchHandler = class PatchHandler extends Handler
|
|||
patch.update {$set:{status:newStatus}}, {}, ->
|
||||
target.update {$pull:{patches:patch.get('_id')}}, {}, ->
|
||||
@sendSuccess(res, null)
|
||||
|
||||
onPostSuccess: (req, doc) ->
|
||||
log.error "Error sending patch created: could not find the loaded target on the patch object." unless doc.targetLoaded
|
||||
return unless doc.targetLoaded
|
||||
watchers = doc.targetLoaded.get('watchers') or []
|
||||
watchers = (w for w in watchers when not w.equals(editor.get('_id')))
|
||||
return unless watchers?.length
|
||||
User.find({_id:{$in:watchers}}).select({email:1, name:1}).exec (err, watchers) =>
|
||||
for watcher in watchers
|
||||
@sendPatchCreatedEmail req.user, watcher, doc, doc.targetLoaded, req.body.editPath
|
||||
|
||||
sendPatchCreatedEmail: (patchCreator, watcher, patch, target, editPath) ->
|
||||
# return if watcher._id is patchCreator._id
|
||||
context =
|
||||
email_id: sendwithus.templates.patch_created
|
||||
recipient:
|
||||
address: watcher.get('email')
|
||||
name: watcher.get('name')
|
||||
email_data:
|
||||
doc_name: target.get('name') or '???'
|
||||
submitter_name: patchCreator.get('name') or '???'
|
||||
doc_link: "http://codecombat.com#{editPath}"
|
||||
commit_message: patch.get('commitMessage')
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
|
||||
module.exports = new PatchHandler()
|
||||
|
|
|
@ -14,3 +14,5 @@ if config.unittest
|
|||
module.exports.templates =
|
||||
welcome_email: 'utnGaBHuSU4Hmsi7qrAypU'
|
||||
ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4'
|
||||
patch_created: 'tem_xhxuNosLALsizTNojBjNcL'
|
||||
change_made_notify_watcher: 'tem_7KVkfmv9SZETb25dtHbUtG'
|
||||
|
|
|
@ -64,13 +64,13 @@ GLOBAL.unittest = {}
|
|||
unittest.users = unittest.users or {}
|
||||
|
||||
unittest.getNormalJoe = (done, force) ->
|
||||
unittest.getUser('normal@jo.com', 'food', done, force)
|
||||
unittest.getUser('Joe', 'normal@jo.com', 'food', done, force)
|
||||
unittest.getOtherSam = (done, force) ->
|
||||
unittest.getUser('other@sam.com', 'beer', done, force)
|
||||
unittest.getUser('Sam', 'other@sam.com', 'beer', done, force)
|
||||
unittest.getAdmin = (done, force) ->
|
||||
unittest.getUser('admin@afc.com', '80yqxpb38j', done, force)
|
||||
unittest.getUser('Admin', 'admin@afc.com', '80yqxpb38j', done, force)
|
||||
|
||||
unittest.getUser = (email, password, done, force) ->
|
||||
unittest.getUser = (name, email, password, done, force) ->
|
||||
# Creates the user if it doesn't already exist.
|
||||
|
||||
return done(unittest.users[email]) if unittest.users[email] and not force
|
||||
|
@ -81,6 +81,7 @@ unittest.getUser = (email, password, done, force) ->
|
|||
throw err if err
|
||||
User.findOne({email:email}).exec((err, user) ->
|
||||
user.set('permissions', if password is '80yqxpb38j' then [ 'admin' ] else [])
|
||||
user.set('name', name)
|
||||
user.save (err) ->
|
||||
wrapUpGetUser(email, user, done)
|
||||
)
|
||||
|
@ -88,7 +89,6 @@ unittest.getUser = (email, password, done, force) ->
|
|||
form = req.form()
|
||||
form.append('email', email)
|
||||
form.append('password', password)
|
||||
|
||||
|
||||
wrapUpGetUser = (email, user, done) ->
|
||||
unittest.users[email] = user
|
||||
|
|
|
@ -16,13 +16,14 @@ describe '/db/patch', ->
|
|||
patch =
|
||||
commitMessage: 'Accept this patch!'
|
||||
delta: {name:['test']}
|
||||
editPath: '/who/knows/yes'
|
||||
target:
|
||||
id:null
|
||||
collection: 'article'
|
||||
|
||||
it 'creates an Article to patch', (done) ->
|
||||
loginAdmin ->
|
||||
request.post {uri:articleURL, json:patch}, (err, res, body) ->
|
||||
request.post {uri:articleURL, json:article}, (err, res, body) ->
|
||||
articles[0] = body
|
||||
patch.target.id = articles[0]._id
|
||||
done()
|
||||
|
@ -51,29 +52,29 @@ describe '/db/patch', ->
|
|||
body = JSON.parse(body)
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(body.length).toBe(1)
|
||||
done()
|
||||
done()
|
||||
|
||||
it 'allows you to set yourself as listening', (done) ->
|
||||
listeningURL = getURL("/db/article/#{articles[0]._id}/listen")
|
||||
request.put {uri: listeningURL, json: {on:true}}, (err, res, body) ->
|
||||
expect(body.listeners[0]).toBeDefined()
|
||||
it 'allows you to set yourself as watching', (done) ->
|
||||
watchingURL = getURL("/db/article/#{articles[0]._id}/watch")
|
||||
request.put {uri: watchingURL, json: {on:true}}, (err, res, body) ->
|
||||
expect(body.watchers[1]).toBeDefined()
|
||||
done()
|
||||
|
||||
it 'added the listener to the target document', (done) ->
|
||||
it 'added the watcher to the target document', (done) ->
|
||||
Article.findOne({}).exec (err, article) ->
|
||||
expect(article.toObject().listeners[0]).toBeDefined()
|
||||
expect(article.toObject().watchers[1]).toBeDefined()
|
||||
done()
|
||||
|
||||
it 'does not add duplicate listeners', (done) ->
|
||||
listeningURL = getURL("/db/article/#{articles[0]._id}/listen")
|
||||
request.put {uri: listeningURL, json: {on:true}}, (err, res, body) ->
|
||||
expect(body.listeners.length).toBe(1)
|
||||
it 'does not add duplicate watchers', (done) ->
|
||||
watchingURL = getURL("/db/article/#{articles[0]._id}/watch")
|
||||
request.put {uri: watchingURL, json: {on:true}}, (err, res, body) ->
|
||||
expect(body.watchers.length).toBe(2)
|
||||
done()
|
||||
|
||||
it 'allows removing yourself', (done) ->
|
||||
listeningURL = getURL("/db/article/#{articles[0]._id}/listen")
|
||||
request.put {uri: listeningURL, json: {on:false}}, (err, res, body) ->
|
||||
expect(body.listeners.length).toBe(0)
|
||||
watchingURL = getURL("/db/article/#{articles[0]._id}/watch")
|
||||
request.put {uri: watchingURL, json: {on:false}}, (err, res, body) ->
|
||||
expect(body.watchers.length).toBe(1)
|
||||
done()
|
||||
|
||||
it 'allows the submitter to withdraw the pull request', (done) ->
|
||||
|
|
|
@ -112,7 +112,7 @@ ghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghl
|
|||
unittest.getNormalJoe (joe) ->
|
||||
req = request.put getURL(urlUser), (err, res) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
unittest.getUser('New@email.com', 'null', (joe) ->
|
||||
unittest.getUser('Wilhelm', 'New@email.com', 'null', (joe) ->
|
||||
expect(joe.get('name')).toBe('Wilhelm')
|
||||
expect(joe.get('emailLower')).toBe('new@email.com')
|
||||
expect(joe.get('email')).toBe('New@email.com')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue