mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' into sk-session-revocation
This commit is contained in:
commit
fc6cf75ad5
27 changed files with 244 additions and 76 deletions
|
@ -17,8 +17,9 @@ module.exports = (grunt) ->
|
|||
grunt.loadNpmTasks 'grunt-contrib-watch'
|
||||
grunt.loadNpmTasks 'grunt-parallel'
|
||||
grunt.loadNpmTasks 'grunt-exec'
|
||||
grunt.loadNpmTasks 'grunt-contrib-imagemin'
|
||||
grunt.loadNpmTasks 'grunt-contrib-cssmin'
|
||||
# grunt.loadNpmTasks 'grunt-contrib-imagemin'
|
||||
# grunt.loadNpmTasks 'grunt-sprity'
|
||||
|
||||
config =
|
||||
|
||||
|
@ -47,18 +48,26 @@ module.exports = (grunt) ->
|
|||
stream:true
|
||||
|
||||
|
||||
imagemin:
|
||||
dynamic:
|
||||
files: [{
|
||||
expand: true
|
||||
cwd: 'public/img/'
|
||||
src: ['**/*.{png,jpg,gif}']
|
||||
dest: 'public/img/'
|
||||
}]
|
||||
options:
|
||||
interlaced:false
|
||||
optimizationLevel: 7
|
||||
# imagemin:
|
||||
# dynamic:
|
||||
# files: [{
|
||||
# expand: true
|
||||
# cwd: 'public/img/'
|
||||
# src: ['**/*.{png,jpg,gif}']
|
||||
# dest: 'public/img/'
|
||||
# }]
|
||||
# options:
|
||||
# interlaced:false
|
||||
# optimizationLevel: 7
|
||||
|
||||
# sprity:
|
||||
# sprite:
|
||||
# options:
|
||||
# cssPath:"/img/"
|
||||
# 'style': '../../public/stylesheets/app/sprites.less'
|
||||
# margin: 0
|
||||
# src: ['./public/img/flags/24/*.png']
|
||||
# dest: './public/img/sprite'
|
||||
|
||||
|
||||
coffee:
|
||||
|
@ -218,6 +227,7 @@ module.exports = (grunt) ->
|
|||
pattern: "@@RELEASE@@"
|
||||
replacement: process.env.BUILD_NUMBER || "(unknown build)"
|
||||
|
||||
|
||||
|
||||
|
||||
availabletasks:
|
||||
|
|
|
@ -6,9 +6,17 @@ web-sharelatex is the front-end web service of the open-source web-based collabo
|
|||
It serves all the HTML pages, CSS and javascript to the client. web-sharelatex also contains
|
||||
a lot of logic around creating and editing projects, and account management.
|
||||
|
||||
|
||||
The rest of the ShareLaTeX stack, along with information about contributing can be found in the
|
||||
[sharelatex/sharelatex](https://github.com/sharelatex/sharelatex) repository.
|
||||
|
||||
Build process
|
||||
----------------
|
||||
|
||||
web-sharelatex uses [Grunt](http://gruntjs.com/) to build its front-end related assets.
|
||||
|
||||
Image processing tasks are commented out in the gruntfile and the needed packages aren't presently in the project's `package.json`. If the images need to be processed again (minified and sprited), start by fetching the packages (`npm install grunt-contrib-imagemin grunt-sprity`), then *decomment* the tasks in `Gruntfile.coffee`. After this, the tasks can be called (explicitly, via `grunt imagemin` and `grunt sprity`).
|
||||
|
||||
Unit test status
|
||||
----------------
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ module.exports = BlogController =
|
|||
|
||||
logger.log url:url, "proxying request to blog api"
|
||||
request.get blogUrl, (err, r, data)->
|
||||
if r?.statusCode == 404
|
||||
if r?.statusCode == 404 or r?.statusCode == 403
|
||||
return ErrorController.notFound(req, res, next)
|
||||
if err?
|
||||
return res.send 500
|
||||
|
|
|
@ -30,12 +30,17 @@ module.exports = CollaboratorsHandler =
|
|||
getMembersWithPrivilegeLevels: (project_id, callback = (error, members) ->) ->
|
||||
CollaboratorsHandler.getMemberIdsWithPrivilegeLevels project_id, (error, members = []) ->
|
||||
return callback(error) if error?
|
||||
result = []
|
||||
async.mapLimit members, 3,
|
||||
(member, cb) ->
|
||||
UserGetter.getUser member.id, (error, user) ->
|
||||
return cb(error) if error?
|
||||
return cb(null, { user: user, privilegeLevel: member.privilegeLevel })
|
||||
callback
|
||||
if user?
|
||||
result.push { user: user, privilegeLevel: member.privilegeLevel }
|
||||
cb()
|
||||
(error) ->
|
||||
return callback(error) if error?
|
||||
callback null, result
|
||||
|
||||
getMemberIdPrivilegeLevel: (user_id, project_id, callback = (error, privilegeLevel) ->) ->
|
||||
# In future if the schema changes and getting all member ids is more expensive (multiple documents)
|
||||
|
@ -82,6 +87,18 @@ module.exports = CollaboratorsHandler =
|
|||
logger.error err: err, "problem removing user from project collaberators"
|
||||
callback(err)
|
||||
|
||||
removeUserFromAllProjets: (user_id, callback = (error) ->) ->
|
||||
CollaboratorsHandler.getProjectsUserIsCollaboratorOf user_id, { _id: 1 }, (error, readAndWriteProjects = [], readOnlyProjects = []) ->
|
||||
return callback(error) if error?
|
||||
allProjects = readAndWriteProjects.concat(readOnlyProjects)
|
||||
jobs = []
|
||||
for project in allProjects
|
||||
do (project) ->
|
||||
jobs.push (cb) ->
|
||||
return cb() if !project?
|
||||
CollaboratorsHandler.removeUserFromProject project._id, user_id, cb
|
||||
async.series jobs, callback
|
||||
|
||||
addEmailToProject: (project_id, adding_user_id, unparsed_email, privilegeLevel, callback = (error, user) ->) ->
|
||||
emails = mimelib.parseAddresses(unparsed_email)
|
||||
email = emails[0]?.address?.toLowerCase()
|
||||
|
|
|
@ -29,8 +29,6 @@ module.exports = CompileController =
|
|||
options.compiler = req.body.compiler
|
||||
if req.body?.draft
|
||||
options.draft = req.body.draft
|
||||
if req.query?.isolated is "true"
|
||||
options.isolated = true
|
||||
logger.log {options:options, project_id:project_id, user_id:user_id}, "got compile request"
|
||||
CompileManager.compile project_id, user_id, options, (error, status, outputFiles, clsiServerId, limits, validationProblems) ->
|
||||
return next(error) if error?
|
||||
|
@ -44,17 +42,15 @@ module.exports = CompileController =
|
|||
}
|
||||
|
||||
_compileAsUser: (req, callback) ->
|
||||
# callback with user_id if isolated flag is set on request, undefined otherwise
|
||||
isolated = req.query?.isolated is "true"
|
||||
if isolated
|
||||
# callback with user_id if per-user, undefined otherwise
|
||||
if not Settings.disablePerUserCompiles
|
||||
AuthenticationController.getLoggedInUserId req, callback # -> (error, user_id)
|
||||
else
|
||||
callback() # do a per-project compile, not per-user
|
||||
|
||||
_downloadAsUser: (req, callback) ->
|
||||
# callback with user_id if isolated flag or user_id param is set on request, undefined otherwise
|
||||
isolated = req.query?.isolated is "true" or req.params.user_id?
|
||||
if isolated
|
||||
# callback with user_id if per-user, undefined otherwise
|
||||
if not Settings.disablePerUserCompiles
|
||||
AuthenticationController.getLoggedInUserId req, callback # -> (error, user_id)
|
||||
else
|
||||
callback() # do a per-project compile, not per-user
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = CompileManager =
|
|||
for key, value of limits
|
||||
options[key] = value
|
||||
# only pass user_id down to clsi if this is a per-user compile
|
||||
compileAsUser = if options.isolated then user_id else undefined
|
||||
compileAsUser = if Settings.disablePerUserCompiles then undefined else user_id
|
||||
ClsiManager.sendRequest project_id, compileAsUser, options, (error, status, outputFiles, clsiServerId, validationProblems) ->
|
||||
return callback(error) if error?
|
||||
logger.log files: outputFiles, "output files"
|
||||
|
|
|
@ -3,6 +3,8 @@ PersonalEmailLayout = require("./Layouts/PersonalEmailLayout")
|
|||
NotificationEmailLayout = require("./Layouts/NotificationEmailLayout")
|
||||
settings = require("settings-sharelatex")
|
||||
|
||||
|
||||
|
||||
templates = {}
|
||||
|
||||
templates.registered =
|
||||
|
@ -114,6 +116,8 @@ module.exports =
|
|||
template = templates[templateName]
|
||||
opts.siteUrl = settings.siteUrl
|
||||
opts.body = template.compiledTemplate(opts)
|
||||
if settings.email?.templates?.customFooter?
|
||||
opts.body += settings.email?.templates?.customFooter
|
||||
return {
|
||||
subject : template.subject(opts)
|
||||
html: template.layout(opts)
|
||||
|
|
|
@ -6,7 +6,7 @@ oneSecond = 1000
|
|||
|
||||
makeRequest = (opts, callback)->
|
||||
if !settings.apis.notifications?.url?
|
||||
return callback()
|
||||
return callback(null, statusCode:200)
|
||||
else
|
||||
request(opts, callback)
|
||||
|
||||
|
@ -18,7 +18,7 @@ module.exports =
|
|||
json: true
|
||||
timeout: oneSecond
|
||||
method: "GET"
|
||||
request opts, (err, res, unreadNotifications)->
|
||||
makeRequest opts, (err, res, unreadNotifications)->
|
||||
statusCode = if res? then res.statusCode else 500
|
||||
if err? or statusCode != 200
|
||||
e = new Error("something went wrong getting notifications, #{err}, #{statusCode}")
|
||||
|
@ -40,7 +40,7 @@ module.exports =
|
|||
templateKey:templateKey
|
||||
}
|
||||
logger.log opts:opts, "creating notification for user"
|
||||
request opts, callback
|
||||
makeRequest opts, callback
|
||||
|
||||
markAsReadWithKey: (user_id, key, callback)->
|
||||
opts =
|
||||
|
@ -51,7 +51,7 @@ module.exports =
|
|||
key:key
|
||||
}
|
||||
logger.log user_id:user_id, key:key, "sending mark notification as read with key to notifications api"
|
||||
request opts, callback
|
||||
makeRequest opts, callback
|
||||
|
||||
|
||||
markAsRead: (user_id, notification_id, callback)->
|
||||
|
@ -60,4 +60,4 @@ module.exports =
|
|||
uri: "#{settings.apis.notifications?.url}/user/#{user_id}/notification/#{notification_id}"
|
||||
timeout:oneSecond
|
||||
logger.log user_id:user_id, notification_id:notification_id, "sending mark notification as read to notifications api"
|
||||
request opts, callback
|
||||
makeRequest opts, callback
|
||||
|
|
|
@ -24,9 +24,11 @@ module.exports = ProjectDeleter =
|
|||
update = {deletedByExternalDataSource: false}
|
||||
Project.update conditions, update, {}, callback
|
||||
|
||||
deleteUsersProjects: (owner_id, callback)->
|
||||
logger.log owner_id:owner_id, "deleting users projects"
|
||||
Project.remove owner_ref:owner_id, callback
|
||||
deleteUsersProjects: (user_id, callback)->
|
||||
logger.log {user_id}, "deleting users projects"
|
||||
Project.remove owner_ref:user_id, (error) ->
|
||||
return callback(error) if error?
|
||||
CollaboratorsHandler.removeUserFromAllProjets user_id, callback
|
||||
|
||||
deleteProject: (project_id, callback = (error) ->) ->
|
||||
# archiveProject takes care of the clean-up
|
||||
|
|
|
@ -15,7 +15,7 @@ footer.site-footer
|
|||
aria-expanded="false",
|
||||
tooltip="#{translate('language')}"
|
||||
)
|
||||
img(src="/img/flags/24/#{currentLngCode}.png")
|
||||
figure(class="sprite-icon sprite-icon-lang sprite-icon-#{currentLngCode}")
|
||||
|
||||
ul.dropdown-menu(role="menu")
|
||||
li.dropdown-header #{translate("language")}
|
||||
|
@ -23,9 +23,9 @@ footer.site-footer
|
|||
if !subdomainDetails.hide
|
||||
li.lngOption
|
||||
a.menu-indent(href=subdomainDetails.url+currentUrl)
|
||||
img(src="/img/flags/24/#{subdomainDetails.lngCode}.png")
|
||||
figure(class="sprite-icon sprite-icon-lang sprite-icon-#{subdomainDetails.lngCode}")
|
||||
| #{translate(subdomainDetails.lngCode)}
|
||||
|
||||
//- img(src="/img/flags/24/.png")
|
||||
each item in nav.left_footer
|
||||
li
|
||||
if item.url
|
||||
|
|
|
@ -128,12 +128,12 @@ div.full-size.pdf(ng-controller="PdfController")
|
|||
)
|
||||
label.card-hint-feedback-label #{translate("log_hint_feedback_label")}
|
||||
a.card-hint-feedback-positive(
|
||||
ng-click="feedbackSent = true;"
|
||||
ng-click="trackLogHintsPositiveFeedback(entry.ruleId); feedbackSent = true;"
|
||||
href
|
||||
) #{translate("answer_yes")}
|
||||
span /
|
||||
a.card-hint-feedback-negative(
|
||||
ng-click="feedbackSent = true;"
|
||||
ng-click="trackLogHintsNegativeFeedback(entry.ruleId); feedbackSent = true;"
|
||||
href
|
||||
) #{translate("answer_no")}
|
||||
.card-hint-feedback(ng-show="feedbackSent")
|
||||
|
|
|
@ -262,6 +262,10 @@ module.exports = settings =
|
|||
# Should we allow access to any page without logging in? This includes
|
||||
# public projects, /learn, /templates, about pages, etc.
|
||||
allowPublicAccess: if process.env["SHARELATEX_ALLOW_PUBLIC_ACCESS"] == 'true' then true else false
|
||||
|
||||
# Use a single compile directory for all users in a project
|
||||
# (otherwise each user has their own directory)
|
||||
# disablePerUserCompiles: true
|
||||
|
||||
# Maximum size of text documents in the real-time editing system.
|
||||
max_doc_length: 2 * 1024 * 1024 # 2mb
|
||||
|
|
|
@ -70,7 +70,6 @@
|
|||
"grunt-contrib-clean": "0.5.0",
|
||||
"grunt-contrib-coffee": "0.10.0",
|
||||
"grunt-contrib-cssmin": "^1.0.1",
|
||||
"grunt-contrib-imagemin": "^1.0.1",
|
||||
"grunt-contrib-less": "0.9.0",
|
||||
"grunt-contrib-requirejs": "0.4.1",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
|
|
|
@ -12,7 +12,8 @@ define [
|
|||
ruleDetails = _getRule entry.message
|
||||
|
||||
if (ruleDetails?)
|
||||
entry.ruleId = 'hint_' + ruleDetails.regexToMatch.toString().replace(/[^a-zA-Z0-9]/g, '_').toLowerCase() if ruleDetails.regexToMatch?
|
||||
entry.ruleId = 'hint_' + ruleDetails.regexToMatch.toString().replace(/\s/g, '_').slice(1, -1) if ruleDetails.regexToMatch?
|
||||
|
||||
entry.humanReadableHint = ruleDetails.humanReadableHint if ruleDetails.humanReadableHint?
|
||||
entry.extraInfoURL = ruleDetails.extraInfoURL if ruleDetails.extraInfoURL?
|
||||
|
||||
|
|
|
@ -15,6 +15,13 @@ define [
|
|||
$scope.shouldShowLogs = false
|
||||
$scope.wikiEnabled = window.wikiEnabled;
|
||||
|
||||
# log hints tracking
|
||||
trackLogHintsFeedback = (isPositive, hintId) ->
|
||||
event_tracking.send 'log-hints', (if isPositive then 'feedback-positive' else 'feedback-negative'), hintId
|
||||
|
||||
$scope.trackLogHintsPositiveFeedback = (hintId) -> trackLogHintsFeedback true, hintId
|
||||
$scope.trackLogHintsNegativeFeedback = (hintId) -> trackLogHintsFeedback false, hintId
|
||||
|
||||
if ace.require("ace/lib/useragent").isMac
|
||||
$scope.modifierKey = "Cmd"
|
||||
else
|
||||
|
@ -50,8 +57,6 @@ define [
|
|||
params = {}
|
||||
if options.isAutoCompile
|
||||
params["auto_compile"]=true
|
||||
if perUserCompile # send ?isolated=true for per-user compiles
|
||||
params["isolated"] = true
|
||||
return $http.post url, {
|
||||
rootDoc_id: options.rootDocOverride_id or null
|
||||
draft: $scope.draft
|
||||
|
@ -125,9 +130,6 @@ define [
|
|||
# convert the qs hash into a query string and append it
|
||||
$scope.pdf.qs = createQueryString qs
|
||||
$scope.pdf.url += $scope.pdf.qs
|
||||
# special case for the download url
|
||||
if perUserCompile
|
||||
qs.isolated = true
|
||||
# Save all downloads as files
|
||||
qs.popupDownload = true
|
||||
$scope.pdf.downloadUrl = "/project/#{$scope.project_id}/output/output.pdf" + createQueryString(qs)
|
||||
|
@ -147,8 +149,6 @@ define [
|
|||
else
|
||||
file.name = file.path
|
||||
qs = {}
|
||||
if perUserCompile
|
||||
qs.isolated = true
|
||||
if response.clsiServerId?
|
||||
qs.clsiserverid = response.clsiServerId
|
||||
file.url = "/project/#{project_id}/output/#{file.path}" + createQueryString qs
|
||||
|
@ -237,7 +237,7 @@ define [
|
|||
return null
|
||||
|
||||
normalizeFilePath = (path) ->
|
||||
path = path.replace(/^(.*)\/compiles\/[0-9a-f]{24}\/(\.\/)?/, "")
|
||||
path = path.replace(/^(.*)\/compiles\/[0-9a-f]{24}(-[0-9a-f]{24})?\/(\.\/)?/, "")
|
||||
path = path.replace(/^\/compile\//, "")
|
||||
|
||||
rootDocDirname = ide.fileTreeManager.getRootDocDirname()
|
||||
|
@ -274,7 +274,6 @@ define [
|
|||
method: "DELETE"
|
||||
params:
|
||||
clsiserverid:ide.clsiServerId
|
||||
isolated: perUserCompile
|
||||
headers:
|
||||
"X-Csrf-Token": window.csrfToken
|
||||
}
|
||||
|
@ -361,7 +360,6 @@ define [
|
|||
line: row + 1
|
||||
column: column
|
||||
clsiserverid:ide.clsiServerId
|
||||
isolated: perUserCompile
|
||||
}
|
||||
})
|
||||
.success (data) ->
|
||||
|
@ -407,7 +405,6 @@ define [
|
|||
h: h.toFixed(2)
|
||||
v: v.toFixed(2)
|
||||
clsiserverid:ide.clsiServerId
|
||||
isolated: perUserCompile
|
||||
}
|
||||
})
|
||||
.success (data) ->
|
||||
|
|
|
@ -12,7 +12,8 @@ define [
|
|||
|
||||
constructor: (@url, @options) ->
|
||||
# PDFJS.disableFontFace = true # avoids repaints, uses worker more
|
||||
# PDFJS.disableAutoFetch = true # enable this to prevent loading whole file
|
||||
if @options.disableAutoFetch
|
||||
PDFJS.disableAutoFetch = true # prevent loading whole file
|
||||
# PDFJS.disableStream
|
||||
# PDFJS.disableRange
|
||||
@scale = @options.scale || 1
|
||||
|
|
|
@ -27,13 +27,21 @@ define [
|
|||
$scope.document.destroy() if $scope.document?
|
||||
$scope.loadCount = if $scope.loadCount? then $scope.loadCount + 1 else 1
|
||||
# TODO need a proper url manipulation library to add to query string
|
||||
$scope.document = new PDFRenderer($scope.pdfSrc + '&pdfng=true' , {
|
||||
url = $scope.pdfSrc
|
||||
# add 'pdfng=true' to show that we are using the angular pdfjs viewer
|
||||
queryStringExists = url.match(/\?/)
|
||||
url = url + (if not queryStringExists then '?' else '&') + 'pdfng=true'
|
||||
# for isolated compiles, load the pdf on-demand because nobody will overwrite it
|
||||
onDemandLoading = window.location?.search?.match(/isolated=true/)?
|
||||
$scope.document = new PDFRenderer(url, {
|
||||
scale: 1,
|
||||
disableAutoFetch: if onDemandLoading then true else undefined
|
||||
navigateFn: (ref) ->
|
||||
# this function captures clicks on the annotation links
|
||||
$scope.navigateTo = ref
|
||||
$scope.$apply()
|
||||
progressCallback: (progress) ->
|
||||
return if onDemandLoading is true # don't show progress for on-demand page loading
|
||||
$scope.$emit 'progress', progress
|
||||
loadedCallback: () ->
|
||||
$scope.$emit 'loaded'
|
||||
|
|
|
@ -5,15 +5,11 @@ define [
|
|||
$scope.status =
|
||||
loading:true
|
||||
|
||||
# enable per-user containers by default
|
||||
perUserCompile = true
|
||||
|
||||
opts =
|
||||
url:"/project/#{ide.project_id}/wordcount"
|
||||
method:"GET"
|
||||
params:
|
||||
clsiserverid:ide.clsiServerId
|
||||
isolated: perUserCompile
|
||||
$http opts
|
||||
.success (data) ->
|
||||
$scope.status.loading = false
|
||||
|
@ -22,4 +18,4 @@ define [
|
|||
$scope.status.error = true
|
||||
|
||||
$scope.cancel = () ->
|
||||
$modalInstance.dismiss('cancel')
|
||||
$modalInstance.dismiss('cancel')
|
||||
|
|
BIN
services/web/public/img/sprite.png
Normal file
BIN
services/web/public/img/sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
105
services/web/public/stylesheets/app/sprites.less
Normal file
105
services/web/public/stylesheets/app/sprites.less
Normal file
|
@ -0,0 +1,105 @@
|
|||
|
||||
.sprite-icon {
|
||||
background-image: url('/img/sprite.png');
|
||||
}
|
||||
|
||||
.sprite-icon-ko {
|
||||
background-position: -0px -0px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-cn {
|
||||
background-position: -0px -24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-da {
|
||||
background-position: -0px -48px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-de {
|
||||
background-position: -0px -72px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-en {
|
||||
background-position: -0px -96px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-es {
|
||||
background-position: -0px -120px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-fi {
|
||||
background-position: -0px -144px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-fr {
|
||||
background-position: -0px -168px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-it {
|
||||
background-position: -0px -192px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-ja {
|
||||
background-position: -0px -216px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-cs {
|
||||
background-position: -0px -240px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-nl {
|
||||
background-position: -0px -264px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-no {
|
||||
background-position: -0px -288px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-pl {
|
||||
background-position: -0px -312px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-pt {
|
||||
background-position: -0px -336px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-ru {
|
||||
background-position: -0px -360px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-sv {
|
||||
background-position: -0px -384px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-tr {
|
||||
background-position: -0px -408px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-uk {
|
||||
background-position: -0px -432px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.sprite-icon-zh-CN {
|
||||
background-position: -0px -456px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
|
@ -41,11 +41,6 @@
|
|||
.example {
|
||||
max-width: 100%;
|
||||
|
||||
& > div {
|
||||
display: block !important;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.code {
|
||||
pre {
|
||||
background-color: @gray-lightest;
|
||||
|
@ -60,9 +55,9 @@
|
|||
padding-top: 10px;
|
||||
|
||||
img {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
box-shadow: 0 1px 3px @gray-light;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
|
|
@ -23,3 +23,8 @@ footer.site-footer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sprite-icon-lang {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
|
@ -73,3 +73,4 @@
|
|||
@import "app/wiki.less";
|
||||
@import "app/translations.less";
|
||||
@import "app/contact-us.less";
|
||||
@import "app/sprites.less";
|
||||
|
|
|
@ -77,17 +77,19 @@ describe "CollaboratorsHandler", ->
|
|||
{ id: "read-only-ref-2", privilegeLevel: "readOnly" }
|
||||
{ id: "read-write-ref-1", privilegeLevel: "readAndWrite" }
|
||||
{ id: "read-write-ref-2", privilegeLevel: "readAndWrite" }
|
||||
{ id: "doesnt-exist", privilegeLevel: "readAndWrite" }
|
||||
])
|
||||
@UserGetter.getUser = sinon.stub()
|
||||
@UserGetter.getUser.withArgs("read-only-ref-1").yields(null, { _id: "read-only-ref-1" })
|
||||
@UserGetter.getUser.withArgs("read-only-ref-2").yields(null, { _id: "read-only-ref-2" })
|
||||
@UserGetter.getUser.withArgs("read-write-ref-1").yields(null, { _id: "read-write-ref-1" })
|
||||
@UserGetter.getUser.withArgs("read-write-ref-2").yields(null, { _id: "read-write-ref-2" })
|
||||
@UserGetter.getUser.withArgs("doesnt-exist").yields(null, null)
|
||||
@CollaboratorHandler.getMembersWithPrivilegeLevels @project_id, @callback
|
||||
|
||||
it "should return an array of members with their privilege levels", ->
|
||||
@callback
|
||||
.calledWith(undefined, [
|
||||
.calledWith(null, [
|
||||
{ user: { _id: "read-only-ref-1" }, privilegeLevel: "readOnly" }
|
||||
{ user: { _id: "read-only-ref-2" }, privilegeLevel: "readOnly" }
|
||||
{ user: { _id: "read-write-ref-1" }, privilegeLevel: "readAndWrite" }
|
||||
|
@ -274,6 +276,19 @@ describe "CollaboratorsHandler", ->
|
|||
it "should not add any users to the proejct", ->
|
||||
@CollaboratorHandler.addUserIdToProject.called.should.equal false
|
||||
|
||||
|
||||
|
||||
|
||||
describe "removeUserFromAllProjects", ->
|
||||
beforeEach (done) ->
|
||||
@CollaboratorHandler.getProjectsUserIsCollaboratorOf = sinon.stub()
|
||||
@CollaboratorHandler.getProjectsUserIsCollaboratorOf.withArgs(@user_id, { _id: 1 }).yields(
|
||||
null,
|
||||
[ { _id: "read-and-write-0" }, { _id: "read-and-write-1" }, null ],
|
||||
[ { _id: "read-only-0" }, { _id: "read-only-1" }, null ]
|
||||
)
|
||||
@CollaboratorHandler.removeUserFromProject = sinon.stub().yields()
|
||||
@CollaboratorHandler.removeUserFromAllProjets @user_id, done
|
||||
|
||||
it "should remove the user from each project", ->
|
||||
for project_id in ["read-and-write-0", "read-and-write-1", "read-only-0", "read-only-1"]
|
||||
@CollaboratorHandler.removeUserFromProject
|
||||
.calledWith(project_id, @user_id)
|
||||
.should.equal true
|
|
@ -139,7 +139,7 @@ describe "CompileController", ->
|
|||
.should.equal true
|
||||
|
||||
it "should proxy the PDF from the CLSI", ->
|
||||
@CompileController.proxyToClsi.calledWith(@project_id, "/project/#{@project_id}/output/output.pdf", @req, @res, @next).should.equal true
|
||||
@CompileController.proxyToClsi.calledWith(@project_id, "/project/#{@project_id}/user/#{@user_id}/output/output.pdf", @req, @res, @next).should.equal true
|
||||
|
||||
describe "when the pdf is not going to be used in pdfjs viewer", ->
|
||||
|
||||
|
@ -338,8 +338,6 @@ describe "CompileController", ->
|
|||
@req =
|
||||
params:
|
||||
project_id:@project_id
|
||||
query:
|
||||
isolated: "true"
|
||||
@CompileManager.compile.callsArgWith(3)
|
||||
@CompileController.proxyToClsi = sinon.stub()
|
||||
@res =
|
||||
|
@ -362,8 +360,6 @@ describe "CompileController", ->
|
|||
@CompileManager.wordCount = sinon.stub().callsArgWith(3, null, {content:"body"})
|
||||
@req.params =
|
||||
Project_id: @project_id
|
||||
@req.query =
|
||||
isolated: "true"
|
||||
@res.send = sinon.stub()
|
||||
@res.contentType = sinon.stub()
|
||||
@CompileController.wordCount @req, @res, @next
|
||||
|
|
|
@ -71,7 +71,7 @@ describe "CompileManager", ->
|
|||
|
||||
it "should run the compile with the compile limits", ->
|
||||
@ClsiManager.sendRequest
|
||||
.calledWith(@project_id, undefined, {
|
||||
.calledWith(@project_id, @user_id, {
|
||||
timeout: @limits.timeout
|
||||
})
|
||||
.should.equal true
|
||||
|
|
|
@ -27,13 +27,15 @@ describe 'ProjectDeleter', ->
|
|||
removeProjectFromAllTags: sinon.stub().callsArgWith(2)
|
||||
@ProjectGetter =
|
||||
getProject:sinon.stub()
|
||||
@CollaboratorsHandler =
|
||||
removeUserFromAllProjets: sinon.stub().yields()
|
||||
@deleter = SandboxedModule.require modulePath, requires:
|
||||
"../Editor/EditorController": @editorController
|
||||
'../../models/Project':{Project:@Project}
|
||||
'../DocumentUpdater/DocumentUpdaterHandler': @documentUpdaterHandler
|
||||
"../Tags/TagsHandler":@TagsHandler
|
||||
"../FileStore/FileStoreHandler": @FileStoreHandler = {}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler
|
||||
"./ProjectGetter": @ProjectGetter
|
||||
'logger-sharelatex':
|
||||
log:->
|
||||
|
@ -74,6 +76,12 @@ describe 'ProjectDeleter', ->
|
|||
@Project.remove.calledWith(owner_ref:user_id).should.equal true
|
||||
done()
|
||||
|
||||
it "should remove all the projects the user is a collaborator of", (done)->
|
||||
user_id = 1234
|
||||
@deleter.deleteUsersProjects user_id, =>
|
||||
@CollaboratorsHandler.removeUserFromAllProjets.calledWith(user_id).should.equal true
|
||||
done()
|
||||
|
||||
describe "deleteProject", ->
|
||||
beforeEach (done) ->
|
||||
@project_id = "mock-project-id-123"
|
||||
|
|
Loading…
Reference in a new issue