Merge branch 'master' into dcl-i850

Conflicts repaired:
app/views/user/settings.pug

modified:   app/coffee/Features/Authentication/AuthenticationController.coffee
modified:   app/coffee/Features/Editor/EditorHttpController.coffee
modified:   app/coffee/Features/Email/EmailBuilder.coffee
modified:   app/coffee/Features/Errors/Errors.coffee
modified:   app/coffee/Features/Exports/ExportsController.coffee
modified:   app/coffee/Features/Exports/ExportsHandler.coffee
modified:   app/coffee/Features/Notifications/NotificationsBuilder.coffee
modified:   app/coffee/Features/Notifications/NotificationsHandler.coffee
modified:   app/coffee/Features/Project/ProjectController.coffee
modified:   app/coffee/Features/StaticPages/HomeController.coffee
modified:   app/coffee/Features/Subscription/planFeatures.coffee
modified:   app/coffee/Features/TokenAccess/TokenAccessController.coffee
modified:   app/coffee/Features/TokenAccess/TokenAccessHandler.coffee
modified:   app/coffee/Features/User/UserGetter.coffee
modified:   app/coffee/infrastructure/RedirectManager.coffee
modified:   app/coffee/infrastructure/Server.coffee
modified:   app/coffee/models/User.coffee
modified:   app/coffee/router.coffee
modified:   app/views/_mixins/links.pug
modified:   app/views/general/500.pug
modified:   app/views/layout/navbar.pug
modified:   app/views/project/editor/header.pug
modified:   app/views/project/editor/share.pug
modified:   app/views/project/list.pug
modified:   app/views/project/list/modals.pug
modified:   app/views/project/list/notifications.pug
modified:   app/views/project/list/side-bar.pug
modified:   app/views/project/list/v1-item.pug
modified:   app/views/subscriptions/_modal_group_inquiry.pug
modified:   app/views/subscriptions/dashboard.pug
modified:   app/views/user/settings.pug
modified:   config/settings.defaults.coffee
modified:   npm-shrinkwrap.json
modified:   package.json
new file:   public/apple-touch-icon.png
new file:   public/atlassian-domain-verification.html
modified:   public/coffee/ide/editor/directives/aceEditor.coffee
modified:   public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee
modified:   public/coffee/ide/share/controllers/ShareController.coffee
modified:   public/coffee/main.coffee
deleted:    public/coffee/main/account-merge-checker.coffee
modified:   public/coffee/main/contact-us.coffee
modified:   public/coffee/main/project-list/left-hand-menu-promo-controller.coffee
new file:   public/google4f15e48c48709a75.html
new file:   public/googleef256f97939bd9b7.html
new file:   public/img/advocates/friend.jpeg
new file:   public/img/grid.png
new file:   public/img/homepage.png
new file:   public/img/homepage@2x.png
new file:   public/img/other-brands/logo_google.svg
new file:   public/img/other-brands/logo_google_alt.svg
new file:   public/img/other-brands/logo_ieee.svg
new file:   public/img/other-brands/logo_orcid.svg
new file:   public/img/other-brands/logo_orcid_alt.svg
new file:   public/img/other-brands/logo_sharelatex.svg
new file:   public/img/other-brands/logo_twitter.svg
new file:   public/img/overleaf-partner/overleaf-greygreen-410.png
new file:   public/img/overleaf-partner/overleaf-white-410.png
new file:   public/static/brochures/Overleaf-Information-v8.pdf
new file:   public/static/brochures/Overleaf-Institutional-Solutions-v1.pdf
new file:   public/static/brochures/Overleaf-Tri-Fold-leaflet.pdf
new file:   public/static/brochures/Overleaf-request-for-service.pdf
new file:   public/static/brochures/Overleaf_Institutional.pdf
new file:   public/static/campus-challenge/campus-challenge-2016-flyer.pdf
new file:   public/static/campus-challenge/campus-challenge-2016-poster.pdf
new file:   public/static/campus-challenge/campus-challenge-2017-flyer.pdf
new file:   public/static/campus-challenge/campus-challenge-2017-poster.pdf
new file:   public/static/latex/learn/free-online-introduction-to-latex-part-1.pdf
new file:   public/static/latex/learn/free-online-introduction-to-latex-part-2.pdf
new file:   public/static/latex/learn/free-online-introduction-to-latex-part-3.pdf
new file:   public/static/partners/caltech-library.gif
new file:   public/static/partners/stanford-building.jpg
modified:   public/stylesheets/_ol_style_includes.less
modified:   public/stylesheets/app/blog-posts.less
modified:   public/stylesheets/app/cms-page.less
new file:   public/stylesheets/app/content_page.less
modified:   public/stylesheets/app/editor/share.less
modified:   public/stylesheets/app/editor/toolbar.less
modified:   public/stylesheets/app/features.less
modified:   public/stylesheets/app/homepage.less
new file:   public/stylesheets/app/login-register.less
modified:   public/stylesheets/app/plans.less
modified:   public/stylesheets/app/portals.less
modified:   public/stylesheets/app/templates-v2.less
modified:   public/stylesheets/app/wiki.less
modified:   public/stylesheets/components/alerts.less
modified:   public/stylesheets/components/buttons.less
modified:   public/stylesheets/components/icons.less
new file:   public/stylesheets/components/images.less
new file:   public/stylesheets/components/tabs.less
modified:   public/stylesheets/core/_common-variables.less
modified:   public/stylesheets/core/ol-variables.less
modified:   public/stylesheets/ol-style.less
modified:   test/acceptance/coffee/ExportsTests.coffee
new file:   test/acceptance/coffee/RedirectUrlsTests.coffee
modified:   test/acceptance/coffee/TokenAccessTests.coffee
modified:   test/acceptance/coffee/helpers/User.coffee
modified:   test/acceptance/config/settings.test.coffee
modified:   test/unit/coffee/Authentication/AuthenticationControllerTests.coffee
modified:   test/unit/coffee/Editor/EditorHttpControllerTests.coffee
modified:   test/unit/coffee/Exports/ExportsControllerTests.coffee
modified:   test/unit/coffee/Exports/ExportsHandlerTests.coffee
new file:   test/unit/coffee/Notifications/NotificationsBuilderTests.coffee
modified:   test/unit/coffee/Project/ProjectControllerTests.coffee
modified:   test/unit/coffee/TokenAccess/TokenAccessControllerTests.coffee
modified:   test/unit/coffee/User/UserGetterTests.coffee
deleted:    test/unit/coffee/infrastructure/RedirectManagerTests.coffee
This commit is contained in:
Douglas Lovell
2018-09-24 09:11:48 -03:00
107 changed files with 4644 additions and 354 deletions

View File

@@ -11,6 +11,7 @@ UserHandler = require("../User/UserHandler")
UserSessionsManager = require("../User/UserSessionsManager")
Analytics = require "../Analytics/AnalyticsManager"
passport = require 'passport'
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
module.exports = AuthenticationController =
@@ -43,7 +44,7 @@ module.exports = AuthenticationController =
return callback(err)
req.sessionStore.generate(req)
for key, value of oldSession
req.session[key] = value
req.session[key] = value unless key == '__tmp'
# copy to the old `session.user` location, for backward-comptability
req.session.user = req.session.passport.user
req.session.save (err) ->
@@ -112,6 +113,7 @@ module.exports = AuthenticationController =
UserHandler.setupLoginData(user, ()->)
LoginRateLimiter.recordSuccessfulLogin(user.email)
AuthenticationController._recordSuccessfulLogin(user._id)
AuthenticationController.ipMatchCheck(req, user)
Analytics.recordEvent(user._id, "user-logged-in", {ip:req.ip})
Analytics.identifyUser(user._id, req.sessionID)
logger.log email: user.email, user_id: user._id.toString(), "successful log in"
@@ -119,6 +121,13 @@ module.exports = AuthenticationController =
# capture the request ip for use when creating the session
user._login_req_ip = req.ip
ipMatchCheck: (req, user) ->
if req.ip != user.lastLoginIp
NotificationsBuilder.ipMatcherAffiliation(user._id, req.ip).create()
UserUpdater.updateUser user._id.toString(), {
$set: { "lastLoginIp": req.ip }
}
setInSessionUser: (req, props) ->
for key, value of props
if req?.session?.passport?.user?

View File

@@ -41,21 +41,19 @@ module.exports = EditorHttpController =
return callback(new Error("not found")) if !project?
CollaboratorsHandler.getInvitedMembersWithPrivilegeLevels project_id, (error, members) ->
return callback(error) if error?
UserGetter.getUser user_id, { isAdmin: true }, (error, user) ->
token = TokenAccessHandler.getRequestToken(req, project_id)
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, token, (error, privilegeLevel) ->
return callback(error) if error?
token = TokenAccessHandler.getRequestToken(req, project_id)
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, token, (error, privilegeLevel) ->
if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
logger.log {project_id, user_id, privilegeLevel}, "not an acceptable privilege level, returning null"
return callback null, null, false
CollaboratorsInviteHandler.getAllInvites project_id, (error, invites) ->
return callback(error) if error?
if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
logger.log {project_id, user_id, privilegeLevel}, "not an acceptable privilege level, returning null"
return callback null, null, false
CollaboratorsInviteHandler.getAllInvites project_id, (error, invites) ->
return callback(error) if error?
logger.log {project_id, user_id, memberCount: members.length, inviteCount: invites.length, privilegeLevel}, "returning project model view"
callback(null,
ProjectEditorHandler.buildProjectModelView(project, members, invites),
privilegeLevel
)
logger.log {project_id, user_id, memberCount: members.length, inviteCount: invites.length, privilegeLevel}, "returning project model view"
callback(null,
ProjectEditorHandler.buildProjectModelView(project, members, invites),
privilegeLevel
)
_nameIsAcceptableLength: (name)->
return name? and name.length < 150 and name.length != 0

View File

@@ -40,6 +40,23 @@ The #{settings.appName} Team - #{settings.siteUrl}
templates = {}
templates.accountMergeToOverleafAddress = CTAEmailTemplate({
subject: () -> "Confirm Account Merge - #{settings.appName}"
title: () -> "Confirm Account Merge"
message: () ->
"""
To merge your ShareLaTeX and Overleaf accounts, click the button below.
If you think you have received this message in error,
please contact us at https://www.overleaf.com/contact
"""
ctaText: () -> "Confirm Account Merge"
ctaURL: (opts) -> opts.tokenLinkUrl
secondaryMessage: (opts) ->
"If the button does not appear, open this link in your browser: #{opts.tokenLinkUrl}"
})
templates.accountMergeToSharelatexAddress = templates.accountMergeToOverleafAddress
templates.registered = CTAEmailTemplate({
subject: () -> "Activate your #{settings.appName} Account"
message: (opts) -> """

View File

@@ -31,56 +31,56 @@ UnsupportedFileTypeError = (message) ->
error.name = "UnsupportedFileTypeError"
error.__proto__ = UnsupportedFileTypeError.prototype
return error
UnsupportedFileTypeError.prototype.__proto___ = Error.prototype
UnsupportedFileTypeError.prototype.__proto__ = Error.prototype
UnsupportedBrandError = (message) ->
error = new Error(message)
error.name = "UnsupportedBrandError"
error.__proto__ = UnsupportedBrandError.prototype
return error
UnsupportedBrandError.prototype.__proto___ = Error.prototype
UnsupportedBrandError.prototype.__proto__ = Error.prototype
UnsupportedExportRecordsError = (message) ->
error = new Error(message)
error.name = "UnsupportedExportRecordsError"
error.__proto__ = UnsupportedExportRecordsError.prototype
return error
UnsupportedExportRecordsError.prototype.__proto___ = Error.prototype
UnsupportedExportRecordsError.prototype.__proto__ = Error.prototype
V1HistoryNotSyncedError = (message) ->
error = new Error(message)
error.name = "V1HistoryNotSyncedError"
error.__proto__ = V1HistoryNotSyncedError.prototype
return error
V1HistoryNotSyncedError.prototype.__proto___ = Error.prototype
V1HistoryNotSyncedError.prototype.__proto__ = Error.prototype
ProjectHistoryDisabledError = (message) ->
error = new Error(message)
error.name = "ProjectHistoryDisabledError"
error.__proto__ = ProjectHistoryDisabledError.prototype
return error
ProjectHistoryDisabledError.prototype.__proto___ = Error.prototype
ProjectHistoryDisabledError.prototype.__proto__ = Error.prototype
V1ConnectionError = (message) ->
error = new Error(message)
error.name = "V1ConnectionError"
error.__proto__ = V1ConnectionError.prototype
return error
V1ConnectionError.prototype.__proto___ = Error.prototype
V1ConnectionError.prototype.__proto__ = Error.prototype
UnconfirmedEmailError = (message) ->
error = new Error(message)
error.name = "UnconfirmedEmailError"
error.__proto__ = UnconfirmedEmailError.prototype
return error
UnconfirmedEmailError.prototype.__proto___ = Error.prototype
UnconfirmedEmailError.prototype.__proto__ = Error.prototype
EmailExistsError = (message) ->
error = new Error(message)
error.name = "EmailExistsError"
error.__proto__ = EmailExistsError.prototype
return error
EmailExistsError.prototype.__proto___ = Error.prototype
EmailExistsError.prototype.__proto__ = Error.prototype
module.exports = Errors =
NotFoundError: NotFoundError

View File

@@ -13,13 +13,19 @@ module.exports =
user_id: user_id
}
if req.body && req.body.firstName && req.body.lastName
export_params.first_name = req.body.firstName.trim()
export_params.last_name = req.body.lastName.trim()
if req.body
export_params.first_name = req.body.firstName.trim() if req.body.firstName
export_params.last_name = req.body.lastName.trim() if req.body.lastName
# additional parameters for gallery exports
export_params.title = req.body.title.trim() if req.body.title
export_params.description = req.body.description.trim() if req.body.description
export_params.author = req.body.author.trim() if req.body.author
export_params.license = req.body.license.trim() if req.body.license
export_params.show_source = req.body.show_source if req.body.show_source
ExportsHandler.exportProject export_params, (err, export_data) ->
return next(err) if err?
logger.log
return err if err?
logger.log
user_id:user_id
project_id: project_id
brand_variation_id:brand_variation_id
@@ -30,11 +36,13 @@ module.exports =
exportStatus: (req, res) ->
{export_id} = req.params
ExportsHandler.fetchExport export_id, (err, export_json) ->
return next(err) if err?
return err if err?
parsed_export = JSON.parse(export_json)
json = {
status_summary: parsed_export.status_summary,
status_detail: parsed_export.status_detail
status_detail: parsed_export.status_detail,
partner_submission_id: parsed_export.partner_submission_id,
token: parsed_export.token
}
res.send export_json: json

View File

@@ -20,9 +20,7 @@ module.exports = ExportsHandler = self =
callback null, export_data
_buildExport: (export_params, callback=(err, export_data) ->) ->
project_id = export_params.project_id
user_id = export_params.user_id
brand_variation_id = export_params.brand_variation_id
{project_id, user_id, brand_variation_id, title, description, author, license, show_source} = export_params
jobs =
project: (cb) ->
ProjectGetter.getProject project_id, cb
@@ -60,6 +58,11 @@ module.exports = ExportsHandler = self =
metadata:
compiler: project.compiler
imageName: project.imageName
title: title
description: description
author: author
license: license
show_source: show_source
user:
id: user_id
firstName: user.first_name

View File

@@ -1,5 +1,7 @@
logger = require("logger-sharelatex")
NotificationsHandler = require("./NotificationsHandler")
request = require "request"
settings = require "settings-sharelatex"
module.exports =
@@ -29,3 +31,29 @@ module.exports =
NotificationsHandler.createNotification user._id, @key, "notification_project_invite", messageOpts, invite.expires, callback
read: (callback=()->) ->
NotificationsHandler.markAsReadByKeyOnly @key, callback
ipMatcherAffiliation: (userId, ip) ->
key: "ip-matched-affiliation-#{ip}"
create: (callback=()->) ->
return null unless settings?.apis?.v1?.url # service is not configured
_key = @key
request {
method: 'GET'
url: "#{settings.apis.v1.url}/api/v2/users/#{userId}/ip_matcher"
auth: { user: settings.apis.v1.user, pass: settings.apis.v1.pass }
body: { ip: ip }
json: true
timeout: 20 * 1000
}, (error, response, body) ->
return error if error?
return null unless response.statusCode == 200
messageOpts =
university_id: body.id
university_name: body.name
content: body.enrolment_ad_html
logger.log user_id:userId, key:_key, "creating notification key for user"
NotificationsHandler.createNotification userId, _key, "notification_ip_matched_affiliation", messageOpts, null, false, callback
read: (callback = ->)->
NotificationsHandler.markAsReadWithKey userId, @key, callback

View File

@@ -29,12 +29,15 @@ module.exports =
unreadNotifications = []
callback(null, unreadNotifications)
createNotification: (user_id, key, templateKey, messageOpts, expiryDateTime, callback)->
createNotification: (user_id, key, templateKey, messageOpts, expiryDateTime, forceCreate, callback)->
if !callback
callback = forceCreate
forceCreate = true
payload = {
key:key
messageOpts:messageOpts
templateKey:templateKey
forceCreate: true
forceCreate:forceCreate
}
if expiryDateTime?
payload.expires = expiryDateTime

View File

@@ -26,6 +26,8 @@ TokenAccessHandler = require '../TokenAccess/TokenAccessHandler'
CollaboratorsHandler = require '../Collaborators/CollaboratorsHandler'
Modules = require '../../infrastructure/Modules'
ProjectEntityHandler = require './ProjectEntityHandler'
UserGetter = require("../User/UserGetter")
NotificationsBuilder = require("../Notifications/NotificationsBuilder")
crypto = require 'crypto'
{ V1ConnectionError } = require '../Errors/Errors'
Features = require('../../infrastructure/Features')
@@ -209,6 +211,11 @@ module.exports = ProjectController =
user = results.user
warnings = ProjectController._buildWarningsList results.v1Projects
# in v2 add notifications for matching university IPs
if Settings.overleaf?
UserGetter.getUser user_id, { 'lastLoginIp': 1 }, (error, user) ->
if req.ip != user.lastLoginIp
NotificationsBuilder.ipMatcherAffiliation(user._id, req.ip).create()
ProjectController._injectProjectOwners projects, (error, projects) ->
return next(error) if error?

View File

@@ -9,7 +9,8 @@ fs = require "fs"
ErrorController = require "../Errors/ErrorController"
AuthenticationController = require('../Authentication/AuthenticationController')
homepageExists = fs.existsSync Path.resolve(__dirname + "/../../../views/external/home.pug")
slHomepageExists = fs.existsSync Path.resolve(__dirname + "/../../../views/external/home/sl.pug")
v2HomepageExists = fs.existsSync Path.resolve(__dirname + "/../../../views/external/home/v2.pug")
module.exports = HomeController =
index : (req,res)->
@@ -21,11 +22,13 @@ module.exports = HomeController =
else
HomeController.home(req, res)
home: (req, res)->
if Features.hasFeature('homepage') and homepageExists
res.render 'external/home'
home: (req, res, next)->
if Features.hasFeature('homepage') and !Settings.overleaf and slHomepageExists
res.render 'external/home/sl'
else if Features.hasFeature('homepage') and Settings.overleaf and v2HomepageExists
res.render 'external/home/v2'
else
res.redirect "/login"
res.redirect '/login'
externalPage: (page, title) ->
return (req, res, next = (error) ->) ->

View File

@@ -33,7 +33,7 @@ module.exports =
student: true
}
{
feature: 'hundreds_templates'
feature: 'thousands_templates'
value: 'bool'
info: 'hundreds_templates_info'
plans: {

View File

@@ -3,6 +3,7 @@ AuthenticationController = require '../Authentication/AuthenticationController'
TokenAccessHandler = require './TokenAccessHandler'
Errors = require '../Errors/Errors'
logger = require 'logger-sharelatex'
settings = require 'settings-sharelatex'
module.exports = TokenAccessController =
@@ -11,11 +12,16 @@ module.exports = TokenAccessController =
return ProjectController.loadEditor(req, res, next)
_tryHigherAccess: (token, userId, req, res, next) ->
TokenAccessHandler.findProjectWithHigherAccess token, userId, (err, project) ->
TokenAccessHandler.findProjectWithHigherAccess token, userId, (err, project, projectExists) ->
if err?
logger.err {err, token, userId},
"[TokenAccess] error finding project with higher access"
return next(err)
if !projectExists and settings.overleaf
logger.log {token, userId},
"[TokenAccess] no project found for this token"
# Project does not exist, but may be unimported - try it on v1
return res.redirect(settings.overleaf.host + req.url)
if !project?
logger.log {token, userId},
"[TokenAccess] no project with higher access found for this user and token"

View File

@@ -22,7 +22,7 @@ module.exports = TokenAccessHandler =
'publicAccesLevel': PublicAccessLevels.TOKEN_BASED
}, {_id: 1, publicAccesLevel: 1, owner_ref: 1}, callback
findProjectWithHigherAccess: (token, userId, callback=(err, project)->) ->
findProjectWithHigherAccess: (token, userId, callback=(err, project, projectExists)->) ->
Project.findOne {
$or: [
{'tokens.readAndWrite': token},
@@ -32,12 +32,16 @@ module.exports = TokenAccessHandler =
if err?
return callback(err)
if !project?
return callback(null, null)
return callback(null, null, false) # Project doesn't exist, so we handle differently
projectId = project._id
CollaboratorsHandler.isUserInvitedMemberOfProject userId, projectId, (err, isMember) ->
if err?
return callback(err)
callback(null, if isMember == true then project else null)
callback(
null,
if isMember == true then project else null,
true # Project does exist, but user doesn't have access
)
addReadOnlyUserToProject: (userId, projectId, callback=(err)->) ->
userId = ObjectId(userId.toString())

View File

@@ -8,6 +8,7 @@ Errors = require("../Errors/Errors")
module.exports = UserGetter =
getUser: (query, projection, callback = (error, user) ->) ->
return callback(new Error("no query provided")) unless query?
if query?.email?
return callback(new Error("Don't use getUser to find user by email"), null)
if arguments.length == 2
@@ -34,7 +35,7 @@ module.exports = UserGetter =
getUserAffiliations userId, (error, affiliationsData) ->
return callback error if error?
callback null, decorateFullEmails(user.email, user.emails, affiliationsData)
callback null, decorateFullEmails(user.email, user.emails or [], affiliationsData)
getUserByMainEmail: (email, projection, callback = (error, user) ->) ->
email = email.trim()

View File

@@ -1,20 +1,33 @@
settings = require("settings-sharelatex")
logger = require("logger-sharelatex")
module.exports = (req, res, next)->
requestedUrl = req.url
module.exports = RedirectManager =
apply: (webRouter) ->
for redirectUrl, target of settings.redirects
for method in (target.methods or ['get'])
webRouter[method] redirectUrl, RedirectManager.createRedirect(target)
redirectUrl = settings.redirects[requestedUrl]
#remove starting slash
if !redirectUrl? and requestedUrl[requestedUrl.length-1] == "/"
requestedUrl = requestedUrl.substring(0, requestedUrl.length - 1)
redirectUrl = settings.redirects[requestedUrl]
if redirectUrl?
logger.log redirectUrl:redirectUrl, reqUrl:req.url, "redirecting to new path"
res.redirect 301, "#{redirectUrl}"
else
next()
createRedirect: (target) ->
(req, res, next) ->
code = 302
if typeof target is 'string'
url = target
else
if req.method == "POST"
code = 307
if typeof target.url == "function"
url = target.url(req.params)
if !url
return next()
else
url = target.url
if target.baseUrl?
url = "#{target.baseUrl}#{url}"
res.redirect code, url + getQueryString(req)
# Naively get the query params string. Stringifying the req.query object may
# have differences between Express and Rails, so safer to just pass the raw
# string
getQueryString = (req) ->
qs = req.url.match(/\?.*$/)
if qs? then qs[0] else ""

View File

@@ -73,7 +73,7 @@ app.use multer(dest: Settings.path.uploadFolder)
app.use methodOverride()
app.use metrics.http.monitor(logger)
app.use RedirectManager
RedirectManager.apply(webRouter)
ProxyManager.apply(publicApiRouter)

View File

@@ -22,6 +22,7 @@ UserSchema = new Schema
confirmed : {type : Boolean, default : false}
signUpDate : {type : Date, default: () -> new Date() }
lastLoggedIn : {type : Date}
lastLoginIp : {type : String, default : ''}
loginCount : {type : Number, default: 0}
holdingAccount : {type : Boolean, default: false}
ace : {

View File

@@ -68,6 +68,7 @@ module.exports = class Router
webRouter.get '/logout', UserController.logout
webRouter.get '/restricted', AuthorizationMiddlewear.restricted
if Features.hasFeature('registration')
webRouter.get '/register', UserPagesController.registerPage
AuthenticationController.addEndpointToLoginWhitelist '/register'
@@ -329,6 +330,21 @@ module.exports = class Router
AuthenticationController.httpAuth,
CompileController.getFileFromClsiWithoutUser
webRouter.get '/teams', (req, res, next) ->
# Match v1 behaviour - if the user is signed in, show their teams list
# Otherwise show some information about teams
if AuthenticationController.isUserLoggedIn(req)
res.redirect('/user/subscription')
else
res.redirect("#{settings.v1Api.host}/teams")
webRouter.get '/chrome', (req, res, next) ->
# Match v1 behaviour - this is used for a Chrome web app
if AuthenticationController.isUserLoggedIn(req)
res.redirect('/project')
else
res.redirect('/register')
#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

@@ -18,8 +18,7 @@ mixin linkAdvisors(linkText, linkClass, track)
| #{linkText ? linkText : 'advisor programme'}
mixin linkBenefits(linkText, linkClass)
//- To Do: verify path
a(href="/benefits" class=linkClass ? linkClass : '')
a(href="/for/authors" class=linkClass ? linkClass : '')
| #{linkText ? linkText : 'benefits'}
mixin linkBlog(linkText, linkClass, slug)
@@ -36,8 +35,7 @@ mixin linkDash(linkText, linkClass)
| #{linkText ? linkText : 'project dashboard'}
mixin linkEducation(linkText, linkClass)
//- To Do: verify path
a(href="/plans" class=linkClass ? linkClass : '')
a(href="/for/edu" class=linkClass ? linkClass : '')
| #{linkText ? linkText : 'teaching toolkit'}
mixin linkEmail(linkText, linkClass, email)
@@ -66,8 +64,7 @@ mixin linkInvite(linkText, linkClass, track)
| #{linkText ? linkText : 'invite your friends'}
mixin linkPlansAndPricing(linkText, linkClass)
//- To Do: verify path
a(href="/plans" class=linkClass ? linkClass : '')
a(href="/user/subscription/plans" class=linkClass ? linkClass : '')
| #{linkText ? linkText : 'plans and pricing'}
mixin linkPrintNewTab(linkText, linkClass, icon, track)
@@ -121,6 +118,5 @@ mixin linkTweet(linkText, linkClass, tweetText, track)
) #{linkText ? linkText : 'tweet'}
mixin linkUniversities(linkText, linkClass)
//- To Do: verify path
a(href="/universities" class=linkClass ? linkClass : '')
a(href="/for/universities" class=linkClass ? linkClass : '')
| #{linkText ? linkText : 'universities'}

View File

@@ -2,7 +2,7 @@ doctype html
html.full-height(itemscope, itemtype='http://schema.org/Product')
head
title Something went wrong
link(rel="icon", href="/favicon.ico")
link(rel="icon", href="/" + settings.brandPrefix + "favicon.ico")
if buildCssPath
link(rel="stylesheet", href=buildCssPath("/" + settings.brandPrefix + "style.css"))
link(href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css",rel="stylesheet")

View File

@@ -27,8 +27,18 @@ nav.navbar.navbar-default.navbar-main
// loop over header_extras
each item in nav.header_extras
-
if ((item.only_when_logged_in && getSessionUser())
|| (item.only_when_logged_out && (!getSessionUser()))
|| (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages)
|| (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks))
){
var showNavItem = true
} else {
var showNavItem = false
}
if ((item.only_when_logged_in && getSessionUser()) || (item.only_when_logged_out && (!getSessionUser())) || (!item.only_when_logged_out && !item.only_when_logged_in))
if showNavItem
if item.dropdown
li.dropdown(class=item.class, dropdown)
a.dropdown-toggle(href, dropdown-toggle)

View File

@@ -103,12 +103,8 @@ header.toolbar.toolbar-header.toolbar-with-labels(
a.btn.btn-full-height(
href
ng-class="{ 'btn-full-height-disabled' : !permissions.admin }"
ng-click="openShareProjectModal(permissions.admin);"
ng-controller="ShareController"
tooltip-enable="!permissions.admin"
tooltip="Only the project owner can use the Share menu at the moment, but we're working on making it accessible to collaborators, too."
tooltip-placement="bottom"
)
i.fa.fa-fw.fa-group
p.toolbar-label #{translate("share")}

View File

@@ -8,9 +8,8 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
h3 #{translate("share_project")}
.modal-body.modal-body-share
.container-fluid
//- Private (with token-access available)
.row.public-access-level(ng-show="project.publicAccesLevel == 'private'")
.row.public-access-level(ng-show="isAdmin && project.publicAccesLevel == 'private'")
.col-xs-12.text-center
| #{translate('link_sharing_is_off')}
| &nbsp;&nbsp;
@@ -28,7 +27,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
)
//- Token-based access
.row.public-access-level(ng-show="project.publicAccesLevel == 'tokenBased'")
.row.public-access-level(ng-show="isAdmin && project.publicAccesLevel == 'tokenBased'")
.col-xs-12.text-center
strong
| #{translate('link_sharing_is_on')}.
@@ -57,7 +56,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
pre.access-token(ng-hide="readOnlyTokenLink") #{translate('loading')}...
//- legacy public-access
.row.public-access-level(ng-show="project.publicAccesLevel == 'readAndWrite' || project.publicAccesLevel == 'readOnly'")
.row.public-access-level(ng-show="isAdmin && (project.publicAccesLevel == 'readAndWrite' || project.publicAccesLevel == 'readOnly')")
.col-xs-12.text-center
strong(ng-if="project.publicAccesLevel == 'readAndWrite'") #{translate("this_project_is_public")}
strong(ng-if="project.publicAccesLevel == 'readOnly'") #{translate("this_project_is_public_read_only")}
@@ -77,7 +76,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
.col-xs-3.text-left
span(ng-show="member.privileges == 'readAndWrite'") #{translate("can_edit")}
span(ng-show="member.privileges == 'readOnly'") #{translate("read_only")}
.col-xs-1
.col-xs-1(ng-show="isAdmin")
a(
href
tooltip=translate('remove_collaborator')
@@ -102,7 +101,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
ng-click="revokeInvite(invite)"
)
i.fa.fa-times
.row.invite-controls
.row.invite-controls(ng-show="isAdmin")
form(ng-show="canAddCollaborators")
.small #{translate("share_with_your_collabs")}
.form-group
@@ -177,8 +176,9 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
) #{translate("start_free_trial")}
p.small(ng-show="startedFreeTrial")
| #{translate("refresh_page_after_starting_free_trial")}.
| #{translate("refresh_page_after_starting_free_trial")}
.row.public-access-level.public-access-level--notice(ng-show="!isAdmin")
.col-xs-12.text-center #{translate("to_add_more_collaborators")}
.modal-footer.modal-footer-share
.modal-footer-left
i.fa.fa-refresh.fa-spin(ng-show="state.inflight")

View File

@@ -1,5 +1,8 @@
extends ../layout
block vars
- var suppressNavContentLinks = true
block content
//- We need to do .replace(/\//g, '\\/') do that '</script>' -> '<\/script>'
//- and doesn't prematurely end the script tag.
@@ -98,7 +101,7 @@ block content
| To tag or rename your v1 projects, please go back to Overleaf v1.
div(ng-show="visible")
a.project-list-sidebar-v1-link(
href=settings.overleaf.host + "/dash?prefer-v1-dash=1"
href='/sign_in_to_v1?return_to=/dash%3Fprefer-v1-dash%3D1'
) Go back to v1
if userIsFromSL(user)
div(ng-show="visible")

View File

@@ -356,7 +356,7 @@ script(type="text/ng-template", id="v1ImportModalTemplate")
form-messages(for="v1ImportForm")
if settings.overleaf && settings.overleaf.host
a.btn.btn-primary.v1-import-btn(
ng-href=settings.overleaf.host + "/{{project.id}}"
ng-href='/sign_in_to_v1?return_to=/{{project.id}}'
ng-class="{disabled: v1ImportForm.inflight || v1ImportForm.response.success}"
) No thanks, open in v1
input.btn.btn-primary.v1-import-btn(

View File

@@ -60,6 +60,21 @@ span(ng-controller="NotificationsController").userNotifications
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-info(ng-switch-when="notification_ip_matched_affiliation")
div.notification_inner
.notification_body
| It looks like you're at
strong {{ notification.messageOpts.university_name }}! <br/>
| Did you know that {{notification.messageOpts.university_name}} is providing
strong free Overleaf Professional accounts
| to everyone at {{notification.messageOpts.university_name}}? <br/>
| Add an institutional email address to claim your account.
a.pull-right.btn.btn-sm.btn-info(href="/user/settings")
| Add Affiliation
span().notification_close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-info(ng-switch-default)
div.notification_inner
span(ng-bind-html="notification.html").notification_body

View File

@@ -115,19 +115,14 @@
span(ng-controller="LeftHandMenuPromoController", ng-cloak)
.row-spaced#userProfileInformation(ng-if="hasProjects")
div(ng-controller="UserProfileController")
hr(ng-show="percentComplete < 100")
.text-centered.user-profile(ng-show="percentComplete < 100")
.progress
.progress-bar.progress-bar-info(ng-style="{'width' : (percentComplete+'%')}")
p.small #{translate("profile_complete_percentage", {percentval:"{{percentComplete}}"})}
div(ng-show="userEmails.length > 0 && userAffiliations.length == 0", ng-cloak)
hr
.text-centered.user-profile
p Are you affiliated with an institution?
button#completeUserProfileInformation.btn.btn-info(
ng-hide="formVisable",
ng-click="openUserProfileModal()"
) #{translate("complete")}
a.btn.btn-info(
href="/user/settings"
) Add Affiliation
.row-spaced(ng-if="hasProjects && userHasNoSubscription", ng-cloak).text-centered
hr

View File

@@ -13,7 +13,7 @@
ng-show="project.accessLevel == 'owner'"
) {{project.name}}
a.projectName(
href=settings.overleaf.host + "/{{project.id}}"
href='/sign_in_to_v1?return_to=/{{project.id}}'
target="_blank"
ng-hide="project.accessLevel == 'owner'"
) {{project.name}}

View File

@@ -2,7 +2,7 @@ script(type="text/ng-template", id="groupPlanModalTemplate")
.modal-header
h3 #{translate("group_plan_enquiry")}
.modal-body
form.text-left.form(ng-controller="UniverstiesContactController", ng-submit="contactUs()")
form.text-left.form(ng-controller="GroupPlanContactController", ng-submit="contactUs()")
span(ng-show="sent == false && error == false")
.form-group
label#title9(for='Field9')

View File

@@ -122,7 +122,7 @@ block content
p
| You are subscribed to Overleaf through Overleaf v1
p
a.btn.btn-primary(href=settings.overleaf.host+"/users/edit#status") Manage v1 Subscription
a.btn.btn-primary(href='/sign_in_to_v1?return_to=/users/edit%23status') Manage v1 Subscription
hr
if settings.overleaf && v1Subscriptions && v1Subscriptions.teams && v1Subscriptions.teams.length > 0
@@ -130,7 +130,7 @@ block content
p
| You are a member of the Overleaf v1 team: <strong>#{team.name}</strong>
p
a.btn.btn-primary(href=settings.overleaf.host+"/teams") Manage v1 Team Membership
a.btn.btn-primary(href="/sign_in_to_v1?return_to=/teams") Manage v1 Team Membership
hr
.card(ng-if="view == 'cancelation'")

View File

@@ -197,7 +197,7 @@ block content
if settings.createV1AccountOnLogin && settings.overleaf
p
strong
| This will also delete your user account on #[a(href=settings.overleaf.host target="_blank") Overleaf v1].
| This will also delete your user account on #[a(href='/sign_in_to_v1?return_to=/dash%3Fprefer-v1-dash%3D1' target="_blank") Overleaf v1].
| If you want to remove your projects from Overleaf v1, you must do this before you
| delete your account by going to your My Projects page in Overleaf v1, moving your
| projects to the Trash, and then from there either leaving or purging them, as appropriate.
@@ -248,7 +248,7 @@ block content
div.alert.alert-info
| If you can't remember your password, or if you are using Single-Sign-On with another provider
| to sign in (such as Twitter or Google), please
| #[a(href=settings.overleaf.host+'/users/password/new', target='_blank') reset your password],
| #[a(href='/sign_in_to_v1?return_to=/users/password/new', target='_blank') reset your password],
| and try again.
.modal-footer
button.btn.btn-default(

View File

@@ -491,7 +491,7 @@ module.exports = settings =
modules:
sanitize:
options:
allowedTags: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'iframe', 'img', 'figure', 'figcaption' ]
allowedTags: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'iframe', 'img', 'figure', 'figcaption', 'source', 'video' ]
allowedAttributes:
'a': [ 'href', 'name', 'target', 'class' ]
'div': [ 'class', 'id', 'style' ]
@@ -504,4 +504,6 @@ module.exports = settings =
'figure': [ 'class', 'id', 'style']
'figcaption': [ 'class', 'id', 'style']
'iframe': [ 'allowfullscreen', 'frameborder', 'height', 'src', 'width' ]
'img': [ 'alt', 'class', 'src', 'style' ]
'img': [ 'alt', 'class', 'src', 'style' ]
'source': [ 'src', 'type' ]
'video': [ 'alt', 'class', 'controls', 'height', 'width' ]

View File

@@ -1545,6 +1545,11 @@
}
}
},
"chownr": {
"version": "1.1.1",
"from": "chownr@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz"
},
"ci-info": {
"version": "1.1.3",
"from": "ci-info@>=1.0.0 <2.0.0",
@@ -3482,6 +3487,12 @@
"resolved": "https://registry.npmjs.org/flatiron/-/flatiron-0.4.3.tgz",
"dev": true,
"dependencies": {
"minimist": {
"version": "0.0.10",
"from": "minimist@>=0.0.1 <0.1.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"dev": true
},
"optimist": {
"version": "0.6.0",
"from": "optimist@0.6.0",
@@ -3603,6 +3614,11 @@
"from": "fs-extra@>=4.0.2 <5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz"
},
"fs-minipass": {
"version": "1.2.5",
"from": "fs-minipass@>=1.2.5 <2.0.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz"
},
"fs.realpath": {
"version": "1.0.0",
"from": "fs.realpath@>=1.0.0 <2.0.0",
@@ -6696,6 +6712,38 @@
"from": "lynx@0.1.1",
"resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz"
},
"mailchimp-api-v3": {
"version": "1.12.0",
"from": "mailchimp-api-v3@>=1.12.0 <2.0.0",
"resolved": "https://registry.npmjs.org/mailchimp-api-v3/-/mailchimp-api-v3-1.12.0.tgz",
"dependencies": {
"bluebird": {
"version": "3.5.2",
"from": "bluebird@>=3.4.0 <4.0.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz"
},
"lodash": {
"version": "4.17.11",
"from": "lodash@>=4.17.10 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz"
},
"safe-buffer": {
"version": "5.1.2",
"from": "safe-buffer@>=5.1.2 <6.0.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
},
"tar": {
"version": "4.4.6",
"from": "tar@>=4.0.2 <5.0.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz"
},
"yallist": {
"version": "3.0.2",
"from": "yallist@>=3.0.2 <4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz"
}
}
},
"mailcomposer": {
"version": "3.3.2",
"from": "mailcomposer@3.3.2",
@@ -6981,9 +7029,31 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz"
},
"minimist": {
"version": "0.0.8",
"from": "minimist@0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
"version": "1.2.0",
"from": "minimist@1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz"
},
"minipass": {
"version": "2.3.4",
"from": "minipass@>=2.3.3 <3.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz",
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"from": "safe-buffer@^5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
},
"yallist": {
"version": "3.0.2",
"from": "yallist@^3.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz"
}
}
},
"minizlib": {
"version": "1.1.0",
"from": "minizlib@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz"
},
"mixin-deep": {
"version": "1.3.1",
@@ -7002,7 +7072,14 @@
"mkdirp": {
"version": "0.5.1",
"from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"dependencies": {
"minimist": {
"version": "0.0.8",
"from": "minimist@0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
}
}
},
"mocha": {
"version": "5.0.1",
@@ -7312,6 +7389,12 @@
"resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz",
"dev": true
},
"minimist": {
"version": "0.0.10",
"from": "minimist@>=0.0.1 <0.1.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"dev": true
},
"optimist": {
"version": "0.6.0",
"from": "optimist@0.6.0",
@@ -8145,7 +8228,14 @@
"optimist": {
"version": "0.6.1",
"from": "optimist@0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz"
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"dependencies": {
"minimist": {
"version": "0.0.10",
"from": "minimist@>=0.0.1 <0.1.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz"
}
}
},
"optionator": {
"version": "0.8.2",
@@ -8318,6 +8408,11 @@
"from": "passport@>=0.3.2 <0.4.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.3.2.tgz"
},
"passport-google-oauth20": {
"version": "1.0.0",
"from": "passport-google-oauth20@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-1.0.0.tgz"
},
"passport-ldapauth": {
"version": "0.6.0",
"from": "passport-ldapauth@>=0.6.0 <0.7.0",
@@ -8328,6 +8423,11 @@
"from": "passport-local@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz"
},
"passport-oauth1": {
"version": "1.1.0",
"from": "passport-oauth1@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz"
},
"passport-oauth2": {
"version": "1.4.0",
"from": "passport-oauth2@>=1.4.0 <2.0.0",
@@ -8338,6 +8438,11 @@
"from": "passport-oauth2-refresh@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/passport-oauth2-refresh/-/passport-oauth2-refresh-1.0.0.tgz"
},
"passport-orcid": {
"version": "0.0.3",
"from": "https://registry.npmjs.org/passport-orcid/-/passport-orcid-0.0.3.tgz",
"resolved": "https://registry.npmjs.org/passport-orcid/-/passport-orcid-0.0.3.tgz"
},
"passport-saml": {
"version": "0.15.0",
"from": "passport-saml@>=0.15.0 <0.16.0",
@@ -8372,6 +8477,11 @@
"from": "passport-strategy@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz"
},
"passport-twitter": {
"version": "1.0.4",
"from": "https://registry.npmjs.org/passport-twitter/-/passport-twitter-1.0.4.tgz",
"resolved": "https://registry.npmjs.org/passport-twitter/-/passport-twitter-1.0.4.tgz"
},
"path-browserify": {
"version": "0.0.0",
"from": "path-browserify@0.0.0",
@@ -12502,6 +12612,11 @@
"from": "xtend@>=4.0.1 <5.0.0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
},
"xtraverse": {
"version": "0.1.0",
"from": "xtraverse@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/xtraverse/-/xtraverse-0.1.0.tgz"
},
"y18n": {
"version": "3.2.1",
"from": "y18n@>=3.2.1 <4.0.0",

View File

@@ -63,6 +63,7 @@
"marked": "^0.3.5",
"method-override": "^2.3.3",
"metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1",
"minimist": "1.2.0",
"mocha": "^5.0.1",
"mongojs": "2.4.0",
"mongoose": "4.11.4",
@@ -75,11 +76,14 @@
"nvd3": "^1.8.6",
"optimist": "0.6.1",
"passport": "^0.3.2",
"passport-google-oauth20": "^1.0.0",
"passport-ldapauth": "^0.6.0",
"passport-local": "^1.0.0",
"passport-oauth2": "^1.4.0",
"passport-oauth2-refresh": "^1.0.0",
"passport-orcid": "0.0.3",
"passport-saml": "^0.15.0",
"passport-twitter": "^1.0.4",
"pug": "^2.0.0-beta6",
"react": "^15.4.2",
"react-dom": "^15.4.2",
@@ -99,8 +103,7 @@
"v8-profiler": "^5.2.3",
"valid-url": "^1.0.9",
"xml2js": "0.2.0",
"yauzl": "^2.8.0",
"minimist": "1.2.0"
"yauzl": "^2.8.0"
},
"devDependencies": {
"autoprefixer": "^6.6.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,15 @@
<html>
<body>
<h1>Instructions</h1>
<h2>What is this file?</h2>
This file is used by Atlassian Cloud to verify ownership of your domain name.
<br>
Generated at 2017-02-02 11:42:45 UTC.
<h1>Domain Verification Token</h1>
This token must be found in the page at: <code>https://example.com/atlassian-domain-verification.html</code>
<hr>
<code id="domain-verification-token">6YyZjsahYa6Y5nyQDaNakzRPBJ6wVMQwN+2CDx-2wQtG/97OvOjc3gh4NCQ6Sgkv</code>
</body>
</html>

View File

@@ -516,6 +516,7 @@ define [
scope.$on '$destroy', () ->
if scope.sharejsDoc?
scope.$broadcast('changeEditor')
tearDownSpellCheck()
tearDownCursorPosition()
detachFromAce(scope.sharejsDoc)

View File

@@ -3,12 +3,12 @@ define [], () ->
constructor: (@$scope, @adapter, @localStorage) ->
@$scope.$on 'editorInit', @jumpToPositionInNewDoc
@$scope.$on 'beforeChangeDocument', () =>
@storeCursorPosition()
@storeFirstVisibleLine()
@$scope.$on 'beforeChangeDocument', @storePositionAndLine
@$scope.$on 'afterChangeDocument', @jumpToPositionInNewDoc
@$scope.$on 'changeEditor', @storePositionAndLine
@$scope.$on "#{@$scope.name}:gotoLine", (e, line, column) =>
if line?
setTimeout () =>
@@ -24,6 +24,10 @@ define [], () ->
@$scope.$on "#{@$scope.name}:clearSelection", (e) =>
@adapter.clearSelection()
storePositionAndLine: () =>
@storeCursorPosition()
@storeFirstVisibleLine()
jumpToPositionInNewDoc: () =>
@doc_id = @$scope.sharejsDoc?.doc_id
setTimeout () =>

View File

@@ -4,9 +4,7 @@ define [
App.controller "ShareController", ["$scope", "$modal", "ide", "projectInvites", "projectMembers", "event_tracking",
($scope, $modal, ide, projectInvites, projectMembers, event_tracking) ->
$scope.openShareProjectModal = (isAdmin) ->
if !isAdmin
return
$scope.isAdmin = isAdmin;
event_tracking.sendMBOnce "ide-open-share-modal-once"
$modal.open(

View File

@@ -24,7 +24,6 @@ define [
"main/affiliations/controllers/UserAffiliationsController"
"main/affiliations/factories/UserAffiliationsDataService"
"main/keys"
"main/account-merge-checker"
"main/cms/index"
"analytics/AbTestingManager"
"directives/asyncForm"

View File

@@ -1,5 +0,0 @@
define [
"base"
], (App) ->
App.controller "AccountMergeCheckerController", ($scope) ->
$scope.hasOlAccount = null

View File

@@ -2,7 +2,7 @@ define [
"base"
"libs/platform"
], (App, platform) ->
App.controller 'UniverstiesContactController', ($scope, $modal, $http) ->
App.controller 'GroupPlanContactController', ($scope, $modal, $http) ->
$scope.form = {}
$scope.sent = false
@@ -20,7 +20,7 @@ define [
email: $scope.form.email
labels: "#{$scope.form.source} accounts"
message: "Please contact me with more details"
subject: "#{$scope.form.name} - General Enquiry - #{$scope.form.position} - #{$scope.form.university}"
subject: "#{$scope.form.name} - Group Enquiry - #{$scope.form.position} - #{$scope.form.university}"
inbox: "accounts"
request = $http.post "/support", data

View File

@@ -2,8 +2,18 @@ define [
"base"
], (App) ->
App.controller 'LeftHandMenuPromoController', ($scope) ->
App.controller 'LeftHandMenuPromoController', ($scope, UserAffiliationsDataService) ->
$scope.hasProjects = window.data.projects.length > 0
$scope.userHasNoSubscription = window.userHasNoSubscription
_userHasNoAffiliation = () ->
$scope.userEmails = []
$scope.userAffiliations = []
UserAffiliationsDataService.getUserEmails().then (emails) ->
$scope.userEmails = emails
for email in emails
if email.affiliation
$scope.userAffiliations.push email.affiliation
_userHasNoAffiliation()

View File

@@ -0,0 +1 @@
google-site-verification: google4f15e48c48709a75.html

View File

@@ -0,0 +1 @@
google-site-verification: googleef256f97939bd9b7.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 510.2"><title>Shape-Copy</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path id="Shape-Copy" d="M254.85,303.57v-94.9H493.62C497.19,224.74,500,239.8,500,261c0,145.66-97.7,249.23-244.9,249.23C114.29,510.2,0,395.92,0,255.1S114.29,0,255.1,0C324,0,381.63,25.26,425.77,66.58L353.32,137c-18.37-17.35-50.51-37.76-98.21-37.76-84.44,0-153.32,70.15-153.32,156.12S170.66,411.48,255.1,411.48c97.7,0,133.67-67.6,140.31-107.65H254.85Z" fill="#fff"/></g></g></svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 510.2"><title>Shape-Copy</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path id="Shape-Copy" d="M254.85,303.57v-94.9H493.62C497.19,224.74,500,239.8,500,261c0,145.66-97.7,249.23-244.9,249.23C114.29,510.2,0,395.92,0,255.1S114.29,0,255.1,0C324,0,381.63,25.26,425.77,66.58L353.32,137c-18.37-17.35-50.51-37.76-98.21-37.76-84.44,0-153.32,70.15-153.32,156.12S170.66,411.48,255.1,411.48c97.7,0,133.67-67.6,140.31-107.65H254.85Z" fill="#4885ED"/></g></g></svg>

After

Width:  |  Height:  |  Size: 543 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M490.59,233.83C472.22,217,415.33,179.95,367.7,132.31,320.44,85,282.75,27.78,266.17,9.41c-12.55-12.55-19.79-12.55-32.35,0C217.23,27.78,179.56,85,132.3,132.31,84.67,179.95,27.78,218.5,9.41,233.83c-12.55,12.55-12.55,19.79,0,32.35C27.78,282.53,85,320.44,132.31,367.7c47.64,47.64,84.94,104.52,101.52,122.89,12.55,12.55,19.79,12.55,32.35,0,16.36-18.37,53.88-75.25,101.52-122.89,47.26-47.26,104.9-86.41,122.89-101.52C503.14,253.62,503.14,246.38,490.59,233.83Zm-56.89,29.12c-5.49,7.32-40.07,53.66-80.71,94.12l0,0C312.49,397.74,271.34,428.48,264,434l-.13.08c-7.59,6.29-20.19,6-27.94,0-7.32-5.49-50.39-38.15-90.85-78.79l0,0c-40.65-40.46-73.31-83.53-78.79-90.85h0c-6.29-7.59-6-20.19,0-27.93,5.49-7.32,38.15-50.39,78.79-90.85l0,0c40.46-40.65,83.53-73.31,90.85-78.79,7.74-6,20.35-6.29,27.93,0h0c7.32,5.49,50.39,38.15,90.85,78.79l0,0c40.65,40.46,73.39,84.8,78.88,92.12v0C440,245.32,439.71,255.21,433.69,262.95Z" fill="#fff"/><path d="M420.46,238.64c-5.09-6.79-35.48-47.94-73.2-85.49l0,0c-37.55-37.72-77.52-68-84.31-73.12h0l-.14-.13c-7-5.84-18.6-5.45-25.79.14-6.79,5.09-46.76,35.4-84.31,73.12l0,0c-37.72,37.55-68,77.52-73.12,84.31-5.59,7.18-5.84,18.88,0,25.92h0c5.09,6.79,35.4,46.76,73.12,84.31l0,0c37.55,37.72,77.52,68,84.31,73.12,7.18,5.59,18.88,5.84,25.92,0l.12-.07a787.78,787.78,0,0,0,82.53-71.34l0,0c37.72-37.55,69.81-80.56,74.9-87.35,5.59-7.18,5.84-16.36,0-23.4ZM249.6,118l27.6,79.64H258.79l3.43,75.83c-9.18,1.59-15.89,1.27-24.9-.14l3.27-75.69h-18.8Zm-16,253.83,3.37-77.95c5.31.89,21.4.77,26.72-.06l3.54,78C258.34,374.22,242,373.81,233.55,371.86Zm11.64-82.5c-57.38,0-103.78-18.47-103.89-41.25-.08-15.63,21.78-29.23,54.16-36.22l24.65,15.87c-25.23,4.53-42.51,13.77-42.51,24.43,0,15.13,34.84,27.4,77.81,27.4,36.91,0,78.35-12.27,78.35-27.4-.19-9.53-11.23-15.05-21.94-20.39l-14.7,3.36-7.75-21.39,47.72,11.66-10.77,2.74s18.21,9.43,22.09,20C356,271,306.22,289.35,245.18,289.35Z" fill="#fff"/><path d="M335.42,444.59c-9.3,0-17.58,6.44-17.58,17.49s8.27,17.54,17.58,17.54,17.53-6.47,17.53-17.54S344.66,444.59,335.42,444.59Zm0,31v-.05c-6.94.05-12.54-5.34-12.54-13.43s5.6-13.41,12.54-13.41c6.78,0,12.55,5.36,12.55,13.41S342.19,475.56,335.42,475.56Zm7.83-17.22c0-4.32-2.85-5.68-7.73-5.68h-7v18.85h4v-7.92h1.92l4.34,7.92h4.81l-4.86-8.27C341.24,463,343.25,461.79,343.25,458.34Zm-7.15,2H332.5v-4.55h2.84c1.54,0,3.62.15,3.62,2.11S337.9,460.38,336.1,460.38Z" fill="#fff" fill-rule="evenodd"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M289.06,181.64H242.77V336.72h47.85c68.16,0,83.79-51.76,83.79-77.54C374.41,217.19,347.66,181.64,289.06,181.64Z" fill="#fff"/><path d="M250,0C111.91,0,0,111.91,0,250S111.91,500,250,500,500,388.09,500,250,388.09,0,250,0ZM168.55,249V363.67H138.48V154.49h30.08Zm-15-118.36a19.73,19.73,0,1,1,19.73-19.73A19.8,19.8,0,0,1,153.52,130.66Zm140.82,233.2H212.7V154.49h81.25c77.34,0,111.33,55.27,111.33,104.69C405.27,312.89,363.28,363.87,294.34,363.87Z" fill="#fff"/></g></g></svg>

After

Width:  |  Height:  |  Size: 634 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M289.06,181.64H242.77V336.72h47.85c68.16,0,83.79-51.76,83.79-77.54C374.41,217.19,347.66,181.64,289.06,181.64Z" fill="#95ba2e"/><path d="M250,0C111.91,0,0,111.91,0,250S111.91,500,250,500,500,388.09,500,250,388.09,0,250,0ZM168.55,249V363.67H138.48V154.49h30.08Zm-15-118.36a19.73,19.73,0,1,1,19.73-19.73A19.8,19.8,0,0,1,153.52,130.66Zm140.82,233.2H212.7V154.49h81.25c77.34,0,111.33,55.27,111.33,104.69C405.27,312.89,363.28,363.87,294.34,363.87Z" fill="#95ba2e"/></g></g></svg>

After

Width:  |  Height:  |  Size: 640 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 430.79"><title>Asset 4</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><circle cx="291.76" cy="172.38" r="11.72" fill="#fff"/><path d="M307.11,310.48a4.71,4.71,0,0,0-5.89-3.08c-12.93,4-29.57,6.22-46.52,6.56V257c0-.14-.07-.25-.08-.39,14.13-4.8,43.14-26.44,37.62-37.14-2.48-4.81-21.73-5.28-42.35-5.41-20.43-.13-39.65.8-42.06,5.61-5.81,11.62,22.39,32.84,37.49,37.13V314c-17-.35-33.6-2.52-46.52-6.58a4.72,4.72,0,0,0-2.82,9h0c15.16,4.76,34.61,7.13,54.07,7.13s38.86-2.37,54-7.11a4.71,4.71,0,0,0,3.06-5.92Z" fill="#fff"/><circle cx="208.27" cy="172.38" r="11.72" fill="#fff"/><path d="M249.29,404.89c2.88,9.34,5.7,19.73,7.43,25.28,37.9-.39,72.72-.26,109.1,0-.09-14.67-.93-23.15-1-35.49l19.91,21.41c18.71-36.35,41.83-62.9,48.36-103.75,26.95-17,36.71-51.27,55.46-77.28-9.25-4.64-18.86-5.69-27.55-10,11.42-3.81,27-7,39-11-16.71-26.89-30-52.75-45.65-78.11-7.54-14.6-22.8-18.82-37.65-7.17.35-10.22,15.18-29.16,10.76-37.39-12.87-24-26.86-50.76-41-74.1L357,41.14,368.25.65C340.84,1.34,312,3.08,287,2.16c-19.65-.73-37.79,6.58-41.27,29.92-1.86-10.37-5.19-19.41-18.85-24.18C196.14-2.76,138.19,0,123.25,1.29c5.28,13,6.07,27.18,11.07,39.46L107.79,28.32C96.56,48.9,84,68.33,71.62,86.9c-8.47,16.32-12,31.15,13.72,45-14.26-.49-25-4.33-34.37-4.65C34.13,157.48,15.5,184.43,0,214.7c16.88-.43,33.29,7.54,43.2,7.29h0c-8,5.5-10.43,16.17-22.73,24.61,22.64,19,17.76,60.78,48.78,67.77-.73,5.22-4.71,14.07-2.2,19,14.17,28.1,32.86,51.37,48.65,79.92,9.39-13.15,19.17-17.67,26.48-27.21L137.57,429c17.87.61,35,1.42,52.21,1.74C211.09,431.11,242.32,431.14,249.29,404.89ZM348.57,86.16c4.24-6.39,25.13,3.51,28.49,9.37,2.82,4.91.82,29.09-6.37,29.42-2.86.13-8.14-8.58-13.63-18S347,88.53,348.57,86.16ZM122.66,95.53c3.36-5.85,24.25-15.75,28.49-9.37,1.57,2.37-3.06,11.43-8.5,20.77s-10.77,18.14-13.65,18C121.84,124.62,119.84,100.44,122.66,95.53Zm20,290Zm20.9-86.71,6.21-10.75-43.25-74.92,61.74-106.94H311.73l61.75,106.94-43.26,74.92,6.21,10.76-43.22,74.86H206.78Z" fill="#fff"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 406.28"><title>Asset 3</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M157.24,406.28c188.68,0,291.88-156.32,291.88-291.88,0-4.44,0-8.86-.3-13.26A208.72,208.72,0,0,0,500,48a204.76,204.76,0,0,1-58.92,16.14,102.94,102.94,0,0,0,45.1-56.74A205.58,205.58,0,0,1,421,32.34,102.68,102.68,0,0,0,246.22,125.9,291.24,291.24,0,0,1,34.8,18.72,102.66,102.66,0,0,0,66.56,155.66,101.82,101.82,0,0,1,20,142.82v1.3a102.62,102.62,0,0,0,82.3,100.56A102.42,102.42,0,0,1,56,246.44a102.7,102.7,0,0,0,95.84,71.24,205.84,205.84,0,0,1-127.4,44A208.82,208.82,0,0,1,0,360.2a290.42,290.42,0,0,0,157.24,46" fill="#fff"/></g></g></svg>

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

View File

@@ -2,3 +2,4 @@
@import "app/front-chat-widget.less";
@import "app/ol-chat.less";
@import "app/templates-v2.less";
@import "app/login-register.less";

View File

@@ -32,7 +32,7 @@
li {
display: inline-block;
margin: 0;
padding: 0 @margin-sm 0 0;
padding: 0 @padding-sm @padding-sm 0;
}
a {
font-size: small;
@@ -44,4 +44,16 @@
border-radius: @border-radius-base;
padding: @padding-sm;
}
.figure {
background-color: #ffffff;
border: 1px solid @ol-blue-gray-2;
display: inline-block;
margin: 0 auto @margin-sm 0;
padding: @padding-sm;
.figure-caption {
padding-top: @padding-sm;
font-size: small;
}
}
}

View File

@@ -4,6 +4,10 @@
including About and Blog
*/
.cms-page {
img {
height: auto;
max-width: 100%;
}
.btn-description {
margin-right: @margin-sm;
}
@@ -85,4 +89,57 @@
margin-bottom: @margin-sm;
}
}
/*
universities page
*/
#universities-container {
display: table;
padding: @padding-md;
width: 100%;
.row {
display: table-row;
div {
border-bottom: 1px solid @gray-lightest;
display: table-cell;
float: none;
padding: @padding-md;
vertical-align: middle;
}
}
.row:first-child {
// parent container contains padding
div {
padding-top: 0;
}
}
.row:last-child {
// parent container contains padding
div {
border: 0;
padding-bottom: 0;
}
}
p {
margin: 0 auto;
width: 100%;
}
// Logos
.uni-logo {
margin: 0 auto;
max-height: 55px;
min-width: 55px;
}
.university-claim-btn {
text-align: center;
}
}
/*
videos
*/
video {
height: auto;
max-width: 100%;
}
}

View File

@@ -0,0 +1,9 @@
/*
Styling for content pages
Including: about, home, blog, /for/__, legal, contact, portals
*/
.content-page {
a:not(.btn) {
color: @link-color-alt;
}
}

View File

@@ -30,6 +30,13 @@
}
}
.public-access-level.public-access-level--notice {
background-color: @gray-lightest;
border-bottom: none;
margin-top: @margin-md;
padding-top: @margin-md;
}
.project-member, .project-invite {
&:hover {
background-color: @gray-lightest;

View File

@@ -72,17 +72,6 @@
background-color: @toolbar-btn-active-bg-color;
box-shadow: @toolbar-btn-active-shadow;
}
&.btn-full-height-disabled {
opacity: 0.65;
&:hover,
&.active,
&:active {
text-shadow: none;
background-color: transparent;
color: @toolbar-btn-color;
box-shadow: none;
}
}
.label {
top: 4px;
right: 4px;

View File

@@ -13,9 +13,6 @@
h3 {
margin: 0;
}
p {
color: @gray
}
i {
color: lighten(@blue, 15%);
}

View File

@@ -1,3 +1,5 @@
@register-v-spacing: 20px;
.deprecated-sl-masthead {
display: inline-block;
margin-top: @header-height;
@@ -38,15 +40,12 @@
}
.masthead {
display: inline-block;
width: 100%;
background-color: rgba(0,0,0,0.85);
background-image: -webkit-linear-gradient(left,rgba(153,93,179,0.6),rgba(44,155,219,0.6));
background-image: linear-gradient(to right,rgba(153,93,179,0.6),rgba(44,155,219,0.6));
background-image: -webkit-linear-gradient(to left, rgba(79,156,69,1.0), rgba(28,91,38,1.0));
background-image: linear-gradient(to left, rgba(79,156,69,1.0), rgba(28,91,38,1.0));
position: relative;
text-align: center;
overflow: hidden;
margin-top: @header-height;
padding-top: @header-height;
h1, p, label {
color: white;
text-align: center;
@@ -70,10 +69,13 @@
text-rendering: auto;
margin-bottom: @line-height-computed;
}
label {
display: block;
}
.register-banner {
background-image: -webkit-linear-gradient(top,rgba(0,0,0,0.7),rgba(0,0,0,0.9));
background-image: linear-gradient(to bottom,rgba(0,0,0,0.7),rgba(0,0,0,0.9));
padding: @line-height-computed 0;
padding: @register-v-spacing 0;
position: absolute;
bottom: 0;
width: 100%;
@@ -83,13 +85,17 @@
font-family: @font-family-sans-serif;
font-weight: 500;
letter-spacing: 1px;
margin-bottom: @register-v-spacing;
}
.form-group {
margin-left: @line-height-computed / 2;
}
.input-lg {
border-radius: 9999px;
}
}
.screenshot {
height: 550px;
height: 600px;
margin: auto;
margin-bottom: -50px;
overflow-y: hidden;
@@ -97,7 +103,7 @@
max-width: 960px;
.img {
max-width: 960px;
background-image: url('/img/screen.png');
background-image: url('/img/homepage.png');
background-size: 100%;
background-repeat: no-repeat;
margin: auto;
@@ -111,38 +117,75 @@
only screen and ( min-resolution: 192dpi),
only screen and ( min-resolution: 2dppx) {
.img {
background-image: url('/img/screen@2x.png');
}
}
}
.btn-hero {
background-color: transparent;
border-color: white;
border-width: 3px;
color: white;
margin-right: @line-height-computed / 2;
&:hover {
background-color: white;
color: @gray-dark;
}
&.btn-primary {
border-color: lighten(@link-color, 10%);
color: lighten(@link-color, 10%);
&:hover {
background-color: lighten(@link-color, 10%);
color: white;
background-image: url('/img/homepage@2x.png');
}
}
}
}
.hp-register-external-separator {
margin: 0 0 (@register-v-spacing / 2);
color: #FFF;
}
.hp-register-external-separator-or {
vertical-align: middle;
&::before,
&::after {
content: "";
display: inline-block;
vertical-align: middle;
width: 5em;
height: 1px;
background-color: rgba(255, 255, 255, .3);
}
&::before {
margin-right: 1.25em;
}
&::after {
margin-left: 1.25em;
}
}
.hp-register-newsletter-checkbox {
font-size: 90%;
& > .checkbox-newsletter {
text-align: left;
@media only screen and (min-width: @screen-sm-min) {
text-align: center;
}
}
}
.hp-login-btn {
min-width: 220px;
}
.hp-register-form-email-pwd {
position: relative;
}
.hp-register-form-email-pwd-btn-container.form-group {
display: block;
margin-top: (@register-v-spacing / 2);
@media only screen and (min-width: @screen-md-min) {
position: absolute;
display: inline-block;
height: 100%;
top: -(@register-v-spacing / 2);
& > .btn-hero {
height: 100%;
}
}
}
.universities {
padding-bottom: @line-height-computed;
border-bottom: 1px solid @gray-lighter;
text-align: center;
margin: 0 auto;
img {
max-width: 100%;
max-height: 60px;
.uni-logo {
display: inline-block;
padding: 0 @padding-md;
width: 20%;
}
}
@@ -152,6 +195,12 @@
border-bottom: 1px solid @gray-lightest;
}
.pattern-grid {
background: url('/img/grid.png') repeat @content-alt-bg-color;
border-top: 1px solid @gray-lighter;
border-bottom: 1px solid @gray-lighter;
}
.real-time-example {
.cursor {
background-color: hsl(200, 70%, 70%);
@@ -212,3 +261,9 @@
}
box-shadow: 0 3px 5px rgba(0,0,0,.3);
}
@media only screen and (max-width: @screen-sm-max) {
.doc-history-example {
margin-bottom: @margin-md;
}
}

View File

@@ -0,0 +1,195 @@
@brand-ieee-color : #00629B;
@brand-google-color : #4885ED;
@brand-twitter-color : #1DA1F2;
@brand-orcid-color : #A6CE39;
@brand-sharelatex-color : #A93529;
.make-login-register-brand-btn(@bg-color) {
background-color: @bg-color;
text-indent: -10px;
padding-left: 0;
padding-right: 0;
&:focus,
&:hover {
background-color: darken(@bg-color, 8%);
}
&:active {
background-color: darken(@bg-color, 16%);
}
}
.make-login-register-brand-btn-alt(@color) {
background-color: #FFF;
color: @color;
text-indent: -10px;
padding-left: 0;
padding-right: 0;
&:focus,
&:hover {
background-color: #FFF;
color: darken(@color, 8%);
}
&:active {
background-color: #FFF;
color: darken(@color, 16%);
}
}
.login-register-container {
max-width: 400px;
margin: 0 auto;
}
.login-register-header {
padding-top: @line-height-computed;
padding-bottom: @line-height-computed - 5;
border-bottom: solid 1px @hr-border;
}
.login-register-header-heading {
margin: 0;
color: @text-color;
}
.login-register-card {
padding-top: 0;
padding-bottom: 0;
text-align: center;
}
.login-register-form,
.login-register-sharelatex {
padding: @line-height-computed;
border-bottom: solid 1px @hr-border;
&:last-child {
border-bottom-width: 0;
}
}
.login-register-other-links {
padding: @line-height-computed;
}
.login-register-text,
.login-register-hr-text-container {
margin: 0;
line-height: 1;
font-size: 90%;
}
.login-register-text {
padding-bottom: 25px;
&:last-child {
padding-bottom: 0;
}
}
.login-register-hr-text-container {
position: relative;
padding: @line-height-computed 0;
&::before {
content: '';
position: absolute;
height: 1px;
background-color: @hr-border;
top: 50%;
left: 0;
right: 0;
}
}
.login-register-hr-text {
position: relative;
background-color: #FFF;
padding: 0 (@line-height-computed / 2);
}
.login-register-sharelatex-tooltip {
display: inline-block;
font-size: 135%;
position: relative;
top: 2px;
margin-left: 3px;
color: @link-color;
cursor: pointer;
}
.login-register-newsletter {
font-size: 90%;
margin-top: @line-height-computed;
text-align: left;
& > .checkbox {
margin-bottom: 0;
}
}
.login-btn-ieee {
.make-login-register-brand-btn(@brand-ieee-color);
}
.login-btn-google {
.make-login-register-brand-btn(@brand-google-color);
}
.login-btn-google-alt {
.make-login-register-brand-btn-alt(@brand-google-color);
}
.login-btn-twitter {
.make-login-register-brand-btn(@brand-twitter-color);
}
.login-btn-orcid {
.make-login-register-brand-btn(@brand-orcid-color);
}
.login-btn-orcid-alt {
.make-login-register-brand-btn-alt(darken(@brand-orcid-color, 6%));
}
.login-btn-sharelatex {
.make-login-register-brand-btn(@brand-sharelatex-color);
}
.login-btn-icon {
display: inline-block;
float: left;
margin-left: 5px;
background: url(/img/brand/lion.svg) center/contain no-repeat;
width: 24px;
&::before {
content: '\00a0'; // Non-breakable space. A non-breakable character here makes this icon work like font-awesome.
}
}
.login-btn-icon-ieee {
background-image: url(/img/other-brands/logo_ieee.svg);
}
.login-btn-icon-google {
background-image: url(/img/other-brands/logo_google.svg);
}
.login-btn-icon-google-alt {
background-image: url(/img/other-brands/logo_google_alt.svg);
}
.login-btn-icon-twitter {
background-image: url(/img/other-brands/logo_twitter.svg);
}
.login-btn-icon-orcid {
background-image: url(/img/other-brands/logo_orcid.svg);
}
.login-btn-icon-orcid-alt {
background-image: url(/img/other-brands/logo_orcid_alt.svg);
}
.login-btn-icon-sharelatex {
background-image: url(/img/other-brands/logo_sharelatex.svg);
}
.login-register-header-heading {
margin: 0;
color: @text-color;
}
.registration-message-heading {
color: @text-color;
}
.registration-message-details {
font-size: 90%;
}

View File

@@ -77,17 +77,7 @@
}
}
.circle-img {
border-radius: 50%;
float: right;
height: 100px;
overflow: hidden;
position: relative;
width: 100px;
img {
display: inline;
margin: 0 auto;
width: 100%;
}
}
.faq {
p {

View File

@@ -100,52 +100,10 @@
}
// End Print
/*
Begin Tabs
*/
.nav-tabs {
// Overrides for nav.less
background-color: @ol-blue-gray-0;
border: 0!important;
margin-bottom: @margin-md;
margin-top: -@line-height-computed; //- adjusted for portal-name
padding: @padding-lg 0 @padding-md;
text-align: center;
a {
color: @link-color;
&:hover {
background-color: transparent!important;
border: 0!important;
color: @link-hover-color!important;
}
}
li {
display: inline-block;
float: none;
a {
border: 0;
}
}
li.active > a {
background-color: transparent!important;
border: 0;
border-bottom: 1px solid @accent-color-secondary!important;
color: @accent-color-secondary;
&:hover {
color: @accent-color-secondary!important;
}
}
}
.tab-content:extend(.container) {
background-color: transparent!important;
border: none!important;
}
// End Tabs
@media (max-width: @screen-size-sm-max) {
.content-pull {
padding: 0;

View File

@@ -16,100 +16,81 @@
}
.cta-links {
margin-top: @margin-lg;
margin-bottom: @margin-md;
.cta-link.btn {
margin: 0 @margin-sm;
margin-right: @margin-sm;
}
}
.tag-link {
margin-right: @margin-xs;
margin: 0 @margin-xs @margin-sm 0;
}
.templates-container {
column-count: 3;
column-gap: 1em;
}
.template-thumbnail {
&.template-thumbnail__container {
display: inline-block;
margin: 0 0 1em;
width: 100%;
}
.thumbnail {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-webkit-box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);
height: 465px;
margin: 5% 0;
&.thumbnail-tag {
height: 100px;
}
img {
width: 100%;
height: 100%;
}
}
a {
padding:0px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
h3 {
color:@link-color;
margin: 10px 0px 10px 20px;
}
display: flex;
justify-content: center;
align-items: center;
&.thumbnail-tag {
height: 100px;
}
}
.caption {
// Override ShareLatex template styles
background: none;
border: none;
// Override ShareLatex template styles
background: none;
border: none;
text-align: center;
}
.description {
font-style: italic;
padding: 5px 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.caption__description {
font-style: italic;
padding: 5px 0;
.text-overflow();
}
.caption__title {
display: inline-block;
max-width: 100%;
.text-overflow();
}
/* Media Queries */
@media (max-width: @screen-md-min) {
.thumbnail {
height: 400px;
width: 300px;
margin: 5% auto;
}
img {
width: 90%;
}
.caption .description {
padding: 5px 50px;
}
}
}
.section-preview {
margin-top: @margin-xl;
.abstract {
padding-right:10%;
padding-bottom: 10%;
@media (max-width: @screen-md-min) {
padding: 5%;
}
}
}
.section-tags {
margin-bottom: @margin-xl;
margin-top: @margin-xl;
margin-top: @margin-md;
}
.template-large-pdf-preview {

View File

@@ -15,6 +15,7 @@
}
img {
height: auto;
max-width: 100%;
}

View File

@@ -65,6 +65,9 @@
.alert-danger {
.alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);
}
.alert-alt {
.alert-variant(@alert-alt-bg; @alert-alt-border; @alert-alt-text);
}
.alert when (@is-overleaf = true) {
a {

View File

@@ -122,6 +122,10 @@
// Button Sizes
// --------------------------------------------------
.btn-xl {
.button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-h2; @line-height-large; @btn-border-radius-large);
}
.btn-lg {
// line-height: ensure even-numbered height of button next to large input
.button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);

View File

@@ -1,6 +1,6 @@
// Colors
.icon-accent {
color: @accent-color-secondary;
color: @accent-color-secondary!important;
}
// Sizes

View File

@@ -0,0 +1,17 @@
.circle-img {
border-radius: 50%;
float: left;
// float: right;
height: 100px;
overflow: hidden;
position: relative;
max-width: 100px;
img {
display: inline;
margin: 0 auto;
width: 100%;
}
}
.blockquote-with-img {
margin-left: 115px;
}

View File

@@ -0,0 +1,44 @@
.ol-tabs {
// Overrides for nav.less
.nav-tabs {
border: 0!important;
margin-bottom: @margin-md;
margin-top: -@line-height-computed; //- adjusted for portal-name
padding: @padding-lg 0 @padding-md;
text-align: center;
a {
color: @link-color;
&:hover {
background-color: transparent!important;
border: 0!important;
color: @link-hover-color!important;
}
}
}
li {
display: inline-block;
float: none;
a {
border: 0;
}
}
li.active > a {
background-color: transparent!important;
border: 0!important;
border-bottom: 1px solid @accent-color-secondary!important;
color: @accent-color-secondary!important;
&:hover {
border-bottom: 1px solid @accent-color-secondary!important;
color: @accent-color-secondary!important;
}
}
.tab-content:extend(.container) {
background-color: transparent!important;
border: none!important;
}
}

View File

@@ -17,7 +17,6 @@
//** Link hover color set via `darken()` function.
@link-hover-color: darken(@link-color, 15%);
//== Typography
//
//## Font, line-height, and color for body text, headings, and more.
@@ -82,6 +81,11 @@
@padding-xs-vertical: 1px;
@padding-xs-horizontal: 5px;
@padding-sm: 10px;
@padding-md: 20px;
@padding-lg: 30px;
@padding-xl: 40px;
@line-height-large: 1.33;
@line-height-small: 1.5;
@@ -586,6 +590,10 @@
@alert-danger-text: @state-danger-text;
@alert-danger-border: @state-danger-border;
@alert-alt-bg: @state-info-bg;
@alert-alt-text: @state-info-text;
@alert-alt-border: @state-info-border;
//== Progress bars
//

View File

@@ -49,8 +49,10 @@
@text-small-color : @ol-type-color;
@text-color : @ol-type-color;
@link-color : @ol-blue;
@link-color-alt : @ol-green;
@link-active-color : @ol-dark-green;
@link-hover-color : @ol-dark-blue;
@hr-border : @ol-blue-gray-1;
// Button colors and sizing
@btn-border-width : 0;
@@ -82,11 +84,6 @@
// Padding
@padding-xs-horizontal : 8px;
@padding-sm: 10px;
@padding-md: 20px;
@padding-lg: 30px;
@padding-xl: 40px;
// Alerts
@alert-padding : 15px;
@alert-border-radius : @border-radius-base;
@@ -108,6 +105,10 @@
@alert-danger-text : #FFF;
@alert-danger-border : transparent;
@alert-alt-bg : @ol-blue-gray-1;
@alert-alt-text : @ol-type-color;
@alert-alt-border: transparent;
// Tags
@tag-border-radius : 9999px;

View File

@@ -8,12 +8,15 @@
@import "_ol_style_includes.less";
@import "components/embed-responsive.less";
@import "components/icons.less";
@import "components/images.less";
@import "components/navs-ol.less";
@import "components/pagination.less";
@import "components/tabs.less";
// Pages
@import "app/about.less";
@import "app/blog-posts.less";
@import "app/cms-page.less";
@import "app/content_page.less";
@import "app/plans-ol.less";
@import "app/portals.less";

View File

@@ -30,7 +30,13 @@ describe 'Exports', ->
@owner.request {
method: 'POST',
url: "/project/#{@project_id}/export/#{@brand_variation_id}",
json: {},
json: true,
body:
title: 'title'
description: 'description'
author: 'author'
license: 'other'
show_source: true
}, (error, response, body) =>
throw error if error?
expect(response.statusCode).to.equal 200
@@ -42,6 +48,12 @@ describe 'Exports', ->
# project details should match
expect(project.id).to.equal @project_id
expect(project.rootDocPath).to.equal '/main.tex'
# gallery details should match
expect(project.metadata.title).to.equal 'title'
expect(project.metadata.description).to.equal 'description'
expect(project.metadata.author).to.equal 'author'
expect(project.metadata.license).to.equal 'other'
expect(project.metadata.show_source).to.equal true
# version should match what was retrieved from project-history
expect(project.historyVersion).to.equal @version
# user details should match

View File

@@ -0,0 +1,34 @@
should = require('chai').should()
assert = require('chai').assert
async = require("async")
request = require "./helpers/request"
MockV1Api = require "./helpers/MockV1Api"
assertRedirect = (method, path, expectedStatusCode, destination, cb) ->
request[method] path, (error, response) ->
should.not.exist error
response.statusCode.should.equal expectedStatusCode
response.headers.location.should.equal destination
cb()
describe "RedirectUrls", ->
before ->
@timeout(1000)
it 'proxy static URLs', (done) ->
assertRedirect 'get', '/redirect/one', 302, '/destination/one', done
it 'proxy dynamic URLs', (done) ->
assertRedirect 'get', '/redirect/params/42', 302, '/destination/42/params', done
it 'proxy URLs with baseUrl', (done) ->
assertRedirect 'get', '/redirect/base_url', 302, 'https://example.com/destination/base_url', done
it 'proxy URLs with POST with a 307', (done) ->
assertRedirect 'post', '/redirect/get_and_post', 307, '/destination/get_and_post', done
it 'proxy URLs with multiple support methods', (done) ->
assertRedirect 'get', '/redirect/get_and_post', 302, '/destination/get_and_post', done
it 'redirects with query params', (done) ->
assertRedirect 'get', '/redirect/qs?foo=bar&baz[]=qux1&baz[]=qux2', 302, '/destination/qs?foo=bar&baz[]=qux1&baz[]=qux2', done

View File

@@ -415,3 +415,13 @@ describe 'TokenAccess', ->
try_content_access(@other2, @project_id, (response, body) =>
expect(body.privilegeLevel).to.equal false
, done)
describe 'unimported v1 project', ->
it 'should redirect to v1', (done) ->
unimportedV1Token = '123abc'
try_read_and_write_token_access(@owner, unimportedV1Token, (response, body) =>
expect(response.statusCode).to.equal 302
expect(response.headers.location).to.equal(
'http://overleaf.test:5000/123abc'
)
, done)

View File

@@ -58,7 +58,7 @@ class User
@getCsrfToken (error) =>
return callback(error) if error?
@request.post {
url: "/login"
url: if settings.enableLegacyLogin then "/login/legacy" else "/login"
json: { email, @password }
}, callback

View File

@@ -3,6 +3,7 @@ v1Api =
module.exports =
enableSubscriptions: true
enableLegacyRegistration: true
features: features =
v1_free:
@@ -105,3 +106,25 @@ module.exports =
path: (params) -> "/universities/list/#{params.id}"
'/institutions/domains': { baseUrl: v1Api.url, path: '/university/domains' }
'/proxy/missing/baseUrl': path: '/foo/bar'
'/proxy/get_and_post': {
methods: ['get', 'post'],
path: '/destination/get_and_post'
}
overleaf:
host: "http://overleaf.test:5000"
redirects:
'/redirect/one': '/destination/one',
'/redirect/get_and_post': {
methods: ['get', 'post'],
url: '/destination/get_and_post'
},
'/redirect/base_url': {
baseUrl: 'https://example.com'
url: '/destination/base_url'
},
'/redirect/params/:id': {
url: (params) -> "/destination/#{params.id}/params"
},
'/redirect/qs': '/destination/qs'

View File

@@ -15,7 +15,7 @@ describe "AuthenticationController", ->
tk.freeze(Date.now())
@AuthenticationController = SandboxedModule.require modulePath, requires:
"./AuthenticationManager": @AuthenticationManager = {}
"../User/UserUpdater" : @UserUpdater = {}
"../User/UserUpdater" : @UserUpdater = {updateUser:sinon.stub()}
"metrics-sharelatex": @Metrics = { inc: sinon.stub() }
"../Security/LoginRateLimiter": @LoginRateLimiter = { processLoginRequest:sinon.stub(), recordSuccessfulLogin:sinon.stub() }
"../User/UserHandler": @UserHandler = {setupLoginData:sinon.stub()}

View File

@@ -137,11 +137,6 @@ describe "EditorHttpController", ->
.calledWith(@project_id)
.should.equal true
it "should look up the user", ->
@UserGetter.getUser
.calledWith(@user_id, { isAdmin: true })
.should.equal true
it "should check the privilege level", ->
@AuthorizationManager.getPrivilegeLevelForProject
.calledWith(@user_id, @project_id, @token)

Some files were not shown because too many files have changed in this diff Show More