mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-05 07:20:08 +00:00
Add in creating of files to editor
This commit is contained in:
parent
6d0053e709
commit
f41534ee4d
11 changed files with 203 additions and 28 deletions
|
@ -163,7 +163,7 @@ module.exports = EditorController =
|
|||
logger.log sl_req_id:sl_req_id, "sending new doc to project #{project_id}"
|
||||
Metrics.inc "editor.add-doc"
|
||||
ProjectEntityHandler.addDoc project_id, folder_id, docName, docLines, sl_req_id, (err, doc, folder_id)=>
|
||||
EditorRealTimeController.emitToRoom(project_id, 'reciveNewDoc', folder_id, doc)
|
||||
EditorRealTimeController.emitToRoom(project_id, 'docCreated', folder_id, doc)
|
||||
callback(err, doc)
|
||||
|
||||
addFile: (project_id, folder_id, fileName, path, sl_req_id, callback = (error, file)->)->
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
ProjectEntityHandler = require "../Project/ProjectEntityHandler"
|
||||
logger = require "logger-sharelatex"
|
||||
EditorRealTimeController = require "./EditorRealTimeController"
|
||||
EditorController = require "./EditorController"
|
||||
|
||||
module.exports = EditorHttpController =
|
||||
restoreDoc: (req, res, next) ->
|
||||
|
@ -19,3 +20,10 @@ module.exports = EditorHttpController =
|
|||
doc_id: doc._id
|
||||
}
|
||||
|
||||
addDoc: (req, res, next) ->
|
||||
project_id = req.params.Project_id
|
||||
name = req.body.name
|
||||
parent_folder_id = req.body.parent_folder_id
|
||||
EditorController.addDoc project_id, parent_folder_id, name, [], (error, doc) ->
|
||||
return next(error) if error?
|
||||
res.json doc
|
||||
|
|
|
@ -87,6 +87,8 @@ module.exports = class Router
|
|||
app.get '/Project/:Project_id', SecurityManager.requestCanAccessProject, ProjectController.loadEditor
|
||||
app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
|
||||
|
||||
app.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc
|
||||
|
||||
app.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
||||
app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
||||
app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
||||
|
|
|
@ -40,9 +40,14 @@ block content
|
|||
i.fa.fa-comment
|
||||
|
||||
#editor-content(ng-cloak, layout="main", ng-hide="state.loading")
|
||||
aside#file-tree.ui-layout-west
|
||||
aside#file-tree.ui-layout-west(ng-controller="FileTreeController")
|
||||
.toolbar.toolbar-small
|
||||
a(href, tooltip-html-unsafe="New<br>File", tooltip-placement="bottom")
|
||||
a(
|
||||
href,
|
||||
ng-click="openNewDocModal()",
|
||||
tooltip-html-unsafe="New<br>File",
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-file
|
||||
a(href, tooltip="New Folder", tooltip-placement="bottom")
|
||||
i.fa.fa-folder
|
||||
|
@ -58,7 +63,7 @@ block content
|
|||
ul.list-unstyled.file-tree-list
|
||||
file-entity(
|
||||
entity="entity",
|
||||
ng-repeat="entity in rootFolder.children"
|
||||
ng-repeat="entity in rootFolder.children | orderBy:'name'"
|
||||
)
|
||||
.ui-layout-center
|
||||
|
||||
|
@ -97,8 +102,32 @@ block content
|
|||
span(ng-click="select()") {{ entity.name }}
|
||||
|
||||
ul.list-unstyled(ng-show="expanded")
|
||||
file-entity(entity="child", ng-repeat="child in entity.children")
|
||||
file-entity(entity="child", ng-repeat="child in entity.children | orderBy:'name'")
|
||||
|
||||
script(type='text/ng-template', id='newDocModalTemplate')
|
||||
.modal-header
|
||||
h3 New File
|
||||
.modal-body
|
||||
form(novalidate, name="newDocForm")
|
||||
input.form-control(
|
||||
type="text",
|
||||
placeholder="File Name",
|
||||
required,
|
||||
ng-model="inputs.name",
|
||||
ng-enter="create()",
|
||||
focus-on="open"
|
||||
)
|
||||
.modal-footer
|
||||
button.btn.btn-default(
|
||||
ng-disabled="state.inflight"
|
||||
ng-click="cancel()"
|
||||
) Cancel
|
||||
button.btn.btn-primary(
|
||||
ng-disabled="newDocForm.$invalid || state.inflight"
|
||||
ng-click="create()"
|
||||
)
|
||||
span(ng-hide="state.inflight") Create
|
||||
span(ng-show="state.inflight") Creating...
|
||||
|
||||
//- #loadingScreen
|
||||
//- h3 Loading...
|
||||
|
|
|
@ -2,35 +2,35 @@ define [
|
|||
"base"
|
||||
"ide/file-tree/FileTreeManager"
|
||||
"ide/directives/layout"
|
||||
"ide/services/ide"
|
||||
"directives/focusOn"
|
||||
], (
|
||||
App
|
||||
FileTreeManager
|
||||
) ->
|
||||
App.controller "IdeController", ["$scope", "$timeout", ($scope, $timeout) ->
|
||||
App.controller "IdeController", ["$scope", "$timeout", "ide", ($scope, $timeout, ide) ->
|
||||
$scope.state = {
|
||||
loading: true
|
||||
load_progress: 40
|
||||
}
|
||||
|
||||
window.ide = ide = {
|
||||
'$scope': $scope
|
||||
}
|
||||
window._ide = ide
|
||||
|
||||
ide.project_id = window.project_id
|
||||
ide.$scope = $scope
|
||||
ide.socket = io.connect null,
|
||||
reconnect: false
|
||||
"force new connection": true
|
||||
|
||||
ide.fileTreeManager = new FileTreeManager(ide, $scope)
|
||||
|
||||
$scope.project_id = window.project_id
|
||||
|
||||
ioOptions =
|
||||
reconnect: false
|
||||
"force new connection": true
|
||||
$scope.socket = io.connect null, ioOptions
|
||||
|
||||
$scope.socket.on "connect", () ->
|
||||
ide.socket.on "connect", () ->
|
||||
$scope.$apply () ->
|
||||
$scope.state.load_progress = 80
|
||||
|
||||
joinProject = () =>
|
||||
$scope.socket.emit 'joinProject', {
|
||||
project_id: $scope.project_id
|
||||
ide.socket.emit 'joinProject', {
|
||||
project_id: ide.project_id
|
||||
}, (err, project, permissionsLevel, protocolVersion) =>
|
||||
if $scope.protocolVersion? and $scope.protocolVersion != protocolVersion
|
||||
location.reload(true)
|
||||
|
@ -43,8 +43,6 @@ define [
|
|||
|
||||
$scope.$emit "project:joined"
|
||||
|
||||
console.log "Project", $scope.project, $scope.rootFolder
|
||||
|
||||
setTimeout(joinProject, 100) ]
|
||||
|
||||
angular.bootstrap(document.body, ["SharelatexApp"])
|
|
@ -1,30 +1,61 @@
|
|||
define [
|
||||
"ide/file-tree/directives/fileEntity"
|
||||
"ide/file-tree/controllers/FileTreeController"
|
||||
"ide/file-tree/controllers/FileTreeFolderController"
|
||||
"ide/file-tree/controllers/FileTreeEntityController"
|
||||
], () ->
|
||||
class FileTreeManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
@$scope.$on "project:joined", =>
|
||||
console.log "Joined"
|
||||
@loadRootFolder()
|
||||
|
||||
@_bindToSocketEvents()
|
||||
|
||||
_bindToSocketEvents: () ->
|
||||
@ide.socket.on "docCreated", (parent_folder_id, doc) =>
|
||||
console.log "Doc created", parent_folder_id, doc
|
||||
parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder
|
||||
@$scope.$apply () ->
|
||||
parent_folder.children.push {
|
||||
name: doc.name
|
||||
id: doc._id
|
||||
type: "doc"
|
||||
}
|
||||
|
||||
findEntityById: (id) ->
|
||||
@_findEntityByIdInFolder @$scope.rootFolder, id
|
||||
|
||||
_findEntityByIdInFolder: (folder, id) ->
|
||||
for entity in folder.children or []
|
||||
if entity.id == id
|
||||
return entity
|
||||
else if entity.children?
|
||||
result = @_findEntityByIdInFolder(entity, id)
|
||||
return result if result?
|
||||
|
||||
return null
|
||||
|
||||
forEachEntity: (callback) ->
|
||||
@_forEachEntityInFolder(@$scope.rootFolder, callback)
|
||||
|
||||
_forEachEntityInFolder: (folder, callback) ->
|
||||
for entity in folder.children
|
||||
for entity in folder.children or []
|
||||
callback(entity)
|
||||
if entity.children?
|
||||
@_forEachEntityInFolder(entity, callback)
|
||||
|
||||
# forEachFolder: (callback) ->
|
||||
# @forEachEntity (entity) ->
|
||||
# if entity.type == "folder"
|
||||
# callback(entity)
|
||||
|
||||
loadRootFolder: () ->
|
||||
@$scope.rootFolder = @_parseFolder(@$scope.project.rootFolder[0])
|
||||
|
||||
_parseFolder: (rawFolder) ->
|
||||
folder = {
|
||||
name: rawFolder.name
|
||||
id: rawFolder.id
|
||||
id: rawFolder._id
|
||||
type: "folder"
|
||||
children: []
|
||||
}
|
||||
|
@ -32,18 +63,54 @@ define [
|
|||
for doc in rawFolder.docs or []
|
||||
folder.children.push {
|
||||
name: doc.name
|
||||
type: "doc"
|
||||
id: doc._id
|
||||
type: "doc"
|
||||
}
|
||||
|
||||
for file in rawFolder.fileRefs or []
|
||||
folder.children.push {
|
||||
name: file.name
|
||||
type: "file"
|
||||
id: file._id
|
||||
type: "file"
|
||||
}
|
||||
|
||||
for childFolder in rawFolder.folders or []
|
||||
folder.children.push @_parseFolder(childFolder)
|
||||
|
||||
return folder
|
||||
|
||||
getCurrentFolder: (startFolder = @$scope.rootFolder) ->
|
||||
for entity in startFolder.children or []
|
||||
# The 'current' folder is either the one selected, or
|
||||
# the one containing the selected doc/file
|
||||
if entity.selected
|
||||
if entity.type == "folder"
|
||||
return entity
|
||||
else
|
||||
return startFolder
|
||||
|
||||
if entity.type == "folder"
|
||||
result = @getCurrentFolder(entity)
|
||||
return result if result?
|
||||
|
||||
return null
|
||||
|
||||
createDocInCurrentFolder: (name, callback = (error, doc) ->) ->
|
||||
# We'll wait for the socket.io 'createDoc' notification to actually
|
||||
# add the doc for us.
|
||||
parent_folder = @getCurrentFolder()
|
||||
$.ajax {
|
||||
url: "/project/#{@ide.project_id}/doc"
|
||||
type: "POST"
|
||||
contentType: "application/json; charset=utf-8"
|
||||
data: JSON.stringify {
|
||||
name: name,
|
||||
parent_folder_id: parent_folder?.id
|
||||
_csrf: window.csrfToken
|
||||
}
|
||||
dataType: "json"
|
||||
success: (doc) ->
|
||||
console.log "Got doc from POST", doc
|
||||
callback(null, doc)
|
||||
failure: (error) -> callback(error)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "FileTreeController", ["$scope", "$modal", ($scope, $modal) ->
|
||||
$scope.openNewDocModal = () ->
|
||||
$modal.open(
|
||||
templateUrl: "newDocModalTemplate"
|
||||
controller: "NewDocModalController"
|
||||
)
|
||||
]
|
||||
|
||||
App.controller "NewDocModalController", [
|
||||
"$scope", "ide", "$modalInstance", "$timeout",
|
||||
($scope, ide, $modalInstance, $timeout) ->
|
||||
$scope.inputs =
|
||||
name: "name.tex"
|
||||
$scope.state =
|
||||
inflight: false
|
||||
|
||||
$modalInstance.opened.then () ->
|
||||
$timeout () ->
|
||||
$scope.$broadcast "open"
|
||||
, 700
|
||||
|
||||
$scope.create = () ->
|
||||
$scope.state.inflight = true
|
||||
ide.fileTreeManager.createDocInCurrentFolder $scope.inputs.name, (error, doc) ->
|
||||
$scope.state.inflight = false
|
||||
$modalInstance.close()
|
||||
|
||||
$scope.cancel = () ->
|
||||
$modalInstance.dismiss('cancel')
|
||||
]
|
|
@ -1,7 +1,7 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "FileTreeEntityController", ["$scope", ($scope) ->
|
||||
App.controller "FileTreeEntityController", ["$scope", "ide", ($scope, ide) ->
|
||||
$scope.select = ($event) ->
|
||||
ide.fileTreeManager.forEachEntity (entity) ->
|
||||
entity.selected = false
|
||||
|
|
7
services/web/public/coffee/app/ide/services/ide.coffee
Normal file
7
services/web/public/coffee/app/ide/services/ide.coffee
Normal file
|
@ -0,0 +1,7 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
# We create and provide this as service so that we can access the global ide
|
||||
# from within other parts of the angular app.
|
||||
App.factory "ide", () ->
|
||||
return {}
|
|
@ -71,7 +71,14 @@
|
|||
|
||||
ul.file-tree-list {
|
||||
font-size: 0.8rem;
|
||||
margin-top: (@line-height-computed / 4);
|
||||
margin: 0;
|
||||
padding: (@line-height-computed / 4) 0;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: scroll;
|
||||
|
||||
ul {
|
||||
margin-left: (@line-height-computed / 2);
|
||||
|
|
|
@ -9,8 +9,10 @@ describe "EditorHttpController", ->
|
|||
'../Project/ProjectEntityHandler' : @ProjectEntityHandler = {}
|
||||
"./EditorRealTimeController": @EditorRealTimeController = {}
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||
"./EditorController": @EditorController = {}
|
||||
@project_id = "mock-project-id"
|
||||
@doc_id = "mock-doc-id"
|
||||
@parent_folder_id = "mock-folder-id"
|
||||
@req = {}
|
||||
@res =
|
||||
send: sinon.stub()
|
||||
|
@ -44,3 +46,25 @@ describe "EditorHttpController", ->
|
|||
@res.json
|
||||
.calledWith(doc_id: @new_doc_id)
|
||||
.should.equal true
|
||||
|
||||
describe "addDoc", ->
|
||||
beforeEach ->
|
||||
@doc = { "mock": "doc" }
|
||||
@req.params =
|
||||
Project_id: @project_id
|
||||
@req.body =
|
||||
name: @name = "doc-name"
|
||||
parent_folder_id: @parent_folder_id
|
||||
@EditorController.addDoc = sinon.stub().callsArgWith(4, null, @doc)
|
||||
@EditorHttpController.addDoc @req, @res
|
||||
|
||||
it "should call EditorController.addDoc", ->
|
||||
@EditorController.addDoc
|
||||
.calledWith(@project_id, @parent_folder_id, @name, [])
|
||||
.should.equal true
|
||||
|
||||
it "should send the doc back as JSON", ->
|
||||
@res.json
|
||||
.calledWith(@doc)
|
||||
.should.equal true
|
||||
|
||||
|
|
Loading…
Reference in a new issue