From b80f751cb2d9157cb04d70ad7e1f5b8ddc422ae7 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jun 2014 17:08:54 +0100 Subject: [PATCH] templates works basically, needs error handling and few little improvments --- .../Templates/TemplatesMiddlewear.coffee | 16 ++ .../Features/Templates/TemplatesRouter.coffee | 19 +++ .../Templates/TemplatesWebController.coffee | 64 ++++++++ services/web/app/coffee/router.coffee | 2 + services/web/app/views/project/editor.jade | 1 + services/web/app/views/templates/index.jade | 46 ++++++ services/web/app/views/templates/tag.jade | 16 ++ .../web/app/views/templates/template.jade | 55 +++++++ .../dropbox.jade | 0 .../TemplatesWebControllerTests.coffee | 147 ++++++++++++++++++ 10 files changed, 366 insertions(+) create mode 100644 services/web/app/coffee/Features/Templates/TemplatesRouter.coffee create mode 100644 services/web/app/coffee/Features/Templates/TemplatesWebController.coffee create mode 100644 services/web/app/views/templates/index.jade create mode 100644 services/web/app/views/templates/tag.jade create mode 100644 services/web/app/views/templates/template.jade rename services/web/app/views/{templates => view_templates}/dropbox.jade (100%) create mode 100644 services/web/test/UnitTests/coffee/Templates/TemplatesWebControllerTests.coffee diff --git a/services/web/app/coffee/Features/Templates/TemplatesMiddlewear.coffee b/services/web/app/coffee/Features/Templates/TemplatesMiddlewear.coffee index cba54087fb..fed05edc9d 100644 --- a/services/web/app/coffee/Features/Templates/TemplatesMiddlewear.coffee +++ b/services/web/app/coffee/Features/Templates/TemplatesMiddlewear.coffee @@ -1,7 +1,23 @@ +settings = require("settings-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() \ No newline at end of file diff --git a/services/web/app/coffee/Features/Templates/TemplatesRouter.coffee b/services/web/app/coffee/Features/Templates/TemplatesRouter.coffee new file mode 100644 index 0000000000..10fce46cab --- /dev/null +++ b/services/web/app/coffee/Features/Templates/TemplatesRouter.coffee @@ -0,0 +1,19 @@ + +TemplatesWebController = require("./TemplatesWebController") +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 + + diff --git a/services/web/app/coffee/Features/Templates/TemplatesWebController.coffee b/services/web/app/coffee/Features/Templates/TemplatesWebController.coffee new file mode 100644 index 0000000000..5bc463bc76 --- /dev/null +++ b/services/web/app/coffee/Features/Templates/TemplatesWebController.coffee @@ -0,0 +1,64 @@ +request = require("request") +settings = require("settings-sharelatex") +logger = require("logger-sharelatex") + +module.exports = TemplatesWebController = + + renderTemplatesIndexPage: (req, res)-> + logger.log "rendering index page of templates" + TemplatesWebController._getDataFromTemplatesApi "/user/#{req.params.user_id}", (err, data)-> + res.render "templates/index", data + + renerTemplateInTag: (req, res)-> + logger.log "rendering latex template page" + {user_id, tag_name, template_name} = req.params + TemplatesWebController._getDataFromTemplatesApi "/user/#{user_id}/tag/#{tag_name}/template/#{template_name}", (err, data)-> + 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 + logger.log url:url, "proxying request to templates api" + getReq = request.get("#{settings.apis.templates_api.url}#{url}") + getReq.pipe(res) + getReq.on "error", (error) -> + logger.error err: error, "templates proxy API error" + res.send 500 + + _renderCanonicalPage: _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)-> + data.tag = null + res.render "templates/template", data + + _renderAllTemplatesPage: _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)-> + data.title = "HELLLLo" + res.render "templates/tag", data + + _renderTagPage: _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)-> + res.render "templates/tag", data + + _getDataFromTemplatesApi: _getDataFromTemplatesApi = (path, callback)-> + opts = + url: "#{settings.apis.templates_api.url}#{path}" + json:true + request.get opts, (err, response, data)-> + callback err, data \ No newline at end of file diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 43d59ad48c..fbbf38aa34 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -16,6 +16,7 @@ UploadsRouter = require './Features/Uploads/UploadsRouter' metrics = require('./infrastructure/Metrics') ReferalController = require('./Features/Referal/ReferalController') ReferalMiddleware = require('./Features/Referal/ReferalMiddleware') +TemplatesRouter = require('./Features/Templates/TemplatesRouter') TemplatesController = require('./Features/Templates/TemplatesController') TemplatesMiddlewear = require('./Features/Templates/TemplatesMiddlewear') AuthenticationController = require('./Features/Authentication/AuthenticationController') @@ -60,6 +61,7 @@ module.exports = class Router UploadsRouter.apply(app) PasswordResetRouter.apply(app) StaticPagesRouter.apply(app) + TemplatesRouter.apply(app) if Settings.enableSubscriptions app.get '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.jade index 2193b2a927..1b3eada737 100644 --- a/services/web/app/views/project/editor.jade +++ b/services/web/app/views/project/editor.jade @@ -72,6 +72,7 @@ block content //- include ../templates //- include ../templates/dropbox + script(src='/socket.io/socket.io.js') script(type='text/javascript'). diff --git a/services/web/app/views/templates/index.jade b/services/web/app/views/templates/index.jade new file mode 100644 index 0000000000..3f4e557f3c --- /dev/null +++ b/services/web/app/views/templates/index.jade @@ -0,0 +1,46 @@ +extends ../layout + +block content + .content.content-alt + .container + .row + #searchArea(ng-app="templatesSearchApp").span12 + div(ng-controller="SearchController") + .row-fluid + .span6.offset3.search-field + i.icon-search + input(type='text', ng-model='searchQueryText', ng-keyup='search()', placeholder="Search")#searchField.span12 + ul.unstyled + li(ng-repeat='hit in hits') + .thumbnail.searchResult + .row-fluid + a(ng-href='{{hit.url}}') + .span3 + img(ng-src='{{hit.image_url}}') + .span9 + h2(ng-bind-html='hit.name') + p(ng-bind-html='hit.description') + + + .row + -each tag in tags + -if(tag.totalNumberOfTemplates > 0) + .page-header.span12 + h2 + a(href=tag.tagPagePath) #{tag.name} + .span12 + .row-fluid + -each template in tag.exampleTemplates + .span3 + a(href=template.templatePagePath ? template.templatePagePath : template.canonicalUrl) + .template-thumbnail.thumbnail + img(src=template.thumbnailUrl) + h3.txt-middle #{template.name} + + -if(tag.totalNumberOfTemplates > 4) + .row-fluid + .span6.offset3.more-templates + a(href=tag.tagPagePath) View all #{tag.totalNumberOfTemplates} #{tag.name} templates + + + diff --git a/services/web/app/views/templates/tag.jade b/services/web/app/views/templates/tag.jade new file mode 100644 index 0000000000..31ff5b6b3f --- /dev/null +++ b/services/web/app/views/templates/tag.jade @@ -0,0 +1,16 @@ +extends ../layout + +block content + .content.content-alt + .container + .row + .page-header + h2 + a(href=tag.tagPagePath) #{tag.name} + + -each template in templates + a(href=template.templatePagePath || template.canonicalUrl) + .span3 + .template-thumbnail.thumbnail + img(src=template.thumbnailUrl) + h3.txt-middle #{template.name} diff --git a/services/web/app/views/templates/template.jade b/services/web/app/views/templates/template.jade new file mode 100644 index 0000000000..dbae94882a --- /dev/null +++ b/services/web/app/views/templates/template.jade @@ -0,0 +1,55 @@ +extends ../layout + +block content + .content.content-alt + .container + .row + .page-header + h1 #{template.name} LaTeX Template + -if(tag) + a(href=tag.tagPagePath) ← Back to more #{tag.name} LaTeX templates + .container.txt-lefty + .row-fluid() + .span6 + .entry + .row-fluid + .span12 + a(href=template.pdfUrl) + img(src="#{template.previewUrl}") + + .span6(ng-app="openInSl") + h3 About + div !{template.description} + + h3 Actions + span(ng-controller="openInSlController") + a.btn.btn-success.btn-large.open-in-sharelatex(href=template.open_in_sharelatex_url, ng-click='open()', ng-disabled="isDisabled", rel='nofollow') {{openInSlText}} + |   + a.btn.btn-large.download-zip(href=template.zipUrl, rel='nofollow', ng-click='downloadZip()') Download Zip + + + .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') + div + h3 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 diff --git a/services/web/app/views/templates/dropbox.jade b/services/web/app/views/view_templates/dropbox.jade similarity index 100% rename from services/web/app/views/templates/dropbox.jade rename to services/web/app/views/view_templates/dropbox.jade diff --git a/services/web/test/UnitTests/coffee/Templates/TemplatesWebControllerTests.coffee b/services/web/test/UnitTests/coffee/Templates/TemplatesWebControllerTests.coffee new file mode 100644 index 0000000000..7a1947ab0b --- /dev/null +++ b/services/web/test/UnitTests/coffee/Templates/TemplatesWebControllerTests.coffee @@ -0,0 +1,147 @@ +should = require('chai').should() +SandboxedModule = require('sandboxed-module') +assert = require('assert') +path = require('path') +sinon = require('sinon') +modulePath = path.join __dirname, "../../../../app/js/Features/Templates/TemplatesWebController" +expect = require("chai").expect + +describe "TemplatesWebController", -> + + beforeEach -> + + @settings = + apis: + templates_api: + url:"templates.sharelatex.env" + @TemplatesWebController = SandboxedModule.require modulePath, requires: + "settings-sharelatex":@settings + "logger-sharelatex": log:-> + @stubbedApiData = + templates:[{_id:"12312321"}] + + @TemplatesWebController._getDataFromTemplatesApi = sinon.stub().callsArgWith(1, null, @stubbedApiData) + + @user_id = "12332lk3jlkj" + @tag_name = "tag-name-here" + @template_name = "template-name-here" + @template_id = "template_id_here" + @req = + params: + user_id: @user_id + @res = {} + + describe "renderTemplatesIndexPage", -> + + it "should get the data from the templates api", (done)-> + @res.render = (viewName, data)=> + @TemplatesWebController._getDataFromTemplatesApi.calledWith("/user/#{@user_id}").should.equal true + data.should.equal @stubbedApiData + done() + @TemplatesWebController.renderTemplatesIndexPage @req, @res + + + describe "renerTemplateInTag", -> + + it "should get the data from the templates api", (done)-> + @res.render = (viewName, data)=> + @TemplatesWebController._getDataFromTemplatesApi.calledWith("/user/#{@user_id}/tag/#{@tag_name}/template/#{@template_name}").should.equal true + data.should.equal @stubbedApiData + done() + + @req.params = + user_id:@user_id + template_name:@template_name + tag_name:@tag_name + + @TemplatesWebController.renerTemplateInTag @req, @res + + + describe "tagOrCanonicalPage", -> + + beforeEach -> + @TemplatesWebController._renderCanonicalPage = sinon.stub() + @TemplatesWebController._renderAllTemplatesPage = sinon.stub() + @TemplatesWebController._renderTagPage = sinon.stub() + + it "should call _renderCanonicalPage if there is a template id", ()-> + + @req.params = + template_id:@template_id + + @TemplatesWebController.tagOrCanonicalPage @req, @res + + @TemplatesWebController._renderCanonicalPage.called.should.equal true + @TemplatesWebController._renderAllTemplatesPage.called.should.equal false + @TemplatesWebController._renderTagPage.called.should.equal false + + it "should call _renderAllTemplatesPage the tag name is all", ()-> + + @req.params = + tag_name:"all" + + @TemplatesWebController.tagOrCanonicalPage @req, @res + + @TemplatesWebController._renderCanonicalPage.called.should.equal false + @TemplatesWebController._renderAllTemplatesPage.called.should.equal true + @TemplatesWebController._renderTagPage.called.should.equal false + + + it "should call _renderTagPage the tag name is set", ()-> + + @req.params = + tag_name:"some-tag" + + @TemplatesWebController.tagOrCanonicalPage @req, @res + + @TemplatesWebController._renderCanonicalPage.called.should.equal false + @TemplatesWebController._renderAllTemplatesPage.called.should.equal false + @TemplatesWebController._renderTagPage.called.should.equal true + + describe "_renderCanonicalPage", -> + + it "should get the data from the templates api", (done)-> + @res.render = (viewName, data)=> + @TemplatesWebController._getDataFromTemplatesApi.calledWith("/user/#{@user_id}/template/#{@template_id}").should.equal true + data.tag = null + data.should.equal @stubbedApiData + done() + + @req.params = + user_id:@user_id + template_id:@template_id + + @TemplatesWebController._renderCanonicalPage @req, @res + + + describe "_renderAllTemplatesPage", -> + + it "should get the data from the templates api", (done)-> + @res.render = (viewName, data)=> + @TemplatesWebController._getDataFromTemplatesApi.calledWith("/user/#{@user_id}/all").should.equal true + data.should.equal @stubbedApiData + done() + + @req.params = + user_id:@user_id + + @TemplatesWebController._renderAllTemplatesPage @req, @res + + + describe "_renderTagPage", -> + + it "should get the data from the templates api", (done)-> + @res.render = (viewName, data)=> + @TemplatesWebController._getDataFromTemplatesApi.calledWith("/user/#{@user_id}/tag/#{@tag_name}").should.equal true + data.should.equal @stubbedApiData + done() + + @req.params = + user_id:@user_id + tag_name:@tag_name + + @TemplatesWebController._renderTagPage @req, @res + + + +