Merge branch 'master' into stable
commit
0aa85b46e7
|
@ -12,5 +12,5 @@ app/coffee/modules/locales/locale*.coffee
|
|||
tags
|
||||
tmp/
|
||||
app/config/main.coffee
|
||||
app/plugins/taiga-front-extras
|
||||
scss-lint.log
|
||||
e2e/screenshots/
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
sudo: false
|
||||
|
||||
before_install:
|
||||
- export CHROME_BIN=chromium-browser
|
||||
- export DISPLAY=:99.0
|
||||
|
@ -11,4 +13,4 @@ before_script:
|
|||
- gulp deploy
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
- "4.1"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = sr@latin:sr-latn, zh-Hans:zh-hans, zh-Hant:zh-hant
|
||||
lang_map = sr@latin:sr-latn, zh-Hans:zh-hans, zh-Hant:zh-hant, pt_BR:pt-br
|
||||
|
||||
[taiga-front.locale-enjson]
|
||||
file_filter = app/locales/locale-<lang>.json
|
||||
|
|
|
@ -27,3 +27,5 @@ answer newbie questions, and generally made Taiga that much better:
|
|||
- Florian Bezagu
|
||||
- Ryan Swanstrom
|
||||
- Chris Wilson <chris.wilson@aridhia.com>
|
||||
- Brett Profitt <brett.profitt@gmail.com>
|
||||
- Vlad Topala <topalavlad@gmail.com>
|
||||
|
|
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -1,6 +1,30 @@
|
|||
# Changelog #
|
||||
|
||||
|
||||
## 1.9.0 Abies Siberica (2015-11-XX)
|
||||
|
||||
### Features
|
||||
- Ability to create single-line or multi-line custom fields. (thanks to [@artlepool](https://github.com/artlepool)).
|
||||
- Ability to date custom fields. (thanks to [@artlepool](https://github.com/artlepool)).
|
||||
- Add custom videoconference system.
|
||||
- Make burndown chart collapsible at the backlog panel.
|
||||
- Ability to choose a theme (thanks to [@astagi](https://github.com/astagi)).
|
||||
- Inline viewing of image attachments (thanks to [@brettp](https://github.com/brettp)).
|
||||
- Autocomplete for usernames, user stories, tasks, issues, and wiki pages in text areas (thanks to [@brettp](https://github.com/brettp)).
|
||||
- Support authentication via Application Tokens.
|
||||
- User onboarding: improve placeholders and add joyriders.
|
||||
- i18n.
|
||||
- Add italian (it) translation.
|
||||
- Add polish (pl) translation.
|
||||
- Add portuguese (Brazil) (pt_BR) translation.
|
||||
- Add russian (ru) translation.
|
||||
|
||||
### Misc
|
||||
- Improve performance: Show cropped images in timelines.
|
||||
- Caps lock warning in login and register form.
|
||||
- Lots of small and not so small bugfixes.
|
||||
|
||||
|
||||
## 1.8.0 Saracenia Purpurea (2015-06-18)
|
||||
|
||||
### Features
|
||||
|
|
152
README.md
152
README.md
|
@ -3,13 +3,107 @@
|
|||

|
||||
[](https://taiga.io "Managed with Taiga")
|
||||
[](https://travis-ci.org/taigaio/taiga-front)
|
||||
[](https://www.versioneye.com/user/projects/561ba659a193340f280013f4)
|
||||
|
||||
|
||||
## Get the compiled version ##
|
||||
|
||||
You can get the compiled version of this code in the
|
||||
[taiga-front-dist](http://github.com/taigaio/taiga-front-dist) repository
|
||||
|
||||
## Setup initial environment ##
|
||||
|
||||
## Contribute to Taiga ##
|
||||
|
||||
#### Where to start ####
|
||||
|
||||
There are many different ways to contribute to Taiga's development, just find the one that best fits with your skills. Examples of contributions we would love to receive include:
|
||||
|
||||
- **Code patches**
|
||||
- **Documentation improvements**
|
||||
- **Translations**
|
||||
- **Bug reports**
|
||||
- **Patch reviews**
|
||||
- **UI enhancements**
|
||||
|
||||
Big features are also welcome but if you want to see your contributions included in Taiga codebase we strongly recommend you start by initiating a chat though our [mailing list](http://groups.google.co.uk/d/forum/taigaio)
|
||||
|
||||
|
||||
#### License ####
|
||||
|
||||
Every code patch accepted in taiga codebase is licensed under [AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html). You should must be careful to not include any code that can not be licensed under this license.
|
||||
|
||||
Please read carefully [our license](https://github.com/taigaio/taiga-front/blob/master/LICENSE) and ask us if you have any questions.
|
||||
|
||||
|
||||
#### Bug reports, enhancements and support ####
|
||||
|
||||
If you **nedd help to setup Taiga**, you want to **talk about some cool enhancemnt** or you have **some questions** please write us to our [mailing list](http://groups.google.com/d/forum/taigaio).
|
||||
|
||||
If you **find a bug** in Taiga you can always report it:
|
||||
|
||||
- in our [mailing list](http://groups.google.com/d/forum/taigaio).
|
||||
- in [github issues](https://github.com/taigaio/taiga-front/issues).
|
||||
- send us a mail to support@taiga.io if is a bug related to tree.taiga.io.
|
||||
- send a mail to security@taiga.io if is a **security bug**.
|
||||
|
||||
One of our fellow Taiga developers will search, find and hunt it as soon as possible.
|
||||
|
||||
Please, before reporting an bug write down how can we reproduce it, your operating system, your browser and version, and if it's possible, a screenshot. Sometimes it take less time to fix a bug if the developer know how to find it and we will solve your problem as fast as possible.
|
||||
|
||||
|
||||
#### Documentation improvements ####
|
||||
|
||||
We are gathering lots of information from our users to build and enhance our documentation. If you are the documentation to install or develop with Taiga and find any mistakes, omissions or confused sequences, it is enormously helpful to report it. Or better still, if you believe you can author additions, please make a pull-request to taiga project.
|
||||
|
||||
Currently, we have authored three main documentation hubs:
|
||||
|
||||
- **[API Docs](https://github.com/taigaio/taiga-doc)**: Our API documentation and reference for developing from Taiga API.
|
||||
- **[Installation Guide](https://github.com/taigaio/taiga-doc)**: If you need to install Taiga on your own server, this is the place to find some guides.
|
||||
- **[Taiga Support](https://github.com/taigaio/taiga-doc)**: This page is intended to be the support reference page for the users. If you find any mistake, please report it.
|
||||
|
||||
|
||||
#### Translation ####
|
||||
|
||||
We are ready now to accept your help translating Taiga. It's easy (and fun!) just access to our team of translators with the link below, set up an account in Transifex and start contributing. Join us to make sure your language is covered! **[Help Taiga to trasnlate content](https://www.transifex.com/signup/ "Help Taiga to trasnlatecontent")**
|
||||
|
||||
|
||||
#### Code patches ####
|
||||
|
||||
Taiga will always be glad to receive code patches to update, fix or improve its code.
|
||||
|
||||
If you know how to improve our code base or you found a bug, a security vulnerabilities a performance issue and you think you can solve, we will be very happy to accept your pull-request. If your code requires considerable changes, we recommend you first talk to us directly. We will find the best way to help.
|
||||
|
||||
|
||||
#### UI enhancements ####
|
||||
|
||||
Taiga is made for developers and designers. We care enormously about UI because usability and design are both critical aspects of the Taiga experience.
|
||||
|
||||
There are two possible ways to contribute to our UI:
|
||||
- **Bugs**: If you find a bug regarding front-end, please report it as previously indicated in the Bug reports section or send a pull-request as indicated in the Code Patches section.
|
||||
- **Enhancements**: If its a design or UX bug or enhancement we will love to receive your feedback. Please send us your enhancement, with the reason and, if it's possible, an example. Our design and UX team will review your enhancement and fix it as soon as possible. We recommend you to use our [mailing list](http://groups.google.co.uk/d/forum/taigaio){target="_blank"} so we can have a lot of different opinions and debate.
|
||||
- **Language Localization**: We are eager to offer localized versions of Taiga. Some members of the community have already volunteered to work to provide a variety of languages. We are working to implement some changes to allow for this and expect to accept these requests in the near future.
|
||||
|
||||
|
||||
|
||||
## Community ##
|
||||
|
||||
[Taiga has a mailing list](http://groups.google.com/d/forum/taigaio). Feel free to join it and ask any questions you may have.
|
||||
|
||||
To subscribe for announcements of releases, important changes and so on, please follow [@taigaio](https://twitter.com/taigaio) on Twitter.
|
||||
|
||||
|
||||
## Donations ##
|
||||
|
||||
We are grateful for your emails volunteering donations to Taiga. We feel comfortable accepting them under these conditions: The first that we will only do so while we are in the current beta / pre-revenue stage and that whatever money is donated will go towards a bounty fund. Starting Q2 2015 we will be engaging much more actively with our community to help further the development of Taiga, and we will use these donations to reward people working alongside us.
|
||||
|
||||
If you wish to make a donation to this Taiga fund, you can do so via http://www.paypal.com using the email: eposner@taiga.io
|
||||
|
||||
|
||||
## Setup ##
|
||||
|
||||
All the information about the different installation methods (production, development, vagrant, docker...) can be found here http://taigaio.github.io/taiga-doc/dist/#_installation_guide.
|
||||
|
||||
#### Initial dev env ####
|
||||
|
||||
Install requirements:
|
||||
|
||||
|
@ -19,36 +113,62 @@ You can install Ruby through the apt package manager, rbenv, or rvm.
|
|||
Install Sass through your **Terminal or Command Prompt**.
|
||||
|
||||
```
|
||||
$ gem install sass scss-lint
|
||||
$ export PATH="~/.gem/ruby/2.1.0/bin:$PATH"
|
||||
$ sass -v // should return Sass 3.3.8 (Maptastic Maple)
|
||||
gem install sass scss-lint
|
||||
export PATH="~/.gem/ruby/2.1.0/bin:$PATH"
|
||||
sass -v # should return Sass 3.3.8 (Maptastic Maple)
|
||||
```
|
||||
|
||||
Complete process for all OS at: http://sass-lang.com/install
|
||||
|
||||
**Node + Bower + Gulp**
|
||||
|
||||
We recommend using [nvm](https://github.com/creationix/nvmv) to manage diferent node versions
|
||||
```
|
||||
$ sudo npm install -g gulp
|
||||
$ sudo npm install -g bower
|
||||
$ npm install
|
||||
$ bower install
|
||||
$ gulp
|
||||
npm install -g gulp
|
||||
npm install -g bower
|
||||
npm install
|
||||
bower install
|
||||
gulp
|
||||
```
|
||||
|
||||
And go in your browser to: http://localhost:9001/
|
||||
|
||||
All the information about the different installation methods (production, development, vagrant, docker...) can be found here http://taigaio.github.io/taiga-doc/dist/#_installation_guide.
|
||||
#### E2E test ####
|
||||
|
||||
## Community ##
|
||||
If you want to run e2e tests
|
||||
|
||||
[Taiga has a mailing list](http://groups.google.com/d/forum/taigaio). Feel free to join it and ask any questions you may have.
|
||||
```
|
||||
npm install -g protractor
|
||||
npm install -g mocha
|
||||
npm install -g babel
|
||||
|
||||
To subscribe for announcements of releases, important changes and so on, please follow [@taigaio](https://twitter.com/taigaio) on Twitter.
|
||||
webdriver-manager update
|
||||
```
|
||||
|
||||
## Donations ##
|
||||
## Tests ##
|
||||
|
||||
We are grateful for your emails volunteering donations to Taiga. We feel comfortable accepting them under these conditions: The first that we will only do so while we are in the current beta / pre-revenue stage and that whatever money is donated will go towards a bounty fund. Starting Q2 2015 we will be engaging much more actively with our community to help further the development of Taiga, and we will use these donations to reward people working alongside us.
|
||||
#### Unit tests ####
|
||||
|
||||
If you wish to make a donation to this Taiga fund, you can do so via http://www.paypal.com using the email: eposner@taiga.io
|
||||
- To run **unit tests**
|
||||
|
||||
```
|
||||
gulp
|
||||
```
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
#### E2E tests ####
|
||||
|
||||
- To run **e2e tests** you need [taiga-back](https://github.com/taigaio/taiga-back) running and
|
||||
|
||||
```
|
||||
gulp
|
||||
```
|
||||
```
|
||||
webdriver-manager start
|
||||
```
|
||||
```
|
||||
protractor conf.e2e.js --suite=auth # To tests authentication
|
||||
protractor conf.e2e.js --suite=full # To test all the platform authenticated
|
||||
```
|
||||
|
|
|
@ -4,6 +4,8 @@ window.taigaConfig = {
|
|||
"eventsUrl": null,
|
||||
"debug": true,
|
||||
"defaultLanguage": "en",
|
||||
"themes": ["taiga", "material-design", "high-contrast"],
|
||||
"defaultTheme": "taiga",
|
||||
"publicRegisterEnabled": true,
|
||||
"feedbackEnabled": true,
|
||||
"privacyPolicyUrl": null,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -66,9 +66,11 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
access: {
|
||||
requiresLogin: true
|
||||
},
|
||||
loader: true,
|
||||
title: "HOME.PAGE_TITLE",
|
||||
loader: true,
|
||||
description: "HOME.PAGE_DESCRIPTION",
|
||||
loader: true
|
||||
joyride: "dashboard"
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -100,7 +102,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
{
|
||||
templateUrl: "search/search.html",
|
||||
reloadOnSearch: false,
|
||||
section: "search"
|
||||
section: "search",
|
||||
loader: true
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -108,7 +111,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
{
|
||||
templateUrl: "backlog/backlog.html",
|
||||
loader: true,
|
||||
section: "backlog"
|
||||
section: "backlog",
|
||||
joyride: "backlog"
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -116,7 +120,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
{
|
||||
templateUrl: "kanban/kanban.html",
|
||||
loader: true,
|
||||
section: "kanban"
|
||||
section: "kanban",
|
||||
joyride: "kanban"
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -333,29 +338,25 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
$routeProvider.when("/login",
|
||||
{
|
||||
templateUrl: "auth/login.html",
|
||||
title: "LOGIN.PAGE_TITLE"
|
||||
description: "LOGIN.PAGE_DESCRIPTION"
|
||||
title: "LOGIN.PAGE_TITLE",
|
||||
description: "LOGIN.PAGE_DESCRIPTION",
|
||||
disableHeader: true
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/register",
|
||||
{
|
||||
templateUrl: "auth/register.html",
|
||||
title: "REGISTER.PAGE_TITLE",
|
||||
description: "REGISTER.PAGE_DESCRIPTION"
|
||||
description: "REGISTER.PAGE_DESCRIPTION",
|
||||
disableHeader: true
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/forgot-password",
|
||||
{
|
||||
templateUrl: "auth/forgot-password.html",
|
||||
title: "FORGOT_PASSWORD.PAGE_TITLE",
|
||||
description: "FORGOT_PASSWORD.PAGE_DESCRIPTION"
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/change-password",
|
||||
{
|
||||
templateUrl: "auth/change-password-from-recovery.html",
|
||||
title: "CHANGE_PASSWORD.PAGE_TITLE",
|
||||
description: "CHANGE_PASSWORD.PAGE_TITLE",
|
||||
description: "FORGOT_PASSWORD.PAGE_DESCRIPTION",
|
||||
disableHeader: true
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/change-password/:token",
|
||||
|
@ -363,13 +364,26 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
templateUrl: "auth/change-password-from-recovery.html",
|
||||
title: "CHANGE_PASSWORD.PAGE_TITLE",
|
||||
description: "CHANGE_PASSWORD.PAGE_TITLE",
|
||||
disableHeader: true
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/invitation/:token",
|
||||
{
|
||||
templateUrl: "auth/invitation.html",
|
||||
title: "INVITATION.PAGE_TITLE",
|
||||
description: "INVITATION.PAGE_DESCRIPTION"
|
||||
description: "INVITATION.PAGE_DESCRIPTION",
|
||||
disableHeader: true
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/external-apps",
|
||||
{
|
||||
templateUrl: "external-apps/external-app.html",
|
||||
title: "EXTERNAL_APP.PAGE_TITLE",
|
||||
description: "EXTERNAL_APP.PAGE_DESCRIPTION",
|
||||
controller: "ExternalApp",
|
||||
controllerAs: "vm",
|
||||
disableHeader: true,
|
||||
mobileViewport: true
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -405,13 +419,13 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
# Add next param when user try to access to a secction need auth permissions.
|
||||
authHttpIntercept = ($q, $location, $navUrls, $lightboxService) ->
|
||||
httpResponseError = (response) ->
|
||||
if response.status == 0
|
||||
if response.status == 0 || response.status == -1
|
||||
$lightboxService.closeAll()
|
||||
$location.path($navUrls.resolve("error"))
|
||||
$location.replace()
|
||||
else if response.status == 401
|
||||
nextPath = $location.path()
|
||||
$location.url($navUrls.resolve("login")).search("next=#{nextPath}")
|
||||
else if response.status == 401 and $location.url().indexOf('/login') == -1
|
||||
nextUrl = encodeURIComponent($location.url())
|
||||
$location.url($navUrls.resolve("login")).search("next=#{nextUrl}")
|
||||
|
||||
return $q.reject(response)
|
||||
|
||||
|
@ -499,9 +513,13 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
.addInterpolation('$translateMessageFormatInterpolation')
|
||||
.preferredLanguage(preferedLangCode)
|
||||
|
||||
if not window.taigaConfig.debugInfo
|
||||
$translateProvider.fallbackLanguage(preferedLangCode)
|
||||
$translateProvider.fallbackLanguage(preferedLangCode)
|
||||
|
||||
# decoratos
|
||||
decorators = _.where(@.taigaContribPlugins, {"type": "decorator"})
|
||||
|
||||
_.each decorators, (decorator) ->
|
||||
$provide.decorator decorator.provider, decorator.decorator
|
||||
|
||||
# decoratos
|
||||
decorators = _.where(@.taigaContribPlugins, {"type": "decorator"})
|
||||
|
@ -544,7 +562,7 @@ i18nInit = (lang, $translate) ->
|
|||
checksley.updateMessages('default', messages)
|
||||
|
||||
|
||||
init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, appMetaService, projectService, loaderService) ->
|
||||
init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, appMetaService, projectService, loaderService, navigationBarService) ->
|
||||
$log.debug("Initialize application")
|
||||
|
||||
# Taiga Plugins
|
||||
|
@ -588,7 +606,7 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
|
|||
projectService.setSection(next.section)
|
||||
|
||||
if next.params.pslug
|
||||
projectService.setProject(next.params.pslug)
|
||||
projectService.setProjectBySlug(next.params.pslug)
|
||||
else
|
||||
projectService.cleanProject()
|
||||
|
||||
|
@ -597,6 +615,17 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
|
|||
description = $translate.instant(next.description or "")
|
||||
appMetaService.setAll(title, description)
|
||||
|
||||
if next.mobileViewport
|
||||
appMetaService.addMobileViewport()
|
||||
else
|
||||
appMetaService.removeMobileViewport()
|
||||
|
||||
if next.disableHeader
|
||||
navigationBarService.disableHeader()
|
||||
else
|
||||
navigationBarService.enableHeader()
|
||||
|
||||
pluginsWithModule = _.filter(@.taigaContribPlugins, (plugin) -> plugin.module)
|
||||
|
||||
pluginsWithModule = _.filter(@.taigaContribPlugins, (plugin) -> plugin.module)
|
||||
|
||||
|
@ -634,6 +663,7 @@ modules = [
|
|||
"taigaProfile",
|
||||
"taigaHome",
|
||||
"taigaUserTimeline",
|
||||
"taigaExternalApps",
|
||||
|
||||
# template cache
|
||||
"templates",
|
||||
|
@ -641,6 +671,7 @@ modules = [
|
|||
# Vendor modules
|
||||
"ngRoute",
|
||||
"ngAnimate",
|
||||
"ngAria",
|
||||
"pascalprecht.translate",
|
||||
"infinite-scroll",
|
||||
"tgRepeat"
|
||||
|
@ -673,5 +704,7 @@ module.run([
|
|||
"tgAppMetaService",
|
||||
"tgProjectService",
|
||||
"tgLoader",
|
||||
"tgNavigationBarService",
|
||||
"$route",
|
||||
init
|
||||
])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -41,7 +41,7 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading, lightboxService,
|
|||
template = _.template("""
|
||||
<div class="add-member-wrapper">
|
||||
<fieldset>
|
||||
<input type="email" placeholder="{{'LIGHTBOX.CREATE_MEMBER.PLACEHOLDER_TYPE_EMAIL' | translate}}"
|
||||
<input tg-capslock type="email" placeholder="{{'LIGHTBOX.CREATE_MEMBER.PLACEHOLDER_TYPE_EMAIL' | translate}}"
|
||||
<% if(required) { %> data-required="true" <% } %> data-type="email" />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -210,15 +210,17 @@ module.directive("tgMemberships", ["$tgTemplate", "$compile", MembershipsDirecti
|
|||
## Member Avatar Directive
|
||||
#############################################################################
|
||||
|
||||
MembershipsRowAvatarDirective = ($log, $template) ->
|
||||
MembershipsRowAvatarDirective = ($log, $template, $translate) ->
|
||||
template = $template.get("admin/memberships-row-avatar.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
pending = $translate.instant("ADMIN.MEMBERSHIP.STATUS_PENDING")
|
||||
render = (member) ->
|
||||
ctx = {
|
||||
full_name: if member.full_name then member.full_name else ""
|
||||
email: if member.user_email then member.user_email else member.email
|
||||
imgurl: if member.photo then member.photo else "/images/unnamed.png"
|
||||
pending: if !member.is_user_active then pending else ""
|
||||
}
|
||||
|
||||
html = template(ctx)
|
||||
|
@ -236,7 +238,7 @@ MembershipsRowAvatarDirective = ($log, $template) ->
|
|||
return {link: link}
|
||||
|
||||
|
||||
module.directive("tgMembershipsRowAvatar", ["$log", "$tgTemplate", MembershipsRowAvatarDirective])
|
||||
module.directive("tgMembershipsRowAvatar", ["$log", "$tgTemplate", '$translate', MembershipsRowAvatarDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -357,9 +359,8 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla
|
|||
"""
|
||||
|
||||
pendingTemplate = """
|
||||
<a class="pending" href="">
|
||||
{{'ADMIN.MEMBERSHIP.STATUS_PENDING' | translate}}
|
||||
<span class="icon icon-reload"></span>
|
||||
<a class="resend" href="">
|
||||
{{'ADMIN.MEMBERSHIP.RESEND' | translate}}
|
||||
</a>
|
||||
<a class="delete" href="">
|
||||
<span class="icon icon-delete"></span>
|
||||
|
@ -402,9 +403,9 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla
|
|||
defaultMsg = $translate.instant("ADMIN.MEMBERSHIP.DEFAULT_DELETE_MESSAGE", {email: member.email})
|
||||
message = if member.user then member.full_name else defaultMsg
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) ->
|
||||
onSuccess = ->
|
||||
finish()
|
||||
$confirm.askOnDelete(title, message).then (askResponse) ->
|
||||
onSuccess = =>
|
||||
askResponse.finish()
|
||||
|
||||
if $scope.page > 1 && ($scope.count - 1) <= $scope.paginatedBy
|
||||
$ctrl.selectFilter("page", $scope.page - 1)
|
||||
|
@ -414,8 +415,8 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla
|
|||
text = $translate.instant("ADMIN.MEMBERSHIP.SUCCESS_DELETE")
|
||||
$confirm.notify("success", null, text)
|
||||
|
||||
onError = ->
|
||||
finish(false)
|
||||
onError = =>
|
||||
askResponse.finish(false)
|
||||
|
||||
text = $translate.instant("ADMIN.MEMBERSHIP.ERROR_DELETE", {message: message})
|
||||
$confirm.notify("error", null, text)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -104,7 +104,7 @@ module.controller("ProjectProfileController", ProjectProfileController)
|
|||
## Project Profile Directive
|
||||
#############################################################################
|
||||
|
||||
ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location, projectService) ->
|
||||
ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location, projectService, currentUserService) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$ctrl = $el.controller()
|
||||
|
||||
|
@ -130,6 +130,7 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location, proje
|
|||
$ctrl.loadInitialData()
|
||||
|
||||
projectService.fetchProject()
|
||||
currentUserService.loadProjects()
|
||||
|
||||
promise.then null, (data) ->
|
||||
currentLoading.finish()
|
||||
|
@ -144,7 +145,7 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location, proje
|
|||
return {link:link}
|
||||
|
||||
module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation",
|
||||
"tgProjectService", ProjectProfileDirective])
|
||||
"tgProjectService", "tgCurrentUserService", ProjectProfileDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -192,9 +193,10 @@ module.directive("tgProjectDefaultValues", ["$tgRepo", "$tgConfirm", "$tgLoading
|
|||
|
||||
ProjectModulesDirective = ($repo, $confirm, $loading, projectService) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley()
|
||||
submit = =>
|
||||
form = $el.find("form").checksley()
|
||||
return if not form.validate()
|
||||
|
||||
target = angular.element(".admin-functionalities .submit-button")
|
||||
currentLoading = $loading()
|
||||
.target(target)
|
||||
|
@ -226,7 +228,7 @@ ProjectModulesDirective = ($repo, $confirm, $loading, projectService) ->
|
|||
else
|
||||
$el.find(".videoconference-attributes").addClass("hidden")
|
||||
$scope.project.videoconferences = null
|
||||
$scope.project.videoconferences_salt = ""
|
||||
$scope.project.videoconferences_extra_data = ""
|
||||
|
||||
$scope.$watch "project", (project) ->
|
||||
if project.videoconferences?
|
||||
|
@ -357,7 +359,7 @@ class CsvExporterController extends taiga.Controller
|
|||
setCsvUuid: =>
|
||||
@scope.csvUuid = @scope.project["#{@.type}_csv_uuid"]
|
||||
|
||||
_generateUuid: (finish) =>
|
||||
_generateUuid: (response=null) =>
|
||||
promise = @rs.projects["regenerate_#{@.type}_csv_uuid"](@scope.projectId)
|
||||
|
||||
promise.then (data) =>
|
||||
|
@ -367,7 +369,7 @@ class CsvExporterController extends taiga.Controller
|
|||
@confirm.notify("error")
|
||||
|
||||
promise.finally ->
|
||||
finish()
|
||||
response.finish() if response
|
||||
return promise
|
||||
|
||||
regenerateUuid: ->
|
||||
|
@ -377,7 +379,7 @@ class CsvExporterController extends taiga.Controller
|
|||
|
||||
@confirm.ask(title, subtitle).then @._generateUuid
|
||||
else
|
||||
@._generateUuid(_.identity)
|
||||
@._generateUuid()
|
||||
|
||||
|
||||
class CsvExporterUserstoriesController extends CsvExporterController
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -374,6 +374,27 @@ module.directive("tgColorSelection", ColorSelectionDirective)
|
|||
## Custom Attributes Controller
|
||||
#############################################################################
|
||||
|
||||
# Custom attributes types (see taiga-back/taiga/projects/custom_attributes/choices.py)
|
||||
TEXT_TYPE = "text"
|
||||
MULTILINE_TYPE = "multiline"
|
||||
DATE_TYPE = "date"
|
||||
|
||||
|
||||
TYPE_CHOICES = [
|
||||
{
|
||||
key: TEXT_TYPE,
|
||||
name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_TEXT"
|
||||
},
|
||||
{
|
||||
key: MULTILINE_TYPE,
|
||||
name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_MULTI"
|
||||
},
|
||||
{
|
||||
key: DATE_TYPE,
|
||||
name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_DATE"
|
||||
}
|
||||
]
|
||||
|
||||
class ProjectCustomAttributesController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
|
@ -390,6 +411,8 @@ class ProjectCustomAttributesController extends mixOf(taiga.Controller, taiga.Pa
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appMetaService,
|
||||
@translate) ->
|
||||
@scope.TYPE_CHOICES = TYPE_CHOICES
|
||||
|
||||
@scope.project = {}
|
||||
|
||||
@rootscope.$on "project:loaded", =>
|
||||
|
@ -630,13 +653,11 @@ ProjectCustomAttributesDirective = ($log, $confirm, animationFrame, $translate)
|
|||
title = $translate.instant("COMMON.CUSTOM_ATTRIBUTES.DELETE")
|
||||
text = $translate.instant("COMMON.CUSTOM_ATTRIBUTES.CONFIRM_DELETE")
|
||||
|
||||
$confirm.ask(title, text, message).then (finish) ->
|
||||
$confirm.ask(title, text, message).then (response) ->
|
||||
onSucces = ->
|
||||
$ctrl.loadCustomAttributes().finally ->
|
||||
finish()
|
||||
$ctrl.loadCustomAttributes().finally -> response.finish()
|
||||
|
||||
onError = ->
|
||||
finish(false)
|
||||
$confirm.notify("error", null, "We have not been able to delete '#{message}'.")
|
||||
|
||||
$ctrl.deleteCustomAttribute(attr).then(onSucces, onError)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -129,7 +129,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
|
|||
|
||||
return @repo.remove(@scope.role, {moveTo: response.selected}).then onSuccess, onError
|
||||
|
||||
setComputable: debounce 2000, ->
|
||||
_enableComputable: =>
|
||||
onSuccess = =>
|
||||
@confirm.notify("success")
|
||||
@.loadProject()
|
||||
|
@ -140,9 +140,37 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
|
|||
|
||||
@repo.save(@scope.role).then onSuccess, onError
|
||||
|
||||
_disableComputable: =>
|
||||
askOnSuccess = (response) =>
|
||||
onSuccess = =>
|
||||
response.finish()
|
||||
@confirm.notify("success")
|
||||
@.loadProject()
|
||||
onError = =>
|
||||
response.finish()
|
||||
@confirm.notify("error")
|
||||
@scope.role.revert()
|
||||
@repo.save(@scope.role).then onSuccess, onError
|
||||
|
||||
askOnError = (response) =>
|
||||
@scope.role.revert()
|
||||
|
||||
title = @translate.instant("ADMIN.ROLES.DISABLE_COMPUTABLE_ALERT_TITLE")
|
||||
subtitle = @translate.instant("ADMIN.ROLES.DISABLE_COMPUTABLE_ALERT_SUBTITLE", {
|
||||
roleName: @scope.role.name
|
||||
})
|
||||
message = @translate.instant("ADMIN.ROLES.DISABLE_COMPUTABLE_ALERT_MESSAGE")
|
||||
return @confirm.ask(title, subtitle, message).then askOnSuccess, askOnError
|
||||
|
||||
toggleComputable: debounce 2000, ->
|
||||
if not @scope.role.computable
|
||||
@._disableComputable()
|
||||
else
|
||||
@._enableComputable()
|
||||
|
||||
module.controller("RolesController", RolesController)
|
||||
|
||||
|
||||
EditRoleDirective = ($repo, $confirm) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
toggleView = ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -177,13 +177,13 @@ WebhookDirective = ($rs, $repo, $confirm, $loading, $translate) ->
|
|||
title = $translate.instant("ADMIN.WEBHOOKS.DELETE")
|
||||
message = $translate.instant("ADMIN.WEBHOOKS.WEBHOOK_NAME", {name: webhook.name})
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) =>
|
||||
$confirm.askOnDelete(title, message).then (askResponse) =>
|
||||
onSucces = ->
|
||||
finish()
|
||||
askResponse.finish()
|
||||
$scope.$emit("webhooks:reload")
|
||||
|
||||
onError = ->
|
||||
finish(false)
|
||||
askResponse.finish(false)
|
||||
$confirm.notify("error")
|
||||
|
||||
$repo.remove(webhook).then(onSucces, onError)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -37,10 +37,15 @@ class AuthService extends taiga.Service
|
|||
"$tgUrls",
|
||||
"$tgConfig",
|
||||
"$translate",
|
||||
"tgCurrentUserService"]
|
||||
"tgCurrentUserService",
|
||||
"tgThemeService"]
|
||||
|
||||
constructor: (@rootscope, @storage, @model, @rs, @http, @urls, @config, @translate, @currentUserService) ->
|
||||
constructor: (@rootscope, @storage, @model, @rs, @http, @urls, @config, @translate, @currentUserService,
|
||||
@themeService) ->
|
||||
super()
|
||||
|
||||
@._currentTheme = @config.get("defaultTheme") || "taiga" # load on index.jade
|
||||
|
||||
userModel = @.getUser()
|
||||
@.setUserdata(userModel)
|
||||
|
||||
|
@ -51,9 +56,18 @@ class AuthService extends taiga.Service
|
|||
else
|
||||
@.userData = null
|
||||
|
||||
_getUserTheme: ->
|
||||
return @rootscope.user?.theme || @config.get("defaultTheme") || "taiga"
|
||||
|
||||
_setTheme: ->
|
||||
newTheme = @._getUserTheme()
|
||||
|
||||
if @._currentTheme != newTheme
|
||||
@._currentTheme = newTheme
|
||||
@themeService.use(@._currentTheme)
|
||||
|
||||
_setLocales: ->
|
||||
lang = @rootscope.user.lang || @config.get("defaultLanguage") || "en"
|
||||
lang = @rootscope.user?.lang || @config.get("defaultLanguage") || "en"
|
||||
@translate.preferredLanguage(lang) # Needed for calls to the api in the correct language
|
||||
@translate.use(lang) # Needed for change the interface in runtime
|
||||
|
||||
|
@ -66,6 +80,9 @@ class AuthService extends taiga.Service
|
|||
user = @model.make_model("users", userData)
|
||||
@rootscope.user = user
|
||||
@._setLocales()
|
||||
|
||||
@._setTheme()
|
||||
|
||||
return user
|
||||
|
||||
return null
|
||||
|
@ -78,6 +95,7 @@ class AuthService extends taiga.Service
|
|||
@.setUserdata(user)
|
||||
|
||||
@._setLocales()
|
||||
@._setTheme()
|
||||
|
||||
clear: ->
|
||||
@rootscope.auth = null
|
||||
|
@ -117,9 +135,12 @@ class AuthService extends taiga.Service
|
|||
logout: ->
|
||||
@.removeToken()
|
||||
@.clear()
|
||||
|
||||
@currentUserService.removeUser()
|
||||
|
||||
@._setTheme()
|
||||
@._setLocales()
|
||||
|
||||
|
||||
register: (data, type, existing) ->
|
||||
url = @urls.resolve("auth-register")
|
||||
|
||||
|
@ -200,12 +221,12 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
|
|||
link = ($scope, $el, $attrs) ->
|
||||
onSuccess = (response) ->
|
||||
if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("login")
|
||||
nextUrl = $routeParams['next']
|
||||
nextUrl = decodeURIComponent($routeParams['next'])
|
||||
else
|
||||
nextUrl = $navUrls.resolve("home")
|
||||
|
||||
$events.setupConnection()
|
||||
$location.path(nextUrl)
|
||||
$location.url(nextUrl)
|
||||
|
||||
onError = (response) ->
|
||||
$confirm.notify("light-error", $translate.instant("LOGIN_FORM.ERROR_AUTH_INCORRECT"))
|
||||
|
@ -343,7 +364,10 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav
|
|||
$scope.tokenInParams = true
|
||||
$scope.data.token = $params.token
|
||||
else
|
||||
$scope.tokenInParams = false
|
||||
$location.path($navUrls.resolve("login"))
|
||||
|
||||
text = $translate.instant("CHANGE_PASSWORD_RECOVERY_FORM.ERROR")
|
||||
$confirm.notify("light-error",text)
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
|
||||
|
@ -354,7 +378,7 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav
|
|||
$confirm.success(text)
|
||||
|
||||
onErrorSubmit = (response) ->
|
||||
text = $translate.instant("COMMON.GENERIC_ERROR", {error: response.data._error_message})
|
||||
text = $translate.instant("CHANGE_PASSWORD_RECOVERY_FORM.ERROR")
|
||||
$confirm.notify("light-error", text)
|
||||
|
||||
submit = debounce 2000, (event) =>
|
||||
|
@ -394,7 +418,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics
|
|||
$location.path($navUrls.resolve("login"))
|
||||
|
||||
text = $translate.instant("INVITATION_LOGIN_FORM.NOT_FOUND")
|
||||
$confirm.success(text)
|
||||
$confirm.notify("light-error", text)
|
||||
|
||||
# Login form
|
||||
$scope.dataLogin = {token: token}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -35,11 +35,13 @@ module = angular.module("taigaBacklog")
|
|||
## Issues Filters Directive
|
||||
#############################################################################
|
||||
|
||||
BacklogFiltersDirective = ($log, $location, $templates) ->
|
||||
BacklogFiltersDirective = ($q, $log, $location, $templates) ->
|
||||
template = $templates.get("backlog/filters.html", true)
|
||||
templateSelected = $templates.get("backlog/filter-selected.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
currentFiltersType = ''
|
||||
|
||||
$ctrl = $el.closest(".wrapper").controller()
|
||||
selectedFilters = []
|
||||
|
||||
|
@ -50,16 +52,18 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
|
|||
$el.find("h2 a.subfilter span.title").html(title)
|
||||
$el.find("h2 a.subfilter span.title").prop("data-type", type)
|
||||
|
||||
currentFiltersType = getFiltersType()
|
||||
|
||||
showCategories = ->
|
||||
$el.find(".filters-cats").show()
|
||||
$el.find(".filter-list").addClass("hidden")
|
||||
$el.find("h2.breadcrumb").addClass("hidden")
|
||||
|
||||
initializeSelectedFilters = (filters) ->
|
||||
initializeSelectedFilters = () ->
|
||||
showCategories()
|
||||
selectedFilters = []
|
||||
|
||||
for name, values of filters
|
||||
for name, values of $scope.filters
|
||||
for val in values
|
||||
selectedFilters.push(val) if val.selected
|
||||
|
||||
|
@ -81,43 +85,62 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
|
|||
html = template({filters:filters})
|
||||
$el.find(".filter-list").html(html)
|
||||
|
||||
getFiltersType = () ->
|
||||
return $el.find("h2 a.subfilter span.title").prop('data-type')
|
||||
|
||||
reloadUserstories = () ->
|
||||
currentFiltersType = getFiltersType()
|
||||
|
||||
$q.all([$ctrl.loadUserstories(), $ctrl.generateFilters()]).then () ->
|
||||
currentFilters = $scope.filters[currentFiltersType]
|
||||
renderFilters(_.reject(currentFilters, "selected"))
|
||||
|
||||
toggleFilterSelection = (type, id) ->
|
||||
currentFiltersType = getFiltersType()
|
||||
|
||||
filters = $scope.filters[type]
|
||||
filter = _.find(filters, {id: taiga.toString(id)})
|
||||
filter = _.find(filters, {id: id})
|
||||
filter.selected = (not filter.selected)
|
||||
|
||||
if filter.selected
|
||||
selectedFilters.push(filter)
|
||||
$scope.$apply ->
|
||||
$ctrl.selectFilter(type, id)
|
||||
else
|
||||
selectedFilters = _.reject(selectedFilters, filter)
|
||||
$scope.$apply ->
|
||||
$ctrl.unselectFilter(type, id)
|
||||
selectedFilters = _.reject selectedFilters, (selected) ->
|
||||
return filter.type == selected.type && filter.id == selected.id
|
||||
|
||||
$ctrl.unselectFilter(type, id)
|
||||
|
||||
renderSelectedFilters(selectedFilters)
|
||||
|
||||
currentFiltersType = $el.find("h2 a.subfilter span.title").prop('data-type')
|
||||
if type == currentFiltersType
|
||||
renderFilters(_.reject(filters, "selected"))
|
||||
|
||||
$ctrl.loadUserstories()
|
||||
reloadUserstories()
|
||||
|
||||
selectQFilter = debounceLeading 100, (value) ->
|
||||
return if value is undefined
|
||||
|
||||
if value.length == 0
|
||||
$ctrl.replaceFilter("q", null)
|
||||
else
|
||||
$ctrl.replaceFilter("q", value)
|
||||
$ctrl.loadUserstories()
|
||||
|
||||
reloadUserstories()
|
||||
|
||||
$scope.$watch("filtersQ", selectQFilter)
|
||||
|
||||
## Angular Watchers
|
||||
$scope.$on "filters:loaded", (ctx, filters) ->
|
||||
initializeSelectedFilters(filters)
|
||||
$scope.$on "backlog:loaded", (ctx) ->
|
||||
initializeSelectedFilters()
|
||||
|
||||
$scope.$on "filters:update", (ctx, filters) ->
|
||||
renderFilters(filters)
|
||||
$scope.$on "filters:update", (ctx) ->
|
||||
$ctrl.generateFilters().then () ->
|
||||
filters = $scope.filters[currentFiltersType]
|
||||
|
||||
if currentFiltersType
|
||||
renderFilters(_.reject(filters, "selected"))
|
||||
|
||||
## Dom Event Handlers
|
||||
$el.on "click", ".filters-cats > ul > li > a", (event) ->
|
||||
|
@ -126,7 +149,7 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
|
|||
tags = $scope.filters[target.data("type")]
|
||||
|
||||
renderFilters(_.reject(tags, "selected"))
|
||||
showFilters(target.attr("title"), target.data("type"))
|
||||
showFilters(target.attr("title"), target.data('type'))
|
||||
|
||||
$el.on "click", ".filters-inner > .filters-step-cat > .breadcrumb > .back", (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -153,4 +176,4 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
|
|||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgBacklogFilters", ["$log", "$tgLocation", "$tgTemplate", BacklogFiltersDirective])
|
||||
module.directive("tgBacklogFilters", ["$q", "$log", "$tgLocation", "$tgTemplate", BacklogFiltersDirective])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -34,12 +34,13 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading,
|
|||
hasErrors = false
|
||||
createSprint = true
|
||||
|
||||
$scope.sprint = {
|
||||
project: null
|
||||
name: null
|
||||
estimated_start: null
|
||||
estimated_finish: null
|
||||
}
|
||||
resetSprint = () ->
|
||||
$scope.sprint = {
|
||||
project: null
|
||||
name: null
|
||||
estimated_start: null
|
||||
estimated_finish: null
|
||||
}
|
||||
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
@ -95,19 +96,30 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading,
|
|||
title = $translate.instant("LIGHTBOX.DELETE_SPRINT.TITLE")
|
||||
message = $scope.sprint.name
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) =>
|
||||
$confirm.askOnDelete(title, message).then (askResponse) =>
|
||||
onSuccess = ->
|
||||
finish()
|
||||
askResponse.finish()
|
||||
$scope.milestonesCounter -= 1
|
||||
lightboxService.close($el)
|
||||
$rootscope.$broadcast("sprintform:remove:success")
|
||||
$rootscope.$broadcast("sprintform:remove:success", $scope.sprint)
|
||||
|
||||
onError = ->
|
||||
finish(false)
|
||||
askResponse.finish(false)
|
||||
$confirm.notify("error")
|
||||
$repo.remove($scope.sprint).then(onSuccess, onError)
|
||||
|
||||
getLastSprint = ->
|
||||
openSprints = _.filter $scope.sprints, (sprint) ->
|
||||
return !sprint.closed
|
||||
|
||||
sortedSprints = _.sortBy openSprints, (sprint) ->
|
||||
return moment(sprint.estimated_finish, 'YYYY-MM-DD').format('X')
|
||||
|
||||
return sortedSprints[sortedSprints.length - 1]
|
||||
|
||||
$scope.$on "sprintform:create", (event, projectId) ->
|
||||
resetSprint()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
form.reset()
|
||||
|
||||
|
@ -117,20 +129,24 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading,
|
|||
$scope.sprint.name = null
|
||||
$scope.sprint.slug = null
|
||||
|
||||
lastSprint = $scope.sprints[0]
|
||||
lastSprint = getLastSprint()
|
||||
|
||||
estimatedStart = moment()
|
||||
if $scope.sprint.estimated_start
|
||||
estimatedStart = moment($scope.sprint.estimated_start)
|
||||
else if lastSprint?
|
||||
|
||||
if lastSprint
|
||||
estimatedStart = moment(lastSprint.estimated_finish)
|
||||
else if $scope.sprint.estimated_start
|
||||
estimatedStart = moment($scope.sprint.estimated_start)
|
||||
|
||||
$scope.sprint.estimated_start = estimatedStart.format(prettyDate)
|
||||
|
||||
estimatedFinish = moment().add(2, "weeks")
|
||||
if $scope.sprint.estimated_finish
|
||||
estimatedFinish = moment($scope.sprint.estimated_finish)
|
||||
else if lastSprint?
|
||||
|
||||
if lastSprint
|
||||
estimatedFinish = moment(lastSprint.estimated_finish).add(2, "weeks")
|
||||
else if $scope.sprint.estimated_finish
|
||||
estimatedFinish = moment($scope.sprint.estimated_finish)
|
||||
|
||||
$scope.sprint.estimated_finish = estimatedFinish.format(prettyDate)
|
||||
|
||||
lastSprintNameDom = $el.find(".last-sprint-name")
|
||||
|
@ -152,6 +168,8 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading,
|
|||
$el.find(".last-sprint-name").removeClass("disappear")
|
||||
|
||||
$scope.$on "sprintform:edit", (ctx, sprint) ->
|
||||
resetSprint()
|
||||
|
||||
createSprint = false
|
||||
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
|
||||
|
@ -187,6 +205,8 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading,
|
|||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
resetSprint()
|
||||
|
||||
return {link: link}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -28,6 +28,7 @@ bindOnce = @.taiga.bindOnce
|
|||
groupBy = @.taiga.groupBy
|
||||
timeout = @.taiga.timeout
|
||||
bindMethods = @.taiga.bindMethods
|
||||
generateHash = @.taiga.generateHash
|
||||
|
||||
module = angular.module("taigaBacklog")
|
||||
|
||||
|
@ -60,6 +61,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@scope.sectionName = @translate.instant("BACKLOG.SECTION_NAME")
|
||||
@showTags = false
|
||||
@activeFilters = false
|
||||
@scope.showGraphPlaceholder = null
|
||||
|
||||
@.initializeEventHandlers()
|
||||
|
||||
|
@ -96,18 +98,26 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@scope.$on "usform:new:success", =>
|
||||
@.loadUserstories()
|
||||
@.loadProjectStats()
|
||||
|
||||
@rootscope.$broadcast("filters:update")
|
||||
@analytics.trackEvent("userstory", "create", "create userstory on backlog", 1)
|
||||
|
||||
@scope.$on "sprintform:edit:success", =>
|
||||
@.loadProjectStats()
|
||||
|
||||
@scope.$on "sprintform:remove:success", =>
|
||||
@scope.$on "sprintform:remove:success", (event, sprint) =>
|
||||
@.loadSprints()
|
||||
@.loadProjectStats()
|
||||
@.loadUserstories()
|
||||
|
||||
if sprint.closed
|
||||
@.loadClosedSprints()
|
||||
|
||||
@rootscope.$broadcast("filters:update")
|
||||
|
||||
@scope.$on "usform:edit:success", =>
|
||||
@.loadUserstories()
|
||||
@rootscope.$broadcast("filters:update")
|
||||
|
||||
@scope.$on("sprint:us:move", @.moveUs)
|
||||
@scope.$on("sprint:us:moved", @.loadSprints)
|
||||
|
@ -137,18 +147,16 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
loadProjectStats: ->
|
||||
return @rs.projects.stats(@scope.projectId).then (stats) =>
|
||||
@scope.stats = stats
|
||||
totalPoints = if stats.total_points then stats.total_points else stats.defined_points
|
||||
|
||||
if stats.total_points
|
||||
@scope.stats.completedPercentage = Math.round(100 * stats.closed_points / stats.total_points)
|
||||
if totalPoints
|
||||
@scope.stats.completedPercentage = Math.round(100 * stats.closed_points / totalPoints)
|
||||
else
|
||||
@scope.stats.completedPercentage = 0
|
||||
|
||||
@scope.showGraphPlaceholder = !(stats.total_points? && stats.total_milestones?)
|
||||
return stats
|
||||
|
||||
refreshTagsColors: ->
|
||||
return @rs.projects.tagsColors(@scope.projectId).then (tags_colors) =>
|
||||
@scope.project.tags_colors = tags_colors
|
||||
|
||||
unloadClosedSprints: ->
|
||||
@scope.$apply =>
|
||||
@scope.closedSprints = []
|
||||
|
@ -156,17 +164,29 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
loadClosedSprints: ->
|
||||
params = {closed: true}
|
||||
return @rs.sprints.list(@scope.projectId, params).then (sprints) =>
|
||||
return @rs.sprints.list(@scope.projectId, params).then (result) =>
|
||||
sprints = result.milestones
|
||||
|
||||
@scope.totalClosedMilestones = result.closed
|
||||
|
||||
# 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
|
||||
@scope.closedSprintsById = groupBy(sprints, (x) -> x.id)
|
||||
@rootscope.$broadcast("closed-sprints:reloaded", sprints)
|
||||
return sprints
|
||||
|
||||
loadSprints: ->
|
||||
params = {closed: false}
|
||||
return @rs.sprints.list(@scope.projectId, params).then (sprints) =>
|
||||
return @rs.sprints.list(@scope.projectId, params).then (result) =>
|
||||
sprints = result.milestones
|
||||
|
||||
@scope.totalMilestones = sprints
|
||||
@scope.totalClosedMilestones = result.closed
|
||||
@scope.totalOpenMilestones = result.open
|
||||
@scope.totalMilestones = @scope.totalOpenMilestones + @scope.totalClosedMilestones
|
||||
|
||||
# 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")
|
||||
|
@ -182,7 +202,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
resetFilters: ->
|
||||
selectedTags = _.filter(@scope.filters.tags, "selected")
|
||||
selectedStatuses = _.filter(@scope.filters.statuses, "selected")
|
||||
selectedStatuses = _.filter(@scope.filters.status, "selected")
|
||||
|
||||
@scope.filtersQ = ""
|
||||
|
||||
|
@ -195,23 +215,20 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@.unselectFilter(item.type, item.id)
|
||||
|
||||
@.loadUserstories()
|
||||
@rootscope.$broadcast("filters:update")
|
||||
|
||||
loadUserstories: ->
|
||||
@scope.httpParams = @.getUrlFilters()
|
||||
@rs.userstories.storeQueryParams(@scope.projectId, @scope.httpParams)
|
||||
|
||||
promise = @q.all([@.refreshTagsColors(), @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams)])
|
||||
promise = @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams)
|
||||
|
||||
return promise.then (data) =>
|
||||
userstories = data[1]
|
||||
return promise.then (userstories) =>
|
||||
# NOTE: Fix order of USs because the filter orderBy does not work propertly in the partials files
|
||||
@scope.userstories = _.sortBy(userstories, "backlog_order")
|
||||
|
||||
@.setSearchDataFilters()
|
||||
@.filterVisibleUserstories()
|
||||
@.generateFilters()
|
||||
|
||||
@rootscope.$broadcast("filters:loaded", @scope.filters)
|
||||
# The broadcast must be executed when the DOM has been fully reloaded.
|
||||
# We can't assure when this exactly happens so we need a defer
|
||||
scopeDefer @scope, =>
|
||||
|
@ -233,7 +250,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
@scope.projectId = project.id
|
||||
@scope.project = project
|
||||
@scope.totalClosedMilestones = project.total_closed_milestones
|
||||
@scope.closedMilestones = !!project.total_closed_milestones
|
||||
@scope.$emit('project:loaded', project)
|
||||
@scope.points = _.sortBy(project.points, "order")
|
||||
@scope.pointsById = groupBy(project.points, (x) -> x.id)
|
||||
|
@ -244,25 +261,13 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
loadInitialData: ->
|
||||
promise = @.loadProject()
|
||||
promise.then (project) =>
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
@.initializeSubscription()
|
||||
|
||||
return promise.then(=> @.loadBacklog())
|
||||
|
||||
filterVisibleUserstories: ->
|
||||
@scope.visibleUserstories = []
|
||||
|
||||
# Filter by tags
|
||||
@scope.visibleUserstories = _.reject @scope.userstories, (us) =>
|
||||
return _.some us.tags, (tag) =>
|
||||
return @isFilterSelected("tag", tag)
|
||||
|
||||
# Filter by status
|
||||
@scope.visibleUserstories = _.filter @scope.visibleUserstories, (us) =>
|
||||
if @searchdata["statuses"] && Object.keys(@searchdata["statuses"]).length
|
||||
return @isFilterSelected("statuses", taiga.toString(us.status))
|
||||
|
||||
return true
|
||||
return promise
|
||||
.then(=> @.loadBacklog())
|
||||
.then(=> @.generateFilters())
|
||||
.then(=> @scope.$emit("backlog:loaded"))
|
||||
|
||||
prepareBulkUpdateData: (uses, field="backlog_order") ->
|
||||
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
|
||||
|
@ -281,6 +286,23 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
oldSprintId = usList[0].milestone
|
||||
project = usList[0].project
|
||||
|
||||
movedFromClosedSprint = false
|
||||
movedToClosedSprint = false
|
||||
|
||||
sprint = @scope.sprintsById[oldSprintId]
|
||||
|
||||
# Move from closed sprint
|
||||
if !sprint && @scope.closedSprintsById
|
||||
sprint = @scope.closedSprintsById[oldSprintId]
|
||||
movedFromClosedSprint = true if sprint
|
||||
|
||||
newSprint = @scope.sprintsById[newSprintId]
|
||||
|
||||
# Move to closed sprint
|
||||
if !newSprint && newSprintId
|
||||
newSprint = @scope.closedSprintsById[newSprintId]
|
||||
movedToClosedSprint = true if newSprint
|
||||
|
||||
# In the same sprint or in the backlog
|
||||
if newSprintId == oldSprintId
|
||||
items = null
|
||||
|
@ -289,7 +311,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
if newSprintId == null
|
||||
userstories = @scope.userstories
|
||||
else
|
||||
userstories = @scope.sprintsById[newSprintId].user_stories
|
||||
userstories = newSprint.user_stories
|
||||
|
||||
@scope.$apply ->
|
||||
for us, key in usList
|
||||
|
@ -333,16 +355,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@scope.$apply =>
|
||||
# Add new us to backlog userstories list
|
||||
# @scope.userstories.splice(newUsIndex, 0, us)
|
||||
# @scope.visibleUserstories.splice(newUsIndex, 0, us)
|
||||
args = [newUsIndex, 0].concat(usList)
|
||||
Array.prototype.splice.apply(@scope.userstories, args)
|
||||
Array.prototype.splice.apply(@scope.visibleUserstories, args)
|
||||
|
||||
# Execute the prefiltering of user stories
|
||||
@.filterVisibleUserstories()
|
||||
|
||||
# Remove the us from the sprint list.
|
||||
sprint = @scope.sprintsById[oldSprintId]
|
||||
for us, key in usList
|
||||
r = sprint.user_stories.indexOf(us)
|
||||
sprint.user_stories.splice(r, 1)
|
||||
|
@ -358,13 +373,15 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
return @rs.userstories.bulkUpdateBacklogOrder(us.project, data).then =>
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
if movedFromClosedSprint
|
||||
@rootscope.$broadcast("backlog:load-closed-sprints")
|
||||
|
||||
promise.then null, ->
|
||||
console.log "FAIL" # TODO
|
||||
|
||||
return promise
|
||||
|
||||
# From backlog to sprint
|
||||
newSprint = @scope.sprintsById[newSprintId]
|
||||
if oldSprintId == null
|
||||
us.milestone = newSprintId for us in usList
|
||||
|
||||
|
@ -376,9 +393,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
# Remove moving us from backlog userstories lists.
|
||||
for us, key in usList
|
||||
r = @scope.visibleUserstories.indexOf(us)
|
||||
@scope.visibleUserstories.splice(r, 1)
|
||||
|
||||
r = @scope.userstories.indexOf(us)
|
||||
@scope.userstories.splice(r, 1)
|
||||
|
||||
|
@ -394,9 +408,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
# Remove the us from the sprint list.
|
||||
for us in usList
|
||||
oldSprint = @scope.sprintsById[oldSprintId]
|
||||
r = oldSprint.user_stories.indexOf(us)
|
||||
oldSprint.user_stories.splice(r, 1)
|
||||
r = sprint.user_stories.indexOf(us)
|
||||
sprint.user_stories.splice(r, 1)
|
||||
|
||||
# Persist the milestone change of userstory
|
||||
promises = _.map usList, (us) => @repo.save(us)
|
||||
|
@ -407,13 +420,16 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
|
||||
data = @.prepareBulkUpdateData(items, "sprint_order")
|
||||
|
||||
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
||||
@rs.userstories.bulkUpdateSprintOrder(project, data).then (result) =>
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||
for us in usList
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
if movedToClosedSprint || movedFromClosedSprint
|
||||
@scope.$broadcast("backlog:load-closed-sprints")
|
||||
|
||||
promise.then null, ->
|
||||
console.log "FAIL" # TODO
|
||||
|
||||
|
@ -439,74 +455,77 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@searchdata[name][val] = true
|
||||
|
||||
getUrlFilters: ->
|
||||
return _.pick(@location.search(), "statuses", "tags", "q")
|
||||
return _.pick(@location.search(), "status", "tags", "q")
|
||||
|
||||
generateFilters: ->
|
||||
urlfilters = @.getUrlFilters()
|
||||
@scope.filters = {}
|
||||
@scope.filters = {}
|
||||
|
||||
#tags
|
||||
plainTags = _.flatten(_.filter(_.map(@scope.visibleUserstories, "tags")))
|
||||
plainTags.sort()
|
||||
loadFilters = {}
|
||||
loadFilters.project = @scope.projectId
|
||||
loadFilters.tags = urlfilters.tags
|
||||
loadFilters.status = urlfilters.status
|
||||
loadFilters.q = urlfilters.q
|
||||
loadFilters.milestone = 'null'
|
||||
|
||||
if plainTags.length == 0 and urlfilters["tags"]
|
||||
plainTags.push(urlfilters["tags"])
|
||||
return @rs.userstories.filtersData(loadFilters).then (data) =>
|
||||
choicesFiltersFormat = (choices, type, byIdObject) =>
|
||||
_.map choices, (t) ->
|
||||
t.type = type
|
||||
return t
|
||||
|
||||
@scope.filters.tags = _.map _.countBy(plainTags), (v, k) =>
|
||||
obj = {
|
||||
id: k,
|
||||
type: "tags",
|
||||
name: k,
|
||||
color: @scope.project.tags_colors[k],
|
||||
count: v
|
||||
}
|
||||
obj.selected = true if @isFilterSelected("tags", obj.id)
|
||||
return obj
|
||||
tagsFilterFormat = (tags) =>
|
||||
return _.map tags, (t) ->
|
||||
t.id = t.name
|
||||
t.type = 'tags'
|
||||
return t
|
||||
|
||||
selectedTags = _.filter(@scope.filters.tags, "selected")
|
||||
selectedTags = _.map(selectedTags, "name")
|
||||
# Build filters data structure
|
||||
@scope.filters.status = choicesFiltersFormat(data.statuses, "status", @scope.usStatusById)
|
||||
@scope.filters.tags = tagsFilterFormat(data.tags)
|
||||
|
||||
#status
|
||||
plainStatuses = _.map(@scope.visibleUserstories, "status")
|
||||
selectedTags = _.filter(@scope.filters.tags, "selected")
|
||||
selectedTags = _.map(selectedTags, "id")
|
||||
|
||||
plainStatuses = _.filter plainStatuses, (status) =>
|
||||
if status
|
||||
return status
|
||||
selectedStatuses = _.filter(@scope.filters.status, "selected")
|
||||
selectedStatuses = _.map(selectedStatuses, "id")
|
||||
|
||||
if plainStatuses.length == 0 and urlfilters["statuses"]
|
||||
plainStatuses.push(urlfilters["statuses"])
|
||||
@.markSelectedFilters(@scope.filters, urlfilters)
|
||||
|
||||
@scope.filters.statuses = _.map _.countBy(plainStatuses), (v, k) =>
|
||||
obj = {
|
||||
id: k,
|
||||
type: "statuses",
|
||||
name: @scope.usStatusById[k].name,
|
||||
color: @scope.usStatusById[k].color,
|
||||
count:v
|
||||
}
|
||||
obj.selected = true if @isFilterSelected("statuses", obj.id)
|
||||
#store query params
|
||||
@rs.userstories.storeQueryParams(@scope.projectId, {
|
||||
"status": selectedStatuses,
|
||||
"tags": selectedTags,
|
||||
"project": @scope.projectId
|
||||
"milestone": null
|
||||
})
|
||||
|
||||
return obj
|
||||
markSelectedFilters: (filters, urlfilters) ->
|
||||
# Build selected filters (from url) fast lookup data structure
|
||||
searchdata = {}
|
||||
for name, value of _.omit(urlfilters, "page", "orderBy")
|
||||
if not searchdata[name]?
|
||||
searchdata[name] = {}
|
||||
|
||||
selectedStatuses = _.filter(@scope.filters.statuses, "selected")
|
||||
selectedStatuses = _.map(selectedStatuses, "id")
|
||||
for val in "#{value}".split(",")
|
||||
searchdata[name][val] = true
|
||||
|
||||
#store query params
|
||||
@rs.userstories.storeQueryParams(@scope.projectId, {
|
||||
"status": selectedStatuses,
|
||||
"tags": selectedTags,
|
||||
"project": @scope.projectId
|
||||
"milestone": null
|
||||
})
|
||||
isSelected = (type, id) ->
|
||||
if searchdata[type]? and searchdata[type][id]
|
||||
return true
|
||||
return false
|
||||
|
||||
for key, value of filters
|
||||
for obj in value
|
||||
obj.selected = if isSelected(obj.type, obj.id) then true else undefined
|
||||
|
||||
## Template actions
|
||||
|
||||
updateUserStoryStatus: () ->
|
||||
@.setSearchDataFilters()
|
||||
@.filterVisibleUserstories()
|
||||
@.generateFilters()
|
||||
@rootscope.$broadcast("filters:update", @scope.filters['statuses'])
|
||||
@.loadProjectStats()
|
||||
@.generateFilters().then () =>
|
||||
@rootscope.$broadcast("filters:update")
|
||||
@.loadProjectStats()
|
||||
|
||||
editUserStory: (projectId, ref, $event) ->
|
||||
target = $($event.target)
|
||||
|
@ -527,16 +546,15 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
message = us.subject
|
||||
|
||||
@confirm.askOnDelete(title, message).then (finish) =>
|
||||
@confirm.askOnDelete(title, message).then (askResponse) =>
|
||||
# We modify the userstories in scope so the user doesn't see the removed US for a while
|
||||
@scope.userstories = _.without(@scope.userstories, us)
|
||||
@filterVisibleUserstories()
|
||||
promise = @.repo.remove(us)
|
||||
promise.then =>
|
||||
finish()
|
||||
askResponse.finish()
|
||||
@.loadBacklog()
|
||||
promise.then null, =>
|
||||
finish(false)
|
||||
askResponse.finish(false)
|
||||
@confirm.notify("error")
|
||||
|
||||
addNewUs: (type) ->
|
||||
|
@ -563,7 +581,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
|
||||
linkDoomLine = ($scope, $el, $attrs, $ctrl) ->
|
||||
reloadDoomLine = ->
|
||||
if $scope.stats?
|
||||
if $scope.stats? and $scope.stats.total_points? and $scope.stats.total_points != 0
|
||||
removeDoomlineDom()
|
||||
|
||||
stats = $scope.stats
|
||||
|
@ -571,9 +589,9 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
total_points = stats.total_points
|
||||
current_sum = stats.assigned_points
|
||||
|
||||
return if not $scope.visibleUserstories
|
||||
return if not $scope.userstories
|
||||
|
||||
for us, i in $scope.visibleUserstories
|
||||
for us, i in $scope.userstories
|
||||
current_sum += us.total_points
|
||||
|
||||
if current_sum > total_points
|
||||
|
@ -614,7 +632,6 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
# Update the total of points
|
||||
$scope.sprints[0].total_points += totalExtraPoints
|
||||
|
||||
$ctrl.filterVisibleUserstories()
|
||||
$repo.saveAll(selectedUss).then ->
|
||||
$ctrl.loadSprints()
|
||||
$ctrl.loadProjectStats()
|
||||
|
@ -626,7 +643,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
checkSelected = (target) ->
|
||||
lastChecked = target.closest(".us-item-row")
|
||||
moveToCurrentSprintDom = $el.find("#move-to-current-sprint")
|
||||
selectedUsDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked")
|
||||
selectedUsDom = $el.find(".backlog-table-body input:checkbox:checked")
|
||||
|
||||
if selectedUsDom.length > 0 and $scope.sprints.length > 0
|
||||
moveToCurrentSprintDom.show()
|
||||
|
@ -641,7 +658,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
return true
|
||||
|
||||
# Enable move to current sprint only when there are selected us's
|
||||
$el.on "change", ".backlog-table-body .user-stories input:checkbox", (event) ->
|
||||
$el.on "change", ".backlog-table-body input:checkbox", (event) ->
|
||||
# check elements between the last two if shift is pressed
|
||||
if lastChecked && shiftPressed
|
||||
elements = []
|
||||
|
@ -656,15 +673,16 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
|
||||
_.map elements, (elm) ->
|
||||
input = $(elm).find("input:checkbox")
|
||||
input.prop('checked', true);
|
||||
input.prop('checked', true)
|
||||
checkSelected(input)
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
target.closest(".us-item-row").toggleClass('is-checked')
|
||||
checkSelected(target)
|
||||
|
||||
$el.on "click", "#move-to-current-sprint", (event) =>
|
||||
# Calculating the us's to be modified
|
||||
ussDom = $el.find(".backlog-table-body .user-stories input:checkbox:checked")
|
||||
ussDom = $el.find(".backlog-table-body input:checkbox:checked")
|
||||
|
||||
ussToMove = _.map ussDom, (item) ->
|
||||
item = $(item).closest('.tg-scope')
|
||||
|
@ -688,12 +706,12 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
elm.addClass("active")
|
||||
|
||||
text = $translate.instant("BACKLOG.TAGS.HIDE")
|
||||
elm.find(".text").text(text)
|
||||
elm.text(text)
|
||||
else
|
||||
elm.removeClass("active")
|
||||
|
||||
text = $translate.instant("BACKLOG.TAGS.SHOW")
|
||||
elm.find(".text").text(text)
|
||||
elm.text(text)
|
||||
|
||||
showHideFilter = ($scope, $el, $ctrl) ->
|
||||
sidebar = $el.find("sidebar.filters-bar")
|
||||
|
@ -736,8 +754,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
|
|||
$el.find(".backlog-table-body").disableSelection()
|
||||
|
||||
filters = $ctrl.getUrlFilters()
|
||||
|
||||
if filters.statuses ||
|
||||
if filters.status ||
|
||||
filters.tags ||
|
||||
filters.q
|
||||
showHideFilter($scope, $el, $ctrl)
|
||||
|
@ -915,6 +932,56 @@ UsPointsDirective = ($tgEstimationsService, $repo, $tgTemplate) ->
|
|||
module.directive("tgBacklogUsPoints", ["$tgEstimationsService", "$tgRepo", "$tgTemplate", UsPointsDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Burndown graph directive
|
||||
#############################################################################
|
||||
ToggleBurndownVisibility = ($storage) ->
|
||||
hide = () ->
|
||||
$(".js-burndown-graph").removeClass("shown")
|
||||
$(".js-toggle-burndown-visibility-button").removeClass("active")
|
||||
$(".js-burndown-graph").removeClass("open")
|
||||
|
||||
show = (firstLoad) ->
|
||||
$(".js-toggle-burndown-visibility-button").addClass("active")
|
||||
|
||||
if firstLoad
|
||||
$(".js-burndown-graph").addClass("shown")
|
||||
else
|
||||
$(".js-burndown-graph").addClass("open")
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
firstLoad = true
|
||||
hash = generateHash(["is-burndown-grpahs-collapsed"])
|
||||
$scope.isBurndownGraphCollapsed = $storage.get(hash) or false
|
||||
|
||||
toggleGraph = ->
|
||||
if $scope.isBurndownGraphCollapsed
|
||||
hide(firstLoad)
|
||||
else
|
||||
show(firstLoad)
|
||||
|
||||
firstLoad = false
|
||||
|
||||
$scope.$watch "showGraphPlaceholder", () ->
|
||||
if $scope.showGraphPlaceholder?
|
||||
$scope.isBurndownGraphCollapsed = $scope.isBurndownGraphCollapsed || $scope.showGraphPlaceholder
|
||||
toggleGraph()
|
||||
|
||||
$el.on "click", ".js-toggle-burndown-visibility-button", ->
|
||||
$scope.isBurndownGraphCollapsed = !$scope.isBurndownGraphCollapsed
|
||||
$storage.set(hash, $scope.isBurndownGraphCollapsed)
|
||||
toggleGraph()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {
|
||||
link: link
|
||||
}
|
||||
|
||||
module.directive("tgToggleBurndownVisibility", ["$tgStorage", ToggleBurndownVisibility])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Burndown graph directive
|
||||
#############################################################################
|
||||
|
@ -1007,16 +1074,16 @@ BurndownBacklogGraphDirective = ($translate) ->
|
|||
tooltipOpts: {
|
||||
content: (label, xval, yval, flotItem) ->
|
||||
if flotItem.seriesIndex == 1
|
||||
ctx = {xval: xval, yval: yval}
|
||||
ctx = {sprintName: dataToDraw.milestones[xval].name, value: Math.abs(yval)}
|
||||
return $translate.instant("BACKLOG.CHART.OPTIMAL", ctx)
|
||||
else if flotItem.seriesIndex == 2
|
||||
ctx = {xval: xval, yval: yval}
|
||||
ctx = {sprintName: dataToDraw.milestones[xval].name, value: Math.abs(yval)}
|
||||
return $translate.instant("BACKLOG.CHART.REAL", ctx)
|
||||
else if flotItem.seriesIndex == 3
|
||||
ctx = {xval: xval, yval: Math.abs(yval)}
|
||||
ctx = {sprintName: dataToDraw.milestones[xval].name, value: Math.abs(yval)}
|
||||
return $translate.instant("BACKLOG.CHART.INCREMENT_TEAM", ctx)
|
||||
else
|
||||
ctx = {xval: xval, yval: Math.abs(yval)}
|
||||
ctx = {sprintName: dataToDraw.milestones[xval].name, value: Math.abs(yval)}
|
||||
return $translate.instant("BACKLOG.CHART.INCREMENT_CLIENT", ctx)
|
||||
}
|
||||
}
|
||||
|
@ -1046,14 +1113,16 @@ module.directive("tgBurndownBacklogGraph", ["$translate", BurndownBacklogGraphDi
|
|||
## Backlog progress bar directive
|
||||
#############################################################################
|
||||
|
||||
TgBacklogProgressBarDirective = ($template) ->
|
||||
TgBacklogProgressBarDirective = ($template, $compile) ->
|
||||
template = $template.get("backlog/progress-bar.html", true)
|
||||
|
||||
render = (el, projectPointsPercentaje, closedPointsPercentaje) ->
|
||||
el.html(template({
|
||||
render = (scope, el, projectPointsPercentaje, closedPointsPercentaje) ->
|
||||
html = template({
|
||||
projectPointsPercentaje: projectPointsPercentaje,
|
||||
closedPointsPercentaje:closedPointsPercentaje
|
||||
}))
|
||||
})
|
||||
html = $compile(html)(scope)
|
||||
el.html(html)
|
||||
|
||||
adjustPercentaje = (percentage) ->
|
||||
adjusted = _.max([0 , percentage])
|
||||
|
@ -1065,7 +1134,7 @@ TgBacklogProgressBarDirective = ($template) ->
|
|||
|
||||
$scope.$watch $attrs.tgBacklogProgressBar, (stats) ->
|
||||
if stats?
|
||||
totalPoints = stats.total_points
|
||||
totalPoints = if stats.total_points then stats.total_points else stats.defined_points
|
||||
definedPoints = stats.defined_points
|
||||
closedPoints = stats.closed_points
|
||||
if definedPoints > totalPoints
|
||||
|
@ -1077,11 +1146,11 @@ TgBacklogProgressBarDirective = ($template) ->
|
|||
|
||||
projectPointsPercentaje = adjustPercentaje(projectPointsPercentaje - 3)
|
||||
closedPointsPercentaje = adjustPercentaje(closedPointsPercentaje - 3)
|
||||
render($el, projectPointsPercentaje, closedPointsPercentaje)
|
||||
render($scope, $el, projectPointsPercentaje, closedPointsPercentaje)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgBacklogProgressBar", ["$tgTemplate", TgBacklogProgressBarDirective])
|
||||
module.directive("tgBacklogProgressBar", ["$tgTemplate", "$compile", TgBacklogProgressBarDirective])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -144,6 +144,7 @@ BacklogEmptySortableDirective = ($repo, $rs, $rootscope) ->
|
|||
# If the user has not enough permissions we don't enable the sortable
|
||||
if project.my_permissions.indexOf("modify_us") > -1
|
||||
$el.sortable({
|
||||
items: ".us-item-row",
|
||||
dropOnEmpty: true
|
||||
})
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -35,12 +35,6 @@ BacklogSprintDirective = ($repo, $rootscope) ->
|
|||
easing: 'linear'
|
||||
}
|
||||
|
||||
refreshSprintTableHeight = (sprintTable) =>
|
||||
if !sprintTable.find(".row").length
|
||||
sprintTable.css("height", sprintTableMinHeight)
|
||||
else
|
||||
sprintTable.css("height", "auto")
|
||||
|
||||
toggleSprint = ($el) =>
|
||||
sprintTable = $el.find(".sprint-table")
|
||||
sprintArrow = $el.find(".icon-arrow-up")
|
||||
|
@ -48,8 +42,6 @@ BacklogSprintDirective = ($repo, $rootscope) ->
|
|||
sprintArrow.toggleClass('active')
|
||||
sprintTable.toggleClass('open')
|
||||
|
||||
refreshSprintTableHeight(sprintTable)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$scope.$watch $attrs.tgBacklogSprint, (sprint) ->
|
||||
sprint = $scope.$eval($attrs.tgBacklogSprint)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -71,16 +71,39 @@ NavigationUrlsDirective = ($navurls, $auth, $q, $location) ->
|
|||
parseNav = (data, $scope) ->
|
||||
[name, params] = _.map(data.split(":"), trim)
|
||||
if params
|
||||
params = _.map(params.split(","), trim)
|
||||
# split by 'xxx='
|
||||
# example
|
||||
# project=vm.timeline.getIn(['data', 'project', 'slug']), ref=vm.timeline.getIn(['obj', 'ref'])
|
||||
# ["", "project", "vm.timeline.getIn(['data', 'project', 'slug']), ", "ref", "vm.timeline.getIn(['obj', 'ref'])"]
|
||||
result = params.split(/(\w+)=/)
|
||||
|
||||
# remove empty string
|
||||
result = _.filter result, (str) -> return str.length
|
||||
|
||||
# remove , at the end of the string
|
||||
result = _.map result, (str) -> return trim(str.replace(/,$/g, ''))
|
||||
|
||||
params = []
|
||||
index = 0
|
||||
|
||||
# ['param1', 'value'] => [{'param1': 'value'}]
|
||||
while index < result.length
|
||||
obj = {}
|
||||
obj[result[index]] = result[index + 1]
|
||||
params.push obj
|
||||
index = index + 2
|
||||
else
|
||||
params = []
|
||||
values = _.map(params, (x) -> trim(x.split("=")[1]))
|
||||
|
||||
values = _.map params, (param) -> _.values(param)[0]
|
||||
promises = _.map(values, (x) -> bindOnceP($scope, x))
|
||||
|
||||
return $q.all(promises).then ->
|
||||
options = {}
|
||||
for item in params
|
||||
[key, value] = _.map(item.split("="), trim)
|
||||
for param in params
|
||||
key = Object.keys(param)[0]
|
||||
value = param[key]
|
||||
|
||||
options[key] = $scope.$eval(value)
|
||||
return [name, options]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -140,7 +140,7 @@ class RepositoryService extends taiga.Service
|
|||
|
||||
return defered.promise
|
||||
|
||||
queryMany: (name, params, options={}) ->
|
||||
queryMany: (name, params, options={}, headers=false) ->
|
||||
url = @urls.resolve(name)
|
||||
httpOptions = {headers: {}}
|
||||
|
||||
|
@ -148,7 +148,12 @@ class RepositoryService extends taiga.Service
|
|||
httpOptions.headers["x-disable-pagination"] = "1"
|
||||
|
||||
return @http.get(url, params, httpOptions).then (data) =>
|
||||
return _.map(data.data, (x) => @model.make_model(name, x))
|
||||
result = _.map(data.data, (x) => @model.make_model(name, x))
|
||||
|
||||
if headers
|
||||
return [result, data.headers]
|
||||
|
||||
return result
|
||||
|
||||
queryOneAttribute: (name, id, attribute, params, options={}) ->
|
||||
url = @urls.resolve(name, id)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -23,6 +23,57 @@ taiga = @.taiga
|
|||
|
||||
module = angular.module("taigaCommon", [])
|
||||
|
||||
#############################################################################
|
||||
## Default datepicker config
|
||||
#############################################################################
|
||||
DataPickerConfig = ($translate) ->
|
||||
return {
|
||||
get: () ->
|
||||
return {
|
||||
i18n: {
|
||||
previousMonth: $translate.instant("COMMON.PICKERDATE.PREV_MONTH"),
|
||||
nextMonth: $translate.instant("COMMON.PICKERDATE.NEXT_MONTH"),
|
||||
months: [
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.JAN"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.FEB"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.MAR"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.APR"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.MAY"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.JUN"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.JUL"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.AUG"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.SEP"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.OCT"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.NOV"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.DEC")
|
||||
],
|
||||
weekdays: [
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.SUN"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.MON"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.TUE"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.WED"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.THU"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.FRI"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.SAT")
|
||||
],
|
||||
weekdaysShort: [
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.SUN"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.MON"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.TUE"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.WED"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.THU"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.FRI"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.SAT")
|
||||
]
|
||||
},
|
||||
isRTL: $translate.instant("COMMON.PICKERDATE.IS_RTL") == "true",
|
||||
firstDay: parseInt($translate.instant("COMMON.PICKERDATE.FIRST_DAY_OF_WEEK"), 10),
|
||||
format: $translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
}
|
||||
}
|
||||
|
||||
module.factory("tgDatePickerConfigService", ["$translate", DataPickerConfig])
|
||||
|
||||
#############################################################################
|
||||
## Get the selected text
|
||||
#############################################################################
|
||||
|
@ -229,3 +280,46 @@ Template = ($templateCache) ->
|
|||
}
|
||||
|
||||
module.factory("$tgTemplate", ["$templateCache", Template])
|
||||
|
||||
#############################################################################
|
||||
## Permission directive, hide elements when necessary
|
||||
#############################################################################
|
||||
|
||||
Capslock = ($translate) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
open = false
|
||||
|
||||
warningIcon = $('<div>')
|
||||
.addClass('icon')
|
||||
.addClass('icon-capslock')
|
||||
.attr('title', $translate.instant('COMMON.CAPSLOCK_WARNING'))
|
||||
|
||||
hideIcon = () ->
|
||||
warningIcon.fadeOut () ->
|
||||
open = false
|
||||
|
||||
$(this).remove()
|
||||
|
||||
showIcon = (e) ->
|
||||
return if open
|
||||
element = e.currentTarget
|
||||
$(element).parent().append(warningIcon)
|
||||
$('.icon-capslock').fadeIn()
|
||||
|
||||
open = true
|
||||
|
||||
$el.on 'blur', (e) ->
|
||||
hideIcon()
|
||||
|
||||
$el.on 'keyup.capslock, focus', (e) ->
|
||||
if $el.val() == $el.val().toLowerCase()
|
||||
hideIcon(e)
|
||||
else
|
||||
showIcon(e)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off('.capslock')
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgCapslock", ["$translate", Capslock])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -136,16 +136,16 @@ class AttachmentsController extends taiga.Controller
|
|||
title = @translate.instant("ATTACHMENT.TITLE_LIGHTBOX_DELETE_ATTACHMENT")
|
||||
message = @translate.instant("ATTACHMENT.MSG_LIGHTBOX_DELETE_ATTACHMENT", {fileName: attachment.name})
|
||||
|
||||
return @confirm.askOnDelete(title, message).then (finish) =>
|
||||
return @confirm.askOnDelete(title, message).then (askResponse) =>
|
||||
onSuccess = =>
|
||||
finish()
|
||||
askResponse.finish()
|
||||
index = @.attachments.indexOf(attachment)
|
||||
@.attachments.splice(index, 1)
|
||||
@.updateCounters()
|
||||
@rootscope.$broadcast("attachment:delete")
|
||||
|
||||
onError = =>
|
||||
finish(false)
|
||||
askResponse.finish(false)
|
||||
message = @translate.instant("ATTACHMENT.ERROR_DELETE_ATTACHMENT", {errorMessage: message})
|
||||
@confirm.notify("error", null, message)
|
||||
return @q.reject()
|
||||
|
@ -197,6 +197,7 @@ AttachmentsDirective = ($config, $confirm, $templates, $translate) ->
|
|||
|
||||
$el.on "change", ".attachments-header input", (event) ->
|
||||
files = _.toArray(event.target.files)
|
||||
|
||||
return if files.length < 1
|
||||
|
||||
$scope.$apply ->
|
||||
|
@ -245,7 +246,7 @@ AttachmentsDirective = ($config, $confirm, $templates, $translate) ->
|
|||
module.directive("tgAttachments", ["$tgConfig", "$tgConfirm", "$tgTemplate", "$translate", AttachmentsDirective])
|
||||
|
||||
|
||||
AttachmentDirective = ($template, $compile, $translate) ->
|
||||
AttachmentDirective = ($template, $compile, $translate, $rootScope) ->
|
||||
template = $template.get("attachment/attachment.html", true)
|
||||
templateEdit = $template.get("attachment/attachment-edit.html", true)
|
||||
|
||||
|
@ -283,6 +284,7 @@ AttachmentDirective = ($template, $compile, $translate) ->
|
|||
saveAttachment = ->
|
||||
attachment.description = $el.find("input[name='description']").val()
|
||||
attachment.is_deprecated = $el.find("input[name='is-deprecated']").prop("checked")
|
||||
attachment.isCreatedRightNow = false
|
||||
|
||||
$scope.$apply ->
|
||||
$ctrl.updateAttachment(attachment).then ->
|
||||
|
@ -297,7 +299,7 @@ AttachmentDirective = ($template, $compile, $translate) ->
|
|||
if event.keyCode == 13
|
||||
saveAttachment()
|
||||
else if event.keyCode == 27
|
||||
render(attachment, false)
|
||||
$scope.$apply -> render(attachment, false)
|
||||
|
||||
$el.on "click", "a.editable-settings.icon-delete", (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -314,6 +316,12 @@ AttachmentDirective = ($template, $compile, $translate) ->
|
|||
$scope.$apply ->
|
||||
$ctrl.removeAttachment(attachment)
|
||||
|
||||
$el.on "click", "div.attachment-name a", (event) ->
|
||||
if null != attachment.name.match(/\.(jpe?g|png|gif|gifv|webm)/i)
|
||||
event.preventDefault()
|
||||
$scope.$apply ->
|
||||
$rootScope.$broadcast("attachment:preview", attachment)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
@ -329,4 +337,4 @@ AttachmentDirective = ($template, $compile, $translate) ->
|
|||
restrict: "AE"
|
||||
}
|
||||
|
||||
module.directive("tgAttachment", ["$tgTemplate", "$compile", "$translate", AttachmentDirective])
|
||||
module.directive("tgAttachment", ["$tgTemplate", "$compile", "$translate", "$rootScope", AttachmentDirective])
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
###
|
||||
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: bind-scope.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaCommon")
|
||||
|
||||
BindScope = (config) ->
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
###
|
||||
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: compile-html.directive.coffee
|
||||
###
|
||||
|
||||
CompileHtmlDirective = ($compile) ->
|
||||
link = (scope, element, attrs) ->
|
||||
scope.$watch attrs.tgCompileHtml, (newValue, oldValue) ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -52,52 +52,23 @@ module.directive("tgDateRange", ["$translate", DateRangeDirective])
|
|||
## Date Selector Directive (using pikaday)
|
||||
#############################################################################
|
||||
|
||||
DateSelectorDirective = ($rootscope, $translate) ->
|
||||
DateSelectorDirective = ($rootscope, datePickerConfigService) ->
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
selectedDate = null
|
||||
|
||||
initialize = () ->
|
||||
$el.picker = new Pikaday({
|
||||
datePickerConfig = datePickerConfigService.get()
|
||||
|
||||
_.merge(datePickerConfig, {
|
||||
field: $el[0]
|
||||
onSelect: (date) =>
|
||||
selectedDate = date
|
||||
onOpen: =>
|
||||
$el.picker.setDate(selectedDate) if selectedDate?
|
||||
i18n: {
|
||||
previousMonth: $translate.instant("COMMON.PICKERDATE.PREV_MONTH"),
|
||||
nextMonth: $translate.instant("COMMON.PICKERDATE.NEXT_MONTH"),
|
||||
months: [$translate.instant("COMMON.PICKERDATE.MONTHS.JAN"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.FEB"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.MAR"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.APR"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.MAY"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.JUN"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.JUL"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.AUG"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.SEP"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.OCT"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.NOV"),
|
||||
$translate.instant("COMMON.PICKERDATE.MONTHS.DEC")],
|
||||
weekdays: [$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.SUN"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.MON"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.TUE"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.WED"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.THU"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.FRI"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.SAT")],
|
||||
weekdaysShort: [$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.SUN"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.MON"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.TUE"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.WED"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.THU"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.FRI"),
|
||||
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.SAT")]
|
||||
},
|
||||
isRTL: $translate.instant("COMMON.PICKERDATE.IS_RTL") == "true",
|
||||
firstDay: parseInt($translate.instant("COMMON.PICKERDATE.FIRST_DAY_OF_WEEK"), 10),
|
||||
format: $translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
})
|
||||
|
||||
$el.picker = new Pikaday(datePickerConfig)
|
||||
|
||||
unbind = $rootscope.$on "$translateChangeEnd", (ctx) => initialize()
|
||||
|
||||
$scope.$watch $attrs.ngModel, (val) ->
|
||||
|
@ -113,7 +84,7 @@ DateSelectorDirective = ($rootscope, $translate) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgDateSelector", ["$rootScope", "$translate", DateSelectorDirective])
|
||||
module.directive("tgDateSelector", ["$rootScope", "tgDatePickerConfigService", DateSelectorDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -152,7 +123,7 @@ module.directive("tgSprintProgressbar", SprintProgressBarDirective)
|
|||
## Created-by display directive
|
||||
#############################################################################
|
||||
|
||||
CreatedByDisplayDirective = ($template, $compile, $translate)->
|
||||
CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)->
|
||||
# Display the owner information (full name and photo) and the date of
|
||||
# creation of an object (like USs, tasks and issues).
|
||||
#
|
||||
|
@ -168,13 +139,14 @@ CreatedByDisplayDirective = ($template, $compile, $translate)->
|
|||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
render = (model) ->
|
||||
owner = $scope.usersById?[model.owner] or {
|
||||
owner = model.owner_extra_info or {
|
||||
full_name_display: $translate.instant("COMMON.EXTERNAL_USER")
|
||||
photo: "/images/unnamed.png"
|
||||
photo: "/images/user-noimage.png"
|
||||
}
|
||||
|
||||
html = template({
|
||||
owner: owner
|
||||
url: if owner?.is_active then $navUrls.resolve("user-profile", {username: owner.username}) else ""
|
||||
date: moment(model.created_date).format($translate.instant("COMMON.DATETIME"))
|
||||
})
|
||||
|
||||
|
@ -194,7 +166,8 @@ CreatedByDisplayDirective = ($template, $compile, $translate)->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", CreatedByDisplayDirective])
|
||||
module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls",
|
||||
CreatedByDisplayDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -250,11 +223,7 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile,
|
|||
html = $compile(template(ctx))($scope)
|
||||
$el.html(html)
|
||||
|
||||
if isEditable() and watchers.length == 0
|
||||
$el.find(".title").text("Add watchers")
|
||||
$el.find(".watchers-header").addClass("no-watchers")
|
||||
|
||||
$el.on "click", ".icon-delete", (event) ->
|
||||
$el.on "click", ".js-delete-watcher", (event) ->
|
||||
event.preventDefault()
|
||||
return if not isEditable()
|
||||
target = angular.element(event.currentTarget)
|
||||
|
@ -263,15 +232,15 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile,
|
|||
title = $translate.instant("COMMON.WATCHERS.TITLE_LIGHTBOX_DELETE_WARTCHER")
|
||||
message = $scope.usersById[watcherId].full_name_display
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) =>
|
||||
finish()
|
||||
$confirm.askOnDelete(title, message).then (askResponse) =>
|
||||
askResponse.finish()
|
||||
|
||||
watcherIds = _.clone($model.$modelValue.watchers, false)
|
||||
watcherIds = _.pull(watcherIds, watcherId)
|
||||
|
||||
deleteWatcher(watcherIds)
|
||||
|
||||
$el.on "click", ".add-watcher", (event) ->
|
||||
$el.on "click", ".js-add-watcher", (event) ->
|
||||
event.preventDefault()
|
||||
return if not isEditable()
|
||||
$scope.$apply ->
|
||||
|
@ -353,8 +322,8 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template
|
|||
return if not isEditable()
|
||||
title = $translate.instant("COMMON.ASSIGNED_TO.CONFIRM_UNASSIGNED")
|
||||
|
||||
$confirm.ask(title).then (finish) =>
|
||||
finish()
|
||||
$confirm.ask(title).then (response) =>
|
||||
response.finish()
|
||||
$model.$modelValue.assigned_to = null
|
||||
save(null)
|
||||
|
||||
|
@ -443,18 +412,18 @@ DeleteButtonDirective = ($log, $repo, $confirm, $location, $template) ->
|
|||
if not $attrs.onDeleteTitle
|
||||
return $log.error "DeleteButtonDirective requires on-delete-title set in scope."
|
||||
|
||||
$el.on "click", ".button", (event) ->
|
||||
$el.on "click", ".button-delete", (event) ->
|
||||
title = $attrs.onDeleteTitle
|
||||
subtitle = $model.$modelValue.subject
|
||||
|
||||
$confirm.askOnDelete(title, subtitle).then (finish) =>
|
||||
$confirm.askOnDelete(title, subtitle).then (askResponse) =>
|
||||
promise = $repo.remove($model.$modelValue)
|
||||
promise.then =>
|
||||
finish()
|
||||
askResponse.finish()
|
||||
url = $scope.$eval($attrs.onDeleteGoToUrl)
|
||||
$location.path(url)
|
||||
promise.then null, =>
|
||||
finish(false)
|
||||
askResponse.finish(false)
|
||||
$confirm.notify("error")
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
|
@ -554,7 +523,7 @@ module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$
|
|||
|
||||
|
||||
#############################################################################
|
||||
## Editable subject directive
|
||||
## Editable description directive
|
||||
#############################################################################
|
||||
|
||||
EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading, $selectedText, $qqueue, $template) ->
|
||||
|
@ -603,6 +572,13 @@ EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading,
|
|||
$el.find('.view-description').hide()
|
||||
$el.find('textarea').focus()
|
||||
|
||||
$el.on "click", "a", (event) ->
|
||||
target = angular.element(event.target)
|
||||
href = target.attr('href')
|
||||
if href.indexOf("#") == 0
|
||||
event.preventDefault()
|
||||
$('body').scrollTop($(href).offset().top)
|
||||
|
||||
$el.on "click", ".save", (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
|
@ -673,14 +649,14 @@ ListItemAssignedtoDirective = ($template) ->
|
|||
template = $template.get("common/components/list-item-assigned-to-avatar.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
bindOnce $scope, "membersById", (membersById) ->
|
||||
bindOnce $scope, "usersById", (usersById) ->
|
||||
item = $scope.$eval($attrs.tgListitemAssignedto)
|
||||
ctx = {name: "Unassigned", imgurl: "/images/unnamed.png"}
|
||||
|
||||
member = membersById[item.assigned_to]
|
||||
member = usersById[item.assigned_to]
|
||||
if member
|
||||
ctx.imgurl = member.photo
|
||||
ctx.name = member.full_name
|
||||
ctx.name = member.full_name_display
|
||||
|
||||
$el.html(template(ctx))
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -67,11 +67,12 @@ class ConfirmService extends taiga.Service
|
|||
currentLoading = @loading()
|
||||
.target(target)
|
||||
.start()
|
||||
|
||||
defered.resolve (ok=true) =>
|
||||
currentLoading.finish()
|
||||
if ok
|
||||
@.hide(el)
|
||||
defered.resolve {
|
||||
finish: (ok=true) =>
|
||||
currentLoading.finish()
|
||||
if ok
|
||||
@.hide(el)
|
||||
}
|
||||
|
||||
el.on "click.confirm-dialog", "a.button-red", (event) =>
|
||||
event.preventDefault()
|
||||
|
@ -118,9 +119,10 @@ class ConfirmService extends taiga.Service
|
|||
.start()
|
||||
defered.resolve {
|
||||
selected: choicesField.val()
|
||||
finish: =>
|
||||
finish: (ok=true) =>
|
||||
currentLoading.finish()
|
||||
@.hide(el)
|
||||
if ok
|
||||
@.hide(el)
|
||||
}
|
||||
|
||||
el.on "click.confirm-dialog", "a.button-red", (event) =>
|
||||
|
@ -245,7 +247,7 @@ class ConfirmService extends taiga.Service
|
|||
|
||||
delete @.tsem
|
||||
|
||||
el.on "click", ".icon-delete", (event) =>
|
||||
el.on "click", ".icon-delete, .close", (event) =>
|
||||
body.find(selector)
|
||||
.removeClass('active')
|
||||
.addClass('inactive')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -27,6 +27,28 @@ generateHash = taiga.generateHash
|
|||
|
||||
module = angular.module("taigaCommon")
|
||||
|
||||
# Custom attributes types (see taiga-back/taiga/projects/custom_attributes/choices.py)
|
||||
TEXT_TYPE = "text"
|
||||
MULTILINE_TYPE = "multiline"
|
||||
DATE_TYPE = "date"
|
||||
|
||||
|
||||
TYPE_CHOICES = [
|
||||
{
|
||||
key: TEXT_TYPE,
|
||||
name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_TEXT"
|
||||
},
|
||||
{
|
||||
key: MULTILINE_TYPE,
|
||||
name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_MULTI"
|
||||
},
|
||||
{
|
||||
key: DATE_TYPE,
|
||||
name: "ADMIN.CUSTOM_FIELDS.FIELD_TYPE_DATE"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
class CustomAttributesValuesController extends taiga.Controller
|
||||
@.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q"]
|
||||
|
@ -118,85 +140,110 @@ CustomAttributesValuesDirective = ($templates, $storage) ->
|
|||
template: templateFn
|
||||
}
|
||||
|
||||
module.directive("tgCustomAttributesValues", ["$tgTemplate", "$tgStorage", CustomAttributesValuesDirective])
|
||||
module.directive("tgCustomAttributesValues", ["$tgTemplate", "$tgStorage", "$translate",
|
||||
CustomAttributesValuesDirective])
|
||||
|
||||
|
||||
CustomAttributeValueDirective = ($template, $selectedText, $compile) ->
|
||||
CustomAttributeValueDirective = ($template, $selectedText, $compile, $translate, datePickerConfigService) ->
|
||||
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) ->
|
||||
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
|
||||
render = (attributeValue, edit=false) ->
|
||||
value = attributeValue.value
|
||||
if attributeValue.type is DATE_TYPE and attributeValue.value
|
||||
value = moment(attributeValue.value, "YYYY-MM-DD").format(prettyDate)
|
||||
else
|
||||
value = attributeValue.value
|
||||
editable = isEditable()
|
||||
|
||||
ctx = {
|
||||
id: attributeValue.id
|
||||
name: attributeValue.name
|
||||
description: attributeValue.description
|
||||
value: value
|
||||
isEditable: editable
|
||||
type: attributeValue.type
|
||||
}
|
||||
|
||||
if editable and (edit or not value)
|
||||
html = templateEdit(ctx)
|
||||
html = $compile(html)($scope)
|
||||
$el.html(html)
|
||||
|
||||
if attributeValue.type == DATE_TYPE
|
||||
datePickerConfig = datePickerConfigService.get()
|
||||
_.merge(datePickerConfig, {
|
||||
field: $el.find("input[name=value]")[0]
|
||||
onSelect: (date) =>
|
||||
selectedDate = date
|
||||
onOpen: =>
|
||||
$el.picker.setDate(selectedDate) if selectedDate?
|
||||
})
|
||||
$el.picker = new Pikaday(datePickerConfig)
|
||||
else
|
||||
html = template(ctx)
|
||||
html = $compile(html)($scope)
|
||||
|
||||
$el.html(html)
|
||||
$el.html(html)
|
||||
|
||||
isEditable = ->
|
||||
permissions = $scope.project.my_permissions
|
||||
requiredEditionPerm = $attrs.requiredEditionPerm
|
||||
return permissions.indexOf(requiredEditionPerm) > -1
|
||||
|
||||
saveAttributeValue = ->
|
||||
attributeValue.value = $el.find("input").val()
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
attributeValue.value = $el.find("input[name=value], textarea[name='value']").val()
|
||||
if attributeValue.type is DATE_TYPE
|
||||
if moment(attributeValue.value, prettyDate).isValid()
|
||||
attributeValue.value = moment(attributeValue.value, prettyDate).format("YYYY-MM-DD")
|
||||
else
|
||||
attributeValue.value = ""
|
||||
|
||||
$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()
|
||||
$scope.$apply()
|
||||
|
||||
$el.on "click", "a.icon-edit", (event) ->
|
||||
event.preventDefault()
|
||||
render(attributeValue, true)
|
||||
$el.find("input[name='description']").focus().select()
|
||||
$scope.$apply()
|
||||
|
||||
## 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()
|
||||
setFocusAndSelectOnInputField = ->
|
||||
$el.find("input[name='value'], textarea[name='value']").focus().select()
|
||||
|
||||
# Bootstrap
|
||||
attributeValue = $scope.$eval($attrs.tgCustomAttributeValue)
|
||||
render(attributeValue)
|
||||
|
||||
## Actions (on view mode)
|
||||
$el.on "click", ".js-value-view-mode", ->
|
||||
return if not isEditable()
|
||||
return if $selectedText.get().length
|
||||
render(attributeValue, true)
|
||||
setFocusAndSelectOnInputField()
|
||||
|
||||
$el.on "click", "a.icon-edit", (event) ->
|
||||
event.preventDefault()
|
||||
render(attributeValue, true)
|
||||
setFocusAndSelectOnInputField()
|
||||
|
||||
## Actions (on edit mode)
|
||||
$el.on "keyup", "input[name=value], textarea[name='value']", (event) ->
|
||||
if event.keyCode is 13 and event.currentTarget.type isnt "textarea"
|
||||
submit(event)
|
||||
else if event.keyCode == 27
|
||||
render(attributeValue, false)
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
|
||||
$el.on "click", "a.icon-floppy", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {
|
||||
link: link
|
||||
require: "^tgCustomAttributesValues"
|
||||
restrict: "AE"
|
||||
}
|
||||
|
||||
module.directive("tgCustomAttributeValue", ["$tgTemplate", "$selectedText", "$compile", CustomAttributeValueDirective])
|
||||
module.directive("tgCustomAttributeValue", ["$tgTemplate", "$selectedText", "$compile", "$translate",
|
||||
"tgDatePickerConfigService", CustomAttributeValueDirective])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -226,6 +226,10 @@ EstimationsService = ($template, $qqueue, $repo, $confirm, $q) ->
|
|||
|
||||
@$el.find(".pop-points-open").show()
|
||||
|
||||
pop = @$el.find(".pop-points-open")
|
||||
if pop.offset().top + pop.height() > document.body.clientHeight
|
||||
pop.addClass('pop-bottom')
|
||||
|
||||
create = ($el, us, project) ->
|
||||
$el.unbind("click")
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -26,6 +26,14 @@ debounce = @.taiga.debounce
|
|||
|
||||
module = angular.module("taigaCommon")
|
||||
|
||||
IGNORED_FIELDS = {
|
||||
"userstories.userstory": [
|
||||
"watchers", "kanban_order", "backlog_order", "sprint_order", "finish_date"
|
||||
]
|
||||
"tasks.task": [
|
||||
"watchers", "us_order", "taskboard_order"
|
||||
]
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
## History Directive (Main)
|
||||
|
@ -68,7 +76,7 @@ class HistoryController extends taiga.Controller
|
|||
return @rs.history.undeleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
|
||||
|
||||
|
||||
HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $compile) ->
|
||||
HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $compile, $navUrls, $rootScope) ->
|
||||
templateChangeDiff = $template.get("common/history/history-change-diff.html", true)
|
||||
templateChangePoints = $template.get("common/history/history-change-points.html", true)
|
||||
templateChangeGeneric = $template.get("common/history/history-change-generic.html", true)
|
||||
|
@ -136,15 +144,6 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
|
||||
return humanizedFieldNames[field] or field
|
||||
|
||||
getUserFullName = (userId) ->
|
||||
return $scope.usersById[userId]?.full_name_display
|
||||
|
||||
getUserAvatar = (userId) ->
|
||||
if $scope.usersById[userId]?
|
||||
return $scope.usersById[userId].photo
|
||||
else
|
||||
return "/images/unnamed.png"
|
||||
|
||||
countChanges = (comment) ->
|
||||
return _.keys(comment.values_diff).length
|
||||
|
||||
|
@ -263,6 +262,10 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
return templateChangeGeneric({name:name, from:from, to: to})
|
||||
|
||||
renderChangeEntries = (change) ->
|
||||
changeModel = change.key.split(":")[0]
|
||||
if IGNORED_FIELDS[changeModel]?
|
||||
change.values_diff = _.removeKeys(change.values_diff, IGNORED_FIELDS[changeModel])
|
||||
|
||||
return _.map(change.values_diff, (value, field) -> renderChangeEntry(field, value))
|
||||
|
||||
renderChangesHelperText = (change) ->
|
||||
|
@ -286,8 +289,9 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
return html[0].outerHTML
|
||||
|
||||
html = templateActivity({
|
||||
avatar: getUserAvatar(comment.user.pk)
|
||||
avatar: comment.user.photo
|
||||
userFullName: comment.user.name
|
||||
userProfileUrl: if comment.user.is_active then $navUrls.resolve("user-profile", {username: comment.user.username}) else ""
|
||||
creationDate: moment(comment.created_at).format(getPrettyDateFormat())
|
||||
comment: comment.comment_html
|
||||
changesText: renderChangesHelperText(comment)
|
||||
|
@ -305,8 +309,9 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
|
||||
renderChange = (change) ->
|
||||
return templateActivity({
|
||||
avatar: getUserAvatar(change.user.pk)
|
||||
avatar: change.user.photo
|
||||
userFullName: change.user.name
|
||||
userProfileUrl: if change.user.is_active then $navUrls.resolve("user-profile", {username: change.user.username}) else ""
|
||||
creationDate: moment(change.created_at).format(getPrettyDateFormat())
|
||||
comment: change.comment_html
|
||||
changes: renderChangeEntries(change)
|
||||
|
@ -359,6 +364,8 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
.start()
|
||||
|
||||
onSuccess = ->
|
||||
$rootScope.$broadcast("comment:new")
|
||||
|
||||
$ctrl.loadHistory(type, objectId).finally ->
|
||||
currentLoading.finish()
|
||||
|
||||
|
@ -385,6 +392,13 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
target = angular.element(event.currentTarget)
|
||||
save(target)
|
||||
|
||||
$el.on "click", "a", (event) ->
|
||||
target = angular.element(event.target)
|
||||
href = target.attr('href')
|
||||
if href && href.indexOf("#") == 0
|
||||
event.preventDefault()
|
||||
$('body').scrollTop($(href).offset().top)
|
||||
|
||||
$el.on "click", ".show-more", (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
|
@ -419,8 +433,13 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
$(this).addClass('active')
|
||||
|
||||
$el.on "click", ".history-tabs li a", (event) ->
|
||||
$el.find(".history-tabs li a").toggleClass("active")
|
||||
$el.find(".history section").toggleClass("hidden")
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
$el.find(".history-tabs li a").removeClass("active")
|
||||
target.addClass("active")
|
||||
|
||||
$el.find(".history section").addClass("hidden")
|
||||
$el.find(".history section.#{target.data('section-class')}").removeClass("hidden")
|
||||
|
||||
$el.on "click", ".comment-delete", debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -454,4 +473,4 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c
|
|||
|
||||
|
||||
module.directive("tgHistory", ["$log", "$tgLoading", "$tgQqueue", "$tgTemplate", "$tgConfirm", "$translate",
|
||||
"$compile", HistoryDirective])
|
||||
"$compile", "$tgNavUrls", "$rootScope", HistoryDirective])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -39,13 +39,14 @@ class LightboxService extends taiga.Service
|
|||
lightboxContent = $el.children().not(".close")
|
||||
lightboxContent.hide()
|
||||
|
||||
$el.css('display', 'flex')
|
||||
@animationFrame.add ->
|
||||
$el.css('display', 'flex')
|
||||
|
||||
@animationFrame.add =>
|
||||
@animationFrame.add ->
|
||||
$el.addClass("open")
|
||||
|
||||
@animationFrame.add ->
|
||||
$el.find('input,textarea').first().focus()
|
||||
@animationFrame.add ->
|
||||
$el.find('input,textarea').first().focus()
|
||||
|
||||
@animationFrame.add =>
|
||||
lightboxContent.show()
|
||||
|
@ -475,10 +476,9 @@ AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationServic
|
|||
}
|
||||
|
||||
html = usersTemplate(ctx)
|
||||
|
||||
html = $compile(html)($scope)
|
||||
|
||||
$el.find("div.watchers").html(html)
|
||||
$el.find(".assigned-to-list").html(html)
|
||||
|
||||
closeLightbox = () ->
|
||||
lightboxKeyboardNavigationService.stop()
|
||||
|
@ -499,7 +499,7 @@ AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationServic
|
|||
render(selectedUser, searchingText)
|
||||
$el.find('input').focus()
|
||||
|
||||
$el.on "click", ".watcher-single", (event) ->
|
||||
$el.on "click", ".user-list-single", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
|
@ -543,7 +543,7 @@ module.directive("tgLbAssignedto", ["lightboxService", "lightboxKeyboardNavigati
|
|||
## Watchers Lightbox directive
|
||||
#############################################################################
|
||||
|
||||
WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationService, $template) ->
|
||||
WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationService, $template, $compile) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
selectedItem = null
|
||||
usersTemplate = $template.get("common/lightbox/lightbox-assigned-to-users.html", true)
|
||||
|
@ -572,7 +572,8 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS
|
|||
}
|
||||
|
||||
html = usersTemplate(ctx)
|
||||
$el.find("div.watchers").html(html)
|
||||
html = $compile(html)($scope)
|
||||
$el.find(".ticket-watchers").html(html)
|
||||
|
||||
closeLightbox = () ->
|
||||
lightboxKeyboardNavigationService.stop()
|
||||
|
@ -596,7 +597,7 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS
|
|||
render(users)
|
||||
$el.find("input").focus()
|
||||
|
||||
$el.on "click", ".watcher-single", debounce 2000, (event) ->
|
||||
$el.on "click", ".user-list-single", debounce 2000, (event) ->
|
||||
closeLightbox()
|
||||
|
||||
event.preventDefault()
|
||||
|
@ -622,4 +623,37 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS
|
|||
link:link
|
||||
}
|
||||
|
||||
module.directive("tgLbWatchers", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", WatchersLightboxDirective])
|
||||
module.directive("tgLbWatchers", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", WatchersLightboxDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Attachment Preview Lighbox
|
||||
#############################################################################
|
||||
|
||||
AttachmentPreviewLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationService, $template, $compile) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
template = $template.get("common/lightbox/lightbox-attachment-preview.html", true)
|
||||
|
||||
$scope.$on "attachment:preview", (event, attachment) ->
|
||||
lightboxService.open($el)
|
||||
render(attachment)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
render = (attachment) ->
|
||||
ctx = {
|
||||
url: attachment.url,
|
||||
title: attachment.description,
|
||||
name: attachment.name
|
||||
}
|
||||
|
||||
html = template(ctx)
|
||||
html = $compile(html)($scope)
|
||||
$el.html(html)
|
||||
|
||||
return {
|
||||
link: link
|
||||
}
|
||||
|
||||
module.directive("tgLbAttachmentPreview", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", AttachmentPreviewLightboxDirective])
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
|
||||
# Copyright (C) 2014 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
|
||||
# Copyright (C) 2014-2015 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -77,19 +77,11 @@ Loader = ($rootscope) ->
|
|||
lastResponseDate = 0
|
||||
|
||||
autoClose = () ->
|
||||
maxAuto = 5000
|
||||
timeoutAuto = setTimeout (() ->
|
||||
pageLoaded()
|
||||
|
||||
clearInterval(intervalAuto)
|
||||
), maxAuto
|
||||
|
||||
intervalAuto = setInterval (() ->
|
||||
if lastResponseDate && requestCount == 0
|
||||
pageLoaded()
|
||||
|
||||
clearInterval(intervalAuto)
|
||||
clearTimeout(timeoutAuto)
|
||||
), 50
|
||||
|
||||
start = () ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -21,19 +21,29 @@
|
|||
|
||||
module = angular.module("taigaCommon")
|
||||
|
||||
TgLoadingService = ->
|
||||
TgLoadingService = ($compile) ->
|
||||
spinner = "<img class='loading-spinner' src='/svg/spinner-circle.svg' alt='loading...' />"
|
||||
|
||||
return () ->
|
||||
service = {
|
||||
settings: {
|
||||
target: null,
|
||||
scope: null,
|
||||
classes: []
|
||||
timeout: 0
|
||||
timeout: 0,
|
||||
template: null
|
||||
},
|
||||
target: (target) ->
|
||||
service.settings.target = target
|
||||
|
||||
return service
|
||||
scope: (scope) ->
|
||||
service.settings.scope = scope
|
||||
|
||||
return service
|
||||
template: (template) ->
|
||||
service.settings.template = template
|
||||
|
||||
return service
|
||||
removeClasses: (classess...) ->
|
||||
service.settings.classes = classess
|
||||
|
@ -51,9 +61,11 @@ TgLoadingService = ->
|
|||
# The loader is shown after that quantity of milliseconds
|
||||
timeoutId = setTimeout (->
|
||||
if not target.hasClass('loading')
|
||||
service.settings.oldContent = target.html()
|
||||
if !service.settings.template
|
||||
service.settings.template = target.html()
|
||||
|
||||
target.addClass('loading')
|
||||
|
||||
target.html(spinner)
|
||||
), service.settings.timeout
|
||||
|
||||
|
@ -71,27 +83,37 @@ TgLoadingService = ->
|
|||
removeClasses = service.settings.classes
|
||||
removeClasses.map (className) -> service.settings.target.addClass(className)
|
||||
|
||||
target.html(service.settings.oldContent)
|
||||
target.html(service.settings.template)
|
||||
target.removeClass('loading')
|
||||
|
||||
if service.settings.scope
|
||||
$compile(target.contents())(service.settings.scope)
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
return service
|
||||
|
||||
TgLoadingService.$inject = [
|
||||
"$compile"
|
||||
]
|
||||
|
||||
module.factory("$tgLoading", TgLoadingService)
|
||||
|
||||
LoadingDirective = ($loading) ->
|
||||
link = ($scope, $el, attr) ->
|
||||
currentLoading = null
|
||||
template = $el.html()
|
||||
|
||||
$scope.$watch attr.tgLoading, (showLoading) =>
|
||||
|
||||
if showLoading
|
||||
currentLoading = $loading()
|
||||
.target($el)
|
||||
.timeout(50)
|
||||
.template(template)
|
||||
.scope($scope)
|
||||
.start()
|
||||
else
|
||||
else if currentLoading
|
||||
currentLoading.finish()
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -149,6 +149,8 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
|
|||
startIndex = result.index
|
||||
break
|
||||
|
||||
return if !result
|
||||
|
||||
regex = />>>/gi
|
||||
endIndex = 0
|
||||
loop
|
||||
|
@ -181,10 +183,19 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
|
|||
onShiftEnter: {keepDefault:false, openWith:"\n\n"}
|
||||
onEnter:
|
||||
keepDefault: false,
|
||||
replaceWith: () -> "\n"
|
||||
replaceWith: () ->
|
||||
# Allow textcomplete to intercept the enter key if the options list is displayed
|
||||
# @todo There doesn't seem to be a more graceful way to do this with the textcomplete API.
|
||||
if not $('.textcomplete-dropdown').is(':visible')
|
||||
"\n"
|
||||
afterInsert: (data) ->
|
||||
lines = data.textarea.value.split("\n")
|
||||
cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
|
||||
# Detect if we are in this situation +- aa at the beginning if the textarea
|
||||
if data.caretPosition > 0
|
||||
cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
|
||||
else
|
||||
cursorLine = 1
|
||||
|
||||
newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0]
|
||||
lastLine = lines[cursorLine - 1]
|
||||
|
||||
|
@ -342,6 +353,104 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
|
|||
element
|
||||
.markItUpRemove()
|
||||
.markItUp(markdownSettings)
|
||||
.textcomplete([
|
||||
# us, task, and issue autocomplete: #id or #<part of title>
|
||||
{
|
||||
cache: true
|
||||
match: /(^|\s)#([a-z0-9]+)$/i,
|
||||
search: (term, callback) ->
|
||||
term = taiga.slugify(term)
|
||||
|
||||
searchTypes = ['issues', 'tasks', 'userstories']
|
||||
searchProps = ['ref', 'subject']
|
||||
|
||||
filter = (item) =>
|
||||
for prop in searchProps
|
||||
if taiga.slugify(item[prop]).indexOf(term) >= 0
|
||||
return true
|
||||
return false
|
||||
|
||||
$rs.search.do($scope.projectId, term).then (res) =>
|
||||
# ignore wikipages if they're the only results. can't exclude them in search
|
||||
if res.count < 1 or res.count == res.wikipages.length
|
||||
callback([])
|
||||
|
||||
else
|
||||
for type in searchTypes
|
||||
if res[type] and res[type].length > 0
|
||||
callback(res[type].filter(filter), true)
|
||||
|
||||
# must signal end of lists
|
||||
callback([])
|
||||
|
||||
replace: (res) ->
|
||||
return "$1\##{res.ref} "
|
||||
|
||||
template: (res, term) ->
|
||||
return "\##{res.ref} - #{res.subject}"
|
||||
}
|
||||
|
||||
# username autocomplete: @username or @<part of name>
|
||||
{
|
||||
cache: true
|
||||
match: /(^|\s)@([a-z0-9\-\._]{2,})$/i
|
||||
search: (term, callback) ->
|
||||
username = taiga.slugify(term)
|
||||
searchProps = ['username', 'full_name', 'full_name_display']
|
||||
|
||||
if $scope.project.members.length < 1
|
||||
callback([])
|
||||
|
||||
else
|
||||
callback $scope.project.members.filter (user) =>
|
||||
for prop in searchProps
|
||||
if taiga.slugify(user[prop]).indexOf(username) >= 0
|
||||
return true
|
||||
return false
|
||||
|
||||
replace: (user) ->
|
||||
return "$1@#{user.username} "
|
||||
|
||||
template: (user) ->
|
||||
return "#{user.username} - #{user.full_name_display}"
|
||||
}
|
||||
|
||||
# wiki pages autocomplete: [[slug or [[<part of slug>
|
||||
# if the search function was called with the 3rd param the regex
|
||||
# like the docs claim, we could combine this with the #123 search
|
||||
{
|
||||
cache: true
|
||||
match: /(^|\s)\[\[([a-z0-9\-]+)$/i
|
||||
search: (term, callback) ->
|
||||
term = taiga.slugify(term)
|
||||
|
||||
$rs.search.do($scope.projectId, term).then (res) =>
|
||||
if res.count < 1
|
||||
callback([])
|
||||
|
||||
if res.count < 1 or not res.wikipages or res.wikipages.length <= 0
|
||||
callback([])
|
||||
|
||||
else
|
||||
callback res.wikipages.filter((page) =>
|
||||
return taiga.slugify(page['slug']).indexOf(term) >= 0
|
||||
), true
|
||||
|
||||
# must signal end of lists
|
||||
callback([])
|
||||
|
||||
|
||||
replace: (res) ->
|
||||
return "$1[[#{res.slug}]]"
|
||||
|
||||
template: (res, term) ->
|
||||
return res.slug
|
||||
}
|
||||
],
|
||||
{
|
||||
debounce: 200
|
||||
}
|
||||
)
|
||||
|
||||
renderMarkItUp()
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -41,9 +41,9 @@ class PageMixin
|
|||
@scope.usersById = groupBy(@scope.users, (e) -> e.id)
|
||||
|
||||
@scope.roles = _.sortBy(roles, "order")
|
||||
availableRoles = _(@scope.project.memberships).map("role").uniq().value()
|
||||
computableRoles = _(@scope.project.members).map("role").uniq().value()
|
||||
@scope.computableRoles = _(roles).filter("computable")
|
||||
.filter((x) -> _.contains(availableRoles, x.id))
|
||||
.filter((x) -> _.contains(computableRoles, x.id))
|
||||
.value()
|
||||
loadUsersAndRoles: ->
|
||||
promise = @q.all([
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -26,6 +26,7 @@ toString = @.taiga.toString
|
|||
joinStr = @.taiga.joinStr
|
||||
groupBy = @.taiga.groupBy
|
||||
bindOnce = @.taiga.bindOnce
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaIssues")
|
||||
|
||||
|
@ -52,6 +53,8 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||
@log, @appMetaService, @analytics, @navUrls, @translate) ->
|
||||
bindMethods(@)
|
||||
|
||||
@scope.issueRef = @params.issueref
|
||||
@scope.sectionName = @translate.instant("ISSUES.SECTION_NAME")
|
||||
@.initializeEventHandlers()
|
||||
|
@ -83,20 +86,16 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
|
||||
initializeEventHandlers: ->
|
||||
@scope.$on "attachment:create", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
@analytics.trackEvent("attachment", "create", "create attachment on issue", 1)
|
||||
|
||||
@scope.$on "attachment:edit", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
|
||||
@scope.$on "attachment:delete", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
|
||||
@scope.$on "promote-issue-to-us:success", =>
|
||||
@analytics.trackEvent("issue", "promoteToUserstory", "promote issue to userstory", 1)
|
||||
@rootscope.$broadcast("object:updated")
|
||||
@.loadIssue()
|
||||
|
||||
@scope.$on "comment:new", =>
|
||||
@.loadIssue()
|
||||
|
||||
@scope.$on "custom-attributes-values:edit", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
|
||||
|
@ -120,7 +119,6 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
@scope.severityById = groupBy(project.severities, (x) -> x.id)
|
||||
@scope.priorityList = project.priorities
|
||||
@scope.priorityById = groupBy(project.priorities, (x) -> x.id)
|
||||
@scope.membersById = groupBy(project.memberships, (x) -> x.user)
|
||||
return project
|
||||
|
||||
loadIssue: ->
|
||||
|
@ -146,9 +144,52 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
loadInitialData: ->
|
||||
promise = @.loadProject()
|
||||
return promise.then (project) =>
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
@.loadIssue()
|
||||
|
||||
###
|
||||
# Note: This methods (onUpvote() and onDownvote()) are related to tg-vote-button.
|
||||
# See app/modules/components/vote-button for more info
|
||||
###
|
||||
onUpvote: ->
|
||||
onSuccess = =>
|
||||
@.loadIssue()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.issues.upvote(@scope.issueId).then(onSuccess, onError)
|
||||
|
||||
onDownvote: ->
|
||||
onSuccess = =>
|
||||
@.loadIssue()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.issues.downvote(@scope.issueId).then(onSuccess, onError)
|
||||
|
||||
###
|
||||
# Note: This methods (onWatch() and onUnwatch()) are related to tg-watch-button.
|
||||
# See app/modules/components/watch-button for more info
|
||||
###
|
||||
onWatch: ->
|
||||
onSuccess = =>
|
||||
@.loadIssue()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.issues.watch(@scope.issueId).then(onSuccess, onError)
|
||||
|
||||
onUnwatch: ->
|
||||
onSuccess = =>
|
||||
@.loadIssue()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.issues.unwatch(@scope.issueId).then(onSuccess, onError)
|
||||
|
||||
module.controller("IssueDetailController", IssueDetailController)
|
||||
|
||||
|
@ -558,7 +599,7 @@ module.directive("tgIssuePriorityButton", ["$rootScope", "$tgRepo", "$tgConfirm"
|
|||
PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue, $translate) ->
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
|
||||
save = $qqueue.bindAdd (issue, finish) =>
|
||||
save = $qqueue.bindAdd (issue, askResponse) =>
|
||||
data = {
|
||||
generated_from_issue: issue.id
|
||||
project: issue.project,
|
||||
|
@ -570,12 +611,12 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue, $transl
|
|||
}
|
||||
|
||||
onSuccess = ->
|
||||
finish()
|
||||
askResponse.finish()
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("promote-issue-to-us:success")
|
||||
|
||||
onError = ->
|
||||
finish(false)
|
||||
askResponse.finish()
|
||||
$confirm.notify("error")
|
||||
|
||||
$repo.create("userstories", data).then(onSuccess, onError)
|
||||
|
@ -589,9 +630,8 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue, $transl
|
|||
message = $translate.instant("ISSUES.CONFIRM_PROMOTE.MESSAGE")
|
||||
subtitle = issue.subject
|
||||
|
||||
$confirm.ask(title, subtitle, message).then (finish) =>
|
||||
save(issue, finish)
|
||||
|
||||
$confirm.ask(title, subtitle, message).then (response) =>
|
||||
save(issue, response)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -83,8 +83,6 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@scope.$on "issueform:new:success", =>
|
||||
@analytics.trackEvent("issue", "create", "create issue on issues list", 1)
|
||||
@.loadIssues()
|
||||
@.loadFilters()
|
||||
|
||||
|
||||
initializeSubscription: ->
|
||||
routingKey = "changes.project.#{@scope.projectId}.issues"
|
||||
|
@ -112,13 +110,13 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@scope.issueTypes = _.sortBy(project.issue_types, "order")
|
||||
@scope.issueTypeById = groupBy(project.issue_types, (x) -> x.id)
|
||||
|
||||
@scope.membersById = groupBy(project.memberships, (x) -> x.user)
|
||||
return project
|
||||
|
||||
getUrlFilters: ->
|
||||
filters = _.pick(@location.search(), "page", "tags", "statuses", "types",
|
||||
filters = _.pick(@location.search(), "page", "tags", "status", "types",
|
||||
"q", "severities", "priorities",
|
||||
"assignedTo", "createdBy", "orderBy")
|
||||
|
||||
filters.page = 1 if not filters.page
|
||||
return filters
|
||||
|
||||
|
@ -181,20 +179,30 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@scope.filters.myFilters = myFilters
|
||||
return myFilters
|
||||
|
||||
loadFilters = {}
|
||||
loadFilters.project = @scope.projectId
|
||||
loadFilters.tags = urlfilters.tags
|
||||
loadFilters.status = urlfilters.status
|
||||
loadFilters.q = urlfilters.q
|
||||
loadFilters.types = urlfilters.types
|
||||
loadFilters.severities = urlfilters.severities
|
||||
loadFilters.priorities = urlfilters.priorities
|
||||
loadFilters.assigned_to = urlfilters.assignedTo
|
||||
loadFilters.owner = urlfilters.createdBy
|
||||
|
||||
# Load default filters data
|
||||
promise = promise.then =>
|
||||
return @rs.issues.filtersData(@scope.projectId)
|
||||
return @rs.issues.filtersData(loadFilters)
|
||||
|
||||
# Format filters and set them on scope
|
||||
return promise.then (data) =>
|
||||
usersFiltersFormat = (users, type, unknownOption) =>
|
||||
reformatedUsers = _.map users, (t) =>
|
||||
return {
|
||||
id: t[0],
|
||||
count: t[1],
|
||||
type: type
|
||||
name: if t[0] then @scope.usersById[t[0]].full_name_display else unknownOption
|
||||
}
|
||||
t.type = type
|
||||
t.name = if t.full_name then t.full_name else unknownOption
|
||||
|
||||
return t
|
||||
|
||||
unknownItem = _.remove(reformatedUsers, (u) -> not u.id)
|
||||
reformatedUsers = _.sortBy(reformatedUsers, (u) -> u.name.toUpperCase())
|
||||
if unknownItem.length > 0
|
||||
|
@ -203,34 +211,27 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
choicesFiltersFormat = (choices, type, byIdObject) =>
|
||||
_.map choices, (t) ->
|
||||
return {
|
||||
id: t[0],
|
||||
name: byIdObject[t[0]].name,
|
||||
color: byIdObject[t[0]].color,
|
||||
count: t[1],
|
||||
type: type}
|
||||
t.type = type
|
||||
return t
|
||||
|
||||
tagsFilterFormat = (tags) =>
|
||||
return _.map tags, (t) =>
|
||||
return {
|
||||
id: t[0],
|
||||
name: t[0],
|
||||
color: @scope.project.tags_colors[t[0]],
|
||||
count: t[1],
|
||||
type: "tags"
|
||||
}
|
||||
return _.map tags, (t) ->
|
||||
t.id = t.name
|
||||
t.type = 'tags'
|
||||
return t
|
||||
|
||||
# Build filters data structure
|
||||
@scope.filters.statuses = choicesFiltersFormat(data.statuses, "statuses", @scope.issueStatusById)
|
||||
@scope.filters.status = choicesFiltersFormat(data.statuses, "status", @scope.issueStatusById)
|
||||
@scope.filters.severities = choicesFiltersFormat(data.severities, "severities", @scope.severityById)
|
||||
@scope.filters.priorities = choicesFiltersFormat(data.priorities, "priorities", @scope.priorityById)
|
||||
@scope.filters.assignedTo = usersFiltersFormat(data.assigned_to, "assignedTo", "Unassigned")
|
||||
@scope.filters.createdBy = usersFiltersFormat(data.created_by, "createdBy", "Unknown")
|
||||
@scope.filters.createdBy = usersFiltersFormat(data.owners, "createdBy", "Unknown")
|
||||
@scope.filters.types = choicesFiltersFormat(data.types, "types", @scope.issueTypeById)
|
||||
@scope.filters.tags = tagsFilterFormat(data.tags)
|
||||
|
||||
@.removeNotExistingFiltersFromUrl()
|
||||
@.markSelectedFilters(@scope.filters, urlfilters)
|
||||
|
||||
@rootscope.$broadcast("filters:loaded", @scope.filters)
|
||||
|
||||
# We need to guarantee that the last petition done here is the finally used
|
||||
|
@ -258,7 +259,7 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
name = "assigned_to"
|
||||
else if name == "createdBy"
|
||||
name = "owner"
|
||||
else if name == "statuses"
|
||||
else if name == "status"
|
||||
name = "status"
|
||||
else if name == "types"
|
||||
name = "type"
|
||||
|
@ -273,14 +274,19 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@scope.page = data.current
|
||||
@scope.count = data.count
|
||||
@scope.paginatedBy = data.paginatedBy
|
||||
|
||||
return data
|
||||
|
||||
return promise
|
||||
|
||||
loadInitialData: ->
|
||||
promise = @.loadProject()
|
||||
return promise.then (project) =>
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
@.initializeSubscription()
|
||||
return @q.all([@.loadFilters(), @.loadIssues()])
|
||||
@.loadFilters()
|
||||
|
||||
return @.loadIssues()
|
||||
|
||||
saveCurrentFiltersTo: (newFilter) ->
|
||||
deferred = @q.defer()
|
||||
|
@ -401,7 +407,7 @@ IssuesDirective = ($log, $location, $template, $compile) ->
|
|||
# Draw the arrow the first time
|
||||
currentOrder = $ctrl.getUrlFilter("orderBy") or "created_date"
|
||||
if currentOrder
|
||||
icon = if startswith(currentOrder, "-") then "icon-caret-up" else "icon-caret-down"
|
||||
icon = if startswith(currentOrder, "-") then "icon-arrow-up" else "icon-arrow-bottom"
|
||||
colHeadElement = $el.find(".row.title > div[data-fieldname='#{trim(currentOrder, "-")}']")
|
||||
colHeadElement.html("#{colHeadElement.html()}<span class='icon #{icon}'></span>")
|
||||
|
||||
|
@ -419,7 +425,7 @@ IssuesDirective = ($log, $location, $template, $compile) ->
|
|||
$ctrl.loadIssues().then ->
|
||||
# Update the arrow
|
||||
$el.find(".row.title > div > span.icon").remove()
|
||||
icon = if startswith(finalOrder, "-") then "icon-caret-up" else "icon-caret-down"
|
||||
icon = if startswith(finalOrder, "-") then "icon-arrow-up" else "icon-arrow-bottom"
|
||||
target.html("#{target.html()}<span class='icon #{icon}'></span>")
|
||||
|
||||
## Issues Link
|
||||
|
@ -440,12 +446,13 @@ module.directive("tgIssues", ["$log", "$tgLocation", "$tgTemplate", "$compile",
|
|||
## Issues Filters Directive
|
||||
#############################################################################
|
||||
|
||||
IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $translate, $compile, $auth) ->
|
||||
IssuesFiltersDirective = ($q, $log, $location, $rs, $confirm, $loading, $template, $translate, $compile, $auth) ->
|
||||
template = $template.get("issue/issues-filters.html", true)
|
||||
templateSelected = $template.get("issue/issues-filters-selected.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$ctrl = $el.closest(".wrapper").controller()
|
||||
|
||||
selectedFilters = []
|
||||
|
||||
showFilters = (title, type) ->
|
||||
|
@ -491,6 +498,16 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
html = $compile(html)($scope)
|
||||
$el.find(".filter-list").html(html)
|
||||
|
||||
getFiltersType = () ->
|
||||
return $el.find("h2 a.subfilter span.title").prop('data-type')
|
||||
|
||||
reloadIssues = () ->
|
||||
currentFiltersType = getFiltersType()
|
||||
|
||||
$q.all([$ctrl.loadIssues(), $ctrl.loadFilters()]).then () ->
|
||||
filters = $scope.filters[currentFiltersType]
|
||||
renderFilters(_.reject(filters, "selected"))
|
||||
|
||||
toggleFilterSelection = (type, id) ->
|
||||
if type == "myFilters"
|
||||
$rs.issues.getMyFilters($scope.projectId).then (data) ->
|
||||
|
@ -507,7 +524,6 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
filters = $scope.filters[type]
|
||||
filterId = if type == 'tags' then taiga.toString(id) else id
|
||||
filter = _.find(filters, {id: filterId})
|
||||
|
||||
filter.selected = (not filter.selected)
|
||||
|
||||
# Convert id to null as string for properly
|
||||
|
@ -516,22 +532,23 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
|
||||
if filter.selected
|
||||
selectedFilters.push(filter)
|
||||
$scope.$apply ->
|
||||
$ctrl.selectFilter(type, id)
|
||||
$ctrl.selectFilter("page", 1)
|
||||
$ctrl.storeFilters()
|
||||
$ctrl.loadIssues()
|
||||
$ctrl.selectFilter(type, id)
|
||||
$ctrl.selectFilter("page", 1)
|
||||
$ctrl.storeFilters()
|
||||
else
|
||||
selectedFilters = _.reject(selectedFilters, filter)
|
||||
$scope.$apply ->
|
||||
$ctrl.unselectFilter(type, id)
|
||||
$ctrl.selectFilter("page", 1)
|
||||
$ctrl.storeFilters()
|
||||
$ctrl.loadIssues()
|
||||
selectedFilters = _.reject selectedFilters, (f) ->
|
||||
return f.id == filter.id && f.type == filter.type
|
||||
|
||||
$ctrl.unselectFilter(type, id)
|
||||
$ctrl.selectFilter("page", 1)
|
||||
$ctrl.storeFilters()
|
||||
|
||||
reloadIssues()
|
||||
|
||||
renderSelectedFilters(selectedFilters)
|
||||
|
||||
currentFiltersType = $el.find("h2 a.subfilter span.title").prop('data-type')
|
||||
currentFiltersType = getFiltersType()
|
||||
|
||||
if type == currentFiltersType
|
||||
renderFilters(_.reject(filters, "selected"))
|
||||
|
||||
|
@ -540,7 +557,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
initializeSelectedFilters(filters)
|
||||
|
||||
$scope.$on "filters:issueupdate", (ctx, filters) ->
|
||||
html = template({filters:filters.statuses})
|
||||
html = template({filters:filters.status})
|
||||
html = $compile(html)($scope)
|
||||
$el.find(".filter-list").html(html)
|
||||
|
||||
|
@ -555,7 +572,8 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
else
|
||||
$ctrl.replaceFilter("q", value)
|
||||
$ctrl.storeFilters()
|
||||
$ctrl.loadIssues()
|
||||
|
||||
reloadIssues()
|
||||
|
||||
$scope.$watch("filtersQ", selectQFilter)
|
||||
|
||||
|
@ -602,18 +620,18 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
title = $translate.instant("ISSUES.FILTERS.CONFIRM_DELETE.TITLE")
|
||||
message = $translate.instant("ISSUES.FILTERS.CONFIRM_DELETE.MESSAGE", {customFilterName: customFilterName})
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) ->
|
||||
$confirm.askOnDelete(title, message).then (askResponse) ->
|
||||
promise = $ctrl.deleteMyFilter(customFilterName)
|
||||
promise.then ->
|
||||
promise = $ctrl.loadMyFilters()
|
||||
promise.then (filters) ->
|
||||
finish()
|
||||
askResponse.finish()
|
||||
$scope.filters.myFilters = filters
|
||||
renderFilters($scope.filters.myFilters)
|
||||
promise.then null, ->
|
||||
finish()
|
||||
askResponse.finish()
|
||||
promise.then null, ->
|
||||
finish(false)
|
||||
askResponse.finish(false)
|
||||
$confirm.notify("error")
|
||||
|
||||
|
||||
|
@ -664,7 +682,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
|
|||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgIssuesFilters", ["$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading",
|
||||
module.directive("tgIssuesFilters", ["$q", "$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading",
|
||||
"$tgTemplate", "$translate", "$compile", "$tgAuth", IssuesFiltersDirective])
|
||||
|
||||
|
||||
|
@ -711,7 +729,7 @@ IssueStatusInlineEditionDirective = ($repo, $template, $rootscope) ->
|
|||
event.stopPropagation()
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
for filter in $scope.filters.statuses
|
||||
for filter in $scope.filters.status
|
||||
if filter.id == issue.status
|
||||
filter.count--
|
||||
|
||||
|
@ -723,15 +741,10 @@ IssueStatusInlineEditionDirective = ($repo, $template, $rootscope) ->
|
|||
$repo.save(issue).then ->
|
||||
$ctrl.loadIssues()
|
||||
|
||||
for filter in $scope.filters.statuses
|
||||
if filter.id == issue.status
|
||||
filter.count++
|
||||
|
||||
$rootscope.$broadcast("filters:issueupdate", $scope.filters)
|
||||
|
||||
for filter in $scope.filters.statuses
|
||||
for filter in $scope.filters.status
|
||||
if filter.id == issue.status
|
||||
filter.count++
|
||||
|
||||
$rootscope.$broadcast("filters:issueupdate", $scope.filters)
|
||||
|
||||
taiga.bindOnce $scope, "project", (project) ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -34,14 +34,10 @@ module = angular.module("taigaKanban")
|
|||
# Vars
|
||||
|
||||
defaultViewMode = "maximized"
|
||||
defaultViewModes = {
|
||||
maximized: {
|
||||
cardClass: "kanban-task-maximized"
|
||||
}
|
||||
minimized: {
|
||||
cardClass: "kanban-task-minimized"
|
||||
}
|
||||
}
|
||||
viewModes = [
|
||||
"maximized",
|
||||
"minimized"
|
||||
]
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -157,6 +153,10 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
usByStatus[status.id] = _.sortBy(usByStatus[status.id], "kanban_order")
|
||||
|
||||
if userstories.length == 0
|
||||
status = @scope.usStatusList[0]
|
||||
usByStatus[status.id].push({isPlaceholder: true})
|
||||
|
||||
@scope.usByStatus = usByStatus
|
||||
|
||||
# The broadcast must be executed when the DOM has been fully reloaded.
|
||||
|
@ -209,7 +209,7 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
loadInitialData: ->
|
||||
promise = @.loadProject()
|
||||
return promise.then (project) =>
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
@.initializeSubscription()
|
||||
@.loadKanban().then( => @scope.$broadcast("redraw:wip"))
|
||||
|
||||
|
@ -221,8 +221,9 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
@scope.statusViewModes = {}
|
||||
for status in @scope.usStatusList
|
||||
mode = storedStatusViewModes[status.id]
|
||||
@scope.statusViewModes[status.id] = if _.has(defaultViewModes, mode) then mode else defaultViewMode
|
||||
mode = storedStatusViewModes[status.id] || defaultViewMode
|
||||
|
||||
@scope.statusViewModes[status.id] = mode
|
||||
|
||||
@.storeStatusViewModes()
|
||||
|
||||
|
@ -233,9 +234,13 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@scope.statusViewModes[statusId] = newViewMode
|
||||
@.storeStatusViewModes()
|
||||
|
||||
getCardClass: (statusId)->
|
||||
isMaximized: (statusId) ->
|
||||
mode = @scope.statusViewModes[statusId] or defaultViewMode
|
||||
return defaultViewModes[mode].cardClass or defaultViewModes[defaultViewMode].cardClass
|
||||
return mode == 'maximized'
|
||||
|
||||
isMinimized: (statusId) ->
|
||||
mode = @scope.statusViewModes[statusId] or defaultViewMode
|
||||
return mode == 'minimized'
|
||||
|
||||
# Utils methods
|
||||
|
||||
|
@ -317,7 +322,7 @@ KanbanArchivedStatusHeaderDirective = ($rootscope, $translate) ->
|
|||
status = $scope.$eval($attrs.tgKanbanArchivedStatusHeader)
|
||||
hidden = true
|
||||
|
||||
$scope.class = "icon icon-open-eye"
|
||||
$scope.class = "icon-open-eye"
|
||||
$scope.title = showArchivedText
|
||||
|
||||
$el.on "click", (event) ->
|
||||
|
@ -325,12 +330,12 @@ KanbanArchivedStatusHeaderDirective = ($rootscope, $translate) ->
|
|||
|
||||
$scope.$apply ->
|
||||
if hidden
|
||||
$scope.class = "icon icon-open-eye"
|
||||
$scope.class = "icon-open-eye"
|
||||
$scope.title = showArchivedText
|
||||
$rootscope.$broadcast("kanban:hide-userstories-for-status", status.id)
|
||||
|
||||
else
|
||||
$scope.class = "icon icon-closed-eye"
|
||||
$scope.class = "icon-closed-eye"
|
||||
$scope.title = hideArchivedText
|
||||
$rootscope.$broadcast("kanban:show-userstories-for-status", status.id)
|
||||
|
||||
|
@ -414,7 +419,7 @@ KanbanUserstoryDirective = ($rootscope, $loading, $rs) ->
|
|||
else if not us.is_blocked and $el.hasClass("blocked")
|
||||
$el.removeClass("blocked")
|
||||
|
||||
$el.find(".icon-edit").on "click", (event) ->
|
||||
$el.on 'click', '.icon-edit', (event) ->
|
||||
if $el.find(".icon-edit").hasClass("noclick")
|
||||
return
|
||||
|
||||
|
@ -431,11 +436,17 @@ KanbanUserstoryDirective = ($rootscope, $loading, $rs) ->
|
|||
$rootscope.$broadcast("usform:edit", editingUserStory)
|
||||
currentLoading.finish()
|
||||
|
||||
$scope.getTemplateUrl = () ->
|
||||
if $scope.us.isPlaceholder
|
||||
return "common/components/kanban-placeholder.html"
|
||||
else
|
||||
return "kanban/kanban-task.html"
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {
|
||||
templateUrl: "kanban/kanban-task.html"
|
||||
template: '<ng-include src="getTemplateUrl()"/>',
|
||||
link: link
|
||||
require: "ngModel"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -47,7 +47,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
|
|||
|
||||
$location.url($projectUrl.get(response))
|
||||
lightboxService.close($el)
|
||||
currentUserService._loadProjects()
|
||||
currentUserService.loadProjects()
|
||||
|
||||
onErrorSubmit = (response) ->
|
||||
currentLoading.finish()
|
||||
|
@ -74,10 +74,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
|
|||
promise.then(onSuccessSubmit, onErrorSubmit)
|
||||
|
||||
openLightbox = ->
|
||||
$scope.data = {
|
||||
total_story_points: 100
|
||||
total_milestones: 5
|
||||
}
|
||||
$scope.data = {}
|
||||
|
||||
if !$scope.templates.length
|
||||
$rs.projects.templates().then (result) =>
|
||||
|
@ -173,7 +170,7 @@ DeleteProjectDirective = ($repo, $rootscope, $auth, $location, $navUrls, $confir
|
|||
$rootscope.$broadcast("projects:reload")
|
||||
$location.path($navUrls.resolve("home"))
|
||||
$confirm.notify("success")
|
||||
currentUserService._loadProjects()
|
||||
currentUserService.loadProjects()
|
||||
|
||||
# FIXME: error handling?
|
||||
promise.then null, ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -85,14 +85,15 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $tem
|
|||
task = $model.$modelValue
|
||||
message = task.subject
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) ->
|
||||
$confirm.askOnDelete(title, message).then (askResponse) ->
|
||||
promise = $repo.remove(task)
|
||||
promise.then ->
|
||||
finish()
|
||||
askResponse.finish()
|
||||
$confirm.notify("success")
|
||||
$scope.$emit("related-tasks:delete")
|
||||
|
||||
promise.then null, ->
|
||||
askResponse.finish(false)
|
||||
$confirm.notify("error")
|
||||
|
||||
$scope.$watch $attrs.ngModel, (val) ->
|
||||
|
@ -216,7 +217,7 @@ RelatedTasksDirective = ($repo, $rs, $rootscope) ->
|
|||
link = ($scope, $el, $attrs) ->
|
||||
loadTasks = ->
|
||||
return $rs.tasks.list($scope.projectId, null, $scope.usId).then (tasks) =>
|
||||
$scope.tasks = tasks
|
||||
$scope.tasks = _.sortBy(tasks, 'ref')
|
||||
return tasks
|
||||
|
||||
$scope.$on "related-tasks:add", ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -37,8 +37,11 @@ urls = {
|
|||
"users-change-password": "/users/change_password"
|
||||
"users-change-email": "/users/change_email"
|
||||
"users-cancel-account": "/users/cancel"
|
||||
"contacts": "/users/%s/contacts"
|
||||
"stats": "/users/%s/stats"
|
||||
"user-stats": "/users/%s/stats"
|
||||
"user-liked": "/users/%s/liked"
|
||||
"user-voted": "/users/%s/voted"
|
||||
"user-watched": "/users/%s/watched"
|
||||
"user-contacts": "/users/%s/contacts"
|
||||
|
||||
# User - Notification
|
||||
"permissions": "/permissions"
|
||||
|
@ -63,6 +66,10 @@ urls = {
|
|||
"project-templates": "/project-templates"
|
||||
"project-modules": "/projects/%s/modules"
|
||||
"bulk-update-projects-order": "/projects/bulk_update_order"
|
||||
"project-like": "/projects/%s/like"
|
||||
"project-unlike": "/projects/%s/unlike"
|
||||
"project-watch": "/projects/%s/watch"
|
||||
"project-unwatch": "/projects/%s/unwatch"
|
||||
|
||||
# Project Values - Choises
|
||||
"userstory-statuses": "/userstory-statuses"
|
||||
|
@ -82,15 +89,29 @@ urls = {
|
|||
"bulk-update-us-backlog-order": "/userstories/bulk_update_backlog_order"
|
||||
"bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order"
|
||||
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
|
||||
"userstories-filters": "/userstories/filters_data"
|
||||
"userstory-upvote": "/userstories/%s/upvote"
|
||||
"userstory-downvote": "/userstories/%s/downvote"
|
||||
"userstory-watch": "/userstories/%s/watch"
|
||||
"userstory-unwatch": "/userstories/%s/unwatch"
|
||||
|
||||
# Tasks
|
||||
"tasks": "/tasks"
|
||||
"bulk-create-tasks": "/tasks/bulk_create"
|
||||
"bulk-update-task-taskboard-order": "/tasks/bulk_update_taskboard_order"
|
||||
"task-upvote": "/tasks/%s/upvote"
|
||||
"task-downvote": "/tasks/%s/downvote"
|
||||
"task-watch": "/tasks/%s/watch"
|
||||
"task-unwatch": "/tasks/%s/unwatch"
|
||||
|
||||
# Issues
|
||||
"issues": "/issues"
|
||||
"bulk-create-issues": "/issues/bulk_create"
|
||||
"issues-filters": "/issues/filters_data"
|
||||
"issue-upvote": "/issues/%s/upvote"
|
||||
"issue-downvote": "/issues/%s/downvote"
|
||||
"issue-watch": "/issues/%s/watch"
|
||||
"issue-unwatch": "/issues/%s/unwatch"
|
||||
|
||||
# Wiki pages
|
||||
"wiki": "/wiki"
|
||||
|
@ -147,6 +168,10 @@ urls = {
|
|||
|
||||
# locales
|
||||
"locales": "/locales"
|
||||
|
||||
# Application tokens
|
||||
"applications": "/applications"
|
||||
"application-tokens": "/application-tokens"
|
||||
}
|
||||
|
||||
# Initialize api urls service
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -55,11 +55,27 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
|
|||
params = {project_id: projectId, bulk_issues: data}
|
||||
return $http.post(url, params)
|
||||
|
||||
service.upvote = (issueId) ->
|
||||
url = $urls.resolve("issue-upvote", issueId)
|
||||
return $http.post(url)
|
||||
|
||||
service.downvote = (issueId) ->
|
||||
url = $urls.resolve("issue-downvote", issueId)
|
||||
return $http.post(url)
|
||||
|
||||
service.watch = (issueId) ->
|
||||
url = $urls.resolve("issue-watch", issueId)
|
||||
return $http.post(url)
|
||||
|
||||
service.unwatch = (issueId) ->
|
||||
url = $urls.resolve("issue-unwatch", issueId)
|
||||
return $http.post(url)
|
||||
|
||||
service.stats = (projectId) ->
|
||||
return $repo.queryOneRaw("projects", "#{projectId}/issues_stats")
|
||||
|
||||
service.filtersData = (projectId) ->
|
||||
return $repo.queryOneRaw("projects", "#{projectId}/issue_filters_data")
|
||||
service.filtersData = (params) ->
|
||||
return $repo.queryOneRaw("issues-filters", null, params)
|
||||
|
||||
service.listValues = (projectId, type) ->
|
||||
params = {"project": projectId}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
###
|
||||
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: modules.coffee
|
||||
###
|
||||
|
||||
resourceProvider = ($repo) ->
|
||||
service = {}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -39,12 +39,20 @@ resourceProvider = ($repo, $model, $storage) ->
|
|||
service.list = (projectId, filters) ->
|
||||
params = {"project": projectId}
|
||||
params = _.extend({}, params, filters or {})
|
||||
return $repo.queryMany("milestones", params).then (milestones) =>
|
||||
return $repo.queryMany("milestones", params, {}, true).then (result) =>
|
||||
milestones = result[0]
|
||||
headers = result[1]
|
||||
|
||||
for m in milestones
|
||||
uses = m.user_stories
|
||||
uses = _.map(uses, (u) => $model.make_model("userstories", u))
|
||||
m._attrs.user_stories = uses
|
||||
return milestones
|
||||
|
||||
return {
|
||||
milestones: milestones,
|
||||
closed: parseInt(headers("Taiga-Info-Total-Closed-Milestones"), 10),
|
||||
open: parseInt(headers("Taiga-Info-Total-Opened-Milestones"), 10)
|
||||
}
|
||||
|
||||
|
||||
return (instance) ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -57,6 +57,22 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
|||
return $http.post(url, params).then (result) ->
|
||||
return result.data
|
||||
|
||||
service.upvote = (taskId) ->
|
||||
url = $urls.resolve("task-upvote", taskId)
|
||||
return $http.post(url)
|
||||
|
||||
service.downvote = (taskId) ->
|
||||
url = $urls.resolve("task-downvote", taskId)
|
||||
return $http.post(url)
|
||||
|
||||
service.watch = (taskId) ->
|
||||
url = $urls.resolve("task-watch", taskId)
|
||||
return $http.post(url)
|
||||
|
||||
service.unwatch = (taskId) ->
|
||||
url = $urls.resolve("task-unwatch", taskId)
|
||||
return $http.post(url)
|
||||
|
||||
service.bulkUpdateTaskTaskboardOrder = (projectId, data) ->
|
||||
url = $urls.resolve("bulk-update-task-taskboard-order")
|
||||
params = {project_id: projectId, bulk_tasks: data}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -28,7 +28,7 @@ resourceProvider = ($http, $urls) ->
|
|||
service = {}
|
||||
|
||||
service.contacts = (userId, options={}) ->
|
||||
url = $urls.resolve("contacts", userId)
|
||||
url = $urls.resolve("user-contacts", userId)
|
||||
httpOptions = {headers: {}}
|
||||
|
||||
if not options.enablePagination
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -41,6 +41,9 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
|||
service.listInAllProjects = (filters) ->
|
||||
return $repo.queryMany("userstories", filters)
|
||||
|
||||
service.filtersData = (params) ->
|
||||
return $repo.queryOneRaw("userstories-filters", null, params)
|
||||
|
||||
service.listUnassigned = (projectId, filters) ->
|
||||
params = {"project": projectId, "milestone": "null"}
|
||||
params = _.extend({}, params, filters or {})
|
||||
|
@ -64,6 +67,22 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
|||
|
||||
return $http.post(url, data)
|
||||
|
||||
service.upvote = (userStoryId) ->
|
||||
url = $urls.resolve("userstory-upvote", userStoryId)
|
||||
return $http.post(url)
|
||||
|
||||
service.downvote = (userStoryId) ->
|
||||
url = $urls.resolve("userstory-downvote", userStoryId)
|
||||
return $http.post(url)
|
||||
|
||||
service.watch = (userStoryId) ->
|
||||
url = $urls.resolve("userstory-watch", userStoryId)
|
||||
return $http.post(url)
|
||||
|
||||
service.unwatch = (userStoryId) ->
|
||||
url = $urls.resolve("userstory-unwatch", userStoryId)
|
||||
return $http.post(url)
|
||||
|
||||
service.bulkUpdateBacklogOrder = (projectId, data) ->
|
||||
url = $urls.resolve("bulk-update-us-backlog-order")
|
||||
params = {project_id: projectId, bulk_stories: data}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
###
|
||||
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: webhooklogs.coffee
|
||||
###
|
||||
|
||||
resourceProvider = ($repo, $urls, $http) ->
|
||||
service = {}
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
###
|
||||
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: webhooks.coffee
|
||||
###
|
||||
|
||||
resourceProvider = ($repo, $urls, $http) ->
|
||||
service = {}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -64,12 +64,16 @@ class SearchController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
promise.then null, @.onInitialDataError.bind(@)
|
||||
|
||||
# Search input watcher
|
||||
@scope.searchTerm = ""
|
||||
@scope.searchTerm = null
|
||||
loadSearchData = debounceLeading(100, (t) => @.loadSearchData(t))
|
||||
|
||||
bindOnce @scope, "projectId", (projectId) =>
|
||||
if !@scope.searchResults && @scope.searchTerm
|
||||
@.loadSearchData()
|
||||
|
||||
@scope.$watch "searchTerm", (term) =>
|
||||
if term
|
||||
loadSearchData(term)
|
||||
if term != undefined && @scope.projectId
|
||||
@.loadSearchData(term)
|
||||
|
||||
loadFilters: ->
|
||||
defered = @q.defer()
|
||||
|
@ -84,21 +88,31 @@ class SearchController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
@scope.taskStatusById = groupBy(project.task_statuses, (x) -> x.id)
|
||||
@scope.severityById = groupBy(project.severities, (x) -> x.id)
|
||||
@scope.priorityById = groupBy(project.priorities, (x) -> x.id)
|
||||
@scope.membersById = groupBy(project.memberships, (x) -> x.user)
|
||||
@scope.usStatusById = groupBy(project.us_statuses, (x) -> x.id)
|
||||
return project
|
||||
|
||||
loadSearchData: (term) ->
|
||||
promise = @rs.search.do(@scope.projectId, term).then (data) =>
|
||||
@scope.searchResults = data
|
||||
return data
|
||||
loadSearchData: (term = "") ->
|
||||
@scope.loading = true
|
||||
|
||||
return promise
|
||||
@._loadSearchData(term).then (data) =>
|
||||
if data
|
||||
@scope.searchResults = data
|
||||
@scope.loading = false
|
||||
|
||||
_loadSearchData: (term = "") ->
|
||||
@.deferredAbort.resolve() if @.deferredAbort
|
||||
|
||||
@.deferredAbort = @q.defer()
|
||||
|
||||
@rs.search.do(@scope.projectId, term).then (data) =>
|
||||
@.deferredAbort.resolve(data)
|
||||
|
||||
return @.deferredAbort.promise
|
||||
|
||||
loadInitialData: ->
|
||||
return @.loadProject().then (project) =>
|
||||
@scope.projectId = project.id
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
|
||||
module.controller("SearchController", SearchController)
|
||||
|
||||
|
@ -162,13 +176,22 @@ module.directive("tgSearchBox", SearchBoxDirective)
|
|||
|
||||
SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) ->
|
||||
linkTable = ($scope, $el, $attrs, $ctrl) ->
|
||||
applyAutoTab = true
|
||||
activeSectionName = "userstories"
|
||||
tabsDom = $el.find("section.search-filter")
|
||||
lastSeatchResults = null
|
||||
lastSearchResults = null
|
||||
|
||||
getActiveSection = (data) ->
|
||||
maxVal = 0
|
||||
selectedSectionName = null
|
||||
selectedSectionData = null
|
||||
selectedSection = {}
|
||||
selectedSection.name = "userstories"
|
||||
selectedSection.value = []
|
||||
|
||||
if !applyAutoTab
|
||||
selectedSection.name = activeSectionName
|
||||
selectedSection.value = data[activeSectionName]
|
||||
|
||||
return selectedSection
|
||||
|
||||
if data
|
||||
for name in ["userstories", "issues", "tasks", "wikipages"]
|
||||
|
@ -176,14 +199,14 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) ->
|
|||
|
||||
if value.length > maxVal
|
||||
maxVal = value.length
|
||||
selectedSectionName = name
|
||||
selectedSectionData = value
|
||||
selectedSection.name = name
|
||||
selectedSection.value = value
|
||||
break;
|
||||
|
||||
if maxVal == 0
|
||||
return {name: "userstories", value: []}
|
||||
return selectedSection
|
||||
|
||||
return {name:selectedSectionName, value: selectedSectionData}
|
||||
return selectedSection
|
||||
|
||||
renderFilterTabs = (data) ->
|
||||
for name, value of data
|
||||
|
@ -195,6 +218,9 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) ->
|
|||
tabsDom.find("a.active").removeClass("active")
|
||||
tabsDom.find("li.#{section.name} a").addClass("active")
|
||||
|
||||
applyAutoTab = false
|
||||
activeSectionName = section.name
|
||||
|
||||
templates = {
|
||||
issues: $templatecache.get("search-issues")
|
||||
tasks: $templatecache.get("search-tasks")
|
||||
|
@ -218,21 +244,26 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) ->
|
|||
$el.find(".search-result-table").html(element)
|
||||
|
||||
$scope.$watch "searchResults", (data) ->
|
||||
lastSeatchResults = data
|
||||
lastSearchResults = data
|
||||
|
||||
return if !lastSearchResults
|
||||
|
||||
activeSection = getActiveSection(data)
|
||||
|
||||
renderFilterTabs(data)
|
||||
|
||||
renderTableContent(activeSection)
|
||||
markSectionTabActive(activeSection)
|
||||
|
||||
$scope.$watch "searchTerm", (searchTerm) ->
|
||||
$location.search("text", searchTerm) if searchTerm
|
||||
$location.search("text", searchTerm) if searchTerm != undefined
|
||||
|
||||
$el.on "click", ".search-filter li > a", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
sectionName = target.parent().data("name")
|
||||
sectionData = lastSeatchResults[sectionName]
|
||||
sectionData = if !lastSearchResults then [] else lastSearchResults[sectionName]
|
||||
|
||||
section = {
|
||||
name: sectionName,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -135,7 +135,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
|
||||
@scope.$emit('project:loaded', project)
|
||||
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
|
||||
return project
|
||||
|
||||
|
@ -184,6 +184,15 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
if @scope.usTasks[task.user_story]? and @scope.usTasks[task.user_story][task.status]?
|
||||
@scope.usTasks[task.user_story][task.status].push(task)
|
||||
|
||||
if tasks.length == 0
|
||||
|
||||
if @scope.userstories.length > 0
|
||||
usId = @scope.userstories[0].id
|
||||
else
|
||||
usId = null
|
||||
|
||||
@scope.usTasks[usId][@scope.taskStatusList[0].id].push({isPlaceholder: true})
|
||||
|
||||
return tasks
|
||||
|
||||
loadTaskboard: ->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
# Copyright (C) 2014-2015 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014-2015 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014-2015 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -23,6 +23,7 @@ taiga = @.taiga
|
|||
|
||||
mixOf = @.taiga.mixOf
|
||||
groupBy = @.taiga.groupBy
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaTasks")
|
||||
|
||||
|
@ -50,6 +51,8 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||
@log, @appMetaService, @navUrls, @analytics, @translate) ->
|
||||
bindMethods(@)
|
||||
|
||||
@scope.taskRef = @params.taskref
|
||||
@scope.sectionName = @translate.instant("TASK.SECTION_NAME")
|
||||
@.initializeEventHandlers()
|
||||
|
@ -77,13 +80,10 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
initializeEventHandlers: ->
|
||||
@scope.$on "attachment:create", =>
|
||||
@analytics.trackEvent("attachment", "create", "create attachment on task", 1)
|
||||
@rootscope.$broadcast("object:updated")
|
||||
@scope.$on "attachment:edit", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
@scope.$on "attachment:delete", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
@scope.$on "custom-attributes-values:edit", =>
|
||||
@rootscope.$broadcast("object:updated")
|
||||
@scope.$on "comment:new", =>
|
||||
@.loadTask()
|
||||
|
||||
initializeOnDeleteGoToUrl: ->
|
||||
ctx = {project: @scope.project.slug}
|
||||
|
@ -107,7 +107,6 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
@scope.$emit('project:loaded', project)
|
||||
@scope.statusList = project.task_statuses
|
||||
@scope.statusById = groupBy(project.task_statuses, (x) -> x.id)
|
||||
@scope.membersById = groupBy(project.memberships, (x) -> x.user)
|
||||
return project
|
||||
|
||||
loadTask: ->
|
||||
|
@ -146,9 +145,53 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
loadInitialData: ->
|
||||
promise = @.loadProject()
|
||||
return promise.then (project) =>
|
||||
@.fillUsersAndRoles(project.users, project.roles)
|
||||
@.fillUsersAndRoles(project.members, project.roles)
|
||||
@.loadTask().then(=> @q.all([@.loadSprint(), @.loadUserStory()]))
|
||||
|
||||
###
|
||||
# Note: This methods (onUpvote() and onDownvote()) are related to tg-vote-button.
|
||||
# See app/modules/components/vote-button for more info
|
||||
###
|
||||
onUpvote: ->
|
||||
onSuccess = =>
|
||||
@.loadTask()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.tasks.upvote(@scope.taskId).then(onSuccess, onError)
|
||||
|
||||
onDownvote: ->
|
||||
onSuccess = =>
|
||||
@.loadTask()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.tasks.downvote(@scope.taskId).then(onSuccess, onError)
|
||||
|
||||
###
|
||||
# Note: This methods (onWatch() and onUnwatch()) are related to tg-watch-button.
|
||||
# See app/modules/components/watch-button for more info
|
||||
###
|
||||
onWatch: ->
|
||||
onSuccess = =>
|
||||
@.loadTask()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.tasks.watch(@scope.taskId).then(onSuccess, onError)
|
||||
|
||||
onUnwatch: ->
|
||||
onSuccess = =>
|
||||
@.loadTask()
|
||||
@rootscope.$broadcast("object:updated")
|
||||
onError = =>
|
||||
@confirm.notify("error")
|
||||
|
||||
return @rs.tasks.unwatch(@scope.taskId).then(onSuccess, onError)
|
||||
|
||||
module.controller("TaskDetailController", TaskDetailController)
|
||||
|
||||
|
||||
|
@ -199,7 +242,7 @@ module.directive("tgTaskStatusDisplay", ["$tgTemplate", "$compile", TaskStatusDi
|
|||
## Task status button directive
|
||||
#############################################################################
|
||||
|
||||
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $compile, $translate) ->
|
||||
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $compile, $translate, $template) ->
|
||||
# Display the status of Task and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -210,21 +253,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $co
|
|||
# - scope.statusById object
|
||||
# - $scope.project.my_permissions
|
||||
|
||||
template = _.template("""
|
||||
<div class="status-data <% if(editable){ %>clickable<% }%>">
|
||||
<span class="level" style="background-color:<%- status.color %>"></span>
|
||||
<span class="status-status"><%- status.name %></span>
|
||||
<% if(editable){ %><span class="icon icon-arrow-bottom"></span><% }%>
|
||||
<span class="level-name" translate="COMMON.FIELDS.STATUS"></span>
|
||||
|
||||
<ul class="popover pop-status">
|
||||
<% _.each(statuses, function(st) { %>
|
||||
<li><a href="" class="status" title="<%- st.name %>"
|
||||
data-status-id="<%- st.id %>"><%- st.name %></a></li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
</div>
|
||||
""")
|
||||
template = $template.get("us/us-status-button.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
isEditable = ->
|
||||
|
@ -292,7 +321,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $co
|
|||
}
|
||||
|
||||
module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
|
||||
"$compile", "$translate", TaskStatusButtonDirective])
|
||||
"$compile", "$translate", "$tgTemplate", TaskStatusButtonDirective])
|
||||
|
||||
|
||||
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $compile) ->
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue