Merge pull request #93 from taigaio/analytics

Analytics
stable
Alejandro 2014-10-13 11:03:30 +02:00
commit 84a4f2d26b
15 changed files with 281 additions and 124 deletions

View File

@ -186,13 +186,16 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
linewidth: "The subject must have a maximum size of %s"
})
init = ($log, $i18n, $config, $rootscope, $auth, $events) ->
init = ($log, $i18n, $config, $rootscope, $auth, $events, $analytics) ->
$i18n.initialize($config.get("defaultLanguage"))
$log.debug("Initialize application")
if $auth.isAuthenticated()
$events.setupConnection()
$analytics.initialize()
modules = [
# Main Global Modules
"taigaBase",
@ -244,5 +247,6 @@ module.run([
"$rootScope",
"$tgAuth",
"$tgEvents",
"$tgAnalytics",
init
])

View File

@ -214,7 +214,7 @@ module.directive("tgLogin", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgConfig"
## Register Directive
#############################################################################
RegisterDirective = ($auth, $confirm, $location, $navUrls, $config) ->
RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics) ->
link = ($scope, $el, $attrs) ->
if not $config.get("publicRegisterEnabled")
$location.path($navUrls.resolve("not-found"))
@ -224,6 +224,7 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config) ->
form = $el.find("form").checksley()
onSuccessSubmit = (response) ->
$analytics.trackEvent("auth", "register", "user registration", 1)
$confirm.notify("success", "Our Oompa Loompas are happy, welcome to Taiga.") #TODO: i18n
$location.path($navUrls.resolve("home"))
@ -251,7 +252,7 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config) ->
return {link:link}
module.directive("tgRegister", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgNavUrls", "$tgConfig",
RegisterDirective])
"$tgAnalytics", RegisterDirective])
#############################################################################
## Forgot Password Directive
@ -342,7 +343,7 @@ module.directive("tgChangePasswordFromRecovery", ["$tgAuth", "$tgConfirm", "$tgL
## Invitation
#############################################################################
InvitationDirective = ($auth, $confirm, $location, $params, $navUrls) ->
InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics) ->
link = ($scope, $el, $attrs) ->
token = $params.token
@ -360,6 +361,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls) ->
loginForm = $el.find("form.login-form").checksley()
onSuccessSubmitLogin = (response) ->
$analytics.trackEvent("auth", "invitationAccept", "invitation accept with existing user", 1)
$location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
$confirm.notify("success", "You've successfully joined this project",
"Welcome to #{$scope.invitation.project_name}")
@ -388,6 +390,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls) ->
registerForm = $el.find("form.register-form").checksley()
onSuccessSubmitRegister = (response) ->
$analytics.trackEvent("auth", "invitationAccept", "invitation accept with new user", 1)
$location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
$confirm.notify("success", "You've successfully joined this project",
"Welcome to #{$scope.invitation.project_name}")
@ -414,7 +417,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls) ->
return {link:link}
module.directive("tgInvitation", ["$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", InvitationDirective])
"$tgNavUrls", "$tgAnalytics", InvitationDirective])
#############################################################################
## Change Email

View File

@ -33,6 +33,7 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
link = ($scope, $el, attrs) ->
hasErrors = false
createSprint = true
$scope.sprint = {
project: null
name: null
@ -41,35 +42,41 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
}
submit = (event) ->
target = angular.element(event.currentTarget)
form = $el.find("form").checksley()
if not form.validate()
hasErrors = true
$el.find(".last-sprint-name").addClass("disappear")
return
hasErrors = false
hasErrors = false
newSprint = angular.copy($scope.sprint)
broadcastEvent = null
if createSprint
newSprint.estimated_start = moment(newSprint.estimated_start).format("YYYY-MM-DD")
newSprint.estimated_finish = moment(newSprint.estimated_finish).format("YYYY-MM-DD")
promise = $repo.create("milestones", newSprint)
broadcastEvent = "sprintform:create:success"
else
newSprint.setAttr("estimated_start", moment(newSprint.estimated_start).format("YYYY-MM-DD"))
newSprint.setAttr("estimated_finish", moment(newSprint.estimated_finish).format("YYYY-MM-DD"))
promise = $repo.save(newSprint)
broadcastEvent = "sprintform:edit:success"
target = angular.element(event.currentTarget)
$loading.start(target)
promise.then (data) ->
$loading.finish(target)
$scope.sprintsCounter += 1 if createSprint
$rootscope.$broadcast(broadcastEvent, data)
lightboxService.close($el)
$rootscope.$broadcast("sprintform:create:success", data)
promise.then null, (data) ->
$loading.finish(target)
form.setErrors(data)
if data._error_message
$confirm.notify("light-error", data._error_message)

View File

@ -47,16 +47,19 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
"$appTitle",
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q,
@location, @appTitle, @navUrls, @events, tgLoader) ->
@location, @appTitle, @navUrls, @events, @analytics, tgLoader) ->
_.bindAll(@)
@scope.sectionName = "Backlog"
@showTags = false
@.initializeEventHandlers()
promise = @.loadInitialData()
# On Success
@ -70,12 +73,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
tgLoader.pageLoaded()
# $(".backlog, .sidebar").mCustomScrollbar({
# theme: 'minimal-dark'
# scrollInertia: 0
# axis: 'y'
# })
# On Error
promise.then null, (xhr) =>
if xhr and xhr.status == 404
@ -83,16 +80,33 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@location.replace()
return @q.reject(xhr)
@scope.$on("usform:bulk:success", @.loadUserstories)
@scope.$on("sprintform:create:success", @.loadSprints)
@scope.$on("sprintform:create:success", @.loadProjectStats)
@scope.$on("sprintform:remove:success", @.loadSprints)
@scope.$on("sprintform:remove:success", @.loadProjectStats)
@scope.$on("sprintform:remove:success", @.loadUserstories)
@scope.$on("usform:new:success", @.loadUserstories)
@scope.$on("usform:edit:success", @.loadUserstories)
@scope.$on("usform:new:success", @.loadProjectStats)
@scope.$on("usform:bulk:success", @.loadProjectStats)
initializeEventHandlers: ->
@scope.$on "usform:bulk:success", =>
@.loadUserstories()
@.loadProjectStats()
@analytics.trackEvent("userstory", "create", "bulk create userstory on backlog", 1)
@scope.$on "sprintform:create:success", =>
@.loadSprints()
@.loadProjectStats()
@analytics.trackEvent("sprint", "create", "create sprint on backlog", 1)
@scope.$on "usform:new:success", =>
@.loadUserstories()
@.loadProjectStats()
@analytics.trackEvent("userstory", "create", "create userstory on backlog", 1)
@scope.$on "sprintform:edit:success", =>
@.loadProjectStats()
@scope.$on "sprintform:remove:success", =>
@.loadSprints()
@.loadProjectStats()
@.loadUserstories()
@scope.$on "usform:edit:success", =>
@.loadUserstories()
@scope.$on("sprint:us:move", @.moveUs)
@scope.$on("sprint:us:moved", @.loadSprints)
@scope.$on("sprint:us:moved", @.loadProjectStats)

View File

@ -0,0 +1,83 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/common/analytics.coffee
###
taiga = @.taiga
module = angular.module("taigaCommon")
class AnalyticsService extends taiga.Service
@.$inject = ["$rootScope", "$log", "$tgConfig", "$window", "$document", "$location"]
constructor: (@rootscope, @log, @config, @win, @doc, @location) ->
@.initialized = false
conf = @config.get("analytics", {})
@.accountId = conf.accountId
@.pageEvent = conf.pageEvent or "$routeChangeSuccess"
@.trackRoutes = conf.trackRoutes or true
@.ignoreFirstPageLoad = conf.ignoreFirstPageLoad or false
initialize: ->
if not @.accountId
@log.debug "Analytics: no acount id provided. Disabling."
return
@.injectAnalytics()
@win.ga("create", @.accountId, "auto")
@win.ga("require", "displayfeatures")
if @.trackRoutes and (not @.ignoreFirstPageLoad)
@win.ga("send", "pageview", @.getUrl());
# activates page tracking
if @.trackRoutes
@rootscope.$on @.pageEvent, =>
@.trackPage(@.getUrl())
@.initialized = true
getUrl: ->
return @location.path()
injectAnalytics: ->
fn = `(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments);},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m);})`
fn(window, document, "script", "//www.google-analytics.com/analytics.js", "ga")
trackPage: (url, title) ->
return if not @.initialized
title = title or @doc[0].title
@win.ga("send", "pageview", {
"page": url,
"title": title
})
trackEvent: (category, action, label, value) ->
return if not @.initialized
@win.ga("send", "event", category, action, label, value)
module.service("$tgAnalytics", AnalyticsService)

View File

@ -267,7 +267,6 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
if isNew
promise = $repo.create("userstories", $scope.us)
broadcastEvent = "usform:new:success"
else
promise = $repo.save($scope.us)
broadcastEvent = "usform:edit:success"
@ -315,15 +314,12 @@ CreateBulkUserstoriesDirective = ($repo, $rs, $rootscope, lightboxService, $load
$el.on "click", ".button-green", debounce 2000, (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
form = $el.find("form").checksley({
onlyOneErrorElement: true
})
form = $el.find("form").checksley({onlyOneErrorElement: true})
if not form.validate()
return
target = angular.element(event.currentTarget)
$loading.start(target)
promise = $rs.userstories.bulkCreate($scope.new.projectId, $scope.new.statusId, $scope.new.bulk)

View File

@ -45,12 +45,15 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgLocation",
"$log",
"$appTitle",
"$tgAnalytics",
"$tgNavUrls"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @log, @appTitle, @navUrls) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appTitle, @analytics, @navUrls) ->
@scope.issueRef = @params.issueref
@scope.sectionName = "Issue Details"
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -65,10 +68,20 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@location.replace()
return @q.reject(xhr)
@scope.$on "attachment:create", => @rootscope.$broadcast("history:reload")
@scope.$on "attachment:edit", => @rootscope.$broadcast("history:reload")
@scope.$on "attachment:delete", => @rootscope.$broadcast("history:reload")
initializeEventHandlers: ->
@scope.$on "attachment:create", =>
@rootscope.$broadcast("history:reload")
@analytics.trackEvent("attachment", "create", "create attachment on issue", 1)
@scope.$on "attachment:edit", =>
@rootscope.$broadcast("history:reload")
@scope.$on "attachment:delete", =>
@rootscope.$broadcast("history:reload")
@scope.$on "promote-issue-to-us:success", =>
@analytics.trackEvent("issue", "promoteToUserstory", "promote issue to userstory", 1)
@rootscope.$broadcast("history:reload")
@.loadIssue()

View File

@ -60,15 +60,16 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$appTitle",
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@appTitle, @navUrls, @events, tgLoader) ->
@appTitle, @navUrls, @events, @analytics, tgLoader) ->
_.bindAll(@)
@scope.sectionName = "Kanban"
@scope.statusViewModes = {}
@.initializeEventHandlers()
promise = @.loadInitialData()
# On Success
@ -83,8 +84,16 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@location.replace()
return @q.reject(xhr)
@scope.$on("usform:new:success", @.loadUserstories)
@scope.$on("usform:bulk:success", @.loadUserstories)
initializeEventHandlers: ->
@scope.$on "usform:new:success", =>
@.loadUserstories()
@analytics.trackEvent("userstory", "create", "create userstory on kanban", 1)
@scope.$on "usform:bulk:success", =>
@.loadUserstories()
@analytics.trackEvent("userstory", "create", "bulk create userstory on kanban", 1)
@scope.$on("usform:edit:success", @.loadUserstories)
@scope.$on("assigned-to:added", @.onAssignedToChanged)
@scope.$on("kanban:us:move", @.moveUs)

View File

@ -25,7 +25,7 @@ debounce = @.taiga.debounce
module = angular.module("taigaRelatedTasks", [])
RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading) ->
RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $analytics) ->
templateView = _.template("""
<div class="tasks">
<div class="task-name">
@ -168,7 +168,7 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading) ->
module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", RelatedTaskRowDirective])
RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading) ->
RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading, $analytics) ->
template = _.template("""
<div class="tasks">
<div class="task-name">
@ -209,6 +209,7 @@ RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading)
$loading.start($el.find('.task-name'))
promise = $repo.create("tasks", task)
promise.then ->
$analytics.trackEvent("task", "create", "create task on userstory", 1)
$loading.finish($el.find('.task-name'))
$scope.$emit("related-tasks:add")
$confirm.notify("success")

View File

@ -47,14 +47,16 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgLocation",
"$tgNavUrls"
"$tgEvents"
"$tgAnalytics",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appTitle, @location, @navUrls,
@events, tgLoader) ->
@events, @analytics, tgLoader) ->
_.bindAll(@)
@scope.sectionName = "Taskboard"
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -70,10 +72,17 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
@location.replace()
return @q.reject(xhr)
initializeEventHandlers: ->
# TODO: Reload entire taskboard after create/edit tasks seems
# a big overhead. It should be optimized in near future.
@scope.$on("taskform:bulk:success", => @.loadTaskboard())
@scope.$on("taskform:new:success", => @.loadTaskboard())
@scope.$on "taskform:bulk:success", =>
@.loadTaskboard()
@analytics.trackEvent("task", "create", "bulk create task on taskboard", 1)
@scope.$on "taskform:new:success", =>
@.loadTaskboard()
@analytics.trackEvent("task", "create", "create task on taskboard", 1)
@scope.$on("taskform:edit:success", => @.loadTaskboard())
@scope.$on("taskboard:task:move", @.taskMove)

View File

@ -43,13 +43,15 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$log",
"$appTitle",
"$tgNavUrls",
"$tgAnalytics",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @log, @appTitle, @navUrls,
tgLoader) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appTitle, @navUrls, @analytics, tgLoader) ->
@scope.taskRef = @params.taskref
@scope.sectionName = "Task Details"
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -63,10 +65,14 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@location.replace()
return @q.reject(xhr)
@scope.$on("attachment:create", => @rootscope.$broadcast("history:reload"))
@scope.$on("attachment:edit", => @rootscope.$broadcast("history:reload"))
@scope.$on("attachment:delete", => @rootscope.$broadcast("history:reload"))
initializeEventHandlers: ->
@scope.$on "attachment:create", =>
@analytics.trackEvent("attachment", "create", "create attachment on task", 1)
@rootscope.$broadcast("history:reload")
@scope.$on "attachment:edit", =>
@rootscope.$broadcast("history:reload")
@scope.$on "attachment:delete", =>
@rootscope.$broadcast("history:reload")
loadProject: ->
return @rs.projects.get(@scope.projectId).then (project) =>

View File

@ -44,12 +44,15 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$log",
"$appTitle",
"$tgNavUrls",
"$tgAnalytics",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @log, @appTitle, @navUrls, tgLoader) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appTitle, @navUrls, @analytics, tgLoader) ->
@scope.issueRef = @params.issueref
@scope.sectionName = "User Story Details"
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -65,9 +68,16 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@location.replace()
return @q.reject(xhr)
@scope.$on("attachment:create", => @rootscope.$broadcast("history:reload"))
@scope.$on("attachment:edit", => @rootscope.$broadcast("history:reload"))
@scope.$on("attachment:delete", => @rootscope.$broadcast("history:reload"))
initializeEventHandlers: ->
@scope.$on "attachment:create", =>
@analytics.trackEvent("attachment", "create", "create attachment on userstory", 1)
@rootscope.$broadcast("history:reload")
@scope.$on "attachment:edit", =>
@rootscope.$broadcast("history:reload")
@scope.$on "attachment:delete", =>
@rootscope.$broadcast("history:reload")
loadProject: ->
return @rs.projects.get(@scope.projectId).then (project) =>
@ -133,7 +143,6 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
unblock: ->
@rootscope.$broadcast("unblock", @scope.us)
delete: ->
#TODO: i18n
title = "Delete User Story"

View File

@ -47,11 +47,12 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$log",
"$appTitle",
"$tgNavUrls",
"$tgAnalytics",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @filter, @log, @appTitle,
@navUrls, tgLoader) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@filter, @log, @appTitle, @navUrls, @analytics, tgLoader) ->
@scope.projectSlug = @params.pslug
@scope.wikiSlug = @params.slug
@scope.sectionName = "Wiki"
@ -174,6 +175,7 @@ class WikiEditController extends WikiDetailController
if @scope.wiki.id
@repo.save(@scope.wiki).then onSuccess, onError
else
@analytics.trackEvent("wikipage", "create", "create wiki page", 1)
@scope.wiki.project = @scope.projectId
@scope.wiki.slug = @scope.wikiSlug
@repo.create("wiki", @scope.wiki).then onSuccess, onError

View File

@ -34,7 +34,7 @@ module = angular.module("taigaWiki")
## Wiki Main Directive
#############################################################################
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) ->
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $loading) ->
template = _.template("""
<header>
<h1>Links</h1>
@ -132,6 +132,7 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) ->
promise = $tgrepo.create("wiki-links", {project: $scope.projectId, title: newLink, href: slugify(newLink)})
promise.then ->
$analytics.trackEvent("wikilink", "create", "create wiki link", 1)
loadPromise = $ctrl.loadWikiLinks()
loadPromise.then ->
$loading.finish($el.find(".new"))
@ -166,4 +167,5 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) ->
return {link:link}
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls", "$tgLoading", WikiNavDirective])
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls",
"$tgAnalytics", "$tgLoading", WikiNavDirective])

View File

@ -38,4 +38,3 @@ html(lang="en", ng-app="taiga")
script(src="/js/libs.js?v=#{v}")
script(src="/js/app.js?v=#{v}")
tg-analytics