ProjectMembersManager.coffee 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. define [
  2. "models/User"
  3. "models/ProjectMemberList"
  4. "account/AccountManager"
  5. "utils/Modal"
  6. "moment"
  7. "libs/backbone"
  8. "libs/mustache"
  9. ], (User, ProjectMemberList, AccountManager, Modal, moment) ->
  10. INFINITE_COLLABORATORS = -1
  11. class ProjectMembersManager
  12. templates:
  13. userPanel: $("#userPanelTemplate").html()
  14. constructor: (@ide, options) ->
  15. options || = {}
  16. setupArea = _.once =>
  17. @ide.tabManager.addTab
  18. id : "collaborators"
  19. name: "Share"
  20. content : $(@templates.userPanel)
  21. lock: true
  22. onShown: () =>
  23. @publishProjectView?.refreshPublishStatus()
  24. setupPublish = _.once =>
  25. if @ide.security? and @ide.security.permissionsLevel == "owner"
  26. @publishProjectView = new PublishProjectView
  27. ide: @ide
  28. el: $("#publishProject")
  29. @publishProjectView.render()
  30. setupArea()
  31. if @ide?
  32. @ide.on "afterJoinProject", (project) =>
  33. @project = project
  34. if !@view?
  35. @members = @project.get("members")
  36. @view = new ProjectMemberListView
  37. ide: @ide
  38. collection: @members
  39. el: $('#projectMembersList')
  40. manager: this
  41. allowedCollaborators = @project.get("features").collaborators
  42. allowedMembers = allowedCollaborators + 1 # include owner
  43. if @members.length > allowedMembers and allowedCollaborators != INFINITE_COLLABORATORS
  44. AccountManager.showUpgradeDialog @ide,
  45. message: "This project has too many collaborators for your plan. Please upgrade your account or remove some collaborators"
  46. if @ide.security? and @ide.security.permissionsLevel == "owner"
  47. @view.options.showAdminControls = true
  48. else
  49. @view.options.showAdminControls = false
  50. @view.render()
  51. if @ide.project.get("owner") == @ide.user and @ide.user.get("id") != "openUser"
  52. setupPublish()
  53. if @ide.project.get("owner") == @ide.user or @ide.project.get("publicAccesLevel") != "private"
  54. if !@socialView?
  55. @socialView = new SocialSharingView
  56. ide: @ide
  57. el: $("#socialSharing")
  58. @socialView.render()
  59. else
  60. $("#socialSharing").hide()
  61. if @ide.socket?
  62. @ide.socket.on "userRemovedFromProject", (userId) =>
  63. @afterMemberRemoved(userId)
  64. @ide.socket.on "userAddedToProject", (user, privileges) =>
  65. @afterMemberAdded(user, privileges)
  66. removeMember: (member) ->
  67. @ide.socket.emit "removeUserFromProject", member.id
  68. addMember: (email, privileges) ->
  69. console.log "Adding member", email
  70. @ide.socket.emit "addUserToProject", email, privileges, (error, added) =>
  71. if error?
  72. @ide.showGenericServerErrorMessage()
  73. return
  74. if !added
  75. console.log "got response", error, added
  76. ga('send', 'event', 'subscription-funnel', 'askToUpgrade', "projectMemebrs")
  77. AccountManager.askToUpgrade @ide,
  78. why: "to add additional collaborators"
  79. onUpgrade: () =>
  80. ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', "projectMemebrs")
  81. afterMemberRemoved: (memberId) ->
  82. for member in @members.models
  83. if member.id == memberId
  84. toRemove = member
  85. @members.remove(toRemove) if toRemove?
  86. afterMemberAdded: (member, privileges) ->
  87. @members.add new User
  88. email : member.email
  89. id : member._id
  90. privileges : privileges
  91. ProjectMemberListView = Backbone.View.extend
  92. template : $("#projectMemberListTemplate").html()
  93. events:
  94. "click .addUser" : "addMember"
  95. initialize: ->
  96. @itemViews = {}
  97. @options.showAdminControls ||= false
  98. @collection.on "add", (model) =>
  99. @addItem model
  100. @collection.on "reset", (collection) =>
  101. @addItem model for model in collection.models
  102. @collection.on "remove", (model) =>
  103. @removeItem model
  104. render: ->
  105. $(@el).html Mustache.to_html(@template, showAdminControls: @options.showAdminControls)
  106. @addItem model for model in @collection.models
  107. return this
  108. addItem: (model) ->
  109. view = new ProjectMemberListItemView
  110. model : model
  111. showRemove : @options.showAdminControls and model.get("privileges") != "owner"
  112. manager: @options.manager
  113. @itemViews[model.id] = view
  114. @$("tbody").append view.$el
  115. removeItem: (model) ->
  116. if @itemViews[model.id]?
  117. @itemViews[model.id].remove()
  118. delete @itemViews[model.id]
  119. addMember: (e) ->
  120. e.preventDefault()
  121. email = @$(".addUserForm .email").val()
  122. privileges= @$(".addUserForm .privileges").val()
  123. if email.indexOf('@') == -1
  124. @options.ide.showErrorModal("Invalid Email", "#{email} is not a valid email address")
  125. else
  126. @options.manager.addMember email, privileges
  127. @$(".addUserForm .email").val("")
  128. ProjectMemberListItemView = Backbone.View.extend
  129. template : $("#projectMemberListItemTemplate").html()
  130. events:
  131. "click .removeUser": "removeMember"
  132. initialize: -> @render()
  133. render: ->
  134. @setElement $(Mustache.to_html(@template, @modelView()))
  135. return this
  136. modelView: ->
  137. modelView = @model.toJSON()
  138. modelView.privileges = {
  139. readOnly : "Read Only"
  140. readAndWrite : "Read and Write"
  141. owner : "Owner"
  142. }[modelView.privileges]
  143. modelView.showRemove = @options.showRemove
  144. return modelView
  145. removeMember: (e) ->
  146. e.preventDefault()
  147. @options.manager.removeMember(@model)
  148. PublishProjectView = Backbone.View.extend
  149. template: $("#publishProjectTemplate").html()
  150. events:
  151. "click #publishProjectAsTemplate": "publishProjectAsTemplate"
  152. "click #republishProjectAsTemplate": "publishProjectAsTemplate"
  153. "click #unPublishProjectAsTemplate": "unPublishProjectAsTemplate"
  154. "blur #projectDescription" : "updateDescription"
  155. initialize: () ->
  156. @ide = @options.ide
  157. @model = @ide.project
  158. @render()
  159. render: ->
  160. viewModel =
  161. description: @model.get("description")
  162. canonicalUrl: @model.get("template.canonicalUrl")
  163. isPublished: @model.get("template.isPublished")
  164. publishedDate: moment(@model.get("template.publishedDate")).format("Do MMM YYYY, h:mm a")
  165. @$el.html $(Mustache.to_html(@template, viewModel))
  166. @publishedArea = $('.show-when-published')
  167. @unpublishedArea = $('.show-when-unpublished')
  168. $('#problemWithPublishingArea').hide()
  169. $('#publishWorkingArea').hide()
  170. refreshView: () ->
  171. if @model.get("template.isPublished")
  172. @$("a#templateLink").attr("href", @model.get("template.canonicalUrl"))
  173. @publishedArea.show()
  174. else
  175. @unpublishedArea.show()
  176. refreshPublishStatus: ->
  177. @showWorking()
  178. @ide.socket.emit "getPublishedDetails", @ide.user.get("id"), (err, details)=>
  179. @hideWorking()
  180. if err?
  181. return @showError()
  182. @model.set("template.isPublished", details.exists)
  183. if details.exists
  184. @model.set("template.canonicalUrl", details.canonicalUrl)
  185. @model.set("template.publishedDate", details.publishedDate)
  186. @refreshView()
  187. showError: ->
  188. @publishedArea.hide()
  189. @unpublishedArea.hide()
  190. $('#problemWithPublishingArea').show()
  191. showWorking: ->
  192. @publishedArea.hide()
  193. @unpublishedArea.hide()
  194. $('#publishWorkingArea').show()
  195. hideWorking: ->
  196. $('#publishWorkingArea').hide()
  197. publishProjectAsTemplate: ->
  198. @showWorking()
  199. @unpublishedArea.hide()
  200. @publishedArea.hide()
  201. @ide.socket.emit "publishProjectAsTemplate", @ide.user.get("id"), (err)=>
  202. @hideWorking()
  203. if err?
  204. @showError()
  205. else
  206. @refreshPublishStatus()
  207. unPublishProjectAsTemplate: ->
  208. @showWorking()
  209. @publishedArea.hide()
  210. @ide.socket.emit "unPublishProjectAsTemplate", @ide.user.get("id"), (err)=>
  211. @hideWorking()
  212. if err?
  213. @showError()
  214. else
  215. @refreshPublishStatus()
  216. updateDescription: ->
  217. newDescription = $('#projectDescription').val()
  218. @model.set("description", newDescription)
  219. SocialSharingView = Backbone.View.extend
  220. template: $("#socialSharingTemplate").html()
  221. events:
  222. "click .btn-facebook": "postToFacebook"
  223. "click .btn-twitter": "postToTwitter"
  224. "click .btn-google-plus": "postToGoogle"
  225. "click .btn-url": "shareUrl"
  226. initialize: () ->
  227. @ide = @options.ide
  228. url: (medium) ->
  229. "#{window.sharelatex.siteUrl}/project/#{@ide.project.get("id")}" +
  230. "?r=#{@ide.user.get("referal_id")}&rs=ps&rm=#{medium}" # Referal source = public share
  231. render: ->
  232. $(@el).html $(@template)
  233. postToFacebook: () ->
  234. @ensurePublic (error, success) =>
  235. if success
  236. url = "https://www.facebook.com/dialog/feed?link=#{encodeURIComponent(@url("fb"))}&" +
  237. "app_id=148710621956179&" +
  238. "picture=#{window.sharelatex.siteUrl}/brand/logo/logo-128.png&" +
  239. "name=#{@ide.project.get("name")}&" +
  240. "caption=My LaTeX project (#{@ide.project.get("name")}) is available online on ShareLaTeX&" +
  241. "redirect_uri=#{window.sharelatex.siteUrl}&" +
  242. "display=popup"
  243. ga('send', 'event', 'editor-interaction', 'project-shared', "facebook")
  244. window.open(
  245. url
  246. ""
  247. 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600'
  248. )
  249. postToTwitter: () ->
  250. @ensurePublic (error, success) =>
  251. if success
  252. ga('send', 'event', 'editor-interaction', 'project-shared', "twitter")
  253. window.open(
  254. "https://www.twitter.com/share/?text=Check out my online LaTeX Project: #{@ide.project.get("name")}&url=#{encodeURIComponent(@url("t"))}"
  255. ""
  256. 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600'
  257. )
  258. postToGoogle: () ->
  259. @ensurePublic (error, success) =>
  260. if success
  261. ga('send', 'event', 'editor-interaction', 'project-shared', "google-plus")
  262. window.open(
  263. "https://plus.google.com/share?url=#{encodeURIComponent(@url("gp"))}"
  264. ""
  265. 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600'
  266. )
  267. shareUrl: () ->
  268. @ensurePublic (error, success) =>
  269. if success
  270. ga('send', 'event', 'editor-interaction', 'project-shared', "url")
  271. Modal.createModal
  272. el: $(
  273. "<p>You can share you project with your friends and colleagues via this URL:</p>" +
  274. "<pre>#{@url("d")}</pre>"
  275. )
  276. title: "Share your project"
  277. buttons: [
  278. text: "OK"
  279. class: "btn btn-primary"
  280. ]
  281. ensurePublic: (callback = (error, success) ->) ->
  282. accessLevel = @ide.project.get("publicAccesLevel")
  283. if accessLevel == "private"
  284. Modal.createModal
  285. title: "Make project public?"
  286. message: "Your project needs to be public before you can share it. This means anyone with the URL will be able to access it. Would you like to make your project public?"
  287. buttons: [{
  288. text: "Cancel"
  289. class: "btn"
  290. callback: () =>
  291. callback null, false
  292. }, {
  293. text: "Yes, make publicly readable"
  294. class: "btn btn-primary"
  295. callback: () =>
  296. @ide.project.set("publicAccesLevel", "readOnly")
  297. callback null, true
  298. }, {
  299. text: "Yes, make publicly editable"
  300. class: "btn btn-primary"
  301. callback: () =>
  302. @ide.project.set("publicAccesLevel", "readAndWrite")
  303. callback null, true
  304. }]
  305. else
  306. callback null, true
  307. return {
  308. ProjectMembersManager : ProjectMembersManager
  309. ProjectMemberList : ProjectMemberList
  310. ProjectMemberListView : ProjectMemberListView
  311. }