1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-07 14:35:28 +02:00

Trello board rendering

This commit is contained in:
Harvey Kandola 2016-05-16 22:10:28 -07:00
parent 20e70f52d0
commit d697e7926b
6 changed files with 346 additions and 104 deletions

View file

@ -1,40 +0,0 @@
package section
import (
"net/http"
)
type code struct {
}
func init() {
sectionsMap["code"] = &code{}
}
func (*code) Meta() TypeMeta {
section := TypeMeta{}
section.ID = "4f6f2b02-8397-483d-9bb9-eea1fef13304"
section.Title = "Code"
section.Description = "Code snippets supporting 50+ languages"
section.ContentType = "code"
section.IconFontLigature = "code"
section.Order = 9997
return section
}
// Command stub.
func (*code) Command(w http.ResponseWriter, r *http.Request) {
writeEmpty(w)
}
// Render just sends back HMTL as-is.
func (*code) Render(config, data string) string {
return data
}
// Refresh just sends back data as-is.
func (*code) Refresh(config, data string) string {
return data
}

View file

@ -10,7 +10,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
authenticated: false,
config: {},
boards: null,
lists: null,
didReceiveAttrs() {
let config = {};
@ -24,8 +23,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
appKey: "",
token: "",
board: null,
list: null,
cards: null
lists: []
};
}
@ -47,7 +45,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
let board = this.get('config.board');
this.set('waiting', true);
if (board === null) {
if (is.null(board)) {
if (boards.length) {
board = boards[0];
this.set('config.board', board);
@ -56,7 +54,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
this.set('config.board', boards.findBy('id', board.id));
}
Trello.get(`boards/${board.id}/lists/open`,
Trello.get(`boards/${board.id}/lists/open?fields=id,name,url`,
function(lists) {
let savedLists = self.get('config.lists');
if (savedLists === null) {
@ -74,8 +72,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
self.set('config.lists', lists);
self.set('waiting', false);
// self.getListCards();
});
},
@ -155,25 +151,31 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
this.getBoardLists();
},
// onListChange(list) {
// this.set('config.list', list);
// this.getListCards();
// },
onCancel() {
this.attrs.onCancel();
},
onAction(title) {
this.set('waiting', false);
let self = this;
let page = this.get('page');
let meta = this.get('meta');
page.set('title', title);
// meta.set('rawBody', JSON.stringify(this.get("items")));
meta.set('rawBody', '');
meta.set('config', JSON.stringify(this.get('config')));
meta.set('externalSource', true);
this.attrs.onAction(page, meta);
this.get('sectionService').fetch(page, "cards", this.get('config'))
.then(function(response) {
console.log(response);
meta.set('rawBody', JSON.stringify(response));
self.set('waiting', false);
self.attrs.onAction(page, meta);
}, function(reason) { //jshint ignore: line
self.set('waiting', false);
self.attrs.onAction(page, meta);
});
}
}
});

View file

@ -1,18 +1,18 @@
<style>
.board {
.trello-board {
width: 100%;
padding: 10px;
white-space: nowrap;
overflow: auto
}
.board-title {
.trello-board-title {
font-weight: bold;
color: #fff;
font-size: 16px;
}
.list {
.trello-list {
background-color: #e2e4e6;
padding: 10px;
border-radius: 3px;
@ -20,19 +20,19 @@
max-width: 300px;
}
.list-title {
.trello-list-title {
font-weight: bold;
color: #4c4c4c;
font-size: 14px;
margin: 5px;
}
.list-checkbox {
.trello-list-checkbox {
vertical-align: text-bottom;
}
</style>
{{#section/base-editor document=document folder=folder page=page busy=waiting tip="Trello, yellow, mellow" isDirty=(action 'isDirty') onCancel=(action 'onCancel') onAction=(action 'onAction')}}
{{#section/base-editor document=document folder=folder page=page busy=waiting 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')}}
<div class="pull-left width-45">
<div class="input-form">
@ -73,18 +73,16 @@
<div class="input-control">
<label>Lists</label>
<div class="tip">Select lists to include</div>
<div class="board" style="background-color:{{config.board.prefs.backgroundColor}};">
<div class="board-title">{{config.board.name}}</div>
<div class="trello-board" style="background-color:{{config.board.prefs.backgroundColor}};">
<div class="trello-board-title">{{config.board.name}}</div>
{{#each config.lists as |list|}}
<div class="list" {{action 'onListCheckbox' list.id}}>
<div class="trello-list" {{action 'onListCheckbox' list.id}}>
{{#if list.included}}
<i class="material-icons widget-checkbox checkbox-gray list-checkbox" >check_box</i>
<i class="material-icons widget-checkbox checkbox-gray trello-list-checkbox" >check_box</i>
{{else}}
<i class="material-icons widget-checkbox checkbox-gray list-checkbox">check_box_outline_blank</i>
<i class="material-icons widget-checkbox checkbox-gray trello-list-checkbox">check_box_outline_blank</i>
{{/if}}
<span class="list-title">{{list.name}}</span>
{{!--<input type="checkbox" id="trello-list-{{list.id}}" checked={{list.included}} />--}}
{{!--<label class="list-title" for="trello-list-{{list.id}}">{{list.name}}</label>--}}
<span class="trello-list-title">{{list.name}}</span>
</div>
{{/each}}
<div class="clearfix" />

View file

@ -1,5 +1,5 @@
<style>
.board {
.trello-board {
width: 100%;
max-height: 600px;
padding: 10px;
@ -7,7 +7,13 @@
overflow: auto
}
.list {
.trello-board-title {
font-weight: bold;
color: #fff;
font-size: 16px;
}
.trello-list {
background-color: #e2e4e6;
padding: 10px;
border-radius: 3px;
@ -16,17 +22,18 @@
max-height: 500px;
display: inline-block;
white-space: nowrap;
overflow: auto
overflow: auto;
vertical-align: top;
}
.list-title {
.trello-list-title {
font-weight: bold;
color: #4c4c4c;
font-size: 14px;
margin: 0 10px 10px 0;
}
.card {
.trello-card {
color: #4c4c4c;
border-bottom: 1px solid #CDD2D4;
background-color: #fff;
@ -39,6 +46,8 @@
overflow: hidden;
word-wrap: break-word;
white-space: normal;
cursor: pointer;
vertical-align: top;
}
</style>

View file

@ -13,8 +13,6 @@ import (
"github.com/documize/community/wordsmith/log"
)
const sectionName = "gemini"
// the HTML that is rendered by this section.
const renderTemplate = `
<p class="margin-left-20">The Gemini workspace <a href="{{.Config.URL}}/workspace/{{.Config.WorkspaceID}}/items">{{.Config.WorkspaceName}}</a> contains {{.Config.ItemCount}} items.</p>
@ -47,17 +45,16 @@ type gemini struct {
// Register ourselves.
func init() {
sectionsMap[sectionName] = &gemini{}
sectionsMap["gemini"] = &gemini{}
}
// Meta describes this section type.
func (*gemini) Meta() TypeMeta {
section := TypeMeta{}
section.ID = "23b133f9-4020-4616-9291-a98fb939735f"
section.Title = "Gemini"
section.Description = "Display work items and tickets from Gemini workspaces"
section.ContentType = sectionName
section.ContentType = "gemini"
section.IconFontLigature = ""
section.IconFilePath = "sections/gemini.png"
@ -93,7 +90,7 @@ func (*gemini) Command(w http.ResponseWriter, r *http.Request) {
method := query.Get("method")
if len(method) == 0 {
writeMessage(w, sectionName, "missing method name")
writeMessage(w, "gemini", "missing method name")
return
}
@ -222,7 +219,7 @@ func auth(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
writeMessage(w, sectionName, "Bad payload")
writeMessage(w, "gemini", "Bad payload")
return
}
@ -230,24 +227,24 @@ func auth(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(body, &config)
if err != nil {
writeMessage(w, sectionName, "Bad payload")
writeMessage(w, "gemini", "Bad payload")
return
}
config.Clean()
if len(config.URL) == 0 {
writeMessage(w, sectionName, "Missing URL value")
writeMessage(w, "gemini", "Missing URL value")
return
}
if len(config.Username) == 0 {
writeMessage(w, sectionName, "Missing Username value")
writeMessage(w, "gemini", "Missing Username value")
return
}
if len(config.APIKey) == 0 {
writeMessage(w, sectionName, "Missing APIKey value")
writeMessage(w, "gemini", "Missing APIKey value")
return
}
@ -261,7 +258,7 @@ func auth(w http.ResponseWriter, r *http.Request) {
if err != nil {
fmt.Println(err)
writeError(w, sectionName, err)
writeError(w, "gemini", err)
return
}
@ -278,7 +275,7 @@ func auth(w http.ResponseWriter, r *http.Request) {
if err != nil {
fmt.Println(err)
writeError(w, sectionName, err)
writeError(w, "gemini", err)
return
}
@ -290,7 +287,7 @@ func workspace(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
writeMessage(w, sectionName, "Bad payload")
writeMessage(w, "gemini", "Bad payload")
return
}
@ -298,29 +295,29 @@ func workspace(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(body, &config)
if err != nil {
writeMessage(w, sectionName, "Bad payload")
writeMessage(w, "gemini", "Bad payload")
return
}
config.Clean()
if len(config.URL) == 0 {
writeMessage(w, sectionName, "Missing URL value")
writeMessage(w, "gemini", "Missing URL value")
return
}
if len(config.Username) == 0 {
writeMessage(w, sectionName, "Missing Username value")
writeMessage(w, "gemini", "Missing Username value")
return
}
if len(config.APIKey) == 0 {
writeMessage(w, sectionName, "Missing APIKey value")
writeMessage(w, "gemini", "Missing APIKey value")
return
}
if config.UserID == 0 {
writeMessage(w, sectionName, "Missing UserId value")
writeMessage(w, "gemini", "Missing UserId value")
return
}
@ -334,7 +331,7 @@ func workspace(w http.ResponseWriter, r *http.Request) {
if err != nil {
fmt.Println(err)
writeError(w, sectionName, err)
writeError(w, "gemini", err)
return
}
@ -363,7 +360,7 @@ func items(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
writeMessage(w, sectionName, "Bad payload")
writeMessage(w, "gemini", "Bad payload")
return
}
@ -371,24 +368,24 @@ func items(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(body, &config)
if err != nil {
writeMessage(w, sectionName, "Bad payload")
writeMessage(w, "gemini", "Bad payload")
return
}
config.Clean()
if len(config.URL) == 0 {
writeMessage(w, sectionName, "Missing URL value")
writeMessage(w, "gemini", "Missing URL value")
return
}
if len(config.Username) == 0 {
writeMessage(w, sectionName, "Missing Username value")
writeMessage(w, "gemini", "Missing Username value")
return
}
if len(config.APIKey) == 0 {
writeMessage(w, sectionName, "Missing APIKey value")
writeMessage(w, "gemini", "Missing APIKey value")
return
}
@ -397,7 +394,7 @@ func items(w http.ResponseWriter, r *http.Request) {
filter, err := json.Marshal(config.Filter)
if err != nil {
fmt.Println(err)
writeError(w, sectionName, err)
writeError(w, "gemini", err)
return
}
@ -411,7 +408,7 @@ func items(w http.ResponseWriter, r *http.Request) {
if err != nil {
fmt.Println(err)
writeError(w, sectionName, err)
writeError(w, "gemini", err)
return
}
@ -428,7 +425,7 @@ func items(w http.ResponseWriter, r *http.Request) {
if err != nil {
fmt.Println(err)
writeError(w, sectionName, err)
writeError(w, "gemini", err)
return
}

View file

@ -1,7 +1,13 @@
package section
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"strings"
)
type trello struct {
@ -13,7 +19,6 @@ func init() {
func (*trello) Meta() TypeMeta {
section := TypeMeta{}
section.ID = "c455a552-202e-441c-ad79-397a8152920b"
section.Title = "Trello"
section.Description = "Trello boards"
@ -25,15 +30,286 @@ func (*trello) Meta() TypeMeta {
// Command stub.
func (*trello) Command(w http.ResponseWriter, r *http.Request) {
writeEmpty(w)
query := r.URL.Query()
method := query.Get("method")
if len(method) == 0 {
writeMessage(w, "trello", "missing method name")
return
}
switch method {
case "cards":
cards(w, r)
}
}
// Render just sends back HMTL as-is.
func (*trello) Render(config, data string) string {
return data
raw := []trelloListCards{}
payload := trelloRender{}
var c = trelloConfig{}
json.Unmarshal([]byte(data), &raw)
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)
}
t := template.New("trello")
t, _ = t.Parse(trelloTemplate)
buffer := new(bytes.Buffer)
t.Execute(buffer, payload)
return buffer.String()
}
// Refresh just sends back data as-is.
func (*trello) Refresh(config, data string) string {
return data
}
// Helpers
func cards(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
writeMessage(w, "trello", "Bad body")
return
}
var config = trelloConfig{}
err = json.Unmarshal(body, &config)
if err != nil {
writeError(w, "trello", err)
// writeMessage(w, "trello", "Bad payload")
return
}
config.Clean()
if len(config.AppKey) == 0 {
writeMessage(w, "trello", "Missing appKey")
return
}
if len(config.Token) == 0 {
writeMessage(w, "trello", "Missing token")
return
}
render := []trelloListCards{}
for _, list := range config.Lists {
if !list.Included {
continue
}
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)
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
writeError(w, "trello", err)
return
}
if res.StatusCode != http.StatusOK {
writeForbidden(w)
return
}
defer res.Body.Close()
var cards []trelloCard
dec := json.NewDecoder(res.Body)
err = dec.Decode(&cards)
if err != nil {
fmt.Println(err)
writeError(w, "trello", err)
return
}
data := trelloListCards{}
data.Cards = cards
data.List = list
render = append(render, data)
}
writeJSON(w, render)
}
type trelloConfig struct {
AppKey string `json:"appKey"`
Token string `json:"token"`
Board trelloBoard `json:"board"`
Lists []trelloList `json:"lists"`
}
func (c *trelloConfig) Clean() {
c.AppKey = strings.TrimSpace(c.AppKey)
c.Token = strings.TrimSpace(c.Token)
}
type trelloBoard struct {
ID string `json:"id"`
Name string `json:"name"`
Desc string `json:"desc"`
DescData struct {
Emoji struct{} `json:"emoji"`
} `json:"descData"`
Closed bool `json:"closed"`
OrganizationID string `json:"idOrganization"`
Pinned bool `json:"pinned"`
URL string `json:"url"`
ShortURL string `json:"shortUrl"`
Prefs struct {
PermissionLevel string `json:"permissionLevel"`
Voting string `json:"voting"`
Comments string `json:"comments"`
Invitations string `json:"invitations"`
SelfJoin bool `json:"selfjoin"`
CardCovers bool `json:"cardCovers"`
CardAging string `json:"cardAging"`
CalendarFeedEnabled bool `json:"calendarFeedEnabled"`
Background string `json:"background"`
BackgroundColor string `json:"backgroundColor"`
BackgroundImage string `json:"backgroundImage"`
BackgroundImageScaled []trelloBoardBackground `json:"backgroundImageScaled"`
BackgroundTile bool `json:"backgroundTile"`
BackgroundBrightness string `json:"backgroundBrightness"`
CanBePublic bool `json:"canBePublic"`
CanBeOrg bool `json:"canBeOrg"`
CanBePrivate bool `json:"canBePrivate"`
CanInvite bool `json:"canInvite"`
} `json:"prefs"`
LabelNames struct {
Red string `json:"red"`
Orange string `json:"orange"`
Yellow string `json:"yellow"`
Green string `json:"green"`
Blue string `json:"blue"`
Purple string `json:"purple"`
} `json:"labelNames"`
}
type trelloBoardBackground struct {
width int `json:"width"`
height int `json:"height"`
url string `json:"url"`
}
type trelloList struct {
ID string `json:"id"`
Name string `json:"name"`
Closed bool `json:"closed"`
BoardID string `json:"idBoard"`
Pos float32 `json:"pos"`
Included bool `json:"included"` // indicates whether we display cards from this list
}
type trelloCard struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
ShortID int `json:"idShort"`
AttachmentCoverID string `json:"idAttachmentCover"`
CheckListsID []string `json:"idCheckLists"`
BoardID string `json:"idBoard"`
ListID string `json:"idList"`
MembersID []string `json:"idMembers"`
MembersVotedID []string `json:"idMembersVoted"`
ManualCoverAttachment bool `json:"manualCoverAttachment"`
Closed bool `json:"closed"`
Pos float32 `json:"pos"`
ShortLink string `json:"shortLink"`
DateLastActivity string `json:"dateLastActivity"`
ShortURL string `json:"shortUrl"`
Subscribed bool `json:"subscribed"`
URL string `json:"url"`
Due string `json:"due"`
Desc string `json:"desc"`
DescData struct {
Emoji struct{} `json:"emoji"`
} `json:"descData"`
CheckItemStates []struct {
CheckItemID string `json:"idCheckItem"`
State string `json:"state"`
} `json:"checkItemStates"`
Badges struct {
Votes int `json:"votes"`
ViewingMemberVoted bool `json:"viewingMemberVoted"`
Subscribed bool `json:"subscribed"`
Fogbugz string `json:"fogbugz"`
CheckItems int `json:"checkItems"`
CheckItemsChecked int `json:"checkItemsChecked"`
Comments int `json:"comments"`
Attachments int `json:"attachments"`
Description bool `json:"description"`
Due string `json:"due"`
} `json:"badges"`
Labels []struct {
Color string `json:"color"`
Name string `json:"name"`
} `json:"labels"`
}
type trelloListCards struct {
List trelloList
Cards []trelloCard
}
type trelloRender struct {
Board trelloBoard
Data []trelloListCards
CardCount int
ListCount int
}
// the HTML that is rendered by this section
const trelloTemplate = `
<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>
`
/*
refresh method
does server side load up all data? YES!!??
we need method to use different trello accounts
- does this mean logout button?
- does this only work on add section phase?
is appKey is global?
- where stored?
- how access?
- does section.go ask config to give us saved json
*/