diff --git a/AUTHORS.rst b/AUTHORS.rst index 1afd86b6..c1066e92 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -7,14 +7,22 @@ The PRIMARY AUTHORS are: - Alejandro Alonso - Anler Hernández - Juan Francisco Alcántara +- Esther Moreno Riesco -Special thanks to Kaleidos Open Source S.L. for provide time for taiga +Special thanks to Kaleidos Open Source S.L. for provide time for Taiga development. And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- people who have submitted patches, reported bugs, added translations, helped -answer newbie questions, and generally made taiga that much better: +answer newbie questions, and generally made Taiga that much better: - Pilar Esteban - Guilhem Got - ... +- Ramiro Sánchez +- Miguel de la Cruz +- Andrea Stagi +- Jordan Rinke +- Wil Wade +- Daniel Koch +- Florian Bezagu +- Ryan Swanstrom diff --git a/CHANGELOG.md b/CHANGELOG.md index 62a7b842..f4648024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog # + +## 1.6.0 Abies Bifolia (2015-03-17) + +### Features +- Added custom fields per project for user stories, tasks and issues. +- Add to the Admin Panel the export to CSV sections. +- Reorganized the Admin Panel. + +### Misc +- New contrib plugin for hipchat (by Δndrea Stagi) +- Plugin based authentication. +- Added Taiga Style Guide in support Pages to enhance open source design. +- Lots of small and not so small bugfixes. + ## 1.5.0 Betula Pendula - FOSDEM 2015 (2015-01-29) ### Features diff --git a/README.md b/README.md index de60d8f1..c03c93a2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Taiga Front # ![Kaleidos Project](http://kaleidos.net/static/img/badge.png "Kaleidos Project") +[![Managed with Taiga](https://taiga.io/media/support/attachments/article-22/banner-gh.png)](https://taiga.io "Managed with Taiga") ## Get the compiled version ## diff --git a/app-loader/app-loader.coffee b/app-loader/app-loader.coffee index d17794e9..e216a9f0 100644 --- a/app-loader/app-loader.coffee +++ b/app-loader/app-loader.coffee @@ -8,7 +8,6 @@ window.taigaConfig = { "privacyPolicyUrl": null, "termsOfServiceUrl": null, "maxUploadFileSize": null, - "gitHubClientId": null, "contribPlugins": [] } diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index e7f49f72..6858425d 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -36,20 +36,26 @@ taiga.generateUniqueSessionIdentifier = -> taiga.sessionId = taiga.generateUniqueSessionIdentifier() -configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider) -> +configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider, $compileProvider) -> $routeProvider.when("/", {templateUrl: "project/projects.html", resolve: {loader: tgLoaderProvider.add()}}) + $routeProvider.when("/project/:pslug/", {templateUrl: "project/project.html"}) - $routeProvider.when("/project/:pslug/backlog", - {templateUrl: "backlog/backlog.html", resolve: {loader: tgLoaderProvider.add()}}) - $routeProvider.when("/project/:pslug/taskboard/:sslug", - {templateUrl: "taskboard/taskboard.html", resolve: {loader: tgLoaderProvider.add()}}) + $routeProvider.when("/project/:pslug/search", {templateUrl: "search/search.html", reloadOnSearch: false}) + + $routeProvider.when("/project/:pslug/backlog", + {templateUrl: "backlog/backlog.html", resolve: {loader: tgLoaderProvider.add()}}) + $routeProvider.when("/project/:pslug/kanban", {templateUrl: "kanban/kanban.html", resolve: {loader: tgLoaderProvider.add()}}) + # Milestone + $routeProvider.when("/project/:pslug/taskboard/:sslug", + {templateUrl: "taskboard/taskboard.html", resolve: {loader: tgLoaderProvider.add()}}) + # User stories $routeProvider.when("/project/:pslug/us/:usref", {templateUrl: "us/us-detail.html", resolve: {loader: tgLoaderProvider.add()}}) @@ -74,7 +80,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven $routeProvider.when("/project/:pslug/issue/:issueref", {templateUrl: "issue/issues-detail.html", resolve: {loader: tgLoaderProvider.add()}}) - # Admin + # Admin - Project Profile $routeProvider.when("/project/:pslug/admin/project-profile/details", {templateUrl: "admin/admin-project-profile.html"}) $routeProvider.when("/project/:pslug/admin/project-profile/default-values", @@ -83,24 +89,28 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven {templateUrl: "admin/admin-project-modules.html"}) $routeProvider.when("/project/:pslug/admin/project-profile/export", {templateUrl: "admin/admin-project-export.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/us-status", - {templateUrl: "admin/admin-project-values-us-status.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/us-points", - {templateUrl: "admin/admin-project-values-us-points.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/task-status", - {templateUrl: "admin/admin-project-values-task-status.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/issue-status", - {templateUrl: "admin/admin-project-values-issue-status.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/issue-types", - {templateUrl: "admin/admin-project-values-issue-types.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/issue-priorities", - {templateUrl: "admin/admin-project-values-issue-priorities.html"}) - $routeProvider.when("/project/:pslug/admin/project-values/issue-severities", - {templateUrl: "admin/admin-project-values-issue-severities.html"}) + $routeProvider.when("/project/:pslug/admin/project-profile/reports", + {templateUrl: "admin/admin-project-reports.html"}) + + $routeProvider.when("/project/:pslug/admin/project-values/status", + {templateUrl: "admin/admin-project-values-status.html"}) + $routeProvider.when("/project/:pslug/admin/project-values/points", + {templateUrl: "admin/admin-project-values-points.html"}) + $routeProvider.when("/project/:pslug/admin/project-values/priorities", + {templateUrl: "admin/admin-project-values-priorities.html"}) + $routeProvider.when("/project/:pslug/admin/project-values/severities", + {templateUrl: "admin/admin-project-values-severities.html"}) + $routeProvider.when("/project/:pslug/admin/project-values/types", + {templateUrl: "admin/admin-project-values-types.html"}) + $routeProvider.when("/project/:pslug/admin/project-values/custom-fields", + {templateUrl: "admin/admin-project-values-custom-fields.html"}) + $routeProvider.when("/project/:pslug/admin/memberships", {templateUrl: "admin/admin-memberships.html"}) + # Admin - Roles $routeProvider.when("/project/:pslug/admin/roles", {templateUrl: "admin/admin-roles.html"}) + # Admin - Third Parties $routeProvider.when("/project/:pslug/admin/third-parties/webhooks", {templateUrl: "admin/admin-third-parties-webhooks.html"}) $routeProvider.when("/project/:pslug/admin/third-parties/github", @@ -109,6 +119,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven {templateUrl: "admin/admin-third-parties-gitlab.html"}) $routeProvider.when("/project/:pslug/admin/third-parties/bitbucket", {templateUrl: "admin/admin-third-parties-bitbucket.html"}) + # Admin - Contrib Plugins $routeProvider.when("/project/:pslug/admin/contrib/:plugin", {templateUrl: "contrib/main.html"}) @@ -223,6 +234,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven linewidth: "The subject must have a maximum size of %s" }) + $compileProvider.debugInfoEnabled(window.taigaConfig.debugInfo || false) + init = ($log, $i18n, $config, $rootscope, $auth, $events, $analytics) -> $i18n.initialize($config.get("defaultLanguage")) $log.debug("Initialize application") @@ -280,6 +293,7 @@ module.config([ "$provide", "$tgEventsProvider", "tgLoaderProvider", + "$compileProvider", configure ]) diff --git a/app/coffee/modules/admin/lightboxes.coffee b/app/coffee/modules/admin/lightboxes.coffee index 6ad67d99..a900273c 100644 --- a/app/coffee/modules/admin/lightboxes.coffee +++ b/app/coffee/modules/admin/lightboxes.coffee @@ -33,7 +33,9 @@ MAX_MEMBERSHIP_FIELDSETS = 4 CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService) -> extraTextTemplate = """
- +
""" @@ -150,7 +152,6 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService) submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link: link} diff --git a/app/coffee/modules/admin/memberships.coffee b/app/coffee/modules/admin/memberships.coffee index dc21cfa3..574e38de 100644 --- a/app/coffee/modules/admin/memberships.coffee +++ b/app/coffee/modules/admin/memberships.coffee @@ -67,6 +67,9 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => + if not project.i_am_owner + @location.path(@navUrls.resolve("permission-denied")) + @scope.project = project @scope.$emit('project:loaded', project) return project @@ -307,7 +310,7 @@ MembershipsRowRoleSelectorDirective = ($log, $repo, $confirm) -> member = $scope.$eval($attrs.tgMembershipsRowRoleSelector) html = render(member) - $el.on "click", "select", (event) => + $el.on "change", "select", (event) => onSuccess = -> $confirm.notify("success") diff --git a/app/coffee/modules/admin/project-profile.coffee b/app/coffee/modules/admin/project-profile.coffee index fcdca9f4..8e109cd6 100644 --- a/app/coffee/modules/admin/project-profile.coffee +++ b/app/coffee/modules/admin/project-profile.coffee @@ -65,6 +65,9 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => + if not project.i_am_owner + @location.path(@navUrls.resolve("permission-denied")) + @scope.project = project @scope.pointsList = _.sortBy(project.points, "order") @scope.usStatusList = _.sortBy(project.us_statuses, "order") @@ -120,7 +123,6 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location) -> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -154,7 +156,6 @@ ProjectDefaultValuesDirective = ($repo, $confirm, $loading) -> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "$destroy", -> $el.off() @@ -233,7 +234,7 @@ ProjectExportDirective = ($window, $rs, $confirm) -> resultTitleEl = $el.find(".result-title") setLoadingTitle = -> resultTitleEl.html("We are generating your dump file") # TODO: i18n setAsyncTitle = -> resultTitleEl.html("We are generating your dump file") # TODO: i18n - setSyncTitle = -> resultTitleEl.html("Your dump file ir ready!") # TODO: i18n + setSyncTitle = -> resultTitleEl.html("Your dump file is ready!") # TODO: i18n resultMessageEl = $el.find(".result-message ") setLoadingMessage = -> resultMessageEl.html("Please don't close this page.") # TODO: i18n @@ -296,3 +297,67 @@ ProjectExportDirective = ($window, $rs, $confirm) -> return {link:link} module.directive("tgProjectExport", ["$window", "$tgResources", "$tgConfirm", ProjectExportDirective]) + + +############################################################################# +## CSV Export Controllers +############################################################################# + +class CsvExporterController extends taiga.Controller + @.$inject = [ + "$scope", + "$rootScope", + "$tgUrls", + "$tgConfirm", + "$tgResources", + ] + + constructor: (@scope, @rootscope, @urls, @confirm, @rs) -> + @rootscope.$on("project:loaded", @.setCsvUuid) + @scope.$watch "csvUuid", (value) => + if value + @scope.csvUrl = @urls.resolveAbsolute("#{@.type}-csv", value) + else + @scope.csvUrl = "" + + setCsvUuid: => + @scope.csvUuid = @scope.project["#{@.type}_csv_uuid"] + + _generateUuid: (finish) => + promise = @rs.projects["regenerate_#{@.type}_csv_uuid"](@scope.projectId) + + promise.then (data) => + @scope.csvUuid = data.data?.uuid + + promise.then null, => + @confirm.notify("error") + + promise.finally -> + finish() + return promise + + regenerateUuid: -> + #TODO: i18n + if @scope.csvUuid + title = "Change URL" + subtitle = "You going to change the CSV data access url. The previous url will be disabled. Are you sure?" + @confirm.ask(title, subtitle).then @._generateUuid + else + @._generateUuid(_.identity) + + +class CsvExporterUserstoriesController extends CsvExporterController + type: "userstories" + + +class CsvExporterTasksController extends CsvExporterController + type: "tasks" + + +class CsvExporterIssuesController extends CsvExporterController + type: "issues" + + +module.controller("CsvExporterUserstoriesController", CsvExporterUserstoriesController) +module.controller("CsvExporterTasksController", CsvExporterTasksController) +module.controller("CsvExporterIssuesController", CsvExporterIssuesController) diff --git a/app/coffee/modules/admin/project-values.coffee b/app/coffee/modules/admin/project-values.coffee index 188daba2..35390de9 100644 --- a/app/coffee/modules/admin/project-values.coffee +++ b/app/coffee/modules/admin/project-values.coffee @@ -32,10 +32,10 @@ debounce = @.taiga.debounce module = angular.module("taigaAdmin") ############################################################################# -## Project values Controller +## Project values section Controller ############################################################################# -class ProjectValuesController extends mixOf(taiga.Controller, taiga.PageMixin) +class ProjectValuesSectionController extends mixOf(taiga.Controller, taiga.PageMixin) @.$inject = [ "$scope", "$rootScope", @@ -59,29 +59,47 @@ class ProjectValuesController extends mixOf(taiga.Controller, taiga.PageMixin) promise.then null, @.onInitialDataError.bind(@) - @scope.$on("admin:project-values:move", @.moveValue) - loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => + if not project.i_am_owner + @location.path(@navUrls.resolve("permission-denied")) + @scope.project = project @scope.$emit('project:loaded', project) return project - loadValues: -> - return @rs[@scope.resource].listValues(@scope.projectId, @scope.type).then (values) => - @scope.values = values - @scope.maxValueOrder = _.max(values, "order").order - return values - loadInitialData: -> promise = @repo.resolve({pslug: @params.pslug}).then (data) => @scope.projectId = data.project return data - return promise.then( => @q.all([ - @.loadProject(), - @.loadValues(), - ])) + return promise.then => @.loadProject() + + +module.controller("ProjectValuesSectionController", ProjectValuesSectionController) + +############################################################################# +## Project values Controller +############################################################################# + +class ProjectValuesController extends taiga.Controller + @.$inject = [ + "$scope", + "$rootScope", + "$tgRepo", + "$tgConfirm", + "$tgResources", + ] + + constructor: (@scope, @rootscope, @repo, @confirm, @rs) -> + @scope.$on("admin:project-values:move", @.moveValue) + @rootscope.$on("project:loaded", @.loadValues) + + loadValues: => + return @rs[@scope.resource].listValues(@scope.projectId, @scope.type).then (values) => + @scope.values = values + @scope.maxValueOrder = _.max(values, "order").order + return values moveValue: (ctx, itemValue, itemIndex) => values = @scope.values @@ -147,21 +165,14 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) -> $(document.body).scrollTop(table.offset().top + table.height()) if focus - $(".new-value input").focus() + $el.find(".new-value input:visible").first().focus() - submit = debounce 2000, => - promise = $repo.save($scope.project) - promise.then -> - $confirm.notify("success") - - promise.then null, (data) -> - $confirm.notify("error", data._error_message) - - saveValue = debounce 2000, (target) -> - form = target.parents("form").checksley() + saveValue = (target) -> + formEl = target.parents("form") + form = formEl.checksley() return if not form.validate() - value = target.scope().value + value = formEl.scope().value promise = $repo.save(value) promise.then => row = target.parents(".row.table-main") @@ -169,25 +180,37 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) -> row.siblings(".visualization").removeClass('hidden') promise.then null, (data) -> - $confirm.notify("error") + form.setErrors(data) + + saveNewValue = (target) -> + formEl = target.parents("form") + form = formEl.checksley() + return if not form.validate() + + $scope.newValue.project = $scope.project.id + + $scope.newValue.order = if $scope.maxValueOrder then $scope.maxValueOrder + 1 else 1 + + promise = $repo.create(valueType, $scope.newValue) + promise.then (data) => + target.addClass("hidden") + + $scope.values.push(data) + $scope.maxValueOrder = data.order + initializeNewValue() + + promise.then null, (data) -> form.setErrors(data) cancel = (target) -> row = target.parents(".row.table-main") - value = target.scope().value + formEl = target.parents("form") + value = formEl.scope().value $scope.$apply -> row.addClass("hidden") value.revert() row.siblings(".visualization").removeClass('hidden') - $el.on "submit", "form", (event) -> - event.preventDefault() - submit() - - $el.on "click", "form a.button-green", (event) -> - event.preventDefault() - submit() - $el.on "click", ".show-add-new", (event) -> event.preventDefault() $el.find(".new-value").removeClass('hidden') @@ -196,29 +219,12 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) -> $el.on "click", ".add-new", debounce 2000, (event) -> event.preventDefault() - form = $el.find(".new-value").parents("form").checksley() - return if not form.validate() - - $scope.newValue.project = $scope.project.id - - $scope.newValue.order = if $scope.maxValueOrder then $scope.maxValueOrder + 1 else 1 - - promise = $repo.create(valueType, $scope.newValue) - promise.then => - $ctrl.loadValues().then -> - animationFrame.add () -> - goToBottomList() - - $el.find(".new-value").addClass("hidden") - initializeNewValue() - - promise.then null, (data) -> - $confirm.notify("error") - form.setErrors(data) + target = $el.find(".new-value") + saveNewValue(target) $el.on "click", ".delete-new", (event) -> event.preventDefault() - $el.find(".new-value").hide() + $el.find(".new-value").addClass("hidden") initializeNewValue() $el.on "click", ".edit-value", (event) -> @@ -240,6 +246,14 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) -> target = angular.element(event.currentTarget) cancel(target) + $el.on "keyup", ".new-value input", (event) -> + if event.keyCode == 13 + target = $el.find(".new-value") + saveNewValue(target) + else if event.keyCode == 27 + $el.find(".new-value").addClass("hidden") + initializeNewValue() + $el.on "click", ".save", (event) -> event.preventDefault() target = angular.element(event.currentTarget) @@ -253,7 +267,9 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) -> $el.on "click", ".delete-value", (event) -> event.preventDefault() target = angular.element(event.currentTarget) - value = target.scope().value + formEl = target.parents("form") + value = formEl.scope().value + choices = {} _.each $scope.values, (option) -> if value.id != option.id @@ -337,3 +353,276 @@ ColorSelectionDirective = () -> } module.directive("tgColorSelection", ColorSelectionDirective) + + +############################################################################# +## Custom Attributes Controller +############################################################################# + +class ProjectCustomAttributesController extends mixOf(taiga.Controller, taiga.PageMixin) + @.$inject = [ + "$scope", + "$rootScope", + "$tgRepo", + "$tgResources", + "$routeParams", + "$q", + "$tgLocation", + "$tgNavUrls", + "$appTitle", + ] + + constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle) -> + @scope.project = {} + + @rootscope.$on "project:loaded", => + @.loadCustomAttributes() + @appTitle.set("Project Custom Attributes - " + @scope.sectionName + " - " + @scope.project.name) + + ######################### + # Custom Attribute + ######################### + + loadCustomAttributes: => + return @rs.customAttributes[@scope.type].list(@scope.projectId).then (customAttributes) => + @scope.customAttributes = customAttributes + @scope.maxOrder = _.max(customAttributes, "order").order + return customAttributes + + createCustomAttribute: (attrValues) => + return @repo.create("custom-attributes/#{@scope.type}", attrValues) + + saveCustomAttribute: (attrModel) => + return @repo.save(attrModel) + + deleteCustomAttribute: (attrModel) => + return @repo.remove(attrModel) + + moveCustomAttributes: (attrModel, newIndex) => + customAttributes = @scope.customAttributes + r = customAttributes.indexOf(attrModel) + customAttributes.splice(r, 1) + customAttributes.splice(newIndex, 0, attrModel) + + _.each customAttributes, (val, idx) -> + val.order = idx + + @repo.saveAll(customAttributes) + + +module.controller("ProjectCustomAttributesController", ProjectCustomAttributesController) + + +############################################################################# +## Custom Attributes Directive +############################################################################# + +ProjectCustomAttributesDirective = ($log, $confirm, animationFrame) -> + link = ($scope, $el, $attrs) -> + $ctrl = $el.controller() + + $scope.$on "$destroy", -> + $el.off() + + ################################## + # Drag & Drop + ################################## + sortableEl = $el.find(".js-sortable") + + sortableEl.sortable({ + handle: ".js-view-custom-field", + dropOnEmpty: true + revert: 400 + axis: "y" + }) + + sortableEl.on "sortstop", (event, ui) -> + itemEl = ui.item + itemAttr = itemEl.scope().attr + itemIndex = itemEl.index() + $ctrl.moveCustomAttributes(itemAttr, itemIndex) + + ################################## + # New custom attribute + ################################## + + showCreateForm = -> + $el.find(".js-new-custom-field").removeClass("hidden") + $el.find(".js-new-custom-field input:visible").first().focus() + + hideCreateForm = -> + $el.find(".js-new-custom-field").addClass("hidden") + + showAddButton = -> + $el.find(".js-add-custom-field-button").removeClass("hidden") + + hideAddButton = -> + $el.find(".js-add-custom-field-button").addClass("hidden") + + showCancelButton = -> + $el.find(".js-cancel-new-custom-field-button").removeClass("hidden") + + hideCancelButton = -> + $el.find(".js-cancel-new-custom-field-button").addClass("hidden") + + resetNewAttr = -> + $scope.newAttr = {} + + create = (formEl) -> + form = formEl.checksley() + return if not form.validate() + + onSucces = => + $ctrl.loadCustomAttributes() + hideCreateForm() + resetNewAttr() + $confirm.notify("success") + + onError = (data) => + form.setErrors(data) + + attr = $scope.newAttr + attr.project = $scope.projectId + attr.order = if $scope.maxOrder then $scope.maxOrder + 1 else 1 + + $ctrl.createCustomAttribute(attr).then(onSucces, onError) + + cancelCreate = -> + hideCreateForm() + resetNewAttr() + + $scope.$watch "customAttributes", (customAttributes) -> + return if not customAttributes + + if customAttributes.length == 0 + hideCancelButton() + hideAddButton() + showCreateForm() + else + hideCreateForm() + showAddButton() + showCancelButton() + + $el.on "click", ".js-add-custom-field-button", (event) -> + event.preventDefault() + + showCreateForm() + + $el.on "click", ".js-create-custom-field-button", debounce 2000, (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + formEl = target.closest("form") + + create(formEl) + + $el.on "click", ".js-cancel-new-custom-field-button", (event) -> + event.preventDefault() + + cancelCreate() + + $el.on "keyup", ".js-new-custom-field input", (event) -> + if event.keyCode == 13 # Enter + target = angular.element(event.currentTarget) + formEl = target.closest("form") + create(formEl) + else if event.keyCode == 27 # Esc + cancelCreate() + + ################################## + # Edit custom attribute + ################################## + + showEditForm = (formEl) -> + formEl.find(".js-view-custom-field").addClass("hidden") + formEl.find(".js-edit-custom-field").removeClass("hidden") + formEl.find(".js-edit-custom-field input:visible").first().focus().select() + + hideEditForm = (formEl) -> + formEl.find(".js-edit-custom-field").addClass("hidden") + formEl.find(".js-view-custom-field").removeClass("hidden") + + revertChangesInCustomAttribute = (formEl) -> + $scope.$apply -> + formEl.scope().attr.revert() + + update = (formEl) -> + form = formEl.checksley() + return if not form.validate() + + onSucces = => + $ctrl.loadCustomAttributes() + hideEditForm(formEl) + $confirm.notify("success") + + onError = (data) => + form.setErrors(data) + + attr = formEl.scope().attr + $ctrl.saveCustomAttribute(attr).then(onSucces, onError) + + cancelUpdate = (formEl) -> + hideEditForm(formEl) + revertChangesInCustomAttribute(formEl) + + $el.on "click", ".js-edit-custom-field-button", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + formEl = target.closest("form") + + showEditForm(formEl) + + $el.on "click", ".js-update-custom-field-button", debounce 2000, (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + formEl = target.closest("form") + + update(formEl) + + $el.on "click", ".js-cancel-edit-custom-field-button", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + formEl = target.closest("form") + + cancelUpdate(formEl) + + $el.on "keyup", ".js-edit-custom-field input", (event) -> + if event.keyCode == 13 # Enter + target = angular.element(event.currentTarget) + formEl = target.closest("form") + update(formEl) + else if event.keyCode == 27 # Esc + target = angular.element(event.currentTarget) + formEl = target.closest("form") + cancelUpdate(formEl) + + ################################## + # Delete custom attribute + ################################## + + deleteCustomAttribute = (formEl) -> + attr = formEl.scope().attr + + title = "Delete custom attribute" # i18n + subtitle = "Remeber that all values in this custom field will be deleted.
Are you sure you want to continue?" + message = attr.name + $confirm.ask(title, subtitle, message).then (finish) -> + onSucces = -> + $ctrl.loadCustomAttributes().finally -> + finish() + + onError = -> + finish(false) + $confirm.notify("error", null, "We have not been able to delete '#{message}'.") + + $ctrl.deleteCustomAttribute(attr).then(onSucces, onError) + + $el.on "click", ".js-delete-custom-field-button", debounce 2000, (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + formEl = target.closest("form") + + deleteCustomAttribute(formEl) + + return {link: link} + +module.directive("tgProjectCustomAttributes", ["$log", "$tgConfirm", "animationFrame", ProjectCustomAttributesDirective]) diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index b14effb2..ff47fca8 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -63,17 +63,40 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => + if not project.i_am_owner + @location.path(@navUrls.resolve("permission-denied")) + @scope.project = project + @scope.$emit('project:loaded', project) @scope.anyComputableRole = _.some(_.map(project.roles, (point) -> point.computable)) return project + loadExternalUserRole: (roles) -> + roles = roles.map (role) -> + role.external_user = false + + return role + + public_permission = { + "name": "External User", + "permissions": @scope.project.public_permissions, + "external_user": true + } + + roles.push(public_permission) + + return roles + loadRoles: -> - return @rs.roles.list(@scope.projectId).then (data) => - @scope.roles = data - @scope.role = @scope.roles[0] - return data + return @rs.roles.list(@scope.projectId) + .then @loadExternalUserRole + .then (roles) => + @scope.roles = roles + @scope.role = @scope.roles[0] + + return roles loadInitialData: -> promise = @repo.resolve({pslug: @params.pslug}).then (data) => @@ -256,7 +279,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
<%- permission.description %>
- checked="checked"<% } %>/> + disabled="disabled"<% } %> <% if(permission.active) { %>checked="checked"<% } %>/>
Yes No @@ -279,10 +302,23 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) -> setActivePermissions = (permissions) -> return _.map(permissions, (x) -> _.extend({}, x, {active: x["key"] in role.permissions})) + isPermissionEditable = (permission, role, project) -> + if role.external_user && + !project.is_private && + permission.key.indexOf("view_") == 0 + return false + else + return true + setActivePermissionsPerCategory = (category) -> - return _.map(category, (x) -> - _.extend({}, x, { - activePermissions: _.filter(x["permissions"], "active").length + return _.map(category, (cat) -> + cat.permissions = cat.permissions.map (permission) -> + permission.editable = isPermissionEditable(permission, role, $scope.project) + + return permission + + _.extend({}, cat, { + activePermissions: _.filter(cat["permissions"], "active").length }) ) @@ -366,10 +402,11 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) -> return activePermissions target = angular.element(event.currentTarget) + $scope.role.permissions = getActivePermissions() - onSuccess = (role) -> - categories = generateCategoriesFromRole(role) + onSuccess = () -> + categories = generateCategoriesFromRole($scope.role) categoryId = target.parents(".category-config").data("id") renderResume(target.parents(".category-config"), categories[categoryId]) $rootscope.$broadcast("projects:reload") @@ -381,7 +418,14 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) -> target.prop "checked", !target.prop("checked") $scope.role.permissions = getActivePermissions() - $repo.save($scope.role).then onSuccess, onError + if $scope.role.external_user + $scope.project.public_permissions = $scope.role.permissions + $scope.project.anon_permissions = $scope.role.permissions.filter (permission) -> + return permission.indexOf("view_") == 0 + + $repo.save($scope.project).then onSuccess, onError + else + $repo.save($scope.role).then onSuccess, onError $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/admin/third-parties.coffee b/app/coffee/modules/admin/third-parties.coffee index 3e611b46..ceeaaf2f 100644 --- a/app/coffee/modules/admin/third-parties.coffee +++ b/app/coffee/modules/admin/third-parties.coffee @@ -38,10 +38,12 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga. "$tgRepo", "$tgResources", "$routeParams", + "$tgLocation", + "$tgNavUrls", "$appTitle" ] - constructor: (@scope, @repo, @rs, @params, @appTitle) -> + constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appTitle) -> bindMethods(@) @scope.sectionName = "Webhooks" #i18n @@ -62,6 +64,9 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga. loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => + if not project.i_am_owner + @location.path(@navUrls.resolve("permission-denied")) + @scope.project = project @scope.$emit('project:loaded', project) return project @@ -89,7 +94,7 @@ WebhookDirective = ($rs, $repo, $confirm, $loading) -> for log in webhooklogs log.validStatus = 200 <= log.status < 300 log.prettySentHeaders = _.map(_.pairs(log.request_headers), ([header, value]) -> "#{header}: #{value}").join("\n") - log.prettySentData = JSON.stringify(log.request_data.data, undefined, 2) + log.prettySentData = JSON.stringify(log.request_data) log.prettyDate = moment(log.created).format("DD MMM YYYY [at] hh:mm:ss") # TODO: i18n webhook.logs_counter = webhooklogs.length @@ -123,8 +128,6 @@ WebhookDirective = ($rs, $repo, $confirm, $loading) -> save = debounce 2000, (target) -> form = target.parents("form").checksley() return if not form.validate() - - value = target.scope().value promise = $repo.save(webhook) promise.then => showVisualizationMode() @@ -152,7 +155,7 @@ WebhookDirective = ($rs, $repo, $confirm, $loading) -> $el.on "keyup", ".edition-mode input", (event) -> if event.keyCode == 13 target = angular.element(event.currentTarget) - saveWebhook(target) + save(target) else if event.keyCode == 27 target = angular.element(event.currentTarget) cancel(target) @@ -231,8 +234,7 @@ NewWebhookDirective = ($rs, $repo, $confirm, $loading) -> formDOMNode.addClass("hidden") addWebhookDOMNode.removeClass("hidden") - formDOMNode.on "click", ".add-new", debounce 2000, (event) -> - event.preventDefault() + save = debounce 2000, () -> form = formDOMNode.checksley() return if not form.validate() @@ -246,6 +248,14 @@ NewWebhookDirective = ($rs, $repo, $confirm, $loading) -> $confirm.notify("error") form.setErrors(data) + formDOMNode.on "click", ".add-new", (event) -> + event.preventDefault() + save() + + formDOMNode.on "keyup", "input", (event) -> + if event.keyCode == 13 + save() + formDOMNode.on "click", ".cancel-new", (event) -> $scope.$apply -> initializeNewValue() @@ -445,7 +455,6 @@ GithubWebhooksDirective = ($repo, $confirm, $loading) -> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -481,7 +490,6 @@ GitlabWebhooksDirective = ($repo, $confirm, $loading) -> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -517,7 +525,6 @@ BitbucketWebhooksDirective = ($repo, $confirm, $loading) -> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} diff --git a/app/coffee/modules/auth.coffee b/app/coffee/modules/auth.coffee index 9086f1eb..856488ef 100644 --- a/app/coffee/modules/auth.coffee +++ b/app/coffee/modules/auth.coffee @@ -197,11 +197,12 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $ "password": $el.find("form.login-form input[name=password]").val() } - promise = $auth.login(data) + loginFormType = $config.get("loginFormType", "normal") + + promise = $auth.login(data, loginFormType) return promise.then(onSuccess, onError) $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -242,7 +243,6 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics) promise.then(onSuccessSubmit, onErrorSubmit) $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -279,7 +279,6 @@ ForgotPasswordDirective = ($auth, $confirm, $location, $navUrls) -> promise.then(onSuccessSubmit, onErrorSubmit) $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -321,7 +320,6 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav promise.then(onSuccessSubmit, onErrorSubmit) $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -471,7 +469,6 @@ CancelAccountDirective = ($repo, $model, $auth, $confirm, $location, $params, $n promise.then(onSuccessSubmit, onErrorSubmit) $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} diff --git a/app/coffee/modules/backlog/filters.coffee b/app/coffee/modules/backlog/filters.coffee index 56199e3e..8185fdf2 100644 --- a/app/coffee/modules/backlog/filters.coffee +++ b/app/coffee/modules/backlog/filters.coffee @@ -89,12 +89,10 @@ BacklogFiltersDirective = ($log, $location, $templates) -> selectedFilters.push(filter) $scope.$apply -> $ctrl.selectFilter(type, id) - $ctrl.filterVisibleUserstories() else selectedFilters = _.reject(selectedFilters, filter) $scope.$apply -> $ctrl.unselectFilter(type, id) - $ctrl.filterVisibleUserstories() renderSelectedFilters(selectedFilters) diff --git a/app/coffee/modules/backlog/lightboxes.coffee b/app/coffee/modules/backlog/lightboxes.coffee index 9f620178..d77ac2ee 100644 --- a/app/coffee/modules/backlog/lightboxes.coffee +++ b/app/coffee/modules/backlog/lightboxes.coffee @@ -103,6 +103,9 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading) $repo.remove($scope.sprint).then(onSuccess, onError) $scope.$on "sprintform:create", (event, projectId) -> + form = $el.find("form").checksley() + form.reset() + createSprint = true $scope.sprint.project = projectId $scope.sprint.name = null @@ -158,7 +161,6 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading) submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $el.on "click", ".delete-sprint .icon-delete", (event) -> event.preventDefault() diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee index dfd8336f..ab7773c8 100644 --- a/app/coffee/modules/backlog/main.coffee +++ b/app/coffee/modules/backlog/main.coffee @@ -59,7 +59,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F @scope.sectionName = "Backlog" @showTags = false @activeFilters = false - @excludeClosedSprints = true @.initializeEventHandlers() @@ -111,7 +110,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F @scope.$on("sprint:us:moved", @.loadSprints) @scope.$on("sprint:us:moved", @.loadProjectStats) - @scope.$on("backlog:toggle-closed-sprints-visualization", @.toggleClosedSprintsVisualization) + @scope.$on("backlog:load-closed-sprints", @.loadClosedSprints) + @scope.$on("backlog:unload-closed-sprints", @.unloadClosedSprints) initializeSubscription: -> routingKey1 = "changes.project.#{@scope.projectId}.userstories" @@ -146,11 +146,23 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F return @rs.projects.tagsColors(@scope.projectId).then (tags_colors) => @scope.project.tags_colors = tags_colors - loadSprints: -> - params = {} - if @excludeClosedSprints - params["closed"] = false + unloadClosedSprints: -> + @scope.$apply => + @scope.closedSprints = [] + @rootscope.$broadcast("closed-sprints:reloaded", []) + loadClosedSprints: -> + params = {closed: true} + return @rs.sprints.list(@scope.projectId, params).then (sprints) => + # NOTE: Fix order of USs because the filter orderBy does not work propertly in partials files + for sprint in sprints + sprint.user_stories = _.sortBy(sprint.user_stories, "sprint_order") + @scope.closedSprints = sprints + @rootscope.$broadcast("closed-sprints:reloaded", sprints) + return sprints + + loadSprints: -> + params = {closed: false} return @rs.sprints.list(@scope.projectId, params).then (sprints) => # NOTE: Fix order of USs because the filter orderBy does not work propertly in partials files for sprint in sprints @@ -158,10 +170,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F @scope.sprints = sprints @scope.openSprints = _.filter(sprints, (sprint) => not sprint.closed).reverse() - @scope.closedSprints = _.filter(sprints, (sprint) => sprint.closed) - if not @excludeClosedSprints - @scope.totalClosedMilestones = @scope.closedSprints.length - + @scope.closedSprints = [] if !@scope.closedSprints + @scope.sprintsCounter = sprints.length @scope.sprintsById = groupBy(sprints, (x) -> x.id) @rootscope.$broadcast("sprints:loaded", sprints) @@ -194,8 +204,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F # NOTE: Fix order of USs because the filter orderBy does not work propertly in the partials files @scope.userstories = _.sortBy(userstories, "backlog_order") - @.generateFilters() + @.setSearchDataFilters() @.filterVisibleUserstories() + @.generateFilters() @rootscope.$broadcast("filters:loaded", @scope.filters) # The broadcast must be executed when the DOM has been fully reloaded. @@ -214,6 +225,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F loadProject: -> return @rs.projects.getBySlug(@params.pslug).then (project) => + if not project.is_backlog_activated + @location.path(@navUrls.resolve("permission-denied")) + @scope.projectId = project.id @scope.project = project @scope.totalClosedMilestones = project.total_closed_milestones @@ -232,40 +246,20 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F return promise.then(=> @.loadBacklog()) - toggleClosedSprintsVisualization: -> - @excludeClosedSprints = not @excludeClosedSprints - @.loadSprints() - filterVisibleUserstories: -> @scope.visibleUserstories = [] # Filter by tags - selectedTags = _.filter(@scope.filters.tags, "selected") - selectedTags = _.map(selectedTags, "name") - - if selectedTags.length == 0 - @scope.visibleUserstories = _.clone(@scope.userstories, false) - else - @scope.visibleUserstories = _.reject @scope.userstories, (us) => - if _.intersection(selectedTags, us.tags).length == 0 - return true - return false + @scope.visibleUserstories = _.reject @scope.userstories, (us) => + return _.some us.tags, (tag) => + return @isFilterSelected("tag", tag) # Filter by status - selectedStatuses = _.filter(@scope.filters.statuses, "selected") - selectedStatuses = _.map(selectedStatuses, "id") + @scope.visibleUserstories = _.filter @scope.visibleUserstories, (us) => + if @searchdata["statuses"] && Object.keys(@searchdata["statuses"]).length + return @isFilterSelected("statuses", taiga.toString(us.status)) - if selectedStatuses.length > 0 - @scope.visibleUserstories = _.reject @scope.visibleUserstories, (us) => - res = _.find(selectedStatuses, (x) -> x == taiga.toString(us.status)) - return not res - - @rs.userstories.storeQueryParams(@scope.projectId, { - "status": selectedStatuses, - "tags": selectedTags, - "project": @scope.projectId - "milestone": null - }) + return true prepareBulkUpdateData: (uses, field="backlog_order") -> return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]}) @@ -422,31 +416,33 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F return promise - getUrlFilters: -> - return _.pick(@location.search(), "statuses", "tags", "q") + isFilterSelected: (type, id) -> + if @searchdata[type]? and @searchdata[type][id] + return true + return false - generateFilters: -> + setSearchDataFilters: () -> urlfilters = @.getUrlFilters() if urlfilters.q @scope.filtersQ = @scope.filtersQ or urlfilters.q - searchdata = {} + @searchdata = {} for name, value of urlfilters - if not searchdata[name]? - searchdata[name] = {} + if not @searchdata[name]? + @searchdata[name] = {} for val in taiga.toString(value).split(",") - searchdata[name][val] = true + @searchdata[name][val] = true - isSelected = (type, id) -> - if searchdata[type]? and searchdata[type][id] - return true - return false + getUrlFilters: -> + return _.pick(@location.search(), "statuses", "tags", "q") + generateFilters: -> @scope.filters = {} - plainTags = _.flatten(_.filter(_.map(@scope.userstories, "tags"))) + #tags + plainTags = _.flatten(_.filter(_.map(@scope.visibleUserstories, "tags"))) plainTags.sort() @scope.filters.tags = _.map _.countBy(plainTags), (v, k) => @@ -457,10 +453,14 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F color: @scope.project.tags_colors[k], count: v } - obj.selected = true if isSelected("tags", obj.id) + obj.selected = true if @isFilterSelected("tags", obj.id) return obj - plainStatuses = _.map(@scope.userstories, "status") + selectedTags = _.filter(@scope.filters.tags, "selected") + selectedTags = _.map(selectedTags, "name") + + #status + plainStatuses = _.map(@scope.visibleUserstories, "status") plainStatuses = _.filter plainStatuses, (status) => if status @@ -474,11 +474,20 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F color: @scope.usStatusById[k].color, count:v } - obj.selected = true if isSelected("statuses", obj.id) + obj.selected = true if @isFilterSelected("statuses", obj.id) return obj - return @scope.filters + selectedStatuses = _.filter(@scope.filters.statuses, "selected") + selectedStatuses = _.map(selectedStatuses, "id") + + #store query params + @rs.userstories.storeQueryParams(@scope.projectId, { + "status": selectedStatuses, + "tags": selectedTags, + "project": @scope.projectId + "milestone": null + }) ## Template actions @@ -514,7 +523,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F module.controller("BacklogController", BacklogController) - ############################################################################# ## Backlog Directive ############################################################################# @@ -531,29 +539,27 @@ BacklogDirective = ($repo, $rootscope) -> if $scope.stats? removeDoomlineDom() - elements = getUsItems() stats = $scope.stats total_points = stats.total_points current_sum = stats.assigned_points - for element in elements - scope = element.scope() + return if not $scope.visibleUserstories - if not scope.us? - continue - - current_sum += scope.us.total_points + for us, i in $scope.visibleUserstories + current_sum += us.total_points if current_sum > total_points - addDoomLineDom(element) + domElement = $el.find('.backlog-table-body .us-item-row')[i] + addDoomLineDom(domElement) + break removeDoomlineDom = -> $el.find(".doom-line").remove() addDoomLineDom = (element) -> - element?.before(doomLineTemplate({})) + $(element).before(doomLineTemplate({})) getUsItems = -> rowElements = $el.find('.backlog-table-body .us-item-row') @@ -604,7 +610,8 @@ BacklogDirective = ($repo, $rootscope) -> ussDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked") ussToMove = _.map ussDom, (item) -> - itemScope = angular.element(item).scope() + item = $(item).closest('.tg-scope') + itemScope = item.scope() itemScope.us.milestone = $scope.sprints[0].id return itemScope.us @@ -727,7 +734,6 @@ UsRolePointsSelectorDirective = ($rootscope, $template) -> $el.on "click", ".role", (event) -> event.preventDefault() event.stopPropagation() - target = angular.element(event.currentTarget) rolScope = target.scope() $rootscope.$broadcast("uspoints:select", target.data("role-id"), target.text()) @@ -740,170 +746,107 @@ UsRolePointsSelectorDirective = ($rootscope, $template) -> module.directive("tgUsRolePointsSelector", ["$rootScope", "$tgTemplate", UsRolePointsSelectorDirective]) -UsPointsDirective = ($repo, $tgTemplate) -> - rolesTemplate = $tgTemplate.get("backlog/us-points-roles-popover.html", true) - pointsTemplate = $tgTemplate.get("backlog/us-points-popover.html", true) +UsPointsDirective = ($tgEstimationsService, $repo, $tgTemplate) -> + rolesTemplate = $tgTemplate.get("common/estimation/us-points-roles-popover.html", true) link = ($scope, $el, $attrs) -> $ctrl = $el.controller() - - us = $scope.$eval($attrs.tgBacklogUsPoints) - updatingSelectedRoleId = null selectedRoleId = null - numberOfRoles = _.size(us.points) + filteringRoleId = null + estimationProcess = null - # Preselect the role if we have only one - if numberOfRoles == 1 - selectedRoleId = _.keys(us.points)[0] + $scope.$on "uspoints:select", (ctx, roleId, roleName) -> + us = $scope.$eval($attrs.tgBacklogUsPoints) + selectedRoleId = roleId + estimationProcess.render() - roles = [] - updatePointsRoles = -> - roles = _.map computableRoles, (role) -> - pointId = us.points[role.id] - pointObj = $scope.pointsById[pointId] + $scope.$on "uspoints:clear-selection", (ctx) -> + us = $scope.$eval($attrs.tgBacklogUsPoints) + selectedRoleId = null + estimationProcess.render() - role = _.clone(role, true) - role.points = if pointObj.value? then pointObj.value else "?" - return role + $scope.$watch $attrs.tgBacklogUsPoints, (us) -> + if us + estimationProcess = $tgEstimationsService.create($el, us, $scope.project) - computableRoles = _.filter($scope.project.roles, "computable") - updatePointsRoles() + # Update roles + roles = estimationProcess.calculateRoles() + if roles.length == 0 + $el.find(".icon-arrow-bottom").remove() + $el.find("a.us-points").addClass("not-clickable") - if roles.length == 0 - $el.find(".icon-arrow-bottom").remove() - $el.find("a.us-points").addClass("not-clickable") + else if roles.length == 1 + # Preselect the role if we have only one + selectedRoleId = _.keys(us.points)[0] - renderPointsSelector = (us, roleId) -> - # Prepare data for rendering - points = _.map $scope.project.points, (point) -> - point = _.clone(point, true) - point.selected = if us.points[roleId] == point.id then false else true - return point + if estimationProcess.isEditable + bindClickElements() - html = pointsTemplate({"points": points}) + estimationProcess.onSelectedPointForRole = (roleId, pointId) -> + @save(roleId, pointId).then -> + $ctrl.loadProjectStats() - # Remove any prevous state - $el.find(".popover").popover().close() - $el.find(".pop-points-open").remove() + estimationProcess.render = () -> + totalPoints = @calculateTotalPoints() + if not selectedRoleId? or roles.length == 1 + text = totalPoints + title = totalPoints + else + pointId = @us.points[selectedRoleId] + pointObj = @pointsById[pointId] + text = "#{pointObj.name} / #{totalPoints}" + title = "#{pointObj.name} / #{totalPoints}" - # Render into DOM and show the new created element - $el.append(html) + ctx = { + totalPoints: totalPoints + roles: @calculateRoles() + editable: @isEditable + text: text + title: title + } + mainTemplate = "common/estimation/us-estimation-total.html" + template = $tgTemplate.get(mainTemplate, true) + html = template(ctx) + @$el.html(html) - # If not showing role selection let's move to the left - if not $el.find(".pop-role:visible").css("left")? - $el.find(".pop-points-open").css("left", "110px") - - $el.find(".pop-points-open").popover().open() - - renderRolesSelector = (us) -> - updatePointsRoles() + estimationProcess.render() + renderRolesSelector = () -> + roles = estimationProcess.calculateRoles() html = rolesTemplate({"roles": roles}) - # Render into DOM and show the new created element $el.append(html) $el.find(".pop-role").popover().open(() -> $(this).remove()) - renderPoints = (us, roleId) -> - dom = $el.find("a > span.points-value") - - if roleId == null or numberOfRoles == 1 - totalPoints = if us.total_points? then us.total_points else "?" - dom.text(totalPoints) - dom.parent().prop("title", totalPoints) - else - pointId = us.points[roleId] - pointObj = $scope.pointsById[pointId] - dom.html("#{pointObj.name} / #{us.total_points}") - dom.parent().prop("title", "#{pointObj.name} / #{us.total_points}") - - calculateTotalPoints = -> - values = _.map(us.points, (v, k) -> $scope.pointsById[v].value) - values = _.filter(values, (num) -> num?) - - if values.length == 0 - return "?" - - return _.reduce(values, (acc, num) -> acc + num) - - $scope.$watch $attrs.tgBacklogUsPoints, (us) -> - renderPoints(us, selectedRoleId) if us - - $scope.$on "uspoints:select", (ctx, roleId, roleName) -> - us = $scope.$eval($attrs.tgBacklogUsPoints) - renderPoints(us, roleId) - selectedRoleId = roleId - - $scope.$on "uspoints:clear-selection", (ctx) -> - us = $scope.$eval($attrs.tgBacklogUsPoints) - renderPoints(us, null) - selectedRoleId = null - - if roles.length > 0 + bindClickElements = () -> $el.on "click", "a.us-points span", (event) -> event.preventDefault() event.stopPropagation() - us = $scope.$eval($attrs.tgBacklogUsPoints) updatingSelectedRoleId = selectedRoleId - if selectedRoleId? - renderPointsSelector(us, selectedRoleId) + estimationProcess.renderPointsSelector(selectedRoleId) else - renderRolesSelector(us) + renderRolesSelector() $el.on "click", ".role", (event) -> event.preventDefault() event.stopPropagation() target = angular.element(event.currentTarget) - us = $scope.$eval($attrs.tgBacklogUsPoints) - updatingSelectedRoleId = target.data("role-id") - popRolesDom = $el.find(".pop-role") popRolesDom.find("a").removeClass("active") popRolesDom.find("a[data-role-id='#{updatingSelectedRoleId}']").addClass("active") - - renderPointsSelector(us, updatingSelectedRoleId) - - $el.on "click", ".point", (event) -> - event.preventDefault() - event.stopPropagation() - - target = angular.element(event.currentTarget) - $el.find(".pop-points-open").hide() - $el.find(".pop-role").hide() - - us = $scope.$eval($attrs.tgBacklogUsPoints) - - points = _.clone(us.points, true) - points[updatingSelectedRoleId] = target.data("point-id") - - $scope.$apply -> - us.points = points - us.total_points = calculateTotalPoints(us) - - renderPoints(us, selectedRoleId) - - $repo.save(us).then -> - # Little Hack for refresh. - $repo.refresh(us).then -> - $ctrl.loadProjectStats() - - bindOnce $scope, "project", (project) -> - # If the user has not enough permissions the click events are unbinded - if project.my_permissions.indexOf("modify_us") == -1 - $el.unbind("click") - $el.find("a").addClass("not-clickable") + estimationProcess.renderPointsSelector(updatingSelectedRoleId) $scope.$on "$destroy", -> $el.off() return {link: link} -module.directive("tgBacklogUsPoints", ["$tgRepo", "$tgTemplate", UsPointsDirective]) +module.directive("tgBacklogUsPoints", ["$tgEstimationsService", "$tgRepo", "$tgTemplate", UsPointsDirective]) ############################################################################# ## Burndown graph directive diff --git a/app/coffee/modules/backlog/sortable.coffee b/app/coffee/modules/backlog/sortable.coffee index ac0cc1ac..96d74f0f 100644 --- a/app/coffee/modules/backlog/sortable.coffee +++ b/app/coffee/modules/backlog/sortable.coffee @@ -58,6 +58,7 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm) -> $el.sortable({ items: ".us-item-row", + cancel: ".popover" connectWith: ".sprint" containment: ".wrapper" dropOnEmpty: true diff --git a/app/coffee/modules/backlog/sprints.coffee b/app/coffee/modules/backlog/sprints.coffee index 7a30ba9c..98419c65 100644 --- a/app/coffee/modules/backlog/sprints.coffee +++ b/app/coffee/modules/backlog/sprints.coffee @@ -61,6 +61,8 @@ BacklogSprintDirective = ($repo, $rootscope) -> # Event Handlers $el.on "click", ".sprint-name > .icon-arrow-up", (event) -> + event.preventDefault() + toggleSprint($el) $el.find(".sprint-table").slideToggle(slideOptions) @@ -135,26 +137,38 @@ module.directive("tgBacklogSprintHeader", ["$tgNavUrls", "$tgTemplate", BacklogS ############################################################################# ToggleExcludeClosedSprintsVisualization = ($rootscope, $loading) -> - excludeClosedSprints = false + excludeClosedSprints = true link = ($scope, $el, $attrs) -> + # insert loading wrapper + loadingElm = $("
") + $el.after(loadingElm) + # Event Handlers - $el.on "click", "", (event) -> - $loading.start($el.parent().siblings('.loading-spinner')) - $rootscope.$broadcast("backlog:toggle-closed-sprints-visualization") + $el.on "click", (event) -> + event.preventDefault() + excludeClosedSprints = not excludeClosedSprints + + $loading.start(loadingElm) + + if excludeClosedSprints + $rootscope.$broadcast("backlog:unload-closed-sprints") + else + $rootscope.$broadcast("backlog:load-closed-sprints") $scope.$on "$destroy", -> $el.off() - $scope.$on "sprints:loaded", (ctx, sprints) => - closedSprints = _.filter(sprints, (sprint) -> sprint.closed) - $loading.finish($el.parent().siblings('.loading-spinner')) + $scope.$on "closed-sprints:reloaded", (ctx, sprints) => + $loading.finish(loadingElm) #TODO: i18n - if closedSprints.length > 0 - $el.text("Hide closed sprints") + if sprints.length > 0 + text = "Hide closed sprints" else - $el.text("Show closed sprints") + text = "Show closed sprints" + + $el.find(".text").text(text) return {link: link} diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index 3e44ac58..8c762e77 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -84,13 +84,15 @@ urls = { "project-admin-project-profile-default-values": "/project/:project/admin/project-profile/default-values" "project-admin-project-profile-modules": "/project/:project/admin/project-profile/modules" "project-admin-project-profile-export": "/project/:project/admin/project-profile/export" - "project-admin-project-values-us-status": "/project/:project/admin/project-values/us-status" - "project-admin-project-values-us-points": "/project/:project/admin/project-values/us-points" - "project-admin-project-values-task-status": "/project/:project/admin/project-values/task-status" - "project-admin-project-values-issue-status": "/project/:project/admin/project-values/issue-status" - "project-admin-project-values-issue-types": "/project/:project/admin/project-values/issue-types" - "project-admin-project-values-issue-priorities": "/project/:project/admin/project-values/issue-priorities" - "project-admin-project-values-issue-severities": "/project/:project/admin/project-values/issue-severities" + "project-admin-project-profile-reports": "/project/:project/admin/project-profile/reports" + + "project-admin-project-values-status": "/project/:project/admin/project-values/status" + "project-admin-project-values-points": "/project/:project/admin/project-values/points" + "project-admin-project-values-priorities": "/project/:project/admin/project-values/priorities" + "project-admin-project-values-severities": "/project/:project/admin/project-values/severities" + "project-admin-project-values-types": "/project/:project/admin/project-values/types" + "project-admin-project-values-custom-fields": "/project/:project/admin/project-values/custom-fields" + "project-admin-memberships": "/project/:project/admin/memberships" "project-admin-roles": "/project/:project/admin/roles" "project-admin-third-parties-webhooks": "/project/:project/admin/third-parties/webhooks" diff --git a/app/coffee/modules/base/location.coffee b/app/coffee/modules/base/location.coffee index f99f0391..a6c3fc46 100644 --- a/app/coffee/modules/base/location.coffee +++ b/app/coffee/modules/base/location.coffee @@ -28,6 +28,10 @@ locationFactory = ($location, $route, $rootscope) -> un() return $location + + $location.isInCurrentRouteParams = (name, value) -> + return $route.current.params[name] == value + return $location diff --git a/app/coffee/modules/base/urls.coffee b/app/coffee/modules/base/urls.coffee index c542476a..46339b68 100644 --- a/app/coffee/modules/base/urls.coffee +++ b/app/coffee/modules/base/urls.coffee @@ -49,6 +49,14 @@ class UrlsService extends taiga.Service _.str.ltrim(url, "/") ]) + resolveAbsolute: -> + url = @.resolve.apply(@, arguments) + if (/^https?:\/\//i).test(url) + return url + if (/^\//).test(url) + return "#{window.location.protocol}//#{window.location.host}#{url}" + return "#{window.location.protocol}//#{window.location.host}/#{url}" + module = angular.module("taigaBase") module.service('$tgUrls', UrlsService) diff --git a/app/coffee/modules/common.coffee b/app/coffee/modules/common.coffee index bb11a405..7c03560c 100644 --- a/app/coffee/modules/common.coffee +++ b/app/coffee/modules/common.coffee @@ -60,6 +60,42 @@ CheckPermissionDirective = -> module.directive("tgCheckPermission", CheckPermissionDirective) +############################################################################# +## Add class based on permissions +############################################################################# + +ClassPermissionDirective = -> + name = "tgClassPermission" + + link = ($scope, $el, $attrs) -> + checkPermissions = (project, className, permission) -> + negation = permission[0] == "!" + + permission = permission.slice(1) if negation + + if negation && project.my_permissions.indexOf(permission) == -1 + $el.addClass(className) + else if !negation && project.my_permissions.indexOf(permission) != -1 + $el.addClass(className) + else + $el.removeClass(className) + + tgClassPermissionWatchAction = (project) -> + if project + unbindWatcher() + + classes = $scope.$eval($attrs[name]) + + for className, permission of classes + checkPermissions(project, className, permission) + + + unbindWatcher = $scope.$watch "project", tgClassPermissionWatchAction + + return {link:link} + +module.directive("tgClassPermission", ClassPermissionDirective) + ############################################################################# ## Animation frame service, apply css changes in the next render frame ############################################################################# diff --git a/app/coffee/modules/common/bind-scope.coffee b/app/coffee/modules/common/bind-scope.coffee new file mode 100644 index 00000000..2278e4b2 --- /dev/null +++ b/app/coffee/modules/common/bind-scope.coffee @@ -0,0 +1,15 @@ +module = angular.module("taigaCommon") + +BindScope = (config) -> + if !config.debugInfo + jQuery.fn.scope = () -> this.data('scope') + + link = ($scope, $el) -> + if !config.debugInfo + $el + .data('scope', $scope) + .addClass('tg-scope') + + return {link: link} + +module.directive("tgBindScope", ["$tgConfig", BindScope]) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 9b5ea62e..dd902037 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -536,7 +536,9 @@ EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading, $el.find('.view-description').hide() $el.find('textarea').focus() - $el.on "click", ".save", -> + $el.on "click", ".save", (e) -> + e.preventDefault() + description = $scope.item.description save(description) diff --git a/app/coffee/modules/common/custom-field-values.coffee b/app/coffee/modules/common/custom-field-values.coffee new file mode 100644 index 00000000..c827afb8 --- /dev/null +++ b/app/coffee/modules/common/custom-field-values.coffee @@ -0,0 +1,198 @@ +### +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino Garcia +# Copyright (C) 2014 David Barragán Merino +# +# 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 . +# +# File: modules/common/custom-field-values.coffee +### + +taiga = @.taiga +bindMethods = @.taiga.bindMethods +bindOnce = @.taiga.bindOnce +debounce = @.taiga.debounce +generateHash = taiga.generateHash + +module = angular.module("taigaCommon") + + +class CustomAttributesValuesController extends taiga.Controller + @.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q"] + + constructor: (@scope, @rootscope, @repo, @rs, @confirm, @q) -> + bindMethods(@) + @.type = null + @.objectId = null + @.projectId = null + @.customAttributes = [] + @.customAttributesValues = null + + initialize: (type, objectId) -> + @.project = @scope.project + @.type = type + @.objectId = objectId + @.projectId = @scope.projectId + + loadCustomAttributesValues: -> + return @.customAttributesValues if not @.objectId + return @rs.customAttributesValues[@.type].get(@.objectId).then (customAttributesValues) => + @.customAttributes = @.project["#{@.type}_custom_attributes"] + @.customAttributesValues = customAttributesValues + return customAttributesValues + + getAttributeValue: (attribute) -> + attributeValue = _.clone(attribute, false) + attributeValue.value = @.customAttributesValues.attributes_values[attribute.id] + return attributeValue + + updateAttributeValue: (attributeValue) -> + onSuccess = => + @rootscope.$broadcast("custom-attributes-values:edit") + + onError = (response) => + @confirm.notify("error") + return @q.reject() + + # We need to update the full array so angular understand the model is modified + attributesValues = _.clone(@.customAttributesValues.attributes_values, true) + attributesValues[attributeValue.id] = attributeValue.value + @.customAttributesValues.attributes_values = attributesValues + @.customAttributesValues.id = @.objectId + return @repo.save(@.customAttributesValues).then(onSuccess, onError) + + +CustomAttributesValuesDirective = ($templates, $storage) -> + template = $templates.get("custom-attributes/custom-attributes-values.html", true) + collapsedHash = (type) -> + return generateHash(["custom-attributes-collapsed", type]) + + link = ($scope, $el, $attrs, $ctrls) -> + $ctrl = $ctrls[0] + $model = $ctrls[1] + + bindOnce $scope, $attrs.ngModel, (value) -> + $ctrl.initialize($attrs.type, value.id) + $ctrl.loadCustomAttributesValues() + + $el.on "click", ".custom-fields-header a", -> + hash = collapsedHash($attrs.type) + collapsed = not($storage.get(hash) or false) + $storage.set(hash, collapsed) + if collapsed + $el.find(".custom-fields-header a").removeClass("open") + $el.find(".custom-fields-body").removeClass("open") + else + $el.find(".custom-fields-header a").addClass("open") + $el.find(".custom-fields-body").addClass("open") + + $scope.$on "$destroy", -> + $el.off() + + templateFn = ($el, $attrs) -> + collapsed = $storage.get(collapsedHash($attrs.type)) or false + + return template({ + requiredEditionPerm: $attrs.requiredEditionPerm + collapsed: collapsed + }) + + return { + require: ["tgCustomAttributesValues", "ngModel"] + controller: CustomAttributesValuesController + controllerAs: "ctrl" + restrict: "AE" + scope: true + link: link + template: templateFn + } + +module.directive("tgCustomAttributesValues", ["$tgTemplate", "$tgStorage", CustomAttributesValuesDirective]) + + +CustomAttributeValueDirective = ($template, $selectedText) -> + template = $template.get("custom-attributes/custom-attribute-value.html", true) + templateEdit = $template.get("custom-attributes/custom-attribute-value-edit.html", true) + + link = ($scope, $el, $attrs, $ctrl) -> + render = (attributeValue, edit=false) -> + value = attributeValue.value + editable = isEditable() + ctx = { + id: attributeValue.id + name: attributeValue.name + description: attributeValue.description + value: value + isEditable: editable + } + + if editable and (edit or not value) + html = templateEdit(ctx) + else + html = template(ctx) + + $el.html(html) + + isEditable = -> + permissions = $scope.project.my_permissions + requiredEditionPerm = $attrs.requiredEditionPerm + return permissions.indexOf(requiredEditionPerm) > -1 + + saveAttributeValue = -> + attributeValue.value = $el.find("input").val() + + $scope.$apply -> + $ctrl.updateAttributeValue(attributeValue).then -> + render(attributeValue, false) + + $el.on "keyup", "input[name=description]", (event) -> + if event.keyCode == 13 + submit(event) + else if event.keyCode == 27 + render(attributeValue, false) + + ## Actions (on view mode) + $el.on "click", ".custom-field-value.read-mode", -> + return if not isEditable() + return if $selectedText.get().length + render(attributeValue, true) + $el.find("input[name='description']").focus().select() + + $el.on "click", "a.icon-edit", (event) -> + event.preventDefault() + render(attributeValue, true) + $el.find("input[name='description']").focus().select() + + ## Actions (on edit mode) + submit = debounce 2000, (event) => + event.preventDefault() + saveAttributeValue() + + $el.on "submit", "form", submit + $el.on "click", "a.icon-floppy", submit + + $scope.$on "$destroy", -> + $el.off() + + # Bootstrap + attributeValue = $scope.$eval($attrs.tgCustomAttributeValue) + render(attributeValue) + + return { + link: link + require: "^tgCustomAttributesValues" + restrict: "AE" + } + +module.directive("tgCustomAttributeValue", ["$tgTemplate", "$selectedText", CustomAttributeValueDirective]) diff --git a/app/coffee/modules/common/estimation.coffee b/app/coffee/modules/common/estimation.coffee index 6965bd24..3ca34f5f 100644 --- a/app/coffee/modules/common/estimation.coffee +++ b/app/coffee/modules/common/estimation.coffee @@ -20,6 +20,7 @@ ### taiga = @.taiga +groupBy = @.taiga.groupBy module = angular.module("taigaCommon") @@ -27,7 +28,53 @@ module = angular.module("taigaCommon") ## User story estimation directive (for Lightboxes) ############################################################################# -LbUsEstimationDirective = ($rootScope, $repo, $confirm, $template) -> +LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $template) -> + # Display the points of a US and you can edit it. + # + # Example: + # tg-lb-us-estimation-progress-bar(ng-model="us") + # + # Requirements: + # - Us object (ng-model) + # - scope.project object + + link = ($scope, $el, $attrs, $model) -> + $scope.$watch $attrs.ngModel, (us) -> + if us + estimationProcess = $tgEstimationsService.create($el, us, $scope.project) + estimationProcess.onSelectedPointForRole = (roleId, pointId) -> + $scope.$apply -> + $model.$setViewValue(us) + + estimationProcess.render = () -> + ctx = { + totalPoints: @calculateTotalPoints() + roles: @calculateRoles() + editable: @isEditable + } + mainTemplate = "common/estimation/us-estimation-points-per-role.html" + template = $template.get(mainTemplate, true) + html = template(ctx) + @$el.html(html) + + estimationProcess.render() + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgLbUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgTemplate", LbUsEstimationDirective]) + + +############################################################################# +## User story estimation directive +############################################################################# + +UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qqueue, $template) -> # Display the points of a US and you can edit it. # # Example: @@ -37,90 +84,26 @@ LbUsEstimationDirective = ($rootScope, $repo, $confirm, $template) -> # - Us object (ng-model) # - scope.project object - mainTemplate = $template.get("common/estimation/lb-us-estimation-points-per-role.html", true) - pointsTemplate = $template.get("common/estimation/lb-us-estimation-points.html", true) - link = ($scope, $el, $attrs, $model) -> - render = (points) -> - totalPoints = calculateTotalPoints(points) or 0 - computableRoles = _.filter($scope.project.roles, "computable") + $scope.$watch $attrs.ngModel, (us) -> + if us + estimationProcess = $tgEstimationsService.create($el, us, $scope.project) + estimationProcess.onSelectedPointForRole = (roleId, pointId) -> + @save(roleId, pointId).then -> + $rootScope.$broadcast("history:reload") - roles = _.map computableRoles, (role) -> - pointId = points[role.id] - pointObj = $scope.pointsById[pointId] + estimationProcess.render = () -> + ctx = { + totalPoints: @calculateTotalPoints() + roles: @calculateRoles() + editable: @isEditable + } + mainTemplate = "common/estimation/us-estimation-points-per-role.html" + template = $template.get(mainTemplate, true) + html = template(ctx) + @$el.html(html) - role = _.clone(role, true) - role.points = if pointObj? and pointObj.name? then pointObj.name else "?" - return role - - ctx = { - totalPoints: totalPoints - roles: roles - } - html = mainTemplate(ctx) - $el.html(html) - - renderPoints = (target, usPoints, roleId) -> - points = _.map $scope.project.points, (point) -> - point = _.clone(point, true) - point.selected = if usPoints[roleId] == point.id then false else true - return point - - html = pointsTemplate({"points": points, roleId: roleId}) - - # Remove any prevous state - $el.find(".popover").popover().close() - $el.find(".pop-points-open").remove() - - # If not showing role selection let's move to the left - if not $el.find(".pop-role:visible").css("left")? - $el.find(".pop-points-open").css("left", "110px") - - $el.find(".pop-points-open").remove() - - # Render into DOM and show the new created element - $el.find(target).append(html) - - $el.find(".pop-points-open").popover().open(-> $(this).removeClass("active")) - $el.find(".pop-points-open").show() - - calculateTotalPoints = (points) -> - values = _.map(points, (v, k) -> $scope.pointsById[v]?.value or 0) - if values.length == 0 - return "0" - return _.reduce(values, (acc, num) -> acc + num) - - $el.on "click", ".total.clickable", (event) -> - event.preventDefault() - event.stopPropagation() - - target = angular.element(event.currentTarget) - roleId = target.data("role-id") - - points = $model.$modelValue - renderPoints(target, points, roleId) - - target.siblings().removeClass('active') - target.addClass('active') - - $el.on "click", ".point", (event) -> - event.preventDefault() - event.stopPropagation() - - target = angular.element(event.currentTarget) - roleId = target.data("role-id") - pointId = target.data("point-id") - - $el.find(".popover").popover().close() - - points = _.clone($model.$modelValue, true) - points[roleId] = pointId - - $scope.$apply -> - $model.$setViewValue(points) - - $scope.$watch $attrs.ngModel, (points) -> - render(points) if points + estimationProcess.render() $scope.$on "$destroy", -> $el.off() @@ -131,81 +114,46 @@ LbUsEstimationDirective = ($rootScope, $repo, $confirm, $template) -> require: "ngModel" } -module.directive("tgLbUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgTemplate", LbUsEstimationDirective]) +module.directive("tgUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", "$tgTemplate", + UsEstimationDirective]) ############################################################################# -## User story estimation directive +## Estimations service ############################################################################# -UsEstimationDirective = ($rootScope, $repo, $confirm, $qqueue, $template) -> - # Display the points of a US and you can edit it. - # - # Example: - # tg-us-estimation-progress-bar(ng-model="us") - # - # Requirements: - # - Us object (ng-model) - # - scope.project object - - mainTemplate = $template.get("common/estimation/us-estimation-points-per-role.html", true) +EstimationsService = ($template, $qqueue, $repo, $confirm, $q) -> pointsTemplate = $template.get("common/estimation/us-estimation-points.html", true) - link = ($scope, $el, $attrs, $model) -> - isEditable = -> - return $scope.project.my_permissions.indexOf("modify_us") != -1 + class EstimationProcess + constructor: (@$el, @us, @project) -> + @isEditable = @project.my_permissions.indexOf("modify_us") != -1 + @roles = @project.roles + @points = @project.points + @pointsById = groupBy(@points, (x) -> x.id) + @onSelectedPointForRole = (roleId, pointId) -> + @render = () -> - render = (us) -> - totalPoints = if us.total_points? then us.total_points else "?" - computableRoles = _.filter($scope.project.roles, "computable") + save: (roleId, pointId) -> + deferred = $q.defer() + $qqueue.add () => + onSuccess = => + deferred.resolve() + $confirm.notify("success") - roles = _.map computableRoles, (role) -> - pointId = us.points[role.id] - pointObj = $scope.pointsById[pointId] + onError = => + $confirm.notify("error") + @us.revert() + @render() + deferred.reject() - role = _.clone(role, true) - role.points = if pointObj? and pointObj.name? then pointObj.name else "?" - return role + $repo.save(@us).then(onSuccess, onError) - ctx = { - totalPoints: totalPoints - roles: roles - editable: isEditable() - } - html = mainTemplate(ctx) - $el.html(html) + return deferred.promise - renderPoints = (target, us, roleId) -> - points = _.map $scope.project.points, (point) -> - point = _.clone(point, true) - point.selected = if us.points[roleId] == point.id then false else true - return point + calculateTotalPoints: () -> + values = _.map(@us.points, (v, k) => @pointsById[v]?.value) - html = pointsTemplate({"points": points, roleId: roleId}) - - # Remove any prevous state - $el.find(".popover").popover().close() - $el.find(".pop-points-open").remove() - - # If not showing role selection let's move to the left - if not $el.find(".pop-role:visible").css("left")? - $el.find(".pop-points-open").css("left", "110px") - - $el.find(".pop-points-open").remove() - - # Render into DOM and show the new created element - $el.find(target).append(html) - - $el.find(".pop-points-open").popover().open -> - $(this) - .removeClass("active") - .closest("li").removeClass("active") - - - $el.find(".pop-points-open").show() - - calculateTotalPoints = (us) -> - values = _.map(us.points, (v, k) -> $scope.pointsById[v]?.value) if values.length == 0 return "0" @@ -215,64 +163,82 @@ UsEstimationDirective = ($rootScope, $repo, $confirm, $qqueue, $template) -> return _.reduce(notNullValues, (acc, num) -> acc + num) - save = $qqueue.bindAdd (roleId, pointId) => - $el.find(".popover").popover().close() + calculateRoles: () -> + computableRoles = _.filter(@project.roles, "computable") + roles = _.map computableRoles, (role) => + pointId = @us.points[role.id] + pointObj = @pointsById[pointId] + role = _.clone(role, true) + role.points = if pointObj? and pointObj.name? then pointObj.name else "?" + return role - # Hell starts here - us = angular.copy($model.$modelValue) - points = _.clone($model.$modelValue.points, true) - points[roleId] = pointId - us.setAttr('points', points) - us.points = points - us.total_points = calculateTotalPoints(us) - $model.$setViewValue(us) - # Hell ends here + return roles - onSuccess = -> - $confirm.notify("success") - $rootScope.$broadcast("history:reload") - onError = -> - $confirm.notify("error") - us.revert() - $model.$setViewValue(us) + bindClickEvents: => + @$el.on "click", ".total.clickable", (event) => + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + roleId = target.data("role-id") + @renderPointsSelector(roleId, target) + target.siblings().removeClass('active') + target.addClass('active') - $repo.save($model.$modelValue).then(onSuccess, onError) + @$el.on "click", ".point", (event) => + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + roleId = target.data("role-id") + pointId = target.data("point-id") + @$el.find(".popover").popover().close() + points = _.clone(@us.points, true) + points[roleId] = pointId + @us.points = points + @render() + @onSelectedPointForRole(roleId, pointId) - $el.on "click", ".total.clickable", (event) -> - event.preventDefault() - event.stopPropagation() - return if not isEditable() + renderPointsSelector: (roleId, target) -> + points = _.map @points, (point) => + point = _.clone(point, true) + point.selected = if @us.points[roleId] == point.id then false else true + return point - target = angular.element(event.currentTarget) - roleId = target.data("role-id") + maxPointLength = 5 + horizontalList = _.some points, (point) => point.name.length > maxPointLength - us = $model.$modelValue - renderPoints(target, us, roleId) + html = pointsTemplate({"points": points, roleId: roleId, horizontal: horizontalList}) + # Remove any previous state + @$el.find(".popover").popover().close() + @$el.find(".pop-points-open").remove() + # Render into DOM and show the new created element + if target? + @$el.find(target).append(html) + else + @$el.append(html) - target.siblings().removeClass('active') - target.addClass('active') + @$el.find(".pop-points-open").popover().open -> + $(this) + .removeClass("active") + .closest("li").removeClass("active") - $el.on "click", ".point", (event) -> - event.preventDefault() - event.stopPropagation() - return if not isEditable() + @$el.find(".pop-points-open").show() - target = angular.element(event.currentTarget) - roleId = target.data("role-id") - pointId = target.data("point-id") + create = ($el, us, project) -> + estimationProcess = $el.data("estimationProcess") - save(roleId, pointId) + if !estimationProcess + estimationProcess = new EstimationProcess($el, us, project) + $el.data("estimationProcess", estimationProcess) - $scope.$watch $attrs.ngModel, (us) -> - render(us) if us + if estimationProcess.isEditable + estimationProcess.bindClickEvents() + else + $el.unbind("click") - $scope.$on "$destroy", -> - $el.off() + return estimationProcess return { - link: link - restrict: "EA" - require: "ngModel" + create: create } -module.directive("tgUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", "$tgTemplate", UsEstimationDirective]) +module.factory("$tgEstimationsService", ["$tgTemplate", "$tgQqueue", "$tgRepo", "$tgConfirm", "$q", EstimationsService]) diff --git a/app/coffee/modules/common/history.coffee b/app/coffee/modules/common/history.coffee index 08c34fda..c7ab11d5 100644 --- a/app/coffee/modules/common/history.coffee +++ b/app/coffee/modules/common/history.coffee @@ -51,6 +51,13 @@ class HistoryController extends taiga.Controller delete historyResult.values_diff.description_html delete historyResult.values_diff.description_diff + # If block note was modified take only the blocked_note_html field + if historyResult.values_diff.blocked_note_diff? + historyResult.values_diff.blocked_note = historyResult.values_diff.blocked_note_diff + + delete historyResult.values_diff.blocked_note_html + delete historyResult.values_diff.blocked_note_diff + @scope.history = history @scope.comments = _.filter(history, (item) -> item.comment != "") @@ -66,6 +73,7 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) -> templateChangePoints = $template.get("common/history/history-change-points.html", true) templateChangeGeneric = $template.get("common/history/history-change-generic.html", true) templateChangeAttachment = $template.get("common/history/history-change-attachment.html", true) + templateChangeList = $template.get("common/history/history-change-list.html", true) templateDeletedComment = $template.get("common/history/history-deleted-comment.html", true) templateActivity = $template.get("common/history/history-activity.html", true) templateBaseEntries = $template.get("common/history/history-base-entries.html", true) @@ -103,6 +111,9 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) -> # Attachment is_deprecated: "is deprecated" + + blocked_note: "blocked note" + is_blocked: "is blocked" } # TODO i18n return humanizedFieldNames[field] or field @@ -121,18 +132,18 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) -> formatChange = (change) -> if _.isArray(change) if change.length == 0 - return "nil" + return "empty" return change.join(", ") if change == "" - return "nil" + return "empty" + + if not change? or change == false + return "no" if change == true return "yes" - if change == false - return "no" - return change # Render into string (operations without mutability) @@ -162,14 +173,50 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) -> return _.flatten(attachments).join("\n") + renderCustomAttributesEntry = (value) -> + customAttributes = _.map value, (changes, type) -> + if type == "new" + return _.map changes, (change) -> + return templateChangeGeneric({ + name: change.name, + from: formatChange(""), + to: formatChange(change.value) + }) + else if type == "deleted" + return _.map changes, (change) -> + # TODO: i18n + return templateChangeDiff({ + name: "deleted custom attribute", + diff: change.name + }) + else + return _.map changes, (change) -> + customAttrsChanges = _.map change.changes, (values) -> + return templateChangeGeneric({ + name: change.name + from: formatChange(values[0]) + to: formatChange(values[1]) + }) + return _.flatten(customAttrsChanges).join("\n") + + return _.flatten(customAttributes).join("\n") + renderChangeEntry = (field, value) -> if field == "description" - # TODO: i18n - return templateChangeDiff({name: "description", diff: value[1]}) + return templateChangeDiff({name: getHumanizedFieldName("description"), diff: value[1]}) + else if field == "blocked_note" + return templateChangeDiff({name: getHumanizedFieldName("blocked_note"), diff: value[1]}) else if field == "points" return templateChangePoints({points: value}) else if field == "attachments" return renderAttachmentEntry(value) + else if field == "custom_attributes" + return renderCustomAttributesEntry(value) + else if field in ["tags", "watchers"] + name = getHumanizedFieldName(field) + removed = _.difference(value[0], value[1]) + added = _.difference(value[1], value[0]) + return templateChangeList({name:name, removed:removed, added: added}) else if field == "assigned_to" name = getHumanizedFieldName(field) from = formatChange(value[0] or "Unassigned") @@ -211,7 +258,7 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) -> deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date deleteCommentUser: comment.delete_comment_user.name if comment.delete_comment_user?.name activityId: comment.id - canDeleteComment: comment.user.pk == $scope.user.id or $scope.project.my_permissions.indexOf("modify_project") > -1 + canDeleteComment: comment.user.pk == $scope.user?.id or $scope.project.my_permissions.indexOf("modify_project") > -1 }) renderChange = (change) -> diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index 667edf39..f270837d 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -45,6 +45,8 @@ class LightboxService extends taiga.Service @animationFrame.add => $el.addClass("open") + + @animationFrame.add => lightboxContent.show() defered.resolve() @@ -334,7 +336,6 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $el.on "click", ".close", (event) -> event.preventDefault() @@ -403,7 +404,6 @@ CreateBulkUserstoriesDirective = ($repo, $rs, $rootscope, lightboxService, $load submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "$destroy", -> $el.off() @@ -604,41 +604,3 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS } module.directive("tgLbWatchers", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", WatchersLightboxDirective]) - -############################################################################# -## Notion Lightbox Directive -############################################################################# - -# Lightbox -NotionLightboxDirective = (lightboxService) -> - link = ($scope, $el, $attrs, $model) -> - $scope.$on "notion:open", (event, lightboxId) -> - if $el.attr("id") == lightboxId - lightboxService.open($el) - - $el.on "click", ".button-green", (event) -> - lightboxService.close($el) - - $scope.$on "$destroy", -> - $el.off() - - return {link:link} - -module.directive("tgLbNotion", ["lightboxService", NotionLightboxDirective]) - - -# Button -NotionButtonDirective = ($log, $rootScope) -> - link = ($scope, $el, $attrs, $model) -> - if not $attrs.tgLbNotionButton? - return $log.error "NotionButtonDirective: the directive need the id of the notion lightbox" - - $el.on "click", -> - $rootScope.$broadcast("notion:open", $attrs.tgLbNotionButton) - - $scope.$on "$destroy", -> - $el.off() - - return {link:link} - -module.directive("tgLbNotionButton", ["$log", "$rootScope", NotionButtonDirective]) diff --git a/app/coffee/modules/common/wisiwyg.coffee b/app/coffee/modules/common/wisiwyg.coffee index 96ffcf77..1527ec01 100644 --- a/app/coffee/modules/common/wisiwyg.coffee +++ b/app/coffee/modules/common/wisiwyg.coffee @@ -24,6 +24,33 @@ bindOnce = @.taiga.bindOnce module = angular.module("taigaCommon") +# How to test lists (-, *, 1.) +# test it with text after & before the list +# + is the cursor position + +# CASE 1 +# - aa+ +# --> enter +# - aa +# - + + +# CASE 1 +# - + +# --> enter + +# + + +# CASE 3 +# - bb+cc +# --> enter +# - bb +# - cc + +# CASE 3 +# +- aa +# --> enter + +# - aa ############################################################################# ## WYSIWYG markitup editor directive @@ -64,35 +91,52 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) -> markdown.off(".preview") closePreviewMode() - markdownCaretPositon = false - - setCaretPosition = (elm, caretPos) -> - if elm.createTextRange - range = elm.createTextRange() - range.move("character", caretPos) + setCaretPosition = (textarea, caretPosition) -> + if textarea.createTextRange + range = textarea.createTextRange() + range.move("character", caretPosition) range.select() - else if elm.selectionStart - elm.focus() - elm.setSelectionRange(caretPos, caretPos) + else if textarea.selectionStart + textarea.focus() + textarea.setSelectionRange(caretPosition, caretPosition) - removeEmptyLine = (textarea, line, currentCaretPosition) -> + # Calculate the scroll position + totalLines = textarea.value.split("\n").length + line = textarea.value[0..(caretPosition - 1)].split("\n").length + scrollRelation = line / totalLines + $el.scrollTop((scrollRelation * $el[0].scrollHeight) - ($el.height() / 2)) + + addLine = (textarea, nline, replace) -> lines = textarea.value.split("\n") - removedLineLength = lines[line].length - lines[line] = "" + if replace + lines[nline] = replace + lines[nline] + else + lines[nline] = "" + + cursorPosition = 0 + + for line, key in lines + cursorPosition += line.length + 1 || 1 + + break if key == nline textarea.value = lines.join("\n") #return the new position - return currentCaretPosition - removedLineLength + 1 + if replace + return cursorPosition - lines[nline].length + replace.length - 1 + else + return cursorPosition markdownSettings = nameSpace: "markdown" onShiftEnter: {keepDefault:false, openWith:"\n\n"} onEnter: - keepDefault: false - replaceWith: (data) => + keepDefault: false, + replaceWith: () -> "\n" + afterInsert: (data) -> lines = data.textarea.value.split("\n") cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0] @@ -105,12 +149,9 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) -> emptyListItem = lastLine.match /^(\s*)\-\s$/ if emptyListItem - markdownCaretPositon = removeEmptyLine(data.textarea, lines.length - 1, data.caretPosition) + markdownCaretPositon = addLine(data.textarea, cursorLine - 1) else - breakLineAtBeginning = newLineContent.match /^(\s*)\-\s/ - - if !breakLineAtBeginning - return "\n#{match[1]}" if match + markdownCaretPositon = addLine(data.textarea, cursorLine, "#{match[1]}") # unordered list * match = lastLine.match /^(\s*\* ).*/ @@ -119,12 +160,9 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) -> emptyListItem = lastLine.match /^(\s*\* )$/ if emptyListItem - markdownCaretPositon = removeEmptyLine(data.textarea, lines.length - 1, data.caretPosition) + markdownCaretPositon = addLine(data.textarea, cursorLine - 1) else - breakLineAtBeginning = newLineContent.match /^(\s*)\*\s/ - - if !breakLineAtBeginning - return "\n#{match[1]}" if match + markdownCaretPositon = addLine(data.textarea, cursorLine, "#{match[1]}") # ordered list match = lastLine.match /^(\s*)(\d+)\.\s/ @@ -133,29 +171,12 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) -> emptyListItem = lastLine.match /^(\s*)(\d+)\.\s$/ if emptyListItem - markdownCaretPositon = removeEmptyLine(data.textarea, lines.length - 1, data.caretPosition) + markdownCaretPositon = addLine(data.textarea, cursorLine - 1) else - breakLineAtBeginning = newLineContent.match /^(\s*)(\d+)\.\s/ + markdownCaretPositon = addLine(data.textarea, cursorLine, "#{match[1] + (parseInt(match[2], 10) + 1)}. ") - if !breakLineAtBeginning - return "\n#{match[1] + (parseInt(match[2], 10) + 1)}. " - return "\n" - - afterInsert: (data) -> - # Calculate the scroll position - - if markdownCaretPositon - setCaretPosition(data.textarea, markdownCaretPositon) - caretPosition = markdownCaretPositon - markdownCaretPositon = false - else - caretPosition = data.caretPosition - - totalLines = data.textarea.value.split("\n").length - line = data.textarea.value[0..(caretPosition - 1)].split("\n").length - scrollRelation = line / totalLines - $el.scrollTop((scrollRelation * $el[0].scrollHeight) - ($el.height() / 2)) + setCaretPosition(data.textarea, markdownCaretPositon) if markdownCaretPositon markupSet: [ { @@ -214,14 +235,18 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) -> { name: $tr.t("markdown-editor.picture") key: "P" - replaceWith: '![[![Alternative text]!]]([![Url:!:http://]!] "[![Title]!]")' + replaceWith: '![[![Alternative text]!]](<<<[![Url:!:http://]!]>>> "[![Title]!]")' + beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp) + afterInsert:(markItUp) -> urlFormatting(markItUp) }, { name: $tr.t("markdown-editor.link") key: "L" openWith: "[" - closeWith: ']([![Url:!:http://]!] "[![Title]!]")' + closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")' placeHolder: $tr.t("markdown-editor.link-placeholder") + beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp) + afterInsert:(markItUp) -> urlFormatting(markItUp) }, { separator: "---------------" @@ -256,6 +281,45 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) -> target = angular.element(event.textarea) $model.$setViewValue(target.val()) + prepareUrlFormatting = (markItUp) -> + console.log(markItUp) + regex = /(<<<|>>>)/gi + result = 0 + indices = [] + (indices.push(result.index)) while ( (result = regex.exec(markItUp.textarea.value)) ) + markItUp.donotparse = indices + console.log(indices) + + urlFormatting = (markItUp) -> + console.log(markItUp.donotparse) + regex = /<<>>/gi + endIndex = 0 + loop + result = regex.exec(markItUp.textarea.value) + break if !result + if result.index not in markItUp.donotparse + endIndex = result.index + break + + value = markItUp.textarea.value + url = value.substring(startIndex, endIndex).replace('<<<', '').replace('>>>', '') + url = url.replace('(', '%28').replace(')', '%29') + url = url.replace('[', '%5B').replace(']', '%5D') + value = value.substring(0, startIndex) + url + value.substring(endIndex+3, value.length) + markItUp.textarea.value = value + markItUp.donotparse = undefined + markdownTitle = (markItUp, char) -> heading = "" n = $.trim(markItUp.selection or markItUp.placeHolder).length diff --git a/app/coffee/modules/controllerMixins.coffee b/app/coffee/modules/controllerMixins.coffee index cf63d08e..34b5adb6 100644 --- a/app/coffee/modules/controllerMixins.coffee +++ b/app/coffee/modules/controllerMixins.coffee @@ -62,7 +62,7 @@ taiga.PageMixin = PageMixin ############################################################################# ## Filters Mixin ############################################################################# -# This mixin requires @location ($tgLocation) and @scope +# This mixin requires @location ($tgLocation), and @scope class FiltersMixin selectFilter: (name, value, load=false) -> @@ -73,12 +73,14 @@ class FiltersMixin existing = _.compact(existing) value = joinStr(",", _.uniq(existing)) - location = if load then @location else @location.noreload(@scope) - location.search(name, value) + if !@location.isInCurrentRouteParams(name, value) + location = if load then @location else @location.noreload(@scope) + location.search(name, value) replaceFilter: (name, value, load=false) -> - location = if load then @location else @location.noreload(@scope) - location.search(name, value) + if !@location.isInCurrentRouteParams(name, value) + location = if load then @location else @location.noreload(@scope) + location.search(name, value) replaceAllFilters: (filters, load=false) -> location = if load then @location else @location.noreload(@scope) diff --git a/app/coffee/modules/feedback.coffee b/app/coffee/modules/feedback.coffee index 781f6ddc..403b46da 100644 --- a/app/coffee/modules/feedback.coffee +++ b/app/coffee/modules/feedback.coffee @@ -55,7 +55,6 @@ FeedbackDirective = ($lightboxService, $repo, $confirm, $loading)-> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "feedback:show", -> $scope.$apply -> diff --git a/app/coffee/modules/integrations/github.coffee b/app/coffee/modules/integrations/github.coffee deleted file mode 100644 index fa61a86f..00000000 --- a/app/coffee/modules/integrations/github.coffee +++ /dev/null @@ -1,111 +0,0 @@ -### -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino Garcia -# Copyright (C) 2014 David Barragán Merino -# -# 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 . -# -# File: modules/integrations/github.coffee -### - -taiga = @.taiga - -module = angular.module("taigaIntegrations") - -AUTH_URL = "https://github.com/login/oauth/authorize" - - -############################################################################# -## User story team requirements button directive -############################################################################# - -GithubLoginButtonDirective = ($window, $params, $location, $config, $events, $confirm, $auth, $navUrls, $loader) -> - # Login or registar a user with his/her github account. - # - # Example: - # tg-github-login-button() - # - # Requirements: - # - ... - - template = """ - - - Login with Github - - """ #TODO: i18n - - link = ($scope, $el, $attrs) -> - clientId = $config.get("gitHubClientId", null) - return if not clientId - - renderGitHubButton = -> - $el.html(template) if clientId - - loginOnSuccess = (response) -> - if $params.next and $params.next != $navUrls.resolve("login") - nextUrl = $params.next - else - nextUrl = $navUrls.resolve("home") - - $events.setupConnection() - - $location.search("next", null) - $location.search("token", null) - $location.search("state", null) - $location.search("code", null) - $location.path(nextUrl) - - loginOnError = (response) -> - $location.search("state", null) - $location.search("code", null) - $loader.pageLoaded() - - if response.data.error_message - $confirm.notify("light-error", response.data.error_message ) - else - $confirm.notify("light-error", "Our Oompa Loompas have not been able to get you - credentials from GitHub.") #TODO: i18n - - loginWithGitHubAccount = -> - type = $params.state - code = $params.code - token = $params.token - - return if not (type == "github" and code) - $loader.start() - - data = {code: code, token: token} - $auth.login(data, type).then(loginOnSuccess, loginOnError) - - renderGitHubButton() - loginWithGitHubAccount() - - $el.on "click", ".button-github", (event) -> - redirectToUri = $location.absUrl() - url = "#{AUTH_URL}?client_id=#{clientId}&redirect_uri=#{redirectToUri}&state=github&scope=user:email" - $window.location.href = url - - $scope.$on "$destroy", -> - $el.off() - - return { - link: link - restrict: "EA" - template: "" - } - -module.directive("tgGithubLoginButton", ["$window", '$routeParams', "$tgLocation", "$tgConfig", "$tgEvents", - "$tgConfirm", "$tgAuth", "$tgNavUrls", "tgLoader", - GithubLoginButtonDirective]) diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index eea923dc..2be4c7d7 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -85,6 +85,9 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @rootscope.$broadcast("history:reload") @.loadIssue() + @scope.$on "custom-attributes-values:edit", => + @rootscope.$broadcast("history:reload") + initializeOnDeleteGoToUrl: -> ctx = {project: @scope.project.slug} if @scope.project.is_issues_activated diff --git a/app/coffee/modules/issues/lightboxes.coffee b/app/coffee/modules/issues/lightboxes.coffee index 1195954c..f05f2caf 100644 --- a/app/coffee/modules/issues/lightboxes.coffee +++ b/app/coffee/modules/issues/lightboxes.coffee @@ -75,7 +75,6 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading) submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} @@ -123,7 +122,6 @@ CreateBulkIssuesDirective = ($repo, $rs, $confirm, $rootscope, $loading, lightbo submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/issues/list.coffee b/app/coffee/modules/issues/list.coffee index 7cff727a..7d2e1234 100644 --- a/app/coffee/modules/issues/list.coffee +++ b/app/coffee/modules/issues/list.coffee @@ -94,6 +94,9 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi loadProject: -> return @rs.projects.getBySlug(@params.pslug).then (project) => + if not project.is_issues_activated + @location.path(@navUrls.resolve("permission-denied")) + @scope.projectId = project.id @scope.project = project @scope.$emit('project:loaded', project) @@ -530,6 +533,9 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) - selectQFilter = debounceLeading 100, (value) -> return if value is undefined + + $ctrl.replaceFilter("page", null) + if value.length == 0 $ctrl.replaceFilter("q", null) $ctrl.storeFilters() diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee index d82a8065..b56a46a7 100644 --- a/app/coffee/modules/kanban/main.coffee +++ b/app/coffee/modules/kanban/main.coffee @@ -138,13 +138,20 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi @scope.userstories = userstories usByStatus = _.groupBy(userstories, "status") + us_archived = [] for status in @scope.usStatusList if not usByStatus[status.id]? usByStatus[status.id] = [] + if @scope.usByStatus? + for us in @scope.usByStatus[status.id] + if us.status != status.id + us_archived.push(us) # Must preserve the archived columns if loaded - if status.is_archived and @scope.usByStatus? - usByStatus[status.id] = @scope.usByStatus[status.id] + if status.is_archived and @scope.usByStatus? and @scope.usByStatus[status.id].length != 0 + for us in @scope.usByStatus[status.id].concat(us_archived) + if us.status == status.id + usByStatus[status.id].push(us) usByStatus[status.id] = _.sortBy(usByStatus[status.id], "kanban_order") @@ -176,6 +183,9 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi loadProject: -> return @rs.projects.getBySlug(@params.pslug).then (project) => + if not project.is_kanban_activated + @location.path(@navUrls.resolve("permission-denied")) + @scope.projectId = project.id @scope.project = project @scope.projectId = project.id @@ -293,36 +303,6 @@ KanbanDirective = ($repo, $rootscope) -> module.directive("tgKanban", ["$tgRepo", "$rootScope", KanbanDirective]) - -############################################################################# -## Kanban Column Height Fixer Directive -############################################################################# - -KanbanColumnHeightFixerDirective = -> - mainPadding = 32 # px - scrollPadding = 0 # px - - renderSize = ($el) -> - elementOffset = $el.parent().parent().offset().top - windowHeight = angular.element(window).height() - columnHeight = windowHeight - elementOffset - mainPadding - scrollPadding - $el.css("height", "#{columnHeight}px") - - link = ($scope, $el, $attrs) -> - timeout(500, -> renderSize($el)) - - $scope.$on "resize", -> - renderSize($el) - - $scope.$on "$destroy", -> - $el.off() - - return {link:link} - - -module.directive("tgKanbanColumnHeightFixer", KanbanColumnHeightFixerDirective) - - ############################################################################# ## Kanban Archived Status Column Header Control ############################################################################# @@ -527,6 +507,9 @@ KanbanUserDirective = ($log) -> clickable = false link = ($scope, $el, $attrs, $model) -> + username_label = $el.parent().find("a.task-assigned") + username_label.addClass("not-clickable") + if not $attrs.tgKanbanUserAvatar return $log.error "KanbanUserDirective: no attr is defined" @@ -546,15 +529,7 @@ KanbanUserDirective = ($log) -> html = template(ctx) $el.html(html) - username_label = $el.parent().find("a.task-assigned") - username_label.html(ctx.name) - username_label.on "click", (event) -> - if $el.find("a").hasClass("noclick") - return - - us = $model.$modelValue - $ctrl = $el.controller() - $ctrl.changeUsAssignedTo(us) + username_label.text(ctx.name) bindOnce $scope, "project", (project) -> if project.my_permissions.indexOf("modify_us") > -1 @@ -567,6 +542,15 @@ KanbanUserDirective = ($log) -> $ctrl = $el.controller() $ctrl.changeUsAssignedTo(us) + username_label.removeClass("not-clickable") + username_label.on "click", (event) -> + if $el.find("a").hasClass("noclick") + return + + us = $model.$modelValue + $ctrl = $el.controller() + $ctrl.changeUsAssignedTo(us) + $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/nav.coffee b/app/coffee/modules/nav.coffee index ffe765c6..578890e0 100644 --- a/app/coffee/modules/nav.coffee +++ b/app/coffee/modules/nav.coffee @@ -49,7 +49,7 @@ class ProjectsNavigationController extends taiga.Controller @.loadInitialData() loadInitialData: -> - return @rs.projects.list().then (projects) => + return @rs.projects.listByMember(@rootscope.user?.id).then (projects) => for project in projects project.url = @projectUrl.get(project) @scope.projects = projects diff --git a/app/coffee/modules/projects/lightboxes.coffee b/app/coffee/modules/projects/lightboxes.coffee index 91497caf..c38afbc8 100644 --- a/app/coffee/modules/projects/lightboxes.coffee +++ b/app/coffee/modules/projects/lightboxes.coffee @@ -118,7 +118,6 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $el.on "click", ".close", (event) -> event.preventDefault() diff --git a/app/coffee/modules/projects/main.coffee b/app/coffee/modules/projects/main.coffee index 97d68d77..8fda3a20 100644 --- a/app/coffee/modules/projects/main.coffee +++ b/app/coffee/modules/projects/main.coffee @@ -50,7 +50,7 @@ class ProjectsController extends taiga.Controller promise = @.loadInitialData() promise.then () => - @scope.$emit("projects:loaded") + @scope.$emit("projects:loaded", @.projects) promise.then null, @.onInitialDataError.bind(@) @@ -58,7 +58,7 @@ class ProjectsController extends taiga.Controller promise.finally tgLoader.pageLoaded loadInitialData: -> - return @rs.projects.list().then (projects) => + return @rs.projects.listByMember(@rootscope.user?.id).then (projects) => @.projects = {'recents': projects.slice(0, 8), 'all': projects} for project in projects project.url = @projectUrl.get(project) @@ -257,8 +257,8 @@ ProjectsListDirective = ($compile, $template) -> $el.html($compile(template({projects: projects}))($scope)) $scope.$emit("regenerate:project-pagination") - $scope.$watch "projects", (projects) -> - render(projects) if projects? + $scope.$on "projects:loaded", (ctx, projects) -> + render(projects.all) if projects.all? return { link: link diff --git a/app/coffee/modules/resources.coffee b/app/coffee/modules/resources.coffee index 9f067e78..2364baa3 100644 --- a/app/coffee/modules/resources.coffee +++ b/app/coffee/modules/resources.coffee @@ -102,12 +102,27 @@ urls = { "attachments/task": "/tasks/attachments" "attachments/wiki_page": "/wiki/attachments" + # Custom Attributess + "custom-attributes/userstory": "/userstory-custom-attributes" + "custom-attributes/issue": "/issue-custom-attributes" + "custom-attributes/task": "/task-custom-attributes" + + # Custom field values + "custom-attributes-values/userstory": "/userstories/custom-attributes-values" + "custom-attributes-values/issue": "/issues/custom-attributes-values" + "custom-attributes-values/task": "/tasks/custom-attributes-values" + # Feedback "feedback": "/feedback" # Export/Import "exporter": "/exporter" "importer": "/importer/load_dump" + + # CSV + "userstories-csv": "/userstories/csv?uuid=%s" + "tasks-csv": "/tasks/csv?uuid=%s" + "issues-csv": "/issues/csv?uuid=%s" } # Initialize api urls service @@ -133,6 +148,8 @@ module.run([ "$log", "$tgResources", "$tgProjectsResourcesProvider", + "$tgCustomAttributesResourcesProvider", + "$tgCustomAttributesValuesResourcesProvider", "$tgMembershipsResourcesProvider", "$tgNotifyPoliciesResourcesProvider", "$tgInvitationsResourcesProvider", diff --git a/app/coffee/modules/resources/custom-attributes-values.coffee b/app/coffee/modules/resources/custom-attributes-values.coffee new file mode 100644 index 00000000..5322c639 --- /dev/null +++ b/app/coffee/modules/resources/custom-attributes-values.coffee @@ -0,0 +1,44 @@ +### +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino Garcia +# Copyright (C) 2014 David Barragán Merino +# +# 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 . +# +# File: modules/resources/custom-field-values.coffee +### + +taiga = @.taiga + +resourceProvider = ($repo) -> + _get = (objectId, resource) -> + return $repo.queryOne(resource, objectId) + + service = { + userstory: { + get: (objectId) -> _get(objectId, "custom-attributes-values/userstory") + } + task: { + get: (objectId) -> _get(objectId, "custom-attributes-values/task") + } + issue: { + get: (objectId) -> _get(objectId, "custom-attributes-values/issue") + } + } + + return (instance) -> + instance.customAttributesValues = service + +module = angular.module("taigaResources") +module.factory("$tgCustomAttributesValuesResourcesProvider", ["$tgRepo", resourceProvider]) diff --git a/app/coffee/modules/resources/custom-attributes.coffee b/app/coffee/modules/resources/custom-attributes.coffee new file mode 100644 index 00000000..cf14c398 --- /dev/null +++ b/app/coffee/modules/resources/custom-attributes.coffee @@ -0,0 +1,48 @@ +### +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino Garcia +# Copyright (C) 2014 David Barragán Merino +# +# 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 . +# +# File: modules/resources/projects.coffee +### + + +taiga = @.taiga +sizeFormat = @.taiga.sizeFormat + + +resourceProvider = ($repo) -> + _list = (projectId, resource) -> + return $repo.queryMany(resource, {project: projectId}) + + service = { + userstory:{ + list: (projectId) -> _list(projectId, "custom-attributes/userstory") + } + task:{ + list: (projectId) -> _list(projectId, "custom-attributes/task") + } + issue: { + list: (projectId) -> _list(projectId, "custom-attributes/issue") + } + } + + return (instance) -> + instance.customAttributes = service + + +module = angular.module("taigaResources") +module.factory("$tgCustomAttributesResourcesProvider", ["$tgRepo", resourceProvider]) diff --git a/app/coffee/modules/resources/projects.coffee b/app/coffee/modules/resources/projects.coffee index d6a5a0ad..88393f48 100644 --- a/app/coffee/modules/resources/projects.coffee +++ b/app/coffee/modules/resources/projects.coffee @@ -36,6 +36,10 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $rootScope) -> service.list = -> return $repo.queryMany("projects") + service.listByMember = (memberId) -> + params = {"member": memberId} + return $repo.queryMany("projects", params) + service.templates = -> return $repo.queryMany("project-templates") @@ -50,6 +54,18 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $rootScope) -> service.stats = (projectId) -> return $repo.queryOneRaw("projects", "#{projectId}/stats") + service.regenerate_userstories_csv_uuid = (projectId) -> + url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_userstories_csv_uuid" + return $http.post(url) + + service.regenerate_issues_csv_uuid = (projectId) -> + url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_issues_csv_uuid" + return $http.post(url) + + service.regenerate_tasks_csv_uuid = (projectId) -> + url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_tasks_csv_uuid" + return $http.post(url) + service.leave = (projectId) -> url = "#{$urls.resolve("projects")}/#{projectId}/leave" return $http.post(url) diff --git a/app/coffee/modules/search.coffee b/app/coffee/modules/search.coffee index 69724468..a863f7ff 100644 --- a/app/coffee/modules/search.coffee +++ b/app/coffee/modules/search.coffee @@ -134,7 +134,6 @@ SearchBoxDirective = ($lightboxService, $navurls, $location, $route)-> $el.find("#search-text").val("") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit return {link:link} diff --git a/app/coffee/modules/taskboard/lightboxes.coffee b/app/coffee/modules/taskboard/lightboxes.coffee index dc8a8fe7..367aba17 100644 --- a/app/coffee/modules/taskboard/lightboxes.coffee +++ b/app/coffee/modules/taskboard/lightboxes.coffee @@ -40,7 +40,7 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer $scope.isNew = true # Update texts for creation - $el.find(".button-green span").html("Create") #TODO: i18n + $el.find(".button-green").html("Create") #TODO: i18n $el.find(".title").html("New task ") #TODO: i18n $el.find(".tag-input").val("") @@ -51,7 +51,7 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer $scope.isNew = false # Update texts for edition - $el.find(".button-green span").html("Save") #TODO: i18n + $el.find(".button-green").html("Save") #TODO: i18n $el.find(".title").html("Edit task ") #TODO: i18n $el.find(".tag-input").val("") @@ -83,7 +83,6 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer $rootscope.$broadcast(broadcastEvent, data) $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "$destroy", -> $el.off() @@ -127,7 +126,6 @@ CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService) - submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/taskboard/main.coffee b/app/coffee/modules/taskboard/main.coffee index 00cef123..8a922148 100644 --- a/app/coffee/modules/taskboard/main.coffee +++ b/app/coffee/modules/taskboard/main.coffee @@ -104,6 +104,9 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => + if not project.is_backlog_activated + @location.path(@navUrls.resolve("permission-denied")) + @scope.project = project # Not used at this momment @scope.pointsList = _.sortBy(project.points, "order") @@ -116,6 +119,8 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) @scope.$emit('project:loaded', project) + @.fillUsersAndRoles(project.users, project.roles) + return project loadSprintStats: -> @@ -185,7 +190,6 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) return data return promise.then(=> @.loadProject()) - .then(=> @.loadUsersAndRoles()) .then(=> @.loadTaskboard()) refreshTasksOrder: (tasks) -> @@ -297,30 +301,6 @@ TaskboardTaskDirective = ($rootscope) -> module.directive("tgTaskboardTask", ["$rootScope", TaskboardTaskDirective]) -############################################################################# -## Taskboard Table Height Fixer Directive -############################################################################# - -TaskboardTableHeightFixerDirective = -> - mainPadding = 32 # px - - renderSize = ($el) -> - elementOffset = $el.offset().top - windowHeight = angular.element(window).height() - columnHeight = windowHeight - elementOffset - mainPadding - $el.css("height", "#{columnHeight}px") - - link = ($scope, $el, $attrs) -> - timeout(500, -> renderSize($el)) - - $scope.$on "resize", -> - renderSize($el) - - return {link:link} - - -module.directive("tgTaskboardTableHeightFixer", TaskboardTableHeightFixerDirective) - ############################################################################# ## Taskboard Squish Column Directive ############################################################################# @@ -421,12 +401,7 @@ TaskboardUserDirective = ($log) -> link = ($scope, $el, $attrs) -> username_label = $el.parent().find("a.task-assigned") - username_label.on "click", (event) -> - if $el.find('a').hasClass('noclick') - return - - $ctrl = $el.controller() - $ctrl.editTaskAssignedTo($scope.task) + username_label.addClass("not-clickable") $scope.$watch 'task.assigned_to', (assigned_to) -> user = $scope.usersById[assigned_to] @@ -449,6 +424,15 @@ TaskboardUserDirective = ($log) -> $ctrl = $el.controller() $ctrl.editTaskAssignedTo($scope.task) + username_label.removeClass("not-clickable") + username_label.on "click", (event) -> + if $el.find('a').hasClass('noclick') + return + + $ctrl = $el.controller() + $ctrl.editTaskAssignedTo($scope.task) + + return { link: link, templateUrl: "taskboard/taskboard-user.html", diff --git a/app/coffee/modules/taskboard/sortable.coffee b/app/coffee/modules/taskboard/sortable.coffee index 16aa5020..57f99711 100644 --- a/app/coffee/modules/taskboard/sortable.coffee +++ b/app/coffee/modules/taskboard/sortable.coffee @@ -36,47 +36,52 @@ module = angular.module("taigaBacklog") TaskboardSortableDirective = ($repo, $rs, $rootscope) -> link = ($scope, $el, $attrs) -> - oldParentScope = null - newParentScope = null - itemEl = null - tdom = $el + bindOnce $scope, "project", (project) -> + # If the user has not enough permissions we don't enable the sortable + if not (project.my_permissions.indexOf("modify_us") > -1) + return - deleteElement = (itemEl) -> - # Completelly remove item and its scope from dom - itemEl.scope().$destroy() - itemEl.off() - itemEl.remove() + oldParentScope = null + newParentScope = null + itemEl = null + tdom = $el - tdom.sortable({ - handle: ".taskboard-task-inner", - dropOnEmpty: true - connectWith: ".taskboard-tasks-box" - revert: 400 - }) + deleteElement = (itemEl) -> + # Completelly remove item and its scope from dom + itemEl.scope().$destroy() + itemEl.off() + itemEl.remove() - tdom.on "sortstop", (event, ui) -> - parentEl = ui.item.parent() - itemEl = ui.item - itemTask = itemEl.scope().task - itemIndex = itemEl.index() - newParentScope = parentEl.scope() + tdom.sortable({ + handle: ".taskboard-task-inner", + dropOnEmpty: true + connectWith: ".taskboard-tasks-box" + revert: 400 + }) - oldUsId = if oldParentScope.us then oldParentScope.us.id else null - oldStatusId = oldParentScope.st.id - newUsId = if newParentScope.us then newParentScope.us.id else null - newStatusId = newParentScope.st.id + tdom.on "sortstop", (event, ui) -> + parentEl = ui.item.parent() + itemEl = ui.item + itemTask = itemEl.scope().task + itemIndex = itemEl.index() + newParentScope = parentEl.scope() - if newStatusId != oldStatusId or newUsId != oldUsId - deleteElement(itemEl) + oldUsId = if oldParentScope.us then oldParentScope.us.id else null + oldStatusId = oldParentScope.st.id + newUsId = if newParentScope.us then newParentScope.us.id else null + newStatusId = newParentScope.st.id - $scope.$apply -> - $rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex) + if newStatusId != oldStatusId or newUsId != oldUsId + deleteElement(itemEl) - ui.item.find('a').removeClass('noclick') + $scope.$apply -> + $rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex) - tdom.on "sortstart", (event, ui) -> - oldParentScope = ui.item.parent().scope() - ui.item.find('a').addClass('noclick') + ui.item.find('a').removeClass('noclick') + + tdom.on "sortstart", (event, ui) -> + oldParentScope = ui.item.parent().scope() + ui.item.find('a').addClass('noclick') $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 1dd56d59..353fbaff 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -71,6 +71,8 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @rootscope.$broadcast("history:reload") @scope.$on "attachment:delete", => @rootscope.$broadcast("history:reload") + @scope.$on "custom-attributes-values:edit", => + @rootscope.$broadcast("history:reload") initializeOnDeleteGoToUrl: -> ctx = {project: @scope.project.slug} diff --git a/app/coffee/modules/team/main.coffee b/app/coffee/modules/team/main.coffee index 69830a2f..9bc9b57a 100644 --- a/app/coffee/modules/team/main.coffee +++ b/app/coffee/modules/team/main.coffee @@ -69,18 +69,18 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin) loadMembers: -> return @rs.memberships.list(@scope.projectId, {}, false).then (data) => currentUser = @auth.getUser() - if not currentUser.photo? + if currentUser? and not currentUser.photo? currentUser.photo = "/images/unnamed.png" @scope.currentUser = _.find data, (membership) => - return membership.user == currentUser.id + return currentUser? and membership.user == currentUser.id @scope.totals = {} _.forEach data, (membership) => @scope.totals[membership.user] = 0 @scope.memberships = _.filter data, (membership) => - if membership.user && membership.user != currentUser.id && membership.is_user_active + if membership.user && (not currentUser? or membership.user != currentUser.id) && membership.is_user_active return membership for membership in @scope.memberships diff --git a/app/coffee/modules/user-settings/change-password.coffee b/app/coffee/modules/user-settings/change-password.coffee index 4dc9fa28..d8df8ddd 100644 --- a/app/coffee/modules/user-settings/change-password.coffee +++ b/app/coffee/modules/user-settings/change-password.coffee @@ -97,7 +97,6 @@ UserChangePasswordDirective = ($rs, $confirm, $loading) -> submitButton = $el.find(".submit-button") $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/user-settings/main.coffee b/app/coffee/modules/user-settings/main.coffee index 7edb8eb1..656decd4 100644 --- a/app/coffee/modules/user-settings/main.coffee +++ b/app/coffee/modules/user-settings/main.coffee @@ -91,7 +91,8 @@ UserProfileDirective = ($confirm, $auth, $repo) -> changeEmail = $scope.user.isAttributeModified("email") onSuccess = (data) => - $auth.setUser($scope.user) + $auth.setUser(data) + if changeEmail $confirm.success("Check your inbox!
We have sent a mail to your account
@@ -107,14 +108,12 @@ UserProfileDirective = ($confirm, $auth, $repo) -> $el.on "submit", "form", submit - $el.on "click", ".submit-button", submit - $scope.$on "$destroy", -> $el.off() return {link:link} -module.directive("tgUserProfile", ["$tgConfirm", "$tgAuth", "$tgRepo", UserProfileDirective]) +module.directive("tgUserProfile", ["$tgConfirm", "$tgAuth", "$tgRepo", UserProfileDirective]) ############################################################################# @@ -131,26 +130,26 @@ UserAvatarDirective = ($auth, $model, $rs, $confirm) -> $auth.setUser(user) $scope.user = user - $el.find('.overlay').hide() + $el.find('.overlay').addClass('hidden') $confirm.notify('success') onError = (response) -> showSizeInfo() if response.status == 413 - $el.find('.overlay').hide() + $el.find('.overlay').addClass('hidden') $confirm.notify('error', response.data._error_message) # Change photo - $el.on "click", ".button.change", -> + $el.on "click", ".js-change-avatar", -> $el.find("#avatar-field").click() $el.on "change", "#avatar-field", (event) -> if $scope.avatarAttachment - $el.find('.overlay').css('display', 'flex') + $el.find('.overlay').removeClass('hidden') $rs.userSettings.changeAvatar($scope.avatarAttachment).then(onSuccess, onError) # Use gravatar photo $el.on "click", "a.use-gravatar", (event) -> - $el.find('.overlay').show() + $el.find('.overlay').removeClass('hidden') $rs.userSettings.removeAvatar().then(onSuccess, onError) $scope.$on "$destroy", -> diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 6db4ccac..7347f72d 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -80,6 +80,9 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @scope.$on "attachment:delete", => @rootscope.$broadcast("history:reload") + @scope.$on "custom-attributes-values:edit", => + @rootscope.$broadcast("history:reload") + initializeOnDeleteGoToUrl: -> ctx = {project: @scope.project.slug} @scope.onDeleteGoToUrl = @navUrls.resolve("project", ctx) diff --git a/app/coffee/modules/wiki/main.coffee b/app/coffee/modules/wiki/main.coffee index 6ef71c78..9596212b 100644 --- a/app/coffee/modules/wiki/main.coffee +++ b/app/coffee/modules/wiki/main.coffee @@ -70,6 +70,9 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) loadProject: -> return @rs.projects.getBySlug(@params.pslug).then (project) => + if not project.is_wiki_activated + @location.path(@navUrls.resolve("permission-denied")) + @scope.projectId = project.id @scope.project = project @scope.$emit('project:loaded', project) @@ -159,9 +162,6 @@ WikiSummaryDirective = ($log, $template) -> return if not wikiPage render(wikiPage) - $scope.$on "wiki:edit", (event, wikiPage) -> - render(wikiPage) - $scope.$on "$destroy", -> $el.off() @@ -215,8 +215,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $ if not wiki.id? $analytics.trackEvent("wikipage", "create", "create wiki page", 1) - $model.$modelValue = wikiPage - $scope.$broadcast("wiki:edit", wikiPage) + $model.$setViewValue wikiPage $confirm.notify("success") switchToReadMode() @@ -235,23 +234,16 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $ $loading.finish($el.find('.save-container')) $el.on "mousedown", ".view-wiki-content", (event) -> - # Prepare the scroll movement detection - target = angular.element(event.target) - if target.is('pre') - target.data("scroll-pos", target[0].scrollLeft) - - $el.on "mouseup", ".view-wiki-content", (event) -> - # We want to dettect the a inside the div so we use the target and - # not the currentTarget target = angular.element(event.target) return if not isEditable() - return if target.is('a') + return if event.button == 2 + + $el.on "mouseup", ".view-wiki-content", (event) -> + target = angular.element(event.target) return if getSelectedText() - if target.is('pre') - prevPos = target.data("scroll-pos") - target.data("scroll-pos", null) - if prevPos != target[0].scrollLeft - return + return if not isEditable() + return if target.is('a') + return if target.is('pre') switchToEditMode() diff --git a/app/partials/admin/admin-memberships.jade b/app/partials/admin/admin-memberships.jade index cf996944..880ccb68 100644 --- a/app/partials/admin/admin-memberships.jade +++ b/app/partials/admin/admin-memberships.jade @@ -9,7 +9,7 @@ div.wrapper.memberships(ng-controller="MembershipsController as ctrl", include ../includes/components/mainTitle .action-buttons - a.button.button-green(title="Add new member" href="" ng-click="ctrl.addNewMembers()") + a.button-green(title="Add new member" href="" ng-click="ctrl.addNewMembers()") span.text + New member include ../includes/modules/admin/admin-membership-table diff --git a/app/partials/admin/admin-project-export.jade b/app/partials/admin/admin-project-export.jade index ccac3ecb..3274b08a 100644 --- a/app/partials/admin/admin-project-export.jade +++ b/app/partials/admin/admin-project-export.jade @@ -12,10 +12,15 @@ div.wrapper(ng-controller="ProjectProfileController as ctrl", p.admin-subtitle Export your project to save a backup or to create a new one based on this. div.admin-project-export-buttons - a.button.button-green.button-export(href="", title="Export your project") Export + a.button-green.button-export(href="", title="Export your project") + span Export div.admin-project-export-result.hidden div.spin.hidden img(src="/svg/spinner-circle.svg", alt="loading...") h3.result-title p.result-message + + a.help-button(href="https://taiga.io/support/import-export-projects/", target="_blank") + span.icon.icon-help + span Do you need help? Check out our support page! diff --git a/app/partials/admin/admin-project-modules.jade b/app/partials/admin/admin-project-modules.jade index 21e1be00..0df09adf 100644 --- a/app/partials/admin/admin-project-modules.jade +++ b/app/partials/admin/admin-project-modules.jade @@ -87,5 +87,4 @@ div.wrapper(tg-project-modules, ng-controller="ProjectProfileController as ctrl" option(value="") Select a videoconference system input(type="text", ng-model="project.videoconferences_salt", placeholder="If you want you can append a salt code to the name of the chat room") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") Save diff --git a/app/partials/admin/admin-project-profile.jade b/app/partials/admin/admin-project-profile.jade index a2e93726..cb90def2 100644 --- a/app/partials/admin/admin-project-profile.jade +++ b/app/partials/admin/admin-project-profile.jade @@ -37,19 +37,18 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl" textarea(name="description", placeholder="Description", id="project-description", ng-model="project.description", data-required="true") - tg-privacy-settings-inputs + div div.privacy-settings div - input.hidden(type="radio", disabled="disabled") - label.button(for="public-project") Public Project + input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="false") + label.trans-button(for="public-project") + span Public Project div - input.hidden(type="radio", checked="checked", disabled="disabled") - label.button(for="private-project") Private Project + input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="true") + label.trans-button(for="private-project") + span Private Project - p All projects are private during Taiga's beta period. - - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") Save a.delete-project(href="", title="Delete this project", ng-click="ctrl.openDeleteLightbox()") Delete this project div.lightbox.lightbox-delete-project(tg-lb-delete-project) diff --git a/app/partials/admin/admin-project-reports.jade b/app/partials/admin/admin-project-reports.jade new file mode 100644 index 00000000..a066b767 --- /dev/null +++ b/app/partials/admin/admin-project-reports.jade @@ -0,0 +1,32 @@ +div.wrapper(ng-controller="ProjectProfileController as ctrl", + ng-init="section='admin'; sectionName='Reports'") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="reports") + include ../includes/modules/admin-submenu-project-profile + + section.main.admin-common(tg-project-export) + header + include ../includes/components/mainTitle + p.admin-subtitle Export your project data in CSV format and make your own reports. + + p Download a CSV file or copy the generated URL and open it in your favourite text editor or spreadsheet to make your own project data reports. You will be able to visualize and analize all your data easily. + + - var csvType = "US"; + - var controller = "CsvExporterUserstoriesController"; + div.admin-attributes-section + include ../includes/modules/admin/project-csv + + - var csvType = "Task"; + - var controller = "CsvExporterTasksController"; + div.admin-attributes-section + include ../includes/modules/admin/project-csv + + - var csvType = "Issues"; + - var controller = "CsvExporterIssuesController"; + div.admin-attributes-section + include ../includes/modules/admin/project-csv + a.help-button(href="https://taiga.io/support/csv-reports/", target="_blank") + span.icon.icon-help + span How to use this on my own spreadsheet? diff --git a/app/partials/admin/admin-project-values-custom-fields.jade b/app/partials/admin/admin-project-values-custom-fields.jade new file mode 100644 index 00000000..ddc03dfc --- /dev/null +++ b/app/partials/admin/admin-project-values-custom-fields.jade @@ -0,0 +1,31 @@ +div.wrapper(ng-controller="ProjectValuesSectionController") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-custom-fields") + include ../includes/modules/admin-submenu-project-values + + section.main.admin-common.admin-attributes + include ../includes/components/mainTitle + p.admin-subtitle Specify the custom fields for your user stories, tasks and issues + + div.admin-attributes-section(tg-project-custom-attributes, + ng-controller="ProjectCustomAttributesController as ctrl", + ng-init="type='userstory';") + - var customFieldSectionTitle = "User stories custom fields" + - var customFieldButtonTitle = "Add a custom field in user stories" + include ../includes/modules/admin/admin-custom-attributes + + div.admin-attributes-section(tg-project-custom-attributes, + ng-controller="ProjectCustomAttributesController as ctrl", + ng-init="type='task';") + - var customFieldSectionTitle = "Tasks custom fields" + - var customFieldButtonTitle = "Add a custom field in tasks" + include ../includes/modules/admin/admin-custom-attributes + + div.admin-attributes-section(tg-project-custom-attributes, + ng-controller="ProjectCustomAttributesController as ctrl", + ng-init="type='issue';") + - var customFieldSectionTitle = "Issues custom fields" + - var customFieldButtonTitle = "Add a custom field in issues" + include ../includes/modules/admin/admin-custom-attributes diff --git a/app/partials/admin/admin-project-values-issue-priorities.jade b/app/partials/admin/admin-project-values-issue-priorities.jade deleted file mode 100644 index cb2a2116..00000000 --- a/app/partials/admin/admin-project-values-issue-priorities.jade +++ /dev/null @@ -1,18 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='issues'; type='priorities'; sectionName='Issue Priorities'", - type="priorities") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-priorities") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the priority levels users can assign to issues - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new priority - - include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-issue-severities.jade b/app/partials/admin/admin-project-values-issue-severities.jade deleted file mode 100644 index 57fea5a7..00000000 --- a/app/partials/admin/admin-project-values-issue-severities.jade +++ /dev/null @@ -1,18 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='issues'; type='severities'; sectionName='Issue severities'", - type="severities") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-severities") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the severity level users can select to classify issues - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new severity - - include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-issue-status.jade b/app/partials/admin/admin-project-values-issue-status.jade deleted file mode 100644 index b02c72e6..00000000 --- a/app/partials/admin/admin-project-values-issue-status.jade +++ /dev/null @@ -1,18 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='issues'; type='issue-statuses'; sectionName='Issue Statuses'", - type="issue-statuses") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-issue-status") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the column headers that you will use to classify Issues - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new status - - include ../includes/modules/admin/project-status diff --git a/app/partials/admin/admin-project-values-issue-types.jade b/app/partials/admin/admin-project-values-issue-types.jade deleted file mode 100644 index 20072b9e..00000000 --- a/app/partials/admin/admin-project-values-issue-types.jade +++ /dev/null @@ -1,18 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='issues'; type='issue-types'; sectionName='Issue Types'", - type="issue-types") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-issue-types") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the categories users can select to classify issues - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new type - - include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-points.jade b/app/partials/admin/admin-project-values-points.jade new file mode 100644 index 00000000..64ef3fb1 --- /dev/null +++ b/app/partials/admin/admin-project-values-points.jade @@ -0,0 +1,15 @@ +div.wrapper(ng-controller="ProjectValuesSectionController") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-points") + include ../includes/modules/admin-submenu-project-values + + section.main.admin-common.admin-attributes + include ../includes/components/mainTitle + p.admin-subtitle Specify the points your user stories could be estimated to + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='userstories'; type='points'; sectionName='Us points'", + type="points") + include ../includes/modules/admin/project-points diff --git a/app/partials/admin/admin-project-values-priorities.jade b/app/partials/admin/admin-project-values-priorities.jade new file mode 100644 index 00000000..4e0c5098 --- /dev/null +++ b/app/partials/admin/admin-project-values-priorities.jade @@ -0,0 +1,15 @@ +div.wrapper(ng-controller="ProjectValuesSectionController") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-priorities") + include ../includes/modules/admin-submenu-project-values + + section.main.admin-common.admin-attributes + include ../includes/components/mainTitle + p.admin-subtitle Specify the priorities your issues will have + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='issues'; type='priorities'; sectionName='Issue priorities'; objName='priority'", + type="priorities") + include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-severities.jade b/app/partials/admin/admin-project-values-severities.jade new file mode 100644 index 00000000..507ff930 --- /dev/null +++ b/app/partials/admin/admin-project-values-severities.jade @@ -0,0 +1,15 @@ +div.wrapper(ng-controller="ProjectValuesSectionController") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-severities") + include ../includes/modules/admin-submenu-project-values + + section.main.admin-common.admin-attributes + include ../includes/components/mainTitle + p.admin-subtitle Specify the severities your issues will have + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='issues'; type='severities'; sectionName='Issue severities'; objName='severity'", + type="severities") + include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-status.jade b/app/partials/admin/admin-project-values-status.jade new file mode 100644 index 00000000..5f6e8876 --- /dev/null +++ b/app/partials/admin/admin-project-values-status.jade @@ -0,0 +1,25 @@ +div.wrapper(ng-controller="ProjectValuesSectionController") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-status") + include ../includes/modules/admin-submenu-project-values + + section.main.admin-common.admin-attributes + include ../includes/components/mainTitle + p.admin-subtitle Specify the statuses your user stories, tasks and issues will go through + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='userstories'; type='userstory-statuses'; sectionName='Us Statuses'", + type="userstory-statuses") + include ../includes/modules/admin/project-us-status + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='tasks'; type='task-statuses'; sectionName='Task Statuses'", + type="task-statuses") + include ../includes/modules/admin/project-status + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='issues'; type='issue-statuses'; sectionName='Issue Statuses'", + type="issue-statuses") + include ../includes/modules/admin/project-status diff --git a/app/partials/admin/admin-project-values-task-status.jade b/app/partials/admin/admin-project-values-task-status.jade deleted file mode 100644 index be883b1f..00000000 --- a/app/partials/admin/admin-project-values-task-status.jade +++ /dev/null @@ -1,18 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='tasks'; type='task-statuses'; sectionName='Task Statuses'", - type="task-statuses") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-task-status") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the column headers that you will use to classify Tasks related to each User Stories - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new status - - include ../includes/modules/admin/project-status diff --git a/app/partials/admin/admin-project-values-types.jade b/app/partials/admin/admin-project-values-types.jade new file mode 100644 index 00000000..2f033b29 --- /dev/null +++ b/app/partials/admin/admin-project-values-types.jade @@ -0,0 +1,15 @@ +div.wrapper(ng-controller="ProjectValuesSectionController") + sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") + include ../includes/modules/admin-menu + + sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-types") + include ../includes/modules/admin-submenu-project-values + + section.main.admin-common.admin-attributes + include ../includes/components/mainTitle + p.admin-subtitle Specify the types your user stories could be estimated to + + div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", + ng-init="section='admin'; resource='issues'; type='issue-types'; sectionName='Issue types'; objName='type'", + type="issue-types") + include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-us-points.jade b/app/partials/admin/admin-project-values-us-points.jade deleted file mode 100644 index 4483a03f..00000000 --- a/app/partials/admin/admin-project-values-us-points.jade +++ /dev/null @@ -1,24 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='userstories'; type='points'; sectionName='Us points'", - type="points") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-us-points") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the numerical system you will use to indicate the level of difficulty for each User Story - - - var helpLightboxId = "notion-admin-project-values-us-points" - include ../includes/components/help-notion-button - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new point - - include ../includes/modules/admin/project-points - -div.lightbox.lightbox-generic-notion.notion-admin-project-values-us-points(id="notion-admin-project-values-us-points", tg-lb-notion) - include ../includes/modules/help-notions/lightbox-notion-admin-project-values-us-points diff --git a/app/partials/admin/admin-project-values-us-status.jade b/app/partials/admin/admin-project-values-us-status.jade deleted file mode 100644 index eb6980cc..00000000 --- a/app/partials/admin/admin-project-values-us-status.jade +++ /dev/null @@ -1,18 +0,0 @@ -div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='userstories'; type='userstory-statuses'; sectionName='Us Statuses'", - type="userstory-statuses") - sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values") - include ../includes/modules/admin-menu - - sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-us-status") - include ../includes/modules/admin-submenu-project-values - - section.main.admin-common - include ../includes/components/mainTitle - p.admin-subtitle Specify the column headers that you will use to classify User Stories - - div.project-values-options - a.button.button-green.show-add-new(href="", title="Add New") - span Add new status - - include ../includes/modules/admin/project-us-status diff --git a/app/partials/admin/admin-roles.jade b/app/partials/admin/admin-roles.jade index 335cef2d..2899d960 100644 --- a/app/partials/admin/admin-roles.jade +++ b/app/partials/admin/admin-roles.jade @@ -8,27 +8,33 @@ div.wrapper.roles(ng-controller="RolesController as ctrl", section.main.admin-roles.admin-common .header-with-actions include ../includes/components/mainTitle - .action-buttons - a.button.button-red.delete-role(href="", title="Delete", ng-click="ctrl.delete()") Delete + .action-buttons(ng-if="!role.external_user") + a.button-red.delete-role(href="", title="Delete", ng-click="ctrl.delete()") + span Delete - div(tg-edit-role) - .edit-role - input(type="text", value="{{ role.name }}") - a.save.icon.icon-floppy(href="", title="Save") + div(ng-if="!role.external_user") + div(tg-edit-role) + .edit-role + input(type="text", value="{{ role.name }}") + a.save.icon.icon-floppy(href="", title="Save") + p.total + span.role-name(title="{{ role.members_count }} members with this role") {{ role.name }} + a.edit-value.icon.icon-edit + + div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories + + div.general-category + | When enabled, members assigned to this role will be able to estimate the point value for user stories + div.check + input(type="checkbox", ng-model="role.computable", ng-change="ctrl.setComputable()") + div + span.check-text.check-yes Yes + span.check-text.check-no No + + div(ng-if="role.external_user") p.total - span.role-name(title="{{ role.members_count }} members with this role") {{ role.name }} - a.edit-value.icon.icon-edit - - div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories - - div.general-category - | When enabled, members assigned to this role will be able to estimate the point value for user stories - div.check - input(type="checkbox", ng-model="role.computable", ng-change="ctrl.setComputable()") - div - span.check-text.check-yes Yes - span.check-text.check-no No + span.role-name {{ role.name }} div(tg-role-permissions, ng-model="role") diff --git a/app/partials/admin/admin-third-parties-bitbucket.jade b/app/partials/admin/admin-third-parties-bitbucket.jade index e2a1f05c..0fb33c63 100644 --- a/app/partials/admin/admin-third-parties-bitbucket.jade +++ b/app/partials/admin/admin-third-parties-bitbucket.jade @@ -27,9 +27,7 @@ div.wrapper.roles(tg-bitbucket-webhooks, ng-controller="BitbucketController as c label(for="valid-origin-ips") Valid origin ips (separated by ,) input(type="text", name="valid-origin-ips", tg-valid-origin-ips, ng-model="bitbucket.valid_origin_ips", placeholder="Bitbucket requests are not signed so the best way of verifying the origin is by IP. If the field is empty there will be no IP validation.", id="valid-origin-ips") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save - + button.button-green.submit-button(type="submit", title="Save") Save a.help-button(href="https://taiga.io/support/bitbucket-integration/", target="_blank") span.icon.icon-help diff --git a/app/partials/admin/admin-third-parties-github.jade b/app/partials/admin/admin-third-parties-github.jade index ae92c1fa..c9ce5e8d 100644 --- a/app/partials/admin/admin-third-parties-github.jade +++ b/app/partials/admin/admin-third-parties-github.jade @@ -23,9 +23,7 @@ div.wrapper.roles(tg-github-webhooks, ng-controller="GithubController as ctrl", .icon.icon-copy .help-copy Copy to clipboard: Ctrl+C - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save - + button.button-green.submit-button(type="submit", title="Save") Save a.help-button(href="https://taiga.io/support/github-integration/", target="_blank") span.icon.icon-help diff --git a/app/partials/admin/admin-third-parties-gitlab.jade b/app/partials/admin/admin-third-parties-gitlab.jade index dc89117f..6ce05ce2 100644 --- a/app/partials/admin/admin-third-parties-gitlab.jade +++ b/app/partials/admin/admin-third-parties-gitlab.jade @@ -31,8 +31,7 @@ block content label(for="valid-origin-ips") Valid origin ips (separated by ,) input(type="text", name="valid-origin-ips", tg-valid-origin-ips, ng-model="gitlab.valid_origin_ips", placeholder="Gitlab requests are not signed so the best way of verifying the origin is by IP. If the field is empty there will be no IP validation.", id="valid-origin-ips") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") Save a.help-button(href="https://taiga.io/support/gitlab-integration/", target="_blank") span.icon.icon-help diff --git a/app/partials/admin/admin-third-parties-webhooks.jade b/app/partials/admin/admin-third-parties-webhooks.jade index e56468a5..b88fd939 100644 --- a/app/partials/admin/admin-third-parties-webhooks.jade +++ b/app/partials/admin/admin-third-parties-webhooks.jade @@ -14,7 +14,7 @@ block content p.admin-subtitle Webhooks notify external services about events in Taiga, like comments, user stories.... div.webhooks-options - a.button.button-green.hidden.add-webhook(href="",title="Add a New Webhook") Add Webhook + a.button-green.hidden.add-webhook(href="",title="Add a New Webhook") Add Webhook section.webhooks-table.basic-table div.table-header diff --git a/app/partials/attachment/attachments.jade b/app/partials/attachment/attachments.jade index 363b71a8..bc025b6c 100644 --- a/app/partials/attachment/attachments.jade +++ b/app/partials/attachment/attachments.jade @@ -11,7 +11,7 @@ section.attachments input(id="add-attach", type="file", multiple="multiple") .attachment-body.sortable - .single-attachment(ng-repeat="attach in ctrl.attachments|filter:ctrl.filterAttachments track by attach.id" tg-attachment="attach") + .single-attachment(ng-repeat="attach in ctrl.attachments|filter:ctrl.filterAttachments track by attach.id" tg-attachment="attach", tg-bind-scope) .single-attachment(ng-repeat="file in ctrl.uploadingAttachments") .attachment-name diff --git a/app/partials/backlog/backlog.jade b/app/partials/backlog/backlog.jade index e8b4dd55..1b96fa32 100644 --- a/app/partials/backlog/backlog.jade +++ b/app/partials/backlog/backlog.jade @@ -9,16 +9,17 @@ div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl", div.burndown(tg-gm-backlog-graph) include ../includes/modules/burndown div.backlog-menu - a.trans-button.move-to-current-sprint(href="", title="Move to Current Sprint", - id="move-to-current-sprint") - span.icon.icon-move - span.text Move to current Sprint - a.trans-button(href="", title="Show Filters", id="show-filters-button") - span.icon.icon-filter - span.text Show Filters - a.trans-button(href="", title="Show Tags", id="show-tags") - span.icon.icon-tag - span.text Show Tags + div.backlog-table-options + a.trans-button.move-to-current-sprint(href="", title="Move to Current Sprint", + id="move-to-current-sprint") + span.icon.icon-move + span.text Move to current Sprint + a.trans-button(href="", title="Show Filters", id="show-filters-button") + span.icon.icon-filter + span.text Show Filters + a.trans-button(href="", title="Show Tags", id="show-tags") + span.icon.icon-tag + span.text Show Tags include ../includes/components/addnewus section.backlog-table(ng-class="{'hidden': !visibleUserstories.length}") include ../includes/modules/backlog-table diff --git a/app/partials/backlog/us-points-popover.jade b/app/partials/backlog/us-points-popover.jade deleted file mode 100644 index 0e39c5f3..00000000 --- a/app/partials/backlog/us-points-popover.jade +++ /dev/null @@ -1,11 +0,0 @@ -ul.popover.pop-points-open - <% _.each(points, function(point) { %> - li - <% if (point.selected) { %> - a.point(href="", title!="<%- point.name %>", data-point-id!="<%- point.id %>") - | <%- point.name %> - <% } else { %> - a.point.active(href="", title!="<%- point.name %>", data-point-id!="<%- point.id %>") - | <%- point.name %> - <% } %> - <% }); %> diff --git a/app/partials/common/components/block-button.jade b/app/partials/common/components/block-button.jade index 354ec00d..7e187687 100644 --- a/app/partials/common/components/block-button.jade +++ b/app/partials/common/components/block-button.jade @@ -1,2 +1,4 @@ -a(href="#", class="button button-gray item-block") Block -a(href="#", class="button button-red item-unblock") Unblock \ No newline at end of file +a(href="#", class="button button-gray item-block") + span Block +a(href="#", class="button button-red item-unblock") + span Unblock diff --git a/app/partials/common/components/delete-button.jade b/app/partials/common/components/delete-button.jade index c33733cf..dc2f2065 100644 --- a/app/partials/common/components/delete-button.jade +++ b/app/partials/common/components/delete-button.jade @@ -1 +1,2 @@ -a(href="", class="button button-red") Delete \ No newline at end of file +a(href="", class="button button-red") + span Delete diff --git a/app/partials/common/components/watchers.jade b/app/partials/common/components/watchers.jade index 1361d34c..8d5dac17 100644 --- a/app/partials/common/components/watchers.jade +++ b/app/partials/common/components/watchers.jade @@ -11,8 +11,7 @@ <% if(watcher) { %> .watcher-single .watcher-avatar - span.avatar(title!="<%- watcher.full_name_display %>") - img(src!="<%- watcher.photo %>" alt!="<%- watcher.full_name_display %>") + img(src!="<%- watcher.photo %>" alt!="<%- watcher.full_name_display %>") .watcher-name span <%- watcher.full_name_display %> @@ -20,4 +19,4 @@ a.icon.icon-delete(data-watcher-id!="<%- watcher.id %>" href="" title="delete-watcher") <% }; %> <% } %> -<% }); %> \ No newline at end of file +<% }); %> diff --git a/app/partials/common/estimation/lb-us-estimation-points-per-role.jade b/app/partials/common/estimation/lb-us-estimation-points-per-role.jade deleted file mode 100644 index e49e6628..00000000 --- a/app/partials/common/estimation/lb-us-estimation-points-per-role.jade +++ /dev/null @@ -1,9 +0,0 @@ -ul.points-per-role - li.total - span.points <%- totalPoints %> - span.role total - <% _.each(roles, function(role) { %> - li.total.clickable(data-role-id!="<%- role.id %>") - span.points <%- role.points %> - span.role <%- role.name %> - <% }); %> diff --git a/app/partials/common/estimation/lb-us-estimation-points.jade b/app/partials/common/estimation/lb-us-estimation-points.jade deleted file mode 100644 index 04fbceaf..00000000 --- a/app/partials/common/estimation/lb-us-estimation-points.jade +++ /dev/null @@ -1,13 +0,0 @@ -ul.popover.pop-points-open - <% _.each(points, function(point) { %> - li - <% if (point.selected) { %> - a(href="", class="point", title!="<%- point.name %>", - data-point-id!="<%- point.id %>", data-role-id!="<%- roleId %>") - | <%- point.name %> - <% } else { %> - a(href="", class="point active", title!="<%- point.name %>", - data-point-id!="<%- point.id %>" data-role-id!="<%- roleId %>") - | <%- point.name %> - <% } %> - <% }); %> diff --git a/app/partials/common/estimation/us-estimation-points-per-role.jade b/app/partials/common/estimation/us-estimation-points-per-role.jade index e905cf6b..7491d5a8 100644 --- a/app/partials/common/estimation/us-estimation-points-per-role.jade +++ b/app/partials/common/estimation/us-estimation-points-per-role.jade @@ -3,7 +3,7 @@ ul.points-per-role span.points <%- totalPoints %> span.role total <% _.each(roles, function(role) { %> - li(class!="total <% if(editable){ %>clickable<% } %>", data-role-id!="<%- role.id %>") + li(class!="total <% if(editable){ %>clickable<% } %>", data-role-id!="<%- role.id %>", title!="<%- role.name %>") span.points <%- role.points %> span.role <%- role.name %> <% }); %> diff --git a/app/partials/common/estimation/us-estimation-points.jade b/app/partials/common/estimation/us-estimation-points.jade index 1fb05d0b..8440a616 100644 --- a/app/partials/common/estimation/us-estimation-points.jade +++ b/app/partials/common/estimation/us-estimation-points.jade @@ -1,4 +1,4 @@ -ul.popover.pop-points-open +ul.popover.pop-points-open(class!="<% if (horizontal) { %>horizontal<% }; %>") <% _.each(points, function(point) { %> li <% if (point.selected) { %> diff --git a/app/partials/common/estimation/us-estimation-total.jade b/app/partials/common/estimation/us-estimation-total.jade new file mode 100644 index 00000000..235494bb --- /dev/null +++ b/app/partials/common/estimation/us-estimation-total.jade @@ -0,0 +1,5 @@ +a.us-points(href="", title!="<%= title %>", class!="<% if (!editable) { %>not-clickable<% } %>") + span.points-value <%= text %> + <% if (editable) { %> + span.icon.icon-arrow-bottom(tg-check-permission="modify_us") + <% } %> diff --git a/app/partials/backlog/us-points-roles-popover.jade b/app/partials/common/estimation/us-points-roles-popover.jade similarity index 100% rename from app/partials/backlog/us-points-roles-popover.jade rename to app/partials/common/estimation/us-points-roles-popover.jade diff --git a/app/partials/common/history/history-base.jade b/app/partials/common/history/history-base.jade index cd3cfc41..383c21ab 100644 --- a/app/partials/common/history/history-base.jade +++ b/app/partials/common/history/history-base.jade @@ -11,13 +11,13 @@ section.history section.history-comments .comments-list div(tg-check-permission!="modify_<%- type %>", tg-toggle-comment, class="add-comment") - textarea(placeholder="Type a new comment here", - ng-model!="<%- ngmodel %>.comment", tg-markitup="tg-markitup") + textarea(placeholder="Type a new comment here", ng-model!="<%- ngmodel %>.comment", tg-markitup="tg-markitup") <% if (mode !== "edit") { %> a(class="help-markdown", href="https://taiga.io/support/taiga-markdown-syntax/", target="_blank", title="Mardown syntax help") span.icon.icon-help span Markdown syntax help - a(href="", title="Comment", class="button button-green save-comment") Comment + a(href="", title="Comment", class="button button-green save-comment") + span Comment <% } %> section.history-activity.hidden .changes-list diff --git a/app/partials/common/history/history-change-list.jade b/app/partials/common/history/history-change-list.jade new file mode 100644 index 00000000..f11c5fa0 --- /dev/null +++ b/app/partials/common/history/history-change-list.jade @@ -0,0 +1,17 @@ +.change-entry + .activity-changed + span <%- name %> + .activity-fromto + <% if (removed.length > 0) { %> + p + strong removed + br + span <%- removed %> + <% } %> + + <% if (added.length > 0) { %> + p + strong added + br + span <%- added %> + <% } %> diff --git a/app/partials/common/lightbox/lightbox-block.jade b/app/partials/common/lightbox/lightbox-block.jade index b80a0177..a6290d16 100644 --- a/app/partials/common/lightbox/lightbox-block.jade +++ b/app/partials/common/lightbox/lightbox-block.jade @@ -5,5 +5,5 @@ div.form fieldset textarea.reason(placeholder="Please explain the reason") - a.button.button-green(href="") + a.button-green(href="") span Save diff --git a/app/partials/custom-attributes/custom-attribute-value-edit.jade b/app/partials/custom-attributes/custom-attribute-value-edit.jade new file mode 100644 index 00000000..d3c3aabc --- /dev/null +++ b/app/partials/custom-attributes/custom-attribute-value-edit.jade @@ -0,0 +1,14 @@ +form.custom-field-single.editable + div.custom-field-data + label.custom-field-name(for="custom-field-description") + <%- name %> + <% if (description){ %> + span.custom-field-description + <%- description %> + <% } %> + + div.custom-field-value + input#custom-field-description(name="description", type="text", value!="<%- value %>") + + div.custom-field-options + a.icon.icon-floppy(href="", title="Save Custom Field") diff --git a/app/partials/custom-attributes/custom-attribute-value.jade b/app/partials/custom-attributes/custom-attribute-value.jade new file mode 100644 index 00000000..b2502c04 --- /dev/null +++ b/app/partials/custom-attributes/custom-attribute-value.jade @@ -0,0 +1,17 @@ +div.custom-field-single + div.custom-field-data + span.custom-field-name + <%- name %> + <% if (description){ %> + span.custom-field-description + <%- description %> + <% } %> + + div.custom-field-value.read-mode + span + <%- value %> + + <% if (isEditable) { %> + div.custom-field-options + a.icon.icon-edit(href="", title="Edit Custom Field") + <% } %> diff --git a/app/partials/custom-attributes/custom-attributes-values.jade b/app/partials/custom-attributes/custom-attributes-values.jade new file mode 100644 index 00000000..cadbb100 --- /dev/null +++ b/app/partials/custom-attributes/custom-attributes-values.jade @@ -0,0 +1,7 @@ +section.duty-custom-fields(ng-show="ctrl.customAttributes.length") + div.custom-fields-header + span Custom Fields + // Remove .open class on click on this button in both .icon and .custom-fields-body to close + a.icon.icon-arrow-bottom(class!="<% if (!collapsed) { %>open<% } %>") + div.custom-fields-body(class!="<% if (!collapsed) { %>open<% } %>") + div(ng-repeat="att in ctrl.customAttributes", tg-custom-attribute-value="ctrl.getAttributeValue(att)", required-edition-perm!="<%- requiredEditionPerm %>") diff --git a/app/partials/includes/components/addnewus.jade b/app/partials/includes/components/addnewus.jade index 815ca0b1..403c271c 100644 --- a/app/partials/includes/components/addnewus.jade +++ b/app/partials/includes/components/addnewus.jade @@ -1,9 +1,9 @@ div.new-us - a.button.button-green(href="", title="Add a new User Story", + a.button-green(href="", title="Add a new User Story", ng-click="ctrl.addNewUs('standard')", tg-check-permission="add_us") span.text + Add a new User Story - a.button.button-bulk(href="", title="Add some new User Stories in bulk", + a.button-bulk(href="", title="Add some new User Stories in bulk", ng-click="ctrl.addNewUs('bulk')", tg-check-permission="add_us") span.icon.icon-bulk diff --git a/app/partials/includes/components/backlog-row.jade b/app/partials/includes/components/backlog-row.jade index a340e1ae..7b10d154 100644 --- a/app/partials/includes/components/backlog-row.jade +++ b/app/partials/includes/components/backlog-row.jade @@ -1,4 +1,4 @@ -div.row.us-item-row(ng-repeat="us in visibleUserstories track by us.id", tg-draggable, ng-class="{blocked: us.is_blocked}") +div.row.us-item-row(ng-repeat="us in visibleUserstories track by us.id", tg-bind-scope, ng-class="{blocked: us.is_blocked}", tg-class-permission="{'readonly': '!modify_us'}") div.user-stories div.tags-block(tg-colorize-tags="us.tags", tg-colorize-tags-type="backlog") div.user-story-name @@ -20,7 +20,5 @@ div.row.us-item-row(ng-repeat="us in visibleUserstories track by us.id", tg-drag div.points(tg-backlog-us-points="us") a.us-points(href="", title="Points") - span.points-value 0 - span.icon.icon-arrow-bottom(tg-check-permission="modify_us") a.icon.icon-drag-v(tg-check-permission="modify_us", href="", title="Drag") diff --git a/app/partials/includes/components/help-notion-button.jade b/app/partials/includes/components/help-notion-button.jade deleted file mode 100644 index 510a7fbd..00000000 --- a/app/partials/includes/components/help-notion-button.jade +++ /dev/null @@ -1,12 +0,0 @@ -//- NOTE: - Add a lightbox-notion at the end of parent template. Ex: -//- -//- div.hide.lightbox.lightbox-generic-notion(id="notion-admin-project-values-us-points", -//- tg-lb-notion) -//- include views/modules/help-notions/lightbox-notion-admin-project-values-us-points -//- -//- - Defined variable 'helpLightboxId' in parent template. Ex: -//- -//- - var helpLightboxId = "admin-project-values-us-points" -//- include views/components/help - -a.icon.icon-idea.help(href="", title="You need some help?", tg-lb-notion-button=helpLightboxId) diff --git a/app/partials/includes/components/kanban-task.jade b/app/partials/includes/components/kanban-task.jade deleted file mode 100644 index 8431c184..00000000 --- a/app/partials/includes/components/kanban-task.jade +++ /dev/null @@ -1,23 +0,0 @@ -div.kanban-tagline(tg-colorize-tags="us.tags", tg-colorize-tags-type="kanban", ng-hide="us.isArchived") -div.kanban-task-inner(ng-class="{'task-archived': us.isArchived}") - div.avatar-wrapper(tg-kanban-user-avatar="us.assigned_to", ng-model="us", ng-hide="us.isArchived") - div.task-text(ng-hide="us.isArchived") - a.task-assigned(href="", title="Assign User Story") - span.task-num(tg-bo-ref="us.ref") - a.task-name(href="", title="See user story detail", ng-bind="us.subject", - tg-nav="project-userstories-detail:project=project.slug,ref=us.ref") - - p.task-points(href="", title="Total Us points") - span(ng-if="us.total_points !== null", ng-bind="us.total_points") - span(ng-if="us.total_points !== null") points - span(ng-if="us.total_points === null") Not estimated - - div.task-archived-text(ng-show="us.isArchived") - p You have archived - p - span.task-num(tg-bo-ref="us.ref") - span.task-name(ng-bind="us.subject") - p Drag & drop again to undo - - a.icon.icon-edit(tg-check-permission="modify_us", href="", title="Edit", ng-hide="us.isArchived") - a.icon.icon-drag-h(tg-check-permission="modify_us", href="", title="Drag&Drop", ng-hide="us.isArchived") diff --git a/app/partials/includes/components/large-summary.jade b/app/partials/includes/components/large-summary.jade index a20b326d..ed410e63 100644 --- a/app/partials/includes/components/large-summary.jade +++ b/app/partials/includes/components/large-summary.jade @@ -1,37 +1,35 @@ div.summary.large-summary - div - div.summary-progress-bar - div.current-progress - div.data - span.number 30% - span.description completed - ul - li + div.large-summary-wrapper + div + div.summary-progress-bar + div.current-progress + div.data + span.number 30% + span.description completed + div.summary-stats span.number 12 span.description project
points - li + div.summary-stats span.number 23 span.description defined
points - li + div.summary-stats span.number 12 span.description assigned
points - li + div.summary-stats.summary-stats-divider span.number 23 span.description closed
points - ul - li + div.summary-stats span.icon.icon-bulk span.number 73 span.description created
tasks - li + div.summary-stats span.number 72 span.description closed
tasks - li + div.summary-stats span.number 18 span.description remaining
tasks - ul - li + div.summary-stats span.icon.icon-iocaine span.number 10 span.description iocanie
doses diff --git a/app/partials/includes/components/sprint-summary.jade b/app/partials/includes/components/sprint-summary.jade index 585f7b25..64d6bdbb 100644 --- a/app/partials/includes/components/sprint-summary.jade +++ b/app/partials/includes/components/sprint-summary.jade @@ -1,28 +1,26 @@ div.summary.large-summary - div - div.summary-progress-bar(tg-progress-bar="stats.completedPercentage") - div.data - span.number(ng-bind="stats.completedPercentage + '%'") + div.large-summary-wrapper + div.summary-progress-wrapper + div.summary-progress-bar(tg-progress-bar="stats.completedPercentage") + div.data + span.number(ng-bind="stats.completedPercentage + '%'") - ul - li + div.summary-stats span.number(ng-bind="stats.totalPointsSum|default:'--'") span.description total
points - li + div.summary-stats span.number(ng-bind="stats.completedPointsSum|default:'--'") span.description completed
points - ul - li + div.summary-stats span.icon.icon-bulk span.number(ng-bind="stats.openTasks|default:'--'") span.description open
tasks - li + div.summary-stats span.number(ng-bind="stats.completed_tasks|default:'--'") span.description closed
tasks - ul - li(title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!") + div.summary-stats(title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!") span.icon.icon-iocaine span.number(ng-bind="stats.iocaine_doses|default:'--'") span.description iocaine
doses diff --git a/app/partials/includes/components/summary.jade b/app/partials/includes/components/summary.jade index 9af07112..11624d8d 100644 --- a/app/partials/includes/components/summary.jade +++ b/app/partials/includes/components/summary.jade @@ -3,16 +3,16 @@ div.summary div.data span.number(ng-bind="stats.completedPercentage + '%'") - ul - li - span.number(ng-bind="stats.total_points") -- - span.description project
points - li - span.number(ng-bind="stats.defined_points") -- - span.description defined
points - li - span.number(ng-bind="stats.closed_points") -- - span.description closed
points - li - span.number(ng-bind="stats.speed | number:0") -- - span.description points /
sprint + + div.summary-stats + span.number(ng-bind="stats.total_points") -- + span.description project
points + div.summary-stats + span.number(ng-bind="stats.defined_points") -- + span.description defined
points + div.summary-stats + span.number(ng-bind="stats.closed_points") -- + span.description closed
points + div.summary-stats + span.number(ng-bind="stats.speed | number:0") -- + span.description points /
sprint diff --git a/app/partials/includes/components/taskboard-task.jade b/app/partials/includes/components/taskboard-task.jade index 29311f7c..7460a74f 100644 --- a/app/partials/includes/components/taskboard-task.jade +++ b/app/partials/includes/components/taskboard-task.jade @@ -5,10 +5,7 @@ div.taskboard-task-inner p.taskboard-text a.task-assigned(href="", title="Assign task") span.task-num(tg-bo-ref="task.ref") - a.task-name(href="", title="See task details", ng-bind="task.subject", + a.task-name(href="", title="#{{ ::task.ref }} {{ ::task.subject }}", ng-bind="task.subject", tg-nav="project-tasks-detail:project=project.slug,ref=task.ref") a.icon.icon-edit(tg-check-permission="modify_task", href="", title="Edit task") - - a.icon.icon-drag-h(tg-check-permission="modify_task", - href="", title="Drag&Drop") diff --git a/app/partials/includes/modules/admin-menu.jade b/app/partials/includes/modules/admin-menu.jade index 6f924633..0a4cb08e 100644 --- a/app/partials/includes/modules/admin-menu.jade +++ b/app/partials/includes/modules/admin-menu.jade @@ -6,19 +6,19 @@ section.admin-menu ul li#adminmenu-project-profile a(href="", tg-nav="project-admin-project-profile-details:project=project.slug") - span.title Project profile + span.title Project span.icon.icon-arrow-right li#adminmenu-project-values - a(href="", tg-nav="project-admin-project-values-us-status:project=project.slug") - span.title Custom Attributes + a(href="", tg-nav="project-admin-project-values-status:project=project.slug") + span.title Attributes span.icon.icon-arrow-right li#adminmenu-memberships a(href="" tg-nav="project-admin-memberships:project=project.slug") - span.title Manage members + span.title Members span.icon.icon-arrow-right li#adminmenu-roles a(href="" tg-nav="project-admin-roles:project=project.slug") - span.title Roles & Permissions + span.title Permissions span.icon.icon-arrow-right li#adminmenu-third-parties a(href="" tg-nav="project-admin-third-parties-webhooks:project=project.slug") diff --git a/app/partials/includes/modules/admin-submenu-project-profile.jade b/app/partials/includes/modules/admin-submenu-project-profile.jade index e9522a86..30be41da 100644 --- a/app/partials/includes/modules/admin-submenu-project-profile.jade +++ b/app/partials/includes/modules/admin-submenu-project-profile.jade @@ -20,3 +20,7 @@ section.admin-submenu a(href="", tg-nav="project-admin-project-profile-export:project=project.slug") span.title Export span.icon.icon-arrow-right + li#adminmenu-reports + a(href="", tg-nav="project-admin-project-profile-reports:project=project.slug") + span.title Reports + span.icon.icon-arrow-right diff --git a/app/partials/includes/modules/admin-submenu-project-values.jade b/app/partials/includes/modules/admin-submenu-project-values.jade index 47cc1134..57df5300 100644 --- a/app/partials/includes/modules/admin-submenu-project-values.jade +++ b/app/partials/includes/modules/admin-submenu-project-values.jade @@ -1,40 +1,35 @@ section.admin-submenu header - h1 Custom Attributes + h1 Attributes nav ul - li#adminmenu-values-us-status - a(href="", tg-nav="project-admin-project-values-us-status:project=project.slug") - span.title US statuses + li#adminmenu-values-status + a(href="", tg-nav="project-admin-project-values-status:project=project.slug") + span.title Status span.icon.icon-arrow-right - li#adminmenu-values-us-points - a(href="", tg-nav="project-admin-project-values-us-points:project=project.slug") - span.title US points - span.icon.icon-arrow-right - - li#adminmenu-values-task-status - a(href="", tg-nav="project-admin-project-values-task-status:project=project.slug") - span.title Task statuses - span.icon.icon-arrow-right - - li#adminmenu-values-issue-status - a(href="", tg-nav="project-admin-project-values-issue-status:project=project.slug") - span.title Issue statuses - span.icon.icon-arrow-right - - li#adminmenu-values-issue-types - a(href="", tg-nav="project-admin-project-values-issue-types:project=project.slug") - span.title Issue types + li#adminmenu-values-points + a(href="", tg-nav="project-admin-project-values-points:project=project.slug") + span.title Points span.icon.icon-arrow-right li#adminmenu-values-priorities - a(href="", tg-nav="project-admin-project-values-issue-priorities:project=project.slug") - span.title Issue Priorities + a(href="", tg-nav="project-admin-project-values-priorities:project=project.slug") + span.title Priorities span.icon.icon-arrow-right li#adminmenu-values-severities - a(href="", tg-nav="project-admin-project-values-issue-severities:project=project.slug") - span.title Issue Severities + a(href="", tg-nav="project-admin-project-values-severities:project=project.slug") + span.title Severities + span.icon.icon-arrow-right + + li#adminmenu-values-types + a(href="", tg-nav="project-admin-project-values-types:project=project.slug") + span.title Types + span.icon.icon-arrow-right + + li#adminmenu-values-custom-fields + a(href="", tg-nav="project-admin-project-values-custom-fields:project=project.slug") + span.title Custom fields span.icon.icon-arrow-right diff --git a/app/partials/includes/modules/admin-submenu-roles.jade b/app/partials/includes/modules/admin-submenu-roles.jade index e4c2455d..3b5b12e0 100644 --- a/app/partials/includes/modules/admin-submenu-roles.jade +++ b/app/partials/includes/modules/admin-submenu-roles.jade @@ -5,10 +5,11 @@ section.admin-submenu-roles nav ul li(ng-repeat="item in roles") - a(href="" ng-click="ctrl.setRole(item)", ng-class="{active: role.id == item.id}") {{ item.name }} + a(href="" ng-click="ctrl.setRole(item)", ng-class="{active: role.id == item.id}") + span.single-role {{ item.name }} span.icon.icon-arrow-right div(tg-new-role) - a.button.button-gray.add-button(href="", title="Add New Role") + a.button-gray.add-button(href="", title="Add New Role") span.text + New role input(type="text", class="hidden new") diff --git a/app/partials/includes/modules/admin-submenu.jade b/app/partials/includes/modules/admin-submenu.jade index f6b296ca..ecd19c37 100644 --- a/app/partials/includes/modules/admin-submenu.jade +++ b/app/partials/includes/modules/admin-submenu.jade @@ -20,5 +20,5 @@ section.admin-submenu a(href="") Front span.icon.icon-arrow-right - a.button.button-gray(href="", title="Add New role") + a.button-gray(href="", title="Add New role") span.text + New role diff --git a/app/partials/includes/modules/admin/admin-custom-attributes.jade b/app/partials/includes/modules/admin/admin-custom-attributes.jade new file mode 100644 index 00000000..09186600 --- /dev/null +++ b/app/partials/includes/modules/admin/admin-custom-attributes.jade @@ -0,0 +1,53 @@ +section.custom-fields-table.basic-table + div.project-values-title + h2 #{customFieldSectionTitle} + a.button.button-gray.show-add-new.js-add-custom-field-button(href="", title="#{customFieldButtonTitle}") + span Add custom field + + div.table-header + div.row + div.custom-name + span Name + div.custom-description + span Description + div.custom-options + + div.table-body + div.js-sortable + form.js-form(ng-repeat="attr in customAttributes track by attr.id", tg-bind-scope) + div.row.single-custom-field.js-view-custom-field + span.icon.icon-drag-v + div.custom-name + span {{ attr.name }} + div.custom-description + span {{ attr.description }} + div.custom-options + div.custom-options-wrapper + a.js-edit-custom-field-button.icon.icon-edit(href="", title="Edit Custom Field") + a.js-delete-custom-field-button.icon.icon-delete(href="", title="Delete Custom Field") + + div.row.single-custom-field.js-edit-custom-field.hidden + fieldset.custom-name + input(type="text", name="name", placeholder="Set your custom field name", + ng-model="attr.name", data-required="true" data-maxlength="64") + fieldset.custom-description + input(type="text", name="description", placeholder="Set your custom field description", + ng-model="attr.description") + + fieldset.custom-options + div.custom-options-wrapper + a.js-update-custom-field-button.icon.icon-floppy(href="", title="Update Custom Field") + a.js-cancel-edit-custom-field-button.icon.icon-delete(href="", title="Cancel edition") + + form.row.single-custom-field.js-new-custom-field.hidden + fieldset.custom-name + input(type="text", name="name", placeholder="Set your custom field name", + ng-model="newAttr.name", data-required="true", data-maxlength="64") + fieldset.custom-description + input(type="text", name="description", placeholder="Set your custom field description", + ng-model="newAttr.description") + + fieldset.custom-options + div.custom-options-wrapper + a.js-create-custom-field-button.icon.icon-floppy(href="", title="Save Custom Field") + a.js-cancel-new-custom-field-button.icon.icon-delete(href="", title="Cancel creation") diff --git a/app/partials/includes/modules/admin/default-values.jade b/app/partials/includes/modules/admin/default-values.jade index c2d74b2c..6cff329a 100644 --- a/app/partials/includes/modules/admin/default-values.jade +++ b/app/partials/includes/modules/admin/default-values.jade @@ -36,5 +36,5 @@ section.default-values ng-options="s.id as s.name for s in issueStatusList") fieldset - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") + span Save diff --git a/app/partials/includes/modules/admin/project-csv.jade b/app/partials/includes/modules/admin/project-csv.jade new file mode 100644 index 00000000..07254021 --- /dev/null +++ b/app/partials/includes/modules/admin/project-csv.jade @@ -0,0 +1,16 @@ +section.project-csv(ng-controller='#{controller} as ctrl', tg-select-input-text) + div.project-values-title + h2 #{csvType} reports + a.button.button-gray(title="Download #{csvType} CSV", ng-href="{{csvUrl}}", ng-show="csvUrl", target="_blank") + span Download CSV + + div.csv-regenerate-field + .field-with-options + input(type="text", placeholder="Please regenerate CSV url", readonly, ng-model="csvUrl") + .option-wrapper.select-input-content + .icon.icon-copy + a(href="", title="Regenerate CSV url", ng-click="ctrl.regenerateUuid()") + span.icon.icon-plus(ng-hide="csvUrl") + span(ng-hide="csvUrl") Generate URL + span.icon.icon-reload(ng-Show="csvUrl") + span(ng-Show="csvUrl") Regenerate diff --git a/app/partials/includes/modules/admin/project-points.jade b/app/partials/includes/modules/admin/project-points.jade index 02ad65f3..70ecb29c 100644 --- a/app/partials/includes/modules/admin/project-points.jade +++ b/app/partials/includes/modules/admin/project-points.jade @@ -1,4 +1,10 @@ section.project-values-table + + div.project-values-title + h2(ng-bind="sectionName") + a.button.button-gray.show-add-new(href="", title="Add New") + span Add new point + div.project-values-header div.project-values-row div.project-values-name @@ -9,7 +15,7 @@ section.project-values-table div.project-values-body div.sortable - form(ng-repeat="value in values") + form(ng-repeat="value in values", tg-bind-scope) div.project-values-row.row.table-main.visualization span.icon.icon-drag-v diff --git a/app/partials/includes/modules/admin/project-status.jade b/app/partials/includes/modules/admin/project-status.jade index d1c5a99a..4e90d97d 100644 --- a/app/partials/includes/modules/admin/project-status.jade +++ b/app/partials/includes/modules/admin/project-status.jade @@ -1,4 +1,9 @@ -section.colors-table +section.colors-table.admin-status-table + div.project-values-title + h2(ng-bind="sectionName") + a.button.button-gray.show-add-new(href="", title="Add New") + span Add new status + div.table-header div.row div.color-column Color @@ -9,7 +14,7 @@ section.colors-table div.table-main div.sortable - form(ng-repeat="value in values") + form(ng-repeat="value in values", tg-bind-scope) div.row.table-main.visualization span.icon.icon-drag-v div.color-column diff --git a/app/partials/includes/modules/admin/project-types.jade b/app/partials/includes/modules/admin/project-types.jade index 4f504e38..6d02b520 100644 --- a/app/partials/includes/modules/admin/project-types.jade +++ b/app/partials/includes/modules/admin/project-types.jade @@ -1,4 +1,10 @@ section.colors-table + + div.project-values-title + h2 {{ sectionName }} + a.button.button-gray.show-add-new(href="", title="Add New") + span Add new {{ objName }} + div.table-header div.row div.color-column Color @@ -7,7 +13,7 @@ section.colors-table div.table-main div.sortable - form(ng-repeat="value in values") + form(ng-repeat="value in values", tg-bind-scope) div.row.table-main.visualization span.icon.icon-drag-v diff --git a/app/partials/includes/modules/admin/project-us-status.jade b/app/partials/includes/modules/admin/project-us-status.jade index a78f7607..f9f2a8ba 100644 --- a/app/partials/includes/modules/admin/project-us-status.jade +++ b/app/partials/includes/modules/admin/project-us-status.jade @@ -1,89 +1,96 @@ -section.colors-table - div.table-header - div.row - div.color-column Color - div.status-name Name - div.status-slug Slug - div.is-closed-column Is closed? - div.is-archived-column Is archived? - div.status-wip-limit WIP Limit - div.options-column +section.project-us-status - div.table-main - div.sortable - form(ng-repeat="value in values") - div.row.table-main.visualization - span.icon.icon-drag-v + div.project-values-title + h2(ng-bind="sectionName") + a.button.button-gray.show-add-new(href="", title="Add New") + span Add new status - div.color-column - div.current-color(style="background: {{ value.color }}") + div.colors-table + div.table-header + div.row + div.color-column Color + div.status-name Name + div.status-slug Slug + div.is-closed-column Is closed? + div.is-archived-column Is archived? + div.status-wip-limit WIP Limit + div.options-column - div.status-name - span {{ value.name }} + div.table-main + div.sortable + form(ng-repeat="value in values", tg-bind-scope) + div.row.table-main.visualization + span.icon.icon-drag-v - div.status-slug - span {{ value.slug }} + div.color-column + div.current-color(style="background: {{ value.color }}") - div.is-closed-column - div.icon.icon-check-square(ng-show="value.is_closed") + div.status-name + span {{ value.name }} - div.is-archived-column - div.icon.icon-check-square(ng-show="value.is_archived") + div.status-slug + span {{ value.slug }} - div.status-wip-limit - span {{ value.wip_limit }} + div.is-closed-column + div.icon.icon-check-square(ng-show="value.is_closed") - div.options-column - a.edit-value.icon.icon-edit(href="", title="Edit value") - a.delete-value.icon.icon-delete(href="", title="Delete value") + div.is-archived-column + div.icon.icon-check-square(ng-show="value.is_archived") - div.row.table-main.edition.hidden - div.color-column(tg-color-selection, ng-model="value") - div.current-color(style="background: {{ value.color }}") + div.status-wip-limit + span {{ value.wip_limit }} + + div.options-column + a.edit-value.icon.icon-edit(href="", title="Edit value") + a.delete-value.icon.icon-delete(href="", title="Delete value") + + div.row.table-main.edition.hidden + div.color-column(tg-color-selection, ng-model="value") + div.current-color(style="background: {{ value.color }}") + include ../../components/select-color + + div.status-name + input(name="name", type="text", placeholder="Write a name for the new status", + ng-model="value.name", data-required="true", data-maxlength="255") + + div.is-closed-column + select(name="is_closed", ng-model="value.is_closed", data-required="true", + ng-options="e.id as e.name for e in [{'id':true, 'name':'Yes'},{'id':false, 'name': 'No'}]") + + div.is-archived-column + select(name="is_archived", ng-model="value.is_archived", data-required="true", + ng-options="e.id as e.name for e in [{'id':true, 'name':'Yes'},{'id':false, 'name': 'No'}]") + + div.status-wip-limit + input(name="wip_limit", type="number", placeholder="WIP Limit", + ng-model="value.wip_limit", data-type="digits") + + div.options-column + a.save.icon.icon-floppy(href="", title="Save changes") + a.cancel.icon.icon-delete(href="", title="Cancel") + + form + div.row.table-main.new-value.hidden + div.color-column(tg-color-selection, ng-model="newValue") + div.current-color(style="background: {{ newValue.color }}") include ../../components/select-color div.status-name input(name="name", type="text", placeholder="Write a name for the new status", - ng-model="value.name", data-required="true", data-maxlength="255") + ng-model="newValue.name", data-required="true", data-maxlength="255") div.is-closed-column - select(name="is_closed", ng-model="value.is_closed", data-required="true", + select(name="is_closed", ng-model="newValue.is_closed", data-required="true", ng-options="e.id as e.name for e in [{'id':true, 'name':'Yes'},{'id':false, 'name': 'No'}]") div.is-archived-column - select(name="is_archived", ng-model="value.is_archived", data-required="true", + select(name="is_archived", ng-model="newValue.is_archived", data-required="true", ng-options="e.id as e.name for e in [{'id':true, 'name':'Yes'},{'id':false, 'name': 'No'}]") div.status-wip-limit input(name="wip_limit", type="number", placeholder="WIP Limit", - ng-model="value.wip_limit", data-type="digits") + ng-model="newValue.wip_limit", data-type="digits") div.options-column - a.save.icon.icon-floppy(href="", title="Save changes") - a.cancel.icon.icon-delete(href="", title="Cancel") - - form - div.row.table-main.new-value.hidden - div.color-column(tg-color-selection, ng-model="newValue") - div.current-color(style="background: {{ newValue.color }}") - include ../../components/select-color - - div.status-name - input(name="name", type="text", placeholder="Write a name for the new status", - ng-model="newValue.name", data-required="true", data-maxlength="255") - - div.is-closed-column - select(name="is_closed", ng-model="newValue.is_closed", data-required="true", - ng-options="e.id as e.name for e in [{'id':true, 'name':'Yes'},{'id':false, 'name': 'No'}]") - - div.is-archived-column - select(name="is_archived", ng-model="newValue.is_archived", data-required="true", - ng-options="e.id as e.name for e in [{'id':true, 'name':'Yes'},{'id':false, 'name': 'No'}]") - - div.status-wip-limit - input(name="wip_limit", type="number", placeholder="WIP Limit", - ng-model="newValue.wip_limit", data-type="digits") - - div.options-column - a.add-new.icon.icon-floppy(href="", title="Add") - a.delete-new.icon.icon-delete(href="", title="Cancel") + a.add-new.icon.icon-floppy(href="", title="Add") + a.delete-new.icon.icon-delete(href="", title="Cancel") diff --git a/app/partials/includes/modules/backlog-filters.jade b/app/partials/includes/modules/backlog-filters.jade index 5ddd61a1..4008631b 100644 --- a/app/partials/includes/modules/backlog-filters.jade +++ b/app/partials/includes/modules/backlog-filters.jade @@ -5,7 +5,7 @@ section.filters form fieldset - input(type="text", placeholder="Search by subject or reference...", ng-model="filtersQ") + input(type="text", placeholder="Subject or reference", ng-model="filtersQ") a.icon.icon-search(href="", title="search") div.filters-step-cat diff --git a/app/partials/includes/modules/cancel-account-form.jade b/app/partials/includes/modules/cancel-account-form.jade index 5e99a94b..979dc25a 100644 --- a/app/partials/includes/modules/cancel-account-form.jade +++ b/app/partials/includes/modules/cancel-account-form.jade @@ -8,5 +8,4 @@ div.change-email-form-container(tg-cancel-account) input(type="hidden", name="cancel_token", ng-model="data.cancel_token", data-required="true", placeholder="cancel account token") - a.button.button-cancel-account.button-gray(href="", title="Yes, I'm leaving") Yes, I'm leaving! - button(type="submit", class="hidden") + button.button-cancel-account.button-gray(type="submit", title="Yes, I'm leaving!") Yes, I'm leaving! diff --git a/app/partials/includes/modules/change-email-form.jade b/app/partials/includes/modules/change-email-form.jade index 4acd78fb..bd980f8b 100644 --- a/app/partials/includes/modules/change-email-form.jade +++ b/app/partials/includes/modules/change-email-form.jade @@ -8,5 +8,5 @@ div.change-email-form-container(tg-change-email) input(type="hidden", name="email_token", ng-model="data.email_token", data-required="true", placeholder="change email token") - a.button.button-change-email.button-gray(href="", title="Change email") Change email + a.button-change-email.button-gray(href="", title="Change email") Change email button(type="submit", class="hidden") diff --git a/app/partials/includes/modules/change-password-from-recovery-form.jade b/app/partials/includes/modules/change-password-from-recovery-form.jade index 8a2e36a7..22f527df 100644 --- a/app/partials/includes/modules/change-password-from-recovery-form.jade +++ b/app/partials/includes/modules/change-password-from-recovery-form.jade @@ -16,5 +16,4 @@ div.change-password-form-container(tg-change-password-from-recovery) input(type="password", name="password2", id="password2", ng-model="data.password2", data-required="true", data-equalto="#password", placeholder="Re-type new password") fieldset - a.button.button-change-password.button-gray.submit-button(href="", title="Reset Password") Reset Password - button(type="submit", class="hidden") + button.button-change-password.button-gray.submit-button(type="submit", title="Reset Password") Reset Password diff --git a/app/partials/includes/modules/filters.jade b/app/partials/includes/modules/filters.jade index e55aff79..7f3bcde2 100644 --- a/app/partials/includes/modules/filters.jade +++ b/app/partials/includes/modules/filters.jade @@ -8,7 +8,7 @@ section.filters form fieldset - input(type="text", placeholder="Search by subject or reference...", ng-model="filtersQ") + input(type="text", placeholder="Subject or reference", ng-model="filtersQ") a.icon.icon-search(href="", title="search") h2 a.hidden(href="", title="cat-name") diff --git a/app/partials/includes/modules/forgot-form.jade b/app/partials/includes/modules/forgot-form.jade index 074a4051..fe7b595f 100644 --- a/app/partials/includes/modules/forgot-form.jade +++ b/app/partials/includes/modules/forgot-form.jade @@ -10,7 +10,6 @@ div.forgot-form-container(tg-forgot-password) input(type="text", name="username", ng-model="data.username", data-required="true", placeholder="Username or email") fieldset - button(type="submit", class="hidden") - a.button.button-gray.submit-button.button-forgot(href="", title="Reset Password") Reset Password + button.button-gray.submit-button.button-forgot(type="submit", title="Reset Password") Reset Password a(href="", title="Login", tg-nav="login") Nah, take me back. I think I remember it. diff --git a/app/partials/includes/modules/help-notions/lightbox-generic-notion.jade b/app/partials/includes/modules/help-notions/lightbox-generic-notion.jade deleted file mode 100644 index 00ce3a93..00000000 --- a/app/partials/includes/modules/help-notions/lightbox-generic-notion.jade +++ /dev/null @@ -1,12 +0,0 @@ -a.close(href="", title="close") - span.icon.icon-delete - -section - h2.title - block title - - block content - - div.options - a.button.button-green(href="", title="Accept") - span Accept diff --git a/app/partials/includes/modules/help-notions/lightbox-notion-admin-project-values-us-points.jade b/app/partials/includes/modules/help-notions/lightbox-notion-admin-project-values-us-points.jade deleted file mode 100644 index e8e70206..00000000 --- a/app/partials/includes/modules/help-notions/lightbox-notion-admin-project-values-us-points.jade +++ /dev/null @@ -1,12 +0,0 @@ -extends lightbox-generic-notion - -block title - About User Story (US) Points: - -block content - p - | Most people start off with 1 point = 1 man day of effort. If you're new to scrum & agile start there. - p - | After the first week if you and your team expected to accomplish 25 points (i.e. 5 people 5 full days) - | and only got half done, learn from your findings, and adjust expectations for the following week. - | By doing so you'll learn your teams true 'velocity.' diff --git a/app/partials/includes/modules/invitation-login-form.jade b/app/partials/includes/modules/invitation-login-form.jade index d6f35ebd..ebdd4bbc 100644 --- a/app/partials/includes/modules/invitation-login-form.jade +++ b/app/partials/includes/modules/invitation-login-form.jade @@ -8,7 +8,6 @@ form.login-form placeholder="Password") a.forgot-pass(href="", tg-nav="forgot-password", title="Did you forgot your password?") Forgot it? fieldset - a.button.button-login.button-gray.submit-button(href="", title="Log in") Enter - button(type="submit", class="hidden") + button.button-login.button-gray.submit-button(type="submit", title="Enter") Enter - fieldset(tg-github-login-button) + fieldset(ng-repeat="plugin in contribPlugins|filter:{type: 'auth'}", ng-include="plugin.template") diff --git a/app/partials/includes/modules/invitation-register-form.jade b/app/partials/includes/modules/invitation-register-form.jade index bfd4118e..04ef0fbb 100644 --- a/app/partials/includes/modules/invitation-register-form.jade +++ b/app/partials/includes/modules/invitation-register-form.jade @@ -20,7 +20,6 @@ form.register-form placeholder="Set a password") fieldset - button(type="submit", class="hidden") - a.button.button-register.button-gray.submit-button(href="", title="Sign up") Sign up + button.button-register.button-gray.submit-button(type="submit", title="Sign up") Sign up tg-terms-notice diff --git a/app/partials/includes/modules/issues-filters.jade b/app/partials/includes/modules/issues-filters.jade index 81873495..0de01d96 100644 --- a/app/partials/includes/modules/issues-filters.jade +++ b/app/partials/includes/modules/issues-filters.jade @@ -4,7 +4,7 @@ section.filters span.title filters form fieldset - input(type="text", placeholder="Search by subject or reference...", ng-model="filtersQ") + input(type="text", placeholder="Subject or reference", ng-model="filtersQ") a.icon.icon-search(href="", title="search") div.filters-step-cat div.filters-applied diff --git a/app/partials/includes/modules/issues-table.jade b/app/partials/includes/modules/issues-table.jade index 68682860..0c75f311 100644 --- a/app/partials/includes/modules/issues-table.jade +++ b/app/partials/includes/modules/issues-table.jade @@ -12,7 +12,7 @@ section.issues-table.basic-table(ng-class="{empty: !issues.length}") div.level-field(tg-listitem-severity="issue") div.level-field(tg-listitem-priority="issue") div.subject - a(href="", tg-nav="project-issues-detail:project=project.slug,ref=issue.ref", title="{{ ::issue.subject }}") + a(href="", tg-nav="project-issues-detail:project=project.slug,ref=issue.ref", title="#{{ ::issue.ref }} {{ ::issue.subject }}") span(tg-bo-ref="issue.ref") span(ng-bind="issue.subject") diff --git a/app/partials/includes/modules/kanban-table.jade b/app/partials/includes/modules/kanban-table.jade index a3507eac..4152d5f6 100644 --- a/app/partials/includes/modules/kanban-table.jade +++ b/app/partials/includes/modules/kanban-table.jade @@ -32,15 +32,16 @@ div.kanban-table(tg-kanban-squish-column) tg-kanban-archived-status-header="s") div.kanban-table-body - div.kanban-table-inner(tg-kanban-row-width-fixer) + div.kanban-table-inner div.kanban-uses-box.task-column(ng-class='{vfold:folds[s.id]}', ng-repeat="s in usStatusList track by s.id", tg-kanban-sortable, tg-kanban-wip-limit="s.wip_limit", - tg-kanban-column-height-fixer) + tg-kanban-column-height-fixer, + tg-bind-scope) div.kanban-task(ng-repeat="us in usByStatus[s.id] track by us.id", - tg-kanban-userstory, ng-model="us", + tg-kanban-userstory, ng-model="us", tg-bind-scope, ng-class="ctrl.getCardClass(s.id)") div.kanban-column-intro(ng-if="s.is_archived", tg-kanban-archived-status-intro="s") diff --git a/app/partials/includes/modules/lightbox-add-member.jade b/app/partials/includes/modules/lightbox-add-member.jade index 596c40d5..dfb3d4eb 100644 --- a/app/partials/includes/modules/lightbox-add-member.jade +++ b/app/partials/includes/modules/lightbox-add-member.jade @@ -6,9 +6,6 @@ form //- Form is set in a directive .add-member-forms - - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Create") - span Create + button.button-green.submit-button(type="submit", title="Create") Create p.help-text If users are already registered on Taiga, they will be added automatically. Otherwise they will receive an invitation. diff --git a/app/partials/includes/modules/lightbox-ask-choice.jade b/app/partials/includes/modules/lightbox-ask-choice.jade index 4e69be3b..815c1076 100644 --- a/app/partials/includes/modules/lightbox-ask-choice.jade +++ b/app/partials/includes/modules/lightbox-ask-choice.jade @@ -9,7 +9,7 @@ form p.warning div.options - a.button.button-green(href="", title="Accept") + a.button-green(href="", title="Accept") span Accept - a.button.button-red(href="", title="Delete") + a.button-red(href="", title="Delete") span Cancel diff --git a/app/partials/includes/modules/lightbox-attachments.jade b/app/partials/includes/modules/lightbox-attachments.jade index b8d4cd19..8e86d3d5 100644 --- a/app/partials/includes/modules/lightbox-attachments.jade +++ b/app/partials/includes/modules/lightbox-attachments.jade @@ -4,7 +4,7 @@ fieldset.attachments span.icon.icon-attachment span.attachments-num 1 span.attachments-text attachment - a.button.button-gray(href="", title="Add new attachment") + a.button-gray(href="", title="Add new attachment") span + add attachment div.attachment-body - for(var x = 0; x < 1; x++) @@ -14,4 +14,4 @@ fieldset.attachments a(href="", title="Attachment pefildeusuario.png") pefildeusuariopefildeusuariopefildeusuario.png div.attachment-comment span Comentario sobre el contenido - span.attachment-size (125kb.) \ No newline at end of file + span.attachment-size (125kb.) diff --git a/app/partials/includes/modules/lightbox-create-issue.jade b/app/partials/includes/modules/lightbox-create-issue.jade index 54bf1234..cf36de34 100644 --- a/app/partials/includes/modules/lightbox-create-issue.jade +++ b/app/partials/includes/modules/lightbox-create-issue.jade @@ -20,6 +20,4 @@ form textarea.description(placeholder="Description", ng-model="issue.description") // include lightbox-attachments - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") - span Create + button.button-green.submit-button(type="submit", title="Create") Create diff --git a/app/partials/includes/modules/lightbox-delete-project.jade b/app/partials/includes/modules/lightbox-delete-project.jade index b05fb346..843f80f7 100644 --- a/app/partials/includes/modules/lightbox-delete-project.jade +++ b/app/partials/includes/modules/lightbox-delete-project.jade @@ -6,7 +6,7 @@ form span.question Are you sure you want to delete this project? span.subtitle All project data US/Tasks/Issues/Sprints/WikiPages will be lost! :-( div.options - a.button.button-green(href="", title="Yes, I'm really sure") + a.button-green(href="", title="Yes, I'm really sure") span Yes, I'm really sure - a.button.button-red(href="", title="Cancel") + a.button-red(href="", title="Cancel") span Cancel diff --git a/app/partials/includes/modules/lightbox-feedback.jade b/app/partials/includes/modules/lightbox-feedback.jade index 7003e702..1aa22336 100644 --- a/app/partials/includes/modules/lightbox-feedback.jade +++ b/app/partials/includes/modules/lightbox-feedback.jade @@ -6,6 +6,4 @@ form textarea(ng-model="feedback.comment", data-required="true", placeholder="...a bug, some suggestions, something cool... or even your worst nightmare with Taiga") fieldset - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Send feedback") - span Send feedback + button.button-green.submit-button(type="submit", title="Send feedback") Send feedback diff --git a/app/partials/includes/modules/lightbox-generic-ask.jade b/app/partials/includes/modules/lightbox-generic-ask.jade index af346d15..e09ef2ae 100644 --- a/app/partials/includes/modules/lightbox-generic-ask.jade +++ b/app/partials/includes/modules/lightbox-generic-ask.jade @@ -6,7 +6,7 @@ form span.subtitle span.message div.options - a.button.button-green(href="", title="Accept") + a.button-green(href="", title="Accept") span Accept - a.button.button-red(href="", title="Delete") + a.button-red(href="", title="Delete") span Cancel diff --git a/app/partials/includes/modules/lightbox-generic-error.jade b/app/partials/includes/modules/lightbox-generic-error.jade index e8277d96..75fc6022 100644 --- a/app/partials/includes/modules/lightbox-generic-error.jade +++ b/app/partials/includes/modules/lightbox-generic-error.jade @@ -3,5 +3,5 @@ a.close(href="", title="close") section h2.title div.options - a.button.button-green(href="", title="Accept") + a.button-green(href="", title="Accept") span Accept diff --git a/app/partials/includes/modules/lightbox-generic-success.jade b/app/partials/includes/modules/lightbox-generic-success.jade index 02230f5d..1fd9208b 100644 --- a/app/partials/includes/modules/lightbox-generic-success.jade +++ b/app/partials/includes/modules/lightbox-generic-success.jade @@ -4,5 +4,5 @@ section h2.title p.message div.options - a.button.button-green(href="", title="Accept") + a.button-green(href="", title="Accept") span Accept diff --git a/app/partials/includes/modules/lightbox-issue-bulk.jade b/app/partials/includes/modules/lightbox-issue-bulk.jade index 485fc429..a3d70883 100644 --- a/app/partials/includes/modules/lightbox-issue-bulk.jade +++ b/app/partials/includes/modules/lightbox-issue-bulk.jade @@ -5,5 +5,4 @@ form fieldset textarea(cols="200", wrap="off", tg-limit-line-length, tr="placeholder:common.one-item-line", ng-model="new.bulk", data-required="true", data-linewidth="200") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") Save diff --git a/app/partials/includes/modules/lightbox-search.jade b/app/partials/includes/modules/lightbox-search.jade index 592d46ce..09ef479f 100644 --- a/app/partials/includes/modules/lightbox-search.jade +++ b/app/partials/includes/modules/lightbox-search.jade @@ -5,5 +5,4 @@ form fieldset input(type="text", name="text", id="search-text", placeholder="What are you looking for?", data-required="true") fieldset - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Search") Search \ No newline at end of file + button.button-green.submit-button(type="submit", title="Search") Search \ No newline at end of file diff --git a/app/partials/includes/modules/lightbox-sprint-add-edit.jade b/app/partials/includes/modules/lightbox-sprint-add-edit.jade index 9fa21fcb..61ab85ad 100644 --- a/app/partials/includes/modules/lightbox-sprint-add-edit.jade +++ b/app/partials/includes/modules/lightbox-sprint-add-edit.jade @@ -15,9 +15,7 @@ form input.date-end(type="text", name="estimated_finish", placeholder="Estimated End", ng-model="sprint.estimated_finish", data-required="true", tg-date-selector) - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Create") - span Create + button.button-green.submit-button(type="submit", title="Create") Create div(tg-check-permission="delete_milestone") span.delete-sprint.hidden Do you want to delete this sprint? diff --git a/app/partials/includes/modules/lightbox-task-bulk.jade b/app/partials/includes/modules/lightbox-task-bulk.jade index 28b77204..8b1ef334 100644 --- a/app/partials/includes/modules/lightbox-task-bulk.jade +++ b/app/partials/includes/modules/lightbox-task-bulk.jade @@ -5,5 +5,4 @@ form fieldset textarea(cols="200", wrap="off", tg-limit-line-length, tr="placeholder:common.one-item-line", ng-model="form.data", data-required="true") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") Save diff --git a/app/partials/includes/modules/lightbox-task-create-edit.jade b/app/partials/includes/modules/lightbox-task-create-edit.jade index 770f66ac..4541d3a4 100644 --- a/app/partials/includes/modules/lightbox-task-create-edit.jade +++ b/app/partials/includes/modules/lightbox-task-create-edit.jade @@ -24,16 +24,14 @@ form div.settings fieldset.iocaine-flag(title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!") input(type="checkbox", ng-model="task.is_iocaine", name="iocaine-task", id="iocaine-task", ng-value="true") - label.iocaine(for="iocaine-task") + label.iocaine.trans-button(for="iocaine-task") span.icon.icon-iocaine(for="iocaine-task icon-iocaine") span Iocaine fieldset.blocking-flag input(type="checkbox", ng-model="task.is_blocked", name="blocked-task", id="blocked-task", ng-value="true") - label.blocked(for="blocked-task", tr="common.blocked") + label.blocked.trans-button(for="blocked-task", tr="common.blocked") tg-blocking-message-input(watch="task.is_blocked", ng-model="task.blocked_note") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") - span Create + button.button-green.submit-button(type="submit", title="Create") Create diff --git a/app/partials/includes/modules/lightbox-us-bulk.jade b/app/partials/includes/modules/lightbox-us-bulk.jade index 8c92eeae..45fe85e0 100644 --- a/app/partials/includes/modules/lightbox-us-bulk.jade +++ b/app/partials/includes/modules/lightbox-us-bulk.jade @@ -5,5 +5,4 @@ form fieldset textarea(cols="200", wrap="off", tg-limit-line-length, tr="placeholder:common.one-item-line", ng-model="new.bulk", data-required="true", data-linewidth="200") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save \ No newline at end of file + button.button-green.submit-button(type="submit", title="Save") Save \ No newline at end of file diff --git a/app/partials/includes/modules/lightbox-us-create-edit.jade b/app/partials/includes/modules/lightbox-us-create-edit.jade index 817b8f54..bbeb8962 100644 --- a/app/partials/includes/modules/lightbox-us-create-edit.jade +++ b/app/partials/includes/modules/lightbox-us-create-edit.jade @@ -7,7 +7,7 @@ form data-required="true", data-maxlength="500") fieldset.estimation - tg-lb-us-estimation(ng-model="us.points") + tg-lb-us-estimation(ng-model="us") fieldset select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList", @@ -23,18 +23,17 @@ form fieldset.team-requirement input(type="checkbox", name="team_requirement", ng-model="us.team_requirement", id="team-requirement", ng-value="true") - label.requirement(for="team-requirement", tr="us.team-requirement") + label.requirement.trans-button(for="team-requirement", tr="us.team-requirement") fieldset.client-requirement input(type="checkbox", name="client_requirement", ng-model="us.client_requirement", id="client-requirement", ng-value="true") - label.requirement(for="client-requirement", tr="us.client-requirement") + label.requirement.trans-button(for="client-requirement", tr="us.client-requirement") fieldset.blocking-flag input(type="checkbox", name="is_blocked", ng-model="us.is_blocked", id="blocked-us" ng-value="true") - label.blocked(for="blocked-us", tr="common.blocked") + label.blocked.trans-button(for="blocked-us", tr="common.blocked") tg-blocking-message-input(watch="us.is_blocked", ng-model="us.blocked_note") - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Submit") Create + button.button-green.submit-button(type="submit", title="Create") Create diff --git a/app/partials/includes/modules/list-filters-kanban.jade b/app/partials/includes/modules/list-filters-kanban.jade index f97b82e7..75151fb0 100644 --- a/app/partials/includes/modules/list-filters-kanban.jade +++ b/app/partials/includes/modules/list-filters-kanban.jade @@ -6,8 +6,7 @@ section.list-filters | SHOW FILTERS div.new-issue - a.button.button-green(href="") - span.text - | + NEW TASK + a.button-green(href="") + span.text + NEW TASK a.button-bulk(href="") span.icon.icon-bulk diff --git a/app/partials/includes/modules/list-filters.jade b/app/partials/includes/modules/list-filters.jade index f4b4d71e..459540c9 100644 --- a/app/partials/includes/modules/list-filters.jade +++ b/app/partials/includes/modules/list-filters.jade @@ -1,17 +1,6 @@ section.list-filters(tg-check-permission="add_issue") - ul - // li - // a(href="#").active - // span.icon.icon-issues - // | SHOW LIST - - // li - // a(href="#") - // span.icon.icon-graph - // | SHOW GRAPH - div.new-issue - a.button.button-green(href="", ng-click="ctrl.addNewIssue()") + a.button-green(href="", ng-click="ctrl.addNewIssue()") span.text | + NEW ISSUE a.button-bulk(href="", ng-click="ctrl.addIssuesInBulk()") diff --git a/app/partials/includes/modules/login-form.jade b/app/partials/includes/modules/login-form.jade index 9174b672..3517cbde 100644 --- a/app/partials/includes/modules/login-form.jade +++ b/app/partials/includes/modules/login-form.jade @@ -10,9 +10,8 @@ div.login-form-container(tg-login) a.forgot-pass(href="", tg-nav="forgot-password", title="Did you forgot your password?") Forgot it? fieldset - button(type="submit", class="hidden") - a.button.button-login.button-gray.submit-button(href="", title="Sign in") Sign in + button.button-green.submit-button(type="submit", title="Sign in") Sign in - fieldset(tg-github-login-button) + fieldset(ng-repeat="plugin in contribPlugins|filter:{type: 'auth'}", ng-include="plugin.template") tg-public-register-message diff --git a/app/partials/includes/modules/nav.jade b/app/partials/includes/modules/nav.jade deleted file mode 100644 index 738da6e4..00000000 --- a/app/partials/includes/modules/nav.jade +++ /dev/null @@ -1,48 +0,0 @@ -// This menu is deprecated and it is only available -// for correct not integrated pages. -// The compiled version of this is embedded in coffescript -// due to mandatory tricky hacks related to scope resolution. -// Check base.coffee for the JS template -nav.menu - h1.logo - a(href="", title="Home") - img(src="/images/logo.png", alt="Taiga") - ul.main-nav - li(data-name="search") - a(href="", title="Search", tg-nav="project-search:project=project.slug") - span.icon.icon-search - span.item Search - li(data-name="backlog", tg-nav="project-backlog:project=project.slug") - a.active(href="", title="Backlog") - span.icon.icon-backlog - span.item Backlog - li(data-name="kanban") - a(href="", title="Kanban") - span.icon.icon-kanban - span.item Kanban - li(data-name="issues") - a(href="", title="Issues", tg-nav="project-issues:project=project.slug") - span.icon.icon-issues - span.item Issues - li(data-name="wiki") - a(href="", title="Wiki") - span.icon.icon-wiki - span.item Wiki - li(data-name="video") - a(href="", title="Meet Up") - span.icon.icon-video - span.item Meet Up - div.user - div.user-settings - ul.popover - li - a(href="", title="Account settings") Account settings - li - a(href="", title="Logout") Logout - a.avatar(href="", title="User preferences") - img(src="http://thecodeplayer.com/u/uifaces/12.jpg", alt="username") - - div.settings - a(href="", title="User preferences") Pilar - a(href="", title="Site preferences") - span.icon.icon-settings diff --git a/app/partials/includes/modules/register-form.jade b/app/partials/includes/modules/register-form.jade index 6aa4cccd..5341588a 100644 --- a/app/partials/includes/modules/register-form.jade +++ b/app/partials/includes/modules/register-form.jade @@ -21,8 +21,7 @@ div.register-form-container(tg-register) placeholder="Set a password (case sensitive)") fieldset - button(type="submit", class="hidden") - a.button.button-register.button-gray.submit-button(href="", title="Sign up") Sign up + button.button-register.button-gray.submit-button(type="submit", title="Sign up") Sign up fieldset(tg-github-login-button) diff --git a/app/partials/includes/modules/search-result-issues-table.jade b/app/partials/includes/modules/search-result-issues-table.jade deleted file mode 100644 index b9ce175b..00000000 --- a/app/partials/includes/modules/search-result-issues-table.jade +++ /dev/null @@ -1,15 +0,0 @@ -section.search-result-table.basic-table - div.row.title - div.user-stories.width-6 Issue - div.status.width-2 Status - div.points.width-1 Assigned to - - for (var x = 0; x < 50; x++) - div.row.table-main - div.user-stories.width-6 - div.user-story-name - a(href="") Crear el perfil de usuario Senior en el admin - div.status.width-2 Status - div.points.width-1 - figure.avatar - img(src="http://thecodeplayer.com/u/uifaces/12.jpg", alt="username") - figcaption Pilar \ No newline at end of file diff --git a/app/partials/includes/modules/search-result-table.jade b/app/partials/includes/modules/search-result-table.jade index b259828c..c22f92a5 100644 --- a/app/partials/includes/modules/search-result-table.jade +++ b/app/partials/includes/modules/search-result-table.jade @@ -1,7 +1,7 @@ section.search-result-table script(type="text/ng-template", id="search-issues") - div.search-result-table-container(ng-class="{'hidden': !issues.length}") + div.search-result-table-container(ng-class="{'hidden': !issues.length}", tg-bind-scope) div.search-result-table-header div.row.title div.user-stories Issues @@ -23,7 +23,7 @@ script(type="text/ng-template", id="search-issues") script(type="text/ng-template", id="search-userstories") - div.search-result-table-container(ng-class="{'hidden': !userstories.length}") + div.search-result-table-container(ng-class="{'hidden': !userstories.length}", tg-bind-scope) div.search-result-table-header div.row.title div.user-stories User Stories @@ -44,7 +44,7 @@ script(type="text/ng-template", id="search-userstories") span Maybe try one of the tabs above or search again script(type="text/ng-template", id="search-tasks") - div.search-result-table-container(ng-class="{'hidden': !tasks.length}") + div.search-result-table-container(ng-class="{'hidden': !tasks.length}", tg-bind-scope) div.search-result-table-header div.row.title div.user-stories Task @@ -65,7 +65,7 @@ script(type="text/ng-template", id="search-tasks") span Maybe try one of the tabs above or search again script(type="text/ng-template", id="search-wikipages") - div.search-result-table-container(ng-class="{'hidden': !wikipages.length}") + div.search-result-table-container(ng-class="{'hidden': !wikipages.length}", tg-bind-scope) div.search-result-table-header div.row.title div.user-stories Wiki page @@ -80,4 +80,3 @@ script(type="text/ng-template", id="search-wikipages") span.icon.icon-issues span.title It looks like nothing was found with your search criteria. span Maybe try one of the tabs above or search again - diff --git a/app/partials/includes/modules/sprint.jade b/app/partials/includes/modules/sprint.jade index ab3f19f6..80b1e024 100644 --- a/app/partials/includes/modules/sprint.jade +++ b/app/partials/includes/modules/sprint.jade @@ -3,7 +3,7 @@ header(tg-backlog-sprint-header, ng-model="sprint") div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points") div.sprint-table - div.row.milestone-us-item-row(ng-repeat="us in sprint.user_stories track by us.id") + div.row.milestone-us-item-row(ng-repeat="us in sprint.user_stories track by us.id", tg-bind-scope) div.column-us a.us-name.clickable(tg-nav="project-userstories-detail:project=project.slug,ref=us.ref", tg-bo-title="'#' + us.ref + ' ' + us.subject", @@ -12,7 +12,7 @@ div.sprint-table span(tg-bo-bind="us.subject") div.column-points.width-1(tg-bo-bind="us.total_points", ng-class="{closed: us.is_closed, blocked: us.is_blocked}") -a.button.button-gray(tg-bo-title="'Go to Taskboard of ' + sprint.name", +a.button-gray(tg-bo-title="'Go to Taskboard of ' + sprint.name", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", tg-check-permission="view_milestones") diff --git a/app/partials/includes/modules/sprints.jade b/app/partials/includes/modules/sprints.jade index 3e95cf9d..2974915a 100644 --- a/app/partials/includes/modules/sprints.jade +++ b/app/partials/includes/modules/sprints.jade @@ -2,23 +2,18 @@ section.sprints header h1 SPRINTS div.summary - ul - li - span.number(ng-bind="project.total_milestones") -- - span.description
sprints - div.new-sprint - a.button.button-green(href="", title="Add New sprint", - ng-click="ctrl.addNewSprint()", - tg-check-permission="add_milestone") - span.text + New sprint + div.total-sprints + span.number(ng-bind="project.total_milestones") -- + span.description
sprints + a.button-green.add-sprint(href="", title="Add New sprint", ng-click="ctrl.addNewSprint()", tg-check-permission="add_milestone") + span.text + New sprint div.sprint.sprint-open(ng-repeat="sprint in openSprints track by sprint.id", tg-backlog-sprint="sprint", tg-sprint-sortable) include sprint - a.filter-closed-sprints(href="", ng-show="totalClosedMilestones") + a.filter-closed-sprints(tg-backlog-toggle-closed-sprints-visualization, href="", ng-show="totalClosedMilestones") span.icon.icon-archive - span(tg-backlog-toggle-closed-sprints-visualization="ctrl.excludeClosedSprints") Show closed sprints - div.loading-spinner + span.text Show closed sprints div.sprint.sprint-closed(ng-repeat="sprint in closedSprints track by sprint.id" tg-backlog-sprint="sprint", tg-sprint-sortable) include sprint diff --git a/app/partials/includes/modules/taskboard-table.jade b/app/partials/includes/modules/taskboard-table.jade index 9621b44e..17478b62 100644 --- a/app/partials/includes/modules/taskboard-table.jade +++ b/app/partials/includes/modules/taskboard-table.jade @@ -22,9 +22,9 @@ div.taskboard-table(tg-taskboard-squish-column) span(ng-bind="us.total_points") span points include ../components/addnewtask - div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}") + div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope) div.taskboard-task(ng-repeat="task in usTasks[us.id][st.id] track by task.id", - tg-taskboard-task) + tg-taskboard-task, tg-bind-scope) include ../components/taskboard-task div.task-row(ng-init="us = null", ng-class="{'row-fold':usFolded[null]}") @@ -34,7 +34,7 @@ div.taskboard-table(tg-taskboard-squish-column) h3.us-title span Unassigned tasks include ../components/addnewtask.jade - div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}") + div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope) div.taskboard-task(ng-repeat="task in usTasks[null][st.id] track by task.id", - tg-taskboard-task) + tg-taskboard-task, tg-bind-scope) include ../components/taskboard-task diff --git a/app/partials/includes/modules/team/team-table.jade b/app/partials/includes/modules/team/team-table.jade index 9ee2279c..f4e0c739 100644 --- a/app/partials/includes/modules/team/team-table.jade +++ b/app/partials/includes/modules/team/team-table.jade @@ -27,7 +27,7 @@ section.table-team.basic-table div.popover.attribute-explanation span Total Points - div.hero(tg-team-current-user, stats="stats", currentuser="currentUser", projectid="projectId", issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled") + div.hero(tg-team-current-user, stats="stats", currentuser="currentUser", projectid="projectId", issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled", ng-if="::currentUser") h2(ng-show="memberships.length") span Team > diff --git a/app/partials/includes/modules/wizard-create-project.jade b/app/partials/includes/modules/wizard-create-project.jade index 3395d5ac..52075256 100644 --- a/app/partials/includes/modules/wizard-create-project.jade +++ b/app/partials/includes/modules/wizard-create-project.jade @@ -30,7 +30,7 @@ form fieldset.wizard-action div a.button-prev.button.button-gray(href="", title="Prev") Prev - a.submit-button.button.button-green(href="", title="Create") Create + button.button-green.submit-button(type="submit", title="Create") Create button(type="submit", class="hidden") diff --git a/app/partials/issue/issues-detail.jade b/app/partials/issue/issues-detail.jade index 8c90aeb9..ab528bc9 100644 --- a/app/partials/issue/issues-detail.jade +++ b/app/partials/issue/issues-detail.jade @@ -35,6 +35,9 @@ div.wrapper(ng-controller="IssueDetailController as ctrl", section.duty-content(tg-editable-description, ng-model="issue", required-perm="modify_issue") + // Custom Fields + tg-custom-attributes-values(ng-model="issue", type="issue", project="project", required-edition-perm="modify_issue") + tg-attachments(ng-model="issue", type="issue") tg-history(ng-model="issue", type="issue") diff --git a/app/partials/issue/issues-status-display.jade b/app/partials/issue/issues-status-display.jade deleted file mode 100644 index fd283835..00000000 --- a/app/partials/issue/issues-status-display.jade +++ /dev/null @@ -1,9 +0,0 @@ -span - <% if (status.is_closed) { %> - | Closed - <% } else { %> - | Open - <% } %> - -span(class="us-detail-status", style!="color:<%- status.color %>") - | <%- status.name %> diff --git a/app/partials/issue/promote-issue-to-us-button.jade b/app/partials/issue/promote-issue-to-us-button.jade index baf86991..6bbd17c5 100644 --- a/app/partials/issue/promote-issue-to-us-button.jade +++ b/app/partials/issue/promote-issue-to-us-button.jade @@ -1,2 +1,2 @@ a(class="button button-gray editable", tg-check-permission="add_us") - | Promote to User Story + span Promote to User Story diff --git a/app/partials/kanban/kanban-task.jade b/app/partials/kanban/kanban-task.jade index 8431c184..68d2fd19 100644 --- a/app/partials/kanban/kanban-task.jade +++ b/app/partials/kanban/kanban-task.jade @@ -4,7 +4,7 @@ div.kanban-task-inner(ng-class="{'task-archived': us.isArchived}") div.task-text(ng-hide="us.isArchived") a.task-assigned(href="", title="Assign User Story") span.task-num(tg-bo-ref="us.ref") - a.task-name(href="", title="See user story detail", ng-bind="us.subject", + a.task-name(href="", title="#{{ ::us.ref }} {{ us.subject }}", ng-bind="us.subject", tg-nav="project-userstories-detail:project=project.slug,ref=us.ref") p.task-points(href="", title="Total Us points") @@ -20,4 +20,3 @@ div.kanban-task-inner(ng-class="{'task-archived': us.isArchived}") p Drag & drop again to undo a.icon.icon-edit(tg-check-permission="modify_us", href="", title="Edit", ng-hide="us.isArchived") - a.icon.icon-drag-h(tg-check-permission="modify_us", href="", title="Drag&Drop", ng-hide="us.isArchived") diff --git a/app/partials/kanban/kanban.jade b/app/partials/kanban/kanban.jade index 3a010018..606098d0 100644 --- a/app/partials/kanban/kanban.jade +++ b/app/partials/kanban/kanban.jade @@ -1,17 +1,7 @@ div.wrapper(tg-kanban, ng-controller="KanbanController as ctrl" ng-init="section='kanban'") section.main.kanban - header - include ../includes/components/mainTitle - //- div.kanban-settings - //- // a.button.button-trans(href="", title="Filter") - //- // span.icon.icon-filter - //- // span Filters - //- //a.button.button-gray(href="", title="Filter") - //- // span Show Statistics - //-include ../includes/components/large-summary - //-include ../includes/modules/burndown - //-include ../includes/modules/list-filters-kanban + include ../includes/components/mainTitle include ../includes/modules/kanban-table div.lightbox.lightbox-generic-form.lb-create-edit-userstory(tg-lb-create-edit-userstory) diff --git a/app/partials/project/project-colors.jade b/app/partials/project/project-colors.jade index 9867471b..bab64a73 100644 --- a/app/partials/project/project-colors.jade +++ b/app/partials/project/project-colors.jade @@ -10,7 +10,7 @@ div.wrapper include ../includes/components/mainTitle div.project-colors-options - a.button.button-green.new-color(href="") + a.button-green.new-color(href="") span.text | + Add new status diff --git a/app/partials/project/project-menu.jade b/app/partials/project/project-menu.jade index beb4a7f8..ea56ff56 100644 --- a/app/partials/project/project-menu.jade +++ b/app/partials/project/project-menu.jade @@ -44,6 +44,7 @@ div(class="menu-container") span(class="icon icon-settings") span(class="item") Admin <% } %> + <% if (user) { %> div(class="user") div(class="user-settings") ul(class="popover") @@ -60,4 +61,5 @@ div(class="menu-container") li a(href="" title="Logout" class="logout") Logout a(href="" title="User preferences" class="avatar" id="nav-user-settings") - img(src!="<%- user.photo %>" alt!="<%- user.full_name_display %>") + img(src="{{ user.photo }}" alt="{{ user.full_name_display }}") + <% } %> diff --git a/app/partials/project/project-navigation-base.jade b/app/partials/project/project-navigation-base.jade index f9c7cc15..030087b6 100644 --- a/app/partials/project/project-navigation-base.jade +++ b/app/partials/project/project-navigation-base.jade @@ -1,20 +1,20 @@ h1 Your projects form fieldset - input(type="text", placeholder="Search in...", class="search-project") - a(class="icon icon-search") + input.search-project(type="text", placeholder="Search in...") + a.icon.icon-search -div(class="create-project-button-wrapper") - a(class="button button-green create-project-button" href="" title="Create new project"). - Create project +div.create-project-button-wrapper + a.button-green.create-project-button(href="" title="Create new project") + span Create project div(tg-import-project-button) - a(class="button button-blackish import-project-button" href="" title="Import project") - span(class="icon icon-upload") - input(class="import-file hidden" type="file") + a.button-blackish.import-project-button(href="" title="Import project") + span.icon.icon-upload + input.import-file.hidden(type="file") -div(class="projects-pagination" tg-projects-pagination) - a(class="v-pagination-previous icon icon-arrow-up", href="") - div(class="v-pagination-list") - ul(class="projects-list") - a(class="v-pagination-next icon icon-arrow-bottom" href="") +div.projects-pagination(tg-projects-pagination) + a.v-pagination-previous.icon.icon-arrow-up(href="", title="Show previous projects") + div.v-pagination-list + ul.projects-list + a.v-pagination-next.icon.icon-arrow-bottom(href="", title="Show next projects") diff --git a/app/partials/project/project.jade b/app/partials/project/project.jade index 77a571ec..56fb4f81 100644 --- a/app/partials/project/project.jade +++ b/app/partials/project/project.jade @@ -3,19 +3,18 @@ div.wrapper(ng-controller="ProjectController as ctrl") h1 span.green(tg-bo-bind="project.name", class="project-name") div.summary - ul.home-project-info-list - li - span.info-num(tg-bo-bind="stats.total_points") - span.info-text project
points - li - span.info-num(tg-bo-bind="stats.defined_points") - span.info-text defined
points - li - span.info-num(tg-bo-bind="stats.assigned_points") - span.info-text assigned
points - li - span.info-num(tg-bo-bind="stats.closed_points") - span.info-text closed
points + div.summary-stats + span.info-num(tg-bo-bind="stats.total_points") + span.info-text project
points + div.summary-stats + span.info-num(tg-bo-bind="stats.defined_points") + span.info-text defined
points + div.summary-stats + span.info-num(tg-bo-bind="stats.assigned_points") + span.info-text assigned
points + div.summary-stats + span.info-num(tg-bo-bind="stats.closed_points") + span.info-text closed
points div.project-data-container p.description(tg-bo-bind="project.description") ul diff --git a/app/partials/project/projects.jade b/app/partials/project/projects.jade index ed120fba..94a16268 100644 --- a/app/partials/project/projects.jade +++ b/app/partials/project/projects.jade @@ -25,10 +25,10 @@ div.home-projects-list(ng-controller="ProjectsController as ctrl") div(tg-projects-list) .create-project-button-wrapper - a.button.button-green.create-project-button(href="", ng-click="ctrl.newProject()", - title="Create new project") Create project + a.button-green.create-project-button(href="", ng-click="ctrl.newProject()", title="Create new project") + span Create project div(tg-import-project-button) - a.button.button-blackish.import-project-button(href="", title="Import project") + a.button-blackish.import-project-button(href="", title="Import project") span.icon.icon-upload input.import-file.hidden(type="file") diff --git a/app/partials/task/related-task-row.jade b/app/partials/task/related-task-row.jade index 9b708579..d384c9a0 100644 --- a/app/partials/task/related-task-row.jade +++ b/app/partials/task/related-task-row.jade @@ -1,7 +1,7 @@ div(class="tasks") div(class="task-name") span(class="icon icon-iocaine") - a(tg-nav="project-tasks-detail:project=project.slug,ref=task.ref" title!="<%- task.ref %> <%- task.subject %>" class="clickable") + a(tg-nav="project-tasks-detail:project=project.slug,ref=task.ref" title!="#<%- task.ref %> <%- task.subject %>" class="clickable") span #<%- task.ref %>  span <%- task.subject %> div(class="task-settings") diff --git a/app/partials/task/task-detail.jade b/app/partials/task/task-detail.jade index d8ef1366..4d149454 100644 --- a/app/partials/task/task-detail.jade +++ b/app/partials/task/task-detail.jade @@ -4,7 +4,7 @@ div.wrapper(ng-controller="TaskDetailController as ctrl", div.us-detail-header.header-with-actions include ../includes/components/mainTitle .action-buttons - a.button.button-gray( + a.button-gray( tg-check-permission="view_milestones", href="", title="Go to taskboard", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", @@ -40,6 +40,9 @@ div.wrapper(ng-controller="TaskDetailController as ctrl", section.duty-content(tg-editable-description, ng-model="task", required-perm="modify_task") + // Custom Fields + tg-custom-attributes-values(ng-model="task", type="task", project="project", required-edition-perm="modify_task") + tg-attachments(ng-model="task", type="task") tg-history(ng-model="task", type="task") diff --git a/app/partials/us/us-detail.jade b/app/partials/us/us-detail.jade index 6b706ed6..8c8708a1 100644 --- a/app/partials/us/us-detail.jade +++ b/app/partials/us/us-detail.jade @@ -4,7 +4,7 @@ div.wrapper(ng-controller="UserStoryDetailController as ctrl", div.us-detail-header.header-with-actions include ../includes/components/mainTitle .action-buttons - a.button.button-gray( + a.button-gray( tg-check-permission="view_milestones", href="", title="Go to taskboard", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", @@ -39,6 +39,9 @@ div.wrapper(ng-controller="UserStoryDetailController as ctrl", section.duty-content(tg-editable-description, ng-model="us", required-perm="modify_us") + // Custom Fields + tg-custom-attributes-values(ng-model="us", type="userstory", project="project", required-edition-perm="modify_us") + include ../includes/modules/related-tasks tg-attachments(ng-model="us", type="us") diff --git a/app/partials/us/us-task-progress.jade b/app/partials/us/us-task-progress.jade index 804b154c..8ced2ed5 100644 --- a/app/partials/us/us-task-progress.jade +++ b/app/partials/us/us-task-progress.jade @@ -1,3 +1,3 @@ .current-progress(style!='width:<%- progress %>%') - span.tasks-completed - | <%- totalClosedTasks %>/<%- totalTasks %> tasks completed \ No newline at end of file +div.tasks-completed + | <%- totalClosedTasks %>/<%- totalTasks %> tasks completed \ No newline at end of file diff --git a/app/partials/user/cancel-account.jade b/app/partials/user/cancel-account.jade index c333188a..1735da1e 100644 --- a/app/partials/user/cancel-account.jade +++ b/app/partials/user/cancel-account.jade @@ -1,4 +1,4 @@ -div.wrapper +div.wrapper.cancel-account div.login-main div.login-container h1.logo diff --git a/app/partials/user/lightbox/lightbox-delete-account.jade b/app/partials/user/lightbox/lightbox-delete-account.jade index bd7c15af..03b3b528 100644 --- a/app/partials/user/lightbox/lightbox-delete-account.jade +++ b/app/partials/user/lightbox/lightbox-delete-account.jade @@ -6,7 +6,7 @@ form span.question Are you sure you want to delete your Taiga account? span.subtitle We're going to miss you! :-( div.options - a.button.button-green(href="", title="Accept") + a.button-green(href="", title="Accept") span Accept - a.button.button-red(href="", title="Cancel") + a.button-red(href="", title="Cancel") span Cancel diff --git a/app/partials/user/user-change-password.jade b/app/partials/user/user-change-password.jade index 5ffef696..4eb2698d 100644 --- a/app/partials/user/user-change-password.jade +++ b/app/partials/user/user-change-password.jade @@ -19,5 +19,4 @@ div.wrapper(tg-user-change-password, ng-controller="UserChangePasswordController label(for="retype-password") Retype Password input(type="password", placeholder="Retype Password", id="retype-password", ng-model="newPassword2") fieldset - button(type="submit", class="hidden") - a.button.button-green.submit-button(href="", title="Save") Save + button.button-green.submit-button(type="submit", title="Save") Save diff --git a/app/partials/user/user-profile.jade b/app/partials/user/user-profile.jade index 5be8ae8b..6c39fb79 100644 --- a/app/partials/user/user-profile.jade +++ b/app/partials/user/user-profile.jade @@ -14,13 +14,13 @@ div.wrapper(tg-user-profile, ng-controller="UserSettingsController as ctrl", fieldset(tg-user-avatar) .image-container img.avatar(ng-src="{{user.big_photo}}" alt="avatar") - .overlay + .overlay.hidden img.loading-spinner(src="/svg/spinner-circle.svg", alt="loading...") input(type="file", id="avatar-field", class="hidden", tg-avatar-model="avatarAttachment") p The image will be cropped to 80x80px.
span.size-info.hidden(tg-bo-html="maxFileSizeMsg") - a.button.button-green.change(tg-bo-title="'Change photo. ' + maxFileSizeMsg") Change + a.button-green.change.js-change-avatar(tg-bo-title="'Change photo. ' + maxFileSizeMsg") Change a.use-gravatar Use gravatar image div.data @@ -49,8 +49,7 @@ div.wrapper(tg-user-profile, ng-controller="UserSettingsController as ctrl", ng-model="user.bio") fieldset.submit - button(type="submit", title="Save", class="hidden") - a.button.button-green.save-profile.submit-button(href="") Save + button.button-green.submit-button(type="submit", title="Save") Save a.delete-account(href="", title="Delete Taiga account", ng-click="ctrl.openDeleteLightbox()") Delete Taiga account diff --git a/app/partials/wiki/wiki-nav.jade b/app/partials/wiki/wiki-nav.jade index ecafc1ca..759bccaa 100644 --- a/app/partials/wiki/wiki-nav.jade +++ b/app/partials/wiki/wiki-nav.jade @@ -15,5 +15,6 @@ nav li.new.hidden input(type="text" placeholder="name") <% if (addWikiLinkPermission) { %> -a(href="" title="Add link" class="add-button button button-gray") Add link -<% } %> \ No newline at end of file +a(href="" title="Add link" class="add-button button-gray") + span Add link +<% } %> diff --git a/app/partials/wiki/wiki-summary.jade b/app/partials/wiki/wiki-summary.jade index 68b2624d..7fb31db1 100644 --- a/app/partials/wiki/wiki-summary.jade +++ b/app/partials/wiki/wiki-summary.jade @@ -1,13 +1,14 @@ -ul - li - span.number <%- totalEditions %> - span.description times
edited - li - span.number <%- lastModifiedDate %> - span.description last
edit +div.wiki-times-edited + span.number <%- totalEditions %> + span.description times
edited - li.username-edition - figure.avatar - img(src!="<%- user.imgUrl %>" alt!="<%- user.name %>") +div.wiki-last-modified + span.number <%- lastModifiedDate %> + span.description last
edit + +div.wiki-username-edition + figure.avatar + img(src!="<%- user.imgUrl %>" alt!="<%- user.name %>") + div.wiki-user-modification span.description last modification span.username <%- user.name %> diff --git a/app/partials/wiki/wiki.jade b/app/partials/wiki/wiki.jade index 2566e557..aabca782 100644 --- a/app/partials/wiki/wiki.jade +++ b/app/partials/wiki/wiki.jade @@ -14,6 +14,6 @@ div.wrapper(ng-controller="WikiDetailController as ctrl", tg-attachments(ng-model="wiki", type="wiki_page", ng-if="wiki.id") - a.remove(href="", ng-click="ctrl.delete()", ng-if="wiki.id", title="Remove this wiki page") + a.remove(href="", ng-click="ctrl.delete()", ng-if="wiki.id", title="Remove this wiki page", tg-check-permission="delete_wiki_page") span.icon.icon-delete span Remove this wiki page diff --git a/app/plugins/humanshtml/humanshtml.coffee b/app/plugins/humanshtml/humanshtml.coffee index 0cf8312d..59130f8a 100644 --- a/app/plugins/humanshtml/humanshtml.coffee +++ b/app/plugins/humanshtml/humanshtml.coffee @@ -23,7 +23,7 @@ # and add additional template. taiga = @.taiga -module = angular.module("taigaPlugins", ["ngRoute"]) +module = angular.module("taigaPlugins") configure = ($routeProvider) -> $routeProvider.when("/humans.html", {"templateUrl": "/plugins/humanshtml/templates/humans.html"}) diff --git a/app/plugins/terms/terms.coffee b/app/plugins/terms/terms.coffee index 512445ad..ef4fa4d2 100644 --- a/app/plugins/terms/terms.coffee +++ b/app/plugins/terms/terms.coffee @@ -21,7 +21,7 @@ taiga = @.taiga -module = angular.module("taigaPlugins", ["ngRoute"]) +module = angular.module("taigaPlugins") template = _.template("""

diff --git a/app/styles/components/buttons.scss b/app/styles/components/buttons.scss index 67d55b3c..e961ce8f 100755 --- a/app/styles/components/buttons.scss +++ b/app/styles/components/buttons.scss @@ -1,82 +1,99 @@ -// Buttons components -.trans-button { - @extend %large; +.button, +%button { + @extend %medium; @extend %title; + background: transparent; + border: 0; + color: $white; + display: inline-block; + padding: .4rem 2.5rem; text-transform: uppercase; - &:hover, - &.active { - color: $green-taiga; - transition: color .3s linear; + transition: all .3s linear; + vertical-align: middle; + &:hover { + color: $white; + transition: all .3s linear; + } + &.loading { + span { + animation: loading .5s linear; + } + } + span { + color: $white; } .icon { + color: $white; margin-right: .3rem; } } -.button { +.trans-button { + @extend %medium; + @extend %title; @extend %button; + span, + .icon { + color: $blackish; + transition: color .2s linear; + } + &:hover, + &.active { + span, + .icon { + color: $green-taiga; + } + } } -a.button-green { + +.submit-button { + width: 100%; +} + +.button-green { + @extend %button; background: $green-taiga; - color: $white; - vertical-align: middle; &:hover, &.active { background: $fresh-taiga; } - span { - color: $white; - } } .button-gray, a.button-gray { - background: $button-gray; - &:hover { + @extend %button; + background: $gray; + &:hover, + &.active { background: $fresh-taiga; color: $white; } - span { - color: $white; - } } -a.button-blackish { +.button-blackish { + @extend %button; background: $blackish; color: $whitish; &:hover { - background: $button-gray-hover; - color: $white; - } - span { + background: $blackish; color: $white; } } -a.button-red { +.button-red { + @extend %button; background: $red-light; &:hover { background: $red; color: $white; } - span { + .icon { color: $white; } } -a.button-orange { - background: $orange; - &:hover { - background: lighten($orange, 10%); - color: $white; - } - span { - color: $white; - } -} - -a.button-block { +.button-block { background: $white; color: $red; &:hover { @@ -85,35 +102,28 @@ a.button-block { } } -a.button-bulk { +.button-bulk { @extend %button; background: $green-taiga; - font-size: 22px; - margin-left: 2px; - padding: .3rem .5rem; - vertical-align: middle; + padding: .35rem .5rem; .icon { - @extend %medium; - color: $white; margin-right: 0; } &:hover { background: $fresh-taiga; - transition: background .3s linear; } } -.button-github { + +.button-auth { @extend %button; background: $grayer; - vertical-align: middle; - .icon { + .icon, + img { @extend %large; color: $white; margin-right: .5rem; - vertical-align: text-bottom; } &:hover { background: $black; - transition: background .3s linear; } } diff --git a/app/styles/components/help-notion-button.scss b/app/styles/components/help-notion-button.scss deleted file mode 100644 index 528c8f63..00000000 --- a/app/styles/components/help-notion-button.scss +++ /dev/null @@ -1,11 +0,0 @@ -a.help { - color: $gray-light; - position: absolute; - right: 1rem; - top: 1rem; - transition: color .2s linear; - &:hover { - color: $green-taiga; - transition: color .2s linear; - } -} diff --git a/app/styles/components/paginator.scss b/app/styles/components/paginator.scss index 6cfaaf04..4d8f4ad8 100644 --- a/app/styles/components/paginator.scss +++ b/app/styles/components/paginator.scss @@ -1,23 +1,23 @@ .paginator { margin: 2rem 0; ul { + display: flex; margin-left: 1rem; } li { - display: inline-block; + margin-right: .4rem; } a, .active span, .dots { background: $gray-light; color: $white; - margin-right: .1rem; padding: .5rem 1rem; transition: all .3s linear; } a { &:hover { - background: $button-gray-hover; + background: $blackish; color: $white; transition: all .3s linear; } @@ -39,7 +39,7 @@ .v-pagination-next, .v-pagination-previous { - background-color: $button-gray; + background-color: $gray; color: $whitish; display: block; padding: .1rem 0; @@ -47,7 +47,7 @@ visibility: hidden; width: 100%; &:hover { - background-color: $button-gray / 2; + background-color: $blackish; transition: background .3s linear; } } diff --git a/app/styles/components/popover-points.scss b/app/styles/components/popover-points.scss new file mode 100644 index 00000000..63903eee --- /dev/null +++ b/app/styles/components/popover-points.scss @@ -0,0 +1,10 @@ +// This file should contain in future all the common styles for the points popover + +.popover.pop-points-open { + &.horizontal { + li { + white-space: nowrap; + width: 100%; + } + } +} diff --git a/app/styles/components/select-color.scss b/app/styles/components/select-color.scss index d8206ea2..b2216cbb 100644 --- a/app/styles/components/select-color.scss +++ b/app/styles/components/select-color.scss @@ -26,7 +26,7 @@ input { @extend %medium; @extend %text; - background-color: $very-light-gray; + background-color: $whitish; width: 243px; @include placeholder { color: $gray; diff --git a/app/styles/components/summary.scss b/app/styles/components/summary.scss index 7eb54e96..0c50809c 100644 --- a/app/styles/components/summary.scss +++ b/app/styles/components/summary.scss @@ -1,21 +1,17 @@ .summary { - @include clearfix; + align-content: center; background: $grayer; color: $white; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; margin-bottom: 2rem; padding: 1em; - ul { - display: inline-block; - margin: 0; - padding: 0; - } - li { - display: inline-block; - margin-right: 1rem; + .summary-stats { + display: flex; + margin: 0 .5rem; } .data { - float: left; - margin-right: 1em; margin-top: 4px; .number { color: $fresh-taiga; @@ -25,29 +21,24 @@ } .icon { @extend %large; - float: left; } .number { @extend %xlarge; @extend %bold; - float: left; margin-right: .3rem; position: relative; top: 5px; } .description { - // line-height: 0; @extend %small; @extend %text; - float: left; line-height: .9rem; } } .summary-progress-bar { - background: $whitish; - float: left; + background: $white; height: 30px; margin-bottom: 0; margin-right: 10px; @@ -78,15 +69,30 @@ .large-summary { - ul { - border-right: 1px solid $whitish; + justify-content: space-between; + .large-summary-wrapper { + align-content: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + } + .summary-progress-wrapper { + display: flex; + } + .summary-progress-bar { + flex-basis: 200px; + min-width: 200px; + } + .summary-stats { margin-right: 1rem; - vertical-align: top; - &:last-of-type { + &:last-child { border: 0; margin: 0; } } + .summary-stats-divider { + margin-right: 2rem; + } .icon { @extend %xlarge; margin-right: .4rem; diff --git a/app/styles/components/watchers.scss b/app/styles/components/watchers.scss index 0d0f0223..5c456568 100644 --- a/app/styles/components/watchers.scss +++ b/app/styles/components/watchers.scss @@ -25,7 +25,7 @@ border-bottom: 1px solid $gray-light; display: flex; justify-content: center; - padding: .5rem; + padding: .5rem 0 .3rem; vertical-align: middle; &:last-child { border: 0; @@ -37,6 +37,14 @@ } } } + .watcher-avatar { + flex-basis: 3rem; + max-width: 3rem; + padding-left: .3rem; + img { + width: 100%; + } + } .watcher-name { @extend %small; color: $grayer; @@ -44,9 +52,6 @@ margin-left: 1rem; position: relative; } - .watcher-avatar { - flex: 1 0 0; - } .icon-delete { opacity: 0; position: absolute; diff --git a/app/styles/components/wysiwyg.scss b/app/styles/components/wysiwyg.scss index 45906f7c..5e906637 100644 --- a/app/styles/components/wysiwyg.scss +++ b/app/styles/components/wysiwyg.scss @@ -48,13 +48,18 @@ line-height: 1.4rem; margin-bottom: 1rem; } + .codehilite { + overflow: auto; + } pre, code { @extend %small; - background: $whitish; + background: lighten($grayer, 10%); + color: $whitish; direction: ltr; font-family: 'courier new', 'monospace'; margin-bottom: 1rem; + overflow: auto; unicode-bidi: embed; white-space: pre; } diff --git a/app/styles/dependencies/colors.scss b/app/styles/dependencies/colors.scss index e5f37455..aeaffb2d 100755 --- a/app/styles/dependencies/colors.scss +++ b/app/styles/dependencies/colors.scss @@ -6,29 +6,15 @@ $grayer: #444; $gray: #555; $gray-light: #b8b8b8; $whitish: #f5f5f5; -$very-light-gray: #fcfcfc; $white: #fff; $green-taiga: #72a114; $fresh-taiga: #9dce0a; $dark-taiga: #879b89; -$dry-taiga: #70a87d; -$morning-taiga: #7ab987; -$dark-grayish-lime-green: #8b9e8d; - $red-light: #ff8282; $red: #f00; -$rgba-red: 240, 15, 0, .8; - -$orange: #d98a0b; - -$button-green: #699b05; -$button-green-hover: #9dce0a; -$button-gray: #585858; -$button-gray-hover: #879b89; - $postit: #fff8e4; $postit-hover: #f1e8cd; -$postit-dark-hover: #e4d6ad; +$postit-dark-hover: #cfc29b; diff --git a/app/styles/dependencies/helpers.scss b/app/styles/dependencies/helpers.scss index b259563d..9440bf64 100644 --- a/app/styles/dependencies/helpers.scss +++ b/app/styles/dependencies/helpers.scss @@ -61,31 +61,6 @@ } } -.loading-spinner { - @extend %loading-spinner; -} - -%button { - @extend %medium; - @extend %title; - display: inline-block; - padding: 7px 40px 6px; - transition: background .3s linear; - text-transform: uppercase; - &:hover { - transition: background .3s linear; - } - &.loading { - span { - animation: loading .5s linear; - animation: spin 1s linear infinite; - } - } - .icon { - margin-right: .3rem; - } -} - // Background %triangled-bg { background: url('/images/bg.png') no-repeat center center; @@ -105,3 +80,7 @@ max-width: 1rem; transform-origin: 32 32; } + +.loading-spinner { + @extend %loading-spinner; +} diff --git a/app/styles/dependencies/mixins.scss b/app/styles/dependencies/mixins.scss index ad1189b7..19730da8 100644 --- a/app/styles/dependencies/mixins.scss +++ b/app/styles/dependencies/mixins.scss @@ -31,7 +31,7 @@ background: rgba($red, $green, $blue, $opacity); } -@mixin popover($width, $top: '', $left: '', $bottom: '', $right: '', $arrow-width: 0, $arrow-top: '', $arrow-left: '', $arrow-bottom: '') { +@mixin popover($width, $top: '', $left: '', $bottom: '', $right: '', $arrow-width: 0, $arrow-top: '', $arrow-left: '', $arrow-bottom: '', $arrow-height: 15px) { @extend %text; background: $blackish; bottom: #{$bottom}; @@ -67,7 +67,7 @@ background: $blackish; bottom: #{$arrow-bottom}; content: ''; - height: 15px; + height: #{$arrow-height}; left: #{$arrow-left}; position: absolute; top: #{$arrow-top}; diff --git a/app/styles/layout/admin-project-values.scss b/app/styles/layout/admin-project-values.scss index 904f3dd8..749d7178 100644 --- a/app/styles/layout/admin-project-values.scss +++ b/app/styles/layout/admin-project-values.scss @@ -1,3 +1,9 @@ +.admin-attributes { + .admin-attributes-section { + margin-bottom: 2rem; + } +} + .admin-roles { header { position: relative; @@ -12,7 +18,27 @@ } } -.project-values-options { - margin-bottom: 1rem; - text-align: right; +.project-values-title { + align-content: center; + align-items: center; + background: $whitish; + display: flex; + justify-content: space-between; + padding: .8em 1rem; + text-transform: uppercase; + h2 { + margin: 0; + span { + margin-left: .5rem; + text-transform: none; + } + } + a { + display: inline-block; + } } + +//.project-values-options { +// margin-bottom: 1rem; +// text-align: right; +//} diff --git a/app/styles/layout/animation.scss b/app/styles/layout/animation.scss index e47897df..978ec66b 100644 --- a/app/styles/layout/animation.scss +++ b/app/styles/layout/animation.scss @@ -1,5 +1,5 @@ //Loading -@include keyframes(loading) { +@keyframes loading { 0% { filter: blur(5px); opacity: 0; @@ -10,21 +10,14 @@ } } -//Spin -@-webkit-keyframes rotate { +@keyframes rotate { 50% { filter: invert(1); transform: rotate(360deg); } } -@keyframes rotate { - 50% { - transform: rotate(360deg); - } -} - -@include keyframes(formSlide) { +@keyframes formSlide { 0% { filter: blur(5px); opacity: 0; @@ -40,7 +33,7 @@ } //Bar loading -@include keyframes(loadBar) { +@keyframes loadBar { 0% { flex: 1; } diff --git a/app/styles/layout/backlog.scss b/app/styles/layout/backlog.scss index 324d0eb5..65941a74 100644 --- a/app/styles/layout/backlog.scss +++ b/app/styles/layout/backlog.scss @@ -1,13 +1,8 @@ -.backlog { - .new-us { - float: right; - } -} - .backlog-menu { - @include clearfix; background: $whitish; color: $blackish; + display: flex; + justify-content: space-between; margin-bottom: 1rem; padding: .5rem; .trans-button { @@ -21,4 +16,7 @@ vertical-align: middle; } } + .button-bulk { + margin-left: .2rem; + } } diff --git a/app/styles/layout/base.scss b/app/styles/layout/base.scss index d192396f..cae94352 100644 --- a/app/styles/layout/base.scss +++ b/app/styles/layout/base.scss @@ -93,7 +93,7 @@ body { flex: 0 0 auto; min-height: 100vh; min-width: 0; - padding: 2rem 1rem; + padding: 1rem; width: 320px; &.filters-bar { flex: 0 0 auto; @@ -110,10 +110,13 @@ body { } } } + .search-in { + margin-top: .5rem; + } } .menu-tertiary { - background-color: $dark-grayish-lime-green; + background-color: $dark-taiga; flex: 0 0 auto; min-height: 100vh; padding: 2em 1em; @@ -127,8 +130,7 @@ body { .main { flex: 4; min-width: 600px; - padding: 2rem; - padding-bottom: 1rem; + padding: 1rem 2rem; } .icon { diff --git a/app/styles/layout/forms.scss b/app/styles/layout/forms.scss index 3d2b3895..efc8d085 100644 --- a/app/styles/layout/forms.scss +++ b/app/styles/layout/forms.scss @@ -19,7 +19,6 @@ input[type="date"], input[type="password"], select, textarea { - @extend %title; background: $whitish; border: 1px solid $gray-light; color: $grayer; @@ -27,7 +26,11 @@ textarea { padding: 8px; width: 100%; @include placeholder { - color: $gray-light; + color: darken($gray-light, 10%); + } + &.checksley-error { + border: 1px solid $red; + transition: border .3s linear; } } @@ -36,9 +39,12 @@ textarea { resize: vertical; } + .checksley-error-list { @extend %small; + background: rgba($whitish, .8); margin-bottom: 0; + padding: 0 .5rem; position: absolute; right: 2rem; top: 10px; diff --git a/app/styles/layout/issues.scss b/app/styles/layout/issues.scss index d577a4a5..1c122c2f 100644 --- a/app/styles/layout/issues.scss +++ b/app/styles/layout/issues.scss @@ -5,6 +5,6 @@ } .filters-inner { opacity: 1; - padding: 2em 1em; + padding: 1rem; } } diff --git a/app/styles/layout/kanban.scss b/app/styles/layout/kanban.scss index 41e161d9..236ee1a0 100644 --- a/app/styles/layout/kanban.scss +++ b/app/styles/layout/kanban.scss @@ -1,4 +1,11 @@ .kanban { + display: flex; + flex-direction: column; + height: 100vh; + max-height: 100vh; + header { + min-height: 70px; + } .kanban-settings { float: right; } diff --git a/app/styles/layout/taskboard.scss b/app/styles/layout/taskboard.scss index 40f80cf1..71ee04c2 100644 --- a/app/styles/layout/taskboard.scss +++ b/app/styles/layout/taskboard.scss @@ -1,4 +1,12 @@ .taskboard { + display: flex; + flex-direction: column; + max-height: 100vh; + h1, + .graphics-container, + .summary { + flex-shrink: 0; + } .graphics-container { @include slide(300px, hidden); } diff --git a/app/styles/layout/us-detail.scss b/app/styles/layout/us-detail.scss index 67853687..90107884 100644 --- a/app/styles/layout/us-detail.scss +++ b/app/styles/layout/us-detail.scss @@ -100,8 +100,7 @@ margin-top: .5rem; a { border-left: 1px solid $gray-light; - padding: 0 .2rem; - transition: color .3s linear; + padding: 0 .2rem; } a:hover { color: $green-taiga; @@ -226,6 +225,9 @@ top: .4rem; transition: all .2s linear; } + .preview { + padding-top: 1.5rem; + } } } @@ -273,7 +275,8 @@ flex-grow: 1; flex-shrink: 0; margin: .1rem; - padding: .5rem; + max-width: 50%; + padding: .5rem 0 .1rem; position: relative; text-align: center; transition: color .3s linear; @@ -297,13 +300,14 @@ text-align: center; } .role { + @extend %small; @include ellipsis(90%); display: inline-block; - line-height: .8rem; + line-height: 1rem; text-align: center; } .popover { - @include popover(200px, $top: 105%, $left: 35%, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 10px); + @include popover(200px, $top: 105%, $left: 35%, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 10px, $arrow-height: 10px); li { display: inline-block; width: 23%; @@ -318,9 +322,10 @@ } } &.fix { - @include popover(200px, $top: 105%, $left: -160px, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 90%); + @include popover(200px, $top: 105%, $left: -160px, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 90%, $arrow-height: 10px); } } + } .duty-data-container { diff --git a/app/styles/layout/wiki.scss b/app/styles/layout/wiki.scss index 9bb55308..62fb3b23 100644 --- a/app/styles/layout/wiki.scss +++ b/app/styles/layout/wiki.scss @@ -22,12 +22,16 @@ .wiki-content { margin-bottom: 2rem; position: relative; - .view-wiki-content { + &.editable { &:hover { .wysiwyg { background: $whitish; cursor: pointer; } + } + } + .view-wiki-content { + &:hover { .edit { opacity: 1; top: -1.5rem; @@ -67,4 +71,7 @@ top: .4rem; } } + .preview { + padding-top: 1.8rem; + } } diff --git a/app/styles/modules/admin/admin-common.scss b/app/styles/modules/admin/admin-common.scss index dbedf557..0af84f5e 100644 --- a/app/styles/modules/admin/admin-common.scss +++ b/app/styles/modules/admin/admin-common.scss @@ -6,7 +6,7 @@ } .admin-subtitle { color: $gray-light; - margin: 0; + margin-bottom: 2rem; } .total { @extend %large; diff --git a/app/styles/modules/admin/admin-custom-attributes.scss b/app/styles/modules/admin/admin-custom-attributes.scss new file mode 100644 index 00000000..a6ba34fa --- /dev/null +++ b/app/styles/modules/admin/admin-custom-attributes.scss @@ -0,0 +1,103 @@ +.custom-field-options { + margin-bottom: 1rem; + text-align: right; +} + +.custom-fields-table { + margin-bottom: 2em; + .row { + border-bottom: 0; + padding: .5rem 0; + } + .table-header { + @extend %bold; + border-bottom: 3px solid $whitish; + .custom-name span, + .custom-description span { + padding-left: 1.1rem; + } + } + .table-body { + form:last-child { + .row { + border: 0; + } + } + .row:hover { + background: rgba($fresh-taiga, .05); + cursor: move; + transition: background .2s linear; + .icon-drag-v, + .custom-options { + opacity: 1; + transition: opacity .2s linear; + } + } + form { + &.row:hover { + background: none; + cursor: default; + } + } + .custom-description { + color: $gray-light; + } + } + .single-custom-field { + border-bottom: 1px solid $whitish; + color: $gray; + } + .icon-drag-v { + color: $gray-light; + opacity: 0; + padding: 0 .1rem; + transition: color .2s linear; + vertical-align: middle; + &:hover { + color: $gray; + cursor: move; + transition: color .2s linear; + } + } + .custom-name, + .custom-description { + color: $gray; + margin-right: .5rem; + } + .custom-name { + flex-basis: 25%; + flex-shrink: 0; + } + .custom-description { + @include ellipsis(100%); + flex-basis: 90%; + flex-grow: 8; + } + .custom-options { + flex-basis: 100px; + flex-grow: 0; + flex-shrink: 0; + opacity: 0; + text-align: center; + a { + color: $gray-light; + margin-right: .5rem; + transition: color .2s linear; + vertical-align: middle; + &:hover { + color: $green-taiga; + transition: color .2s linear; + } + } + } + .custom-options-wrapper { + opacity: 0; + transition: opacity .3s linear; + } + form { + .custom-options-wrapper { + opacity: 1; + } + } + +} diff --git a/app/styles/modules/admin/admin-membership-table.scss b/app/styles/modules/admin/admin-membership-table.scss index 0b296195..820a3232 100644 --- a/app/styles/modules/admin/admin-membership-table.scss +++ b/app/styles/modules/admin/admin-membership-table.scss @@ -116,7 +116,7 @@ width: 500px; z-index: 999; + div { - background-color: $button-gray; + background-color: $gray; height: 25px; transition: all .2s linear; width: 50%; diff --git a/app/styles/modules/admin/admin-project-export.scss b/app/styles/modules/admin/admin-project-export.scss index b4f31b45..d87a6279 100644 --- a/app/styles/modules/admin/admin-project-export.scss +++ b/app/styles/modules/admin/admin-project-export.scss @@ -1,5 +1,5 @@ .admin-project-export-buttons { - margin-top: 2rem; + margin: 2rem 0 1rem; } .admin-project-export-result { diff --git a/app/styles/modules/admin/admin-project-profile.scss b/app/styles/modules/admin/admin-project-profile.scss index 01049811..cefec654 100644 --- a/app/styles/modules/admin/admin-project-profile.scss +++ b/app/styles/modules/admin/admin-project-profile.scss @@ -21,24 +21,42 @@ .privacy-settings { display: flex; margin-bottom: 2rem; - div { + > div { flex-basis: 0; flex-grow: 1; + overflow: hidden; + position: relative; &:first-child { margin-right: .5rem; } } label { @extend %title; - background: $white; - border: 1px solid $whitish; - cursor: pointer; + border: 1px solid $gray-light; + cursor: not-allowed; display: block; text-align: center; + transition: all .2s linear; + span { + color: $gray-light; + } } - input { - &:checked+label { - background: $fresh-taiga; + } + .privacy-project { + cursor: pointer; + height: 500px; + left: -10px; + opacity: 0; + position: absolute; + top: -10px; + width: 500px; + z-index: 999; + } + .privacy-project:checked { + + label { + background: $fresh-taiga; + border: 1px solid $fresh-taiga; + span { color: $white; } } diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index 12814b17..daa55c21 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -65,7 +65,7 @@ width: 500px; z-index: 999; + div { - background-color: $button-gray; + background-color: $gray; height: 25px; transition: all .2s linear; width: 50%; @@ -101,5 +101,11 @@ opacity: 0; } } + input:disabled { + cursor: auto; + + div { + background-color: #ccc; + } + } } } diff --git a/app/styles/modules/admin/admin-submenu-roles.scss b/app/styles/modules/admin/admin-submenu-roles.scss index b43cc4bb..441df1ab 100644 --- a/app/styles/modules/admin/admin-submenu-roles.scss +++ b/app/styles/modules/admin/admin-submenu-roles.scss @@ -25,6 +25,10 @@ } } } + .single-role { + @include ellipsis(175px); + display: inline-block; + } .icon { color: $white; float: right; @@ -34,7 +38,7 @@ padding: .5rem 0; text-align: center; &:hover { - background-color: darken($button-gray-hover, 15%); + background-color: $blackish; } } } diff --git a/app/styles/modules/admin/admin-submenu.scss b/app/styles/modules/admin/admin-submenu.scss index a8524778..5af5f8e5 100644 --- a/app/styles/modules/admin/admin-submenu.scss +++ b/app/styles/modules/admin/admin-submenu.scss @@ -20,6 +20,7 @@ &:hover { color: $blackish; .icon { + color: $blackish; opacity: 1; transition: opacity .3s linear; } @@ -34,7 +35,7 @@ padding: .5rem 0; text-align: center; &:hover { - background-color: darken($button-gray-hover, 15%); + background-color: $blackish; } } } diff --git a/app/styles/modules/admin/contrib.scss b/app/styles/modules/admin/contrib.scss index bea56e3c..0581bf82 100644 --- a/app/styles/modules/admin/contrib.scss +++ b/app/styles/modules/admin/contrib.scss @@ -4,23 +4,31 @@ max-width: 700px; width: 100%; } - input, - textarea { - @extend %title; - } fieldset { margin-bottom: 1rem; } + .contrib-input { + flex: 1; + } + .contrib-test { + align-items: flex-end; + display: flex; + flex-basis: 7.5rem; + margin-left: .5rem; + text-align: center; + a { + padding: .6rem 2.5rem; + } + } label { @extend %title; display: block; margin-bottom: .2rem; } - textarea { - height: 10rem; + .contrib-form-wrapper { + display: flex; } - .button-green { - color: $white; + .submit-button { display: block; text-align: center; } diff --git a/app/styles/modules/admin/project-csv.scss b/app/styles/modules/admin/project-csv.scss new file mode 100644 index 00000000..e6f16a08 --- /dev/null +++ b/app/styles/modules/admin/project-csv.scss @@ -0,0 +1,43 @@ +.project-csv { + margin-bottom: 2.5rem; + .project-values-title { + margin-bottom: 1rem; + } + .csv-regenerate-field { + align-content: center; + align-items: center; + display: flex; + justify-content: space-between; + margin-bottom: 1rem; + a { + @extend %small; + min-width: 110px; + } + .icon { + margin-right: .3rem; + } + } + .field-with-options { + display: flex; + margin-right: 1rem; + width: 100%; + input { + flex-grow: 1; + } + } + .option-wrapper { + align-items: center; + border: 1px solid $gray-light; + border-left: 0; + border-radius: 0 5px 5px 0; + cursor: pointer; + display: flex; + padding: 0 1rem; + } + .button { + padding: .5rem 1rem; + span { + margin: 0; + } + } +} diff --git a/app/styles/modules/admin/project-values.scss b/app/styles/modules/admin/project-values.scss index 9f96b6ee..e8920a8f 100644 --- a/app/styles/modules/admin/project-values.scss +++ b/app/styles/modules/admin/project-values.scss @@ -21,9 +21,16 @@ .project-values-header { @extend %bold; - border-bottom: 2px solid $gray-light; + border-bottom: 3px solid $whitish; } .project-values-body { + form { + &:last-child { + .project-values-row { + border: 0; + } + } + } .project-values-row { &:hover { background: lighten($green-taiga, 60%); diff --git a/app/styles/modules/auth/cancel-account.scss b/app/styles/modules/auth/cancel-account.scss new file mode 100644 index 00000000..52776464 --- /dev/null +++ b/app/styles/modules/auth/cancel-account.scss @@ -0,0 +1,5 @@ +.cancel-account { + fieldset { + text-align: center; + } +} diff --git a/app/styles/modules/backlog/backlog-table.scss b/app/styles/modules/backlog/backlog-table.scss index d5a1341e..c92e6333 100644 --- a/app/styles/modules/backlog/backlog-table.scss +++ b/app/styles/modules/backlog/backlog-table.scss @@ -77,7 +77,7 @@ padding-right: 3rem; } .pop-points-open { - @include popover(200px, 0, 260px, '', ''); + @include popover(200px, 0, 30px, '', ''); li { display: inline-block; width: 23%; @@ -237,4 +237,8 @@ opacity: 0; padding: .1rem .5rem 0 0; } + .readonly { + cursor: auto; + padding-right: 45px; + } } diff --git a/app/styles/modules/backlog/sprints.scss b/app/styles/modules/backlog/sprints.scss index 05545d50..c6731682 100644 --- a/app/styles/modules/backlog/sprints.scss +++ b/app/styles/modules/backlog/sprints.scss @@ -1,32 +1,33 @@ .sprints { .summary { - @include clearfix; - background: $gray-light; - ul { - width: 40%; - } - li { - color: $grayer; - } + background: darken($whitish, 10%); + display: flex; + justify-content: space-between; } - .new-sprint { - float: right; - .button { - padding: .5rem 1.5rem; - } + .total-sprints { + align-items: flex-start; + color: $grayer; + display: flex; + } + .add-sprint { + margin: 0; + padding: .3rem 1.5rem; } .filter-closed-sprints { @extend %small; - display: block; + align-content: center; + display: flex; + justify-content: center; padding-bottom: 1rem; - text-align: center; - .icon-kanban { + vertical-align: middle; + .icon-archive { margin-right: .3rem; - vertical-align: middle; } } + .loading { + text-align: center; + } .loading-spinner { - @extend %loading-spinner; border: 0; flex-grow: 0; margin-bottom: 1rem; diff --git a/app/styles/modules/backlog/taskboard-table.scss b/app/styles/modules/backlog/taskboard-table.scss index 3a75204f..6863e530 100644 --- a/app/styles/modules/backlog/taskboard-table.scss +++ b/app/styles/modules/backlog/taskboard-table.scss @@ -45,12 +45,15 @@ $column-margin: 0 10px 0 0; } .taskboard-table { + display: flex; + flex-direction: column; overflow: hidden; width: 100%; } .taskboard-table-header { margin-bottom: .5rem; + min-height: 40px; position: relative; width: 100%; .taskboard-table-inner { @@ -107,8 +110,7 @@ $column-margin: 0 10px 0 0; .taskboard-table-body { height: 700px; - overflow-x: scroll; - overflow-y: scroll; + overflow: auto; width: 100%; .task-column { flex-basis: $column-width; diff --git a/app/styles/modules/common/assigned-to.scss b/app/styles/modules/common/assigned-to.scss index f68e5126..ee87db88 100644 --- a/app/styles/modules/common/assigned-to.scss +++ b/app/styles/modules/common/assigned-to.scss @@ -26,7 +26,6 @@ .assigned-to { flex-grow: 3; margin-left: 1rem; - margin-top: 15px; .assigned-title { @extend %small; color: $gray-light; diff --git a/app/styles/modules/common/attachments.scss b/app/styles/modules/common/attachments.scss index f5cfac80..374170ae 100644 --- a/app/styles/modules/common/attachments.scss +++ b/app/styles/modules/common/attachments.scss @@ -84,7 +84,7 @@ } } .editable-attachment-comment { - @extend %medium; + @extend %small; } .attachment-settings { flex-basis: 15%; diff --git a/app/styles/modules/common/category-config.scss b/app/styles/modules/common/category-config.scss index 63d4b125..7a9346d3 100644 --- a/app/styles/modules/common/category-config.scss +++ b/app/styles/modules/common/category-config.scss @@ -50,7 +50,7 @@ } .category-item { align-items: center; - border-bottom: 1px solid $very-light-gray; + border-bottom: 1px solid $whitish; display: flex; justify-content: space-between; padding: .5rem .5rem .5rem 2rem; diff --git a/app/styles/modules/common/colors-table.scss b/app/styles/modules/common/colors-table.scss index 630c3c0d..f84a0d6e 100644 --- a/app/styles/modules/common/colors-table.scss +++ b/app/styles/modules/common/colors-table.scss @@ -2,7 +2,7 @@ .table-header { @extend %medium; @extend %bold; - border-bottom: 2px solid $gray-light; + border-bottom: 3px solid $whitish; &:hover { background: transparent; } @@ -10,17 +10,40 @@ padding-left: 50px; } } + form { + &:last-child { + .row { + border: 0; + } + } + } .row { align-items: center; + border-bottom: 1px solid $whitish; display: flex; justify-content: center; padding: 1rem; &:hover { + background: lighten($green-taiga, 60%); + cursor: move; + transition: background .2s ease-in; + .icon { + opacity: 1; + transition: opacity .2s ease-in; + } .options-column { opacity: 1; transition: opacity .3s linear; } } + &:last-child { + border: 0; + } + &.edition { + .current-color { + cursor: pointer; + } + } &.edition, &.new-value { padding-left: 50px; @@ -28,11 +51,6 @@ &.hidden { display: none; } - &.edition { - .current-color { - cursor: pointer; - } - } .color-column { flex-basis: 60px; flex-grow: 1; @@ -86,16 +104,7 @@ opacity: 1; } } - .table-main { - border-bottom: 1px solid $gray-light; - .row { - &:hover { - background: lighten($green-taiga, 60%); - cursor: move; - transition: background .2s ease-in; - } - } - } + .current-color { background-color: $gray-light; border-radius: 2px; @@ -106,8 +115,10 @@ @extend %large; color: $gray-light; margin-right: 1rem; + opacity: 0; &:hover { color: $green-taiga; + transition: all .2s ease-in; } } .icon-delete { diff --git a/app/styles/modules/common/custom-fields.scss b/app/styles/modules/common/custom-fields.scss new file mode 100644 index 00000000..9500491a --- /dev/null +++ b/app/styles/modules/common/custom-fields.scss @@ -0,0 +1,79 @@ +.duty-custom-fields { + margin-bottom: 2rem; + .custom-fields-header { + @extend %bold; + align-content: space-between; + align-items: center; + background: $whitish; + display: flex; + justify-content: space-between; + padding: .5rem 1rem; + .icon-arrow-bottom { + @extend %large; + cursor: pointer; + transform: rotate(-90deg); + transition: transform .2s linear; + &.open { + transform: rotate(0); + transition: transform .2s linear; + } + } + } + .custom-fields-body { + @include slide(1000px, hidden, $min: 0); + } + .custom-field-single { + border-bottom: 1px solid $whitish; + display: flex; + padding: 1rem; + &:hover { + .custom-field-options { + opacity: 1; + } + } + &.editable { + .custom-field-options { + margin-top: .5rem; + } + } + .custom-field-options { + opacity: 0; + transition: opacity .2s linear; + a { + color: $gray-light; + } + a:hover { + color: $green-taiga; + } + } + } + .custom-field-data { + flex: 0; + flex-basis: 200px; + .custom-field-name { + @extend %bold; + display: block; + } + .custom-field-description { + @extend %small; + color: $gray-light; + display: block; + line-height: .9rem; + } + } + .custom-field-options { + margin: 0; + } + .custom-field-value { + flex: 1; + padding: 0 1rem 0 2rem; + } + form { + label { + cursor: pointer; + } + input { + width: 100%; + } + } +} diff --git a/app/styles/modules/common/history.scss b/app/styles/modules/common/history.scss index 1cfc957d..688bcb9e 100644 --- a/app/styles/modules/common/history.scss +++ b/app/styles/modules/common/history.scss @@ -35,11 +35,13 @@ } .activity-fromto { @extend %small; + word-wrap: break-word; } } .history-tabs { @extend %title; border-bottom: 3px solid $gray-light; + margin-bottom: 0; padding: .5rem 0; li { @extend %large; @@ -213,10 +215,10 @@ margin-bottom: .5rem; } .activity-user { - flex-basis: 50px; - flex-grow: 1; + flex-basis: 60px; + flex-shrink: 0; + margin-right: 1rem; img { - max-width: 70px; width: 100%; } } @@ -224,10 +226,9 @@ color: $green-taiga; margin-bottom: .5rem; } - .activity-content { - flex-basis: 150px; - flex-grow: 20; + flex-shrink: 0; + width: calc(100% - 80px); } .changes { background: $whitish; diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index 44f3ee38..bdfdd09c 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -2,27 +2,6 @@ @extend %lightbox; } -.markdown-preview { - display: inline-block; - margin-bottom: .5rem; - a { - @extend %button; - @extend %small; - color: $gray-light; - padding: 3px 20px; - &:first-child { - border-right: 1px solid $gray-light; - } - &:hover { - color: $grayer; - transition: color .2s linear; - } - } - .active { - color: $grayer; - } -} - .lightbox-generic-form { form { flex-basis: 600px; @@ -41,7 +20,6 @@ } label { - @extend %button; border: 1px solid $gray-light; color: $grayer; cursor: pointer; @@ -117,11 +95,6 @@ display: none; } } - .points-per-role { - .popover { - @include popover(200px, $top: 105%, $left: 35%, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 10px); - } - } } .lightbox-generic-bulk { @@ -212,6 +185,7 @@ max-width: 600px; } .last-sprint-name { + @extend %small; color: $gray; opacity: 1; position: absolute; @@ -273,9 +247,9 @@ .options { display: flex; a { - flex-grow: 1; padding: 8px 0; text-align: center; + width: 100%; &:first-child { margin-right: .5rem; } diff --git a/app/styles/modules/common/projects-nav.scss b/app/styles/modules/common/projects-nav.scss index 4949a0d3..d922ff23 100644 --- a/app/styles/modules/common/projects-nav.scss +++ b/app/styles/modules/common/projects-nav.scss @@ -41,6 +41,7 @@ display: flex; flex-direction: column; margin-top: 1rem; + min-height: 1px; //firefox bug #2057 } .create-project-button-wrapper { display: flex; diff --git a/app/styles/modules/common/related-tasks.scss b/app/styles/modules/common/related-tasks.scss index d6617bac..aab7266b 100644 --- a/app/styles/modules/common/related-tasks.scss +++ b/app/styles/modules/common/related-tasks.scss @@ -49,15 +49,17 @@ border: 0; } .tasks { - flex-basis: 78%; - flex-grow: 10; + overflow: hidden; + width: 100%; } .status { - flex-basis: 10%; + flex-shrink: 0; + width: 100px; } .assigned-to { cursor: pointer; - flex-basis: 10%; + flex-shrink: 0; + width: 150px; } } .related-task-create-form { diff --git a/app/styles/modules/filters/list-filters.scss b/app/styles/modules/filters/list-filters.scss index 609cb8bd..56e0a7e9 100644 --- a/app/styles/modules/filters/list-filters.scss +++ b/app/styles/modules/filters/list-filters.scss @@ -2,32 +2,10 @@ align-items: center; background-color: $whitish; display: flex; - justify-content: space-between; + justify-content: flex-end; margin-bottom: 2rem; padding: .5rem 1rem; - ul { - display: flex; - margin-bottom: 0; - } - li { - margin-right: 2rem; - a { - @extend %large; - @extend %title; - opacity: .4; - &:hover { - color: $blackish; - opacity: 1; - transition: opacity .3s linear; - } - } - .active { - color: $blackish; - opacity: 1; - transition: opacity .3s linear; - } - .icon { - padding-right: .5rem; - } + .button-bulk { + margin-left: .2rem; } } diff --git a/app/styles/modules/home-project.scss b/app/styles/modules/home-project.scss index a106546f..b093b945 100644 --- a/app/styles/modules/home-project.scss +++ b/app/styles/modules/home-project.scss @@ -1,10 +1,6 @@ -.home-project-info-list { +.summary-stats { + align-items: flex-start; display: flex; - li { - flex-basis: 0; - flex-grow: 1; - margin-right: 1rem; - } .info-num { @extend %xlarge; @extend %bold; @@ -22,11 +18,10 @@ .project-data-container { display: flex; - p { - flex-grow: 3; - } + justify-content: space-between; ul { - flex-grow: 1; + flex-grow: 0; + max-width: 33%; } li { display: inline-block; diff --git a/app/styles/modules/kanban/kanban-table.scss b/app/styles/modules/kanban/kanban-table.scss index d542a418..16620a05 100644 --- a/app/styles/modules/kanban/kanban-table.scss +++ b/app/styles/modules/kanban/kanban-table.scss @@ -7,6 +7,9 @@ $column-shrink: 0; $column-margin: 0 10px 0 0; .kanban-table { + display: flex; + flex-direction: column; + height: 100%; overflow: hidden; width: 100%; .vfold { @@ -42,11 +45,15 @@ $column-margin: 0 10px 0 0; .kanban-task { display: none; } + .kanban-column-intro { + display: none; + } } } .kanban-table-header { margin-bottom: .5rem; + min-height: 40px; position: relative; width: 100%; .kanban-table-inner { @@ -91,6 +98,7 @@ $column-margin: 0 10px 0 0; .kanban-table-body { @extend %medium; display: flex; + height: 100%; overflow: hidden; overflow-x: auto; width: 100%; diff --git a/app/styles/modules/team/team-table.scss b/app/styles/modules/team/team-table.scss index 50a9c6b1..5660a6ca 100644 --- a/app/styles/modules/team/team-table.scss +++ b/app/styles/modules/team/team-table.scss @@ -61,7 +61,7 @@ .hero { width: 100%; .row { - background: $very-light-gray; + background: $whitish; border-bottom: 0; margin: 1rem 0; } diff --git a/app/styles/modules/user-settings/user-profile.scss b/app/styles/modules/user-settings/user-profile.scss index 62f39807..2deea46a 100644 --- a/app/styles/modules/user-settings/user-profile.scss +++ b/app/styles/modules/user-settings/user-profile.scss @@ -21,7 +21,7 @@ align-items: center; background: rgba($blackish, .8); bottom: 0; - display: none; + display: flex; justify-content: center; left: 0; position: absolute; diff --git a/app/styles/modules/wiki/wiki-nav.scss b/app/styles/modules/wiki/wiki-nav.scss index 08b7a76a..d80724ab 100644 --- a/app/styles/modules/wiki/wiki-nav.scss +++ b/app/styles/modules/wiki/wiki-nav.scss @@ -24,20 +24,22 @@ transition: opacity 1s linear; } input { + @extend %text; + @extend %medium; background: $grayer; color: $whitish; @include placeholder { color: $gray-light; } } - &.loading { + .loading { margin: 0; padding: 8px; text-align: center; width: 100%; } } - .button { + .add-button { color: $white; display: block; margin-bottom: .5rem; diff --git a/app/styles/modules/wiki/wiki-summary.scss b/app/styles/modules/wiki/wiki-summary.scss index 7d83451f..0328f1eb 100644 --- a/app/styles/modules/wiki/wiki-summary.scss +++ b/app/styles/modules/wiki/wiki-summary.scss @@ -1,31 +1,28 @@ .wiki-summary { - @include clearfix(); - figure { - float: left; - margin-right: .5rem; - vertical-align: sub; - width: 32px; - } - ul { - align-items: flex-start; + align-items: center; + flex-wrap: wrap; + justify-content: flex-start; + div { display: flex; - flex-direction: row; + justify-content: space-between; + margin-right: 1rem; + } + .number { + line-height: 2rem; + top: 0; + } + .wiki-user-modification { + display: flex; + flex-direction: column; justify-content: flex-start; } - .username-edition { - min-width: 240px; - span { - display: block; - float: none; - } - .username { - @extend %large; - color: $fresh-taiga; - white-space: nowrap; - } + figure { + margin-right: .3rem; + width: 32px; } - .button { - color: $white; - float: right; + .username { + @extend %large; + color: $fresh-taiga; + white-space: nowrap; } } diff --git a/app/styles/shame/shame.scss b/app/styles/shame/shame.scss index 436b0a6c..43772411 100644 --- a/app/styles/shame/shame.scss +++ b/app/styles/shame/shame.scss @@ -1,17 +1,5 @@ // Shame SCSS decalrations to be refactorized -////TASKBOARD-TABLE.SCSS && TASKBOARD-TABLE.JADE//// - -//Taskboard table header brokes when added position relative and positionabsolute to its child -// No clearfix or known hack fixes it -// Could be because of flexbox? -// height of the table has been set manually, wich is ugly. -.taskboard-table-header, -.kanban-table-header { - height: 40px; -} - - _:-ms-fullscreen, :root .taskboard-table-body { .task-row { diff --git a/app/styles/vendor/codehilite.github.scss b/app/styles/vendor/codehilite.github.css similarity index 100% rename from app/styles/vendor/codehilite.github.scss rename to app/styles/vendor/codehilite.github.css diff --git a/conf/conf.example.json b/conf/conf.example.json index bd6340a9..f378107e 100644 --- a/conf/conf.example.json +++ b/conf/conf.example.json @@ -7,6 +7,6 @@ "privacyPolicyUrl": null, "termsOfServiceUrl": null, "maxUploadFileSize": null, - "gitHubClientId": null, - "contribPlugins": [] + "contribPlugins": [], + "debugInfo": false } diff --git a/gulpfile.coffee b/gulpfile.coffee deleted file mode 100644 index 34be192d..00000000 --- a/gulpfile.coffee +++ /dev/null @@ -1,357 +0,0 @@ -gulp = require("gulp") -jade = require("gulp-jade") -gutil = require("gulp-util") -coffee = require("gulp-coffee") -concat = require("gulp-concat") -uglify = require("gulp-uglify") -plumber = require("gulp-plumber") -wrap = require("gulp-wrap") -rename = require("gulp-rename") -flatten = require('gulp-flatten') -gulpif = require('gulp-if') -replace = require("gulp-replace") - -minifyHTML = require("gulp-minify-html") -sass = require("gulp-ruby-sass") -csslint = require("gulp-csslint") -minifyCSS = require("gulp-minify-css") -watch = require("gulp-watch") -notify = require("gulp-notify") -scsslint = require("gulp-scss-lint") -newer = require("gulp-newer") -cache = require("gulp-cached") -jadeInheritance = require('gulp-jade-inheritance') -sourcemaps = require('gulp-sourcemaps') -insert = require("gulp-insert") -runSequence = require('run-sequence') -lazypipe = require('lazypipe') -del = require('del') -imagemin = require('gulp-imagemin') -autoprefixer = require('gulp-autoprefixer') -templateCache = require('gulp-angular-templatecache') - -mainSass = require("./main-sass").files - -paths = {} -paths.app = "app/" -paths.dist = "dist/" -paths.tmp = "tmp/" -paths.tmpStyles = paths.tmp + "styles/" -paths.tmpStylesExtras = "#{paths.tmpStyles}/taiga-front-extras/**/*.css" -paths.extras = "extras/" - -paths.jade = "#{paths.app}/**/*.jade" - -paths.htmlPartials = [ - "#{paths.tmp}/partials/**/*.html", - "!#{paths.tmp}/partials/{includes,includes/**}" -] - -paths.images = paths.app + "images/**/*" -paths.svg = paths.app + "svg/**/*" -paths.css = paths.app + "styles/vendor/*.css" -paths.locales = paths.app + "locales/**/*.json" -paths.sass = [ - "#{paths.app}/styles/**/*.scss" - "#{paths.app}/plugins/**/*.scss" - "!#{paths.app}/styles/bourbon/**/*.scss" - "!#{paths.app}/styles/dependencies/**/*.scss" - "!#{paths.app}/styles/extras/**/*.scss" -] - -paths.coffee = [ - paths.app + "coffee/app.coffee", - paths.app + "coffee/*.coffee", - paths.app + "coffee/modules/controllerMixins.coffee", - paths.app + "coffee/modules/*.coffee", - paths.app + "coffee/modules/common/*.coffee", - paths.app + "coffee/modules/backlog/*.coffee", - paths.app + "coffee/modules/taskboard/*.coffee", - paths.app + "coffee/modules/kanban/*.coffee", - paths.app + "coffee/modules/issues/*.coffee", - paths.app + "coffee/modules/userstories/*.coffee", - paths.app + "coffee/modules/tasks/*.coffee", - paths.app + "coffee/modules/team/*.coffee", - paths.app + "coffee/modules/wiki/*.coffee", - paths.app + "coffee/modules/admin/*.coffee", - paths.app + "coffee/modules/projects/*.coffee", - paths.app + "coffee/modules/locales/*.coffee", - paths.app + "coffee/modules/base/*.coffee", - paths.app + "coffee/modules/resources/*.coffee", - paths.app + "coffee/modules/user-settings/*.coffee" - paths.app + "coffee/modules/integrations/*.coffee" - paths.app + "plugins/**/*.coffee" -] - -paths.js = [ - paths.app + "vendor/jquery/dist/jquery.js", - paths.app + "vendor/lodash/dist/lodash.js", - paths.app + "vendor/emoticons/lib/emoticons.js", - paths.app + "vendor/underscore.string/lib/underscore.string.js", - paths.app + "vendor/angular/angular.js", - paths.app + "vendor/angular-route/angular-route.js", - paths.app + "vendor/angular-sanitize/angular-sanitize.js", - paths.app + "vendor/angular-animate/angular-animate.js", - paths.app + "vendor/i18next/i18next.js", - paths.app + "vendor/moment/min/moment-with-langs.js", - paths.app + "vendor/checksley/checksley.js", - paths.app + "vendor/pikaday/pikaday.js", - paths.app + "vendor/jquery-flot/jquery.flot.js", - paths.app + "vendor/jquery-flot/jquery.flot.pie.js", - paths.app + "vendor/jquery-flot/jquery.flot.time.js", - paths.app + "vendor/flot-axislabels/jquery.flot.axislabels.js", - paths.app + "vendor/flot.tooltip/js/jquery.flot.tooltip.js", - paths.app + "vendor/jquery-textcomplete/jquery.textcomplete.js", - paths.app + "vendor/markitup-1x/markitup/jquery.markitup.js", - paths.app + "vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js", - paths.app + "vendor/raven-js/dist/raven.js", - paths.app + "vendor/l.js/l.js", - paths.app + "js/jquery.ui.git-custom.js", - paths.app + "js/jquery-ui.drag-multiple-custom.js", - paths.app + "js/sha1-custom.js", - paths.app + "plugins/**/*.js" -] - -isDeploy = process.argv[process.argv.length - 1] == 'deploy' - -############################################################################ -# Layout/CSS Related tasks -############################################################################## - -gulp.task "jade", -> - gulp.src(paths.jade) - .pipe(plumber()) - .pipe(cache("jade")) - .pipe(jade({pretty: true, locals:{v:(new Date()).getTime()}})) - .pipe(gulp.dest(paths.tmp)) - -gulp.task "jade-inheritance", -> - gulp.src(paths.jade) - .pipe(plumber()) - .pipe(cache("jade")) - .pipe(jadeInheritance({basedir: "./app/"})) - .pipe(jade({pretty: true, locals:{v:(new Date()).getTime()}})) - .pipe(gulp.dest(paths.tmp)) - -gulp.task "copy-index", -> - gulp.src(paths.tmp + "index.html") - .pipe(gulp.dest(paths.dist)) - -gulp.task "template-cache", -> - gulp.src(paths.htmlPartials) - .pipe(templateCache({standalone: true})) - .pipe(gulp.dest(paths.dist + "js/")) - -gulp.task "jade-deploy", (cb) -> - runSequence("jade", "copy-index", "template-cache", cb) - -gulp.task "jade-watch", (cb) -> - runSequence("jade-inheritance", "copy-index", "template-cache", cb) - -############################################################################## -# CSS Related tasks -############################################################################## - -gulp.task "sass-lint", -> - gulp.src(paths.sass.concat("!#{paths.app}/styles/shame/**/*.scss")) - .pipe(cache("sasslint")) - .pipe(gulpif(!isDeploy, scsslint({config: "scsslint.yml"}))) - -gulp.task "sass-compile", ["sass-lint"], -> - gulp.src(paths.sass) - .pipe(plumber()) - .pipe(cache("scss")) - .pipe(insert.prepend('@import "dependencies";')) - .pipe(sass({ - 'sourcemap=none': true, - loadPath: [ - "#{paths.app}styles/extras/" - ] - })) - .pipe(gulp.dest(paths.tmpStyles)) - -csslintChannel = lazypipe() - .pipe(csslint, "csslintrc.json") - .pipe(csslint.reporter) - -gulp.task "css-lint-app", -> - gulp.src(mainSass.concat([paths.tmpStylesExtras])) - .pipe(cache("csslint")) - .pipe(gulpif(!isDeploy, csslintChannel())) - -gulp.task "css-join", ["css-lint-app"], -> - gulp.src(mainSass.concat([paths.tmpStylesExtras])) - .pipe(concat("app.css")) - .pipe(autoprefixer({ - cascade: false - })) - .pipe(gulp.dest(paths.tmp)) - -gulp.task "css-app", (cb) -> - runSequence("sass-compile", "css-join", cb) - -gulp.task "css-vendor", -> - gulp.src(paths.css) - .pipe(concat("vendor.css")) - .pipe(gulp.dest(paths.tmp)) - - -gulp.task "styles", ["css-app", "css-vendor"], -> - _paths = [ - paths.tmp + "vendor.css", - paths.tmp + "app.css" - ] - - gulp.src(_paths) - .pipe(concat("main.css")) - .pipe(gulpif(isDeploy, minifyCSS({noAdvanced: true}))) - .pipe(gulp.dest(paths.dist + "styles/")) - -############################################################################## -# JS Related tasks -############################################################################## - -gulp.task "conf", -> - gulp.src(["conf/conf.example.json"]) - .pipe(gulp.dest(paths.dist + "js/")) - -gulp.task "app-loader", -> - gulp.src("app-loader/app-loader.coffee") - .pipe(replace("___VERSION___", (new Date()).getTime())) - .pipe(coffee()) - .pipe(gulp.dest(paths.dist + "js/")) - -gulp.task "locales", -> - gulp.src("app/locales/en/app.json") - .pipe(wrap("angular.module('taigaBase').value('localesEn', <%= contents %>);")) - .pipe(rename("locales.en.js")) - .pipe(gulp.dest(paths.tmp)) - -gulp.task "coffee", -> - gulp.src(paths.coffee) - .pipe(plumber()) - .pipe(coffee()) - .pipe(concat("app.js")) - .pipe(gulp.dest(paths.tmp)) - -gulp.task "jslibs-watch", -> - gulp.src(paths.js) - .pipe(plumber()) - .pipe(concat("libs.js")) - .pipe(gulp.dest(paths.dist + "js/")) - -gulp.task "jslibs-deploy", -> - gulp.src(paths.js) - .pipe(plumber()) - .pipe(sourcemaps.init()) - .pipe(concat("libs.js")) - .pipe(uglify({mangle:false, preserveComments: false})) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(paths.dist + "js/")) - -gulp.task "app-watch", ["coffee", "conf", "locales", "app-loader"], -> - _paths = [ - paths.tmp + "app.js", - paths.tmp + "locales.en.js" - ] - - gulp.src(_paths) - .pipe(concat("app.js")) - .pipe(gulp.dest(paths.dist + "js/")) - -gulp.task "app-deploy", ["coffee", "conf", "locales", "app-loader"], -> - _paths = [ - paths.tmp + "app.js", - paths.tmp + "locales.en.js" - ] - - gulp.src(_paths) - .pipe(sourcemaps.init()) - .pipe(concat("app.js")) - .pipe(uglify({mangle:false, preserveComments: false})) - .pipe(sourcemaps.write('./')) - .pipe(gulp.dest(paths.dist + "js/")) - -############################################################################## -# Common tasks -############################################################################## - -# SVG -gulp.task "copy-svg", -> - gulp.src("#{paths.app}/svg/**/*") - .pipe(gulp.dest("#{paths.dist}/svg/")) - -gulp.task "copy-fonts", -> - gulp.src("#{paths.app}/fonts/*") - .pipe(gulp.dest("#{paths.dist}/fonts/")) - -gulp.task "copy-images", -> - gulp.src("#{paths.app}/images/**/*") - .pipe(imagemin({progressive: true})) - .pipe(gulp.dest("#{paths.dist}/images/")) - - gulp.src("#{paths.app}/plugins/**/images/*") - .pipe(flatten()) - .pipe(gulp.dest("#{paths.dist}/images/")) - -gulp.task "copy-plugin-templates", -> - gulp.src("#{paths.app}/plugins/**/templates/**/*.html") - .pipe(gulp.dest("#{paths.dist}/plugins/")) - -gulp.task "copy-extras", -> - gulp.src("#{paths.extras}/*") - .pipe(gulp.dest("#{paths.dist}/")) - - -gulp.task "copy", ["copy-fonts", "copy-images", "copy-plugin-templates", "copy-svg", "copy-extras"] - -gulp.task "express", -> - express = require("express") - app = express() - - app.use("/js", express.static("#{__dirname}/dist/js")) - app.use("/styles", express.static("#{__dirname}/dist/styles")) - app.use("/images", express.static("#{__dirname}/dist/images")) - app.use("/svg", express.static("#{__dirname}/dist/svg")) - app.use("/partials", express.static("#{__dirname}/dist/partials")) - app.use("/fonts", express.static("#{__dirname}/dist/fonts")) - app.use("/plugins", express.static("#{__dirname}/dist/plugins")) - - app.all "/*", (req, res, next) -> - # Just send the index.html for other files to support HTML5Mode - res.sendFile("index.html", {root: "#{__dirname}/dist/"}) - - app.listen(9001) - -# Rerun the task when a file changes -gulp.task "watch", -> - gulp.watch(paths.jade, ["jade-watch"]) - gulp.watch(paths.sass, ["styles"]) - gulp.watch(paths.svg, ["copy-svg"]) - gulp.watch(paths.coffee, ["app-watch"]) - gulp.watch(paths.js, ["jslibs-watch"]) - gulp.watch(paths.locales, ["app-watch"]) - gulp.watch(paths.images, ["copy-images"]) - gulp.watch(paths.fonts, ["copy-fonts"]) - -# Remove the tmp directory -del.sync(paths.tmp) - -gulp.task "deploy", [ - "copy", - "jade-deploy", - "app-deploy", - "jslibs-deploy", - "styles" -] - -# The default task (called when you run gulp from cli) -gulp.task "default", [ - "copy", - "styles", - "app-watch", - "jslibs-watch", - "jade-deploy", - "express", - "watch" -] diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..dbef97e0 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,414 @@ +var gulp = require("gulp"), + imagemin = require("gulp-imagemin"), + jade = require("gulp-jade"), + coffee = require("gulp-coffee"), + concat = require("gulp-concat"), + uglify = require("gulp-uglify"), + plumber = require("gulp-plumber"), + wrap = require("gulp-wrap"), + rename = require("gulp-rename"), + flatten = require("gulp-flatten"), + gulpif = require("gulp-if"), + replace = require("gulp-replace"), + sass = require("gulp-sass"), + csslint = require("gulp-csslint"), + minifyCSS = require("gulp-minify-css"), + scsslint = require("gulp-scss-lint"), + cache = require("gulp-cache"), + cached = require("gulp-cached"), + jadeInheritance = require("gulp-jade-inheritance"), + sourcemaps = require("gulp-sourcemaps"), + insert = require("gulp-insert"), + autoprefixer = require("gulp-autoprefixer"), + templateCache = require("gulp-angular-templatecache"), + runSequence = require("run-sequence"), + del = require("del"); + +var mainSass = require("./main-sass").files; + +var paths = {}; +paths.app = "app/"; +paths.dist = "dist/"; +paths.tmp = "tmp/"; +paths.extras = "extras/"; + +paths.jade = [ + paths.app + "**/*.jade" +]; + +paths.htmlPartials = [ + paths.tmp + "partials/**/*.html", + paths.tmp + "plugins/**/*.html", + "!" + paths.tmp + "partials/includes/**/*.html" +]; + +paths.images = paths.app + "images/**/*"; +paths.svg = paths.app + "svg/**/*"; +paths.css = paths.app + "styles/vendor/*.css"; +paths.locales = paths.app + "locales/**/*.json"; + +paths.sass = [ + paths.app + "**/*.scss", + "!" + paths.app + "/styles/bourbon/**/*.scss", + "!" + paths.app + "/styles/dependencies/**/*.scss", + "!" + paths.app + "/styles/extras/**/*.scss" +]; + +paths.coffee = paths.app + "**/*.coffee"; + +paths.js = [ + paths.tmp + "coffee/app.js", + paths.tmp + "coffee/*.js", + paths.tmp + "coffee/modules/controllerMixins.js", + paths.tmp + "coffee/modules/*.js", + paths.tmp + "coffee/modules/common/*.js", + paths.tmp + "coffee/modules/backlog/*.js", + paths.tmp + "coffee/modules/taskboard/*.js", + paths.tmp + "coffee/modules/kanban/*.js", + paths.tmp + "coffee/modules/issues/*.js", + paths.tmp + "coffee/modules/userstories/*.js", + paths.tmp + "coffee/modules/tasks/*.js", + paths.tmp + "coffee/modules/team/*.js", + paths.tmp + "coffee/modules/wiki/*.js", + paths.tmp + "coffee/modules/admin/*.js", + paths.tmp + "coffee/modules/projects/*.js", + paths.tmp + "coffee/modules/locales/*.js", + paths.tmp + "coffee/modules/base/*.js", + paths.tmp + "coffee/modules/resources/*.js", + paths.tmp + "coffee/modules/user-settings/*.js", + paths.tmp + "coffee/modules/integrations/*.js", + paths.tmp + "plugins/**/*.js" +]; + +paths.libs = [ + paths.app + "vendor/jquery/dist/jquery.js", + paths.app + "vendor/lodash/dist/lodash.js", + paths.app + "vendor/emoticons/lib/emoticons.js", + paths.app + "vendor/underscore.string/lib/underscore.string.js", + paths.app + "vendor/angular/angular.js", + paths.app + "vendor/angular-route/angular-route.js", + paths.app + "vendor/angular-sanitize/angular-sanitize.js", + paths.app + "vendor/angular-animate/angular-animate.js", + paths.app + "vendor/i18next/i18next.js", + paths.app + "vendor/moment/min/moment-with-langs.js", + paths.app + "vendor/checksley/checksley.js", + paths.app + "vendor/pikaday/pikaday.js", + paths.app + "vendor/jquery-flot/jquery.flot.js", + paths.app + "vendor/jquery-flot/jquery.flot.pie.js", + paths.app + "vendor/jquery-flot/jquery.flot.time.js", + paths.app + "vendor/flot-axislabels/jquery.flot.axislabels.js", + paths.app + "vendor/flot.tooltip/js/jquery.flot.tooltip.js", + paths.app + "vendor/jquery-textcomplete/jquery.textcomplete.js", + paths.app + "vendor/markitup-1x/markitup/jquery.markitup.js", + paths.app + "vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js", + paths.app + "vendor/raven-js/dist/raven.js", + paths.app + "vendor/l.js/l.js", + paths.app + "js/jquery.ui.git-custom.js", + paths.app + "js/jquery-ui.drag-multiple-custom.js", + paths.app + "js/sha1-custom.js" +]; + +var isDeploy = process.argv[process.argv.length - 1] == "deploy"; + +/* +############################################################################ +# Layout/CSS Related tasks +############################################################################## +*/ + +var jadeIncludes = paths.app +'partials/includes/**/*'; + +gulp.task("jade", function() { + return gulp.src(paths.jade) + .pipe(plumber()) + .pipe(cached("jade")) + .pipe(jade({pretty: true, locals:{v:(new Date()).getTime()}})) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("jade-inheritance", function() { + return gulp.src(paths.jade) + .pipe(plumber()) + .pipe(cached("jade")) + .pipe(jadeInheritance({basedir: "./app/"})) + .pipe(jade({pretty: true, locals:{v:(new Date()).getTime()}})) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("copy-index", function() { + return gulp.src(paths.tmp + "index.html") + .pipe(gulp.dest(paths.dist)); +}); + +gulp.task("template-cache", function() { + return gulp.src(paths.htmlPartials) + .pipe(templateCache({standalone: true})) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +gulp.task("jade-deploy", function(cb) { + return runSequence("jade", "copy-index", "template-cache", cb); +}); + +gulp.task("jade-watch", function(cb) { + return runSequence("jade-inheritance", "copy-index", "template-cache", cb); +}); + +/* +############################################################################## +# CSS Related tasks +############################################################################## +*/ + +gulp.task("scss-lint", [], function() { + var ignore = [ + "!" + paths.app + "/styles/shame/**/*.scss", + "!" + paths.app + "/styles/components/markitup.scss" + ]; + + var fail = process.argv.indexOf("--fail") !== -1; + + return gulp.src(paths.sass.concat(ignore)) + .pipe(gulpif(!isDeploy, cache(scsslint({endless: true, sync: true, config: "scsslint.yml"}), { + success: function(scsslintFile) { + return scsslintFile.scsslint.success; + }, + value: function(scsslintFile) { + return { + scsslint: scsslintFile.scsslint + }; + } + }))) + .pipe(gulpif(fail, scsslint.failReporter())) +}); + +gulp.task("sass-compile", ["scss-lint"], function() { + return gulp.src(paths.sass) + .pipe(plumber()) + .pipe(insert.prepend('@import "dependencies";')) + .pipe(cache(sass({ + includePaths: [ + paths.app + "styles/extras/" + ] + }))) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("css-lint-app", function() { + return gulp.src(mainSass.concat([paths.tmp + "plugins/**/*.css"])) + .pipe(gulpif(!isDeploy, cache(csslint("csslintrc.json"), { + success: function(csslintFile) { + return csslintFile.csslint.success; + }, + value: function(csslintFile) { + return { + csslint: csslintFile.csslint + }; + } + }))) + .pipe(csslint.reporter()); +}); + +gulp.task("css-join", ["css-lint-app"], function() { + return gulp.src(mainSass.concat([paths.tmp + "plugins/**/*.css"])) + .pipe(concat("app.css")) + .pipe(autoprefixer({ + cascade: false + })) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("css-app", function(cb) { + return runSequence("sass-compile", "css-join", cb); +}); + +gulp.task("css-vendor", function() { + return gulp.src(paths.css) + .pipe(concat("vendor.css")) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("styles", ["css-app", "css-vendor"], function() { + var _paths = [ + paths.tmp + "vendor.css", + paths.tmp + "app.css" + ]; + + return gulp.src(_paths) + .pipe(concat("main.css")) + .pipe(gulpif(isDeploy, minifyCSS({noAdvanced: true}))) + .pipe(gulp.dest(paths.dist + "styles/")) +}); + +/* +############################################################################## +# JS Related tasks +############################################################################## +*/ +gulp.task("conf", function() { + return gulp.src(["conf/conf.example.json"]) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +gulp.task("app-loader", function() { + return gulp.src("app-loader/app-loader.coffee") + .pipe(replace("___VERSION___", (new Date()).getTime())) + .pipe(coffee()) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +gulp.task("locales", function() { + return gulp.src("app/locales/en/app.json") + .pipe(wrap("angular.module('taigaBase').value('localesEn', <%= contents %>);", {}, {parse: false})) + .pipe(rename("locales.en.js")) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("coffee", function() { + return gulp.src(paths.coffee) + .pipe(cache(coffee())) + .on("error", function(err) { + console.log(err.toString()); + this.emit("end"); + }) + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("plugins-js", function() { + return gulp.src(paths.app + "plugins/**/*.js") + .pipe(gulp.dest(paths.tmp)); +}); + +gulp.task("jslibs-watch", function() { + return gulp.src(paths.libs) + .pipe(plumber()) + .pipe(concat("libs.js")) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +gulp.task("jslibs-deploy", function() { + return gulp.src(paths.libs) + .pipe(plumber()) + .pipe(sourcemaps.init()) + .pipe(concat("libs.js")) + .pipe(uglify({mangle:false, preserveComments: false})) + .pipe(sourcemaps.write("./")) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +gulp.task("app-watch", ["coffee", "plugins-js", "conf", "locales", "app-loader"], function() { + var _paths = paths.js.concat(paths.tmp + "locales.en.js") + + return gulp.src(_paths) + .pipe(concat("app.js")) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +gulp.task("app-deploy", ["coffee", "plugins-js", "conf", "locales", "app-loader"], function() { + var _paths = paths.js.concat(paths.tmp + "locales.en.js") + + return gulp.src(_paths) + .pipe(sourcemaps.init()) + .pipe(concat("app.js")) + .pipe(uglify({mangle:false, preserveComments: false})) + .pipe(sourcemaps.write("./")) + .pipe(gulp.dest(paths.dist + "js/")); +}); + +/* +############################################################################## +# Common tasks +############################################################################## +*/ +gulp.task("clear", function(done) { + del.sync(paths.tmp); + + return cache.clearAll(done); +}); + +//SVG +gulp.task("copy-svg", function() { + return gulp.src(paths.app + "/svg/**/*") + .pipe(gulp.dest(paths.dist + "/svg/")); +}); + +gulp.task("copy-fonts", function() { + return gulp.src(paths.app + "/fonts/*") + .pipe(gulp.dest(paths.dist + "/fonts/")); +}); + +gulp.task("copy-images", function() { + return gulp.src(paths.app + "/images/**/*") + .pipe(imagemin({progressive: true})) + .pipe(gulp.dest(paths.dist + "/images/")); +}); + +gulp.task("copy-images-plugins", function() { + return gulp.src(paths.app + "/plugins/**/images/*") + .pipe(flatten()) + .pipe(gulp.dest(paths.dist + "/images/")); +}); + +gulp.task("copy-plugin-templates", function() { + return gulp.src(paths.app + "/plugins/**/templates/**/*.html") + .pipe(gulp.dest(paths.dist + "/plugins/")); +}); + +gulp.task("copy-extras", function() { + return gulp.src(paths.extras + "/*") + .pipe(gulp.dest(paths.dist + "/")); +}); + +gulp.task("copy", ["copy-fonts", "copy-images", "copy-images-plugins", "copy-plugin-templates", "copy-svg", "copy-extras"]); + +gulp.task("express", function() { + var express = require("express"); + var app = express(); + + app.use("/js", express.static(__dirname + "/dist/js")); + app.use("/styles", express.static(__dirname + "/dist/styles")); + app.use("/images", express.static(__dirname + "/dist/images")); + app.use("/svg", express.static(__dirname + "/dist/svg")); + app.use("/partials", express.static(__dirname + "/dist/partials")); + app.use("/fonts", express.static(__dirname + "/dist/fonts")); + app.use("/plugins", express.static(__dirname + "/dist/plugins")); + + app.all("/*", function(req, res, next) { + //Just send the index.html for other files to support HTML5Mode + res.sendFile("index.html", {root: __dirname + "/dist/"}); + }); + + app.listen(9001); +}); + +//Rerun the task when a file changes +gulp.task("watch", function() { + gulp.watch(paths.jade, ["jade-watch"]); + gulp.watch(paths.sass, ["styles"]); + gulp.watch(paths.svg, ["copy-svg"]); + gulp.watch(paths.coffee, ["app-watch"]); + gulp.watch(paths.libs, ["jslibs-watch"]); + gulp.watch(paths.locales, ["app-watch"]); + gulp.watch(paths.images, ["copy-images"]); + gulp.watch(paths.fonts, ["copy-fonts"]); +}); + +gulp.task("deploy", function(cb) { + runSequence("clear", [ + "copy", + "jade-deploy", + "app-deploy", + "jslibs-deploy", + "styles" + ], cb); +}); +//The default task (called when you run gulp from cli) +gulp.task("default", [ + "copy", + "styles", + "app-watch", + "jslibs-watch", + "jade-deploy", + "express", + "watch" +]); diff --git a/main-sass.js b/main-sass.js index 1f266882..5ebdb49f 100644 --- a/main-sass.js +++ b/main-sass.js @@ -52,10 +52,10 @@ exports.files = function () { 'components/select-color', 'components/loader', 'components/loading-bar', - 'components/help-notion-button', 'components/beta', 'components/markitup', 'components/markdown-help', + 'components/popover-points', //################################################# @@ -74,6 +74,7 @@ exports.files = function () { 'modules/common/history', 'modules/common/wizard', 'modules/common/external-reference', + 'modules/common/custom-fields', //Project modules 'modules/home-projects-list', @@ -107,6 +108,7 @@ exports.files = function () { 'modules/auth/register-form', 'modules/auth/forgot-form', 'modules/auth/change-password-from-recovery', + 'modules/auth/cancel-account', //Wiki modules 'modules/wiki/wiki-nav', @@ -123,10 +125,12 @@ exports.files = function () { 'modules/admin/admin-membership-table', 'modules/admin/admin-project-profile', 'modules/admin/default-values', + 'modules/admin/admin-custom-attributes', 'modules/admin/project-values', 'modules/admin/third-parties', 'modules/admin/admin-third-parties-webhooks', 'modules/admin/contrib', + 'modules/admin/project-csv', //Modules user Settings 'modules/user-settings/user-profile', diff --git a/package.json b/package.json index 275d5241..0b5bd476 100644 --- a/package.json +++ b/package.json @@ -16,46 +16,44 @@ "url": "https://github.com/taigaio/taiga-front/blob/master/LICENSE" } ], + "scripts": { + "scss-lint": "gulp scss-lint --fail" + }, "devDependencies": { - "coffee-script": "^1.8.0", + "coffee-script": "^1.9.1", "del": "^1.1.1", - "express": "^4.9.5", - "gulp": "^3.8.10", + "express": "^4.12.0", + "gulp": "^3.8.11", "gulp-angular-templatecache": "^1.5.0", - "gulp-autoprefixer": "^2.0.0", - "gulp-cached": "0.0.3", - "gulp-changed": "^0.4.0", - "gulp-clean": "^0.2.4", - "gulp-coffee": "^2.2.0", + "gulp-autoprefixer": "^2.1.0", + "gulp-cache": "^0.2.8", + "gulp-cached": "1.0.2", + "gulp-coffee": "^2.3.1", "gulp-coffeelint": "~0.4.0", - "gulp-concat": "^2.1.7", + "gulp-concat": "^2.5.2", "gulp-csslint": "^0.1.5", "gulp-flatten": "0.0.4", "gulp-if": "^1.2.5", - "gulp-imagemin": "^2.0.0", + "gulp-imagemin": "^2.2.1", "gulp-insert": "^0.4.0", - "gulp-intermediate": "^3.0.1", - "gulp-jade": "^0.5.0", - "gulp-jade-inheritance": "0.0.4", - "gulp-minify-css": "^0.3.1", - "gulp-minify-html": "^0.1.3", - "gulp-newer": "^0.3.0", - "gulp-notify": "^1.2.5", - "gulp-plumber": "^0.6.2", + "gulp-jade": "^1.0.0", + "gulp-jade-inheritance": "0.5.0", + "gulp-minify-css": "^0.4.6", + "gulp-plumber": "^0.6.6", "gulp-rename": "^1.2.0", - "gulp-ruby-sass": "^0.7.1", - "gulp-scss-lint": "0.1.4", - "gulp-sourcemaps": "^1.2.4", - "gulp-styledocco": "0.0.1", - "gulp-template": "^0.1.1", - "gulp-uglify": "~0.2.0", - "gulp-util": "~2.2.14", - "gulp-watch": "^0.5.4", - "gulp-wrap": "^0.3.0", - "lazypipe": "^0.2.2", - "readable-stream": "~1.0.31", - "run-sequence": "^1.0.1", - "through2": "~0.6.1", - "gulp-replace": "^0.5.2" - } + "gulp-replace": "^0.5.3", + "gulp-sass": "^1.3.3", + "gulp-scss-lint": "0.1.9", + "gulp-sourcemaps": "^1.5.0", + "gulp-template": "^3.0.0", + "gulp-uglify": "~1.1.0", + "gulp-wrap": "^0.11.0", + "pre-commit": "^1.0.5", + "readable-stream": "~1.0.33", + "run-sequence": "^1.0.2", + "through2": "~0.6.3" + }, + "pre-commit": [ + "scss-lint" + ] } diff --git a/scsslint.yml b/scsslint.yml index 77f808be..3aacec8f 100644 --- a/scsslint.yml +++ b/scsslint.yml @@ -77,6 +77,14 @@ linters: enabled: true extra_properties: [] + QualifyingElement: + enabled: true + allow_element_with_attribute: true + exclude: + - 'app/styles/components/buttons.scss' + - 'app/styles/layout/forms.scss' + - 'app/styles/components/markdown-help.scss' + SelectorDepth: enabled: true max_depth: 4 @@ -130,4 +138,3 @@ linters: Compass::*: enabled: false -