This commit is contained in:
Henry Oswald 2016-06-08 15:36:20 +01:00
commit 895af7c07a
11 changed files with 283 additions and 2 deletions

View file

@ -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 "/beta/opt-in"
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:'sharelatex_beta_program'
user: user,
languages: Settings.languages,

View file

@ -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)

View file

@ -38,6 +38,7 @@ InactiveProjectController = require("./Features/InactiveData/InactiveProjectCont
ContactRouter = require("./Features/Contacts/ContactRouter")
ReferencesController = require('./Features/References/ReferencesController')
AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
logger = require("logger-sharelatex")
_ = require("underscore")
@ -197,6 +198,9 @@ module.exports = class Router
webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index
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
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

View file

@ -0,0 +1,40 @@
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")}
p.text-centered
| #{translate("beta_program_badge_description")}
span.beta-feature-badge
p.text-centered
| #{translate("beta_program_current_beta_features_description")}
ul.list-unstyled.text-center
li
i.fa.fa-fw.fa-book
|  #{translate("mendeley_integration")}
.row.text-centered
.col-md-12
if user.betaProgram
p #{translate("beta_program_already_participating")}
.form-group
a(href="/project").btn.btn-info #{translate("back_to_your_projects")}
else
form(method="post", action="/beta/opt-in", novalidate)
.form-group
input(type="hidden", name="_csrf", value=csrfToken)
button.btn.btn-primary(
type="submit"
)
span #{translate("beta_program_opt_in_action")}

View file

@ -15,7 +15,7 @@ aside#left-menu.full-size(
| #{translate("source")}
li
a(
ng-href="{{pdf.url}}"
ng-href="{{pdf.downloadUrl || pdf.url}}"
target="_blank"
ng-if="pdf.url"
)

View file

@ -46,7 +46,7 @@ div.full-size.pdf(ng-controller="PdfController")
) {{ pdf.logEntries.errors.length + pdf.logEntries.warnings.length }}
a(
ng-href="{{pdf.url}}"
ng-href="{{pdf.downloadUrl || pdf.url}}"
target="_blank"
ng-if="pdf.url"
tooltip="#{translate('download_pdf')}"

View file

@ -104,6 +104,7 @@ define [
qs_args = ("#{k}=#{v}" for k, v of qs)
$scope.pdf.qs = if qs_args.length then "?" + qs_args.join("&") else ""
$scope.pdf.url += $scope.pdf.qs
$scope.pdf.downloadUrl = "/Project/#{$scope.project_id}/output/output.pdf" + $scope.pdf.qs
fetchLogs(fileByPath['output.log'], fileByPath['output.blg'])

View file

@ -0,0 +1,21 @@
.beta-opt-in-wrapper {
min-height: 400px;
}
.beta-opt-in {
.form-group {
margin-top: 15px;
}
}
.beta-feature-badge {
&:extend(.label);
&:extend(.label-warning);
vertical-align: 11%;
padding-bottom: 4px;
padding-top: 2px;
margin-left: 12px;
&:before {
content: "β";
}
}

View file

@ -58,6 +58,7 @@
// ShareLaTeX app classes
@import "app/base.less";
@import "app/account-settings.less";
@import "app/beta-program.less";
@import "app/about-page.less";
@import "app/project-list.less";
@import "app/editor.less";

View file

@ -0,0 +1,102 @@
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 '/beta/opt-in'", () ->
@BetaProgramController.optIn @req, @res, @next
@res.redirect.callCount.should.equal 1
@res.redirect.firstCall.args[0].should.equal "/beta/opt-in"
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

View file

@ -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()