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

fixes and Trello updates

1. Fixed Firefox input field height.
2. Moved to server side Trello API calls.
3. New mixing for Section types.
4. Cleaned up TOC entry HTML
This commit is contained in:
Harvey Kandola 2016-05-18 16:22:42 -07:00
parent 99dca0e1ad
commit 58a8f9169e
10 changed files with 270 additions and 92 deletions

View file

@ -12,8 +12,9 @@
import Ember from 'ember'; import Ember from 'ember';
import NotifierMixin from '../../../mixins/notifier'; import NotifierMixin from '../../../mixins/notifier';
import TooltipMixin from '../../../mixins/tooltip'; import TooltipMixin from '../../../mixins/tooltip';
import SectionMixin from '../../../mixins/section';
export default Ember.Component.extend(NotifierMixin, TooltipMixin, { export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
sectionService: Ember.inject.service('section'), sectionService: Ember.inject.service('section'),
isDirty: false, isDirty: false,
waiting: false, waiting: false,
@ -22,14 +23,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
workspaces: [], workspaces: [],
config: {}, config: {},
fieldEditable: function() {
if (this.get('page.userId') !== this.session.user.id) {
return "readonly";
} else {
return undefined;
}
}.property('config'),
didReceiveAttrs() { didReceiveAttrs() {
let config = {}; let config = {};

View file

@ -13,11 +13,12 @@
import Ember from 'ember'; import Ember from 'ember';
import NotifierMixin from '../../../mixins/notifier'; import NotifierMixin from '../../../mixins/notifier';
import TooltipMixin from '../../../mixins/tooltip'; import TooltipMixin from '../../../mixins/tooltip';
import SectionMixin from '../../../mixins/section';
export default Ember.Component.extend(NotifierMixin, TooltipMixin, { export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
sectionService: Ember.inject.service('section'), sectionService: Ember.inject.service('section'),
isDirty: false, isDirty: false,
waiting: false, busy: false,
authenticated: false, authenticated: false,
config: {}, config: {},
boards: null, boards: null,
@ -42,6 +43,8 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
if (this.get('config.appKey') !== "" && if (this.get('config.appKey') !== "" &&
this.get('config.token') !== "") { this.get('config.token') !== "") {
console.log(this.get('isReadonly'));
console.log(this.get('isMine'));
this.send('auth'); this.send('auth');
} }
}, },
@ -51,12 +54,14 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
}, },
getBoardLists() { getBoardLists() {
this.set('busy', true);
let self = this; let self = this;
let boards = this.get('boards'); let boards = this.get('boards');
let board = this.get('config.board'); let board = this.get('config.board');
this.set('waiting', true); let page = this.get('page');
if (is.null(board)) { if (is.null(board) || is.undefined(board)) {
if (boards.length) { if (boards.length) {
board = boards[0]; board = boards[0];
this.set('config.board', board); this.set('config.board', board);
@ -65,8 +70,8 @@ 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?fields=id,name,url`, this.get('sectionService').fetch(page, "lists", self.get('config'))
function(lists) { .then(function(lists) {
let savedLists = self.get('config.lists'); let savedLists = self.get('config.lists');
if (savedLists === null) { if (savedLists === null) {
savedLists = []; savedLists = [];
@ -82,14 +87,40 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
}); });
self.set('config.lists', lists); self.set('config.lists', lists);
self.set('waiting', false); self.set('busy', false);
}, }, function(error) { //jshint ignore: line
function(error) { self.set('busy', false);
self.set('waiting', false);
self.set('authenticated', false); self.set('authenticated', false);
self.showNotification("Unable to fetch board lists"); self.showNotification("Unable to fetch board lists");
console.log(error); console.log(error);
}); });
// Trello.get(`boards/${board.id}/lists/open?fields=id,name,url`,
// 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;
// }
// list.included = included;
// });
//
// self.set('config.lists', lists);
// self.set('busy', false);
// },
// function(error) {
// self.set('busy', false);
// self.set('authenticated', false);
// self.showNotification("Unable to fetch board lists");
// console.log(error);
// });
}, },
actions: { actions: {
@ -124,7 +155,9 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
} }
let self = this; let self = this;
self.set('waiting', true); let page = this.get('page');
self.set('busy', true);
Ember.$.getScript("https://api.trello.com/1/client.js?key=" + this.get('config.appKey'), function() { Ember.$.getScript("https://api.trello.com/1/client.js?key=" + this.get('config.appKey'), function() {
Trello.authorize({ Trello.authorize({
@ -140,24 +173,36 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
success: function() { success: function() {
self.set('authenticated', true); self.set('authenticated', true);
self.set('config.token', Trello.token()); self.set('config.token', Trello.token());
self.set('waiting', true); self.set('busy', true);
Trello.get("members/me/boards?fields=id,name,url,closed,prefs,idOrganization", self.get('sectionService').fetch(page, "boards", self.get('config'))
function(boards) { .then(function(boards) {
self.set('waiting', false); self.set('busy', false);
self.set('boards', boards.filterBy("closed", false)); self.set('boards', boards.filterBy("closed", false));
self.getBoardLists(); self.getBoardLists();
}, }, function(error) { //jshint ignore: line
function(error) { self.set('busy', false);
self.set('waiting', false);
self.set('authenticated', false); self.set('authenticated', false);
self.showNotification("Unable to fetch boards"); self.showNotification("Unable to fetch boards");
console.log(error); console.log(error);
} });
);
// Trello.get("members/me/boards?fields=id,name,url,closed,prefs,idOrganization",
// function(boards) {
// self.set('busy', false);
// self.set('boards', boards.filterBy("closed", false));
// self.getBoardLists();
// },
// function(error) {
// self.set('busy', false);
// self.set('authenticated', false);
// self.showNotification("Unable to fetch boards");
// console.log(error);
// }
// );
}, },
error: function(error) { error: function(error) {
self.set('waiting', false); self.set('busy', false);
self.set('authenticated', false); self.set('authenticated', false);
self.showNotification("Unable to authenticate"); self.showNotification("Unable to authenticate");
console.log(error); console.log(error);
@ -178,7 +223,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
}, },
onAction(title) { onAction(title) {
this.set('waiting', false); this.set('busy', true);
let self = this; let self = this;
let page = this.get('page'); let page = this.get('page');
@ -191,12 +236,15 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
this.get('sectionService').fetch(page, "cards", this.get('config')) this.get('sectionService').fetch(page, "cards", this.get('config'))
.then(function(response) { .then(function(response) {
meta.set('rawBody', JSON.stringify(response)); meta.set('rawBody', JSON.stringify(response));
self.set('waiting', false); self.set('busy', false);
self.attrs.onAction(page, meta); self.attrs.onAction(page, meta);
}, function(reason) { //jshint ignore: line }, function(reason) { //jshint ignore: line
self.set('waiting', false); self.set('busy', false);
self.attrs.onAction(page, meta); self.attrs.onAction(page, meta);
}); });
} }
} }
}); });
// show who owner is -- logout
// key really required?

View file

@ -13,6 +13,7 @@ export function initialize(application) {
application.inject('route', 'session', 'service:session'); application.inject('route', 'session', 'service:session');
application.inject('controller', 'session', 'service:session'); application.inject('controller', 'session', 'service:session');
application.inject('component', 'session', 'service:session'); application.inject('component', 'session', 'service:session');
application.inject('mixin', 'session', 'service:session');
} }
export default { export default {

26
app/app/mixins/section.js Normal file
View file

@ -0,0 +1,26 @@
// 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
import Ember from 'ember';
export default Ember.Mixin.create({
isReadonly: function() {
if (this.get('page.userId') !== this.session.user.id) {
return "readonly";
} else {
return undefined;
}
}.property('page'),
isMine: function() {
return this.get('page.userId') !== this.session.user.id;
}.property('page')
});

View file

@ -53,7 +53,7 @@
padding: 5px; padding: 5px;
border: 1px solid $color-input; border: 1px solid $color-input;
border-radius: 2px; border-radius: 2px;
height: 2rem; height: 2.3rem;
font-size: 1rem; font-size: 1rem;
display: inline-block; display: inline-block;

View file

@ -47,17 +47,17 @@
<div class="input-control"> <div class="input-control">
<label>Gemini URL</label> <label>Gemini URL</label>
<div class="tip">e.g. http://helpdesk.countersoft.com</div> <div class="tip">e.g. http://helpdesk.countersoft.com</div>
{{focus-input id="gemini-url" type="text" value=config.url readonly=fieldEditable}} {{focus-input id="gemini-url" type="text" value=config.url readonly=isReadonly}}
</div> </div>
<div class="input-control"> <div class="input-control">
<label>Username</label> <label>Username</label>
<div class="tip">Gemini username</div> <div class="tip">Gemini username</div>
{{input id="gemini-username" type="text" value=config.username readonly=fieldEditable}} {{input id="gemini-username" type="text" value=config.username readonly=isReadonly}}
</div> </div>
<div class="input-control"> <div class="input-control">
<label>API Key</label> <label>API Key</label>
<div class="tip">Gemini user API key (from user profile)</div> <div class="tip">Gemini user API key (from user profile)</div>
{{input id="gemini-apikey" type="password" value=config.APIKey readonly=fieldEditable}} {{input id="gemini-apikey" type="password" value=config.APIKey readonly=isReadonly}}
</div> </div>
<div class="regular-button button-blue" {{ action 'auth' }}>Authenticate</div> <div class="regular-button button-blue" {{ action 'auth' }}>Authenticate</div>
</form> </form>

View file

@ -32,7 +32,7 @@
} }
</style> </style>
{{#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')}} {{#section/base-editor document=document folder=folder page=page busy=busy 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">
@ -72,7 +72,8 @@
action=(action 'onBoardChange') action=(action 'onBoardChange')
optionValuePath="id" optionValuePath="id"
optionLabelPath="name" optionLabelPath="name"
selection=config.board}} selection=config.board
readonly=isReadonly}}
</div> </div>
<div class="input-control"> <div class="input-control">
<label>Lists</label> <label>Lists</label>

View file

@ -1,4 +1,4 @@
<select {{action 'change' on='change'}} class={{csslass}}> <select {{action 'change' on='change'}} class={{csslass}} readonly={{readonly}}>
{{#if prompt}} {{#if prompt}}
<option disabled selected={{is-not selection}}> <option disabled selected={{is-not selection}}>
{{prompt}} {{prompt}}

View file

@ -1,14 +1,19 @@
import { documentTocEntry } from '../../../../helpers/document/toc-entry'; import {
import { module, test } from 'qunit'; documentTocEntry
} from '../../../../helpers/document/toc-entry';
import {
module,
test
} from 'qunit';
module('Unit | Helper | document/toc entry'); module('Unit | Helper | document/toc entry');
test('toc entry should be not indented and not selected', function(assert) { test('toc entry should be not indented and not selected', function(assert) {
let result = documentTocEntry(['node-123', 'node-321', 1]); let result = documentTocEntry(['node-123', 'node-321', 1]);
assert.equal(result.toString(), "<span style='margin-left: 0px;'></span><span class=''><i class='material-icons toc-bullet'>remove</i></span>"); assert.equal(result.toString(), "<span style='margin-left: 0px;'></span><span class=''></span>");
}); });
test('toc entry should be indented and selected', function(assert) { test('toc entry should be indented and selected', function(assert) {
let result = documentTocEntry(['node-123', 'node-123', 2]); let result = documentTocEntry(['node-123', 'node-123', 2]);
assert.equal(result.toString(), "<span style='margin-left: 20px;'></span><span class='selected'><i class='material-icons toc-bullet'>remove</i></span>"); assert.equal(result.toString(), "<span style='margin-left: 20px;'></span><span class='selected'></span>");
}); });

View file

@ -51,9 +51,67 @@ func (*trello) Command(w http.ResponseWriter, r *http.Request) {
return return
} }
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)
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
}
switch method { switch method {
case "cards": case "cards":
cards(w, r) render, err := getCards(config)
if err != nil {
fmt.Println(err)
writeError(w, "trello", err)
return
}
writeJSON(w, render)
case "boards":
render, err := getBoards(config)
if err != nil {
fmt.Println(err)
writeError(w, "trello", err)
return
}
writeJSON(w, render)
case "lists":
render, err := getLists(config)
if err != nil {
fmt.Println(err)
writeError(w, "trello", err)
return
}
writeJSON(w, render)
} }
} }
@ -105,50 +163,102 @@ func (*trello) Refresh(config, data string) string {
} }
// Helpers // Helpers
func cards(w http.ResponseWriter, r *http.Request) { // func cards(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close() // defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body) // 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)
// 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, err := getCards(config)
//
// if err != nil {
// fmt.Println(err)
// writeError(w, "trello", err)
// return
// }
//
// writeJSON(w, render)
// }
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)
client := &http.Client{}
res, err := client.Do(req)
if err != nil { if err != nil {
writeMessage(w, "trello", "Bad body") return nil, err
return
} }
var config = trelloConfig{} if res.StatusCode != http.StatusOK {
err = json.Unmarshal(body, &config) return nil, fmt.Errorf("error: HTTP status code %d", res.StatusCode)
if err != nil {
writeError(w, "trello", err)
// writeMessage(w, "trello", "Bad payload")
return
} }
config.Clean() defer res.Body.Close()
if len(config.AppKey) == 0 { dec := json.NewDecoder(res.Body)
writeMessage(w, "trello", "Missing appKey") err = dec.Decode(&boards)
return
}
if len(config.Token) == 0 {
writeMessage(w, "trello", "Missing token")
return
}
render, err := getCards(config)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
writeError(w, "trello", err) return nil, err
return
} }
writeJSON(w, render) 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)
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)
}
defer res.Body.Close()
dec := json.NewDecoder(res.Body)
err = dec.Decode(&lists)
if err != nil {
fmt.Println(err)
return nil, err
}
return lists, nil
} }
func getCards(config trelloConfig) (listCards []trelloListCards, err error) { func getCards(config trelloConfig) (listCards []trelloListCards, err error) {
for _, list := range config.Lists { for _, list := range config.Lists {
// don't process lists that user excluded from rendering
if !list.Included { if !list.Included {
continue continue
} }
@ -200,15 +310,15 @@ func (c *trelloConfig) Clean() {
type trelloBoard struct { type trelloBoard struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Desc string `json:"desc"`
DescData struct {
Emoji struct{} `json:"emoji"`
} `json:"descData"`
Closed bool `json:"closed"` Closed bool `json:"closed"`
OrganizationID string `json:"idOrganization"` OrganizationID string `json:"idOrganization"`
Pinned bool `json:"pinned"` Pinned bool `json:"pinned"`
URL string `json:"url"` URL string `json:"url"`
ShortURL string `json:"shortUrl"` ShortURL string `json:"shortUrl"`
Desc string `json:"desc"`
DescData struct {
Emoji struct{} `json:"emoji"`
} `json:"descData"`
Prefs struct { Prefs struct {
PermissionLevel string `json:"permissionLevel"` PermissionLevel string `json:"permissionLevel"`
Voting string `json:"voting"` Voting string `json:"voting"`
@ -334,10 +444,4 @@ const trelloTemplate = `
/* /*
does server side load up all data? YES!!?? does server side load up all data? YES!!??
owner read-only control?
is appKey is global?
- where stored?
- how access?
- does section.go ask config to give us saved json
*/ */