mirror of
https://github.com/documize/community.git
synced 2025-07-25 08:09:43 +02:00
Merge pull request #37 from documize/trello-improvements
Trello improvements
This commit is contained in:
commit
42b8397a3f
13 changed files with 1098 additions and 147 deletions
|
@ -50,7 +50,8 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
|
|||
token: "",
|
||||
user: null,
|
||||
board: null,
|
||||
lists: []
|
||||
lists: [],
|
||||
boards: []
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -101,6 +102,12 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
|
|||
|
||||
this.set('noBoards', false);
|
||||
|
||||
if (is.undefined(self.get('initDateTimePicker'))) {
|
||||
$.datetimepicker.setLocale('en');
|
||||
$('#trello-since').datetimepicker();
|
||||
self.set('initDateTimePicker', "Done");
|
||||
}
|
||||
|
||||
if (is.null(board) || is.undefined(board)) {
|
||||
if (boards.length) {
|
||||
board = boards[0];
|
||||
|
@ -110,30 +117,34 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
|
|||
this.set('config.board', boards.findBy('id', board.id));
|
||||
}
|
||||
|
||||
this.get('sectionService').fetch(page, "lists", self.get('config'))
|
||||
.then(function (lists) {
|
||||
let savedLists = self.get('config.lists');
|
||||
if (savedLists === null) {
|
||||
savedLists = [];
|
||||
}
|
||||
|
||||
lists.forEach(function (list) {
|
||||
let saved = savedLists.findBy("id", list.id);
|
||||
let included = true;
|
||||
if (is.not.undefined(saved)) {
|
||||
included = saved.included;
|
||||
if (is.null(board.id) || is.undefined(board.id)) {
|
||||
self.set('busy', false);
|
||||
} else {
|
||||
this.get('sectionService').fetch(page, "lists", self.get('config'))
|
||||
.then(function (lists) {
|
||||
let savedLists = self.get('config.lists');
|
||||
if (savedLists === null) {
|
||||
savedLists = [];
|
||||
}
|
||||
list.included = included;
|
||||
});
|
||||
|
||||
self.set('config.lists', lists);
|
||||
self.set('busy', false);
|
||||
}, function (error) { //jshint ignore: line
|
||||
self.set('busy', false);
|
||||
self.set('authenticated', false);
|
||||
self.showNotification("Unable to fetch board lists");
|
||||
console.log(error);
|
||||
});
|
||||
lists.forEach(function (list) {
|
||||
let saved = savedLists.findBy("id", list.id);
|
||||
let included = true;
|
||||
if (is.not.undefined(saved)) {
|
||||
included = saved.included;
|
||||
}
|
||||
list.included = included;
|
||||
});
|
||||
|
||||
self.set('config.lists', lists);
|
||||
self.set('busy', false);
|
||||
}, function (error) { //jshint ignore: line
|
||||
self.set('busy', false);
|
||||
self.set('authenticated', false);
|
||||
self.showNotification("Unable to fetch board lists");
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -150,6 +161,15 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
|
|||
}
|
||||
},
|
||||
|
||||
onBoardCheckbox(id) {
|
||||
let boards = this.get('config.boards');
|
||||
let board = boards.findBy('id', id);
|
||||
|
||||
if (board !== null) {
|
||||
Ember.set(board, 'included', !board.included);
|
||||
}
|
||||
},
|
||||
|
||||
auth() {
|
||||
if (this.get('appKey') === "") {
|
||||
$("#trello-appkey").addClass('error').focus();
|
||||
|
@ -187,6 +207,8 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
|
|||
self.get('sectionService').fetch(page, "boards", self.get('config'))
|
||||
.then(function (boards) {
|
||||
self.set('busy', false);
|
||||
boards.unshift({ id: null, namePath: "< do not show >", backgroundColor: "white" }); // add the non-selection to the front
|
||||
self.set('config.boards', boards); // save the boards in the config too
|
||||
self.set('boards', boards);
|
||||
self.getBoardLists();
|
||||
}, function (error) { //jshint ignore: line
|
||||
|
|
|
@ -1,38 +1,141 @@
|
|||
.section-trello-board {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: auto
|
||||
}
|
||||
.section-trello-editor {
|
||||
.section-trello-board {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.section-trello-board-title {
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
.section-trello-board-title {
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.section-trello-list {
|
||||
background-color: #e2e4e6;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
margin: 10px 10px 0 0;
|
||||
max-width: 300px;
|
||||
}
|
||||
.section-trello-list {
|
||||
background-color: #e2e4e6;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
margin: 10px 10px 0 0;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.section-trello-list-title {
|
||||
font-weight: bold;
|
||||
color: #4c4c4c;
|
||||
font-size: 14px;
|
||||
margin: 5px;
|
||||
}
|
||||
.section-trello-list-title {
|
||||
font-weight: bold;
|
||||
color: #4c4c4c;
|
||||
font-size: 14px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.section-trello-list-checkbox {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
.section-trello-list-checkbox {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.trello-list {
|
||||
margin-bottom: 10px;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.trello-label {
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
padding: 0 8px;
|
||||
margin-right: 5px;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 -1px 0 rgba(0,0,0,.12);
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.section-trello-render {
|
||||
> .trello-board {
|
||||
.trello-table {
|
||||
border: none!important;
|
||||
margin: 0px !important;
|
||||
|
||||
td {
|
||||
border: none !important;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 1.6rem;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.trello-label {
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
padding: 0 8px;
|
||||
margin-right: 5px;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 -1px 0 rgba(0,0,0,.12);
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
text-shadow: 0 0 5px rgba(0,0,0,.2),0 0 2px #000;
|
||||
}
|
||||
|
||||
.board-stats {
|
||||
margin-bottom: 10px;
|
||||
td {
|
||||
color: $color-gray;
|
||||
padding: 20px 40px 30px 0px !important;
|
||||
}
|
||||
.stat-number {
|
||||
font-family: "open_sanslight";
|
||||
font-size: 40px;
|
||||
margin-right: 20px;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
.trello-board {
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
width: 190px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
margin-right: 20px;
|
||||
|
||||
span {
|
||||
display: table;
|
||||
font-size: 14px;
|
||||
line-height: 10px;
|
||||
opacity: 0.5;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.board-summary {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-size: 1.2rem;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
.board-meta, .member-meta {
|
||||
color: $color-gray;
|
||||
}
|
||||
|
||||
.trello-avatar {
|
||||
border-radius: 4px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
> .single-trello-board {
|
||||
width: 100%;
|
||||
max-height: 600px;
|
||||
padding: 10px;
|
||||
|
@ -44,7 +147,7 @@
|
|||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .trello-list {
|
||||
|
@ -65,7 +168,7 @@
|
|||
font-size: 14px;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
|
||||
> a {
|
||||
> .trello-card {
|
||||
color: #4c4c4c;
|
||||
|
@ -82,8 +185,8 @@
|
|||
white-space: normal;
|
||||
cursor: pointer;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,45 +2,80 @@
|
|||
tip="Trello is the visual way to manage your projects and organize anything (https://trello.com)"
|
||||
isDirty=(action 'isDirty') onCancel=(action 'onCancel') onAction=(action 'onAction')}}
|
||||
|
||||
{{#if authenticated}}
|
||||
<div class="section-trello-editor">
|
||||
|
||||
<div class="pull-left width-50">
|
||||
<div class="input-form">
|
||||
<div class="heading">
|
||||
<div class="title">Select Board & Lists</div>
|
||||
<div class="tip">Choose lists to include from board</div>
|
||||
{{#if authenticated}}
|
||||
{{#if noBoards}}
|
||||
<div class="input-control">
|
||||
<div class="color-error">You have no team boards to share - personal boards are never shown.</div>
|
||||
</div>
|
||||
{{#if noBoards}}
|
||||
<div class="input-control">
|
||||
<div class="color-error">You have no team boards to share - personal boards are never shown.</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="input-control">
|
||||
<label>Board</label>
|
||||
<div class="tip">Select board</div>
|
||||
{{ui-select id="boards-dropdown" content=boards action=(action 'onBoardChange') optionValuePath="id" optionLabelPath="name" selection=config.board}}
|
||||
{{else}}
|
||||
|
||||
<div class="pull-left width-45">
|
||||
<div class="input-form">
|
||||
<div class="heading">
|
||||
<div class="title">Select Board & Lists</div>
|
||||
<div class="tip">Choose lists to include from board</div>
|
||||
</div>
|
||||
<div class="input-control">
|
||||
<label>Lists</label>
|
||||
<div class="tip">Select lists to include</div>
|
||||
<div class="section-trello-board" style= {{boardStyle}}>
|
||||
<div class="section-trello-board-title">{{config.board.name}}</div>
|
||||
{{#each config.lists as |list|}}
|
||||
<div class="section-trello-list" {{action 'onListCheckbox' list.id}}>
|
||||
{{#if list.included}}
|
||||
<i class="material-icons widget-checkbox checkbox-gray section-trello-list-checkbox">check_box</i>
|
||||
{{else}}
|
||||
<i class="material-icons widget-checkbox checkbox-gray section-trello-list-checkbox">check_box_outline_blank</i>
|
||||
<label>Show summary from (default 7 days ago)</label>
|
||||
{{input id="trello-since" value=config.since type="text" }}<br>
|
||||
</div>
|
||||
|
||||
<div class="input-control">
|
||||
<label>Select which boards you want to see</label>
|
||||
<div class="tip">All boards are selectd by default</div>
|
||||
<div class="trello-board">
|
||||
{{#each config.boards as |board|}}
|
||||
{{#if board.id}}
|
||||
<div class="trello-list" {{action 'onBoardCheckbox' board.id}}>
|
||||
{{#if board.included}}
|
||||
<i class="material-icons widget-checkbox checkbox-gray trello-list-checkbox">check_box</i>
|
||||
{{else}}
|
||||
<i class="material-icons widget-checkbox checkbox-gray trello-list-checkbox">check_box_outline_blank</i>
|
||||
{{/if}}
|
||||
<span class="trello-label" style="background-color: {{board.prefs.backgroundColor}}">{{board.orgName}} / {{board.name}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<span class="trello-list-title">{{list.name}}</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="clearfix" />
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="clearfix" />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-left width-10"> </div>
|
||||
|
||||
<div class="pull-left width-45">
|
||||
<div class="input-form">
|
||||
<div class="input-control">
|
||||
<label>Individual Board</label>
|
||||
<div class="tip">Select board</div>
|
||||
{{ui-select id="boards-dropdown" content=boards action=(action 'onBoardChange') optionValuePath="id" optionLabelPath="namePath" selection=config.board}}
|
||||
</div>
|
||||
{{#if config.board.id}}
|
||||
<div class="input-control">
|
||||
<label>Lists</label>
|
||||
<div class="tip">Select lists to include</div>
|
||||
<div class="section-trello-board" style= {{boardStyle}}>
|
||||
<div class="section-trello-board-title">{{config.board.name}}</div>
|
||||
{{#each config.lists as |list|}}
|
||||
<div class="section-trello-list" {{action 'onListCheckbox' list.id}}>
|
||||
{{#if list.included}}
|
||||
<i class="material-icons widget-checkbox checkbox-gray section-trello-list-checkbox">check_box</i>
|
||||
{{else}}
|
||||
<i class="material-icons widget-checkbox checkbox-gray section-trello-list-checkbox">check_box_outline_blank</i>
|
||||
{{/if}}
|
||||
<span class="trello-list-title">{{list.name}}</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="clearfix" />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{else}}
|
||||
|
||||
|
@ -57,5 +92,5 @@
|
|||
</div>
|
||||
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
{{/section/base-editor}}
|
||||
|
|
30
core/section/trello/activitytranslation.go
Normal file
30
core/section/trello/activitytranslation.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
var activityTranslation = map[string]string{
|
||||
"add checklist to card": "checklist added to card",
|
||||
"add member to card": "member added to card",
|
||||
"comment card": "commented on card",
|
||||
"create card": "created card",
|
||||
"create list": "created list",
|
||||
"delete card": "deleted card",
|
||||
"update board": "updated board",
|
||||
"add to team board": "",
|
||||
"create board": "",
|
||||
"update card": "updated card",
|
||||
"update check item state on card": "check item updated on card",
|
||||
"update list": "updated list",
|
||||
"add attachment to card": "attachment added to card",
|
||||
"copy card": "copied card",
|
||||
"copy comment card": "copied comment on card",
|
||||
}
|
62
core/section/trello/archive_template.go
Normal file
62
core/section/trello/archive_template.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const archiveTemplate = `
|
||||
{{if false}}
|
||||
<div class="section-trello-render">
|
||||
|
||||
{{if gt (len .Boards) 0}}
|
||||
<div class="heading">Deleted and Archived Cards</div>
|
||||
<p>Changes since {{.Since}}.</p>
|
||||
<div class="section-trello-render">
|
||||
<table class="trello-table" class="width-100">
|
||||
<tbody class="trello">
|
||||
{{range $b := .Boards}}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ $b.Board.URL }}">
|
||||
<span class="trello-board" style="background-color: {{$b.Board.Prefs.BackgroundColor}}">{{$b.Board.Name}}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{range $act := $b.Actions}}
|
||||
{{if eq $act.Type "deleteCard" }}
|
||||
Deleted:
|
||||
{{$act.Data.List.Name}}
|
||||
{{if ne $act.Data.Card.Name ""}}
|
||||
: {{$act.Data.Card.Name}}
|
||||
{{if ne $act.Data.Text ""}}
|
||||
- {{$act.Data.Text}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<br>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{range $arch := $b.Archived}}
|
||||
Archived:
|
||||
{{$arch.Name}}
|
||||
{{if ne $arch.Desc ""}}
|
||||
- {{$arch.Desc}}
|
||||
{{end}}
|
||||
<br>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
`
|
55
core/section/trello/boards_template.go
Normal file
55
core/section/trello/boards_template.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const boardsTemplate = `
|
||||
<div class="section-trello-render">
|
||||
{{if gt (len .Boards) 0}}
|
||||
<div class="heading">Boards</div>
|
||||
<p>There are {{len .Boards}} boards, {{.ListTotal}} since lists, {{.CardTotal}} cards and {{len .MemberBoardAssign}} members {{.Since}}.</p>
|
||||
<div class="section-trello-render">
|
||||
<table class="trello-table" class="width-100">
|
||||
<tbody class="trello">
|
||||
{{range $b := .Boards}}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ $b.Board.URL }}">
|
||||
<div class="trello-board" style="background-color: {{$b.Board.Prefs.BackgroundColor}}">
|
||||
{{$b.Board.Name}}
|
||||
<span>{{$b.Board.OrgName}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div class="board-summary">
|
||||
<!-- {{ len $b.Actions }}{{if eq 1 (len $b.Actions)}} action {{else}} actions {{end}} -->
|
||||
</div>
|
||||
<span class="board-meta">
|
||||
{{range $act, $tot := $b.ActionSummary}}
|
||||
{{$tot}} {{$act}}{{if ne 1 $tot}}s{{end}},
|
||||
{{end}}
|
||||
{{if gt (len $b.Archived) 0}}
|
||||
{{len $b.Archived}} {{if eq 1 (len $b.Archived)}}card {{else}} cards {{end}}archived
|
||||
{{else}}
|
||||
no cards archived
|
||||
{{end}}
|
||||
<br>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
`
|
32
core/section/trello/graphs_template.go
Normal file
32
core/section/trello/graphs_template.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const graphsTemplate = `
|
||||
{{if false}}
|
||||
|
||||
<div class="heading">Single Boards (graphs)</div>
|
||||
{{range $b := .Boards}}
|
||||
<div>
|
||||
<p>There are {{ $b.CardCount }} cards across {{ $b.ListCount }} lists for board <a href="{{ $b.Board.URL }}">{{$b.Board.Name}}.</a></p>
|
||||
<div>
|
||||
{{range $data := $b.Data}}
|
||||
<div style="background-color: {{$b.Board.Prefs.BackgroundColor}}">
|
||||
<progress value="{{len $data.Cards}}" max="{{ $b.CardCount }}"></progress> {{ $data.List.Name }}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
`
|
37
core/section/trello/labels_template.go
Normal file
37
core/section/trello/labels_template.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const labelsTemplate = `
|
||||
<div class="section-trello-render">
|
||||
{{if gt (len .SharedLabels) 0}}
|
||||
<div class="heading">Labels</div>
|
||||
<p>There are {{len .SharedLabels}} common labels across the boards.</p>
|
||||
<div class="section-trello-render">
|
||||
<table class="trello-table" class="width-100">
|
||||
<tbody class="trello">
|
||||
{{range $l := .SharedLabels}}
|
||||
<tr>
|
||||
<td class="no-width">
|
||||
<span class="trello-label" style="background-color: {{ $l.Color }}">{{ $l.Name }} ({{len $l.Boards}})</span>
|
||||
</td>
|
||||
<td>
|
||||
{{range $idx, $brd := $l.Boards}}{{if gt $idx 0}}, {{end}}<a class="link" href="{{$brd.URL}}">{{$brd.OrgName}}/{{$brd.Name}}</a>{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
`
|
22
core/section/trello/master_template.go
Normal file
22
core/section/trello/master_template.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const renderTemplate = `<p>Activity since {{.Since}} for boards:
|
||||
{{range $idx, $brd := .Boards}}{{if gt $idx 0}}, {{end}}<a class="link" href="{{$brd.Board.URL}}">{{$brd.Board.OrgName}}/{{$brd.Board.Name}}</a>{{end}}.</p>` +
|
||||
labelsTemplate +
|
||||
boardsTemplate +
|
||||
graphsTemplate +
|
||||
membersTemplate +
|
||||
archiveTemplate +
|
||||
tradTemplate +
|
||||
``
|
44
core/section/trello/members_template.go
Normal file
44
core/section/trello/members_template.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const membersTemplate = `
|
||||
<div class="section-trello-render">
|
||||
{{if gt (len .Boards) 0}}
|
||||
<div class="heading">Members</div>
|
||||
<p>
|
||||
There {{if eq 1 (len .MemberBoardAssign)}} is one member {{else}} are {{len .MemberBoardAssign}} members {{end}} assigned to {{.CardAssignTotal}} cards of the total {{.CardTotal}} cards across {{len .Boards}} boards.
|
||||
</p>
|
||||
|
||||
<div class="section-trello-render">
|
||||
<table class="trello-table no-width">
|
||||
<tbody>
|
||||
{{range $m := .MemberBoardAssign}}
|
||||
<tr>
|
||||
<td class="no-width">
|
||||
<img class="trello-avatar" src="https://trello-avatars.s3.amazonaws.com/{{$m.AvatarHash}}/50.png" alt="Member Avatar">
|
||||
</td>
|
||||
<td>
|
||||
<div class="member-name">{{$m.MemberName}}</div>
|
||||
<div class="member-meta">
|
||||
{{range $idx, $ac := $m.AssignCounts}}{{if gt $idx 0}}, {{end}}{{$ac.BoardName}} ({{$ac.Count}}){{end}}
|
||||
</div>
|
||||
<div class="margin-top-10"></div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
`
|
|
@ -11,39 +11,25 @@
|
|||
|
||||
package trello
|
||||
|
||||
import "strings"
|
||||
|
||||
const renderTemplate = `
|
||||
<p class="non-printable-message">Non-printable</p>
|
||||
<div class="section-trello-render non-printable">
|
||||
<p>There are {{ .CardCount }} cards across {{ .ListCount }} lists for board <a href="{{ .Board.URL }}">{{.Board.Name}}.</a></p>
|
||||
<div class="trello-board" style="background-color: {{.Board.Prefs.BackgroundColor}}">
|
||||
<a href="{{ .Board.URL }}"><div class="trello-board-title">{{.Board.Name}}</div></a>
|
||||
{{range $data := .Data}}
|
||||
<div class="trello-list">
|
||||
<div class="trello-list-title">{{ $data.List.Name }}</div>
|
||||
{{range $card := $data.Cards}}
|
||||
<a href="{{ $card.URL }}">
|
||||
<div class="trello-card">
|
||||
{{ $card.Name }}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type secrets struct {
|
||||
Token string `json:"token"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type trelloConfig struct {
|
||||
AppKey string `json:"appKey"`
|
||||
Token string `json:"token"`
|
||||
Board trelloBoard `json:"board"`
|
||||
Lists []trelloList `json:"lists"`
|
||||
AppKey string `json:"appKey"`
|
||||
Token string `json:"token"`
|
||||
Board trelloBoard `json:"board"`
|
||||
Lists []trelloList `json:"lists"`
|
||||
Boards []trelloBoard `json:"boards"`
|
||||
Since string `json:"since,omitempty"`
|
||||
SincePtr *time.Time `json:"-"`
|
||||
|
||||
OrgByID map[string]trelloOrganization `json:"-"`
|
||||
}
|
||||
|
||||
func (c *trelloConfig) Clean() {
|
||||
|
@ -52,6 +38,73 @@ func (c *trelloConfig) Clean() {
|
|||
}
|
||||
|
||||
// Trello objects based upon https://github.com/VojtechVitek/go-trello
|
||||
|
||||
type trelloOrganization struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Desc string `json:"desc"`
|
||||
DescData string `json:"descData"`
|
||||
URL string `json:"url"`
|
||||
Website string `json:"website"`
|
||||
LogoHash string `json:"logoHash"`
|
||||
Products []string `json:"products"`
|
||||
PowerUps []string `json:"powerUps"`
|
||||
}
|
||||
|
||||
type trelloAction struct {
|
||||
ID string `json:"id"`
|
||||
IDMemberCreator string `json:"idMemberCreator"`
|
||||
Data struct {
|
||||
DateLastEdited string `json:"dateLastEdited"`
|
||||
ListBefore struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"listBefore"`
|
||||
ListAfter struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"listAfter"`
|
||||
CheckItem struct {
|
||||
ID string `json:"id"`
|
||||
State string `json:"state"`
|
||||
Name string `json:"name"`
|
||||
} `json:"checkItem"`
|
||||
CheckList struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"checklist"`
|
||||
List struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"list"`
|
||||
TextData struct {
|
||||
Emoji struct{} `json:"emoji"`
|
||||
} `json:"textData"`
|
||||
Board struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ShortLink string `json:"shortLink"`
|
||||
} `json:"board"`
|
||||
Card struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ShortLink string `json:"shortLink"`
|
||||
IDShort int `json:"idShort"`
|
||||
} `json:"card"`
|
||||
Text string `json:"text"`
|
||||
} `json:"data"`
|
||||
Type string `json:"type"`
|
||||
Date string `json:"date"`
|
||||
MemberCreator struct {
|
||||
ID string `json:"id"`
|
||||
AvatarHash string `json:"avatarHash"`
|
||||
FullName string `json:"fullName"`
|
||||
Initials string `json:"initials"`
|
||||
Username string `json:"username"`
|
||||
} `json:"memberCreator"`
|
||||
}
|
||||
|
||||
type trelloMember struct {
|
||||
ID string `json:"id"`
|
||||
AvatarHash string `json:"avatarHash"`
|
||||
|
@ -94,6 +147,7 @@ type trelloBoard struct {
|
|||
Name string `json:"name"`
|
||||
Closed bool `json:"closed"`
|
||||
OrganizationID string `json:"idOrganization"`
|
||||
OrgName string `json:"orgName"`
|
||||
Pinned bool `json:"pinned"`
|
||||
URL string `json:"url"`
|
||||
ShortURL string `json:"shortUrl"`
|
||||
|
@ -129,6 +183,8 @@ type trelloBoard struct {
|
|||
Blue string `json:"blue"`
|
||||
Purple string `json:"purple"`
|
||||
} `json:"labelNames"`
|
||||
Included bool `json:"included"` // indicates whether we display this board
|
||||
NamePath string `json:"namePath"` // the "team / board" form
|
||||
}
|
||||
|
||||
type trelloBoardBackground struct {
|
||||
|
@ -197,9 +253,43 @@ type trelloListCards struct {
|
|||
Cards []trelloCard
|
||||
}
|
||||
|
||||
type trelloRender struct {
|
||||
Board trelloBoard
|
||||
Data []trelloListCards
|
||||
CardCount int
|
||||
ListCount int
|
||||
type trelloRenderBoard struct {
|
||||
Board trelloBoard
|
||||
Data []trelloListCards
|
||||
CardCount int
|
||||
ListCount int
|
||||
Actions []trelloAction
|
||||
ActionSummary map[string]int
|
||||
Archived []trelloCard
|
||||
}
|
||||
|
||||
type trelloSharedLabel struct {
|
||||
Name string
|
||||
Color string
|
||||
Boards []trelloBoard
|
||||
}
|
||||
|
||||
type trelloBoardAssignCount struct {
|
||||
BoardName string
|
||||
Count int
|
||||
}
|
||||
|
||||
type trelloBoardAssign struct {
|
||||
AvatarHash string
|
||||
MemberName string
|
||||
AssignCounts []trelloBoardAssignCount
|
||||
}
|
||||
|
||||
type trelloRender struct {
|
||||
Boards []trelloRenderBoard
|
||||
Since string
|
||||
Detail trelloRenderBoard
|
||||
|
||||
// items below are generated during the render phase
|
||||
SharedLabels []trelloSharedLabel
|
||||
MembersByID map[string]trelloMember
|
||||
MemberBoardAssign []trelloBoardAssign
|
||||
CardAssignTotal int
|
||||
CardTotal int
|
||||
ListTotal int
|
||||
}
|
||||
|
|
40
core/section/trello/trad_template.go
Normal file
40
core/section/trello/trad_template.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
package trello
|
||||
|
||||
const tradTemplate = `
|
||||
<div class="section-trello-render">
|
||||
{{if ne .Detail.Board.ID ""}}
|
||||
<div class="heading">{{.Detail.Board.Name}} Board</div>
|
||||
<p class="non-printable-message">Non-printable</p>
|
||||
<div class="section-trello-render non-printable">
|
||||
<p>There are {{ .Detail.CardCount }} cards across {{ .Detail.ListCount }} lists
|
||||
for board <a href="{{ .Detail.Board.URL }}">{{.Detail.Board.Name}}.</a></p>
|
||||
<div class="single-trello-board" style="background-color: {{.Detail.Board.Prefs.BackgroundColor}}">
|
||||
<a href="{{ .Detail.Board.URL }}"><div class="trello-board-title">{{.Detail.Board.Name}}</div></a>
|
||||
{{range $data := .Detail.Data}}
|
||||
<div class="trello-list">
|
||||
<div class="trello-list-title">{{ $data.List.Name }}</div>
|
||||
{{range $card := $data.Cards}}
|
||||
<a href="{{ $card.URL }}">
|
||||
<div class="trello-card">
|
||||
{{ $card.Name }}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
`
|
|
@ -18,10 +18,14 @@ import (
|
|||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/documize/community/core/api/request"
|
||||
"github.com/documize/community/core/section/provider"
|
||||
"github.com/documize/community/core/log"
|
||||
"github.com/documize/community/core/section/provider"
|
||||
)
|
||||
|
||||
var meta provider.TypeMeta
|
||||
|
@ -98,7 +102,7 @@ func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.R
|
|||
provider.WriteJSON(w, render)
|
||||
|
||||
case "boards":
|
||||
render, err := getBoards(config)
|
||||
render, err := getBoards(&config)
|
||||
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
|
@ -147,28 +151,32 @@ func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.R
|
|||
log.IfErr(ctx.SaveSecrets(string(b)))
|
||||
}
|
||||
|
||||
// Render just sends back HMTL as-is.
|
||||
// Render the payload using the template.
|
||||
func (*Provider) Render(ctx *provider.Context, config, data string) string {
|
||||
raw := []trelloListCards{}
|
||||
payload := trelloRender{}
|
||||
var payload = trelloRender{}
|
||||
var c = trelloConfig{}
|
||||
|
||||
json.Unmarshal([]byte(data), &raw)
|
||||
json.Unmarshal([]byte(data), &payload)
|
||||
json.Unmarshal([]byte(config), &c)
|
||||
|
||||
payload.Board = c.Board
|
||||
payload.Data = raw
|
||||
payload.ListCount = len(raw)
|
||||
|
||||
for _, list := range raw {
|
||||
payload.CardCount += len(list.Cards)
|
||||
}
|
||||
buildPayloadAnalysis(&c, &payload)
|
||||
|
||||
t := template.New("trello")
|
||||
t, _ = t.Parse(renderTemplate)
|
||||
var err error
|
||||
t, err = t.Parse(renderTemplate)
|
||||
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return ""
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
t.Execute(buffer, payload)
|
||||
err = t.Execute(buffer, payload)
|
||||
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
@ -176,15 +184,80 @@ func (*Provider) Render(ctx *provider.Context, config, data string) string {
|
|||
// Refresh just sends back data as-is.
|
||||
func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
|
||||
var c = trelloConfig{}
|
||||
json.Unmarshal([]byte(config), &c)
|
||||
log.IfErr(json.Unmarshal([]byte(config), &c))
|
||||
|
||||
refreshed, err := getCards(c)
|
||||
save := trelloRender{}
|
||||
save.Boards = make([]trelloRenderBoard, 0, len(c.Boards))
|
||||
|
||||
if err != nil {
|
||||
return data
|
||||
if len(c.Since) >= len("yyyy/mm/dd hh:ss") {
|
||||
var since time.Time
|
||||
tt := []byte("yyyy-mm-ddThh:mm:00Z")
|
||||
for _, i := range []int{0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15} {
|
||||
tt[i] = c.Since[i]
|
||||
}
|
||||
err := since.UnmarshalText(tt)
|
||||
if err != nil {
|
||||
log.ErrorString("Date unmarshall '" + c.Since + "'->'" + string(tt) + "' error: " + err.Error())
|
||||
} else {
|
||||
c.SincePtr = &since
|
||||
}
|
||||
}
|
||||
dateMessage := ""
|
||||
if c.SincePtr == nil {
|
||||
dateMessage = " (the last 7 days)"
|
||||
since := time.Now().AddDate(0, 0, -7)
|
||||
c.SincePtr = &since
|
||||
c.Since = (*c.SincePtr).Format("2006/01/02 ")
|
||||
}
|
||||
save.Since = (*c.SincePtr).Format("January 2, 2006") + dateMessage
|
||||
|
||||
c.AppKey = request.ConfigString(meta.ConfigHandle(), "appKey")
|
||||
|
||||
if c.Board.ID != "" { // set up detail board
|
||||
var err error
|
||||
save.Detail.Board = c.Board
|
||||
save.Detail.Data, err = getCards(c)
|
||||
log.IfErr(err)
|
||||
save.Detail.ListCount = len(save.Detail.Data)
|
||||
for _, list := range save.Detail.Data {
|
||||
save.Detail.CardCount += len(list.Cards)
|
||||
}
|
||||
}
|
||||
|
||||
j, err := json.Marshal(refreshed)
|
||||
for _, board := range c.Boards {
|
||||
if board.Included && board.ID != "" {
|
||||
var payload = trelloRenderBoard{}
|
||||
|
||||
c.Board = board
|
||||
|
||||
lsts, err := getLists(c)
|
||||
log.IfErr(err)
|
||||
if err == nil {
|
||||
c.Lists = lsts
|
||||
}
|
||||
|
||||
for l := range c.Lists {
|
||||
c.Lists[l].Included = true
|
||||
}
|
||||
|
||||
refreshed, err := getCards(c)
|
||||
log.IfErr(err)
|
||||
|
||||
payload.Board = c.Board
|
||||
payload.Data = refreshed
|
||||
payload.ListCount = len(refreshed)
|
||||
|
||||
for _, list := range refreshed {
|
||||
payload.CardCount += len(list.Cards)
|
||||
}
|
||||
|
||||
payload.Actions, payload.Archived = fetchBoardActions(&c, &save, board.ID, c.Since)
|
||||
|
||||
save.Boards = append(save.Boards, payload)
|
||||
}
|
||||
}
|
||||
|
||||
j, err := json.Marshal(save)
|
||||
|
||||
if err != nil {
|
||||
log.Error("unable to marshall trello cards", err)
|
||||
|
@ -195,8 +268,48 @@ func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
|
|||
}
|
||||
|
||||
// Helpers
|
||||
func getBoards(config trelloConfig) (boards []trelloBoard, err error) {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.trello.com/1/members/me/boards?fields=id,name,url,closed,prefs,idOrganization&key=%s&token=%s", config.AppKey, config.Token), nil)
|
||||
|
||||
func getOrg(config *trelloConfig, orgID string) (*trelloOrganization, error) {
|
||||
if config.OrgByID == nil {
|
||||
config.OrgByID = make(map[string]trelloOrganization)
|
||||
}
|
||||
if org, found := config.OrgByID[orgID]; found {
|
||||
return &org, nil
|
||||
}
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf(
|
||||
"https://api.trello.com/1/organizations/%s?fields=name,desc&key=%s&token=%s",
|
||||
orgID, config.AppKey, config.Token), nil)
|
||||
log.IfErr(err)
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("error: HTTP status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
b := trelloOrganization{}
|
||||
|
||||
defer res.Body.Close()
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&b)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.OrgByID[orgID] = b
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
func getBoards(config *trelloConfig) (boards []trelloBoard, err error) {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf(
|
||||
"https://api.trello.com/1/members/me/boards?fields=id,name,url,closed,prefs,idOrganization&key=%s&token=%s",
|
||||
config.AppKey, config.Token), nil)
|
||||
log.IfErr(err)
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
|
||||
|
@ -213,24 +326,45 @@ func getBoards(config trelloConfig) (boards []trelloBoard, err error) {
|
|||
defer res.Body.Close()
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&b)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we only show open, team boards (not personal)
|
||||
for _, b := range b {
|
||||
if !b.Closed && len(b.OrganizationID) > 0 {
|
||||
if o, e := getOrg(config, b.OrganizationID); e == nil {
|
||||
b.OrgName = o.Name
|
||||
b.NamePath = o.Name + " / " + b.Name
|
||||
} else {
|
||||
log.Error("failed to get organisation infomation", e)
|
||||
}
|
||||
boards = append(boards, b)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
for bx, bd := range boards {
|
||||
for _, cd := range config.Boards {
|
||||
if bd.ID == cd.ID {
|
||||
boards[bx].Included = cd.Included // to pick up the previous selection or not
|
||||
goto foundID
|
||||
}
|
||||
}
|
||||
boards[bx].Included = true // include boards by default
|
||||
foundID:
|
||||
}
|
||||
|
||||
return boards, nil
|
||||
}
|
||||
|
||||
func getLists(config trelloConfig) (lists []trelloList, err error) {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.trello.com/1/boards/%s/lists/open?key=%s&token=%s", config.Board.ID, config.AppKey, config.Token), nil)
|
||||
if config.Board.ID == "" {
|
||||
return
|
||||
}
|
||||
uri := fmt.Sprintf("https://api.trello.com/1/boards/%s/lists/open?key=%s&token=%s", config.Board.ID, config.AppKey, config.Token)
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
log.IfErr(err)
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
|
||||
|
@ -264,6 +398,7 @@ func getCards(config trelloConfig) (listCards []trelloListCards, err error) {
|
|||
}
|
||||
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.trello.com/1/lists/%s/cards?key=%s&token=%s", list.ID, config.AppKey, config.Token), nil)
|
||||
log.IfErr(err)
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
|
||||
|
@ -294,3 +429,247 @@ func getCards(config trelloConfig) (listCards []trelloListCards, err error) {
|
|||
|
||||
return listCards, nil
|
||||
}
|
||||
|
||||
func fetchMember(config *trelloConfig, render *trelloRender, memberID string) (memberInfo trelloMember) {
|
||||
memberInfo.FullName = "(unknown)"
|
||||
|
||||
if render.MembersByID == nil {
|
||||
render.MembersByID = make(map[string]trelloMember)
|
||||
}
|
||||
found := false
|
||||
if memberInfo, found = render.MembersByID[memberID]; found {
|
||||
return
|
||||
}
|
||||
render.MembersByID[memberID] = memberInfo // write unknown, so that we do not retry on errors
|
||||
|
||||
if len(config.AppKey) == 0 {
|
||||
config.AppKey = request.ConfigString(meta.ConfigHandle(), "appKey")
|
||||
}
|
||||
uri := fmt.Sprintf("https://api.trello.com/1/members/%s?key=%s&token=%s", memberID, config.AppKey, config.Token)
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
log.ErrorString("Trello fetch member HTTP status not OK")
|
||||
return
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&memberInfo)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
render.MembersByID[memberID] = memberInfo
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func fetchBoardActions(config *trelloConfig, render *trelloRender, boardID string, since string) (actions []trelloAction, archived []trelloCard) {
|
||||
|
||||
sinceString := since[:10]
|
||||
|
||||
if len(config.AppKey) == 0 {
|
||||
config.AppKey = request.ConfigString(meta.ConfigHandle(), "appKey")
|
||||
}
|
||||
|
||||
{
|
||||
uri := fmt.Sprintf("https://api.trello.com/1/boards/%s/actions?limit=1000&since=%s&key=%s&token=%s", boardID, sinceString, config.AppKey, config.Token)
|
||||
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
log.ErrorString("Trello fetch board actions HTTP status not OK")
|
||||
return
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&actions)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uri := fmt.Sprintf("https://api.trello.com/1/boards/%s/cards?filter=closed&since=%s&key=%s&token=%s",
|
||||
boardID, sinceString, config.AppKey, config.Token)
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
msg := ""
|
||||
txt, err := ioutil.ReadAll(res.Body)
|
||||
if err == nil {
|
||||
msg = string(txt)
|
||||
} else {
|
||||
msg = err.Error()
|
||||
}
|
||||
log.ErrorString("Trello fetch board archived HTTP status not OK - " + msg)
|
||||
return
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&archived)
|
||||
if err != nil {
|
||||
log.IfErr(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func buildPayloadAnalysis(config *trelloConfig, render *trelloRender) {
|
||||
|
||||
//totals
|
||||
render.CardTotal = 0
|
||||
render.CardAssignTotal = 0
|
||||
render.ListTotal = 0
|
||||
|
||||
// pre-process labels
|
||||
type labT struct {
|
||||
color string
|
||||
boards map[string]trelloBoard
|
||||
}
|
||||
labels := make(map[string]labT)
|
||||
|
||||
// pre-process member stats
|
||||
memberBoardCount := make(map[string]map[string]int)
|
||||
|
||||
// main loop
|
||||
for brdIdx, brd := range render.Boards {
|
||||
for _, lst := range brd.Data {
|
||||
render.ListTotal++
|
||||
for _, crd := range lst.Cards {
|
||||
render.CardTotal++
|
||||
if len(crd.MembersID) > 0 {
|
||||
render.CardAssignTotal++
|
||||
}
|
||||
|
||||
// process labels
|
||||
for _, lab := range crd.Labels {
|
||||
if _, exists := labels[lab.Name]; !exists {
|
||||
labels[lab.Name] = labT{color: lab.Color, boards: make(map[string]trelloBoard)}
|
||||
}
|
||||
labels[lab.Name].boards[brd.Board.URL+" / "+brd.Board.Name] = brd.Board
|
||||
}
|
||||
|
||||
// process member stats
|
||||
for _, mem := range crd.MembersID {
|
||||
if _, exists := memberBoardCount[mem]; !exists {
|
||||
memberBoardCount[mem] = make(map[string]int)
|
||||
}
|
||||
memberBoardCount[mem][brd.Board.ID]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ActionSummary
|
||||
if render.Boards[brdIdx].ActionSummary == nil {
|
||||
render.Boards[brdIdx].ActionSummary = make(map[string]int)
|
||||
}
|
||||
for _, act := range brd.Actions {
|
||||
englishType := ""
|
||||
for _, c := range act.Type {
|
||||
if unicode.IsUpper(c) {
|
||||
englishType += " "
|
||||
englishType += string(unicode.ToLower(c))
|
||||
} else {
|
||||
englishType += string(c)
|
||||
}
|
||||
}
|
||||
englishType = strings.Replace(englishType, "organization", "team", -1)
|
||||
if newTxt, found := activityTranslation[englishType]; found {
|
||||
englishType = newTxt
|
||||
}
|
||||
if len(englishType) > 0 {
|
||||
render.Boards[brdIdx].ActionSummary[englishType]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//post-process labels
|
||||
labs := make([]string, 0, len(labels))
|
||||
for lname := range labels {
|
||||
labs = append(labs, lname)
|
||||
}
|
||||
sort.Strings(labs)
|
||||
for _, lname := range labs {
|
||||
thisLabel := labels[lname].boards
|
||||
if l := len(thisLabel); l > 1 {
|
||||
brds := make([]string, 0, l)
|
||||
for bname := range thisLabel {
|
||||
brds = append(brds, bname)
|
||||
}
|
||||
sort.Strings(brds)
|
||||
lbrds := []trelloBoard{}
|
||||
for _, h := range brds {
|
||||
lbrds = append(lbrds, labels[lname].boards[h])
|
||||
}
|
||||
render.SharedLabels = append(render.SharedLabels, trelloSharedLabel{
|
||||
Name: lname, Color: labels[lname].color, Boards: lbrds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//post-process member stats
|
||||
mNames := make([]string, 0, len(memberBoardCount))
|
||||
for mID := range memberBoardCount {
|
||||
memInfo := fetchMember(config, render, mID)
|
||||
mNames = append(mNames, memInfo.FullName)
|
||||
}
|
||||
sort.Strings(mNames)
|
||||
for _, mNam := range mNames {
|
||||
for mem, brdCounts := range memberBoardCount {
|
||||
memInfo := fetchMember(config, render, mem)
|
||||
if mNam == memInfo.FullName {
|
||||
render.MemberBoardAssign = append(render.MemberBoardAssign, trelloBoardAssign{MemberName: mNam, AvatarHash: memInfo.AvatarHash})
|
||||
for _, b := range render.Boards { // these are already in order
|
||||
if count, ok := brdCounts[b.Board.ID]; ok {
|
||||
render.MemberBoardAssign[len(render.MemberBoardAssign)-1].AssignCounts =
|
||||
append(render.MemberBoardAssign[len(render.MemberBoardAssign)-1].AssignCounts,
|
||||
trelloBoardAssignCount{BoardName: b.Board.Name, Count: count})
|
||||
}
|
||||
}
|
||||
goto found
|
||||
}
|
||||
}
|
||||
found:
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue