mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-10 21:07:22 +00:00
Merge branch 'master' of github.com:sharelatex/web-sharelatex
This commit is contained in:
commit
52cb1a4dcd
16 changed files with 295 additions and 136 deletions
|
@ -1,13 +1,21 @@
|
|||
logger = require "logger-sharelatex"
|
||||
request = require "request"
|
||||
settings = require "settings-sharelatex"
|
||||
AuthenticationController = require "../Authentication/AuthenticationController"
|
||||
|
||||
module.exports = TrackChangesController =
|
||||
proxyToTrackChangesApi: (req, res, next = (error) ->) ->
|
||||
url = settings.apis.trackchanges.url + req.url
|
||||
logger.log url: url, "proxying to track-changes api"
|
||||
getReq = request.get(url)
|
||||
getReq.pipe(res)
|
||||
getReq.on "error", (error) ->
|
||||
logger.error err: error, "track-changes API error"
|
||||
next(error)
|
||||
AuthenticationController.getLoggedInUserId req, (error, user_id) ->
|
||||
return next(error) if error?
|
||||
url = settings.apis.trackchanges.url + req.url
|
||||
logger.log url: url, "proxying to track-changes api"
|
||||
getReq = request(
|
||||
url: url
|
||||
method: req.method
|
||||
headers:
|
||||
"X-User-Id": user_id
|
||||
)
|
||||
getReq.pipe(res)
|
||||
getReq.on "error", (error) ->
|
||||
logger.error err: error, "track-changes API error"
|
||||
next(error)
|
|
@ -125,6 +125,7 @@ module.exports = class Router
|
|||
|
||||
app.get "/project/:Project_id/doc/:doc_id/updates", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
app.get "/project/:Project_id/doc/:doc_id/diff", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
|
||||
|
||||
app.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
|
||||
app.get '/project/:Project_id/collaborators', SecutiryManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
|
||||
|
|
|
@ -140,8 +140,7 @@
|
|||
button.btn.btn-primary ok
|
||||
|
||||
script(type="text/template")#genericModalButtonTemplate
|
||||
a(href="#",class="btn {{ class }}") {{ text }}
|
||||
|
||||
button(class="btn {{ class }}") {{ text }}
|
||||
|
||||
script(type="text/template")#editorPanelTemplate
|
||||
#editorArea(style='display: none;')
|
||||
|
@ -440,10 +439,16 @@
|
|||
input(type="radio",name="toVersion").change-selector-to
|
||||
|
||||
div(class='change-description')
|
||||
div(class='change-date') {{date}}
|
||||
div(class='change-name')
|
||||
div.color-square(style="background-color: hsl({{hue}}, 100%, 70%);")
|
||||
span {{name}}
|
||||
div(class='change-date') {{date}}
|
||||
div {{{users}}}
|
||||
|
||||
div(class='restore')
|
||||
a(href="#") Restore to here
|
||||
|
||||
script(type='text/template')#changeListItemUserTemplate
|
||||
div(class='change-name')
|
||||
div.color-square(style="background-color: hsl({{hue}}, 100%, 70%);")
|
||||
span {{name}}
|
||||
|
||||
script(type='text/template')#changeListTemplate
|
||||
ul.change-list.nav.nav-pills.nav-stacked
|
||||
|
|
|
@ -224,7 +224,7 @@ define [
|
|||
document.on "externalUpdate", () =>
|
||||
Modal.createModal
|
||||
title: "Document Updated Externally"
|
||||
message: "This document was just updated externally (probably via Dropbox). Any recent changes you have made may have been overwritten. To see previous versions please look in the history."
|
||||
message: "This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history."
|
||||
buttons:[
|
||||
text: "Ok"
|
||||
]
|
||||
|
|
|
@ -144,6 +144,11 @@ define [
|
|||
# it's not the root folder so keep going
|
||||
path = entity.get("name") + "/" + path
|
||||
return path
|
||||
|
||||
getNameOfEntityId: (entity_id) ->
|
||||
entity = @getEntity(entity_id)
|
||||
return if !entity?
|
||||
return entity.get("name")
|
||||
|
||||
# RENAMING
|
||||
renameSelected: () ->
|
||||
|
|
|
@ -49,4 +49,7 @@ define [
|
|||
find: (id) ->
|
||||
@loadedModel ||= {}
|
||||
return @loadedModel[id]
|
||||
|
||||
getAnonymousUser: () ->
|
||||
return User.findOrBuild("anonymous", { first_name: "Anonymous", email: "anon@sharelatex.com" })
|
||||
}
|
||||
|
|
|
@ -143,7 +143,6 @@ define [
|
|||
@view.showLog()
|
||||
|
||||
if outputFiles?
|
||||
console.log "outputFiles", outputFiles
|
||||
@view.showOutputFileDownloadLinks(outputFiles)
|
||||
|
||||
fetchLogAndUpdateView: (pdfExists) ->
|
||||
|
|
|
@ -7,7 +7,7 @@ define [
|
|||
template: $("#changeListTemplate").html()
|
||||
|
||||
events:
|
||||
"scroll" : "loadUntilFull"
|
||||
"scroll" : () -> @loadUntilFull()
|
||||
|
||||
initialize: () ->
|
||||
@itemViews = []
|
||||
|
@ -39,20 +39,13 @@ define [
|
|||
view.$el.insertBefore(elementAtIndex)
|
||||
|
||||
view.on "click", (e, v) =>
|
||||
@selectedToIndex = index
|
||||
@selectedFromIndex = index
|
||||
@resetAllSelectors()
|
||||
@triggerChangeDiff()
|
||||
@setSelectionRange(index, index)
|
||||
|
||||
view.on "selected:to", (e, v) =>
|
||||
@selectedToIndex = index
|
||||
@resetAllSelectors()
|
||||
@triggerChangeDiff()
|
||||
@setSelectionRange(@selectedFromIndex, index)
|
||||
|
||||
view.on "selected:from", (e, v) =>
|
||||
@selectedFromIndex = index
|
||||
@resetAllSelectors()
|
||||
@triggerChangeDiff()
|
||||
@setSelectionRange(index, @selectedToIndex)
|
||||
|
||||
view.on "mouseenter:to", (e) =>
|
||||
@hoverToIndex = index
|
||||
|
@ -70,18 +63,27 @@ define [
|
|||
delete @hoverFromIndex
|
||||
@resetHoverStates()
|
||||
|
||||
view.on "click:restore", (e) =>
|
||||
@trigger "restore", view.model
|
||||
|
||||
view.resetSelector(index, @selectedFromIndex, @selectedToIndex)
|
||||
|
||||
setSelectionRange: (fromIndex, toIndex) ->
|
||||
@selectedFromIndex = fromIndex
|
||||
@selectedToIndex = toIndex
|
||||
@resetAllSelectors()
|
||||
@triggerChangeDiff()
|
||||
|
||||
resetAllSelectors: () ->
|
||||
for view, i in @itemViews
|
||||
view.resetSelector(i, @selectedFromIndex, @selectedToIndex)
|
||||
|
||||
resetHoverStates: () ->
|
||||
if @hoverToIndex?
|
||||
if @hoverToIndex? and @hoverToIndex != @selectedToIndex
|
||||
@$("ul").addClass("hover-state")
|
||||
for view, i in @itemViews
|
||||
view.resetHoverState(i, @selectedFromIndex, @hoverToIndex)
|
||||
else if @hoverFromIndex?
|
||||
else if @hoverFromIndex? and @hoverFromIndex != @selectedFromIndex
|
||||
@$("ul").addClass("hover-state")
|
||||
for view, i in @itemViews
|
||||
view.resetHoverState(i, @hoverFromIndex, @selectedToIndex)
|
||||
|
@ -99,23 +101,23 @@ define [
|
|||
atEndOfListView: ->
|
||||
@$el.scrollTop() + @$el.height() >= @$(".change-list").height() - 30
|
||||
|
||||
loadUntilFull: (e, callback) ->
|
||||
loadUntilFull: (callback = (error) ->) ->
|
||||
if (@listShorterThanContainer() or @atEndOfListView()) and not @atEndOfCollection and not @loading
|
||||
@showLoading()
|
||||
@hideEmptyMessage()
|
||||
@collection.fetchNextBatch
|
||||
error: =>
|
||||
error: (error) =>
|
||||
@hideLoading()
|
||||
@showEmptyMessageIfCollectionEmpty()
|
||||
callback() if callback?
|
||||
callback(error)
|
||||
success: (collection, response) =>
|
||||
@hideLoading()
|
||||
if response.updates.length == @collection.batchSize
|
||||
@loadUntilFull(e, callback)
|
||||
@loadUntilFull(callback)
|
||||
else
|
||||
@atEndOfCollection = true
|
||||
@showEmptyMessageIfCollectionEmpty()
|
||||
callback() if callback?
|
||||
callback()
|
||||
|
||||
else
|
||||
callback() if callback?
|
||||
|
@ -152,26 +154,29 @@ define [
|
|||
@trigger "mouseenter:from", args...
|
||||
"mouseleave .change-selector-from": (args...) ->
|
||||
@trigger "mouseleave:from", args...
|
||||
"click .restore a": "onRestoreClick"
|
||||
|
||||
|
||||
template : $("#changeListItemTemplate").html()
|
||||
templates:
|
||||
item: $("#changeListItemTemplate").html()
|
||||
user: $("#changeListItemUserTemplate").html()
|
||||
|
||||
initialize: ->
|
||||
@render()
|
||||
|
||||
render: ->
|
||||
@$el.html Mustache.to_html(@template, @modelView())
|
||||
return this
|
||||
|
||||
modelView: ->
|
||||
modelView = {
|
||||
hue: @model.get("user").hue()
|
||||
userHtml = for user in @model.get("users")
|
||||
Mustache.to_html @templates.user, {
|
||||
hue: user.hue()
|
||||
name: user.name()
|
||||
}
|
||||
data = {
|
||||
date: moment(parseInt(@model.get("end_ts"), 10)).calendar()
|
||||
name: @model.get("user").name()
|
||||
users: userHtml.join("")
|
||||
}
|
||||
# modelView.start_ts = util.formatDate(modelView.start_ts)
|
||||
# modelView.end_ts = util.formatDate(modelView.end_ts)
|
||||
return modelView
|
||||
|
||||
@$el.html Mustache.to_html(@templates.item, data)
|
||||
return this
|
||||
|
||||
onClick: (e) ->
|
||||
e.preventDefault()
|
||||
|
@ -183,6 +188,10 @@ define [
|
|||
onFromSelectorClick: (e) ->
|
||||
@trigger "selected:from", e, @
|
||||
|
||||
onRestoreClick: (e) ->
|
||||
e.preventDefault()
|
||||
@trigger "click:restore", e, @
|
||||
|
||||
isSelectedFrom: () ->
|
||||
@$(".change-selector-from").is(":checked")
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@ define [
|
|||
"ace/ace"
|
||||
"ace/mode/latex"
|
||||
"ace/range"
|
||||
"moment"
|
||||
"libs/backbone"
|
||||
], (Ace, LatexMode, Range)->
|
||||
], (Ace, LatexMode, Range, moment)->
|
||||
DiffView = Backbone.View.extend
|
||||
initialize: () ->
|
||||
@model.on "change:diff", () => @render()
|
||||
|
@ -19,6 +20,7 @@ define [
|
|||
session.setMode(new LatexMode.Mode())
|
||||
session.setUseWrapMode(true)
|
||||
@insertMarkers()
|
||||
@insertNameTag()
|
||||
return @
|
||||
|
||||
createAceEditor: () ->
|
||||
|
@ -44,6 +46,7 @@ define [
|
|||
insertMarkers: () ->
|
||||
row = 0
|
||||
column = 0
|
||||
@entries = []
|
||||
for entry, i in @model.get("diff") or []
|
||||
content = entry.u or entry.i or entry.d
|
||||
content ||= ""
|
||||
|
@ -62,9 +65,12 @@ define [
|
|||
range = new Range.Range(
|
||||
startRow, startColumn, endRow, endColumn
|
||||
)
|
||||
@addMarker(range, "change-marker-#{i}", entry)
|
||||
entry.range = range
|
||||
@addMarker(range, entry)
|
||||
if entry.i? or entry.d?
|
||||
@entries.push entry
|
||||
|
||||
addMarker: (range, id, entry) ->
|
||||
addMarker: (range, entry) ->
|
||||
session = @aceEditor.getSession()
|
||||
markerBackLayer = @aceEditor.renderer.$markerBack
|
||||
markerFrontLayer = @aceEditor.renderer.$markerFront
|
||||
|
@ -72,10 +78,9 @@ define [
|
|||
if entry.i? or entry.d?
|
||||
hue = entry.meta.user.hue()
|
||||
if entry.i?
|
||||
@_addMarkerWithCustomStyle session, markerBackLayer, range, "deleted-change-background", false, """
|
||||
@_addMarkerWithCustomStyle session, markerBackLayer, range, "inserted-change-background", false, """
|
||||
background-color : hsl(#{hue}, 70%, 85%);
|
||||
"""
|
||||
tag = "Added by #{entry.meta.user.name()}"
|
||||
if entry.d?
|
||||
@_addMarkerWithCustomStyle session, markerBackLayer, range, "deleted-change-background", false, """
|
||||
background-color : hsl(#{hue}, 70%, 95%);
|
||||
|
@ -84,13 +89,6 @@ define [
|
|||
height: #{Math.round(lineHeight/2) - 1}px;
|
||||
border-bottom: 2px solid hsl(#{hue}, 70%, 40%);
|
||||
"""
|
||||
tag = "Deleted by #{entry.meta.user.name()}"
|
||||
|
||||
date = moment(parseInt(entry.meta.end_ts, 10)).format("Do MMM YYYY, h:mm:ss a")
|
||||
tag += " on #{date}"
|
||||
@_addNameTag session, id, range, tag, """
|
||||
background-color : hsl(#{hue}, 70%, 95%);
|
||||
"""
|
||||
|
||||
_addMarkerWithCustomStyle: (session, markerLayer, range, klass, foreground, style) ->
|
||||
session.addMarker range, klass, (html, range, left, top, config) ->
|
||||
|
@ -100,42 +98,65 @@ define [
|
|||
markerLayer.drawSingleLineMarker(html, range, "#{klass} ace_start", config, 0, style)
|
||||
, foreground
|
||||
|
||||
_addNameTag: (session, id, range, content, style) ->
|
||||
@nameMarkers ||= []
|
||||
@nameMarkers.push
|
||||
range: range
|
||||
id: id
|
||||
startRange = new Range.Range(
|
||||
range.start.row, range.start.column
|
||||
range.start.row, range.start.column + 1
|
||||
)
|
||||
session.addMarker startRange, "change-name-marker", (html, range, left, top, config) ->
|
||||
html.push """
|
||||
<div
|
||||
id = '#{id}'
|
||||
class = 'change-name-marker'
|
||||
style = '
|
||||
height: #{config.lineHeight}px;
|
||||
top: #{top}px;
|
||||
left: #{left}px;
|
||||
'
|
||||
>
|
||||
<div
|
||||
class="name" style="
|
||||
display: none;
|
||||
bottom: #{config.lineHeight + 3}px;
|
||||
#{style}
|
||||
">#{content}</div>
|
||||
</div>
|
||||
"""
|
||||
, true
|
||||
insertNameTag: () ->
|
||||
@$ace = $(@aceEditor.renderer.container).find(".ace_scroller")
|
||||
@$nameTagEl = $("<div class='change-name-marker'></div>")
|
||||
@$nameTagEl.css({
|
||||
position: "absolute"
|
||||
})
|
||||
@$nameTagEl.hide()
|
||||
@$ace.append(@$nameTagEl)
|
||||
|
||||
_drawNameTag: (entry, position) ->
|
||||
@$nameTagEl.show()
|
||||
|
||||
if entry.i?
|
||||
text = "Added by #{entry.meta.user.name()}"
|
||||
else if entry.d?
|
||||
text = "Deleted by #{entry.meta.user.name()}"
|
||||
date = moment(parseInt(entry.meta.end_ts, 10)).format("Do MMM YYYY, h:mm a")
|
||||
text += " on #{date}"
|
||||
@$nameTagEl.text(text)
|
||||
|
||||
position = @aceEditor.renderer.textToScreenCoordinates(position.row, position.column)
|
||||
offset = @$ace.offset()
|
||||
position.pageX = position.pageX - offset.left
|
||||
position.pageY = position.pageY - offset.top
|
||||
height = @$ace.height()
|
||||
|
||||
hue = entry.meta.user.hue()
|
||||
css = {
|
||||
"background-color" : "hsl(#{hue}, 70%, 90%)";
|
||||
}
|
||||
|
||||
if position.pageX + @$nameTagEl.width() < @$ace.width()
|
||||
css["left"] = position.pageX
|
||||
css["right"] = "auto"
|
||||
else
|
||||
css["right"] = 0
|
||||
css["left"] = "auto"
|
||||
|
||||
if position.pageY > 2 * @$nameTagEl.height()
|
||||
css["bottom"] = height - position.pageY
|
||||
css["top"] = "auto"
|
||||
else
|
||||
css["top"] = position.pageY + @aceEditor.renderer.lineHeight
|
||||
css["bottom"] = "auto"
|
||||
|
||||
@$nameTagEl.css css
|
||||
|
||||
_hideNameTag: () ->
|
||||
@$nameTagEl.hide()
|
||||
|
||||
updateVisibleNames: (e) ->
|
||||
for marker in @nameMarkers or []
|
||||
if marker.range.contains(e.position.row, e.position.column)
|
||||
$("##{marker.id}").find(".name").show()
|
||||
else
|
||||
$("##{marker.id}").find(".name").hide()
|
||||
visibleName = false
|
||||
for entry in @entries or []
|
||||
if entry.range.contains(e.position.row, e.position.column)
|
||||
@_drawNameTag(entry, e.position)
|
||||
visibleName = true
|
||||
break
|
||||
if !visibleName
|
||||
@_hideNameTag()
|
||||
|
||||
return DiffView
|
||||
|
||||
|
|
|
@ -3,21 +3,23 @@ define [
|
|||
"track-changes/models/Diff"
|
||||
"track-changes/ChangeListView"
|
||||
"track-changes/DiffView"
|
||||
], (ChangeList, Diff, ChangeListView, DiffView) ->
|
||||
"utils/Modal"
|
||||
"moment"
|
||||
], (ChangeList, Diff, ChangeListView, DiffView, Modal, moment) ->
|
||||
class TrackChangesManager
|
||||
template: $("#trackChangesPanelTemplate").html()
|
||||
|
||||
constructor: (@ide) ->
|
||||
@$el = $(@template)
|
||||
$("#editorWrapper").append(@$el)
|
||||
@hideEl()
|
||||
@hide()
|
||||
|
||||
@ide.editor.on "change:doc", () =>
|
||||
@hideEl()
|
||||
@hide()
|
||||
|
||||
@$el.find(".track-changes-close").on "click", (e) =>
|
||||
e.preventDefault
|
||||
@hideEl()
|
||||
@hide()
|
||||
|
||||
show: () ->
|
||||
@project_id = window.userSettings.project_id
|
||||
|
@ -29,29 +31,89 @@ define [
|
|||
el : @$el.find(".change-list-area")
|
||||
)
|
||||
@changeListView.render()
|
||||
@changeListView.loadUntilFull()
|
||||
@changeListView.loadUntilFull (error) =>
|
||||
@autoSelectDiff()
|
||||
|
||||
@changeListView.on "change_diff", (fromModel, toModel) =>
|
||||
@diff = new Diff({
|
||||
project_id: @project_id
|
||||
doc_id: @doc_id
|
||||
from: fromModel.get("version")
|
||||
to: toModel.get("version")
|
||||
})
|
||||
@diffView = new DiffView(
|
||||
model: @diff
|
||||
el: @$el.find(".track-changes-diff")
|
||||
)
|
||||
@diff.fetch()
|
||||
@showDiff(fromModel, toModel)
|
||||
|
||||
@changeListView.on "restore", (change) =>
|
||||
@restore(change)
|
||||
|
||||
@showEl()
|
||||
|
||||
autoSelectDiff: () ->
|
||||
if @changes.models.length == 0
|
||||
return
|
||||
|
||||
# Find all change until the last one we made
|
||||
fromIndex = null
|
||||
for change, i in @changes.models
|
||||
if ide.user in change.get("users")
|
||||
if i > 0
|
||||
fromIndex = i - 1
|
||||
else
|
||||
fromIndex = 0
|
||||
break
|
||||
fromIndex = 0 if !fromIndex
|
||||
|
||||
toChange = @changes.models[0]
|
||||
fromChange = @changes.models[fromIndex]
|
||||
@showDiff(fromChange, toChange)
|
||||
@changeListView.setSelectionRange(fromIndex, 0)
|
||||
|
||||
showDiff: (fromModel, toModel) ->
|
||||
@diff = new Diff({
|
||||
project_id: @project_id
|
||||
doc_id: @doc_id
|
||||
from: fromModel.get("fromVersion")
|
||||
to: toModel.get("toVersion")
|
||||
})
|
||||
@diffView = new DiffView(
|
||||
model: @diff
|
||||
el: @$el.find(".track-changes-diff")
|
||||
)
|
||||
@diff.fetch()
|
||||
|
||||
showEl: ->
|
||||
@ide.editor.hide()
|
||||
@$el.show()
|
||||
|
||||
hideEl: () ->
|
||||
hide: () ->
|
||||
@ide.editor.show()
|
||||
@$el.hide()
|
||||
|
||||
restore: (change) ->
|
||||
name = @ide.fileTreeManager.getNameOfEntityId(@doc_id)
|
||||
date = moment(change.get("start_ts")).format("Do MMM YYYY, h:mm:ss a")
|
||||
modal = new Modal({
|
||||
title: "Restore document"
|
||||
message: "Are you sure you want to restore <strong>#{name}</strong> to before the changes on #{date}?"
|
||||
buttons: [{
|
||||
text: "Cancel"
|
||||
}, {
|
||||
text: "Restore"
|
||||
class: "btn-success"
|
||||
close: false
|
||||
callback: ($button) =>
|
||||
$button.text("Restoring...")
|
||||
$button.prop("disabled", true)
|
||||
@doRestore change.get("version"), (error) =>
|
||||
modal.remove()
|
||||
@hide()
|
||||
}]
|
||||
})
|
||||
|
||||
doRestore: (version, callback = (error) ->) ->
|
||||
$.ajax {
|
||||
url: "/project/#{@project_id}/doc/#{@doc_id}/version/#{version}/restore"
|
||||
type: "POST"
|
||||
headers:
|
||||
"X-CSRF-Token": window.csrfToken
|
||||
success: () ->
|
||||
callback()
|
||||
error: (error) ->
|
||||
callback(error)
|
||||
}
|
||||
|
||||
return TrackChangesManager
|
||||
|
|
|
@ -4,9 +4,15 @@ define [
|
|||
], (User)->
|
||||
Change = Backbone.Model.extend
|
||||
parse: (change) ->
|
||||
return {
|
||||
model = {
|
||||
start_ts: change.meta.start_ts
|
||||
end_ts: change.meta.end_ts
|
||||
user: User.findOrBuild(change.meta.user.id, change.meta.user)
|
||||
version: change.v
|
||||
}
|
||||
fromVersion: change.fromV
|
||||
toVersion: change.toV
|
||||
}
|
||||
model.users = []
|
||||
for user in change.meta.users or []
|
||||
model.users.push User.findOrBuild(user.id, user)
|
||||
if model.users.length == 0
|
||||
model.users.push User.getAnonymousUser()
|
||||
return model
|
|
@ -12,7 +12,7 @@ define [
|
|||
url = "/project/#{@options.project_id}/doc/#{@options.doc_id}/updates?limit=#{@batchSize}"
|
||||
if @models.length > 0
|
||||
last = @models[@models.length - 1]
|
||||
url += "&to=#{last.get("version") - 1}"
|
||||
url += "&to=#{last.get("fromVersion") - 1}"
|
||||
return url
|
||||
|
||||
parse: (json) ->
|
||||
|
|
|
@ -8,6 +8,9 @@ define [
|
|||
|
||||
parse: (diff) ->
|
||||
for entry in diff.diff
|
||||
if entry.meta? and entry.meta.user?
|
||||
entry.meta.user = User.findOrBuild(entry.meta.user.id, entry.meta.user)
|
||||
if entry.meta?
|
||||
if entry.meta.user?
|
||||
entry.meta.user = User.findOrBuild(entry.meta.user.id, entry.meta.user)
|
||||
else
|
||||
entry.meta.user = User.getAnonymousUser()
|
||||
return diff
|
||||
|
|
|
@ -19,8 +19,9 @@ define [
|
|||
button.on "click", (e) ->
|
||||
e.preventDefault()
|
||||
if buttonOptions.callback?
|
||||
buttonOptions.callback()
|
||||
self.remove()
|
||||
buttonOptions.callback(button)
|
||||
if !buttonOptions.close? or buttonOptions.close
|
||||
self.remove()
|
||||
|
||||
@$el.modal
|
||||
# make sure we control when the modal is hidden
|
||||
|
@ -39,7 +40,6 @@ define [
|
|||
|
||||
@$el.find('input').focus()
|
||||
|
||||
|
||||
remove: () ->
|
||||
@$el.modal("hide")
|
||||
Backbone.View.prototype.remove.call(this)
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
.ace_active-line, .ace_cursor-layer, .ace_gutter-active-line {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +31,7 @@
|
|||
background-color: white;
|
||||
|
||||
.track-changes-header {
|
||||
background-color: black;
|
||||
background-color: #282828;
|
||||
h3 {
|
||||
color: white;
|
||||
padding-left: 8px;
|
||||
|
@ -53,29 +56,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
.deleted-change-background, .deleted-change-foreground, .inserted-change, .change-name-marker {
|
||||
.deleted-change-background, .deleted-change-foreground, .inserted-change-background, .change-name-marker {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.change-name-marker {
|
||||
.name {
|
||||
font-size: 0.8em;
|
||||
padding: 2px 6px;
|
||||
.border-radius(3px 3px 3px 3px);
|
||||
position: absolute;
|
||||
border: 1px solid #999;
|
||||
left: 0;
|
||||
}
|
||||
font-size: 0.8em;
|
||||
padding: 2px 6px;
|
||||
.border-radius(3px 3px 3px 3px);
|
||||
position: absolute;
|
||||
border: 1px solid #999;
|
||||
left: 0;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
ul.change-list {
|
||||
li {
|
||||
padding: 6px 4px;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #ccc;
|
||||
cursor: pointer;
|
||||
min-height: 38px;
|
||||
.change-selectors {
|
||||
.change-selector-from {
|
||||
position: absolute;
|
||||
|
@ -98,7 +98,8 @@
|
|||
}
|
||||
}
|
||||
.change-description {
|
||||
padding-left: 26px;
|
||||
padding: 6px 4px 6px 30px;
|
||||
min-height: 38px;
|
||||
}
|
||||
.change-name {
|
||||
font-size: 0.9em;
|
||||
|
@ -112,10 +113,22 @@
|
|||
margin-right: 4px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.restore {
|
||||
a {
|
||||
display: block;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
}
|
||||
li.loading-changes, li.empty-message {
|
||||
padding: 6px;
|
||||
}
|
||||
li.selected {
|
||||
background-color: #eaeaea;
|
||||
.change-selectors {
|
||||
|
@ -137,12 +150,16 @@
|
|||
li.selected-from {
|
||||
.change-selectors {
|
||||
.range {
|
||||
bottom: 10px;
|
||||
bottom: 37px;
|
||||
}
|
||||
.change-selector-from {
|
||||
opacity: 1;
|
||||
bottom: 32px;
|
||||
}
|
||||
}
|
||||
.restore {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul.change-list.hover-state {
|
||||
|
@ -182,5 +199,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
li.selected-from.hover-selected-from {
|
||||
.change-selectors {
|
||||
.range {
|
||||
bottom: 37px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,15 +7,17 @@ SandboxedModule = require('sandboxed-module')
|
|||
describe "TrackChangesController", ->
|
||||
beforeEach ->
|
||||
@TrackChangesController = SandboxedModule.require modulePath, requires:
|
||||
"request" : @request = {}
|
||||
"request" : @request = sinon.stub()
|
||||
"settings-sharelatex": @settings = {}
|
||||
"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()}
|
||||
"../Authentication/AuthenticationController": @AuthenticationController = {}
|
||||
|
||||
describe "proxyToTrackChangesApi", ->
|
||||
beforeEach ->
|
||||
@req = { url: "/mock/url" }
|
||||
@req = { url: "/mock/url", method: "POST" }
|
||||
@res = "mock-res"
|
||||
@next = sinon.stub()
|
||||
@user_id = "user-id-123"
|
||||
@settings.apis =
|
||||
trackchanges:
|
||||
url: "http://trackchanges.example.com"
|
||||
|
@ -23,13 +25,24 @@ describe "TrackChangesController", ->
|
|||
events: {}
|
||||
pipe: sinon.stub()
|
||||
on: (event, handler) -> @events[event] = handler
|
||||
@request.get = sinon.stub().returns @proxy
|
||||
@request.returns @proxy
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id)
|
||||
@TrackChangesController.proxyToTrackChangesApi @req, @res, @next
|
||||
|
||||
describe "successfully", ->
|
||||
it "should get the user id", ->
|
||||
@AuthenticationController.getLoggedInUserId
|
||||
.calledWith(@req)
|
||||
.should.equal true
|
||||
|
||||
it "should call the track changes api", ->
|
||||
@request.get
|
||||
.calledWith("#{@settings.apis.trackchanges.url}#{@req.url}")
|
||||
@request
|
||||
.calledWith({
|
||||
url: "#{@settings.apis.trackchanges.url}#{@req.url}"
|
||||
method: @req.method
|
||||
headers:
|
||||
"X-User-Id": @user_id
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should pipe the response to the client", ->
|
||||
|
|
Loading…
Add table
Reference in a new issue