mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Add basic BetaProgram
feature.
This commit is contained in:
parent
a297c07bbb
commit
d8f1e8ec93
8 changed files with 263 additions and 0 deletions
|
@ -0,0 +1,29 @@
|
||||||
|
BetaProgramHandler = require './BetaProgramHandler'
|
||||||
|
UserLocator = require "../User/UserLocator"
|
||||||
|
Settings = require "settings-sharelatex"
|
||||||
|
logger = require 'logger-sharelatex'
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = BetaProgramController =
|
||||||
|
|
||||||
|
optIn: (req, res, next) ->
|
||||||
|
user_id = req?.session?.user?._id
|
||||||
|
logger.log {user_id}, "user opting in to beta program"
|
||||||
|
if !user_id
|
||||||
|
return next(new Error("no user id in session"))
|
||||||
|
BetaProgramHandler.optIn user_id, (err) ->
|
||||||
|
if err
|
||||||
|
return next(err)
|
||||||
|
return res.redirect "/"
|
||||||
|
|
||||||
|
optInPage: (req, res, next)->
|
||||||
|
user_id = req.session?.user?._id
|
||||||
|
logger.log {user_id}, "showing beta opt-in page for user"
|
||||||
|
UserLocator.findById user_id, (err, user)->
|
||||||
|
if err
|
||||||
|
logger.err {err, user_id}, "error fetching user"
|
||||||
|
return next(err)
|
||||||
|
res.render 'beta_program/opt_in',
|
||||||
|
title:'beta_program_opt_in'
|
||||||
|
user: user,
|
||||||
|
languages: Settings.languages,
|
|
@ -0,0 +1,18 @@
|
||||||
|
User = require("../../models/User").User
|
||||||
|
logger = require 'logger-sharelatex'
|
||||||
|
metrics = require("../../infrastructure/Metrics")
|
||||||
|
|
||||||
|
module.exports = BetaProgramHandler =
|
||||||
|
|
||||||
|
optIn: (user_id, callback=(err)->) ->
|
||||||
|
User.findById user_id, (err, user) ->
|
||||||
|
if err
|
||||||
|
logger.err {err, user_id}, "problem adding user to beta"
|
||||||
|
return callback(err)
|
||||||
|
metrics.inc "beta-program.opt-in"
|
||||||
|
user.betaProgram = true
|
||||||
|
user.save (err) ->
|
||||||
|
if err
|
||||||
|
logger.err {err, user_id}, "problem adding user to beta"
|
||||||
|
return callback(err)
|
||||||
|
return callback(null)
|
|
@ -197,6 +197,9 @@ module.exports = class Router
|
||||||
webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
|
webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
|
||||||
webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
|
webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll
|
||||||
|
|
||||||
|
webRouter.get "/beta/opt-in", AuthenticationController.requireLogin(), BetaProgramController.optInPage
|
||||||
|
webRouter.post "/beta/opt-in", AuthenticationController.requireLogin(), BetaProgramController.optIn
|
||||||
|
|
||||||
#Admin Stuff
|
#Admin Stuff
|
||||||
webRouter.get '/admin', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.index
|
webRouter.get '/admin', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.index
|
||||||
webRouter.get '/admin/user', AuthorizationMiddlewear.ensureUserIsSiteAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
|
webRouter.get '/admin/user', AuthorizationMiddlewear.ensureUserIsSiteAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
|
||||||
|
|
30
services/web/app/views/beta_program/opt_in.jade
Normal file
30
services/web/app/views/beta_program/opt_in.jade
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content.content-alt
|
||||||
|
.container.beta-opt-in-wrapper
|
||||||
|
.row
|
||||||
|
.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
|
||||||
|
.card
|
||||||
|
.page-header.text-centered
|
||||||
|
h1
|
||||||
|
| #{translate("sharelatex_beta_program")}
|
||||||
|
.beta-opt-in
|
||||||
|
.container-fluid
|
||||||
|
.row
|
||||||
|
.col-md-12
|
||||||
|
p.text-centered #{translate("beta_program_benefits")}
|
||||||
|
.row.text-centered
|
||||||
|
.col-md-12
|
||||||
|
if user.betaProgram
|
||||||
|
p #{translate("beta_program_already_participating")}
|
||||||
|
else
|
||||||
|
form(method="post", action="/beta/opt-in", novalidate)
|
||||||
|
.form-group
|
||||||
|
input(type="hidden", name="_csrf", value=csrfToken)
|
||||||
|
button.btn.btn-lg.btn-primary(
|
||||||
|
type="submit"
|
||||||
|
)
|
||||||
|
span #{translate("beta_program_opt_in_action")}
|
||||||
|
span.beta-feature-badge(tooltip="Beta Feature" tooltip-placement="right") β
|
||||||
|
|
16
services/web/public/stylesheets/app/beta-program.less
Normal file
16
services/web/public/stylesheets/app/beta-program.less
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
.beta-opt-in-wrapper {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-opt-in {
|
||||||
|
.form-group {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta-feature-badge {
|
||||||
|
&:extend(.label);
|
||||||
|
&:extend(.label-warning);
|
||||||
|
padding-bottom: 2px;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
|
@ -58,6 +58,7 @@
|
||||||
// ShareLaTeX app classes
|
// ShareLaTeX app classes
|
||||||
@import "app/base.less";
|
@import "app/base.less";
|
||||||
@import "app/account-settings.less";
|
@import "app/account-settings.less";
|
||||||
|
@import "app/beta-program.less";
|
||||||
@import "app/about-page.less";
|
@import "app/about-page.less";
|
||||||
@import "app/project-list.less";
|
@import "app/project-list.less";
|
||||||
@import "app/editor.less";
|
@import "app/editor.less";
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
should = require('chai').should()
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
assert = require('assert')
|
||||||
|
path = require('path')
|
||||||
|
sinon = require('sinon')
|
||||||
|
modulePath = path.join __dirname, "../../../../app/js/Features/BetaProgram/BetaProgramController"
|
||||||
|
expect = require("chai").expect
|
||||||
|
|
||||||
|
describe "BetaProgramController", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user =
|
||||||
|
_id: @user_id = "a_simple_id"
|
||||||
|
email: "user@example.com"
|
||||||
|
features: {}
|
||||||
|
betaProgram: false
|
||||||
|
@req =
|
||||||
|
query: {}
|
||||||
|
session:
|
||||||
|
user: @user
|
||||||
|
@BetaProgramController = SandboxedModule.require modulePath, requires:
|
||||||
|
"./BetaProgramHandler": @BetaProgramHandler = {
|
||||||
|
optIn: sinon.stub()
|
||||||
|
},
|
||||||
|
"../User/UserLocator": @UserLocator = {
|
||||||
|
findById: sinon.stub()
|
||||||
|
},
|
||||||
|
"settings-sharelatex": @settings = {
|
||||||
|
languages: {}
|
||||||
|
}
|
||||||
|
"logger-sharelatex": @logger = {
|
||||||
|
log: sinon.stub()
|
||||||
|
err: sinon.stub()
|
||||||
|
error: sinon.stub()
|
||||||
|
}
|
||||||
|
@res =
|
||||||
|
send: sinon.stub()
|
||||||
|
redirect: sinon.stub()
|
||||||
|
render: sinon.stub()
|
||||||
|
@next = sinon.stub()
|
||||||
|
|
||||||
|
describe "optIn", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@BetaProgramHandler.optIn.callsArgWith(1, null)
|
||||||
|
|
||||||
|
it "should redirect to '/'", () ->
|
||||||
|
@BetaProgramController.optIn @req, @res, @next
|
||||||
|
@res.redirect.callCount.should.equal 1
|
||||||
|
|
||||||
|
it "should not call next with an error", () ->
|
||||||
|
@BetaProgramController.optIn @req, @res, @next
|
||||||
|
@next.callCount.should.equal 0
|
||||||
|
|
||||||
|
it "should not call next with an error", () ->
|
||||||
|
@BetaProgramController.optIn @req, @res, @next
|
||||||
|
@next.callCount.should.equal 0
|
||||||
|
|
||||||
|
it "should call BetaProgramHandler.optIn", () ->
|
||||||
|
@BetaProgramController.optIn @req, @res, @next
|
||||||
|
@BetaProgramHandler.optIn.callCount.should.equal 1
|
||||||
|
|
||||||
|
describe "when BetaProgramHandler.opIn produces an error", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@BetaProgramHandler.optIn.callsArgWith(1, new Error('woops'))
|
||||||
|
|
||||||
|
it "should not redirect to '/'", () ->
|
||||||
|
@BetaProgramController.optIn @req, @res, @next
|
||||||
|
@res.redirect.callCount.should.equal 0
|
||||||
|
|
||||||
|
it "should produce an error", () ->
|
||||||
|
@BetaProgramController.optIn @req, @res, @next
|
||||||
|
@next.callCount.should.equal 1
|
||||||
|
@next.firstCall.args[0].should.be.instanceof Error
|
||||||
|
|
||||||
|
describe "optInPage", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@UserLocator.findById.callsArgWith(1, null, @user)
|
||||||
|
|
||||||
|
it "should render the opt-in page", () ->
|
||||||
|
@BetaProgramController.optInPage @req, @res, @next
|
||||||
|
@res.render.callCount.should.equal 1
|
||||||
|
args = @res.render.firstCall.args
|
||||||
|
args[0].should.equal 'beta_program/opt_in'
|
||||||
|
|
||||||
|
|
||||||
|
describe "when UserLocator.findById produces an error", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@UserLocator.findById.callsArgWith(1, new Error('woops'))
|
||||||
|
|
||||||
|
it "should not render the opt-in page", () ->
|
||||||
|
@BetaProgramController.optInPage @req, @res, @next
|
||||||
|
@res.render.callCount.should.equal 0
|
||||||
|
|
||||||
|
it "should produce an error", () ->
|
||||||
|
@BetaProgramController.optInPage @req, @res, @next
|
||||||
|
@next.callCount.should.equal 1
|
||||||
|
@next.firstCall.args[0].should.be.instanceof Error
|
|
@ -0,0 +1,65 @@
|
||||||
|
should = require('chai').should()
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
assert = require('assert')
|
||||||
|
path = require('path')
|
||||||
|
modulePath = path.join __dirname, '../../../../app/js/Features/BetaProgram/BetaProgramHandler'
|
||||||
|
sinon = require("sinon")
|
||||||
|
expect = require("chai").expect
|
||||||
|
|
||||||
|
|
||||||
|
describe 'BetaProgramHandler', ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user_id = "some_id"
|
||||||
|
@user =
|
||||||
|
_id: @user_id
|
||||||
|
email: 'user@example.com'
|
||||||
|
features: {}
|
||||||
|
betaProgram: false
|
||||||
|
save: sinon.stub().callsArgWith(0, null)
|
||||||
|
@handler = SandboxedModule.require modulePath, requires:
|
||||||
|
"../../models/User": {
|
||||||
|
User:
|
||||||
|
findById: sinon.stub().callsArgWith(1, null, @user)
|
||||||
|
},
|
||||||
|
"logger-sharelatex": @logger = {
|
||||||
|
log: sinon.stub()
|
||||||
|
err: sinon.stub()
|
||||||
|
},
|
||||||
|
"../../infrastructure/Metrics": @logger = {
|
||||||
|
inc: sinon.stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
describe "optIn", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@call = (callback) =>
|
||||||
|
@handler.optIn @user_id, callback
|
||||||
|
|
||||||
|
it "should set betaProgram = true on user object", (done) ->
|
||||||
|
@call (err) =>
|
||||||
|
@user.betaProgram.should.equal true
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should call user.save", (done) ->
|
||||||
|
@call (err) =>
|
||||||
|
@user.save.callCount.should.equal 1
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should not produce an error", (done) ->
|
||||||
|
@call (err) =>
|
||||||
|
expect(err).to.equal null
|
||||||
|
expect(err).to.not.be.instanceof Error
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "when user.save produces an error", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@user.save.callsArgWith(0, new Error('woops'))
|
||||||
|
|
||||||
|
it "should produce an error", (done) ->
|
||||||
|
@call (err) =>
|
||||||
|
expect(err).to.not.equal null
|
||||||
|
expect(err).to.be.instanceof Error
|
||||||
|
done()
|
Loading…
Reference in a new issue