This commit is contained in:
Henry Oswald
2015-02-10 18:05:51 +00:00
44 changed files with 30 additions and 1700 deletions
@@ -1,81 +0,0 @@
request = require('request')
settings = require('settings-sharelatex')
logger = require('logger-sharelatex')
Project = require('../../models/Project').Project
projectEntityHandler = require '../Project/ProjectEntityHandler'
_ = require('underscore')
async = require('async')
module.exports =
getUserRegistrationStatus: (user_id, callback)->
logger.log user_id:user_id, "getting dropbox registration status from tpds"
opts =
url : "#{settings.apis.thirdPartyDataStore.url}/user/#{user_id}/dropbox/status"
timeout: 5000
request.get opts, (err, response, body)->
safelyGetResponse err, response, body, (err, body)->
if err?
logger.err err:err, response:response, "getUserRegistrationStatus problem"
return callback err
logger.log status:body, "getting dropbox registration status for user #{user_id}"
callback err, body
getDropboxRegisterUrl: (user_id, callback)->
opts =
url: "#{settings.apis.thirdPartyDataStore.url}/user/#{user_id}/dropbox/register"
timeout: 5000
request.get opts, (err, response, body)->
safelyGetResponse err, response, body, (err, body)->
if err?
logger.err err:err, response:response, "getUserRegistrationStatus problem"
return callback err
url = "#{body.authorize_url}&oauth_callback=#{settings.siteUrl}/dropbox/completeRegistration"
logger.log user_id:user_id, url:url, "starting dropbox register"
callback err, url
completeRegistration: (user_id, callback)->
opts =
url: "#{settings.apis.thirdPartyDataStore.url}/user/#{user_id}/dropbox/getaccesstoken"
timeout: 5000
request.get opts, (err, response, body)=>
safelyGetResponse err, response, body, (err, body)=>
if err?
logger.err err:err, response:response, "getUserRegistrationStatus problem"
return callback err
success = body.success
logger.log user_id:user_id, success:body.success, "completing dropbox register"
if success
@flushUsersProjectToDropbox user_id
callback err, body.success
unlinkAccount: (user_id, callback)->
opts =
url: "#{settings.apis.thirdPartyDataStore.url}/user/#{user_id}/dropbox"
timeout: 5000
request.del opts, (err, response, body)=>
callback(err)
flushUsersProjectToDropbox: (user_id, callback)->
Project.findAllUsersProjects user_id, '_id', (err, projects = [], collabertions = [], readOnlyProjects = [])->
projectList = []
projectList = projectList.concat(projects)
projectList = projectList.concat(collabertions)
projectList = projectList.concat(readOnlyProjects)
projectIds = _.pluck(projectList, "_id")
logger.log projectIds:projectIds, user_id:user_id, "flushing all a users projects to tpds"
jobs = projectIds.map (project_id)->
return (cb)->
projectEntityHandler.flushProjectToThirdPartyDataStore project_id, cb
async.series jobs, callback
safelyGetResponse = (err, res, body, callback)->
statusCode = if res? then res.statusCode else 500
if err? or statusCode != 200
e = new Error("something went wrong getting response from dropbox, #{err}, #{statusCode}")
logger.err err:err
callback(e, [])
else
body = JSON.parse body
callback(null, body)
@@ -1,12 +0,0 @@
DropboxHandler = require "./DropboxHandler"
ProjectGetter = require "../Project/ProjectGetter"
module.exports = DropboxProjectController =
getStatus: (req, res, next) ->
project_id = req.params.Project_id
ProjectGetter.getProject project_id, {owner_ref: 1}, (error, project) ->
return next(error) if error?
DropboxHandler.getUserRegistrationStatus project.owner_ref, (error, status) ->
return next(error) if error?
res.json status
@@ -1,18 +0,0 @@
DropboxUserController = require './DropboxUserController'
DropboxWebhookController = require './DropboxWebhookController'
DropboxProjectController = require "./DropboxProjectController"
SecurityManager = require "../../managers/SecurityManager"
module.exports =
apply: (app) ->
app.get '/dropbox/beginAuth', DropboxUserController.redirectUserToDropboxAuth
app.get '/dropbox/completeRegistration', DropboxUserController.completeDropboxRegistration
app.get '/dropbox/unlink', DropboxUserController.unlinkDropbox
app.get '/dropbox/webhook', DropboxWebhookController.verify
app.post '/dropbox/webhook', DropboxWebhookController.webhook
app.ignoreCsrf('post', '/dropbox/webhook')
app.get '/project/:Project_id/dropbox/status', SecurityManager.requestIsOwner, DropboxProjectController.getStatus
@@ -1,26 +0,0 @@
dropboxHandler = require('./DropboxHandler')
logger = require('logger-sharelatex')
module.exports =
redirectUserToDropboxAuth: (req, res, next)->
user_id = req.session.user._id
dropboxHandler.getDropboxRegisterUrl user_id, (err, url)->
return next(err) if err?
logger.log url:url, "redirecting user for dropbox auth"
res.redirect url
completeDropboxRegistration: (req, res, next)->
user_id = req.session.user._id
dropboxHandler.completeRegistration user_id, (err, success)->
return next(err) if err?
res.redirect('/user/settings#dropboxSettings')
unlinkDropbox: (req, res, next)->
user_id = req.session.user._id
dropboxHandler.unlinkAccount user_id, (err, success)->
return next(err) if err?
res.redirect('/user/settings#dropboxSettings')
@@ -1,21 +0,0 @@
logger = require("logger-sharelatex")
DropboxWebhookHandler = require("./DropboxWebhookHandler")
module.exports = DropboxWebhookController =
verify: (req, res, next = (error) ->) ->
res.send(req.query.challenge)
req.session.destroy()
webhook: (req, res, next = (error) ->) ->
dropbox_uids = req.body?.delta?.users
logger.log dropbox_uids: dropbox_uids, "received webhook request from Dropbox"
if !dropbox_uids?
return res.send(400) # Bad Request
# Do this in the background so as not to keep Dropbox waiting
DropboxWebhookHandler.pollDropboxUids dropbox_uids, (error) ->
if error?
logger.error err: error, dropbox_uids: dropbox_uids, "error in webhook"
res.send(200)
req.session.destroy()
@@ -1,48 +0,0 @@
logger = require("logger-sharelatex")
settings = require("settings-sharelatex")
async = require "async"
User = require("../../models/User").User
TpdsUpdateSender = require "../ThirdPartyDataStore/TpdsUpdateSender"
redis = require("redis-sharelatex")
rclient = redis.createClient(settings.redis.web)
module.exports = DropboxWebhookHandler =
pollDropboxUids: (dropbox_uids, callback = (error) ->) ->
jobs = []
for uid in dropbox_uids
do (uid) ->
jobs.push (callback) ->
DropboxWebhookHandler.pollDropboxUid uid, callback
async.series jobs, callback
pollDropboxUid: (dropbox_uid, callback = (error) ->) ->
DropboxWebhookHandler._delayAndBatchPoll dropbox_uid, (error, shouldPoll) ->
return callback(error) if error?
return callback() if !shouldPoll
User.find {
"dropbox.access_token.uid": dropbox_uid.toString()
"features.dropbox": true
}, (error, users = []) ->
return callback(error) if error?
user = users[0]
if !user?
logger.log dropbox_uid: dropbox_uid, "no sharelatex user found"
return callback()
TpdsUpdateSender.pollDropboxForUser user._id, callback
POLL_DELAY_IN_MS: 5000 # 5 seconds
_delayAndBatchPoll: (dropbox_uid, callback = (error, shouldPoll) ->) ->
rclient.set(
"dropbox-poll-lock:#{dropbox_uid}", "LOCK",
"PX", DropboxWebhookHandler.POLL_DELAY_IN_MS,
"NX",
(error, gotLock) ->
return callback(error) if error?
if gotLock
setTimeout () ->
callback(null, true)
, DropboxWebhookHandler.POLL_DELAY_IN_MS
else
callback(null, false)
)
@@ -151,9 +151,6 @@ module.exports = EditorController =
return {_id:doc._id, path:path.substring(1)}
callback(null, docList)
forceResyncOfDropbox: (project_id, callback)->
ProjectEntityHandler.flushProjectToThirdPartyDataStore project_id, callback
notifyUsersProjectHasBeenDeletedOrRenamed: (project_id, callback)->
EditorRealTimeController.emitToRoom(project_id, 'projectRenamedOrDeletedByExternalSource')
callback()
@@ -6,7 +6,7 @@ logger = require('logger-sharelatex')
SubscriptionUpdater = require("./SubscriptionUpdater")
LimitationsManager = require('./LimitationsManager')
EmailHandler = require("../Email/EmailHandler")
DropboxHandler = require("../Dropbox/DropboxHandler")
Events = require "../../infrastructure/Events"
module.exports =
@@ -52,8 +52,8 @@ module.exports =
ONE_HOUR_IN_MS = 1000 * 60 * 60
setTimeout (-> EmailHandler.sendEmail "canceledSubscription", emailOpts
), ONE_HOUR_IN_MS
DropboxHandler.unlinkAccount user._id, ->
callback()
Events.emit "cancelSubscription", user._id
callback()
else
callback()
@@ -1,93 +0,0 @@
path = require('path')
ProjectUploadManager = require('../Uploads/ProjectUploadManager')
ProjectOptionsHandler = require("../Project/ProjectOptionsHandler")
ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
ProjectGetter = require('../Project/ProjectGetter')
EditorController = require('../Editor/EditorController')
TemplatesPublisher = require("./TemplatesPublisher")
settings = require('settings-sharelatex')
fs = require('fs')
request = require('request')
uuid = require('node-uuid')
logger = require('logger-sharelatex')
async = require("async")
module.exports =
createProjectFromZipTemplate: (req, res)->
logger.log body:req.session.templateData, "creating project from zip"
if !req.session.templateData?
return res.redirect "/project"
dumpPath = "#{settings.path.dumpFolder}/#{uuid.v4()}"
writeStream = fs.createWriteStream(dumpPath)
zipUrl = req.session.templateData.zipUrl
if zipUrl.slice(0,12).indexOf("templates") == -1
zipUrl = "#{settings.apis.web.url}#{zipUrl}"
else
zipUrl = "#{settings.apis.templates.url}#{zipUrl}"
zipReq = request(zipUrl)
zipReq.on "error", (error) ->
logger.error err: error, "error getting zip from template API"
zipReq.pipe(writeStream)
writeStream.on 'close', ->
ProjectUploadManager.createProjectFromZipArchive req.session.user._id, req.session.templateData.templateName, dumpPath, (err, project)->
if err?
logger.err err:err, zipUrl:zipUrl, "problem building project from zip"
return res.send 500
setCompiler project._id, req.session.templateData.compiler, ->
fs.unlink dumpPath, ->
delete req.session.templateData
res.redirect "/project/#{project._id}"
publishProject: (req, res, next) ->
project_id = req.params.Project_id
ProjectGetter.getProject project_id, {owner_ref: 1}, (error, project) ->
return callback(error) if error?
user_id = project.owner_ref.toString()
logger.log user_id:user_id, project_id:project_id, "receiving request to publish project as template"
TemplatesPublisher.publish user_id, project_id, (error) ->
return next(error) if error?
res.send 204
unpublishProject: (req, res, next) ->
project_id = req.params.Project_id
ProjectGetter.getProject project_id, {owner_ref: 1}, (error, project) ->
return callback(error) if error?
user_id = project.owner_ref.toString()
logger.log user_id:user_id, project_id:project_id, "receiving request to unpublish project"
TemplatesPublisher.unpublish user_id, project_id, (error) ->
return next(error) if error?
res.send 204
updateProjectDescription: (req, res, next) ->
project_id = req.params.Project_id
{description} = req.body
EditorController.updateProjectDescription project_id, description, (error) ->
return next(error) if error?
res.send 204
getTemplateDetails: (req, res, next)->
project_id = req.params.Project_id
ProjectGetter.getProject project_id, {owner_ref: 1}, (error, project) ->
return next(error) if error?
user_id = project.owner_ref.toString()
async.parallel {
details: (cb)->
TemplatesPublisher.getTemplateDetails user_id, project_id, cb
description: (cb)->
ProjectDetailsHandler.getProjectDescription project_id, cb
}, (err, results)->
if err?
logger.err err:err, user_id:user_id, project_id:project_id, "something went wrong getting template details"
return next(err)
details = results.details
details.description = results.description
res.json details
setCompiler = (project_id, compiler, callback)->
if compiler?
ProjectOptionsHandler.setCompiler project_id, compiler, callback
else
callback()
@@ -1,25 +0,0 @@
settings = require("settings-sharelatex")
logger = require("logger-sharelatex")
module.exports =
saveTemplateDataInSession: (req, res, next)->
if req.query.templateName
req.session.templateData = req.query
next()
id_or_tag_parse: (req, res, next)->
tag_or_template_id = req.params.tag_or_template_id
if _isObjectId(tag_or_template_id)
req.params.template_id = tag_or_template_id
else
req.params.tag_name = tag_or_template_id
next()
_isObjectId: _isObjectId = (tag_or_id)->
checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$")
checkForHexRegExp.test(tag_or_id)
insert_templates_user_id: (req, res, next)->
req.params.user_id = settings.templates.user_id
next()
@@ -1,36 +0,0 @@
request = require("request")
settings = require("settings-sharelatex")
logger = require("logger-sharelatex")
module.exports =
publish : (user_id, project_id, callback)->
url = buildUrl(user_id, project_id)
request.post url, (err)->
if err?
logger.err err:err, "something went wrong publishing project as template"
callback err
unpublish: (user_id, project_id, callback)->
url = buildUrl(user_id, project_id)
request.del url, (err)->
callback()
getTemplateDetails: (user_id, project_id, callback)->
url = buildUrl(user_id, project_id)+"/details"
request.get url, (err, res, body)->
if err?
logger.err err:err, user_id:user_id, project_id:project_id, body:body, "error getting template details"
return callback err
try
json = JSON.parse body
catch err
logger.err err:err, user_id:user_id, project_id:project_id, body:body, "error parsing project json details"
return callback err
logger.log json:json, user_id:user_id, project_id:project_id, "got template details"
callback(err, json)
buildUrl = (user_id, project_id)->
url = "#{settings.apis.templates.url}/templates/user/#{user_id}/project/#{project_id}"
@@ -1,30 +0,0 @@
SecurityManager = require("../../managers/SecurityManager")
AuthenticationController = require("../Authentication/AuthenticationController")
TemplatesWebController = require("./TemplatesWebController")
TemplatesController = require("./TemplatesController")
TemplatesMiddlewear = require('./TemplatesMiddlewear')
middleWear = require("./TemplatesMiddlewear")
module.exports =
apply: (app)->
app.get "/templates", middleWear.insert_templates_user_id, TemplatesWebController.renderTemplatesIndexPage
app.get "/templates/user/:user_id", TemplatesWebController.renderTemplatesIndexPage
app.get "/templates/:tag_or_template_id", middleWear.id_or_tag_parse, middleWear.insert_templates_user_id, TemplatesWebController.tagOrCanonicalPage
app.get "/templates/user/:user_id/:tag_or_template_id", middleWear.id_or_tag_parse, TemplatesWebController.tagOrCanonicalPage
app.get "/templates/:tag_name/:template_name", middleWear.insert_templates_user_id, TemplatesWebController.renerTemplateInTag
app.get "/templates/user/:user_id/:tag_name/:template_name", TemplatesWebController.renerTemplateInTag
app.get "/templates/:template_id/v/:version/:file_type", TemplatesWebController.proxyToTemplatesApi
app.post "/project/:Project_id/template/publish", SecurityManager.requestIsOwner, TemplatesController.publishProject
app.post "/project/:Project_id/template/unpublish", SecurityManager.requestIsOwner, TemplatesController.unpublishProject
app.post "/project/:Project_id/template/description", SecurityManager.requestCanModifyProject, TemplatesController.updateProjectDescription
# Make sure the /project/new/template route comes before the /project/:project_id/template route
# This is a get request so that it can be linked to.
app.get '/project/new/template', TemplatesMiddlewear.saveTemplateDataInSession, AuthenticationController.requireLogin(), TemplatesController.createProjectFromZipTemplate
app.get "/project/:Project_id/template", SecurityManager.requestCanAccessProject, TemplatesController.getTemplateDetails
@@ -1,102 +0,0 @@
request = require("request")
settings = require("settings-sharelatex")
logger = require("logger-sharelatex")
ErrorController = require("../Errors/ErrorController")
module.exports = TemplatesWebController =
renderTemplatesIndexPage: (req, res)->
logger.log "rendering index page of templates"
TemplatesWebController._getDataFromTemplatesApi "/user/#{req.params.user_id}", (err, data)->
if err? or !data?
logger.err err:err, "something went wrong in renderTemplatesIndexPage"
return res.send 500
data.title = "latex_templates"
res.render "templates/index", data
renerTemplateInTag: (req, res)->
{user_id, tag_name, template_name} = req.params
logger.log user_id:user_id, tag_name:tag_name, template_name:template_name, "rendering latex template page"
TemplatesWebController._getDataFromTemplatesApi "/user/#{user_id}/tag/#{tag_name}/template/#{template_name}", (err, data)->
if err? and err == 404
return ErrorController.notFound req, res
if err? or !data?
logger.err err:err, user_id:user_id, tag_name:tag_name, template_name:template_name, "something went wrong in renerTemplateInTag"
return res.send 500
data.title = data?.template?.name
res.render "templates/template", data
tagOrCanonicalPage: (req, res)->
if req.params.template_id?
TemplatesWebController._renderCanonicalPage(req, res)
else if req.params.tag_name?.toLowerCase() == "all"
TemplatesWebController._renderAllTemplatesPage(req, res)
else if req.params.tag_name?
TemplatesWebController._renderTagPage(req, res)
else
logger.log params:req.params, "problem rendering tagOrCanonicalPage"
res.send 500
proxyToTemplatesApi: (req, res)->
url = req.url
name = req.query.name or "Template"
if req.query.inline?
disposition = "inline"
else
disposition = "attachment"
res.header({"content-disposition": "#{disposition}; filename=\"#{name.replace("\"", "-")}.#{req.params.file_type}\""})
logger.log url:url, template_name: name, disposition: disposition, "proxying request to templates api"
getReq = request.get("#{settings.apis.templates.url}#{url}")
getReq.pipe(res)
getReq.on "error", (error) ->
logger.error err: error, "templates proxy API error"
res.send 500
_renderCanonicalPage: (req, res)->
{user_id, template_id} = req.params
logger.log user_id:user_id, template_id:template_id, "rendering template page"
TemplatesWebController._getDataFromTemplatesApi "/user/#{user_id}/template/#{template_id}", (err, data)->
if err? and err == 404
return ErrorController.notFound req, res
if err?
logger.err err:err, user_id:user_id, template_id:template_id, "something went wrong in _renderCanonicalPage"
return res.send 500
data.title = data?.template?.name
data.tag = null
res.render "templates/template", data
_renderAllTemplatesPage: (req, res)->
{user_id} = req.params
logger.log user_id:user_id, "rendering all templates page"
TemplatesWebController._getDataFromTemplatesApi "/user/#{user_id}/all", (err, data)->
if err? and err == 404
return ErrorController.notFound req, res
if err?
logger.err err:err, user_id:user_id, "something went wrong in _renderCanonicalPage"
return res.send 500
data.title = "all_templates"
res.render "templates/tag", data
_renderTagPage: (req, res)->
{user_id, tag_name} = req.params
logger.log user_id:user_id, tag_name:tag_name, "rendinging tag page for templates"
TemplatesWebController._getDataFromTemplatesApi "/user/#{user_id}/tag/#{tag_name}", (err, data)->
if err? and err == 404
return ErrorController.notFound req, res
if err?
logger.err err:err, user_id:user_id, tag_name:tag_name, "something went wrong in _renderCanonicalPage"
return res.send 500
data.title = data?.tag?.name
res.render "templates/tag", data
_getDataFromTemplatesApi: (path, callback)->
opts =
url: "#{settings.apis.templates.url}#{path}"
json:true
request.get opts, (err, response, data)->
if response.statusCode == 404
return callback 404
callback err, data
@@ -1,5 +1,4 @@
UserLocator = require("./UserLocator")
dropboxHandler = require('../Dropbox/DropboxHandler')
logger = require("logger-sharelatex")
Settings = require("settings-sharelatex")
fs = require('fs')
@@ -27,14 +26,12 @@ module.exports =
title: 'login',
redir: req.query.redir
settingsPage : (req, res)->
settingsPage : (req, res, next)->
logger.log user: req.session.user, "loading settings page"
UserLocator.findById req.session.user._id, (err, user)->
dropboxHandler.getUserRegistrationStatus user._id, (err, status)->
userIsRegisteredWithDropbox = !err? and status.registered
res.render 'user/settings',
title:'account_settings'
userIsRegisteredWithDropbox: userIsRegisteredWithDropbox
user: user,
languages: Settings.languages,
accountSettingsTabActive: true
return next(err) if err?
res.render 'user/settings',
title:'account_settings'
user: user,
languages: Settings.languages,
accountSettingsTabActive: true
@@ -0,0 +1,2 @@
events = require "events"
module.exports = new events.EventEmitter()
@@ -157,5 +157,6 @@ module.exports = (app)->
if Settings.reloadModuleViewsOnEachRequest
Modules.loadViewIncludes()
res.locals.moduleIncludes = Modules.moduleIncludes
res.locals.moduleIncludesAvailable = Modules.moduleIncludesAvailable
next()
@@ -32,5 +32,8 @@ module.exports = Modules =
compiler = jade.compile(partial, doctype: "html")
html += compiler(locals)
return html
moduleIncludesAvailable: (view) ->
return (Modules.viewIncludes[view] or []).length > 0
Modules.loadModules()
-15
View File
@@ -14,7 +14,6 @@ UploadsRouter = require './Features/Uploads/UploadsRouter'
metrics = require('./infrastructure/Metrics')
ReferalController = require('./Features/Referal/ReferalController')
ReferalMiddleware = require('./Features/Referal/ReferalMiddleware')
TemplatesRouter = require('./Features/Templates/TemplatesRouter')
AuthenticationController = require('./Features/Authentication/AuthenticationController')
TagsController = require("./Features/Tags/TagsController")
CollaboratorsRouter = require('./Features/Collaborators/CollaboratorsRouter')
@@ -33,8 +32,6 @@ StaticPagesRouter = require("./Features/StaticPages/StaticPagesRouter")
ChatController = require("./Features/Chat/ChatController")
BlogController = require("./Features/Blog/BlogController")
WikiController = require("./Features/Wiki/WikiController")
DropboxRouter = require "./Features/Dropbox/DropboxRouter"
dropboxHandler = require "./Features/Dropbox/DropboxHandler"
Modules = require "./infrastructure/Modules"
RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
@@ -65,8 +62,6 @@ module.exports = class Router
UploadsRouter.apply(app)
PasswordResetRouter.apply(app)
StaticPagesRouter.apply(app)
TemplatesRouter.apply(app)
DropboxRouter.apply(app)
Modules.applyRouter(app)
@@ -190,16 +185,6 @@ module.exports = class Router
), 10000
req.session.destroy()
app.get '/test', (req, res) ->
res.render "tests",
privilegeLevel: "owner"
project:
name: "test"
date: Date.now()
layout: false
userCanSeeDropbox: true
languages: []
app.get "/ip", (req, res, next) ->
res.send({
ip: req.ip
@@ -56,7 +56,6 @@ block content
include ./editor/binary-file
include ./editor/track-changes
include ./editor/publish-template
include ./editor/dropbox
.ui-layout-east
include ./editor/chat
@@ -1,41 +0,0 @@
script(type="text/ng-template", id="dropboxModalTemplate")
.modal-header
button.close(
type="button"
data-dismiss="modal"
ng-click="cancel()"
) ×
h3 #{translate("dropbox_sync")}
.modal-body.modal-body-share
div(ng-show="dbState.gotLinkStatus")
div(ng-hide="dbState.userIsLinkedToDropbox || !dbState.hasDropboxFeature")
span(ng-hide="dbState.startedLinkProcess") #{translate("account_not_linked_to_dropbox")}
|    
a(ng-click="linkToDropbox()").btn.btn-info #{translate("update_dropbox_settings")}
p.small.text-center(ng-show="dbState.startedLinkProcess")
| #{translate("refresh_page_after_starting_free_trial")}
div(ng-show="dbState.hasDropboxFeature && dbState.userIsLinkedToDropbox")
p.small
| #{translate("this_project_will_appear_in_your_dropbox_folder_at")}
strong Dropbox/sharelatex/{{ project.name }}
div.text-center(ng-hide="dbState.hasDropboxFeature")
p #{translate("need_to_upgrade_for_dropbox")}
p(ng-controller="FreeTrialModalController")
a.btn(ng-click="startFreeTrial('dropbox')", ng-class="buttonClass") #{translate("start_free_trial")}
p.small(ng-show="startedFreeTrial")
| #{translate("refresh_page_after_starting_free_trial")}
div(ng-hide="dbState.gotLinkStatus")
i.fa.fa-refresh.fa-spin
span.small   #{translate("checking_dropbox_status")}
.modal-footer()
button.btn.btn-default(
ng-click="cancel()",
)
span #{translate("dismiss")}
@@ -41,21 +41,12 @@ aside#left-menu.full-size(
)
i.fa.fa-fw.fa-copy
|    #{translate("copy_project")}
li(ng-controller="TemplatesController", ng-show="permissions.admin")
a(ng-click="openPublishTemplateModal()")
i.fa.fa-external-link.fa-fw
|    #{translate("publish_as_template")}
!{moduleIncludes("editorLeftMenu:actions", locals)}
div(ng-show="!anonymous")
h4() #{translate("sync")}
span(ng-controller="DropboxController")
ul.list-unstyled.nav()
li
a(ng-click="openDropboxModal()")
i.fa.fa-dropbox.fa-fw
|    Dropbox
!{moduleIncludes("editorLeftMenu", locals)}
if (moduleIncludesAvailable("editorLeftMenu:sync"))
div(ng-show="!anonymous")
h4() #{translate("sync")}
!{moduleIncludes("editorLeftMenu:sync", locals)}
h4(ng-show="!anonymous") #{translate("settings")}
form.settings(ng-controller="SettingsController", ng-show="!anonymous")
@@ -1,80 +0,0 @@
extends ../layout
block vars
- var meta = "Over 400 LaTeX templates for journal articles, theses, CV and resumes, posters, presentations, and much more"
block content
.content.content-alt
.container
.row.template-page-header(ng-controller="SearchController")
.col-md-2
h2
a(href="/templates") #{translate("templates")}
.col-md-8
form.project-search.form-horizontal(role="form")
.form-group.has-feedback.has-feedback-left.col-md-12
input.form-control.col-md-12(type='text', ng-model='searchQueryText', ng-keyup='search()', placeholder="Search template library....")
i.fa.fa-search.form-control-feedback-left
i.fa.fa-times.form-control-feedback(
ng-click="clearSearchText()",
style="cursor: pointer;",
ng-show="searchQueryText.length > 0"
)
.col-md-2(ng-controller="MissingTemplateController")
a.btn.btn-primary(ng-click="showMissingTemplateModal()") #{translate("missing_template_question")}
.col-md-8(ng-cloak)
ul.list-unstyled
li(ng-repeat='hit in hits')
.thumbnail.searchResult
.row
a(ng-href='{{hit.url}}')
.col-md-3
img(ng-src='{{hit.image_url}}')
.col-md-7
h1(ng-bind-html='hit.name')
p(ng-bind-html='hit.description')
.row
-each tag in tags
-if(tag.totalNumberOfTemplates > 0)
.template-section-header.col-md-12
h2
a(href=tag.tagPagePath) #{tag.name}
.row
-each template in tag.exampleTemplates
.col-md-3.template-thumbnail
a(href=template.templatePagePath ? template.templatePagePath : template.canonicalUrl).thumbnail
img(src=template.thumbnailUrl)
div.caption
h3.txt-middle #{template.name}
-if(tag.totalNumberOfTemplates > 4)
.row
.col-md-12.text-center
a(href=tag.tagPagePath) View all #{tag.totalNumberOfTemplates} #{tag.name} templates »
script(type="text/ng-template", id="missingTemplateModal")
.modal-header
button.close(
type="button"
data-dismiss="modal"
ng-click="cancel()"
) ×
h3 #{translate("missing_template_question")}
.modal-body #{translate("tell_us_about_the_template")}
.modal-footer
button.btn.btn-default(
ng-click="cancel()",
)
span #{translate("dismiss")}
a.btn.btn-primary(href='mailto:team@sharelatex.com?Subject=template') #{translate("email_us")}
-31
View File
@@ -1,31 +0,0 @@
extends ../layout
mixin template(template)
.template-thumbnail
a(href=template.templatePagePath ? template.templatePagePath : template.canonicalUrl).thumbnail
img(src=template.thumbnailUrl)
div.caption
h3 #{template.name}
block content
.content.content-alt
.container
.row
.page-header
h2
a(href="/templates") #{translate("templates")}
|
a(href=tag.tagPagePath) #{tag.name}
- for (var row = 0; row <= Math.floor(templates.length / 4); row++)
.row
- for (var column = 0; column < 4; column++)
- if (templates[row*4 + column])
.col-md-3
+template(templates[row*4 + column])
//- -each template in templates
//- a(href=template.templatePagePath || template.canonicalUrl)
//- .col-md-3.template-thumbnail
//- a(href=template.templatePagePath ? template.templatePagePath : template.canonicalUrl).thumbnail
//- img(src=template.thumbnailUrl)
//- div.caption
//- h3 #{template.name}
@@ -1,69 +0,0 @@
extends ../layout
block vars
- var meta = template.description
- title = title + " - LaTeX Template"
block content
.content.content-alt
.container
.row
.page-header
h2
a(href="/templates") #{translate("templates")}
|
- if(tag)
a(href=tag.tagPagePath) #{tag.name}
|
| #{template.name}
.row
.col-md-6
.entry
.row
.col-md-12.template-large-pdf-preview
a(href="#{template.pdfUrl}?inline=true&name=#{template.name}")
img(src="#{template.previewUrl}")
.col-md-6
.template-details-section
h3 #{translate("about")}
div !{template.description}
div(ng-controller="openInSlController", ng-cloak).download-buttons
a.btn.btn-primary.btn-large(href=template.open_in_sharelatex_url, ng-click='open()', ng-disabled="isDisabled", rel='nofollow') {{openInSlText}}
| &nbsp;
a.btn.btn-default(
href="#{template.zipUrl}?name=#{template.name}",
rel='nofollow',
ng-click='downloadZip()',
tooltip-placement="bottom",
tooltip="#{translate('download_zip_file')}"
)
i.fa.fa-cloud-download
.template-details-section.social_buttons
.addthis_toolbox.addthis_default_style.addthis_32x32_style
a.addthis_button_facebook
a.addthis_button_twitter
a.addthis_button_google_plusone_share
a.addthis_button_compact
script(type='text/javascript', src='//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-517c16586439faa7')
h3 #{translate("comment")}
#disqus_thread
script(type='text/javascript').
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'sharelatextemplates'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
noscript
| Please enable JavaScript to view the
a(href='http://disqus.com/?ref_noscript') comments powered by Disqus.
a.dsq-brlink(href='http://disqus.com')
| comments powered by
span.logo-disqus Disqus
-21
View File
@@ -96,27 +96,6 @@ block content
ng-disabled="changePasswordForm.$invalid"
) #{translate("change")}
hr
h3 #{translate("dropbox_integration")}
span.small
a(href='/help/kb/dropbox-2') (#{translate("learn_more")})
- if(!user.features.dropbox)
p.small #{translate("dropbox_sync_description")}
.alert.alert-info
p #{translate("dropbox_is_premium")}
p
a.btn.btn-info(href='/user/subscription/plans') #{translate("upgrade")}
- else if(userIsRegisteredWithDropbox)
.alert.alert-success
| #{translate("account_is_linked")}.
|
a(href='/dropbox/unlink') #{translate("unlink_dropbox")}
- else
p.small #{translate("dropbox_sync_description")}
p
a.btn.btn-info(href='/dropbox/beginAuth') #{translate("link_to_dropbox")}
| !{moduleIncludes("userSettings", locals)}
hr