1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-10 16:05:34 +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, authenticated: false,
config: {}, config: {},
boards: null, boards: null,
lists: null,
didReceiveAttrs() { didReceiveAttrs() {
let config = {}; let config = {};
@ -24,8 +23,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
appKey: "", appKey: "",
token: "", token: "",
board: null, board: null,
list: null, lists: []
cards: null
}; };
} }
@ -47,7 +45,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
let board = this.get('config.board'); let board = this.get('config.board');
this.set('waiting', true); this.set('waiting', true);
if (board === null) { if (is.null(board)) {
if (boards.length) { if (boards.length) {
board = boards[0]; board = boards[0];
this.set('config.board', board); 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)); 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) { function(lists) {
let savedLists = self.get('config.lists'); let savedLists = self.get('config.lists');
if (savedLists === null) { if (savedLists === null) {
@ -74,8 +72,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
self.set('config.lists', lists); self.set('config.lists', lists);
self.set('waiting', false); self.set('waiting', false);
// self.getListCards();
}); });
}, },
@ -155,25 +151,31 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
this.getBoardLists(); this.getBoardLists();
}, },
// onListChange(list) {
// this.set('config.list', list);
// this.getListCards();
// },
onCancel() { onCancel() {
this.attrs.onCancel(); this.attrs.onCancel();
}, },
onAction(title) { onAction(title) {
this.set('waiting', false);
let self = this;
let page = this.get('page'); let page = this.get('page');
let meta = this.get('meta'); let meta = this.get('meta');
page.set('title', title); page.set('title', title);
// meta.set('rawBody', JSON.stringify(this.get("items")));
meta.set('rawBody', ''); meta.set('rawBody', '');
meta.set('config', JSON.stringify(this.get('config'))); meta.set('config', JSON.stringify(this.get('config')));
meta.set('externalSource', true); 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> <style>
.board { .trello-board {
width: 100%; width: 100%;
padding: 10px; padding: 10px;
white-space: nowrap; white-space: nowrap;
overflow: auto overflow: auto
} }
.board-title { .trello-board-title {
font-weight: bold; font-weight: bold;
color: #fff; color: #fff;
font-size: 16px; font-size: 16px;
} }
.list { .trello-list {
background-color: #e2e4e6; background-color: #e2e4e6;
padding: 10px; padding: 10px;
border-radius: 3px; border-radius: 3px;
@ -20,19 +20,19 @@
max-width: 300px; max-width: 300px;
} }
.list-title { .trello-list-title {
font-weight: bold; font-weight: bold;
color: #4c4c4c; color: #4c4c4c;
font-size: 14px; font-size: 14px;
margin: 5px; margin: 5px;
} }
.list-checkbox { .trello-list-checkbox {
vertical-align: text-bottom; vertical-align: text-bottom;
} }
</style> </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="pull-left width-45">
<div class="input-form"> <div class="input-form">
@ -73,18 +73,16 @@
<div class="input-control"> <div class="input-control">
<label>Lists</label> <label>Lists</label>
<div class="tip">Select lists to include</div> <div class="tip">Select lists to include</div>
<div class="board" style="background-color:{{config.board.prefs.backgroundColor}};"> <div class="trello-board" style="background-color:{{config.board.prefs.backgroundColor}};">
<div class="board-title">{{config.board.name}}</div> <div class="trello-board-title">{{config.board.name}}</div>
{{#each config.lists as |list|}} {{#each config.lists as |list|}}
<div class="list" {{action 'onListCheckbox' list.id}}> <div class="trello-list" {{action 'onListCheckbox' list.id}}>
{{#if list.included}} {{#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}} {{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}} {{/if}}
<span class="list-title">{{list.name}}</span> <span class="trello-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>--}}
</div> </div>
{{/each}} {{/each}}
<div class="clearfix" /> <div class="clearfix" />

View file

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

View file

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

View file

@ -1,7 +1,13 @@
package section package section
import ( import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"net/http" "net/http"
"strings"
) )
type trello struct { type trello struct {
@ -13,7 +19,6 @@ func init() {
func (*trello) Meta() TypeMeta { func (*trello) Meta() TypeMeta {
section := TypeMeta{} section := TypeMeta{}
section.ID = "c455a552-202e-441c-ad79-397a8152920b" section.ID = "c455a552-202e-441c-ad79-397a8152920b"
section.Title = "Trello" section.Title = "Trello"
section.Description = "Trello boards" section.Description = "Trello boards"
@ -25,15 +30,286 @@ func (*trello) Meta() TypeMeta {
// Command stub. // Command stub.
func (*trello) Command(w http.ResponseWriter, r *http.Request) { 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. // Render just sends back HMTL as-is.
func (*trello) Render(config, data string) string { 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. // Refresh just sends back data as-is.
func (*trello) Refresh(config, data string) string { func (*trello) Refresh(config, data string) string {
return data 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
*/