mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Add reset progress feature in account settings
This commit is contained in:
parent
62cd00e328
commit
9e61928080
5 changed files with 103 additions and 38 deletions
|
@ -725,6 +725,8 @@
|
|||
wrong_password: "Wrong Password"
|
||||
upload_picture: "Upload a picture"
|
||||
delete_this_account: "Delete this account permanently"
|
||||
reset_progress_tab: "Reset All Progress"
|
||||
reset_your_progress: "Clear all your progress and start over"
|
||||
god_mode: "God Mode"
|
||||
password_tab: "Password"
|
||||
emails_tab: "Emails"
|
||||
|
@ -732,6 +734,7 @@
|
|||
new_password: "New Password"
|
||||
new_password_verify: "Verify"
|
||||
type_in_email: "Type in your email to confirm account deletion."
|
||||
type_in_email_progress: "Type in your email to confirm deleting your progress."
|
||||
type_in_password: "Also, type in your password."
|
||||
email_subscriptions: "Email Subscriptions"
|
||||
email_subscriptions_none: "No Email Subscriptions."
|
||||
|
|
|
@ -52,13 +52,13 @@ else
|
|||
.panel-heading
|
||||
.panel-title#delete-account-panel-title(data-i18n="account_settings.delete_account_tab")
|
||||
.panel-body
|
||||
.form
|
||||
.form#delete-account-form
|
||||
.form-group
|
||||
label.control-label(for="email1", data-i18n="account_settings.type_in_email")
|
||||
input#email1.form-control(name="email1", type="text")
|
||||
.form-group
|
||||
input#email1.form-control(name="email", type="email")
|
||||
.form-group
|
||||
label.control-label(for="password1", data-i18n="account_settings.type_in_password")
|
||||
input#password1.form-control(name="password1", type="password")
|
||||
input#password1.form-control(name="password", type="password")
|
||||
button#delete-account-btn.btn.form-control.btn-primary(data-i18n="account_settings.delete_this_account")
|
||||
|
||||
.col-md-6
|
||||
|
@ -96,7 +96,7 @@ else
|
|||
hr
|
||||
|
||||
h4(data-i18n="account_settings.contributor_emails") Contributor Class Emails
|
||||
span(data-i18n="account_settings.contribute_prefix") We're looking for people to join our party! Check out the
|
||||
span(data-i18n="account_settings.contribute_prefix") We\'re looking for people to join our party! Check out the
|
||||
a(href="/contribute", data-i18n="account_settings.contribute_page") contribute page
|
||||
span(data-i18n="account_settings.contribute_suffix") to find out more.
|
||||
|
||||
|
@ -163,4 +163,19 @@ else
|
|||
|
||||
button#toggle-all-btn.btn.btn-primary.form-control(data-i18n="account_settings.email_toggle") Toggle All
|
||||
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
.panel-title#reset-progress-panel-title(data-i18n="account_settings.reset_progress_tab")
|
||||
.panel-body
|
||||
.form#reset-progress-form
|
||||
.form-group
|
||||
label.control-label(for="email-reset-progress", data-i18n="account_settings.type_in_email_progress")
|
||||
input#email-reset-progress.form-control(name="email", type="email")
|
||||
.form-group
|
||||
label.control-label(for="password-reset-progress", data-i18n="account_settings.type_in_password")
|
||||
input#password-reset-progress.form-control(name="password", type="password")
|
||||
button#reset-progress-btn.btn.form-control.btn-primary(data-i18n="account_settings.reset_your_progress")
|
||||
|
||||
|
||||
|
||||
.clearfix
|
||||
|
|
|
@ -18,6 +18,7 @@ module.exports = class AccountSettingsView extends CocoView
|
|||
'click #toggle-all-btn': 'onClickToggleAllButton'
|
||||
'click #profile-photo-panel-body': 'onClickProfilePhotoPanelBody'
|
||||
'click #delete-account-btn': 'onClickDeleteAccountButton'
|
||||
'click #reset-progress-btn': 'onClickResetProgressButton'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
@ -62,61 +63,68 @@ module.exports = class AccountSettingsView extends CocoView
|
|||
@trigger 'inputChanged', e
|
||||
@$el.find('.gravatar-fallback').toggle not me.get 'photoURL'
|
||||
|
||||
onClickDeleteAccountButton: (e) ->
|
||||
@validateCredentialsForDestruction @$el.find('#delete-account-form'), =>
|
||||
renderData =
|
||||
confirmTitle: 'Are you really sure?'
|
||||
confirmBody: 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?'
|
||||
confirmDecline: 'Not really'
|
||||
confirmConfirm: 'Definitely'
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @deleteAccount
|
||||
@openModalView confirmModal
|
||||
|
||||
#- Just copied from OptionsView, TODO refactor
|
||||
onClickDeleteAccountButton: ->
|
||||
forms.clearFormAlerts(@$el)
|
||||
myEmail = me.get 'email'
|
||||
email1 = document.getElementById('email1').value
|
||||
password1 = document.getElementById('password1').value
|
||||
if Boolean(email1) and email1 is myEmail
|
||||
onClickResetProgressButton: ->
|
||||
@validateCredentialsForDestruction @$el.find('#reset-progress-form'), =>
|
||||
renderData =
|
||||
confirmTitle: 'Are you really sure?'
|
||||
confirmBody: 'This will completely erase your progress: code, levels, achievements, earned gems, etc. This action CANNOT be undone. Are you entirely sure?'
|
||||
confirmDecline: 'Not really'
|
||||
confirmConfirm: 'Definitely'
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @resetProgress
|
||||
@openModalView confirmModal
|
||||
|
||||
validateCredentialsForDestruction: ($form, onSuccess) ->
|
||||
forms.clearFormAlerts($form)
|
||||
enteredEmail = $form.find('input[type="email"]').val()
|
||||
enteredPassword = $form.find('input[type="password"]').val()
|
||||
if enteredEmail and enteredEmail is me.get('email')
|
||||
isPasswordCorrect = false
|
||||
toBeDelayed = true
|
||||
$.ajax
|
||||
url: '/auth/login'
|
||||
type: 'POST'
|
||||
data:
|
||||
{
|
||||
username: email1,
|
||||
password: password1
|
||||
}
|
||||
username: enteredEmail
|
||||
password: enteredPassword
|
||||
parse: true
|
||||
error: (error) ->
|
||||
toBeDelayed = false
|
||||
'Bad Error. Can\'t connect to server or something. ' + error
|
||||
success: (response, textStatus, jqXHR) ->
|
||||
toBeDelayed = false
|
||||
unless jqXHR.status is 200
|
||||
return
|
||||
return unless jqXHR.status is 200
|
||||
isPasswordCorrect = true
|
||||
callback = =>
|
||||
if toBeDelayed
|
||||
setTimeout callback, 100
|
||||
else
|
||||
if isPasswordCorrect
|
||||
renderData =
|
||||
'confirmTitle': 'Are you really sure?'
|
||||
'confirmBody': 'This will completely delete your account. This action CANNOT be undone. Are you entirely sure?'
|
||||
'confirmDecline': 'Not really'
|
||||
'confirmConfirm': 'Definitely'
|
||||
confirmModal = new ConfirmModal renderData
|
||||
confirmModal.on 'confirm', @deleteAccount
|
||||
@openModalView confirmModal
|
||||
onSuccess()
|
||||
else
|
||||
message = $.i18n.t('account_settings.wrong_password', defaultValue: 'Wrong Password.')
|
||||
err = [message: message, property: 'password1', formatted: true]
|
||||
forms.applyErrorsToForm(@$el, err)
|
||||
err = [message: message, property: 'password', formatted: true]
|
||||
forms.applyErrorsToForm($form, err)
|
||||
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
|
||||
setTimeout callback, 100
|
||||
else
|
||||
message = $.i18n.t('account_settings.wrong_email', defaultValue: 'Wrong Email.')
|
||||
err = [message: message, property: 'email1', formatted: true]
|
||||
forms.applyErrorsToForm(@$el, err)
|
||||
err = [message: message, property: 'email', formatted: true]
|
||||
forms.applyErrorsToForm($form, err)
|
||||
$('.nano').nanoScroller({scrollTo: @$el.find('.has-error')})
|
||||
|
||||
|
||||
deleteAccount: ->
|
||||
myID = me.id
|
||||
$.ajax
|
||||
type: 'DELETE'
|
||||
success: ->
|
||||
|
@ -133,11 +141,33 @@ module.exports = class AccountSettingsView extends CocoView
|
|||
, 500
|
||||
error: (jqXHR, status, error) ->
|
||||
console.error jqXHR
|
||||
timeout: 5000
|
||||
text: "Deleting account failed with error code #{jqXHR.status}"
|
||||
type: 'error'
|
||||
layout: 'topCenter'
|
||||
url: "/db/user/#{myID}"
|
||||
noty
|
||||
timeout: 5000
|
||||
text: "Deleting account failed with error code #{jqXHR.status}"
|
||||
type: 'error'
|
||||
layout: 'topCenter'
|
||||
url: "/db/user/#{me.id}"
|
||||
|
||||
resetProgress: ->
|
||||
$.ajax
|
||||
type: 'POST'
|
||||
success: ->
|
||||
noty
|
||||
timeout: 5000
|
||||
text: 'Your progress is gone.'
|
||||
type: 'success'
|
||||
layout: 'topCenter'
|
||||
localStorage.clear()
|
||||
me.fetch cache: false
|
||||
_.delay (-> window.location.reload()), 1000
|
||||
error: (jqXHR, status, error) ->
|
||||
console.error jqXHR
|
||||
noty
|
||||
timeout: 5000
|
||||
text: "Resetting progress failed with error code #{jqXHR.status}"
|
||||
type: 'error'
|
||||
layout: 'topCenter'
|
||||
url: "/db/user/#{me.id}/reset_progress"
|
||||
|
||||
onClickProfilePhotoPanelBody: (e) ->
|
||||
return if window.application.isIPadApp # TODO: have an iPad-native way of uploading a photo, since we don't want to load FilePicker on iPad (memory)
|
||||
|
|
|
@ -87,6 +87,7 @@ module.exports = class PollModal extends ModalView
|
|||
randomNumber = reward.random
|
||||
randomGems = Math.ceil 2 * randomNumber * reward.level
|
||||
totalGems = if @previousReward then me.gems() else Math.round me.gems() + randomGems
|
||||
playSound = @playSound
|
||||
|
||||
if @previousReward
|
||||
utils.replaceText @$randomNumber.show(), commentStart + randomNumber.toFixed(7)
|
||||
|
@ -103,7 +104,7 @@ module.exports = class PollModal extends ModalView
|
|||
if Math.random() < randomGems / 40
|
||||
gemTrigger = 'gem-' + (gemNoisesPlayed % 4) # 4 gem sounds
|
||||
++gemNoisesPlayed
|
||||
@playSound gemTrigger, (0.475 + i / 2000)
|
||||
playSound gemTrigger, (0.475 + i / 2000)
|
||||
@$randomNumber.delay 25
|
||||
@$randomGems.delay(1100).queue ->
|
||||
utils.replaceText $(@), commentStart + randomGems
|
||||
|
|
|
@ -24,6 +24,8 @@ UserRemark = require './remarks/UserRemark'
|
|||
hipchat = require '../hipchat'
|
||||
sendwithus = require '../sendwithus'
|
||||
Prepaid = require '../prepaids/Prepaid'
|
||||
UserPollsRecord = require '../polls/UserPollsRecord'
|
||||
EarnedAchievement = require '../achievements/EarnedAchievement'
|
||||
|
||||
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset', 'lastIP']
|
||||
candidateProperties = [
|
||||
|
@ -326,6 +328,7 @@ UserHandler = class UserHandler extends Handler
|
|||
return @getSubSponsor(req, res) if args[1] is 'sub_sponsor'
|
||||
return @getSubSponsors(req, res) if args[1] is 'sub_sponsors'
|
||||
return @sendOneTimeEmail(req, res, args[0]) if args[1] is 'send_one_time_email'
|
||||
return @resetProgress(req, res, args[0]) if args[1] is 'reset_progress'
|
||||
return @sendNotFoundError(res)
|
||||
super(arguments...)
|
||||
|
||||
|
@ -697,6 +700,19 @@ UserHandler = class UserHandler extends Handler
|
|||
return @sendDatabaseError res, err if err
|
||||
@sendSuccess res, users
|
||||
|
||||
resetProgress: (req, res, userID) ->
|
||||
return @sendMethodNotAllowed res unless req.method is 'POST'
|
||||
return @sendForbiddenError res unless userID and userID is req.user?._id + '' # Only you can reset your own progress
|
||||
return @sendForbiddenError res if req.user?.isAdmin() # Protect admins from resetting their progress
|
||||
async.parallel [
|
||||
(cb) -> LevelSession.remove {creator: req.user._id + ''}, cb
|
||||
(cb) -> EarnedAchievement.remove {user: req.user._id + ''}, cb
|
||||
(cb) -> UserPollsRecord.remove {user: req.user._id + ''}, cb
|
||||
(cb) -> req.user.update {points: 0, 'stats.gamesCompleted': 0, 'stats.concepts': {}, 'earned.gems': 0, 'earned.levels': [], 'earned.items': [], 'earned.heroes': [], 'purchased.items': [], 'purchased.heroes': [], spent: 0}, cb
|
||||
], (err, results) =>
|
||||
return @sendDatabaseError res, err if err
|
||||
@sendSuccess res, result: 'success'
|
||||
|
||||
|
||||
countEdits = (model, done) ->
|
||||
statKey = User.statsMapping.edits[model.modelName]
|
||||
|
|
Loading…
Reference in a new issue