ProjectMembersManager.coffee 11 KB

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